1#![crate_name = "to_shmem"]
13#![crate_type = "rlib"]
14
15use std::alloc::Layout;
16use std::collections::HashSet;
17use std::ffi::CString;
18use std::marker::PhantomData;
19use std::mem::{self, ManuallyDrop};
20use std::num::Wrapping;
21use std::ops::Range;
22use std::os::raw::c_char;
23use std::ptr::{self, NonNull};
24use std::slice;
25use std::str;
26
27pub type Result<T> = std::result::Result<ManuallyDrop<T>, String>;
31
32pub struct SharedMemoryBuilder {
39 buffer: *mut u8,
41 capacity: usize,
43 index: usize,
46 #[cfg(all(debug_assertions, feature = "servo_arc"))]
51 shared_values: HashSet<*const std::os::raw::c_void>,
52}
53
54fn padding_needed_for(size: usize, align: usize) -> usize {
57 padded_size(size, align).wrapping_sub(size)
58}
59
60fn padded_size(size: usize, align: usize) -> usize {
62 size.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1)
63}
64
65impl SharedMemoryBuilder {
66 pub unsafe fn new(buffer: *mut u8, capacity: usize) -> SharedMemoryBuilder {
68 SharedMemoryBuilder {
69 buffer,
70 capacity,
71 index: 0,
72 #[cfg(all(debug_assertions, feature = "servo_arc"))]
73 shared_values: HashSet::new(),
74 }
75 }
76
77 #[inline]
79 pub fn len(&self) -> usize {
80 self.index
81 }
82
83 pub fn write<T: ToShmem>(&mut self, value: &T) -> std::result::Result<*mut T, String> {
91 let dest: *mut T = self.alloc_value();
93
94 let value = value.to_shmem(self)?;
97
98 unsafe {
99 ptr::write(dest, ManuallyDrop::into_inner(value));
101 }
102
103 Ok(dest)
105 }
106
107 pub fn alloc_value<T>(&mut self) -> *mut T {
112 self.alloc(Layout::new::<T>())
113 }
114
115 pub fn alloc_array<T>(&mut self, len: usize) -> *mut T {
120 if len == 0 {
121 return NonNull::dangling().as_ptr();
122 }
123
124 let size = mem::size_of::<T>();
125 let align = mem::align_of::<T>();
126
127 self.alloc(Layout::from_size_align(padded_size(size, align) * len, align).unwrap())
128 }
129
130 pub fn alloc<T>(&mut self, layout: Layout) -> *mut T {
135 let padding = padding_needed_for(self.buffer as usize + self.index, layout.align());
141
142 let start = self.index.checked_add(padding).unwrap();
144 assert!(start <= isize::MAX as usize); let end = start.checked_add(layout.size()).unwrap();
148 assert!(end <= self.capacity);
149
150 self.index = end;
151 unsafe { self.buffer.add(start) as *mut T }
152 }
153}
154
155pub trait ToShmem: Sized {
157 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self>;
170}
171
172#[macro_export]
173macro_rules! impl_trivial_to_shmem {
174 ($($ty:ty),*) => {
175 $(
176 impl $crate::ToShmem for $ty {
177 fn to_shmem(
178 &self,
179 _builder: &mut $crate::SharedMemoryBuilder,
180 ) -> $crate::Result<Self> {
181 $crate::Result::Ok(::std::mem::ManuallyDrop::new(*self))
182 }
183 }
184 )*
185 };
186}
187
188impl_trivial_to_shmem!(
189 (),
190 bool,
191 f32,
192 f64,
193 i8,
194 i16,
195 i32,
196 i64,
197 u8,
198 u16,
199 u32,
200 u64,
201 isize,
202 usize,
203 std::num::NonZeroUsize
204);
205
206impl<T> ToShmem for PhantomData<T> {
207 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
208 Ok(ManuallyDrop::new(*self))
209 }
210}
211
212impl<T: ToShmem> ToShmem for Range<T> {
213 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
214 Ok(ManuallyDrop::new(Range {
215 start: ManuallyDrop::into_inner(self.start.to_shmem(builder)?),
216 end: ManuallyDrop::into_inner(self.end.to_shmem(builder)?),
217 }))
218 }
219}
220
221impl<T: ToShmem, U: ToShmem> ToShmem for (T, U) {
222 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
223 Ok(ManuallyDrop::new((
224 ManuallyDrop::into_inner(self.0.to_shmem(builder)?),
225 ManuallyDrop::into_inner(self.1.to_shmem(builder)?),
226 )))
227 }
228}
229
230impl<T: ToShmem> ToShmem for Wrapping<T> {
231 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
232 Ok(ManuallyDrop::new(Wrapping(ManuallyDrop::into_inner(
233 self.0.to_shmem(builder)?,
234 ))))
235 }
236}
237
238impl<T: ToShmem> ToShmem for Box<T> {
239 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
240 let dest: *mut T = builder.alloc_value();
242
243 let value = (**self).to_shmem(builder)?;
246
247 unsafe {
248 ptr::write(dest, ManuallyDrop::into_inner(value));
250
251 Ok(ManuallyDrop::new(Box::from_raw(dest)))
252 }
253 }
254}
255
256unsafe fn to_shmem_slice_ptr<'a, T, I>(
259 src: I,
260 dest: *mut T,
261 builder: &mut SharedMemoryBuilder,
262) -> std::result::Result<*mut [T], String>
263where
264 T: 'a + ToShmem,
265 I: ExactSizeIterator<Item = &'a T>,
266{
267 unsafe {
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}
279
280pub unsafe fn to_shmem_slice<'a, T, I>(
283 src: I,
284 builder: &mut SharedMemoryBuilder,
285) -> std::result::Result<*mut [T], String>
286where
287 T: 'a + ToShmem,
288 I: ExactSizeIterator<Item = &'a T>,
289{
290 let dest = builder.alloc_array(src.len());
291 unsafe { to_shmem_slice_ptr(src, dest, builder) }
292}
293
294impl<T: ToShmem> ToShmem for Box<[T]> {
295 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
296 unsafe {
297 let dest = to_shmem_slice(self.iter(), builder)?;
298 Ok(ManuallyDrop::new(Box::from_raw(dest)))
299 }
300 }
301}
302
303impl ToShmem for Box<str> {
304 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
305 let dest: *mut u8 = builder.alloc_array(self.len());
307
308 unsafe {
309 ptr::copy(self.as_ptr(), dest, self.len());
311
312 Ok(ManuallyDrop::new(Box::from_raw(
313 str::from_utf8_unchecked_mut(slice::from_raw_parts_mut(dest, self.len())),
314 )))
315 }
316 }
317}
318
319impl ToShmem for String {
320 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
321 let dest: *mut u8 = builder.alloc_array(self.len());
323
324 unsafe {
325 ptr::copy(self.as_ptr(), dest, self.len());
327
328 Ok(ManuallyDrop::new(String::from_raw_parts(
329 dest,
330 self.len(),
331 self.len(),
332 )))
333 }
334 }
335}
336
337impl ToShmem for CString {
338 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
339 let len = self.as_bytes_with_nul().len();
340
341 let dest: *mut c_char = builder.alloc_array(len);
343
344 unsafe {
345 ptr::copy(self.as_ptr(), dest, len);
347
348 Ok(ManuallyDrop::new(CString::from_raw(dest)))
349 }
350 }
351}
352
353impl<T: ToShmem> ToShmem for Vec<T> {
354 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
355 unsafe {
356 let dest = to_shmem_slice(self.iter(), builder)? as *mut T;
357 let dest_vec = Vec::from_raw_parts(dest, self.len(), self.len());
358 Ok(ManuallyDrop::new(dest_vec))
359 }
360 }
361}
362
363impl<T: ToShmem, S> ToShmem for HashSet<T, S>
364where
365 Self: Default,
366{
367 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
368 if !self.is_empty() {
369 return Err(format!(
370 "ToShmem failed for HashSet: We only support empty sets \
371 (we don't expect custom properties in UA sheets, they're observable by content)",
372 ));
373 }
374 Ok(ManuallyDrop::new(Self::default()))
375 }
376}
377
378impl<T: ToShmem> ToShmem for Option<T> {
379 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
380 let v = match self {
381 Some(v) => Some(ManuallyDrop::into_inner(v.to_shmem(builder)?)),
382 None => None,
383 };
384
385 Ok(ManuallyDrop::new(v))
386 }
387}
388
389#[cfg(feature = "smallvec")]
390impl<T: ToShmem, A: smallvec::Array<Item = T>> ToShmem for smallvec::SmallVec<A> {
391 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
392 let dest_vec = unsafe {
393 if self.spilled() {
394 let dest = to_shmem_slice(self.iter(), builder)? as *mut T;
397 Self::from_raw_parts(dest, self.len(), self.len())
398 } else {
399 let mut s = Self::new();
401 to_shmem_slice_ptr(self.iter(), s.as_mut_ptr(), builder)?;
402 s.set_len(self.len());
403 s
404 }
405 };
406
407 Ok(ManuallyDrop::new(dest_vec))
408 }
409}
410
411#[cfg(feature = "servo_arc")]
412impl<A: 'static, B: 'static> ToShmem for servo_arc::ArcUnion<A, B>
413where
414 servo_arc::Arc<A>: ToShmem,
415 servo_arc::Arc<B>: ToShmem,
416{
417 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
418 use servo_arc::ArcUnionBorrow;
419
420 Ok(ManuallyDrop::new(match self.borrow() {
421 ArcUnionBorrow::First(first) => Self::from_first(ManuallyDrop::into_inner(
422 first.with_arc(|a| a.to_shmem(builder))?,
423 )),
424 ArcUnionBorrow::Second(second) => Self::from_second(ManuallyDrop::into_inner(
425 second.with_arc(|a| a.to_shmem(builder))?,
426 )),
427 }))
428 }
429}
430#[cfg(feature = "servo_arc")]
431impl<T: ToShmem> ToShmem for servo_arc::Arc<T> {
432 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
433 #[cfg(debug_assertions)]
436 assert!(
437 !builder.shared_values.contains(&self.heap_ptr()),
438 "ToShmem failed for Arc<{}>: encountered a value with multiple \
439 references.",
440 std::any::type_name::<T>()
441 );
442
443 let value = (**self).to_shmem(builder)?;
446
447 unsafe {
450 let static_arc = Self::new_static(
451 |layout| builder.alloc(layout),
452 ManuallyDrop::into_inner(value),
453 );
454
455 #[cfg(debug_assertions)]
456 builder.shared_values.insert(self.heap_ptr());
457
458 Ok(ManuallyDrop::new(static_arc))
459 }
460 }
461}
462#[cfg(feature = "servo_arc")]
463impl<H: ToShmem, T: ToShmem> ToShmem for servo_arc::Arc<servo_arc::HeaderSlice<H, T>> {
464 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
465 #[cfg(debug_assertions)]
468 assert!(
469 !builder.shared_values.contains(&self.heap_ptr()),
470 "ToShmem failed for ThinArc<T>: encountered a value with multiple references, which \
471 is not currently supported",
472 );
473
474 let header = self.header.to_shmem(builder)?;
477 let mut values = Vec::with_capacity(self.len());
478 for v in self.slice().iter() {
479 values.push(v.to_shmem(builder)?);
480 }
481
482 let len = values.len();
485 let static_arc = Self::from_header_and_iter_alloc(
486 |layout| builder.alloc(layout),
487 ManuallyDrop::into_inner(header),
488 values.into_iter().map(ManuallyDrop::into_inner),
489 len,
490 true,
491 );
492
493 #[cfg(debug_assertions)]
494 builder.shared_values.insert(self.heap_ptr());
495
496 Ok(ManuallyDrop::new(static_arc))
497 }
498}
499
500#[cfg(feature = "thin-vec")]
501impl<T: ToShmem> ToShmem for thin_vec::ThinVec<T> {
502 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
503 assert_eq!(mem::size_of::<Self>(), mem::size_of::<*const ()>());
504
505 let len = self.len();
509
510 let header_size = 2 * mem::size_of::<u32>();
514 let header_align = mem::size_of::<u32>();
515
516 let item_size = mem::size_of::<T>();
517 let item_align = mem::align_of::<T>();
518
519 assert!(item_align >= header_align);
521
522 assert!(item_align <= header_size);
525 let header_padding = 0;
526
527 let layout = Layout::from_size_align(
528 header_size + header_padding + padded_size(item_size, item_align) * len,
529 item_align,
530 )
531 .unwrap();
532
533 let shmem_header_ptr = builder.alloc::<u8>(layout);
534 let shmem_data_ptr = unsafe { shmem_header_ptr.add(header_size + header_padding) };
535
536 let data_ptr = self.as_ptr() as *const T as *const u8;
537 let header_ptr = unsafe { data_ptr.sub(header_size + header_padding) };
538
539 unsafe {
540 ptr::copy(header_ptr, shmem_header_ptr, header_size);
543 to_shmem_slice_ptr(self.iter(), shmem_data_ptr as *mut T, builder)?;
545 let shmem_thinvec: Self = mem::transmute(shmem_header_ptr);
547
548 debug_assert_eq!(shmem_thinvec.as_ptr(), shmem_data_ptr as *const T);
550 debug_assert_eq!(shmem_thinvec.len(), len);
551
552 Ok(ManuallyDrop::new(shmem_thinvec))
553 }
554 }
555}
556
557#[cfg(feature = "smallbitvec")]
558impl ToShmem for smallbitvec::SmallBitVec {
559 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
560 use smallbitvec::InternalStorage;
561
562 let storage = match self.clone().into_storage() {
563 InternalStorage::Spilled(vs) => {
564 let len = vs.len();
566 let dest: *mut usize = builder.alloc_array(len);
567
568 unsafe {
569 let src = vs.as_ptr() as *const usize;
571 ptr::copy(src, dest, len);
572
573 let dest_slice =
574 Box::from_raw(slice::from_raw_parts_mut(dest, len) as *mut [usize]);
575 InternalStorage::Spilled(dest_slice)
576 }
577 },
578 InternalStorage::Inline(x) => InternalStorage::Inline(x),
579 };
580 Ok(ManuallyDrop::new(unsafe { Self::from_storage(storage) }))
581 }
582}
583
584#[cfg(feature = "string_cache")]
585impl<Static: string_cache::StaticAtomSet> ToShmem for string_cache::Atom<Static> {
586 fn to_shmem(&self, _: &mut SharedMemoryBuilder) -> Result<Self> {
587 unimplemented!(
592 "If servo wants to share stylesheets across processes, \
593 then ToShmem for Atom needs to be implemented"
594 )
595 }
596}
597
598impl ToShmem for std::sync::atomic::AtomicBool {
599 fn to_shmem(&self, _: &mut SharedMemoryBuilder) -> Result<Self> {
600 use std::sync::atomic::Ordering;
601 Ok(ManuallyDrop::new(Self::new(self.load(Ordering::Relaxed))))
602 }
603}
604
605#[cfg(feature = "cssparser")]
606impl_trivial_to_shmem!(
607 cssparser::SourceLocation,
608 cssparser::SourcePosition,
609 cssparser::TokenSerializationType
610);
611#[cfg(feature = "cssparser")]
612impl ToShmem for cssparser::UnicodeRange {
613 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
614 Ok(ManuallyDrop::new(Self {
615 start: self.start,
616 end: self.end,
617 }))
618 }
619}