| 57 | } |
| 58 | |
| 59 | pub fn decode(bytes: &[u8]) -> ArrayResult<Self> { |
| 60 | if bytes.len() < HEADER_SIZE { |
| 61 | return Err(ArrayError::SegmentCorruption { |
| 62 | detail: format!("segment header truncated: {} bytes", bytes.len()), |
| 63 | }); |
| 64 | } |
| 65 | if bytes[..8] != HEADER_MAGIC { |
| 66 | return Err(ArrayError::SegmentCorruption { |
| 67 | detail: "segment header magic mismatch (not NDAS)".into(), |
| 68 | }); |
| 69 | } |
| 70 | let mut u32_buf = [0u8; 4]; |
| 71 | u32_buf.copy_from_slice(&bytes[20..24]); |
| 72 | let crc_stored = u32::from_le_bytes(u32_buf); |
| 73 | let crc_calc = crc32c::crc32c(&bytes[..20]); |
| 74 | if crc_stored != crc_calc { |
| 75 | return Err(ArrayError::SegmentCorruption { |
| 76 | detail: format!( |
| 77 | "segment header CRC mismatch: stored={crc_stored:08x} \ |
| 78 | calc={crc_calc:08x}" |
| 79 | ), |
| 80 | }); |
| 81 | } |
| 82 | let mut u16_buf = [0u8; 2]; |
| 83 | u16_buf.copy_from_slice(&bytes[8..10]); |
| 84 | let version = u16::from_le_bytes(u16_buf); |
| 85 | u16_buf.copy_from_slice(&bytes[10..12]); |
| 86 | let flags = u16::from_le_bytes(u16_buf); |
| 87 | let mut u64_buf = [0u8; 8]; |
| 88 | u64_buf.copy_from_slice(&bytes[12..20]); |
| 89 | let schema_hash = u64::from_le_bytes(u64_buf); |
| 90 | if version != FORMAT_VERSION { |
| 91 | return Err(ArrayError::UnsupportedSegmentFormat { version }); |
| 92 | } |
| 93 | Ok(Self { |
| 94 | version, |
| 95 | flags, |
| 96 | schema_hash, |
| 97 | }) |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | #[cfg(test)] |