Parse a footer from the tail of a segment byte slice. Reads footer_len and CRC from the last 8 bytes, then deserializes the footer and validates the CRC.
(data: &[u8])
| 302 | /// Reads footer_len and CRC from the last 8 bytes, then deserializes |
| 303 | /// the footer and validates the CRC. |
| 304 | pub fn from_segment_tail(data: &[u8]) -> Result<Self, crate::error::ColumnarError> { |
| 305 | if data.len() < 8 { |
| 306 | return Err(crate::error::ColumnarError::TruncatedSegment { |
| 307 | expected: 8, |
| 308 | got: data.len(), |
| 309 | }); |
| 310 | } |
| 311 | |
| 312 | let tail = &data[data.len() - 8..]; |
| 313 | let footer_len = u32::from_le_bytes(tail[0..4].try_into().map_err(|_| { |
| 314 | crate::error::ColumnarError::Corruption { |
| 315 | segment_id: None, |
| 316 | reason: "footer length field: expected 4 bytes at segment tail - 8".into(), |
| 317 | offset: Some((data.len() - 8) as u64), |
| 318 | } |
| 319 | })?) as usize; |
| 320 | let stored_crc = u32::from_le_bytes(tail[4..8].try_into().map_err(|_| { |
| 321 | crate::error::ColumnarError::Corruption { |
| 322 | segment_id: None, |
| 323 | reason: "footer CRC field: expected 4 bytes at segment tail - 4".into(), |
| 324 | offset: Some((data.len() - 4) as u64), |
| 325 | } |
| 326 | })?); |
| 327 | |
| 328 | let footer_start = data.len().checked_sub(8 + footer_len).ok_or( |
| 329 | crate::error::ColumnarError::TruncatedSegment { |
| 330 | expected: 8 + footer_len, |
| 331 | got: data.len(), |
| 332 | }, |
| 333 | )?; |
| 334 | |
| 335 | let footer_bytes = &data[footer_start..footer_start + footer_len]; |
| 336 | let computed_crc = crc32c::crc32c(footer_bytes); |
| 337 | |
| 338 | if computed_crc != stored_crc { |
| 339 | return Err(crate::error::ColumnarError::FooterCrcMismatch { |
| 340 | stored: stored_crc, |
| 341 | computed: computed_crc, |
| 342 | }); |
| 343 | } |
| 344 | |
| 345 | zerompk::from_msgpack(footer_bytes) |
| 346 | .map_err(|e| crate::error::ColumnarError::Serialization(e.to_string())) |
| 347 | } |
| 348 | } |
| 349 | |
| 350 | #[cfg(test)] |