/* 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. #[repr(C)] #[derive(Debug, Eq, PartialEq, ToShmem)] pub struct ArcSlice(#[shmem(field_bound)] ThinArc); impl Deref for ArcSlice { type Target = [T]; #[inline] fn deref(&self) -> &Self::Target { debug_assert_eq!(self.0.header, ARC_SLICE_CANARY); self.0.slice() } } impl Clone for ArcSlice { fn clone(&self) -> Self { ArcSlice(self.0.clone()) } } lazy_static! { // ThinArc doesn't support alignments greater than align_of::. static ref EMPTY_ARC_SLICE: ArcSlice = { ArcSlice::from_iter_leaked(iter::empty()) }; } impl Default for ArcSlice { #[allow(unsafe_code)] fn default() -> Self { debug_assert!( mem::align_of::() <= mem::align_of::(), "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 Serialize for ArcSlice { fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.deref().serialize(serializer) } } impl<'de, T: Deserialize<'de>> Deserialize<'de> for ArcSlice { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let r = Vec::deserialize(deserializer)?; Ok(ArcSlice::from_iter(r.into_iter())) } } impl ArcSlice { /// Creates an Arc for a slice using the given iterator to generate the /// slice. #[inline] pub fn from_iter(items: I) -> Self where I: Iterator + 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(items: I) -> Self where I: Iterator + ExactSizeIterator, { let arc = ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items); arc.mark_as_intentionally_leaked(); ArcSlice(arc) } /// Creates a value that can be passed via FFI, and forgets this value /// altogether. #[inline] #[allow(unsafe_code)] pub fn forget(self) -> ForgottenArcSlicePtr { let ret = unsafe { ForgottenArcSlicePtr(NonNull::new_unchecked( self.0.raw_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.raw_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.is_unique() } } impl MallocUnconditionalSizeOf for ArcSlice { #[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, 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(NonNull);