mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01: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]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
|
|
@ -1967,9 +1967,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
|||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.38.1"
|
||||
version = "0.38.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9845d9dccf565065824e69f9f235fafba1587031eda353c1f1561cd6a6be78f4"
|
||||
checksum = "d200a41a7797e6461bd04e4e95c3347053a731c32c87f066f2f0dda22dbdbba8"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
|
@ -2153,7 +2153,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
|||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"libredox",
|
||||
"thiserror 2.0.15",
|
||||
"thiserror 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2574,11 +2574,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.15"
|
||||
version = "2.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850"
|
||||
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.15",
|
||||
"thiserror-impl 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2594,9 +2594,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.15"
|
||||
version = "2.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0"
|
||||
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -3153,11 +3153,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -78,13 +78,13 @@ impl Cd {
|
|||
Ok(s) => {
|
||||
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() {
|
||||
return MgrProxy::cd(&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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ impl Create {
|
|||
provider::create_dir_all(&new).await?;
|
||||
} else if let Some(real) = realname(&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?;
|
||||
} else {
|
||||
provider::create_dir_all(&parent).await.ok();
|
||||
|
|
@ -56,8 +56,8 @@ impl Create {
|
|||
provider::create(&new).await?;
|
||||
}
|
||||
|
||||
if let Ok(f) = File::new(new.clone()).await {
|
||||
FilesOp::Upserting(parent, [(f.urn_owned(), f)].into()).emit();
|
||||
if let Ok(f) = File::new(&new).await {
|
||||
FilesOp::Upserting(parent.into(), [(f.urn_owned(), f)].into()).emit();
|
||||
MgrProxy::reveal(&new)
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ impl Actor for Open {
|
|||
tokio::spawn(async move {
|
||||
let mut files = Vec::with_capacity(todo.len());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,15 +69,15 @@ impl Rename {
|
|||
|
||||
if let Some(o) = overwritten {
|
||||
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 {
|
||||
FilesOp::Upserting(p_old, [(n_old, file)].into()).emit();
|
||||
FilesOp::Upserting(p_old.into(), [(n_old, file)].into()).emit();
|
||||
} else {
|
||||
FilesOp::Deleting(p_old, [n_old].into()).emit();
|
||||
FilesOp::Upserting(p_new, [(n_new, file)].into()).emit();
|
||||
FilesOp::Deleting(p_old.into(), [n_old].into()).emit();
|
||||
FilesOp::Upserting(p_new.into(), [(n_new, file)].into()).emit();
|
||||
}
|
||||
|
||||
MgrProxy::reveal(&new);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ impl Actor for Reveal {
|
|||
// If the child is not hovered, which means it doesn't exist,
|
||||
// create a dummy file
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ impl Boot {
|
|||
};
|
||||
|
||||
if provider::metadata(&entry).await.is_ok_and(|m| m.is_file()) {
|
||||
(parent, child)
|
||||
(parent.into(), child)
|
||||
} else {
|
||||
(entry, UrnBuf::default())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,15 @@ impl DerefMut for 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
|
||||
'a: 'b,
|
||||
T: Into<Url<'b>>,
|
||||
{
|
||||
let url = url.into();
|
||||
if url.scheme.is_virtual() {
|
||||
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))
|
||||
} else {
|
||||
Box::new(self.iter().filter(move |(_, v)| *v == url).map(|(k, _)| k))
|
||||
|
|
@ -34,7 +36,7 @@ impl Linked {
|
|||
if url.scheme.is_virtual() {
|
||||
vec![]
|
||||
} 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 {
|
||||
vec![]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,9 +68,12 @@ impl Watcher {
|
|||
if !watched.contains(&p) && !LINKED.read().from_dir(&p).any(|u| watched.contains(u)) {
|
||||
continue;
|
||||
}
|
||||
out_tx.send(u).ok();
|
||||
if !parents.contains(&p) {
|
||||
out_tx.send(p.clone()).ok();
|
||||
if parents.contains(&p) {
|
||||
out_tx.send(u).ok();
|
||||
} else {
|
||||
let p = p.to_owned();
|
||||
out_tx.send(u).ok();
|
||||
out_tx.send(p.to_owned()).ok();
|
||||
parents.insert(p);
|
||||
}
|
||||
}
|
||||
|
|
@ -134,8 +137,8 @@ impl Watcher {
|
|||
|
||||
for u in urls {
|
||||
let Some((parent, urn)) = u.pair() else { continue };
|
||||
let Ok(file) = File::new(u).await else {
|
||||
ops.push(FilesOp::Deleting(parent, [urn].into()));
|
||||
let Ok(file) = File::new(&u).await else {
|
||||
ops.push(FilesOp::Deleting(parent.into(), [urn].into()));
|
||||
continue;
|
||||
};
|
||||
|
||||
|
|
@ -144,11 +147,11 @@ impl Watcher {
|
|||
|| realname_unchecked(u, &mut cached).await.is_ok_and(|s| urn.as_urn() == s);
|
||||
|
||||
if !eq {
|
||||
ops.push(FilesOp::Deleting(parent, [urn].into()));
|
||||
ops.push(FilesOp::Deleting(parent.into(), [urn].into()));
|
||||
continue;
|
||||
}
|
||||
|
||||
ops.push(FilesOp::Upserting(parent, [(urn, file)].into()));
|
||||
ops.push(FilesOp::Upserting(parent.into(), [(urn, file)].into()));
|
||||
}
|
||||
|
||||
FilesOp::mutate(ops);
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ impl Selected {
|
|||
self.inner.extend(urls.iter().enumerate().map(|(i, u)| (u.into(), now + i as u64)));
|
||||
|
||||
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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{ffi::OsStr, fs::{FileType, Metadata}, hash::{BuildHasher, Hash, Hasher}, ops::Deref};
|
||||
|
||||
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};
|
||||
|
||||
|
|
@ -21,9 +21,10 @@ impl Deref for File {
|
|||
|
||||
impl File {
|
||||
#[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?;
|
||||
Ok(Self::from_follow(url, meta).await)
|
||||
Ok(Self::from_follow(url.into_owned(), meta).await)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -55,9 +55,9 @@ impl FilesOp {
|
|||
let Some(o_p) = o.parent_url() else { continue };
|
||||
let Some(n_p) = n.url.parent_url() else { continue };
|
||||
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 {
|
||||
match (o.is_empty(), n.is_empty()) {
|
||||
|
|
@ -124,7 +124,7 @@ impl FilesOp {
|
|||
} else if maybe_exists(cwd).await {
|
||||
Self::IOErr(cwd.clone(), kind).emit();
|
||||
} 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 yazi_fs::path::expand_url;
|
||||
use yazi_shared::{event::CmdCow, url::{UrlBuf, UrlCow}};
|
||||
use yazi_shared::{event::CmdCow, url::{Url, UrlBuf, UrlCow}};
|
||||
|
||||
#[derive(Debug)]
|
||||
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 {
|
||||
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();
|
||||
FilesOp::Size(
|
||||
parent,
|
||||
parent.into(),
|
||||
HashMap::from_iter(buf.into_iter().map(|(u, s)| (u.urn_owned(), s))),
|
||||
)
|
||||
.emit();
|
||||
|
|
|
|||
|
|
@ -85,16 +85,19 @@ impl LocBuf {
|
|||
}
|
||||
|
||||
pub fn zeroed(path: impl Into<PathBuf>) -> Self {
|
||||
let mut loc = Self::from(path.into());
|
||||
(loc.uri, loc.urn) = (0, 0);
|
||||
loc
|
||||
let loc = Self::from(path.into());
|
||||
let Loc { inner, uri, urn } = Loc::zeroed(&loc.inner);
|
||||
|
||||
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 {
|
||||
let mut loc = Self::from(path.into());
|
||||
loc.uri =
|
||||
loc.inner.strip_prefix(base).expect("Loc must start with the given base").as_os_str().len();
|
||||
loc
|
||||
let loc = Self::from(path.into());
|
||||
let Loc { inner, uri, urn } = Loc::floated(&loc.inner, base);
|
||||
|
||||
debug_assert!(inner.as_os_str() == loc.inner.as_os_str());
|
||||
Self { inner: loc.inner, uri, urn }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -110,9 +110,25 @@ impl<'a> Loc<'a> {
|
|||
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]
|
||||
pub fn as_loc(self) -> Loc<'a> { self }
|
||||
|
||||
#[inline]
|
||||
pub fn as_path(self) -> &'a Path { self.inner }
|
||||
|
||||
#[inline]
|
||||
pub fn uri(self) -> &'a Uri {
|
||||
Uri::new(unsafe {
|
||||
|
|
@ -161,6 +177,9 @@ impl<'a> Loc<'a> {
|
|||
#[inline]
|
||||
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]
|
||||
pub fn triple(self) -> (&'a Path, &'a Path, &'a Path) {
|
||||
let len = self.bytes().len();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
yazi_macro::mod_flat!(pool ptr symbol traits);
|
||||
|
||||
static SYMBOLS: crate::RoCell<parking_lot::Mutex<hashbrown::HashMap<SymbolPtr, u64>>> =
|
||||
crate::RoCell::new();
|
||||
static SYMBOLS: crate::RoCell<
|
||||
parking_lot::Mutex<hashbrown::HashMap<SymbolPtr, u64, foldhash::fast::FixedState>>,
|
||||
> = crate::RoCell::new();
|
||||
|
||||
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 crate::pool::{SYMBOLS, Symbol, SymbolPtr};
|
||||
use hashbrown::hash_map::RawEntryMut;
|
||||
|
||||
use crate::pool::{SYMBOLS, Symbol, SymbolPtr, compute_hash};
|
||||
|
||||
pub struct Pool<T: ?Sized> {
|
||||
_phantom: PhantomData<T>,
|
||||
|
|
@ -8,18 +10,23 @@ pub struct Pool<T: ?Sized> {
|
|||
|
||||
impl Pool<[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) {
|
||||
*count += 1;
|
||||
return Symbol::new(ptr.clone());
|
||||
match SYMBOLS.lock().raw_entry_mut().from_key_hashed_nocheck(hash, value) {
|
||||
RawEntryMut::Occupied(mut oe) => {
|
||||
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 crate::pool::{Pool, SYMBOLS, SymbolPtr};
|
||||
use hashbrown::hash_map::RawEntryMut;
|
||||
|
||||
use crate::pool::{Pool, SYMBOLS, SymbolPtr, compute_hash};
|
||||
|
||||
pub struct Symbol<T: ?Sized> {
|
||||
ptr: SymbolPtr,
|
||||
|
|
@ -13,22 +15,29 @@ unsafe impl<T: ?Sized> Sync for Symbol<T> {}
|
|||
|
||||
impl<T: ?Sized> Clone for Symbol<T> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for Symbol<T> {
|
||||
fn drop(&mut self) {
|
||||
let mut lock = SYMBOLS.lock();
|
||||
let count = lock.get_mut(&self.ptr).unwrap();
|
||||
let hash = compute_hash(&self.ptr);
|
||||
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 {
|
||||
lock.remove(&self.ptr);
|
||||
unsafe {
|
||||
drop(Box::from_raw(self.ptr.as_ptr()));
|
||||
if *count == 0 {
|
||||
oe.remove();
|
||||
drop(unsafe { 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> {
|
||||
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]> {
|
||||
|
|
@ -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); }
|
||||
}
|
||||
|
||||
// --- 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
|
||||
impl Ord for Symbol<[u8]> {
|
||||
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()) }
|
||||
}
|
||||
|
||||
// --- 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
|
||||
impl std::fmt::Debug for Symbol<[u8]> {
|
||||
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 {
|
||||
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 {
|
||||
|
|
@ -91,35 +91,23 @@ impl PartialEq<Url<'_>> for &UrlBuf {
|
|||
}
|
||||
|
||||
impl UrlBuf {
|
||||
pub fn join(&self, path: impl AsRef<Path>) -> Self {
|
||||
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(),
|
||||
};
|
||||
|
||||
Self { loc, scheme: self.scheme.clone() }
|
||||
}
|
||||
#[inline]
|
||||
pub fn join(&self, path: impl AsRef<Path>) -> Self { self.as_url().join(path) }
|
||||
|
||||
#[inline]
|
||||
pub fn components(&self) -> Components<'_> { Components::from(self) }
|
||||
|
||||
#[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]
|
||||
pub fn display(&self) -> Display<'_> { Display::new(self) }
|
||||
|
||||
#[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]
|
||||
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> {
|
||||
use Scheme as S;
|
||||
|
|
@ -172,8 +160,11 @@ impl UrlBuf {
|
|||
Self { loc: self.loc.rebase(base), scheme: self.scheme.clone() }
|
||||
}
|
||||
|
||||
// TODO: use Urn instead of UrlBuf
|
||||
#[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]
|
||||
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() }
|
||||
|
||||
#[inline]
|
||||
pub fn to_regular(&self) -> Self {
|
||||
Self { loc: self.loc.to_path().into(), scheme: Scheme::Regular }
|
||||
}
|
||||
pub fn to_regular(&self) -> Self { self.as_url().into_regular().into() }
|
||||
|
||||
#[inline]
|
||||
pub fn into_regular(mut self) -> Self {
|
||||
|
|
@ -243,9 +232,7 @@ impl UrlBuf {
|
|||
}
|
||||
|
||||
impl Debug for UrlBuf {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}{}", Encode::from(self), self.loc.display())
|
||||
}
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.as_url().fmt(f) }
|
||||
}
|
||||
|
||||
impl Serialize for UrlBuf {
|
||||
|
|
|
|||
|
|
@ -62,6 +62,10 @@ impl From<UrlCow<'_>> for UrlBufCov {
|
|||
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 {
|
||||
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) }
|
||||
|
||||
#[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() }
|
||||
|
||||
#[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]
|
||||
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)>)> {
|
||||
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 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 loc: Loc<'a>,
|
||||
pub scheme: Scheme,
|
||||
|
|
@ -41,6 +41,13 @@ impl Equivalent<UrlBuf> for Url<'_> {
|
|||
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> {
|
||||
#[inline]
|
||||
pub fn regular<T: AsRef<Path> + ?Sized>(path: &'a T) -> Self {
|
||||
|
|
@ -50,12 +57,35 @@ impl<'a> Url<'a> {
|
|||
#[inline]
|
||||
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]
|
||||
pub fn as_url(&'a self) -> Url<'a> { Self::from(self) }
|
||||
|
||||
#[inline]
|
||||
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> {
|
||||
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<UrlBuf> {
|
||||
pub fn parent_url(&self) -> Option<Self> {
|
||||
use Scheme as S;
|
||||
|
||||
let parent = self.loc.parent()?;
|
||||
|
|
@ -81,41 +110,43 @@ impl<'a> Url<'a> {
|
|||
|
||||
Some(match self.scheme {
|
||||
// Regular
|
||||
S::Regular => UrlBuf { loc: parent.into(), scheme: S::Regular },
|
||||
S::Regular => Self { loc: parent.into(), scheme: S::Regular },
|
||||
|
||||
// Search
|
||||
S::Search(_) if uri.is_empty() => UrlBuf { loc: parent.into(), scheme: S::Regular },
|
||||
S::Search(_) => UrlBuf {
|
||||
loc: LocBuf::new(parent, self.loc.base(), self.loc.base()),
|
||||
S::Search(_) if uri.is_empty() => Self { loc: parent.into(), scheme: S::Regular },
|
||||
S::Search(_) => Self {
|
||||
loc: Loc::new(parent, self.loc.base(), self.loc.base()),
|
||||
scheme: self.scheme.clone(),
|
||||
},
|
||||
|
||||
// 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() => {
|
||||
UrlBuf { loc: LocBuf::zeroed(parent), scheme: self.scheme.clone() }
|
||||
Self { loc: Loc::zeroed(parent), scheme: self.scheme.clone() }
|
||||
}
|
||||
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
|
||||
S::Sftp(_) => UrlBuf { loc: parent.into(), scheme: self.scheme.clone() },
|
||||
S::Sftp(_) => Self { loc: parent.into(), scheme: self.scheme.clone() },
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn components(&self) -> Components<'_> { Components::from(self) }
|
||||
|
||||
#[inline]
|
||||
pub fn os_str(&self) -> Cow<'_, OsStr> { self.components().os_str() }
|
||||
|
||||
#[inline]
|
||||
pub fn covariant(&self, other: impl Into<Self>) -> bool {
|
||||
let other = other.into();
|
||||
self.scheme.covariant(&other.scheme) && self.loc == other.loc
|
||||
}
|
||||
|
||||
// TODO: use Urn instead as the return type
|
||||
#[inline]
|
||||
pub fn pair(&self) -> Option<(UrlBuf, UrnBuf)> {
|
||||
pub fn pair(&self) -> Option<(Url<'a>, UrnBuf)> {
|
||||
Some((self.parent_url()?, self.loc.urn_owned()))
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue