Validate the envelope header and deserialize the body into `T`. `expected_version` is checked exactly — bumping a codec's on-disk format is an explicit decision; silently accepting older bodies has bitten us before. Callers that need a window of compatible versions should call [`peek_version`] and dispatch.
(
magic: &[u8; MAGIC_LEN],
expected_version: u8,
buf: &[u8],
)
| 40 | /// before. Callers that need a window of compatible versions should call |
| 41 | /// [`peek_version`] and dispatch. |
| 42 | pub fn decode<T: for<'de> FromMessagePack<'de>>( |
| 43 | magic: &[u8; MAGIC_LEN], |
| 44 | expected_version: u8, |
| 45 | buf: &[u8], |
| 46 | ) -> Result<T, CodecError> { |
| 47 | if buf.len() < HEADER_LEN { |
| 48 | return Err(CodecError::Truncated { |
| 49 | expected: HEADER_LEN, |
| 50 | actual: buf.len(), |
| 51 | }); |
| 52 | } |
| 53 | if &buf[..MAGIC_LEN] != magic { |
| 54 | return Err(CodecError::Corrupt { |
| 55 | detail: "bad magic".into(), |
| 56 | }); |
| 57 | } |
| 58 | let got = buf[MAGIC_LEN]; |
| 59 | if got != expected_version { |
| 60 | return Err(CodecError::Corrupt { |
| 61 | detail: format!("unsupported version {got}"), |
| 62 | }); |
| 63 | } |
| 64 | zerompk::from_msgpack(&buf[HEADER_LEN..]).map_err(|e| CodecError::Corrupt { |
| 65 | detail: e.to_string(), |
| 66 | }) |
| 67 | } |
| 68 | |
| 69 | /// Read the version byte without decoding the body. Returns `None` if the |
| 70 | /// buffer is too short or the magic does not match. |