File: WinOLELock.h

package info (click to toggle)
firefox 147.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,683,324 kB
  • sloc: cpp: 7,607,156; javascript: 6,532,492; ansic: 3,775,158; python: 1,415,368; xml: 634,556; asm: 438,949; java: 186,241; sh: 62,751; makefile: 18,079; objc: 13,092; perl: 12,808; yacc: 4,583; cs: 3,846; pascal: 3,448; lex: 1,720; ruby: 1,003; php: 436; lisp: 258; awk: 247; sql: 66; sed: 54; csh: 10; exp: 6
file content (193 lines) | stat: -rw-r--r-- 5,814 bytes parent folder | download | duplicates (11)
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__