/rust/registry/src/index.crates.io-6f17d22bba15001f/urlencoding-2.1.3/src/dec.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use std::borrow::Cow; |
2 | | use std::string::FromUtf8Error; |
3 | | |
4 | | #[inline] |
5 | 12.0M | pub(crate) fn from_hex_digit(digit: u8) -> Option<u8> { |
6 | 12.0M | match digit { |
7 | 11.9M | b'0'..=b'9' => Some(digit - b'0'), |
8 | 5.68M | b'A'..=b'F' => Some(digit - b'A' + 10), |
9 | 46.5k | b'a'..=b'f' => Some(digit - b'a' + 10), |
10 | 109k | _ => None, |
11 | | } |
12 | 12.0M | } |
13 | | |
14 | | /// Decode percent-encoded string assuming UTF-8 encoding. |
15 | | /// |
16 | | /// If you need a `String`, call `.into_owned()` (not `.to_owned()`). |
17 | | /// |
18 | | /// Unencoded `+` is preserved literally, and _not_ changed to a space. |
19 | 0 | pub fn decode(data: &str) -> Result<Cow<str>, FromUtf8Error> { |
20 | 0 | match decode_binary(data.as_bytes()) { |
21 | 0 | Cow::Borrowed(_) => Ok(Cow::Borrowed(data)), |
22 | 0 | Cow::Owned(s) => Ok(Cow::Owned(String::from_utf8(s)?)), |
23 | | } |
24 | 0 | } |
25 | | |
26 | | /// Decode percent-encoded string as binary data, in any encoding. |
27 | | /// |
28 | | /// Unencoded `+` is preserved literally, and _not_ changed to a space. |
29 | 715k | pub fn decode_binary(data: &[u8]) -> Cow<[u8]> { |
30 | 6.99M | let offset = data.iter().take_while(|&&c| c != b'%').count(); |
31 | 715k | if offset >= data.len() { |
32 | 378k | return Cow::Borrowed(data) |
33 | 337k | } |
34 | 337k | |
35 | 337k | let mut decoded: Vec<u8> = Vec::with_capacity(data.len()); |
36 | 337k | let mut out = NeverRealloc(&mut decoded); |
37 | 337k | |
38 | 337k | let (ascii, mut data) = data.split_at(offset); |
39 | 337k | out.extend_from_slice(ascii); |
40 | | |
41 | 6.38M | loop { |
42 | 16.5M | let mut parts = data.splitn(2, |&c| c == b'%'); |
43 | 6.38M | // first the decoded non-% part |
44 | 6.38M | let non_escaped_part = parts.next().unwrap(); |
45 | 6.38M | let rest = parts.next(); |
46 | 6.38M | if rest.is_none() && out.0.is_empty() { |
47 | | // if empty there were no '%' in the string |
48 | 0 | return data.into(); |
49 | 6.38M | } |
50 | 6.38M | out.extend_from_slice(non_escaped_part); |
51 | 6.38M | |
52 | 6.38M | // then decode one %xx |
53 | 6.38M | match rest { |
54 | 6.07M | Some(rest) => match rest.get(0..2) { |
55 | 6.05M | Some(&[first, second]) => match from_hex_digit(first) { |
56 | 5.95M | Some(first_val) => match from_hex_digit(second) { |
57 | 5.94M | Some(second_val) => { |
58 | 5.94M | out.push((first_val << 4) | second_val); |
59 | 5.94M | data = &rest[2..]; |
60 | 5.94M | }, |
61 | 10.5k | None => { |
62 | 10.5k | out.extend_from_slice(&[b'%', first]); |
63 | 10.5k | data = &rest[1..]; |
64 | 10.5k | }, |
65 | | }, |
66 | 98.7k | None => { |
67 | 98.7k | out.push(b'%'); |
68 | 98.7k | data = rest; |
69 | 98.7k | }, |
70 | | }, |
71 | | _ => { |
72 | | // too short |
73 | 19.5k | out.push(b'%'); |
74 | 19.5k | out.extend_from_slice(rest); |
75 | 19.5k | break; |
76 | | }, |
77 | | }, |
78 | 317k | None => break, |
79 | | } |
80 | | } |
81 | 337k | Cow::Owned(decoded) |
82 | 715k | } |
83 | | |
84 | | |
85 | | struct NeverRealloc<'a, T>(pub &'a mut Vec<T>); |
86 | | |
87 | | impl<T> NeverRealloc<'_, T> { |
88 | | #[inline] |
89 | 6.05M | pub fn push(&mut self, val: T) { |
90 | 6.05M | // these branches only exist to remove redundant reallocation code |
91 | 6.05M | // (the capacity is always sufficient) |
92 | 6.05M | if self.0.len() != self.0.capacity() { |
93 | 6.05M | self.0.push(val); |
94 | 6.05M | } |
95 | 6.05M | } |
96 | | #[inline] |
97 | 6.75M | pub fn extend_from_slice(&mut self, val: &[T]) where T: Clone { |
98 | 6.75M | if self.0.capacity() - self.0.len() >= val.len() { |
99 | 6.75M | self.0.extend_from_slice(val); |
100 | 6.75M | } |
101 | 6.75M | } |
102 | | } |
103 | | |
104 | | #[test] |
105 | | fn dec_borrows() { |
106 | | assert!(matches!(decode("hello"), Ok(Cow::Borrowed("hello")))); |
107 | | assert!(matches!(decode("hello%20"), Ok(Cow::Owned(s)) if s == "hello ")); |
108 | | assert!(matches!(decode("%20hello"), Ok(Cow::Owned(s)) if s == " hello")); |
109 | | } |