moveit/
cxx_support.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Support for `cxx` types.

use std::mem::MaybeUninit;
use std::pin::Pin;

use cxx::memory::UniquePtrTarget;
use cxx::UniquePtr;

use crate::move_ref::AsMove;
use crate::slot::DroppingSlot;
use crate::DerefMove;
use crate::Emplace;
use crate::MoveRef;
use crate::TryNew;

/// A type which has the ability to create heap storage space
/// for itself in C++, without initializing that storage.
///
/// # Safety
///
/// Implementers must ensure that the pointer returned by
/// `allocate_uninitialized_cpp_storage` is a valid, non-null,
/// pointer to a new but uninitialized storage block, and that
/// such blocks must be freeable using either of these routes:
///
/// * before they're initialized, using `free_uninitialized_cpp_storage`
/// * after they're initialized, via a delete expression like `delete p;`
pub unsafe trait MakeCppStorage: Sized {
  /// Allocates heap space for this type in C++ and return a pointer
  /// to that space, but do not initialize that space (i.e. do not
  /// yet call a constructor).
  ///
  /// # Safety
  ///
  /// To avoid memory leaks, callers must ensure that this space is
  /// freed using `free_uninitialized_cpp_storage`, or is converted into
  /// a [`UniquePtr`] such that it can later be freed by
  /// `std::unique_ptr<T, std::default_delete<T>>`.
  unsafe fn allocate_uninitialized_cpp_storage() -> *mut Self;

  /// Frees a C++ allocation which has not yet
  /// had a constructor called.
  ///
  /// # Safety
  ///
  /// Callers guarantee that the pointer here was allocated by
  /// `allocate_uninitialized_cpp_storage` and has not been
  /// initialized.
  unsafe fn free_uninitialized_cpp_storage(ptr: *mut Self);
}

impl<T: MakeCppStorage + UniquePtrTarget> Emplace<T> for UniquePtr<T> {
  type Output = Self;

  fn try_emplace<N: TryNew<Output = T>>(n: N) -> Result<Self, N::Error> {
    unsafe {
      let uninit_ptr = T::allocate_uninitialized_cpp_storage();
      let uninit =
        Pin::new_unchecked(&mut *(uninit_ptr as *mut MaybeUninit<T>));
      // FIXME - this is not panic safe.
      let result = n.try_new(uninit);
      if let Err(err) = result {
        T::free_uninitialized_cpp_storage(uninit_ptr);
        return Err(err);
      }
      Ok(UniquePtr::from_raw(uninit_ptr))
    }
  }
}

/// This is an implementation detail of the support for moving out of
/// a [`cxx::UniquePtr`]. It stores a raw pointer which points to
/// C++ space which is allocated yet unoccupied, and will arrange to
/// deallocate that if it goes out of scope.
#[doc(hidden)]
pub struct DeallocateSpaceGuard<T: MakeCppStorage>(*mut T);

impl<T: MakeCppStorage> DeallocateSpaceGuard<T> {
  fn assume_init_mut(&mut self) -> &mut T {
    unsafe { &mut *self.0 }
  }
}

impl<T: MakeCppStorage> Drop for DeallocateSpaceGuard<T> {
  fn drop(&mut self) {
    unsafe { T::free_uninitialized_cpp_storage(self.0) };
  }
}

impl<T> AsMove for UniquePtr<T>
where
  T: UniquePtrTarget + MakeCppStorage,
{
  type Storage = DeallocateSpaceGuard<T>;

  #[inline]
  fn as_move<'frame>(
    self,
    storage: DroppingSlot<'frame, Self::Storage>,
  ) -> Pin<MoveRef<'frame, Self::Target>>
  where
    Self: 'frame,
  {
    let cast = DeallocateSpaceGuard(self.into_raw());
    let (storage, drop_flag) = storage.put(cast);
    let this =
      unsafe { MoveRef::new_unchecked(storage.assume_init_mut(), drop_flag) };
    MoveRef::into_pin(this)
  }
}

unsafe impl<T> DerefMove for UniquePtr<T>
where
  T: MakeCppStorage + UniquePtrTarget,
  T: Unpin,
{
  #[inline]
  fn deref_move<'frame>(
    self,
    storage: DroppingSlot<'frame, Self::Storage>,
  ) -> MoveRef<'frame, Self::Target>
  where
    Self: 'frame,
  {
    Pin::into_inner(self.as_move(storage))
  }
}