summaryrefslogtreecommitdiffstats
path: root/servo/components/style_traits/arc_slice.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--servo/components/style_traits/arc_slice.rs163
1 files changed, 163 insertions, 0 deletions
diff --git a/servo/components/style_traits/arc_slice.rs b/servo/components/style_traits/arc_slice.rs
new file mode 100644
index 0000000000..1f10ed9455
--- /dev/null
+++ b/servo/components/style_traits/arc_slice.rs
@@ -0,0 +1,163 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! A thin atomically-reference-counted slice.
+
+use serde::de::{Deserialize, Deserializer};
+use serde::ser::{Serialize, Serializer};
+use servo_arc::ThinArc;
+use std::ops::Deref;
+use std::ptr::NonNull;
+use std::{iter, mem};
+
+use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
+
+/// A canary that we stash in ArcSlices.
+///
+/// Given we cannot use a zero-sized-type for the header, since well, C++
+/// doesn't have zsts, and we want to use cbindgen for this type, we may as well
+/// assert some sanity at runtime.
+///
+/// We use an u64, to guarantee that we can use a single singleton for every
+/// empty slice, even if the types they hold are aligned differently.
+const ARC_SLICE_CANARY: u64 = 0xf3f3f3f3f3f3f3f3;
+
+/// A wrapper type for a refcounted slice using ThinArc.
+///
+/// cbindgen:derive-eq=false
+/// cbindgen:derive-neq=false
+#[repr(C)]
+#[derive(Debug, Eq, PartialEq, ToShmem)]
+pub struct ArcSlice<T>(#[shmem(field_bound)] ThinArc<u64, T>);
+
+impl<T> Deref for ArcSlice<T> {
+ type Target = [T];
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ debug_assert_eq!(self.0.header.header, ARC_SLICE_CANARY);
+ &self.0.slice
+ }
+}
+
+impl<T> Clone for ArcSlice<T> {
+ fn clone(&self) -> Self {
+ ArcSlice(self.0.clone())
+ }
+}
+
+lazy_static! {
+ // ThinArc doesn't support alignments greater than align_of::<u64>.
+ static ref EMPTY_ARC_SLICE: ArcSlice<u64> = {
+ ArcSlice::from_iter_leaked(iter::empty())
+ };
+}
+
+impl<T> Default for ArcSlice<T> {
+ #[allow(unsafe_code)]
+ fn default() -> Self {
+ debug_assert!(
+ mem::align_of::<T>() <= mem::align_of::<u64>(),
+ "Need to increase the alignment of EMPTY_ARC_SLICE"
+ );
+ unsafe {
+ let empty: ArcSlice<_> = EMPTY_ARC_SLICE.clone();
+ let empty: Self = mem::transmute(empty);
+ debug_assert_eq!(empty.len(), 0);
+ empty
+ }
+ }
+}
+
+impl<T: Serialize> Serialize for ArcSlice<T> {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ self.deref().serialize(serializer)
+ }
+}
+
+impl<'de, T: Deserialize<'de>> Deserialize<'de> for ArcSlice<T> {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ let r = Vec::deserialize(deserializer)?;
+ Ok(ArcSlice::from_iter(r.into_iter()))
+ }
+}
+
+impl<T> ArcSlice<T> {
+ /// Creates an Arc for a slice using the given iterator to generate the
+ /// slice.
+ #[inline]
+ pub fn from_iter<I>(items: I) -> Self
+ where
+ I: Iterator<Item = T> + ExactSizeIterator,
+ {
+ if items.len() == 0 {
+ return Self::default();
+ }
+ ArcSlice(ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items))
+ }
+
+ /// Creates an Arc for a slice using the given iterator to generate the
+ /// slice, and marks the arc as intentionally leaked from the refcount
+ /// logging point of view.
+ #[inline]
+ pub fn from_iter_leaked<I>(items: I) -> Self
+ where
+ I: Iterator<Item = T> + ExactSizeIterator,
+ {
+ let thin_arc = ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items);
+ thin_arc.with_arc(|a| a.mark_as_intentionally_leaked());
+ ArcSlice(thin_arc)
+ }
+
+ /// Creates a value that can be passed via FFI, and forgets this value
+ /// altogether.
+ #[inline]
+ #[allow(unsafe_code)]
+ pub fn forget(self) -> ForgottenArcSlicePtr<T> {
+ let ret = unsafe {
+ ForgottenArcSlicePtr(NonNull::new_unchecked(self.0.ptr() as *const _ as *mut _))
+ };
+ mem::forget(self);
+ ret
+ }
+
+ /// Leaks an empty arc slice pointer, and returns it. Only to be used to
+ /// construct ArcSlices from FFI.
+ #[inline]
+ pub fn leaked_empty_ptr() -> *mut std::os::raw::c_void {
+ let empty: ArcSlice<_> = EMPTY_ARC_SLICE.clone();
+ let ptr = empty.0.ptr();
+ std::mem::forget(empty);
+ ptr as *mut _
+ }
+
+ /// Returns whether there's only one reference to this ArcSlice.
+ pub fn is_unique(&self) -> bool {
+ self.0.with_arc(|arc| arc.is_unique())
+ }
+}
+
+impl<T: MallocSizeOf> MallocUnconditionalSizeOf for ArcSlice<T> {
+ #[allow(unsafe_code)]
+ fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
+ let mut size = unsafe { ops.malloc_size_of(self.0.heap_ptr()) };
+ for el in self.iter() {
+ size += el.size_of(ops);
+ }
+ size
+ }
+}
+
+/// The inner pointer of an ArcSlice<T>, to be sent via FFI.
+/// The type of the pointer is a bit of a lie, we just want to preserve the type
+/// but these pointers cannot be constructed outside of this crate, so we're
+/// good.
+#[repr(C)]
+pub struct ForgottenArcSlicePtr<T>(NonNull<T>);