#[cfg(target_arch = "x86_64")]
use crate::core::config::EncodingMode;
#[cfg(target_arch = "x86_64")]
use crate::core::dictionary::Dictionary;
#[cfg(target_arch = "x86_64")]
use std::sync::OnceLock;
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
pub mod lut;
pub mod variants;
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
pub mod generic;
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
pub mod translate;
#[cfg(target_arch = "x86_64")]
mod x86_64;
#[cfg(target_arch = "aarch64")]
mod aarch64;
#[cfg(target_arch = "x86_64")]
pub use x86_64::{
decode_base16_simd, decode_base32_simd, decode_base64_simd, decode_base256_simd,
encode_base16_simd, encode_base32_simd, encode_base64_simd, encode_base256_simd,
};
#[cfg(target_arch = "aarch64")]
pub use aarch64::{
decode_base16_simd, decode_base32_simd, decode_base64_simd, decode_base256_simd,
encode_base16_simd, encode_base32_simd, encode_base64_simd, encode_base256_simd,
};
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
pub use generic::GenericSimdCodec;
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
pub use lut::{Base64LutCodec, GappedSequentialCodec, SmallLutCodec};
#[cfg(target_arch = "x86_64")]
static HAS_SSSE3: OnceLock<bool> = OnceLock::new();
#[cfg(target_arch = "x86_64")]
pub fn has_avx2() -> bool {
crate::simd::x86_64::has_avx2()
}
#[cfg(target_arch = "x86_64")]
pub fn has_ssse3() -> bool {
*HAS_SSSE3.get_or_init(|| is_x86_feature_detected!("ssse3"))
}
#[cfg(target_arch = "aarch64")]
#[allow(dead_code)]
pub fn has_avx2() -> bool {
false }
#[cfg(target_arch = "aarch64")]
#[allow(dead_code)]
pub fn has_ssse3() -> bool {
false }
#[cfg(target_arch = "aarch64")]
#[allow(dead_code)]
pub fn has_neon() -> bool {
true
}
#[cfg(all(not(target_arch = "x86_64"), not(target_arch = "aarch64")))]
pub fn has_avx2() -> bool {
false
}
#[cfg(all(not(target_arch = "x86_64"), not(target_arch = "aarch64")))]
pub fn has_ssse3() -> bool {
false
}
#[cfg(target_arch = "x86_64")]
pub fn encode_with_simd(data: &[u8], dict: &Dictionary) -> Option<String> {
if !has_avx2() && !has_ssse3() {
return None;
}
let base = dict.base();
if base == 64
&& let Some(_variant) = variants::identify_base64_variant(dict)
{
return encode_base64_simd(data, dict);
}
if base == 32
&& let Some(_variant) = variants::identify_base32_variant(dict)
{
return encode_base32_simd(data, dict);
}
if base == 16 && is_standard_hex(dict) {
return encode_base16_simd(data, dict);
}
if base == 256 && *dict.mode() == EncodingMode::ByteRange {
return encode_base256_simd(data, dict);
}
if let Some(codec) = GenericSimdCodec::from_dictionary(dict) {
return codec.encode(data, dict);
}
if let Some(codec) = GappedSequentialCodec::from_dictionary(dict) {
return codec.encode(data, dict);
}
if base <= 16
&& base.is_power_of_two()
&& let Some(codec) = SmallLutCodec::from_dictionary(dict)
{
return codec.encode(data, dict);
}
if (17..=64).contains(&base)
&& base.is_power_of_two()
&& let Some(codec) = Base64LutCodec::from_dictionary(dict)
{
return codec.encode(data, dict);
}
None
}
#[cfg(target_arch = "x86_64")]
#[allow(dead_code)]
pub fn decode_with_simd(encoded: &str, dict: &Dictionary) -> Option<Vec<u8>> {
if !has_avx2() && !has_ssse3() {
return None;
}
let base = dict.base();
if base == 64 && variants::identify_base64_variant(dict).is_some() {
return decode_base64_simd(encoded, dict);
}
if base == 32 && variants::identify_base32_variant(dict).is_some() {
return decode_base32_simd(encoded, dict);
}
if base == 16 && is_standard_hex(dict) {
return decode_base16_simd(encoded, dict);
}
if base == 256 && *dict.mode() == EncodingMode::ByteRange {
return decode_base256_simd(encoded, dict);
}
if let Some(codec) = GenericSimdCodec::from_dictionary(dict) {
return codec.decode(encoded, dict);
}
if let Some(codec) = GappedSequentialCodec::from_dictionary(dict) {
return codec.decode(encoded, dict);
}
if base <= 16
&& base.is_power_of_two()
&& let Some(codec) = SmallLutCodec::from_dictionary(dict)
{
return codec.decode(encoded, dict);
}
if (17..=64).contains(&base)
&& base.is_power_of_two()
&& let Some(codec) = Base64LutCodec::from_dictionary(dict)
{
return codec.decode(encoded, dict);
}
None
}
#[cfg(target_arch = "x86_64")]
fn is_standard_hex(dict: &Dictionary) -> bool {
if dict.base() != 16 {
return false;
}
let uppercase = "0123456789ABCDEF";
let mut matches_upper = true;
for (i, expected) in uppercase.chars().enumerate() {
if dict.encode_digit(i) != Some(expected) {
matches_upper = false;
break;
}
}
if matches_upper {
return true;
}
let lowercase = "0123456789abcdef";
let mut matches_lower = true;
for (i, expected) in lowercase.chars().enumerate() {
if dict.encode_digit(i) != Some(expected) {
matches_lower = false;
break;
}
}
matches_lower
}
#[cfg(target_arch = "aarch64")]
use crate::core::dictionary::Dictionary;
#[cfg(target_arch = "aarch64")]
pub fn encode_with_simd(data: &[u8], dict: &Dictionary) -> Option<String> {
let base = dict.base();
if base == 64 && variants::identify_base64_variant(dict).is_some() {
return encode_base64_simd(data, dict);
}
if base == 32 && variants::identify_base32_variant(dict).is_some() {
return encode_base32_simd(data, dict);
}
if base == 16 && is_standard_hex_aarch64(dict) {
return encode_base16_simd(data, dict);
}
if base == 256 {
return encode_base256_simd(data, dict);
}
if let Some(codec) = GenericSimdCodec::from_dictionary(dict) {
return codec.encode(data, dict);
}
if let Some(codec) = GappedSequentialCodec::from_dictionary(dict) {
return codec.encode(data, dict);
}
if base <= 16
&& base.is_power_of_two()
&& let Some(codec) = SmallLutCodec::from_dictionary(dict)
{
return codec.encode(data, dict);
}
if (base == 32 || base == 64)
&& let Some(codec) = Base64LutCodec::from_dictionary(dict)
{
return codec.encode(data, dict);
}
None
}
#[cfg(target_arch = "aarch64")]
#[allow(dead_code)]
pub fn decode_with_simd(encoded: &str, dict: &Dictionary) -> Option<Vec<u8>> {
let base = dict.base();
if base == 64 && variants::identify_base64_variant(dict).is_some() {
return decode_base64_simd(encoded, dict);
}
if base == 32 && variants::identify_base32_variant(dict).is_some() {
return decode_base32_simd(encoded, dict);
}
if base == 16 && is_standard_hex_aarch64(dict) {
return decode_base16_simd(encoded, dict);
}
if base == 256 {
return decode_base256_simd(encoded, dict);
}
if let Some(codec) = GenericSimdCodec::from_dictionary(dict) {
return codec.decode(encoded, dict);
}
if let Some(codec) = GappedSequentialCodec::from_dictionary(dict) {
return codec.decode(encoded, dict);
}
if base <= 16
&& base.is_power_of_two()
&& let Some(codec) = SmallLutCodec::from_dictionary(dict)
{
return codec.decode(encoded, dict);
}
if (base == 32 || base == 64)
&& let Some(codec) = Base64LutCodec::from_dictionary(dict)
{
return codec.decode(encoded, dict);
}
None
}
#[cfg(target_arch = "aarch64")]
fn is_standard_hex_aarch64(dict: &Dictionary) -> bool {
if dict.base() != 16 {
return false;
}
let uppercase = "0123456789ABCDEF";
for (i, expected) in uppercase.chars().enumerate() {
if dict.encode_digit(i) != Some(expected) {
let lowercase = "0123456789abcdef";
for (j, exp_lower) in lowercase.chars().enumerate() {
if dict.encode_digit(j) != Some(exp_lower) {
return false;
}
}
return true;
}
}
true
}
#[cfg(test)]
mod tests {
#[cfg(target_arch = "x86_64")]
use super::has_ssse3;
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
use super::{Dictionary, decode_with_simd, encode_with_simd};
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
use crate::core::config::EncodingMode;
#[test]
#[cfg(target_arch = "x86_64")]
fn test_custom_dictionary_auto_simd() {
if !has_ssse3() {
eprintln!("SSSE3 not available, skipping test");
return;
}
let chars: Vec<char> = (0x21..0x31).map(|cp| char::from_u32(cp).unwrap()).collect();
let dict = Dictionary::builder().chars(chars).build().unwrap();
let data = b"\x01\x23\x45\x67\x89\xAB\xCD\xEF\xFE\xDC\xBA\x98\x76\x54\x32\x10\
\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF";
let result = encode_with_simd(data, &dict);
assert!(
result.is_some(),
"Custom dictionary should get SIMD acceleration"
);
let encoded = result.unwrap();
assert_eq!(encoded.len(), 64, "32 bytes should produce 64 hex chars");
for c in encoded.chars() {
let codepoint = c as u32;
assert!(
(0x21..0x31).contains(&codepoint),
"Output char U+{:04X} '{}' should be in custom dictionary range U+0021..U+0031",
codepoint,
c
);
}
assert_eq!(encoded.chars().next().unwrap(), '\x21'); assert_eq!(encoded.chars().nth(1).unwrap(), '\x22'); }
#[test]
#[cfg(target_arch = "x86_64")]
fn test_standard_base64_uses_specialized() {
if !has_ssse3() {
eprintln!("SSSE3 not available, skipping test");
return;
}
let chars: Vec<char> = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
.chars()
.collect();
let dict = Dictionary::builder()
.chars(chars)
.mode(EncodingMode::Chunked)
.padding('=')
.build()
.unwrap();
let data = b"Hello, World!";
let result = encode_with_simd(data, &dict);
assert!(
result.is_some(),
"Standard base64 should get SIMD acceleration"
);
}
#[test]
#[cfg(target_arch = "x86_64")]
fn test_standard_hex_uses_specialized() {
if !has_ssse3() {
eprintln!("SSSE3 not available, skipping test");
return;
}
let chars: Vec<char> = "0123456789abcdef".chars().collect();
let dict = Dictionary::builder().chars(chars).build().unwrap();
let data = b"\x01\x23\x45\x67\x89\xAB\xCD\xEF\xFE\xDC\xBA\x98\x76\x54\x32\x10";
let result = encode_with_simd(data, &dict);
assert!(
result.is_some(),
"Standard hex should get SIMD acceleration"
);
}
#[test]
#[cfg(target_arch = "x86_64")]
fn test_arbitrary_dictionary_uses_largelut() {
if !has_ssse3() {
eprintln!("SSSE3 not available, skipping test");
return;
}
let chars: Vec<char> = "ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba9876543210+/"
.chars()
.collect();
let dict = Dictionary::builder()
.chars(chars)
.mode(EncodingMode::Chunked)
.build()
.unwrap();
let data = b"Hello, World!";
let result = encode_with_simd(data, &dict);
assert!(
result.is_some(),
"Arbitrary base64 dictionary should get SIMD acceleration via Base64LutCodec"
);
}
#[test]
#[cfg(target_arch = "x86_64")]
fn test_sequential_base64_uses_generic() {
if !has_ssse3() {
eprintln!("SSSE3 not available, skipping test");
return;
}
let chars: Vec<char> = (0x100..0x140)
.map(|cp| char::from_u32(cp).unwrap())
.collect();
let dict = Dictionary::builder()
.chars(chars)
.mode(EncodingMode::Chunked)
.build()
.unwrap();
let data = b"Hello, World!!!!\x00";
let result = encode_with_simd(data, &dict);
assert!(
result.is_some(),
"Sequential base64 should get SIMD acceleration via GenericSimdCodec"
);
}
#[test]
#[cfg(target_arch = "x86_64")]
fn test_decode_with_simd_base64_round_trip() {
if !has_ssse3() {
eprintln!("SSSE3 not available, skipping test");
return;
}
let chars: Vec<char> = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
.chars()
.collect();
let dict = Dictionary::builder()
.chars(chars)
.mode(EncodingMode::Chunked)
.padding('=')
.build()
.unwrap();
let data = b"The quick brown fox jumps over the lazy dog";
let encoded = encode_with_simd(data, &dict).expect("Encode failed");
let decoded = decode_with_simd(&encoded, &dict).expect("Decode failed");
assert_eq!(
&decoded[..],
&data[..],
"Round-trip decode failed for base64"
);
}
#[test]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
fn test_decode_with_simd_base16_round_trip() {
#[cfg(target_arch = "x86_64")]
if !has_ssse3() {
eprintln!("SSSE3 not available, skipping test");
return;
}
let chars: Vec<char> = "0123456789ABCDEF".chars().collect();
let dict = Dictionary::builder()
.chars(chars)
.mode(EncodingMode::Chunked)
.build()
.unwrap();
let data: Vec<u8> = (0..32).map(|i| (i * 7) as u8).collect();
let encoded = encode_with_simd(&data, &dict).expect("Encode failed");
let decoded = decode_with_simd(&encoded, &dict).expect("Decode failed");
assert_eq!(
&decoded[..],
&data[..],
"Round-trip decode failed for base16"
);
}
#[test]
#[cfg(target_arch = "x86_64")]
fn test_decode_with_simd_custom_hex_round_trip() {
if !has_ssse3() {
eprintln!("SSSE3 not available, skipping test");
return;
}
let chars: Vec<char> = (0x21..0x31).map(|cp| char::from_u32(cp).unwrap()).collect();
let dict = Dictionary::builder().chars(chars).build().unwrap();
let data = b"\x01\x23\x45\x67\x89\xAB\xCD\xEF\xFE\xDC\xBA\x98\x76\x54\x32\x10\
\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF";
let encoded = encode_with_simd(data, &dict).expect("Encode failed");
let decoded = decode_with_simd(&encoded, &dict).expect("Decode failed");
assert_eq!(
&decoded[..],
&data[..],
"Round-trip decode failed for custom hex"
);
}
}