pi_append_log 0.3.0

Storage-agnostic append-only block log traits, codec, layout, and file backend
use pi_append_log::format::{
    BlockDecoder, BlockEncoder, DEFAULT_MAGIC_BYTES, DEFAULT_MAX_PAYLOAD_LEN, DEFAULT_VERSION,
    DefaultBlockCodec,
};

// 使用独立实现计算 CRC32,用于验证生产 codec 的覆盖范围和结果,而不是复用被测代码。
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]
// 验证默认 codec 使用 4 MiB 上限,并确认调用方可以传入其他 Payload 上限。
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]
// 验证 V1 envelope 的每个字段、字段顺序、长度计算、Magic、Version、Payload 和 CRC32。
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]
// 验证正向和反向解析都返回借用输入的完整 block,并正确暴露 Payload、序号、Flags 和长度。
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]
// 验证严格解析拒绝 CRC 损坏、零序号、未知 Flags 和超过配置上限的 Payload。
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]
// 验证尾部恢复可以跳过半写 block 和垃圾,并返回最后一个完整 block 的结束偏移;空输入返回 None。
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]
// 验证 Payload 内出现与 Magic 相同的字节时,尾部探测仍会回到真正的 block 起点并完成严格校验。
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()));
}