coap_lite/block_handler/
block_value.rs1use alloc::vec::Vec;
2use core::convert::TryFrom;
3
4use crate::error::{IncompatibleOptionValueFormat, InvalidBlockValue};
5use crate::option_value::{OptionValueType, OptionValueU16};
6
7#[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 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}