hecs_schedule/context/
mod.rs1use std::{any::TypeId, cmp::Ordering, ptr::NonNull};
4
5use atomic_refcell::AtomicRefCell;
6
7use crate::{borrow::ContextBorrow, Error, IntoAccess, Result};
8use hecs::Component;
9
10pub struct Context<'a> {
13 data: &'a dyn Data,
14}
15
16unsafe impl Send for Context<'_> {}
18unsafe impl Sync for Context<'_> {}
19
20mod erased_cell;
21use erased_cell::*;
22
23impl<'a> Context<'a> {
24 pub fn new(data: &'a dyn Data) -> Context {
26 Self { data }
27 }
28
29 pub fn borrow<T>(&'a self) -> Result<T::Target>
31 where
32 T: ContextBorrow<'a>,
33 {
34 T::borrow(self)
35 }
36
37 pub fn cell<T: IntoAccess>(&'a self) -> Result<&AtomicRefCell<NonNull<u8>>> {
40 let access = T::access();
41 self.data
42 .get(access.id())
43 .ok_or_else(|| Error::MissingData(access.name()))
44 }
45}
46
47pub trait Data {
49 fn get(&self, ty: TypeId) -> Option<&AtomicRefCell<NonNull<u8>>>;
51}
52
53pub trait IntoData<With>: Send + Sync {
55 type Target: Data;
57 unsafe fn into_data(self, with: &mut With) -> Self::Target;
62}
63
64impl<With: Component> IntoData<With> for () {
65 type Target = [ErasedCell; 1];
66
67 unsafe fn into_data(self, with: &mut With) -> Self::Target {
68 [ErasedCell::from_ref(with)]
69 }
70}
71
72macro_rules! tuple_impl {
73 ($([$idx: tt => $name: ident]),*) => {
74 impl<$( $name ), *, With> IntoData<With> for ($(&mut $name,) *)
75 where
76 With: Component,
77 $($name: Component), *
78 {
79 type Target = [ErasedCell; 1 + count!($($name )*)];
80
81 unsafe fn into_data(self, with: &mut With) -> Self::Target {
82
83 let mut val = [
84 $( ErasedCell::from_ref::<$name>(self.$idx),)*
85 ErasedCell::from_ref(with),
86 ];
87
88 val.sort_unstable();
89
90 val
91 }
92 }
93 };
94}
95
96impl_for_tuples_idx!(tuple_impl);
97
98impl<const C: usize> Data for [ErasedCell; C] {
99 fn get(&self, ty: TypeId) -> Option<&AtomicRefCell<NonNull<u8>>> {
100 let mut low = 0;
101 let mut high = C - 1;
102
103 while low <= high {
104 let mid = (high - low) / 2 + low;
105 let val = &self[mid];
106
107 match val.cmp_id(ty) {
108 Ordering::Less => low = mid + 1,
109 Ordering::Equal => return Some(&val.cell),
110 Ordering::Greater if mid == 0 => break,
111 Ordering::Greater => high = mid - 1,
112 }
113 }
114
115 None
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::{Context, IntoData};
122
123 #[test]
124 fn context() {
125 let mut a = 64_i32;
126 let mut b = "Hello, World";
127
128 let data = unsafe { (&mut a, &mut b).into_data(&mut ()) };
129
130 let context = Context::new(&data);
131
132 {
133 let a = context.borrow::<&i32>().unwrap();
134 let mut b = context.borrow::<&mut &str>().unwrap();
135
136 assert_eq!(*b, "Hello, World");
137 *b = "Foo Fighters";
138 drop(b);
139
140 let b = context.borrow::<&&str>().unwrap();
141 assert_eq!(*a, 64);
142 assert_eq!(*b, "Foo Fighters");
143
144 let c = context.borrow::<&f32>();
145 assert!(c.is_err());
146 }
147 }
148}