//! Utilities

// TODO: migrate to our owned implementation?
#[cfg(all(
    feature = "runtime-cpu-detection",
    any(
        target_arch = "x86",
        target_arch = "x86_64",
        target_arch = "aarch64",
        target_arch = "loongarch64"
    )
))]
#[allow(clippy::single_component_path_imports, reason = "XXX")]
pub(crate) use cpufeatures;

#[cfg(not(all(
    feature = "runtime-cpu-detection",
    any(
        target_arch = "x86",
        target_arch = "x86_64",
        target_arch = "aarch64",
        target_arch = "loongarch64"
    )
)))]
pub(crate) mod cpufeatures {
    #[allow(unused_macros, reason = "XXX")]
    macro_rules! new {
        ($name:ident, $($tt:tt)+) => {
            mod $name {
                #[inline]
                pub(crate) const fn get() -> bool {
                    false
                }
            }
        };
    }

    #[allow(unused_imports, reason = "XXX")]
    pub(crate) use new;
}

/// The lowercase hexadecimal chars: `"0123456789abcdef"`.
pub const HEX_CHARS_LOWER: &[u8; 16] = b"0123456789abcdef";

/// The uppercase hexadecimal chars: `"0123456789ABCDEF"`.
pub const HEX_CHARS_UPPER: &[u8; 16] = b"0123456789ABCDEF";

// TODO: move to a dedicate crate?
macro_rules! constcat {
    ([]) => {{ &[] }};
    (&[]) => {{ &[] }};
    ($($e:expr),+ $(,)?) => {{
        $(
            // We need `&[u8]` expressions here.
            const _: &[u8] = $e;
        )*

        const LEN: usize = $( $e.len() + )* 0;

        const ARR: [u8; LEN] = {
            let mut arr: [::core::mem::MaybeUninit<u8>; LEN] = [::core::mem::MaybeUninit::uninit(); LEN];

            let mut offset = 0;

            $(
                let src = $e;
                #[allow(unsafe_code, reason = "XXX")]
                // SAFETY: this is const indexing (not stable)
                let dst = unsafe { arr.as_mut_ptr().add(offset).cast() };

                #[allow(unsafe_code, reason = "XXX")]
                unsafe {
                    // const: 1.83.0
                    ::core::ptr::copy_nonoverlapping(src.as_ptr(), dst, src.len());
                }

                #[allow(unused_assignments, reason = "XXX")]
                {
                    offset += src.len();
                }
            )+

            #[allow(unsafe_code, reason = "XXX")]
            // SAFETY:
            // As per the documentation of `core::mem::MaybeUninit`:
            // <https://doc.rust-lang.org/core/mem/union.MaybeUninit.html#layout-1>
            //
            // MaybeUninit<T> is guaranteed to have the same size, alignment,
            // and ABI as T.
            //
            // This means as long as all of the MaybeUninits are initialized
            // then it is safe to transmute a MaybeUninit<T> to T, and therefore
            // also [MaybeUninit<T>; N] to [T; N]. We know that all of the
            // elements are initialized because in the function call above the
            // number of initialized elements are computed and then there is a
            // guard that compares that to the total length of the array.
            //
            // See for more information:
            // https://doc.rust-lang.org/core/mem/union.MaybeUninit.html#initializing-an-array-element-by-element
            unsafe { ::core::mem::transmute(arr) }
        };

        &ARR
    }};
}

#[inline]
pub(crate) const fn lut16<const UPPER: bool>() -> &'static [u8; 16] {
    if UPPER {
        HEX_CHARS_UPPER
    } else {
        HEX_CHARS_LOWER
    }
}

#[allow(unused, reason = "SIMD256")]
#[inline]
pub(crate) const fn lut32<const UPPER: bool>() -> &'static [u8; 32] {
    if UPPER {
        constcat!(HEX_CHARS_UPPER, HEX_CHARS_UPPER)
    } else {
        constcat!(HEX_CHARS_LOWER, HEX_CHARS_LOWER)
    }
}

#[allow(unused, reason = "SIMD512")]
#[inline]
pub(crate) const fn lut64<const UPPER: bool>() -> &'static [u8; 64] {
    if UPPER {
        constcat!(
            HEX_CHARS_UPPER,
            HEX_CHARS_UPPER,
            HEX_CHARS_UPPER,
            HEX_CHARS_UPPER
        )
    } else {
        constcat!(
            HEX_CHARS_LOWER,
            HEX_CHARS_LOWER,
            HEX_CHARS_LOWER,
            HEX_CHARS_LOWER
        )
    }
}
