tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

UniquePtrExtensions.h (9989B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /* Useful extensions to UniquePtr. */
      8 
      9 #ifndef mozilla_UniquePtrExtensions_h
     10 #define mozilla_UniquePtrExtensions_h
     11 
     12 #include <type_traits>
     13 
     14 #include "mozilla/Assertions.h"
     15 #include "mozilla/Attributes.h"
     16 #include "mozilla/DebugOnly.h"
     17 #include "mozilla/fallible.h"
     18 #include "mozilla/UniquePtr.h"
     19 
     20 #ifdef XP_WIN
     21 #  include <cstdint>
     22 #endif
     23 #if defined(XP_DARWIN)
     24 #  include <mach/mach.h>
     25 #endif
     26 
     27 namespace mozilla {
     28 
     29 /**
     30 * MakeUniqueFallible works exactly like MakeUnique, except that the memory
     31 * allocation performed is done fallibly, i.e. it can return nullptr.
     32 */
     33 template <typename T, typename... Args>
     34 typename detail::UniqueSelector<T>::SingleObject MakeUniqueFallible(
     35    Args&&... aArgs) {
     36  return UniquePtr<T>(new (fallible) T(std::forward<Args>(aArgs)...));
     37 }
     38 
     39 template <typename T>
     40 typename detail::UniqueSelector<T>::UnknownBound MakeUniqueFallible(
     41    decltype(sizeof(int)) aN) {
     42  using ArrayType = std::remove_extent_t<T>;
     43  return UniquePtr<T>(new (fallible) ArrayType[aN]());
     44 }
     45 
     46 template <typename T, typename... Args>
     47 typename detail::UniqueSelector<T>::KnownBound MakeUniqueFallible(
     48    Args&&... aArgs) = delete;
     49 
     50 /**
     51 * MakeUniqueForOverwrite and MakeUniqueFallibleForOverwrite are like MakeUnique
     52 * and MakeUniqueFallible except they use default-initialization. This is
     53 * useful, for example, when you have a POD type array that will be overwritten
     54 * directly after construction and so zero-initialization is a waste.
     55 */
     56 template <typename T, typename... Args>
     57 typename detail::UniqueSelector<T>::SingleObject MakeUniqueForOverwrite() {
     58  return UniquePtr<T>(new T);
     59 }
     60 
     61 template <typename T>
     62 typename detail::UniqueSelector<T>::UnknownBound MakeUniqueForOverwrite(
     63    decltype(sizeof(int)) aN) {
     64  using ArrayType = std::remove_extent_t<T>;
     65  return UniquePtr<T>(new ArrayType[aN]);
     66 }
     67 
     68 template <typename T, typename... Args>
     69 typename detail::UniqueSelector<T>::KnownBound MakeUniqueForOverwrite(
     70    Args&&... aArgs) = delete;
     71 
     72 template <typename T, typename... Args>
     73 typename detail::UniqueSelector<T>::SingleObject
     74 MakeUniqueForOverwriteFallible() {
     75  return UniquePtr<T>(new (fallible) T);
     76 }
     77 
     78 template <typename T>
     79 typename detail::UniqueSelector<T>::UnknownBound MakeUniqueForOverwriteFallible(
     80    decltype(sizeof(int)) aN) {
     81  using ArrayType = std::remove_extent_t<T>;
     82  return UniquePtr<T>(new (fallible) ArrayType[aN]);
     83 }
     84 
     85 template <typename T, typename... Args>
     86 typename detail::UniqueSelector<T>::KnownBound MakeUniqueForOverwriteFallible(
     87    Args&&... aArgs) = delete;
     88 
     89 namespace detail {
     90 
     91 template <typename T>
     92 struct FreePolicy {
     93  void operator()(const void* ptr) { free(const_cast<void*>(ptr)); }
     94 };
     95 
     96 #if defined(XP_WIN)
     97 // Can't include <windows.h> to get the actual definition of HANDLE
     98 // because of namespace pollution.
     99 typedef void* FileHandleType;
    100 #elif defined(XP_UNIX)
    101 typedef int FileHandleType;
    102 #else
    103 #  error "Unsupported OS?"
    104 #endif
    105 
    106 struct FileHandleHelper {
    107  MOZ_IMPLICIT FileHandleHelper(FileHandleType aHandle) : mHandle(aHandle) {
    108 #if defined(XP_UNIX) && (defined(DEBUG) || defined(FUZZING))
    109    MOZ_RELEASE_ASSERT(aHandle == kInvalidHandle || aHandle > 2);
    110 #endif
    111  }
    112 
    113  MOZ_IMPLICIT constexpr FileHandleHelper() : mHandle(kInvalidHandle) {}
    114 
    115  MOZ_IMPLICIT constexpr FileHandleHelper(std::nullptr_t)
    116      : mHandle(kInvalidHandle) {}
    117 
    118  bool operator!=(std::nullptr_t) const {
    119 #ifdef XP_WIN
    120    // Windows uses both nullptr and INVALID_HANDLE_VALUE (-1 cast to
    121    // HANDLE) in different situations, but nullptr is more reliably
    122    // null while -1 is also valid input to some calls that take
    123    // handles.  So class considers both to be null (since neither
    124    // should be closed) but default-constructs as nullptr.
    125    if (mHandle == (void*)-1) {
    126      return false;
    127    }
    128 #endif
    129    return mHandle != kInvalidHandle;
    130  }
    131 
    132  operator FileHandleType() const { return mHandle; }
    133 
    134 #ifdef XP_WIN
    135  // NSPR uses an integer type for PROsfd, so this conversion is
    136  // provided for working with it without needing reinterpret casts
    137  // everywhere.
    138  operator std::intptr_t() const {
    139    return reinterpret_cast<std::intptr_t>(mHandle);
    140  }
    141 #endif
    142 
    143  // When there's only one user-defined conversion operator, the
    144  // compiler will use that to derive equality, but that doesn't work
    145  // when the conversion is ambiguoug (the XP_WIN case above).
    146  bool operator==(const FileHandleHelper& aOther) const {
    147    return mHandle == aOther.mHandle;
    148  }
    149 
    150 private:
    151  FileHandleType mHandle;
    152 
    153 #ifdef XP_WIN
    154  // See above for why this is nullptr.  (Also, INVALID_HANDLE_VALUE
    155  // can't be expressed as a constexpr.)
    156  static constexpr FileHandleType kInvalidHandle = nullptr;
    157 #else
    158  static constexpr FileHandleType kInvalidHandle = -1;
    159 #endif
    160 };
    161 
    162 struct FileHandleDeleter {
    163  using pointer = FileHandleHelper;
    164  using receiver = FileHandleType;
    165  MFBT_API void operator()(FileHandleHelper aHelper);
    166 };
    167 
    168 #if defined(XP_DARWIN)
    169 struct MachPortHelper {
    170  MOZ_IMPLICIT MachPortHelper(mach_port_t aPort) : mPort(aPort) {}
    171 
    172  MOZ_IMPLICIT constexpr MachPortHelper() : mPort(MACH_PORT_NULL) {}
    173 
    174  MOZ_IMPLICIT constexpr MachPortHelper(std::nullptr_t) : MachPortHelper() {}
    175 
    176  bool operator!=(std::nullptr_t) const { return mPort != MACH_PORT_NULL; }
    177 
    178  operator const mach_port_t&() const { return mPort; }
    179  operator mach_port_t&() { return mPort; }
    180 
    181 private:
    182  mach_port_t mPort;
    183 };
    184 
    185 struct MachSendRightDeleter {
    186  using pointer = MachPortHelper;
    187  using receiver = mach_port_t;
    188  MFBT_API void operator()(MachPortHelper aHelper) {
    189    DebugOnly<kern_return_t> kr =
    190        mach_port_deallocate(mach_task_self(), aHelper);
    191    MOZ_ASSERT(kr == KERN_SUCCESS, "failed to deallocate mach send right");
    192  }
    193 };
    194 
    195 struct MachReceiveRightDeleter {
    196  using pointer = MachPortHelper;
    197  using receiver = mach_port_t;
    198  MFBT_API void operator()(MachPortHelper aHelper) {
    199    DebugOnly<kern_return_t> kr = mach_port_mod_refs(
    200        mach_task_self(), aHelper, MACH_PORT_RIGHT_RECEIVE, -1);
    201    MOZ_ASSERT(kr == KERN_SUCCESS, "failed to release mach receive right");
    202  }
    203 };
    204 
    205 struct MachPortSetDeleter {
    206  using pointer = MachPortHelper;
    207  using receiver = mach_port_t;
    208  MFBT_API void operator()(MachPortHelper aHelper) {
    209    DebugOnly<kern_return_t> kr = mach_port_mod_refs(
    210        mach_task_self(), aHelper, MACH_PORT_RIGHT_PORT_SET, -1);
    211    MOZ_ASSERT(kr == KERN_SUCCESS, "failed to release mach port set");
    212  }
    213 };
    214 #endif
    215 
    216 }  // namespace detail
    217 
    218 template <typename T>
    219 using UniqueFreePtr = UniquePtr<T, detail::FreePolicy<T>>;
    220 
    221 // A RAII class for the OS construct used for open files and similar
    222 // objects: a file descriptor on Unix or a handle on Windows.
    223 using UniqueFileHandle =
    224    UniquePtr<detail::FileHandleType, detail::FileHandleDeleter>;
    225 
    226 #ifndef __wasm__
    227 // WASI does not have `dup`
    228 MFBT_API UniqueFileHandle DuplicateFileHandle(detail::FileHandleType aFile);
    229 inline UniqueFileHandle DuplicateFileHandle(const UniqueFileHandle& aFile) {
    230  return DuplicateFileHandle(aFile.get());
    231 }
    232 #endif
    233 
    234 #if defined(XP_DARWIN)
    235 // A RAII class for a Mach port that names a send right.
    236 using UniqueMachSendRight =
    237    UniquePtr<mach_port_t, detail::MachSendRightDeleter>;
    238 // A RAII class for a Mach port that names a receive right.
    239 using UniqueMachReceiveRight =
    240    UniquePtr<mach_port_t, detail::MachReceiveRightDeleter>;
    241 // A RAII class for a Mach port set.
    242 using UniqueMachPortSet = UniquePtr<mach_port_t, detail::MachPortSetDeleter>;
    243 
    244 // Increases the user reference count for MACH_PORT_RIGHT_SEND by 1 and returns
    245 // a new UniqueMachSendRight to manage the additional right.
    246 inline UniqueMachSendRight RetainMachSendRight(mach_port_t aPort) {
    247  kern_return_t kr =
    248      mach_port_mod_refs(mach_task_self(), aPort, MACH_PORT_RIGHT_SEND, 1);
    249  if (kr == KERN_SUCCESS) {
    250    return UniqueMachSendRight(aPort);
    251  }
    252  return nullptr;
    253 }
    254 #endif
    255 
    256 namespace detail {
    257 
    258 template <typename T, typename D, typename = void>
    259 struct PointerType {
    260  using type = T*;
    261 };
    262 
    263 template <typename T, typename D>
    264 struct PointerType<T, D,
    265                   std::void_t<typename std::remove_reference_t<D>::pointer>> {
    266  using type = typename std::remove_reference_t<D>::pointer;
    267 };
    268 
    269 template <typename T, typename D, typename = void>
    270 struct ReceiverType : PointerType<T, D> {};
    271 
    272 template <typename T, typename D>
    273 struct ReceiverType<
    274    T, D, std::void_t<typename std::remove_reference_t<D>::receiver>> {
    275  using type = typename std::remove_reference_t<D>::receiver;
    276 };
    277 
    278 template <typename T, typename D>
    279 class MOZ_TEMPORARY_CLASS UniquePtrGetterTransfers {
    280 public:
    281  using Ptr = UniquePtr<T, D>;
    282  using Receiver = typename ReceiverType<T, D>::type;
    283 
    284  explicit UniquePtrGetterTransfers(Ptr& p)
    285      : mPtr(p), mReceiver(typename Ptr::pointer(nullptr)) {}
    286  ~UniquePtrGetterTransfers() { mPtr.reset(mReceiver); }
    287 
    288  operator Receiver*() { return &mReceiver; }
    289  Receiver& operator*() { return mReceiver; }
    290 
    291  // operator void** is conditionally enabled if `Receiver` is a pointer.
    292  template <typename U = Receiver,
    293            typename = std::enable_if_t<
    294                std::is_pointer_v<U> && std::is_same_v<U, Receiver>, void>>
    295  operator void**() {
    296    return reinterpret_cast<void**>(&mReceiver);
    297  }
    298 
    299 private:
    300  Ptr& mPtr;
    301  Receiver mReceiver;
    302 };
    303 
    304 }  // namespace detail
    305 
    306 // Helper for passing a UniquePtr to an old-style function that uses raw
    307 // pointers for out params. Example usage:
    308 //
    309 //   void AllocateFoo(Foo** out) { *out = new Foo(); }
    310 //   UniquePtr<Foo> foo;
    311 //   AllocateFoo(getter_Transfers(foo));
    312 template <typename T, typename D>
    313 auto getter_Transfers(UniquePtr<T, D>& up) {
    314  return detail::UniquePtrGetterTransfers<T, D>(up);
    315 }
    316 
    317 }  // namespace mozilla
    318 
    319 #endif  // mozilla_UniquePtrExtensions_h