use core::ptr::NonNull;
use core::sync::atomic::{AtomicPtr, Ordering};
use crate::class::{self, SPAN_BYTES};
pub use crate::pagemap::{NO_CLASS, SpanMeta};
pub const SEGMENT_BYTES: usize = 4 * 1024 * 1024;
pub const SPANS_PER_SEGMENT: usize = SEGMENT_BYTES / SPAN_BYTES;
pub const FIRST_DATA_SPAN: usize = 1;
const MAGIC: u64 = 0x6b65_7679_616c_6c63;
#[repr(C)]
pub struct Segment {
magic: u64,
pub next: *mut Segment,
pub owner: usize,
pub foreign: AtomicPtr<u8>,
pub foreign_bytes: core::sync::atomic::AtomicUsize,
pub foreign_live: core::sync::atomic::AtomicUsize,
pub spans: [SpanMeta; SPANS_PER_SEGMENT],
}
impl Segment {
pub unsafe fn init(base: NonNull<u8>, owner: usize) -> NonNull<Segment> {
let seg = base.as_ptr().cast::<Segment>();
unsafe {
seg.write(Segment {
magic: MAGIC,
next: core::ptr::null_mut(),
owner,
foreign: AtomicPtr::new(core::ptr::null_mut()),
foreign_bytes: core::sync::atomic::AtomicUsize::new(0),
foreign_live: core::sync::atomic::AtomicUsize::new(0),
spans: [SpanMeta::new(); SPANS_PER_SEGMENT],
});
}
unsafe { NonNull::new_unchecked(seg) }
}
#[must_use]
pub fn span_base(&self, index: usize) -> *mut u8 {
let base = core::ptr::from_ref(self) as usize;
(base + index * SPAN_BYTES) as *mut u8
}
#[must_use]
pub fn is_valid(&self) -> bool {
self.magic == MAGIC
}
}
#[inline]
#[must_use]
pub unsafe fn segment_of(ptr: NonNull<u8>) -> NonNull<Segment> {
let base = ptr.as_ptr() as usize & !(SEGMENT_BYTES - 1);
unsafe { NonNull::new_unchecked(base as *mut Segment) }
}
#[inline]
#[must_use]
pub fn span_index_of(ptr: NonNull<u8>) -> usize {
(ptr.as_ptr() as usize & (SEGMENT_BYTES - 1)) / SPAN_BYTES
}
#[inline]
#[must_use]
pub fn slot_index_of(ptr: NonNull<u8>, class: usize) -> u32 {
let off = ptr.as_ptr() as usize & (SPAN_BYTES - 1);
class::slot_of_offset(off, class)
}
pub unsafe fn splice_foreign(
seg: &Segment,
head: *mut u8,
tail: *mut u8,
live_sum: usize,
bytes_sum: usize,
) {
seg.foreign_live.fetch_add(live_sum, Ordering::Relaxed);
seg.foreign_bytes.fetch_add(bytes_sum, Ordering::Relaxed);
let mut old = seg.foreign.load(Ordering::Relaxed);
loop {
unsafe { tail.cast::<*mut u8>().write(old) };
match seg.foreign.compare_exchange_weak(old, head, Ordering::Release, Ordering::Relaxed) {
Ok(_) => break,
Err(actual) => old = actual,
}
}
}
#[must_use]
pub fn take_foreign(seg: &Segment) -> *mut u8 {
seg.foreign_bytes.store(0, Ordering::Relaxed);
seg.foreign_live.store(0, Ordering::Relaxed);
seg.foreign.swap(core::ptr::null_mut(), Ordering::Acquire)
}
pub const FOREIGN_SIZE_OFFSET: usize = core::mem::size_of::<*mut u8>();
#[must_use]
pub unsafe fn foreign_requested(slot: NonNull<u8>) -> usize {
unsafe { slot.as_ptr().add(FOREIGN_SIZE_OFFSET).cast::<u32>().read() as usize }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_header_fits_inside_the_span_it_occupies() {
assert!(
core::mem::size_of::<Segment>() <= SPAN_BYTES,
"the header spills out of span 0 into an allocation span"
);
}
#[test]
fn geometry_is_maskable() {
assert!(SEGMENT_BYTES.is_power_of_two());
assert!(SPAN_BYTES.is_power_of_two());
assert_eq!(SEGMENT_BYTES % SPAN_BYTES, 0);
assert_eq!(SPANS_PER_SEGMENT, 64);
}
#[test]
fn the_bitmap_header_still_fits_its_span() {
assert!(core::mem::size_of::<SpanMeta>() >= crate::pagemap::BITMAP_WORDS * 8);
assert!(core::mem::size_of::<Segment>() <= SPAN_BYTES);
}
}