Skip to main content

style_traits/
arc_slice.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! A thin atomically-reference-counted slice.
6
7use serde::de::{Deserialize, Deserializer};
8use serde::ser::{Serialize, Serializer};
9use servo_arc::ThinArc;
10use std::ops::Deref;
11use std::ptr::NonNull;
12use std::sync::LazyLock;
13use std::{
14    hash::{Hash, Hasher},
15    iter, mem,
16};
17
18use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
19
20/// A canary that we stash in ArcSlices.
21///
22/// Given we cannot use a zero-sized-type for the header, since well, C++
23/// doesn't have zsts, and we want to use cbindgen for this type, we may as well
24/// assert some sanity at runtime.
25///
26/// We use an u64, to guarantee that we can use a single singleton for every
27/// empty slice, even if the types they hold are aligned differently.
28const ARC_SLICE_CANARY: u64 = 0xf3f3f3f3f3f3f3f3;
29
30/// A wrapper type for a refcounted slice using ThinArc.
31#[repr(C)]
32#[derive(Debug, Eq, PartialEq, ToShmem)]
33pub struct ArcSlice<T>(#[shmem(field_bound)] ThinArc<u64, T>);
34
35impl<T> Deref for ArcSlice<T> {
36    type Target = [T];
37
38    #[inline]
39    fn deref(&self) -> &Self::Target {
40        debug_assert_eq!(self.0.header, ARC_SLICE_CANARY);
41        self.0.slice()
42    }
43}
44
45impl<T> Clone for ArcSlice<T> {
46    fn clone(&self) -> Self {
47        ArcSlice(self.0.clone())
48    }
49}
50
51// ThinArc doesn't support alignments greater than align_of::<u64>.
52static EMPTY_ARC_SLICE: LazyLock<ArcSlice<u64>> =
53    LazyLock::new(|| ArcSlice::from_iter_leaked(iter::empty()));
54
55impl<T> Default for ArcSlice<T> {
56    #[allow(unsafe_code)]
57    fn default() -> Self {
58        debug_assert!(
59            mem::align_of::<T>() <= mem::align_of::<u64>(),
60            "Need to increase the alignment of EMPTY_ARC_SLICE"
61        );
62        unsafe {
63            let empty: ArcSlice<_> = EMPTY_ARC_SLICE.clone();
64            let empty: Self = mem::transmute(empty);
65            debug_assert_eq!(empty.len(), 0);
66            empty
67        }
68    }
69}
70
71impl<T: Serialize> Serialize for ArcSlice<T> {
72    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
73    where
74        S: Serializer,
75    {
76        self.deref().serialize(serializer)
77    }
78}
79
80impl<'de, T: Deserialize<'de>> Deserialize<'de> for ArcSlice<T> {
81    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
82    where
83        D: Deserializer<'de>,
84    {
85        let r = Vec::deserialize(deserializer)?;
86        Ok(ArcSlice::from_iter(r.into_iter()))
87    }
88}
89
90impl<T> ArcSlice<T> {
91    /// Creates an Arc for a slice using the given iterator to generate the
92    /// slice.
93    #[inline]
94    #[allow(clippy::should_implement_trait)]
95    pub fn from_iter(items: impl ExactSizeIterator<Item = T>) -> Self {
96        if items.len() == 0 {
97            return Self::default();
98        }
99        ArcSlice(ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items))
100    }
101
102    /// Creates an Arc for a slice using the given iterator to generate the
103    /// slice, and marks the arc as intentionally leaked from the refcount
104    /// logging point of view.
105    #[inline]
106    pub fn from_iter_leaked(items: impl ExactSizeIterator<Item = T>) -> Self {
107        let arc = ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items);
108        arc.mark_as_intentionally_leaked();
109        ArcSlice(arc)
110    }
111
112    /// Creates a value that can be passed via FFI, and forgets this value
113    /// altogether.
114    #[inline]
115    pub fn forget(self) -> ForgottenArcSlicePtr<T> {
116        let ret = ForgottenArcSlicePtr(self.0.raw_ptr().cast());
117        mem::forget(self);
118        ret
119    }
120
121    /// Leaks an empty arc slice pointer, and returns it. Only to be used to
122    /// construct ArcSlices from FFI.
123    #[inline]
124    pub fn leaked_empty_ptr() -> *mut std::os::raw::c_void {
125        let empty: ArcSlice<_> = EMPTY_ARC_SLICE.clone();
126        let ptr = empty.0.raw_ptr();
127        std::mem::forget(empty);
128        ptr.cast().as_ptr()
129    }
130
131    /// Returns whether there's only one reference to this ArcSlice.
132    pub fn is_unique(&self) -> bool {
133        self.0.is_unique()
134    }
135}
136
137impl<T: MallocSizeOf> MallocUnconditionalSizeOf for ArcSlice<T> {
138    #[allow(unsafe_code)]
139    fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
140        let mut size = unsafe { ops.malloc_size_of(self.0.heap_ptr()) };
141        for el in self.iter() {
142            size += el.size_of(ops);
143        }
144        size
145    }
146}
147
148impl<T: Hash> Hash for ArcSlice<T> {
149    fn hash<H: Hasher>(&self, state: &mut H) {
150        T::hash_slice(self, state)
151    }
152}
153
154/// The inner pointer of an ArcSlice<T>, to be sent via FFI.
155/// The type of the pointer is a bit of a lie, we just want to preserve the type
156/// but these pointers cannot be constructed outside of this crate, so we're
157/// good.
158#[repr(C)]
159pub struct ForgottenArcSlicePtr<T>(NonNull<T>);