coap_lite/block_handler/
block_value.rs

1use alloc::vec::Vec;
2use core::convert::TryFrom;
3
4use crate::error::{IncompatibleOptionValueFormat, InvalidBlockValue};
5use crate::option_value::{OptionValueType, OptionValueU16};
6
7/// The block option value.
8#[derive(Debug, Clone, Eq, PartialEq)]
9pub struct BlockValue {
10    pub num: u16,
11    pub more: bool,
12    pub size_exponent: u8,
13}
14
15impl BlockValue {
16    pub fn new(
17        num: usize,
18        more: bool,
19        size: usize,
20    ) -> Result<Self, InvalidBlockValue> {
21        let true_size_exponent = Self::largest_power_of_2_not_in_excess(size)
22            .ok_or(InvalidBlockValue::SizeExponentEncodingError(size))?;
23
24        let size_exponent = u8::try_from(true_size_exponent.saturating_sub(4))
25            .map_err(InvalidBlockValue::TypeBoundsError)?;
26        if size_exponent > 0x7 {
27            return Err(InvalidBlockValue::SizeExponentEncodingError(size));
28        }
29        let num =
30            u16::try_from(num).map_err(InvalidBlockValue::TypeBoundsError)?;
31        Ok(Self {
32            num,
33            more,
34            size_exponent,
35        })
36    }
37
38    /// Finds the largest power of 2 that does not exceed `target`.
39    fn largest_power_of_2_not_in_excess(target: usize) -> Option<usize> {
40        if target == 0 {
41            return None;
42        }
43
44        let max_power = usize::try_from(usize::BITS).unwrap();
45        let power_in_excess = (0..max_power).find(|i| (1 << i) > target);
46
47        match power_in_excess {
48            Some(size) => Some(size - 1),
49            None => Some(max_power),
50        }
51    }
52
53    pub fn size(&self) -> usize {
54        1 << (self.size_exponent + 4)
55    }
56}
57
58impl From<BlockValue> for Vec<u8> {
59    fn from(block_value: BlockValue) -> Vec<u8> {
60        let scalar = block_value.num << 4
61            | u16::from(block_value.more) << 3
62            | u16::from(block_value.size_exponent & 0x7);
63        Vec::from(OptionValueU16(scalar))
64    }
65}
66
67impl TryFrom<Vec<u8>> for BlockValue {
68    type Error = IncompatibleOptionValueFormat;
69
70    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
71        let scalar = OptionValueU16::try_from(value)?.0;
72
73        let num: u16 = scalar >> 4;
74        let more = scalar >> 3 & 0x1 == 0x1;
75        let size_exponent: u8 = (scalar & 0x7) as u8;
76        Ok(Self {
77            num,
78            more,
79            size_exponent,
80        })
81    }
82}
83
84impl OptionValueType for BlockValue {}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn test_highest_containing_power_of_2() {
92        assert_eq!(BlockValue::largest_power_of_2_not_in_excess(0), None);
93        assert_eq!(BlockValue::largest_power_of_2_not_in_excess(256), Some(8));
94        assert_eq!(BlockValue::largest_power_of_2_not_in_excess(257), Some(8));
95        assert_eq!(
96            BlockValue::largest_power_of_2_not_in_excess(usize::MAX),
97            Some(usize::try_from(usize::BITS).unwrap())
98        );
99    }
100
101    #[test]
102    fn test_block_value_exponent() {
103        assert!(BlockValue::new(0, false, 0).is_err());
104        assert!(BlockValue::new(0, false, usize::MAX).is_err());
105        assert_eq!(
106            BlockValue::new(0, false, 1158).unwrap(),
107            BlockValue {
108                num: 0,
109                more: false,
110                size_exponent: 6
111            }
112        );
113        assert_eq!(
114            BlockValue::new(0, false, 256).unwrap(),
115            BlockValue {
116                num: 0,
117                more: false,
118                size_exponent: 4
119            }
120        );
121    }
122}