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}