mod reader;
#[cfg(test)]
mod tests;
use tessarikv_constants::MAX_LOG_PAYLOAD_BYTES;
use crate::error::{Error, Result};
pub use reader::{EndKind, FrameReader, LogEnd, LogRead};
pub const FRAME_HEADER_BYTES: usize = 16;
const _: () = assert!(matches!(
MAX_LOG_PAYLOAD_BYTES.checked_shr(32),
None | Some(0)
));
pub fn frame_bytes(payload_len: usize) -> Result<usize> {
if payload_len > MAX_LOG_PAYLOAD_BYTES {
return Err(too_large(payload_len));
}
payload_len
.checked_add(FRAME_HEADER_BYTES)
.ok_or_else(|| too_large(payload_len))
}
pub fn encode_frame(log_number: u64, payload: &[u8], destination: &mut [u8]) -> Result<()> {
if destination.len() != frame_bytes(payload.len())? {
return Err(Error::Malformed {
what: "log frame",
reason: "destination is not exactly one frame long",
});
}
let (header, body) = destination.split_at_mut(FRAME_HEADER_BYTES);
body.copy_from_slice(payload);
header.copy_from_slice(&frame_header(
log_number,
payload.len(),
payload_checksum(payload),
)?);
Ok(())
}
pub fn payload_checksum(payload: &[u8]) -> u32 {
crc32c::crc32c(payload)
}
pub fn concat_checksum(front: u32, back: u32, back_len: usize) -> u32 {
crc32c::crc32c_combine(front, back, back_len)
}
pub fn frame_header(
log_number: u64,
payload_len: usize,
payload_checksum: u32,
) -> Result<[u8; FRAME_HEADER_BYTES]> {
frame_bytes(payload_len)?;
let length = u32::try_from(payload_len).map_err(|_| too_large(payload_len))?;
let mut header = [0; FRAME_HEADER_BYTES];
header[4..8].copy_from_slice(&length.to_le_bytes());
header[8..16].copy_from_slice(&log_number.to_le_bytes());
let checksum = crc32c::crc32c_append(payload_checksum, &header[4..16]);
header[0..4].copy_from_slice(&checksum.to_le_bytes());
Ok(header)
}
fn checksum(fields: &[u8], payload: &[u8]) -> u32 {
crc32c::crc32c_append(crc32c::crc32c(payload), fields)
}
fn too_large(payload_len: usize) -> Error {
Error::TooLarge {
what: "log frame payload",
size: u64::try_from(payload_len).unwrap_or(u64::MAX),
limit: u64::try_from(MAX_LOG_PAYLOAD_BYTES).unwrap_or(u64::MAX),
}
}