use crate::runtime::region_heap::HeapIndex;
use crate::types::RegionId;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
pub struct RRef<T> {
region_id: RegionId,
index: HeapIndex,
_marker: PhantomData<T>,
}
impl<T> Clone for RRef<T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for RRef<T> {}
impl<T> fmt::Debug for RRef<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RRef")
.field("region_id", &self.region_id)
.field("index", &self.index)
.finish()
}
}
impl<T> PartialEq for RRef<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.region_id == other.region_id && self.index == other.index
}
}
impl<T> Eq for RRef<T> {}
impl<T> Hash for RRef<T> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.region_id.hash(state);
self.index.hash(state);
}
}
impl<T> RRef<T> {
#[inline]
#[must_use]
pub const fn region_id(&self) -> RegionId {
self.region_id
}
#[inline]
#[must_use]
pub const fn heap_index(&self) -> HeapIndex {
self.index
}
}
impl<T: Send + Sync + 'static> RRef<T> {
#[inline]
#[must_use]
pub const fn new(region_id: RegionId, index: HeapIndex) -> Self {
Self {
region_id,
index,
_marker: PhantomData,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RRefError {
RegionNotFound(RegionId),
AllocationInvalid,
RegionMismatch {
expected: RegionId,
actual: RegionId,
},
RegionClosed,
WrongRegion,
}
impl fmt::Display for RRefError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::RegionNotFound(id) => write!(f, "region not found: {id:?}"),
Self::AllocationInvalid => write!(f, "heap allocation is invalid"),
Self::RegionMismatch { expected, actual } => {
write!(f, "region mismatch: expected {expected:?}, got {actual:?}")
}
Self::RegionClosed => write!(f, "region is closed"),
Self::WrongRegion => write!(f, "access witness references wrong region"),
}
}
}
impl std::error::Error for RRefError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RRefAccessWitness {
region_id: RegionId,
}
impl RRefAccessWitness {
#[must_use]
pub(crate) const fn new(region_id: RegionId) -> Self {
Self { region_id }
}
#[must_use]
pub const fn region(&self) -> RegionId {
self.region_id
}
}
impl<T> RRef<T> {
pub fn validate_witness(&self, witness: &RRefAccessWitness) -> Result<(), RRefError> {
if witness.region() != self.region_id {
return Err(RRefError::WrongRegion);
}
Ok(())
}
}
pub trait RRefAccess {
fn rref_get<T: Clone + 'static>(&self, rref: &RRef<T>) -> Result<T, RRefError>;
fn rref_with<T: 'static, R, F: FnOnce(&T) -> R>(
&self,
rref: &RRef<T>,
f: F,
) -> Result<R, RRefError>;
fn rref_get_with<T: Clone + 'static>(
&self,
rref: &RRef<T>,
witness: RRefAccessWitness,
) -> Result<T, RRefError>;
fn rref_with_witness<T: 'static, R, F: FnOnce(&T) -> R>(
&self,
rref: &RRef<T>,
witness: RRefAccessWitness,
f: F,
) -> Result<R, RRefError>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::record::RegionRecord;
use crate::types::Budget;
use crate::util::ArenaIndex;
fn test_region_id() -> RegionId {
RegionId::from_arena(ArenaIndex::new(0, 0))
}
#[test]
fn rref_is_copy_and_clone() {
let region_id = test_region_id();
let record = RegionRecord::new(region_id, None, Budget::INFINITE);
let index = record.heap_alloc(42u32).expect("heap alloc");
let rref = RRef::<u32>::new(region_id, index);
let rref2 = rref;
assert_eq!(rref.region_id(), rref2.region_id());
assert_clone::<RRef<u32>>();
}
#[test]
fn rref_equality() {
let region_id = test_region_id();
let record = RegionRecord::new(region_id, None, Budget::INFINITE);
let index1 = record.heap_alloc(1u32).expect("heap alloc");
let index2 = record.heap_alloc(2u32).expect("heap alloc");
let rref1a = RRef::<u32>::new(region_id, index1);
let rref1_clone = RRef::<u32>::new(region_id, index1);
let rref2 = RRef::<u32>::new(region_id, index2);
assert_eq!(rref1a, rref1_clone);
assert_ne!(rref1a, rref2);
}
#[test]
fn rref_accessors() {
let region_id = test_region_id();
let record = RegionRecord::new(region_id, None, Budget::INFINITE);
let index = record.heap_alloc("hello".to_string()).expect("heap alloc");
let rref = RRef::<String>::new(region_id, index);
assert_eq!(rref.region_id(), region_id);
assert_eq!(rref.heap_index(), index);
}
#[test]
fn rref_debug_format() {
let region_id = test_region_id();
let record = RegionRecord::new(region_id, None, Budget::INFINITE);
let index = record.heap_alloc(42u32).expect("heap alloc");
let rref = RRef::<u32>::new(region_id, index);
let debug_str = format!("{rref:?}");
assert!(debug_str.contains("RRef"));
assert!(debug_str.contains("region_id"));
assert!(debug_str.contains("index"));
}
#[test]
fn rref_access_through_region_record() {
let region_id = test_region_id();
let record = RegionRecord::new(region_id, None, Budget::INFINITE);
let index = record.heap_alloc("hello".to_string()).expect("heap alloc");
let rref = RRef::<String>::new(region_id, index);
let value = record.rref_get(&rref).expect("rref_get");
assert_eq!(value, "hello");
let len = record.rref_with(&rref, String::len).expect("rref_with");
assert_eq!(len, 5);
}
#[test]
fn rref_region_mismatch_is_error() {
let region_a = test_region_id();
let region_b = RegionId::from_arena(ArenaIndex::new(1, 0));
let record_a = RegionRecord::new(region_a, None, Budget::INFINITE);
let record_b = RegionRecord::new(region_b, None, Budget::INFINITE);
let index = record_a.heap_alloc(7u32).expect("heap alloc");
let rref = RRef::<u32>::new(region_a, index);
let err = record_b.rref_get(&rref).expect_err("region mismatch");
assert_eq!(
err,
RRefError::RegionMismatch {
expected: region_a,
actual: region_b,
}
);
}
fn assert_clone<T: Clone>() {}
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
#[test]
fn rref_send_sync_bounds() {
assert_send::<RRef<u32>>();
assert_send::<RRef<String>>();
assert_send::<RRef<Vec<i32>>>();
assert_sync::<RRef<u32>>();
assert_sync::<RRef<String>>();
assert_sync::<RRef<Vec<i32>>>();
}
#[test]
fn validate_witness_matching_region_succeeds() {
let rid = test_region_id();
let record = RegionRecord::new(rid, None, Budget::INFINITE);
let index = record.heap_alloc(42u32).expect("heap alloc");
let rref = RRef::<u32>::new(rid, index);
let witness = RRefAccessWitness::new(rid);
assert!(rref.validate_witness(&witness).is_ok());
}
#[test]
fn validate_witness_wrong_region_fails() {
let rid_a = test_region_id();
let rid_b = RegionId::from_arena(ArenaIndex::new(77, 0));
let record = RegionRecord::new(rid_a, None, Budget::INFINITE);
let index = record.heap_alloc(42u32).expect("heap alloc");
let rref = RRef::<u32>::new(rid_a, index);
let wrong_witness = RRefAccessWitness::new(rid_b);
let err = rref.validate_witness(&wrong_witness);
assert_eq!(err.unwrap_err(), RRefError::WrongRegion);
}
#[test]
fn access_witness_is_copy_and_eq() {
let rid = test_region_id();
let w1 = RRefAccessWitness::new(rid);
let w2 = w1; assert_eq!(w1, w2);
assert_eq!(w1.region(), rid);
}
#[test]
fn rref_error_display_coverage() {
let rid = test_region_id();
let cases: Vec<(RRefError, &str)> = vec![
(RRefError::RegionNotFound(rid), "region not found"),
(RRefError::AllocationInvalid, "heap allocation is invalid"),
(
RRefError::RegionMismatch {
expected: rid,
actual: rid,
},
"region mismatch",
),
(RRefError::RegionClosed, "region is closed"),
(
RRefError::WrongRegion,
"access witness references wrong region",
),
];
for (err, expected_substring) in cases {
let msg = format!("{err}");
assert!(
msg.contains(expected_substring),
"expected '{expected_substring}' in '{msg}'"
);
}
}
}