Skip to main content

driven/binary/
infinity_format.rs

1//! DX ∞ Infinity Format for Rules
2//!
3//! Inspired by dx-serializer's world-record 186-byte baseline format.
4
5use bytemuck::{Pod, Zeroable};
6
7use crate::{DrivenError, Result};
8
9/// Magic bytes: "DRV∞" (DRV + infinity symbol UTF-8)
10pub const INFINITY_MAGIC: &[u8; 4] = b"DRV\x00";
11
12/// Infinity Header (32 bytes, fixed, memory-mapped)
13#[repr(C)]
14#[derive(Debug, Clone, Copy, Pod, Zeroable)]
15pub struct InfinityHeader {
16    /// Magic: "DRV\0" (4 bytes)
17    pub magic: [u8; 4],
18    /// Format version (2 bytes)
19    pub version: u16,
20    /// Flags bitfield (2 bytes)
21    pub flags: u16,
22    /// Checksum (16 bytes)
23    pub checksum: [u8; 16],
24    /// Reserved (8 bytes)
25    pub _reserved: [u8; 8],
26}
27
28/// Section offsets for O(1) access
29#[repr(C)]
30#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]
31pub struct SectionOffsets {
32    /// String table offset
33    pub string_table: u32,
34    /// Persona section offset
35    pub persona: u32,
36    /// Standards section offset
37    pub standards: u32,
38    /// Workflow section offset
39    pub workflow: u32,
40    /// Context section offset
41    pub context: u32,
42    /// Signature offset
43    pub signature: u32,
44}
45
46/// Rule flags bitfield
47#[repr(transparent)]
48#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]
49pub struct RuleFlags(pub u16);
50
51impl RuleFlags {
52    /// Has persona section
53    pub const HAS_PERSONA: u16 = 1 << 0;
54    /// Has standards section
55    pub const HAS_STANDARDS: u16 = 1 << 1;
56    /// Has workflow section
57    pub const HAS_WORKFLOW: u16 = 1 << 2;
58    /// Has context section
59    pub const HAS_CONTEXT: u16 = 1 << 3;
60    /// Is cryptographically signed
61    pub const IS_SIGNED: u16 = 1 << 4;
62    /// Uses fusion cache
63    pub const USES_FUSION: u16 = 1 << 5;
64    /// Compressed payload
65    pub const COMPRESSED: u16 = 1 << 6;
66    /// Contains SIMD-optimized data
67    pub const SIMD_OPTIMIZED: u16 = 1 << 7;
68
69    pub fn new() -> Self {
70        Self(0)
71    }
72
73    pub fn has_persona(self) -> bool {
74        self.0 & Self::HAS_PERSONA != 0
75    }
76
77    pub fn has_standards(self) -> bool {
78        self.0 & Self::HAS_STANDARDS != 0
79    }
80
81    pub fn has_workflow(self) -> bool {
82        self.0 & Self::HAS_WORKFLOW != 0
83    }
84
85    pub fn has_context(self) -> bool {
86        self.0 & Self::HAS_CONTEXT != 0
87    }
88
89    pub fn is_signed(self) -> bool {
90        self.0 & Self::IS_SIGNED != 0
91    }
92
93    pub fn uses_fusion(self) -> bool {
94        self.0 & Self::USES_FUSION != 0
95    }
96
97    pub fn set(&mut self, flag: u16) {
98        self.0 |= flag;
99    }
100
101    pub fn clear(&mut self, flag: u16) {
102        self.0 &= !flag;
103    }
104}
105
106impl InfinityHeader {
107    /// Create a new header
108    pub fn new(version: u16, flags: RuleFlags) -> Self {
109        Self {
110            magic: *INFINITY_MAGIC,
111            version,
112            flags: flags.0,
113            checksum: [0; 16],
114            _reserved: [0; 8],
115        }
116    }
117
118    /// Parse header from bytes (zero-copy)
119    pub fn from_bytes(data: &[u8]) -> Result<&Self> {
120        if data.len() < std::mem::size_of::<Self>() {
121            return Err(DrivenError::InvalidBinary(
122                "Data too small for infinity header".into(),
123            ));
124        }
125
126        let header: &Self = bytemuck::from_bytes(&data[..std::mem::size_of::<Self>()]);
127
128        if &header.magic != INFINITY_MAGIC {
129            return Err(DrivenError::InvalidBinary("Invalid magic bytes".into()));
130        }
131
132        Ok(header)
133    }
134
135    /// Get flags as typed struct
136    pub fn flags(&self) -> RuleFlags {
137        RuleFlags(self.flags)
138    }
139
140    /// Serialize header to bytes
141    pub fn to_bytes(&self) -> &[u8] {
142        bytemuck::bytes_of(self)
143    }
144
145    /// Header size in bytes
146    pub const fn size() -> usize {
147        std::mem::size_of::<Self>()
148    }
149}
150
151/// Complete infinity rule (header + sections)
152#[derive(Debug)]
153pub struct InfinityRule<'a> {
154    /// The header
155    pub header: &'a InfinityHeader,
156    /// Section offsets (follows header in binary)
157    pub section_offsets: &'a SectionOffsets,
158    /// Raw data (memory-mapped)
159    data: &'a [u8],
160}
161
162impl<'a> InfinityRule<'a> {
163    /// Create from raw bytes (zero-copy)
164    pub fn from_bytes(data: &'a [u8]) -> Result<Self> {
165        let header_size = std::mem::size_of::<InfinityHeader>();
166        let offsets_size = std::mem::size_of::<SectionOffsets>();
167        let min_size = header_size + offsets_size;
168
169        if data.len() < min_size {
170            return Err(DrivenError::InvalidBinary(
171                "Data too small for infinity rule".into(),
172            ));
173        }
174
175        let header = InfinityHeader::from_bytes(data)?;
176        let section_offsets: &SectionOffsets =
177            bytemuck::from_bytes(&data[header_size..header_size + offsets_size]);
178
179        Ok(Self {
180            header,
181            section_offsets,
182            data,
183        })
184    }
185
186    /// Get string table section
187    pub fn string_table_data(&self) -> Option<&'a [u8]> {
188        let offset = self.section_offsets.string_table as usize;
189        if offset == 0 || offset >= self.data.len() {
190            return None;
191        }
192        Some(&self.data[offset..])
193    }
194
195    /// Get persona section
196    pub fn persona_data(&self) -> Option<&'a [u8]> {
197        if !self.header.flags().has_persona() {
198            return None;
199        }
200        let offset = self.section_offsets.persona as usize;
201        if offset == 0 || offset >= self.data.len() {
202            return None;
203        }
204        Some(&self.data[offset..])
205    }
206
207    /// Get standards section
208    pub fn standards_data(&self) -> Option<&'a [u8]> {
209        if !self.header.flags().has_standards() {
210            return None;
211        }
212        let offset = self.section_offsets.standards as usize;
213        if offset == 0 || offset >= self.data.len() {
214            return None;
215        }
216        Some(&self.data[offset..])
217    }
218
219    /// Get workflow section
220    pub fn workflow_data(&self) -> Option<&'a [u8]> {
221        if !self.header.flags().has_workflow() {
222            return None;
223        }
224        let offset = self.section_offsets.workflow as usize;
225        if offset == 0 || offset >= self.data.len() {
226            return None;
227        }
228        Some(&self.data[offset..])
229    }
230
231    /// Get context section
232    pub fn context_data(&self) -> Option<&'a [u8]> {
233        if !self.header.flags().has_context() {
234            return None;
235        }
236        let offset = self.section_offsets.context as usize;
237        if offset == 0 || offset >= self.data.len() {
238            return None;
239        }
240        Some(&self.data[offset..])
241    }
242
243    /// Check if signed
244    pub fn is_signed(&self) -> bool {
245        self.header.flags().is_signed()
246    }
247
248    /// Get raw data
249    pub fn raw_data(&self) -> &'a [u8] {
250        self.data
251    }
252}
253
254#[cfg(test)]
255mod tests {
256    use super::*;
257
258    #[test]
259    fn test_header_size() {
260        assert_eq!(InfinityHeader::size(), 32);
261    }
262
263    #[test]
264    fn test_flags() {
265        let mut flags = RuleFlags::new();
266        assert!(!flags.has_persona());
267
268        flags.set(RuleFlags::HAS_PERSONA);
269        assert!(flags.has_persona());
270
271        flags.set(RuleFlags::IS_SIGNED);
272        assert!(flags.is_signed());
273    }
274
275    #[test]
276    fn test_header_roundtrip() {
277        let header = InfinityHeader::new(1, RuleFlags::new());
278        let bytes = header.to_bytes();
279        assert_eq!(bytes.len(), 32);
280
281        let parsed = InfinityHeader::from_bytes(bytes).unwrap();
282        assert_eq!(parsed.version, 1);
283    }
284}