Reassemble a [`TileSnapshot`] from a header and a slice of chunks. Sorts chunks by `chunk_index`, validates that exactly `0..total_chunks` are present (no gaps, no duplicates), concatenates their payloads, and reconstructs the snapshot. Validation failures → [`ArrayError::SegmentCorruption`].
(
header: &SnapshotHeader,
chunks: &mut [SnapshotChunk],
)
| 207 | /// |
| 208 | /// Validation failures → [`ArrayError::SegmentCorruption`]. |
| 209 | pub fn assemble_chunks( |
| 210 | header: &SnapshotHeader, |
| 211 | chunks: &mut [SnapshotChunk], |
| 212 | ) -> ArrayResult<TileSnapshot> { |
| 213 | chunks.sort_by_key(|c| c.chunk_index); |
| 214 | |
| 215 | let expected = header.total_chunks as usize; |
| 216 | |
| 217 | if chunks.len() != expected { |
| 218 | return Err(ArrayError::SegmentCorruption { |
| 219 | detail: format!("expected {} chunks, got {}", expected, chunks.len()), |
| 220 | }); |
| 221 | } |
| 222 | |
| 223 | for (i, chunk) in chunks.iter().enumerate() { |
| 224 | if chunk.chunk_index as usize != i { |
| 225 | return Err(ArrayError::SegmentCorruption { |
| 226 | detail: format!("chunk index gap: expected {i}, got {}", chunk.chunk_index), |
| 227 | }); |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | let tile_blob: Vec<u8> = chunks |
| 232 | .iter() |
| 233 | .flat_map(|c| c.payload.iter().copied()) |
| 234 | .collect(); |
| 235 | |
| 236 | Ok(TileSnapshot { |
| 237 | array: header.array.clone(), |
| 238 | coord_range: header.coord_range.clone(), |
| 239 | tile_blob, |
| 240 | snapshot_hlc: header.snapshot_hlc, |
| 241 | schema_hlc: header.schema_hlc, |
| 242 | }) |
| 243 | } |
| 244 | |
| 245 | // ─── Sink trait ────────────────────────────────────────────────────────────── |
| 246 |