#![allow(dead_code)]
use crate::tls::Error;
use alloc::vec::Vec;
pub(crate) const ACK_CONTENT_TYPE: u8 = 26;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct RecordNumber {
pub(crate) epoch: u64,
pub(crate) seq: u64,
}
const RECORD_NUMBER_LEN: usize = 16;
pub(crate) fn encode(records: &[RecordNumber]) -> Vec<u8> {
let len_bytes = records.len() * RECORD_NUMBER_LEN;
debug_assert!(
len_bytes <= u16::MAX as usize,
"ACK record_numbers vector exceeds u16::MAX bytes"
);
let mut out = Vec::with_capacity(2 + len_bytes);
out.extend_from_slice(&(len_bytes as u16).to_be_bytes());
for rn in records {
out.extend_from_slice(&rn.epoch.to_be_bytes());
out.extend_from_slice(&rn.seq.to_be_bytes());
}
out
}
pub(crate) fn decode(body: &[u8]) -> Result<Vec<RecordNumber>, Error> {
if body.len() < 2 {
return Err(Error::Decode);
}
let len = u16::from_be_bytes([body[0], body[1]]) as usize;
let rest = &body[2..];
if rest.len() != len {
return Err(Error::Decode);
}
if !len.is_multiple_of(RECORD_NUMBER_LEN) {
return Err(Error::Decode);
}
let count = len / RECORD_NUMBER_LEN;
let mut out = Vec::with_capacity(count);
let mut off = 0;
for _ in 0..count {
let epoch = u64::from_be_bytes(rest[off..off + 8].try_into().unwrap());
let seq = u64::from_be_bytes(rest[off + 8..off + 16].try_into().unwrap());
out.push(RecordNumber { epoch, seq });
off += RECORD_NUMBER_LEN;
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn empty_ack_roundtrip() {
let encoded = encode(&[]);
assert_eq!(encoded, vec![0x00, 0x00]);
let decoded = decode(&encoded).expect("empty ack decodes");
assert!(decoded.is_empty());
}
#[test]
fn single_record_roundtrip() {
let input = [RecordNumber { epoch: 3, seq: 100 }];
let encoded = encode(&input);
assert_eq!(encoded.len(), 18);
assert_eq!(&encoded[..2], &[0x00, 0x10]);
let decoded = decode(&encoded).expect("single ack decodes");
assert_eq!(decoded.as_slice(), &input);
}
#[test]
fn multi_record_roundtrip() {
let input = [
RecordNumber { epoch: 0, seq: 0 },
RecordNumber { epoch: 1, seq: 1 },
RecordNumber {
epoch: 2,
seq: 0xDEAD_BEEF,
},
RecordNumber {
epoch: 0xFFFF,
seq: 0x0123_4567_89AB_CDEF,
},
RecordNumber {
epoch: 42,
seq: 999_999_999,
},
];
let encoded = encode(&input);
assert_eq!(encoded.len(), 2 + 5 * 16);
let decoded = decode(&encoded).expect("multi ack decodes");
assert_eq!(decoded.as_slice(), &input);
}
#[test]
fn truncated_body_rejected() {
let mut bad = vec![0x00, 0x20];
bad.extend_from_slice(&[0u8; 16]);
assert_eq!(decode(&bad), Err(Error::Decode));
}
#[test]
fn shorter_than_length_prefix_rejected() {
assert_eq!(decode(&[0x00]), Err(Error::Decode));
assert_eq!(decode(&[]), Err(Error::Decode));
}
#[test]
fn non_multiple_of_16_rejected() {
let mut bad = vec![0x00, 0x0F];
bad.extend_from_slice(&[0u8; 15]);
assert_eq!(decode(&bad), Err(Error::Decode));
let mut bad = vec![0x00, 0x11];
bad.extend_from_slice(&[0u8; 17]);
assert_eq!(decode(&bad), Err(Error::Decode));
}
#[test]
fn trailing_bytes_after_vector_rejected() {
let mut bad = vec![0x00, 0x10];
bad.extend_from_slice(&[0u8; 18]);
assert_eq!(decode(&bad), Err(Error::Decode));
}
#[test]
fn ack_content_type_value() {
assert_eq!(ACK_CONTENT_TYPE, 26);
}
}