sq3_rs/file_header/
freelist_pages.rs

1//! # The Freelist
2//!  A database file might contain one or more pages that are not in active use.
3//! Unused pages can come about, for example, when information is deleted from
4//! the database. Unused pages are stored on the freelist and are reused when
5//! additional pages are required.
6//!
7//!  The freelist is organized as a linked list of freelist trunk pages with
8//! each trunk page containing page numbers for zero or more freelist leaf
9//! pages.
10//!
11//!  A freelist trunk page consists of an array of 4-byte big-endian integers.
12//! The size of the array is as many integers as will fit in the usable space of
13//! a page. The minimum usable space is 480 bytes so the array will always be at
14//! least 120 entries in length. The first integer on a freelist trunk page is
15//! the page number of the next freelist trunk page in the list or zero if this
16//! is the last freelist trunk page. The second integer on a freelist trunk page
17//! is the number of leaf page pointers to follow. Call the second integer on a
18//! freelist trunk page L. If L is greater than zero then integers with array
19//! indexes between 2 and L+1 inclusive contain page numbers for freelist leaf
20//! pages.
21//!
22//!  Freelist leaf pages contain no information. SQLite avoids reading or
23//! writing freelist leaf pages in order to reduce disk I/O.
24//!
25//!  A bug in SQLite versions prior to 3.6.0 (2008-07-16) caused the database to
26//! be reported as corrupt if any of the last 6 entries in the freelist trunk
27//! page array contained non-zero values. Newer versions of SQLite do not have
28//! this problem. However, newer versions of SQLite still avoid using the last
29//! six entries in the freelist trunk page array in order that database files
30//! created by newer versions of SQLite can be read by older versions of SQLite.
31//!
32//!  The number of freelist pages is stored as a 4-byte big-endian integer in
33//! the database header at an offset of 36 from the beginning of the file. The
34//! database header also stores the page number of the first freelist trunk page
35//! as a 4-byte big-endian integer at an offset of 32 from the beginning of the
36//! file.
37
38use std::ops::Deref;
39
40use sq3_derive::Name;
41use sq3_parser::TypeName;
42
43use crate::{result::SqliteResult, traits::ParseBytes};
44
45/// # Free page list (8 Bytes) => First(4 Bytes) + TotalPages (4 Bytes)
46///  Unused pages in the database file are stored on a freelist.
47#[derive(Debug, Default, Name)]
48pub struct FreeListPages {
49    /// Page number of the first freelist trunk page. (4 Bytes)
50    first: FreeListPagesFirstTrunkPage,
51    /// Total number of freelist pages. (4 Bytes)
52    total: FreeListPagesTotalPages,
53}
54
55impl FreeListPages {
56    pub fn first(&self) -> &FreeListPagesFirstTrunkPage {
57        &self.first
58    }
59
60    pub fn total(&self) -> &FreeListPagesTotalPages {
61        &self.total
62    }
63}
64
65impl ParseBytes for FreeListPages {
66    const LENGTH_BYTES: usize = 8;
67
68    fn parsing_handler(bytes: &[u8]) -> SqliteResult<Self> {
69        let first = FreeListPagesFirstTrunkPage::parse_bytes(&bytes[0..=3])?;
70        let total = FreeListPagesTotalPages::parse_bytes(&bytes[4..=7])?;
71
72        Ok(Self { first, total })
73    }
74}
75
76///  FreeListPagesFirstTrunkPage: The 4-byte big-endian integer at offset 32
77/// stores the page number of the first page of the freelist, or zero if the
78/// freelist is empty.
79#[derive(Debug, Default, Name)]
80pub struct FreeListPagesFirstTrunkPage(u32);
81impl Deref for FreeListPagesFirstTrunkPage {
82    type Target = u32;
83
84    fn deref(&self) -> &Self::Target {
85        &self.0
86    }
87}
88
89impl ParseBytes for FreeListPagesFirstTrunkPage {
90    const LENGTH_BYTES: usize = 4;
91
92    fn parsing_handler(bytes: &[u8]) -> SqliteResult<Self> {
93        let buf: [u8; Self::LENGTH_BYTES] = bytes.try_into()?;
94        let first_page_trunk = u32::from_be_bytes(buf);
95        Ok(Self(first_page_trunk))
96    }
97}
98
99///  FreeListPagesTotalPages: The 4-byte big-endian integer at offset 36
100/// stores the total number of pages on the freelist.
101#[derive(Debug, Default, Name)]
102pub struct FreeListPagesTotalPages(u32);
103impl Deref for FreeListPagesTotalPages {
104    type Target = u32;
105
106    fn deref(&self) -> &Self::Target {
107        &self.0
108    }
109}
110
111impl ParseBytes for FreeListPagesTotalPages {
112    const LENGTH_BYTES: usize = 4;
113
114    fn parsing_handler(bytes: &[u8]) -> SqliteResult<Self> {
115        let buf: [u8; Self::LENGTH_BYTES] = bytes.try_into()?;
116        let total_pages = u32::from_be_bytes(buf);
117        Ok(Self(total_pages))
118    }
119}