use pi_append_log::format::{
BlockDecoder, BlockEncoder, DEFAULT_MAGIC_BYTES, DEFAULT_MAX_PAYLOAD_LEN, DEFAULT_VERSION,
DefaultBlockCodec,
};
fn crc32(bytes: &[u8]) -> u32 {
let mut crc = !0u32;
for byte in bytes {
crc ^= u32::from(*byte);
for _ in 0..8 {
let mask = 0u32.wrapping_sub(crc & 1);
crc = (crc >> 1) ^ (0xedb8_8320 & mask);
}
}
!crc
}
#[test]
fn default_codec_exposes_four_mebibyte_limit_and_external_limit() {
assert_eq!(
DefaultBlockCodec::default().max_payload_len(),
DEFAULT_MAX_PAYLOAD_LEN
);
assert_eq!(DefaultBlockCodec::new(1234).max_payload_len(), 1234);
}
#[test]
fn codec_encodes_the_frozen_v1_layout() {
let codec = DefaultBlockCodec::new(1024);
let encoded = codec.encode(1, 0, b"abc").expect("encode must exist");
assert_eq!(u32::from_le_bytes(encoded[0..4].try_into().unwrap()), 19);
assert_eq!(&encoded[4..8], &DEFAULT_MAGIC_BYTES);
assert_eq!(
u16::from_le_bytes(encoded[8..10].try_into().unwrap()),
DEFAULT_VERSION
);
assert_eq!(u16::from_le_bytes(encoded[10..12].try_into().unwrap()), 0);
assert_eq!(u64::from_le_bytes(encoded[12..20].try_into().unwrap()), 1);
assert_eq!(&encoded[20..23], b"abc");
assert_eq!(u32::from_le_bytes(encoded[23..27].try_into().unwrap()), 19);
assert_eq!(
u32::from_le_bytes(encoded[27..31].try_into().unwrap()),
crc32(&encoded[..27])
);
}
#[test]
fn forward_and_backward_decoding_returns_a_borrowed_complete_block() {
let codec = DefaultBlockCodec::default();
let encoded = codec.encode(7, 0, b"payload").expect("encode must exist");
let forward = codec
.decode_forward(&encoded)
.expect("forward decode must exist");
assert_eq!(forward.encoded(), encoded.as_slice());
assert_eq!(forward.payload(), b"payload");
assert_eq!(forward.block_seq(), 7);
assert_eq!(forward.flags(), 0);
assert_eq!(forward.encoded_len(), encoded.len());
let backward = codec
.decode_backward(&encoded)
.expect("backward decode must exist");
assert_eq!(backward.payload(), b"payload");
assert_eq!(backward.block_seq(), 7);
}
#[test]
fn strict_decoding_rejects_invalid_fields_and_crc() {
let codec = DefaultBlockCodec::default();
let encoded = codec.encode(1, 0, b"payload").expect("encode must exist");
let mut invalid_crc = encoded.clone();
invalid_crc[20] ^= 1;
assert!(codec.decode_forward(&invalid_crc).is_err());
assert!(codec.decode_backward(&invalid_crc).is_err());
assert!(codec.encode(0, 0, b"payload").is_err());
assert!(codec.encode(1, 1, b"payload").is_err());
assert!(
codec
.encode(1, 0, &vec![0; DEFAULT_MAX_PAYLOAD_LEN + 1])
.is_err()
);
}
#[test]
fn tail_recovery_returns_the_last_complete_block_boundary() {
let codec = DefaultBlockCodec::default();
let first = codec.encode(1, 0, b"first").expect("encode must exist");
let second = codec.encode(2, 0, b"second").expect("encode must exist");
let mut stream = first.clone();
stream.extend_from_slice(&second);
let complete_len = stream.len();
stream.extend_from_slice(&second[..second.len() - 3]);
stream.extend_from_slice(b"garbage");
assert_eq!(
codec.find_last_complete(&stream).unwrap(),
Some(complete_len)
);
assert_eq!(codec.find_last_complete(&[]).unwrap(), None);
}
#[test]
fn tail_recovery_does_not_treat_payload_magic_as_a_block_start() {
let codec = DefaultBlockCodec::default();
let block = codec
.encode(1, 0, b"prefix-pial-suffix")
.expect("encode must exist");
let mut input = block.clone();
input.extend_from_slice(b"torn-tail");
assert_eq!(codec.find_last_complete(&input).unwrap(), Some(block.len()));
}