Validate the TSEG header at the start of `data` and return the number of bytes consumed (always [`TSEG_HEADER_SIZE`] on success).
(data: &[u8])
| 98 | /// Validate the TSEG header at the start of `data` and return |
| 99 | /// the number of bytes consumed (always [`TSEG_HEADER_SIZE`] on success). |
| 100 | pub fn decode_tseg_header(data: &[u8]) -> Result<usize, SegmentReadError> { |
| 101 | if data.len() < TSEG_HEADER_SIZE { |
| 102 | return Err(SegmentReadError::TooSmall { size: data.len() }); |
| 103 | } |
| 104 | |
| 105 | let mut cur = Cursor::new(data); |
| 106 | |
| 107 | let magic = cur |
| 108 | .read_bytes(4) |
| 109 | .expect("invariant: TSEG_HEADER_SIZE guard at line 99 ensures header bytes available"); |
| 110 | if magic != SEGMENT_MAGIC { |
| 111 | return Err(SegmentReadError::InvalidMagic); |
| 112 | } |
| 113 | |
| 114 | let version = cur |
| 115 | .read_u16_le() |
| 116 | .expect("invariant: TSEG_HEADER_SIZE guard at line 99 ensures 2 bytes for version field"); |
| 117 | let _reserved = cur |
| 118 | .read_u16_le() |
| 119 | .expect("invariant: TSEG_HEADER_SIZE guard at line 99 ensures 2 bytes for reserved field"); |
| 120 | let crc_stored = cur |
| 121 | .read_u32_le() |
| 122 | .expect("invariant: TSEG_HEADER_SIZE guard at line 99 ensures 4 bytes for crc field"); |
| 123 | |
| 124 | if version != TSEG_HEADER_VERSION { |
| 125 | return Err(SegmentReadError::UnsupportedVersion { version }); |
| 126 | } |
| 127 | |
| 128 | // CRC covers bytes 0..8 (magic + version + reserved). |
| 129 | let crc_calc = crc32c::crc32c(&data[..8]); |
| 130 | if crc_stored != crc_calc { |
| 131 | return Err(SegmentReadError::InvalidHeaderCrc { |
| 132 | stored: crc_stored, |
| 133 | calc: crc_calc, |
| 134 | }); |
| 135 | } |
| 136 | |
| 137 | Ok(TSEG_HEADER_SIZE) |
| 138 | } |
| 139 | |
| 140 | // ── Public types ────────────────────────────────────────────────────────────── |
| 141 |