1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
/* -*- 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 <type_traits>
#include <minwindef.h>
#include <winbase.h>
#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 <typename, typename = void>
constexpr bool is_complete_type_v = false;
template <typename T>
constexpr bool is_complete_type_v<T, std::void_t<decltype(sizeof(T))>> = 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 <typename T>
constexpr bool is_dereferenceable_v =
std::is_pointer_v<T> && is_complete_type_v<std::remove_pointer_t<T>>;
template <typename T>
constexpr bool is_arrowable_v =
std::is_pointer_v<T> && is_complete_type_v<std::remove_pointer_t<T>> &&
std::is_class_v<std::remove_pointer_t<T>>;
// SFINAE mixins for ScopedOLELock, below.
template <typename Derived, typename T, bool = is_dereferenceable_v<T>,
bool = is_arrowable_v<T>>
struct OLELockMixin {};
template <typename D, typename T>
struct OLELockMixin<D, T, true, false> {
auto operator*() const -> std::remove_pointer_t<T>& {
T ptr = static_cast<D const*>(this)->get();
MOZ_ASSERT(ptr);
return *ptr;
}
};
template <typename D, typename T>
struct OLELockMixin<D, T, true, true> : OLELockMixin<D, T, true, false> {
auto operator->() const -> T {
T ptr = static_cast<D const*>(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 T>
class ScopedOLELock;
// Handle/pointer implementation of ScopedHGLock.
template <class T>
class ScopedOLELock : public details::OLELockMixin<ScopedOLELock<T>, T> {
public:
explicit ScopedOLELock(HGLOBAL glob) : mGlobal(glob) {
static_assert(std::is_pointer_v<T>);
mData = static_cast<T>(::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 U>
class ScopedOLELock<U[]> {
public:
explicit ScopedOLELock(HGLOBAL glob) : mGlobal(glob) {
static_assert(details::is_complete_type_v<U>);
mData = static_cast<U*>(::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 <typename T>
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<T>, "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<T*> lock() const { return ScopedOLELock<T*>(mHandle); }
explicit operator bool() const { return bool(mHandle); }
HGLOBAL forget() {
HGLOBAL r{mHandle};
mHandle = nullptr;
return r;
}
private:
HGLOBAL mHandle;
};
template <typename U>
class ScopedOLEMemory<U[]> {
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<U>, "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<U[]> lock() const { return ScopedOLELock<U[]>(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__
|