/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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/. */ #ifndef mozilla_widget_WinOLELock_h__ #define mozilla_widget_WinOLELock_h__ #include #include #include #include "mozilla/Assertions.h" namespace details { // implementation of `is_complete_type_v` borrowed from Raymond Chen: // https://devblogs.microsoft.com/oldnewthing/20190711-00/?p=102682 template constexpr bool is_complete_type_v = false; template constexpr bool is_complete_type_v> = true; // The Windows SDK typically declares handle types to be of type "pointer to // incomplete struct type"; this is broadly a good choice, but implies that we // need to take additional steps to avoid declaring operator* or operator-> for // smart pointers thereto. template constexpr bool is_dereferenceable_v = std::is_pointer_v && is_complete_type_v>; template constexpr bool is_arrowable_v = std::is_pointer_v && is_complete_type_v> && std::is_class_v>; // SFINAE mixins for ScopedOLELock, below. template , bool = is_arrowable_v> struct OLELockMixin {}; template struct OLELockMixin { auto operator*() const -> std::remove_pointer_t& { T ptr = static_cast(this)->get(); MOZ_ASSERT(ptr); return *ptr; } }; template struct OLELockMixin : OLELockMixin { auto operator->() const -> T { T ptr = static_cast(this)->get(); MOZ_ASSERT(ptr); return ptr; } }; } // namespace details // RAII scoped-handle object for ::GlobalLock()ed data -- which, in practice, // means data associated with either the clipboard or with drag-and-drop. // // T must be of pointer, handle, or array type. template class ScopedOLELock; // Handle/pointer implementation of ScopedHGLock. template class ScopedOLELock : public details::OLELockMixin, T> { public: explicit ScopedOLELock(HGLOBAL glob) : mGlobal(glob) { static_assert(std::is_pointer_v); mData = static_cast(::GlobalLock(mGlobal)); } ~ScopedOLELock() { ::GlobalUnlock(mGlobal); } auto operator=(ScopedOLELock const&) = delete; ScopedOLELock(ScopedOLELock const&) = delete; explicit operator bool() const { return bool(mData); } auto get() const -> T { return mData; } // operator* and operator-> are also SFINAE'd in, when usable. // TODO(C++20): inline them here. private: HGLOBAL mGlobal; T mData; }; template class ScopedOLELock { public: explicit ScopedOLELock(HGLOBAL glob) : mGlobal(glob) { static_assert(details::is_complete_type_v); mData = static_cast(::GlobalLock(mGlobal)); auto const [quot, rem] = std::lldiv(GlobalSize(mGlobal), sizeof(U)); mExtent = quot; NS_WARNING_ASSERTION( rem == 0, "size of alleged array is not a multiple of the array element size"); } ~ScopedOLELock() { ::GlobalUnlock(mGlobal); } auto operator=(ScopedOLELock const&) = delete; ScopedOLELock(ScopedOLELock const&) = delete; explicit operator bool() const { return bool(mData); } auto get() const -> U* { return mData; } // STL-style, for range-based iteration and so forth auto begin() const -> U* { return mData; } auto end() const -> U* { return mData + mExtent; } auto size() const -> size_t { return mExtent; } auto operator[](size_t index) const { MOZ_ASSERT(index < mExtent); return mData[index]; } private: HGLOBAL mGlobal; U* mData; size_t mExtent; }; // RAII scoped-handle object for _locally-created_ ::GlobalLock()ed data. // // T must be of POD type. A specialication for runtime-bounded array types is // provided below. template class ScopedOLEMemory { static_assert(alignof(T) <= 8, "GlobalAlloc only aligns to 8-byte boundaries"); // This could be weakened if needed, but not eliminated: the destructor must // be trivial. static_assert(std::is_pod_v, "type must be POD"); public: explicit ScopedOLEMemory() : mHandle(::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(T))) {} ~ScopedOLEMemory() { // documented as `_Frees_ptr_opt_`, so we don't need to check for NULL ::GlobalFree(mHandle); } ScopedOLELock lock() const { return ScopedOLELock(mHandle); } explicit operator bool() const { return bool(mHandle); } HGLOBAL forget() { HGLOBAL r{mHandle}; mHandle = nullptr; return r; } private: HGLOBAL mHandle; }; template class ScopedOLEMemory { static_assert(alignof(U) <= 8, "GlobalAlloc only aligns to 8-byte boundaries"); // This could be weakened if needed, but not eliminated: the destructor must // be trivial. static_assert(std::is_pod_v, "type must be POD"); public: explicit ScopedOLEMemory(size_t n) : mHandle(::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(U) * n)), mExtent(n) {} ~ScopedOLEMemory() { ::GlobalFree(mHandle); } ScopedOLELock lock() const { return ScopedOLELock(mHandle); } explicit operator bool() const { return bool(mHandle); } HGLOBAL forget() { HGLOBAL r{mHandle}; mHandle = nullptr; return r; } private: HGLOBAL mHandle; size_t mExtent; }; #endif // mozilla_widget_WinOLELock_h__