driven/binary/
infinity_format.rs1use bytemuck::{Pod, Zeroable};
6
7use crate::{DrivenError, Result};
8
9pub const INFINITY_MAGIC: &[u8; 4] = b"DRV\x00";
11
12#[repr(C)]
14#[derive(Debug, Clone, Copy, Pod, Zeroable)]
15pub struct InfinityHeader {
16 pub magic: [u8; 4],
18 pub version: u16,
20 pub flags: u16,
22 pub checksum: [u8; 16],
24 pub _reserved: [u8; 8],
26}
27
28#[repr(C)]
30#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]
31pub struct SectionOffsets {
32 pub string_table: u32,
34 pub persona: u32,
36 pub standards: u32,
38 pub workflow: u32,
40 pub context: u32,
42 pub signature: u32,
44}
45
46#[repr(transparent)]
48#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]
49pub struct RuleFlags(pub u16);
50
51impl RuleFlags {
52 pub const HAS_PERSONA: u16 = 1 << 0;
54 pub const HAS_STANDARDS: u16 = 1 << 1;
56 pub const HAS_WORKFLOW: u16 = 1 << 2;
58 pub const HAS_CONTEXT: u16 = 1 << 3;
60 pub const IS_SIGNED: u16 = 1 << 4;
62 pub const USES_FUSION: u16 = 1 << 5;
64 pub const COMPRESSED: u16 = 1 << 6;
66 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 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 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 pub fn flags(&self) -> RuleFlags {
137 RuleFlags(self.flags)
138 }
139
140 pub fn to_bytes(&self) -> &[u8] {
142 bytemuck::bytes_of(self)
143 }
144
145 pub const fn size() -> usize {
147 std::mem::size_of::<Self>()
148 }
149}
150
151#[derive(Debug)]
153pub struct InfinityRule<'a> {
154 pub header: &'a InfinityHeader,
156 pub section_offsets: &'a SectionOffsets,
158 data: &'a [u8],
160}
161
162impl<'a> InfinityRule<'a> {
163 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 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 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 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 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 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 pub fn is_signed(&self) -> bool {
245 self.header.flags().is_signed()
246 }
247
248 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}