sqlite_types/
lib.rs

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    /// Page size of the database
27    /// While it should be of type u16,
28    /// it uses u32 to support the special 64kib page size.
29    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 uses big-endian, which we'll assume for now.
109            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}