agentic_reality/format/
file.rs1use std::path::Path;
4
5use crate::engine::RealityEngine;
6use crate::types::error::{RealityError, RealityResult};
7use crate::types::{FOOTER_SIZE, HEADER_SIZE};
8
9use super::footer::ArealFooter;
10use super::header::ArealHeader;
11use super::sections::{SectionEntry, SectionType, SECTION_ENTRY_SIZE};
12
13pub struct ArealWriter;
15
16impl ArealWriter {
17 pub fn save(engine: &RealityEngine, path: &Path) -> RealityResult<()> {
19 let incarnation_bytes = engine
20 .incarnation_id()
21 .map(|id| *id.as_uuid().as_bytes())
22 .unwrap_or([0u8; 16]);
23
24 let mut header = ArealHeader::new(incarnation_bytes);
25 let mut sections_data: Vec<(SectionType, Vec<u8>)> = vec![];
26
27 let deployment = serde_json::to_vec(&engine.deployment_store)
29 .map_err(|e| RealityError::Serialization(e.to_string()))?;
30 sections_data.push((SectionType::Deployment, deployment));
31
32 let environment = serde_json::to_vec(&engine.environment_store)
33 .map_err(|e| RealityError::Serialization(e.to_string()))?;
34 sections_data.push((SectionType::Environment, environment));
35
36 let resource = serde_json::to_vec(&engine.resource_store)
37 .map_err(|e| RealityError::Serialization(e.to_string()))?;
38 sections_data.push((SectionType::Resource, resource));
39
40 let reality = serde_json::to_vec(&engine.reality_store)
41 .map_err(|e| RealityError::Serialization(e.to_string()))?;
42 sections_data.push((SectionType::Reality, reality));
43
44 let topology = serde_json::to_vec(&engine.topology_store)
45 .map_err(|e| RealityError::Serialization(e.to_string()))?;
46 sections_data.push((SectionType::Topology, topology));
47
48 let temporal = serde_json::to_vec(&engine.temporal_store)
49 .map_err(|e| RealityError::Serialization(e.to_string()))?;
50 sections_data.push((SectionType::Temporal, temporal));
51
52 let stakes = serde_json::to_vec(&engine.stakes_store)
53 .map_err(|e| RealityError::Serialization(e.to_string()))?;
54 sections_data.push((SectionType::Stakes, stakes));
55
56 let coherence = serde_json::to_vec(&engine.coherence_store)
57 .map_err(|e| RealityError::Serialization(e.to_string()))?;
58 sections_data.push((SectionType::Coherence, coherence));
59
60 header.section_count = sections_data.len() as u32;
61 header.section_table_offset = HEADER_SIZE as u64;
62
63 let mut buf = Vec::new();
65
66 header.write_to(&mut buf)?;
68
69 let section_table_start = buf.len();
71
72 let table_size = sections_data.len() * SECTION_ENTRY_SIZE;
74 buf.resize(buf.len() + table_size, 0);
75
76 let mut entries = Vec::new();
78 for (section_type, data) in §ions_data {
79 let offset = buf.len() as u64;
80 let len = data.len() as u64;
81 let checksum_hash = blake3::hash(data);
82 let mut checksum = [0u8; 8];
83 checksum.copy_from_slice(&checksum_hash.as_bytes()[..8]);
84
85 entries.push(SectionEntry {
86 section_type: *section_type,
87 flags: 0,
88 offset,
89 length: len,
90 uncompressed_length: len,
91 checksum,
92 });
93
94 buf.extend_from_slice(data);
95 }
96
97 let mut table_buf = Vec::new();
99 for entry in &entries {
100 entry.write_to(&mut table_buf);
101 }
102 buf[section_table_start..section_table_start + table_size].copy_from_slice(&table_buf);
103
104 let global_hash = blake3::hash(&buf);
106 let footer = ArealFooter::new(*global_hash.as_bytes(), entries.len() as u8);
107 footer.write_to(&mut buf);
108
109 let header_hash = blake3::hash(&buf[..HEADER_SIZE - 8]);
111 let mut header_checksum = [0u8; 8];
112 header_checksum.copy_from_slice(&header_hash.as_bytes()[..8]);
113
114 let tmp_path = path.with_extension("areal.tmp");
116 std::fs::write(&tmp_path, &buf)?;
117 std::fs::rename(&tmp_path, path)?;
118
119 Ok(())
120 }
121}
122
123pub struct ArealReader;
125
126impl ArealReader {
127 pub fn load(path: &Path) -> RealityResult<RealityEngine> {
129 let data = std::fs::read(path)?;
130
131 if data.len() < HEADER_SIZE + FOOTER_SIZE {
132 return Err(RealityError::InvalidFormat("file too small".into()));
133 }
134
135 let header = ArealHeader::read_from(&data)?;
137
138 let _footer = ArealFooter::read_from(&data)?;
140
141 let table_start = header.section_table_offset as usize;
143 let section_count = header.section_count as usize;
144
145 let mut engine = RealityEngine::new();
146
147 for i in 0..section_count {
148 let entry_start = table_start + i * SECTION_ENTRY_SIZE;
149 if entry_start + SECTION_ENTRY_SIZE > data.len() {
150 return Err(RealityError::InvalidFormat("section table overflow".into()));
151 }
152 let entry = SectionEntry::read_from(&data[entry_start..])?;
153 let section_data = &data[entry.offset as usize..(entry.offset + entry.length) as usize];
154
155 match entry.section_type {
156 SectionType::Deployment => {
157 engine.deployment_store = serde_json::from_slice(section_data)
158 .map_err(|e| RealityError::Serialization(e.to_string()))?;
159 }
160 SectionType::Environment => {
161 engine.environment_store = serde_json::from_slice(section_data)
162 .map_err(|e| RealityError::Serialization(e.to_string()))?;
163 }
164 SectionType::Resource => {
165 engine.resource_store = serde_json::from_slice(section_data)
166 .map_err(|e| RealityError::Serialization(e.to_string()))?;
167 }
168 SectionType::Reality => {
169 engine.reality_store = serde_json::from_slice(section_data)
170 .map_err(|e| RealityError::Serialization(e.to_string()))?;
171 }
172 SectionType::Topology => {
173 engine.topology_store = serde_json::from_slice(section_data)
174 .map_err(|e| RealityError::Serialization(e.to_string()))?;
175 }
176 SectionType::Temporal => {
177 engine.temporal_store = serde_json::from_slice(section_data)
178 .map_err(|e| RealityError::Serialization(e.to_string()))?;
179 }
180 SectionType::Stakes => {
181 engine.stakes_store = serde_json::from_slice(section_data)
182 .map_err(|e| RealityError::Serialization(e.to_string()))?;
183 }
184 SectionType::Coherence => {
185 engine.coherence_store = serde_json::from_slice(section_data)
186 .map_err(|e| RealityError::Serialization(e.to_string()))?;
187 }
188 SectionType::Indexes => {
189 engine.indexes = serde_json::from_slice(section_data)
190 .map_err(|e| RealityError::Serialization(e.to_string()))?;
191 }
192 }
193 }
194
195 Ok(engine)
196 }
197}