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