agentic_codebase/types/
header.rs1use std::io::{Read, Write};
7
8use super::error::{AcbError, AcbResult};
9use super::{ACB_MAGIC, FORMAT_VERSION};
10
11#[derive(Debug, Clone, Copy, PartialEq)]
30pub struct FileHeader {
31 pub magic: [u8; 4],
33
34 pub version: u32,
36
37 pub dimension: u32,
39
40 pub language_count: u32,
42
43 pub unit_count: u64,
45
46 pub edge_count: u64,
48
49 pub unit_table_offset: u64,
51
52 pub edge_table_offset: u64,
54
55 pub string_pool_offset: u64,
57
58 pub feature_vec_offset: u64,
60
61 pub temporal_offset: u64,
63
64 pub index_offset: u64,
66
67 pub repo_hash: [u8; 32],
69
70 pub compiled_at: u64,
72
73 pub _reserved: [u8; 8],
75}
76
77pub const HEADER_SIZE: usize = 128;
79
80impl FileHeader {
81 pub fn new(dimension: u32) -> Self {
83 Self {
84 magic: ACB_MAGIC,
85 version: FORMAT_VERSION,
86 dimension,
87 language_count: 0,
88 unit_count: 0,
89 edge_count: 0,
90 unit_table_offset: HEADER_SIZE as u64,
91 edge_table_offset: HEADER_SIZE as u64,
92 string_pool_offset: 0,
93 feature_vec_offset: 0,
94 temporal_offset: 0,
95 index_offset: 0,
96 repo_hash: [0u8; 32],
97 compiled_at: crate::types::now_micros(),
98 _reserved: [0u8; 8],
99 }
100 }
101
102 pub fn write_to(&self, w: &mut impl Write) -> AcbResult<()> {
104 w.write_all(&self.magic)?;
105 w.write_all(&self.version.to_le_bytes())?;
106 w.write_all(&self.dimension.to_le_bytes())?;
107 w.write_all(&self.language_count.to_le_bytes())?;
108 w.write_all(&self.unit_count.to_le_bytes())?;
109 w.write_all(&self.edge_count.to_le_bytes())?;
110 w.write_all(&self.unit_table_offset.to_le_bytes())?;
111 w.write_all(&self.edge_table_offset.to_le_bytes())?;
112 w.write_all(&self.string_pool_offset.to_le_bytes())?;
113 w.write_all(&self.feature_vec_offset.to_le_bytes())?;
114 w.write_all(&self.temporal_offset.to_le_bytes())?;
115 w.write_all(&self.index_offset.to_le_bytes())?;
116 w.write_all(&self.repo_hash)?;
117 w.write_all(&self.compiled_at.to_le_bytes())?;
118 w.write_all(&self._reserved)?;
119 Ok(())
120 }
121
122 pub fn read_from(r: &mut impl Read) -> AcbResult<Self> {
130 let mut magic = [0u8; 4];
131 r.read_exact(&mut magic)?;
132 if magic != ACB_MAGIC {
133 return Err(AcbError::InvalidMagic);
134 }
135
136 let version = read_u32(r)?;
137 if version > FORMAT_VERSION {
138 return Err(AcbError::UnsupportedVersion(version));
139 }
140
141 let dimension = read_u32(r)?;
142 let language_count = read_u32(r)?;
143 let unit_count = read_u64(r)?;
144 let edge_count = read_u64(r)?;
145 let unit_table_offset = read_u64(r)?;
146 let edge_table_offset = read_u64(r)?;
147 let string_pool_offset = read_u64(r)?;
148 let feature_vec_offset = read_u64(r)?;
149 let temporal_offset = read_u64(r)?;
150 let index_offset = read_u64(r)?;
151
152 let mut repo_hash = [0u8; 32];
153 r.read_exact(&mut repo_hash)?;
154
155 let compiled_at = read_u64(r)?;
156
157 let mut reserved = [0u8; 8];
158 r.read_exact(&mut reserved)?;
159
160 Ok(Self {
161 magic,
162 version,
163 dimension,
164 language_count,
165 unit_count,
166 edge_count,
167 unit_table_offset,
168 edge_table_offset,
169 string_pool_offset,
170 feature_vec_offset,
171 temporal_offset,
172 index_offset,
173 repo_hash,
174 compiled_at,
175 _reserved: reserved,
176 })
177 }
178
179 pub fn to_bytes(&self) -> [u8; HEADER_SIZE] {
181 let mut buf = [0u8; HEADER_SIZE];
182 let mut cursor = std::io::Cursor::new(&mut buf[..]);
183 self.write_to(&mut cursor)
185 .expect("header write to fixed buffer");
186 buf
187 }
188
189 pub fn from_bytes(data: &[u8; HEADER_SIZE]) -> AcbResult<Self> {
191 let mut cursor = std::io::Cursor::new(&data[..]);
192 Self::read_from(&mut cursor)
193 }
194}
195
196fn read_u32(r: &mut impl Read) -> AcbResult<u32> {
201 let mut buf = [0u8; 4];
202 r.read_exact(&mut buf)?;
203 Ok(u32::from_le_bytes(buf))
204}
205
206fn read_u64(r: &mut impl Read) -> AcbResult<u64> {
207 let mut buf = [0u8; 8];
208 r.read_exact(&mut buf)?;
209 Ok(u64::from_le_bytes(buf))
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn header_roundtrip() {
218 let h = FileHeader::new(256);
219 let bytes = h.to_bytes();
220 assert_eq!(bytes.len(), HEADER_SIZE);
221 let h2 = FileHeader::from_bytes(&bytes).unwrap();
222 assert_eq!(h, h2);
223 }
224}