philbin 1.0.1

A pure Rust AEGIS library with SIMD and runtime CPU detection
Documentation
#![cfg(target_arch = "x86_64")]

use crate::arch::{HasAvx2, Simd, with_simd};
use crate::base::block::{
  Array128, Block, gen_shared_block256, gen_shared_block512,
};
use std::arch::x86_64::*;
use std::ops::{BitAnd, BitXor};
use zerocopy::transmute;

/// A 256-bit SIMD vector using AVX2.
#[derive(Clone, Copy)]
pub struct Avx1x256<S: Simd> {
  val: __m256i,
  simd: S,
}

impl<S: Simd> Block for Avx1x256<S>
where
  S: HasAvx2,
{
  gen_shared_block256!(S);

  fn xor_down(self) -> Array128 {
    with_simd! {
      fn helper(
        token: impl HasAvx2,
        input: __m256i,
      ) -> Array128 {
        // NOTE: _mm256_castsi256_si128(x) is identical to, yet cheaper than,
        // _mm256_extracti128_si256(x, 0).
        let low = _mm256_castsi256_si128(input);
        let high = _mm256_extracti128_si256(input, 1);
        let out = _mm_xor_si128(low, high);
        transmute!(out)
      }
    }

    helper(self.simd, self.val)
  }

  fn aes_encrypt_round(state: Self, round_key: Self) -> Self {
    with_simd! {
      fn helper(
        token: impl HasAvx2,
        state: __m256i,
        round_key: __m256i
      ) -> __m256i {
        _mm256_aesenc_epi128(state, round_key)
      }
    }

    Self {
      val: helper(state.simd, state.val, round_key.val),
      simd: state.simd,
    }
  }
}

impl<S: Simd> BitAnd for Avx1x256<S>
where
  S: HasAvx2,
{
  type Output = Avx1x256<S>;

  #[inline(always)]
  fn bitand(self, rhs: Self) -> Self::Output {
    with_simd! {
      fn helper(
        token: impl HasAvx2,
        first: __m256i,
        second: __m256i,
      ) -> __m256i {
        _mm256_and_si256(first, second)
      }
    }

    Self {
      val: helper(self.simd, self.val, rhs.val),
      simd: self.simd,
    }
  }
}

impl<S: Simd> BitXor for Avx1x256<S>
where
  S: HasAvx2,
{
  type Output = Avx1x256<S>;

  #[inline(always)]
  fn bitxor(self, rhs: Self) -> Self::Output {
    with_simd! {
      fn helper(
        token: impl HasAvx2,
        first: __m256i,
        second: __m256i,
      ) -> __m256i {
        _mm256_xor_si256(first, second)
      }
    }

    Self {
      val: helper(self.simd, self.val, rhs.val),
      simd: self.simd,
    }
  }
}

/// A 512-bit SIMD vector using AVX2.
#[derive(Clone, Copy)]
pub struct Avx2x256<S: Simd> {
  val: [__m256i; 2],
  simd: S,
}

impl<S: Simd> Block for Avx2x256<S>
where
  S: HasAvx2,
{
  gen_shared_block512!(S);

  fn xor_down(self) -> Array128 {
    with_simd! {
      fn helper(
        token: impl HasAvx2,
        input: [__m256i; 2]
      ) -> Array128 {
        // 512 bit vec can be seen as 4x128: (a, b, c, d)
        //
        // (a || b) ^ (c || d) -> (a ^ c) || (b ^ d)
        let temp = _mm256_xor_si256(input[0], input[1]);

        // (a ^ c) ^ (b ^ d) == a ^ b ^ c ^ d

        // NOTE: _mm256_castsi256_si128(x) is identical to, yet cheaper than,
        // _mm256_extracti128_si256(x, 0).
        let low = _mm256_castsi256_si128(temp);
        let high = _mm256_extracti128_si256(temp, 1);
        let out = _mm_xor_si128(low, high);
        transmute!(out)
      }
    }

    helper(self.simd, self.val)
  }

  fn aes_encrypt_round(state: Self, round_key: Self) -> Self {
    with_simd! {
      fn helper(
        token: impl HasAvx2,
        state: [__m256i; 2],
        round_key: [__m256i; 2],
      ) -> [__m256i; 2] {
        [
          _mm256_aesenc_epi128(state[0], round_key[0]),
          _mm256_aesenc_epi128(state[1], round_key[1]),
        ]
      }
    }

    Self {
      val: helper(state.simd, state.val, round_key.val),
      simd: state.simd,
    }
  }
}

impl<S: Simd> BitAnd for Avx2x256<S>
where
  S: HasAvx2,
{
  type Output = Avx2x256<S>;

  #[inline(always)]
  fn bitand(self, rhs: Self) -> Self::Output {
    with_simd! {
      fn helper(
        token: impl HasAvx2,
        first: [__m256i; 2],
        second: [__m256i; 2],
      ) -> [__m256i; 2] {
        [
          _mm256_and_si256(first[0], second[0]),
          _mm256_and_si256(first[1], second[1]),
        ]
      }
    }

    Self {
      val: helper(self.simd, self.val, rhs.val),
      simd: self.simd,
    }
  }
}

impl<S: Simd> BitXor for Avx2x256<S>
where
  S: HasAvx2,
{
  type Output = Avx2x256<S>;

  #[inline(always)]
  fn bitxor(self, rhs: Self) -> Self::Output {
    with_simd! {
      fn helper(
        token: impl HasAvx2,
        first: [__m256i; 2],
        second: [__m256i; 2],
      ) -> [__m256i; 2] {
        [
          _mm256_xor_si256(first[0], second[0]),
          _mm256_xor_si256(first[1], second[1]),
        ]
      }
    }

    Self {
      val: helper(self.simd, self.val, rhs.val),
      simd: self.simd,
    }
  }
}