coap_lite/
option_value.rs

1//! Convenience types for option values.
2
3use alloc::{
4    string::{String, ToString},
5    vec::Vec,
6};
7use core::convert::TryFrom;
8
9use crate::error::IncompatibleOptionValueFormat;
10
11/// Supertrait for types that can be used as option values.
12pub trait OptionValueType:
13    Into<Vec<u8>> + TryFrom<Vec<u8>, Error = IncompatibleOptionValueFormat>
14{
15}
16
17macro_rules! option_value_uint_impl {
18    ($struct_name:ident, $type:ty, $bytes:expr) => {
19        #[derive(Debug, Clone, PartialEq)]
20        pub struct $struct_name(pub $type);
21
22        impl From<$struct_name> for Vec<u8> {
23            fn from(value: $struct_name) -> Self {
24                option_from_uint(value.0.into(), $bytes)
25            }
26        }
27
28        impl TryFrom<Vec<u8>> for $struct_name {
29            type Error = IncompatibleOptionValueFormat;
30
31            fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
32                option_to_uint(&value, $bytes)
33                    // Safe because option_to_uint will yield error if the size
34                    // would overflow
35                    .map(|value_as_u64| $struct_name(value_as_u64 as $type))
36            }
37        }
38
39        impl OptionValueType for $struct_name {}
40    };
41}
42
43fn option_from_uint(value_as_u64: u64, value_size: usize) -> Vec<u8> {
44    // Optimize common paths
45    if value_as_u64 == 0 {
46        vec![]
47    } else if value_as_u64 < 256 {
48        vec![value_as_u64 as u8]
49    } else {
50        let mut draining_value = value_as_u64;
51
52        let mut output = Vec::with_capacity(value_size);
53        while draining_value > 0 {
54            let next_lowest_byte = (draining_value & 0xff) as u8;
55            assert!(output.len() < value_size);
56            output.push(next_lowest_byte);
57            draining_value >>= 8;
58        }
59
60        // Output is in little endian, flip it to big endian (network order) as
61        // required
62        output.reverse();
63
64        output
65    }
66}
67
68fn option_to_uint(
69    encoded: &[u8],
70    value_size: usize,
71) -> Result<u64, IncompatibleOptionValueFormat> {
72    if encoded.len() > value_size {
73        Err(IncompatibleOptionValueFormat {
74            message: format!(
75                "overflow: got {} bytes, expected {}",
76                encoded.len(),
77                value_size
78            ),
79        })
80    } else {
81        Ok(encoded.iter().fold(0, |acc, &b| (acc << 8) + b as u64))
82    }
83}
84
85option_value_uint_impl!(OptionValueU8, u8, 1);
86option_value_uint_impl!(OptionValueU16, u16, 2);
87option_value_uint_impl!(OptionValueU32, u32, 4);
88option_value_uint_impl!(OptionValueU64, u64, 8);
89
90#[derive(Debug, Clone, PartialEq)]
91pub struct OptionValueString(pub String);
92
93impl From<OptionValueString> for Vec<u8> {
94    fn from(option_value: OptionValueString) -> Self {
95        option_value.0.into_bytes()
96    }
97}
98
99impl TryFrom<Vec<u8>> for OptionValueString {
100    type Error = IncompatibleOptionValueFormat;
101
102    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
103        String::from_utf8(value)
104            .map(OptionValueString)
105            .map_err(|e| IncompatibleOptionValueFormat {
106                message: e.to_string(),
107            })
108    }
109}
110
111impl OptionValueType for OptionValueString {}