use crate::heap;
use crate::panic::{RuntimeError, runtime_error};
use crate::trace::{Trace, Tracer};
use crate::value::GoValue;
use core::cell::Cell;
use core::ptr::NonNull;
pub trait Place: 'static {
type Value: GoValue;
fn new(v: Self::Value) -> Self;
fn load(&self) -> Self::Value;
fn store(&self, v: Self::Value);
}
#[repr(transparent)]
pub struct Slot<T>(Cell<T>);
impl<T: GoValue> Place for Slot<T> {
type Value = T;
#[inline]
fn new(v: T) -> Self {
Slot(Cell::new(v))
}
#[inline]
fn load(&self) -> T {
self.0.get()
}
#[inline]
fn store(&self, v: T) {
self.0.set(v)
}
}
impl<P: Place, const N: usize> Place for [P; N] {
type Value = [P::Value; N];
fn new(v: Self::Value) -> Self {
core::array::from_fn(|i| P::new(v[i]))
}
fn load(&self) -> Self::Value {
core::array::from_fn(|i| self[i].load())
}
fn store(&self, v: Self::Value) {
for (p, v) in self.iter().zip(v) {
p.store(v);
}
}
}
#[inline]
fn zst_normalized<P>(p: NonNull<P>) -> NonNull<P> {
if size_of::<P>() == 0 {
crate::heap::zerobase().cast()
} else {
p
}
}
pub struct Ptr<P: 'static>(Option<NonNull<P>>);
impl<P> Clone for Ptr<P> {
fn clone(&self) -> Self {
*self
}
}
impl<P> Copy for Ptr<P> {}
impl<P> PartialEq for Ptr<P> {
fn eq(&self, other: &Self) -> bool {
self.addr() == other.addr()
}
}
impl<P> Eq for Ptr<P> {}
impl<P> GoValue for Ptr<P> {
#[inline]
fn zero() -> Self {
Ptr(None)
}
}
impl<P: Place + Trace> Ptr<P> {
pub fn alloc(v: P::Value) -> Self {
Ptr(Some(heap::allocate(P::new(v))))
}
}
impl<P: Place> Ptr<P> {
#[inline]
pub fn load(self) -> P::Value {
self.place().load()
}
#[inline]
pub fn store(self, v: P::Value) {
self.place().store(v)
}
}
impl<P> Ptr<P> {
pub const NIL: Self = Ptr(None);
#[inline]
pub fn zero() -> Self {
Ptr(None)
}
#[inline]
pub unsafe fn from_addr(addr: usize) -> Self {
Ptr(NonNull::new(addr as *mut P))
}
#[inline]
pub fn to_place(p: &P) -> Self {
Ptr(Some(zst_normalized(NonNull::from(p))))
}
#[inline]
pub fn to_global(p: &'static P) -> Self {
Ptr(Some(zst_normalized(NonNull::from(p))))
}
#[inline]
pub fn project<Q>(self, f: impl FnOnce(&P) -> &Q) -> Ptr<Q> {
let q = NonNull::from(f(self.place()));
Ptr(Some(zst_normalized(q)))
}
#[inline]
pub(crate) fn place(self) -> &'static P {
match self.0 {
Some(p) => {
heap::check_live(p.as_ptr() as usize);
unsafe { p.as_ref() }
}
None => runtime_error(RuntimeError::NilDeref),
}
}
#[inline]
pub fn nil_checked(self) -> Self {
let _ = self.place();
self
}
#[inline]
pub fn addr(self) -> u64 {
self.0.map_or(0, |p| p.as_ptr() as usize as u64)
}
}
impl<P, const N: usize> Ptr<[P; N]> {
#[inline]
pub fn to_slice(self) -> crate::slice::Slice<P> {
crate::slice::of_array(self.place())
}
}
impl<P> Trace for Ptr<P> {
#[inline]
fn trace(&self, t: &mut Tracer<'_>) {
t.edge(self.addr() as usize);
}
}
impl<T: Trace + GoValue> Trace for Slot<T> {
#[inline]
fn trace(&self, t: &mut Tracer<'_>) {
self.load().trace(t);
}
}