use super::*;
pub(super) struct SlotPacker {
committed_live_slots: FxHashMap<u64, u32>,
current_live_slots: FxHashMap<u64, u32>,
insert_cursor: Option<u64>,
}
impl SlotPacker {
pub(super) fn new() -> Self {
SlotPacker {
committed_live_slots: FxHashMap::default(),
current_live_slots: FxHashMap::default(),
insert_cursor: None,
}
}
pub(super) fn from_committed(committed: FxHashMap<u64, u32>) -> Self {
let current_live_slots = committed.clone();
SlotPacker {
committed_live_slots: committed,
current_live_slots,
insert_cursor: None,
}
}
pub(super) fn insert(
&mut self,
cache: &mut PageCache,
alloc: &mut dyn FnMut(&mut PageCache) -> Result<u64>,
packing_enabled: bool,
value: &[u8],
) -> Result<(u64, u16)> {
if let Some(cursor_page_id) = self.insert_cursor {
let slot_option = {
let buf = cache.get_mut(cursor_page_id)?;
let result = DataPage::insert(buf, value);
if result.is_some() {
page::stamp_checksum(buf);
}
result
};
if let Some(slot) = slot_option {
*self.current_live_slots.entry(cursor_page_id).or_insert(0) += 1;
return Ok((cursor_page_id, slot));
}
}
let page_id = alloc(cache)?;
let slot = {
let buf = cache.get_mut(page_id)?;
DataPage::init_page(buf);
let slot = DataPage::insert(buf, value).expect("value fits in empty page");
page::stamp_checksum(buf);
slot
};
if packing_enabled {
self.insert_cursor = Some(page_id);
}
*self.current_live_slots.entry(page_id).or_insert(0) += 1;
Ok((page_id, slot))
}
pub(super) fn release(&mut self, page_id: u64) -> Option<u64> {
let count = self.current_live_slots.get_mut(&page_id)?;
if *count > 0 {
*count -= 1;
}
if *count == 0 {
self.current_live_slots.remove(&page_id);
if self.insert_cursor == Some(page_id) {
self.insert_cursor = None;
}
return Some(page_id);
}
None
}
pub(super) fn begin(&mut self) {
self.current_live_slots = self.committed_live_slots.clone();
self.insert_cursor = None;
}
pub(super) fn commit(&mut self) {
self.committed_live_slots = self.current_live_slots.clone();
self.insert_cursor = None;
}
pub(super) fn rollback(&mut self) {
self.current_live_slots = self.committed_live_slots.clone();
self.insert_cursor = None;
}
pub(super) fn snapshot(&self) -> (FxHashMap<u64, u32>, Option<u64>) {
(self.current_live_slots.clone(), self.insert_cursor)
}
pub(super) fn restore(&mut self, snap: (FxHashMap<u64, u32>, Option<u64>)) {
self.current_live_slots = snap.0;
self.insert_cursor = snap.1;
}
pub(super) fn clear_cursor(&mut self) {
self.insert_cursor = None;
}
pub(super) fn current_live_slots(&self) -> &FxHashMap<u64, u32> {
&self.current_live_slots
}
#[cfg(test)]
pub(super) fn is_current_empty(&self) -> bool {
self.current_live_slots.is_empty()
}
#[cfg(test)]
pub(super) fn insert_cursor(&self) -> Option<u64> {
self.insert_cursor
}
}
impl TransactionManager {
pub(super) fn insert_into_data_page(&mut self, value: &[u8]) -> Result<(u64, u16)> {
let reuse = self.savepoints.is_empty();
let packing_enabled = self.savepoints.is_empty();
let mut tree = self.freemap.take_tree(&self.current_roots);
let result = {
let mut cache = self.cache.borrow_mut();
let mut alloc = |c: &mut PageCache| self.freemap.cow_alloc_into(c, &mut tree, reuse);
self.packer
.insert(&mut cache, &mut alloc, packing_enabled, value)
};
self.freemap.put_tree(&mut self.current_roots, tree);
result
}
pub(super) fn release_data_slot(&mut self, page_id: u64) {
if let Some(freed) = self.packer.release(page_id) {
self.txn_freed_pages.push(freed);
}
}
pub(super) fn ensure_handle_table(&mut self) -> Result<()> {
if self.current_roots.handle_table_page == PAGE_ID_NONE {
let root = {
let mut cache = self.cache.borrow_mut();
self.handle_table.create_root(&mut cache)?
};
self.current_roots.handle_table_page = root;
}
Ok(())
}
}