2020-02-05 04:59:06 +01:00
|
|
|
// The piece length picker attempts to pick a reasonable piece length
|
|
|
|
// for a torrent given the size of the torrent's contents.
|
|
|
|
//
|
|
|
|
// Constraints:
|
|
|
|
// - Decreasing piece length increases protocol overhead.
|
|
|
|
// - Decreasing piece length increases torrent metainfo size.
|
2020-03-06 07:51:00 +01:00
|
|
|
// - Increasing piece length increases the amount of data that must be thrown
|
|
|
|
// away in case of corruption.
|
2020-02-05 04:59:06 +01:00
|
|
|
// - Increasing piece length increases the amount of data that must be
|
|
|
|
// downloaded before it can be verified and uploaded to other peers.
|
2020-03-06 07:51:00 +01:00
|
|
|
// - Decreasing piece length increases the proportion of disk seeks to disk
|
|
|
|
// reads. This can be an issue for spinning disks.
|
|
|
|
// - The BitTorrent v2 specification requires that piece sizes be larger than 16
|
|
|
|
// KiB.
|
2020-02-05 04:59:06 +01:00
|
|
|
//
|
|
|
|
// These constraints could probably be exactly defined and optimized
|
|
|
|
// using an integer programming solver, but instead we just copy what
|
|
|
|
// libtorrent does.
|
|
|
|
|
|
|
|
use crate::common::*;
|
|
|
|
|
|
|
|
pub(crate) struct PieceLengthPicker;
|
|
|
|
|
|
|
|
impl PieceLengthPicker {
|
|
|
|
pub(crate) fn from_content_size(content_size: Bytes) -> Bytes {
|
|
|
|
#![allow(
|
|
|
|
clippy::as_conversions,
|
|
|
|
clippy::cast_sign_loss,
|
|
|
|
clippy::cast_precision_loss,
|
|
|
|
clippy::cast_possible_truncation
|
|
|
|
)]
|
2020-02-14 09:12:49 +01:00
|
|
|
let exponent = (content_size.count().max(1) as f64).log2().ceil() as u64;
|
|
|
|
Bytes::from(1u64 << (exponent / 2 + 4))
|
2020-02-05 04:59:06 +01:00
|
|
|
.max(Bytes::kib() * 16)
|
|
|
|
.min(Bytes::mib() * 16)
|
|
|
|
}
|
|
|
|
|
2020-02-14 09:12:49 +01:00
|
|
|
pub(crate) fn piece_count(content_size: Bytes, piece_length: Bytes) -> u64 {
|
|
|
|
if content_size == Bytes::from(0u64) {
|
2020-02-05 04:59:06 +01:00
|
|
|
0
|
|
|
|
} else {
|
|
|
|
(content_size / piece_length).max(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn metainfo_size(content_size: Bytes, piece_length: Bytes) -> Bytes {
|
2020-02-14 09:12:49 +01:00
|
|
|
let digest_length: u64 = sha1::DIGEST_LENGTH.into_u64();
|
2020-02-05 04:59:06 +01:00
|
|
|
Bytes::from(Self::piece_count(content_size, piece_length) * digest_length)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn limits() {
|
|
|
|
assert_eq!(
|
|
|
|
PieceLengthPicker::from_content_size(Bytes::mib() * 2),
|
|
|
|
Bytes::kib() * 16
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
PieceLengthPicker::from_content_size(Bytes::mib() * 4),
|
|
|
|
Bytes::kib() * 32
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
PieceLengthPicker::from_content_size(Bytes::mib() * 8),
|
|
|
|
Bytes::kib() * 32
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
PieceLengthPicker::from_content_size(Bytes::mib() * 16),
|
|
|
|
Bytes::kib() * 64
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|