#![allow(unsafe_code)]
use std::alloc::{Layout, alloc_zeroed, dealloc};
use std::ptr::NonNull;
use std::sync::{Arc, Mutex, PoisonError};
use nuvec_core::{Error, Result};
pub(crate) const FRAME: usize = 4096;
pub(crate) const CLASSES: usize = 8;
#[cfg(test)]
pub(crate) const MAX_FRAME: usize = FRAME << (CLASSES - 1);
const ALIGN: usize = 16384;
pub(crate) fn class(bytes: usize, large: bool) -> (usize, usize) {
if large {
for k in (1..CLASSES).rev() {
if bytes >= FRAME << k {
return (k, FRAME << k);
}
}
}
(0, FRAME)
}
struct Memory {
ptr: NonNull<u8>,
len: usize,
lock: Mutex<()>,
}
unsafe impl Send for Memory {}
unsafe impl Sync for Memory {}
impl Drop for Memory {
fn drop(&mut self) {
unsafe { dealloc(self.ptr.as_ptr(), Self::layout(self.len)) }
}
}
impl Memory {
fn layout(len: usize) -> Layout {
Layout::from_size_align(len, ALIGN).expect("frame sizes are small powers of two")
}
}
#[derive(Clone)]
pub(crate) struct Frame(Arc<Memory>);
impl std::fmt::Debug for Frame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Frame({} bytes)", self.0.len)
}
}
impl Frame {
pub(crate) fn new(len: usize) -> Result<Self> {
debug_assert!(len > 0 && len.is_multiple_of(FRAME));
let ptr = unsafe { alloc_zeroed(Memory::layout(len)) };
let ptr = NonNull::new(ptr).ok_or_else(|| {
Error::Backend(format!("jaccl: allocating a {len}-byte frame failed"))
})?;
Ok(Self(Arc::new(Memory {
ptr,
len,
lock: Mutex::new(()),
})))
}
pub(crate) fn len(&self) -> usize {
self.0.len
}
pub(crate) fn as_ptr(&self) -> *mut u8 {
self.0.ptr.as_ptr()
}
pub(crate) fn read<T>(&self, len: usize, f: impl FnOnce(&[u8]) -> T) -> T {
let _guard = self.0.lock.lock().unwrap_or_else(PoisonError::into_inner);
assert!(len <= self.0.len);
f(unsafe { std::slice::from_raw_parts(self.0.ptr.as_ptr(), len) })
}
pub(crate) fn write(&self, data: &[u8]) {
let _guard = self.0.lock.lock().unwrap_or_else(PoisonError::into_inner);
assert!(data.len() <= self.0.len);
unsafe { std::ptr::copy_nonoverlapping(data.as_ptr(), self.0.ptr.as_ptr(), data.len()) }
}
#[cfg(test)]
pub(crate) fn copy_from(&self, src: &Frame, len: usize) {
assert!(!Arc::ptr_eq(&self.0, &src.0) && len <= self.0.len && len <= src.0.len);
let _dst = self.0.lock.lock().unwrap_or_else(PoisonError::into_inner);
let _src = src.0.lock.lock().unwrap_or_else(PoisonError::into_inner);
unsafe { std::ptr::copy_nonoverlapping(src.0.ptr.as_ptr(), self.0.ptr.as_ptr(), len) }
}
}
pub(crate) fn large_frames() -> bool {
#[cfg(target_os = "macos")]
{
os_version().is_some_and(|v| v >= (26, 3))
}
#[cfg(not(target_os = "macos"))]
{
false
}
}
#[cfg(target_os = "macos")]
fn os_version() -> Option<(u32, u32)> {
let mut buf = [0u8; 64];
let mut len = buf.len();
let rc = unsafe {
libc::sysctlbyname(
c"kern.osproductversion".as_ptr(),
buf.as_mut_ptr().cast(),
&mut len,
std::ptr::null_mut(),
0,
)
};
if rc != 0 {
return None;
}
let text = std::str::from_utf8(&buf[..len])
.ok()?
.trim_end_matches('\0');
let mut parts = text.split('.').map(|p| p.parse::<u32>().ok());
Some((parts.next()??, parts.next().flatten().unwrap_or(0)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn classes_follow_mlx() {
assert_eq!(class(0, true), (0, 4096));
assert_eq!(class(8191, true), (0, 4096));
assert_eq!(class(8192, true), (1, 8192));
assert_eq!(class(MAX_FRAME, true), (7, MAX_FRAME));
assert_eq!(class(64 << 20, true), (7, MAX_FRAME));
assert_eq!(class(64 << 20, false), (0, 4096));
}
#[test]
fn frames_are_aligned_and_copy() {
let (a, b) = (Frame::new(8192).unwrap(), Frame::new(4096).unwrap());
assert_eq!(a.as_ptr() as usize % ALIGN, 0);
a.write(&[1, 2, 3]);
b.copy_from(&a, 4096);
assert_eq!(b.read(4, <[u8]>::to_vec), [1, 2, 3, 0]);
#[cfg(target_os = "macos")]
assert!(os_version().is_some_and(|v| v.0 >= 15));
}
}