//! Cache padding to cache line size, copied from crossbeam and cache pages
#[cfg_attr(
    any(
        target_arch = "x86_64",
        target_arch = "aarch64",
        target_arch = "arm64ec",
        target_arch = "powerpc64",
    ),
    repr(align(128))
)]
#[cfg_attr(
    any(
        target_arch = "arm",
        target_arch = "mips",
        target_arch = "mips32r6",
        target_arch = "mips64",
        target_arch = "mips64r6",
        target_arch = "sparc",
        target_arch = "hexagon",
    ),
    repr(align(32))
)]
#[cfg_attr(target_arch = "m68k", repr(align(16)))]
#[cfg_attr(target_arch = "s390x", repr(align(256)))]
#[cfg_attr(
    not(any(
        target_arch = "x86_64",
        target_arch = "aarch64",
        target_arch = "arm64ec",
        target_arch = "powerpc64",
        target_arch = "arm",
        target_arch = "mips",
        target_arch = "mips32r6",
        target_arch = "mips64",
        target_arch = "mips64r6",
        target_arch = "sparc",
        target_arch = "hexagon",
        target_arch = "m68k",
        target_arch = "s390x",
    )),
    repr(align(64))
)]
pub struct CachePadded<T>(pub T);

#[cfg(any(
    target_arch = "x86_64",
    target_arch = "aarch64",
    target_arch = "arm64ec",
    target_arch = "powerpc64",
))]
pub const CACHE_LINE_SIZE: usize = 128;

#[cfg(any(
    target_arch = "arm",
    target_arch = "mips",
    target_arch = "mips32r6",
    target_arch = "mips64",
    target_arch = "mips64r6",
    target_arch = "sparc",
    target_arch = "hexagon",
))]
pub const CACHE_LINE_SIZE: usize = 32;

#[cfg(target_arch = "m68k")]
pub const CACHE_LINE_SIZE: usize = 16;

#[cfg(target_arch = "s390x")]
pub const CACHE_LINE_SIZE: usize = 256;

#[cfg(not(any(
    target_arch = "x86_64",
    target_arch = "aarch64",
    target_arch = "arm64ec",
    target_arch = "powerpc64",
    target_arch = "arm",
    target_arch = "mips",
    target_arch = "mips32r6",
    target_arch = "mips64",
    target_arch = "mips64r6",
    target_arch = "sparc",
    target_arch = "hexagon",
    target_arch = "m68k",
    target_arch = "s390x",
)))]
pub const CACHE_LINE_SIZE: usize = 64;

pub const fn assert_valid_capacity<T, const S: usize>() {
    assert!(
        core::mem::size_of::<T>() > 0,
        "SPSCRBuffer does not support zero-sized types"
    );
    assert!(
        S.is_power_of_two(),
        "SPSCRBuffer capacity S must be a power of two"
    );
    assert!(S >= 2, "SPSCRBuffer capacity S must be at least 2");
}

#[cfg(feature = "perf")]
pub mod page_size {
    pub const DEFAULT_PAGE_SIZE: usize = 4096;

    /// Retrieves the system page size, caching the result in an atomic to avoid
    /// repeated syscalls. Safe for `no_std` environments with an OS.
    pub fn get_page_size() -> usize {
        use std::sync::atomic::{AtomicUsize, Ordering};

        static CACHED_PS: AtomicUsize = AtomicUsize::new(0);

        // Try to load the cached value. Relaxed is sufficient as the value
        // is a hardware constant and we don't need to synchronize other memory.
        let cached = CACHED_PS.load(Ordering::Relaxed);
        if cached != 0 {
            return cached;
        }

        // Determine the value based on the platform
        let ps = query_os_page_size();

        // Cache it for future calls
        CACHED_PS.store(ps, Ordering::Relaxed);
        ps
    }

    fn query_os_page_size() -> usize {
        #[cfg(unix)]
        {
            // SAFETY: sysconf is a standard POSIX call.
            unsafe {
                let ps = libc::sysconf(libc::_SC_PAGESIZE);
                if ps < 0 {
                    DEFAULT_PAGE_SIZE
                } else {
                    ps as usize
                }
            }
        }

        #[cfg(windows)]
        {
            // SAFETY: GetSystemInfo is the standard Win32 API for this.
            unsafe {
                let mut info = core::mem::zeroed::<
                    windows_sys::Win32::System::SystemInformation::SYSTEM_INFO,
                >();
                windows_sys::Win32::System::SystemInformation::GetSystemInfo(&mut info);
                info.dwPageSize as usize
            }
        }

        #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
        {
            // WASM pages are strictly defined as 64KiB in the spec.
            65536
        }

        #[cfg(not(any(unix, windows, target_arch = "wasm32", target_arch = "wasm64")))]
        {
            DEFAULT_PAGE_SIZE
        }
    }
}
