perf(dom): compact named index strings

This commit is contained in:
ldm0
2026-09-04 21:09:05 +08:00
parent 4558bb7d99
commit 58816279cd
7 changed files with 90 additions and 22 deletions
Generated
+1
View File
@@ -2482,6 +2482,7 @@ dependencies = [
"percent-encoding",
"selectors",
"serde_json",
"servo_arc",
"thin-vec",
"url",
]
+1
View File
@@ -94,6 +94,7 @@ cookie = { git = "https://github.com/ldm0/cookie-rs", branch = "priority" }
v8 = { path = "vendor/v8-152.2.0" }
deno_v8 = { path = "vendor/deno_v8-0.3.0" }
selectors = { git = "https://github.com/ldm0/stylo", rev = "671d13d31b3b00e7b1e0088ba2b36a4e35c0858c" }
servo_arc = { git = "https://github.com/ldm0/stylo", rev = "671d13d31b3b00e7b1e0088ba2b36a4e35c0858c" }
stylo = { git = "https://github.com/ldm0/stylo", rev = "671d13d31b3b00e7b1e0088ba2b36a4e35c0858c" }
stylo_atoms = { git = "https://github.com/ldm0/stylo", rev = "671d13d31b3b00e7b1e0088ba2b36a4e35c0858c" }
stylo_dom = { git = "https://github.com/ldm0/stylo", rev = "671d13d31b3b00e7b1e0088ba2b36a4e35c0858c" }
+1
View File
@@ -12,6 +12,7 @@ moli-html-input-temporal = { path = "../moli-html-input-temporal" }
percent-encoding = "2.3"
selectors = "0.40"
serde_json = "1.0.145"
servo_arc = "0.4.3"
thin-vec = "0.2.14"
url = "2.5.7"
+1 -1
View File
@@ -38,7 +38,7 @@ use self::query_index::ElementQueryIndex;
use self::types::{
CachedConnectedShadowRoots, CachedLiveCollection, LiveCollectionCacheKey,
LiveCollectionCacheKind, MutationScope, NamedElementIndex, ShadowRootState,
ShadowSlotNameIndex,
ShadowSlotNameIndex, ThinArcStr,
};
pub use self::types::{ConnectedShadowRootSnapshot, DomHandle, DomHost};
pub use self::types::{HostElementSnapshot, ShadowRootBindingSnapshot, ShadowRootInit};
+5 -6
View File
@@ -320,13 +320,12 @@ impl DomHost {
}
}
if let Some(current_value) = current_value {
let current_value = std::sync::Arc::<str>::from(current_value);
index
let entry = index
.handles_by_value
.entry(current_value.clone())
.or_default()
.insert(handle);
index.value_by_handle.insert(handle, current_value);
.entry(ThinArcStr::from(current_value));
let canonical_value = entry.key().clone();
entry.or_default().insert(handle);
index.value_by_handle.insert(handle, canonical_value);
}
}
+18 -12
View File
@@ -993,14 +993,14 @@ mod tests {
#[test]
fn named_candidate_index_shares_values_between_directions() {
let mut host = test_host();
let element = host.create_element("div");
assert!(host.set_attribute(element, "id", "shared-identifier"));
assert!(host.append_child(host.document_handle(), element));
let first = host.create_element("div");
let second = host.create_element("span");
for element in [first, second] {
assert!(host.set_attribute(element, "id", "shared-identifier"));
assert!(host.append_child(host.document_handle(), element));
}
assert_eq!(
host.element_handle_by_id("shared-identifier"),
Some(element)
);
assert_eq!(host.element_handle_by_id("shared-identifier"), Some(first));
let index = host.id_index.borrow();
let index = index.as_ref().expect("materialized ID index");
@@ -1009,11 +1009,17 @@ mod tests {
.keys()
.find(|value| value.as_ref() == "shared-identifier")
.expect("forward index key");
let reverse = index
.value_by_handle
.get(&element)
.expect("reverse index value");
assert!(std::sync::Arc::ptr_eq(forward, reverse));
for element in [first, second] {
let reverse = index
.value_by_handle
.get(&element)
.expect("reverse index value");
assert!(forward.ptr_eq(reverse));
}
assert_eq!(
std::mem::size_of::<ThinArcStr>(),
std::mem::size_of::<usize>()
);
}
#[test]
+63 -3
View File
@@ -1,5 +1,10 @@
use super::*;
use std::sync::Arc;
use servo_arc::ThinArc;
use std::{
borrow::Borrow,
fmt,
hash::{Hash, Hasher},
};
pub type DomHandle = NativeNodeId;
@@ -10,8 +15,63 @@ pub struct HostElementSnapshot {
#[derive(Debug, Clone, Default)]
pub(super) struct NamedElementIndex {
pub(super) handles_by_value: HashMap<Arc<str>, IndexSet<DomHandle>>,
pub(super) value_by_handle: HashMap<DomHandle, Arc<str>>,
pub(super) handles_by_value: HashMap<ThinArcStr, IndexSet<DomHandle>>,
pub(super) value_by_handle: HashMap<DomHandle, ThinArcStr>,
}
#[repr(transparent)]
#[derive(Clone)]
pub(super) struct ThinArcStr(ThinArc<(), u8>);
impl ThinArcStr {
fn as_str(&self) -> &str {
// SAFETY: Construction only accepts UTF-8 `str` bytes, and the shared
// backing slice cannot be mutated.
unsafe { std::str::from_utf8_unchecked(self.0.slice()) }
}
#[cfg(test)]
pub(super) fn ptr_eq(&self, other: &Self) -> bool {
servo_arc::Arc::ptr_eq(&self.0, &other.0)
}
}
impl From<&str> for ThinArcStr {
fn from(value: &str) -> Self {
Self(ThinArc::from_header_and_iter((), value.bytes()))
}
}
impl Borrow<str> for ThinArcStr {
fn borrow(&self) -> &str {
self.as_str()
}
}
impl AsRef<str> for ThinArcStr {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl PartialEq for ThinArcStr {
fn eq(&self, other: &Self) -> bool {
self.as_str() == other.as_str()
}
}
impl Eq for ThinArcStr {}
impl Hash for ThinArcStr {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
}
}
impl fmt::Debug for ThinArcStr {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_str().fmt(formatter)
}
}
#[derive(Debug, Clone, Default)]