scrypt-opt 0.3.2

A pure-rust optimized scrypt implementation for moderate to high difficulty cases, with AVX2 and AVX512 intrinsics cores and a portable-simd core.
Documentation
macro_rules! block_mix {
    (<$r:ty>; [<$s:ty> $input:expr => $output:expr]) => {
        #[allow(unused_unsafe)]
        {
            use crate::{
                ScryptBlockMixInput, ScryptBlockMixOutput,
                salsa20::{BlockType, Salsa20},
            };

            let mut x: <$s as Salsa20>::Block = unsafe { $input.load(<$r>::USIZE * 2 - 1) };

            macro_rules! iteration {
                ($i:expr) => {{
                    x.xor_with(unsafe { $input.load(2 * $i) });

                    let mut b0 = <$s>::read(GenericArray::from_array([&x]));
                    b0.keystream::<4>();
                    b0.write(GenericArray::from_array([&mut x]));

                    $output.store_even($i, x);

                    x.xor_with(unsafe { $input.load(2 * $i + 1) });

                    let mut b1 = <$s>::read(GenericArray::from_array([&x]));
                    b1.keystream::<4>();
                    b1.write(GenericArray::from_array([&mut x]));

                    $output.store_odd($i, x);
                }};
            }

            if <$r>::USIZE <= crate::fixed_r::MAX_R_FOR_UNROLLING {
                repeat8!(i, {
                    if i < <$r>::USIZE {
                        iteration!(i);
                    }
                });

                let _ = x;
            } else {
                for i in 0..<$r>::USIZE {
                    iteration!(i);
                }
            }
        }
    };
    (<$r:ty>; [
        <$s0:ty> $input0:expr => $output0:expr,
        <$s1:ty> $input1:expr => $output1:expr$(,)?
    ]) => {
        #[allow(unused_unsafe)]
        {
            let mut x0: <$s0 as Salsa20>::Block = unsafe { $input0.load(<$r>::USIZE * 2 - 1) };
            let mut x1: <$s1 as Salsa20>::Block = unsafe { $input1.load(<$r>::USIZE * 2 - 1) };

            macro_rules! iteration {
                ($i:expr) => {{
                    x0.xor_with(unsafe { $input0.load(2 * $i) });
                    x1.xor_with(unsafe { $input1.load(2 * $i) });

                    let mut b0 = <$s0>::read(GenericArray::from_array([&x0, &x1]));
                    b0.keystream::<4>();
                    b0.write(GenericArray::from_array([&mut x0, &mut x1]));

                    $output0.store_even($i, x0);
                    $output1.store_even($i, x1);

                    x0.xor_with(unsafe { $input0.load(2 * $i + 1) });
                    x1.xor_with(unsafe { $input1.load(2 * $i + 1) });

                    let mut b0 = <$s0>::read(GenericArray::from_array([&x0, &x1]));
                    b0.keystream::<4>();
                    b0.write(GenericArray::from_array([&mut x0, &mut x1]));

                    $output0.store_odd($i, x0);
                    $output1.store_odd($i, x1);
                }};
            }

            if <$r>::USIZE <= crate::fixed_r::MAX_R_FOR_UNROLLING {
                repeat8!(i, {
                    if i < <$r>::USIZE {
                        iteration!(i);
                    }
                });

                let _ = x0;
                let _ = x1;
            } else {
                for i in 0..<$r>::USIZE {
                    iteration!(i);
                }
            }
        }
    };
    ($r:expr; [<$s:ty> $input:expr => $output:expr]) => {{
        let output: &mut [Align64<crate::fixed_r::Block<U1>>] = $output;
        let mut x: <$s as Salsa20>::Block = unsafe { $input.load($r * 2 - 1) };

        for i in 0..$r {
            x.xor_with(unsafe { $input.load(2 * i) });
            let mut b = <$s>::read(GenericArray::from_array([&x]));
            b.keystream::<4>();
            b.write(GenericArray::from_array([&mut x]));

            unsafe {
                x.write_to_ptr(output.as_mut_ptr().cast::<[u8; 64]>().add(i).cast());
            }

            x.xor_with(unsafe { $input.load(2 * i + 1) });
            let mut b = S::read(GenericArray::from_array([&x]));
            b.keystream::<4>();
            b.write(GenericArray::from_array([&mut x]));

            unsafe {
                x.write_to_ptr(output.as_mut_ptr().cast::<[u8; 64]>().add(i + $r).cast());
            }
        }
    }};
    ($r:expr; [
        <$s0:ty> $input0:expr => $output0:expr,
        <$s1:ty> $input1:expr => $output1:expr$(,)?
    ]) => {{
        let output0: &mut [Align64<crate::fixed_r::Block<U1>>] = $output0;
        let output1: &mut [Align64<crate::fixed_r::Block<U1>>] = $output1;
        debug_assert_eq!(output0.len(), output1.len());

        let mut x0: <$s0 as Salsa20>::Block = unsafe { $input0.load($r * 2 - 1) };
        let mut x1: <$s1 as Salsa20>::Block = unsafe { $input1.load($r * 2 - 1) };

        for i in 0..$r {
            x0.xor_with(unsafe { $input0.load(2 * i) });
            x1.xor_with(unsafe { $input1.load(2 * i) });
            let mut b0 = <$s0>::read(GenericArray::from_array([&x0, &x1]));
            b0.keystream::<4>();
            b0.write(GenericArray::from_array([&mut x0, &mut x1]));

            unsafe {
                x0.write_to_ptr(output0.as_mut_ptr().cast::<[u8; 64]>().add(i).cast());
                x1.write_to_ptr(output1.as_mut_ptr().cast::<[u8; 64]>().add(i).cast());
            }

            x0.xor_with(unsafe { $input0.load(2 * i + 1) });
            x1.xor_with(unsafe { $input1.load(2 * i + 1) });
            let mut b0 = <$s0>::read(GenericArray::from_array([&x0, &x1]));
            b0.keystream::<4>();
            b0.write(GenericArray::from_array([&mut x0, &mut x1]));

            unsafe {
                x0.write_to_ptr(output0.as_mut_ptr().cast::<[u8; 64]>().add(i + $r).cast());
                x1.write_to_ptr(output1.as_mut_ptr().cast::<[u8; 64]>().add(i + $r).cast());
            }
        }
    }};
}