ringkernel_core/
state.rs

1//! Control block state helpers for GPU-compatible kernel state.
2//!
3//! This module provides utilities for storing kernel state either:
4//! - **Embedded** in ControlBlock's 24-byte `_reserved` field (zero-copy)
5//! - **External** in separate GPU memory with a pointer in the descriptor
6//!
7//! # Example
8//!
9//! ```ignore
10//! use ringkernel_core::prelude::*;
11//!
12//! #[derive(Default, Clone, Copy)]
13//! #[repr(C, align(8))]
14//! struct OrderBookState {
15//!     best_bid: u64,
16//!     best_ask: u64,
17//!     order_count: u32,
18//!     _pad: u32,
19//! }  // 24 bytes - fits in ControlBlock._reserved
20//!
21//! impl EmbeddedState for OrderBookState {}
22//!
23//! // Write state to control block
24//! let mut block = ControlBlock::new();
25//! let state = OrderBookState { best_bid: 100, best_ask: 101, order_count: 42, _pad: 0 };
26//! ControlBlockStateHelper::write_embedded(&mut block, &state)?;
27//!
28//! // Read state from control block
29//! let restored: OrderBookState = ControlBlockStateHelper::read_embedded(&block)?;
30//! ```
31
32use crate::control::ControlBlock;
33use crate::error::{Result, RingKernelError};
34use bytemuck::{Pod, Zeroable};
35
36/// Size of the reserved field in ControlBlock available for state storage.
37pub const CONTROL_BLOCK_STATE_SIZE: usize = 24;
38
39/// Magic number for state descriptor ("STAT" in little-endian).
40pub const STATE_DESCRIPTOR_MAGIC: u32 = 0x54415453; // "STAT"
41
42// ============================================================================
43// Embedded State Trait
44// ============================================================================
45
46/// Trait for state types that can be embedded directly in ControlBlock.
47///
48/// Types implementing this trait must:
49/// - Be `Pod` + `Zeroable` (plain old data, safe to reinterpret)
50/// - Be `Default` for initialization
51/// - Be `Copy` for efficient transfer
52/// - Fit within 24 bytes (checked at compile time via `EmbeddedStateSize`)
53///
54/// # Example
55///
56/// ```ignore
57/// #[derive(Default, Clone, Copy, Pod, Zeroable)]
58/// #[repr(C, align(8))]
59/// struct MyState {
60///     value_a: u64,
61///     value_b: u64,
62///     counter: u32,
63///     _pad: u32,
64/// }
65///
66/// impl EmbeddedState for MyState {}
67/// ```
68pub trait EmbeddedState: Pod + Zeroable + Default + Copy + Send + Sync + 'static {
69    /// State version for forward compatibility.
70    /// Override to support migrations.
71    const VERSION: u32 = 1;
72
73    /// Whether this state is embedded (true) or external (false).
74    fn is_embedded() -> bool {
75        true
76    }
77}
78
79/// Marker trait to verify state fits in 24 bytes at compile time.
80///
81/// Use `assert_embedded_size!()` macro or manual assertion.
82pub trait EmbeddedStateSize: EmbeddedState {
83    /// Compile-time assertion that size fits.
84    const SIZE_CHECK: () = assert!(
85        std::mem::size_of::<Self>() <= CONTROL_BLOCK_STATE_SIZE,
86        "EmbeddedState must fit in 24 bytes"
87    );
88}
89
90// Automatically implement EmbeddedStateSize for all EmbeddedState types
91impl<T: EmbeddedState> EmbeddedStateSize for T {}
92
93// ============================================================================
94// State Descriptor
95// ============================================================================
96
97/// Descriptor stored in `_reserved` when using external state.
98///
99/// When state is too large to embed, this 24-byte descriptor points
100/// to the external GPU memory location.
101#[derive(Debug, Clone, Copy, Default)]
102#[repr(C, align(8))]
103pub struct StateDescriptor {
104    /// Magic number for validation (STATE_DESCRIPTOR_MAGIC).
105    pub magic: u32,
106    /// State version number.
107    pub version: u32,
108    /// Total size of external state in bytes.
109    pub total_size: u64,
110    /// Pointer to external state buffer (GPU address).
111    pub external_ptr: u64,
112}
113
114// SAFETY: StateDescriptor is #[repr(C)] with only primitive types
115unsafe impl Zeroable for StateDescriptor {}
116unsafe impl Pod for StateDescriptor {}
117
118impl EmbeddedState for StateDescriptor {}
119
120const _: () = assert!(std::mem::size_of::<StateDescriptor>() == 24);
121
122impl StateDescriptor {
123    /// Create a new state descriptor.
124    pub const fn new(version: u32, total_size: u64, external_ptr: u64) -> Self {
125        Self {
126            magic: STATE_DESCRIPTOR_MAGIC,
127            version,
128            total_size,
129            external_ptr,
130        }
131    }
132
133    /// Check if this descriptor is valid.
134    pub fn is_valid(&self) -> bool {
135        self.magic == STATE_DESCRIPTOR_MAGIC
136    }
137
138    /// Check if state is external (has external pointer).
139    pub fn is_external(&self) -> bool {
140        self.is_valid() && self.external_ptr != 0
141    }
142
143    /// Check if state is embedded (no external pointer).
144    pub fn is_embedded(&self) -> bool {
145        !self.is_valid() || self.external_ptr == 0
146    }
147}
148
149// ============================================================================
150// GPU State Trait (for external state)
151// ============================================================================
152
153/// Trait for GPU-compatible state types that may be stored externally.
154///
155/// Unlike `EmbeddedState`, types implementing `GpuState` can be larger
156/// than 24 bytes and are stored in separate GPU memory.
157pub trait GpuState: Send + Sync + 'static {
158    /// Serialize state to bytes for GPU transfer.
159    fn to_control_block_bytes(&self) -> Vec<u8>;
160
161    /// Deserialize state from bytes read from GPU.
162    fn from_control_block_bytes(bytes: &[u8]) -> Result<Self>
163    where
164        Self: Sized;
165
166    /// State version for compatibility checking.
167    fn state_version() -> u32 {
168        1
169    }
170
171    /// Whether this state should be embedded (if small enough).
172    fn prefer_embedded() -> bool
173    where
174        Self: Sized,
175    {
176        std::mem::size_of::<Self>() <= CONTROL_BLOCK_STATE_SIZE
177    }
178}
179
180// Blanket implementation for EmbeddedState types
181impl<T: EmbeddedState> GpuState for T {
182    fn to_control_block_bytes(&self) -> Vec<u8> {
183        bytemuck::bytes_of(self).to_vec()
184    }
185
186    fn from_control_block_bytes(bytes: &[u8]) -> Result<Self> {
187        if bytes.len() < std::mem::size_of::<Self>() {
188            return Err(RingKernelError::InvalidState {
189                expected: format!("{} bytes", std::mem::size_of::<Self>()),
190                actual: format!("{} bytes", bytes.len()),
191            });
192        }
193        Ok(*bytemuck::from_bytes(&bytes[..std::mem::size_of::<Self>()]))
194    }
195
196    fn state_version() -> u32 {
197        Self::VERSION
198    }
199
200    fn prefer_embedded() -> bool {
201        true
202    }
203}
204
205// ============================================================================
206// ControlBlock State Helper
207// ============================================================================
208
209/// Helper for reading/writing state to/from ControlBlock.
210pub struct ControlBlockStateHelper;
211
212impl ControlBlockStateHelper {
213    /// Write embedded state to ControlBlock's reserved field.
214    ///
215    /// # Errors
216    ///
217    /// Returns error if state doesn't fit in 24 bytes.
218    pub fn write_embedded<S: EmbeddedState>(block: &mut ControlBlock, state: &S) -> Result<()> {
219        let bytes = bytemuck::bytes_of(state);
220        if bytes.len() > CONTROL_BLOCK_STATE_SIZE {
221            return Err(RingKernelError::InvalidState {
222                expected: format!("<= {} bytes", CONTROL_BLOCK_STATE_SIZE),
223                actual: format!("{} bytes", bytes.len()),
224            });
225        }
226
227        // Clear reserved field first
228        block._reserved = [0u8; 24];
229
230        // Copy state bytes
231        block._reserved[..bytes.len()].copy_from_slice(bytes);
232
233        Ok(())
234    }
235
236    /// Read embedded state from ControlBlock's reserved field.
237    ///
238    /// # Errors
239    ///
240    /// Returns error if state type size exceeds 24 bytes.
241    pub fn read_embedded<S: EmbeddedState>(block: &ControlBlock) -> Result<S> {
242        let size = std::mem::size_of::<S>();
243        if size > CONTROL_BLOCK_STATE_SIZE {
244            return Err(RingKernelError::InvalidState {
245                expected: format!("<= {} bytes", CONTROL_BLOCK_STATE_SIZE),
246                actual: format!("{} bytes", size),
247            });
248        }
249
250        Ok(*bytemuck::from_bytes(&block._reserved[..size]))
251    }
252
253    /// Write state descriptor for external state.
254    pub fn write_descriptor(block: &mut ControlBlock, descriptor: &StateDescriptor) -> Result<()> {
255        Self::write_embedded(block, descriptor)
256    }
257
258    /// Read state descriptor from ControlBlock.
259    ///
260    /// Returns `None` if no valid descriptor is present.
261    pub fn read_descriptor(block: &ControlBlock) -> Option<StateDescriptor> {
262        let desc: StateDescriptor =
263            *bytemuck::from_bytes::<StateDescriptor>(&block._reserved[..24]);
264        if desc.is_valid() {
265            Some(desc)
266        } else {
267            None
268        }
269    }
270
271    /// Check if ControlBlock has embedded state (no external pointer).
272    pub fn has_embedded_state(block: &ControlBlock) -> bool {
273        match Self::read_descriptor(block) {
274            Some(desc) => desc.is_embedded(),
275            None => true, // No descriptor means raw embedded bytes
276        }
277    }
278
279    /// Check if ControlBlock references external state.
280    pub fn has_external_state(block: &ControlBlock) -> bool {
281        match Self::read_descriptor(block) {
282            Some(desc) => desc.is_external(),
283            None => false,
284        }
285    }
286
287    /// Clear all state from ControlBlock.
288    pub fn clear_state(block: &mut ControlBlock) {
289        block._reserved = [0u8; 24];
290    }
291
292    /// Get raw bytes from reserved field.
293    pub fn raw_bytes(block: &ControlBlock) -> &[u8; 24] {
294        &block._reserved
295    }
296
297    /// Get mutable raw bytes from reserved field.
298    pub fn raw_bytes_mut(block: &mut ControlBlock) -> &mut [u8; 24] {
299        &mut block._reserved
300    }
301}
302
303// ============================================================================
304// State Snapshot
305// ============================================================================
306
307/// Snapshot of kernel state for checkpointing.
308#[derive(Debug, Clone)]
309pub struct StateSnapshot {
310    /// State data bytes.
311    pub data: Vec<u8>,
312    /// State version.
313    pub version: u32,
314    /// Whether state was embedded or external.
315    pub was_embedded: bool,
316    /// Kernel ID this state belongs to.
317    pub kernel_id: u64,
318    /// Timestamp when snapshot was taken (HLC counter).
319    pub timestamp: u64,
320}
321
322impl StateSnapshot {
323    /// Create a new state snapshot.
324    pub fn new(data: Vec<u8>, version: u32, was_embedded: bool, kernel_id: u64) -> Self {
325        Self {
326            data,
327            version,
328            was_embedded,
329            kernel_id,
330            timestamp: 0,
331        }
332    }
333
334    /// Create snapshot with timestamp.
335    pub fn with_timestamp(mut self, timestamp: u64) -> Self {
336        self.timestamp = timestamp;
337        self
338    }
339
340    /// Deserialize state from snapshot.
341    pub fn restore<S: GpuState>(&self) -> Result<S> {
342        S::from_control_block_bytes(&self.data)
343    }
344}
345
346// ============================================================================
347// Tests
348// ============================================================================
349
350#[cfg(test)]
351mod tests {
352    use super::*;
353
354    // Test embedded state type (exactly 24 bytes)
355    #[derive(Default, Clone, Copy, Debug, PartialEq)]
356    #[repr(C, align(8))]
357    struct TestState {
358        value_a: u64,
359        value_b: u64,
360        counter: u32,
361        flags: u32,
362    }
363
364    // SAFETY: TestState is #[repr(C)] with only primitive types
365    unsafe impl Zeroable for TestState {}
366    unsafe impl Pod for TestState {}
367
368    impl EmbeddedState for TestState {}
369
370    // Small state (8 bytes)
371    #[derive(Default, Clone, Copy, Debug, PartialEq)]
372    #[repr(C)]
373    struct SmallState {
374        value: u64,
375    }
376
377    unsafe impl Zeroable for SmallState {}
378    unsafe impl Pod for SmallState {}
379
380    impl EmbeddedState for SmallState {}
381
382    #[test]
383    fn test_state_size_constant() {
384        assert_eq!(CONTROL_BLOCK_STATE_SIZE, 24);
385    }
386
387    #[test]
388    fn test_state_descriptor_size() {
389        assert_eq!(std::mem::size_of::<StateDescriptor>(), 24);
390    }
391
392    #[test]
393    fn test_state_descriptor_validation() {
394        let desc = StateDescriptor::new(1, 256, 0x1000);
395        assert!(desc.is_valid());
396        assert!(desc.is_external());
397        assert!(!desc.is_embedded());
398
399        let embedded_desc = StateDescriptor::new(1, 24, 0);
400        assert!(embedded_desc.is_valid());
401        assert!(!embedded_desc.is_external());
402        assert!(embedded_desc.is_embedded());
403
404        let invalid_desc = StateDescriptor::default();
405        assert!(!invalid_desc.is_valid());
406    }
407
408    #[test]
409    fn test_write_read_embedded_state() {
410        let mut block = ControlBlock::new();
411        let state = TestState {
412            value_a: 0x1234567890ABCDEF,
413            value_b: 0xFEDCBA0987654321,
414            counter: 42,
415            flags: 0xFF,
416        };
417
418        ControlBlockStateHelper::write_embedded(&mut block, &state).unwrap();
419        let restored: TestState = ControlBlockStateHelper::read_embedded(&block).unwrap();
420
421        assert_eq!(state, restored);
422    }
423
424    #[test]
425    fn test_write_read_small_state() {
426        let mut block = ControlBlock::new();
427        let state = SmallState { value: 42 };
428
429        ControlBlockStateHelper::write_embedded(&mut block, &state).unwrap();
430        let restored: SmallState = ControlBlockStateHelper::read_embedded(&block).unwrap();
431
432        assert_eq!(state, restored);
433    }
434
435    #[test]
436    fn test_write_read_descriptor() {
437        let mut block = ControlBlock::new();
438        let desc = StateDescriptor::new(2, 1024, 0xDEADBEEF);
439
440        ControlBlockStateHelper::write_descriptor(&mut block, &desc).unwrap();
441
442        let restored = ControlBlockStateHelper::read_descriptor(&block).unwrap();
443        assert_eq!(restored.magic, STATE_DESCRIPTOR_MAGIC);
444        assert_eq!(restored.version, 2);
445        assert_eq!(restored.total_size, 1024);
446        assert_eq!(restored.external_ptr, 0xDEADBEEF);
447    }
448
449    #[test]
450    fn test_has_embedded_external_state() {
451        let mut block = ControlBlock::new();
452
453        // Fresh block has embedded state (no descriptor)
454        assert!(ControlBlockStateHelper::has_embedded_state(&block));
455        assert!(!ControlBlockStateHelper::has_external_state(&block));
456
457        // Write external descriptor
458        let desc = StateDescriptor::new(1, 256, 0x1000);
459        ControlBlockStateHelper::write_descriptor(&mut block, &desc).unwrap();
460
461        assert!(!ControlBlockStateHelper::has_embedded_state(&block));
462        assert!(ControlBlockStateHelper::has_external_state(&block));
463
464        // Write embedded descriptor (external_ptr = 0)
465        let desc = StateDescriptor::new(1, 24, 0);
466        ControlBlockStateHelper::write_descriptor(&mut block, &desc).unwrap();
467
468        assert!(ControlBlockStateHelper::has_embedded_state(&block));
469        assert!(!ControlBlockStateHelper::has_external_state(&block));
470    }
471
472    #[test]
473    fn test_clear_state() {
474        let mut block = ControlBlock::new();
475        let state = TestState {
476            value_a: 123,
477            value_b: 456,
478            counter: 789,
479            flags: 0xABC,
480        };
481
482        ControlBlockStateHelper::write_embedded(&mut block, &state).unwrap();
483        assert!(block._reserved.iter().any(|&b| b != 0));
484
485        ControlBlockStateHelper::clear_state(&mut block);
486        assert!(block._reserved.iter().all(|&b| b == 0));
487    }
488
489    #[test]
490    fn test_raw_bytes_access() {
491        let mut block = ControlBlock::new();
492        block._reserved[0] = 0x42;
493        block._reserved[23] = 0xFF;
494
495        let bytes = ControlBlockStateHelper::raw_bytes(&block);
496        assert_eq!(bytes[0], 0x42);
497        assert_eq!(bytes[23], 0xFF);
498
499        let bytes_mut = ControlBlockStateHelper::raw_bytes_mut(&mut block);
500        bytes_mut[1] = 0x99;
501        assert_eq!(block._reserved[1], 0x99);
502    }
503
504    #[test]
505    fn test_gpu_state_trait() {
506        let state = TestState {
507            value_a: 100,
508            value_b: 200,
509            counter: 300,
510            flags: 400,
511        };
512
513        let bytes = state.to_control_block_bytes();
514        assert_eq!(bytes.len(), 24);
515
516        let restored = TestState::from_control_block_bytes(&bytes).unwrap();
517        assert_eq!(state, restored);
518
519        assert!(TestState::prefer_embedded());
520        assert_eq!(TestState::state_version(), 1);
521    }
522
523    #[test]
524    fn test_state_snapshot() {
525        let state = TestState {
526            value_a: 1,
527            value_b: 2,
528            counter: 3,
529            flags: 4,
530        };
531
532        let snapshot =
533            StateSnapshot::new(state.to_control_block_bytes(), 1, true, 42).with_timestamp(1000);
534
535        assert_eq!(snapshot.version, 1);
536        assert!(snapshot.was_embedded);
537        assert_eq!(snapshot.kernel_id, 42);
538        assert_eq!(snapshot.timestamp, 1000);
539
540        let restored: TestState = snapshot.restore().unwrap();
541        assert_eq!(state, restored);
542    }
543
544    #[test]
545    fn test_embedded_state_size_check() {
546        // This should compile - TestState is exactly 24 bytes
547        assert_eq!(std::mem::size_of::<TestState>(), 24);
548        // Force compile-time size check evaluation
549        assert_eq!(<TestState as EmbeddedStateSize>::SIZE_CHECK, ());
550
551        // SmallState is smaller - also OK
552        assert!(std::mem::size_of::<SmallState>() <= CONTROL_BLOCK_STATE_SIZE);
553        // Force compile-time size check evaluation
554        assert_eq!(<SmallState as EmbeddedStateSize>::SIZE_CHECK, ());
555    }
556}