Skip to main content

palladium_runtime/
zig_core.rs

1use palladium_actor::Envelope;
2use palladium_transport::MailboxMessage;
3
4extern "C" {
5    fn pd_envelope_validate_batch(ptr: *const Envelope, count: usize) -> i32;
6    fn pd_envelope_apply_flags_batch(ptr: *mut Envelope, count: usize, flags: u32);
7    /// Validate `count` envelopes at byte offset 0 within elements of `stride`
8    /// bytes.  See `pd_envelope_validate_strided` in `core.zig`.
9    fn pd_envelope_validate_strided(ptr: *const u8, count: usize, stride: usize) -> i32;
10}
11
12/// Batch validate a slice of envelopes using high-performance Zig logic.
13pub fn validate_envelopes(envelopes: &[Envelope]) -> Result<(), i32> {
14    let res = unsafe { pd_envelope_validate_batch(envelopes.as_ptr(), envelopes.len()) };
15    if res == 0 {
16        Ok(())
17    } else {
18        Err(res)
19    }
20}
21
22/// Apply a bitmask to a batch of envelopes in-place.
23pub fn apply_flags_batch(envelopes: &mut [Envelope], flags: u32) {
24    unsafe {
25        pd_envelope_apply_flags_batch(envelopes.as_mut_ptr(), envelopes.len(), flags);
26    }
27}
28
29/// Validate a batch of [`MailboxMessage`]s without copying their envelopes.
30///
31/// Reads `Envelope` fields directly from the `MailboxMessage` array via Zig's
32/// strided memory walk (Task 4 zero-copy optimisation).  This is safe because
33/// `MailboxMessage` is `#[repr(C)]` with `envelope` at offset 0.
34pub fn validate_message_batch(msgs: &[MailboxMessage]) -> Result<(), i32> {
35    if msgs.is_empty() {
36        return Ok(());
37    }
38    let ptr = msgs.as_ptr() as *const u8;
39    let stride = std::mem::size_of::<MailboxMessage>();
40    let res = unsafe { pd_envelope_validate_strided(ptr, msgs.len(), stride) };
41    if res == 0 {
42        Ok(())
43    } else {
44        Err(res)
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51    use palladium_actor::AddrHash;
52
53    #[test]
54    fn test_zig_validation_edge_cases() {
55        let src = AddrHash::from_parts(1, 0);
56        let dst = AddrHash::from_parts(2, 0);
57
58        // 1. Max payload (64 MiB)
59        let env_max = Envelope::new(src, dst, 0, 64 * 1024 * 1024);
60        assert!(validate_envelopes(&[env_max]).is_ok());
61
62        // 2. Over max payload
63        let env_over = Envelope::new(src, dst, 0, 64 * 1024 * 1024 + 1);
64        assert_eq!(validate_envelopes(&[env_over]), Err(-1));
65
66        // 3. Zero source
67        let env_no_src = Envelope::new(AddrHash::from_parts(0, 0), dst, 0, 100);
68        assert_eq!(validate_envelopes(&[env_no_src]), Err(-2));
69
70        // 4. Reserved flags
71        let mut env_flags = Envelope::new(src, dst, 0, 100);
72        env_flags.flags = 0x08; // bit 3 is reserved
73        assert_eq!(validate_envelopes(&[env_flags]), Err(-4));
74    }
75
76    #[test]
77    fn test_zig_apply_flags_batch() {
78        let src = AddrHash::from_parts(1, 0);
79        let dst = AddrHash::from_parts(2, 0);
80        let mut batch = vec![
81            Envelope::new(src, dst, 0, 100),
82            Envelope::new(src, dst, 0, 200),
83        ];
84
85        // Apply FLAG_RESPONSE (1)
86        apply_flags_batch(&mut batch, 1);
87
88        assert_eq!(batch[0].flags, 1);
89        assert_eq!(batch[1].flags, 1);
90
91        // Apply priority 2 (0b100 = 4)
92        apply_flags_batch(&mut batch, 4);
93        assert_eq!(batch[0].flags, 5); // 1 | 4
94    }
95}