1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use std::{any::Any, fmt};
use fxhash::FxHashMap;
use linkme::distributed_slice;
use serde::{Deserialize, Serialize};
use smallbox::{smallbox, SmallBox};
pub type LocalTypeId = u32;
pub trait Message: fmt::Debug + Clone + Any + Send + Serialize + for<'de> Deserialize<'de> {
#[doc(hidden)]
const _LTID: LocalTypeId;
}
pub trait Request: Message {
type Response: fmt::Debug;
#[doc(hidden)]
type Wrapper: Message + Into<Self::Response> + From<Self::Response>;
}
pub struct AnyMessage {
ltid: LocalTypeId,
data: SmallBox<dyn Any + Send, [u8; 64]>,
}
impl AnyMessage {
pub fn new<M: Message>(message: M) -> Self {
AnyMessage {
ltid: M::_LTID,
data: smallbox!(message),
}
}
pub fn is<T: 'static>(&self) -> bool {
self.data.is::<T>()
}
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
self.data.downcast_ref()
}
pub fn downcast<M: Message>(self) -> Result<M, AnyMessage> {
if M::_LTID != self.ltid {
return Err(self);
}
Ok(self
.data
.downcast::<M>()
.expect("cannot downcast")
.into_inner())
}
}
impl Clone for AnyMessage {
#[inline]
fn clone(&self) -> Self {
with_vtable(self.ltid, |vtable| (vtable.clone)(self))
}
}
impl fmt::Debug for AnyMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
with_vtable(self.ltid, |vtable| (vtable.debug)(self, f))
}
}
#[derive(Clone)]
pub struct MessageVTable {
pub ltid: LocalTypeId,
pub clone: fn(&AnyMessage) -> AnyMessage,
pub debug: fn(&AnyMessage, &mut fmt::Formatter<'_>) -> fmt::Result,
}
#[distributed_slice]
pub static MESSAGE_LIST: [MessageVTable] = [..];
thread_local! {
static MESSAGE_BY_LTID: FxHashMap<LocalTypeId, MessageVTable> = {
MESSAGE_LIST.iter()
.map(|vtable| (vtable.ltid, vtable.clone()))
.collect()
};
}
fn with_vtable<R>(ltid: LocalTypeId, f: impl FnOnce(&MessageVTable) -> R) -> R {
MESSAGE_BY_LTID.with(|map| f(map.get(<id).expect("invalid LTID")))
}
pub(crate) fn init() {
MESSAGE_BY_LTID.with(|_| ());
}