1#![crate_name = "to_shmem"]
13#![crate_type = "rlib"]
14
15use std::alloc::Layout;
16use std::collections::HashSet;
17use std::ffi::CString;
18use std::isize;
19use std::marker::PhantomData;
20use std::mem::{self, ManuallyDrop};
21use std::num::Wrapping;
22use std::ops::Range;
23use std::os::raw::c_char;
24use std::ptr::{self, NonNull};
25use std::slice;
26use std::str;
27
28pub type Result<T> = std::result::Result<ManuallyDrop<T>, String>;
32
33pub struct SharedMemoryBuilder {
40 buffer: *mut u8,
42 capacity: usize,
44 index: usize,
47 #[cfg(all(debug_assertions, feature = "servo_arc"))]
52 shared_values: HashSet<*const std::os::raw::c_void>,
53}
54
55fn padding_needed_for(size: usize, align: usize) -> usize {
58 padded_size(size, align).wrapping_sub(size)
59}
60
61fn padded_size(size: usize, align: usize) -> usize {
63 size.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1)
64}
65
66impl SharedMemoryBuilder {
67 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 #[inline]
80 pub fn len(&self) -> usize {
81 self.index
82 }
83
84 pub fn write<T: ToShmem>(&mut self, value: &T) -> std::result::Result<*mut T, String> {
92 let dest: *mut T = self.alloc_value();
94
95 let value = value.to_shmem(self)?;
98
99 unsafe {
100 ptr::write(dest, ManuallyDrop::into_inner(value));
102 }
103
104 Ok(dest)
106 }
107
108 pub fn alloc_value<T>(&mut self) -> *mut T {
113 self.alloc(Layout::new::<T>())
114 }
115
116 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 pub fn alloc<T>(&mut self, layout: Layout) -> *mut T {
136 let padding = padding_needed_for(self.buffer as usize + self.index, layout.align());
142
143 let start = self.index.checked_add(padding).unwrap();
145 assert!(start <= std::isize::MAX as usize); 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
156pub trait ToShmem: Sized {
158 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self>;
171}
172
173#[macro_export]
174macro_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
189impl_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
207impl<T> ToShmem for PhantomData<T> {
208 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
209 Ok(ManuallyDrop::new(*self))
210 }
211}
212
213impl<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
222impl<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
231impl<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
239impl<T: ToShmem> ToShmem for Box<T> {
240 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
241 let dest: *mut T = builder.alloc_value();
243
244 let value = (**self).to_shmem(builder)?;
247
248 unsafe {
249 ptr::write(dest, ManuallyDrop::into_inner(value));
251
252 Ok(ManuallyDrop::new(Box::from_raw(dest)))
253 }
254 }
255}
256
257unsafe 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>
264where
265 T: 'a + ToShmem,
266 I: ExactSizeIterator<Item = &'a T>,
267{
268 let dest = slice::from_raw_parts_mut(dest, src.len());
269
270 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
279pub unsafe fn to_shmem_slice<'a, T, I>(
282 src: I,
283 builder: &mut SharedMemoryBuilder,
284) -> std::result::Result<*mut [T], String>
285where
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
293impl<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
302impl ToShmem for Box<str> {
303 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
304 let dest: *mut u8 = builder.alloc_array(self.len());
306
307 unsafe {
308 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
318impl ToShmem for String {
319 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
320 let dest: *mut u8 = builder.alloc_array(self.len());
322
323 unsafe {
324 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
336impl ToShmem for CString {
337 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
338 let len = self.as_bytes_with_nul().len();
339
340 let dest: *mut c_char = builder.alloc_array(len);
342
343 unsafe {
344 ptr::copy(self.as_ptr(), dest, len);
346
347 Ok(ManuallyDrop::new(CString::from_raw(dest)))
348 }
349 }
350}
351
352impl<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
362impl<T: ToShmem, S> ToShmem for HashSet<T, S>
363where
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
377impl<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")]
389impl<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 let dest = to_shmem_slice(self.iter(), builder)? as *mut T;
396 Self::from_raw_parts(dest, self.len(), self.len())
397 } else {
398 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")]
411impl<A: 'static, B: 'static> ToShmem for servo_arc::ArcUnion<A, B>
412where
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")]
430impl<T: ToShmem> ToShmem for servo_arc::Arc<T> {
431 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
432 #[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 let value = (**self).to_shmem(builder)?;
445
446 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")]
462impl<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 #[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 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 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 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")]
500impl<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 let len = self.len();
508
509 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 assert!(item_align >= header_align);
520
521 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 ptr::copy(header_ptr, shmem_header_ptr, header_size);
542 to_shmem_slice_ptr(self.iter(), shmem_data_ptr as *mut T, builder)?;
544 let shmem_thinvec: Self = mem::transmute(shmem_header_ptr);
546
547 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")]
557impl 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 let len = vs.len();
565 let dest: *mut usize = builder.alloc_array(len);
566
567 unsafe {
568 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")]
584impl<Static: string_cache::StaticAtomSet> ToShmem for string_cache::Atom<Static> {
585 fn to_shmem(&self, _: &mut SharedMemoryBuilder) -> Result<Self> {
586 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")]
598impl_trivial_to_shmem!(
599 cssparser::SourceLocation,
600 cssparser::SourcePosition,
601 cssparser::TokenSerializationType
602);
603#[cfg(feature = "cssparser")]
604impl 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}