1#![crate_name = "to_shmem"]
13#![crate_type = "rlib"]
14
15use hashbrown::{HashMap, HashSet};
16use std::alloc::Layout;
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) -> Self {
72 Self {
73 buffer,
74 capacity,
75 index: 0,
76 #[cfg(all(debug_assertions, feature = "servo_arc"))]
77 shared_values: HashSet::new(),
78 }
79 }
80
81 #[inline]
83 pub fn len(&self) -> usize {
84 self.index
85 }
86
87 #[inline]
89 pub fn is_empty(&self) -> bool {
90 self.index == 0
91 }
92
93 pub fn write<T: ToShmem>(&mut self, value: &T) -> std::result::Result<*mut T, String> {
101 let dest: *mut T = self.alloc_value();
103
104 let value = value.to_shmem(self)?;
107
108 unsafe {
109 ptr::write(dest, ManuallyDrop::into_inner(value));
111 }
112
113 Ok(dest)
115 }
116
117 pub fn alloc_value<T>(&mut self) -> *mut T {
122 self.alloc(Layout::new::<T>())
123 }
124
125 pub fn alloc_array<T>(&mut self, len: usize) -> *mut T {
130 if len == 0 {
131 return NonNull::dangling().as_ptr();
132 }
133
134 let size = mem::size_of::<T>();
135 let align = mem::align_of::<T>();
136
137 self.alloc(Layout::from_size_align(padded_size(size, align) * len, align).unwrap())
138 }
139
140 pub fn alloc<T>(&mut self, layout: Layout) -> *mut T {
145 let padding = padding_needed_for(self.buffer as usize + self.index, layout.align());
151
152 let start = self.index.checked_add(padding).unwrap();
154 assert!(start <= isize::MAX as usize); let end = start.checked_add(layout.size()).unwrap();
158 assert!(end <= self.capacity);
159
160 self.index = end;
161 unsafe { self.buffer.add(start) as *mut T }
162 }
163}
164
165pub trait ToShmem: Sized {
167 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self>;
180}
181
182#[macro_export]
183macro_rules! impl_trivial_to_shmem {
184 ($($ty:ty),*) => {
185 $(
186 impl $crate::ToShmem for $ty {
187 fn to_shmem(
188 &self,
189 _builder: &mut $crate::SharedMemoryBuilder,
190 ) -> $crate::Result<Self> {
191 $crate::Result::Ok(::std::mem::ManuallyDrop::new(*self))
192 }
193 }
194 )*
195 };
196}
197
198impl_trivial_to_shmem!(
199 (),
200 bool,
201 f32,
202 f64,
203 i8,
204 i16,
205 i32,
206 i64,
207 u8,
208 u16,
209 u32,
210 u64,
211 isize,
212 usize,
213 std::num::NonZeroUsize
214);
215
216impl<T> ToShmem for PhantomData<T> {
217 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
218 Ok(ManuallyDrop::new(*self))
219 }
220}
221
222impl<T: ToShmem> ToShmem for Range<T> {
223 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
224 Ok(ManuallyDrop::new(Range {
225 start: ManuallyDrop::into_inner(self.start.to_shmem(builder)?),
226 end: ManuallyDrop::into_inner(self.end.to_shmem(builder)?),
227 }))
228 }
229}
230
231impl<T: ToShmem, U: ToShmem> ToShmem for (T, U) {
232 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
233 Ok(ManuallyDrop::new((
234 ManuallyDrop::into_inner(self.0.to_shmem(builder)?),
235 ManuallyDrop::into_inner(self.1.to_shmem(builder)?),
236 )))
237 }
238}
239
240impl<T: ToShmem> ToShmem for Wrapping<T> {
241 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
242 Ok(ManuallyDrop::new(Wrapping(ManuallyDrop::into_inner(
243 self.0.to_shmem(builder)?,
244 ))))
245 }
246}
247
248impl<T: ToShmem> ToShmem for Box<T> {
249 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
250 let dest: *mut T = builder.alloc_value();
252
253 let value = (**self).to_shmem(builder)?;
256
257 unsafe {
258 ptr::write(dest, ManuallyDrop::into_inner(value));
260
261 Ok(ManuallyDrop::new(Box::from_raw(dest)))
262 }
263 }
264}
265
266unsafe fn to_shmem_slice_ptr<'a, T>(
273 src: impl ExactSizeIterator<Item = &'a T>,
274 dest: *mut T,
275 builder: &mut SharedMemoryBuilder,
276) -> std::result::Result<*mut [T], String>
277where
278 T: ToShmem + 'a,
279{
280 unsafe {
281 let dest = slice::from_raw_parts_mut(dest, src.len());
282
283 for (src, dest) in src.zip(dest.iter_mut()) {
286 ptr::write(dest, ManuallyDrop::into_inner(src.to_shmem(builder)?));
287 }
288
289 Ok(dest)
290 }
291}
292
293pub unsafe fn to_shmem_slice<'a, T>(
300 src: impl ExactSizeIterator<Item = &'a T>,
301 builder: &mut SharedMemoryBuilder,
302) -> std::result::Result<*mut [T], String>
303where
304 T: ToShmem + 'a,
305{
306 let dest = builder.alloc_array(src.len());
307 unsafe { to_shmem_slice_ptr(src, dest, builder) }
308}
309
310impl<T: ToShmem> ToShmem for Box<[T]> {
311 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
312 unsafe {
313 let dest = to_shmem_slice(self.iter(), builder)?;
314 Ok(ManuallyDrop::new(Box::from_raw(dest)))
315 }
316 }
317}
318
319impl ToShmem for Box<str> {
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(Box::from_raw(
329 str::from_utf8_unchecked_mut(slice::from_raw_parts_mut(dest, self.len())),
330 )))
331 }
332 }
333}
334
335impl ToShmem for String {
336 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
337 let dest: *mut u8 = builder.alloc_array(self.len());
339
340 unsafe {
341 ptr::copy(self.as_ptr(), dest, self.len());
343
344 Ok(ManuallyDrop::new(String::from_raw_parts(
345 dest,
346 self.len(),
347 self.len(),
348 )))
349 }
350 }
351}
352
353impl ToShmem for CString {
354 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
355 let len = self.as_bytes_with_nul().len();
356
357 let dest: *mut c_char = builder.alloc_array(len);
359
360 unsafe {
361 ptr::copy(self.as_ptr(), dest, len);
363
364 Ok(ManuallyDrop::new(CString::from_raw(dest)))
365 }
366 }
367}
368
369impl<T: ToShmem> ToShmem for Vec<T> {
370 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
371 unsafe {
372 let dest = to_shmem_slice(self.iter(), builder)? as *mut T;
373 let dest_vec = Vec::from_raw_parts(dest, self.len(), self.len());
374 Ok(ManuallyDrop::new(dest_vec))
375 }
376 }
377}
378
379impl<T: ToShmem, S> ToShmem for HashSet<T, S>
380where
381 Self: Default,
382{
383 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
384 if !self.is_empty() {
385 return Err("ToShmem failed for HashSet: We only support empty sets \
386 (we don't expect custom properties in UA sheets, they're observable by content)"
387 .to_string());
388 }
389 Ok(ManuallyDrop::new(Self::default()))
390 }
391}
392
393impl<K: ToShmem, V: ToShmem, S> ToShmem for HashMap<K, V, S>
394where
395 Self: Default,
396{
397 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
398 if !self.is_empty() {
399 return Err("ToShmem failed for HashMap: We only support empty maps".to_string());
400 }
401 Ok(ManuallyDrop::new(Self::default()))
402 }
403}
404
405impl<T: ToShmem> ToShmem for Option<T> {
406 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
407 let v = match self {
408 Some(v) => Some(ManuallyDrop::into_inner(v.to_shmem(builder)?)),
409 None => None,
410 };
411
412 Ok(ManuallyDrop::new(v))
413 }
414}
415
416#[cfg(feature = "smallvec")]
417impl<T: ToShmem, A: smallvec::Array<Item = T>> ToShmem for smallvec::SmallVec<A> {
418 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
419 let dest_vec = unsafe {
420 if self.spilled() {
421 let dest = to_shmem_slice(self.iter(), builder)? as *mut T;
424 Self::from_raw_parts(dest, self.len(), self.len())
425 } else {
426 let mut s = Self::new();
428 to_shmem_slice_ptr(self.iter(), s.as_mut_ptr(), builder)?;
429 s.set_len(self.len());
430 s
431 }
432 };
433
434 Ok(ManuallyDrop::new(dest_vec))
435 }
436}
437
438#[cfg(feature = "servo_arc")]
439impl<A: 'static, B: 'static> ToShmem for servo_arc::ArcUnion<A, B>
440where
441 servo_arc::Arc<A>: ToShmem,
442 servo_arc::Arc<B>: ToShmem,
443{
444 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
445 use servo_arc::ArcUnionBorrow;
446
447 Ok(ManuallyDrop::new(match self.borrow() {
448 ArcUnionBorrow::First(first) => Self::from_first(ManuallyDrop::into_inner(
449 first.with_arc(|a| a.to_shmem(builder))?,
450 )),
451 ArcUnionBorrow::Second(second) => Self::from_second(ManuallyDrop::into_inner(
452 second.with_arc(|a| a.to_shmem(builder))?,
453 )),
454 }))
455 }
456}
457#[cfg(feature = "servo_arc")]
458impl<T: ToShmem> ToShmem for servo_arc::Arc<T> {
459 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
460 #[cfg(debug_assertions)]
463 assert!(
464 !builder.shared_values.contains(&self.heap_ptr()),
465 "ToShmem failed for Arc<{}>: encountered a value with multiple \
466 references.",
467 std::any::type_name::<T>()
468 );
469
470 let value = (**self).to_shmem(builder)?;
473
474 unsafe {
477 let static_arc = Self::new_static(
478 |layout| builder.alloc(layout),
479 ManuallyDrop::into_inner(value),
480 );
481
482 #[cfg(debug_assertions)]
483 builder.shared_values.insert(self.heap_ptr());
484
485 Ok(ManuallyDrop::new(static_arc))
486 }
487 }
488}
489#[cfg(feature = "servo_arc")]
490impl<H: ToShmem, T: ToShmem> ToShmem for servo_arc::Arc<servo_arc::HeaderSlice<H, T>> {
491 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
492 #[cfg(debug_assertions)]
495 assert!(
496 !builder.shared_values.contains(&self.heap_ptr()),
497 "ToShmem failed for ThinArc<T>: encountered a value with multiple references, which \
498 is not currently supported",
499 );
500
501 let header = self.header.to_shmem(builder)?;
504 let mut values = Vec::with_capacity(self.len());
505 for v in self.slice().iter() {
506 values.push(v.to_shmem(builder)?);
507 }
508
509 let len = values.len();
512 let static_arc = Self::from_header_and_iter_alloc(
513 |layout| builder.alloc(layout),
514 ManuallyDrop::into_inner(header),
515 values.into_iter().map(ManuallyDrop::into_inner),
516 len,
517 true,
518 );
519
520 #[cfg(debug_assertions)]
521 builder.shared_values.insert(self.heap_ptr());
522
523 Ok(ManuallyDrop::new(static_arc))
524 }
525}
526
527#[cfg(feature = "thin-vec")]
528impl<T: ToShmem> ToShmem for thin_vec::ThinVec<T> {
529 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
530 assert_eq!(mem::size_of::<Self>(), mem::size_of::<*const ()>());
531
532 let len = self.len();
536
537 let header_size = 2 * mem::size_of::<u32>();
541 let header_align = mem::size_of::<u32>();
542
543 let item_size = mem::size_of::<T>();
544 let item_align = mem::align_of::<T>();
545
546 assert!(item_align >= header_align);
548
549 assert!(item_align <= header_size);
552 let header_padding = 0;
553
554 let layout = Layout::from_size_align(
555 header_size + header_padding + padded_size(item_size, item_align) * len,
556 item_align,
557 )
558 .unwrap();
559
560 let shmem_header_ptr = builder.alloc::<u8>(layout);
561 let shmem_data_ptr = unsafe { shmem_header_ptr.add(header_size + header_padding) };
562
563 let data_ptr = self.as_ptr() as *const u8;
564 let header_ptr = unsafe { data_ptr.sub(header_size + header_padding) };
565
566 unsafe {
567 ptr::copy(header_ptr, shmem_header_ptr, header_size);
570 to_shmem_slice_ptr(self.iter(), shmem_data_ptr as *mut T, builder)?;
572 let shmem_thinvec: Self = mem::transmute(shmem_header_ptr);
574
575 debug_assert_eq!(shmem_thinvec.as_ptr(), shmem_data_ptr as *const T);
577 debug_assert_eq!(shmem_thinvec.len(), len);
578
579 Ok(ManuallyDrop::new(shmem_thinvec))
580 }
581 }
582}
583
584#[cfg(feature = "smallbitvec")]
585impl ToShmem for smallbitvec::SmallBitVec {
586 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
587 use smallbitvec::InternalStorage;
588
589 let storage = match self.clone().into_storage() {
590 InternalStorage::Spilled(vs) => {
591 let len = vs.len();
593 let dest: *mut usize = builder.alloc_array(len);
594
595 unsafe {
596 let src = vs.as_ptr();
598 ptr::copy(src, dest, len);
599
600 let dest_slice = Box::from_raw(std::ptr::slice_from_raw_parts_mut(dest, len));
601 InternalStorage::Spilled(dest_slice)
602 }
603 },
604 InternalStorage::Inline(x) => InternalStorage::Inline(x),
605 };
606 Ok(ManuallyDrop::new(unsafe { Self::from_storage(storage) }))
607 }
608}
609
610#[cfg(feature = "string_cache")]
611impl<Static: string_cache::StaticAtomSet> ToShmem for string_cache::Atom<Static> {
612 fn to_shmem(&self, _: &mut SharedMemoryBuilder) -> Result<Self> {
613 unimplemented!(
618 "If servo wants to share stylesheets across processes, \
619 then ToShmem for Atom needs to be implemented"
620 )
621 }
622}
623
624impl ToShmem for std::sync::atomic::AtomicBool {
625 fn to_shmem(&self, _: &mut SharedMemoryBuilder) -> Result<Self> {
626 use std::sync::atomic::Ordering;
627 Ok(ManuallyDrop::new(Self::new(self.load(Ordering::Relaxed))))
628 }
629}
630
631#[cfg(feature = "cssparser")]
632impl_trivial_to_shmem!(
633 cssparser::SourceLocation,
634 cssparser::SourcePosition,
635 cssparser::TokenSerializationType
636);
637#[cfg(feature = "cssparser")]
638impl ToShmem for cssparser::UnicodeRange {
639 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
640 Ok(ManuallyDrop::new(Self {
641 start: self.start,
642 end: self.end,
643 }))
644 }
645}