config_it/config/
group.rs1use bitfield::bitfield;
2use std::any::{Any, TypeId};
3use std::iter::zip;
4use std::sync::atomic::{AtomicU64, Ordering};
5use std::sync::{Arc, Weak};
6use strseq::SharedStringSequence;
7
8use crate::shared::GroupID;
9
10use super::entity::{Entity, EntityData, EntityValue, PropertyInfo};
11use super::noti;
12
13pub trait Template: Clone + 'static {
17 type LocalPropContextArray: LocalPropContextArray;
19
20 #[doc(hidden)]
22 fn props__() -> &'static [PropertyInfo];
23
24 #[doc(hidden)]
26 fn prop_at_offset__(offset: usize) -> Option<&'static PropertyInfo>;
27
28 fn template_name() -> (&'static str, &'static str);
30
31 fn default_config() -> Self;
33
34 #[doc(hidden)]
35 fn elem_at_mut__(&mut self, index: usize) -> &mut dyn Any;
36
37 #[doc(hidden)]
38 fn update_elem_at__(&mut self, index: usize, value: &dyn Any, meta: &PropertyInfo) {
39 let data = self.elem_at_mut__(index);
40 meta.vtable.clone_in_place(value, data);
41 }
42}
43
44#[doc(hidden)]
48pub trait LocalPropContextArray: Clone + Default + std::fmt::Debug {
49 const N: usize;
50
51 fn as_slice(&self) -> &[PropLocalContext];
52 fn as_slice_mut(&mut self) -> &mut [PropLocalContext];
53}
54
55#[doc(hidden)]
56#[derive(Clone, Debug)]
57pub struct LocalPropContextArrayImpl<const N: usize>([PropLocalContext; N]);
58
59impl<const N: usize> LocalPropContextArray for LocalPropContextArrayImpl<N> {
60 const N: usize = N;
61
62 fn as_slice(&self) -> &[PropLocalContext] {
63 &self.0
64 }
65
66 fn as_slice_mut(&mut self) -> &mut [PropLocalContext] {
67 &mut self.0
68 }
69}
70
71impl<const N: usize> Default for LocalPropContextArrayImpl<N> {
72 fn default() -> Self {
73 Self([0; N].map(|_| PropLocalContext::default()))
74 }
75}
76
77#[derive(cs::Debug)]
83pub struct GroupContext {
84 pub group_id: GroupID,
86
87 pub template_type_id: TypeId,
90
91 pub template_name: (&'static str, &'static str),
93
94 pub(crate) sources: Arc<[EntityData]>,
97
98 pub(crate) w_unregister_hook: Weak<dyn Any + Send + Sync>,
101
102 pub(crate) version: AtomicU64,
105
106 pub path: SharedStringSequence,
109
110 pub(crate) update_receiver_channel: noti::Receiver,
113}
114
115mod monitor {
116 use crate::{config::noti, shared::ItemID};
119
120 impl super::GroupContext {
121 pub fn find_item(&self, item_id: ItemID) -> Option<&super::EntityData> {
123 debug_assert!(
124 self.sources.windows(2).all(|w| w[0].id < w[1].id),
125 "Logic Error: Sources are not sorted!"
126 );
127
128 self.sources
129 .binary_search_by(|x| x.id.cmp(&item_id))
130 .map(|index| &self.sources[index])
131 .ok()
132 }
133
134 pub fn watch_update(&self) -> noti::Receiver {
136 self.update_receiver_channel.clone()
137 }
138
139 pub fn entities(&self) -> &[super::EntityData] {
141 &self.sources
142 }
143 }
144}
145
146pub struct Group<T: Template> {
152 __body: T,
154
155 version_cached: u64,
157
158 local: T::LocalPropContextArray,
160
161 origin: Arc<GroupContext>,
163
164 _unregister_hook: Arc<dyn Any + Send + Sync>,
169}
170
171impl<T: Clone + Template> Clone for Group<T> {
172 fn clone(&self) -> Self {
173 Self {
174 __body: self.__body.clone(),
175 version_cached: self.version_cached,
176 local: self.local.clone(),
177 origin: self.origin.clone(),
178 _unregister_hook: self._unregister_hook.clone(),
179 }
180 }
181}
182
183impl<T: std::fmt::Debug + Template> std::fmt::Debug for Group<T> {
184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185 f.debug_struct("Group")
186 .field("__body", &self.__body)
187 .field("fence", &self.version_cached)
188 .finish()
189 }
190}
191
192#[derive(Debug, Clone)]
193pub struct PropLocalContext {
194 bits: VersionBits,
196}
197
198bitfield! {
199 #[derive(Clone, Copy, PartialEq, Eq)]
200 struct VersionBits(u64);
201 impl Debug;
202
203 version, set_version: 62, 0;
204 is_dirty, set_dirty: 63, 63;
205}
206
207impl Default for PropLocalContext {
208 fn default() -> Self {
209 Self {
210 bits: {
211 let mut bits = VersionBits(0);
212 bits.set_dirty(1);
213 bits
214 },
215 }
216 }
217}
218
219pub type WatchUpdate = noti::Receiver;
221
222impl<T: Template> Group<T> {
223 #[doc(hidden)]
224 pub(crate) fn create_with__(
225 core: Arc<GroupContext>,
226 unregister_anchor: Arc<dyn Any + Send + Sync>,
227 ) -> Self {
228 Self {
229 origin: core,
230 __body: T::default_config(),
231 version_cached: 0,
232 local: T::LocalPropContextArray::default(),
233 _unregister_hook: unregister_anchor,
234 }
235 }
236
237 pub fn updated(mut self) -> Self {
253 self.update();
254 self
255 }
256
257 pub fn update(&mut self) -> bool {
268 let local = self.local.as_slice_mut();
269
270 let mut has_update = self.version_cached == 0;
272
273 match self.origin.version.load(Ordering::Relaxed) {
275 new_ver if new_ver == self.version_cached => return false,
276 new_ver => self.version_cached = new_ver,
277 }
278
279 debug_assert_eq!(
281 local.len(),
282 self.origin.sources.len(),
283 "Logic Error: The set was not correctly initialized!"
284 );
285
286 for ((index, local), source) in zip(zip(0..local.len(), &mut *local), &*self.origin.sources)
287 {
288 match source.version() {
290 v if v == local.bits.version() => continue,
295 v => local.bits.set_version(v),
296 }
297
298 has_update = true;
299 local.bits.set_dirty(1);
300
301 let (meta, value) = source.property_value();
302 self.__body.update_elem_at__(index, value.as_any(), meta);
303 }
304
305 has_update
306 }
307
308 pub fn consume_update<U: 'static>(&mut self, prop: *const U) -> bool {
320 let Some(index) = self.get_index_by_ptr(prop) else { return false };
321 let bits = &mut self.local.as_slice_mut()[index].bits;
322
323 if bits.is_dirty() == 1 {
324 bits.set_dirty(0);
325 true
326 } else {
327 false
328 }
329 }
330
331 #[doc(hidden)]
332 pub fn get_index_by_ptr<U: 'static>(&self, e: *const U) -> Option<usize> {
333 debug_assert!({
334 let e = e as usize;
335 let base = &self.__body as *const _ as usize;
336 e >= base && e < base + std::mem::size_of::<T>()
337 });
338
339 self.get_prop_by_ptr(e).map(|prop| prop.index)
340 }
341
342 #[doc(hidden)]
343 pub fn get_prop_by_ptr<U: 'static>(&self, e: *const U) -> Option<&'static PropertyInfo> {
344 let ptr = e as *const u8 as isize;
345 let base = &self.__body as *const _ as *const u8 as isize;
346
347 match ptr - base {
348 v if v < 0 => None,
349 v if v >= std::mem::size_of::<T>() as isize => None,
350 v => {
351 if let Some(prop) = T::prop_at_offset__(v as usize) {
352 debug_assert_eq!(prop.type_id, TypeId::of::<U>());
353 debug_assert!(prop.index < self.local.as_slice().len());
354 Some(prop)
355 } else {
356 None
357 }
358 }
359 }
360 }
361
362 pub fn commit_elem<U: Clone + Entity>(&self, prop: &U, notify: bool) {
371 let elem = &(*self.origin.sources)[self.get_index_by_ptr(prop).unwrap()];
373
374 let impl_copy = elem.meta.vtable.implements_copy();
376
377 let new_value = unsafe { EntityValue::from_value(prop.clone(), impl_copy) };
380
381 elem.__apply_value(new_value);
383 elem.touch(notify);
385 }
386
387 pub fn touch_elem<U: 'static>(&self, prop: *const U) {
390 let elem = &(*self.origin.sources)[self.get_index_by_ptr(prop).unwrap()];
391 elem.touch(true)
392 }
393
394 pub fn watch_update(&self) -> WatchUpdate {
401 self.origin.watch_update()
402 }
403
404 pub fn mark_all_elem_dirty(&mut self) {
408 self.local.as_slice_mut().iter_mut().for_each(|e| e.bits.set_dirty(1));
410 }
411
412 pub fn mark_group_dirty(&mut self) {
416 self.version_cached = 0;
417 }
418
419 pub fn mark_dirty<U: 'static>(&mut self, elem: *const U) {
421 let index = self.get_index_by_ptr(elem).unwrap();
422 self.local.as_slice_mut()[index].bits.set_dirty(1);
423 }
424
425 pub fn meta<U: 'static>(&self, elem: *const U) -> &'static PropertyInfo {
427 self.get_prop_by_ptr(elem).unwrap()
428 }
429
430 pub fn path(&self) -> &SharedStringSequence {
433 &self.origin.path
434 }
435}
436
437impl<T: Template> std::ops::Deref for Group<T> {
438 type Target = T;
439
440 fn deref(&self) -> &Self::Target {
441 &self.__body
442 }
443}
444
445impl<T: Template> std::ops::DerefMut for Group<T> {
446 fn deref_mut(&mut self) -> &mut Self::Target {
447 &mut self.__body
448 }
449}
450
451#[test]
452fn _verify_send_impl() {
453 #[derive(Clone, Default)]
454 struct Example {}
455 impl Template for Example {
456 type LocalPropContextArray = LocalPropContextArrayImpl<0>;
457
458 fn prop_at_offset__(_offset: usize) -> Option<&'static PropertyInfo> {
459 unimplemented!()
460 }
461
462 fn props__() -> &'static [PropertyInfo] {
463 unimplemented!()
464 }
465
466 fn elem_at_mut__(&mut self, _: usize) -> &mut dyn Any {
467 unimplemented!()
468 }
469
470 fn template_name() -> (&'static str, &'static str) {
471 unimplemented!()
472 }
473
474 fn default_config() -> Self {
475 Self::default()
476 }
477 }
478
479 fn _assert_send<T: Send + Sync>() {}
480 _assert_send::<Group<Example>>();
481}
482
483impl<T: Template> Group<T> {
484 #[doc(hidden)]
485 pub fn __macro_as_mut(&mut self) -> &mut Self {
486 self
488 }
489}