#![deny(unsafe_op_in_unsafe_fn)]
use super::*;
use core::clone::CloneToUninit;
use core::borrow::{Borrow};
use core::{cmp, fmt};
use alloc::borrow::Cow;
use core::hash::Hash;
use core::iter::FusedIterator;
impl<'a> Prefix<'a> {
#[inline]
pub(crate) fn len(&self) -> usize {
use self::Prefix::*;
fn os_str_len(s: &DaemonicOsStr) -> usize {
s.as_encoded_bytes().len()
}
match *self {
Verbatim(x) => 4 + os_str_len(x),
VerbatimUNC(x, y) => {
8 + os_str_len(x) + if os_str_len(y) > 0 { 1 + os_str_len(y) } else { 0 }
}
VerbatimDisk(_) => 6,
UNC(x, y) => 2 + os_str_len(x) + if os_str_len(y) > 0 { 1 + os_str_len(y) } else { 0 },
DeviceNS(x) => 4 + os_str_len(x),
Disk(_) => 2,
}
}
#[inline]
#[must_use]
pub fn is_verbatim(&self) -> bool {
use self::Prefix::*;
matches!(*self, Verbatim(_) | VerbatimDisk(_) | VerbatimUNC(..))
}
#[inline]
pub(crate) fn is_drive(&self) -> bool {
matches!(*self, Prefix::Disk(_))
}
#[inline]
fn has_implicit_root(&self) -> bool {
!self.is_drive()
}
}
#[must_use]
pub fn is_separator(c: char) -> bool {
c.is_ascii() && is_sep_byte(c as u8)
}
#[cfg_attr(not(test), rustc_diagnostic_item = "path_main_separator")]
pub const MAIN_SEPARATOR: char = '/';
pub const MAIN_SEPARATOR_STR: &str = "/";
pub(crate) fn iter_after<'a, 'b, I, J>(mut iter: I, mut prefix: J) -> Option<I>
where
I: Iterator<Item=Component<'a>> + Clone,
J: Iterator<Item=Component<'b>>,
{
loop {
let mut iter_next = iter.clone();
match (iter_next.next(), prefix.next()) {
(Some(ref x), Some(ref y)) if x == y => (),
(Some(_), Some(_)) => return None,
(Some(_), None) => return Some(iter),
(None, None) => return Some(iter),
(None, Some(_)) => return None,
}
iter = iter_next;
}
}
pub(crate) fn has_physical_root(s: &[u8], prefix: Option<Prefix<'_>>) -> bool {
let path = if let Some(p) = prefix { &s[p.len()..] } else { s };
!path.is_empty() && is_sep_byte(path[0])
}
pub(crate) fn rsplit_file_at_dot(file: &DaemonicOsStr) -> (Option<&DaemonicOsStr>, Option<&DaemonicOsStr>) {
if file.as_encoded_bytes() == b".." {
return (Some(file), None);
}
let mut iter = file.as_encoded_bytes().rsplitn(2, |b| *b == b'.');
let after = iter.next();
let before = iter.next();
if before == Some(b"") {
(Some(file), None)
} else {
unsafe {
(
before.map(|s| DaemonicOsStr::from_encoded_bytes_unchecked(s)),
after.map(|s| DaemonicOsStr::from_encoded_bytes_unchecked(s)),
)
}
}
}
pub(crate) fn split_file_at_dot(file: &DaemonicOsStr) -> (&DaemonicOsStr, Option<&DaemonicOsStr>) {
let slice = file.as_encoded_bytes();
if slice == b".." {
return (file, None);
}
let i = match slice[1..].iter().position(|b| *b == b'.') {
Some(i) => i + 1,
None => return (file, None),
};
let before = &slice[..i];
let after = &slice[i + 1..];
unsafe {
(
DaemonicOsStr::from_encoded_bytes_unchecked(before),
Some(DaemonicOsStr::from_encoded_bytes_unchecked(after)),
)
}
}
pub(crate) fn validate_extension(extension: &DaemonicOsStr) {
for &b in extension.as_encoded_bytes() {
if is_sep_byte(b) {
panic!("extension cannot contain path separators: {extension:?}");
}
}
}
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub(crate) enum State {
Prefix = 0, StartDir = 1, Body = 2, Done = 3,
}
#[derive(Copy, Clone, Eq, Debug)]
pub struct PrefixComponent<'a> {
raw: &'a DaemonicOsStr,
pub(crate) parsed: Prefix<'a>,
}
impl<'a> DaemonicHashable for PrefixComponent<'a> {
fn declare_hashable<GLASS: DaemonicHasher<GLASS> + Glass<GLASS>>(&self, state: &mut GLASS) {
"PrefixComponent".declare_hashable(state);
self.raw.declare_hashable(state);
self.parsed.declare_hashable(state);
}
}
impl<'a> PrefixComponent<'a> {
#[must_use]
#[inline]
pub fn kind(&self) -> Prefix<'a> {
self.parsed
}
#[must_use]
#[inline]
pub fn as_os_str(&self) -> &'a DaemonicOsStr {
self.raw
}
}
impl<'a> PartialEq for PrefixComponent<'a> {
#[inline]
fn eq(&self, other: &PrefixComponent<'a>) -> bool {
self.parsed == other.parsed
}
}
impl<'a> PartialOrd for PrefixComponent<'a> {
#[inline]
fn partial_cmp(&self, other: &PrefixComponent<'a>) -> Option<cmp::Ordering> {
PartialOrd::partial_cmp(&self.parsed, &other.parsed)
}
}
impl Ord for PrefixComponent<'_> {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
Ord::cmp(&self.parsed, &other.parsed)
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum Component<'a> {
Prefix(PrefixComponent<'a>),
RootDir,
CurDir,
ParentDir,
Normal(&'a DaemonicOsStr),
}
impl<'a> Component<'a> {
#[must_use = "`self` will be dropped if the result is not used"]
pub fn as_os_str(self) -> &'a DaemonicOsStr {
match self {
Component::Prefix(p) => p.as_os_str(),
Component::RootDir => DaemonicOsStr::new(MAIN_SEP_STR),
Component::CurDir => DaemonicOsStr::new("."),
Component::ParentDir => DaemonicOsStr::new(".."),
Component::Normal(path) => path,
}
}
}
impl AsRef<DaemonicOsStr> for Component<'_> {
#[inline]
fn as_ref(&self) -> &DaemonicOsStr {
self.as_os_str()
}
}
impl AsRef<DaemonicPath> for Component<'_> {
#[inline]
fn as_ref(&self) -> &DaemonicPath {
DaemonicPath::new(Component::as_os_str(*self))
}
}
#[derive(Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Components<'a> {
pub(crate) path: &'a [u8],
pub(crate) prefix: Option<Prefix<'a>>,
pub(crate) has_physical_root: bool,
pub(crate) front: State,
pub(crate) back: State,
}
#[derive(Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Iter<'a> {
pub(crate) inner: Components<'a>,
}
impl fmt::Debug for Components<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct DebugHelper<'a>(&'a DaemonicPath);
impl fmt::Debug for DebugHelper<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.0.components()).finish()
}
}
f.debug_tuple("Components").field(&DebugHelper(self.as_path())).finish()
}
}
impl<'a> Components<'a> {
#[inline]
pub(crate) fn prefix_len(&self) -> usize {
self.prefix.as_ref().map(Prefix::len).unwrap_or(0)
}
#[inline]
pub(crate) fn prefix_verbatim(&self) -> bool {
self.prefix.as_ref().map(Prefix::is_verbatim).unwrap_or(false)
}
#[inline]
pub(crate) fn prefix_remaining(&self) -> usize {
if self.front == State::Prefix { self.prefix_len() } else { 0 }
}
#[inline]
fn len_before_body(&self) -> usize {
let root = if self.front <= State::StartDir && self.has_physical_root { 1 } else { 0 };
let cur_dir = if self.front <= State::StartDir && self.include_cur_dir() { 1 } else { 0 };
self.prefix_remaining() + root + cur_dir
}
#[inline]
fn finished(&self) -> bool {
self.front == State::Done || self.back == State::Done || self.front > self.back
}
#[inline]
fn is_sep_byte(&self, b: u8) -> bool {
if self.prefix_verbatim() { is_verbatim_sep(b) } else { is_sep_byte(b) }
}
#[must_use]
pub fn as_path(&self) -> &'a DaemonicPath {
let mut comps = self.clone();
if comps.front == State::Body {
comps.trim_left();
}
if comps.back == State::Body {
comps.trim_right();
}
unsafe { DaemonicPath::from_u8_slice(comps.path) }
}
pub(crate) fn has_root(&self) -> bool {
if self.has_physical_root {
return true;
}
if let Some(p) = self.prefix {
if p.has_implicit_root() {
return true;
}
}
false
}
fn include_cur_dir(&self) -> bool {
if self.has_root() {
return false;
}
let mut iter = self.path[self.prefix_remaining()..].iter();
match (iter.next(), iter.next()) {
(Some(&b'.'), None) => true,
(Some(&b'.'), Some(&b)) => self.is_sep_byte(b),
_ => false,
}
}
unsafe fn parse_single_component<'b>(&self, comp: &'b [u8]) -> Option<Component<'b>> {
match comp {
b"." if self.prefix_verbatim() => Some(Component::CurDir),
b"." => None, b".." => Some(Component::ParentDir),
b"" => None,
_ => Some(Component::Normal(unsafe { DaemonicOsStr::from_encoded_bytes_unchecked(comp) })),
}
}
fn parse_next_component(&self) -> (usize, Option<Component<'a>>) {
debug_assert!(self.front == State::Body);
let (extra, comp) = match self.path.iter().position(|b| self.is_sep_byte(*b)) {
None => (0, self.path),
Some(i) => (1, &self.path[..i]),
};
(comp.len() + extra, unsafe { self.parse_single_component(comp) })
}
fn parse_next_component_back(&self) -> (usize, Option<Component<'a>>) {
debug_assert!(self.back == State::Body);
let start = self.len_before_body();
let (extra, comp) = match self.path[start..].iter().rposition(|b| self.is_sep_byte(*b)) {
None => (0, &self.path[start..]),
Some(i) => (1, &self.path[start + i + 1..]),
};
(comp.len() + extra, unsafe { self.parse_single_component(comp) })
}
fn trim_left(&mut self) {
while !self.path.is_empty() {
let (size, comp) = self.parse_next_component();
if comp.is_some() {
return;
} else {
self.path = &self.path[size..];
}
}
}
fn trim_right(&mut self) {
while self.path.len() > self.len_before_body() {
let (size, comp) = self.parse_next_component_back();
if comp.is_some() {
return;
} else {
self.path = &self.path[..self.path.len() - size];
}
}
}
}
impl AsRef<DaemonicPath> for Components<'_> {
#[inline]
fn as_ref(&self) -> &DaemonicPath {
self.as_path()
}
}
impl AsRef<DaemonicOsStr> for Components<'_> {
#[inline]
fn as_ref(&self) -> &DaemonicOsStr {
self.as_path().as_os_str()
}
}
impl fmt::Debug for Iter<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct DebugHelper<'a>(&'a DaemonicPath);
impl fmt::Debug for DebugHelper<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.0.iter()).finish()
}
}
f.debug_tuple("Iter").field(&DebugHelper(self.as_path())).finish()
}
}
impl<'a> Iter<'a> {
#[must_use]
#[inline]
pub fn as_path(&self) -> &'a DaemonicPath {
self.inner.as_path()
}
}
impl AsRef<DaemonicPath> for Iter<'_> {
#[inline]
fn as_ref(&self) -> &DaemonicPath {
self.as_path()
}
}
impl AsRef<DaemonicOsStr> for Iter<'_> {
#[inline]
fn as_ref(&self) -> &DaemonicOsStr {
self.as_path().as_os_str()
}
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a DaemonicOsStr;
#[inline]
fn next(&mut self) -> Option<&'a DaemonicOsStr> {
self.inner.next().map(Component::as_os_str)
}
}
impl<'a> DoubleEndedIterator for Iter<'a> {
#[inline]
fn next_back(&mut self) -> Option<&'a DaemonicOsStr> {
self.inner.next_back().map(Component::as_os_str)
}
}
impl FusedIterator for Iter<'_> {}
impl<'a> Iterator for Components<'a> {
type Item = Component<'a>;
fn next(&mut self) -> Option<Component<'a>> {
while !self.finished() {
match self.front {
State::Prefix if self.prefix_len() > 0 => {
self.front = State::StartDir;
debug_assert!(self.prefix_len() <= self.path.len());
let raw = &self.path[..self.prefix_len()];
self.path = &self.path[self.prefix_len()..];
return Some(Component::Prefix(PrefixComponent {
raw: unsafe { DaemonicOsStr::from_encoded_bytes_unchecked(raw) },
parsed: self.prefix.unwrap(),
}));
}
State::Prefix => {
self.front = State::StartDir;
}
State::StartDir => {
self.front = State::Body;
if self.has_physical_root {
debug_assert!(!self.path.is_empty());
self.path = &self.path[1..];
return Some(Component::RootDir);
} else if let Some(p) = self.prefix {
if p.has_implicit_root() && !p.is_verbatim() {
return Some(Component::RootDir);
}
} else if self.include_cur_dir() {
debug_assert!(!self.path.is_empty());
self.path = &self.path[1..];
return Some(Component::CurDir);
}
}
State::Body if !self.path.is_empty() => {
let (size, comp) = self.parse_next_component();
self.path = &self.path[size..];
if comp.is_some() {
return comp;
}
}
State::Body => {
self.front = State::Done;
}
State::Done => unreachable!(),
}
}
None
}
}
impl<'a> DoubleEndedIterator for Components<'a> {
fn next_back(&mut self) -> Option<Component<'a>> {
while !self.finished() {
match self.back {
State::Body if self.path.len() > self.len_before_body() => {
let (size, comp) = self.parse_next_component_back();
self.path = &self.path[..self.path.len() - size];
if comp.is_some() {
return comp;
}
}
State::Body => {
self.back = State::StartDir;
}
State::StartDir => {
self.back = State::Prefix;
if self.has_physical_root {
self.path = &self.path[..self.path.len() - 1];
return Some(Component::RootDir);
} else if let Some(p) = self.prefix {
if p.has_implicit_root() && !p.is_verbatim() {
return Some(Component::RootDir);
}
} else if self.include_cur_dir() {
self.path = &self.path[..self.path.len() - 1];
return Some(Component::CurDir);
}
}
State::Prefix if self.prefix_len() > 0 => {
self.back = State::Done;
return Some(Component::Prefix(PrefixComponent {
raw: unsafe { DaemonicOsStr::from_encoded_bytes_unchecked(self.path) },
parsed: self.prefix.unwrap(),
}));
}
State::Prefix => {
self.back = State::Done;
return None;
}
State::Done => unreachable!(),
}
}
None
}
}
impl FusedIterator for Components<'_> {}
impl<'a> PartialEq for Components<'a> {
#[inline]
fn eq(&self, other: &Components<'a>) -> bool {
let Components { path: _, front: _, back: _, has_physical_root: _, prefix: _ } = self;
if self.path.len() == other.path.len()
&& self.front == other.front
&& self.back == State::Body
&& other.back == State::Body
&& self.prefix_verbatim() == other.prefix_verbatim()
{
if self.path == other.path {
return true;
}
}
Iterator::eq(self.clone().rev(), other.clone().rev())
}
}
impl Eq for Components<'_> {}
impl<'a> PartialOrd for Components<'a> {
#[inline]
fn partial_cmp(&self, other: &Components<'a>) -> Option<cmp::Ordering> {
Some(compare_components(self.clone(), other.clone()))
}
}
impl Ord for Components<'_> {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
compare_components(self.clone(), other.clone())
}
}
fn compare_components(mut left: Components<'_>, mut right: Components<'_>) -> cmp::Ordering {
if left.prefix.is_none() && right.prefix.is_none() && left.front == right.front {
let first_difference = match left.path.iter().zip(right.path).position(|(&a, &b)| a != b) {
None if left.path.len() == right.path.len() => return cmp::Ordering::Equal,
None => left.path.len().min(right.path.len()),
Some(diff) => diff,
};
if let Some(previous_sep) =
left.path[..first_difference].iter().rposition(|&b| left.is_sep_byte(b))
{
let mismatched_component_start = previous_sep + 1;
left.path = &left.path[mismatched_component_start..];
left.front = State::Body;
right.path = &right.path[mismatched_component_start..];
right.front = State::Body;
}
}
Iterator::cmp(left, right)
}
#[derive(Copy, Clone, Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Ancestors<'a> {
pub(crate) next: Option<&'a DaemonicPath>,
}
impl<'a> Iterator for Ancestors<'a> {
type Item = &'a DaemonicPath;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let next = self.next;
self.next = next.and_then(DaemonicPath::parent);
next
}
}
impl FusedIterator for Ancestors<'_> {}