hypercall_journal/
frame.rs1use crc::{Crc, CRC_32_ISO_HDLC};
2use std::io::{Read, Write};
3
4pub const WAL_CRC: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
10
11pub const WAL_FRAME_HEADER_LEN: u64 = 8;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct WalFrame {
15 pub payload: Vec<u8>,
16 pub record_start_offset: u64,
17 pub end_offset: u64,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum WalReadStatus {
22 Eof,
23 Torn,
24}
25
26pub fn write_frame(
27 writer: &mut impl Write,
28 payload: &[u8],
29 next_offset: &mut u64,
30 description: &str,
31) -> Result<u64, String> {
32 let payload_len =
33 u32::try_from(payload.len()).map_err(|_| format!("WAL {description} payload too large"))?;
34 let crc = WAL_CRC.checksum(payload);
35
36 writer
37 .write_all(&payload_len.to_le_bytes())
38 .map_err(|e| format!("failed to write WAL length prefix: {}", e))?;
39 writer
40 .write_all(&crc.to_le_bytes())
41 .map_err(|e| format!("failed to write WAL crc prefix: {}", e))?;
42 writer
43 .write_all(payload)
44 .map_err(|e| format!("failed to write WAL payload: {}", e))?;
45
46 *next_offset = next_offset
47 .checked_add(WAL_FRAME_HEADER_LEN + payload.len() as u64)
48 .ok_or_else(|| "WAL offset overflow".to_string())?;
49
50 Ok(*next_offset)
51}
52
53pub fn read_next_frame(
54 reader: &mut impl Read,
55 file_len: u64,
56 offset: &mut u64,
57) -> Result<Result<WalFrame, WalReadStatus>, String> {
58 let record_start_offset = *offset;
59 let mut len_buf = [0u8; 4];
60 match reader.read_exact(&mut len_buf) {
61 Ok(()) => {}
62 Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
63 return Ok(Err(WalReadStatus::Eof));
64 }
65 Err(e) => return Err(format!("failed reading WAL length prefix: {}", e)),
66 }
67
68 let payload_len = u32::from_le_bytes(len_buf) as usize;
69 if payload_len == 0 {
70 panic!(
71 "CRITICAL_FAILURE: WAL record length was zero at offset {}. Data corruption detected.",
72 offset
73 );
74 }
75 *offset += 4;
76
77 let remaining = file_len.saturating_sub(*offset);
78 if payload_len as u64 > remaining {
79 return Ok(Err(WalReadStatus::Torn));
80 }
81
82 let mut crc_buf = [0u8; 4];
83 match reader.read_exact(&mut crc_buf) {
84 Ok(()) => {}
85 Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
86 return Ok(Err(WalReadStatus::Torn));
87 }
88 Err(e) => return Err(format!("failed reading WAL crc prefix: {}", e)),
89 }
90 *offset += 4;
91
92 let expected_crc = u32::from_le_bytes(crc_buf);
93 let mut payload = vec![0u8; payload_len];
94 match reader.read_exact(&mut payload) {
95 Ok(()) => {}
96 Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
97 return Ok(Err(WalReadStatus::Torn));
98 }
99 Err(e) => return Err(format!("failed reading WAL payload: {}", e)),
100 }
101 *offset += payload_len as u64;
102
103 let actual_crc = WAL_CRC.checksum(&payload);
104 if actual_crc != expected_crc {
105 panic!(
106 "CRITICAL_FAILURE: WAL CRC mismatch at end_offset {}: expected {}, got {}. Data corruption detected.",
107 offset, expected_crc, actual_crc
108 );
109 }
110
111 Ok(Ok(WalFrame {
112 payload,
113 record_start_offset,
114 end_offset: *offset,
115 }))
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn frame_roundtrips_payload() {
124 let mut bytes = Vec::new();
125 let mut offset = 0;
126 write_frame(&mut bytes, b"payload", &mut offset, "test").unwrap();
127
128 let mut read_offset = 0;
129 let frame = read_next_frame(&mut bytes.as_slice(), bytes.len() as u64, &mut read_offset)
130 .unwrap()
131 .unwrap();
132
133 assert_eq!(frame.payload, b"payload");
134 assert_eq!(frame.record_start_offset, 0);
135 assert_eq!(frame.end_offset, offset);
136 assert_eq!(read_offset, offset);
137 }
138
139 #[test]
140 fn truncated_frame_reports_torn() {
141 let mut bytes = Vec::new();
142 let mut offset = 0;
143 write_frame(&mut bytes, b"payload", &mut offset, "test").unwrap();
144 bytes.truncate(bytes.len() - 1);
145
146 let mut read_offset = 0;
147 assert_eq!(
148 read_next_frame(&mut bytes.as_slice(), bytes.len() as u64, &mut read_offset).unwrap(),
149 Err(WalReadStatus::Torn)
150 );
151 }
152}