1use std::marker::PhantomData;
11use std::rc::Rc;
12
13use crate::core::types::{AnySource, SourceInner};
14use crate::reactivity::tracking::track_read;
15use crate::shared::notify::Notifier;
16
17pub struct SharedSlotBuffer<T: Copy + PartialEq + 'static> {
33 ptr: *mut T,
34 len: usize,
35 dirty: Option<*mut u8>,
36 default_value: T,
37 notifier: Box<dyn Notifier>,
38 source: Rc<SourceInner<u32>>, _marker: PhantomData<T>,
41}
42
43impl<T: Copy + PartialEq + 'static> SharedSlotBuffer<T> {
44 pub unsafe fn new(
52 ptr: *mut T,
53 len: usize,
54 default_value: T,
55 notifier: impl Notifier,
56 ) -> Self {
57 Self {
58 ptr,
59 len,
60 dirty: None,
61 default_value,
62 notifier: Box::new(notifier),
63 source: Rc::new(SourceInner::new(0u32)),
64 _marker: PhantomData,
65 }
66 }
67
68 pub unsafe fn with_dirty(
74 ptr: *mut T,
75 len: usize,
76 dirty: *mut u8,
77 default_value: T,
78 notifier: impl Notifier,
79 ) -> Self {
80 Self {
81 ptr,
82 len,
83 dirty: Some(dirty),
84 default_value,
85 notifier: Box::new(notifier),
86 source: Rc::new(SourceInner::new(0u32)),
87 _marker: PhantomData,
88 }
89 }
90
91 #[inline]
93 pub fn get(&self, index: usize) -> T {
94 debug_assert!(index < self.len, "SharedSlotBuffer: index out of bounds");
95 track_read(self.source.clone() as Rc<dyn AnySource>);
96 unsafe { *self.ptr.add(index) }
97 }
98
99 #[inline]
101 pub fn peek(&self, index: usize) -> T {
102 debug_assert!(index < self.len, "SharedSlotBuffer: index out of bounds");
103 unsafe { *self.ptr.add(index) }
104 }
105
106 #[inline]
108 pub fn set(&self, index: usize, value: T) {
109 debug_assert!(index < self.len, "SharedSlotBuffer: index out of bounds");
110
111 let current = unsafe { *self.ptr.add(index) };
112 if current == value {
113 return; }
115
116 unsafe { *self.ptr.add(index) = value; }
118
119 if let Some(dirty) = self.dirty {
121 unsafe { *dirty.add(index) = 1; }
122 }
123
124 let new_version = self.source.get() + 1;
126 self.source.set(new_version);
127
128 self.notifier.notify();
130 }
131
132 pub fn set_batch(&self, updates: &[(usize, T)]) {
134 let mut changed = false;
135
136 for &(index, value) in updates {
137 debug_assert!(index < self.len, "SharedSlotBuffer: index out of bounds");
138
139 let current = unsafe { *self.ptr.add(index) };
140 if current != value {
141 unsafe { *self.ptr.add(index) = value; }
142 if let Some(dirty) = self.dirty {
143 unsafe { *dirty.add(index) = 1; }
144 }
145 changed = true;
146 }
147 }
148
149 if changed {
150 let new_version = self.source.get() + 1;
151 self.source.set(new_version);
152 self.notifier.notify();
153 }
154 }
155
156 pub fn notify_changed(&self) {
159 let new_version = self.source.get() + 1;
160 self.source.set(new_version);
161 }
162
163 pub fn source(&self) -> Rc<SourceInner<u32>> {
165 self.source.clone()
166 }
167
168 pub fn clear(&self, index: usize) {
170 self.set(index, self.default_value);
171 }
172
173 pub fn len(&self) -> usize {
175 self.len
176 }
177
178 pub fn is_empty(&self) -> bool {
180 self.len == 0
181 }
182}
183
184#[cfg(test)]
189mod tests {
190 use super::*;
191 use crate::shared::notify::NoopNotifier;
192
193 #[test]
194 fn basic_get_set() {
195 let mut data = vec![0.0f32; 8];
196 let buf = unsafe {
197 SharedSlotBuffer::new(data.as_mut_ptr(), data.len(), 0.0, NoopNotifier)
198 };
199
200 assert_eq!(buf.peek(0), 0.0);
201 buf.set(0, 42.0);
202 assert_eq!(buf.peek(0), 42.0);
203 assert_eq!(buf.len(), 8);
204 }
205
206 #[test]
207 fn equality_check_skips_write() {
208 let mut data = vec![10.0f32; 4];
209 let buf = unsafe {
210 SharedSlotBuffer::new(data.as_mut_ptr(), data.len(), 0.0, NoopNotifier)
211 };
212
213 buf.set(0, 10.0);
215 assert_eq!(buf.peek(0), 10.0);
217
218 buf.set(0, 20.0);
220 assert_eq!(buf.peek(0), 20.0);
221 }
222
223 #[test]
224 fn dirty_flags() {
225 let mut data = vec![0i32; 4];
226 let mut dirty = vec![0u8; 4];
227 let buf = unsafe {
228 SharedSlotBuffer::with_dirty(
229 data.as_mut_ptr(),
230 data.len(),
231 dirty.as_mut_ptr(),
232 0,
233 NoopNotifier,
234 )
235 };
236
237 assert_eq!(dirty[0], 0);
238 buf.set(0, 42);
239 assert_eq!(dirty[0], 1);
240 assert_eq!(dirty[1], 0);
241
242 buf.set(2, 99);
243 assert_eq!(dirty[2], 1);
244 }
245
246 #[test]
247 fn batch_set() {
248 let mut data = vec![0u32; 8];
249 let buf = unsafe {
250 SharedSlotBuffer::new(data.as_mut_ptr(), data.len(), 0, NoopNotifier)
251 };
252
253 buf.set_batch(&[(0, 10), (3, 30), (7, 70)]);
254 assert_eq!(buf.peek(0), 10);
255 assert_eq!(buf.peek(1), 0);
256 assert_eq!(buf.peek(3), 30);
257 assert_eq!(buf.peek(7), 70);
258 }
259
260 #[test]
261 fn clear_resets_to_default() {
262 let mut data = vec![0.0f32; 4];
263 let buf = unsafe {
264 SharedSlotBuffer::new(data.as_mut_ptr(), data.len(), -1.0, NoopNotifier)
265 };
266
267 buf.set(0, 42.0);
268 assert_eq!(buf.peek(0), 42.0);
269
270 buf.clear(0);
271 assert_eq!(buf.peek(0), -1.0);
272 }
273}