1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/// Three items of information may need to be transferred in a
/// Block (Block1 or Block2) option:
/// * the size of the block ([`Block::size`])
/// * whether more blocks are following ([`Block::more`])
/// * the relative number of the block ([`Block::num`]) within a sequence of blocks with the given size.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Block(u32);

impl Block {
  #[allow(missing_docs)]
  pub fn new(size: u16, num: u32, more: bool) -> Self {
    let num = num << 4;
    let more = u32::from(more) << 3;
    let size = f32::from(size.max(16).min(1024)).log2() as u32 - 4;

    Self(num | more | size)
  }

  #[allow(missing_docs)]
  pub fn size(&self) -> u16 {
    let szx = (self.0 & 0b111).min(6);
    2u16.pow(szx + 4)
  }

  #[allow(missing_docs)]
  pub fn more(&self) -> bool {
    (self.0 & 0b1000) >> 3 == 1
  }

  #[allow(missing_docs)]
  pub fn num(&self) -> u32 {
    self.0 >> 4
  }
}

impl From<Block> for u32 {
  fn from(b: Block) -> Self {
    b.0
  }
}

impl From<u32> for Block {
  fn from(n: u32) -> Self {
    Block(n)
  }
}

#[cfg(test)]
mod test {
  use super::*;

  #[test]
  fn block() {
    let b = Block(33);
    assert_eq!(b.size(), 32);
    assert_eq!(b.num(), 2);
    assert_eq!(b.more(), false);

    let b = Block(59);
    assert_eq!(b.size(), 128);
    assert_eq!(b.num(), 3);
    assert_eq!(b.more(), true);

    assert_eq!(Block::new(32, 2, false), Block(33));
    assert_eq!(Block::new(128, 3, true), Block(59));
  }

  #[test]
  fn size_rounds_down_to_nearest_power_of_two() {
    assert_eq!(Block::new(0, 1, false).size(), 16);
    assert_eq!(Block::new(10, 1, false).size(), 16);
    assert_eq!(Block::new(17, 1, false).size(), 16);
    assert_eq!(Block::new(31, 1, false).size(), 16);
    assert_eq!(Block::new(33, 1, false).size(), 32);
    assert_eq!(Block::new(64, 1, false).size(), 64);
    assert_eq!(Block::new(1024, 1, false).size(), 1024);
    assert_eq!(Block::new(2048, 1, false).size(), 1024);
  }
}