btpc_core/create/
piece_length.rs

1use crate::{Error, Result};
2
3/// Stable identifier for the automatic piece-length selection table.
4pub const PIECE_LENGTH_POLICY_ID: &str = "btpc-piece-v1";
5
6/// Protocol constraints applied to an explicit piece length.
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub enum PieceLengthMode {
9    /// v1-only creation permits powers of two from 1 KiB.
10    V1,
11    /// v2 creation requires the BEP 52 16 KiB minimum.
12    V2,
13    /// Hybrid creation follows the v2 minimum.
14    Hybrid,
15}
16
17const PIECE_LENGTH_BANDS: &[(u64, u64)] = &[
18    (16 * 1024 * 1024, 16 * 1024),
19    (32 * 1024 * 1024, 32 * 1024),
20    (64 * 1024 * 1024, 64 * 1024),
21    (128 * 1024 * 1024, 128 * 1024),
22    (256 * 1024 * 1024, 256 * 1024),
23    (512 * 1024 * 1024, 512 * 1024),
24    (1024 * 1024 * 1024, 1024 * 1024),
25    (2 * 1024 * 1024 * 1024, 2 * 1024 * 1024),
26    (4 * 1024 * 1024 * 1024, 4 * 1024 * 1024),
27    (8 * 1024 * 1024 * 1024, 8 * 1024 * 1024),
28    (16 * 1024 * 1024 * 1024, 16 * 1024 * 1024),
29];
30
31/// Selects a stable automatic piece length for total payload bytes.
32///
33/// The table targets at most roughly 1024 pieces in each band while keeping a
34/// 16 KiB minimum for v2 compatibility and a 16 MiB maximum for interoperability.
35#[must_use]
36pub fn automatic_piece_length(total_length: u64) -> u64 {
37    PIECE_LENGTH_BANDS
38        .iter()
39        .find_map(|(maximum, piece_length)| (total_length <= *maximum).then_some(*piece_length))
40        .unwrap_or(16 * 1024 * 1024)
41}
42
43/// Validates a caller-selected piece length.
44///
45/// # Errors
46///
47/// Returns a metainfo error when the value is not a power of two or is outside
48/// the protocol-specific minimum and 16 MiB maximum.
49pub fn validate_piece_length(piece_length: u64, mode: PieceLengthMode) -> Result<u64> {
50    let minimum = match mode {
51        PieceLengthMode::V1 => 1024,
52        PieceLengthMode::V2 | PieceLengthMode::Hybrid => 16 * 1024,
53    };
54    if !piece_length.is_power_of_two() || !(minimum..=16 * 1024 * 1024).contains(&piece_length) {
55        return Err(Error::metainfo_field(
56            "piece length",
57            format!(
58                "must be a power of two between {minimum} and {} bytes",
59                16 * 1024 * 1024
60            ),
61        ));
62    }
63    Ok(piece_length)
64}