sq3_rs/file_header/db_filesize_in_pages.rs
1use std::num::NonZeroU32;
2use std::ops::Deref;
3
4use sq3_derive::Name;
5use sq3_parser::TypeName;
6
7use crate::{
8 result::{SqliteError, SqliteResult},
9 traits::ParseBytes,
10};
11
12/// # In-header database size (4 Bytes)
13///
14/// The in-header database size is a 4-byte big-endian integer at offset 28
15/// into the header stores the size of the database file in pages. If this
16/// in-header datasize size is not valid (see the next paragraph), then the
17/// database size is computed by looking at the actual size of the database
18/// file. Older versions of Sqlite ignored the in-header database size and used
19/// the actual file size exclusively. Newer versions of Sqlite use the in-header
20/// database size if it is available but fall back to the actual file size if
21/// the in-header database size is not valid.
22///
23/// The in-header database size is only considered to be valid if it is
24/// non-zero and if the 4-byte change counter at offset 24 exactly matches the
25/// 4-byte version-valid-for number at offset 92. The in-header database size is
26/// always valid when the database is only modified using recent versions of
27/// Sqlite, versions 3.7.0 (2010-07-21) and later. If a legacy version of Sqlite
28/// writes to the database, it will not know to update the in-header database
29/// size and so the in-header database size could be incorrect. But legacy
30/// versions of Sqlite will also leave the version-valid-for number at offset 92
31/// unchanged so it will not match the change-counter. Hence, invalid in-header
32/// database sizes can be detected (and ignored) by observing when the
33/// change-counter does not match the version-valid-for number.
34#[derive(Debug, Name)]
35pub struct DatabaseFileSizeInPages(u32);
36
37impl Default for DatabaseFileSizeInPages {
38 fn default() -> Self {
39 Self(1)
40 }
41}
42impl Deref for DatabaseFileSizeInPages {
43 type Target = u32;
44
45 fn deref(&self) -> &Self::Target {
46 &self.0
47 }
48}
49
50impl ParseBytes for DatabaseFileSizeInPages {
51 const LENGTH_BYTES: usize = 4;
52
53 fn parsing_handler(bytes: &[u8]) -> SqliteResult<Self> {
54 let buf: [u8; Self::LENGTH_BYTES] = bytes.try_into()?;
55
56 let database_size = NonZeroU32::new(u32::from_be_bytes(buf)).ok_or(SqliteError::Custom(
57 "DatabaseFileSizeInPages can't be `0`".into(),
58 ))?;
59
60 Ok(Self(database_size.get()))
61 }
62}