use std::any::{type_name, Any}; use std::io::{Read, Write}; use std::num::NonZeroU64; use std::ops::Deref; use std::sync::Arc; use std::{fmt, mem}; use derive_destructure::destructure; use orchid_api::atom::{Atom, Fwd}; use orchid_api::expr::{ExprTicket, Release}; use orchid_api::system::SysId; use orchid_api_traits::{Coding, Decode, Encode, Request}; use orchid_base::id_store::{IdRecord, IdStore}; use orchid_base::reqnot::Requester as _; use typeid::ConstTypeId; use crate::expr::GenClause; use crate::other_system::SystemHandle; use crate::system::{DynSystemCard, SystemCard}; pub trait AtomCard: 'static + Sized { type Owner: SystemCard; type Data: Clone + Coding + Sized; type Req: Coding; } pub fn get_info(sys: &(impl DynSystemCard + ?Sized)) -> (usize, &AtomInfo) { sys.atom_info_for(ConstTypeId::of::()).unwrap_or_else(|| { panic!("Atom {} not associated with system {}", type_name::(), sys.name()) }) } pub fn encode_atom_nodrop( sys_id: SysId, sys: &(impl DynSystemCard + ?Sized), data: &A::Data, ) -> Atom { let (info_pos, _) = get_info::(sys); let mut buf = (info_pos as u64).enc_vec(); data.encode(&mut buf); Atom { owner: sys_id, drop: false, data: buf } } pub fn encode_atom_drop( sys_id: SysId, sys: &(impl DynSystemCard + ?Sized), atom_id: u64, data: &A::Data, ) -> Atom { let (info_pos, _) = get_info::(sys); let mut buf = (info_pos as u64).enc_vec(); atom_id.encode(&mut buf); data.encode(&mut buf); Atom { owner: sys_id, drop: true, data: buf } } pub fn decode_atom( sys: &(impl DynSystemCard + ?Sized), Atom { data, drop, owner: _ }: &Atom, ) -> Option { let (info_pos, info) = get_info::(sys); let mut data = &data[..]; if u64::decode(&mut data) != info_pos as u64 { return None; } let val = (info.decode)(data); Some(*val.downcast().expect("The type-id checked out, the decode should've worked")) } #[derive(destructure)] pub struct ForeignAtom { pub(crate) sys: SystemHandle, pub(crate) ticket: ExprTicket, pub(crate) api: Atom, pub(crate) value: A::Data, } impl ForeignAtom { /// Unpack the object, returning the held atom and expr ticket. This is in /// contrast to dropping the atom which releases the expr ticket. pub fn unpack(self) -> (A::Data, ExprTicket, Atom) { let (_, ticket, api, value) = self.destructure(); (value, ticket, api) } pub fn ticket(&self) -> ExprTicket { self.ticket } pub fn request + Request>(&self, req: R) -> R::Response { R::Response::decode(&mut &self.sys.reqnot.request(Fwd(self.api.clone(), req.enc_vec()))[..]) } } impl Deref for ForeignAtom { type Target = A::Data; fn deref(&self) -> &Self::Target { &self.value } } impl Drop for ForeignAtom { fn drop(&mut self) { self.sys.reqnot.notify(Release(self.sys.id(), self.ticket)) } } pub struct AtomInfo { pub tid: ConstTypeId, pub decode: fn(&[u8]) -> Box, pub call: fn(&[u8], ExprTicket) -> GenClause, pub call_ref: fn(&[u8], ExprTicket) -> GenClause, pub same: fn(&[u8], &[u8]) -> bool, pub handle_req: fn(&[u8], &mut dyn Read, &mut dyn Write), pub drop: fn(&[u8]), } pub trait ThinAtom: AtomCard + Coding + fmt::Debug { fn call(&self, arg: ExprTicket) -> GenClause; fn same(&self, other: &Self) -> bool; fn handle_req(&self, req: Self::Req, rep: &mut (impl Write + ?Sized)); } pub const fn thin_atom_info() -> AtomInfo { AtomInfo { tid: ConstTypeId::of::(), decode: |mut b| Box::new(T::decode(&mut b)), call: |mut b, extk| T::decode(&mut b).call(extk), call_ref: |mut b, extk| T::decode(&mut b).call(extk), handle_req: |mut b, req, rep| T::decode(&mut b).handle_req(Decode::decode(req), rep), same: |mut b1, mut b2| T::decode(&mut b1).same(&T::decode(&mut b2)), drop: |mut b1| eprintln!("Received drop signal for non-drop atom {:?}", T::decode(&mut b1)), } } /// Atoms that have a [Drop] pub trait OwnedAtom: AtomCard + Deref + Send + Sync + Any + 'static { fn call_ref(&self, arg: ExprTicket) -> GenClause; fn call(self, arg: ExprTicket) -> GenClause; fn same(&self, other: &Self) -> bool; fn handle_req(&self, req: Self::Req, rep: &mut (impl Write + ?Sized)); } pub trait DynOwnedAtom: Send + Sync + 'static { fn atom_tid(&self) -> ConstTypeId; fn as_any_ref(&self) -> &dyn Any; fn dyn_call_ref(&self, arg: ExprTicket) -> GenClause; fn dyn_call(self: Box, arg: ExprTicket) -> GenClause; fn dyn_same(&self, other: &dyn DynOwnedAtom) -> bool; fn dyn_handle_req(&self, req: &mut dyn Read, rep: &mut dyn Write); } impl DynOwnedAtom for T { fn atom_tid(&self) -> ConstTypeId { ConstTypeId::of::() } fn as_any_ref(&self) -> &dyn Any { self } fn dyn_call_ref(&self, arg: ExprTicket) -> GenClause { self.call_ref(arg) } fn dyn_call(self: Box, arg: ExprTicket) -> GenClause { self.call(arg) } fn dyn_same(&self, other: &dyn DynOwnedAtom) -> bool { if ConstTypeId::of::() != other.as_any_ref().type_id() { return false; } let other_self = other.as_any_ref().downcast_ref().expect("The type_ids are the same"); self.same(other_self) } fn dyn_handle_req(&self, req: &mut dyn Read, rep: &mut dyn Write) { self.handle_req(::Req::decode(req), rep) } } pub(crate) static OBJ_STORE: IdStore> = IdStore::new(); const fn owned_atom_info() -> AtomInfo { fn with_atom(mut b: &[u8], f: impl FnOnce(IdRecord<'_, Box>) -> U) -> U { f(OBJ_STORE.get(NonZeroU64::decode(&mut b)).expect("Received invalid atom ID")) } AtomInfo { tid: ConstTypeId::of::(), decode: |mut b| Box::new(T::Data::decode(&mut b)), call: |b, arg| with_atom(b, |a| a.remove().dyn_call(arg)), call_ref: |b, arg| with_atom(b, |a| a.dyn_call_ref(arg)), same: |b1, b2| with_atom(b1, |a1| with_atom(b2, |a2| a1.dyn_same(&**a2))), handle_req: |b, req, rep| with_atom(b, |a| a.dyn_handle_req(req, rep)), drop: |b| mem::drop(with_atom(b, |a| a.remove())), } }