use core::fmt;
#[cfg(feature = "alloc")]
use alloc::borrow::Cow;
#[cfg(feature = "alloc")]
use alloc::string::{FromUtf8Error, String};
use crate::parser::str::strip_decode_xdigits2;
#[inline]
#[must_use]
pub fn decode_whatwg_bytes(bytes: &[u8]) -> PercentDecodedWhatwgBytes<'_> {
PercentDecodedWhatwgBytes::from_raw(bytes)
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PercentDecodedWhatwgBytes<'a> {
not_yet_decoded: &'a [u8],
}
impl<'a> PercentDecodedWhatwgBytes<'a> {
#[inline]
#[must_use]
fn from_raw(not_yet_decoded: &'a [u8]) -> Self {
Self { not_yet_decoded }
}
#[inline]
#[must_use]
pub fn not_yet_decoded(&self) -> &'a [u8] {
self.not_yet_decoded
}
#[must_use]
fn try_non_allocating_decode(&self) -> Option<(usize, u8, &'a [u8])> {
let mut len_before_pct;
let mut rest = self.not_yet_decoded;
while !rest.is_empty() {
#[cfg(feature = "memchr")]
let pct_pos = memchr::memchr(b'%', rest);
#[cfg(not(feature = "memchr"))]
let pct_pos = rest.iter().position(|&b| b == b'%');
let after_pct;
(len_before_pct, after_pct) = match pct_pos {
None => return None,
Some(pos) => (pos, &rest[(pos + 1)..]),
};
let decoded;
(decoded, rest) = strip_decode_xdigits2(after_pct);
if let Some(decoded) = decoded {
return Some((len_before_pct, decoded, rest));
}
rest = after_pct;
}
None
}
#[inline]
#[must_use]
pub fn to_bytes(&self) -> Option<&'a [u8]> {
match self.try_non_allocating_decode() {
None => Some(self.not_yet_decoded),
_ => None,
}
}
#[cfg(feature = "alloc")]
#[inline]
#[must_use]
pub fn into_bytes(&self) -> Cow<'a, [u8]> {
use crate::parser::str::find_split_hole;
let (mut result, mut rest) = match self.try_non_allocating_decode() {
Some((prefix_len, decoded, rest)) => {
let mut prefix = alloc::vec::Vec::from(&self.not_yet_decoded[..prefix_len]);
prefix.push(decoded);
(prefix, rest)
}
None => return Cow::Borrowed(self.not_yet_decoded),
};
while !rest.is_empty() {
let after_pct = if let Some((no_pct, after_pct)) = find_split_hole(rest, b'%') {
result.extend(no_pct);
after_pct
} else {
result.extend(core::mem::take(&mut rest));
break;
};
let decoded;
(decoded, rest) = strip_decode_xdigits2(after_pct);
result.extend(decoded);
}
Cow::Owned(result)
}
#[cfg(feature = "alloc")]
#[inline]
pub fn into_string(&self) -> Result<String, FromUtf8Error> {
String::from_utf8(self.into_bytes().into_owned())
}
#[inline]
#[must_use]
pub fn bytes_fragments(&self) -> PercentDecodedBytesFragments<'a> {
PercentDecodedBytesFragments {
rest: self.not_yet_decoded,
}
}
}
impl fmt::Debug for PercentDecodedWhatwgBytes<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("PercentDecodedWhatwgBytes");
match core::str::from_utf8(self.not_yet_decoded) {
Ok(s) => f.field("not_yet_decoded", &s),
Err(_) => f.field("not_yet_decoded", &self.not_yet_decoded),
};
f.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DecodedFragment<'a> {
Direct(&'a [u8]),
DecodedByte(u8),
StrayPercent,
}
#[derive(Clone)]
pub struct PercentDecodedBytesFragments<'a> {
rest: &'a [u8],
}
impl fmt::Debug for PercentDecodedBytesFragments<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("PercentDecodedBytesFragments");
match core::str::from_utf8(self.rest) {
Ok(s) => f.field("rest", &s),
Err(_) => f.field("rest", &self.rest),
};
f.finish()
}
}
impl<'a> PercentDecodedBytesFragments<'a> {
#[inline]
#[must_use]
pub fn not_yet_decoded(&self) -> &'a [u8] {
self.rest
}
}
impl<'a> Iterator for PercentDecodedBytesFragments<'a> {
type Item = DecodedFragment<'a>;
fn next(&mut self) -> Option<Self::Item> {
let mut rest = self.rest;
match rest {
[] => None,
[b'%', after_pct @ ..] => {
let decoded;
(decoded, rest) = strip_decode_xdigits2(after_pct);
if let Some(decoded) = decoded {
self.rest = rest;
Some(DecodedFragment::DecodedByte(decoded))
} else {
self.rest = after_pct;
Some(DecodedFragment::StrayPercent)
}
}
[_, after_first @ ..] => {
#[cfg(feature = "memchr")]
let pct_pos_minus_one = memchr::memchr(b'%', after_first);
#[cfg(not(feature = "memchr"))]
let pct_pos_minus_one = after_first.iter().position(|&b| b == b'%');
let before_pct;
(before_pct, self.rest) = match pct_pos_minus_one {
None => (rest, &rest[rest.len()..]),
Some(pct_pos_minus_one) => rest.split_at(pct_pos_minus_one + 1),
};
Some(DecodedFragment::Direct(before_pct))
}
}
}
}