indexbus_core/internal/
state.rs1use core::cell::UnsafeCell;
10use core::sync::atomic::{AtomicU64, Ordering};
11
12use indexbus_abi::layouts::state::StateLayout;
13
14use crate::internal::atomics::{atomic_u32, atomic_u64};
15use crate::{internal, Error};
16
17#[repr(transparent)]
22pub struct StateLayoutCell<const STATE_MAX: usize>(UnsafeCell<StateLayout<STATE_MAX>>);
23
24impl<const STATE_MAX: usize> StateLayoutCell<STATE_MAX> {
25 #[inline]
27 pub fn from_mut(inner: &mut StateLayout<STATE_MAX>) -> &StateLayoutCell<STATE_MAX> {
28 unsafe { &*(inner as *mut StateLayout<STATE_MAX> as *const StateLayoutCell<STATE_MAX>) }
31 }
32
33 #[inline]
38 pub unsafe fn from_ptr<'a>(ptr: *mut StateLayout<STATE_MAX>) -> &'a StateLayoutCell<STATE_MAX> {
39 unsafe { &*(ptr as *const StateLayoutCell<STATE_MAX>) }
40 }
41
42 #[inline]
43 fn as_ptr(&self) -> *mut StateLayout<STATE_MAX> {
44 self.0.get()
45 }
46}
47
48pub struct StatePublisher<const STATE_MAX: usize> {
57 inner: *mut StateLayout<STATE_MAX>,
58}
59
60pub struct StateReader<const STATE_MAX: usize> {
65 inner: *mut StateLayout<STATE_MAX>,
66}
67
68unsafe impl<const STATE_MAX: usize> Send for StatePublisher<STATE_MAX> {}
74unsafe impl<const STATE_MAX: usize> Send for StateReader<STATE_MAX> {}
75unsafe impl<const STATE_MAX: usize> Sync for StateReader<STATE_MAX> {}
76
77impl<const STATE_MAX: usize> StatePublisher<STATE_MAX> {
78 #[inline]
79 pub fn new(inner: &StateLayoutCell<STATE_MAX>) -> Self {
83 Self {
84 inner: inner.as_ptr(),
85 }
86 }
87
88 #[inline]
90 pub fn try_new(inner: &StateLayoutCell<STATE_MAX>) -> Result<Self, Error> {
91 let ptr = inner.as_ptr();
92 internal::validate::validate_state_layout::<STATE_MAX>(unsafe { &*ptr })?;
93 Ok(Self { inner: ptr })
94 }
95
96 pub fn publish(&self, bytes: &[u8]) -> Result<u64, usize> {
105 if bytes.len() > STATE_MAX {
106 return Err(bytes.len());
107 }
108
109 let inner = self.inner;
111 let seq0 = unsafe { atomic_u64(&(*inner).seq) }.fetch_add(1, Ordering::SeqCst) + 1;
112 debug_assert!(seq0 % 2 == 1);
113
114 unsafe {
121 let dst_u8 = core::ptr::addr_of_mut!((*inner).data).cast::<u8>();
122 debug_assert_eq!(dst_u8.align_offset(core::mem::align_of::<AtomicU64>()), 0);
123 let dst = dst_u8.cast::<AtomicU64>();
124
125 let full_words = bytes.len() / 8;
126 for i in 0..full_words {
127 let mut chunk = [0u8; 8];
128 chunk.copy_from_slice(&bytes[i * 8..i * 8 + 8]);
129 (*dst.add(i)).store(u64::from_le_bytes(chunk), Ordering::SeqCst);
130 }
131
132 let rem = bytes.len() % 8;
133 if rem != 0 {
134 let mut chunk = [0u8; 8];
135 chunk[..rem].copy_from_slice(&bytes[full_words * 8..]);
136 (*dst.add(full_words)).store(u64::from_le_bytes(chunk), Ordering::SeqCst);
137 }
138 }
139
140 unsafe { atomic_u32(&(*inner).len) }.store(bytes.len() as u32, Ordering::SeqCst);
141
142 let seq1 = unsafe { atomic_u64(&(*inner).seq) }.fetch_add(1, Ordering::SeqCst) + 1;
144 debug_assert!(seq1 % 2 == 0);
145
146 Ok(seq1)
147 }
148}
149
150impl<const STATE_MAX: usize> StateReader<STATE_MAX> {
151 #[inline]
152 pub fn new(inner: &StateLayoutCell<STATE_MAX>) -> Self {
156 Self {
157 inner: inner.as_ptr(),
158 }
159 }
160
161 #[inline]
163 pub fn try_new(inner: &StateLayoutCell<STATE_MAX>) -> Result<Self, Error> {
164 let ptr = inner.as_ptr();
165 internal::validate::validate_state_layout::<STATE_MAX>(unsafe { &*ptr })?;
166 Ok(Self { inner: ptr })
167 }
168
169 pub fn try_load_into(&self, out: &mut [u8]) -> Result<Option<(usize, u64)>, usize> {
180 let inner = self.inner;
181 let seq0 = unsafe { atomic_u64(&(*inner).seq) }.load(Ordering::SeqCst);
182 if seq0 % 2 == 1 {
183 return Ok(None);
184 }
185
186 let len = unsafe { atomic_u32(&(*inner).len) }.load(Ordering::SeqCst) as usize;
187 if len == 0 {
188 return Ok(None);
189 }
190 if out.len() < len {
191 return Err(len);
192 }
193
194 unsafe {
196 let src_u8 = core::ptr::addr_of!((*inner).data).cast::<u8>();
197 debug_assert_eq!(src_u8.align_offset(core::mem::align_of::<AtomicU64>()), 0);
198 let src = src_u8.cast::<AtomicU64>();
199
200 let full_words = len / 8;
201 for i in 0..full_words {
202 let word = (*src.add(i)).load(Ordering::SeqCst);
203 out[i * 8..i * 8 + 8].copy_from_slice(&word.to_le_bytes());
204 }
205
206 let rem = len % 8;
207 if rem != 0 {
208 let word = (*src.add(full_words)).load(Ordering::SeqCst);
209 out[full_words * 8..len].copy_from_slice(&word.to_le_bytes()[..rem]);
210 }
211 }
212
213 let seq1 = unsafe { atomic_u64(&(*inner).seq) }.load(Ordering::SeqCst);
214 if seq1 == seq0 && seq1 % 2 == 0 {
215 Ok(Some((len, seq1)))
216 } else {
217 Ok(None)
218 }
219 }
220}