mod decode;
mod encode;
mod key;
#[cfg(test)]
mod tests;
pub use encode::SerializableCheckpointRecord;
pub use key::SerializableCheckpointKey;
use super::{io::invalid, SerializableGraph, VersionResult};
use crate::read_control::StorageReadControl;
const MAGIC: &[u8; 8] = b"UQASREC1";
impl SerializableGraph {
pub fn checkpoint_records_changed(&self) -> bool {
self.checkpoint_changed || self.checkpoint_records.is_empty()
}
pub fn write_checkpoint_changes<'a>(
&'a self,
control: &StorageReadControl,
mut update: impl FnMut(
SerializableCheckpointKey,
Option<SerializableCheckpointRecord<'a>>,
) -> VersionResult<()>,
) -> VersionResult<()> {
control.check()?;
if !self.checkpoint_records_changed() {
return Ok(());
}
let current = [
self.transactions.len(),
self.outgoing.len(),
self.predicates.reads.len(),
self.predicates.writes.len(),
]
.into_iter()
.try_fold(1_usize, |total, count| {
total.checked_add(count).ok_or_else(invalid)
})?;
let additional = current
.saturating_sub(self.checkpoint_records.capacity())
.checked_mul(std::mem::size_of::<SerializableCheckpointKey>())
.ok_or_else(invalid)?;
let _future_keys = self.transactions.budget().reserve(additional)?;
let mut old = self.checkpoint_records.iter().copied().peekable();
let mut previous = None;
self.visit_checkpoint_records(|record| {
control.check()?;
let key = record.key(control)?;
if previous.is_some_and(|previous| previous >= key) {
return Err(invalid());
}
previous = Some(key);
while old.peek().is_some_and(|old| *old < key) {
update(old.next().expect("peeked checkpoint key"), None)?;
}
let present = old.peek() == Some(&key);
if present {
old.next();
}
if !present || key == SerializableCheckpointKey::HEADER {
update(key, Some(record))?;
}
Ok(())
})?;
for key in old {
control.check()?;
update(key, None)?;
}
Ok(())
}
fn visit_checkpoint_records<'a>(
&'a self,
mut visit: impl FnMut(SerializableCheckpointRecord<'a>) -> VersionResult<()>,
) -> VersionResult<()> {
visit(SerializableCheckpointRecord::header(self))?;
for &entry in &*self.transactions {
visit(SerializableCheckpointRecord::transaction(self, entry))?;
}
for &edge in &*self.outgoing {
visit(SerializableCheckpointRecord::edge(self, edge))?;
}
for (writing, entries) in [
(false, &self.predicates.reads),
(true, &self.predicates.writes),
] {
for entry in &**entries {
visit(SerializableCheckpointRecord::predicate(
self, entry, writing,
))?;
}
}
Ok(())
}
}