1use alloc::{borrow::Cow, string::String, vec::Vec};
2use core::{mem::swap, str::from_utf8_unchecked};
3
4#[inline]
6pub fn unescape<S: ?Sized + AsRef<str>>(base64_url: &S) -> Cow<'_, str> {
7 let base64_url = base64_url.as_ref();
8
9 match unescape_u8_slice(base64_url) {
10 Cow::Owned(base64) => {
11 let base64 = unsafe { String::from_utf8_unchecked(base64) };
12
13 Cow::from(base64)
14 },
15 Cow::Borrowed(base64) => {
16 let base64 = unsafe { from_utf8_unchecked(base64) };
17
18 Cow::from(base64)
19 },
20 }
21}
22
23pub fn unescape_u8_slice<S: ?Sized + AsRef<[u8]>>(base64_url: &S) -> Cow<'_, [u8]> {
25 let base64_url = base64_url.as_ref();
26 let length = base64_url.len();
27
28 let padding = length & 0b11;
29
30 if padding > 0 {
31 let new_size = base64_url.len() + (4 - padding);
32
33 let mut base64 = Vec::with_capacity(new_size);
34
35 let mut p = 0;
36 let mut start = 0;
37
38 loop {
39 if p == length {
40 break;
41 }
42
43 let e = base64_url[p];
44
45 match e {
46 45 => {
47 base64.extend_from_slice(&base64_url[start..p]);
48 start = p + 1;
49
50 base64.push(43);
51 },
52 95 => {
53 base64.extend_from_slice(&base64_url[start..p]);
54 start = p + 1;
55
56 base64.push(47);
57 },
58 _ => (),
59 }
60
61 p += 1;
62 }
63
64 base64.extend_from_slice(&base64_url[start..p]);
65
66 base64.resize(new_size, 61);
67
68 Cow::from(base64)
69 } else {
70 let mut p = 0;
71
72 let first = loop {
73 if p == length {
74 return Cow::from(base64_url);
75 }
76
77 let e = base64_url[p];
78
79 match e {
80 45 => {
81 break 43;
82 },
83 95 => {
84 break 47;
85 },
86 _ => (),
87 }
88
89 p += 1;
90 };
91
92 let mut base64 = Vec::with_capacity(base64_url.len());
93
94 base64.extend_from_slice(&base64_url[..p]);
95 base64.push(first);
96
97 p += 1;
98
99 let mut start = p;
100
101 loop {
102 if p == length {
103 break;
104 }
105
106 let e = base64_url[p];
107
108 match e {
109 45 => {
110 base64.extend_from_slice(&base64_url[start..p]);
111 start = p + 1;
112
113 base64.push(43);
114 },
115 95 => {
116 base64.extend_from_slice(&base64_url[start..p]);
117 start = p + 1;
118
119 base64.push(47);
120 },
121 _ => (),
122 }
123
124 p += 1;
125 }
126
127 base64.extend_from_slice(&base64_url[start..p]);
128
129 Cow::from(base64)
130 }
131}
132
133#[inline]
135pub fn unescape_in_place(base64_url: &mut String) -> &str {
136 let v = unsafe { base64_url.as_mut_vec() };
137
138 unescape_vec_in_place(v);
139
140 base64_url.as_str()
141}
142
143#[inline]
145pub fn unescape_vec_in_place(base64_url: &mut Vec<u8>) -> &[u8] {
146 let base64 = unescape_u8_slice_try_in_place(base64_url.as_mut_slice());
147
148 if let Cow::Owned(mut base64) = base64 {
149 swap(base64_url, &mut base64);
150 }
151
152 base64_url.as_slice()
153}
154
155#[inline]
157pub fn unescape_u8_slice_try_in_place<S: ?Sized + AsMut<[u8]>>(
158 base64_url: &mut S,
159) -> Cow<'_, [u8]> {
160 let base64_url = base64_url.as_mut();
161
162 for n in base64_url.iter_mut() {
163 match *n {
164 45 => *n = 43,
165 95 => *n = 47,
166 _ => (),
167 }
168 }
169
170 let padding = base64_url.len() & 0b11;
171
172 if padding > 0 {
173 let mut base64_url_vec = base64_url.to_vec();
174
175 let new_size = base64_url.len() + (4 - padding);
176
177 base64_url_vec.resize(new_size, 61);
178
179 Cow::from(base64_url_vec)
180 } else {
181 Cow::from(base64_url as &[u8])
182 }
183}
184
185#[deprecated(since = "1.3.0", note = "Please use the `unescape_in_place` function instead")]
186#[inline]
188pub fn unsafe_unescape<S: Into<String>>(base64_url: S) -> String {
189 let mut base64_url = base64_url.into();
190
191 unescape_in_place(&mut base64_url);
192
193 base64_url
194}
195
196#[deprecated(since = "1.3.0", note = "Please use the `unescape_in_place` function instead")]
197#[inline]
199pub fn unsafe_unescape_string(base64_url: &mut String) {
200 unescape_in_place(base64_url);
201}
202
203#[deprecated(since = "1.3.0", note = "Please use the `unescape_vec_in_place` function instead")]
204pub fn unsafe_unescape_vec(base64_url: &mut Vec<u8>) {
206 unescape_vec_in_place(base64_url);
207}
208
209#[deprecated(
210 since = "1.3.0",
211 note = "Please use the `unescape_u8_slice_try_in_place` function instead"
212)]
213pub fn unsafe_unescape_u8_slice<S: ?Sized + AsMut<[u8]>>(base64_url: &mut S) -> Cow<'_, [u8]> {
215 unescape_u8_slice_try_in_place(base64_url)
216}