Skip to main content

kyu_storage/
page_id.rs

1/// Size of a single database page in bytes (4 KiB).
2pub const PAGE_SIZE: usize = 4096;
3
4/// File identifier within the storage layer.
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
6pub struct FileId(pub u32);
7
8/// A page identifier: (file_id, page_idx) uniquely identifies a page on disk.
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10pub struct PageId {
11    pub file_id: FileId,
12    pub page_idx: u32,
13}
14
15impl PageId {
16    pub const fn new(file_id: FileId, page_idx: u32) -> Self {
17        Self { file_id, page_idx }
18    }
19
20    /// Encode as a u64 for atomic storage: high 32 bits = file_id, low 32 bits = page_idx.
21    pub const fn to_u64(self) -> u64 {
22        ((self.file_id.0 as u64) << 32) | (self.page_idx as u64)
23    }
24
25    /// Decode from a u64.
26    pub const fn from_u64(val: u64) -> Self {
27        Self {
28            file_id: FileId((val >> 32) as u32),
29            page_idx: val as u32,
30        }
31    }
32
33    /// Sentinel value for "no page loaded".
34    pub const INVALID: Self = Self {
35        file_id: FileId(u32::MAX),
36        page_idx: u32::MAX,
37    };
38
39    pub const fn is_valid(self) -> bool {
40        self.file_id.0 != u32::MAX
41    }
42}
43
44impl std::fmt::Display for PageId {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        write!(f, "{}:{}", self.file_id.0, self.page_idx)
47    }
48}
49
50/// Index of a frame within a pool.
51#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
52pub struct FrameIdx(pub u32);
53
54/// Identifies which pool a frame belongs to.
55#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
56pub enum PoolId {
57    Read,
58    Write,
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn page_id_roundtrip() {
67        let pid = PageId::new(FileId(42), 1000);
68        let encoded = pid.to_u64();
69        let decoded = PageId::from_u64(encoded);
70        assert_eq!(pid, decoded);
71    }
72
73    #[test]
74    fn page_id_invalid() {
75        assert!(!PageId::INVALID.is_valid());
76        assert!(PageId::new(FileId(0), 0).is_valid());
77    }
78
79    #[test]
80    fn page_id_display() {
81        let pid = PageId::new(FileId(3), 42);
82        assert_eq!(pid.to_string(), "3:42");
83    }
84
85    #[test]
86    fn page_id_encoding() {
87        let pid = PageId::new(FileId(1), 2);
88        let val = pid.to_u64();
89        assert_eq!(val, (1u64 << 32) | 2);
90    }
91
92    #[test]
93    fn page_size() {
94        assert_eq!(PAGE_SIZE, 4096);
95    }
96
97    #[test]
98    fn pool_id_eq() {
99        assert_eq!(PoolId::Read, PoolId::Read);
100        assert_ne!(PoolId::Read, PoolId::Write);
101    }
102
103    #[test]
104    fn frame_idx_eq() {
105        assert_eq!(FrameIdx(0), FrameIdx(0));
106        assert_ne!(FrameIdx(0), FrameIdx(1));
107    }
108}