#![allow(dead_code)]
use core::sync::atomic::{AtomicUsize, Ordering};
#[inline]
const fn set_bit(x: u64, bit: u32) -> u64 {
x | 1 << bit
}
#[inline]
const fn test_bit(x: u64, bit: u32) -> bool {
x & (1 << bit) != 0
}
const CACHE_CAPACITY: u32 = 62;
#[derive(Copy, Clone)]
pub(crate) struct Initializer(u64);
#[allow(clippy::use_self)]
impl Default for Initializer {
fn default() -> Self {
Initializer(0)
}
}
impl Initializer {
#[inline]
pub(crate) fn test(self, bit: u32) -> bool {
debug_assert!(
bit < CACHE_CAPACITY,
"too many features, time to increase the cache size!"
);
test_bit(self.0, bit)
}
#[inline]
pub(crate) fn set(&mut self, bit: u32) {
debug_assert!(
bit < CACHE_CAPACITY,
"too many features, time to increase the cache size!"
);
let v = self.0;
self.0 = set_bit(v, bit);
}
}
static CACHE: [Cache; 2] = [Cache::uninitialized(), Cache::uninitialized()];
struct Cache(AtomicUsize);
impl Cache {
const CAPACITY: u32 = (core::mem::size_of::<usize>() * 8 - 1) as u32;
const MASK: usize = (1 << Cache::CAPACITY) - 1;
const INITIALIZED_BIT: usize = 1usize << Cache::CAPACITY;
#[allow(clippy::declare_interior_mutable_const)]
const fn uninitialized() -> Self {
Cache(AtomicUsize::new(0))
}
#[inline]
pub(crate) fn test(&self, bit: u32) -> Option<bool> {
let cached = self.0.load(Ordering::Relaxed);
if cached == 0 {
None
} else {
Some(test_bit(cached as u64, bit))
}
}
#[inline]
fn initialize(&self, value: usize) -> usize {
debug_assert_eq!((value & !Cache::MASK), 0);
self.0
.store(value | Cache::INITIALIZED_BIT, Ordering::Relaxed);
value
}
}
#[inline]
fn initialize(value: Initializer) -> Initializer {
CACHE[0].initialize((value.0) as usize & Cache::MASK);
CACHE[1].initialize((value.0 >> Cache::CAPACITY) as usize & Cache::MASK);
value
}
#[cold]
fn detect_and_initialize() -> Initializer {
initialize(super::os::detect_features())
}
#[inline]
pub(crate) fn test(bit: u32) -> bool {
let (relative_bit, idx) = if bit < Cache::CAPACITY {
(bit, 0)
} else {
(bit - Cache::CAPACITY, 1)
};
CACHE[idx]
.test(relative_bit)
.unwrap_or_else(|| detect_and_initialize().test(bit))
}