tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

lib.rs (21384B)


      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 //! Trait for cloning data into a shared memory buffer.
      6 //!
      7 //! This module contains the SharedMemoryBuilder type and ToShmem trait.
      8 //!
      9 //! We put them here (and not in style_traits) so that we can derive ToShmem
     10 //! from the selectors and style crates.
     11 
     12 #![crate_name = "to_shmem"]
     13 #![crate_type = "rlib"]
     14 
     15 use std::alloc::Layout;
     16 use std::collections::HashSet;
     17 use std::ffi::CString;
     18 use std::isize;
     19 use std::marker::PhantomData;
     20 use std::mem::{self, ManuallyDrop};
     21 use std::num::Wrapping;
     22 use std::ops::Range;
     23 use std::os::raw::c_char;
     24 use std::ptr::{self, NonNull};
     25 use std::slice;
     26 use std::str;
     27 
     28 /// Result type for ToShmem::to_shmem.
     29 ///
     30 /// The String is an error message describing why the call failed.
     31 pub type Result<T> = std::result::Result<ManuallyDrop<T>, String>;
     32 
     33 // Various pointer arithmetic functions in this file can be replaced with
     34 // functions on `Layout` once they have stabilized:
     35 //
     36 // https://github.com/rust-lang/rust/issues/55724
     37 
     38 /// A builder object that transforms and copies values into a fixed size buffer.
     39 pub struct SharedMemoryBuilder {
     40    /// The buffer into which values will be copied.
     41    buffer: *mut u8,
     42    /// The size of the buffer.
     43    capacity: usize,
     44    /// The current position in the buffer, where the next value will be written
     45    /// at.
     46    index: usize,
     47    /// Pointers to every shareable value that we store in the shared memory
     48    /// buffer.  We use this to assert against encountering the same value
     49    /// twice, e.g. through another Arc reference, so that we don't
     50    /// inadvertently store duplicate copies of values.
     51    #[cfg(all(debug_assertions, feature = "servo_arc"))]
     52    shared_values: HashSet<*const std::os::raw::c_void>,
     53 }
     54 
     55 /// Amount of padding needed after `size` bytes to ensure that the following
     56 /// address will satisfy `align`.
     57 fn padding_needed_for(size: usize, align: usize) -> usize {
     58    padded_size(size, align).wrapping_sub(size)
     59 }
     60 
     61 /// Rounds up `size` so that the following address will satisfy `align`.
     62 fn padded_size(size: usize, align: usize) -> usize {
     63    size.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1)
     64 }
     65 
     66 impl SharedMemoryBuilder {
     67    /// Creates a new SharedMemoryBuilder using the specified buffer.
     68    pub unsafe fn new(buffer: *mut u8, capacity: usize) -> SharedMemoryBuilder {
     69        SharedMemoryBuilder {
     70            buffer,
     71            capacity,
     72            index: 0,
     73            #[cfg(all(debug_assertions, feature = "servo_arc"))]
     74            shared_values: HashSet::new(),
     75        }
     76    }
     77 
     78    /// Returns the number of bytes currently used in the buffer.
     79    #[inline]
     80    pub fn len(&self) -> usize {
     81        self.index
     82    }
     83 
     84    /// Writes a value into the shared memory buffer and returns a pointer to
     85    /// it in the buffer.
     86    ///
     87    /// The value is cloned and converted into a form suitable for placing into
     88    /// a shared memory buffer by calling ToShmem::to_shmem on it.
     89    ///
     90    /// Panics if there is insufficient space in the buffer.
     91    pub fn write<T: ToShmem>(&mut self, value: &T) -> std::result::Result<*mut T, String> {
     92        // Reserve space for the value.
     93        let dest: *mut T = self.alloc_value();
     94 
     95        // Make a clone of the value with all of its heap allocations
     96        // placed in the shared memory buffer.
     97        let value = value.to_shmem(self)?;
     98 
     99        unsafe {
    100            // Copy the value into the buffer.
    101            ptr::write(dest, ManuallyDrop::into_inner(value));
    102        }
    103 
    104        // Return a pointer to the shared value.
    105        Ok(dest)
    106    }
    107 
    108    /// Reserves space in the shared memory buffer to fit a value of type T,
    109    /// and returns a pointer to that reserved space.
    110    ///
    111    /// Panics if there is insufficient space in the buffer.
    112    pub fn alloc_value<T>(&mut self) -> *mut T {
    113        self.alloc(Layout::new::<T>())
    114    }
    115 
    116    /// Reserves space in the shared memory buffer to fit an array of values of
    117    /// type T, and returns a pointer to that reserved space.
    118    ///
    119    /// Panics if there is insufficient space in the buffer.
    120    pub fn alloc_array<T>(&mut self, len: usize) -> *mut T {
    121        if len == 0 {
    122            return NonNull::dangling().as_ptr();
    123        }
    124 
    125        let size = mem::size_of::<T>();
    126        let align = mem::align_of::<T>();
    127 
    128        self.alloc(Layout::from_size_align(padded_size(size, align) * len, align).unwrap())
    129    }
    130 
    131    /// Reserves space in the shared memory buffer that conforms to the
    132    /// specified layout, and returns a pointer to that reserved space.
    133    ///
    134    /// Panics if there is insufficient space in the buffer.
    135    pub fn alloc<T>(&mut self, layout: Layout) -> *mut T {
    136        // Amount of padding to align the value.
    137        //
    138        // The addition can't overflow, since self.index <= self.capacity, and
    139        // for us to have successfully allocated the buffer, `buffer + capacity`
    140        // can't overflow.
    141        let padding = padding_needed_for(self.buffer as usize + self.index, layout.align());
    142 
    143        // Reserve space for the padding.
    144        let start = self.index.checked_add(padding).unwrap();
    145        assert!(start <= std::isize::MAX as usize); // for the cast below
    146 
    147        // Reserve space for the value.
    148        let end = start.checked_add(layout.size()).unwrap();
    149        assert!(end <= self.capacity);
    150 
    151        self.index = end;
    152        unsafe { self.buffer.add(start) as *mut T }
    153    }
    154 }
    155 
    156 /// A type that can be copied into a SharedMemoryBuilder.
    157 pub trait ToShmem: Sized {
    158    /// Clones this value into a form suitable for writing into a
    159    /// SharedMemoryBuilder.
    160    ///
    161    /// If this value owns any heap allocations, they should be written into
    162    /// `builder` so that the return value of this function can point to the
    163    /// copy in the shared memory buffer.
    164    ///
    165    /// The return type is wrapped in ManuallyDrop to make it harder to
    166    /// accidentally invoke the destructor of the value that is produced.
    167    ///
    168    /// Returns a Result so that we can gracefully recover from unexpected
    169    /// content.
    170    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self>;
    171 }
    172 
    173 #[macro_export]
    174 macro_rules! impl_trivial_to_shmem {
    175    ($($ty:ty),*) => {
    176        $(
    177            impl $crate::ToShmem for $ty {
    178                fn to_shmem(
    179                    &self,
    180                    _builder: &mut $crate::SharedMemoryBuilder,
    181                ) -> $crate::Result<Self> {
    182                    $crate::Result::Ok(::std::mem::ManuallyDrop::new(*self))
    183                }
    184            }
    185        )*
    186    };
    187 }
    188 
    189 impl_trivial_to_shmem!(
    190    (),
    191    bool,
    192    f32,
    193    f64,
    194    i8,
    195    i16,
    196    i32,
    197    i64,
    198    u8,
    199    u16,
    200    u32,
    201    u64,
    202    isize,
    203    usize,
    204    std::num::NonZeroUsize
    205 );
    206 
    207 impl<T> ToShmem for PhantomData<T> {
    208    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
    209        Ok(ManuallyDrop::new(*self))
    210    }
    211 }
    212 
    213 impl<T: ToShmem> ToShmem for Range<T> {
    214    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    215        Ok(ManuallyDrop::new(Range {
    216            start: ManuallyDrop::into_inner(self.start.to_shmem(builder)?),
    217            end: ManuallyDrop::into_inner(self.end.to_shmem(builder)?),
    218        }))
    219    }
    220 }
    221 
    222 impl<T: ToShmem, U: ToShmem> ToShmem for (T, U) {
    223    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    224        Ok(ManuallyDrop::new((
    225            ManuallyDrop::into_inner(self.0.to_shmem(builder)?),
    226            ManuallyDrop::into_inner(self.1.to_shmem(builder)?),
    227        )))
    228    }
    229 }
    230 
    231 impl<T: ToShmem> ToShmem for Wrapping<T> {
    232    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    233        Ok(ManuallyDrop::new(Wrapping(ManuallyDrop::into_inner(
    234            self.0.to_shmem(builder)?,
    235        ))))
    236    }
    237 }
    238 
    239 impl<T: ToShmem> ToShmem for Box<T> {
    240    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    241        // Reserve space for the boxed value.
    242        let dest: *mut T = builder.alloc_value();
    243 
    244        // Make a clone of the boxed value with all of its heap allocations
    245        // placed in the shared memory buffer.
    246        let value = (**self).to_shmem(builder)?;
    247 
    248        unsafe {
    249            // Copy the value into the buffer.
    250            ptr::write(dest, ManuallyDrop::into_inner(value));
    251 
    252            Ok(ManuallyDrop::new(Box::from_raw(dest)))
    253        }
    254    }
    255 }
    256 
    257 /// Converts all the items in `src` into shared memory form, writes them into
    258 /// the specified buffer, and returns a pointer to the slice.
    259 unsafe fn to_shmem_slice_ptr<'a, T, I>(
    260    src: I,
    261    dest: *mut T,
    262    builder: &mut SharedMemoryBuilder,
    263 ) -> std::result::Result<*mut [T], String>
    264 where
    265    T: 'a + ToShmem,
    266    I: ExactSizeIterator<Item = &'a T>,
    267 {
    268    let dest = slice::from_raw_parts_mut(dest, src.len());
    269 
    270    // Make a clone of each element from the iterator with its own heap
    271    // allocations placed in the buffer, and copy that clone into the buffer.
    272    for (src, dest) in src.zip(dest.iter_mut()) {
    273        ptr::write(dest, ManuallyDrop::into_inner(src.to_shmem(builder)?));
    274    }
    275 
    276    Ok(dest)
    277 }
    278 
    279 /// Writes all the items in `src` into a slice in the shared memory buffer and
    280 /// returns a pointer to the slice.
    281 pub unsafe fn to_shmem_slice<'a, T, I>(
    282    src: I,
    283    builder: &mut SharedMemoryBuilder,
    284 ) -> std::result::Result<*mut [T], String>
    285 where
    286    T: 'a + ToShmem,
    287    I: ExactSizeIterator<Item = &'a T>,
    288 {
    289    let dest = builder.alloc_array(src.len());
    290    to_shmem_slice_ptr(src, dest, builder)
    291 }
    292 
    293 impl<T: ToShmem> ToShmem for Box<[T]> {
    294    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    295        unsafe {
    296            let dest = to_shmem_slice(self.iter(), builder)?;
    297            Ok(ManuallyDrop::new(Box::from_raw(dest)))
    298        }
    299    }
    300 }
    301 
    302 impl ToShmem for Box<str> {
    303    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    304        // Reserve space for the string bytes.
    305        let dest: *mut u8 = builder.alloc_array(self.len());
    306 
    307        unsafe {
    308            // Copy the value into the buffer.
    309            ptr::copy(self.as_ptr(), dest, self.len());
    310 
    311            Ok(ManuallyDrop::new(Box::from_raw(
    312                str::from_utf8_unchecked_mut(slice::from_raw_parts_mut(dest, self.len())),
    313            )))
    314        }
    315    }
    316 }
    317 
    318 impl ToShmem for String {
    319    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    320        // Reserve space for the string bytes.
    321        let dest: *mut u8 = builder.alloc_array(self.len());
    322 
    323        unsafe {
    324            // Copy the value into the buffer.
    325            ptr::copy(self.as_ptr(), dest, self.len());
    326 
    327            Ok(ManuallyDrop::new(String::from_raw_parts(
    328                dest,
    329                self.len(),
    330                self.len(),
    331            )))
    332        }
    333    }
    334 }
    335 
    336 impl ToShmem for CString {
    337    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    338        let len = self.as_bytes_with_nul().len();
    339 
    340        // Reserve space for the string bytes.
    341        let dest: *mut c_char = builder.alloc_array(len);
    342 
    343        unsafe {
    344            // Copy the value into the buffer.
    345            ptr::copy(self.as_ptr(), dest, len);
    346 
    347            Ok(ManuallyDrop::new(CString::from_raw(dest)))
    348        }
    349    }
    350 }
    351 
    352 impl<T: ToShmem> ToShmem for Vec<T> {
    353    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    354        unsafe {
    355            let dest = to_shmem_slice(self.iter(), builder)? as *mut T;
    356            let dest_vec = Vec::from_raw_parts(dest, self.len(), self.len());
    357            Ok(ManuallyDrop::new(dest_vec))
    358        }
    359    }
    360 }
    361 
    362 impl<T: ToShmem, S> ToShmem for HashSet<T, S>
    363 where
    364    Self: Default,
    365 {
    366    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
    367        if !self.is_empty() {
    368            return Err(format!(
    369                "ToShmem failed for HashSet: We only support empty sets \
    370                 (we don't expect custom properties in UA sheets, they're observable by content)",
    371            ));
    372        }
    373        Ok(ManuallyDrop::new(Self::default()))
    374    }
    375 }
    376 
    377 impl<T: ToShmem> ToShmem for Option<T> {
    378    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    379        let v = match self {
    380            Some(v) => Some(ManuallyDrop::into_inner(v.to_shmem(builder)?)),
    381            None => None,
    382        };
    383 
    384        Ok(ManuallyDrop::new(v))
    385    }
    386 }
    387 
    388 #[cfg(feature = "smallvec")]
    389 impl<T: ToShmem, A: smallvec::Array<Item = T>> ToShmem for smallvec::SmallVec<A> {
    390    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    391        let dest_vec = unsafe {
    392            if self.spilled() {
    393                // Place the items in a separate allocation in the shared memory
    394                // buffer.
    395                let dest = to_shmem_slice(self.iter(), builder)? as *mut T;
    396                Self::from_raw_parts(dest, self.len(), self.len())
    397            } else {
    398                // Place the items inline.
    399                let mut s = Self::new();
    400                to_shmem_slice_ptr(self.iter(), s.as_mut_ptr(), builder)?;
    401                s.set_len(self.len());
    402                s
    403            }
    404        };
    405 
    406        Ok(ManuallyDrop::new(dest_vec))
    407    }
    408 }
    409 
    410 #[cfg(feature = "servo_arc")]
    411 impl<A: 'static, B: 'static> ToShmem for servo_arc::ArcUnion<A, B>
    412 where
    413    servo_arc::Arc<A>: ToShmem,
    414    servo_arc::Arc<B>: ToShmem,
    415 {
    416    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    417        use servo_arc::ArcUnionBorrow;
    418 
    419        Ok(ManuallyDrop::new(match self.borrow() {
    420            ArcUnionBorrow::First(first) => Self::from_first(ManuallyDrop::into_inner(
    421                first.with_arc(|a| a.to_shmem(builder))?,
    422            )),
    423            ArcUnionBorrow::Second(second) => Self::from_second(ManuallyDrop::into_inner(
    424                second.with_arc(|a| a.to_shmem(builder))?,
    425            )),
    426        }))
    427    }
    428 }
    429 #[cfg(feature = "servo_arc")]
    430 impl<T: ToShmem> ToShmem for servo_arc::Arc<T> {
    431    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    432        // Assert that we don't encounter any shared references to values we
    433        // don't expect.
    434        #[cfg(debug_assertions)]
    435        assert!(
    436            !builder.shared_values.contains(&self.heap_ptr()),
    437            "ToShmem failed for Arc<{}>: encountered a value with multiple \
    438            references.",
    439            std::any::type_name::<T>()
    440        );
    441 
    442        // Make a clone of the Arc-owned value with all of its heap allocations
    443        // placed in the shared memory buffer.
    444        let value = (**self).to_shmem(builder)?;
    445 
    446        // Create a new Arc with the shared value and have it place its
    447        // ArcInner in the shared memory buffer.
    448        unsafe {
    449            let static_arc = Self::new_static(
    450                |layout| builder.alloc(layout),
    451                ManuallyDrop::into_inner(value),
    452            );
    453 
    454            #[cfg(debug_assertions)]
    455            builder.shared_values.insert(self.heap_ptr());
    456 
    457            Ok(ManuallyDrop::new(static_arc))
    458        }
    459    }
    460 }
    461 #[cfg(feature = "servo_arc")]
    462 impl<H: ToShmem, T: ToShmem> ToShmem for servo_arc::Arc<servo_arc::HeaderSlice<H, T>> {
    463    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    464        // We don't currently have any shared ThinArc values in stylesheets,
    465        // so don't support them for now.
    466        #[cfg(debug_assertions)]
    467        assert!(
    468            !builder.shared_values.contains(&self.heap_ptr()),
    469            "ToShmem failed for ThinArc<T>: encountered a value with multiple references, which \
    470             is not currently supported",
    471        );
    472 
    473        // Make a clone of the Arc-owned header and slice values with all of
    474        // their heap allocations placed in the shared memory buffer.
    475        let header = self.header.to_shmem(builder)?;
    476        let mut values = Vec::with_capacity(self.len());
    477        for v in self.slice().iter() {
    478            values.push(v.to_shmem(builder)?);
    479        }
    480 
    481        // Create a new ThinArc with the shared value and have it place
    482        // its ArcInner in the shared memory buffer.
    483        let len = values.len();
    484        let static_arc = Self::from_header_and_iter_alloc(
    485            |layout| builder.alloc(layout),
    486            ManuallyDrop::into_inner(header),
    487            values.into_iter().map(ManuallyDrop::into_inner),
    488            len,
    489            /* is_static = */ true,
    490        );
    491 
    492        #[cfg(debug_assertions)]
    493        builder.shared_values.insert(self.heap_ptr());
    494 
    495        Ok(ManuallyDrop::new(static_arc))
    496    }
    497 }
    498 
    499 #[cfg(feature = "thin-vec")]
    500 impl<T: ToShmem> ToShmem for thin_vec::ThinVec<T> {
    501    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    502        assert_eq!(mem::size_of::<Self>(), mem::size_of::<*const ()>());
    503 
    504        // NOTE: We need to do the work of allocating the header in shared memory even if the
    505        // length is zero, because an empty ThinVec, even though it doesn't allocate, references
    506        // static memory which will not be mapped to other processes, see bug 1841011.
    507        let len = self.len();
    508 
    509        // nsTArrayHeader size.
    510        // FIXME: Would be nice not to hard-code this, but in practice thin-vec crate also relies
    511        // on this.
    512        let header_size = 2 * mem::size_of::<u32>();
    513        let header_align = mem::size_of::<u32>();
    514 
    515        let item_size = mem::size_of::<T>();
    516        let item_align = mem::align_of::<T>();
    517 
    518        // We don't need to support underalignment for now, this could be supported if needed.
    519        assert!(item_align >= header_align);
    520 
    521        // This is explicitly unsupported by ThinVec, see:
    522        // https://searchfox.org/mozilla-central/rev/ad732108b073742d7324f998c085f459674a6846/third_party/rust/thin-vec/src/lib.rs#375-386
    523        assert!(item_align <= header_size);
    524        let header_padding = 0;
    525 
    526        let layout = Layout::from_size_align(
    527            header_size + header_padding + padded_size(item_size, item_align) * len,
    528            item_align,
    529        )
    530        .unwrap();
    531 
    532        let shmem_header_ptr = builder.alloc::<u8>(layout);
    533        let shmem_data_ptr = unsafe { shmem_header_ptr.add(header_size + header_padding) };
    534 
    535        let data_ptr = self.as_ptr() as *const T as *const u8;
    536        let header_ptr = unsafe { data_ptr.sub(header_size + header_padding) };
    537 
    538        unsafe {
    539            // Copy the header. Note this might copy a wrong capacity, but it doesn't matter,
    540            // because shared memory ptrs are immutable anyways, and we can't relocate.
    541            ptr::copy(header_ptr, shmem_header_ptr, header_size);
    542            // ToShmem + copy the contents into the shared buffer.
    543            to_shmem_slice_ptr(self.iter(), shmem_data_ptr as *mut T, builder)?;
    544            // Return the new ThinVec, which is just a pointer to the shared memory buffer.
    545            let shmem_thinvec: Self = mem::transmute(shmem_header_ptr);
    546 
    547            // Sanity-check that the ptr and length match.
    548            debug_assert_eq!(shmem_thinvec.as_ptr(), shmem_data_ptr as *const T);
    549            debug_assert_eq!(shmem_thinvec.len(), len);
    550 
    551            Ok(ManuallyDrop::new(shmem_thinvec))
    552        }
    553    }
    554 }
    555 
    556 #[cfg(feature = "smallbitvec")]
    557 impl ToShmem for smallbitvec::SmallBitVec {
    558    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
    559        use smallbitvec::InternalStorage;
    560 
    561        let storage = match self.clone().into_storage() {
    562            InternalStorage::Spilled(vs) => {
    563                // Reserve space for the boxed slice values.
    564                let len = vs.len();
    565                let dest: *mut usize = builder.alloc_array(len);
    566 
    567                unsafe {
    568                    // Copy the value into the buffer.
    569                    let src = vs.as_ptr() as *const usize;
    570                    ptr::copy(src, dest, len);
    571 
    572                    let dest_slice =
    573                        Box::from_raw(slice::from_raw_parts_mut(dest, len) as *mut [usize]);
    574                    InternalStorage::Spilled(dest_slice)
    575                }
    576            },
    577            InternalStorage::Inline(x) => InternalStorage::Inline(x),
    578        };
    579        Ok(ManuallyDrop::new(unsafe { Self::from_storage(storage) }))
    580    }
    581 }
    582 
    583 #[cfg(feature = "string_cache")]
    584 impl<Static: string_cache::StaticAtomSet> ToShmem for string_cache::Atom<Static> {
    585    fn to_shmem(&self, _: &mut SharedMemoryBuilder) -> Result<Self> {
    586        // NOTE(emilio): In practice, this can be implemented trivially if
    587        // string_cache could expose the implementation detail of static atoms
    588        // being an index into the static table (and panicking in the
    589        // non-static, non-inline cases).
    590        unimplemented!(
    591            "If servo wants to share stylesheets across processes, \
    592             then ToShmem for Atom needs to be implemented"
    593        )
    594    }
    595 }
    596 
    597 #[cfg(feature = "cssparser")]
    598 impl_trivial_to_shmem!(
    599    cssparser::SourceLocation,
    600    cssparser::SourcePosition,
    601    cssparser::TokenSerializationType
    602 );
    603 #[cfg(feature = "cssparser")]
    604 impl ToShmem for cssparser::UnicodeRange {
    605    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
    606        Ok(ManuallyDrop::new(Self {
    607            start: self.start,
    608            end: self.end,
    609        }))
    610    }
    611 }