1use std::collections::HashMap;
2
3pub const MAGIC_NUMBER_1: u32 = 0x377f0682;
4pub const MAGIC_NUMBER_2: u32 = 0x377f0683;
5pub const SUPPORTED_FILE_FORMAT: u32 = 3007000;
6pub const MAGIC_STRING: &[u8] = b"SQLite format 3\0";
7pub const SQLITE_3_37_2_VERSION: u32 = 3038002;
8
9#[derive(Debug, Clone)]
10pub enum TextEncoding {
11 UTF8,
12 UTF16le,
13 UTF16be,
14}
15
16pub type Page = Vec<u8>;
17
18#[derive(Debug)]
19pub struct Db {
20 pub header: DbHeader,
21 pub pages: HashMap<u32, Page>,
22}
23
24#[derive(Debug, Clone)]
25pub struct DbHeader {
26 pub page_size: u32,
30 pub file_format_write_version: u8,
31 pub file_format_read_version: u8,
32 pub max_embedded_payload_frac: u8,
33 pub min_embedded_payload_frac: u8,
34 pub leaf_payload_frac: u8,
35 pub file_change_counter: u32,
36 pub db_size: u32,
37 pub page_num_first_freelist: u32,
38 pub page_count_freelist: u32,
39 pub schema_cookie: u32,
40 pub schema_format_number: u32,
41 pub default_page_cache_size: u32,
42 pub page_num_largest_root_btree: u32,
43 pub text_encoding: TextEncoding,
44 pub user_version: u32,
45 pub vaccum_mode: u32,
46 pub app_id: u32,
47 pub version_valid_for: u32,
48 pub sqlite_version: u32,
49}
50
51#[derive(Debug, Clone)]
52pub struct Wal {
53 pub header: WalHeader,
54 pub frames: Vec<WalFrame>,
55}
56
57#[derive(Debug, Clone)]
58pub struct WalHeader {
59 pub magic_number: u32,
60 pub file_format: u32,
61 pub page_size: u32,
62 pub checkpoint_seq: u32,
63 pub salt_1: u32,
64 pub salt_2: u32,
65 pub checksum_1: u32,
66 pub checksum_2: u32,
67}
68
69#[derive(Debug, Clone)]
70pub struct WalFrameHeader {
71 pub page_number: u32,
72 pub db_size_after_commit: u32,
73 pub salt_1: u32,
74 pub salt_2: u32,
75 pub checksum_1: u32,
76 pub checksum_2: u32,
77}
78
79#[derive(Debug, Clone)]
80pub struct WalFrame {
81 pub header: WalFrameHeader,
82 pub data: Vec<u8>,
83}
84
85impl Wal {
86 pub fn rewrite_salt_1(mut self, value: u32) -> Self {
87 self.header.salt_1 = value;
88 for frame in &mut self.frames {
89 frame.header.salt_1 = value;
90 }
91
92 self
93 }
94
95 pub fn rewrite_salt_2(mut self, value: u32) -> Self {
96 self.header.salt_2 = value;
97 for frame in &mut self.frames {
98 frame.header.salt_2 = value;
99 }
100
101 self
102 }
103}
104
105impl WalHeader {
106 pub fn checksum(&self) -> (u32, u32) {
107 let values = [
108 MAGIC_NUMBER_2,
110 self.file_format,
111 self.page_size,
112 self.checkpoint_seq,
113 self.salt_1,
114 self.salt_2,
115 ];
116 checksum(&values, None, None)
117 }
118}
119
120impl WalFrameHeader {
121 pub fn checksum(&self, checksum_1: u32, checksum_2: u32) -> (u32, u32) {
122 let values = [self.page_number, self.db_size_after_commit];
123 checksum(&values, Some(checksum_1), Some(checksum_2))
124 }
125}
126
127pub fn checksum(input: &[u32], s1: Option<u32>, s2: Option<u32>) -> (u32, u32) {
128 let mut s1 = if let Some(s1) = s1 { s1 } else { 0u32 };
129 let mut s2 = if let Some(s2) = s2 { s2 } else { 0u32 };
130
131 if input.len() * 4 % 8 != 0 {
132 panic!("input must be a multiple of 8 bytes, given {}", input.len())
133 }
134
135 let mut i = 0;
136 loop {
137 s1 = s1.wrapping_add(input[i].wrapping_add(s2));
138 s2 = s2.wrapping_add(input[i + 1].wrapping_add(s1));
139
140 i += 2;
141 if i >= input.len() {
142 break;
143 }
144 }
145
146 (s1, s2)
147}