use super::closure_layout::{ClosureLayout, SHARED_CELL_VALUE_OFFSET, SharedCell, TypedClosureHeader};
use super::heap_header::{HEAP_KIND_V2_CLOSURE, HeapHeader};
use super::struct_layout::FieldKind;
use crate::kinded_slot::KindedSlot;
use crate::native_kind::NativeKind;
use crate::slot::ValueSlot;
use std::sync::Arc;
pub struct OwnedClosureBlock {
ptr: *const u8,
layout: Arc<ClosureLayout>,
}
unsafe impl Send for OwnedClosureBlock {}
unsafe impl Sync for OwnedClosureBlock {}
impl OwnedClosureBlock {
#[inline]
pub unsafe fn from_raw(ptr: *const u8, layout: Arc<ClosureLayout>) -> Self {
Self { ptr, layout }
}
#[inline]
pub fn as_ptr(&self) -> *const u8 {
self.ptr
}
#[inline]
pub fn as_header_ptr(&self) -> *const TypedClosureHeader {
self.ptr as *const TypedClosureHeader
}
#[inline]
pub fn layout(&self) -> &Arc<ClosureLayout> {
&self.layout
}
#[inline]
pub unsafe fn read_capture_kinded(&self, idx: usize) -> (u64, crate::native_kind::NativeKind) {
assert!(
idx < self.layout.capture_count(),
"OwnedClosureBlock::read_capture_kinded: idx {} out of range (capture_count = {})",
idx,
self.layout.capture_count()
);
let off = self.layout.heap_capture_offset(idx);
let bits = unsafe { std::ptr::read(self.ptr.add(off) as *const u64) };
let kind = self.layout.capture_native_kind(idx);
(bits, kind)
}
}
impl Clone for OwnedClosureBlock {
#[inline]
fn clone(&self) -> Self {
unsafe {
retain_typed_closure(self.ptr);
}
Self {
ptr: self.ptr,
layout: Arc::clone(&self.layout),
}
}
}
impl Drop for OwnedClosureBlock {
#[inline]
fn drop(&mut self) {
unsafe {
release_typed_closure(self.ptr as *mut u8, &self.layout);
}
}
}
impl std::fmt::Debug for OwnedClosureBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let fid = unsafe { typed_closure_function_id(self.ptr) };
let tid = unsafe { typed_closure_type_id(self.ptr) };
f.debug_struct("OwnedClosureBlock")
.field("fn_id", &fid)
.field("type_id", &tid)
.field("captures", &self.layout.capture_count())
.finish()
}
}
#[inline]
pub unsafe fn alloc_typed_closure(
function_id: u16,
type_id: u32,
layout: &ClosureLayout,
) -> *mut u8 {
let size = layout.total_heap_size();
let alloc_layout = std::alloc::Layout::from_size_align(size, 8)
.expect("TypedClosureHeader size/align must be valid (size ≥ 16, align = 8)");
let ptr = unsafe { std::alloc::alloc_zeroed(alloc_layout) };
if ptr.is_null() {
std::alloc::handle_alloc_error(alloc_layout);
}
unsafe {
std::ptr::write(
ptr as *mut HeapHeader,
HeapHeader::new(HEAP_KIND_V2_CLOSURE),
);
let hdr = ptr as *mut TypedClosureHeader;
(*hdr).function_id = function_id as u32;
(*hdr).type_id = type_id;
}
ptr
}
#[inline]
pub unsafe fn typed_closure_function_id(ptr: *const u8) -> u16 {
unsafe { (*(ptr as *const TypedClosureHeader)).function_id as u16 }
}
#[inline]
pub unsafe fn typed_closure_type_id(ptr: *const u8) -> u32 {
unsafe { (*(ptr as *const TypedClosureHeader)).type_id }
}
#[inline]
pub unsafe fn typed_closure_kind(ptr: *const u8) -> u16 {
unsafe { (*(ptr as *const HeapHeader)).kind }
}
#[inline]
pub unsafe fn retain_typed_closure(ptr: *const u8) {
unsafe { (*(ptr as *const HeapHeader)).retain() };
}
#[inline]
pub unsafe fn release_typed_closure(ptr: *mut u8, layout: &ClosureLayout) {
use crate::v2::closure_layout::CaptureKind;
let reached_zero = unsafe { (*(ptr as *mut HeapHeader)).release() };
if !reached_zero {
return;
}
for i in 0..layout.capture_count() {
match layout.capture_storage_kind(i) {
CaptureKind::Immutable => {
if layout.is_heap_capture(i) {
let off = layout.heap_capture_offset(i);
let bits = unsafe { std::ptr::read(ptr.add(off) as *const u64) };
let kind = layout.capture_native_kind(i);
unsafe { drop_with_kind(bits, kind) };
}
}
CaptureKind::OwnedMutable => {
unsafe { drop_owned_mutable_capture(layout, ptr, i) };
}
CaptureKind::Shared => {
unsafe { drop_shared_capture(layout, ptr, i) };
}
}
}
unsafe { dealloc_typed_closure_no_drop(ptr, layout) };
}
#[inline]
pub unsafe fn dealloc_typed_closure_no_drop(ptr: *mut u8, layout: &ClosureLayout) {
let size = layout.total_heap_size();
let alloc_layout = std::alloc::Layout::from_size_align(size, 8)
.expect("TypedClosureHeader size/align must be valid");
unsafe { std::alloc::dealloc(ptr, alloc_layout) };
}
#[inline]
unsafe fn shared_cell_payload_ptr(cell: *const SharedCell) -> *const u8 {
unsafe { (cell as *const u8).add(SHARED_CELL_VALUE_OFFSET as usize) }
}
#[inline]
pub unsafe fn read_shared_f64(cell: *const SharedCell) -> f64 {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::read(shared_cell_payload_ptr(cell) as *const f64) }
}
#[inline]
pub unsafe fn write_shared_f64(cell: *const SharedCell, value: f64) {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::write(shared_cell_payload_ptr(cell) as *mut f64, value) };
}
#[inline]
pub unsafe fn read_shared_i64(cell: *const SharedCell) -> i64 {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::read(shared_cell_payload_ptr(cell) as *const i64) }
}
#[inline]
pub unsafe fn write_shared_i64(cell: *const SharedCell, value: i64) {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::write(shared_cell_payload_ptr(cell) as *mut i64, value) };
}
#[inline]
pub unsafe fn read_shared_u64(cell: *const SharedCell) -> u64 {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::read(shared_cell_payload_ptr(cell) as *const u64) }
}
#[inline]
pub unsafe fn write_shared_u64(cell: *const SharedCell, value: u64) {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::write(shared_cell_payload_ptr(cell) as *mut u64, value) };
}
#[inline]
pub unsafe fn read_shared_i32(cell: *const SharedCell) -> i32 {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::read(shared_cell_payload_ptr(cell) as *const i32) }
}
#[inline]
pub unsafe fn write_shared_i32(cell: *const SharedCell, value: i32) {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::write(shared_cell_payload_ptr(cell) as *mut i64, value as i64) };
}
#[inline]
pub unsafe fn read_shared_u32(cell: *const SharedCell) -> u32 {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::read(shared_cell_payload_ptr(cell) as *const u32) }
}
#[inline]
pub unsafe fn write_shared_u32(cell: *const SharedCell, value: u32) {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::write(shared_cell_payload_ptr(cell) as *mut u64, value as u64) };
}
#[inline]
pub unsafe fn read_shared_i16(cell: *const SharedCell) -> i16 {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::read(shared_cell_payload_ptr(cell) as *const i16) }
}
#[inline]
pub unsafe fn write_shared_i16(cell: *const SharedCell, value: i16) {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::write(shared_cell_payload_ptr(cell) as *mut i64, value as i64) };
}
#[inline]
pub unsafe fn read_shared_u16(cell: *const SharedCell) -> u16 {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::read(shared_cell_payload_ptr(cell) as *const u16) }
}
#[inline]
pub unsafe fn write_shared_u16(cell: *const SharedCell, value: u16) {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::write(shared_cell_payload_ptr(cell) as *mut u64, value as u64) };
}
#[inline]
pub unsafe fn read_shared_i8(cell: *const SharedCell) -> i8 {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::read(shared_cell_payload_ptr(cell) as *const i8) }
}
#[inline]
pub unsafe fn write_shared_i8(cell: *const SharedCell, value: i8) {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::write(shared_cell_payload_ptr(cell) as *mut i64, value as i64) };
}
#[inline]
pub unsafe fn read_shared_u8(cell: *const SharedCell) -> u8 {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::read(shared_cell_payload_ptr(cell) as *const u8) }
}
#[inline]
pub unsafe fn write_shared_u8(cell: *const SharedCell, value: u8) {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::write(shared_cell_payload_ptr(cell) as *mut u64, value as u64) };
}
#[inline]
pub unsafe fn read_shared_bool(cell: *const SharedCell) -> bool {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::read(shared_cell_payload_ptr(cell) as *const u8) != 0 }
}
#[inline]
pub unsafe fn write_shared_bool(cell: *const SharedCell, value: bool) {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
let byte: u64 = if value { 1 } else { 0 };
unsafe { std::ptr::write(shared_cell_payload_ptr(cell) as *mut u64, byte) };
}
#[inline]
pub unsafe fn read_shared_ptr(cell: *const SharedCell) -> u64 {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::read(shared_cell_payload_ptr(cell) as *const u64) }
}
#[inline]
pub unsafe fn write_shared_ptr(cell: *const SharedCell, bits: u64) {
let cell_ref = unsafe { &*cell };
let _g = cell_ref.lock();
unsafe { std::ptr::write(shared_cell_payload_ptr(cell) as *mut u64, bits) };
}
#[inline]
pub unsafe fn drop_shared_capture(layout: &ClosureLayout, base: *mut u8, i: usize) {
let off = layout.heap_capture_offset(i);
let cell_ptr = unsafe { std::ptr::read(base.add(off) as *const *const SharedCell) };
if cell_ptr.is_null() {
return;
}
let inner_kind = layout.capture_inner_kind(i);
if inner_kind == FieldKind::Ptr {
let cell_ref = unsafe { &*cell_ptr };
let bits = {
let _g = cell_ref.lock();
unsafe { std::ptr::read(shared_cell_payload_ptr(cell_ptr) as *const u64) }
};
let kind = layout.capture_native_kind(i);
unsafe { drop_with_kind(bits, kind) };
}
unsafe { drop(Arc::from_raw(cell_ptr)) };
}
#[inline]
pub unsafe fn write_capture_raw_u64(ptr: *mut u8, layout: &ClosureLayout, idx: usize, bits: u64) {
let off = layout.heap_capture_offset(idx);
unsafe { std::ptr::write(ptr.add(off) as *mut u64, bits) };
}
#[inline]
pub unsafe fn read_capture_as_value_bits(
ptr: *const u8,
layout: &ClosureLayout,
idx: usize,
) -> u64 {
let kind = layout.capture_kind(idx);
let off = layout.heap_capture_offset(idx);
unsafe {
let field_ptr = ptr.add(off);
match kind {
FieldKind::F64 | FieldKind::I64 | FieldKind::U64 | FieldKind::Ptr => {
std::ptr::read(field_ptr as *const u64)
}
FieldKind::I32 => std::ptr::read(field_ptr as *const i32) as i64 as u64,
FieldKind::U32 => std::ptr::read(field_ptr as *const u32) as u64,
FieldKind::I16 => std::ptr::read(field_ptr as *const i16) as i64 as u64,
FieldKind::U16 => std::ptr::read(field_ptr as *const u16) as u64,
FieldKind::I8 => std::ptr::read(field_ptr as *const i8) as i64 as u64,
FieldKind::U8 => std::ptr::read(field_ptr as *const u8) as u64,
FieldKind::Bool => (std::ptr::read(field_ptr as *const u8) != 0) as u64,
}
}
}
#[inline]
pub unsafe fn write_capture_typed(ptr: *mut u8, layout: &ClosureLayout, idx: usize, bits: u64) {
let kind = layout.capture_kind(idx);
let off = layout.heap_capture_offset(idx);
unsafe {
let field_ptr = ptr.add(off);
match kind {
FieldKind::F64 | FieldKind::I64 | FieldKind::U64 | FieldKind::Ptr => {
std::ptr::write(field_ptr as *mut u64, bits);
}
FieldKind::I32 => std::ptr::write(field_ptr as *mut i32, bits as i32),
FieldKind::U32 => std::ptr::write(field_ptr as *mut u32, bits as u32),
FieldKind::I16 => std::ptr::write(field_ptr as *mut i16, bits as i16),
FieldKind::U16 => std::ptr::write(field_ptr as *mut u16, bits as u16),
FieldKind::I8 => std::ptr::write(field_ptr as *mut i8, bits as i8),
FieldKind::U8 => std::ptr::write(field_ptr as *mut u8, bits as u8),
FieldKind::Bool => std::ptr::write(field_ptr as *mut u8, (bits & 1) as u8),
}
}
}
#[inline]
pub unsafe fn typed_closure_refcount(ptr: *const u8) -> u32 {
unsafe { (*(ptr as *const HeapHeader)).get_refcount() }
}
#[inline]
pub fn alloc_owned_mutable_i64(initial: i64) -> *mut i64 {
Box::into_raw(Box::new(initial))
}
#[inline]
pub unsafe fn read_owned_mutable_i64(ptr: *mut i64) -> i64 {
unsafe { *ptr }
}
#[inline]
pub unsafe fn write_owned_mutable_i64(ptr: *mut i64, value: i64) {
unsafe { *ptr = value };
}
#[inline]
pub fn alloc_owned_mutable_f64(initial: f64) -> *mut f64 {
Box::into_raw(Box::new(initial))
}
#[inline]
pub unsafe fn read_owned_mutable_f64(ptr: *mut f64) -> f64 {
unsafe { *ptr }
}
#[inline]
pub unsafe fn write_owned_mutable_f64(ptr: *mut f64, value: f64) {
unsafe { *ptr = value };
}
#[inline]
pub fn alloc_owned_mutable_i32(initial: i32) -> *mut i32 {
Box::into_raw(Box::new(initial))
}
#[inline]
pub unsafe fn read_owned_mutable_i32(ptr: *mut i32) -> i32 {
unsafe { *ptr }
}
#[inline]
pub unsafe fn write_owned_mutable_i32(ptr: *mut i32, value: i32) {
unsafe { *ptr = value };
}
#[inline]
pub fn alloc_owned_mutable_i16(initial: i16) -> *mut i16 {
Box::into_raw(Box::new(initial))
}
#[inline]
pub unsafe fn read_owned_mutable_i16(ptr: *mut i16) -> i16 {
unsafe { *ptr }
}
#[inline]
pub unsafe fn write_owned_mutable_i16(ptr: *mut i16, value: i16) {
unsafe { *ptr = value };
}
#[inline]
pub fn alloc_owned_mutable_i8(initial: i8) -> *mut i8 {
Box::into_raw(Box::new(initial))
}
#[inline]
pub unsafe fn read_owned_mutable_i8(ptr: *mut i8) -> i8 {
unsafe { *ptr }
}
#[inline]
pub unsafe fn write_owned_mutable_i8(ptr: *mut i8, value: i8) {
unsafe { *ptr = value };
}
#[inline]
pub fn alloc_owned_mutable_u64(initial: u64) -> *mut u64 {
Box::into_raw(Box::new(initial))
}
#[inline]
pub unsafe fn read_owned_mutable_u64(ptr: *mut u64) -> u64 {
unsafe { *ptr }
}
#[inline]
pub unsafe fn write_owned_mutable_u64(ptr: *mut u64, value: u64) {
unsafe { *ptr = value };
}
#[inline]
pub fn alloc_owned_mutable_u32(initial: u32) -> *mut u32 {
Box::into_raw(Box::new(initial))
}
#[inline]
pub unsafe fn read_owned_mutable_u32(ptr: *mut u32) -> u32 {
unsafe { *ptr }
}
#[inline]
pub unsafe fn write_owned_mutable_u32(ptr: *mut u32, value: u32) {
unsafe { *ptr = value };
}
#[inline]
pub fn alloc_owned_mutable_u16(initial: u16) -> *mut u16 {
Box::into_raw(Box::new(initial))
}
#[inline]
pub unsafe fn read_owned_mutable_u16(ptr: *mut u16) -> u16 {
unsafe { *ptr }
}
#[inline]
pub unsafe fn write_owned_mutable_u16(ptr: *mut u16, value: u16) {
unsafe { *ptr = value };
}
#[inline]
pub fn alloc_owned_mutable_u8(initial: u8) -> *mut u8 {
Box::into_raw(Box::new(initial))
}
#[inline]
pub unsafe fn read_owned_mutable_u8(ptr: *mut u8) -> u8 {
unsafe { *ptr }
}
#[inline]
pub unsafe fn write_owned_mutable_u8(ptr: *mut u8, value: u8) {
unsafe { *ptr = value };
}
#[inline]
pub fn alloc_owned_mutable_bool(initial: bool) -> *mut bool {
Box::into_raw(Box::new(initial))
}
#[inline]
pub unsafe fn read_owned_mutable_bool(ptr: *mut bool) -> bool {
unsafe { *ptr }
}
#[inline]
pub unsafe fn write_owned_mutable_bool(ptr: *mut bool, value: bool) {
unsafe { *ptr = value };
}
#[inline]
pub fn alloc_owned_mutable_ptr(initial: u64) -> *mut u64 {
Box::into_raw(Box::new(initial))
}
#[inline]
pub unsafe fn read_owned_mutable_ptr(ptr: *mut u64) -> u64 {
unsafe { *ptr }
}
#[inline]
pub unsafe fn write_owned_mutable_ptr(ptr: *mut u64, value: u64) {
unsafe { *ptr = value };
}
#[inline]
pub unsafe fn drop_owned_mutable_capture(layout: &ClosureLayout, base: *mut u8, i: usize) {
let off = layout.heap_capture_offset(i);
let raw = unsafe { std::ptr::read(base.add(off) as *const *mut u8) };
if raw.is_null() {
return;
}
match layout.capture_inner_kind(i) {
FieldKind::I64 => {
unsafe { drop(Box::from_raw(raw as *mut i64)) };
}
FieldKind::F64 => {
unsafe { drop(Box::from_raw(raw as *mut f64)) };
}
FieldKind::I32 => {
unsafe { drop(Box::from_raw(raw as *mut i32)) };
}
FieldKind::I16 => {
unsafe { drop(Box::from_raw(raw as *mut i16)) };
}
FieldKind::I8 => {
unsafe { drop(Box::from_raw(raw as *mut i8)) };
}
FieldKind::U64 => {
unsafe { drop(Box::from_raw(raw as *mut u64)) };
}
FieldKind::U32 => {
unsafe { drop(Box::from_raw(raw as *mut u32)) };
}
FieldKind::U16 => {
unsafe { drop(Box::from_raw(raw as *mut u16)) };
}
FieldKind::U8 => {
unsafe { drop(Box::from_raw(raw as *mut u8)) };
}
FieldKind::Bool => {
unsafe { drop(Box::from_raw(raw as *mut bool)) };
}
FieldKind::Ptr => {
let cell = raw as *mut u64;
let bits = unsafe { *cell };
let kind = layout.capture_native_kind(i);
unsafe { drop_with_kind(bits, kind) };
unsafe { drop(Box::from_raw(cell)) };
}
}
}
#[inline]
pub(crate) unsafe fn clone_with_kind(bits: u64, kind: NativeKind) {
if bits == 0 {
return;
}
unsafe {
let original = KindedSlot::new(ValueSlot::from_raw(bits), kind);
let cloned = original.clone();
std::mem::forget(original);
std::mem::forget(cloned);
}
}
#[inline]
pub(crate) unsafe fn drop_with_kind(bits: u64, kind: NativeKind) {
if bits == 0 {
return;
}
unsafe {
let _retire = KindedSlot::new(ValueSlot::from_raw(bits), kind);
}
}
#[derive(Debug)]
pub struct ClosureCell {
pub bits: Vec<u64>,
pub kinds: Vec<NativeKind>,
}
impl ClosureCell {
#[inline]
pub fn new() -> Self {
Self {
bits: Vec::new(),
kinds: Vec::new(),
}
}
#[inline]
pub fn with_capacity(cap: usize) -> Self {
Self {
bits: Vec::with_capacity(cap),
kinds: Vec::with_capacity(cap),
}
}
#[inline]
pub fn len(&self) -> usize {
debug_assert_eq!(
self.bits.len(),
self.kinds.len(),
"ClosureCell index invariant: bits.len() == kinds.len()"
);
self.bits.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.bits.is_empty()
}
#[inline]
pub unsafe fn push(&mut self, bits: u64, kind: NativeKind) {
self.bits.push(bits);
self.kinds.push(kind);
debug_assert_eq!(
self.bits.len(),
self.kinds.len(),
"ClosureCell::push violated bits.len() == kinds.len() invariant"
);
}
#[inline]
pub fn pop(&mut self) -> Option<(u64, NativeKind)> {
match (self.bits.pop(), self.kinds.pop()) {
(Some(b), Some(k)) => Some((b, k)),
(None, None) => None,
_ => {
unreachable!("ClosureCell index invariant violated: bits/kinds desync on pop")
}
}
}
#[inline]
pub fn read(&self, idx: usize) -> (u64, NativeKind) {
debug_assert_eq!(
self.bits.len(),
self.kinds.len(),
"ClosureCell::read on desynced cell store"
);
(self.bits[idx], self.kinds[idx])
}
#[inline]
pub fn read_kinded(&self, idx: usize) -> KindedSlot {
let (bits, kind) = self.read(idx);
unsafe { clone_with_kind(bits, kind) };
KindedSlot::new(ValueSlot::from_raw(bits), kind)
}
#[inline]
pub unsafe fn replace(
&mut self,
idx: usize,
bits: u64,
kind: NativeKind,
) -> (u64, NativeKind) {
debug_assert_eq!(
self.bits.len(),
self.kinds.len(),
"ClosureCell::replace on desynced cell store"
);
let prev_bits = std::mem::replace(&mut self.bits[idx], bits);
let prev_kind = std::mem::replace(&mut self.kinds[idx], kind);
(prev_bits, prev_kind)
}
#[inline]
pub fn truncate(&mut self, new_len: usize) {
let old_len = self.len();
if new_len >= old_len {
return;
}
for i in (new_len..old_len).rev() {
let bits = self.bits[i];
let kind = self.kinds[i];
unsafe { drop_with_kind(bits, kind) };
}
self.bits.truncate(new_len);
self.kinds.truncate(new_len);
debug_assert_eq!(
self.bits.len(),
self.kinds.len(),
"ClosureCell::truncate violated bits.len() == kinds.len() invariant"
);
}
}
impl Default for ClosureCell {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Drop for ClosureCell {
fn drop(&mut self) {
self.truncate(0);
}
}
#[cfg(test)]
mod closure_cell_tests {
use super::*;
#[test]
fn empty_cell_satisfies_invariant() {
let cell = ClosureCell::new();
assert_eq!(cell.len(), 0);
assert!(cell.is_empty());
assert_eq!(cell.bits.len(), cell.kinds.len());
}
#[test]
fn push_pop_inline_scalars_round_trip() {
let mut cell = ClosureCell::with_capacity(4);
unsafe {
cell.push(42u64, NativeKind::Int64);
cell.push(f64::to_bits(3.14), NativeKind::Float64);
cell.push(1u64, NativeKind::Bool);
}
assert_eq!(cell.len(), 3);
assert_eq!(cell.pop(), Some((1u64, NativeKind::Bool)));
assert_eq!(cell.pop(), Some((f64::to_bits(3.14), NativeKind::Float64)));
assert_eq!(cell.pop(), Some((42u64, NativeKind::Int64)));
assert_eq!(cell.pop(), None);
}
#[test]
fn read_returns_lockstep_pair() {
let mut cell = ClosureCell::new();
unsafe {
cell.push(7u64, NativeKind::Int64);
cell.push(0u64, NativeKind::Bool);
}
assert_eq!(cell.read(0), (7u64, NativeKind::Int64));
assert_eq!(cell.read(1), (0u64, NativeKind::Bool));
}
#[test]
fn replace_returns_previous_pair() {
let mut cell = ClosureCell::new();
unsafe {
cell.push(1u64, NativeKind::Int64);
let prev = cell.replace(0, 99u64, NativeKind::UInt64);
assert_eq!(prev, (1u64, NativeKind::Int64));
assert_eq!(cell.read(0), (99u64, NativeKind::UInt64));
}
}
#[test]
fn truncate_drops_tail() {
let mut cell = ClosureCell::new();
unsafe {
cell.push(1u64, NativeKind::Int64);
cell.push(2u64, NativeKind::Int64);
cell.push(3u64, NativeKind::Int64);
}
cell.truncate(1);
assert_eq!(cell.len(), 1);
assert_eq!(cell.read(0), (1u64, NativeKind::Int64));
}
#[test]
fn drop_releases_all_cells() {
let mut cell = ClosureCell::new();
let slot = KindedSlot::from_string("hello §2.7.8");
let bits = slot.slot.raw();
let kind = slot.kind;
std::mem::forget(slot); unsafe { cell.push(bits, kind) };
assert_eq!(cell.len(), 1);
drop(cell);
}
#[test]
fn pop_then_explicit_drop_round_trip() {
let mut cell = ClosureCell::new();
let slot = KindedSlot::from_string("popped");
let bits = slot.slot.raw();
let kind = slot.kind;
std::mem::forget(slot);
unsafe { cell.push(bits, kind) };
let (b, k) = cell.pop().expect("non-empty");
unsafe { drop_with_kind(b, k) };
}
}
#[cfg(test)]
mod owned_closure_block_kinded_tests {
use super::*;
use crate::v2::closure_layout::{CaptureKind, ClosureLayout};
use crate::v2::concrete_type::ConcreteType;
use std::sync::Arc;
fn arc_immutable_layout(types: &[ConcreteType]) -> Arc<ClosureLayout> {
let kinds = vec![CaptureKind::Immutable; types.len()];
Arc::new(ClosureLayout::from_capture_types(types, &kinds))
}
#[test]
fn read_capture_kinded_inline_scalar_returns_layout_kind() {
let layout = arc_immutable_layout(&[ConcreteType::I64]);
unsafe {
let ptr = alloc_typed_closure(0, 0, &layout);
write_capture_typed(ptr, &layout, 0, 0xDEAD_BEEF_CAFE_BABE);
let block = OwnedClosureBlock::from_raw(ptr, Arc::clone(&layout));
let (bits, kind) = block.read_capture_kinded(0);
assert_eq!(bits, 0xDEAD_BEEF_CAFE_BABE);
assert_eq!(kind, NativeKind::Int64);
}
}
#[test]
fn read_capture_kinded_f64_returns_float_kind() {
let layout = arc_immutable_layout(&[ConcreteType::F64]);
unsafe {
let ptr = alloc_typed_closure(0, 0, &layout);
write_capture_typed(ptr, &layout, 0, f64::to_bits(2.5));
let block = OwnedClosureBlock::from_raw(ptr, Arc::clone(&layout));
let (bits, kind) = block.read_capture_kinded(0);
assert_eq!(f64::from_bits(bits), 2.5);
assert_eq!(kind, NativeKind::Float64);
}
}
#[test]
fn read_capture_kinded_string_returns_string_kind() {
let layout = arc_immutable_layout(&[ConcreteType::String]);
unsafe {
let ptr = alloc_typed_closure(0, 0, &layout);
let block = OwnedClosureBlock::from_raw(ptr, Arc::clone(&layout));
let (bits, kind) = block.read_capture_kinded(0);
assert_eq!(bits, 0);
assert_eq!(kind, NativeKind::String);
}
}
#[test]
fn read_capture_kinded_multiple_captures_lockstep() {
let layout = arc_immutable_layout(&[
ConcreteType::F64,
ConcreteType::I32,
ConcreteType::Bool,
]);
unsafe {
let ptr = alloc_typed_closure(0, 0, &layout);
write_capture_typed(ptr, &layout, 0, f64::to_bits(1.5));
write_capture_typed(ptr, &layout, 1, (-7i32) as u32 as u64);
write_capture_typed(ptr, &layout, 2, 1);
let block = OwnedClosureBlock::from_raw(ptr, Arc::clone(&layout));
let (b0, k0) = block.read_capture_kinded(0);
assert_eq!(f64::from_bits(b0), 1.5);
assert_eq!(k0, NativeKind::Float64);
let (b1, k1) = block.read_capture_kinded(1);
assert_eq!(b1 as i32, -7);
assert_eq!(k1, NativeKind::Int32);
let (b2, k2) = block.read_capture_kinded(2);
assert_eq!(b2 & 0xFF, 1);
assert_eq!(k2, NativeKind::Bool);
}
}
}