#![crate_name = "to_shmem"]
#![crate_type = "rlib"]
use std::alloc::Layout;
use std::collections::HashSet;
use std::ffi::CString;
use std::isize;
use std::marker::PhantomData;
use std::mem::{self, ManuallyDrop};
use std::num::Wrapping;
use std::ops::Range;
use std::os::raw::c_char;
use std::ptr::{self, NonNull};
use std::slice;
use std::str;
pub type Result<T> = std::result::Result<ManuallyDrop<T>, String>;
pub struct SharedMemoryBuilder {
buffer: *mut u8,
capacity: usize,
index: usize,
#[cfg(all(debug_assertions, feature = "servo_arc"))]
shared_values: HashSet<*const std::os::raw::c_void>,
}
fn padding_needed_for(size: usize, align: usize) -> usize {
padded_size(size, align).wrapping_sub(size)
}
fn padded_size(size: usize, align: usize) -> usize {
size.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1)
}
impl SharedMemoryBuilder {
pub unsafe fn new(buffer: *mut u8, capacity: usize) -> SharedMemoryBuilder {
SharedMemoryBuilder {
buffer,
capacity,
index: 0,
#[cfg(all(debug_assertions, feature = "servo_arc"))]
shared_values: HashSet::new(),
}
}
#[inline]
pub fn len(&self) -> usize {
self.index
}
pub fn write<T: ToShmem>(&mut self, value: &T) -> std::result::Result<*mut T, String> {
let dest: *mut T = self.alloc_value();
let value = value.to_shmem(self)?;
unsafe {
ptr::write(dest, ManuallyDrop::into_inner(value));
}
Ok(dest)
}
pub fn alloc_value<T>(&mut self) -> *mut T {
self.alloc(Layout::new::<T>())
}
pub fn alloc_array<T>(&mut self, len: usize) -> *mut T {
if len == 0 {
return NonNull::dangling().as_ptr();
}
let size = mem::size_of::<T>();
let align = mem::align_of::<T>();
self.alloc(Layout::from_size_align(padded_size(size, align) * len, align).unwrap())
}
pub fn alloc<T>(&mut self, layout: Layout) -> *mut T {
let padding = padding_needed_for(self.buffer as usize + self.index, layout.align());
let start = self.index.checked_add(padding).unwrap();
assert!(start <= std::isize::MAX as usize);
let end = start.checked_add(layout.size()).unwrap();
assert!(end <= self.capacity);
self.index = end;
unsafe { self.buffer.add(start) as *mut T }
}
}
pub trait ToShmem: Sized {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self>;
}
#[macro_export]
macro_rules! impl_trivial_to_shmem {
($($ty:ty),*) => {
$(
impl $crate::ToShmem for $ty {
fn to_shmem(
&self,
_builder: &mut $crate::SharedMemoryBuilder,
) -> $crate::Result<Self> {
$crate::Result::Ok(::std::mem::ManuallyDrop::new(*self))
}
}
)*
};
}
impl_trivial_to_shmem!(
(),
bool,
f32,
f64,
i8,
i16,
i32,
i64,
u8,
u16,
u32,
u64,
isize,
usize,
std::num::NonZeroUsize
);
impl<T> ToShmem for PhantomData<T> {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
Ok(ManuallyDrop::new(*self))
}
}
impl<T: ToShmem> ToShmem for Range<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
Ok(ManuallyDrop::new(Range {
start: ManuallyDrop::into_inner(self.start.to_shmem(builder)?),
end: ManuallyDrop::into_inner(self.end.to_shmem(builder)?),
}))
}
}
impl<T: ToShmem, U: ToShmem> ToShmem for (T, U) {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
Ok(ManuallyDrop::new((
ManuallyDrop::into_inner(self.0.to_shmem(builder)?),
ManuallyDrop::into_inner(self.1.to_shmem(builder)?),
)))
}
}
impl<T: ToShmem> ToShmem for Wrapping<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
Ok(ManuallyDrop::new(Wrapping(ManuallyDrop::into_inner(
self.0.to_shmem(builder)?,
))))
}
}
impl<T: ToShmem> ToShmem for Box<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
let dest: *mut T = builder.alloc_value();
let value = (**self).to_shmem(builder)?;
unsafe {
ptr::write(dest, ManuallyDrop::into_inner(value));
Ok(ManuallyDrop::new(Box::from_raw(dest)))
}
}
}
unsafe fn to_shmem_slice_ptr<'a, T, I>(
src: I,
dest: *mut T,
builder: &mut SharedMemoryBuilder,
) -> std::result::Result<*mut [T], String>
where
T: 'a + ToShmem,
I: ExactSizeIterator<Item = &'a T>,
{
let dest = slice::from_raw_parts_mut(dest, src.len());
for (src, dest) in src.zip(dest.iter_mut()) {
ptr::write(dest, ManuallyDrop::into_inner(src.to_shmem(builder)?));
}
Ok(dest)
}
pub unsafe fn to_shmem_slice<'a, T, I>(
src: I,
builder: &mut SharedMemoryBuilder,
) -> std::result::Result<*mut [T], String>
where
T: 'a + ToShmem,
I: ExactSizeIterator<Item = &'a T>,
{
let dest = builder.alloc_array(src.len());
to_shmem_slice_ptr(src, dest, builder)
}
impl<T: ToShmem> ToShmem for Box<[T]> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
unsafe {
let dest = to_shmem_slice(self.iter(), builder)?;
Ok(ManuallyDrop::new(Box::from_raw(dest)))
}
}
}
impl ToShmem for Box<str> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
let dest: *mut u8 = builder.alloc_array(self.len());
unsafe {
ptr::copy(self.as_ptr(), dest, self.len());
Ok(ManuallyDrop::new(Box::from_raw(
str::from_utf8_unchecked_mut(slice::from_raw_parts_mut(dest, self.len())),
)))
}
}
}
impl ToShmem for String {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
let dest: *mut u8 = builder.alloc_array(self.len());
unsafe {
ptr::copy(self.as_ptr(), dest, self.len());
Ok(ManuallyDrop::new(String::from_raw_parts(
dest,
self.len(),
self.len(),
)))
}
}
}
impl ToShmem for CString {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
let len = self.as_bytes_with_nul().len();
let dest: *mut c_char = builder.alloc_array(len);
unsafe {
ptr::copy(self.as_ptr(), dest, len);
Ok(ManuallyDrop::new(CString::from_raw(dest)))
}
}
}
impl<T: ToShmem> ToShmem for Vec<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
unsafe {
let dest = to_shmem_slice(self.iter(), builder)? as *mut T;
let dest_vec = Vec::from_raw_parts(dest, self.len(), self.len());
Ok(ManuallyDrop::new(dest_vec))
}
}
}
impl<T: ToShmem, S> ToShmem for HashSet<T, S>
where
Self: Default,
{
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
if !self.is_empty() {
return Err(format!(
"ToShmem failed for HashSet: We only support empty sets \
(we don't expect custom properties in UA sheets, they're observable by content)",
));
}
Ok(ManuallyDrop::new(Self::default()))
}
}
impl<T: ToShmem> ToShmem for Option<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
let v = match self {
Some(v) => Some(ManuallyDrop::into_inner(v.to_shmem(builder)?)),
None => None,
};
Ok(ManuallyDrop::new(v))
}
}
#[cfg(feature = "smallvec")]
impl<T: ToShmem, A: smallvec::Array<Item = T>> ToShmem for smallvec::SmallVec<A> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
let dest_vec = unsafe {
if self.spilled() {
let dest = to_shmem_slice(self.iter(), builder)? as *mut T;
Self::from_raw_parts(dest, self.len(), self.len())
} else {
let mut s = Self::new();
to_shmem_slice_ptr(self.iter(), s.as_mut_ptr(), builder)?;
s.set_len(self.len());
s
}
};
Ok(ManuallyDrop::new(dest_vec))
}
}
#[cfg(feature = "servo_arc")]
impl<A: 'static, B: 'static> ToShmem for servo_arc::ArcUnion<A, B>
where
servo_arc::Arc<A>: ToShmem,
servo_arc::Arc<B>: ToShmem,
{
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
use servo_arc::ArcUnionBorrow;
Ok(ManuallyDrop::new(match self.borrow() {
ArcUnionBorrow::First(first) => Self::from_first(ManuallyDrop::into_inner(
first.with_arc(|a| a.to_shmem(builder))?,
)),
ArcUnionBorrow::Second(second) => Self::from_second(ManuallyDrop::into_inner(
second.with_arc(|a| a.to_shmem(builder))?,
)),
}))
}
}
#[cfg(feature = "servo_arc")]
impl<T: ToShmem> ToShmem for servo_arc::Arc<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
#[cfg(debug_assertions)]
assert!(
!builder.shared_values.contains(&self.heap_ptr()),
"ToShmem failed for Arc<{}>: encountered a value with multiple \
references.",
std::any::type_name::<T>()
);
let value = (**self).to_shmem(builder)?;
unsafe {
let static_arc = Self::new_static(
|layout| builder.alloc(layout),
ManuallyDrop::into_inner(value),
);
#[cfg(debug_assertions)]
builder.shared_values.insert(self.heap_ptr());
Ok(ManuallyDrop::new(static_arc))
}
}
}
#[cfg(feature = "servo_arc")]
impl<H: ToShmem, T: ToShmem> ToShmem for servo_arc::Arc<servo_arc::HeaderSlice<H, T>> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
#[cfg(debug_assertions)]
assert!(
!builder.shared_values.contains(&self.heap_ptr()),
"ToShmem failed for ThinArc<T>: encountered a value with multiple references, which \
is not currently supported",
);
let header = self.header.to_shmem(builder)?;
let mut values = Vec::with_capacity(self.len());
for v in self.slice().iter() {
values.push(v.to_shmem(builder)?);
}
let len = values.len();
let static_arc = Self::from_header_and_iter_alloc(
|layout| builder.alloc(layout),
ManuallyDrop::into_inner(header),
values.into_iter().map(ManuallyDrop::into_inner),
len,
true,
);
#[cfg(debug_assertions)]
builder.shared_values.insert(self.heap_ptr());
Ok(ManuallyDrop::new(static_arc))
}
}
#[cfg(feature = "thin-vec")]
impl<T: ToShmem> ToShmem for thin_vec::ThinVec<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
assert_eq!(mem::size_of::<Self>(), mem::size_of::<*const ()>());
let len = self.len();
let header_size = 2 * mem::size_of::<u32>();
let header_align = mem::size_of::<u32>();
let item_size = mem::size_of::<T>();
let item_align = mem::align_of::<T>();
assert!(item_align >= header_align);
assert!(item_align <= header_size);
let header_padding = 0;
let layout = Layout::from_size_align(
header_size + header_padding + padded_size(item_size, item_align) * len,
item_align,
)
.unwrap();
let shmem_header_ptr = builder.alloc::<u8>(layout);
let shmem_data_ptr = unsafe { shmem_header_ptr.add(header_size + header_padding) };
let data_ptr = self.as_ptr() as *const T as *const u8;
let header_ptr = unsafe { data_ptr.sub(header_size + header_padding) };
unsafe {
ptr::copy(header_ptr, shmem_header_ptr, header_size);
to_shmem_slice_ptr(self.iter(), shmem_data_ptr as *mut T, builder)?;
let shmem_thinvec: Self = mem::transmute(shmem_header_ptr);
debug_assert_eq!(shmem_thinvec.as_ptr(), shmem_data_ptr as *const T);
debug_assert_eq!(shmem_thinvec.len(), len);
Ok(ManuallyDrop::new(shmem_thinvec))
}
}
}
#[cfg(feature = "smallbitvec")]
impl ToShmem for smallbitvec::SmallBitVec {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
use smallbitvec::InternalStorage;
let storage = match self.clone().into_storage() {
InternalStorage::Spilled(vs) => {
let len = vs.len();
let dest: *mut usize = builder.alloc_array(len);
unsafe {
let src = vs.as_ptr() as *const usize;
ptr::copy(src, dest, len);
let dest_slice =
Box::from_raw(slice::from_raw_parts_mut(dest, len) as *mut [usize]);
InternalStorage::Spilled(dest_slice)
}
},
InternalStorage::Inline(x) => InternalStorage::Inline(x),
};
Ok(ManuallyDrop::new(unsafe {
Self::from_storage(storage)
}))
}
}
#[cfg(feature = "string_cache")]
impl<Static: string_cache::StaticAtomSet> ToShmem for string_cache::Atom<Static> {
fn to_shmem(&self, _: &mut SharedMemoryBuilder) -> Result<Self> {
unimplemented!(
"If servo wants to share stylesheets across processes, \
then ToShmem for Atom needs to be implemented"
)
}
}
#[cfg(feature = "cssparser")]
impl_trivial_to_shmem!(
cssparser::SourceLocation,
cssparser::SourcePosition,
cssparser::TokenSerializationType
);
#[cfg(feature = "cssparser")]
impl ToShmem for cssparser::UnicodeRange {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
Ok(ManuallyDrop::new(Self {
start: self.start,
end: self.end,
}))
}
}