#[cfg(test)]
pub mod tests;
use std::collections::HashMap;
use std::mem::size_of;
use crate::consts::{Flags, PGID, TXID};
use crate::errors::Error;
use crate::page::{merge_pgids, Page};
use crate::utils::find_contiguous;
#[derive(Debug, Clone, Default)]
pub(crate) struct FreeList {
ids: Vec<PGID>,
pending: HashMap<TXID, Vec<PGID>>,
cache: HashMap<PGID, bool>,
}
impl FreeList {
pub fn size(&self) -> usize {
let mut n = self.count();
if n >= 0xFFFF {
n += 1;
};
Page::header_size() + (size_of::<PGID>() * n)
}
pub fn count(&self) -> usize {
self.free_count() + self.pending_count()
}
pub fn free_count(&self) -> usize {
self.ids.len()
}
pub fn pending_count(&self) -> usize {
self.pending.iter().fold(0, |c, x| c + x.1.len())
}
pub fn get_pgids(&self) -> Vec<PGID> {
let mut m = Vec::with_capacity(self.count());
for list in self.pending.values() {
m.extend_from_slice(list);
}
m.extend_from_slice(&self.ids);
m.sort_unstable();
m
}
pub fn allocate(&mut self, span: u64) -> PGID {
if self.ids.is_empty() {
return 0;
}
if let Some(index) = find_contiguous(&self.ids, span as usize) {
let pgid = self.ids[index];
if pgid <= 1 {
panic!("invalid page allocation: {}", pgid);
};
self.ids.drain(index..index + span as usize);
for i in 0..span {
self.cache.remove(&(pgid + i));
}
pgid
} else {
0
}
}
pub(crate) fn free(&mut self, txid: TXID, p: &Page) -> Result<(), Error> {
if p.id <= 1 {
return Err(format!("cannot free page 0 or 1: {}", p.id).into());
}
let ids = self.pending.entry(txid).or_default();
let max = p.id + u64::from(p.overflow);
for id in p.id..=max {
if self.cache.contains_key(&id) {
return Err(format!("page {id} already freed").into());
}
ids.push(id);
self.cache.insert(id, true);
}
Ok(())
}
pub fn release(&mut self, txid: TXID) {
let mut m = Vec::new();
let mut tx_id_to_remove = Vec::new();
for (tid, ids) in self.pending.iter() {
let tid = *tid;
if tid <= txid {
m.append(&mut ids.clone());
tx_id_to_remove.push(tid);
}
}
for tid in &tx_id_to_remove {
self.pending.remove(tid);
}
m.sort_unstable();
self.ids = merge_pgids(self.ids.as_slice(), m.as_slice());
}
pub fn rollback(&mut self, txid: TXID) {
if let Some(pending) = self.pending.get(&txid) {
for id in pending {
self.cache.remove(id).unwrap();
}
}
self.pending.remove(&txid);
}
pub fn freed(&self, pgid: PGID) -> bool {
self.cache.contains_key(&pgid)
}
pub(crate) fn read(&mut self, p: &Page) {
assert_eq!(
p.flags,
Flags::FREELIST,
"freelist flags incorrect: {}",
p.flags.bits()
);
let mut idx = 0;
let mut count = p.count as usize;
if count == 0xFFFF {
idx = 1;
let frl = p.freelist();
count = frl[0] as usize;
}
if count == 0 {
self.ids.clear();
} else {
let ids = p.freelist();
self.ids = ids[idx..count].to_vec();
self.ids.sort_unstable();
}
self.reindex();
}
pub fn write(&mut self, p: &mut Page) {
let lenids = self.count();
p.flags = Flags::FREELIST;
if lenids == 0 {
p.count = lenids as u16;
} else if lenids < 0xFFFF {
p.count = lenids as u16;
let m = p.freelist_mut();
m.copy_from_slice(&self.get_pgids())
} else {
p.count = 0xFFFF;
let m = p.freelist_mut();
m[0] = lenids as u64;
m.copy_from_slice(&self.get_pgids());
}
}
pub fn reload(&mut self, p: &Page) {
self.read(p);
let mut pcache = Vec::<PGID>::new();
for (_, pids) in self.pending.iter() {
for pid in pids.iter() {
pcache.push(*pid);
}
}
let mut a = Vec::<PGID>::new();
for id in self.ids.iter() {
if !pcache.contains(id) {
a.push(*id);
}
}
self.ids = a;
self.reindex();
}
pub fn reindex(&mut self) {
let mut new_cache: HashMap<PGID, bool> = HashMap::new();
for id in self.ids.iter() {
new_cache.insert(*id, true);
}
for (_key, ids) in self.pending.iter() {
for id in ids.iter() {
new_cache.insert(*id, true);
}
}
self.cache = new_cache;
}
}