diff --git a/Cargo.lock b/Cargo.lock index 16c2026d38..c71816f408 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2482,6 +2482,7 @@ dependencies = [ "percent-encoding", "selectors", "serde_json", + "servo_arc", "thin-vec", "url", ] diff --git a/Cargo.toml b/Cargo.toml index 52a4c96c8c..c103aeac3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } diff --git a/moli-dom/Cargo.toml b/moli-dom/Cargo.toml index 3b6879268a..188044b6f7 100644 --- a/moli-dom/Cargo.toml +++ b/moli-dom/Cargo.toml @@ -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" diff --git a/moli-dom/src/native/host.rs b/moli-dom/src/native/host.rs index 6db720db3f..e16ce26615 100644 --- a/moli-dom/src/native/host.rs +++ b/moli-dom/src/native/host.rs @@ -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}; diff --git a/moli-dom/src/native/host/document.rs b/moli-dom/src/native/host/document.rs index 14c6aa5f8c..f3c40393f5 100644 --- a/moli-dom/src/native/host/document.rs +++ b/moli-dom/src/native/host/document.rs @@ -320,13 +320,12 @@ impl DomHost { } } if let Some(current_value) = current_value { - let current_value = std::sync::Arc::::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); } } diff --git a/moli-dom/src/native/host/query_index.rs b/moli-dom/src/native/host/query_index.rs index 683e687900..26e2346622 100644 --- a/moli-dom/src/native/host/query_index.rs +++ b/moli-dom/src/native/host/query_index.rs @@ -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::(), + std::mem::size_of::() + ); } #[test] diff --git a/moli-dom/src/native/host/types.rs b/moli-dom/src/native/host/types.rs index 9ccf023082..99dc0eb117 100644 --- a/moli-dom/src/native/host/types.rs +++ b/moli-dom/src/native/host/types.rs @@ -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, IndexSet>, - pub(super) value_by_handle: HashMap>, + pub(super) handles_by_value: HashMap>, + pub(super) value_by_handle: HashMap, +} + +#[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 for ThinArcStr { + fn borrow(&self) -> &str { + self.as_str() + } +} + +impl AsRef 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(&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)]