base64_url/
unescape.rs

1use alloc::{borrow::Cow, string::String, vec::Vec};
2use core::{mem::swap, str::from_utf8_unchecked};
3
4/// Unescape a Base64-URL string to a Base64 string. The conversion is not concerning with Base64 decoding. You need to make sure the input string is a correct Base64-URL string by yourself.
5#[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
23/// Unescape Base64-URL data to Base64 data. The conversion is not concerning with Base64 decoding. You need to make sure the input Base64-URL data is correct by yourself.
24pub 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/// In-place unescape a Base64-URL string to a Base64 string. It is unsafe because the conversion is not concerning with Base64 decoding. You need to make sure the input string is a correct Base64-URL string by yourself.
134#[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/// In-place unescape Base64-URL data to Base64 data. It is unsafe because the conversion is not concerning with Base64 decoding. You need to make sure the input Base64-URL data is correct by yourself.
144#[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/// Unescape Base64-URL data to Base64 data and try to do it in-place. It is unsafe because the conversion is not concerning with Base64 decoding. You need to make sure the input Base64-URL data is correct by yourself.
156#[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/// Unescape a Base64-URL string to a Base64 string. It is unsafe because the conversion is not concerning with Base64 decoding. You need to make sure the input string is a correct Base64-URL string by yourself.
187#[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/// In-place unescape a Base64-URL string to a Base64 string. It is unsafe because the conversion is not concerning with Base64 decoding. You need to make sure the input string is a correct Base64-URL string by yourself.
198#[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")]
204/// In-place unescape Base64-URL data to Base64 data. It is unsafe because the conversion is not concerning with Base64 decoding. You need to make sure the input Base64-URL data is correct by yourself.
205pub 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)]
213/// Unescape Base64-URL data to Base64 data. It is unsafe because the conversion is not concerning with Base64 decoding. You need to make sure the input Base64-URL data is correct by yourself.
214pub 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}