Apply rustfmt to entire workspace
Runs cargo fmt --all; all 573 tests still passing, clippy still clean. No logic changes — formatting only. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
3d524e2d63
commit
1c107fe58a
@@ -9,7 +9,7 @@
|
||||
//! Run:
|
||||
//! cargo bench -p clawsync-onion -- iblt_bench
|
||||
|
||||
use clawsync_onion::iblt::{IbltSketch, DEFAULT_HASH_COUNT, MIN_CELLS};
|
||||
use clawsync_onion::iblt::{DEFAULT_HASH_COUNT, IbltSketch, MIN_CELLS};
|
||||
use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -28,8 +28,12 @@ fn diff_sketches(n: usize, d: usize) -> (IbltSketch, Vec<u8>) {
|
||||
let m = ((d * 4).max(MIN_CELLS)).max(IbltSketch::recommended_cells(n));
|
||||
let mut a = IbltSketch::new(m, DEFAULT_HASH_COUNT, SEED);
|
||||
let mut b = IbltSketch::new(m, DEFAULT_HASH_COUNT, SEED);
|
||||
for &k in &a_keys { a.insert(k); }
|
||||
for &k in &b_keys { b.insert(k); }
|
||||
for &k in &a_keys {
|
||||
a.insert(k);
|
||||
}
|
||||
for &k in &b_keys {
|
||||
b.insert(k);
|
||||
}
|
||||
let b_bytes = b.to_bytes();
|
||||
a.subtract(&b);
|
||||
(a, b_bytes)
|
||||
@@ -79,8 +83,12 @@ fn bench_iblt(c: &mut Criterion) {
|
||||
let m = (d * 4).max(MIN_CELLS).max(IbltSketch::recommended_cells(n));
|
||||
let mut a = IbltSketch::new(m, DEFAULT_HASH_COUNT, SEED);
|
||||
let mut b_sk = IbltSketch::new(m, DEFAULT_HASH_COUNT, SEED);
|
||||
for &k in black_box(ak) { a.insert(k); }
|
||||
for &k in black_box(bk) { b_sk.insert(k); }
|
||||
for &k in black_box(ak) {
|
||||
a.insert(k);
|
||||
}
|
||||
for &k in black_box(bk) {
|
||||
b_sk.insert(k);
|
||||
}
|
||||
a.subtract(&b_sk);
|
||||
black_box(a.decode())
|
||||
});
|
||||
@@ -92,14 +100,20 @@ fn bench_iblt(c: &mut Criterion) {
|
||||
|
||||
// ── Wire size table (not timed) ───────────────────────────────────────────
|
||||
println!("\n=== IBLT sketch size vs flat manifest (N × 60 B) ===");
|
||||
println!("{:>8} {:>10} {:>12} {:>10}", "N", "IBLT (B)", "Flat (B)", "ratio");
|
||||
println!(
|
||||
"{:>8} {:>10} {:>12} {:>10}",
|
||||
"N", "IBLT (B)", "Flat (B)", "ratio"
|
||||
);
|
||||
for &n in &[10usize, 100, 1_000, 10_000, 100_000] {
|
||||
let ks = keys(n);
|
||||
let sz = IbltSketch::from_keys(&ks, SEED).to_bytes().len();
|
||||
let flat = n * 60;
|
||||
println!(
|
||||
"{:>8} {:>10} {:>12} {:>9.1}×",
|
||||
n, sz, flat, flat as f64 / sz as f64
|
||||
n,
|
||||
sz,
|
||||
flat,
|
||||
flat as f64 / sz as f64
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,16 +49,21 @@ pub fn diff_revisions(
|
||||
|
||||
let pages: Vec<OnionPage> = raw_pages
|
||||
.into_iter()
|
||||
.map(|(h5_offset, data, codec, orig_size)| OnionPage { h5_offset, data, codec, orig_size })
|
||||
.map(|(h5_offset, data, codec, orig_size)| OnionPage {
|
||||
h5_offset,
|
||||
data,
|
||||
codec,
|
||||
orig_size,
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Parse the blake3 hex back to bytes
|
||||
let blake3_root = hex_to_bytes32(&summary.blake3_hex)
|
||||
.ok_or_else(|| {
|
||||
SyncOnionError::Onion(clawhdf5_onion::OnionError::Malformed(
|
||||
format!("invalid blake3_hex for rev {}", summary.revision),
|
||||
))
|
||||
})?;
|
||||
let blake3_root = hex_to_bytes32(&summary.blake3_hex).ok_or_else(|| {
|
||||
SyncOnionError::Onion(clawhdf5_onion::OnionError::Malformed(format!(
|
||||
"invalid blake3_hex for rev {}",
|
||||
summary.revision
|
||||
)))
|
||||
})?;
|
||||
|
||||
packets.push(OnionLayerPacket {
|
||||
revision: summary.revision,
|
||||
@@ -103,13 +108,18 @@ pub fn packets_for_revisions(
|
||||
let raw_pages = local.revision_pages_raw(rev)?;
|
||||
let pages: Vec<OnionPage> = raw_pages
|
||||
.into_iter()
|
||||
.map(|(h5_offset, data, codec, orig_size)| OnionPage { h5_offset, data, codec, orig_size })
|
||||
.map(|(h5_offset, data, codec, orig_size)| OnionPage {
|
||||
h5_offset,
|
||||
data,
|
||||
codec,
|
||||
orig_size,
|
||||
})
|
||||
.collect();
|
||||
|
||||
let blake3_root = hex_to_bytes32(&summary.blake3_hex).ok_or_else(|| {
|
||||
SyncOnionError::Onion(clawhdf5_onion::OnionError::Malformed(
|
||||
format!("invalid blake3_hex for rev {rev}"),
|
||||
))
|
||||
SyncOnionError::Onion(clawhdf5_onion::OnionError::Malformed(format!(
|
||||
"invalid blake3_hex for rev {rev}"
|
||||
)))
|
||||
})?;
|
||||
|
||||
packets.push(OnionLayerPacket {
|
||||
@@ -198,7 +208,12 @@ pub fn diff_revisions_merkle(
|
||||
let raw_pages = local.revision_pages_raw(rev)?;
|
||||
let pages: Vec<OnionPage> = raw_pages
|
||||
.into_iter()
|
||||
.map(|(h5_offset, data, codec, orig_size)| OnionPage { h5_offset, data, codec, orig_size })
|
||||
.map(|(h5_offset, data, codec, orig_size)| OnionPage {
|
||||
h5_offset,
|
||||
data,
|
||||
codec,
|
||||
orig_size,
|
||||
})
|
||||
.collect();
|
||||
|
||||
let blake3_root = hex_to_bytes32(&summary.blake3_hex).ok_or_else(|| {
|
||||
@@ -413,11 +428,11 @@ mod tests {
|
||||
};
|
||||
|
||||
let merkle_packets = diff_revisions_merkle(&local, &remote_tree.serialise()).unwrap();
|
||||
let flat_packets = diff_revisions(&local, 1 /* remote head = rev 1 */).unwrap();
|
||||
let flat_packets = diff_revisions(&local, 1 /* remote head = rev 1 */).unwrap();
|
||||
|
||||
// Revision numbers should match
|
||||
let merkle_revs: Vec<u64> = merkle_packets.iter().map(|p| p.revision).collect();
|
||||
let flat_revs: Vec<u64> = flat_packets.iter().map(|p| p.revision).collect();
|
||||
let flat_revs: Vec<u64> = flat_packets.iter().map(|p| p.revision).collect();
|
||||
assert_eq!(merkle_revs, flat_revs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,9 @@ impl IbltSketch {
|
||||
pub fn from_keys(keys: &[u64], seed: u64) -> Self {
|
||||
let m = Self::recommended_cells(keys.len());
|
||||
let mut sketch = Self::new(m, DEFAULT_HASH_COUNT, seed);
|
||||
for &k in keys { sketch.insert(k); }
|
||||
for &k in keys {
|
||||
sketch.insert(k);
|
||||
}
|
||||
sketch
|
||||
}
|
||||
|
||||
@@ -182,12 +184,13 @@ impl IbltSketch {
|
||||
/// Panics if `self` and `other` have different cell counts.
|
||||
pub fn subtract(&mut self, other: &IbltSketch) {
|
||||
assert_eq!(
|
||||
self.cells.len(), other.cells.len(),
|
||||
self.cells.len(),
|
||||
other.cells.len(),
|
||||
"cannot subtract sketches of different sizes"
|
||||
);
|
||||
for (a, b) in self.cells.iter_mut().zip(&other.cells) {
|
||||
a.count -= b.count;
|
||||
a.id_sum ^= b.id_sum;
|
||||
a.count -= b.count;
|
||||
a.id_sum ^= b.id_sum;
|
||||
a.hash_sum ^= b.hash_sum;
|
||||
}
|
||||
}
|
||||
@@ -213,9 +216,14 @@ impl IbltSketch {
|
||||
|
||||
let Some(pos) = pure_pos else {
|
||||
// No pure cell found.
|
||||
let done = cells.iter().all(|c| c.count == 0 && c.id_sum == 0 && c.hash_sum == 0);
|
||||
let done = cells
|
||||
.iter()
|
||||
.all(|c| c.count == 0 && c.id_sum == 0 && c.hash_sum == 0);
|
||||
return if done {
|
||||
let mut diff = IbltDiff { only_in_a, only_in_b };
|
||||
let mut diff = IbltDiff {
|
||||
only_in_a,
|
||||
only_in_b,
|
||||
};
|
||||
diff.only_in_a.sort_unstable();
|
||||
diff.only_in_b.sort_unstable();
|
||||
IbltDecodeResult::Complete(diff)
|
||||
@@ -227,15 +235,19 @@ impl IbltSketch {
|
||||
let key = cells[pos].id_sum;
|
||||
let positive = cells[pos].count == 1;
|
||||
|
||||
if positive { only_in_a.push(key); } else { only_in_b.push(key); }
|
||||
if positive {
|
||||
only_in_a.push(key);
|
||||
} else {
|
||||
only_in_b.push(key);
|
||||
}
|
||||
|
||||
// Peel this key from all its mapped cells.
|
||||
let sign: i32 = if positive { 1 } else { -1 };
|
||||
let h_key = xxh3_key(key);
|
||||
for h in 0..k {
|
||||
let idx = cell_index(key, h, seed, m);
|
||||
cells[idx].count -= sign;
|
||||
cells[idx].id_sum ^= key;
|
||||
cells[idx].count -= sign;
|
||||
cells[idx].id_sum ^= key;
|
||||
cells[idx].hash_sum ^= h_key;
|
||||
}
|
||||
}
|
||||
@@ -287,13 +299,21 @@ impl IbltSketch {
|
||||
let mut cells = Vec::with_capacity(m);
|
||||
let mut off = 21usize;
|
||||
for _ in 0..m {
|
||||
let count = i32::from_le_bytes(data[off..off + 4].try_into().unwrap());
|
||||
let id_sum = u64::from_le_bytes(data[off + 4..off + 12].try_into().unwrap());
|
||||
let count = i32::from_le_bytes(data[off..off + 4].try_into().unwrap());
|
||||
let id_sum = u64::from_le_bytes(data[off + 4..off + 12].try_into().unwrap());
|
||||
let hash_sum = u64::from_le_bytes(data[off + 12..off + 20].try_into().unwrap());
|
||||
cells.push(IbltCell { count, id_sum, hash_sum });
|
||||
cells.push(IbltCell {
|
||||
count,
|
||||
id_sum,
|
||||
hash_sum,
|
||||
});
|
||||
off += 20;
|
||||
}
|
||||
Ok(Self { cells, hash_count: k, seed })
|
||||
Ok(Self {
|
||||
cells,
|
||||
hash_count: k,
|
||||
seed,
|
||||
})
|
||||
}
|
||||
|
||||
// ── Private helpers ───────────────────────────────────────────────────────
|
||||
@@ -303,8 +323,8 @@ impl IbltSketch {
|
||||
let m = self.cells.len();
|
||||
for h in 0..self.hash_count {
|
||||
let idx = cell_index(key, h, self.seed, m);
|
||||
self.cells[idx].count += delta;
|
||||
self.cells[idx].id_sum ^= key;
|
||||
self.cells[idx].count += delta;
|
||||
self.cells[idx].id_sum ^= key;
|
||||
self.cells[idx].hash_sum ^= h_key;
|
||||
}
|
||||
}
|
||||
@@ -349,12 +369,16 @@ pub fn reconcile(
|
||||
seed: u64,
|
||||
) -> Result<(IbltDiff, IbltSketch), IbltError> {
|
||||
let b_sketch = IbltSketch::from_bytes(b_sketch_bytes)?;
|
||||
let m_start = b_sketch.cell_count().max(IbltSketch::recommended_cells(a_keys.len()));
|
||||
let m_start = b_sketch
|
||||
.cell_count()
|
||||
.max(IbltSketch::recommended_cells(a_keys.len()));
|
||||
|
||||
let mut m = m_start;
|
||||
loop {
|
||||
let mut a = IbltSketch::new(m, DEFAULT_HASH_COUNT, seed);
|
||||
for &k in a_keys { a.insert(k); }
|
||||
for &k in a_keys {
|
||||
a.insert(k);
|
||||
}
|
||||
|
||||
let mut diff_sketch = a.clone();
|
||||
let b_padded = pad_or_trim_sketch(&b_sketch, m, seed);
|
||||
@@ -372,7 +396,11 @@ pub fn reconcile(
|
||||
fn pad_or_trim_sketch(src: &IbltSketch, target_m: usize, seed: u64) -> IbltSketch {
|
||||
let mut cells = src.cells.clone();
|
||||
cells.resize(target_m, IbltCell::default());
|
||||
IbltSketch { cells, hash_count: src.hash_count, seed }
|
||||
IbltSketch {
|
||||
cells,
|
||||
hash_count: src.hash_count,
|
||||
seed,
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -397,7 +425,11 @@ mod tests {
|
||||
let mut s = IbltSketch::new(64, 3, SEED);
|
||||
s.insert(42);
|
||||
s.remove(42);
|
||||
assert!(s.cells.iter().all(|c| c.count == 0 && c.id_sum == 0 && c.hash_sum == 0));
|
||||
assert!(
|
||||
s.cells
|
||||
.iter()
|
||||
.all(|c| c.count == 0 && c.id_sum == 0 && c.hash_sum == 0)
|
||||
);
|
||||
}
|
||||
|
||||
// ── decode: empty diff ────────────────────────────────────────────────────
|
||||
@@ -409,9 +441,13 @@ mod tests {
|
||||
let b = sketch_of(&keys);
|
||||
a.subtract(&b);
|
||||
let result = a.decode();
|
||||
assert_eq!(result, IbltDecodeResult::Complete(IbltDiff {
|
||||
only_in_a: vec![], only_in_b: vec![],
|
||||
}));
|
||||
assert_eq!(
|
||||
result,
|
||||
IbltDecodeResult::Complete(IbltDiff {
|
||||
only_in_a: vec![],
|
||||
only_in_b: vec![],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// ── decode: one-sided diff ────────────────────────────────────────────────
|
||||
@@ -420,14 +456,22 @@ mod tests {
|
||||
fn decode_one_sided_diff_a_has_extra() {
|
||||
let base: Vec<u64> = (0..20).collect();
|
||||
let extra = 9999u64;
|
||||
let a_keys: Vec<u64> = base.iter().chain(std::iter::once(&extra)).copied().collect();
|
||||
let a_keys: Vec<u64> = base
|
||||
.iter()
|
||||
.chain(std::iter::once(&extra))
|
||||
.copied()
|
||||
.collect();
|
||||
let b_keys = base.clone();
|
||||
|
||||
let m = IbltSketch::recommended_cells(a_keys.len()) * 4; // generous m
|
||||
let mut a = IbltSketch::new(m, DEFAULT_HASH_COUNT, SEED);
|
||||
let mut b = IbltSketch::new(m, DEFAULT_HASH_COUNT, SEED);
|
||||
for &k in &a_keys { a.insert(k); }
|
||||
for &k in &b_keys { b.insert(k); }
|
||||
for &k in &a_keys {
|
||||
a.insert(k);
|
||||
}
|
||||
for &k in &b_keys {
|
||||
b.insert(k);
|
||||
}
|
||||
a.subtract(&b);
|
||||
|
||||
match a.decode() {
|
||||
@@ -444,13 +488,21 @@ mod tests {
|
||||
let base: Vec<u64> = (0..20).collect();
|
||||
let extra = 8888u64;
|
||||
let a_keys = base.clone();
|
||||
let b_keys: Vec<u64> = base.iter().chain(std::iter::once(&extra)).copied().collect();
|
||||
let b_keys: Vec<u64> = base
|
||||
.iter()
|
||||
.chain(std::iter::once(&extra))
|
||||
.copied()
|
||||
.collect();
|
||||
|
||||
let m = IbltSketch::recommended_cells(b_keys.len()) * 4;
|
||||
let mut a = IbltSketch::new(m, DEFAULT_HASH_COUNT, SEED);
|
||||
let mut b = IbltSketch::new(m, DEFAULT_HASH_COUNT, SEED);
|
||||
for &k in &a_keys { a.insert(k); }
|
||||
for &k in &b_keys { b.insert(k); }
|
||||
for &k in &a_keys {
|
||||
a.insert(k);
|
||||
}
|
||||
for &k in &b_keys {
|
||||
b.insert(k);
|
||||
}
|
||||
a.subtract(&b);
|
||||
|
||||
match a.decode() {
|
||||
@@ -476,8 +528,12 @@ mod tests {
|
||||
let m = IbltSketch::recommended_cells(a_keys.len().max(b_keys.len())) * 8;
|
||||
let mut a = IbltSketch::new(m, DEFAULT_HASH_COUNT, SEED);
|
||||
let mut b = IbltSketch::new(m, DEFAULT_HASH_COUNT, SEED);
|
||||
for &k in &a_keys { a.insert(k); }
|
||||
for &k in &b_keys { b.insert(k); }
|
||||
for &k in &a_keys {
|
||||
a.insert(k);
|
||||
}
|
||||
for &k in &b_keys {
|
||||
b.insert(k);
|
||||
}
|
||||
a.subtract(&b);
|
||||
|
||||
match a.decode() {
|
||||
@@ -500,8 +556,12 @@ mod tests {
|
||||
let m = 1; // deliberately too small
|
||||
let mut a = IbltSketch::new(m, DEFAULT_HASH_COUNT, SEED);
|
||||
let mut b = IbltSketch::new(m, DEFAULT_HASH_COUNT, SEED);
|
||||
for &k in &a_keys { a.insert(k); }
|
||||
for &k in &b_keys { b.insert(k); }
|
||||
for &k in &a_keys {
|
||||
a.insert(k);
|
||||
}
|
||||
for &k in &b_keys {
|
||||
b.insert(k);
|
||||
}
|
||||
a.subtract(&b);
|
||||
|
||||
// Must not panic — returns NeedMoreCells
|
||||
|
||||
@@ -19,9 +19,11 @@ pub mod merger;
|
||||
pub mod packet;
|
||||
pub mod selector;
|
||||
|
||||
pub use error::SyncOnionError;
|
||||
pub use iblt::{IbltDecodeResult, IbltDiff, IbltSketch, DEFAULT_HASH_COUNT, IBLT_SYNC_SEED, MIN_CELLS};
|
||||
pub use manifest::{ClawSyncManifest, IbltManifest};
|
||||
pub use differ::packets_for_revisions;
|
||||
pub use error::SyncOnionError;
|
||||
pub use iblt::{
|
||||
DEFAULT_HASH_COUNT, IBLT_SYNC_SEED, IbltDecodeResult, IbltDiff, IbltSketch, MIN_CELLS,
|
||||
};
|
||||
pub use manifest::{ClawSyncManifest, IbltManifest};
|
||||
pub use merger::MergeStats;
|
||||
pub use packet::{OnionLayerPacket, OnionPage};
|
||||
|
||||
@@ -10,7 +10,7 @@ use rkyv::{Archive, Deserialize, Serialize};
|
||||
|
||||
use clawhdf5_onion::writer::OnionFile;
|
||||
|
||||
use crate::iblt::{IbltDiff, IbltDecodeResult, IbltSketch, DEFAULT_HASH_COUNT};
|
||||
use crate::iblt::{DEFAULT_HASH_COUNT, IbltDecodeResult, IbltDiff, IbltSketch};
|
||||
|
||||
/// A compact summary of one revision — used in the manifest.
|
||||
#[derive(Archive, Deserialize, Serialize, Debug, Clone, PartialEq)]
|
||||
@@ -60,10 +60,7 @@ impl ClawSyncManifest {
|
||||
})
|
||||
.unwrap_or((0, [0u8; 32]));
|
||||
|
||||
let last_write = summaries
|
||||
.last()
|
||||
.map(|s| s.timestamp)
|
||||
.unwrap_or(0.0);
|
||||
let last_write = summaries.last().map(|s| s.timestamp).unwrap_or(0.0);
|
||||
|
||||
let revisions = summaries
|
||||
.into_iter()
|
||||
@@ -122,16 +119,16 @@ impl ClawSyncManifest {
|
||||
/// For N=1 000: ~3 KB vs ~60 KB for `ClawSyncManifest`.
|
||||
#[derive(Archive, Deserialize, Serialize, Debug, Clone, PartialEq)]
|
||||
pub struct IbltManifest {
|
||||
pub agent_id: String,
|
||||
pub file_blake3: [u8; 32],
|
||||
pub agent_id: String,
|
||||
pub file_blake3: [u8; 32],
|
||||
pub revision_count: u64,
|
||||
pub head_revision: u64,
|
||||
pub head_blake3: [u8; 32],
|
||||
pub last_write: f64,
|
||||
pub head_revision: u64,
|
||||
pub head_blake3: [u8; 32],
|
||||
pub last_write: f64,
|
||||
/// Serialised [`IbltSketch`] bytes.
|
||||
pub sketch: Vec<u8>,
|
||||
pub sketch: Vec<u8>,
|
||||
/// Number of cells in the sketch (m).
|
||||
pub sketch_cells: u32,
|
||||
pub sketch_cells: u32,
|
||||
}
|
||||
|
||||
impl IbltManifest {
|
||||
@@ -144,7 +141,12 @@ impl IbltManifest {
|
||||
|
||||
let (head_revision, head_blake3) = summaries
|
||||
.last()
|
||||
.map(|s| (s.revision, hex_to_bytes32(&s.blake3_hex).unwrap_or([0u8; 32])))
|
||||
.map(|s| {
|
||||
(
|
||||
s.revision,
|
||||
hex_to_bytes32(&s.blake3_hex).unwrap_or([0u8; 32]),
|
||||
)
|
||||
})
|
||||
.unwrap_or((0, [0u8; 32]));
|
||||
|
||||
let last_write = summaries.last().map(|s| s.timestamp).unwrap_or(0.0);
|
||||
@@ -174,10 +176,7 @@ impl IbltManifest {
|
||||
///
|
||||
/// Returns `Ok(IbltDiff)` or `Err` if the sketch cannot be decoded (too
|
||||
/// small or malformed).
|
||||
pub fn diff_against(
|
||||
&self,
|
||||
local_keys: &[u64],
|
||||
) -> Result<IbltDiff, &'static str> {
|
||||
pub fn diff_against(&self, local_keys: &[u64]) -> Result<IbltDiff, &'static str> {
|
||||
let remote_sketch = IbltSketch::from_bytes(&self.sketch)
|
||||
.map_err(|_| "cannot deserialise remote IBLT sketch")?;
|
||||
|
||||
@@ -189,7 +188,9 @@ impl IbltManifest {
|
||||
.max(IbltSketch::recommended_cells(local_keys.len()));
|
||||
|
||||
let mut a = IbltSketch::new(m, DEFAULT_HASH_COUNT, seed);
|
||||
for &k in local_keys { a.insert(k); }
|
||||
for &k in local_keys {
|
||||
a.insert(k);
|
||||
}
|
||||
|
||||
// Pad remote to same m if needed.
|
||||
let b = if remote_sketch.cell_count() == m {
|
||||
@@ -202,7 +203,7 @@ impl IbltManifest {
|
||||
a.subtract(&b);
|
||||
match a.decode() {
|
||||
IbltDecodeResult::Complete(diff) => Ok(diff),
|
||||
IbltDecodeResult::NeedMoreCells => Err("IBLT sketch too small; retry with larger m"),
|
||||
IbltDecodeResult::NeedMoreCells => Err("IBLT sketch too small; retry with larger m"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,8 +218,7 @@ impl IbltManifest {
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, String> {
|
||||
let mut aligned = rkyv::util::AlignedVec::<16>::with_capacity(bytes.len());
|
||||
aligned.extend_from_slice(bytes);
|
||||
rkyv::from_bytes::<IbltManifest, rkyv::rancor::Error>(&aligned)
|
||||
.map_err(|e| e.to_string())
|
||||
rkyv::from_bytes::<IbltManifest, rkyv::rancor::Error>(&aligned).map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,8 +63,7 @@ pub fn merge_packets(
|
||||
session.record_page(page.h5_offset, &page.data);
|
||||
total_bytes += page.data.len() as u64;
|
||||
} else {
|
||||
let codec = Codec::from_u8(page.codec)
|
||||
.map_err(SyncOnionError::Onion)?;
|
||||
let codec = Codec::from_u8(page.codec).map_err(SyncOnionError::Onion)?;
|
||||
let raw = decompress_page(&page.data, codec, page.orig_size)
|
||||
.map_err(SyncOnionError::Onion)?;
|
||||
total_bytes += raw.len() as u64;
|
||||
@@ -101,8 +100,7 @@ fn verify_packet_hash(packet: &OnionLayerPacket) -> Result<(), SyncOnionError> {
|
||||
if page.codec == 0 {
|
||||
hasher.update(&page.data);
|
||||
} else {
|
||||
let codec = Codec::from_u8(page.codec)
|
||||
.map_err(SyncOnionError::Onion)?;
|
||||
let codec = Codec::from_u8(page.codec).map_err(SyncOnionError::Onion)?;
|
||||
let raw = decompress_page(&page.data, codec, page.orig_size)
|
||||
.map_err(SyncOnionError::Onion)?;
|
||||
hasher.update(&raw);
|
||||
@@ -204,7 +202,12 @@ mod tests {
|
||||
timestamp: 0.0,
|
||||
annotation: None,
|
||||
blake3_root: [0xDEu8; 32], // wrong hash
|
||||
pages: vec![OnionPage { h5_offset: 0, data: page_data.clone(), codec: 0, orig_size: page_data.len() as u32 }],
|
||||
pages: vec![OnionPage {
|
||||
h5_offset: 0,
|
||||
data: page_data.clone(),
|
||||
codec: 0,
|
||||
orig_size: page_data.len() as u32,
|
||||
}],
|
||||
};
|
||||
bad_packet.blake3_root = [0xDE; 32]; // deliberately wrong
|
||||
|
||||
@@ -229,7 +232,12 @@ mod tests {
|
||||
timestamp: 0.0,
|
||||
annotation: None,
|
||||
blake3_root: hash,
|
||||
pages: vec![OnionPage { h5_offset: 0, data: page_data.clone(), codec: 0, orig_size: page_data.len() as u32 }],
|
||||
pages: vec![OnionPage {
|
||||
h5_offset: 0,
|
||||
data: page_data.clone(),
|
||||
codec: 0,
|
||||
orig_size: page_data.len() as u32,
|
||||
}],
|
||||
};
|
||||
|
||||
let (_h5, mut dst) = make_empty_onion();
|
||||
|
||||
@@ -113,8 +113,18 @@ mod tests {
|
||||
annotation: Some(format!("rev {revision}")),
|
||||
blake3_root: [0xABu8; 32],
|
||||
pages: vec![
|
||||
OnionPage { h5_offset: 0, data: vec![0xAAu8; 4096], codec: 0, orig_size: 4096 },
|
||||
OnionPage { h5_offset: 4096, data: vec![0xBBu8; 4096], codec: 0, orig_size: 4096 },
|
||||
OnionPage {
|
||||
h5_offset: 0,
|
||||
data: vec![0xAAu8; 4096],
|
||||
codec: 0,
|
||||
orig_size: 4096,
|
||||
},
|
||||
OnionPage {
|
||||
h5_offset: 4096,
|
||||
data: vec![0xBBu8; 4096],
|
||||
codec: 0,
|
||||
orig_size: 4096,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
@@ -193,11 +203,16 @@ mod tests {
|
||||
blake3_root: [0u8; 32],
|
||||
pages: vec![
|
||||
// "compressed" to 1600 bytes, original 4096
|
||||
OnionPage { h5_offset: 0, data: vec![0xCCu8; 1600], codec: 1, orig_size: 4096 },
|
||||
OnionPage {
|
||||
h5_offset: 0,
|
||||
data: vec![0xCCu8; 1600],
|
||||
codec: 1,
|
||||
orig_size: 4096,
|
||||
},
|
||||
],
|
||||
};
|
||||
assert_eq!(pkt.page_data_size(), 4096); // logical (uncompressed)
|
||||
assert_eq!(pkt.page_wire_size(), 1600); // wire (compressed)
|
||||
assert_eq!(pkt.page_data_size(), 4096); // logical (uncompressed)
|
||||
assert_eq!(pkt.page_wire_size(), 1600); // wire (compressed)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -63,10 +63,7 @@ pub fn filter_packets(
|
||||
/// Compute the remote HEAD for a given selector on the local `OnionFile`.
|
||||
///
|
||||
/// Returns [`NO_PARENT`] if no revisions match (full sync needed).
|
||||
pub fn remote_head_for_selector(
|
||||
_selector: &SyncSelector,
|
||||
remote_revision_count: u64,
|
||||
) -> u64 {
|
||||
pub fn remote_head_for_selector(_selector: &SyncSelector, remote_revision_count: u64) -> u64 {
|
||||
// For a simple linear sync, remote HEAD = remote_revision_count - 1
|
||||
// (or NO_PARENT if empty).
|
||||
if remote_revision_count == 0 {
|
||||
@@ -131,8 +128,11 @@ mod tests {
|
||||
fn selector_branch_nonexistent_errors() {
|
||||
let onion = make_onion(2);
|
||||
let packets = diff_revisions(&onion, NO_PARENT).unwrap();
|
||||
let result =
|
||||
filter_packets(packets, &SyncSelector::Branch("no-such".to_string()), &onion);
|
||||
let result = filter_packets(
|
||||
packets,
|
||||
&SyncSelector::Branch("no-such".to_string()),
|
||||
&onion,
|
||||
);
|
||||
assert!(matches!(result, Err(SyncOnionError::BranchNotFound(_))));
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ use clawhdf5_onion::format::NO_PARENT;
|
||||
use clawhdf5_onion::writer::OnionFile;
|
||||
use clawsync_onion::differ::diff_revisions;
|
||||
use clawsync_onion::merger::merge_packets;
|
||||
use clawsync_onion::selector::{filter_packets, remote_head_for_selector, SyncSelector};
|
||||
use clawsync_onion::selector::{SyncSelector, filter_packets, remote_head_for_selector};
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -309,7 +309,11 @@ fn merge_auto_registers_unknown_branch() {
|
||||
assert_eq!(stats.revisions_merged, 4);
|
||||
// The remote should have both branches registered
|
||||
let branches = dst.list_branches();
|
||||
assert!(branches.len() >= 2, "expected at least 2 branches, got {:?}", branches.len());
|
||||
assert!(
|
||||
branches.len() >= 2,
|
||||
"expected at least 2 branches, got {:?}",
|
||||
branches.len()
|
||||
);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -327,7 +331,10 @@ fn can_branch_on_remote_after_branch_selective_sync() {
|
||||
|
||||
// The remote should now be able to fork its own branch
|
||||
let result = dst.create_branch("experiment", "main");
|
||||
assert!(result.is_ok(), "should be able to create branch after sync: {result:?}");
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"should be able to create branch after sync: {result:?}"
|
||||
);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -340,14 +347,12 @@ fn branch_filtered_merge_is_idempotent() {
|
||||
let mut dst = empty_onion();
|
||||
|
||||
let packets1 = diff_revisions(&src, NO_PARENT).unwrap();
|
||||
let filtered1 =
|
||||
filter_packets(packets1, &SyncSelector::Branch("main".into()), &src).unwrap();
|
||||
let filtered1 = filter_packets(packets1, &SyncSelector::Branch("main".into()), &src).unwrap();
|
||||
merge_packets(&mut dst, filtered1, false).unwrap();
|
||||
|
||||
// Apply again — should skip all
|
||||
let packets2 = diff_revisions(&src, NO_PARENT).unwrap();
|
||||
let filtered2 =
|
||||
filter_packets(packets2, &SyncSelector::Branch("main".into()), &src).unwrap();
|
||||
let filtered2 = filter_packets(packets2, &SyncSelector::Branch("main".into()), &src).unwrap();
|
||||
let stats2 = merge_packets(&mut dst, filtered2, false).unwrap();
|
||||
|
||||
assert_eq!(stats2.revisions_merged, 0);
|
||||
|
||||
Reference in New Issue
Block a user