Skip to main content

page_db/
lib.rs

1//! # page-db
2//!
3//! The paging substrate beneath B-tree and heap storage engines: fixed-size
4//! pages on disk, each carrying a versioned header with a CRC32C integrity
5//! check and an LSN slot for write-ahead-log coordination, read and written
6//! through cross-platform Direct I/O that bypasses the OS page cache.
7//!
8//! Two layers ship today. The [`PageFile`] is the durable foundation: an array
9//! of fixed-size [`Page`]s addressed by [`PageId`], read and written through
10//! Direct I/O, every read verified against its header and checksum. The
11//! [`BufferPool`] sits on top: a bounded cache of frames with pinning and dirty
12//! tracking, so hot pages stay resident and the engine above asks for a page by
13//! id and gets back a pinned frame. The page allocator (a free-list over the
14//! file) is the remaining 0.x piece; see `dev/ROADMAP.md`.
15//!
16//! ## Straight to the file
17//!
18//! Every page carries a header; on write the header's checksum is stamped over
19//! the page bytes, and on read it is verified before the page is handed back —
20//! a corrupt or misdirected page is a typed [`PageError`], never a silent read.
21//!
22//! ```no_run
23//! use page_db::{PageFile, PageId, Lsn};
24//!
25//! # fn main() -> Result<(), page_db::PageError> {
26//! // A 4 KiB-page file, Direct I/O, created if absent.
27//! let file = PageFile::open("data.pages", page_db::DEFAULT_PAGE_SIZE)?;
28//!
29//! // Allocate a fresh page, fill its payload, tag it with a log sequence number.
30//! let mut page = file.allocate_page();
31//! page.set_lsn(Lsn::new(1));
32//! page.payload_mut()[..5].copy_from_slice(b"hello");
33//!
34//! // Write it to slot 0 and make it durable.
35//! let id = PageId::new(0);
36//! file.write_page(id, &mut page)?;
37//! file.sync()?;
38//!
39//! // Read it back — the header and checksum are verified on the way out.
40//! let read = file.read_page(id)?;
41//! assert_eq!(&read.payload()[..5], b"hello");
42//! assert_eq!(read.lsn(), Lsn::new(1));
43//! # Ok(())
44//! # }
45//! ```
46//!
47//! ## Through the buffer pool
48//!
49//! ```no_run
50//! use page_db::{BufferPool, PageId, Lsn, DEFAULT_PAGE_SIZE};
51//!
52//! # fn main() -> Result<(), page_db::PageError> {
53//! let pool = BufferPool::open("data.pages", DEFAULT_PAGE_SIZE, 256)?;
54//!
55//! // Create a page; writing through the guard marks the frame dirty.
56//! {
57//!     let guard = pool.new_page(PageId::new(0))?;
58//!     guard.write().set_lsn(Lsn::new(1));
59//! }
60//! pool.checkpoint()?;   // flush dirty frames, then make the file durable
61//!
62//! // Fetch it — served from cache, the page stays pinned for the guard's life.
63//! let guard = pool.fetch(PageId::new(0))?;
64//! assert_eq!(guard.read().lsn(), Lsn::new(1));
65//! # Ok(())
66//! # }
67//! ```
68
69#![cfg_attr(docsrs, feature(doc_cfg))]
70#![deny(missing_docs)]
71#![deny(unsafe_op_in_unsafe_fn)]
72#![deny(unused_must_use)]
73#![deny(unused_results)]
74#![deny(clippy::unwrap_used)]
75#![deny(clippy::expect_used)]
76#![deny(clippy::todo)]
77#![deny(clippy::unimplemented)]
78#![deny(clippy::print_stdout)]
79#![deny(clippy::print_stderr)]
80#![deny(clippy::dbg_macro)]
81#![deny(clippy::undocumented_unsafe_blocks)]
82
83mod buffer;
84pub mod checksum;
85mod error;
86mod file;
87mod page;
88mod pool;
89mod store;
90mod sync;
91mod sys;
92
93pub use crate::checksum::crc32c;
94pub use crate::error::{PageError, PageResult};
95pub use crate::file::{PageFile, PageFileOptions};
96pub use crate::page::{
97    DEFAULT_PAGE_SIZE, Lsn, MAX_PAGE_SIZE, MIN_PAGE_SIZE, PAGE_HEADER_SIZE, Page, PageId, PageSize,
98};
99pub use crate::pool::{BufferPool, PageGuard, PageMut, PageRef};
100pub use crate::store::PageStore;