use super::snapshot::crc32_hash;
use rustc_hash::FxHashMap;
use std::io::{self, Write};
pub(super) const LEGACY_STORE_MARKER: u8 = 1;
pub(super) const LEGACY_DELETE_MARKER: u8 = 2;
pub(super) const CRC_STORE_MARKER: u8 = 0xC3;
pub(super) const CRC_DELETE_MARKER: u8 = 0xC4;
pub(super) fn compute_store_crc(id: u64, payload: &[u8]) -> u32 {
#[allow(clippy::cast_possible_truncation)]
let len_u32 = payload.len() as u32;
let mut buf = Vec::with_capacity(1 + 8 + 4 + payload.len());
buf.push(CRC_STORE_MARKER);
buf.extend_from_slice(&id.to_le_bytes());
buf.extend_from_slice(&len_u32.to_le_bytes());
buf.extend_from_slice(payload);
crc32_hash(&buf)
}
pub(super) fn compute_delete_crc(id: u64) -> u32 {
let mut buf = [0u8; 1 + 8];
buf[0] = CRC_DELETE_MARKER;
buf[1..9].copy_from_slice(&id.to_le_bytes());
crc32_hash(&buf)
}
pub(super) fn write_store_record(
wal: &mut io::BufWriter<std::fs::File>,
id: u64,
payload: &serde_json::Value,
offset: &mut u64,
index: &mut FxHashMap<u64, u64>,
record_buf: &mut Vec<u8>,
) -> io::Result<()> {
let record_start = *offset;
record_buf.clear();
record_buf.push(CRC_STORE_MARKER);
record_buf.extend_from_slice(&id.to_le_bytes());
let len_pos = record_buf.len();
record_buf.extend_from_slice(&0u32.to_le_bytes());
let payload_start = record_buf.len();
serde_json::to_writer(&mut *record_buf, payload)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let payload_len = record_buf.len() - payload_start;
let len_u32 = u32::try_from(payload_len)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "Payload too large"))?;
record_buf[len_pos..len_pos + 4].copy_from_slice(&len_u32.to_le_bytes());
let crc = crc32_hash(record_buf);
record_buf.extend_from_slice(&crc.to_le_bytes());
wal.write_all(record_buf)?;
let bytes_written = 1 + 8 + 4 + u64::from(len_u32) + 4;
*offset += bytes_written;
index.insert(id, record_start + 9);
Ok(())
}