embedded_mqtt/codec/
string.rs

1use core::{
2    result::Result,
3    str,
4    cmp::min,
5    convert::TryFrom,
6};
7
8use crate::{
9	status::Status,
10	error::{DecodeError, EncodeError},
11};
12
13use super::{
14    values,
15    Decodable,
16    Encodable,
17};
18
19impl<'buf> Decodable<'buf> for &'buf str {
20    fn decode(bytes: &'buf [u8]) -> Result<Status<(usize, &'buf str)>, DecodeError> {
21        parse_string(bytes)
22    }
23}
24
25impl Encodable for str {
26    fn encoded_len(&self) -> usize {
27        2 + self.len()
28    }
29
30    fn encode(&self, bytes: &mut [u8]) -> Result<usize, EncodeError> {
31        encode_string(self, bytes)
32    }
33}
34
35pub fn parse_string(bytes: &[u8]) -> Result<Status<(usize, &str)>, DecodeError> {
36    let offset = 0;
37
38    let (offset, string_len) = read!(values::parse_u16, bytes, offset);
39
40    let available = bytes.len() - offset;
41
42    let needed = string_len as usize - min(available, string_len as usize);
43    if needed > 0 {
44        return Ok(Status::Partial(needed));
45    }
46
47    let val = if string_len > 0 {
48        // Rust string slices are never in the code point range 0xD800 and
49        // 0xDFFF which takes care of requirement MQTT-1.5.3-1. str::from_utf8
50        // will fail if those code points are found in "bytes".
51        //
52        // Rust utf-8 decoding also takes care of MQTT-1.5.3-3. U+FEFF does not
53        // get ignored/stripped off.
54        str::from_utf8(&bytes[2..(2 + string_len) as usize])?
55    } else {
56        ""
57    };
58
59    // Requirement MQTT-1.5.3-2 requires that there be no U+0000 code points
60    // in the string.
61    if val.chars().any(|ch| ch == '\u{0000}') {
62        return Err(DecodeError::Utf8)
63    }
64    
65    Ok(Status::Complete(((2 + string_len) as usize, val)))
66}
67
68pub fn encode_string(string: &str, bytes: &mut [u8]) -> Result<usize, EncodeError> {
69    let size = match u16::try_from(string.len()) {
70        Err(_) => return Err(EncodeError::ValueTooBig),
71        Ok(s) => s,
72    };
73
74    if bytes.len() < (2 + size) as usize {
75        return Err(EncodeError::OutOfSpace)
76    }
77
78    values::encode_u16(size, &mut bytes[0..2])?;
79    (&mut bytes[2..2 + size as usize]).copy_from_slice(string.as_bytes());
80
81    Ok(2 + size as usize)
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87    use std::{
88        io::{Cursor, Write},
89        vec::Vec,
90        format,
91    };
92
93    use byteorder::{
94        BigEndian,
95        ByteOrder,
96    };
97
98    use byteorder::WriteBytesExt;
99
100    #[test]
101    fn small_buffer() {
102        assert_eq!(Ok(Status::Partial(2)), parse_string(&[]));
103        assert_eq!(Ok(Status::Partial(1)), parse_string(&[0]));
104
105        let mut buf = [0u8; 2];
106        BigEndian::write_u16(&mut buf, 16);
107        assert_eq!(Ok(Status::Partial(16)), parse_string(&buf));
108    }
109
110    #[test]
111    fn empty_str() {
112        let mut buf = [0u8; 2];
113        BigEndian::write_u16(&mut buf, 0);
114        assert_eq!(Ok(Status::Complete((2, ""))), parse_string(&buf));
115    }
116
117    #[test]
118    fn parse_str() {
119        let inp = "don't panic!";
120        let mut buf = Cursor::new(Vec::new());
121        buf.write_u16::<BigEndian>(inp.len() as u16).unwrap();
122        buf.write(inp.as_bytes()).unwrap();
123        assert_eq!(
124            Status::Complete((14, inp)),
125            parse_string(buf.get_ref().as_ref()).unwrap()
126        );
127    }
128    
129    #[test]
130    fn invalid_utf8() {
131        let inp = [0, 159, 146, 150];
132        let mut buf = Cursor::new(Vec::new());
133        buf.write_u16::<BigEndian>(inp.len() as u16).unwrap();
134        buf.write(&inp).unwrap();
135        assert_eq!(Err(DecodeError::Utf8), parse_string(buf.get_ref().as_ref()));
136    }
137
138    #[test]
139    fn null_utf8() {
140        let inp = format!("don't {} panic!", '\u{0000}');
141        let mut buf = Cursor::new(Vec::new());
142        buf.write_u16::<BigEndian>(inp.len() as u16).unwrap();
143        buf.write(inp.as_bytes()).unwrap();
144        assert_eq!(Err(DecodeError::Utf8), parse_string(buf.get_ref().as_ref()));
145    }
146
147    #[test]
148    fn encode() {
149        let mut buf = [0u8; 3];
150        let result = encode_string("a", &mut buf[0..3]);
151        assert_eq!(result, Ok(3));
152        assert_eq!(buf, [0b00000000, 0b00000001, 0x61]);
153    }
154}