#![no_std]
#[cfg(feature = "std")]
extern crate std;
mod deinterleave;
mod interleave;
mod mask;
pub use deinterleave::Deinterleave;
pub use interleave::Interleave;
#[inline]
pub fn index_of<I, const N: usize>(array: [I; N]) -> <I as Interleave<N>>::Output
where
I: Interleave<N>,
{
util::generic_index_of(array, Interleave::interleave)
}
#[inline]
pub fn coord_of<I, const N: usize>(index: I) -> [<I as Deinterleave<N>>::Output; N]
where
I: Deinterleave<N> + Copy,
{
util::generic_coord_of(index, Deinterleave::deinterleave)
}
pub mod bmi2 {
pub use crate::{deinterleave::DeinterleaveBMI2, interleave::InterleaveBMI2};
use crate::{Deinterleave, Interleave};
pub fn has_hardware_support() -> bool {
#[cfg(all(target_arch = "x86_64", feature = "std"))]
{
std::is_x86_feature_detected!("bmi2")
}
#[cfg(not(all(target_arch = "x86_64", feature = "std")))]
{
false
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HardwareSupportToken {
_private: (),
}
impl HardwareSupportToken {
pub fn new() -> Option<Self> {
has_hardware_support().then_some(Self { _private: () })
}
}
#[inline]
pub fn index_of<I, const N: usize>(
array: [I; N],
_support_token: HardwareSupportToken,
) -> <I as Interleave<N>>::Output
where
I: InterleaveBMI2<N>,
{
#[cfg(target_arch = "x86_64")]
unsafe {
index_of_unchecked(array)
}
#[cfg(not(target_arch = "x86_64"))]
{
let _ = array;
unreachable!("HardwareSupportToken cannot be created on non-x86_64 platforms")
}
}
#[inline]
#[target_feature(enable = "bmi2")]
#[cfg(target_arch = "x86_64")]
pub unsafe fn index_of_unchecked<I, const N: usize>(
array: [I; N],
) -> <I as Interleave<N>>::Output
where
I: InterleaveBMI2<N>,
{
crate::util::generic_index_of(array, |idx| idx.interleave_bmi2())
}
#[inline]
pub fn coord_of<I, const N: usize>(
index: I,
_support_token: HardwareSupportToken,
) -> [<I as Deinterleave<N>>::Output; N]
where
I: DeinterleaveBMI2<N> + Copy,
{
#[cfg(target_arch = "x86_64")]
unsafe {
coord_of_unchecked(index)
}
#[cfg(not(target_arch = "x86_64"))]
{
let _ = index;
unreachable!("HardwareSupportToken cannot be created on non-x86_64 platforms")
}
}
#[inline]
#[target_feature(enable = "bmi2")]
#[cfg(target_arch = "x86_64")]
pub unsafe fn coord_of_unchecked<I, const N: usize>(
index: I,
) -> [<I as Deinterleave<N>>::Output; N]
where
I: DeinterleaveBMI2<N> + Copy,
{
crate::util::generic_coord_of(index, |idx, i| idx.deinterleave_bmi2(i))
}
}
mod util {
use crate::{Deinterleave, Interleave};
use num_traits::Zero;
#[inline]
pub(super) fn generic_index_of<I, const N: usize>(
array: [I; N],
interleave: impl Fn(I) -> <I as Interleave<N>>::Output,
) -> <I as Interleave<N>>::Output
where
I: Interleave<N>,
{
array.into_iter().map(interleave).enumerate().fold(
<I as Interleave<N>>::Output::zero(),
|acc, (i, interleaved)| acc | (interleaved << i),
)
}
#[inline]
pub(super) fn generic_coord_of<I, const N: usize>(
index: I,
deinterleave: impl Fn(I, usize) -> <I as Deinterleave<N>>::Output,
) -> [<I as Deinterleave<N>>::Output; N]
where
I: Deinterleave<N> + Copy,
{
core::array::from_fn(|i| deinterleave(index, i))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn index_and_back_16() {
for i in 0..10_000u16 {
let array: [_; 2] = coord_of(i);
assert_eq!(index_of(array), i);
}
}
#[test]
fn index_and_back_32() {
for i in 0..10_000u32 {
let array: [_; 2] = coord_of(i);
assert_eq!(index_of(array), i);
}
for i in 0..10_000u32 {
let array: [_; 4] = coord_of(i);
assert_eq!(index_of(array), i);
}
}
#[test]
fn index_and_back_64() {
for i in 0..10_000u64 {
let array: [_; 2] = coord_of(i);
assert_eq!(index_of(array), i);
}
for i in 0..10_000u64 {
let array: [_; 3] = coord_of(i);
assert_eq!(index_of(array), i);
}
for i in 0..10_000u64 {
let array: [_; 5] = coord_of(i);
assert_eq!(index_of(array), i);
}
}
#[test]
fn index_and_back_128() {
for i in 0..10_000u128 {
let array: [_; 2] = coord_of(i);
assert_eq!(index_of(array), i);
}
for i in 0..10_000u128 {
let array: [_; 3] = coord_of(i);
assert_eq!(index_of(array), i);
}
for i in 0..10_000u128 {
let array: [_; 5] = coord_of(i);
assert_eq!(index_of(array), i);
}
for i in 0..10_000u128 {
let array: [_; 9] = coord_of(i);
assert_eq!(index_of(array), i);
}
}
}