1use crate::IResult;
4use crate::ParserError;
5use nom::bytes::complete::take;
6use sqlite_types::{
7 Wal, WalFrame, WalFrameHeader, WalHeader, MAGIC_NUMBER_1, MAGIC_NUMBER_2, SUPPORTED_FILE_FORMAT,
8};
9
10type BoxError = Box<dyn std::error::Error>;
11
12pub fn decode(input: &[u8]) -> Result<Wal, BoxError> {
13 match decode_wal(input) {
14 Ok((_, wal)) => Ok(wal),
15 Err(err) => Err(format!("failed to decode: {}", err).into()),
16 }
17}
18
19fn decode_wal(input: &[u8]) -> IResult<&[u8], Wal> {
20 let (input, input_header) = take(32usize)(input)?;
21 let (_, header) = decode_header(&input_header)?;
22
23 let mut frames = vec![];
24 let mut input = input;
25 loop {
26 if input.len() < header.page_size as usize {
27 break;
29 }
30
31 let ret = decode_frame(&input, &header)?;
32 input = ret.0;
33 frames.push(ret.1);
34 }
35 Ok((input, Wal { header, frames }))
36}
37
38fn read_u32(input: &[u8]) -> IResult<&[u8], u32> {
39 let (input, value) = take(4usize)(input)?;
40 Ok((input, u32::from_be_bytes(value.try_into().unwrap())))
41}
42
43fn decode_header(input: &[u8]) -> IResult<&[u8], WalHeader> {
44 let (input, magic_number) = read_u32(input)?;
45
46 if magic_number != MAGIC_NUMBER_1 && magic_number != MAGIC_NUMBER_2 {
47 return Err(nom::Err::Failure(ParserError(format!(
48 "magic number not found, got: {:?}",
49 magic_number
50 ))));
51 }
52
53 let (input, file_format) = read_u32(input)?;
54
55 if file_format != SUPPORTED_FILE_FORMAT {
56 return Err(nom::Err::Failure(ParserError(format!(
57 "unsupported file format"
58 ))));
59 }
60
61 let (input, page_size) = read_u32(input)?;
62 let (input, checkpoint_seq) = read_u32(input)?;
63 let (input, salt_1) = read_u32(input)?;
64 let (input, salt_2) = read_u32(input)?;
65 let (input, checksum_1) = read_u32(input)?;
66 let (input, checksum_2) = read_u32(input)?;
67
68 Ok((
69 input,
70 WalHeader {
71 magic_number,
72 file_format,
73 page_size,
74 checkpoint_seq,
75 salt_1,
76 salt_2,
77 checksum_1,
78 checksum_2,
79 },
80 ))
81}
82
83fn decode_frame_header(input: &[u8]) -> IResult<&[u8], WalFrameHeader> {
84 let (input, page_number) = read_u32(input)?;
85 let (input, db_size_after_commit) = read_u32(input)?;
86 let (input, salt_1) = read_u32(input)?;
87 let (input, salt_2) = read_u32(input)?;
88 let (input, checksum_1) = read_u32(input)?;
89 let (input, checksum_2) = read_u32(input)?;
90
91 Ok((
92 input,
93 WalFrameHeader {
94 page_number,
95 db_size_after_commit,
96 salt_1,
97 salt_2,
98 checksum_1,
99 checksum_2,
100 },
101 ))
102}
103
104fn decode_frame<'a, 'b>(input: &'a [u8], wal_header: &'b WalHeader) -> IResult<&'a [u8], WalFrame> {
105 let (input, input_frame_header) = take(24usize)(input)?;
106 let (_, frame_header) = decode_frame_header(&input_frame_header)?;
107
108 if wal_header.salt_1 != frame_header.salt_1 || wal_header.salt_2 != frame_header.salt_2 {
109 return Err(nom::Err::Failure(ParserError(format!("Salt don't match"))));
110 }
111
112 let (input, data) = take(wal_header.page_size)(input)?;
115
116 Ok((
117 input,
118 WalFrame {
119 header: frame_header,
120 data: data.to_owned(),
121 },
122 ))
123}