// 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>`. 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 Emplace for UniquePtr { type Output = Self; fn try_emplace>(n: N) -> Result { unsafe { let uninit_ptr = T::allocate_uninitialized_cpp_storage(); let uninit = Pin::new_unchecked(&mut *(uninit_ptr as *mut MaybeUninit)); // 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(*mut T); impl DeallocateSpaceGuard { fn assume_init_mut(&mut self) -> &mut T { unsafe { &mut *self.0 } } } impl Drop for DeallocateSpaceGuard { fn drop(&mut self) { unsafe { T::free_uninitialized_cpp_storage(self.0) }; } } impl AsMove for UniquePtr where T: UniquePtrTarget + MakeCppStorage, { type Storage = DeallocateSpaceGuard; #[inline] fn as_move<'frame>( self, storage: DroppingSlot<'frame, Self::Storage>, ) -> Pin> 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 DerefMove for UniquePtr 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)) } }