Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 54ec3e3

Browse files
committedFeb 13, 2025
Even more size refactoring
1 parent 18d7688 commit 54ec3e3

File tree

6 files changed

+150
-65
lines changed

6 files changed

+150
-65
lines changed
 

‎examples/multiprovider.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use iroh_blobs::{
1010
ObserveRequest, StaticContentDiscovery,
1111
},
1212
store::Store,
13-
util::total_bytes,
1413
Hash,
1514
};
1615

@@ -118,30 +117,30 @@ impl BlobDownloadProgress {
118117
fn update(&mut self, ev: BitfieldEvent) {
119118
match ev {
120119
BitfieldEvent::State(BitfieldState { ranges, size }) => {
121-
self.current.size = self.current.size.min(size);
122120
self.current.ranges = ranges;
123-
self.request.ranges &= ChunkRanges::from(..ChunkNum::chunks(self.current.size));
121+
self.current
122+
.size
123+
.update(size)
124+
.expect("verified size changed");
124125
}
125126
BitfieldEvent::Update(BitfieldUpdate {
126127
added,
127128
removed,
128129
size,
129130
}) => {
130-
self.current.size = self.current.size.min(size);
131-
self.request.ranges &= ChunkRanges::from(..ChunkNum::chunks(self.current.size));
132131
self.current.ranges |= added;
133132
self.current.ranges -= removed;
133+
self.current
134+
.size
135+
.update(size)
136+
.expect("verified size changed");
137+
if let Some(size) = self.current.size.value() {
138+
self.request.ranges &= ChunkRanges::from(..ChunkNum::chunks(size));
139+
}
134140
}
135141
}
136142
}
137143

138-
#[allow(dead_code)]
139-
fn get_stats(&self) -> (u64, u64) {
140-
let total = total_bytes(&self.request.ranges, self.current.size);
141-
let downloaded = total_bytes(&self.current.ranges, self.current.size);
142-
(downloaded, total)
143-
}
144-
145144
#[allow(dead_code)]
146145
fn get_bitmap(&self) -> String {
147146
format!("{:?}", self.current)
@@ -197,7 +196,6 @@ async fn download_impl<S: Store>(args: DownloadArgs, store: S) -> anyhow::Result
197196
progress.update(chunk);
198197
let current = progress.current.ranges.boundaries();
199198
let requested = progress.request.ranges.boundaries();
200-
println!("observe print_bitmap {:?} {:?}", current, requested);
201199
let bitmap = print_bitmap(current, requested, rows as usize);
202200
print!("\r{bitmap}");
203201
if progress.is_done() {

‎src/downloader2.rs

Lines changed: 95 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use crate::{
2727
Stats,
2828
},
2929
protocol::{GetRequest, RangeSpec, RangeSpecSeq},
30-
store::{BaoBatchWriter, MapEntryMut, Store},
30+
store::{BaoBatchWriter, BaoBlobSize, MapEntry, MapEntryMut, Store},
3131
util::local_pool::{self, LocalPool, LocalPoolHandle},
3232
Hash,
3333
};
@@ -89,6 +89,64 @@ pub trait BitfieldSubscription: std::fmt::Debug + Send + 'static {
8989
/// A boxed bitfield subscription
9090
pub type BoxedBitfieldSubscription = Box<dyn BitfieldSubscription>;
9191

92+
/// Knowlege about the size of a blob
93+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94+
pub enum BaoBlobSizeOpt {
95+
/// We have a size that a peer told us about, but we don't know if it is correct
96+
/// It can be off at most by a factor of 2, so it is OK for things like showing
97+
/// a progress bar or even for an allocation size
98+
Unverified(u64),
99+
/// We know the size, and it is verified
100+
/// either by having the last chunk locally or by receiving a size proof from a peer
101+
Verified(u64),
102+
/// We know nothing, e.g. we have never heard of the blob
103+
Unknown,
104+
}
105+
106+
impl BaoBlobSizeOpt {
107+
/// Get the value of the size, if known
108+
pub fn value(self) -> Option<u64> {
109+
match self {
110+
BaoBlobSizeOpt::Unverified(x) => Some(x),
111+
BaoBlobSizeOpt::Verified(x) => Some(x),
112+
BaoBlobSizeOpt::Unknown => None,
113+
}
114+
}
115+
116+
/// Update the size information
117+
///
118+
/// Unkown sizes are always updated
119+
/// Unverified sizes are updated if the new size is verified
120+
/// Verified sizes must never change
121+
pub fn update(&mut self, size: BaoBlobSizeOpt) -> anyhow::Result<()> {
122+
match self {
123+
BaoBlobSizeOpt::Verified(old) => {
124+
if let BaoBlobSizeOpt::Verified(new) = size {
125+
if *old != new {
126+
anyhow::bail!("mismatched verified sizes: {old} != {new}");
127+
}
128+
}
129+
}
130+
BaoBlobSizeOpt::Unverified(_) => {
131+
if let BaoBlobSizeOpt::Verified(new) = size {
132+
*self = BaoBlobSizeOpt::Verified(new);
133+
}
134+
}
135+
BaoBlobSizeOpt::Unknown => *self = size,
136+
};
137+
Ok(())
138+
}
139+
}
140+
141+
impl From<BaoBlobSize> for BaoBlobSizeOpt {
142+
fn from(size: BaoBlobSize) -> Self {
143+
match size {
144+
BaoBlobSize::Unverified(x) => Self::Unverified(x),
145+
BaoBlobSize::Verified(x) => Self::Verified(x),
146+
}
147+
}
148+
}
149+
92150
/// Events from observing a local bitfield
93151
#[derive(Debug, PartialEq, Eq, derive_more::From)]
94152
pub enum BitfieldEvent {
@@ -98,32 +156,32 @@ pub enum BitfieldEvent {
98156
Update(BitfieldUpdate),
99157
}
100158

159+
/// The state of a bitfield
160+
#[derive(Debug, PartialEq, Eq)]
161+
pub struct BitfieldState {
162+
/// The ranges that are set
163+
pub ranges: ChunkRanges,
164+
/// Whatever size information is available
165+
pub size: BaoBlobSizeOpt,
166+
}
167+
101168
/// An update to a bitfield
102169
#[derive(Debug, PartialEq, Eq)]
103170
pub struct BitfieldUpdate {
104171
/// The ranges that were added
105172
pub added: ChunkRanges,
106173
/// The ranges that were removed
107174
pub removed: ChunkRanges,
108-
/// The total size of the bitfield in bytes
109-
pub size: u64,
110-
}
111-
112-
/// The state of a bitfield
113-
#[derive(Debug, PartialEq, Eq)]
114-
pub struct BitfieldState {
115-
/// The ranges that are set
116-
pub ranges: ChunkRanges,
117-
/// The total size of the bitfield in bytes
118-
pub size: u64,
175+
/// Possible update to the size information
176+
pub size: BaoBlobSizeOpt,
119177
}
120178

121179
impl BitfieldState {
122180
/// State for a completely unknown bitfield
123181
pub fn unknown() -> Self {
124182
Self {
125183
ranges: ChunkRanges::empty(),
126-
size: u64::MAX,
184+
size: BaoBlobSizeOpt::Unknown,
127185
}
128186
}
129187
}
@@ -345,7 +403,7 @@ impl BitfieldSubscription for TestBitfieldSubscription {
345403
futures_lite::stream::once(
346404
BitfieldState {
347405
ranges,
348-
size: 1024 * 1024 * 1024 * 1024 * 1024,
406+
size: BaoBlobSizeOpt::Unknown,
349407
}
350408
.into(),
351409
)
@@ -375,7 +433,24 @@ impl<S> SimpleBitfieldSubscription<S> {
375433

376434
async fn get_valid_ranges_local<S: Store>(hash: &Hash, store: S) -> anyhow::Result<BitfieldEvent> {
377435
if let Some(entry) = store.get_mut(hash).await? {
378-
let (ranges, size) = crate::get::db::valid_ranges_and_size::<S>(&entry).await?;
436+
let ranges = crate::get::db::valid_ranges::<S>(&entry).await?;
437+
let size = entry.size();
438+
let size = match size {
439+
size @ BaoBlobSize::Unverified(value) => {
440+
if let Some(last_chunk) = ChunkNum::chunks(value).0.checked_sub(1).map(ChunkNum) {
441+
if ranges.contains(&last_chunk) {
442+
BaoBlobSizeOpt::Verified(value)
443+
} else {
444+
size.into()
445+
}
446+
} else {
447+
// this branch is just for size == 0
448+
// todo: return BaoBlobSize::Verified(0) if the hash is the hash of the empty blob
449+
BaoBlobSizeOpt::Unknown
450+
}
451+
}
452+
size => size.into(),
453+
};
379454
Ok(BitfieldState { ranges, size }.into())
380455
} else {
381456
Ok(BitfieldState::unknown().into())
@@ -391,7 +466,11 @@ async fn get_valid_ranges_remote(
391466
let (size, _) = crate::get::request::get_verified_size(&conn, hash).await?;
392467
let chunks = (size + 1023) / 1024;
393468
let ranges = ChunkRanges::from(ChunkNum(0)..ChunkNum(chunks));
394-
Ok(BitfieldState { ranges, size }.into())
469+
Ok(BitfieldState {
470+
ranges,
471+
size: BaoBlobSizeOpt::Verified(size),
472+
}
473+
.into())
395474
}
396475

397476
impl<S: Store> BitfieldSubscription for SimpleBitfieldSubscription<S> {

‎src/downloader2/actor.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,16 @@ async fn peer_download<S: Store>(
267267
batch.push(parent.into());
268268
}
269269
BaoContentItem::Leaf(leaf) => {
270-
let start_chunk = leaf.offset / 1024;
271-
let added =
272-
ChunkRanges::from(ChunkNum(start_chunk)..ChunkNum(start_chunk + 16));
270+
let size_chunks = ChunkNum::chunks(size);
271+
let start_chunk = ChunkNum::full_chunks(leaf.offset);
272+
let end_chunk =
273+
ChunkNum::full_chunks(leaf.offset + 16 * 1024).min(size_chunks);
274+
let last_chunk = size_chunks
275+
.0
276+
.checked_sub(1)
277+
.map(ChunkNum)
278+
.expect("Size must not be 0");
279+
let added = ChunkRanges::from(start_chunk..end_chunk);
273280
sender
274281
.send(Command::ChunksDownloaded {
275282
time: start.elapsed(),
@@ -281,6 +288,11 @@ async fn peer_download<S: Store>(
281288
.ok();
282289
batch.push(leaf.into());
283290
writer.write_batch(size, std::mem::take(&mut batch)).await?;
291+
let size = if added.contains(&last_chunk) {
292+
BaoBlobSizeOpt::Verified(size)
293+
} else {
294+
BaoBlobSizeOpt::Unverified(size)
295+
};
284296
sender
285297
.send(Command::BitfieldInfo {
286298
peer: BitfieldPeer::Local,

0 commit comments

Comments
 (0)
Please sign in to comment.