use std::borrow::Cow;
use std::mem::swap;
use std::str::from_utf8_unchecked;
#[inline]
pub fn unescape<S: ?Sized + AsRef<str>>(base64_url: &S) -> Cow<str> {
let base64_url = base64_url.as_ref();
match unescape_u8_slice(base64_url) {
Cow::Owned(base64) => {
let base64 = unsafe { String::from_utf8_unchecked(base64) };
Cow::from(base64)
}
Cow::Borrowed(base64) => {
let base64 = unsafe { from_utf8_unchecked(base64) };
Cow::from(base64)
}
}
}
pub fn unescape_u8_slice<S: ?Sized + AsRef<[u8]>>(base64_url: &S) -> Cow<[u8]> {
let base64_url = base64_url.as_ref();
let length = base64_url.len();
let padding = length & 0b11;
if padding > 0 {
let new_size = base64_url.len() + (4 - padding);
let mut base64 = Vec::with_capacity(new_size);
let mut p = 0;
let mut start = 0;
loop {
if p == length {
break;
}
let e = base64_url[p];
match e {
45 => {
base64.extend_from_slice(&base64_url[start..p]);
start = p + 1;
base64.push(43);
}
95 => {
base64.extend_from_slice(&base64_url[start..p]);
start = p + 1;
base64.push(47);
}
_ => (),
}
p += 1;
}
base64.extend_from_slice(&base64_url[start..p]);
base64.resize(new_size, 61);
Cow::from(base64)
} else {
let mut p = 0;
let first = loop {
if p == length {
return Cow::from(base64_url);
}
let e = base64_url[p];
match e {
45 => {
break 43;
}
95 => {
break 47;
}
_ => (),
}
p += 1;
};
let mut base64 = Vec::with_capacity(base64_url.len());
base64.extend_from_slice(&base64_url[..p]);
base64.push(first);
let mut start = p;
p += 1;
loop {
if p == length {
break;
}
let e = base64_url[p];
match e {
45 => {
base64.extend_from_slice(&base64_url[start..p]);
start = p + 1;
base64.push(43);
}
95 => {
base64.extend_from_slice(&base64_url[start..p]);
start = p + 1;
base64.push(47);
}
_ => (),
}
p += 1;
}
base64.extend_from_slice(&base64_url[start..p]);
Cow::from(base64)
}
}
#[inline]
pub fn unescape_in_place(base64_url: &mut String) -> &str {
let v = unsafe { base64_url.as_mut_vec() };
unescape_vec_in_place(v);
base64_url.as_str()
}
#[inline]
pub fn unescape_vec_in_place(base64_url: &mut Vec<u8>) -> &[u8] {
let base64 = unescape_u8_slice_try_in_place(base64_url.as_mut_slice());
if let Cow::Owned(mut base64) = base64 {
swap(base64_url, &mut base64);
}
base64_url.as_slice()
}
#[inline]
pub fn unescape_u8_slice_try_in_place<S: ?Sized + AsMut<[u8]>>(base64_url: &mut S) -> Cow<[u8]> {
let base64_url = base64_url.as_mut();
for n in base64_url.iter_mut() {
match *n {
45 => *n = 43,
95 => *n = 47,
_ => (),
}
}
let padding = base64_url.len() & 0b11;
if padding > 0 {
let mut base64_url_vec = base64_url.to_vec();
let new_size = base64_url.len() + (4 - padding);
base64_url_vec.resize(new_size, 61);
Cow::from(base64_url_vec)
} else {
Cow::from(base64_url as &[u8])
}
}
#[deprecated(since = "1.3.0", note = "Please use the `unescape_in_place` function instead")]
#[inline]
pub fn unsafe_unescape<S: Into<String>>(base64_url: S) -> String {
let mut base64_url = base64_url.into();
unescape_in_place(&mut base64_url);
base64_url
}
#[deprecated(since = "1.3.0", note = "Please use the `unescape_in_place` function instead")]
#[inline]
pub fn unsafe_unescape_string(base64_url: &mut String) {
unescape_in_place(base64_url);
}
#[deprecated(since = "1.3.0", note = "Please use the `unescape_vec_in_place` function instead")]
pub fn unsafe_unescape_vec(base64_url: &mut Vec<u8>) {
unescape_vec_in_place(base64_url);
}
#[deprecated(
since = "1.3.0",
note = "Please use the `unescape_u8_slice_try_in_place` function instead"
)]
pub fn unsafe_unescape_u8_slice<S: ?Sized + AsMut<[u8]>>(base64_url: &mut S) -> Cow<[u8]> {
unescape_u8_slice_try_in_place(base64_url)
}