blob: 08c2c17a835d606886bb50d7c4c25f6a8f443337 [file] [log] [blame]
// 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))
}
}