use super::Version;
use super::edit::{AddedBlobFile, ChangedLevel, TableDesc, VersionEdit};
use crate::coding::Encode;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
impl Version {
pub(crate) fn diff(&self, prior: &Self) -> crate::Result<VersionEdit> {
let level_count = self.level_count().max(prior.level_count());
let mut changed_levels = Vec::new();
for idx in 0..level_count {
let cur = level_runs(self.level(idx));
let old = level_runs(prior.level(idx));
if cur != old {
let level = u8::try_from(idx).map_err(|_| crate::Error::Unrecoverable)?;
changed_levels.push(ChangedLevel { level, runs: cur });
}
}
let mut added_blob_files = Vec::new();
for bf in self.blob_files.iter() {
let cur = bf.checksum().into_u128();
let was = prior
.blob_files
.get(bf.id())
.map(|p| p.checksum().into_u128());
if was != Some(cur) {
added_blob_files.push(AddedBlobFile {
id: bf.id(),
checksum: cur,
});
}
}
let mut removed_blob_file_ids = Vec::new();
for bf in prior.blob_files.iter() {
if !self.blob_files.contains_key(bf.id()) {
removed_blob_file_ids.push(bf.id());
}
}
let gc_stats = if self.gc_stats() == prior.gc_stats() {
None
} else {
let mut buf = Vec::new();
self.gc_stats().encode_into(&mut buf)?;
Some(buf)
};
let mut restrictions = Vec::new();
for table in self.iter_tables() {
if let Some(bound) = table.restrict_lower_bound() {
restrictions.push((table.id(), bound.clone()));
}
}
Ok(VersionEdit {
new_version_id: self.id(),
changed_levels,
added_blob_files,
removed_blob_file_ids,
gc_stats,
restrictions,
})
}
}
fn level_runs(level: Option<&super::Level>) -> Vec<Vec<TableDesc>> {
level.map_or_else(Vec::new, |lvl| {
lvl.iter()
.map(|run| {
run.iter()
.map(|t| TableDesc {
id: t.id(),
checksum: t.checksum().into_u128(),
global_seqno: t.global_seqno(),
})
.collect()
})
.collect()
})
}
#[cfg(test)]
#[expect(clippy::expect_used, reason = "test code")]
mod tests;