use std::{
self,
any::TypeId,
cell::{RefCell, UnsafeCell},
marker::PhantomData,
sync::{
Arc, LazyLock, Mutex,
atomic::{AtomicUsize, Ordering},
},
};
use crate::ui::Widget;
#[derive(Debug)]
pub struct RwData<T: ?Sized> {
value: Arc<UnsafeCell<T>>,
cur_state: Arc<AtomicUsize>,
read_state: Arc<AtomicUsize>,
ty: TypeId,
}
impl<T: 'static> RwData<T> {
pub fn new(value: T) -> Self {
Self {
value: Arc::new(UnsafeCell::new(value)),
ty: TypeId::of::<T>(),
cur_state: Arc::new(AtomicUsize::new(1)),
read_state: Arc::new(AtomicUsize::new(0)),
}
}
}
impl<T: ?Sized> RwData<T> {
#[doc(hidden)]
pub unsafe fn new_unsized<SizedT: 'static>(value: Arc<UnsafeCell<T>>) -> Self {
Self {
value,
ty: TypeId::of::<SizedT>(),
cur_state: Arc::new(AtomicUsize::new(1)),
read_state: Arc::new(AtomicUsize::new(0)),
}
}
pub fn read<'p>(&'p self, _: &'p Pass) -> &'p T {
self.read_state
.store(self.cur_state.load(Ordering::Relaxed), Ordering::Relaxed);
unsafe { &*self.value.get() }
}
pub fn read_as<'p, U: 'static>(&'p self, _: &'p Pass) -> Option<&'p U> {
if TypeId::of::<U>() != self.ty {
return None;
}
self.read_state
.store(self.cur_state.load(Ordering::Relaxed), Ordering::Relaxed);
let ptr = Arc::as_ptr(&self.value) as *const UnsafeCell<U>;
Some(unsafe { &*(&*ptr).get() })
}
pub fn declare_as_read(&self) {
self.read_state
.store(self.cur_state.load(Ordering::Relaxed), Ordering::Relaxed);
}
pub fn write<'p>(&'p self, _: &'p mut Pass) -> &'p mut T {
let prev = self.cur_state.fetch_add(1, Ordering::Relaxed);
self.read_state.store(prev + 1, Ordering::Relaxed);
unsafe { &mut *self.value.get() }
}
pub fn write_as<'p, U: 'static>(&'p self, _: &'p mut Pass) -> Option<&'p mut U> {
if TypeId::of::<U>() != self.ty {
return None;
}
let prev = self.cur_state.fetch_add(1, Ordering::Relaxed);
self.read_state.store(prev + 1, Ordering::Relaxed);
let ptr = Arc::as_ptr(&self.value) as *const UnsafeCell<U>;
Some(unsafe { &mut *(&*ptr).get() })
}
#[track_caller]
#[allow(static_mut_refs)]
pub fn write_then<'p, Tup: WriteableTuple<'p, impl std::any::Any>>(
&'p self,
pa: &'p mut Pass,
tup_fn: impl FnOnce(&'p T) -> Tup,
) -> (&'p mut T, Tup::Return) {
let tup = tup_fn(self.read(pa));
if tup
.state_ptrs()
.into_iter()
.any(|ptr| ptr == CurStatePtr(&self.cur_state))
{
panic!("Tried writing to the same data multiple times at the same time");
}
static PASS: Pass = unsafe { Pass::new() };
let tup_ret = unsafe { (&raw const PASS as *mut Pass).as_mut() }
.unwrap()
.write_many(tup);
let value = self.write(unsafe { (&raw const PASS as *mut Pass).as_mut() }.unwrap());
(value, tup_ret)
}
pub fn declare_written(&self) {
let prev = self.cur_state.fetch_add(1, Ordering::Relaxed);
self.read_state.store(prev + 1, Ordering::Relaxed);
}
pub fn take(&self, pa: &mut Pass) -> T
where
T: Default,
{
std::mem::take(self.write(pa))
}
pub fn map<Ret: 'static>(&self, map: impl FnMut(&T) -> Ret + 'static) -> DataMap<T, Ret> {
let RwData { value, cur_state, .. } = self.clone();
let data = RwData {
value,
cur_state,
read_state: Arc::new(AtomicUsize::new(self.cur_state.load(Ordering::Relaxed))),
ty: TypeId::of::<T>(),
};
DataMap { data, map: Arc::new(RefCell::new(map)) }
}
pub fn map_mut<Ret: 'static>(
&self,
map: impl FnMut(&mut T) -> Ret + 'static,
) -> MutDataMap<T, Ret> {
let RwData { value, cur_state, .. } = self.clone();
let data = RwData {
value,
cur_state,
read_state: Arc::new(AtomicUsize::new(self.cur_state.load(Ordering::Relaxed))),
ty: TypeId::of::<T>(),
};
MutDataMap { data, map: Arc::new(RefCell::new(map)) }
}
pub fn try_downcast<U: 'static>(&self) -> Option<RwData<U>> {
if TypeId::of::<U>() != self.ty {
return None;
}
let ptr = Arc::into_raw(self.value.clone());
let value = unsafe { Arc::from_raw(ptr as *const UnsafeCell<U>) };
Some(RwData {
value,
cur_state: self.cur_state.clone(),
read_state: Arc::new(AtomicUsize::new(self.cur_state.load(Ordering::Relaxed) - 1)),
ty: TypeId::of::<U>(),
})
}
pub fn ptr_eq<U: ?Sized>(&self, other: &RwData<U>) -> bool {
Arc::ptr_eq(&self.cur_state, &other.cur_state)
}
pub fn type_id(&self) -> TypeId {
self.ty
}
pub fn is<U: 'static>(&self) -> bool {
self.ty == TypeId::of::<U>()
}
pub fn has_changed(&self) -> bool {
self.read_state.load(Ordering::Relaxed) < self.cur_state.load(Ordering::Relaxed)
}
pub fn checker(&self) -> impl Fn() -> bool + Send + Sync + 'static {
let (read, cur) = (self.read_state.clone(), self.cur_state.clone());
move || read.load(Ordering::Relaxed) < cur.load(Ordering::Relaxed)
}
}
impl<W: Widget> RwData<W> {
pub fn to_dyn_widget(&self) -> RwData<dyn Widget> {
let ptr = Arc::into_raw(self.value.clone());
let value = unsafe { Arc::from_raw(ptr as *const UnsafeCell<dyn Widget>) };
RwData {
value,
cur_state: self.cur_state.clone(),
read_state: Arc::new(AtomicUsize::new(self.cur_state.load(Ordering::Relaxed) - 1)),
ty: self.ty,
}
}
}
unsafe impl<T: ?Sized + Send> Send for RwData<T> {}
unsafe impl<T: ?Sized + Send> Sync for RwData<T> {}
impl<T: ?Sized> Clone for RwData<T> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
ty: self.ty,
cur_state: self.cur_state.clone(),
read_state: Arc::new(AtomicUsize::new(self.cur_state.load(Ordering::Relaxed) - 1)),
}
}
}
impl<T: Default + 'static> Default for RwData<T> {
fn default() -> Self {
Self {
value: Arc::default(),
cur_state: Arc::new(AtomicUsize::new(1)),
read_state: Arc::new(AtomicUsize::new(0)),
ty: TypeId::of::<T>(),
}
}
}
pub struct DataMap<I: ?Sized + 'static, O: 'static> {
data: RwData<I>,
map: Arc<RefCell<dyn FnMut(&I) -> O>>,
}
impl<I: ?Sized, O> DataMap<I, O> {
pub fn call(&self, pa: &Pass) -> O {
self.map.borrow_mut()(self.data.read(pa))
}
pub fn map<O2>(self, mut f: impl FnMut(O) -> O2 + 'static) -> DataMap<I, O2> {
self.data.map(move |input| f(self.map.borrow_mut()(input)))
}
pub fn has_changed(&self) -> bool {
self.data.has_changed()
}
pub fn checker(&self) -> impl Fn() -> bool + Send + Sync + 'static {
self.data.checker()
}
}
unsafe impl<I: ?Sized + 'static, O: 'static> Send for DataMap<I, O> {}
unsafe impl<I: ?Sized + 'static, O: 'static> Sync for DataMap<I, O> {}
pub struct MutDataMap<I: ?Sized + 'static, O: 'static> {
data: RwData<I>,
map: Arc<RefCell<dyn FnMut(&mut I) -> O>>,
}
impl<I: ?Sized, O> MutDataMap<I, O> {
pub fn call(&self, pa: &mut Pass) -> O {
self.map.borrow_mut()(self.data.write(pa))
}
pub fn map<O2>(self, mut f: impl FnMut(O) -> O2 + 'static) -> MutDataMap<I, O2> {
self.data
.map_mut(move |input| f(self.map.borrow_mut()(input)))
}
pub fn has_changed(&self) -> bool {
self.data.has_changed()
}
pub fn checker(&self) -> impl Fn() -> bool + Send + Sync + 'static {
self.data.checker()
}
}
unsafe impl<I: ?Sized + 'static, O: 'static> Send for MutDataMap<I, O> {}
unsafe impl<I: ?Sized + 'static, O: 'static> Sync for MutDataMap<I, O> {}
#[derive(Default)]
pub struct BulkDataWriter<Data: Default + 'static> {
actions: LazyLock<Arc<Mutex<Vec<Box<dyn FnOnce(&mut Data) + Send + 'static>>>>>,
data: LazyLock<RwData<Data>>,
}
impl<Data: Default + 'static> BulkDataWriter<Data> {
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
Self {
actions: LazyLock::new(|| Arc::new(Mutex::new(Vec::new()))),
data: LazyLock::new(|| RwData::new(Data::default())),
}
}
pub fn mutate(&self, f: impl FnOnce(&mut Data) + Send + 'static) {
self.actions.lock().unwrap().push(Box::new(f));
}
pub fn write<'p>(&'p self, pa: &'p mut Pass) -> &'p mut Data {
let data = self.data.write(pa);
for action in self.actions.lock().unwrap().drain(..) {
action(data);
}
data
}
pub fn try_read<'p>(&'p self, pa: &'p Pass) -> Option<&'p Data> {
self.actions
.lock()
.unwrap()
.is_empty()
.then(|| self.data.read(pa))
}
pub fn map_mut<Ret: 'static>(
&self,
mut map: impl FnMut(&mut Data) -> Ret + 'static,
) -> MutDataMap<Data, Ret> {
let actions = self.actions.clone();
self.data.map_mut(move |data| {
for action in actions.lock().unwrap().drain(..) {
action(data);
}
map(data)
})
}
}
pub struct Pass(PhantomData<()>);
impl Pass {
pub(crate) const unsafe fn new() -> Self {
Pass(PhantomData)
}
#[track_caller]
pub fn write_many<'p, Tup: WriteableTuple<'p, impl std::any::Any>>(
&'p mut self,
tup: Tup,
) -> Tup::Return {
if let Some(ret) = tup.write_all(self) {
ret
} else {
panic!("Tried writing to the same data multiple times");
}
}
pub fn try_write_many<'p, Tup: WriteableTuple<'p, impl std::any::Any>>(
&'p mut self,
tup: Tup,
) -> Option<Tup::Return> {
tup.write_all(self)
}
}
#[doc(hidden)]
pub trait WriteableTuple<'p, _Dummy> {
type Return;
#[doc(hidden)]
fn write_all(self, pa: &'p mut Pass) -> Option<Self::Return>;
#[doc(hidden)]
fn state_ptrs(&self) -> impl IntoIterator<Item = CurStatePtr<'_>>;
}
macro_rules! implWriteableTuple {
($(($tup:ident, $dummy:ident)),+) => {
#[allow(non_snake_case)]
impl<'p, $($tup),+, $($dummy),+> WriteableTuple<'p, ($(&mut $dummy),+)> for ($($tup),+)
where
$($tup: WriteableTuple<'p, $dummy>),+
{
type Return = ($($tup::Return),+);
fn write_all(self, _: &'p mut Pass) -> Option<Self::Return> {
if self.state_ptrs().into_iter().enumerate().any(|(lhs, i)| {
self.state_ptrs()
.into_iter()
.enumerate()
.any(|(rhs, j)| lhs == rhs && i != j)
}) {
return None;
}
let ($($tup),+) = self;
static PASS: Pass = unsafe { Pass::new() };
Some(($(
$tup.write_all(
unsafe { (&raw const PASS as *mut Pass).as_mut() }.unwrap()
)
.unwrap()
),+))
}
fn state_ptrs(&self) -> impl IntoIterator<Item = CurStatePtr<'_>> {
let ($($tup),+) = self;
implWriteableTuple!(@chain $($tup),+)
}
}
};
(@chain $tup:ident $(, $rest:ident)*) => {
$tup.state_ptrs().into_iter().chain(implWriteableTuple!(@chain $($rest),*))
};
(@chain ) => { [] };
}
impl<'p, Data, T> WriteableTuple<'p, (&mut T,)> for &'p Data
where
Data: WriteableData<'p, T>,
T: ?Sized + 'p,
{
type Return = &'p mut T;
fn write_all(self, pa: &'p mut Pass) -> Option<Self::Return> {
Some(self.write_one_of_many(pa))
}
fn state_ptrs(&self) -> impl IntoIterator<Item = CurStatePtr<'_>> {
[self.cur_state_ptr()]
}
}
implWriteableTuple!((D0, T0), (D1, T1));
implWriteableTuple!((D0, T0), (D1, T1), (D2, T2));
implWriteableTuple!((D0, T0), (D1, T1), (D2, T2), (D3, T3));
implWriteableTuple!((D0, T0), (D1, T1), (D2, T2), (D3, T3), (D4, T4));
implWriteableTuple!((D0, T0), (D1, T1), (D2, T2), (D3, T3), (D4, T4), (D5, T5));
#[rustfmt::skip]
implWriteableTuple!((D0, T0), (D1, T1), (D2, T2), (D3, T3), (D4, T4), (D5, T5), (D6, T6));
#[rustfmt::skip]
implWriteableTuple!((D0, T0), (D1, T1), (D2, T2), (D3, T3), (D4, T4), (D5, T5), (D6, T6), (D7, T7));
#[rustfmt::skip]
implWriteableTuple!(
(D0, T0), (D1, T1), (D2, T2), (D3, T3), (D4, T4), (D5, T5), (D6, T6), (D7, T7) , (D8, T8)
);
#[rustfmt::skip]
implWriteableTuple!(
(D0, T0), (D1, T1), (D2, T2), (D3, T3), (D4, T4), (D5, T5), (D6, T6), (D7, T7) , (D8, T8),
(D9, T9)
);
#[rustfmt::skip]
implWriteableTuple!(
(D0, T0), (D1, T1), (D2, T2), (D3, T3), (D4, T4), (D5, T5), (D6, T6), (D7, T7) , (D8, T8),
(D9, T9), (D10, T10)
);
#[rustfmt::skip]
implWriteableTuple!(
(D0, T0), (D1, T1), (D2, T2), (D3, T3), (D4, T4), (D5, T5), (D6, T6), (D7, T7) , (D8, T8),
(D9, T9), (D10, T10), (D11, T11)
);
impl<'p, const N: usize, Tup, Dummy> WriteableTuple<'p, [Dummy; N]> for [Tup; N]
where
Tup: WriteableTuple<'p, Dummy> + 'p,
{
type Return = [Tup::Return; N];
fn write_all(self, _: &'p mut Pass) -> Option<Self::Return> {
if self.state_ptrs().into_iter().enumerate().any(|(lhs, i)| {
self.state_ptrs()
.into_iter()
.enumerate()
.any(|(rhs, j)| lhs == rhs && i != j)
}) {
return None;
}
static PASS: Pass = unsafe { Pass::new() };
Some(self.map(|tup| {
let pa = &raw const PASS as *mut Pass;
tup.write_all(unsafe { pa.as_mut() }.unwrap()).unwrap()
}))
}
fn state_ptrs(&self) -> impl IntoIterator<Item = CurStatePtr<'_>> {
self.iter().flat_map(|tup| tup.state_ptrs())
}
}
#[doc(hidden)]
pub trait WriteableData<'p, T: ?Sized + 'p>: InnerWriteableData {
#[doc(hidden)]
fn write_one_of_many(&'p self, pa: &'p mut Pass) -> &'p mut T;
#[doc(hidden)]
fn cur_state_ptr(&self) -> CurStatePtr<'_>;
}
impl<'p, T: ?Sized + 'p> WriteableData<'p, T> for RwData<T> {
fn write_one_of_many(&'p self, pa: &'p mut Pass) -> &'p mut T {
self.write(pa)
}
fn cur_state_ptr(&self) -> CurStatePtr<'_> {
CurStatePtr(&self.cur_state)
}
}
impl<'p, T: Default> WriteableData<'p, T> for BulkDataWriter<T> {
fn write_one_of_many(&'p self, pa: &'p mut Pass) -> &'p mut T {
self.write(pa)
}
fn cur_state_ptr(&self) -> CurStatePtr<'_> {
CurStatePtr(&self.data.cur_state)
}
}
impl<'p, W: Widget> WriteableData<'p, W> for crate::context::Handle<W> {
fn write_one_of_many(&'p self, pa: &'p mut Pass) -> &'p mut W {
self.write(pa)
}
fn cur_state_ptr(&self) -> CurStatePtr<'_> {
CurStatePtr(&self.widget().cur_state)
}
}
impl<'p> WriteableData<'p, crate::ui::Area> for crate::ui::RwArea {
fn write_one_of_many(&'p self, pa: &'p mut Pass) -> &'p mut crate::ui::Area {
self.write(pa)
}
fn cur_state_ptr(&self) -> CurStatePtr<'_> {
CurStatePtr(&self.0.cur_state)
}
}
trait InnerWriteableData {}
impl<T: ?Sized> InnerWriteableData for RwData<T> {}
impl<T: Default> InnerWriteableData for BulkDataWriter<T> {}
impl<W: Widget> InnerWriteableData for crate::context::Handle<W> {}
impl InnerWriteableData for crate::ui::RwArea {}
#[doc(hidden)]
#[derive(Clone, Copy)]
pub struct CurStatePtr<'p>(&'p Arc<AtomicUsize>);
impl std::cmp::PartialEq for CurStatePtr<'_> {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(self.0, other.0)
}
}