use log::trace;
use crate::{
error::{Error, Result},
header::{HEADER_SIZE, RecordHeader},
};
#[inline]
pub const fn record_size(key_len: usize, val_len: usize) -> usize {
HEADER_SIZE.saturating_add(key_len).saturating_add(val_len)
}
#[inline]
pub const fn checked_record_size(key_len: usize, val_len: usize) -> Option<usize> {
let Some(s) = HEADER_SIZE.checked_add(key_len) else {
return None;
};
s.checked_add(val_len)
}
#[inline]
fn build_header(
prev_addr: u64,
key: &[u8],
val: &[u8],
is_tombstone: bool,
) -> Result<(RecordHeader, usize)> {
if key.len() > u32::MAX as usize {
return Err(Error::KeyLengthOverflow(key.len()));
}
if val.len() > u32::MAX as usize {
return Err(Error::ValueLengthOverflow(val.len()));
}
let header = RecordHeader::new(prev_addr, key.len() as u32, val.len() as u32, is_tombstone)?;
let total_size = checked_record_size(key.len(), val.len()).ok_or(Error::RecordSizeOverflow)?;
Ok((header, total_size))
}
pub fn encode_to_slice(
dst: &mut [u8],
prev_addr: u64,
key: &[u8],
val: &[u8],
is_tombstone: bool,
) -> Result<usize> {
let (header, total_size) = build_header(prev_addr, key, val, is_tombstone)?;
if dst.len() < total_size {
return Err(Error::BufferTooShort {
expected: total_size,
actual: dst.len(),
});
}
let key_len = header.key_len();
let val_len = header.val_len();
trace!(
"编码记录: prev_addr={prev_addr:#x}, key_len={key_len}, val_len={val_len}, is_tombstone={is_tombstone}, total_size={total_size}"
);
let key_end = HEADER_SIZE + key.len();
dst[..HEADER_SIZE].copy_from_slice(&header.to_bytes());
dst[HEADER_SIZE..key_end].copy_from_slice(key);
dst[key_end..total_size].copy_from_slice(val);
Ok(total_size)
}
pub fn try_encode_to_vec(
prev_addr: u64,
key: &[u8],
val: &[u8],
is_tombstone: bool,
) -> Result<Vec<u8>> {
let (header, total_size) = build_header(prev_addr, key, val, is_tombstone)?;
let mut buf = Vec::with_capacity(total_size);
buf.extend_from_slice(&header.to_bytes());
buf.extend_from_slice(key);
buf.extend_from_slice(val);
Ok(buf)
}