mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
perf: lock-free hashing for string interns (#3091)
This commit is contained in:
parent
84907297a0
commit
0054cf0b87
23 changed files with 204 additions and 131 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
|
@ -364,9 +364,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.1"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
|
|
@ -1967,9 +1967,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
version = "0.38.1"
|
version = "0.38.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9845d9dccf565065824e69f9f235fafba1587031eda353c1f1561cd6a6be78f4"
|
checksum = "d200a41a7797e6461bd04e4e95c3347053a731c32c87f066f2f0dda22dbdbba8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
@ -2153,7 +2153,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.2.16",
|
"getrandom 0.2.16",
|
||||||
"libredox",
|
"libredox",
|
||||||
"thiserror 2.0.15",
|
"thiserror 2.0.16",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2574,11 +2574,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.15"
|
version = "2.0.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850"
|
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl 2.0.15",
|
"thiserror-impl 2.0.16",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2594,9 +2594,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "2.0.15"
|
version = "2.0.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0"
|
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -3153,11 +3153,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-util"
|
name = "winapi-util"
|
||||||
version = "0.1.9"
|
version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -78,13 +78,13 @@ impl Cd {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
let Ok(url) = UrlBuf::try_from(s).map(expand_url) else { return };
|
let Ok(url) = UrlBuf::try_from(s).map(expand_url) else { return };
|
||||||
|
|
||||||
let Ok(file) = File::new(url.clone()).await else { return };
|
let Ok(file) = File::new(&url).await else { return };
|
||||||
if file.is_dir() {
|
if file.is_dir() {
|
||||||
return MgrProxy::cd(&url);
|
return MgrProxy::cd(&url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(p) = url.parent_url() {
|
if let Some(p) = url.parent_url() {
|
||||||
FilesOp::Upserting(p, [(url.urn_owned(), file)].into()).emit();
|
FilesOp::Upserting(p.into(), [(url.urn_owned(), file)].into()).emit();
|
||||||
}
|
}
|
||||||
MgrProxy::reveal(&url);
|
MgrProxy::reveal(&url);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ impl Create {
|
||||||
provider::create_dir_all(&new).await?;
|
provider::create_dir_all(&new).await?;
|
||||||
} else if let Some(real) = realname(&new).await {
|
} else if let Some(real) = realname(&new).await {
|
||||||
ok_or_not_found(provider::remove_file(&new).await)?;
|
ok_or_not_found(provider::remove_file(&new).await)?;
|
||||||
FilesOp::Deleting(parent.clone(), [UrnBuf::from(real)].into()).emit();
|
FilesOp::Deleting(parent.to_owned(), [UrnBuf::from(real)].into()).emit();
|
||||||
provider::create(&new).await?;
|
provider::create(&new).await?;
|
||||||
} else {
|
} else {
|
||||||
provider::create_dir_all(&parent).await.ok();
|
provider::create_dir_all(&parent).await.ok();
|
||||||
|
|
@ -56,8 +56,8 @@ impl Create {
|
||||||
provider::create(&new).await?;
|
provider::create(&new).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(f) = File::new(new.clone()).await {
|
if let Ok(f) = File::new(&new).await {
|
||||||
FilesOp::Upserting(parent, [(f.urn_owned(), f)].into()).emit();
|
FilesOp::Upserting(parent.into(), [(f.urn_owned(), f)].into()).emit();
|
||||||
MgrProxy::reveal(&new)
|
MgrProxy::reveal(&new)
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ impl Actor for Open {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut files = Vec::with_capacity(todo.len());
|
let mut files = Vec::with_capacity(todo.len());
|
||||||
for i in todo {
|
for i in todo {
|
||||||
if let Ok(f) = File::new(targets[i].0.clone()).await {
|
if let Ok(f) = File::new(&targets[i].0).await {
|
||||||
files.push(f);
|
files.push(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,15 +69,15 @@ impl Rename {
|
||||||
|
|
||||||
if let Some(o) = overwritten {
|
if let Some(o) = overwritten {
|
||||||
ok_or_not_found(provider::rename(&p_new.join(&o), &new).await)?;
|
ok_or_not_found(provider::rename(&p_new.join(&o), &new).await)?;
|
||||||
FilesOp::Deleting(p_new.clone(), [UrnBuf::from(o)].into()).emit();
|
FilesOp::Deleting(p_new.to_owned(), [UrnBuf::from(o)].into()).emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
let file = File::new(new.clone()).await?;
|
let file = File::new(&new).await?;
|
||||||
if p_new == p_old {
|
if p_new == p_old {
|
||||||
FilesOp::Upserting(p_old, [(n_old, file)].into()).emit();
|
FilesOp::Upserting(p_old.into(), [(n_old, file)].into()).emit();
|
||||||
} else {
|
} else {
|
||||||
FilesOp::Deleting(p_old, [n_old].into()).emit();
|
FilesOp::Deleting(p_old.into(), [n_old].into()).emit();
|
||||||
FilesOp::Upserting(p_new, [(n_new, file)].into()).emit();
|
FilesOp::Upserting(p_new.into(), [(n_new, file)].into()).emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
MgrProxy::reveal(&new);
|
MgrProxy::reveal(&new);
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ impl Actor for Reveal {
|
||||||
// If the child is not hovered, which means it doesn't exist,
|
// If the child is not hovered, which means it doesn't exist,
|
||||||
// create a dummy file
|
// create a dummy file
|
||||||
if !opt.no_dummy && tab.hovered().is_none_or(|f| &child != f.urn()) {
|
if !opt.no_dummy && tab.hovered().is_none_or(|f| &child != f.urn()) {
|
||||||
let op = FilesOp::Creating(parent, vec![File::from_dummy(opt.target, None)]);
|
let op = FilesOp::Creating(parent.into(), vec![File::from_dummy(opt.target, None)]);
|
||||||
tab.current.update_pub(tab.id, op);
|
tab.current.update_pub(tab.id, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ impl Boot {
|
||||||
};
|
};
|
||||||
|
|
||||||
if provider::metadata(&entry).await.is_ok_and(|m| m.is_file()) {
|
if provider::metadata(&entry).await.is_ok_and(|m| m.is_file()) {
|
||||||
(parent, child)
|
(parent.into(), child)
|
||||||
} else {
|
} else {
|
||||||
(entry, UrnBuf::default())
|
(entry, UrnBuf::default())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,15 @@ impl DerefMut for Linked {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Linked {
|
impl Linked {
|
||||||
pub fn from_dir<'a, 'b>(&'a self, url: &'b UrlBuf) -> Box<dyn Iterator<Item = &'a UrlBuf> + 'b>
|
pub fn from_dir<'a, 'b, T>(&'a self, url: T) -> Box<dyn Iterator<Item = &'a UrlBuf> + 'b>
|
||||||
where
|
where
|
||||||
'a: 'b,
|
'a: 'b,
|
||||||
|
T: Into<Url<'b>>,
|
||||||
{
|
{
|
||||||
|
let url = url.into();
|
||||||
if url.scheme.is_virtual() {
|
if url.scheme.is_virtual() {
|
||||||
Box::new(iter::empty())
|
Box::new(iter::empty())
|
||||||
} else if let Some(to) = self.get(url) {
|
} else if let Some(to) = self.get(&url) {
|
||||||
Box::new(self.iter().filter(move |(k, v)| *v == to && *k != url).map(|(k, _)| k))
|
Box::new(self.iter().filter(move |(k, v)| *v == to && *k != url).map(|(k, _)| k))
|
||||||
} else {
|
} else {
|
||||||
Box::new(self.iter().filter(move |(_, v)| *v == url).map(|(k, _)| k))
|
Box::new(self.iter().filter(move |(_, v)| *v == url).map(|(k, _)| k))
|
||||||
|
|
@ -34,7 +36,7 @@ impl Linked {
|
||||||
if url.scheme.is_virtual() {
|
if url.scheme.is_virtual() {
|
||||||
vec![]
|
vec![]
|
||||||
} else if let Some((parent, urn)) = url.pair() {
|
} else if let Some((parent, urn)) = url.pair() {
|
||||||
self.from_dir(&parent).map(|u| u.join(&urn)).collect()
|
self.from_dir(parent).map(|u| u.join(&urn)).collect()
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,9 +68,12 @@ impl Watcher {
|
||||||
if !watched.contains(&p) && !LINKED.read().from_dir(&p).any(|u| watched.contains(u)) {
|
if !watched.contains(&p) && !LINKED.read().from_dir(&p).any(|u| watched.contains(u)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
out_tx.send(u).ok();
|
if parents.contains(&p) {
|
||||||
if !parents.contains(&p) {
|
out_tx.send(u).ok();
|
||||||
out_tx.send(p.clone()).ok();
|
} else {
|
||||||
|
let p = p.to_owned();
|
||||||
|
out_tx.send(u).ok();
|
||||||
|
out_tx.send(p.to_owned()).ok();
|
||||||
parents.insert(p);
|
parents.insert(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -134,8 +137,8 @@ impl Watcher {
|
||||||
|
|
||||||
for u in urls {
|
for u in urls {
|
||||||
let Some((parent, urn)) = u.pair() else { continue };
|
let Some((parent, urn)) = u.pair() else { continue };
|
||||||
let Ok(file) = File::new(u).await else {
|
let Ok(file) = File::new(&u).await else {
|
||||||
ops.push(FilesOp::Deleting(parent, [urn].into()));
|
ops.push(FilesOp::Deleting(parent.into(), [urn].into()));
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -144,11 +147,11 @@ impl Watcher {
|
||||||
|| realname_unchecked(u, &mut cached).await.is_ok_and(|s| urn.as_urn() == s);
|
|| realname_unchecked(u, &mut cached).await.is_ok_and(|s| urn.as_urn() == s);
|
||||||
|
|
||||||
if !eq {
|
if !eq {
|
||||||
ops.push(FilesOp::Deleting(parent, [urn].into()));
|
ops.push(FilesOp::Deleting(parent.into(), [urn].into()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ops.push(FilesOp::Upserting(parent, [(urn, file)].into()));
|
ops.push(FilesOp::Upserting(parent.into(), [(urn, file)].into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
FilesOp::mutate(ops);
|
FilesOp::mutate(ops);
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ impl Selected {
|
||||||
self.inner.extend(urls.iter().enumerate().map(|(i, u)| (u.into(), now + i as u64)));
|
self.inner.extend(urls.iter().enumerate().map(|(i, u)| (u.into(), now + i as u64)));
|
||||||
|
|
||||||
for u in parents {
|
for u in parents {
|
||||||
*self.parents.entry(UrlBufCov(u)).or_insert(0) += self.inner.len() - len;
|
*self.parents.entry_ref(&UrlCov::new(u)).or_default() += self.inner.len() - len;
|
||||||
}
|
}
|
||||||
urls.len()
|
urls.len()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{ffi::OsStr, fs::{FileType, Metadata}, hash::{BuildHasher, Hash, Hasher}, ops::Deref};
|
use std::{ffi::OsStr, fs::{FileType, Metadata}, hash::{BuildHasher, Hash, Hasher}, ops::Deref};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use yazi_shared::url::{Uri, UrlBuf, Urn, UrnBuf};
|
use yazi_shared::url::{Uri, UrlBuf, UrlCow, Urn, UrnBuf};
|
||||||
|
|
||||||
use crate::{cha::Cha, provider};
|
use crate::{cha::Cha, provider};
|
||||||
|
|
||||||
|
|
@ -21,9 +21,10 @@ impl Deref for File {
|
||||||
|
|
||||||
impl File {
|
impl File {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub async fn new(url: UrlBuf) -> Result<Self> {
|
pub async fn new(url: impl Into<UrlCow<'_>>) -> Result<Self> {
|
||||||
|
let url = url.into();
|
||||||
let meta = provider::symlink_metadata(&url).await?;
|
let meta = provider::symlink_metadata(&url).await?;
|
||||||
Ok(Self::from_follow(url, meta).await)
|
Ok(Self::from_follow(url.into_owned(), meta).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,9 @@ impl FilesOp {
|
||||||
let Some(o_p) = o.parent_url() else { continue };
|
let Some(o_p) = o.parent_url() else { continue };
|
||||||
let Some(n_p) = n.url.parent_url() else { continue };
|
let Some(n_p) = n.url.parent_url() else { continue };
|
||||||
if o_p != n_p {
|
if o_p != n_p {
|
||||||
parents.entry(o_p).or_default().0.insert(o.urn_owned());
|
parents.entry_ref(&o_p).or_default().0.insert(o.urn_owned());
|
||||||
}
|
}
|
||||||
parents.entry(n_p).or_default().1.insert(n.urn_owned(), n);
|
parents.entry_ref(&n_p).or_default().1.insert(n.urn_owned(), n);
|
||||||
}
|
}
|
||||||
for (p, (o, n)) in parents {
|
for (p, (o, n)) in parents {
|
||||||
match (o.is_empty(), n.is_empty()) {
|
match (o.is_empty(), n.is_empty()) {
|
||||||
|
|
@ -124,7 +124,7 @@ impl FilesOp {
|
||||||
} else if maybe_exists(cwd).await {
|
} else if maybe_exists(cwd).await {
|
||||||
Self::IOErr(cwd.clone(), kind).emit();
|
Self::IOErr(cwd.clone(), kind).emit();
|
||||||
} else if let Some((p, n)) = cwd.pair() {
|
} else if let Some((p, n)) = cwd.pair() {
|
||||||
Self::Deleting(p, [n].into()).emit();
|
Self::Deleting(p.into(), [n].into()).emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||||
use yazi_fs::path::expand_url;
|
use yazi_fs::path::expand_url;
|
||||||
use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlCow}};
|
use yazi_shared::{event::CmdCow, url::{Url, UrlBuf, UrlCow}};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CdOpt {
|
pub struct CdOpt {
|
||||||
|
|
@ -33,6 +33,10 @@ impl From<(UrlBuf, CdSource)> for CdOpt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<(Url<'_>, CdSource)> for CdOpt {
|
||||||
|
fn from((target, source): (Url, CdSource)) -> Self { Self::from((target.to_owned(), source)) }
|
||||||
|
}
|
||||||
|
|
||||||
impl FromLua for CdOpt {
|
impl FromLua for CdOpt {
|
||||||
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ impl Prework {
|
||||||
|
|
||||||
let parent = buf[0].0.parent_url().unwrap();
|
let parent = buf[0].0.parent_url().unwrap();
|
||||||
FilesOp::Size(
|
FilesOp::Size(
|
||||||
parent,
|
parent.into(),
|
||||||
HashMap::from_iter(buf.into_iter().map(|(u, s)| (u.urn_owned(), s))),
|
HashMap::from_iter(buf.into_iter().map(|(u, s)| (u.urn_owned(), s))),
|
||||||
)
|
)
|
||||||
.emit();
|
.emit();
|
||||||
|
|
|
||||||
|
|
@ -85,16 +85,19 @@ impl LocBuf {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn zeroed(path: impl Into<PathBuf>) -> Self {
|
pub fn zeroed(path: impl Into<PathBuf>) -> Self {
|
||||||
let mut loc = Self::from(path.into());
|
let loc = Self::from(path.into());
|
||||||
(loc.uri, loc.urn) = (0, 0);
|
let Loc { inner, uri, urn } = Loc::zeroed(&loc.inner);
|
||||||
loc
|
|
||||||
|
debug_assert!(inner.as_os_str() == loc.inner.as_os_str());
|
||||||
|
Self { inner: loc.inner, uri, urn }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn floated(path: impl Into<PathBuf>, base: &Path) -> Self {
|
pub fn floated(path: impl Into<PathBuf>, base: &Path) -> Self {
|
||||||
let mut loc = Self::from(path.into());
|
let loc = Self::from(path.into());
|
||||||
loc.uri =
|
let Loc { inner, uri, urn } = Loc::floated(&loc.inner, base);
|
||||||
loc.inner.strip_prefix(base).expect("Loc must start with the given base").as_os_str().len();
|
|
||||||
loc
|
debug_assert!(inner.as_os_str() == loc.inner.as_os_str());
|
||||||
|
Self { inner: loc.inner, uri, urn }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -110,9 +110,25 @@ impl<'a> Loc<'a> {
|
||||||
Ok(loc)
|
Ok(loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn zeroed<T: AsRef<Path> + ?Sized>(path: &'a T) -> Self {
|
||||||
|
let mut loc = Self::from(path.as_ref());
|
||||||
|
(loc.uri, loc.urn) = (0, 0);
|
||||||
|
loc
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn floated<T: AsRef<Path> + ?Sized>(path: &'a T, base: &Path) -> Self {
|
||||||
|
let mut loc = Self::from(path.as_ref());
|
||||||
|
loc.uri =
|
||||||
|
loc.inner.strip_prefix(base).expect("Loc must start with the given base").as_os_str().len();
|
||||||
|
loc
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_loc(self) -> Loc<'a> { self }
|
pub fn as_loc(self) -> Loc<'a> { self }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn as_path(self) -> &'a Path { self.inner }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn uri(self) -> &'a Uri {
|
pub fn uri(self) -> &'a Uri {
|
||||||
Uri::new(unsafe {
|
Uri::new(unsafe {
|
||||||
|
|
@ -161,6 +177,9 @@ impl<'a> Loc<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn name(self) -> &'a OsStr { self.inner.file_name().unwrap_or(OsStr::new("")) }
|
pub fn name(self) -> &'a OsStr { self.inner.file_name().unwrap_or(OsStr::new("")) }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn parent(self) -> Option<&'a Path> { self.inner.parent() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn triple(self) -> (&'a Path, &'a Path, &'a Path) {
|
pub fn triple(self) -> (&'a Path, &'a Path, &'a Path) {
|
||||||
let len = self.bytes().len();
|
let len = self.bytes().len();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
yazi_macro::mod_flat!(pool ptr symbol traits);
|
yazi_macro::mod_flat!(pool ptr symbol traits);
|
||||||
|
|
||||||
static SYMBOLS: crate::RoCell<parking_lot::Mutex<hashbrown::HashMap<SymbolPtr, u64>>> =
|
static SYMBOLS: crate::RoCell<
|
||||||
crate::RoCell::new();
|
parking_lot::Mutex<hashbrown::HashMap<SymbolPtr, u64, foldhash::fast::FixedState>>,
|
||||||
|
> = crate::RoCell::new();
|
||||||
|
|
||||||
pub(super) fn init() { SYMBOLS.with(<_>::default); }
|
pub(super) fn init() { SYMBOLS.with(<_>::default); }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn compute_hash<T: std::hash::Hash>(value: T) -> u64 {
|
||||||
|
use core::hash::BuildHasher;
|
||||||
|
foldhash::fast::FixedState::default().hash_one(value)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use crate::pool::{SYMBOLS, Symbol, SymbolPtr};
|
use hashbrown::hash_map::RawEntryMut;
|
||||||
|
|
||||||
|
use crate::pool::{SYMBOLS, Symbol, SymbolPtr, compute_hash};
|
||||||
|
|
||||||
pub struct Pool<T: ?Sized> {
|
pub struct Pool<T: ?Sized> {
|
||||||
_phantom: PhantomData<T>,
|
_phantom: PhantomData<T>,
|
||||||
|
|
@ -8,18 +10,23 @@ pub struct Pool<T: ?Sized> {
|
||||||
|
|
||||||
impl Pool<[u8]> {
|
impl Pool<[u8]> {
|
||||||
pub fn intern(value: &[u8]) -> Symbol<[u8]> {
|
pub fn intern(value: &[u8]) -> Symbol<[u8]> {
|
||||||
let mut lock = SYMBOLS.lock();
|
let hash = compute_hash(value);
|
||||||
|
|
||||||
if let Some((ptr, count)) = lock.get_key_value_mut(value) {
|
match SYMBOLS.lock().raw_entry_mut().from_key_hashed_nocheck(hash, value) {
|
||||||
*count += 1;
|
RawEntryMut::Occupied(mut oe) => {
|
||||||
return Symbol::new(ptr.clone());
|
let (ptr, count) = oe.get_key_value_mut();
|
||||||
|
|
||||||
|
*count += 1;
|
||||||
|
Symbol::new(ptr.clone())
|
||||||
|
}
|
||||||
|
RawEntryMut::Vacant(ve) => {
|
||||||
|
let boxed = value.to_vec().into_boxed_slice();
|
||||||
|
let ptr = SymbolPtr::leaked(Box::leak(boxed));
|
||||||
|
|
||||||
|
ve.insert_hashed_nocheck(hash, ptr.clone(), 1);
|
||||||
|
Symbol::new(ptr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let boxed = value.to_vec().into_boxed_slice();
|
|
||||||
let ptr = SymbolPtr::leaked(Box::leak(boxed));
|
|
||||||
|
|
||||||
lock.insert(ptr.clone(), 1);
|
|
||||||
Symbol::new(ptr)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use std::{hash::{Hash, Hasher}, marker::PhantomData, mem::ManuallyDrop, ops::Deref, str};
|
use std::{hash::{Hash, Hasher}, marker::PhantomData, mem::ManuallyDrop, ops::Deref, str};
|
||||||
|
|
||||||
use crate::pool::{Pool, SYMBOLS, SymbolPtr};
|
use hashbrown::hash_map::RawEntryMut;
|
||||||
|
|
||||||
|
use crate::pool::{Pool, SYMBOLS, SymbolPtr, compute_hash};
|
||||||
|
|
||||||
pub struct Symbol<T: ?Sized> {
|
pub struct Symbol<T: ?Sized> {
|
||||||
ptr: SymbolPtr,
|
ptr: SymbolPtr,
|
||||||
|
|
@ -13,22 +15,29 @@ unsafe impl<T: ?Sized> Sync for Symbol<T> {}
|
||||||
|
|
||||||
impl<T: ?Sized> Clone for Symbol<T> {
|
impl<T: ?Sized> Clone for Symbol<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
*SYMBOLS.lock().get_mut(&self.ptr).unwrap() += 1;
|
let hash = compute_hash(&self.ptr);
|
||||||
|
match SYMBOLS.lock().raw_entry_mut().from_key_hashed_nocheck(hash, &self.ptr) {
|
||||||
|
RawEntryMut::Occupied(mut oe) => *oe.get_mut() += 1,
|
||||||
|
RawEntryMut::Vacant(_) => unreachable!(),
|
||||||
|
}
|
||||||
Symbol::new(self.ptr.clone())
|
Symbol::new(self.ptr.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized> Drop for Symbol<T> {
|
impl<T: ?Sized> Drop for Symbol<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let mut lock = SYMBOLS.lock();
|
let hash = compute_hash(&self.ptr);
|
||||||
let count = lock.get_mut(&self.ptr).unwrap();
|
match SYMBOLS.lock().raw_entry_mut().from_key_hashed_nocheck(hash, &self.ptr) {
|
||||||
|
RawEntryMut::Occupied(mut oe) => {
|
||||||
|
let count = oe.get_mut();
|
||||||
|
*count -= 1;
|
||||||
|
|
||||||
*count -= 1;
|
if *count == 0 {
|
||||||
if *count == 0 {
|
oe.remove();
|
||||||
lock.remove(&self.ptr);
|
drop(unsafe { Box::from_raw(self.ptr.as_ptr()) });
|
||||||
unsafe {
|
}
|
||||||
drop(Box::from_raw(self.ptr.as_ptr()));
|
|
||||||
}
|
}
|
||||||
|
RawEntryMut::Vacant(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +47,7 @@ impl AsRef<[u8]> for Symbol<[u8]> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<str> for Symbol<str> {
|
impl AsRef<str> for Symbol<str> {
|
||||||
fn as_ref(&self) -> &str { unsafe { str::from_utf8_unchecked(self.ptr.as_ref()) } }
|
fn as_ref(&self) -> &str { unsafe { str::from_utf8_unchecked(self.ptr.bytes()) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Symbol<[u8]> {
|
impl Deref for Symbol<[u8]> {
|
||||||
|
|
@ -74,19 +83,6 @@ impl<T: ?Sized> Hash for Symbol<T> {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) { self.ptr.as_ptr().hash(state); }
|
fn hash<H: Hasher>(&self, state: &mut H) { self.ptr.as_ptr().hash(state); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- PartialOrd
|
|
||||||
impl PartialOrd for Symbol<[u8]> {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
self.as_ref().partial_cmp(other.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for Symbol<str> {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
self.as_ref().partial_cmp(other.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Ord
|
// --- Ord
|
||||||
impl Ord for Symbol<[u8]> {
|
impl Ord for Symbol<[u8]> {
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.as_ref().cmp(other.as_ref()) }
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.as_ref().cmp(other.as_ref()) }
|
||||||
|
|
@ -96,6 +92,15 @@ impl Ord for Symbol<str> {
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.as_ref().cmp(other.as_ref()) }
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.as_ref().cmp(other.as_ref()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- PartialOrd
|
||||||
|
impl PartialOrd for Symbol<[u8]> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Symbol<str> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }
|
||||||
|
}
|
||||||
|
|
||||||
// --- Debug
|
// --- Debug
|
||||||
impl std::fmt::Debug for Symbol<[u8]> {
|
impl std::fmt::Debug for Symbol<[u8]> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ impl From<&Url<'_>> for UrlBuf {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Url<'_>> for UrlBuf {
|
impl From<Url<'_>> for UrlBuf {
|
||||||
fn from(value: Url<'_>) -> Self { Self::from(&value) }
|
fn from(url: Url<'_>) -> Self { Self { loc: url.loc.into(), scheme: url.scheme } }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&UrlBuf> for UrlBuf {
|
impl From<&UrlBuf> for UrlBuf {
|
||||||
|
|
@ -91,35 +91,23 @@ impl PartialEq<Url<'_>> for &UrlBuf {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UrlBuf {
|
impl UrlBuf {
|
||||||
pub fn join(&self, path: impl AsRef<Path>) -> Self {
|
#[inline]
|
||||||
use Scheme as S;
|
pub fn join(&self, path: impl AsRef<Path>) -> Self { self.as_url().join(path) }
|
||||||
|
|
||||||
let join = self.loc.join(path);
|
|
||||||
|
|
||||||
let loc = match self.scheme {
|
|
||||||
S::Regular => join.into(),
|
|
||||||
S::Search(_) => LocBuf::new(join, self.loc.base(), self.loc.base()),
|
|
||||||
S::Archive(_) => LocBuf::floated(join, self.loc.base()),
|
|
||||||
S::Sftp(_) => join.into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Self { loc, scheme: self.scheme.clone() }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn components(&self) -> Components<'_> { Components::from(self) }
|
pub fn components(&self) -> Components<'_> { Components::from(self) }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn covariant(&self, other: &Self) -> bool { self.as_url().covariant(other.as_url()) }
|
pub fn os_str(&self) -> Cow<'_, OsStr> { self.components().os_str() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn display(&self) -> Display<'_> { Display::new(self) }
|
pub fn display(&self) -> Display<'_> { Display::new(self) }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn os_str(&self) -> Cow<'_, OsStr> { self.components().os_str() }
|
pub fn covariant(&self, other: &Self) -> bool { self.as_url().covariant(other.as_url()) }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parent_url(&self) -> Option<UrlBuf> { self.as_url().parent_url() }
|
pub fn parent_url(&self) -> Option<Url<'_>> { self.as_url().parent_url() }
|
||||||
|
|
||||||
pub fn strip_prefix(&self, base: impl AsRef<UrlBuf>) -> Option<&Urn> {
|
pub fn strip_prefix(&self, base: impl AsRef<UrlBuf>) -> Option<&Urn> {
|
||||||
use Scheme as S;
|
use Scheme as S;
|
||||||
|
|
@ -172,8 +160,11 @@ impl UrlBuf {
|
||||||
Self { loc: self.loc.rebase(base), scheme: self.scheme.clone() }
|
Self { loc: self.loc.rebase(base), scheme: self.scheme.clone() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: use Urn instead of UrlBuf
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pair(&self) -> Option<(Self, UrnBuf)> { Some((self.parent_url()?, self.loc.urn_owned())) }
|
pub fn pair(&self) -> Option<(Url<'_>, UrnBuf)> {
|
||||||
|
Some((self.parent_url()?, self.loc.urn_owned()))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hash_u64(&self) -> u64 { foldhash::fast::FixedState::default().hash_one(self) }
|
pub fn hash_u64(&self) -> u64 { foldhash::fast::FixedState::default().hash_one(self) }
|
||||||
|
|
@ -193,9 +184,7 @@ impl UrlBuf {
|
||||||
pub fn is_regular(&self) -> bool { self.as_url().is_regular() }
|
pub fn is_regular(&self) -> bool { self.as_url().is_regular() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_regular(&self) -> Self {
|
pub fn to_regular(&self) -> Self { self.as_url().into_regular().into() }
|
||||||
Self { loc: self.loc.to_path().into(), scheme: Scheme::Regular }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_regular(mut self) -> Self {
|
pub fn into_regular(mut self) -> Self {
|
||||||
|
|
@ -243,9 +232,7 @@ impl UrlBuf {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for UrlBuf {
|
impl Debug for UrlBuf {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.as_url().fmt(f) }
|
||||||
write!(f, "{}{}", Encode::from(self), self.loc.display())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for UrlBuf {
|
impl Serialize for UrlBuf {
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,10 @@ impl From<UrlCow<'_>> for UrlBufCov {
|
||||||
fn from(value: UrlCow<'_>) -> Self { Self(value.into_owned()) }
|
fn from(value: UrlCow<'_>) -> Self { Self(value.into_owned()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Url<'_>> for UrlBufCov {
|
||||||
|
fn from(value: Url<'_>) -> Self { Self(value.to_owned()) }
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&UrlCov<'_>> for UrlBufCov {
|
impl From<&UrlCov<'_>> for UrlBufCov {
|
||||||
fn from(value: &UrlCov<'_>) -> Self { Self(UrlBuf::from(&value.0)) }
|
fn from(value: &UrlCov<'_>) -> Self { Self(UrlBuf::from(&value.0)) }
|
||||||
}
|
}
|
||||||
|
|
@ -87,5 +91,5 @@ impl UrlBufCov {
|
||||||
pub fn as_url(&self) -> UrlCov<'_> { UrlCov::from(self) }
|
pub fn as_url(&self) -> UrlCov<'_> { UrlCov::from(self) }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parent_url(&self) -> Option<UrlBufCov> { self.0.parent_url().map(UrlBufCov) }
|
pub fn parent_url(&self) -> Option<UrlBufCov> { self.0.parent_url().map(Into::into) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,10 +118,10 @@ impl UrlCow<'_> {
|
||||||
pub fn into_owned(self) -> UrlBuf { self.as_url().into() }
|
pub fn into_owned(self) -> UrlBuf { self.as_url().into() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parent_url(&self) -> Option<UrlBuf> { self.as_url().parent_url() }
|
pub fn parent_url(&self) -> Option<Url<'_>> { self.as_url().parent_url() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pair(&self) -> Option<(UrlBuf, UrnBuf)> { self.as_url().pair() }
|
pub fn pair(&self) -> Option<(Url<'_>, UrnBuf)> { self.as_url().pair() }
|
||||||
|
|
||||||
pub fn parse(bytes: &[u8]) -> Result<(Scheme, Cow<'_, Path>, Option<(usize, usize)>)> {
|
pub fn parse(bytes: &[u8]) -> Result<(Scheme, Cow<'_, Path>, Option<(usize, usize)>)> {
|
||||||
let mut skip = 0;
|
let mut skip = 0;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use std::{ops::Deref, path::Path};
|
use std::{borrow::Cow, ffi::OsStr, fmt::{Debug, Formatter}, ops::Deref, path::Path};
|
||||||
|
|
||||||
use hashbrown::Equivalent;
|
use hashbrown::Equivalent;
|
||||||
|
|
||||||
use crate::{loc::{Loc, LocBuf}, url::{Components, Scheme, UrlBuf, UrnBuf}};
|
use crate::{loc::{Loc, LocBuf}, url::{Components, Encode, Scheme, UrlBuf, UrnBuf}};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||||
pub struct Url<'a> {
|
pub struct Url<'a> {
|
||||||
pub loc: Loc<'a>,
|
pub loc: Loc<'a>,
|
||||||
pub scheme: Scheme,
|
pub scheme: Scheme,
|
||||||
|
|
@ -41,6 +41,13 @@ impl Equivalent<UrlBuf> for Url<'_> {
|
||||||
fn equivalent(&self, key: &UrlBuf) -> bool { self == key }
|
fn equivalent(&self, key: &UrlBuf) -> bool { self == key }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Debug
|
||||||
|
impl Debug for Url<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}{}", Encode::from(self), self.loc.display())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Url<'a> {
|
impl<'a> Url<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn regular<T: AsRef<Path> + ?Sized>(path: &'a T) -> Self {
|
pub fn regular<T: AsRef<Path> + ?Sized>(path: &'a T) -> Self {
|
||||||
|
|
@ -50,12 +57,35 @@ impl<'a> Url<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_regular(&self) -> bool { self.scheme == Scheme::Regular }
|
pub fn is_regular(&self) -> bool { self.scheme == Scheme::Regular }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_regular(self) -> Self {
|
||||||
|
Self { loc: self.loc.as_path().into(), scheme: Scheme::Regular }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_search(&self) -> bool { matches!(self.scheme, Scheme::Search(_)) }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_url(&'a self) -> Url<'a> { Self::from(self) }
|
pub fn as_url(&'a self) -> Url<'a> { Self::from(self) }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_owned(&self) -> UrlBuf { self.into() }
|
pub fn to_owned(&self) -> UrlBuf { self.into() }
|
||||||
|
|
||||||
|
pub fn join(&self, path: impl AsRef<Path>) -> UrlBuf {
|
||||||
|
use Scheme as S;
|
||||||
|
|
||||||
|
let join = self.loc.join(path);
|
||||||
|
|
||||||
|
let loc = match self.scheme {
|
||||||
|
S::Regular => join.into(),
|
||||||
|
S::Search(_) => LocBuf::new(join, self.loc.base(), self.loc.base()),
|
||||||
|
S::Archive(_) => LocBuf::floated(join, self.loc.base()),
|
||||||
|
S::Sftp(_) => join.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
UrlBuf { loc, scheme: self.scheme.clone() }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn base(&self) -> Option<Self> {
|
pub fn base(&self) -> Option<Self> {
|
||||||
use Scheme as S;
|
use Scheme as S;
|
||||||
|
|
||||||
|
|
@ -72,8 +102,7 @@ impl<'a> Url<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use Url instead as the return type
|
pub fn parent_url(&self) -> Option<Self> {
|
||||||
pub fn parent_url(&self) -> Option<UrlBuf> {
|
|
||||||
use Scheme as S;
|
use Scheme as S;
|
||||||
|
|
||||||
let parent = self.loc.parent()?;
|
let parent = self.loc.parent()?;
|
||||||
|
|
@ -81,41 +110,43 @@ impl<'a> Url<'a> {
|
||||||
|
|
||||||
Some(match self.scheme {
|
Some(match self.scheme {
|
||||||
// Regular
|
// Regular
|
||||||
S::Regular => UrlBuf { loc: parent.into(), scheme: S::Regular },
|
S::Regular => Self { loc: parent.into(), scheme: S::Regular },
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
S::Search(_) if uri.is_empty() => UrlBuf { loc: parent.into(), scheme: S::Regular },
|
S::Search(_) if uri.is_empty() => Self { loc: parent.into(), scheme: S::Regular },
|
||||||
S::Search(_) => UrlBuf {
|
S::Search(_) => Self {
|
||||||
loc: LocBuf::new(parent, self.loc.base(), self.loc.base()),
|
loc: Loc::new(parent, self.loc.base(), self.loc.base()),
|
||||||
scheme: self.scheme.clone(),
|
scheme: self.scheme.clone(),
|
||||||
},
|
},
|
||||||
|
|
||||||
// Archive
|
// Archive
|
||||||
S::Archive(_) if uri.is_empty() => UrlBuf { loc: parent.into(), scheme: S::Regular },
|
S::Archive(_) if uri.is_empty() => Self { loc: parent.into(), scheme: S::Regular },
|
||||||
S::Archive(_) if uri.nth(1).is_none() => {
|
S::Archive(_) if uri.nth(1).is_none() => {
|
||||||
UrlBuf { loc: LocBuf::zeroed(parent), scheme: self.scheme.clone() }
|
Self { loc: Loc::zeroed(parent), scheme: self.scheme.clone() }
|
||||||
}
|
}
|
||||||
S::Archive(_) => {
|
S::Archive(_) => {
|
||||||
UrlBuf { loc: LocBuf::floated(parent, self.loc.base()), scheme: self.scheme.clone() }
|
Self { loc: Loc::floated(parent, self.loc.base()), scheme: self.scheme.clone() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// SFTP
|
// SFTP
|
||||||
S::Sftp(_) => UrlBuf { loc: parent.into(), scheme: self.scheme.clone() },
|
S::Sftp(_) => Self { loc: parent.into(), scheme: self.scheme.clone() },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn components(&self) -> Components<'_> { Components::from(self) }
|
pub fn components(&self) -> Components<'_> { Components::from(self) }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn os_str(&self) -> Cow<'_, OsStr> { self.components().os_str() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn covariant(&self, other: impl Into<Self>) -> bool {
|
pub fn covariant(&self, other: impl Into<Self>) -> bool {
|
||||||
let other = other.into();
|
let other = other.into();
|
||||||
self.scheme.covariant(&other.scheme) && self.loc == other.loc
|
self.scheme.covariant(&other.scheme) && self.loc == other.loc
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use Urn instead as the return type
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pair(&self) -> Option<(UrlBuf, UrnBuf)> {
|
pub fn pair(&self) -> Option<(Url<'a>, UrnBuf)> {
|
||||||
Some((self.parent_url()?, self.loc.urn_owned()))
|
Some((self.parent_url()?, self.loc.urn_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue