basenc 1.0.0-alpha.1

High-performance SIMD-accelerated Base64, Base32, and hex encoding and decoding with no_std support.
Documentation
use core::arch::aarch64::*;

use super::*;

#[inline]
#[target_feature(enable = "neon")]
unsafe fn decode_hex(input: uint8x16_t) -> Result<uint8x16_t, crate::ErrorKind> {
	let digit = vandq_u8(vcgeq_u8(input, vdupq_n_u8(b'0')), vcleq_u8(input, vdupq_n_u8(b'9')));
	let upper = vandq_u8(vcgeq_u8(input, vdupq_n_u8(b'A')), vcleq_u8(input, vdupq_n_u8(b'F')));
	let lower = vandq_u8(vcgeq_u8(input, vdupq_n_u8(b'a')), vcleq_u8(input, vdupq_n_u8(b'f')));
	let valid = vorrq_u8(digit, vorrq_u8(upper, lower));
	if vminvq_u8(valid) != u8::MAX {
		return Err(crate::ErrorKind::InvalidCharacter);
	}

	let digit_value = vsubq_u8(input, vdupq_n_u8(b'0'));
	let upper_value = vaddq_u8(vsubq_u8(input, vdupq_n_u8(b'A')), vdupq_n_u8(10));
	let lower_value = vaddq_u8(vsubq_u8(input, vdupq_n_u8(b'a')), vdupq_n_u8(10));
	Ok(vbslq_u8(digit, digit_value, vbslq_u8(upper, upper_value, lower_value)))
}

#[inline]
#[target_feature(enable = "neon")]
unsafe fn nibbles_to_bytes(nibbles: uint8x16_t) -> uint8x8_t {
	let high = vget_low_u8(vuzp1q_u8(nibbles, vdupq_n_u8(0)));
	let low = vget_low_u8(vuzp2q_u8(nibbles, vdupq_n_u8(0)));
	vorr_u8(vshl_n_u8::<4>(high), low)
}

#[target_feature(enable = "neon")]
pub unsafe fn decode(mut string: &[u8], mut dest: *mut u8) -> Result<*mut u8, crate::Error> {
	let input_len = string.len();
	while string.len() >= 32 {
		let first = decode_hex(vld1q_u8(string.as_ptr()));
		let second = decode_hex(vld1q_u8(string.as_ptr().add(16)));
		let (Ok(first), Ok(second)) = (first, second)
		else {
			return scalar::decode(string, dest).map_err(|error| error.shifted(input_len - string.len()));
		};
		let bytes = vcombine_u8(nibbles_to_bytes(first), nibbles_to_bytes(second));
		vst1q_u8(dest, bytes);
		string = string.get_unchecked(32..);
		dest = dest.add(16);
	}

	if string.len() >= 16 {
		let Ok(nibbles) = decode_hex(vld1q_u8(string.as_ptr()))
		else {
			return scalar::decode(string, dest).map_err(|error| error.shifted(input_len - string.len()));
		};
		vst1_u8(dest, nibbles_to_bytes(nibbles));
		string = string.get_unchecked(16..);
		dest = dest.add(8);
	}

	scalar::decode(string, dest).map_err(|error| error.shifted(input_len - string.len()))
}