sqlite_decoder/
db.rs

1//! https://www.sqlite.org/fileformat.html
2use crate::util::{read_u16, read_u32, read_u8};
3use crate::IResult;
4use crate::ParserError;
5use nom::bytes::complete::take;
6use sqlite_types::{Db, DbHeader, TextEncoding, MAGIC_STRING};
7use std::collections::HashMap;
8
9type BoxError = Box<dyn std::error::Error>;
10
11pub fn decode<'a>(input: &'a [u8]) -> Result<Db, BoxError> {
12    match decode_db(input) {
13        Ok((_, db)) => Ok(db),
14        Err(err) => Err(format!("failed to decode: {}", err).into()),
15    }
16}
17
18fn decode_db<'a, 'b>(input: &'a [u8]) -> IResult<&'a [u8], Db> {
19    let mut pages = HashMap::new();
20
21    let (input, input_header) = take(100usize)(input)?;
22    let (_, header) = decode_header_inner(&input_header)?;
23
24    // Eat align to page size and discard the bytes
25    let (input, bytes) = take(header.page_size - 100)(input)?;
26
27    // First page contains the header and is page aligned
28    let first_page = [input_header, bytes].concat();
29    pages.insert(1, first_page);
30
31    // The remaining bytes should be pages and the number should match the
32    // db_size in the header
33    assert_eq!(
34        input.len(),
35        header.page_size as usize * (header.db_size as usize - 1)
36    );
37
38    let page_count = input.len() / header.page_size as usize;
39    println!("page_count: {}", page_count);
40
41    let mut input = input;
42    for i in 1..=page_count {
43        let ret = take(header.page_size)(input)?;
44        input = ret.0;
45
46        // Page number are 1 indexed and 1 is the db header
47        let page_number = i + 1;
48        pages.insert(page_number as u32, ret.1.to_owned());
49    }
50
51    assert_eq!(pages.len(), header.db_size as usize);
52    Ok((input, Db { header, pages }))
53}
54
55pub fn decode_header(input: &[u8]) -> Result<DbHeader, BoxError> {
56    match decode_header_inner(input) {
57        Ok((_, header)) => Ok(header),
58        Err(err) => Err(format!("failed to decode: {}", err).into()),
59    }
60}
61
62fn decode_text_encoding(input: &[u8]) -> IResult<&[u8], TextEncoding> {
63    let (input, t) = read_u32(input)?;
64
65    use TextEncoding::*;
66    let enc = match t {
67        1 => UTF8,
68        2 => UTF16le,
69        3 => UTF16be,
70        e => {
71            return Err(nom::Err::Failure(ParserError(format!(
72                "unsupported text encoding: {}",
73                e
74            ))))
75        }
76    };
77
78    Ok((input, enc))
79}
80
81fn decode_header_inner(input: &[u8]) -> IResult<&[u8], DbHeader> {
82    let (input, magic_string) = take(16usize)(input)?;
83    if magic_string != MAGIC_STRING {
84        return Err(nom::Err::Failure(ParserError(format!(
85            "magic string not found, got: {:?}",
86            magic_string
87        ))));
88    }
89
90    let (input, page_size) = read_u16(input)?;
91    let (input, file_format_write_version) = read_u8(input)?;
92    let (input, file_format_read_version) = read_u8(input)?;
93    let (input, _reserved) = take(1usize)(input)?;
94    let (input, max_embedded_payload_frac) = read_u8(input)?;
95    let (input, min_embedded_payload_frac) = read_u8(input)?;
96    let (input, leaf_payload_frac) = read_u8(input)?;
97    let (input, file_change_counter) = read_u32(input)?;
98    let (input, db_size) = read_u32(input)?;
99    let (input, page_num_first_freelist) = read_u32(input)?;
100    let (input, page_count_freelist) = read_u32(input)?;
101    let (input, schema_cookie) = read_u32(input)?;
102    let (input, schema_format_number) = read_u32(input)?;
103    let (input, default_page_cache_size) = read_u32(input)?;
104    let (input, page_num_largest_root_btree) = read_u32(input)?;
105    let (input, text_encoding) = decode_text_encoding(input)?;
106    let (input, user_version) = read_u32(input)?;
107    let (input, vaccum_mode) = read_u32(input)?;
108    let (input, app_id) = read_u32(input)?;
109    let (input, _reserved) = take(20usize)(input)?;
110    let (input, version_valid_for) = read_u32(input)?;
111    let (input, sqlite_version) = read_u32(input)?;
112
113    let page_size = if page_size == 1 {
114        65536
115    } else {
116        page_size as u32
117    };
118
119    Ok((
120        input,
121        DbHeader {
122            page_size,
123            file_format_write_version,
124            file_format_read_version,
125            max_embedded_payload_frac,
126            min_embedded_payload_frac,
127            leaf_payload_frac,
128            file_change_counter,
129            db_size,
130            page_num_first_freelist,
131            page_count_freelist,
132            schema_cookie,
133            schema_format_number,
134            default_page_cache_size,
135            page_num_largest_root_btree,
136            text_encoding,
137            user_version,
138            vaccum_mode,
139            app_id,
140            version_valid_for,
141            sqlite_version,
142        },
143    ))
144}