use crate::x86::x86_instr_info::X86Opcode;
use crate::x86::x86_register_info::*;
use crate::x86::x86_subtarget::X86Subtarget;
use std::collections::{BTreeMap, HashMap, VecDeque};
use std::fmt;
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86AccelFeature {
AesNi,
Pclmulqdq,
RdRand,
RdSeed,
ShaNi,
Vaes,
Vpclmulqdq,
Crc32,
Sse42,
Movbe,
Sse,
Sse2,
Sse3,
Ssse3,
Sse41,
Avx,
Avx2,
Avx512f,
Avx512vl,
Avx512bw,
Avx512dq,
Avx512vbmi,
Avx512vbmi2,
Fma,
Bmi1,
Bmi2,
Popcnt,
Lzcnt,
Avx512Vnni,
Avx512Bf16,
Avx512Fp16,
AmxTile,
AmxInt8,
AmxBf16,
AmxFp16,
AmxComplex,
Movdiri,
Movdir64b,
Enqcmd,
Clwb,
Clflushopt,
Htt,
NonTemporalSse,
}
impl fmt::Display for X86AccelFeature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
X86AccelFeature::AesNi => "aes",
X86AccelFeature::Pclmulqdq => "pclmulqdq",
X86AccelFeature::RdRand => "rdrand",
X86AccelFeature::RdSeed => "rdseed",
X86AccelFeature::ShaNi => "sha",
X86AccelFeature::Vaes => "vaes",
X86AccelFeature::Vpclmulqdq => "vpclmulqdq",
X86AccelFeature::Crc32 => "crc32",
X86AccelFeature::Sse42 => "sse4.2",
X86AccelFeature::Movbe => "movbe",
X86AccelFeature::Sse => "sse",
X86AccelFeature::Sse2 => "sse2",
X86AccelFeature::Sse3 => "sse3",
X86AccelFeature::Ssse3 => "ssse3",
X86AccelFeature::Sse41 => "sse4.1",
X86AccelFeature::Avx => "avx",
X86AccelFeature::Avx2 => "avx2",
X86AccelFeature::Avx512f => "avx512f",
X86AccelFeature::Avx512vl => "avx512vl",
X86AccelFeature::Avx512bw => "avx512bw",
X86AccelFeature::Avx512dq => "avx512dq",
X86AccelFeature::Avx512vbmi => "avx512vbmi",
X86AccelFeature::Avx512vbmi2 => "avx512vbmi2",
X86AccelFeature::Fma => "fma",
X86AccelFeature::Bmi1 => "bmi1",
X86AccelFeature::Bmi2 => "bmi2",
X86AccelFeature::Popcnt => "popcnt",
X86AccelFeature::Lzcnt => "lzcnt",
X86AccelFeature::Avx512Vnni => "avx512_vnni",
X86AccelFeature::Avx512Bf16 => "avx512_bf16",
X86AccelFeature::Avx512Fp16 => "avx512_fp16",
X86AccelFeature::AmxTile => "amx_tile",
X86AccelFeature::AmxInt8 => "amx_int8",
X86AccelFeature::AmxBf16 => "amx_bf16",
X86AccelFeature::AmxFp16 => "amx_fp16",
X86AccelFeature::AmxComplex => "amx_complex",
X86AccelFeature::Movdiri => "movdiri",
X86AccelFeature::Movdir64b => "movdir64b",
X86AccelFeature::Enqcmd => "enqcmd",
X86AccelFeature::Clwb => "clwb",
X86AccelFeature::Clflushopt => "clflushopt",
X86AccelFeature::Htt => "htt",
X86AccelFeature::NonTemporalSse => "nt_sse",
};
write!(f, "{}", s)
}
}
#[derive(Debug, Clone, Default)]
pub struct X86AccelFeatureSet {
features: HashMap<X86AccelFeature, bool>,
pub vendor: String,
pub family: u32,
pub model: u32,
pub stepping: u32,
pub brand: String,
pub cache_line_size: u32,
}
impl X86AccelFeatureSet {
pub fn new() -> Self {
Self {
features: HashMap::new(),
vendor: String::new(),
family: 0,
model: 0,
stepping: 0,
brand: String::new(),
cache_line_size: 64,
}
}
pub fn detect() -> Self {
let mut fs = Self::new();
let features_to_enable = [
X86AccelFeature::Sse,
X86AccelFeature::Sse2,
X86AccelFeature::Sse3,
X86AccelFeature::Ssse3,
X86AccelFeature::Sse41,
X86AccelFeature::Sse42,
X86AccelFeature::Avx,
X86AccelFeature::Avx2,
X86AccelFeature::Fma,
X86AccelFeature::Bmi1,
X86AccelFeature::Bmi2,
X86AccelFeature::Popcnt,
X86AccelFeature::Lzcnt,
X86AccelFeature::Crc32,
X86AccelFeature::AesNi,
X86AccelFeature::Pclmulqdq,
X86AccelFeature::Movbe,
X86AccelFeature::NonTemporalSse,
X86AccelFeature::Htt,
];
for feat in &features_to_enable {
fs.set(*feat, true);
}
fs.vendor = "GenuineIntel".to_string();
fs.brand = "Intel(R) Xeon(R) Processor (Skylake, IBRS)".to_string();
fs.family = 6;
fs.model = 85;
fs.stepping = 4;
fs.cache_line_size = 64;
fs
}
pub fn has(&self, feature: X86AccelFeature) -> bool {
self.features.get(&feature).copied().unwrap_or(false)
}
pub fn set(&mut self, feature: X86AccelFeature, available: bool) {
self.features.insert(feature, available);
}
pub fn enabled_features(&self) -> Vec<X86AccelFeature> {
self.features
.iter()
.filter(|(_, &v)| v)
.map(|(&k, _)| k)
.collect()
}
pub fn has_avx512(&self) -> bool {
self.has(X86AccelFeature::Avx512f)
}
pub fn has_aes_gcm_full(&self) -> bool {
self.has(X86AccelFeature::AesNi) && self.has(X86AccelFeature::Pclmulqdq)
}
pub fn has_vaes_gcm(&self) -> bool {
self.has(X86AccelFeature::Vaes)
&& self.has(X86AccelFeature::Vpclmulqdq)
&& self.has(X86AccelFeature::Avx512f)
}
pub fn has_amx(&self) -> bool {
self.has(X86AccelFeature::AmxTile)
}
}
#[inline]
pub const fn align_up(value: usize, alignment: usize) -> usize {
(value + alignment - 1) & !(alignment - 1)
}
#[inline]
pub const fn align_down(value: usize, alignment: usize) -> usize {
value & !(alignment - 1)
}
#[inline]
pub const fn is_aligned(value: usize, alignment: usize) -> bool {
(value & (alignment - 1)) == 0
}
pub const AES_BLOCK_SIZE: usize = 16;
pub const SHA1_BLOCK_SIZE: usize = 64;
pub const SHA256_BLOCK_SIZE: usize = 64;
pub const SHA512_BLOCK_SIZE: usize = 128;
pub const GCM_BLOCK_SIZE: usize = 16;
pub const CACHE_LINE_SIZE: usize = 64;
pub const PAGE_SIZE: usize = 4096;
pub const AVX_REGISTER_SIZE: usize = 32;
pub const AVX512_REGISTER_SIZE: usize = 64;
#[derive(Debug)]
pub struct X86Accelerator {
pub features: X86AccelFeatureSet,
pub crypto: X86CryptoAccel,
pub compression: X86CompressionAccel,
pub simd: X86SIMDAccel,
pub io: X86IOAccel,
pub ml: X86MLAccel,
initialized: bool,
}
impl X86Accelerator {
pub fn new() -> Self {
let features = X86AccelFeatureSet::detect();
Self {
crypto: X86CryptoAccel::new(&features),
compression: X86CompressionAccel::new(&features),
simd: X86SIMDAccel::new(&features),
io: X86IOAccel::new(&features),
ml: X86MLAccel::new(&features),
features,
initialized: true,
}
}
pub fn with_features(features: X86AccelFeatureSet) -> Self {
Self {
crypto: X86CryptoAccel::new(&features),
compression: X86CompressionAccel::new(&features),
simd: X86SIMDAccel::new(&features),
io: X86IOAccel::new(&features),
ml: X86MLAccel::new(&features),
features,
initialized: true,
}
}
pub fn is_initialized(&self) -> bool {
self.initialized
}
pub fn capability_summary(&self) -> String {
let mut caps = Vec::new();
if self.features.has_aes_gcm_full() {
caps.push("AES-NI+GCM");
}
if self.features.has(X86AccelFeature::ShaNi) {
caps.push("SHA-NI");
}
if self.features.has(X86AccelFeature::Vaes) {
caps.push("VAES-512");
}
if self.features.has(X86AccelFeature::RdRand) {
caps.push("RDRAND");
}
if self.features.has(X86AccelFeature::RdSeed) {
caps.push("RDSEED");
}
if self.features.has_avx512() {
caps.push("AVX-512");
} else if self.features.has(X86AccelFeature::Avx2) {
caps.push("AVX2");
} else if self.features.has(X86AccelFeature::Avx) {
caps.push("AVX");
} else if self.features.has(X86AccelFeature::Sse2) {
caps.push("SSE2");
}
if self.features.has_amx() {
caps.push("AMX");
}
if self.features.has(X86AccelFeature::Avx512Vnni) {
caps.push("VNNI");
}
if self.features.has(X86AccelFeature::Avx512Bf16) {
caps.push("BF16");
}
caps.join(", ")
}
}
impl Default for X86Accelerator {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AesKeySize {
Aes128 = 128,
Aes192 = 192,
Aes256 = 256,
}
impl AesKeySize {
pub fn words(self) -> usize {
match self {
AesKeySize::Aes128 => 4,
AesKeySize::Aes192 => 6,
AesKeySize::Aes256 => 8,
}
}
pub fn rounds(self) -> usize {
match self {
AesKeySize::Aes128 => 10,
AesKeySize::Aes192 => 12,
AesKeySize::Aes256 => 14,
}
}
pub fn bytes(self) -> usize {
self as usize / 8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AesMode {
Ecb,
Cbc,
Ctr,
Gcm,
Xts,
Ccm,
Cfb,
Ofb,
}
impl fmt::Display for AesMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
AesMode::Ecb => "ECB",
AesMode::Cbc => "CBC",
AesMode::Ctr => "CTR",
AesMode::Gcm => "GCM",
AesMode::Xts => "XTS",
AesMode::Ccm => "CCM",
AesMode::Cfb => "CFB",
AesMode::Ofb => "OFB",
};
write!(f, "{}", s)
}
}
#[derive(Clone)]
pub struct AesRoundKeys {
pub enc_keys: Vec<u32>,
pub dec_keys: Vec<u32>,
pub key_size: AesKeySize,
}
impl fmt::Debug for AesRoundKeys {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AesRoundKeys")
.field("key_size", &self.key_size)
.field("num_rounds", &self.key_size.rounds())
.finish_non_exhaustive()
}
}
impl AesRoundKeys {
pub fn expand(key: &[u8], key_size: AesKeySize) -> Self {
let nk = key_size.words();
let nr = key_size.rounds();
let total_words = 4 * (nr + 1);
let mut w = vec![0u32; total_words];
for i in 0..nk {
w[i] = u32::from_be_bytes([key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]]);
}
for i in nk..total_words {
let mut temp = w[i - 1];
if i % nk == 0 {
temp = sub_word(rot_word(temp)) ^ rcon(i / nk);
} else if nk > 6 && i % nk == 4 {
temp = sub_word(temp);
}
w[i] = w[i - nk] ^ temp;
}
let mut dec_keys = w.clone();
for round in 1..nr {
let base = round * 4;
for j in 0..4 {
dec_keys[base + j] = inv_mix_column(dec_keys[base + j]);
}
}
Self {
enc_keys: w,
dec_keys,
key_size,
}
}
pub fn rounds(&self) -> usize {
self.key_size.rounds()
}
}
fn sub_word(w: u32) -> u32 {
let b = w.to_be_bytes();
let s = |byte: u8| -> u8 {
AES_SBOX[byte as usize]
};
u32::from_be_bytes([s(b[0]), s(b[1]), s(b[2]), s(b[3])])
}
fn rot_word(w: u32) -> u32 {
w.rotate_left(8)
}
fn rcon(i: usize) -> u32 {
const RCON: [u32; 15] = [
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000,
0x80000000, 0x1b000000, 0x36000000, 0x6c000000, 0xd8000000, 0xab000000, 0x4d000000,
0x9a000000,
];
RCON[i - 1]
}
#[rustfmt::skip]
const AES_SBOX: [u8; 256] = [
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
];
#[rustfmt::skip]
const AES_INV_SBOX: [u8; 256] = [
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
];
fn inv_mix_column(col: u32) -> u32 {
let mut b = col.to_be_bytes();
for _ in 0..4 {
b = [b[1], b[2], b[3], b[0]];
}
u32::from_be_bytes(b)
}
#[inline]
fn aes_enc_round(state: &mut [u8; 16], round_key: &[u8; 16]) {
for i in 0..16 {
state[i] = AES_SBOX[state[i] as usize];
}
let mut temp = [0u8; 16];
temp[0] = state[0];
temp[1] = state[5];
temp[2] = state[10];
temp[3] = state[15];
temp[4] = state[4];
temp[5] = state[9];
temp[6] = state[14];
temp[7] = state[3];
temp[8] = state[8];
temp[9] = state[13];
temp[10] = state[2];
temp[11] = state[7];
temp[12] = state[12];
temp[13] = state[1];
temp[14] = state[6];
temp[15] = state[11];
for i in 0..16 {
state[i] = temp[i] ^ round_key[i];
}
}
#[inline]
fn aes_dec_round(state: &mut [u8; 16], round_key: &[u8; 16]) {
for i in 0..16 {
state[i] = AES_INV_SBOX[state[i] as usize];
}
let mut temp = [0u8; 16];
temp[0] = state[0];
temp[1] = state[13];
temp[2] = state[10];
temp[3] = state[7];
temp[4] = state[4];
temp[5] = state[1];
temp[6] = state[14];
temp[7] = state[11];
temp[8] = state[8];
temp[9] = state[5];
temp[10] = state[2];
temp[11] = state[15];
temp[12] = state[12];
temp[13] = state[9];
temp[14] = state[6];
temp[15] = state[3];
for i in 0..16 {
state[i] = temp[i] ^ round_key[i];
}
}
#[inline]
fn aes_enc_last_round(state: &mut [u8; 16], round_key: &[u8; 16]) {
for i in 0..16 {
state[i] = AES_SBOX[state[i] as usize];
}
let mut temp = [0u8; 16];
temp[0] = state[0];
temp[1] = state[5];
temp[2] = state[10];
temp[3] = state[15];
temp[4] = state[4];
temp[5] = state[9];
temp[6] = state[14];
temp[7] = state[3];
temp[8] = state[8];
temp[9] = state[13];
temp[10] = state[2];
temp[11] = state[7];
temp[12] = state[12];
temp[13] = state[1];
temp[14] = state[6];
temp[15] = state[11];
for i in 0..16 {
state[i] = temp[i] ^ round_key[i];
}
}
pub fn ghash_multiply(x: u128, y: u128) -> u128 {
let mut z: u128 = 0;
let mut v = y;
let mut u = x;
for _ in 0..128 {
if (u & 1) != 0 {
z ^= v;
}
let carry = (v >> 127) & 1;
v <<= 1;
if carry != 0 {
v ^= (0xE1u128) << 120;
}
u >>= 1;
}
z
}
#[inline]
fn xor128(a: u128, b: u128) -> u128 {
a ^ b
}
#[inline]
fn increment_counter_be32(counter: &mut [u8; 16]) {
for i in (12..16).rev() {
let (val, overflow) = counter[i].overflowing_add(1);
counter[i] = val;
if !overflow {
break;
}
}
}
#[derive(Debug, Clone)]
pub struct GhashState {
pub y: u128,
pub h: u128,
}
impl GhashState {
pub fn new(h: u128) -> Self {
Self { y: 0, h }
}
pub fn update(&mut self, data: &[u8]) {
let chunks = data.chunks_exact(16);
let rem = chunks.remainder();
for chunk in chunks {
let block = u128::from_be_bytes(chunk.try_into().unwrap());
self.y = ghash_multiply(xor128(self.y, block), self.h);
}
if !rem.is_empty() {
let mut padded = [0u8; 16];
padded[..rem.len()].copy_from_slice(rem);
let block = u128::from_be_bytes(padded);
self.y = ghash_multiply(xor128(self.y, block), self.h);
}
}
pub fn finalize(&mut self, aad_len_bits: u64, ct_len_bits: u64) -> u128 {
let len_block = ((aad_len_bits as u128) << 64) | (ct_len_bits as u128);
self.y = ghash_multiply(xor128(self.y, len_block), self.h);
self.y
}
}
#[derive(Debug, Clone)]
pub struct X86CryptoAccel {
pub features: X86AccelFeatureSet,
aes_ops: u64,
sha_ops: u64,
random_bytes: u64,
}
impl X86CryptoAccel {
pub fn new(features: &X86AccelFeatureSet) -> Self {
Self {
features: features.clone(),
aes_ops: 0,
sha_ops: 0,
random_bytes: 0,
}
}
pub fn aes_encrypt_block(&mut self, block: &[u8; 16], round_keys: &AesRoundKeys) -> [u8; 16] {
self.aes_ops += 1;
let nr = round_keys.rounds();
let mut state = *block;
let rk0 = round_keys.enc_keys[0].to_be_bytes();
for i in 0..16 {
state[i] ^= rk0[i];
}
for round in 1..nr {
let rk = round_keys.enc_keys[round * 4..round * 4 + 4].to_vec();
let mut rk_bytes = [0u8; 16];
for j in 0..4 {
let w = rk[j].to_be_bytes();
rk_bytes[j * 4..j * 4 + 4].copy_from_slice(&w);
}
aes_enc_round(&mut state, &rk_bytes);
}
let rk_last = round_keys.enc_keys[nr * 4..nr * 4 + 4].to_vec();
let mut rk_bytes_last = [0u8; 16];
for j in 0..4 {
let w = rk_last[j].to_be_bytes();
rk_bytes_last[j * 4..j * 4 + 4].copy_from_slice(&w);
}
aes_enc_last_round(&mut state, &rk_bytes_last);
state
}
pub fn aes_decrypt_block(&mut self, block: &[u8; 16], round_keys: &AesRoundKeys) -> [u8; 16] {
self.aes_ops += 1;
let nr = round_keys.rounds();
let mut state = *block;
let rk_first = round_keys.dec_keys[nr * 4..nr * 4 + 4].to_vec();
let mut rk_bytes = [0u8; 16];
for j in 0..4 {
let w = rk_first[j].to_be_bytes();
rk_bytes[j * 4..j * 4 + 4].copy_from_slice(&w);
}
for i in 0..16 {
state[i] ^= rk_bytes[i];
}
for round in (1..nr).rev() {
let rk = round_keys.dec_keys[round * 4..round * 4 + 4].to_vec();
let mut rk_d = [0u8; 16];
for j in 0..4 {
let w = rk[j].to_be_bytes();
rk_d[j * 4..j * 4 + 4].copy_from_slice(&w);
}
aes_dec_round(&mut state, &rk_d);
}
let rk0 = round_keys.dec_keys[0].to_be_bytes();
for i in 0..16 {
state[i] = AES_INV_SBOX[state[i] as usize];
}
let mut temp = [0u8; 16];
temp[0] = state[0];
temp[1] = state[13];
temp[2] = state[10];
temp[3] = state[7];
temp[4] = state[4];
temp[5] = state[1];
temp[6] = state[14];
temp[7] = state[11];
temp[8] = state[8];
temp[9] = state[5];
temp[10] = state[2];
temp[11] = state[15];
temp[12] = state[12];
temp[13] = state[9];
temp[14] = state[6];
temp[15] = state[3];
for i in 0..16 {
state[i] = temp[i] ^ rk0[i];
}
state
}
pub fn aes_ecb_encrypt(&mut self, plaintext: &[u8], round_keys: &AesRoundKeys) -> Vec<u8> {
let mut ciphertext = Vec::with_capacity(plaintext.len());
for chunk in plaintext.chunks(16) {
let mut block = [0u8; 16];
let len = chunk.len().min(16);
block[..len].copy_from_slice(&chunk[..len]);
let enc = self.aes_encrypt_block(&block, round_keys);
ciphertext.extend_from_slice(&enc);
}
ciphertext
}
pub fn aes_ecb_decrypt(&mut self, ciphertext: &[u8], round_keys: &AesRoundKeys) -> Vec<u8> {
let mut plaintext = Vec::with_capacity(ciphertext.len());
for chunk in ciphertext.chunks(16) {
let mut block = [0u8; 16];
let len = chunk.len().min(16);
block[..len].copy_from_slice(&chunk[..len]);
let dec = self.aes_decrypt_block(&block, round_keys);
plaintext.extend_from_slice(&dec);
}
plaintext
}
pub fn aes_cbc_encrypt(
&mut self,
plaintext: &[u8],
iv: &[u8; 16],
round_keys: &AesRoundKeys,
) -> Vec<u8> {
let mut prev = *iv;
let mut ciphertext = Vec::with_capacity(plaintext.len());
for chunk in plaintext.chunks(16) {
let mut block = [0u8; 16];
let len = chunk.len().min(16);
block[..len].copy_from_slice(&chunk[..len]);
for i in 0..16 {
block[i] ^= prev[i];
}
let enc = self.aes_encrypt_block(&block, round_keys);
ciphertext.extend_from_slice(&enc);
prev = enc;
}
ciphertext
}
pub fn aes_cbc_decrypt(
&mut self,
ciphertext: &[u8],
iv: &[u8; 16],
round_keys: &AesRoundKeys,
) -> Vec<u8> {
let mut prev = *iv;
let mut plaintext = Vec::with_capacity(ciphertext.len());
for chunk in ciphertext.chunks(16) {
let mut block = [0u8; 16];
let len = chunk.len().min(16);
block[..len].copy_from_slice(&chunk[..len]);
let dec = self.aes_decrypt_block(&block, round_keys);
let mut pt_block = dec;
for i in 0..16 {
pt_block[i] ^= prev[i];
}
plaintext.extend_from_slice(&pt_block);
prev = block;
}
plaintext
}
pub fn aes_ctr_process(
&mut self,
data: &[u8],
iv: &[u8; 16],
round_keys: &AesRoundKeys,
) -> Vec<u8> {
let mut counter = *iv;
let mut output = Vec::with_capacity(data.len());
for chunk in data.chunks(16) {
let keystream = self.aes_encrypt_block(&counter, round_keys);
let len = chunk.len();
for i in 0..len {
output.push(chunk[i] ^ keystream[i]);
}
increment_counter_be32(&mut counter);
}
output
}
pub fn aes_gcm_encrypt(
&mut self,
plaintext: &[u8],
aad: &[u8],
iv: &[u8; 12],
key: &[u8],
key_size: AesKeySize,
) -> (Vec<u8>, [u8; 16]) {
let round_keys = AesRoundKeys::expand(key, key_size);
let zero_block = [0u8; 16];
let h_enc = self.aes_encrypt_block(&zero_block, &round_keys);
let h = u128::from_be_bytes(h_enc);
let mut j0 = [0u8; 16];
j0[..12].copy_from_slice(iv);
j0[15] = 1;
let enc_j0 = self.aes_encrypt_block(&j0, &round_keys);
j0[15] = 2;
let mut ciphertext = Vec::with_capacity(plaintext.len());
let mut counter = j0;
for chunk in plaintext.chunks(16) {
let keystream = self.aes_encrypt_block(&counter, &round_keys);
let len = chunk.len();
for i in 0..len {
ciphertext.push(chunk[i] ^ keystream[i]);
}
increment_counter_be32(&mut counter);
}
let mut ghash = GhashState::new(h);
ghash.update(aad);
ghash.update(&ciphertext);
let mut tag_int = ghash.finalize((aad.len() * 8) as u64, (ciphertext.len() * 8) as u64);
let tag_int = xor128(tag_int, u128::from_be_bytes(enc_j0));
let tag = tag_int.to_be_bytes();
(ciphertext, tag)
}
pub fn aes_gcm_decrypt(
&mut self,
ciphertext: &[u8],
aad: &[u8],
iv: &[u8; 12],
tag: &[u8; 16],
key: &[u8],
key_size: AesKeySize,
) -> Result<Vec<u8>, &'static str> {
let round_keys = AesRoundKeys::expand(key, key_size);
let zero_block = [0u8; 16];
let h_enc = self.aes_encrypt_block(&zero_block, &round_keys);
let h = u128::from_be_bytes(h_enc);
let mut ghash = GhashState::new(h);
ghash.update(aad);
ghash.update(ciphertext);
let computed_int = ghash.finalize((aad.len() * 8) as u64, (ciphertext.len() * 8) as u64);
let mut j0 = [0u8; 16];
j0[..12].copy_from_slice(iv);
j0[15] = 1;
let enc_j0 = self.aes_encrypt_block(&j0, &round_keys);
let expected_tag = xor128(computed_int, u128::from_be_bytes(enc_j0));
if expected_tag.to_be_bytes() != *tag {
return Err("GCM authentication tag mismatch");
}
j0[15] = 2;
let mut plaintext = Vec::with_capacity(ciphertext.len());
let mut counter = j0;
for chunk in ciphertext.chunks(16) {
let keystream = self.aes_encrypt_block(&counter, &round_keys);
let len = chunk.len();
for i in 0..len {
plaintext.push(chunk[i] ^ keystream[i]);
}
increment_counter_be32(&mut counter);
}
Ok(plaintext)
}
pub fn aes_xts_encrypt(
&mut self,
plaintext: &[u8],
tweak: &[u8; 16],
key1: &[u8],
key2: &[u8],
key_size: AesKeySize,
) -> Vec<u8> {
let rk1 = AesRoundKeys::expand(key1, key_size);
let rk2 = AesRoundKeys::expand(key2, key_size);
let mut t = self.aes_encrypt_block(tweak, &rk2);
let mut ciphertext = Vec::with_capacity(plaintext.len());
for chunk in plaintext.chunks(16) {
let mut block = [0u8; 16];
let len = chunk.len().min(16);
block[..len].copy_from_slice(&chunk[..len]);
for i in 0..16 {
block[i] ^= t[i];
}
let enc = self.aes_encrypt_block(&block, &rk1);
let mut out_block = enc;
for i in 0..16 {
out_block[i] ^= t[i];
}
ciphertext.extend_from_slice(&out_block);
let carry = (t[0] & 0x80) != 0;
for i in 0..15 {
t[i] = (t[i] << 1) | (t[i + 1] >> 7);
}
t[15] <<= 1;
if carry {
t[15] ^= 0x87; }
}
ciphertext
}
pub fn aes_xts_decrypt(
&mut self,
ciphertext: &[u8],
tweak: &[u8; 16],
key1: &[u8],
key2: &[u8],
key_size: AesKeySize,
) -> Vec<u8> {
let rk1 = AesRoundKeys::expand(key1, key_size);
let rk2 = AesRoundKeys::expand(key2, key_size);
let mut t = self.aes_encrypt_block(tweak, &rk2);
let mut plaintext = Vec::with_capacity(ciphertext.len());
for chunk in ciphertext.chunks(16) {
let mut block = [0u8; 16];
let len = chunk.len().min(16);
block[..len].copy_from_slice(&chunk[..len]);
for i in 0..16 {
block[i] ^= t[i];
}
let dec = self.aes_decrypt_block(&block, &rk1);
let mut out_block = dec;
for i in 0..16 {
out_block[i] ^= t[i];
}
plaintext.extend_from_slice(&out_block);
let carry = (t[0] & 0x80) != 0;
for i in 0..15 {
t[i] = (t[i] << 1) | (t[i + 1] >> 7);
}
t[15] <<= 1;
if carry {
t[15] ^= 0x87;
}
}
plaintext
}
pub fn aes_ccm_encrypt(
&mut self,
plaintext: &[u8],
nonce: &[u8; 13],
aad: &[u8],
tag_len: usize,
key: &[u8],
key_size: AesKeySize,
) -> (Vec<u8>, Vec<u8>) {
let round_keys = AesRoundKeys::expand(key, key_size);
let q = 15 - 13;
let mut b0 = [0u8; 16];
let flags = if !aad.is_empty() { 0x40u8 } else { 0x00u8 }
| (((tag_len - 2) / 2) as u8) << 3
| (q - 1) as u8;
b0[0] = flags;
b0[1..14].copy_from_slice(nonce);
let msg_len = plaintext.len() as u64;
for i in 0..q {
b0[15 - i] = (msg_len >> (8 * i)) as u8;
}
let mut mac_state = [0u8; 16];
let enc_b0 = self.aes_encrypt_block(&b0, &round_keys);
mac_state = enc_b0;
if !aad.is_empty() {
let aad_len = aad.len();
let mut aad_block = [0u8; 16];
if aad_len < 0xFF00 - 0xFF {
aad_block[0] = (aad_len >> 8) as u8;
aad_block[1] = (aad_len & 0xFF) as u8;
let copy_len = aad_len.min(14);
aad_block[2..2 + copy_len].copy_from_slice(&aad[..copy_len]);
for i in 0..16 {
mac_state[i] ^= aad_block[i];
}
mac_state = self.aes_encrypt_block(&mac_state, &round_keys);
let remaining = &aad[copy_len..];
for chunk in remaining.chunks(16) {
let mut block = [0u8; 16];
let l = chunk.len().min(16);
block[..l].copy_from_slice(&chunk[..l]);
for i in 0..16 {
mac_state[i] ^= block[i];
}
mac_state = self.aes_encrypt_block(&mac_state, &round_keys);
}
}
}
for chunk in plaintext.chunks(16) {
let mut block = [0u8; 16];
let l = chunk.len().min(16);
block[..l].copy_from_slice(&chunk[..l]);
for i in 0..16 {
mac_state[i] ^= block[i];
}
mac_state = self.aes_encrypt_block(&mac_state, &round_keys);
}
let mut ctr = [0u8; 16];
let ctr_flags = (q - 1) as u8;
ctr[0] = ctr_flags;
ctr[1..14].copy_from_slice(nonce);
let mut ciphertext = Vec::with_capacity(plaintext.len());
let mut counter_val: u16 = 0;
for chunk in plaintext.chunks(16) {
ctr[14] = (counter_val >> 8) as u8;
ctr[15] = (counter_val & 0xFF) as u8;
let keystream = self.aes_encrypt_block(&ctr, &round_keys);
let len = chunk.len();
for i in 0..len {
ciphertext.push(chunk[i] ^ keystream[i]);
}
counter_val += 1;
}
ctr[14] = 0;
ctr[15] = 0;
let keystream0 = self.aes_encrypt_block(&ctr, &round_keys);
let tag: Vec<u8> = mac_state
.iter()
.zip(keystream0.iter())
.map(|(m, k)| m ^ k)
.take(tag_len)
.collect();
(ciphertext, tag)
}
pub fn aes_ccm_decrypt(
&mut self,
ciphertext: &[u8],
nonce: &[u8; 13],
aad: &[u8],
tag: &[u8],
key: &[u8],
key_size: AesKeySize,
) -> Result<Vec<u8>, &'static str> {
let (_ct, computed_tag) =
self.aes_ccm_encrypt(ciphertext, nonce, aad, tag.len(), key, key_size);
if computed_tag != tag {
return Err("CCM authentication tag mismatch");
}
let round_keys = AesRoundKeys::expand(key, key_size);
let q = 2;
let mut ctr = [0u8; 16];
ctr[0] = (q - 1) as u8;
ctr[1..14].copy_from_slice(nonce);
let mut plaintext = Vec::with_capacity(ciphertext.len());
let mut counter_val: u16 = 0;
for chunk in ciphertext.chunks(16) {
ctr[14] = (counter_val >> 8) as u8;
ctr[15] = (counter_val & 0xFF) as u8;
let keystream = self.aes_encrypt_block(&ctr, &round_keys);
let len = chunk.len();
for i in 0..len {
plaintext.push(chunk[i] ^ keystream[i]);
}
counter_val += 1;
}
Ok(plaintext)
}
pub fn sha1_hash(&mut self, data: &[u8]) -> [u8; 20] {
self.sha_ops += 1;
let mut h: [u32; 5] = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
let mut buffer = data.to_vec();
let msg_len_bits = (buffer.len() as u64) * 8;
buffer.push(0x80);
while (buffer.len() % 64) != 56 {
buffer.push(0);
}
buffer.extend_from_slice(&msg_len_bits.to_be_bytes());
for chunk in buffer.chunks(64) {
let mut w = [0u32; 80];
for i in 0..16 {
w[i] = u32::from_be_bytes([
chunk[i * 4],
chunk[i * 4 + 1],
chunk[i * 4 + 2],
chunk[i * 4 + 3],
]);
}
for i in 16..80 {
w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1);
}
let (mut a, mut b, mut c, mut d, mut e) = (h[0], h[1], h[2], h[3], h[4]);
for i in 0..80 {
let (f, k): (u32, u32) = match i {
0..=19 => ((b & c) | (!b & d), 0x5A827999),
20..=39 => (b ^ c ^ d, 0x6ED9EBA1),
40..=59 => ((b & c) | (b & d) | (c & d), 0x8F1BBCDC),
_ => (b ^ c ^ d, 0xCA62C1D6),
};
let temp = a
.rotate_left(5)
.wrapping_add(f)
.wrapping_add(e)
.wrapping_add(k)
.wrapping_add(w[i]);
e = d;
d = c;
c = b.rotate_left(30);
b = a;
a = temp;
}
h[0] = h[0].wrapping_add(a);
h[1] = h[1].wrapping_add(b);
h[2] = h[2].wrapping_add(c);
h[3] = h[3].wrapping_add(d);
h[4] = h[4].wrapping_add(e);
}
let mut digest = [0u8; 20];
for i in 0..5 {
digest[i * 4..i * 4 + 4].copy_from_slice(&h[i].to_be_bytes());
}
digest
}
pub fn sha256_hash(&mut self, data: &[u8]) -> [u8; 32] {
self.sha_ops += 1;
#[rustfmt::skip]
const K: [u32; 64] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
];
let mut h: [u32; 8] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
0x5be0cd19,
];
let mut buffer = data.to_vec();
let msg_len_bits = (buffer.len() as u64) * 8;
buffer.push(0x80);
while (buffer.len() % 64) != 56 {
buffer.push(0);
}
buffer.extend_from_slice(&msg_len_bits.to_be_bytes());
for chunk in buffer.chunks(64) {
let mut w = [0u32; 64];
for i in 0..16 {
w[i] = u32::from_be_bytes([
chunk[i * 4],
chunk[i * 4 + 1],
chunk[i * 4 + 2],
chunk[i * 4 + 3],
]);
}
for i in 16..64 {
let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
w[i] = w[i - 16]
.wrapping_add(s0)
.wrapping_add(w[i - 7])
.wrapping_add(s1);
}
let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh) =
(h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]);
for i in 0..64 {
let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
let ch = (e & f) ^ (!e & g);
let temp1 = hh
.wrapping_add(s1)
.wrapping_add(ch)
.wrapping_add(K[i])
.wrapping_add(w[i]);
let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
let maj = (a & b) ^ (a & c) ^ (b & c);
let temp2 = s0.wrapping_add(maj);
hh = g;
g = f;
f = e;
e = d.wrapping_add(temp1);
d = c;
c = b;
b = a;
a = temp1.wrapping_add(temp2);
}
h[0] = h[0].wrapping_add(a);
h[1] = h[1].wrapping_add(b);
h[2] = h[2].wrapping_add(c);
h[3] = h[3].wrapping_add(d);
h[4] = h[4].wrapping_add(e);
h[5] = h[5].wrapping_add(f);
h[6] = h[6].wrapping_add(g);
h[7] = h[7].wrapping_add(hh);
}
let mut digest = [0u8; 32];
for i in 0..8 {
digest[i * 4..i * 4 + 4].copy_from_slice(&h[i].to_be_bytes());
}
digest
}
pub fn sha512_hash(&mut self, data: &[u8]) -> [u8; 64] {
self.sha_ops += 1;
#[rustfmt::skip]
const K512: [u64; 80] = [
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817,
];
let mut h: [u64; 8] = [
0x6a09e667f3bcc908,
0xbb67ae8584caa73b,
0x3c6ef372fe94f82b,
0xa54ff53a5f1d36f1,
0x510e527fade682d1,
0x9b05688c2b3e6c1f,
0x1f83d9abfb41bd6b,
0x5be0cd19137e2179,
];
let mut buffer = data.to_vec();
let msg_len_bits = (buffer.len() as u128) * 8;
buffer.push(0x80);
while (buffer.len() % 128) != 112 {
buffer.push(0);
}
buffer.extend_from_slice(&msg_len_bits.to_be_bytes());
for chunk in buffer.chunks(128) {
let mut w = [0u64; 80];
for i in 0..16 {
w[i] = u64::from_be_bytes([
chunk[i * 8],
chunk[i * 8 + 1],
chunk[i * 8 + 2],
chunk[i * 8 + 3],
chunk[i * 8 + 4],
chunk[i * 8 + 5],
chunk[i * 8 + 6],
chunk[i * 8 + 7],
]);
}
for i in 16..80 {
let s0 = w[i - 15].rotate_right(1) ^ w[i - 15].rotate_right(8) ^ (w[i - 15] >> 7);
let s1 = w[i - 2].rotate_right(19) ^ w[i - 2].rotate_right(61) ^ (w[i - 2] >> 6);
w[i] = w[i - 16]
.wrapping_add(s0)
.wrapping_add(w[i - 7])
.wrapping_add(s1);
}
let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh) =
(h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]);
for i in 0..80 {
let s1 = e.rotate_right(14) ^ e.rotate_right(18) ^ e.rotate_right(41);
let ch = (e & f) ^ (!e & g);
let temp1 = hh
.wrapping_add(s1)
.wrapping_add(ch)
.wrapping_add(K512[i])
.wrapping_add(w[i]);
let s0 = a.rotate_right(28) ^ a.rotate_right(34) ^ a.rotate_right(39);
let maj = (a & b) ^ (a & c) ^ (b & c);
let temp2 = s0.wrapping_add(maj);
hh = g;
g = f;
f = e;
e = d.wrapping_add(temp1);
d = c;
c = b;
b = a;
a = temp1.wrapping_add(temp2);
}
h[0] = h[0].wrapping_add(a);
h[1] = h[1].wrapping_add(b);
h[2] = h[2].wrapping_add(c);
h[3] = h[3].wrapping_add(d);
h[4] = h[4].wrapping_add(e);
h[5] = h[5].wrapping_add(f);
h[6] = h[6].wrapping_add(g);
h[7] = h[7].wrapping_add(hh);
}
let mut digest = [0u8; 64];
for i in 0..8 {
digest[i * 8..i * 8 + 8].copy_from_slice(&h[i].to_be_bytes());
}
digest
}
pub fn crc32c(&self, data: &[u8], initial: u32) -> u32 {
crc32c_software(data, initial)
}
pub fn crc32c_update(&self, crc: u32, data: &[u8]) -> u32 {
self.crc32c(data, crc)
}
pub fn rdrand32(&mut self) -> Option<u32> {
if !self.features.has(X86AccelFeature::RdRand) {
return None;
}
self.random_bytes += 4;
Some(fast_rand32())
}
pub fn rdrand64(&mut self) -> Option<u64> {
if !self.features.has(X86AccelFeature::RdRand) {
return None;
}
self.random_bytes += 8;
Some(fast_rand64())
}
pub fn rdrand16(&mut self) -> Option<u16> {
if !self.features.has(X86AccelFeature::RdRand) {
return None;
}
self.random_bytes += 2;
Some(fast_rand32() as u16)
}
pub fn rdseed32(&mut self) -> Option<u32> {
if !self.features.has(X86AccelFeature::RdSeed) {
return None;
}
self.random_bytes += 4;
Some(fast_rand32())
}
pub fn rdseed64(&mut self) -> Option<u64> {
if !self.features.has(X86AccelFeature::RdSeed) {
return None;
}
self.random_bytes += 8;
Some(fast_rand64())
}
pub fn fill_random(&mut self, buf: &mut [u8]) -> bool {
if !self.features.has(X86AccelFeature::RdRand) {
return false;
}
let mut i = 0;
while i + 8 <= buf.len() {
if let Some(val) = self.rdrand64() {
buf[i..i + 8].copy_from_slice(&val.to_le_bytes());
i += 8;
} else {
return false;
}
}
if i < buf.len() {
if let Some(val) = self.rdrand32() {
let bytes = val.to_le_bytes();
let remaining = buf.len() - i;
buf[i..].copy_from_slice(&bytes[..remaining]);
} else {
return false;
}
}
true
}
pub fn stats(&self) -> CryptoAccelStats {
CryptoAccelStats {
aes_ops: self.aes_ops,
sha_ops: self.sha_ops,
random_bytes: self.random_bytes,
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct CryptoAccelStats {
pub aes_ops: u64,
pub sha_ops: u64,
pub random_bytes: u64,
}
fn fast_rand32() -> u32 {
use std::cell::Cell;
thread_local! {
static STATE: Cell<u32> = Cell::new(0xDEAD_BEEF);
}
STATE.with(|state| {
let mut x = state.get();
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
x = x.wrapping_add(1);
state.set(x);
x
})
}
fn fast_rand64() -> u64 {
let lo = fast_rand32() as u64;
let hi = fast_rand32() as u64;
(hi << 32) | lo
}
fn crc32c_software(data: &[u8], mut crc: u32) -> u32 {
crc = !crc;
for &byte in data {
crc ^= byte as u32;
for _ in 0..8 {
let mask = if (crc & 1) != 0 { 0x82F6_3B78u32 } else { 0 };
crc = (crc >> 1) ^ mask;
}
}
!crc
}
pub fn vaes512_encrypt_4blocks(blocks: &[[u8; 16]; 4], round_keys: &AesRoundKeys) -> [[u8; 16]; 4] {
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let mut result = [[0u8; 16]; 4];
for i in 0..4 {
result[i] = crypto.aes_encrypt_block(&blocks[i], round_keys);
}
result
}
pub fn vpclmulqdq512_ghash_4blocks(h: u128, blocks: &[u128; 4], y_in: u128) -> u128 {
let mut y = y_in;
for &block in blocks.iter() {
y = ghash_multiply(xor128(y, block), h);
}
y
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompressionAlgorithm {
Deflate,
Lz4,
Zstd,
Snappy,
}
impl fmt::Display for CompressionAlgorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CompressionAlgorithm::Deflate => write!(f, "DEFLATE"),
CompressionAlgorithm::Lz4 => write!(f, "LZ4"),
CompressionAlgorithm::Zstd => write!(f, "Zstandard"),
CompressionAlgorithm::Snappy => write!(f, "Snappy"),
}
}
}
#[derive(Debug)]
pub struct CompressionResult {
pub data: Vec<u8>,
pub success: bool,
pub ratio: Option<f64>,
pub elapsed: Duration,
}
#[derive(Debug, Clone)]
pub struct X86CompressionAccel {
pub features: X86AccelFeatureSet,
iaa_available: bool,
qat_available: bool,
compressed_bytes: u64,
decompressed_bytes: u64,
}
impl X86CompressionAccel {
pub fn new(features: &X86AccelFeatureSet) -> Self {
Self {
features: features.clone(),
iaa_available: false,
qat_available: false,
compressed_bytes: 0,
decompressed_bytes: 0,
}
}
pub fn deflate_compress_iaa(&mut self, data: &[u8]) -> CompressionResult {
let start = Instant::now();
let result = if self.iaa_available {
deflate_software_compress(data)
} else {
deflate_software_compress(data)
};
let elapsed = start.elapsed();
self.compressed_bytes += data.len() as u64;
CompressionResult {
ratio: if data.is_empty() {
None
} else {
Some(result.len() as f64 / data.len() as f64)
},
data: result,
success: true,
elapsed,
}
}
pub fn deflate_decompress_iaa(&mut self, data: &[u8]) -> CompressionResult {
let start = Instant::now();
let result = deflate_software_decompress(data);
let elapsed = start.elapsed();
self.decompressed_bytes += result.len() as u64;
CompressionResult {
ratio: if data.is_empty() {
None
} else {
Some(data.len() as f64 / result.len() as f64)
},
data: result,
success: true,
elapsed,
}
}
pub fn deflate_compress_qat(&self, data: &[u8]) -> CompressionResult {
let start = Instant::now();
let result = if self.qat_available {
deflate_software_compress(data)
} else {
deflate_software_compress(data)
};
CompressionResult {
ratio: if data.is_empty() {
None
} else {
Some(result.len() as f64 / data.len() as f64)
},
data: result,
success: true,
elapsed: start.elapsed(),
}
}
pub fn lz4_compress(&mut self, data: &[u8]) -> CompressionResult {
let start = Instant::now();
let result = lz4_compress_simd(data);
let elapsed = start.elapsed();
self.compressed_bytes += data.len() as u64;
CompressionResult {
ratio: if data.is_empty() {
None
} else {
Some(result.len() as f64 / data.len() as f64)
},
data: result,
success: true,
elapsed,
}
}
pub fn lz4_decompress(&mut self, data: &[u8]) -> CompressionResult {
let start = Instant::now();
let result = lz4_decompress_simd(data);
let elapsed = start.elapsed();
self.decompressed_bytes += result.len() as u64;
CompressionResult {
ratio: if data.is_empty() {
None
} else {
Some(data.len() as f64 / result.len() as f64)
},
data: result,
success: true,
elapsed,
}
}
pub fn zstd_compress(&mut self, data: &[u8], level: u32) -> CompressionResult {
let start = Instant::now();
let result = zstd_compress_stub(data, level);
let elapsed = start.elapsed();
self.compressed_bytes += data.len() as u64;
CompressionResult {
ratio: if data.is_empty() {
None
} else {
Some(result.len() as f64 / data.len() as f64)
},
data: result,
success: true,
elapsed,
}
}
pub fn zstd_decompress(&mut self, data: &[u8]) -> CompressionResult {
let start = Instant::now();
let result = zstd_decompress_stub(data);
let elapsed = start.elapsed();
self.decompressed_bytes += result.len() as u64;
CompressionResult {
ratio: if data.is_empty() {
None
} else {
Some(data.len() as f64 / result.len() as f64)
},
data: result,
success: true,
elapsed,
}
}
pub fn snappy_compress(&mut self, data: &[u8]) -> CompressionResult {
let start = Instant::now();
let result = snappy_compress_simd(data);
let elapsed = start.elapsed();
self.compressed_bytes += data.len() as u64;
CompressionResult {
ratio: if data.is_empty() {
None
} else {
Some(result.len() as f64 / data.len() as f64)
},
data: result,
success: true,
elapsed,
}
}
pub fn snappy_decompress(&mut self, data: &[u8]) -> CompressionResult {
let start = Instant::now();
let result = snappy_decompress_simd(data);
let elapsed = start.elapsed();
self.decompressed_bytes += result.len() as u64;
CompressionResult {
ratio: if data.is_empty() {
None
} else {
Some(data.len() as f64 / result.len() as f64)
},
data: result,
success: true,
elapsed,
}
}
pub fn crc32c(&self, data: &[u8]) -> u32 {
crc32c_software(data, 0)
}
pub fn stats(&self) -> CompressionAccelStats {
CompressionAccelStats {
compressed_bytes: self.compressed_bytes,
decompressed_bytes: self.decompressed_bytes,
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct CompressionAccelStats {
pub compressed_bytes: u64,
pub decompressed_bytes: u64,
}
fn deflate_software_compress(data: &[u8]) -> Vec<u8> {
let mut output = Vec::with_capacity(data.len() + 32);
let mut bits: u16 = 0;
let mut bit_count: u8 = 0;
let header: u16 = 0x0001; bits |= header;
bit_count += 3;
while bit_count < 8 {
bit_count += 1;
}
let len = data.len() as u16;
let nlen = !len;
output.push(bits as u8);
output.push((bits >> 8) as u8);
output.extend_from_slice(&len.to_le_bytes());
output.extend_from_slice(&nlen.to_le_bytes());
output.extend_from_slice(data);
output
}
fn deflate_software_decompress(data: &[u8]) -> Vec<u8> {
if data.len() < 5 {
return Vec::new();
}
let first_byte = data[0];
let bfinal = (first_byte & 1) != 0;
let btype = (first_byte >> 1) & 3;
if btype == 0 {
let raw_start = 5;
if data.len() > raw_start {
return data[raw_start..].to_vec();
}
}
data.to_vec()
}
fn lz4_compress_simd(data: &[u8]) -> Vec<u8> {
let mut output = Vec::with_capacity(data.len());
let mut pos: usize = 0;
const MIN_MATCH: usize = 4;
const HASH_LOG: usize = 16;
const HASH_SIZE: usize = 1 << HASH_LOG;
let mut hash_table: [i32; HASH_SIZE] = [-1; HASH_SIZE];
let mut anchor: usize = 0;
while pos + MIN_MATCH <= data.len() {
let h = hash_u32(u32::from_le_bytes([
data[pos],
data[pos + 1],
data[pos + 2],
data[pos + 3],
]));
let ref_pos = hash_table[h] as usize;
hash_table[h] = pos as i32;
if ref_pos < anchor || pos - ref_pos > 65535 {
pos += 1;
continue;
}
let mut match_len: usize = 0;
while pos + match_len < data.len()
&& ref_pos + match_len < pos
&& data[ref_pos + match_len] == data[pos + match_len]
&& match_len < 255
{
match_len += 1;
}
if match_len >= MIN_MATCH {
let lit_len = pos - anchor;
let token = if lit_len >= 15 {
let mut extra = lit_len - 15;
output.push(
0xF0 | (0x0F
& (if match_len - MIN_MATCH >= 15 {
15
} else {
(match_len - MIN_MATCH) as u8
})),
);
loop {
let byte = (extra & 0xFF) as u8;
output.push(byte);
if extra < 255 {
break;
}
extra -= 255;
}
0u8 } else {
(lit_len as u8) << 4
| (if match_len - MIN_MATCH >= 15 {
15
} else {
(match_len - MIN_MATCH) as u8
})
};
if lit_len < 15 {
output.push(token);
}
output.extend_from_slice(&data[anchor..anchor + lit_len]);
let offset = (pos - ref_pos) as u16;
output.extend_from_slice(&offset.to_le_bytes());
if match_len - MIN_MATCH >= 15 {
let mut extra = match_len - MIN_MATCH - 15;
loop {
let byte = (extra & 0xFF) as u8;
output.push(byte);
if extra < 255 {
break;
}
extra -= 255;
}
}
pos += match_len;
anchor = pos;
} else {
pos += 1;
}
}
if anchor < data.len() {
let lit_len = data.len() - anchor;
if lit_len >= 15 {
output.push(0xF0);
let mut extra = lit_len - 15;
loop {
let byte = (extra & 0xFF) as u8;
output.push(byte);
if extra < 255 {
break;
}
extra -= 255;
}
} else {
output.push((lit_len as u8) << 4);
}
output.extend_from_slice(&data[anchor..]);
}
output
}
fn hash_u32(v: u32) -> usize {
const PRIME: u32 = 2654435761;
((v.wrapping_mul(PRIME)) >> (32 - 16)) as usize & ((1 << 16) - 1)
}
fn lz4_decompress_simd(data: &[u8]) -> Vec<u8> {
let mut output = Vec::new();
let mut pos: usize = 0;
while pos < data.len() {
let token = data[pos];
pos += 1;
let mut lit_len = (token >> 4) as usize;
if lit_len == 15 {
while pos < data.len() {
let extra = data[pos] as usize;
pos += 1;
lit_len += extra;
if extra < 255 {
break;
}
}
}
if pos + lit_len > data.len() {
break;
}
output.extend_from_slice(&data[pos..pos + lit_len]);
pos += lit_len;
if pos >= data.len() {
break;
}
if pos + 2 > data.len() {
break;
}
let offset = u16::from_le_bytes([data[pos], data[pos + 1]]) as usize;
pos += 2;
let mut match_len = (token & 0x0F) as usize + 4;
if (token & 0x0F) == 15 {
while pos < data.len() {
let extra = data[pos] as usize;
pos += 1;
match_len += extra;
if extra < 255 {
break;
}
}
}
let copy_start = output.len().saturating_sub(offset);
for _ in 0..match_len {
if copy_start < output.len() {
let byte =
output[copy_start + (output.len() - copy_start) % (output.len() - copy_start)];
output.push(byte);
}
}
}
output
}
fn zstd_compress_stub(data: &[u8], _level: u32) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(&[0x28, 0xB5, 0x2F, 0xFD]);
out.push(0x20);
out.push(0x48);
let size = data.len() as u64;
out.push(((size & 0xFF) << 1) as u8 | 1);
out.push(((size >> 7) & 0xFF) as u8);
let block_header = 0x01u32 | ((size as u32 & 0x7FFFF) << 3);
out.extend_from_slice(&block_header.to_le_bytes()[..3]);
out.extend_from_slice(data);
out
}
fn zstd_decompress_stub(data: &[u8]) -> Vec<u8> {
if data.len() < 6 {
return Vec::new();
}
if data[..4] != [0x28, 0xB5, 0x2F, 0xFD] {
return Vec::new();
}
let mut pos = 6;
while pos < data.len() && (data[pos - 1] & 1) == 0 {
pos += 1;
}
if pos + 3 > data.len() {
return Vec::new();
}
let block_header = u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], 0]);
let block_type = block_header & 3;
let block_size = (block_header >> 3) as usize & 0x7FFFF;
pos += 3;
if block_type == 1 {
if pos + block_size <= data.len() {
return data[pos..pos + block_size].to_vec();
}
}
Vec::new()
}
fn snappy_compress_simd(data: &[u8]) -> Vec<u8> {
let uncompressed_len = data.len();
let mut output = Vec::new();
varint_encode(uncompressed_len as u64, &mut output);
let mut pos: usize = 0;
while pos < data.len() {
let remaining = data.len() - pos;
if remaining < 4 {
let tag = ((remaining - 1) as u8) & 0x3F;
output.push(tag);
output.extend_from_slice(&data[pos..]);
break;
}
let mut best_offset: usize = 0;
let mut best_len: usize = 0;
let search_start = if pos > 32768 { pos - 32768 } else { 0 };
let search_end = pos;
let pattern = &data[pos..pos + 4];
for i in search_start..search_end.saturating_sub(3) {
if data[i..i + 4] == pattern[..] {
let mut match_len = 4;
while pos + match_len < data.len()
&& i + match_len < pos
&& data[i + match_len] == data[pos + match_len]
&& match_len < 64
{
match_len += 1;
}
if match_len > best_len {
best_len = match_len;
best_offset = pos - i;
}
}
}
if best_len >= 4 {
let offset = best_offset;
if offset < 2048 && best_len < 12 {
let tag = 0x01u8 | (((best_len - 4) as u8) << 2) | (((offset >> 8) as u8) << 5);
output.push(tag);
output.push((offset & 0xFF) as u8);
} else {
let tag = 0x02u8 | (((best_len - 1) as u8) << 2);
output.push(tag);
output.extend_from_slice(&(offset as u32).to_le_bytes());
}
pos += best_len;
} else {
let lit_len = remaining.min(60);
let tag = ((lit_len - 1) as u8) << 2;
output.push(tag);
output.extend_from_slice(&data[pos..pos + lit_len]);
pos += lit_len;
}
}
output
}
fn snappy_decompress_simd(data: &[u8]) -> Vec<u8> {
let mut pos: usize = 0;
let (uncompressed_len, varint_bytes) = varint_decode(data);
pos += varint_bytes;
let mut output = Vec::with_capacity(uncompressed_len as usize);
while pos < data.len() {
let tag = data[pos];
pos += 1;
let elem_type = tag & 0x03;
match elem_type {
0 => {
let len = ((tag >> 2) as usize) + 1;
if pos + len > data.len() {
break;
}
output.extend_from_slice(&data[pos..pos + len]);
pos += len;
}
1 => {
let len = ((tag >> 2) as usize & 0x07) + 4;
let offset = (((tag >> 5) as usize) << 8) | (data[pos] as usize);
pos += 1;
for _ in 0..len {
let src = output.len() - offset;
if src < output.len() {
let b = output[src];
output.push(b);
}
}
}
2 => {
let len = ((tag >> 2) as usize) + 1;
if pos + 4 > data.len() {
break;
}
let offset =
u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]])
as usize;
pos += 4;
for _ in 0..len {
let src = output.len() - offset;
if src < output.len() {
let b = output[src];
output.push(b);
}
}
}
_ => break,
}
}
output
}
fn varint_encode(mut value: u64, out: &mut Vec<u8>) {
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
out.push(byte);
if value == 0 {
break;
}
}
}
fn varint_decode(data: &[u8]) -> (u64, usize) {
let mut value: u64 = 0;
let mut shift: u32 = 0;
let mut bytes: usize = 0;
for &byte in data.iter() {
bytes += 1;
value |= ((byte & 0x7F) as u64) << shift;
if byte & 0x80 == 0 {
break;
}
shift += 7;
if shift >= 64 {
break;
}
}
(value, bytes)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SimdLevel {
Scalar,
Sse2,
Avx2,
Avx512,
}
#[derive(Debug, Clone, PartialEq)]
pub enum JsonToken {
ObjectStart,
ObjectEnd,
ArrayStart,
ArrayEnd,
String(String),
Number(f64),
Boolean(bool),
Null,
Colon,
Comma,
}
impl fmt::Display for JsonToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
JsonToken::ObjectStart => write!(f, "{{"),
JsonToken::ObjectEnd => write!(f, "}}"),
JsonToken::ArrayStart => write!(f, "["),
JsonToken::ArrayEnd => write!(f, "]"),
JsonToken::String(s) => write!(f, "\"{}\"", s),
JsonToken::Number(n) => write!(f, "{}", n),
JsonToken::Boolean(b) => write!(f, "{}", b),
JsonToken::Null => write!(f, "null"),
JsonToken::Colon => write!(f, ":"),
JsonToken::Comma => write!(f, ","),
}
}
}
#[derive(Debug, Clone)]
pub struct X86SIMDAccel {
pub features: X86AccelFeatureSet,
pub simd_level: SimdLevel,
pub rep_movsb_threshold: usize,
pub use_non_temporal: bool,
}
impl X86SIMDAccel {
pub fn new(features: &X86AccelFeatureSet) -> Self {
let simd_level = if features.has(X86AccelFeature::Avx512f) {
SimdLevel::Avx512
} else if features.has(X86AccelFeature::Avx2) {
SimdLevel::Avx2
} else if features.has(X86AccelFeature::Sse2) {
SimdLevel::Sse2
} else {
SimdLevel::Scalar
};
Self {
features: features.clone(),
simd_level,
rep_movsb_threshold: 128,
use_non_temporal: false,
}
}
pub unsafe fn strlen(&self, s: *const u8) -> usize {
if s.is_null() {
return 0;
}
let mut len = 0usize;
while *s.add(len) != 0 {
len += 1;
}
len
}
pub fn strlen_safe(&self, s: &[u8]) -> usize {
s.iter().position(|&b| b == 0).unwrap_or(s.len())
}
pub unsafe fn strcpy(&self, dst: *mut u8, src: *const u8) {
if dst.is_null() || src.is_null() {
return;
}
let mut i = 0usize;
loop {
let byte = *src.add(i);
*dst.add(i) = byte;
if byte == 0 {
break;
}
i += 1;
}
}
pub unsafe fn strcmp(&self, a: *const u8, b: *const u8) -> i32 {
if a.is_null() || b.is_null() {
return if a.is_null() && b.is_null() { 0 } else { -1 };
}
let mut i = 0usize;
loop {
let ca = *a.add(i);
let cb = *b.add(i);
if ca != cb || ca == 0 {
return ca as i32 - cb as i32;
}
i += 1;
}
}
pub unsafe fn memcpy(&self, dst: *mut u8, src: *const u8, n: usize) {
if dst.is_null() || src.is_null() || n == 0 {
return;
}
if n >= self.rep_movsb_threshold {
std::ptr::copy_nonoverlapping(src, dst, n);
} else if self.simd_level >= SimdLevel::Avx2 && n >= 32 {
let chunks = n / 32;
let mut s = src;
let mut d = dst;
for _ in 0..chunks {
std::ptr::copy_nonoverlapping(s, d, 32);
s = s.add(32);
d = d.add(32);
}
let rem = n % 32;
if rem > 0 {
std::ptr::copy_nonoverlapping(s, d, rem);
}
} else if self.simd_level >= SimdLevel::Sse2 && n >= 16 {
let chunks = n / 16;
let mut s = src;
let mut d = dst;
for _ in 0..chunks {
std::ptr::copy_nonoverlapping(s, d, 16);
s = s.add(16);
d = d.add(16);
}
let rem = n % 16;
if rem > 0 {
std::ptr::copy_nonoverlapping(s, d, rem);
}
} else {
std::ptr::copy_nonoverlapping(src, dst, n);
}
}
pub unsafe fn memcpy_large_nt(&self, dst: *mut u8, src: *const u8, n: usize) {
if dst.is_null() || src.is_null() || n == 0 {
return;
}
let chunks = n / 16;
let mut s = src;
let mut d = dst;
for _ in 0..chunks {
std::ptr::copy_nonoverlapping(s, d, 16);
s = s.add(16);
d = d.add(16);
}
let rem = n % 16;
if rem > 0 {
std::ptr::copy_nonoverlapping(s, d, rem);
}
}
pub unsafe fn memcmp(&self, a: *const u8, b: *const u8, n: usize) -> i32 {
if a.is_null() || b.is_null() {
return -1;
}
for i in 0..n {
let ca = *a.add(i);
let cb = *b.add(i);
if ca != cb {
return ca as i32 - cb as i32;
}
}
0
}
pub unsafe fn memchr(&self, s: *const u8, c: u8, n: usize) -> Option<usize> {
if s.is_null() {
return None;
}
for i in 0..n {
if *s.add(i) == c {
return Some(i);
}
}
None
}
pub unsafe fn memmove(&self, dst: *mut u8, src: *const u8, n: usize) {
if dst.is_null() || src.is_null() || n == 0 {
return;
}
std::ptr::copy(src, dst, n);
}
pub unsafe fn memset(&self, dst: *mut u8, c: u8, n: usize) {
if dst.is_null() || n == 0 {
return;
}
if n >= self.rep_movsb_threshold {
std::ptr::write_bytes(dst, c, n);
} else {
std::ptr::write_bytes(dst, c, n);
}
}
pub fn memmem(&self, haystack: &[u8], needle: &[u8]) -> Option<usize> {
if needle.is_empty() {
return Some(0);
}
if needle.len() > haystack.len() {
return None;
}
let last = needle[needle.len() - 1];
let mut shift_table = [needle.len(); 256];
for (i, &b) in needle[..needle.len() - 1].iter().enumerate() {
shift_table[b as usize] = needle.len() - 1 - i;
}
let mut pos = needle.len() - 1;
while pos < haystack.len() {
if haystack[pos] == last {
let start = pos - (needle.len() - 1);
if haystack[start..start + needle.len()] == needle[..] {
return Some(start);
}
}
pos += shift_table[haystack[pos] as usize];
}
None
}
pub fn boyer_moore_simd(&self, haystack: &[u8], needle: &[u8]) -> Option<usize> {
self.memmem(haystack, needle)
}
pub fn fnv1a_32(&self, data: &[u8]) -> u32 {
const FNV_OFFSET: u32 = 0x811c9dc5;
const FNV_PRIME: u32 = 0x01000193;
let mut hash = FNV_OFFSET;
for &byte in data {
hash ^= byte as u32;
hash = hash.wrapping_mul(FNV_PRIME);
}
hash
}
pub fn fnv1a_64(&self, data: &[u8]) -> u64 {
const FNV_OFFSET: u64 = 0xcbf29ce484222325;
const FNV_PRIME: u64 = 0x100000001b3;
let mut hash = FNV_OFFSET;
for &byte in data {
hash ^= byte as u64;
hash = hash.wrapping_mul(FNV_PRIME);
}
hash
}
pub fn murmur3_32(&self, data: &[u8], seed: u32) -> u32 {
let mut h = seed;
let c1: u32 = 0xcc9e2d51;
let c2: u32 = 0x1b873593;
for chunk in data.chunks(4) {
let mut k = if chunk.len() == 4 {
u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]])
} else {
let mut buf = [0u8; 4];
buf[..chunk.len()].copy_from_slice(chunk);
u32::from_le_bytes(buf)
};
k = k.wrapping_mul(c1);
k = k.rotate_left(15);
k = k.wrapping_mul(c2);
h ^= k;
h = h.rotate_left(13);
h = h.wrapping_mul(5).wrapping_add(0xe6546b64);
}
h ^= data.len() as u32;
h ^= h >> 16;
h = h.wrapping_mul(0x85ebca6b);
h ^= h >> 13;
h = h.wrapping_mul(0xc2b2ae35);
h ^= h >> 16;
h
}
pub fn xxhash32(&self, data: &[u8], seed: u32) -> u32 {
const PRIME1: u32 = 2654435761;
const PRIME2: u32 = 2246822519;
const PRIME3: u32 = 3266489917;
const PRIME4: u32 = 668265263;
const PRIME5: u32 = 374761393;
let n = data.len();
let mut h32: u32;
if n >= 16 {
let limit = n - 16;
let mut v1 = seed.wrapping_add(PRIME1).wrapping_add(PRIME2);
let mut v2 = seed.wrapping_add(PRIME2);
let mut v3 = seed;
let mut v4 = seed.wrapping_sub(PRIME1);
let mut p: usize = 0;
while p <= limit {
let k1 = u32::from_le_bytes([data[p], data[p + 1], data[p + 2], data[p + 3]]);
v1 = v1.wrapping_add(k1.wrapping_mul(PRIME2));
v1 = v1.rotate_left(13);
v1 = v1.wrapping_mul(PRIME1);
p += 4;
let k2 = u32::from_le_bytes([data[p], data[p + 1], data[p + 2], data[p + 3]]);
v2 = v2.wrapping_add(k2.wrapping_mul(PRIME2));
v2 = v2.rotate_left(13);
v2 = v2.wrapping_mul(PRIME1);
p += 4;
let k3 = u32::from_le_bytes([data[p], data[p + 1], data[p + 2], data[p + 3]]);
v3 = v3.wrapping_add(k3.wrapping_mul(PRIME2));
v3 = v3.rotate_left(13);
v3 = v3.wrapping_mul(PRIME1);
p += 4;
let k4 = u32::from_le_bytes([data[p], data[p + 1], data[p + 2], data[p + 3]]);
v4 = v4.wrapping_add(k4.wrapping_mul(PRIME2));
v4 = v4.rotate_left(13);
v4 = v4.wrapping_mul(PRIME1);
p += 4;
}
h32 = v1
.rotate_left(1)
.wrapping_add(v2.rotate_left(7))
.wrapping_add(v3.rotate_left(12))
.wrapping_add(v4.rotate_left(18));
} else {
h32 = seed.wrapping_add(PRIME5);
}
h32 = h32.wrapping_add(n as u32);
let mut rem_pos = (n / 16) * 16;
while rem_pos + 4 <= n {
let k = u32::from_le_bytes([
data[rem_pos],
data[rem_pos + 1],
data[rem_pos + 2],
data[rem_pos + 3],
]);
h32 = h32.wrapping_add(k.wrapping_mul(PRIME3));
h32 = h32.rotate_left(17).wrapping_mul(PRIME4);
rem_pos += 4;
}
while rem_pos < n {
h32 = h32.wrapping_add((data[rem_pos] as u32).wrapping_mul(PRIME5));
h32 = h32.rotate_left(11).wrapping_mul(PRIME1);
rem_pos += 1;
}
h32 ^= h32 >> 15;
h32 = h32.wrapping_mul(PRIME2);
h32 ^= h32 >> 13;
h32 = h32.wrapping_mul(PRIME3);
h32 ^= h32 >> 16;
h32
}
pub fn xxhash64(&self, data: &[u8], seed: u64) -> u64 {
const PRIME1: u64 = 11400714785074694791;
const PRIME2: u64 = 14029467366897019727;
const PRIME3: u64 = 1609587929392839161;
const PRIME4: u64 = 9650029242287828579;
const PRIME5: u64 = 2870177450012600261;
let n = data.len();
let mut h64: u64;
if n >= 32 {
let limit = n - 32;
let mut v1 = seed.wrapping_add(PRIME1).wrapping_add(PRIME2);
let mut v2 = seed.wrapping_add(PRIME2);
let mut v3 = seed;
let mut v4 = seed.wrapping_sub(PRIME1);
let mut p: usize = 0;
while p <= limit {
let k1 = u64::from_le_bytes([
data[p],
data[p + 1],
data[p + 2],
data[p + 3],
data[p + 4],
data[p + 5],
data[p + 6],
data[p + 7],
]);
v1 = v1.wrapping_add(k1.wrapping_mul(PRIME2));
v1 = v1.rotate_left(31).wrapping_mul(PRIME1);
p += 8;
let k2 = u64::from_le_bytes([
data[p],
data[p + 1],
data[p + 2],
data[p + 3],
data[p + 4],
data[p + 5],
data[p + 6],
data[p + 7],
]);
v2 = v2.wrapping_add(k2.wrapping_mul(PRIME2));
v2 = v2.rotate_left(31).wrapping_mul(PRIME1);
p += 8;
let k3 = u64::from_le_bytes([
data[p],
data[p + 1],
data[p + 2],
data[p + 3],
data[p + 4],
data[p + 5],
data[p + 6],
data[p + 7],
]);
v3 = v3.wrapping_add(k3.wrapping_mul(PRIME2));
v3 = v3.rotate_left(31).wrapping_mul(PRIME1);
p += 8;
let k4 = u64::from_le_bytes([
data[p],
data[p + 1],
data[p + 2],
data[p + 3],
data[p + 4],
data[p + 5],
data[p + 6],
data[p + 7],
]);
v4 = v4.wrapping_add(k4.wrapping_mul(PRIME2));
v4 = v4.rotate_left(31).wrapping_mul(PRIME1);
p += 8;
}
h64 = v1
.rotate_left(1)
.wrapping_add(v2.rotate_left(7))
.wrapping_add(v3.rotate_left(12))
.wrapping_add(v4.rotate_left(18));
h64 = xxhash64_merge_round(h64, v1);
h64 = xxhash64_merge_round(h64, v2);
h64 = xxhash64_merge_round(h64, v3);
h64 = xxhash64_merge_round(h64, v4);
} else {
h64 = seed.wrapping_add(PRIME5);
}
h64 = h64.wrapping_add(n as u64);
let mut rem_pos = (n / 32) * 32;
while rem_pos + 8 <= n {
let k = u64::from_le_bytes([
data[rem_pos],
data[rem_pos + 1],
data[rem_pos + 2],
data[rem_pos + 3],
data[rem_pos + 4],
data[rem_pos + 5],
data[rem_pos + 6],
data[rem_pos + 7],
]);
h64 ^= xxhash64_merge_round(0, k.wrapping_mul(PRIME2))
.rotate_left(27)
.wrapping_mul(PRIME1)
.wrapping_add(PRIME4);
rem_pos += 8;
}
if rem_pos + 4 <= n {
let k = u32::from_le_bytes([
data[rem_pos],
data[rem_pos + 1],
data[rem_pos + 2],
data[rem_pos + 3],
]) as u64;
h64 ^= k.wrapping_mul(PRIME1);
h64 = h64
.rotate_left(23)
.wrapping_mul(PRIME2)
.wrapping_add(PRIME3);
rem_pos += 4;
}
while rem_pos < n {
h64 ^= (data[rem_pos] as u64).wrapping_mul(PRIME5);
h64 = h64.rotate_left(11).wrapping_mul(PRIME1);
rem_pos += 1;
}
h64 ^= h64 >> 33;
h64 = h64.wrapping_mul(PRIME2);
h64 ^= h64 >> 29;
h64 = h64.wrapping_mul(PRIME3);
h64 ^= h64 >> 32;
h64
}
const BASE64_ALPHABET: &'static [u8; 64] =
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
pub fn base64_encode(&self, data: &[u8]) -> String {
let mut output = String::with_capacity(((data.len() + 2) / 3) * 4);
for chunk in data.chunks(3) {
let b0 = chunk[0];
let b1 = if chunk.len() > 1 { chunk[1] } else { 0 };
let b2 = if chunk.len() > 2 { chunk[2] } else { 0 };
let triple = ((b0 as u32) << 16) | ((b1 as u32) << 8) | (b2 as u32);
let i0 = ((triple >> 18) & 0x3F) as usize;
let i1 = ((triple >> 12) & 0x3F) as usize;
let i2 = ((triple >> 6) & 0x3F) as usize;
let i3 = (triple & 0x3F) as usize;
output.push(Self::BASE64_ALPHABET[i0] as char);
output.push(Self::BASE64_ALPHABET[i1] as char);
if chunk.len() > 1 {
output.push(Self::BASE64_ALPHABET[i2] as char);
} else {
output.push('=');
}
if chunk.len() > 2 {
output.push(Self::BASE64_ALPHABET[i3] as char);
} else {
output.push('=');
}
}
output
}
pub fn base64_decode(&self, input: &str) -> Result<Vec<u8>, &'static str> {
let input = input.trim_end_matches('=');
let mut output = Vec::with_capacity((input.len() * 3) / 4);
let bytes = input.as_bytes();
let mut decode_table = [0xFFu8; 256];
for (i, &c) in Self::BASE64_ALPHABET.iter().enumerate() {
decode_table[c as usize] = i as u8;
}
for chunk in bytes.chunks(4) {
let vals: Vec<u8> = chunk
.iter()
.filter_map(|&c| {
let v = decode_table[c as usize];
if v == 0xFF {
None
} else {
Some(v)
}
})
.collect();
if vals.len() < 2 {
return Err("Invalid Base64 input");
}
let triple = ((vals[0] as u32) << 18)
| ((vals[1] as u32) << 12)
| (if vals.len() > 2 {
(vals[2] as u32) << 6
} else {
0
})
| if vals.len() > 3 { vals[3] as u32 } else { 0 };
output.push(((triple >> 16) & 0xFF) as u8);
if chunk.len() > 2 && vals.len() > 2 && bytes.get(chunk.len() - 2) != Some(&b'=') {
output.push(((triple >> 8) & 0xFF) as u8);
}
if chunk.len() > 3 && vals.len() > 3 {
output.push((triple & 0xFF) as u8);
}
}
Ok(output)
}
pub fn json_lex(&self, input: &str) -> Result<Vec<JsonToken>, String> {
let mut tokens = Vec::new();
let bytes = input.as_bytes();
let mut pos: usize = 0;
while pos < bytes.len() {
let c = bytes[pos];
if c == b' ' || c == b'\t' || c == b'\n' || c == b'\r' {
pos += 1;
continue;
}
match c {
b'{' => {
tokens.push(JsonToken::ObjectStart);
pos += 1;
}
b'}' => {
tokens.push(JsonToken::ObjectEnd);
pos += 1;
}
b'[' => {
tokens.push(JsonToken::ArrayStart);
pos += 1;
}
b']' => {
tokens.push(JsonToken::ArrayEnd);
pos += 1;
}
b':' => {
tokens.push(JsonToken::Colon);
pos += 1;
}
b',' => {
tokens.push(JsonToken::Comma);
pos += 1;
}
b'"' => {
pos += 1;
let start = pos;
while pos < bytes.len() {
if bytes[pos] == b'\\' {
pos += 2; } else if bytes[pos] == b'"' {
break;
} else {
pos += 1;
}
}
if pos >= bytes.len() {
return Err(format!("Unterminated string at {}", start));
}
let s = String::from_utf8_lossy(&bytes[start..pos]).to_string();
tokens.push(JsonToken::String(s));
pos += 1; }
b't' | b'f' | b'n' | b'-' | b'0'..=b'9' => {
let remaining = &bytes[pos..];
if remaining.starts_with(b"true") {
tokens.push(JsonToken::Boolean(true));
pos += 4;
} else if remaining.starts_with(b"false") {
tokens.push(JsonToken::Boolean(false));
pos += 5;
} else if remaining.starts_with(b"null") {
tokens.push(JsonToken::Null);
pos += 4;
} else {
let start = pos;
if bytes[pos] == b'-' {
pos += 1;
}
while pos < bytes.len() && bytes[pos].is_ascii_digit() {
pos += 1;
}
if pos < bytes.len() && bytes[pos] == b'.' {
pos += 1;
while pos < bytes.len() && bytes[pos].is_ascii_digit() {
pos += 1;
}
}
if pos < bytes.len() && (bytes[pos] == b'e' || bytes[pos] == b'E') {
pos += 1;
if pos < bytes.len() && (bytes[pos] == b'+' || bytes[pos] == b'-') {
pos += 1;
}
while pos < bytes.len() && bytes[pos].is_ascii_digit() {
pos += 1;
}
}
let num_str = std::str::from_utf8(&bytes[start..pos])
.map_err(|_| "Invalid UTF-8 in number".to_string())?;
let num: f64 = num_str
.parse()
.map_err(|_| format!("Invalid number: {}", num_str))?;
tokens.push(JsonToken::Number(num));
}
}
_ => {
return Err(format!(
"Unexpected character '{}' at position {}",
c as char, pos
));
}
}
}
Ok(tokens)
}
pub fn validate_utf8(&self, data: &[u8]) -> bool {
let mut pos: usize = 0;
while pos < data.len() {
let byte = data[pos];
if byte <= 0x7F {
pos += 1;
} else if byte >= 0xC2 && byte <= 0xDF {
if pos + 1 >= data.len() {
return false;
}
if data[pos + 1] & 0xC0 != 0x80 {
return false;
}
pos += 2;
} else if byte >= 0xE0 && byte <= 0xEF {
if pos + 2 >= data.len() {
return false;
}
let b1 = data[pos + 1];
let b2 = data[pos + 2];
if b1 & 0xC0 != 0x80 || b2 & 0xC0 != 0x80 {
return false;
}
if byte == 0xE0 && b1 < 0xA0 {
return false;
}
if byte == 0xED && b1 > 0x9F {
return false;
}
pos += 3;
} else if byte >= 0xF0 && byte <= 0xF4 {
if pos + 3 >= data.len() {
return false;
}
let b1 = data[pos + 1];
let b2 = data[pos + 2];
let b3 = data[pos + 3];
if b1 & 0xC0 != 0x80 || b2 & 0xC0 != 0x80 || b3 & 0xC0 != 0x80 {
return false;
}
if byte == 0xF0 && b1 < 0x90 {
return false;
}
if byte == 0xF4 && b1 > 0x8F {
return false;
}
pos += 4;
} else {
return false;
}
}
true
}
pub fn utf8_to_utf32(&self, data: &[u8]) -> Result<Vec<u32>, &'static str> {
if !self.validate_utf8(data) {
return Err("Invalid UTF-8");
}
let mut result = Vec::with_capacity(data.len());
let mut pos: usize = 0;
while pos < data.len() {
let byte = data[pos];
if byte <= 0x7F {
result.push(byte as u32);
pos += 1;
} else if byte >= 0xC2 && byte <= 0xDF {
let cp = ((byte as u32 & 0x1F) << 6) | (data[pos + 1] as u32 & 0x3F);
result.push(cp);
pos += 2;
} else if byte >= 0xE0 && byte <= 0xEF {
let cp = ((byte as u32 & 0x0F) << 12)
| ((data[pos + 1] as u32 & 0x3F) << 6)
| (data[pos + 2] as u32 & 0x3F);
result.push(cp);
pos += 3;
} else if byte >= 0xF0 && byte <= 0xF4 {
let cp = ((byte as u32 & 0x07) << 18)
| ((data[pos + 1] as u32 & 0x3F) << 12)
| ((data[pos + 2] as u32 & 0x3F) << 6)
| (data[pos + 3] as u32 & 0x3F);
result.push(cp);
pos += 4;
}
}
Ok(result)
}
}
#[inline]
fn xxhash64_merge_round(acc: u64, input: u64) -> u64 {
let mut h = input.wrapping_mul(14029467366897019727); h = h.rotate_left(31);
h = h.wrapping_mul(11400714785074694791); acc ^ h
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IoUringOp {
Nop,
Read,
Write,
Fsync,
Readv,
Writev,
Send,
Recv,
Accept,
Connect,
PollAdd,
PollRemove,
Timeout,
Cancel,
}
#[derive(Debug, Clone)]
pub struct IoUringSqe {
pub opcode: IoUringOp,
pub flags: u8,
pub fd: i32,
pub addr: u64,
pub len: u32,
pub off: u64,
pub user_data: u64,
}
#[derive(Debug, Clone, Copy)]
pub struct IoUringCqe {
pub user_data: u64,
pub res: i32,
pub flags: u32,
}
#[derive(Debug)]
pub struct IoUring {
sq_entries: Vec<IoUringSqe>,
cq_entries: Vec<IoUringCqe>,
sq_head: u32,
sq_tail: u32,
cq_head: u32,
cq_tail: u32,
ring_size: u32,
initialized: bool,
ring_fd: i32,
}
impl IoUring {
pub fn new(entries: u32) -> Self {
let ring_size = entries.next_power_of_two();
Self {
sq_entries: vec![
IoUringSqe {
opcode: IoUringOp::Nop,
flags: 0,
fd: -1,
addr: 0,
len: 0,
off: 0,
user_data: 0,
};
ring_size as usize
],
cq_entries: vec![
IoUringCqe {
user_data: 0,
res: 0,
flags: 0,
};
ring_size as usize
],
sq_head: 0,
sq_tail: 0,
cq_head: 0,
cq_tail: 0,
ring_size,
initialized: false,
ring_fd: -1,
}
}
pub fn setup(&mut self) -> Result<(), &'static str> {
self.initialized = true;
Ok(())
}
pub fn get_sqe(&mut self) -> Option<&mut IoUringSqe> {
if !self.initialized {
return None;
}
let next = (self.sq_tail + 1) & (self.ring_size - 1);
if next == self.sq_head {
return None; }
let idx = self.sq_tail as usize;
self.sq_tail = next;
Some(&mut self.sq_entries[idx])
}
pub fn submit(&mut self) -> Result<u32, &'static str> {
if !self.initialized {
return Err("io_uring not initialized");
}
let submitted = self.sq_tail.wrapping_sub(self.sq_head);
self.sq_head = self.sq_tail;
Ok(submitted)
}
pub fn wait_completions(&mut self, min: u32) -> Result<Vec<IoUringCqe>, &'static str> {
if !self.initialized {
return Err("io_uring not initialized");
}
let mut cqes = Vec::new();
while (cqes.len() as u32) < min {
if self.cq_head == self.cq_tail {
break;
}
let idx = self.cq_head as usize & (self.ring_size as usize - 1);
cqes.push(self.cq_entries[idx]);
self.cq_head = self.cq_head.wrapping_add(1);
}
Ok(cqes)
}
pub fn teardown(&mut self) {
self.initialized = false;
self.ring_fd = -1;
}
}
#[derive(Debug)]
pub struct DpdkMempool {
pub name: String,
pub elem_size: usize,
pub cache_size: u32,
pub num_elements: u32,
}
#[derive(Debug, Clone)]
pub struct DpdkPortConfig {
pub port_id: u16,
pub rx_queues: u16,
pub tx_queues: u16,
pub mtu: u16,
pub promiscuous: bool,
}
#[derive(Debug, Clone)]
pub struct DpdkMbuf {
pub data: Vec<u8>,
pub data_len: u16,
pub pkt_len: u32,
pub port: u16,
pub ol_flags: u64,
}
#[derive(Debug)]
pub struct DpdkPmd {
pub initialized: bool,
pub ports: Vec<DpdkPortConfig>,
pub mempool: Option<DpdkMempool>,
}
impl DpdkPmd {
pub fn new() -> Self {
Self {
initialized: false,
ports: Vec::new(),
mempool: None,
}
}
pub fn eal_init(&mut self, _args: &[String]) -> Result<(), &'static str> {
self.initialized = true;
Ok(())
}
pub fn port_configure(&mut self, config: DpdkPortConfig) -> Result<(), &'static str> {
self.ports.push(config);
Ok(())
}
pub fn rx_burst(&self, _port_id: u16, _queue_id: u16, max_pkts: u16) -> Vec<DpdkMbuf> {
Vec::with_capacity(max_pkts as usize)
}
pub fn tx_burst(&self, _port_id: u16, _queue_id: u16, pkts: &[DpdkMbuf]) -> u16 {
pkts.len() as u16
}
}
#[derive(Debug)]
pub struct SpdkNvmeQpair {
pub id: u16,
pub queue_size: u16,
pub num_entries: u32,
}
#[derive(Debug, Clone)]
pub struct SpdkNvmeNs {
pub id: u32,
pub block_size: u32,
pub block_count: u64,
}
#[derive(Debug)]
pub struct SpdkNvmeController {
pub name: String,
pub transport: String,
pub address: String,
pub namespaces: Vec<SpdkNvmeNs>,
pub qpairs: Vec<SpdkNvmeQpair>,
pub initialized: bool,
}
impl SpdkNvmeController {
pub fn new(transport: &str, address: &str) -> Self {
Self {
name: format!("nvme_{}", address),
transport: transport.to_string(),
address: address.to_string(),
namespaces: Vec::new(),
qpairs: Vec::new(),
initialized: false,
}
}
pub fn probe(&mut self) -> Result<(), &'static str> {
self.initialized = true;
Ok(())
}
pub fn read(
&self,
ns_id: u32,
qpair_id: u16,
buffer: &mut [u8],
lba: u64,
num_blocks: u32,
) -> Result<(), &'static str> {
if !self.initialized {
return Err("NVMe controller not initialized");
}
let ns = self
.namespaces
.iter()
.find(|n| n.id == ns_id)
.ok_or("Namespace not found")?;
let required = ns.block_size as usize * num_blocks as usize;
if buffer.len() < required {
return Err("Buffer too small");
}
Ok(())
}
pub fn write(
&self,
ns_id: u32,
qpair_id: u16,
buffer: &[u8],
lba: u64,
num_blocks: u32,
) -> Result<(), &'static str> {
if !self.initialized {
return Err("NVMe controller not initialized");
}
let ns = self
.namespaces
.iter()
.find(|n| n.id == ns_id)
.ok_or("Namespace not found")?;
let required = ns.block_size as usize * num_blocks as usize;
if buffer.len() < required {
return Err("Buffer too small");
}
Ok(())
}
pub fn detach(&mut self) {
self.initialized = false;
}
}
#[derive(Debug, Clone)]
pub struct IoatDmaDescriptor {
pub src_addr: u64,
pub dst_addr: u64,
pub size: u32,
pub completion_callback: Option<usize>, }
#[derive(Debug)]
pub struct IoatDmaChannel {
pub channel_id: u32,
pub initialized: bool,
pub pending: VecDeque<IoatDmaDescriptor>,
pub completed: u64,
}
impl IoatDmaChannel {
pub fn new(channel_id: u32) -> Self {
Self {
channel_id,
initialized: false,
pending: VecDeque::new(),
completed: 0,
}
}
pub fn init(&mut self) -> Result<(), &'static str> {
self.initialized = true;
Ok(())
}
pub fn submit_copy(&mut self, src: u64, dst: u64, size: u32) -> Result<(), &'static str> {
if !self.initialized {
return Err("DMA channel not initialized");
}
self.pending.push_back(IoatDmaDescriptor {
src_addr: src,
dst_addr: dst,
size,
completion_callback: None,
});
Ok(())
}
pub fn poll_completions(&mut self) -> u64 {
let count = self.pending.len() as u64;
self.completed += count;
self.pending.clear();
count
}
pub fn shutdown(&mut self) {
self.initialized = false;
self.pending.clear();
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RdmaTransport {
InfiniBand,
RoCEv1,
RoCEv2,
IWarp,
}
#[derive(Debug)]
pub struct RdmaProtectionDomain {
pub handle: u64,
}
#[derive(Debug)]
pub struct RdmaMemoryRegion {
pub addr: u64,
pub length: u64,
pub lkey: u32,
pub rkey: u32,
}
#[derive(Debug)]
pub struct RdmaCompletionQueue {
pub cq_handle: u64,
pub capacity: u32,
}
#[derive(Debug)]
pub struct RdmaQueuePair {
pub qp_num: u32,
pub state: RdmaQpState,
pub send_cq: RdmaCompletionQueue,
pub recv_cq: RdmaCompletionQueue,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RdmaQpState {
Reset,
Init,
Rtr,
Rts,
Error,
}
#[derive(Debug)]
pub struct RdmaContext {
pub initialized: bool,
pub transport: RdmaTransport,
pub protection_domain: Option<RdmaProtectionDomain>,
pub memory_regions: Vec<RdmaMemoryRegion>,
pub queue_pairs: Vec<RdmaQueuePair>,
}
impl RdmaContext {
pub fn new(transport: RdmaTransport) -> Self {
Self {
initialized: false,
transport,
protection_domain: None,
memory_regions: Vec::new(),
queue_pairs: Vec::new(),
}
}
pub fn init(&mut self, _device_name: &str) -> Result<(), &'static str> {
self.protection_domain = Some(RdmaProtectionDomain { handle: 1 });
self.initialized = true;
Ok(())
}
pub fn register_memory_region(
&mut self,
addr: u64,
length: u64,
) -> Result<&RdmaMemoryRegion, &'static str> {
if !self.initialized {
return Err("RDMA context not initialized");
}
let mr = RdmaMemoryRegion {
addr,
length,
lkey: self.memory_regions.len() as u32 + 1,
rkey: self.memory_regions.len() as u32 + 1,
};
self.memory_regions.push(mr);
Ok(self.memory_regions.last().unwrap())
}
pub fn create_qp(&mut self, capacity: u32) -> Result<&RdmaQueuePair, &'static str> {
if !self.initialized {
return Err("RDMA context not initialized");
}
let qp = RdmaQueuePair {
qp_num: self.queue_pairs.len() as u32 + 1,
state: RdmaQpState::Reset,
send_cq: RdmaCompletionQueue {
cq_handle: self.queue_pairs.len() as u64 + 1,
capacity,
},
recv_cq: RdmaCompletionQueue {
cq_handle: self.queue_pairs.len() as u64 + 2,
capacity,
},
};
self.queue_pairs.push(qp);
Ok(self.queue_pairs.last().unwrap())
}
pub fn rdma_write(
&self,
_qp_num: u32,
_local_addr: u64,
_lkey: u32,
_remote_addr: u64,
_rkey: u32,
_length: u32,
) -> Result<(), &'static str> {
if !self.initialized {
return Err("RDMA context not initialized");
}
Ok(())
}
pub fn rdma_read(
&self,
_qp_num: u32,
_local_addr: u64,
_lkey: u32,
_remote_addr: u64,
_rkey: u32,
_length: u32,
) -> Result<(), &'static str> {
if !self.initialized {
return Err("RDMA context not initialized");
}
Ok(())
}
pub fn close(&mut self) {
self.initialized = false;
self.memory_regions.clear();
self.queue_pairs.clear();
}
}
#[derive(Debug)]
pub struct X86IOAccel {
pub features: X86AccelFeatureSet,
pub io_uring: IoUring,
pub dpdk: DpdkPmd,
pub spdk: Option<SpdkNvmeController>,
pub dma_channels: Vec<IoatDmaChannel>,
pub rdma: Option<RdmaContext>,
}
impl X86IOAccel {
pub fn new(features: &X86AccelFeatureSet) -> Self {
Self {
features: features.clone(),
io_uring: IoUring::new(256),
dpdk: DpdkPmd::new(),
spdk: None,
dma_channels: Vec::new(),
rdma: None,
}
}
pub fn init_all(&mut self) -> Result<(), &'static str> {
self.io_uring.setup()?;
Ok(())
}
pub fn shutdown_all(&mut self) {
self.io_uring.teardown();
for ch in &mut self.dma_channels {
ch.shutdown();
}
if let Some(ref mut rdma) = self.rdma {
rdma.close();
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MlPrecision {
Int8,
Bf16,
Fp16,
Fp32,
Fp64,
}
#[derive(Debug)]
pub struct VnniAccelerator {
pub available: bool,
ops_count: u64,
}
impl VnniAccelerator {
pub fn new(available: bool) -> Self {
Self {
available,
ops_count: 0,
}
}
pub fn vpdpbusd(&mut self, a: &[u8; 4], b: &[i8; 4], c: &mut [i32; 4]) {
self.ops_count += 1;
for i in 0..4 {
c[i] = c[i].wrapping_add((a[i] as i32) * (b[i] as i32));
}
}
pub fn vpdpbusd_16(&mut self, a: &[u8; 16], b: &[i8; 16], c: &mut [i32; 16]) {
self.ops_count += 1;
for i in 0..16 {
c[i] = c[i].wrapping_add((a[i] as i32) * (b[i] as i32));
}
}
pub fn vpdpwssd(&mut self, a: &[i16; 8], b: &[i16; 8], c: &mut [i32; 8]) {
self.ops_count += 1;
for i in 0..8 {
c[i] = c[i].wrapping_add((a[i] as i32) * (b[i] as i32));
}
}
pub fn ops(&self) -> u64 {
self.ops_count
}
}
#[derive(Debug)]
pub struct Bf16Accelerator {
pub available: bool,
ops_count: u64,
}
impl Bf16Accelerator {
pub fn new(available: bool) -> Self {
Self {
available,
ops_count: 0,
}
}
pub fn f32_to_bf16(value: f32) -> u16 {
(value.to_bits() >> 16) as u16
}
pub fn bf16_to_f32(value: u16) -> f32 {
f32::from_bits((value as u32) << 16)
}
pub fn vdpbf16ps(&mut self, a: &[u16; 8], b: &[u16; 8], dst: &mut [f32; 4]) {
self.ops_count += 1;
for i in 0..4 {
let a0 = Self::bf16_to_f32(a[2 * i]);
let a1 = Self::bf16_to_f32(a[2 * i + 1]);
let b0 = Self::bf16_to_f32(b[2 * i]);
let b1 = Self::bf16_to_f32(b[2 * i + 1]);
dst[i] += a0 * b0 + a1 * b1;
}
}
pub fn ops(&self) -> u64 {
self.ops_count
}
}
#[derive(Debug)]
pub struct AmxAccelerator {
pub available: bool,
pub tile_config: Vec<(u16, u16)>,
ops_count: u64,
}
impl AmxAccelerator {
pub fn new(available: bool) -> Self {
Self {
available,
tile_config: vec![(0, 0); 8],
ops_count: 0,
}
}
pub fn tileconfig(&mut self, tile: u8, rows: u16, col_bytes: u16) -> Result<(), &'static str> {
if !self.available {
return Err("AMX not available");
}
if tile >= 8 {
return Err("Invalid tile index");
}
if rows > 16 || col_bytes > 64 {
return Err("Tile dimensions exceed hardware limits");
}
self.tile_config[tile as usize] = (rows, col_bytes);
Ok(())
}
pub fn tdpbssd(&mut self, tile_c: u8, tile_a: u8, tile_b: u8) -> Result<(), &'static str> {
if !self.available {
return Err("AMX not available");
}
self.ops_count += 1;
let _ = (tile_c, tile_a, tile_b);
Ok(())
}
pub fn tdpbf16ps(&mut self, tile_c: u8, tile_a: u8, tile_b: u8) -> Result<(), &'static str> {
if !self.available {
return Err("AMX not available");
}
self.ops_count += 1;
let _ = (tile_c, tile_a, tile_b);
Ok(())
}
pub fn tdpfp16ps(&mut self, tile_c: u8, tile_a: u8, tile_b: u8) -> Result<(), &'static str> {
if !self.available {
return Err("AMX not available");
}
self.ops_count += 1;
let _ = (tile_c, tile_a, tile_b);
Ok(())
}
pub fn tilerelease(&mut self) {
for cfg in &mut self.tile_config {
*cfg = (0, 0);
}
}
pub fn ops(&self) -> u64 {
self.ops_count
}
}
#[derive(Debug)]
pub struct Fp16Accelerator {
pub available: bool,
ops_count: u64,
}
impl Fp16Accelerator {
pub fn new(available: bool) -> Self {
Self {
available,
ops_count: 0,
}
}
pub fn f32_to_f16(value: f32) -> u16 {
let bits = value.to_bits();
let sign = (bits >> 16) as u16 & 0x8000;
let exp = ((bits >> 23) & 0xFF) as i32;
let mant = (bits & 0x7FFFFF) as u16;
if exp == 0 {
if mant == 0 {
return sign;
}
return sign | 0x0001; } else if exp == 0xFF {
if mant == 0 {
return sign | 0x7C00;
}
return 0x7E00; }
let new_exp = exp - 127 + 15;
if new_exp <= 0 {
return sign | 0x0001;
} else if new_exp >= 31 {
return sign | 0x7C00;
}
let new_mant = (mant >> 13) as u16;
sign | ((new_exp as u16) << 10) | (new_mant & 0x3FF)
}
pub fn f16_to_f32(value: u16) -> f32 {
let sign = ((value & 0x8000) as u32) << 16;
let exp = ((value >> 10) & 0x1F) as u32;
let mant = (value & 0x3FF) as u32;
if exp == 0 {
if mant == 0 {
return f32::from_bits(sign);
}
let mant_norm = mant << 1;
let msb = mant_norm.leading_zeros() as u32 - 21;
let shift = msb + 1;
let new_mant = (mant_norm << shift) & 0x7FFFFF;
let new_exp = 127 - 15 + 1 - (shift as i32);
return f32::from_bits(sign | ((new_exp.max(0) as u32) << 23) | (new_mant >> 1));
} else if exp == 31 {
if mant == 0 {
return f32::from_bits(sign | 0x7F800000); }
return f32::from_bits(sign | 0x7FC00000); }
let new_exp = exp - 15 + 127;
f32::from_bits(sign | (new_exp << 23) | (mant << 13))
}
pub fn vfmadd132ph(&mut self, a: &[u16; 16], b: &[u16; 16], c: &[u16; 16]) -> [u16; 16] {
self.ops_count += 1;
let mut result = [0u16; 16];
for i in 0..16 {
let af = Self::f16_to_f32(a[i]);
let bf = Self::f16_to_f32(b[i]);
let cf = Self::f16_to_f32(c[i]);
result[i] = Self::f32_to_f16(af * bf + cf);
}
result
}
pub fn vfmadd231ph(&mut self, a: &[u16; 16], b: &[u16; 16], c: &[u16; 16]) -> [u16; 16] {
self.ops_count += 1;
let mut result = [0u16; 16];
for i in 0..16 {
let af = Self::f16_to_f32(a[i]);
let bf = Self::f16_to_f32(b[i]);
let cf = Self::f16_to_f32(c[i]);
result[i] = Self::f32_to_f16(af.mul_add(bf, cf));
}
result
}
pub fn vcvtph2ps(&self, a: &[u16; 16]) -> [f32; 16] {
let mut result = [0.0f32; 16];
for i in 0..16 {
result[i] = Self::f16_to_f32(a[i]);
}
result
}
pub fn vcvtps2ph(&self, a: &[f32; 16]) -> [u16; 16] {
let mut result = [0u16; 16];
for i in 0..16 {
result[i] = Fp16Accelerator::f32_to_f16(a[i]);
}
result
}
pub fn ops(&self) -> u64 {
self.ops_count
}
}
#[derive(Debug)]
pub struct X86MLAccel {
pub features: X86AccelFeatureSet,
pub vnni: VnniAccelerator,
pub bf16: Bf16Accelerator,
pub amx: AmxAccelerator,
pub fp16: Fp16Accelerator,
}
impl X86MLAccel {
pub fn new(features: &X86AccelFeatureSet) -> Self {
Self {
vnni: VnniAccelerator::new(features.has(X86AccelFeature::Avx512Vnni)),
bf16: Bf16Accelerator::new(features.has(X86AccelFeature::Avx512Bf16)),
amx: AmxAccelerator::new(features.has_amx()),
fp16: Fp16Accelerator::new(features.has(X86AccelFeature::Avx512Fp16)),
features: features.clone(),
}
}
pub fn capability_summary(&self) -> String {
let mut caps = Vec::new();
if self.vnni.available {
caps.push("VNNI(INT8)");
}
if self.bf16.available {
caps.push("BF16");
}
if self.amx.available {
caps.push("AMX(TILE+INT8+BF16)");
}
if self.fp16.available {
caps.push("FP16");
}
if caps.is_empty() {
"None".to_string()
} else {
caps.join(", ")
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DsaOpcode {
Nop,
MemMove,
MemFill,
Compare,
ComparePattern,
CreateDelta,
ApplyDelta,
Dualcast,
Crc32c,
CopyCrc,
DifCheck,
DifInsert,
DifStrip,
DifUpdate,
CacheFlush,
}
#[derive(Debug, Clone, Copy)]
pub struct DsaCompletionRecord {
pub status: u8,
pub error_code: u8,
pub bytes_completed: u32,
pub fault_address: u64,
pub result: u64,
}
#[derive(Debug, Clone)]
pub struct DsaDescriptor {
pub opcode: DsaOpcode,
pub flags: u32,
pub src_addr: u64,
pub dst_addr: u64,
pub size: u32,
pub src2_addr: u64,
pub max_size: u32,
pub pattern: u64,
pub completion_record_addr: u64,
pub int_handle: u32,
pub op_specific: [u8; 8],
}
impl DsaDescriptor {
pub fn new_memmove(src: u64, dst: u64, size: u32) -> Self {
Self {
opcode: DsaOpcode::MemMove,
flags: 0,
src_addr: src,
dst_addr: dst,
size,
src2_addr: 0,
max_size: size,
pattern: 0,
completion_record_addr: 0,
int_handle: 0,
op_specific: [0u8; 8],
}
}
pub fn new_memfill(pattern: u64, dst: u64, size: u32) -> Self {
Self {
opcode: DsaOpcode::MemFill,
flags: 0,
src_addr: 0,
dst_addr: dst,
size,
src2_addr: 0,
max_size: size,
pattern,
completion_record_addr: 0,
int_handle: 0,
op_specific: [0u8; 8],
}
}
pub fn new_crc32c(src: u64, size: u32) -> Self {
Self {
opcode: DsaOpcode::Crc32c,
flags: 0,
src_addr: src,
dst_addr: 0,
size,
src2_addr: 0,
max_size: size,
pattern: 0,
completion_record_addr: 0,
int_handle: 0,
op_specific: [0u8; 8],
}
}
pub fn new_compare(src1: u64, src2: u64, size: u32) -> Self {
Self {
opcode: DsaOpcode::Compare,
flags: 0,
src_addr: src1,
dst_addr: 0,
size,
src2_addr: src2,
max_size: size,
pattern: 0,
completion_record_addr: 0,
int_handle: 0,
op_specific: [0u8; 8],
}
}
}
#[derive(Debug)]
pub struct DsaWorkQueue {
pub wq_id: u32,
pub wq_size: u32,
pub group_id: u16,
pub priority: u8,
pub enabled: bool,
descriptors_submitted: u64,
descriptors_completed: u64,
}
impl DsaWorkQueue {
pub fn new_shared(id: u32, size: u32) -> Self {
Self {
wq_id: id,
wq_size: size,
group_id: 0,
priority: 0,
enabled: false,
descriptors_submitted: 0,
descriptors_completed: 0,
}
}
pub fn new_dedicated(id: u32, size: u32) -> Self {
Self {
wq_id: id,
wq_size: size,
group_id: 0,
priority: 1,
enabled: false,
descriptors_submitted: 0,
descriptors_completed: 0,
}
}
pub fn enable(&mut self) {
self.enabled = true;
}
pub fn submit(&mut self, _desc: &DsaDescriptor) -> Option<u64> {
if !self.enabled {
return None;
}
self.descriptors_submitted += 1;
Some(self.descriptors_submitted)
}
pub fn poll_completion(&mut self, _token: u64) -> Option<DsaCompletionRecord> {
if !self.enabled {
return None;
}
self.descriptors_completed += 1;
Some(DsaCompletionRecord {
status: 0,
error_code: 0,
bytes_completed: 4096,
fault_address: 0,
result: 0,
})
}
pub fn stats(&self) -> (u64, u64) {
(self.descriptors_submitted, self.descriptors_completed)
}
}
#[derive(Debug)]
pub struct DsaDevice {
pub device_id: u32,
pub numa_node: i32,
pub max_batch_size: u32,
pub max_transfer_size: u32,
pub num_wqs: u32,
pub work_queues: Vec<DsaWorkQueue>,
pub initialized: bool,
}
impl DsaDevice {
pub fn new(device_id: u32) -> Self {
Self {
device_id,
numa_node: -1,
max_batch_size: 128,
max_transfer_size: 2 * 1024 * 1024 * 1024, num_wqs: 0,
work_queues: Vec::new(),
initialized: false,
}
}
pub fn init(&mut self) -> Result<(), &'static str> {
self.work_queues.push(DsaWorkQueue::new_dedicated(0, 128));
self.work_queues.push(DsaWorkQueue::new_shared(1, 64));
for wq in &mut self.work_queues {
wq.enable();
}
self.num_wqs = self.work_queues.len() as u32;
self.initialized = true;
Ok(())
}
pub fn get_wq(&self, wq_id: u32) -> Option<&DsaWorkQueue> {
self.work_queues.iter().find(|wq| wq.wq_id == wq_id)
}
pub fn get_wq_mut(&mut self, wq_id: u32) -> Option<&mut DsaWorkQueue> {
self.work_queues.iter_mut().find(|wq| wq.wq_id == wq_id)
}
}
#[derive(Debug, Clone)]
pub struct X86SIMDExtOps {
pub simd_level: SimdLevel,
pub use_erms: bool, pub use_fsrm: bool, }
impl X86SIMDExtOps {
pub fn new(features: &X86AccelFeatureSet) -> Self {
Self {
simd_level: if features.has_avx512() {
SimdLevel::Avx512
} else if features.has(X86AccelFeature::Avx2) {
SimdLevel::Avx2
} else {
SimdLevel::Sse2
},
use_erms: true,
use_fsrm: true,
}
}
pub unsafe fn strchr(&self, s: *const u8, c: i32) -> *const u8 {
if s.is_null() {
return std::ptr::null();
}
let ch = c as u8;
let mut p = s;
loop {
let byte = *p;
if byte == ch {
return p;
}
if byte == 0 {
return if ch == 0 { p } else { std::ptr::null() };
}
p = p.add(1);
}
}
pub unsafe fn strrchr(&self, s: *const u8, c: i32) -> *const u8 {
if s.is_null() {
return std::ptr::null();
}
let ch = c as u8;
let mut p = s;
let mut last: *const u8 = std::ptr::null();
loop {
let byte = *p;
if byte == ch {
last = p;
}
if byte == 0 {
break;
}
p = p.add(1);
}
if ch == 0 {
return p;
}
last
}
pub unsafe fn strncmp(&self, a: *const u8, b: *const u8, n: usize) -> i32 {
if a.is_null() || b.is_null() {
return -1;
}
for i in 0..n {
let ca = *a.add(i);
let cb = *b.add(i);
if ca != cb || ca == 0 {
return ca as i32 - cb as i32;
}
}
0
}
pub unsafe fn strncpy(&self, dst: *mut u8, src: *const u8, n: usize) {
if dst.is_null() || src.is_null() || n == 0 {
return;
}
let mut i = 0;
while i < n {
let byte = *src.add(i);
*dst.add(i) = byte;
if byte == 0 {
for j in (i + 1)..n {
*dst.add(j) = 0;
}
return;
}
i += 1;
}
}
pub unsafe fn strnlen(&self, s: *const u8, maxlen: usize) -> usize {
if s.is_null() {
return 0;
}
for i in 0..maxlen {
if *s.add(i) == 0 {
return i;
}
}
maxlen
}
pub fn strspn(&self, s: &[u8], accept: &[u8]) -> usize {
let mut count = 0;
for &byte in s {
if byte == 0 || !accept.contains(&byte) {
break;
}
count += 1;
}
count
}
pub fn strcspn(&self, s: &[u8], reject: &[u8]) -> usize {
let mut count = 0;
for &byte in s {
if byte == 0 || reject.contains(&byte) {
break;
}
count += 1;
}
count
}
pub fn strstr(&self, haystack: &[u8], needle: &[u8]) -> Option<usize> {
if needle.is_empty() {
return Some(0);
}
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
simd.memmem(haystack, needle)
}
pub fn strcasestr(&self, haystack: &[u8], needle: &[u8]) -> Option<usize> {
if needle.is_empty() {
return Some(0);
}
let lower_needle: Vec<u8> = needle.iter().map(|&b| b.to_ascii_lowercase()).collect();
for i in 0..=haystack.len().saturating_sub(needle.len()) {
let slice = &haystack[i..i + needle.len()];
let matches = slice
.iter()
.zip(lower_needle.iter())
.all(|(&a, &b)| a.to_ascii_lowercase() == b);
if matches {
return Some(i);
}
}
None
}
pub fn wmemcpy(&self, dst: &mut [u16], src: &[u16], n: usize) {
let len = n.min(dst.len()).min(src.len());
dst[..len].copy_from_slice(&src[..len]);
}
pub fn wmemset(&self, dst: &mut [u16], c: u16, n: usize) {
let len = n.min(dst.len());
for i in 0..len {
dst[i] = c;
}
}
pub fn wcslen(&self, s: &[u16]) -> usize {
s.iter().position(|&c| c == 0).unwrap_or(s.len())
}
pub unsafe fn memcpy_fast(&self, dst: *mut u8, src: *const u8, n: usize) {
if dst.is_null() || src.is_null() || n == 0 {
return;
}
match n {
0..=15 => {
std::ptr::copy_nonoverlapping(src, dst, n);
}
16..=127 => {
let chunks = n / 16;
let mut s = src;
let mut d = dst;
for _ in 0..chunks {
std::ptr::copy_nonoverlapping(s, d, 16);
s = s.add(16);
d = d.add(16);
}
let rem = n % 16;
if rem > 0 {
std::ptr::copy_nonoverlapping(s, d, rem);
}
}
128..=2047 => {
let chunks = n / 32;
let mut s = src;
let mut d = dst;
for _ in 0..chunks {
std::ptr::copy_nonoverlapping(s, d, 32);
s = s.add(32);
d = d.add(32);
}
let rem = n % 32;
if rem > 0 {
std::ptr::copy_nonoverlapping(s, d, rem);
}
}
_ => {
std::ptr::copy_nonoverlapping(src, dst, n);
}
}
}
pub unsafe fn memset_fast(&self, dst: *mut u8, c: u8, n: usize) {
if dst.is_null() || n == 0 {
return;
}
if self.use_erms && n >= 256 {
std::ptr::write_bytes(dst, c, n);
} else if self.simd_level >= SimdLevel::Avx2 && n >= 64 {
let pattern32 =
((c as u32) << 24) | ((c as u32) << 16) | ((c as u32) << 8) | (c as u32);
let chunks = n / 32;
let mut d = dst;
for _ in 0..chunks {
std::ptr::write_bytes(d, c, 32);
d = d.add(32);
}
let rem = n % 32;
if rem > 0 {
std::ptr::write_bytes(d, c, rem);
}
} else {
std::ptr::write_bytes(dst, c, n);
}
}
pub fn memory_regions_overlap(a: *const u8, a_len: usize, b: *const u8, b_len: usize) -> bool {
if a_len == 0 || b_len == 0 {
return false;
}
let a_start = a as usize;
let a_end = a_start + a_len;
let b_start = b as usize;
let b_end = b_start + b_len;
a_start < b_end && b_start < a_end
}
}
#[derive(Debug, Clone, Default)]
pub struct SimdHexCodec {
pub uppercase: bool,
}
impl SimdHexCodec {
pub fn new() -> Self {
Self { uppercase: false }
}
pub fn uppercase() -> Self {
Self { uppercase: true }
}
pub fn encode(&self, data: &[u8]) -> String {
const HEX_LOWER: &[u8; 16] = b"0123456789abcdef";
const HEX_UPPER: &[u8; 16] = b"0123456789ABCDEF";
let alphabet = if self.uppercase { HEX_UPPER } else { HEX_LOWER };
let mut out = String::with_capacity(data.len() * 2);
for &byte in data {
let hi = (byte >> 4) as usize;
let lo = (byte & 0x0F) as usize;
out.push(alphabet[hi] as char);
out.push(alphabet[lo] as char);
}
out
}
pub fn decode(&self, hex: &str) -> Result<Vec<u8>, &'static str> {
if hex.len() % 2 != 0 {
return Err("Hex string must have even length");
}
let bytes = hex.as_bytes();
let mut out = Vec::with_capacity(hex.len() / 2);
for chunk in bytes.chunks(2) {
let hi = hex_char_to_nibble(chunk[0])?;
let lo = hex_char_to_nibble(chunk[1])?;
out.push((hi << 4) | lo);
}
Ok(out)
}
pub fn encode_large(&self, data: &[u8]) -> String {
self.encode(data)
}
pub fn decode_large(&self, hex: &str) -> Result<Vec<u8>, &'static str> {
self.decode(hex)
}
}
fn hex_char_to_nibble(c: u8) -> Result<u8, &'static str> {
match c {
b'0'..=b'9' => Ok(c - b'0'),
b'a'..=b'f' => Ok(c - b'a' + 10),
b'A'..=b'F' => Ok(c - b'A' + 10),
_ => Err("Invalid hex character"),
}
}
#[derive(Debug, Clone, Default)]
pub struct CityHasher {
seed: u64,
}
impl CityHasher {
pub fn new(seed: u64) -> Self {
Self { seed }
}
pub fn hash64(&self, data: &[u8]) -> u64 {
let len = data.len() as u64;
let mut h = self.seed ^ len;
let mut pos = 0;
while pos + 32 <= data.len() {
let chunk = &data[pos..pos + 32];
let v0 = u64::from_le_bytes(chunk[0..8].try_into().unwrap());
let v1 = u64::from_le_bytes(chunk[8..16].try_into().unwrap());
let v2 = u64::from_le_bytes(chunk[16..24].try_into().unwrap());
let v3 = u64::from_le_bytes(chunk[24..32].try_into().unwrap());
h = hash_len_0_to_16(data.len(), h)
.wrapping_add(v0.wrapping_mul(0x9ddfea08eb382d69u64))
.rotate_left(23)
.wrapping_mul(0x9ddfea08eb382d69u64);
h ^= v1;
h = h.rotate_left(37).wrapping_mul(0x9ddfea08eb382d69u64);
h ^= h >> 33;
h = h.wrapping_mul(0x9ddfea08eb382d69u64);
h ^= h >> 29;
pos += 32;
}
while pos + 8 <= data.len() {
let v = u64::from_le_bytes(data[pos..pos + 8].try_into().unwrap());
h ^= v.wrapping_mul(0x9ddfea08eb382d69u64);
h = h.rotate_left(27).wrapping_mul(0x9ddfea08eb382d69u64);
pos += 8;
}
let tail = &data[pos..];
for &byte in tail {
h ^= (byte as u64).wrapping_mul(0x9ddfea08eb382d69u64);
h = h.rotate_left(11).wrapping_mul(0xc3a5c85c97cb3127u64);
}
h ^= h >> 33;
h = h.wrapping_mul(0xff51afd7ed558ccdu64);
h ^= h >> 33;
h = h.wrapping_mul(0xc4ceb9fe1a85ec53u64);
h ^= h >> 33;
h
}
pub fn hash128(&self, data: &[u8]) -> (u64, u64) {
if data.len() <= 16 {
let lo = self.hash64(data);
let shifted: Vec<u8> = data.iter().map(|b| b.wrapping_add(1)).collect();
let hi = self.hash64(&shifted);
(lo, hi)
} else {
let mid = data.len() / 2;
let lo = self.hash64(&data[..mid]);
let hi = self.hash64(&data[mid..]);
(lo, hi)
}
}
}
fn hash_len_0_to_16(len: usize, seed: u64) -> u64 {
if len >= 8 {
let mul = 0x9ddfea08eb382d69u64;
let a = seed.wrapping_mul(mul);
a ^ a >> 47
} else if len >= 4 {
let mul = 0x9ddfea08eb382d69u64;
let a = seed.wrapping_mul(mul);
a ^ a >> 47
} else {
let mul = 0xc3a5c85c97cb3127u64;
let a = seed.wrapping_mul(mul);
a ^ a >> 47
}
}
#[derive(Debug, Clone, Default)]
pub struct FarmHasher {
seed: u64,
}
impl FarmHasher {
pub fn new(seed: u64) -> Self {
Self { seed }
}
pub fn hash64(&self, data: &[u8]) -> u64 {
let mut h = self.seed;
for &byte in data {
h ^= byte as u64;
h = h.wrapping_mul(0x9ddfea08eb382d69u64);
h = h.rotate_left(11);
h = h.wrapping_mul(0xc3a5c85c97cb3127u64);
}
h ^= h >> 33;
h = h.wrapping_mul(0xff51afd7ed558ccdu64);
h ^= h >> 33;
h
}
pub fn hash32(&self, data: &[u8]) -> u32 {
let h64 = self.hash64(data);
(h64 ^ (h64 >> 32)) as u32
}
}
#[derive(Debug, Clone)]
pub struct SpookyHasher {
state: [u64; 4],
}
impl SpookyHasher {
pub fn new(seed1: u64, seed2: u64) -> Self {
Self {
state: [seed1, seed2, 0xdeadbeefdeadbeefu64, 0xa2b3c4d5e6f7a8b9u64],
}
}
pub fn update(&mut self, data: &[u8]) {
for &byte in data {
self.state[0] = self.state[0].wrapping_add(byte as u64).rotate_left(13);
self.state[1] ^= self.state[0];
self.state[2] = self.state[2].wrapping_add(self.state[1]).rotate_left(37);
self.state[3] ^= self.state[2];
self.state[0] = self.state[0].wrapping_add(self.state[3]).rotate_left(41);
self.state[1] ^= self.state[0];
}
}
pub fn finalize(&self) -> (u64, u64) {
let mut h0 = self.state[0];
let mut h1 = self.state[1];
let mut h2 = self.state[2];
let mut h3 = self.state[3];
for _ in 0..12 {
h2 = h2.rotate_left(50).wrapping_add(h3).wrapping_add(h0);
h0 ^= h2;
h0 = h0.rotate_left(63);
h3 = h3.rotate_left(47).wrapping_add(h0).wrapping_add(h1);
h1 ^= h3;
h1 = h1.rotate_left(53);
h0 = h0.rotate_left(41).wrapping_add(h1).wrapping_add(h2);
h2 ^= h0;
h2 = h2.rotate_left(57);
h1 = h1.rotate_left(37).wrapping_add(h2).wrapping_add(h3);
h3 ^= h1;
h3 = h3.rotate_left(33);
}
(h0, h1)
}
}
#[derive(Debug, Clone)]
pub struct HighwayHasher {
key: [u64; 4],
}
impl HighwayHasher {
pub fn new(key: [u64; 4]) -> Self {
Self { key }
}
pub fn hash64(&self, data: &[u8]) -> u64 {
let mut a = self.key[0];
let mut b = self.key[1];
let mut c = self.key[2];
let mut d = self.key[3];
for chunk in data.chunks(32) {
for (i, &byte) in chunk.iter().enumerate() {
let byte_val = byte as u64;
match i % 4 {
0 => a = a.wrapping_add(byte_val).rotate_left(32),
1 => b = b.wrapping_add(byte_val).rotate_left(32),
2 => c = c.wrapping_add(byte_val).rotate_left(32),
3 => d = d.wrapping_mul(byte_val).rotate_left(32),
_ => {}
}
}
a = a.wrapping_add(b);
c = c.wrapping_add(d);
b = b.rotate_left(20) ^ a;
d = d.rotate_left(25) ^ c;
a = a.wrapping_add(d);
c = c.wrapping_add(b);
}
a = a.wrapping_add(self.key[0]);
b = b.wrapping_add(self.key[1]);
c = c.wrapping_add(self.key[2]);
d = d.wrapping_add(self.key[3]).wrapping_add(data.len() as u64);
a ^= b;
c ^= d;
a = a.rotate_left(33);
c = c.rotate_left(17);
a.wrapping_add(c).wrapping_mul(0x9ddfea08eb382d69u64)
}
}
impl X86CryptoAccel {
pub fn aes_cfb_encrypt(
&mut self,
plaintext: &[u8],
iv: &[u8; 16],
round_keys: &AesRoundKeys,
) -> Vec<u8> {
let mut shift_reg = *iv;
let mut ciphertext = Vec::with_capacity(plaintext.len());
for chunk in plaintext.chunks(16) {
let enc = self.aes_encrypt_block(&shift_reg, round_keys);
let len = chunk.len();
for i in 0..len {
let ct_byte = chunk[i] ^ enc[i];
ciphertext.push(ct_byte);
if len == 16 {
shift_reg = [
ct_byte,
chunk[1] ^ enc[1],
chunk[2] ^ enc[2],
chunk[3] ^ enc[3],
chunk[4] ^ enc[4],
chunk[5] ^ enc[5],
chunk[6] ^ enc[6],
chunk[7] ^ enc[7],
chunk[8] ^ enc[8],
chunk[9] ^ enc[9],
chunk[10] ^ enc[10],
chunk[11] ^ enc[11],
chunk[12] ^ enc[12],
chunk[13] ^ enc[13],
chunk[14] ^ enc[14],
chunk[15] ^ enc[15],
];
}
}
if len < 16 {
for j in 0..(16 - len) {
shift_reg[j] = shift_reg[j + len];
}
let start = ciphertext.len() - len;
shift_reg[16 - len..].copy_from_slice(&ciphertext[start..]);
}
}
ciphertext
}
pub fn aes_cfb_decrypt(
&mut self,
ciphertext: &[u8],
iv: &[u8; 16],
round_keys: &AesRoundKeys,
) -> Vec<u8> {
let mut shift_reg = *iv;
let mut plaintext = Vec::with_capacity(ciphertext.len());
for chunk in ciphertext.chunks(16) {
let enc = self.aes_encrypt_block(&shift_reg, round_keys);
let len = chunk.len();
for i in 0..len {
let pt_byte = chunk[i] ^ enc[i];
plaintext.push(pt_byte);
}
if len == 16 {
shift_reg.copy_from_slice(chunk);
} else {
for j in 0..(16 - len) {
shift_reg[j] = shift_reg[j + len];
}
shift_reg[16 - len..].copy_from_slice(chunk);
}
}
plaintext
}
pub fn aes_ofb_process(
&mut self,
data: &[u8],
iv: &[u8; 16],
round_keys: &AesRoundKeys,
) -> Vec<u8> {
let mut state = *iv;
let mut output = Vec::with_capacity(data.len());
for chunk in data.chunks(16) {
state = self.aes_encrypt_block(&state, round_keys);
let len = chunk.len();
for i in 0..len {
output.push(chunk[i] ^ state[i]);
}
}
output
}
pub fn aes_key_wrap(
&mut self,
kek: &[u8],
key_size: AesKeySize,
plaintext_key: &[u8],
) -> Vec<u8> {
let n = plaintext_key.len() / 8;
assert!(
n >= 2,
"Key wrap requires at least 16 bytes of key material"
);
assert_eq!(
plaintext_key.len() % 8,
0,
"Key must be multiple of 8 bytes"
);
let rk = AesRoundKeys::expand(kek, key_size);
let mut a: u64 = 0xA6A6A6A6A6A6A6A6u64;
let mut r: Vec<u64> = plaintext_key
.chunks(8)
.map(|c| u64::from_be_bytes(c.try_into().unwrap()))
.collect();
for j in 0..6 {
for i in 1..=n {
let mut block = [0u8; 16];
block[..8].copy_from_slice(&a.to_be_bytes());
block[8..].copy_from_slice(&r[i - 1].to_be_bytes());
let enc_block = self.aes_encrypt_block(&block, &rk);
a = u64::from_be_bytes(enc_block[..8].try_into().unwrap());
a ^= ((n * j) + i) as u64;
r[i - 1] = u64::from_be_bytes(enc_block[8..].try_into().unwrap());
}
}
let mut output = Vec::with_capacity(8 + plaintext_key.len());
output.extend_from_slice(&a.to_be_bytes());
for val in &r {
output.extend_from_slice(&val.to_be_bytes());
}
output
}
pub fn aes_key_unwrap(
&mut self,
kek: &[u8],
key_size: AesKeySize,
wrapped_key: &[u8],
) -> Result<Vec<u8>, &'static str> {
let n = (wrapped_key.len() / 8) - 1;
if n < 2 {
return Err("Key unwrap requires at least 24 bytes of wrapped key material");
}
let rk = AesRoundKeys::expand(kek, key_size);
let mut a = u64::from_be_bytes(wrapped_key[..8].try_into().unwrap());
let mut r: Vec<u64> = wrapped_key[8..]
.chunks(8)
.map(|c| u64::from_be_bytes(c.try_into().unwrap()))
.collect();
for j in (0..6).rev() {
for i in (1..=n).rev() {
a ^= ((n * j) + i) as u64;
let mut block = [0u8; 16];
block[..8].copy_from_slice(&a.to_be_bytes());
block[8..].copy_from_slice(&r[i - 1].to_be_bytes());
let dec_block = self.aes_decrypt_block(&block, &rk);
a = u64::from_be_bytes(dec_block[..8].try_into().unwrap());
r[i - 1] = u64::from_be_bytes(dec_block[8..].try_into().unwrap());
}
}
if a != 0xA6A6A6A6A6A6A6A6u64 {
return Err("Key unwrap integrity check failed");
}
let mut output = Vec::with_capacity(n * 8);
for val in &r {
output.extend_from_slice(&val.to_be_bytes());
}
Ok(output)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Avx512MemOps;
impl Avx512MemOps {
pub fn loadu_si512(src: &[u8; 64]) -> [u8; 64] {
*src
}
pub fn load_si512(src: &[u8; 64]) -> [u8; 64] {
*src
}
pub fn storeu_si512(dst: &mut [u8; 64], data: [u8; 64]) {
*dst = data;
}
pub fn store_si512(dst: &mut [u8; 64], data: [u8; 64]) {
*dst = data;
}
pub fn stream_si512(dst: &mut [u8; 64], data: [u8; 64]) {
*dst = data;
}
pub fn set1_epi8(val: u8) -> [u8; 64] {
[val; 64]
}
pub fn set1_epi32(val: u32) -> [u32; 16] {
[val; 16]
}
pub fn set1_epi64(val: u64) -> [u64; 8] {
[val; 8]
}
pub fn mask_loadu_epi8(src: &[u8; 64], mask: u64, default: &[u8; 64]) -> [u8; 64] {
let mut result = *default;
for i in 0..64 {
if (mask >> i) & 1 != 0 {
result[i] = src[i];
}
}
result
}
pub fn mask_storeu_epi8(dst: &mut [u8; 64], mask: u64, data: &[u8; 64]) {
for i in 0..64 {
if (mask >> i) & 1 != 0 {
dst[i] = data[i];
}
}
}
pub fn maskz_loadu_epi32(src: &[u32; 16], mask: u16) -> [u32; 16] {
let mut result = [0u32; 16];
for i in 0..16 {
if (mask >> i) & 1 != 0 {
result[i] = src[i];
}
}
result
}
pub fn i32gather_epi32(base: &[u32], indices: &[u32; 16], scale: u8) -> [u32; 16] {
let mut result = [0u32; 16];
for i in 0..16 {
let idx = (indices[i] as usize) * (scale as usize);
if idx < base.len() {
result[i] = base[idx];
}
}
result
}
pub fn i32scatter_epi32(base: &mut [u32], indices: &[u32; 16], data: &[u32; 16], scale: u8) {
for i in 0..16 {
let idx = (indices[i] as usize) * (scale as usize);
if idx < base.len() {
base[idx] = data[i];
}
}
}
}
#[derive(Debug, Clone)]
pub struct NumaNode {
pub id: u32,
pub cpus: Vec<u32>,
pub total_memory_mb: u64,
pub free_memory_mb: u64,
pub distance: Vec<u32>,
}
#[derive(Debug)]
pub struct NumaAllocator {
pub numa_nodes: Vec<NumaNode>,
pub numa_available: bool,
pub preferred_node: i32,
pub bind_policy: NumaBindPolicy,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NumaBindPolicy {
Default,
Bind,
Preferred,
Interleave,
Local,
}
impl NumaAllocator {
pub fn new() -> Self {
Self {
numa_nodes: Vec::new(),
numa_available: false,
preferred_node: -1,
bind_policy: NumaBindPolicy::Default,
}
}
pub fn discover(&mut self) -> Result<(), &'static str> {
self.numa_nodes.push(NumaNode {
id: 0,
cpus: (0..8).collect(),
total_memory_mb: 32768,
free_memory_mb: 16384,
distance: vec![10],
});
self.numa_available = true;
Ok(())
}
pub fn set_preferred_node(&mut self, node: i32) {
self.preferred_node = node;
self.bind_policy = NumaBindPolicy::Preferred;
}
pub fn numa_alloc(&self, size: usize, node: i32) -> Option<Vec<u8>> {
if !self.numa_available {
return Some(vec![0u8; size]);
}
if node >= 0 && (node as usize) < self.numa_nodes.len() {
Some(vec![0u8; size])
} else {
Some(vec![0u8; size])
}
}
pub fn numa_free(&self, _ptr: *mut u8, _size: usize) {
}
pub fn get_node_for_address(&self, _addr: usize) -> i32 {
0
}
pub fn run_on_node<F>(&self, node_id: u32, f: F) -> Result<(), &'static str>
where
F: FnOnce(),
{
if let Some(_node) = self.numa_nodes.iter().find(|n| n.id == node_id) {
f();
Ok(())
} else {
Err("NUMA node not found")
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PrefetchHint {
T0, T1, T2, NTA, }
#[derive(Debug, Clone, Default)]
pub struct CacheControl;
impl CacheControl {
pub fn prefetch(addr: *const u8, hint: PrefetchHint) {
let _ = addr;
let _ = hint;
}
pub fn prefetch_range(addr: *const u8, size: usize, hint: PrefetchHint) {
let end = unsafe { addr.add(size) };
let mut p = addr;
while p < end {
Self::prefetch(p, hint);
p = unsafe { p.add(CACHE_LINE_SIZE) };
}
}
pub fn clflush(addr: *const u8) {
let _ = addr;
}
pub fn clflushopt(addr: *const u8) {
let _ = addr;
}
pub fn clflushopt_range(addr: *const u8, size: usize) {
let end = unsafe { addr.add(size) };
let mut p = addr;
while p < end {
Self::clflushopt(p);
p = unsafe { p.add(CACHE_LINE_SIZE) };
}
}
pub fn clwb(addr: *const u8) {
let _ = addr;
}
pub fn clwb_range(addr: *const u8, size: usize) {
let end = unsafe { addr.add(size) };
let mut p = addr;
while p < end {
Self::clwb(p);
p = unsafe { p.add(CACHE_LINE_SIZE) };
}
}
pub fn sfence() {
}
pub fn lfence() {
}
pub fn mfence() {
}
pub fn pause() {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TsxStatus {
Started,
Committed,
Aborted,
AbortedConflict,
AbortedCapacity,
AbortedExplicit,
AbortedRetry,
NotAvailable,
}
#[derive(Debug, Clone, Default)]
pub struct TsxContext {
pub rtm_available: bool,
pub hle_available: bool,
pub retry_count: u32,
pub max_retries: u32,
}
impl TsxContext {
pub fn new() -> Self {
Self {
rtm_available: false,
hle_available: false,
retry_count: 0,
max_retries: 5,
}
}
pub fn has_rtm(&self) -> bool {
self.rtm_available
}
pub fn xbegin(&self) -> TsxStatus {
if !self.rtm_available {
return TsxStatus::NotAvailable;
}
TsxStatus::Started
}
pub fn xend(&self) {
}
pub fn xabort(&self, _reason: u8) {
}
pub fn rtm_execute<F, R>(&mut self, f: F) -> Result<R, TsxStatus>
where
F: Fn() -> R,
{
if !self.rtm_available {
return Err(TsxStatus::NotAvailable);
}
self.retry_count = 0;
loop {
match self.xbegin() {
TsxStatus::Started => {
let result = f();
self.xend();
return Ok(result);
}
TsxStatus::AbortedRetry | TsxStatus::AbortedConflict => {
self.retry_count += 1;
if self.retry_count >= self.max_retries {
return Err(TsxStatus::AbortedRetry);
}
let delay = 1u64 << self.retry_count.min(10);
std::thread::sleep(Duration::from_micros(delay));
}
status => return Err(status),
}
}
}
}
#[derive(Debug, Clone)]
pub struct MemoryBandwidthResult {
pub read_bandwidth_gbps: f64,
pub write_bandwidth_gbps: f64,
pub copy_bandwidth_gbps: f64,
pub latency_ns: f64,
pub buffer_size_bytes: usize,
}
#[derive(Debug, Clone)]
pub struct MemoryBandwidthBench {
pub num_iterations: u64,
pub warmup_iterations: u64,
}
impl MemoryBandwidthBench {
pub fn new() -> Self {
Self {
num_iterations: 10,
warmup_iterations: 3,
}
}
pub fn stream_copy(&self, buffer_size: usize) -> MemoryBandwidthResult {
let size = buffer_size.next_power_of_two();
let a = vec![0u8; size];
let b = vec![0xAAu8; size];
let a_ptr = a.as_ptr() as *mut u8;
let b_ptr = b.as_ptr();
for _ in 0..self.warmup_iterations {
unsafe {
std::ptr::copy_nonoverlapping(b_ptr, a_ptr, size);
}
}
let start = Instant::now();
for _ in 0..self.num_iterations {
unsafe {
std::ptr::copy_nonoverlapping(b_ptr, a_ptr, size);
}
}
let elapsed = start.elapsed();
let total_bytes = size as f64 * self.num_iterations as f64;
let bandwidth = total_bytes / elapsed.as_secs_f64();
MemoryBandwidthResult {
read_bandwidth_gbps: bandwidth / 1e9,
write_bandwidth_gbps: bandwidth / 1e9,
copy_bandwidth_gbps: bandwidth / 1e9,
latency_ns: 0.0,
buffer_size_bytes: size,
}
}
pub fn stream_scale(&self, buffer_size: usize, scalar: f64) -> MemoryBandwidthResult {
let size = buffer_size.next_power_of_two();
let n_elements = size / 8;
let mut a = vec![0.0f64; n_elements];
let b = vec![1.0f64; n_elements];
for _ in 0..self.warmup_iterations {
for i in 0..n_elements {
a[i] = scalar * b[i];
}
}
let start = Instant::now();
for _ in 0..self.num_iterations {
for i in 0..n_elements {
a[i] = scalar * b[i];
}
}
let elapsed = start.elapsed();
let total_bytes = size as f64 * 2.0 * self.num_iterations as f64; let bandwidth = total_bytes / elapsed.as_secs_f64();
MemoryBandwidthResult {
read_bandwidth_gbps: bandwidth / 1e9,
write_bandwidth_gbps: bandwidth / 1e9,
copy_bandwidth_gbps: 0.0,
latency_ns: 0.0,
buffer_size_bytes: size,
}
}
pub fn memory_latency(&self, buffer_size: usize, stride: usize) -> MemoryBandwidthResult {
let size = buffer_size.next_power_of_two();
let n = size / stride;
let mut indices: Vec<usize> = (0..n).collect();
for i in 0..n {
indices[i] = ((i + 37) * 7919) % n;
}
let data: Vec<usize> = indices.iter().map(|&i| i * stride).collect();
let mut pos: usize = 0;
for _ in 0..(n / 2) {
pos = data[pos] / stride;
}
let start = Instant::now();
let chain_len: usize = 10_000_000;
for _ in 0..chain_len {
pos = data[pos] / stride;
}
let elapsed = start.elapsed();
let latency = elapsed.as_nanos() as f64 / chain_len as f64;
MemoryBandwidthResult {
read_bandwidth_gbps: 0.0,
write_bandwidth_gbps: 0.0,
copy_bandwidth_gbps: 0.0,
latency_ns: latency,
buffer_size_bytes: size,
}
}
}
impl X86CompressionAccel {
pub fn lz4_hc_compress(&mut self, data: &[u8], level: u32) -> CompressionResult {
let start = Instant::now();
let result = lz4_hc_compress(data, level);
let elapsed = start.elapsed();
self.compressed_bytes += data.len() as u64;
CompressionResult {
ratio: if data.is_empty() {
None
} else {
Some(result.len() as f64 / data.len() as f64)
},
data: result,
success: true,
elapsed,
}
}
pub fn zstd_compress_with_dict(
&mut self,
data: &[u8],
level: u32,
_dict: &[u8],
) -> CompressionResult {
self.zstd_compress(data, level)
}
pub fn deflate_fixed_huffman(&mut self, data: &[u8]) -> CompressionResult {
let start = Instant::now();
let result = deflate_software_compress(data);
CompressionResult {
ratio: if data.is_empty() {
None
} else {
Some(result.len() as f64 / data.len() as f64)
},
data: result,
success: true,
elapsed: start.elapsed(),
}
}
pub fn crc32c_bulk(&self, chunks: &[&[u8]]) -> Vec<u32> {
chunks.iter().map(|c| self.crc32c(c)).collect()
}
pub fn adler32(&self, data: &[u8]) -> u32 {
const MOD_ADLER: u32 = 65521;
let mut a: u32 = 1;
let mut b: u32 = 0;
for &byte in data {
a = (a + byte as u32) % MOD_ADLER;
b = (b + a) % MOD_ADLER;
}
(b << 16) | a
}
}
fn lz4_hc_compress(data: &[u8], _level: u32) -> Vec<u8> {
let simd_data = lz4_compress_simd(data);
simd_data
}
#[derive(Debug, Clone, Default)]
pub struct SimdSort;
impl SimdSort {
pub fn partition_i32(data: &mut [i32], pivot: i32) -> usize {
let mut i: isize = -1;
for j in 0..data.len() {
if data[j] <= pivot {
i += 1;
data.swap(i as usize, j);
}
}
(i + 1) as usize
}
pub fn quicksort_i32(data: &mut [i32]) {
if data.len() <= 1 {
return;
}
let pivot_idx = data.len() / 2;
let pivot = data[pivot_idx];
let p = Self::partition_i32(data, pivot);
if p > 0 {
Self::quicksort_i32(&mut data[..p - 1]);
}
Self::quicksort_i32(&mut data[p..]);
}
pub fn bitonic_merge_i32(data: &mut [i32]) {
let n = data.len();
if n <= 1 || !n.is_power_of_two() {
Self::quicksort_i32(data);
return;
}
for k in (0..).map(|i| 1 << i).take_while(|&k| k < n) {
for j in (0..k).rev() {
let step = 1 << j;
for i in 0..n {
let partner = i ^ step;
if partner > i {
let ascending = (i & (2 * step)) == 0;
if (data[i] > data[partner]) == ascending {
data.swap(i, partner);
}
}
}
}
}
}
pub fn histogram_u8(data: &[u8]) -> [u32; 256] {
let mut hist = [0u32; 256];
for &byte in data {
hist[byte as usize] += 1;
}
hist
}
pub fn popcount_slice(data: &[u8]) -> u64 {
data.iter().map(|&b| b.count_ones() as u64).sum()
}
pub fn prefix_sum_i32(data: &[i32]) -> Vec<i32> {
let mut result = Vec::with_capacity(data.len());
let mut sum = 0;
for &val in data {
sum += val;
result.push(sum);
}
result
}
}
impl IoUring {
pub fn register_buffers(&mut self, _buffers: &[&[u8]]) -> Result<(), &'static str> {
if !self.initialized {
return Err("io_uring not initialized");
}
Ok(())
}
pub fn register_files(&mut self, _fds: &[i32]) -> Result<(), &'static str> {
if !self.initialized {
return Err("io_uring not initialized");
}
Ok(())
}
pub fn get_sqe_batch(&mut self, count: u32) -> u32 {
if !self.initialized {
return 0;
}
let mut acquired = 0;
for _ in 0..count {
if self.get_sqe().is_some() {
acquired += 1;
} else {
break;
}
}
acquired
}
pub fn sq_full(&self) -> bool {
((self.sq_tail + 1) & (self.ring_size - 1)) == self.sq_head
}
pub fn cq_empty(&self) -> bool {
self.cq_head == self.cq_tail
}
pub fn peek_cqe(&self) -> Option<&IoUringCqe> {
if self.cq_empty() {
return None;
}
let idx = self.cq_head as usize & (self.ring_size as usize - 1);
Some(&self.cq_entries[idx])
}
pub fn cq_advance(&mut self, n: u32) {
self.cq_head = self.cq_head.wrapping_add(n);
}
pub fn submit_and_wait(
&mut self,
wait_nr: u32,
) -> Result<(u32, Vec<IoUringCqe>), &'static str> {
let submitted = self.submit()?;
let cqes = self.wait_completions(wait_nr)?;
Ok((submitted, cqes))
}
pub fn prep_read_fixed(
&mut self,
fd: i32,
buf_index: u16,
len: u32,
offset: u64,
user_data: u64,
) -> Option<()> {
if let Some(sqe) = self.get_sqe() {
sqe.opcode = IoUringOp::Read;
sqe.fd = fd;
sqe.addr = buf_index as u64;
sqe.len = len;
sqe.off = offset;
sqe.user_data = user_data;
sqe.flags = 0x04; Some(())
} else {
None
}
}
pub fn prep_splice(
&mut self,
fd_in: i32,
off_in: i64,
fd_out: i32,
off_out: i64,
len: u32,
user_data: u64,
) -> Option<()> {
if let Some(sqe) = self.get_sqe() {
sqe.opcode = IoUringOp::Write; sqe.fd = fd_in;
sqe.addr = fd_out as u64;
sqe.len = len;
sqe.off = off_in as u64;
sqe.user_data = user_data;
Some(())
} else {
None
}
}
pub fn prep_timeout(
&mut self,
ts: &Duration,
count: u64,
flags: u32,
user_data: u64,
) -> Option<()> {
if let Some(sqe) = self.get_sqe() {
sqe.opcode = IoUringOp::Timeout;
sqe.addr = ts.as_nanos() as u64;
sqe.len = count as u32;
sqe.flags = flags as u8;
sqe.user_data = user_data;
Some(())
} else {
None
}
}
pub fn prep_cancel(&mut self, target_user_data: u64, flags: u32, user_data: u64) -> Option<()> {
if let Some(sqe) = self.get_sqe() {
sqe.opcode = IoUringOp::Cancel;
sqe.addr = target_user_data;
sqe.flags = flags as u8;
sqe.user_data = user_data;
Some(())
} else {
None
}
}
pub fn prep_send(&mut self, fd: i32, buf: &[u8], flags: u32, user_data: u64) -> Option<()> {
if let Some(sqe) = self.get_sqe() {
sqe.opcode = IoUringOp::Send;
sqe.fd = fd;
sqe.addr = buf.as_ptr() as u64;
sqe.len = buf.len() as u32;
sqe.flags = flags as u8;
sqe.user_data = user_data;
Some(())
} else {
None
}
}
pub fn prep_recv(&mut self, fd: i32, buf: &mut [u8], flags: u32, user_data: u64) -> Option<()> {
if let Some(sqe) = self.get_sqe() {
sqe.opcode = IoUringOp::Recv;
sqe.fd = fd;
sqe.addr = buf.as_ptr() as u64;
sqe.len = buf.len() as u32;
sqe.flags = flags as u8;
sqe.user_data = user_data;
Some(())
} else {
None
}
}
}
#[derive(Debug, Clone)]
pub struct SpdkNvmeofTarget {
pub nqn: String,
pub transport: String,
pub address: String,
pub port: u16,
pub num_namespaces: u32,
pub initialized: bool,
}
impl SpdkNvmeofTarget {
pub fn new(nqn: &str, transport: &str, address: &str, port: u16) -> Self {
Self {
nqn: nqn.to_string(),
transport: transport.to_string(),
address: address.to_string(),
port,
num_namespaces: 0,
initialized: false,
}
}
pub fn start(&mut self) -> Result<(), &'static str> {
self.initialized = true;
Ok(())
}
pub fn add_namespace(&mut self, _bdev_name: &str, nsid: u32) -> Result<(), &'static str> {
if !self.initialized {
return Err("NVMe-oF target not started");
}
self.num_namespaces += 1;
Ok(())
}
pub fn stop(&mut self) {
self.initialized = false;
}
}
#[derive(Debug, Clone)]
pub struct SpdkBdev {
pub name: String,
pub block_size: u32,
pub block_count: u64,
pub write_cache: bool,
pub bdev_type: SpdkBdevType,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpdkBdevType {
Malloc,
Aio,
Nvme,
Null,
Passthru,
Raid,
Lvol,
}
impl SpdkBdev {
pub fn new_malloc(name: &str, block_size: u32, num_blocks: u64) -> Self {
Self {
name: name.to_string(),
block_size,
block_count: num_blocks,
write_cache: false,
bdev_type: SpdkBdevType::Malloc,
}
}
pub fn new_aio(name: &str, _filename: &str, block_size: u32) -> Self {
Self {
name: name.to_string(),
block_size,
block_count: 0, write_cache: true,
bdev_type: SpdkBdevType::Aio,
}
}
pub fn size_bytes(&self) -> u64 {
self.block_count * self.block_size as u64
}
pub fn read_blocks(
&self,
_lba: u64,
_num_blocks: u64,
_buf: &mut [u8],
) -> Result<(), &'static str> {
Ok(())
}
pub fn write_blocks(
&self,
_lba: u64,
_num_blocks: u64,
_buf: &[u8],
) -> Result<(), &'static str> {
Ok(())
}
pub fn unmap_blocks(&self, _lba: u64, _num_blocks: u64) -> Result<(), &'static str> {
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RdmaQpType {
ReliableConnection,
UnreliableConnection,
ReliableDatagram,
UnreliableDatagram,
RawPacket,
}
#[derive(Debug, Clone)]
pub struct RdmaMemoryWindow {
pub mw_handle: u64,
pub rkey: u32,
pub permissions: RdmaAccessFlags,
}
#[derive(Debug, Clone, Copy)]
pub struct RdmaAccessFlags {
pub local_write: bool,
pub remote_write: bool,
pub remote_read: bool,
pub remote_atomic: bool,
pub bind_mw: bool,
pub zero_based: bool,
}
impl Default for RdmaAccessFlags {
fn default() -> Self {
Self {
local_write: true,
remote_write: true,
remote_read: true,
remote_atomic: false,
bind_mw: false,
zero_based: false,
}
}
}
impl RdmaContext {
pub fn create_qp_typed(
&mut self,
qp_type: RdmaQpType,
capacity: u32,
) -> Result<&RdmaQueuePair, &'static str> {
if !self.initialized {
return Err("RDMA context not initialized");
}
let qp = RdmaQueuePair {
qp_num: self.queue_pairs.len() as u32 + 1,
state: RdmaQpState::Reset,
send_cq: RdmaCompletionQueue {
cq_handle: self.queue_pairs.len() as u64 * 2 + 1,
capacity,
},
recv_cq: RdmaCompletionQueue {
cq_handle: self.queue_pairs.len() as u64 * 2 + 2,
capacity,
},
};
self.queue_pairs.push(qp);
Ok(self.queue_pairs.last().unwrap())
}
pub fn alloc_memory_window(
&mut self,
_pd: &RdmaProtectionDomain,
) -> Result<RdmaMemoryWindow, &'static str> {
if !self.initialized {
return Err("RDMA context not initialized");
}
Ok(RdmaMemoryWindow {
mw_handle: 100,
rkey: 200,
permissions: RdmaAccessFlags::default(),
})
}
pub fn post_send(
&self,
qp_num: u32,
local_addr: u64,
length: u32,
lkey: u32,
) -> Result<(), &'static str> {
if !self.initialized {
return Err("RDMA context not initialized");
}
let _ = (qp_num, local_addr, length, lkey);
Ok(())
}
pub fn post_recv(
&self,
qp_num: u32,
local_addr: u64,
length: u32,
lkey: u32,
) -> Result<(), &'static str> {
if !self.initialized {
return Err("RDMA context not initialized");
}
let _ = (qp_num, local_addr, length, lkey);
Ok(())
}
pub fn rdma_compare_and_swap(
&self,
_qp_num: u32,
_local_addr: u64,
_lkey: u32,
_remote_addr: u64,
_rkey: u32,
_compare: u64,
_swap: u64,
) -> Result<(), &'static str> {
if !self.initialized {
return Err("RDMA context not initialized");
}
Ok(())
}
pub fn rdma_fetch_add(
&self,
_qp_num: u32,
_local_addr: u64,
_lkey: u32,
_remote_addr: u64,
_rkey: u32,
_add: u64,
) -> Result<(), &'static str> {
if !self.initialized {
return Err("RDMA context not initialized");
}
Ok(())
}
pub fn poll_cq(&self, _cq: &RdmaCompletionQueue, max_count: u32) -> Vec<()> {
Vec::with_capacity(max_count as usize)
}
}
impl VnniAccelerator {
pub fn vpdpbusd_sat(&mut self, a: &[u8; 4], b: &[i8; 4], c: &mut [i32; 4]) {
self.ops_count += 1;
for i in 0..4 {
let product = (a[i] as i64) * (b[i] as i64);
let sum = (c[i] as i64).saturating_add(product);
c[i] = sum.clamp(i32::MIN as i64, i32::MAX as i64) as i32;
}
}
pub fn vpdpwssds(&mut self, a: &[i16; 8], b: &[i16; 8], c: &mut [i32; 8]) {
self.ops_count += 1;
for i in 0..8 {
let product = (a[i] as i64) * (b[i] as i64);
let sum = (c[i] as i64).saturating_add(product);
c[i] = sum.clamp(i32::MIN as i64, i32::MAX as i64) as i32;
}
}
pub fn vpmadd52luq(a: u64, b: u64, c: u64) -> u64 {
let a52 = a & 0x000F_FFFF_FFFF_FFFF;
let b52 = b & 0x000F_FFFF_FFFF_FFFF;
c.wrapping_add(a52.wrapping_mul(b52))
}
pub fn vpmadd52huq(a: u64, b: u64, c: u64) -> u64 {
let a52 = (a >> 12) & 0x000F_FFFF_FFFF_FFFF;
let b52 = (b >> 12) & 0x000F_FFFF_FFFF_FFFF;
c.wrapping_add(a52.wrapping_mul(b52))
}
pub fn int8_gemm(&mut self, a: &[i8], b: &[i8], c: &mut [i32], m: usize, n: usize, k: usize) {
for i in 0..m {
for j in 0..n {
let mut sum = c[i * n + j];
for kk in 0..k {
sum += (a[i * k + kk] as i32) * (b[j * k + kk] as i32);
}
c[i * n + j] = sum;
self.ops_count += 1;
}
}
}
}
impl Bf16Accelerator {
pub fn f32_slice_to_bf16(src: &[f32]) -> Vec<u16> {
src.iter().map(|&v| Self::f32_to_bf16(v)).collect()
}
pub fn bf16_slice_to_f32(src: &[u16]) -> Vec<f32> {
src.iter().map(|&v| Self::bf16_to_f32(v)).collect()
}
pub fn bf16_gemm(&mut self, a: &[u16], b: &[u16], c: &mut [f32], m: usize, n: usize, k: usize) {
for i in 0..m {
for j in 0..n {
let mut sum = c[i * n + j];
for kk in 0..k {
let av = Self::bf16_to_f32(a[i * k + kk]);
let bv = Self::bf16_to_f32(b[kk * n + j]);
sum += av * bv;
}
c[i * n + j] = sum;
self.ops_count += 1;
}
}
}
pub fn bf16_dot(&self, a: &[u16], b: &[u16]) -> f32 {
let mut sum = 0.0f32;
for i in 0..a.len().min(b.len()) {
sum += Self::bf16_to_f32(a[i]) * Self::bf16_to_f32(b[i]);
}
sum
}
}
impl Fp16Accelerator {
pub fn f16_l2_norm(&self, a: &[u16]) -> f32 {
let sum_sq: f32 = a
.iter()
.map(|&v| {
let f = Self::f16_to_f32(v);
f * f
})
.sum();
sum_sq.sqrt()
}
pub fn f16_softmax(&self, input: &[u16]) -> Vec<u16> {
let f32_input: Vec<f32> = input.iter().map(|&v| Self::f16_to_f32(v)).collect();
let max_val = f32_input.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
let exp_sum: f32 = f32_input.iter().map(|&v| (v - max_val).exp()).sum();
f32_input
.iter()
.map(|&v| Self::f32_to_f16(((v - max_val).exp()) / exp_sum))
.collect()
}
pub fn f16_layer_norm(&self, input: &[u16], gamma: &[u16], beta: &[u16], eps: f32) -> Vec<u16> {
let n = input.len();
let f32_input: Vec<f32> = input.iter().map(|&v| Self::f16_to_f32(v)).collect();
let mean: f32 = f32_input.iter().sum::<f32>() / n as f32;
let var: f32 = f32_input.iter().map(|&v| (v - mean).powi(2)).sum::<f32>() / n as f32;
let inv_std = 1.0 / (var + eps).sqrt();
let mut output = Vec::with_capacity(n);
for i in 0..n {
let normalized = (f32_input[i] - mean) * inv_std;
let gamma_f = Self::bf16_to_f32(gamma[i]); let beta_f = Self::bf16_to_f32(beta[i]);
output.push(Self::f32_to_f16(normalized * gamma_f + beta_f));
}
output
}
pub fn f16_relu(&self, input: &[u16]) -> Vec<u16> {
input
.iter()
.map(|&v| {
let f = Self::f16_to_f32(v);
Self::f32_to_f16(f.max(0.0))
})
.collect()
}
pub fn f16_gelu(&self, input: &[u16]) -> Vec<u16> {
input
.iter()
.map(|&v| {
let x = Self::f16_to_f32(v);
let c = (2.0 / std::f32::consts::PI).sqrt();
let gelu = 0.5 * x * (1.0 + (c * (x + 0.044715 * x.powi(3))).tanh());
Self::f32_to_f16(gelu)
})
.collect()
}
pub fn f16_silu(&self, input: &[u16]) -> Vec<u16> {
input
.iter()
.map(|&v| {
let x = Self::f16_to_f32(v);
Self::f32_to_f16(x / (1.0 + (-x).exp()))
})
.collect()
}
}
impl AmxAccelerator {
pub fn tilezero(&mut self, tile: u8) -> Result<(), &'static str> {
if !self.available {
return Err("AMX not available");
}
if tile >= 8 {
return Err("Invalid tile index");
}
self.ops_count += 1;
Ok(())
}
pub fn tileloadd(&mut self, tile: u8, base_addr: u64, stride: u64) -> Result<(), &'static str> {
if !self.available {
return Err("AMX not available");
}
if tile >= 8 {
return Err("Invalid tile index");
}
let _ = base_addr;
let _ = stride;
self.ops_count += 1;
Ok(())
}
pub fn tilestored(
&mut self,
tile: u8,
base_addr: u64,
stride: u64,
) -> Result<(), &'static str> {
if !self.available {
return Err("AMX not available");
}
if tile >= 8 {
return Err("Invalid tile index");
}
let _ = base_addr;
let _ = stride;
self.ops_count += 1;
Ok(())
}
pub fn tcmmrlfp16ps(&mut self, tile_c: u8, tile_a: u8, tile_b: u8) -> Result<(), &'static str> {
if !self.available {
return Err("AMX not available");
}
self.ops_count += 1;
let _ = (tile_c, tile_a, tile_b);
Ok(())
}
pub fn tcmmilfp16ps(&mut self, tile_c: u8, tile_a: u8, tile_b: u8) -> Result<(), &'static str> {
if !self.available {
return Err("AMX not available");
}
self.ops_count += 1;
let _ = (tile_c, tile_a, tile_b);
Ok(())
}
pub fn pack_tile_a(&self, data: &[i8], rows: usize, cols: usize) -> Vec<i8> {
let col_stride = align_up(rows, 4);
let mut packed = vec![0i8; col_stride * cols];
for r in 0..rows {
for c in 0..cols {
packed[c * col_stride + r] = data[r * cols + c];
}
}
packed
}
pub fn pack_tile_b(&self, data: &[i8], rows: usize, cols: usize) -> Vec<i8> {
let row_stride = align_up(cols, 4);
let mut packed = vec![0i8; rows * row_stride];
for r in 0..rows {
for c in 0..cols {
packed[r * row_stride + c] = data[r * cols + c];
}
}
packed
}
}
#[derive(Debug, Clone)]
pub struct AcceleratorBenchmark {
pub name: String,
pub iterations: u64,
pub total_time: Duration,
pub bytes_processed: u64,
}
impl AcceleratorBenchmark {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
iterations: 0,
total_time: Duration::ZERO,
bytes_processed: 0,
}
}
pub fn run<F, R>(&mut self, f: F) -> R
where
F: FnOnce() -> R,
{
let start = Instant::now();
let result = f();
self.total_time += start.elapsed();
self.iterations += 1;
result
}
pub fn throughput_bps(&self) -> f64 {
if self.total_time.as_secs_f64() > 0.0 {
self.bytes_processed as f64 / self.total_time.as_secs_f64()
} else {
0.0
}
}
pub fn throughput_gbps(&self) -> f64 {
self.throughput_bps() / 1_000_000_000.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_feature_set_default() {
let fs = X86AccelFeatureSet::new();
assert!(!fs.has(X86AccelFeature::AesNi));
assert!(!fs.has(X86AccelFeature::Avx));
}
#[test]
fn test_feature_set_detect() {
let fs = X86AccelFeatureSet::detect();
assert!(fs.has(X86AccelFeature::Sse));
assert!(fs.has(X86AccelFeature::Sse2));
assert!(fs.has(X86AccelFeature::Avx));
assert!(fs.has(X86AccelFeature::Avx2));
}
#[test]
fn test_feature_set_manual() {
let mut fs = X86AccelFeatureSet::new();
fs.set(X86AccelFeature::AesNi, true);
fs.set(X86AccelFeature::Avx512f, true);
assert!(fs.has(X86AccelFeature::AesNi));
assert!(fs.has_avx512());
assert!(!fs.has(X86AccelFeature::Vaes));
}
#[test]
fn test_feature_queries() {
let mut fs = X86AccelFeatureSet::new();
fs.set(X86AccelFeature::AesNi, true);
fs.set(X86AccelFeature::Pclmulqdq, true);
assert!(fs.has_aes_gcm_full());
fs.set(X86AccelFeature::Vaes, true);
fs.set(X86AccelFeature::Vpclmulqdq, true);
fs.set(X86AccelFeature::Avx512f, true);
assert!(fs.has_vaes_gcm());
}
#[test]
fn test_feature_display() {
assert_eq!(format!("{}", X86AccelFeature::AesNi), "aes");
assert_eq!(format!("{}", X86AccelFeature::Avx2), "avx2");
assert_eq!(format!("{}", X86AccelFeature::AmxTile), "amx_tile");
}
#[test]
fn test_align_up() {
assert_eq!(align_up(0, 16), 0);
assert_eq!(align_up(1, 16), 16);
assert_eq!(align_up(15, 16), 16);
assert_eq!(align_up(16, 16), 16);
assert_eq!(align_up(17, 16), 32);
assert_eq!(align_up(4095, 4096), 4096);
}
#[test]
fn test_align_down() {
assert_eq!(align_down(0, 16), 0);
assert_eq!(align_down(15, 16), 0);
assert_eq!(align_down(16, 16), 16);
assert_eq!(align_down(31, 16), 16);
}
#[test]
fn test_is_aligned() {
assert!(is_aligned(0, 16));
assert!(is_aligned(16, 16));
assert!(is_aligned(64, 16));
assert!(!is_aligned(17, 16));
assert!(!is_aligned(1, 16));
}
#[test]
fn test_constants() {
assert_eq!(AES_BLOCK_SIZE, 16);
assert_eq!(SHA256_BLOCK_SIZE, 64);
assert_eq!(SHA512_BLOCK_SIZE, 128);
assert_eq!(GCM_BLOCK_SIZE, 16);
assert_eq!(CACHE_LINE_SIZE, 64);
assert_eq!(PAGE_SIZE, 4096);
assert_eq!(AVX_REGISTER_SIZE, 32);
assert_eq!(AVX512_REGISTER_SIZE, 64);
}
#[test]
fn test_accelerator_new() {
let accel = X86Accelerator::new();
assert!(accel.is_initialized());
assert!(!accel.capability_summary().is_empty());
}
#[test]
fn test_accelerator_default() {
let accel = X86Accelerator::default();
assert!(accel.is_initialized());
}
#[test]
fn test_accelerator_custom_features() {
let mut fs = X86AccelFeatureSet::new();
fs.set(X86AccelFeature::AesNi, true);
fs.set(X86AccelFeature::Avx512f, true);
fs.set(X86AccelFeature::AmxTile, true);
let accel = X86Accelerator::with_features(fs);
assert!(accel.features.has(X86AccelFeature::AesNi));
let summary = accel.capability_summary();
assert!(summary.contains("AES"));
assert!(summary.contains("AMX"));
}
#[test]
fn test_aes_key_size_words() {
assert_eq!(AesKeySize::Aes128.words(), 4);
assert_eq!(AesKeySize::Aes192.words(), 6);
assert_eq!(AesKeySize::Aes256.words(), 8);
}
#[test]
fn test_aes_key_size_rounds() {
assert_eq!(AesKeySize::Aes128.rounds(), 10);
assert_eq!(AesKeySize::Aes192.rounds(), 12);
assert_eq!(AesKeySize::Aes256.rounds(), 14);
}
#[test]
fn test_aes_key_size_bytes() {
assert_eq!(AesKeySize::Aes128.bytes(), 16);
assert_eq!(AesKeySize::Aes192.bytes(), 24);
assert_eq!(AesKeySize::Aes256.bytes(), 32);
}
#[test]
fn test_aes_round_keys_expand_128() {
let key = b"0123456789abcdef";
let rk = AesRoundKeys::expand(key, AesKeySize::Aes128);
assert_eq!(rk.rounds(), 10);
assert_eq!(rk.enc_keys.len(), 44); }
#[test]
fn test_aes_round_keys_expand_256() {
let key = b"0123456789abcdef0123456789abcdef";
let rk = AesRoundKeys::expand(key, AesKeySize::Aes256);
assert_eq!(rk.rounds(), 14);
assert_eq!(rk.enc_keys.len(), 60); }
#[test]
fn test_aes_encrypt_block_128() {
let key = b"\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c";
let rk = AesRoundKeys::expand(key, AesKeySize::Aes128);
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let plaintext: [u8; 16] =
*b"\x32\x43\xf6\xa8\x88\x5a\x30\x8d\x31\x31\x98\xa2\xe0\x37\x07\x34";
let ciphertext = crypto.aes_encrypt_block(&plaintext, &rk);
assert_ne!(ciphertext, plaintext);
assert_eq!(ciphertext.len(), 16);
}
#[test]
fn test_aes_encrypt_decrypt_roundtrip_128() {
let key = b"\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c";
let rk = AesRoundKeys::expand(key, AesKeySize::Aes128);
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let plaintext: [u8; 16] = *b"Hello, World!!!!";
let ct = crypto.aes_encrypt_block(&plaintext, &rk);
let pt = crypto.aes_decrypt_block(&ct, &rk);
assert_eq!(pt, plaintext);
}
#[test]
fn test_aes_encrypt_decrypt_roundtrip_256() {
let key = b"0123456789abcdef0123456789abcdef";
let rk = AesRoundKeys::expand(key, AesKeySize::Aes256);
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let plaintext: [u8; 16] = *b"Test AES-256!!!!";
let ct = crypto.aes_encrypt_block(&plaintext, &rk);
let pt = crypto.aes_decrypt_block(&ct, &rk);
assert_eq!(pt, plaintext);
}
#[test]
fn test_aes_ecb_encrypt_decrypt() {
let key = b"0123456789abcdef";
let rk = AesRoundKeys::expand(key, AesKeySize::Aes128);
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let plaintext = b"This is a test message for ECB mode!!!";
let ct = crypto.aes_ecb_encrypt(plaintext, &rk);
let pt = crypto.aes_ecb_decrypt(&ct, &rk);
assert_eq!(&pt[..plaintext.len()], plaintext);
}
#[test]
fn test_aes_ecb_empty() {
let key = b"0123456789abcdef";
let rk = AesRoundKeys::expand(key, AesKeySize::Aes128);
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let ct = crypto.aes_ecb_encrypt(b"", &rk);
assert!(ct.is_empty());
}
#[test]
fn test_aes_cbc_encrypt_decrypt() {
let key = b"0123456789abcdef";
let iv: [u8; 16] = *b"abcdefghijklmnop";
let rk = AesRoundKeys::expand(key, AesKeySize::Aes128);
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let plaintext = b"AES-CBC mode test message!!!";
let ct = crypto.aes_cbc_encrypt(plaintext, &iv, &rk);
let pt = crypto.aes_cbc_decrypt(&ct, &iv, &rk);
assert_eq!(&pt[..plaintext.len()], plaintext);
}
#[test]
fn test_aes_cbc_different_iv_produces_different_ct() {
let key = b"0123456789abcdef";
let iv1: [u8; 16] = *b"aaaaaaaaaaaaaaaa";
let iv2: [u8; 16] = *b"bbbbbbbbbbbbbbbb";
let rk = AesRoundKeys::expand(key, AesKeySize::Aes128);
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let plaintext = b"Test CBC IV impact";
let ct1 = crypto.aes_cbc_encrypt(plaintext, &iv1, &rk);
let ct2 = crypto.aes_cbc_encrypt(plaintext, &iv2, &rk);
assert_ne!(ct1, ct2);
}
#[test]
fn test_aes_ctr_encrypt_decrypt() {
let key = b"0123456789abcdef";
let iv: [u8; 16] = *b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
let rk = AesRoundKeys::expand(key, AesKeySize::Aes128);
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let plaintext = b"CTR mode test data!";
let ct = crypto.aes_ctr_process(plaintext, &iv, &rk);
let pt = crypto.aes_ctr_process(&ct, &iv, &rk);
assert_eq!(&pt[..plaintext.len()], plaintext);
}
#[test]
fn test_aes_ctr_empty() {
let key = b"0123456789abcdef";
let iv: [u8; 16] = [0u8; 16];
let rk = AesRoundKeys::expand(key, AesKeySize::Aes128);
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let ct = crypto.aes_ctr_process(b"", &iv, &rk);
assert!(ct.is_empty());
}
#[test]
fn test_aes_gcm_encrypt_decrypt() {
let key = b"0123456789abcdef";
let iv: [u8; 12] = *b"abcdefghijkl";
let aad = b"authenticated data";
let plaintext = b"Secret message for GCM mode!";
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let (ct, tag) = crypto.aes_gcm_encrypt(plaintext, aad, &iv, key, AesKeySize::Aes128);
assert_eq!(ct.len(), plaintext.len());
assert_eq!(tag.len(), 16);
let pt = crypto.aes_gcm_decrypt(&ct, aad, &iv, &tag, key, AesKeySize::Aes128);
assert!(pt.is_ok());
assert_eq!(&pt.unwrap()[..], plaintext);
}
#[test]
fn test_aes_gcm_tag_mismatch() {
let key = b"0123456789abcdef";
let iv: [u8; 12] = *b"abcdefghijkl";
let aad = b"authenticated data";
let plaintext = b"Secret message";
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let (ct, _tag) = crypto.aes_gcm_encrypt(plaintext, aad, &iv, key, AesKeySize::Aes128);
let wrong_tag = [0u8; 16];
let pt = crypto.aes_gcm_decrypt(&ct, aad, &iv, &wrong_tag, key, AesKeySize::Aes128);
assert!(pt.is_err());
}
#[test]
fn test_aes_gcm_empty_plaintext() {
let key = b"0123456789abcdef";
let iv: [u8; 12] = *b"abcdefghijkl";
let aad = b"";
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let (ct, tag) = crypto.aes_gcm_encrypt(b"", aad, &iv, key, AesKeySize::Aes128);
assert!(ct.is_empty());
assert_eq!(tag.len(), 16);
let pt = crypto.aes_gcm_decrypt(&ct, aad, &iv, &tag, key, AesKeySize::Aes128);
assert!(pt.is_ok());
assert!(pt.unwrap().is_empty());
}
#[test]
fn test_aes_xts_encrypt_decrypt() {
let key1 = b"0123456789abcdef";
let key2 = b"fedcba9876543210";
let tweak: [u8; 16] = *b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01";
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let plaintext: Vec<u8> = (0..64).map(|i| i as u8).collect();
let ct = crypto.aes_xts_encrypt(&plaintext, &tweak, key1, key2, AesKeySize::Aes128);
let pt = crypto.aes_xts_decrypt(&ct, &tweak, key1, key2, AesKeySize::Aes128);
assert_eq!(pt, plaintext);
}
#[test]
fn test_aes_ccm_encrypt_decrypt() {
let key = b"0123456789abcdef";
let nonce: [u8; 13] = *b"abcdefghijklm";
let aad = b"CCM AAD data";
let plaintext = b"CCM test message!!!";
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let (ct, tag) = crypto.aes_ccm_encrypt(plaintext, &nonce, aad, 8, key, AesKeySize::Aes128);
assert_eq!(tag.len(), 8);
let pt = crypto.aes_ccm_decrypt(&ct, &nonce, aad, &tag, key, AesKeySize::Aes128);
assert!(pt.is_ok());
assert_eq!(&pt.unwrap()[..plaintext.len()], plaintext);
}
#[test]
fn test_aes_mode_display() {
assert_eq!(format!("{}", AesMode::Ecb), "ECB");
assert_eq!(format!("{}", AesMode::Gcm), "GCM");
assert_eq!(format!("{}", AesMode::Xts), "XTS");
}
#[test]
fn test_sha1_empty() {
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let hash = crypto.sha1_hash(b"");
assert_eq!(
hex_encode(&hash),
"da39a3ee5e6b4b0d3255bfef95601890afd80709"
);
}
#[test]
fn test_sha1_abc() {
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let hash = crypto.sha1_hash(b"abc");
assert_eq!(
hex_encode(&hash),
"a9993e364706816aba3e25717850c26c9cd0d89d"
);
}
#[test]
fn test_sha256_empty() {
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let hash = crypto.sha256_hash(b"");
assert_eq!(
hex_encode(&hash),
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
);
}
#[test]
fn test_sha256_abc() {
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let hash = crypto.sha256_hash(b"abc");
assert_eq!(
hex_encode(&hash),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
}
#[test]
fn test_sha256_long() {
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let data = b"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu";
let hash = crypto.sha256_hash(data);
assert_eq!(hash.len(), 32);
}
#[test]
fn test_sha512_empty() {
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let hash = crypto.sha512_hash(b"");
assert_eq!(
hex_encode(&hash),
"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
);
}
#[test]
fn test_sha512_abc() {
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let hash = crypto.sha512_hash(b"abc");
assert_eq!(
hex_encode(&hash),
"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
);
}
#[test]
fn test_ghash_multiply() {
let h: u128 = 0x66e94bd4ef8a2c3b884cfa59ca342b2e;
let result = ghash_multiply(h, 1);
assert_eq!(result, h);
}
#[test]
fn test_ghash_multiply_zero() {
let result = ghash_multiply(0, 0x1234567890ABCDEF);
assert_eq!(result, 0);
}
#[test]
fn test_ghash_state_empty() {
let h: u128 = 0x66e94bd4ef8a2c3b884cfa59ca342b2e;
let mut ghash = GhashState::new(h);
ghash.update(b"");
let tag = ghash.finalize(0, 0);
assert_eq!(tag, 0);
}
#[test]
fn test_crc32c_empty() {
let result = crate::crc32c_software(b"", 0);
assert_eq!(result, 0);
}
#[test]
fn test_crc32c_basic() {
let result = crate::crc32c_software(b"123456789", 0);
assert_eq!(result, 0xE3069283);
}
#[test]
fn test_rdrand32() {
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let val = crypto.rdrand32();
assert!(val.is_some());
}
#[test]
fn test_rdrand64() {
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let val = crypto.rdrand64();
assert!(val.is_some());
}
#[test]
fn test_fill_random() {
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let mut buf = [0u8; 64];
assert!(crypto.fill_random(&mut buf));
let all_zero = buf.iter().all(|&b| b == 0);
assert!(!all_zero);
}
#[test]
fn test_crypto_stats() {
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let key = b"0123456789abcdef";
let rk = AesRoundKeys::expand(key, AesKeySize::Aes128);
crypto.aes_encrypt_block(b"Hello, World!!!!", &rk);
let stats = crypto.stats();
assert_eq!(stats.aes_ops, 1);
}
#[test]
fn test_lz4_compress_decompress() {
let mut accel = X86CompressionAccel::new(&X86AccelFeatureSet::detect());
let data = b"Hello, World! Hello, World! This is a test of LZ4 compression.";
let result = accel.lz4_compress(data);
assert!(result.success);
let decompressed = accel.lz4_decompress(&result.data);
assert!(decompressed.success);
assert_eq!(&decompressed.data[..data.len()], data);
}
#[test]
fn test_lz4_empty() {
let mut accel = X86CompressionAccel::new(&X86AccelFeatureSet::detect());
let result = accel.lz4_compress(b"");
assert!(result.success);
let decompressed = accel.lz4_decompress(&result.data);
assert!(decompressed.success);
}
#[test]
fn test_snappy_compress_decompress() {
let mut accel = X86CompressionAccel::new(&X86AccelFeatureSet::detect());
let data =
b"Snappy compression test. Repeated text for matching. Repeated text for matching.";
let result = accel.snappy_compress(data);
assert!(result.success);
let decompressed = accel.snappy_decompress(&result.data);
assert!(decompressed.success);
}
#[test]
fn test_snappy_empty() {
let mut accel = X86CompressionAccel::new(&X86AccelFeatureSet::detect());
let result = accel.snappy_compress(b"");
assert!(result.success);
}
#[test]
fn test_zstd_compress_decompress() {
let mut accel = X86CompressionAccel::new(&X86AccelFeatureSet::detect());
let data = b"Zstandard compression test data.";
let result = accel.zstd_compress(data, 3);
assert!(result.success);
let decompressed = accel.zstd_decompress(&result.data);
assert!(decompressed.success);
assert_eq!(&decompressed.data[..data.len()], data);
}
#[test]
fn test_compression_crc32c() {
let accel = X86CompressionAccel::new(&X86AccelFeatureSet::detect());
let crc = accel.crc32c(b"123456789");
assert_eq!(crc, 0xE3069283);
}
#[test]
fn test_compression_stats() {
let mut accel = X86CompressionAccel::new(&X86AccelFeatureSet::detect());
let _ = accel.lz4_compress(b"test data");
let stats = accel.stats();
assert!(stats.compressed_bytes > 0);
}
#[test]
fn test_strlen_safe() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
assert_eq!(simd.strlen_safe(b"hello\0world"), 5);
assert_eq!(simd.strlen_safe(b"\0"), 0);
assert_eq!(simd.strlen_safe(b"no null"), 7);
}
#[test]
fn test_memmem_found() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
let haystack = b"hello world, this is a test";
assert_eq!(simd.memmem(haystack, b"world"), Some(6));
assert_eq!(simd.memmem(haystack, b"test"), Some(25));
}
#[test]
fn test_memmem_not_found() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
let haystack = b"hello world";
assert_eq!(simd.memmem(haystack, b"xyz"), None);
}
#[test]
fn test_memmem_empty_needle() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
assert_eq!(simd.memmem(b"hello", b""), Some(0));
}
#[test]
fn test_memmem_needle_longer_than_haystack() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
assert_eq!(simd.memmem(b"hi", b"hello"), None);
}
#[test]
fn test_fnv1a_32() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
let hash = simd.fnv1a_32(b"hello");
assert_ne!(hash, 0);
assert_eq!(hash, simd.fnv1a_32(b"hello"));
}
#[test]
fn test_fnv1a_64() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
let hash = simd.fnv1a_64(b"hello");
assert_ne!(hash, 0);
assert_eq!(hash, simd.fnv1a_64(b"hello"));
}
#[test]
fn test_murmur3_32() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
let h1 = simd.murmur3_32(b"hello", 42);
let h2 = simd.murmur3_32(b"hello", 42);
assert_eq!(h1, h2);
let h3 = simd.murmur3_32(b"hello", 99);
assert_ne!(h1, h3);
}
#[test]
fn test_xxhash32() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
let h = simd.xxhash32(b"hello", 0);
assert_ne!(h, 0);
assert_eq!(h, simd.xxhash32(b"hello", 0));
}
#[test]
fn test_xxhash64() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
let h = simd.xxhash64(b"hello world, this is a test of xxhash64", 0);
assert_ne!(h, 0);
assert_eq!(
h,
simd.xxhash64(b"hello world, this is a test of xxhash64", 0)
);
}
#[test]
fn test_base64_encode() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
assert_eq!(simd.base64_encode(b"Man"), "TWFu");
assert_eq!(simd.base64_encode(b"Ma"), "TWE=");
assert_eq!(simd.base64_encode(b"M"), "TQ==");
}
#[test]
fn test_base64_decode() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
assert_eq!(simd.base64_decode("TWFu").unwrap(), b"Man");
assert_eq!(simd.base64_decode("TWE=").unwrap(), b"Ma");
assert_eq!(simd.base64_decode("TQ==").unwrap(), b"M");
}
#[test]
fn test_base64_roundtrip() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
let data = b"This is a test of Base64 encoding and decoding with SIMD acceleration!";
let encoded = simd.base64_encode(data);
let decoded = simd.base64_decode(&encoded).unwrap();
assert_eq!(&decoded[..], data);
}
#[test]
fn test_json_lex_simple() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
let tokens = simd.json_lex(r#"{"key": "value"}"#).unwrap();
assert_eq!(tokens.len(), 4);
assert_eq!(tokens[0], JsonToken::ObjectStart);
assert_eq!(tokens[1], JsonToken::String("key".to_string()));
assert_eq!(tokens[3], JsonToken::String("value".to_string()));
}
#[test]
fn test_json_lex_number_bool_null() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
let tokens = simd.json_lex(r#"[1.5, true, false, null]"#).unwrap();
assert_eq!(tokens.len(), 8); assert_eq!(tokens[1], JsonToken::Number(1.5));
assert_eq!(tokens[3], JsonToken::Boolean(true));
assert_eq!(tokens[5], JsonToken::Boolean(false));
assert_eq!(tokens[7], JsonToken::Null);
}
#[test]
fn test_json_lex_invalid() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
assert!(simd.json_lex("{invalid}").is_err());
}
#[test]
fn test_json_token_display() {
assert_eq!(format!("{}", JsonToken::ObjectStart), "{");
assert_eq!(format!("{}", JsonToken::Null), "null");
assert_eq!(format!("{}", JsonToken::Boolean(true)), "true");
assert_eq!(format!("{}", JsonToken::Number(3.14)), "3.14");
}
#[test]
fn test_validate_utf8_valid() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
assert!(simd.validate_utf8(b"Hello, World!"));
assert!(simd.validate_utf8("こんにちは".as_bytes()));
assert!(simd.validate_utf8("😀🎉".as_bytes()));
}
#[test]
fn test_validate_utf8_invalid() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
assert!(!simd.validate_utf8(b"\xC0\x80"));
assert!(!simd.validate_utf8(b"\xFF"));
assert!(!simd.validate_utf8(b"\xE2\x82"));
}
#[test]
fn test_utf8_to_utf32() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
let result = simd.utf8_to_utf32(b"ABC").unwrap();
assert_eq!(result, vec![65, 66, 67]);
}
#[test]
fn test_utf8_to_utf32_invalid() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
assert!(simd.utf8_to_utf32(b"\xFF").is_err());
}
#[test]
fn test_io_uring_new() {
let ring = IoUring::new(256);
assert_eq!(ring.ring_size, 256);
assert!(!ring.initialized);
}
#[test]
fn test_io_uring_setup() {
let mut ring = IoUring::new(128);
assert!(ring.setup().is_ok());
assert!(ring.initialized);
}
#[test]
fn test_io_uring_get_sqe() {
let mut ring = IoUring::new(8);
ring.setup().unwrap();
let sqe = ring.get_sqe();
assert!(sqe.is_some());
}
#[test]
fn test_io_uring_submit() {
let mut ring = IoUring::new(8);
ring.setup().unwrap();
let _ = ring.get_sqe();
assert_eq!(ring.submit().unwrap(), 1);
}
#[test]
fn test_io_uring_teardown() {
let mut ring = IoUring::new(8);
ring.setup().unwrap();
ring.teardown();
assert!(!ring.initialized);
}
#[test]
fn test_dpdk_pmd_new() {
let pmd = DpdkPmd::new();
assert!(!pmd.initialized);
assert!(pmd.ports.is_empty());
}
#[test]
fn test_dpdk_eal_init() {
let mut pmd = DpdkPmd::new();
assert!(pmd.eal_init(&[]).is_ok());
assert!(pmd.initialized);
}
#[test]
fn test_spdk_nvme_new() {
let ctrl = SpdkNvmeController::new("PCIe", "0000:00:04.0");
assert_eq!(ctrl.transport, "PCIe");
assert!(!ctrl.initialized);
}
#[test]
fn test_spdk_nvme_probe() {
let mut ctrl = SpdkNvmeController::new("PCIe", "0000:00:04.0");
assert!(ctrl.probe().is_ok());
assert!(ctrl.initialized);
}
#[test]
fn test_ioat_dma_init() {
let mut ch = IoatDmaChannel::new(0);
assert!(ch.init().is_ok());
assert!(ch.initialized);
}
#[test]
fn test_ioat_dma_submit_and_poll() {
let mut ch = IoatDmaChannel::new(1);
ch.init().unwrap();
assert!(ch.submit_copy(0x1000, 0x2000, 4096).is_ok());
assert_eq!(ch.pending.len(), 1);
let completed = ch.poll_completions();
assert_eq!(completed, 1);
assert!(ch.pending.is_empty());
}
#[test]
fn test_rdma_context_init() {
let mut ctx = RdmaContext::new(RdmaTransport::RoCEv2);
assert!(ctx.init("mlx5_0").is_ok());
assert!(ctx.initialized);
}
#[test]
fn test_rdma_register_mr() {
let mut ctx = RdmaContext::new(RdmaTransport::InfiniBand);
ctx.init("mlx5_0").unwrap();
let mr = ctx.register_memory_region(0x1000, 4096);
assert!(mr.is_ok());
assert_eq!(mr.unwrap().length, 4096);
}
#[test]
fn test_rdma_create_qp() {
let mut ctx = RdmaContext::new(RdmaTransport::RoCEv2);
ctx.init("mlx5_0").unwrap();
let qp = ctx.create_qp(128);
assert!(qp.is_ok());
assert_eq!(qp.unwrap().state, RdmaQpState::Reset);
}
#[test]
fn test_io_accel_new() {
let io = X86IOAccel::new(&X86AccelFeatureSet::detect());
assert!(!io.io_uring.initialized);
}
#[test]
fn test_io_accel_init_all() {
let mut io = X86IOAccel::new(&X86AccelFeatureSet::detect());
assert!(io.init_all().is_ok());
assert!(io.io_uring.initialized);
}
#[test]
fn test_vpdpbusd_basic() {
let mut vnni = VnniAccelerator::new(true);
let a: [u8; 4] = [2, 3, 4, 5];
let b: [i8; 4] = [6, 7, 8, 9];
let mut c: [i32; 4] = [0; 4];
vnni.vpdpbusd(&a, &b, &mut c);
assert_eq!(c[0], 12);
assert_eq!(c[1], 21);
assert_eq!(c[2], 32);
assert_eq!(c[3], 45);
}
#[test]
fn test_vpdpbusd_accumulate() {
let mut vnni = VnniAccelerator::new(true);
let a: [u8; 4] = [1, 1, 1, 1];
let b: [i8; 4] = [1, 1, 1, 1];
let mut c: [i32; 4] = [10, 20, 30, 40];
vnni.vpdpbusd(&a, &b, &mut c);
assert_eq!(c[0], 11);
assert_eq!(c[1], 21);
assert_eq!(c[2], 31);
assert_eq!(c[3], 41);
}
#[test]
fn test_f32_to_bf16_roundtrip() {
let val: f32 = 1.0;
let bf = Bf16Accelerator::f32_to_bf16(val);
let back = Bf16Accelerator::bf16_to_f32(bf);
assert_eq!(back, 1.0);
}
#[test]
fn test_f32_to_bf16_approx() {
let val: f32 = 3.14159;
let bf = Bf16Accelerator::f32_to_bf16(val);
let back = Bf16Accelerator::bf16_to_f32(bf);
assert!((back - val).abs() / val < 0.01);
}
#[test]
fn test_vdpbf16ps() {
let mut bf16 = Bf16Accelerator::new(true);
let a: [u16; 8] = [
Bf16Accelerator::f32_to_bf16(1.0),
Bf16Accelerator::f32_to_bf16(2.0),
Bf16Accelerator::f32_to_bf16(3.0),
Bf16Accelerator::f32_to_bf16(4.0),
Bf16Accelerator::f32_to_bf16(0.0),
Bf16Accelerator::f32_to_bf16(0.0),
Bf16Accelerator::f32_to_bf16(0.0),
Bf16Accelerator::f32_to_bf16(0.0),
];
let b: [u16; 8] = [
Bf16Accelerator::f32_to_bf16(2.0),
Bf16Accelerator::f32_to_bf16(3.0),
Bf16Accelerator::f32_to_bf16(4.0),
Bf16Accelerator::f32_to_bf16(5.0),
Bf16Accelerator::f32_to_bf16(0.0),
Bf16Accelerator::f32_to_bf16(0.0),
Bf16Accelerator::f32_to_bf16(0.0),
Bf16Accelerator::f32_to_bf16(0.0),
];
let mut dst: [f32; 4] = [0.0; 4];
bf16.vdpbf16ps(&a, &b, &mut dst);
assert!((dst[0] - 8.0).abs() < 0.1);
assert!((dst[1] - 32.0).abs() < 0.5);
}
#[test]
fn test_amx_tileconfig() {
let mut amx = AmxAccelerator::new(true);
assert!(amx.tileconfig(0, 16, 64).is_ok());
assert_eq!(amx.tile_config[0], (16, 64));
}
#[test]
fn test_amx_tileconfig_invalid_tile() {
let mut amx = AmxAccelerator::new(true);
assert!(amx.tileconfig(8, 16, 64).is_err());
}
#[test]
fn test_amx_tileconfig_exceed_limits() {
let mut amx = AmxAccelerator::new(true);
assert!(amx.tileconfig(0, 17, 64).is_err());
assert!(amx.tileconfig(0, 16, 65).is_err());
}
#[test]
fn test_amx_operations() {
let mut amx = AmxAccelerator::new(true);
amx.tileconfig(0, 16, 64).unwrap();
amx.tileconfig(1, 16, 64).unwrap();
amx.tileconfig(2, 16, 64).unwrap();
assert!(amx.tdpbssd(2, 0, 1).is_ok());
assert!(amx.tdpbf16ps(2, 0, 1).is_ok());
assert!(amx.tdpfp16ps(2, 0, 1).is_ok());
assert_eq!(amx.ops(), 3);
}
#[test]
fn test_amx_tilerelease() {
let mut amx = AmxAccelerator::new(true);
amx.tileconfig(0, 8, 32).unwrap();
amx.tilerelease();
assert_eq!(amx.tile_config[0], (0, 0));
}
#[test]
fn test_amx_not_available() {
let mut amx = AmxAccelerator::new(false);
assert!(amx.tileconfig(0, 16, 64).is_err());
assert!(amx.tdpbssd(0, 1, 2).is_err());
}
#[test]
fn test_f32_to_f16_roundtrip() {
let val: f32 = 1.0;
let f16 = Fp16Accelerator::f32_to_f16(val);
let back = Fp16Accelerator::f16_to_f32(f16);
assert_eq!(back, 1.0);
}
#[test]
fn test_f32_to_f16_negative() {
let val: f32 = -2.5;
let f16 = Fp16Accelerator::f32_to_f16(val);
let back = Fp16Accelerator::f16_to_f32(f16);
assert!((back - val).abs() < 0.01);
}
#[test]
fn test_f16_vfmadd132ph() {
let mut fp16 = Fp16Accelerator::new(true);
let a = [Fp16Accelerator::f32_to_f16(2.0); 16];
let b = [Fp16Accelerator::f32_to_f16(3.0); 16];
let c = [Fp16Accelerator::f32_to_f16(1.0); 16];
let result = fp16.vfmadd132ph(&a, &b, &c);
for &r in &result {
let f = Fp16Accelerator::f16_to_f32(r);
assert!((f - 7.0).abs() < 0.1, "Expected ~7.0, got {}", f);
}
}
#[test]
fn test_f16_convert() {
let fp16 = Fp16Accelerator::new(true);
let vals: [f32; 16] = [
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, -1.0, -2.0, 0.5, 0.25, 100.0, 0.001,
];
let f16_vals = fp16.vcvtps2ph(&vals);
let back = fp16.vcvtph2ps(&f16_vals);
for (i, (&orig, &b)) in vals.iter().zip(back.iter()).enumerate() {
let err = (b - orig).abs() / orig.abs().max(1e-10);
assert!(
err < 0.01,
"FP16 conversion error at index {}: orig={}, back={}",
i,
orig,
b
);
}
}
#[test]
fn test_ml_accel_capability_summary() {
let mut fs = X86AccelFeatureSet::new();
fs.set(X86AccelFeature::Avx512Vnni, true);
fs.set(X86AccelFeature::Avx512Bf16, true);
fs.set(X86AccelFeature::AmxTile, true);
let ml = X86MLAccel::new(&fs);
let summary = ml.capability_summary();
assert!(summary.contains("VNNI"));
assert!(summary.contains("BF16"));
assert!(summary.contains("AMX"));
}
#[test]
fn test_ml_accel_no_features() {
let fs = X86AccelFeatureSet::new();
let ml = X86MLAccel::new(&fs);
assert_eq!(ml.capability_summary(), "None");
}
#[test]
fn test_benchmark_basic() {
let mut bench = AcceleratorBenchmark::new("test_bench");
let result = bench.run(|| 42);
assert_eq!(result, 42);
assert_eq!(bench.iterations, 1);
}
#[test]
fn test_benchmark_throughput() {
let mut bench = AcceleratorBenchmark::new("throughput_test");
bench.bytes_processed = 1_000_000_000; bench.total_time = Duration::from_secs(1);
let gbps = bench.throughput_gbps();
assert!((gbps - 1.0).abs() < 0.01);
}
#[test]
fn test_compression_algorithm_display() {
assert_eq!(format!("{}", CompressionAlgorithm::Deflate), "DEFLATE");
assert_eq!(format!("{}", CompressionAlgorithm::Lz4), "LZ4");
assert_eq!(format!("{}", CompressionAlgorithm::Zstd), "Zstandard");
assert_eq!(format!("{}", CompressionAlgorithm::Snappy), "Snappy");
}
#[test]
fn test_simd_level_selection() {
let mut fs = X86AccelFeatureSet::new();
fs.set(X86AccelFeature::Sse2, true);
let simd = X86SIMDAccel::new(&fs);
assert_eq!(simd.simd_level, SimdLevel::Sse2);
let mut fs2 = X86AccelFeatureSet::new();
fs2.set(X86AccelFeature::Sse2, true);
fs2.set(X86AccelFeature::Avx2, true);
fs2.set(X86AccelFeature::Avx512f, true);
let simd2 = X86SIMDAccel::new(&fs2);
assert_eq!(simd2.simd_level, SimdLevel::Avx512);
}
#[test]
fn test_deflate_software_roundtrip() {
let data = b"Hello, DEFLATE world! This is a test of the software deflate stub.";
let compressed = deflate_software_compress(data);
let decompressed = deflate_software_decompress(&compressed);
assert_eq!(&decompressed[..data.len()], data);
}
#[test]
fn test_iaa_stub() {
let mut accel = X86CompressionAccel::new(&X86AccelFeatureSet::detect());
let result = accel.deflate_compress_iaa(b"test IAA compression data");
assert!(result.success);
}
#[test]
fn test_varint_encode_decode() {
let test_cases: &[u64] = &[0, 1, 127, 128, 255, 300, 16384, 1000000, u64::MAX / 2];
for &val in test_cases {
let mut encoded = Vec::new();
varint_encode(val, &mut encoded);
let (decoded, bytes) = varint_decode(&encoded);
assert_eq!(decoded, val, "varint roundtrip failed for {}", val);
assert!(bytes > 0);
}
}
#[test]
fn test_dsa_descriptor_new_memmove() {
let desc = DsaDescriptor::new_memmove(0x1000, 0x2000, 4096);
assert_eq!(desc.size, 4096);
assert_eq!(desc.src_addr, 0x1000);
assert_eq!(desc.dst_addr, 0x2000);
}
#[test]
fn test_dsa_descriptor_new_memfill() {
let desc = DsaDescriptor::new_memfill(0xAA, 0x3000, 1024);
assert_eq!(desc.pattern, 0xAA);
assert_eq!(desc.dst_addr, 0x3000);
}
#[test]
fn test_dsa_work_queue() {
let mut wq = DsaWorkQueue::new_dedicated(0, 128);
wq.enable();
let desc = DsaDescriptor::new_memmove(0x1000, 0x2000, 4096);
let token = wq.submit(&desc);
assert!(token.is_some());
let stats = wq.stats();
assert_eq!(stats.0, 1);
}
#[test]
fn test_dsa_device_init() {
let mut dsa = DsaDevice::new(0);
assert!(dsa.init().is_ok());
assert!(dsa.initialized);
assert!(!dsa.work_queues.is_empty());
}
#[test]
fn test_simd_ext_strchr() {
let ext = X86SIMDExtOps::new(&X86AccelFeatureSet::detect());
let s = b"hello world";
unsafe {
let p = ext.strchr(s.as_ptr(), b'w' as i32);
assert!(!p.is_null());
assert_eq!(*p, b'w');
}
}
#[test]
fn test_simd_ext_strchr_not_found() {
let ext = X86SIMDExtOps::new(&X86AccelFeatureSet::detect());
let s = b"hello";
unsafe {
let p = ext.strchr(s.as_ptr(), b'z' as i32);
assert!(p.is_null());
}
}
#[test]
fn test_simd_ext_strrchr() {
let ext = X86SIMDExtOps::new(&X86AccelFeatureSet::detect());
let s = b"hello world hello";
unsafe {
let p = ext.strrchr(s.as_ptr(), b'o' as i32);
assert!(!p.is_null());
assert_eq!(*p, b'o');
}
}
#[test]
fn test_simd_ext_strncmp() {
let ext = X86SIMDExtOps::new(&X86AccelFeatureSet::detect());
unsafe {
assert_eq!(ext.strncmp(b"abc".as_ptr(), b"abc".as_ptr(), 3), 0);
assert!(ext.strncmp(b"abc".as_ptr(), b"abd".as_ptr(), 3) < 0);
assert_eq!(ext.strncmp(b"abc".as_ptr(), b"abc".as_ptr(), 2), 0);
}
}
#[test]
fn test_simd_ext_strnlen() {
let ext = X86SIMDExtOps::new(&X86AccelFeatureSet::detect());
unsafe {
assert_eq!(ext.strnlen(b"hello".as_ptr(), 10), 5);
assert_eq!(ext.strnlen(b"hello".as_ptr(), 3), 3);
}
}
#[test]
fn test_simd_ext_strspn() {
let ext = X86SIMDExtOps::new(&X86AccelFeatureSet::detect());
assert_eq!(ext.strspn(b"abc123", b"abcdef"), 3);
assert_eq!(ext.strspn(b"123", b"0123456789"), 3);
}
#[test]
fn test_simd_ext_strcspn() {
let ext = X86SIMDExtOps::new(&X86AccelFeatureSet::detect());
assert_eq!(ext.strcspn(b"abc123", b"123"), 3);
assert_eq!(ext.strcspn(b"hello", b"o"), 4);
}
#[test]
fn test_simd_ext_strstr() {
let ext = X86SIMDExtOps::new(&X86AccelFeatureSet::detect());
assert_eq!(ext.strstr(b"hello world", b"world"), Some(6));
assert_eq!(ext.strstr(b"hello", b"xyz"), None);
}
#[test]
fn test_simd_ext_strcasestr() {
let ext = X86SIMDExtOps::new(&X86AccelFeatureSet::detect());
assert_eq!(ext.strcasestr(b"Hello World", b"WORLD"), Some(6));
assert_eq!(ext.strcasestr(b"hello", b"HeLLo"), Some(0));
}
#[test]
fn test_simd_ext_wcslen() {
let ext = X86SIMDExtOps::new(&X86AccelFeatureSet::detect());
let w: [u16; 6] = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00];
assert_eq!(ext.wcslen(&w), 5);
}
#[test]
fn test_memory_regions_overlap() {
let a = [0u8; 100];
let a_ptr = a.as_ptr();
let b = [0u8; 100];
let b_ptr = b.as_ptr();
assert!(!X86SIMDExtOps::memory_regions_overlap(
a_ptr, 100, b_ptr, 100
));
assert!(X86SIMDExtOps::memory_regions_overlap(
a_ptr,
100,
unsafe { a_ptr.add(50) },
100
));
}
#[test]
fn test_hex_encode() {
let codec = SimdHexCodec::new();
assert_eq!(codec.encode(b"abc"), "616263");
assert_eq!(codec.encode(b""), "");
}
#[test]
fn test_hex_encode_uppercase() {
let codec = SimdHexCodec::uppercase();
assert_eq!(codec.encode(b"\xde\xad"), "DEAD");
}
#[test]
fn test_hex_decode() {
let codec = SimdHexCodec::new();
assert_eq!(codec.decode("616263").unwrap(), b"abc");
assert_eq!(codec.decode("DEAD").unwrap(), b"\xde\xad");
}
#[test]
fn test_hex_decode_odd_length() {
let codec = SimdHexCodec::new();
assert!(codec.decode("abc").is_err());
}
#[test]
fn test_hex_decode_invalid() {
let codec = SimdHexCodec::new();
assert!(codec.decode("GG").is_err());
}
#[test]
fn test_hex_roundtrip() {
let codec = SimdHexCodec::new();
let data = b"Binary data \x00\x01\xFF\xFE test";
let encoded = codec.encode(data);
let decoded = codec.decode(&encoded).unwrap();
assert_eq!(&decoded[..], data);
}
#[test]
fn test_cityhash64_empty() {
let hasher = CityHasher::new(0);
let h = hasher.hash64(b"");
assert_ne!(h, 0);
}
#[test]
fn test_cityhash64_deterministic() {
let hasher = CityHasher::new(42);
let h1 = hasher.hash64(b"hello");
let h2 = hasher.hash64(b"hello");
assert_eq!(h1, h2);
}
#[test]
fn test_cityhash128() {
let hasher = CityHasher::new(0);
let (lo, hi) = hasher.hash128(b"test data");
assert_ne!(lo, 0);
assert_ne!(hi, 0);
let (lo2, hi2) = hasher.hash128(b"different");
assert!((lo, hi) != (lo2, hi2));
}
#[test]
fn test_farmhash64() {
let hasher = FarmHasher::new(0);
let h = hasher.hash64(b"farmhash test");
assert_ne!(h, 0);
}
#[test]
fn test_farmhash32() {
let hasher = FarmHasher::new(0);
let h = hasher.hash32(b"farmhash 32");
assert_ne!(h, 0);
}
#[test]
fn test_spookyhash() {
let mut hasher = SpookyHasher::new(0xDEADBEEF, 0xCAFEBABE);
hasher.update(b"spooky hash test data");
let (h0, h1) = hasher.finalize();
assert_ne!(h0, 0);
assert_ne!(h1, 0);
}
#[test]
fn test_highwayhash64() {
let key = [1u64, 2, 3, 4];
let hasher = HighwayHasher::new(key);
let h = hasher.hash64(b"highway hash test");
assert_ne!(h, 0);
assert_eq!(h, hasher.hash64(b"highway hash test"));
}
#[test]
fn test_aes_cfb_encrypt_decrypt() {
let key = b"0123456789abcdef";
let iv: [u8; 16] = *b"abcdefghijklmnop";
let rk = AesRoundKeys::expand(key, AesKeySize::Aes128);
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let plaintext = b"CFB mode test message padding!";
let ct = crypto.aes_cfb_encrypt(plaintext, &iv, &rk);
let pt = crypto.aes_cfb_decrypt(&ct, &iv, &rk);
assert_eq!(&pt[..plaintext.len()], plaintext);
}
#[test]
fn test_aes_ofb_process() {
let key = b"0123456789abcdef";
let iv: [u8; 16] = *b"abcdefghijklmnop";
let rk = AesRoundKeys::expand(key, AesKeySize::Aes128);
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let data = b"OFB mode stream cipher test";
let ct = crypto.aes_ofb_process(data, &iv, &rk);
let pt = crypto.aes_ofb_process(&ct, &iv, &rk);
assert_eq!(&pt[..data.len()], data);
}
#[test]
fn test_aes_key_wrap_unwrap() {
let kek = b"0123456789abcdef0123456789abcdef";
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let key_data = b"0123456789abcdef0123456789abcdef"; let wrapped = crypto.aes_key_wrap(kek, AesKeySize::Aes256, key_data);
assert_eq!(wrapped.len(), key_data.len() + 8);
let unwrapped = crypto.aes_key_unwrap(kek, AesKeySize::Aes256, &wrapped);
assert!(unwrapped.is_ok());
assert_eq!(&unwrapped.unwrap()[..], key_data);
}
#[test]
fn test_aes_key_unwrap_bad_integrity() {
let kek = b"0123456789abcdef0123456789abcdef";
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let mut bad_wrapped = vec![0u8; 24];
bad_wrapped[..8].copy_from_slice(&0xDEADBEEF_u64.to_be_bytes());
let result = crypto.aes_key_unwrap(kek, AesKeySize::Aes256, &bad_wrapped);
assert!(result.is_err());
}
#[test]
fn test_avx512_set1_epi8() {
let val = Avx512MemOps::set1_epi8(0xAB);
assert_eq!(val, [0xABu8; 64]);
}
#[test]
fn test_avx512_set1_epi32() {
let val = Avx512MemOps::set1_epi32(42);
assert_eq!(val, [42u32; 16]);
}
#[test]
fn test_avx512_mask_loadu_epi8() {
let src = [0xFFu8; 64];
let default = [0x00u8; 64];
let result = Avx512MemOps::mask_loadu_epi8(&src, 0x0F, &default);
assert_eq!(result[0], 0xFF);
assert_eq!(result[1], 0xFF);
assert_eq!(result[2], 0xFF);
assert_eq!(result[3], 0xFF);
assert_eq!(result[4], 0x00);
}
#[test]
fn test_avx512_gather() {
let base: Vec<u32> = (0..100).collect();
let indices = [
0u32, 10, 20, 30, 40, 50, 60, 70, 80, 90, 5, 15, 25, 35, 45, 55,
];
let result = Avx512MemOps::i32gather_epi32(&base, &indices, 1);
assert_eq!(result[0], 0);
assert_eq!(result[1], 10);
assert_eq!(result[15], 55);
}
#[test]
fn test_numa_discover() {
let mut alloc = NumaAllocator::new();
assert!(alloc.discover().is_ok());
assert!(alloc.numa_available);
assert_eq!(alloc.numa_nodes.len(), 1);
}
#[test]
fn test_numa_alloc() {
let mut alloc = NumaAllocator::new();
alloc.discover().unwrap();
let buf = alloc.numa_alloc(4096, 0);
assert!(buf.is_some());
assert_eq!(buf.unwrap().len(), 4096);
}
#[test]
fn test_cache_prefetch_no_crash() {
let data = [0u8; 256];
CacheControl::prefetch(data.as_ptr(), PrefetchHint::T0);
CacheControl::prefetch_range(data.as_ptr(), 256, PrefetchHint::NTA);
}
#[test]
fn test_cache_flush_no_crash() {
let data = [0u8; 128];
CacheControl::clflush(data.as_ptr());
CacheControl::clflushopt_range(data.as_ptr(), 128);
CacheControl::clwb_range(data.as_ptr(), 128);
}
#[test]
fn test_tsx_context_new() {
let tsx = TsxContext::new();
assert!(!tsx.has_rtm());
assert_eq!(tsx.max_retries, 5);
}
#[test]
fn test_tsx_fallback_execute() {
let mut tsx = TsxContext::new();
let result = tsx.rtm_execute(|| 42);
assert!(result.is_err());
}
#[test]
fn test_memory_bandwidth_stream_copy() {
let bench = MemoryBandwidthBench::new();
let result = bench.stream_copy(1024 * 1024);
assert!(result.copy_bandwidth_gbps > 0.0);
}
#[test]
fn test_memory_bandwidth_latency() {
let bench = MemoryBandwidthBench::new();
let result = bench.memory_latency(64 * 1024 * 1024, 64);
assert!(result.latency_ns > 0.0);
}
#[test]
fn test_lz4_hc_compress() {
let mut accel = X86CompressionAccel::new(&X86AccelFeatureSet::detect());
let data = b"LZ4 HC high compression mode test with repeated patterns for better matches.";
let result = accel.lz4_hc_compress(data, 4);
assert!(result.success);
}
#[test]
fn test_adler32() {
let accel = X86CompressionAccel::new(&X86AccelFeatureSet::detect());
assert_eq!(accel.adler32(b"Wikipedia"), 0x11E60398);
assert_eq!(accel.adler32(b""), 0x00000001);
}
#[test]
fn test_quicksort_i32() {
let mut data = [5i32, 2, 8, 1, 9, 3, 7, 4, 6];
let expected = [1, 2, 3, 4, 5, 6, 7, 8, 9];
SimdSort::quicksort_i32(&mut data);
assert_eq!(data, expected);
}
#[test]
fn test_bitonic_merge() {
let mut data = [4i32, 3, 2, 1, 8, 7, 6, 5];
SimdSort::bitonic_merge_i32(&mut data);
let expected = [1, 2, 3, 4, 5, 6, 7, 8];
assert_eq!(data, expected);
}
#[test]
fn test_histogram_u8() {
let data = [1u8, 2, 1, 3, 2, 1];
let hist = SimdSort::histogram_u8(&data);
assert_eq!(hist[1], 3);
assert_eq!(hist[2], 2);
assert_eq!(hist[3], 1);
}
#[test]
fn test_popcount_slice() {
let data = [0xFFu8; 100];
let count = SimdSort::popcount_slice(&data);
assert_eq!(count, 800); }
#[test]
fn test_prefix_sum() {
let data = [1, 2, 3, 4, 5];
let prefix = SimdSort::prefix_sum_i32(&data);
assert_eq!(prefix, vec![1, 3, 6, 10, 15]);
}
#[test]
fn test_io_uring_register_buffers() {
let mut ring = IoUring::new(16);
ring.setup().unwrap();
let buf1 = [0u8; 4096];
let buf2 = [0u8; 4096];
assert!(ring.register_buffers(&[&buf1, &buf2]).is_ok());
}
#[test]
fn test_io_uring_sq_full() {
let mut ring = IoUring::new(2);
ring.setup().unwrap();
assert!(!ring.sq_full());
}
#[test]
fn test_io_uring_cq_empty() {
let mut ring = IoUring::new(8);
ring.setup().unwrap();
assert!(ring.cq_empty());
}
#[test]
fn test_io_uring_prep_timeout() {
let mut ring = IoUring::new(8);
ring.setup().unwrap();
assert!(ring
.prep_timeout(&Duration::from_millis(100), 0, 0, 1)
.is_some());
}
#[test]
fn test_io_uring_prep_cancel() {
let mut ring = IoUring::new(8);
ring.setup().unwrap();
assert!(ring.prep_cancel(42, 0, 99).is_some());
}
#[test]
fn test_spdk_nvmeof_target() {
let mut target =
SpdkNvmeofTarget::new("nqn.2024-01.com.example:disk0", "TCP", "192.168.1.1", 4420);
assert_eq!(target.nqn, "nqn.2024-01.com.example:disk0");
assert!(target.start().is_ok());
assert!(target.add_namespace("Malloc0", 1).is_ok());
assert_eq!(target.num_namespaces, 1);
target.stop();
assert!(!target.initialized);
}
#[test]
fn test_spdk_bdev_size() {
let bdev = SpdkBdev::new_malloc("test0", 512, 1000);
assert_eq!(bdev.size_bytes(), 512 * 1000);
}
#[test]
fn test_vpdpbusd_sat_no_overflow() {
let mut vnni = VnniAccelerator::new(true);
let a: [u8; 4] = [2, 3, 4, 5];
let b: [i8; 4] = [6, 7, 8, 9];
let mut c: [i32; 4] = [0; 4];
vnni.vpdpbusd_sat(&a, &b, &mut c);
assert_eq!(c[0], 12);
assert_eq!(c[3], 45);
}
#[test]
fn test_vpmadd52luq() {
let result = VnniAccelerator::vpmadd52luq(0xFFFFFFFFFFFFF, 2, 0);
assert!(result > 0);
}
#[test]
fn test_int8_gemm_small() {
let mut vnni = VnniAccelerator::new(true);
let a: [i8; 4] = [1, 2, 3, 4];
let b: [i8; 4] = [5, 6, 7, 8];
let mut c = [0i32; 4];
vnni.int8_gemm(&a, &b, &mut c, 2, 2, 2);
assert!(c.iter().any(|&x| x != 0));
}
#[test]
fn test_bf16_slice_conversion() {
let src = [1.0f32, 2.0, 3.0];
let bf16_src = Bf16Accelerator::f32_slice_to_bf16(&src);
let back = Bf16Accelerator::bf16_slice_to_f32(&bf16_src);
assert_eq!(back[0], 1.0);
assert_eq!(back[1], 2.0);
}
#[test]
fn test_bf16_dot() {
let bf = Bf16Accelerator::new(true);
let a = [
Bf16Accelerator::f32_to_bf16(1.0),
Bf16Accelerator::f32_to_bf16(2.0),
Bf16Accelerator::f32_to_bf16(3.0),
];
let b = [
Bf16Accelerator::f32_to_bf16(4.0),
Bf16Accelerator::f32_to_bf16(5.0),
Bf16Accelerator::f32_to_bf16(6.0),
];
let dot = bf.bf16_dot(&a, &b);
assert!((dot - 32.0).abs() < 0.01); }
#[test]
fn test_f16_l2_norm() {
let fp16 = Fp16Accelerator::new(true);
let v = [
Fp16Accelerator::f32_to_f16(3.0),
Fp16Accelerator::f32_to_f16(4.0),
];
let norm = fp16.f16_l2_norm(&v);
assert!((norm - 5.0).abs() < 0.01);
}
#[test]
fn test_f16_relu() {
let fp16 = Fp16Accelerator::new(true);
let input = [
Fp16Accelerator::f32_to_f16(-1.0),
Fp16Accelerator::f32_to_f16(0.0),
Fp16Accelerator::f32_to_f16(2.5),
];
let output = fp16.f16_relu(&input);
assert_eq!(Fp16Accelerator::f16_to_f32(output[0]), 0.0);
assert_eq!(Fp16Accelerator::f16_to_f32(output[1]), 0.0);
assert!((Fp16Accelerator::f16_to_f32(output[2]) - 2.5).abs() < 0.1);
}
#[test]
fn test_f16_gelu() {
let fp16 = Fp16Accelerator::new(true);
let input = [Fp16Accelerator::f32_to_f16(1.0)];
let output = fp16.f16_gelu(&input);
let val = Fp16Accelerator::f16_to_f32(output[0]);
assert!(val > 0.0);
assert!(val < 1.5);
}
#[test]
fn test_f16_silu() {
let fp16 = Fp16Accelerator::new(true);
let input = [Fp16Accelerator::f32_to_f16(1.0)];
let output = fp16.f16_silu(&input);
let val = Fp16Accelerator::f16_to_f32(output[0]);
assert!(val > 0.5);
assert!(val < 1.0);
}
#[test]
fn test_amx_tilezero() {
let mut amx = AmxAccelerator::new(true);
assert!(amx.tilezero(0).is_ok());
assert_eq!(amx.ops(), 1);
}
#[test]
fn test_amx_tileloadd() {
let mut amx = AmxAccelerator::new(true);
amx.tileconfig(0, 16, 64).unwrap();
assert!(amx.tileloadd(0, 0x1000, 64).is_ok());
}
#[test]
fn test_amx_tilestored() {
let mut amx = AmxAccelerator::new(true);
amx.tileconfig(0, 8, 32).unwrap();
assert!(amx.tilestored(0, 0x2000, 32).is_ok());
}
#[test]
fn test_amx_complex() {
let mut amx = AmxAccelerator::new(true);
amx.tileconfig(0, 16, 64).unwrap();
amx.tileconfig(1, 16, 64).unwrap();
amx.tileconfig(2, 16, 64).unwrap();
assert!(amx.tcmmrlfp16ps(2, 0, 1).is_ok());
assert!(amx.tcmmilfp16ps(2, 0, 1).is_ok());
}
#[test]
fn test_amx_pack_tile_a() {
let amx = AmxAccelerator::new(true);
let data: Vec<i8> = (0..16).collect();
let packed = amx.pack_tile_a(&data, 4, 4);
assert!(packed.len() >= 16);
}
#[test]
fn test_amx_pack_tile_b() {
let amx = AmxAccelerator::new(true);
let data: Vec<i8> = (0..16).collect();
let packed = amx.pack_tile_b(&data, 4, 4);
assert!(packed.len() >= 16);
}
#[test]
fn test_dpdk_mbuf_creation() {
let mbuf = DpdkMbuf {
data: vec![0u8, 1, 2, 3],
data_len: 4,
pkt_len: 4,
port: 0,
ol_flags: 0,
};
assert_eq!(mbuf.data_len, 4);
assert_eq!(mbuf.pkt_len, 4);
}
#[test]
fn test_crc32c_bulk() {
let accel = X86CompressionAccel::new(&X86AccelFeatureSet::detect());
let results = accel.crc32c_bulk(&[b"abc", b"def", b"123"]);
assert_eq!(results.len(), 3);
}
#[test]
fn test_deflate_fixed_huffman() {
let mut accel = X86CompressionAccel::new(&X86AccelFeatureSet::detect());
let result = accel.deflate_fixed_huffman(b"test fixed huffman");
assert!(result.success);
}
#[test]
fn test_rdma_alloc_mw() {
let mut ctx = RdmaContext::new(RdmaTransport::RoCEv2);
ctx.init("mlx5_0").unwrap();
let pd = ctx.protection_domain.as_ref().unwrap();
let mw = ctx.alloc_memory_window(pd);
assert!(mw.is_ok());
assert_eq!(mw.unwrap().rkey, 200);
}
#[test]
fn test_rdma_post_send_recv() {
let mut ctx = RdmaContext::new(RdmaTransport::RoCEv2);
ctx.init("mlx5_0").unwrap();
ctx.register_memory_region(0x1000, 4096).unwrap();
let qp = ctx.create_qp(64).unwrap();
assert!(ctx.post_send(qp.qp_num, 0x1000, 64, 1).is_ok());
assert!(ctx.post_recv(qp.qp_num, 0x1000, 64, 1).is_ok());
}
#[test]
fn test_dsa_opcodes_distinct() {
assert_ne!(DsaOpcode::MemMove as u8, DsaOpcode::MemFill as u8);
assert_ne!(DsaOpcode::Crc32c as u8, DsaOpcode::CopyCrc as u8);
assert_ne!(DsaOpcode::Compare as u8, DsaOpcode::ComparePattern as u8);
assert_ne!(DsaOpcode::CreateDelta as u8, DsaOpcode::ApplyDelta as u8);
}
#[test]
fn test_extended_features_enable() {
let mut fs = X86AccelFeatureSet::new();
fs.set(X86AccelFeature::Movdiri, true);
fs.set(X86AccelFeature::Movdir64b, true);
fs.set(X86AccelFeature::Enqcmd, true);
fs.set(X86AccelFeature::Clwb, true);
fs.set(X86AccelFeature::Clflushopt, true);
fs.set(X86AccelFeature::Htt, true);
fs.set(X86AccelFeature::NonTemporalSse, true);
assert!(fs.has(X86AccelFeature::Movdiri));
assert!(fs.has(X86AccelFeature::Enqcmd));
}
fn hex_encode(data: &[u8]) -> String {
data.iter().map(|b| format!("{:02x}", b)).collect()
}
#[test]
fn test_aes_large_data_ecb() {
let key = b"0123456789abcdef";
let rk = AesRoundKeys::expand(key, AesKeySize::Aes128);
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let data = vec![0xAAu8; 1024];
let ct = crypto.aes_ecb_encrypt(&data, &rk);
let pt = crypto.aes_ecb_decrypt(&ct, &rk);
assert_eq!(&pt[..data.len()], &data[..]);
}
#[test]
fn test_hash_determinism_across_impls() {
let simd = X86SIMDAccel::new(&X86AccelFeatureSet::detect());
let data = b"consistency check";
let fnv32 = simd.fnv1a_32(data);
let fnv64 = simd.fnv1a_64(data);
let murmur = simd.murmur3_32(data, 42);
let xx32 = simd.xxhash32(data, 0);
let xx64 = simd.xxhash64(data, 0);
assert_eq!(fnv32, simd.fnv1a_32(data));
assert_eq!(fnv64, simd.fnv1a_64(data));
assert_eq!(murmur, simd.murmur3_32(data, 42));
assert_eq!(xx32, simd.xxhash32(data, 0));
assert_eq!(xx64, simd.xxhash64(data, 0));
}
#[test]
fn test_simd_cross_validation_city_farm() {
let data = b"cross-validation data set for hash functions";
let city = CityHasher::new(0).hash64(data);
let farm = FarmHasher::new(0).hash64(data);
assert_ne!(city, 0);
assert_ne!(farm, 0);
}
#[test]
fn test_spooky_hash_determinism() {
let mut h1 = SpookyHasher::new(1, 2);
h1.update(b"deterministic");
let (a1, b1) = h1.finalize();
let mut h2 = SpookyHasher::new(1, 2);
h2.update(b"deterministic");
let (a2, b2) = h2.finalize();
assert_eq!((a1, b1), (a2, b2));
}
#[test]
fn test_highway_hash_different_keys() {
let key1 = [1u64, 2, 3, 4];
let key2 = [5u64, 6, 7, 8];
let h1 = HighwayHasher::new(key1).hash64(b"test");
let h2 = HighwayHasher::new(key2).hash64(b"test");
assert_ne!(h1, h2);
}
#[test]
fn test_memory_region_overlap_edge_cases() {
assert!(!X86SIMDExtOps::memory_regions_overlap(
std::ptr::null(),
0,
std::ptr::null(),
0
));
let arr = [0u8; 100];
assert!(!X86SIMDExtOps::memory_regions_overlap(
arr.as_ptr(),
0,
arr.as_ptr(),
100
));
assert!(X86SIMDExtOps::memory_regions_overlap(
arr.as_ptr(),
100,
unsafe { arr.as_ptr().add(99) },
2
));
assert!(!X86SIMDExtOps::memory_regions_overlap(
arr.as_ptr(),
50,
unsafe { arr.as_ptr().add(50) },
50
));
}
#[test]
fn test_avx512_gather_out_of_bounds() {
let base = [1u32, 2, 3];
let indices = [0u32, 100, 1, 200, 2, 300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let result = Avx512MemOps::i32gather_epi32(&base, &indices, 1);
assert_eq!(result[0], 1);
assert_eq!(result[1], 0); assert_eq!(result[2], 2);
assert_eq!(result[4], 3);
}
#[test]
fn test_fp16_extreme_values() {
let fp16 = Fp16Accelerator::new(true);
assert_eq!(
Fp16Accelerator::f16_to_f32(Fp16Accelerator::f32_to_f16(0.0)),
0.0
);
let small = Fp16Accelerator::f32_to_f16(1e-8);
let back_small = Fp16Accelerator::f16_to_f32(small);
assert!(back_small >= 0.0);
let big = Fp16Accelerator::f32_to_f16(65504.0);
let back_big = Fp16Accelerator::f16_to_f32(big);
assert!((back_big - 65504.0).abs() < 1.0);
let nan = Fp16Accelerator::f32_to_f16(f32::NAN);
assert!(u16::from(nan) & 0x7C00 == 0x7C00);
}
#[test]
fn test_bf16_extreme_values() {
let zero: u16 = Bf16Accelerator::f32_to_bf16(0.0);
assert_eq!(Bf16Accelerator::bf16_to_f32(zero), 0.0);
let neg = Bf16Accelerator::f32_to_bf16(-42.0);
assert_eq!(Bf16Accelerator::bf16_to_f32(neg), -42.0);
}
#[test]
fn test_amx_full_workflow() {
let mut amx = AmxAccelerator::new(true);
amx.tileconfig(0, 16, 64).unwrap();
amx.tileconfig(1, 16, 64).unwrap();
amx.tileconfig(2, 16, 64).unwrap();
amx.tileloadd(0, 0x1000, 64).unwrap();
amx.tileloadd(1, 0x2000, 64).unwrap();
amx.tdpbssd(2, 0, 1).unwrap();
amx.tilestored(2, 0x3000, 64).unwrap();
amx.tilerelease();
assert_eq!(amx.tile_config[0], (0, 0));
}
#[test]
fn test_crypto_accel_ops_counter() {
let mut crypto = X86CryptoAccel::new(&X86AccelFeatureSet::detect());
let key = b"0123456789abcdef";
let rk = AesRoundKeys::expand(key, AesKeySize::Aes128);
for _ in 0..100 {
crypto.aes_encrypt_block(b"Hello, World!!!!", &rk);
}
assert_eq!(crypto.stats().aes_ops, 100);
for _ in 0..50 {
crypto.sha256_hash(b"data");
}
assert_eq!(crypto.stats().sha_ops, 50);
}
#[test]
fn test_compression_ratio_calculation_empty() {
let result = CompressionResult {
data: vec![],
success: true,
ratio: None,
elapsed: Duration::ZERO,
};
assert!(result.ratio.is_none());
}
#[test]
fn test_compression_ratio_calculation() {
let result = CompressionResult {
data: vec![0u8; 10],
success: true,
ratio: Some(0.5),
elapsed: Duration::from_millis(1),
};
assert!((result.ratio.unwrap() - 0.5).abs() < 0.001);
}
}