url/
decode.rs

1use alloc::{
2    borrow::Cow,
3    string::{String, ToString},
4    vec::Vec,
5};
6
7use crate::Result;
8
9/// UrlDecode the given string
10pub fn decode(url: &str) -> Result<Cow<str>> {
11    if !url.contains(['%', '+']) {
12        return Ok(url.into());
13    }
14    let mut result: Vec<u8> = Vec::new();
15    let mut it = url.as_bytes().iter();
16    while let Some(b) = it.next() {
17        if *b == b'+' {
18            result.push(b' ');
19            continue;
20        }
21        if *b != b'%' {
22            result.push(*b);
23            continue;
24        }
25        let first = it.next().ok_or("Missing byte after '%'")?;
26        let second = it.next().ok_or("Missing byte after '%'")?;
27        let first = from_hex_digit(*first)?;
28        let second = from_hex_digit(*second)?;
29        let c = (first << 4) | second;
30        result.push(c);
31    }
32    let result = String::from_utf8(result).map_err(|err| err.to_string())?;
33    Ok(result.into())
34}
35
36#[inline(always)]
37fn from_hex_digit(digit: u8) -> Result<u8> {
38    match digit {
39        b'0'..=b'9' => Ok(digit - b'0'),
40        b'A'..=b'F' => Ok(digit - b'A' + 10),
41        b'a'..=b'f' => Ok(digit - b'a' + 10),
42        _ => Err(format!("{digit} is not a valid hex digit").into()),
43    }
44}