helix_core/compiler/
binary.rs

1use serde::{Serialize, Deserialize};
2use std::collections::HashMap;
3#[cfg(feature = "zstd")]
4use zstd;
5pub const MAGIC_BYTES: [u8; 4] = *b"HLXB";
6pub const BINARY_VERSION: u32 = 1;
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct HelixBinary {
9    pub magic: [u8; 4],
10    pub version: u32,
11    pub flags: BinaryFlags,
12    pub metadata: BinaryMetadata,
13    pub symbol_table: SymbolTable,
14    pub data_sections: Vec<DataSection>,
15    pub checksum: u64,
16}
17impl HelixBinary {
18    pub fn new() -> Self {
19        Self {
20            magic: MAGIC_BYTES,
21            version: BINARY_VERSION,
22            flags: BinaryFlags::default(),
23            metadata: BinaryMetadata::default(),
24            symbol_table: SymbolTable::default(),
25            data_sections: Vec::new(),
26            checksum: 0,
27        }
28    }
29    pub fn validate(&self) -> Result<(), String> {
30        if self.magic != MAGIC_BYTES {
31            return Err(format!("Invalid magic bytes: {:?}", self.magic));
32        }
33        if self.version > BINARY_VERSION {
34            return Err(
35                format!(
36                    "Binary version {} is newer than supported version {}", self.version,
37                    BINARY_VERSION
38                ),
39            );
40        }
41        if false && self.checksum != 0 {
42            let calculated = self.calculate_checksum();
43            if calculated != self.checksum {
44                return Err(
45                    format!(
46                        "Checksum mismatch: expected {:x}, got {:x}", self.checksum,
47                        calculated
48                    ),
49                );
50            }
51        }
52        Ok(())
53    }
54    pub fn calculate_checksum(&self) -> u64 {
55        let mut temp = self.clone();
56        temp.checksum = 0;
57        temp.metadata.created_at = 0;
58        temp.metadata.compiler_version = "normalized".to_string();
59        if let Ok(serialized) = bincode::serialize(&temp) {
60            crc32fast::hash(&serialized) as u64
61        } else {
62            0
63        }
64    }
65    pub fn size(&self) -> usize {
66        bincode::serialized_size(self).unwrap_or(0) as usize
67    }
68    pub fn compression_ratio(&self, original_size: usize) -> f64 {
69        if self.flags.compressed && original_size > 0 {
70            original_size as f64 / self.size() as f64
71        } else {
72            1.0
73        }
74    }
75}
76impl Default for HelixBinary {
77    fn default() -> Self {
78        Self::new()
79    }
80}
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct BinaryFlags {
83    pub compressed: bool,
84    pub optimized: bool,
85    pub encrypted: bool,
86    pub signed: bool,
87    pub custom: u32,
88}
89impl Default for BinaryFlags {
90    fn default() -> Self {
91        Self {
92            compressed: false,
93            optimized: false,
94            encrypted: false,
95            signed: false,
96            custom: 0,
97        }
98    }
99}
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct BinaryMetadata {
102    pub created_at: u64,
103    pub compiler_version: String,
104    pub source_hash: String,
105    pub optimization_level: u8,
106    pub platform: String,
107    pub source_path: Option<String>,
108    pub extra: HashMap<String, String>,
109}
110impl Default for BinaryMetadata {
111    fn default() -> Self {
112        Self {
113            created_at: std::time::SystemTime::now()
114                .duration_since(std::time::UNIX_EPOCH)
115                .unwrap()
116                .as_secs(),
117            compiler_version: env!("CARGO_PKG_VERSION").to_string(),
118            source_hash: String::new(),
119            optimization_level: 0,
120            platform: format!("{}-{}", std::env::consts::OS, std::env::consts::ARCH),
121            source_path: None,
122            extra: HashMap::new(),
123        }
124    }
125}
126#[derive(Debug, Clone, Default, Serialize, Deserialize)]
127pub struct SymbolTable {
128    pub strings: Vec<String>,
129    pub string_map: HashMap<String, u32>,
130    pub agents: HashMap<String, u32>,
131    pub workflows: HashMap<String, u32>,
132    pub contexts: HashMap<String, u32>,
133    pub crews: HashMap<String, u32>,
134    pub variables: HashMap<String, Reference>,
135}
136impl SymbolTable {
137    pub fn intern(&mut self, s: &str) -> u32 {
138        if let Some(&id) = self.string_map.get(s) {
139            return id;
140        }
141        let id = self.strings.len() as u32;
142        self.strings.push(s.to_string());
143        self.string_map.insert(s.to_string(), id);
144        id
145    }
146    pub fn get(&self, id: u32) -> Option<&String> {
147        self.strings.get(id as usize)
148    }
149    pub fn stats(&self) -> SymbolTableStats {
150        SymbolTableStats {
151            total_strings: self.strings.len(),
152            unique_strings: self.string_map.len(),
153            total_bytes: self.strings.iter().map(|s| s.len()).sum(),
154            agents: self.agents.len(),
155            workflows: self.workflows.len(),
156            contexts: self.contexts.len(),
157            crews: self.crews.len(),
158        }
159    }
160}
161#[derive(Debug)]
162pub struct SymbolTableStats {
163    pub total_strings: usize,
164    pub unique_strings: usize,
165    pub total_bytes: usize,
166    pub agents: usize,
167    pub workflows: usize,
168    pub contexts: usize,
169    pub crews: usize,
170}
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct Reference {
173    pub ref_type: ReferenceType,
174    pub target: u32,
175    pub location: u32,
176}
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub enum ReferenceType {
179    Agent,
180    Workflow,
181    Memory,
182    Context,
183    Variable,
184    Environment,
185}
186#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct DataSection {
188    pub section_type: SectionType,
189    pub offset: u64,
190    pub size: u64,
191    pub data: Vec<u8>,
192    pub compression: Option<CompressionMethod>,
193}
194impl DataSection {
195    pub fn new(section_type: SectionType, data: Vec<u8>) -> Self {
196        let size = data.len() as u64;
197        Self {
198            section_type,
199            offset: 0,
200            size,
201            data,
202            compression: None,
203        }
204    }
205    pub fn compress(&mut self, method: CompressionMethod) -> Result<(), String> {
206        let compressed = match method {
207            CompressionMethod::None => self.data.clone(),
208            CompressionMethod::Lz4 => lz4_flex::compress_prepend_size(&self.data),
209            #[cfg(feature = "zstd")]
210            CompressionMethod::Zstd(level) => {
211                zstd::encode_all(&self.data[..], level).map_err(|e| e.to_string())?
212            }
213        };
214        self.data = compressed;
215        self.compression = Some(method);
216        Ok(())
217    }
218    pub fn decompress(&mut self) -> Result<(), String> {
219        if let Some(method) = &self.compression {
220            let decompressed = match method {
221                CompressionMethod::None => self.data.clone(),
222                CompressionMethod::Lz4 => {
223                    lz4_flex::decompress_size_prepended(&self.data)
224                        .map_err(|e| e.to_string())?
225                }
226                #[cfg(feature = "zstd")]
227                CompressionMethod::Zstd(_) => {
228                    zstd::decode_all(&self.data[..]).map_err(|e| e.to_string())?
229                }
230            };
231            self.data = decompressed;
232            self.compression = None;
233        }
234        Ok(())
235    }
236}
237#[derive(Debug, Clone, Serialize, Deserialize)]
238pub enum SectionType {
239    Project,
240    Agents,
241    Workflows,
242    Pipelines,
243    Memory,
244    Contexts,
245    Crews,
246    Instructions,
247    Custom(String),
248}
249#[derive(Debug, Clone, Serialize, Deserialize)]
250pub enum CompressionMethod {
251    None,
252    Lz4,
253    #[cfg(feature = "zstd")]
254    Zstd(i32),
255}
256#[derive(Debug, Clone, Serialize, Deserialize)]
257pub enum Instruction {
258    Push(Value),
259    Pop,
260    Dup,
261    Swap,
262    LoadVar(u32),
263    StoreVar(u32),
264    LoadRef(u32),
265    Jump(i32),
266    JumpIf(i32),
267    Call(u32),
268    Return,
269    InvokeAgent(u32),
270    InvokeCrew(u32),
271    Pipeline(u32),
272    CreateObject,
273    SetField(u32),
274    GetField(u32),
275    CreateArray,
276    AppendArray,
277    MemStore(u32),
278    MemLoad(u32),
279    MemEmbed(u32),
280    Add,
281    Sub,
282    Mul,
283    Div,
284    Mod,
285    Eq,
286    Ne,
287    Lt,
288    Le,
289    Gt,
290    Ge,
291    And,
292    Or,
293    Not,
294    Nop,
295    Halt,
296    Debug(String),
297}
298#[derive(Debug, Clone, Serialize, Deserialize)]
299pub enum Value {
300    Null,
301    Bool(bool),
302    Int(i64),
303    Float(f64),
304    String(u32),
305    Reference(u32),
306    Duration(u64),
307    Array(Vec<Value>),
308    Object(HashMap<u32, Value>),
309}
310#[cfg(test)]
311mod tests {
312    use super::*;
313    #[test]
314    fn test_binary_creation() {
315        let binary = HelixBinary::new();
316        assert_eq!(binary.magic, MAGIC_BYTES);
317        assert_eq!(binary.version, BINARY_VERSION);
318        assert!(! binary.flags.compressed);
319    }
320    #[test]
321    fn test_symbol_table_interning() {
322        let mut table = SymbolTable::default();
323        let id1 = table.intern("hello");
324        let id2 = table.intern("world");
325        let id3 = table.intern("hello");
326        assert_eq!(id1, 0);
327        assert_eq!(id2, 1);
328        assert_eq!(id3, id1);
329        assert_eq!(table.strings.len(), 2);
330    }
331    #[test]
332    fn test_data_section_compression() {
333        let data = vec![1u8; 1000];
334        let mut section = DataSection::new(SectionType::Agents, data.clone());
335        assert_eq!(section.data.len(), 1000);
336        section.compress(CompressionMethod::Lz4).unwrap();
337        assert!(section.data.len() < 1000);
338        section.decompress().unwrap();
339        assert_eq!(section.data, data);
340    }
341}