use core::mem::MaybeUninit;
pub unsafe fn b64_ntop(src: *const u8, srclength: usize, target: *mut u8, targsize: usize) -> i32 {
let src = unsafe { std::slice::from_raw_parts(src, srclength) };
let dst = unsafe { std::slice::from_raw_parts_mut(target.cast::<MaybeUninit<u8>>(), targsize) };
match ntop(src, dst) {
Ok(out) => out.len() as i32,
Err(()) => -1,
}
}
pub unsafe fn b64_pton(src: *const u8, target: *mut u8, targsize: usize) -> i32 {
let srclength: usize = unsafe { crate::libc::strlen(src) };
let src = unsafe { std::slice::from_raw_parts(src.cast::<u8>(), srclength) };
let dst = unsafe { std::slice::from_raw_parts_mut(target.cast::<MaybeUninit<u8>>(), targsize) };
match pton(src, dst) {
Ok(out) => out.len() as i32,
Err(()) => -1,
}
}
const ALPHABET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const REVERSE: [u8; 256] = const {
let mut tmp = [u8::MAX; 256];
let mut i: u8 = 0;
while i < ALPHABET.len() as u8 {
tmp[ALPHABET[i as usize] as usize] = i;
i += 1;
}
tmp
};
fn pton<'out>(src: &'_ [u8], dst: &'out mut [MaybeUninit<u8>]) -> Result<&'out mut [u8], ()> {
let targsize = dst.len();
let mut tarindex: usize = 0;
let mut state: u8 = 0;
let mut idx = 0;
let mut got_pad = false;
macro_rules! cur {
($i:expr) => {
unsafe { dst[$i].assume_init() }
};
}
while idx < src.len() {
let ch = src[idx];
idx += 1;
if ch.is_ascii_whitespace() {
continue; }
if ch == b'=' {
got_pad = true; break;
}
let pos = REVERSE[ch as usize];
if pos == u8::MAX {
return Err(()); }
match state {
0 => {
if tarindex >= targsize {
return Err(());
}
dst[tarindex] = MaybeUninit::new(pos << 2);
state = 1;
}
1 => {
if tarindex >= targsize {
return Err(());
}
dst[tarindex] = MaybeUninit::new(cur!(tarindex) | pos >> 4);
let nextbyte = (pos & 0x0f) << 4;
if tarindex + 1 < targsize {
dst[tarindex + 1] = MaybeUninit::new(nextbyte);
} else if nextbyte != 0 {
return Err(());
}
tarindex += 1;
state = 2;
}
2 => {
if tarindex >= targsize {
return Err(());
}
dst[tarindex] = MaybeUninit::new(cur!(tarindex) | pos >> 2);
let nextbyte = (pos & 0x03) << 6;
if tarindex + 1 < targsize {
dst[tarindex + 1] = MaybeUninit::new(nextbyte);
} else if nextbyte != 0 {
return Err(());
}
tarindex += 1;
state = 3;
}
_ => {
if tarindex >= targsize {
return Err(());
}
dst[tarindex] = MaybeUninit::new(cur!(tarindex) | pos);
tarindex += 1;
state = 0;
}
}
}
if got_pad {
match state {
0 | 1 => return Err(()), 2 => {
let mut second_pad = false;
while idx < src.len() {
let c = src[idx];
idx += 1;
if !c.is_ascii_whitespace() {
second_pad = c == b'=';
break;
}
}
if !second_pad {
return Err(());
}
}
_ => {} }
while idx < src.len() {
let c = src[idx];
idx += 1;
if !c.is_ascii_whitespace() {
return Err(());
}
}
if tarindex < targsize && cur!(tarindex) != 0 {
return Err(());
}
} else if state != 0 {
return Err(());
}
Ok(unsafe { std::slice::from_raw_parts_mut(dst.as_mut_ptr().cast::<u8>(), tarindex) })
}
fn ntop<'out>(src: &'_ [u8], dst: &'out mut [MaybeUninit<u8>]) -> Result<&'out mut [u8], ()> {
if dst.len() < src.len().div_ceil(3) * 4 + 1 {
return Err(());
}
let mut i = 0;
let mut it = src.chunks_exact(3);
macro_rules! enc {
($e:expr) => {
MaybeUninit::new(ALPHABET[($e & 0b00111111) as usize])
}
}
for chunk in &mut it {
dst[i] = enc!(chunk[0] >> 2);
dst[i + 1] = enc!(chunk[0] << 4 | chunk[1] >> 4);
dst[i + 2] = enc!(chunk[1] << 2 | chunk[2] >> 6);
dst[i + 3] = enc!(chunk[2]);
i += 4;
}
let chunk = it.remainder();
match chunk.len() {
0 => (),
1 => {
dst[i] = enc!(chunk[0] >> 2);
dst[i + 1] = enc!(chunk[0] << 4);
dst[i + 2] = MaybeUninit::new(b'=');
dst[i + 3] = MaybeUninit::new(b'=');
i += 4;
}
2 => {
dst[i] = enc!(chunk[0] >> 2);
dst[i + 1] = enc!(chunk[0] << 4 | chunk[1] >> 4);
dst[i + 2] = enc!(chunk[1] << 2);
dst[i + 3] = MaybeUninit::new(b'=');
i += 4;
}
_ => unreachable!(),
}
dst[i] = MaybeUninit::new(b'\0');
Ok(unsafe { std::slice::from_raw_parts_mut(dst.as_mut_ptr().cast::<u8>(), i) })
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_b64_pton_valid() {
let input = crate::c!("TWFu");
let mut output = [0u8; 4];
let expected = [b'M', b'a', b'n', 0];
unsafe {
let result = b64_pton(input, output.as_mut_ptr(), output.len());
assert_eq!(&output, &expected);
assert_eq!(result, 3);
}
}
#[test]
fn test_b64_pton_invalid() {
let input = crate::c!("****");
let mut output = [0u8; 3];
unsafe {
let result = b64_pton(input, output.as_mut_ptr(), output.len());
assert_eq!(result, -1);
}
}
#[test]
fn test_b64_pton_partial() {
let input = crate::c!("TWE=");
let mut output = [0u8; 2];
unsafe {
let result = b64_pton(input, output.as_mut_ptr(), output.len());
assert_eq!(result, 2);
assert_eq!(&output, b"Ma");
}
}
#[test]
fn test_b64_ntop_full_quantum() {
let mut out = [0xffu8; 8];
let ret = unsafe { b64_ntop(b"Man".as_ptr(), 3, out.as_mut_ptr(), out.len()) };
assert_eq!(&out[..5], b"TWFu\0");
assert_eq!(ret, 4);
}
#[test]
fn test_b64_ntop_one_pad() {
let mut out = [0xffu8; 8];
let ret = unsafe { b64_ntop(b"M".as_ptr(), 1, out.as_mut_ptr(), out.len()) };
assert_eq!(&out[..5], b"TQ==\0");
assert_eq!(ret, 4);
}
#[test]
fn test_b64_ntop_two_pad() {
let mut out = [0xffu8; 8];
let ret = unsafe { b64_ntop(b"Ma".as_ptr(), 2, out.as_mut_ptr(), out.len()) };
assert_eq!(&out[..5], b"TWE=\0");
assert_eq!(ret, 4);
}
#[test]
fn test_b64_ntop_multi_quantum() {
let mut out = [0xffu8; 16];
let ret = unsafe { b64_ntop(b"foobar".as_ptr(), 6, out.as_mut_ptr(), out.len()) };
assert_eq!(&out[..9], b"Zm9vYmFy\0");
assert_eq!(ret, 8);
}
#[test]
fn test_b64_ntop_empty_returns_zero() {
let mut out = [0xffu8; 4];
let ret = unsafe { b64_ntop(b"".as_ptr(), 0, out.as_mut_ptr(), out.len()) };
assert_eq!(ret, 0);
assert_eq!(out[0], 0);
}
#[test]
fn test_b64_ntop_target_too_small() {
let mut out = [0xffu8; 4];
let ret = unsafe { b64_ntop(b"Man".as_ptr(), 3, out.as_mut_ptr(), 4) };
assert_eq!(ret, -1);
}
#[test]
fn test_b64_ntop_plus_slash() {
let mut out = [0xffu8; 8];
let ret = unsafe { b64_ntop([0xFB, 0xFF, 0xBFu8].as_ptr(), 3, out.as_mut_ptr(), out.len()) };
assert_eq!(&out[..5], b"+/+/\0");
assert_eq!(ret, 4);
}
#[test]
fn test_b64_roundtrip_no_padding() {
let src = b"Man"; let mut enc = [0u8; 16];
unsafe {
b64_ntop(src.as_ptr(), src.len(), enc.as_mut_ptr(), enc.len());
}
assert_eq!(&enc[..5], b"TWFu\0");
let mut dec = [0u8; 16];
let n = unsafe { b64_pton(enc.as_ptr(), dec.as_mut_ptr(), dec.len()) };
assert_eq!(n, 3);
assert_eq!(&dec[..3], src);
}
#[test]
fn test_b64_pton_decodes_digits() {
let mut out = [0u8; 16];
let n = unsafe { b64_pton(crate::c!("Zm9vYmFy"), out.as_mut_ptr(), out.len()) };
assert_eq!(n, 6);
assert_eq!(&out[..6], b"foobar");
}
#[test]
fn test_b64_pton_skips_whitespace() {
let input = crate::c!("TW Fu");
let mut out = [0u8; 8];
let n = unsafe { b64_pton(input, out.as_mut_ptr(), out.len()) };
assert_eq!(n, 3);
assert_eq!(&out[..3], b"Man");
}
#[test]
fn test_b64_pton_incomplete_group() {
let input = crate::c!("TWF");
let mut out = [0u8; 8];
let n = unsafe { b64_pton(input, out.as_mut_ptr(), out.len()) };
assert_eq!(n, -1);
}
#[test]
fn test_b64_pton_two_pad() {
let mut out = [0u8; 8];
let n = unsafe { b64_pton(crate::c!("TQ=="), out.as_mut_ptr(), out.len()) };
assert_eq!(n, 1);
assert_eq!(out[0], b'M');
}
#[test]
fn test_b64_pton_subliminal_bits_rejected() {
let mut out = [0u8; 8];
let n = unsafe { b64_pton(crate::c!("TZ=="), out.as_mut_ptr(), out.len()) };
assert_eq!(n, -1);
}
#[test]
fn test_b64_pton_missing_second_pad() {
let mut out = [0u8; 8];
let n = unsafe { b64_pton(crate::c!("TQ="), out.as_mut_ptr(), out.len()) };
assert_eq!(n, -1);
}
#[test]
fn test_b64_pton_output_too_small() {
let mut out = [0u8; 4];
let n = unsafe { b64_pton(crate::c!("TWFuTWFu"), out.as_mut_ptr(), out.len()) };
assert_eq!(n, -1);
}
#[test]
fn test_b64_roundtrip_all_lengths() {
let src: &[u8] = b"abcdefghi";
for len in 0..=src.len() {
let mut enc = [0u8; 32];
let ncoded =
unsafe { b64_ntop(src.as_ptr(), len, enc.as_mut_ptr(), enc.len()) };
assert_eq!(ncoded as usize, if len == 0 { 0 } else { len.div_ceil(3) * 4 });
assert_eq!(enc[ncoded as usize], 0, "NUL terminator at len {len}");
let mut dec = [0u8; 32];
let ndec = unsafe { b64_pton(enc.as_ptr(), dec.as_mut_ptr(), dec.len()) };
assert_eq!(ndec, len as i32, "decoded length at len {len}");
assert_eq!(&dec[..len], &src[..len], "roundtrip at len {len}");
}
}
#[test]
fn test_b64_pton_empty_returns_zero() {
let mut out = [0u8; 8];
let n = unsafe { b64_pton(crate::c!(""), out.as_mut_ptr(), out.len()) };
assert_eq!(n, 0);
}
#[test]
fn test_b64_ntop_all_zero_bytes() {
let mut out = [0xffu8; 8];
let ret = unsafe { b64_ntop([0u8, 0, 0].as_ptr(), 3, out.as_mut_ptr(), out.len()) };
assert_eq!(&out[..5], b"AAAA\0");
assert_eq!(ret, 4);
let mut dec = [0xffu8; 8];
let n = unsafe { b64_pton(crate::c!("AAAA"), dec.as_mut_ptr(), dec.len()) };
assert_eq!(n, 3);
assert_eq!(&dec[..3], &[0u8, 0, 0]);
}
#[test]
fn test_b64_pton_skips_interior_newlines() {
let mut out = [0u8; 16];
let n = unsafe { b64_pton(crate::c!("Zm9v\nYmFy\t"), out.as_mut_ptr(), out.len()) };
assert_eq!(n, 6);
assert_eq!(&out[..6], b"foobar");
}
}