#[cfg(all(not(ra_small_profile), not(all(target_arch = "wasm32", not(miri)))))]
use core::sync::atomic::{AtomicU32, Ordering};
use crate::segment::Segment;
use crate::types::SEGMENT_SIZE;
#[cfg(all(not(ra_small_profile), not(all(target_arch = "wasm32", not(miri)))))]
const ADDR_BITS: usize = if usize::BITS == 64 {
48
} else {
usize::BITS as usize
};
const WINDOW_SHIFT: usize = SEGMENT_SIZE.trailing_zeros() as usize;
#[cfg(all(not(ra_small_profile), not(all(target_arch = "wasm32", not(miri)))))]
const MAP_BITS: usize = 1 << (ADDR_BITS - WINDOW_SHIFT);
#[cfg(all(not(ra_small_profile), not(all(target_arch = "wasm32", not(miri)))))]
const MAP_WORDS: usize = MAP_BITS / WORD_BITS;
const _: () = assert!(1 << WINDOW_SHIFT == SEGMENT_SIZE);
#[cfg(all(not(ra_small_profile), not(all(target_arch = "wasm32", not(miri)))))]
const WORD_BITS: usize = u32::BITS as usize;
#[cfg(all(not(ra_small_profile), not(all(target_arch = "wasm32", not(miri)))))]
static MAP: [AtomicU32; MAP_WORDS] = [const { AtomicU32::new(0) }; MAP_WORDS];
#[cfg(all(ra_small_profile, not(all(target_arch = "wasm32", not(miri)))))]
mod range_table {
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
const MAX_RANGES: usize = 64;
static BASE: [AtomicUsize; MAX_RANGES] = [const { AtomicUsize::new(0) }; MAX_RANGES];
static END: [AtomicUsize; MAX_RANGES] = [const { AtomicUsize::new(0) }; MAX_RANGES];
static LOCK: AtomicBool = AtomicBool::new(false);
static OVERFLOWED: AtomicBool = AtomicBool::new(false);
struct Guard;
impl Guard {
fn acquire() -> Self {
while LOCK
.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
core::hint::spin_loop();
}
Self
}
}
impl Drop for Guard {
fn drop(&mut self) {
LOCK.store(false, Ordering::Release);
}
}
pub(super) fn set(base: usize, size: usize) {
let _g = Guard::acquire();
for i in 0..MAX_RANGES {
if END[i].load(Ordering::Relaxed) == 0 {
BASE[i].store(base, Ordering::Relaxed);
END[i].store(base + size.max(1), Ordering::Release);
return;
}
}
OVERFLOWED.store(true, Ordering::Release);
}
pub(super) fn overflowed() -> bool {
OVERFLOWED.load(Ordering::Acquire)
}
pub(super) fn clear(base: usize, size: usize) {
let end = base + size.max(1);
let _g = Guard::acquire();
for i in 0..MAX_RANGES {
if BASE[i].load(Ordering::Relaxed) == base && END[i].load(Ordering::Relaxed) == end {
END[i].store(0, Ordering::Release);
BASE[i].store(0, Ordering::Relaxed);
return;
}
}
}
pub(super) fn contains(addr: usize) -> bool {
if OVERFLOWED.load(Ordering::Acquire) {
return true;
}
(0..MAX_RANGES).any(|i| {
let e = END[i].load(Ordering::Acquire);
e != 0 && addr >= BASE[i].load(Ordering::Relaxed) && addr < e
})
}
}
#[cfg(all(target_arch = "wasm32", not(miri)))]
mod base_table {
use core::sync::atomic::{AtomicU32, Ordering};
const SLICE_SHIFT: usize = 16;
const _: () = assert!(1 << SLICE_SHIFT == crate::types::SEGMENT_SLICE_SIZE);
const SLOTS: usize = 1 << (32 - SLICE_SHIFT);
static BASE: [AtomicU32; SLOTS] = [const { AtomicU32::new(0) }; SLOTS];
fn slots(base: usize, size: usize) -> core::ops::Range<usize> {
let start = base >> SLICE_SHIFT;
let end = (base + size.max(1)).div_ceil(1 << SLICE_SHIFT).min(SLOTS);
start.min(SLOTS)..end
}
pub(super) fn set(base: usize, size: usize) {
let entry = ((base >> SLICE_SHIFT) + 1) as u32;
for i in slots(base, size) {
BASE[i].store(entry, Ordering::Relaxed);
}
}
pub(super) fn clear(base: usize, size: usize) {
for i in slots(base, size) {
BASE[i].store(0, Ordering::Relaxed);
}
}
pub(super) fn get(addr: usize) -> usize {
let i = addr >> SLICE_SHIFT;
if i >= SLOTS {
return 0;
}
match BASE[i].load(Ordering::Relaxed) {
0 => 0,
e => ((e - 1) as usize) << SLICE_SHIFT,
}
}
}
#[cfg(all(target_arch = "wasm32", not(miri)))]
#[inline]
pub fn base_of(addr: usize) -> usize {
base_table::get(addr)
}
#[cfg(all(not(ra_small_profile), not(all(target_arch = "wasm32", not(miri)))))]
#[inline]
fn locate(addr: usize) -> Option<(usize, u32)> {
let idx = addr >> WINDOW_SHIFT;
if idx >= MAP_BITS {
return None;
}
Some((idx / WORD_BITS, 1u32 << (idx % WORD_BITS)))
}
pub fn register(seg: *mut Segment) {
register_range(seg.addr(), SEGMENT_SIZE);
}
pub fn register_range(base: usize, size: usize) {
if crate::ONE_REGION {
return;
}
#[cfg(all(target_arch = "wasm32", not(miri)))]
{
base_table::set(base, size);
}
#[cfg(all(ra_small_profile, not(all(target_arch = "wasm32", not(miri)))))]
{
range_table::set(base, size);
}
#[cfg(all(not(ra_small_profile), not(all(target_arch = "wasm32", not(miri)))))]
{
let mut a = base;
let end = base + size.max(1);
while a < end {
if let Some((w, bit)) = locate(a) {
MAP[w].fetch_or(bit, Ordering::Release);
}
a += SEGMENT_SIZE;
}
}
}
pub fn unregister(seg: *mut Segment) {
unregister_range(seg.addr(), SEGMENT_SIZE);
}
pub fn unregister_range(base: usize, size: usize) {
if crate::ONE_REGION {
return;
}
#[cfg(all(target_arch = "wasm32", not(miri)))]
{
base_table::clear(base, size);
}
#[cfg(all(ra_small_profile, not(all(target_arch = "wasm32", not(miri)))))]
{
range_table::clear(base, size);
}
#[cfg(all(not(ra_small_profile), not(all(target_arch = "wasm32", not(miri)))))]
{
let mut a = base;
let end = base + size.max(1);
while a < end {
if let Some((w, bit)) = locate(a) {
MAP[w].fetch_and(!bit, Ordering::Release);
}
a += SEGMENT_SIZE;
}
}
}
pub fn contains(p: *const u8) -> bool {
if crate::ONE_REGION {
return crate::prim::fixed::region_contains(p.addr());
}
#[cfg(all(target_arch = "wasm32", not(miri)))]
{
base_table::get(p.addr()) != 0
}
#[cfg(all(ra_small_profile, not(all(target_arch = "wasm32", not(miri)))))]
{
range_table::contains(p.addr())
}
#[cfg(all(not(ra_small_profile), not(all(target_arch = "wasm32", not(miri)))))]
{
match locate(p.addr()) {
Some((w, bit)) => MAP[w].load(Ordering::Acquire) & bit != 0,
None => false,
}
}
}
#[must_use]
pub fn range_table_overflowed() -> bool {
if crate::ONE_REGION {
return false; }
#[cfg(all(ra_small_profile, not(all(target_arch = "wasm32", not(miri)))))]
{
range_table::overflowed()
}
#[cfg(not(all(ra_small_profile, not(all(target_arch = "wasm32", not(miri))))))]
{
false
}
}