Skip to main content

oi4_dnp_encoding/
decode.rs

1#[cfg(feature = "strict")]
2use crate::encode::is_unreserved;
3#[cfg(feature = "alloc")]
4use crate::error::{Error, ErrorKind};
5#[cfg(not(feature = "strict"))]
6use crate::hex::hex_val;
7#[cfg(feature = "strict")]
8use crate::hex::{has_lowercase_hex, hex_val};
9#[cfg(feature = "alloc")]
10use alloc::string::String;
11
12#[cfg(feature = "alloc")]
13pub fn decode(input: &str) -> Result<String, Error> {
14    #[cfg(not(feature = "strict"))]
15    {
16        if !input.as_bytes().contains(&b',') {
17            // fast path only in non-strict mode
18            return Ok(String::from(input));
19        }
20    }
21    let mut out = String::with_capacity(input.len());
22    let bytes = input.as_bytes();
23    let mut i = 0usize;
24    while i < bytes.len() {
25        if bytes[i] == b',' {
26            // escape expected
27            if i + 2 >= bytes.len() {
28                return Err(Error::new(ErrorKind::LoneComma, Some(i)));
29            }
30            let h1 = bytes[i + 1];
31            let h2 = bytes[i + 2];
32            #[cfg(feature = "strict")]
33            {
34                if has_lowercase_hex(h1, h2) {
35                    return Err(Error::new(ErrorKind::LowercaseHexInStrict, Some(i)));
36                }
37            }
38            let v1 = hex_val(h1)
39                .ok_or_else(|| Error::new(ErrorKind::InvalidHexDigit(h1 as char), Some(i + 1)))?;
40            let v2 = hex_val(h2)
41                .ok_or_else(|| Error::new(ErrorKind::InvalidHexDigit(h2 as char), Some(i + 2)))?;
42            let val = (v1 << 4) | v2;
43            out.push(val as char);
44            i += 3;
45        } else {
46            // Copy next UTF-8 char verbatim (could be multibyte)
47            let s = &input[i..];
48            let ch = s.chars().next().unwrap();
49            #[cfg(feature = "strict")]
50            {
51                if ch.is_ascii() {
52                    let bch = ch as u8;
53                    if !is_unreserved(bch) {
54                        return Err(Error::new(ErrorKind::UnescapedReservedAscii(ch), Some(i)));
55                    }
56                }
57            }
58            out.push(ch);
59            i += ch.len_utf8();
60        }
61    }
62    Ok(out)
63}