1use sqlite_types::{checksum, Wal, WalFrame, WalFrameHeader, WalHeader, MAGIC_NUMBER_2};
2
3type BoxError = Box<dyn std::error::Error>;
4
5pub fn encode(wal: Wal) -> Result<Vec<u8>, BoxError> {
6 let mut buff = Vec::new();
7
8 let (checksum_1, checksum_2) = wal.header.checksum();
9
10 write_wal_header(&mut buff, &wal.header, checksum_1, checksum_2)
11 .map_err(|err| format!("failed to encode header: {}", err))?;
12
13 let mut checksum_1 = checksum_1;
14 let mut checksum_2 = checksum_2;
15
16 for frame in &wal.frames {
17 (checksum_1, checksum_2) = frame.header.checksum(checksum_1, checksum_2);
18 (checksum_1, checksum_2) = checksum_bytes(&frame.data, checksum_1, checksum_2);
19
20 write_wal_frame(&mut buff, frame, checksum_1, checksum_2).map_err(|err| {
21 format!(
22 "failed to write WAL frame #{}: {}",
23 frame.header.page_number, err
24 )
25 })?;
26 }
27
28 Ok(buff)
29}
30
31fn write_u32(writer: &mut Vec<u8>, value: u32) {
32 writer.extend(value.to_be_bytes());
33}
34
35pub fn write_wal_header(
36 writer: &mut Vec<u8>,
37 header: &WalHeader,
38 checksum_1: u32,
39 checksum_2: u32,
40) -> Result<(), BoxError> {
41 write_u32(writer, MAGIC_NUMBER_2);
43 write_u32(writer, header.file_format);
44 write_u32(writer, header.page_size);
45 write_u32(writer, header.checkpoint_seq);
46 write_u32(writer, header.salt_1);
47 write_u32(writer, header.salt_2);
48
49 write_u32(writer, checksum_1);
50 write_u32(writer, checksum_2);
51 Ok(())
52}
53
54pub fn write_wal_frame(
55 writer: &mut Vec<u8>,
56 frame: &WalFrame,
57 checksum_1: u32,
58 checksum_2: u32,
59) -> Result<(), BoxError> {
60 write_wal_frame_header(writer, &frame.header, checksum_1, checksum_2)?;
61 writer.extend(&frame.data);
62 Ok(())
63}
64
65pub fn write_wal_frame_header(
66 writer: &mut Vec<u8>,
67 header: &WalFrameHeader,
68 checksum_1: u32,
69 checksum_2: u32,
70) -> Result<(), BoxError> {
71 write_u32(writer, header.page_number);
72 write_u32(writer, header.db_size_after_commit);
73 write_u32(writer, header.salt_1);
74 write_u32(writer, header.salt_2);
75 write_u32(writer, checksum_1);
76 write_u32(writer, checksum_2);
77 Ok(())
78}
79
80fn checksum_bytes(bytes: &[u8], checksum_1: u32, checksum_2: u32) -> (u32, u32) {
81 let mut out = Vec::with_capacity(bytes.len() / 4);
82
83 for i in (0..bytes.len()).step_by(4) {
84 let v = u32::from_be_bytes([bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]]);
85 out.push(v);
86 }
87
88 checksum(&out, Some(checksum_1), Some(checksum_2))
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 }