linkrs_storage/edge/immutable_csr/
persistence.rs1use super::super::csr_trait::CsrBase;
2use super::super::mutable_csr::serialization::{
3 decode_topology_i64_column, decode_topology_u32_column, decode_topology_u64_column,
4 encode_topology_i64_column, encode_topology_u32_column, encode_topology_u64_column,
5 TopologyColumnEncoding,
6};
7use super::super::{ColdStamps, EdgeId, HotNbr, INVALID_EDGE_ID};
8use super::ImmutableCsr;
9use crate::persistence::read_u64_le;
10use bitvec::order::Lsb0;
11use bitvec::vec::BitVec;
12use linkrs_core::{StorageError, StorageResult};
13
14impl ImmutableCsr {
15 pub fn used_memory_size(&self) -> usize {
17 self.hot_entries
18 .len()
19 .saturating_mul(std::mem::size_of::<HotNbr>())
20 .saturating_add(
21 self.cold_entries
22 .len()
23 .saturating_mul(std::mem::size_of::<ColdStamps>()),
24 )
25 .saturating_add(
26 self.degrees
27 .len()
28 .saturating_mul(std::mem::size_of::<u32>()),
29 )
30 .saturating_add(
31 self.offsets
32 .len()
33 .saturating_mul(std::mem::size_of::<u32>()),
34 )
35 .saturating_add(self.values.len().saturating_mul(std::mem::size_of::<u64>()))
36 .saturating_add(self.packed_valid_bytes().len())
37 .saturating_add(std::mem::size_of::<Self>())
38 }
39
40 pub fn topology_encoding_report(&self) -> Vec<(String, TopologyColumnEncoding, usize, usize)> {
42 let endpoints: Vec<u32> = self.hot_entries.iter().map(|hot| hot.endpoint).collect();
43 let edge_ids: Vec<u64> = self.hot_entries.iter().map(|hot| hot.edge_id.0).collect();
44 let (endpoint_choice, _) = encode_topology_u32_column(&endpoints);
45 let (edge_id_choice, _) = encode_topology_u64_column(&edge_ids);
46 let (degrees_choice, _) = encode_topology_u32_column(&self.degrees);
47 let mut report = vec![
48 (
49 "neighbor".to_string(),
50 endpoint_choice.encoding,
51 endpoint_choice.plain_bytes,
52 endpoint_choice.encoded_bytes,
53 ),
54 (
55 "edge_id".to_string(),
56 edge_id_choice.encoding,
57 edge_id_choice.plain_bytes,
58 edge_id_choice.encoded_bytes,
59 ),
60 (
61 "lengths".to_string(),
62 degrees_choice.encoding,
63 degrees_choice.plain_bytes,
64 degrees_choice.encoded_bytes,
65 ),
66 ];
67 if self.has_valued_entries() {
68 let (values_choice, _) = encode_topology_u64_column(&self.values);
69 report.push((
70 "values".to_string(),
71 values_choice.encoding,
72 values_choice.plain_bytes,
73 values_choice.encoded_bytes,
74 ));
75 }
76 report
77 }
78
79 pub fn dump(&self) -> Vec<u8> {
98 let mut result = Vec::new();
99 self.dump_into(&mut result);
100 result
101 }
102
103 pub fn dump_into(&self, out: &mut Vec<u8>) {
105 let mut scratch = super::super::mutable_csr::persistence::CsrDumpScratch::new();
106 self.dump_into_with_scratch(out, &mut scratch);
107 }
108
109 pub fn dump_into_with_scratch(
115 &self,
116 out: &mut Vec<u8>,
117 scratch: &mut super::super::mutable_csr::persistence::CsrDumpScratch,
118 ) {
119 let start = out.len();
120 out.extend_from_slice(&(self.degrees.len() as u64).to_le_bytes());
121 out.extend_from_slice(&self.edge_count.to_le_bytes());
122 out.extend_from_slice(&(self.hot_entries.len() as u64).to_le_bytes());
123 let (_, degrees_payload) = encode_topology_u32_column(&self.degrees);
124 out.extend_from_slice(°rees_payload);
125 scratch.fill_from_split(&self.hot_entries, &self.cold_entries);
126 let (_, endpoints_payload) = encode_topology_u32_column(scratch.endpoints());
127 out.extend_from_slice(&endpoints_payload);
128 let (_, ranks_payload) = encode_topology_i64_column(scratch.ranks());
129 out.extend_from_slice(&ranks_payload);
130 let (_, edge_ids_payload) = encode_topology_u64_column(scratch.edge_ids());
131 out.extend_from_slice(&edge_ids_payload);
132 let (_, delete_payload) = encode_topology_u64_column(scratch.deletes());
133 out.extend_from_slice(&delete_payload);
134 let valued = self.has_valued_entries();
135 out.extend_from_slice(&(u64::from(valued)).to_le_bytes());
136 if valued {
137 let (_, values_payload) = encode_topology_u64_column(&self.values);
138 out.extend_from_slice(&values_payload);
139 let valid_bytes = self.packed_valid_bytes();
140 out.extend_from_slice(&(valid_bytes.len() as u64).to_le_bytes());
141 out.extend_from_slice(valid_bytes);
142 }
143 let crc = crc32fast::hash(&out[start..]);
144 out.extend_from_slice(&crc.to_le_bytes());
145 }
146
147 pub fn load(&mut self, data: &[u8]) -> StorageResult<()> {
153 if data.len() < 32 {
154 return Err(StorageError::deserialize_error(
155 "frozen CSR data too short for header",
156 ));
157 }
158 let (body, trailer) = data.split_at(data.len() - 4);
159 let mut stored_bytes = [0u8; 4];
160 stored_bytes.copy_from_slice(trailer);
161 let stored = u32::from_le_bytes(stored_bytes);
162 let computed = crc32fast::hash(body);
163 if stored != computed {
164 return Err(StorageError::deserialize_error(format!(
165 "frozen CSR dump CRC mismatch: stored={:#x} computed={:#x}",
166 stored, computed
167 )));
168 }
169 let data = body;
170 let mut offset = 0usize;
171 let rows = read_u64_le(data, &mut offset)? as usize;
172 let edge_count = read_u64_le(data, &mut offset)?;
173 let entries_len = read_u64_le(data, &mut offset)? as usize;
174 let degrees = decode_topology_u32_column(data, &mut offset)?;
175 if degrees.len() != rows {
176 return Err(StorageError::deserialize_error(
177 "frozen CSR degree column length mismatch",
178 ));
179 }
180 let endpoints = decode_topology_u32_column(data, &mut offset)?;
181 let ranks = decode_topology_i64_column(data, &mut offset)?;
182 let edge_ids = decode_topology_u64_column(data, &mut offset)?;
183 let delete_stamps = decode_topology_u64_column(data, &mut offset)?;
184 if endpoints.len() != entries_len
185 || ranks.len() != entries_len
186 || edge_ids.len() != entries_len
187 || delete_stamps.len() != entries_len
188 {
189 return Err(StorageError::deserialize_error(
190 "frozen CSR neighbor column length mismatch",
191 ));
192 }
193 let mut hot_entries = Vec::with_capacity(entries_len);
194 let mut cold_entries = Vec::with_capacity(entries_len);
195 let mut recomputed: u64 = 0;
196 for index in 0..entries_len {
197 let hot = HotNbr {
198 endpoint: endpoints[index],
199 rank: ranks[index],
200 edge_id: EdgeId(edge_ids[index]),
201 };
202 let cold = ColdStamps {
203 delete_ts: delete_stamps[index],
204 };
205 if hot.edge_id != INVALID_EDGE_ID && cold.is_live() {
206 recomputed += 1;
207 }
208 hot_entries.push(hot);
209 cold_entries.push(cold);
210 }
211 let mut covered = 0usize;
212 for degree in °rees {
213 covered = covered.saturating_add(*degree as usize);
214 }
215 if covered != entries_len {
216 return Err(StorageError::deserialize_error(format!(
217 "frozen CSR row window mismatch: degrees cover {} entries, payload holds {}",
218 covered, entries_len
219 )));
220 }
221 if recomputed != edge_count {
222 return Err(StorageError::deserialize_error(format!(
223 "frozen CSR edge count mismatch: stored={}, recomputed={}",
224 edge_count, recomputed
225 )));
226 }
227 let has_values = read_u64_le(data, &mut offset)?;
228 let (values, valid) = match has_values {
229 0 => (Vec::new(), BitVec::new()),
230 1 => {
231 let words = decode_topology_u64_column(data, &mut offset)?;
232 if words.len() != entries_len {
233 return Err(StorageError::deserialize_error(
234 "frozen CSR value column length mismatch",
235 ));
236 }
237 let valid_len = read_u64_le(data, &mut offset)? as usize;
238 let want_valid = entries_len.div_ceil(8);
239 if valid_len != want_valid {
240 return Err(StorageError::deserialize_error(format!(
241 "frozen CSR validity length mismatch: holds {} bytes, layout needs {}",
242 valid_len, want_valid
243 )));
244 }
245 let valid_bytes = data.get(offset..offset + valid_len).ok_or_else(|| {
246 StorageError::deserialize_error("frozen CSR validity bytes truncated")
247 })?;
248 offset += valid_len;
249 let mut bits = BitVec::<u8, Lsb0>::from_slice(valid_bytes);
250 bits.truncate(entries_len);
251 (words, bits)
252 }
253 other => {
254 return Err(StorageError::deserialize_error(format!(
255 "frozen CSR value flag out of range: {other}"
256 )));
257 }
258 };
259 if offset != data.len() {
260 return Err(StorageError::deserialize_error(
261 "unexpected trailing data in frozen CSR payload",
262 ));
263 }
264 self.hot_entries = hot_entries;
265 self.cold_entries = cold_entries;
266 self.degrees = degrees;
267 self.edge_count = edge_count;
268 self.values = values;
269 self.valid = valid;
270 self.rebuild_offsets();
271 Ok(())
272 }
273}
274
275impl CsrBase for ImmutableCsr {
276 fn vertex_capacity(&self) -> usize {
277 self.degrees.len()
278 }
279
280 fn edge_count(&self) -> u64 {
281 self.edge_count
282 }
283
284 fn dump(&self) -> Vec<u8> {
285 ImmutableCsr::dump(self)
286 }
287
288 fn dump_into(&self, out: &mut Vec<u8>) {
289 ImmutableCsr::dump_into(self, out);
290 }
291
292 fn load(&mut self, data: &[u8]) -> StorageResult<()> {
293 ImmutableCsr::load(self, data)
294 }
295}