1use std::path::PathBuf;
4
5pub const DEFAULT_PAGE_SIZE: usize = 8192;
7
8pub type PageNum = u64;
10
11#[derive(Debug, Clone)]
13pub struct Frame {
14 pub page_num: PageNum,
16 pub pin_count: u32,
18 pub is_dirty: bool,
20 pub clock_ref: bool,
22 pub data: Vec<u8>,
24 pub numa_node: u32,
26}
27
28impl Frame {
29 pub fn new(page_num: PageNum, data: Vec<u8>) -> Self {
30 Self {
31 page_num,
32 pin_count: 0,
33 is_dirty: false,
34 clock_ref: true,
35 data,
36 numa_node: 0,
37 }
38 }
39
40 #[inline(always)]
42 pub fn pin(&mut self) {
43 self.pin_count += 1;
44 }
45
46 #[inline(always)]
48 pub fn unpin(&mut self) {
49 if self.pin_count > 0 {
50 self.pin_count -= 1;
51 }
52 }
53
54 #[inline(always)]
56 pub fn is_pinned(&self) -> bool {
57 self.pin_count > 0
58 }
59
60 #[inline(always)]
62 pub fn mark_dirty(&mut self) {
63 self.is_dirty = true;
64 }
65}
66
67#[derive(Debug, Clone)]
69pub struct FileHandle {
70 pub path: PathBuf,
72 pub num_pages: u64,
74 pub page_size: usize,
76 pub free_space_manager: Option<std::sync::Arc<crate::free_space_manager::FreeSpaceManager>>,
78}
79
80impl FileHandle {
81 pub fn new(path: PathBuf, page_size: usize) -> Self {
82 let num_pages = if path.exists() {
83 let len = std::fs::metadata(&path).map(|m| m.len()).unwrap_or(0);
84 len / page_size as u64
85 } else {
86 0
87 };
88 Self {
89 path,
90 num_pages,
91 page_size,
92 free_space_manager: None,
93 }
94 }
95
96 pub fn with_free_space_manager(mut self, fsm: std::sync::Arc<crate::free_space_manager::FreeSpaceManager>) -> Self {
97 self.free_space_manager = Some(fsm);
98 self
99 }
100
101 #[inline(always)]
103 pub fn page_offset(&self, page_num: PageNum) -> u64 {
104 page_num * self.page_size as u64
105 }
106
107 #[cfg(not(target_arch = "wasm32"))]
109 pub fn read_page(&self, page_num: PageNum) -> std::io::Result<Vec<u8>> {
110 use std::io::{Read, Seek, SeekFrom};
111 let mut file = std::fs::File::open(&self.path)?;
112 let offset = self.page_offset(page_num);
113 file.seek(SeekFrom::Start(offset))?;
114 let mut buf = vec![0u8; self.page_size];
115 file.read_exact(&mut buf)?;
116 Ok(buf)
117 }
118
119 #[cfg(target_arch = "wasm32")]
120 pub fn read_page(&self, page_num: PageNum) -> std::io::Result<Vec<u8>> {
121 Ok(vec![0u8; self.page_size])
123 }
124
125 #[cfg(not(target_arch = "wasm32"))]
127 pub fn write_page(&self, page_num: PageNum, data: &[u8]) -> std::io::Result<()> {
128 use std::io::{Seek, SeekFrom, Write};
129 let mut file = std::fs::OpenOptions::new()
130 .write(true)
131 .create(true)
132 .truncate(false)
133 .open(&self.path)?;
134 let offset = self.page_offset(page_num);
135 file.seek(SeekFrom::Start(offset))?;
136 file.write_all(data)?;
137 Ok(())
138 }
139
140 #[cfg(target_arch = "wasm32")]
141 pub fn write_page(&self, _page_num: PageNum, _data: &[u8]) -> std::io::Result<()> {
142 Ok(())
144 }
145
146 pub fn allocate_page(&mut self) -> PageNum {
148 if let Some(fsm) = &self.free_space_manager {
149 if let Some(range) = fsm.pop_free_pages(1) {
150 return range.start_page_idx;
151 }
152 }
153 let page_num = self.num_pages;
154 self.num_pages += 1;
155 page_num
156 }
157
158 pub fn free_page(&self, page_num: PageNum) {
160 if let Some(fsm) = &self.free_space_manager {
161 fsm.add_uncheckpointed_free_pages(crate::free_space_manager::PageRange::new(page_num, 1));
162 }
163 }
164
165 #[cfg(not(target_arch = "wasm32"))]
169 pub fn extend_file(&mut self, num_pages: u64) -> std::io::Result<PageNum> {
170 use std::io::Write;
171
172 let start_page = self.num_pages;
173 let extend_bytes = (num_pages as usize) * self.page_size;
174
175 let mut file = std::fs::OpenOptions::new().create(true).append(true).open(&self.path)?;
176
177 let zeros = vec![0u8; extend_bytes];
178 file.write_all(&zeros)?;
179 file.flush()?;
180
181 self.num_pages += num_pages;
182 Ok(start_page)
183 }
184
185 #[cfg(target_arch = "wasm32")]
186 pub fn extend_file(&mut self, num_pages: u64) -> std::io::Result<PageNum> {
187 let start_page = self.num_pages;
188 self.num_pages += num_pages;
189 Ok(start_page)
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn test_frame_pin_unpin() {
199 let mut frame = Frame::new(0, vec![0u8; DEFAULT_PAGE_SIZE]);
200 assert!(!frame.is_pinned());
201 assert_eq!(frame.pin_count, 0);
202
203 frame.pin();
204 assert!(frame.is_pinned());
205 assert_eq!(frame.pin_count, 1);
206
207 frame.unpin();
208 assert!(!frame.is_pinned());
209 }
210
211 #[test]
212 fn test_frame_dirty() {
213 let mut frame = Frame::new(1, vec![0u8; DEFAULT_PAGE_SIZE]);
214 assert!(!frame.is_dirty);
215 frame.mark_dirty();
216 assert!(frame.is_dirty);
217 }
218
219 #[test]
220 fn test_page_offset() {
221 let fh = FileHandle::new(PathBuf::from("test.db"), DEFAULT_PAGE_SIZE);
222 assert_eq!(fh.page_offset(0), 0);
223 assert_eq!(fh.page_offset(1), 8192);
224 assert_eq!(fh.page_offset(5), 40960);
225 }
226
227 #[test]
228 fn test_file_handle_fsm() {
229 let path = PathBuf::from("test_fsm.db");
230 let _ = std::fs::remove_file(&path); let fsm = std::sync::Arc::new(crate::free_space_manager::FreeSpaceManager::new());
233 let mut fh = FileHandle::new(path.clone(), DEFAULT_PAGE_SIZE).with_free_space_manager(fsm.clone());
234
235 let p1 = fh.allocate_page(); let p2 = fh.allocate_page(); let p3 = fh.allocate_page(); assert_eq!(p1, 0);
240 assert_eq!(p2, 1);
241 assert_eq!(p3, 2);
242 assert_eq!(fh.num_pages, 3);
243
244 fh.free_page(p2);
246
247 fsm.finalize_checkpoint();
249
250 let p4 = fh.allocate_page();
252 assert_eq!(p4, 1);
253 assert_eq!(fh.num_pages, 3); let p5 = fh.allocate_page();
257 assert_eq!(p5, 3);
258 assert_eq!(fh.num_pages, 4);
259
260 let _ = std::fs::remove_file(&path); }
262}