use std::{cell::RefCell, rc::Rc};
use log::trace;
use super::storage::start_storage::StartStorage;
pub struct WriteOp {
offset: usize,
new_data: Vec<u8>,
old_data: Vec<u8>,
}
pub struct RecoveryUnit {
storage: Rc<RefCell<StartStorage>>,
pending_ops: Vec<WriteOp>,
committed: bool
}
impl RecoveryUnit {
pub fn new(storage: Rc<RefCell<StartStorage>>) -> Self {
Self {
storage,
pending_ops: vec![],
committed: false,
}
}
pub fn write(&mut self, offset: usize, data: &[u8]) {
let old = self.storage.borrow()[offset..offset+data.len()].to_vec();
self.pending_ops.push(WriteOp {
offset,
new_data: data.to_vec(),
old_data: old,
});
}
pub fn commit(&mut self) {
let mut ss = self.storage.borrow_mut();
for op in self.pending_ops.iter() {
trace!("Commiting op");
trace!("{}: '{:?}' to '{:?}'", op.offset, op.old_data, op.new_data);
ss[op.offset..op.offset+op.new_data.len()].copy_from_slice(&op.new_data);
}
self.committed = true;
}
pub fn rollback(&mut self) {
self.pending_ops.clear();
}
pub fn effective_view(&self, offset: usize, len: usize) -> Vec<u8> {
let mut result = self.storage.borrow()[offset..offset + len].to_vec();
for op in &self.pending_ops {
let op_start = op.offset;
let op_end = op.offset + op.new_data.len();
let view_start = offset;
let view_end = offset + len;
if op_end > view_start && op_start < view_end {
let overlap_start = op_start.max(view_start);
let overlap_end = op_end.min(view_end);
let result_start = overlap_start - view_start;
let op_data_start = overlap_start - op_start;
let count = overlap_end - overlap_start;
result[result_start..result_start + count]
.copy_from_slice(&op.new_data[op_data_start..op_data_start + count]);
}
}
result
}
pub fn is_committed(&self) -> bool {
self.committed
}
}
impl Drop for RecoveryUnit {
fn drop(&mut self) {
if !self.committed {
self.rollback();
}
}
}
#[test]
fn test_atomic_commit_and_rollback() {
use std::rc::Rc;
use std::cell::RefCell;
let storage = Rc::new(RefCell::new(StartStorage::in_memory()));
storage.borrow_mut().resize(16).unwrap();
{
let mut s = storage.borrow_mut();
s[0..4].copy_from_slice(&[1, 2, 3, 4]);
s[4..8].copy_from_slice(&[5, 6, 7, 8]);
}
{
let mut ru = RecoveryUnit::new(storage.clone());
ru.write(0, &[10, 11, 12, 13]);
ru.write(4, &[20, 21, 22, 23]);
ru.commit();
assert!(ru.is_committed());
}
{
let s = storage.borrow();
assert_eq!(&s[0..4], &[10, 11, 12, 13]);
assert_eq!(&s[4..8], &[20, 21, 22, 23]);
}
{
let mut ru = RecoveryUnit::new(storage.clone());
ru.write(0, &[100, 101, 102, 103]);
ru.write(4, &[200, 201, 202, 203]);
assert!(!ru.is_committed()); }
{
let s = storage.borrow();
assert_eq!(&s[0..4], &[10, 11, 12, 13]);
assert_eq!(&s[4..8], &[20, 21, 22, 23]);
}
}