use std::collections::{HashMap, HashSet}; use std::fs::{File, OpenOptions}; use std::io::{self, Read, Seek, SeekFrom, Write}; use std::path::Path; // 页管理:定长页的读写,外加一层页缓冲池。 // 页 /// 页号:从 0 开始 pub type PageId = u64; /// 缓冲池默认容量(页数) pub const DEFAULT_POOL_SIZE: usize = 64; #[derive(Debug, Clone)] /// 一页:定长字节 + 页号 pub struct Page { pub id: PageId, pub data: Vec, } impl Page { /// 新建一页,全 0 pub fn new(id: PageId, size: usize) -> Self { Page { id, data: vec![0u8; size], } } /// 清零 pub fn zero(&mut self) { self.data.fill(0); } } #[derive(Debug)] /// 页文件:定长页读写 + 页缓存 pub struct PageFile { file: File, page_size: usize, capacity: usize, cache: HashMap, last_used: HashMap, clock: u64, dirty: HashSet, hits: u64, misses: u64, } impl PageFile { /// 打开页文件(默认池容量) pub fn open>(path: P, page_size: usize) -> io::Result { Self::with_options(path, page_size, DEFAULT_POOL_SIZE) } /// 打开页文件并指定池容量 pub fn with_options>( path: P, page_size: usize, capacity: usize, ) -> io::Result { let file = OpenOptions::new() .read(true) .write(true) .create(true) .truncate(false) .open(path)?; Ok(PageFile { file, page_size, capacity, cache: HashMap::new(), last_used: HashMap::new(), clock: 0, dirty: HashSet::new(), hits: 0, misses: 0, }) } /// 页大小 pub fn page_size(&self) -> usize { self.page_size } // 缓冲池 /// 池容量(页数) pub fn capacity(&self) -> usize { self.capacity } /// 当前缓存了多少页 pub fn cached_pages(&self) -> usize { self.cache.len() } /// 命中次数 pub fn hits(&self) -> u64 { self.hits } /// 未命中次数 pub fn misses(&self) -> u64 { self.misses } /// 命中率 pub fn hit_rate(&self) -> f64 { let total = self.hits + self.misses; if total == 0 { 0.0 } else { self.hits as f64 / total as f64 } } /// 标记为最近使用 fn touch(&mut self, id: PageId) { self.clock += 1; self.last_used.insert(id, self.clock); } /// 淘汰一个最冷的页 fn evict_one(&mut self) -> io::Result { let victim = self .cache .keys() .min_by_key(|id| self.last_used.get(id).copied().unwrap_or(0)) .copied(); let Some(victim) = victim else { return Ok(false); }; if self.dirty.contains(&victim) { let p = self.cache[&victim].clone(); self.write_raw(&p)?; self.dirty.remove(&victim); } self.cache.remove(&victim); self.last_used.remove(&victim); Ok(true) } /// 直接写文件 fn write_raw(&mut self, page: &Page) -> io::Result<()> { if page.data.len() != self.page_size { return Err(io::Error::new( io::ErrorKind::InvalidInput, format!( "page has {} bytes, this file uses {}", page.data.len(), self.page_size ), )); } self.file .seek(SeekFrom::Start(page.id * self.page_size as u64))?; self.file.write_all(&page.data) } /// 直接从文件读 fn read_raw(&mut self, id: PageId) -> io::Result { let mut data = vec![0u8; self.page_size]; self.file.seek(SeekFrom::Start(id * self.page_size as u64))?; self.file.read_exact(&mut data)?; Ok(Page { id, data }) } /// 读一页(走缓存) pub fn read_page(&mut self, id: PageId) -> io::Result { self.note_access(id)?; Ok(self.cache[&id].clone()) } /// 只读地用一页(走缓存,不整页拷贝) pub fn with_page(&mut self, id: PageId, f: impl FnOnce(&Page) -> R) -> io::Result { self.note_access(id)?; Ok(f(&self.cache[&id])) } /// 就地改一页(走缓存,不整页拷贝) pub fn update_page(&mut self, id: PageId, f: impl FnOnce(&mut Page) -> R) -> io::Result { self.note_access(id)?; let r = f(self.cache.get_mut(&id).expect("cached above")); self.dirty.insert(id); Ok(r) } /// 记一次访问:命中加 hits,未命中读进来并记 misses fn note_access(&mut self, id: PageId) -> io::Result<()> { if self.cache.contains_key(&id) { self.hits += 1; } else { self.misses += 1; let p = self.read_raw(id)?; if self.cache.len() >= self.capacity { self.evict_one()?; } self.cache.insert(id, p); } self.touch(id); Ok(()) } /// 写一页(走缓存) pub fn write_page(&mut self, page: &Page) -> io::Result<()> { if page.data.len() != self.page_size { return Err(io::Error::new( io::ErrorKind::InvalidInput, format!( "page has {} bytes, this file uses {}", page.data.len(), self.page_size ), )); } if self.cache.len() >= self.capacity && !self.cache.contains_key(&page.id) { self.evict_one()?; } self.cache.insert(page.id, page.clone()); self.dirty.insert(page.id); self.touch(page.id); Ok(()) } /// 末尾追加一页 pub fn append_page(&mut self) -> io::Result { let id = self.num_pages()?; let page = Page::new(id, self.page_size); self.write_page(&page)?; Ok(page) } /// 文件里有多少页 pub fn num_pages(&mut self) -> io::Result { let len = self.file.metadata()?.len(); let size = self.page_size as u64; if len % size != 0 { return Err(io::Error::new( io::ErrorKind::InvalidData, format!( "file length {} is not a multiple of page size {} (did page.size change?)", len, size ), )); } let on_disk = len / size; let cached_max = self .cache .keys() .copied() .max() .map(|m| m + 1) .unwrap_or(0); Ok(on_disk.max(cached_max)) } /// 把脏页写回磁盘 pub fn flush(&mut self) -> io::Result<()> { let dirty: Vec = self.dirty.iter().copied().collect(); for id in dirty { if let Some(p) = self.cache.get(&id) { let p = p.clone(); self.write_raw(&p)?; } } self.file.sync_all()?; self.dirty.clear(); Ok(()) } /// flush 之后 fsync pub fn sync(&mut self) -> io::Result<()> { self.flush() } /// 清空文件 pub fn truncate(&mut self) -> io::Result<()> { self.cache.clear(); self.last_used.clear(); self.dirty.clear(); self.file.set_len(0)?; self.file.seek(SeekFrom::Start(0))?; Ok(()) } } /// 退出前把脏页写回 impl Drop for PageFile { fn drop(&mut self) { let _ = self.flush(); } }