1#![crate_name = "to_shmem"]
13#![crate_type = "rlib"]
14
15use hashbrown::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<T: ToShmem> ToShmem for Option<T> {
394 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
395 let v = match self {
396 Some(v) => Some(ManuallyDrop::into_inner(v.to_shmem(builder)?)),
397 None => None,
398 };
399
400 Ok(ManuallyDrop::new(v))
401 }
402}
403
404#[cfg(feature = "smallvec")]
405impl<T: ToShmem, A: smallvec::Array<Item = T>> ToShmem for smallvec::SmallVec<A> {
406 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
407 let dest_vec = unsafe {
408 if self.spilled() {
409 let dest = to_shmem_slice(self.iter(), builder)? as *mut T;
412 Self::from_raw_parts(dest, self.len(), self.len())
413 } else {
414 let mut s = Self::new();
416 to_shmem_slice_ptr(self.iter(), s.as_mut_ptr(), builder)?;
417 s.set_len(self.len());
418 s
419 }
420 };
421
422 Ok(ManuallyDrop::new(dest_vec))
423 }
424}
425
426#[cfg(feature = "servo_arc")]
427impl<A: 'static, B: 'static> ToShmem for servo_arc::ArcUnion<A, B>
428where
429 servo_arc::Arc<A>: ToShmem,
430 servo_arc::Arc<B>: ToShmem,
431{
432 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
433 use servo_arc::ArcUnionBorrow;
434
435 Ok(ManuallyDrop::new(match self.borrow() {
436 ArcUnionBorrow::First(first) => Self::from_first(ManuallyDrop::into_inner(
437 first.with_arc(|a| a.to_shmem(builder))?,
438 )),
439 ArcUnionBorrow::Second(second) => Self::from_second(ManuallyDrop::into_inner(
440 second.with_arc(|a| a.to_shmem(builder))?,
441 )),
442 }))
443 }
444}
445#[cfg(feature = "servo_arc")]
446impl<T: ToShmem> ToShmem for servo_arc::Arc<T> {
447 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
448 #[cfg(debug_assertions)]
451 assert!(
452 !builder.shared_values.contains(&self.heap_ptr()),
453 "ToShmem failed for Arc<{}>: encountered a value with multiple \
454 references.",
455 std::any::type_name::<T>()
456 );
457
458 let value = (**self).to_shmem(builder)?;
461
462 unsafe {
465 let static_arc = Self::new_static(
466 |layout| builder.alloc(layout),
467 ManuallyDrop::into_inner(value),
468 );
469
470 #[cfg(debug_assertions)]
471 builder.shared_values.insert(self.heap_ptr());
472
473 Ok(ManuallyDrop::new(static_arc))
474 }
475 }
476}
477#[cfg(feature = "servo_arc")]
478impl<H: ToShmem, T: ToShmem> ToShmem for servo_arc::Arc<servo_arc::HeaderSlice<H, T>> {
479 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
480 #[cfg(debug_assertions)]
483 assert!(
484 !builder.shared_values.contains(&self.heap_ptr()),
485 "ToShmem failed for ThinArc<T>: encountered a value with multiple references, which \
486 is not currently supported",
487 );
488
489 let header = self.header.to_shmem(builder)?;
492 let mut values = Vec::with_capacity(self.len());
493 for v in self.slice().iter() {
494 values.push(v.to_shmem(builder)?);
495 }
496
497 let len = values.len();
500 let static_arc = Self::from_header_and_iter_alloc(
501 |layout| builder.alloc(layout),
502 ManuallyDrop::into_inner(header),
503 values.into_iter().map(ManuallyDrop::into_inner),
504 len,
505 true,
506 );
507
508 #[cfg(debug_assertions)]
509 builder.shared_values.insert(self.heap_ptr());
510
511 Ok(ManuallyDrop::new(static_arc))
512 }
513}
514
515#[cfg(feature = "thin-vec")]
516impl<T: ToShmem> ToShmem for thin_vec::ThinVec<T> {
517 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
518 assert_eq!(mem::size_of::<Self>(), mem::size_of::<*const ()>());
519
520 let len = self.len();
524
525 let header_size = 2 * mem::size_of::<u32>();
529 let header_align = mem::size_of::<u32>();
530
531 let item_size = mem::size_of::<T>();
532 let item_align = mem::align_of::<T>();
533
534 assert!(item_align >= header_align);
536
537 assert!(item_align <= header_size);
540 let header_padding = 0;
541
542 let layout = Layout::from_size_align(
543 header_size + header_padding + padded_size(item_size, item_align) * len,
544 item_align,
545 )
546 .unwrap();
547
548 let shmem_header_ptr = builder.alloc::<u8>(layout);
549 let shmem_data_ptr = unsafe { shmem_header_ptr.add(header_size + header_padding) };
550
551 let data_ptr = self.as_ptr() as *const u8;
552 let header_ptr = unsafe { data_ptr.sub(header_size + header_padding) };
553
554 unsafe {
555 ptr::copy(header_ptr, shmem_header_ptr, header_size);
558 to_shmem_slice_ptr(self.iter(), shmem_data_ptr as *mut T, builder)?;
560 let shmem_thinvec: Self = mem::transmute(shmem_header_ptr);
562
563 debug_assert_eq!(shmem_thinvec.as_ptr(), shmem_data_ptr as *const T);
565 debug_assert_eq!(shmem_thinvec.len(), len);
566
567 Ok(ManuallyDrop::new(shmem_thinvec))
568 }
569 }
570}
571
572#[cfg(feature = "smallbitvec")]
573impl ToShmem for smallbitvec::SmallBitVec {
574 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
575 use smallbitvec::InternalStorage;
576
577 let storage = match self.clone().into_storage() {
578 InternalStorage::Spilled(vs) => {
579 let len = vs.len();
581 let dest: *mut usize = builder.alloc_array(len);
582
583 unsafe {
584 let src = vs.as_ptr();
586 ptr::copy(src, dest, len);
587
588 let dest_slice = Box::from_raw(std::ptr::slice_from_raw_parts_mut(dest, len));
589 InternalStorage::Spilled(dest_slice)
590 }
591 },
592 InternalStorage::Inline(x) => InternalStorage::Inline(x),
593 };
594 Ok(ManuallyDrop::new(unsafe { Self::from_storage(storage) }))
595 }
596}
597
598#[cfg(feature = "string_cache")]
599impl<Static: string_cache::StaticAtomSet> ToShmem for string_cache::Atom<Static> {
600 fn to_shmem(&self, _: &mut SharedMemoryBuilder) -> Result<Self> {
601 unimplemented!(
606 "If servo wants to share stylesheets across processes, \
607 then ToShmem for Atom needs to be implemented"
608 )
609 }
610}
611
612impl ToShmem for std::sync::atomic::AtomicBool {
613 fn to_shmem(&self, _: &mut SharedMemoryBuilder) -> Result<Self> {
614 use std::sync::atomic::Ordering;
615 Ok(ManuallyDrop::new(Self::new(self.load(Ordering::Relaxed))))
616 }
617}
618
619#[cfg(feature = "cssparser")]
620impl_trivial_to_shmem!(
621 cssparser::SourceLocation,
622 cssparser::SourcePosition,
623 cssparser::TokenSerializationType
624);
625#[cfg(feature = "cssparser")]
626impl ToShmem for cssparser::UnicodeRange {
627 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
628 Ok(ManuallyDrop::new(Self {
629 start: self.start,
630 end: self.end,
631 }))
632 }
633}