aboutsummaryrefslogtreecommitdiffstats
path: root/src/swarm/blob.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/swarm/blob.rs')
-rw-r--r--src/swarm/blob.rs31
1 files changed, 27 insertions, 4 deletions
diff --git a/src/swarm/blob.rs b/src/swarm/blob.rs
index 11bf3dc..0bd2d37 100644
--- a/src/swarm/blob.rs
+++ b/src/swarm/blob.rs
@@ -9,7 +9,8 @@
//! one sync message would be too large; `swarm::sync`).
//!
//! * Serving: a request without chunks gets the manifest (size, chunk size, a BLAKE3
-//! hash per chunk); one with chunks gets each chunk as a message of its own. A node
+//! hash per chunk); one with chunks gets each chunk as a message of its own, deflated
+//! where that shrinks it (the hash is of the chunk, so any source's matches). A node
//! that doesn't hold the blob answers `Missing`. The bytes of the blob served last
//! are kept, so serving a download doesn't extract them for every chunk. Serving runs
//! in tasks of its own, at most [`MAX_ANSWERING`] requests and [`MAX_SERVING`] chunk
@@ -335,8 +336,19 @@ async fn serve(
let Ok(_permit) = state.blobs.serving.acquire().await else {
return;
};
- let data = bytes::Bytes::copy_from_slice(&prepared.bytes[start..end]);
- reply(state, peer, id, BlobBody::Chunk { index, data }).await;
+ let chunk = &prepared.bytes[start..end];
+ // Deflated where it shrinks (`compress`): changes are stored uncompressed.
+ let body = match crate::compress::deflate(chunk) {
+ Some(deflated) => BlobBody::DeflatedChunk {
+ index,
+ data: deflated.into(),
+ },
+ None => BlobBody::Chunk {
+ index,
+ data: bytes::Bytes::copy_from_slice(chunk),
+ },
+ };
+ reply(state, peer, id, body).await;
}
}
@@ -527,7 +539,18 @@ pub(crate) async fn fetch(
request(state, &source.peer, id, batch, delta).await;
}
- match tokio::time::timeout(Duration::from_millis(500), answers.recv()).await {
+ let answer = tokio::time::timeout(Duration::from_millis(500), answers.recv()).await;
+ // A deflated chunk inflated, to no more than a chunk; one that doesn't is bad.
+ let answer = match answer {
+ Ok(Some((from, BlobBody::DeflatedChunk { index, data }))) => {
+ let data = crate::compress::inflate(&data, manifest.chunk_size as usize)
+ .map(bytes::Bytes::from)
+ .unwrap_or_default();
+ Ok(Some((from, BlobBody::Chunk { index, data })))
+ }
+ other => other,
+ };
+ match answer {
Ok(Some((from, BlobBody::Chunk { index, data }))) => {
let Some(source) = sources.iter_mut().find(|source| source.peer == from) else {
continue;