/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* Useful extensions to UniquePtr. */ #ifndef mozilla_UniquePtrExtensions_h #define mozilla_UniquePtrExtensions_h #include #include "mozilla/Attributes.h" #include "mozilla/DebugOnly.h" #include "mozilla/fallible.h" #include "mozilla/UniquePtr.h" #ifdef XP_WIN # include #endif #if defined(XP_MACOSX) && !defined(RUST_BINDGEN) # include #endif namespace mozilla { /** * MakeUniqueFallible works exactly like MakeUnique, except that the memory * allocation performed is done fallibly, i.e. it can return nullptr. */ template typename detail::UniqueSelector::SingleObject MakeUniqueFallible( Args&&... aArgs) { return UniquePtr(new (fallible) T(std::forward(aArgs)...)); } template typename detail::UniqueSelector::UnknownBound MakeUniqueFallible( decltype(sizeof(int)) aN) { using ArrayType = std::remove_extent_t; return UniquePtr(new (fallible) ArrayType[aN]()); } template typename detail::UniqueSelector::KnownBound MakeUniqueFallible( Args&&... aArgs) = delete; /** * MakeUniqueForOverwrite and MakeUniqueFallibleForOverwrite are like MakeUnique * and MakeUniqueFallible except they use default-initialization. This is * useful, for example, when you have a POD type array that will be overwritten * directly after construction and so zero-initialization is a waste. */ template typename detail::UniqueSelector::SingleObject MakeUniqueForOverwrite() { return UniquePtr(new T); } template typename detail::UniqueSelector::UnknownBound MakeUniqueForOverwrite( decltype(sizeof(int)) aN) { using ArrayType = std::remove_extent_t; return UniquePtr(new ArrayType[aN]); } template typename detail::UniqueSelector::KnownBound MakeUniqueForOverwrite( Args&&... aArgs) = delete; template typename detail::UniqueSelector::SingleObject MakeUniqueForOverwriteFallible() { return UniquePtr(new (fallible) T); } template typename detail::UniqueSelector::UnknownBound MakeUniqueForOverwriteFallible( decltype(sizeof(int)) aN) { using ArrayType = std::remove_extent_t; return UniquePtr(new (fallible) ArrayType[aN]); } template typename detail::UniqueSelector::KnownBound MakeUniqueForOverwriteFallible( Args&&... aArgs) = delete; namespace detail { template struct FreePolicy { void operator()(const void* ptr) { free(const_cast(ptr)); } }; #if defined(XP_WIN) // Can't include to get the actual definition of HANDLE // because of namespace pollution. typedef void* FileHandleType; #elif defined(XP_UNIX) typedef int FileHandleType; #else # error "Unsupported OS?" #endif struct FileHandleHelper { MOZ_IMPLICIT FileHandleHelper(FileHandleType aHandle) : mHandle(aHandle) {} MOZ_IMPLICIT constexpr FileHandleHelper(std::nullptr_t) : mHandle(kInvalidHandle) {} bool operator!=(std::nullptr_t) const { #ifdef XP_WIN // Windows uses both nullptr and INVALID_HANDLE_VALUE (-1 cast to // HANDLE) in different situations, but nullptr is more reliably // null while -1 is also valid input to some calls that take // handles. So class considers both to be null (since neither // should be closed) but default-constructs as nullptr. if (mHandle == (void*)-1) { return false; } #endif return mHandle != kInvalidHandle; } operator FileHandleType() const { return mHandle; } #ifdef XP_WIN // NSPR uses an integer type for PROsfd, so this conversion is // provided for working with it without needing reinterpret casts // everywhere. operator std::intptr_t() const { return reinterpret_cast(mHandle); } #endif // When there's only one user-defined conversion operator, the // compiler will use that to derive equality, but that doesn't work // when the conversion is ambiguoug (the XP_WIN case above). bool operator==(const FileHandleHelper& aOther) const { return mHandle == aOther.mHandle; } private: FileHandleType mHandle; #ifdef XP_WIN // See above for why this is nullptr. (Also, INVALID_HANDLE_VALUE // can't be expressed as a constexpr.) static constexpr FileHandleType kInvalidHandle = nullptr; #else static constexpr FileHandleType kInvalidHandle = -1; #endif }; struct FileHandleDeleter { using pointer = FileHandleHelper; using receiver = FileHandleType; MFBT_API void operator()(FileHandleHelper aHelper); }; #if defined(XP_MACOSX) && !defined(RUST_BINDGEN) struct MachPortHelper { MOZ_IMPLICIT MachPortHelper(mach_port_t aPort) : mPort(aPort) {} MOZ_IMPLICIT constexpr MachPortHelper(std::nullptr_t) : mPort(MACH_PORT_NULL) {} bool operator!=(std::nullptr_t) const { return mPort != MACH_PORT_NULL; } operator const mach_port_t&() const { return mPort; } operator mach_port_t&() { return mPort; } private: mach_port_t mPort; }; struct MachSendRightDeleter { using pointer = MachPortHelper; using receiver = mach_port_t; MFBT_API void operator()(MachPortHelper aHelper) { DebugOnly kr = mach_port_deallocate(mach_task_self(), aHelper); MOZ_ASSERT(kr == KERN_SUCCESS, "failed to deallocate mach send right"); } }; struct MachReceiveRightDeleter { using pointer = MachPortHelper; using receiver = mach_port_t; MFBT_API void operator()(MachPortHelper aHelper) { DebugOnly kr = mach_port_mod_refs( mach_task_self(), aHelper, MACH_PORT_RIGHT_RECEIVE, -1); MOZ_ASSERT(kr == KERN_SUCCESS, "failed to release mach receive right"); } }; struct MachPortSetDeleter { using pointer = MachPortHelper; using receiver = mach_port_t; MFBT_API void operator()(MachPortHelper aHelper) { DebugOnly kr = mach_port_mod_refs( mach_task_self(), aHelper, MACH_PORT_RIGHT_PORT_SET, -1); MOZ_ASSERT(kr == KERN_SUCCESS, "failed to release mach port set"); } }; #endif } // namespace detail template using UniqueFreePtr = UniquePtr>; // A RAII class for the OS construct used for open files and similar // objects: a file descriptor on Unix or a handle on Windows. using UniqueFileHandle = UniquePtr; #if defined(XP_MACOSX) && !defined(RUST_BINDGEN) // A RAII class for a Mach port that names a send right. using UniqueMachSendRight = UniquePtr; // A RAII class for a Mach port that names a receive right. using UniqueMachReceiveRight = UniquePtr; // A RAII class for a Mach port set. using UniqueMachPortSet = UniquePtr; // Increases the user reference count for MACH_PORT_RIGHT_SEND by 1 and returns // a new UniqueMachSendRight to manage the additional right. inline UniqueMachSendRight RetainMachSendRight(mach_port_t aPort) { kern_return_t kr = mach_port_mod_refs(mach_task_self(), aPort, MACH_PORT_RIGHT_SEND, 1); if (kr == KERN_SUCCESS) { return UniqueMachSendRight(aPort); } return nullptr; } #endif namespace detail { struct HasReceiverTypeHelper { template static double Test(...); template static char Test(typename U::receiver* = 0); }; template class HasReceiverType : public std::integral_constant( 0)) == 1> {}; template ::value> struct ReceiverTypeImpl { using Type = typename D::receiver; }; template struct ReceiverTypeImpl { using Type = typename PointerType::Type; }; template struct ReceiverType { using Type = typename ReceiverTypeImpl>::Type; }; template class MOZ_TEMPORARY_CLASS UniquePtrGetterTransfers { public: using Ptr = UniquePtr; using Receiver = typename detail::ReceiverType::Type; explicit UniquePtrGetterTransfers(Ptr& p) : mPtr(p), mReceiver(typename Ptr::Pointer(nullptr)) {} ~UniquePtrGetterTransfers() { mPtr.reset(mReceiver); } operator Receiver*() { return &mReceiver; } Receiver& operator*() { return mReceiver; } // operator void** is conditionally enabled if `Receiver` is a pointer. template && std::is_same_v, int> = 0> operator void**() { return reinterpret_cast(&mReceiver); } private: Ptr& mPtr; Receiver mReceiver; }; } // namespace detail // Helper for passing a UniquePtr to an old-style function that uses raw // pointers for out params. Example usage: // // void AllocateFoo(Foo** out) { *out = new Foo(); } // UniquePtr foo; // AllocateFoo(getter_Transfers(foo)); template auto getter_Transfers(UniquePtr& up) { return detail::UniquePtrGetterTransfers(up); } } // namespace mozilla #endif // mozilla_UniquePtrExtensions_h