xref: /aosp_15_r20/external/cronet/base/win/scoped_safearray.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_WIN_SCOPED_SAFEARRAY_H_
6 #define BASE_WIN_SCOPED_SAFEARRAY_H_
7 
8 #include <objbase.h>
9 
10 #include <optional>
11 
12 #include "base/base_export.h"
13 #include "base/check_op.h"
14 #include "base/memory/raw_ptr_exclusion.h"
15 #include "base/win/variant_conversions.h"
16 
17 namespace base {
18 namespace win {
19 
20 // Manages a Windows SAFEARRAY. This is a minimal wrapper that simply provides
21 // RAII semantics and does not duplicate the extensive functionality that
22 // CComSafeArray offers.
23 class BASE_EXPORT ScopedSafearray {
24  public:
25   // LockScope<VARTYPE> class for automatically managing the lifetime of a
26   // SAFEARRAY lock, and granting easy access to the underlying data either
27   // through random access or as an iterator.
28   // It is undefined behavior if the underlying SAFEARRAY is destroyed
29   // before the LockScope.
30   // LockScope implements std::iterator_traits as a random access iterator, so
31   // that LockScope is compatible with STL methods that require these traits.
32   template <VARTYPE ElementVartype>
33   class BASE_EXPORT LockScope final {
34    public:
35     // Type declarations to support std::iterator_traits
36     using iterator_category = std::random_access_iterator_tag;
37     using value_type =
38         typename internal::VariantConverter<ElementVartype>::Type;
39     using difference_type = ptrdiff_t;
40     using reference = value_type&;
41     using const_reference = const value_type&;
42     using pointer = value_type*;
43     using const_pointer = const value_type*;
44 
45     LockScope() = default;
46 
LockScope(LockScope<ElementVartype> && other)47     LockScope(LockScope<ElementVartype>&& other)
48         : safearray_(std::exchange(other.safearray_, nullptr)),
49           vartype_(std::exchange(other.vartype_, VT_EMPTY)),
50           array_(std::exchange(other.array_, nullptr)),
51           array_size_(std::exchange(other.array_size_, 0U)) {}
52 
53     LockScope<ElementVartype>& operator=(LockScope<ElementVartype>&& other) {
54       DCHECK_NE(this, &other);
55       Reset();
56       safearray_ = std::exchange(other.safearray_, nullptr);
57       vartype_ = std::exchange(other.vartype_, VT_EMPTY);
58       array_ = std::exchange(other.array_, nullptr);
59       array_size_ = std::exchange(other.array_size_, 0U);
60       return *this;
61     }
62 
63     LockScope(const LockScope&) = delete;
64     LockScope& operator=(const LockScope&) = delete;
65 
~LockScope()66     ~LockScope() { Reset(); }
67 
Type()68     VARTYPE Type() const { return vartype_; }
69 
size()70     size_t size() const { return array_size_; }
71 
begin()72     pointer begin() { return array_; }
end()73     pointer end() { return array_ + array_size_; }
begin()74     const_pointer begin() const { return array_; }
end()75     const_pointer end() const { return array_ + array_size_; }
76 
data()77     pointer data() { return array_; }
data()78     const_pointer data() const { return array_; }
79 
80     reference operator[](size_t index) { return at(index); }
81     const_reference operator[](size_t index) const { return at(index); }
82 
at(size_t index)83     reference at(size_t index) {
84       DCHECK_NE(array_, nullptr);
85       DCHECK_LT(index, array_size_);
86       return array_[index];
87     }
at(size_t index)88     const_reference at(size_t index) const {
89       return const_cast<LockScope<ElementVartype>*>(this)->at(index);
90     }
91 
92    private:
LockScope(SAFEARRAY * safearray,VARTYPE vartype,pointer array,size_t array_size)93     LockScope(SAFEARRAY* safearray,
94               VARTYPE vartype,
95               pointer array,
96               size_t array_size)
97         : safearray_(safearray),
98           vartype_(vartype),
99           array_(array),
100           array_size_(array_size) {}
101 
Reset()102     void Reset() {
103       if (safearray_)
104         SafeArrayUnaccessData(safearray_);
105       safearray_ = nullptr;
106       vartype_ = VT_EMPTY;
107       array_ = nullptr;
108       array_size_ = 0U;
109     }
110 
111     // RAW_PTR_EXCLUSION: Comes from the operating system and may have been
112     // laundered. If rewritten, it may generate an incorrect Dangling Pointer
113     // Detector error.
114     RAW_PTR_EXCLUSION SAFEARRAY* safearray_ = nullptr;
115     VARTYPE vartype_ = VT_EMPTY;
116     pointer array_ = nullptr;
117     size_t array_size_ = 0U;
118 
119     friend class ScopedSafearray;
120   };
121 
122   explicit ScopedSafearray(SAFEARRAY* safearray = nullptr)
safearray_(safearray)123       : safearray_(safearray) {}
124 
125   ScopedSafearray(const ScopedSafearray&) = delete;
126   ScopedSafearray& operator=(const ScopedSafearray&) = delete;
127 
128   // Move constructor
ScopedSafearray(ScopedSafearray && r)129   ScopedSafearray(ScopedSafearray&& r) noexcept : safearray_(r.safearray_) {
130     r.safearray_ = nullptr;
131   }
132 
133   // Move operator=. Allows assignment from a ScopedSafearray rvalue.
134   ScopedSafearray& operator=(ScopedSafearray&& rvalue) {
135     Reset(rvalue.Release());
136     return *this;
137   }
138 
~ScopedSafearray()139   ~ScopedSafearray() { Destroy(); }
140 
141   // Creates a LockScope for accessing the contents of a
142   // single-dimensional SAFEARRAYs.
143   template <VARTYPE ElementVartype>
CreateLockScope()144   std::optional<LockScope<ElementVartype>> CreateLockScope() const {
145     if (!safearray_ || SafeArrayGetDim(safearray_) != 1)
146       return std::nullopt;
147 
148     VARTYPE vartype;
149     HRESULT hr = SafeArrayGetVartype(safearray_, &vartype);
150     if (FAILED(hr) ||
151         !internal::VariantConverter<ElementVartype>::IsConvertibleTo(vartype)) {
152       return std::nullopt;
153     }
154 
155     typename LockScope<ElementVartype>::pointer array = nullptr;
156     hr = SafeArrayAccessData(safearray_, reinterpret_cast<void**>(&array));
157     if (FAILED(hr))
158       return std::nullopt;
159 
160     const size_t array_size = GetCount();
161     return LockScope<ElementVartype>(safearray_, vartype, array, array_size);
162   }
163 
Destroy()164   void Destroy() {
165     if (safearray_) {
166       HRESULT hr = SafeArrayDestroy(safearray_);
167       DCHECK_EQ(S_OK, hr);
168       safearray_ = nullptr;
169     }
170   }
171 
172   // Give ScopedSafearray ownership over an already allocated SAFEARRAY or
173   // nullptr.
174   void Reset(SAFEARRAY* safearray = nullptr) {
175     if (safearray != safearray_) {
176       Destroy();
177       safearray_ = safearray;
178     }
179   }
180 
181   // Releases ownership of the SAFEARRAY to the caller.
Release()182   SAFEARRAY* Release() {
183     SAFEARRAY* safearray = safearray_;
184     safearray_ = nullptr;
185     return safearray;
186   }
187 
188   // Retrieves the pointer address.
189   // Used to receive SAFEARRAYs as out arguments (and take ownership).
190   // This function releases any existing references because it will leak
191   // the existing ref otherwise.
192   // Usage: GetSafearray(safearray.Receive());
Receive()193   SAFEARRAY** Receive() {
194     Destroy();
195     return &safearray_;
196   }
197 
198   // Returns the number of elements in a dimension of the array.
199   size_t GetCount(UINT dimension = 0) const {
200     DCHECK(safearray_);
201     // Initialize |lower| and |upper| so this method will return zero if either
202     // SafeArrayGetLBound or SafeArrayGetUBound returns failure because they
203     // only write to the output parameter when successful.
204     LONG lower = 0;
205     LONG upper = -1;
206     DCHECK_LT(dimension, SafeArrayGetDim(safearray_));
207     HRESULT hr = SafeArrayGetLBound(safearray_, dimension + 1, &lower);
208     DCHECK(SUCCEEDED(hr));
209     hr = SafeArrayGetUBound(safearray_, dimension + 1, &upper);
210     DCHECK(SUCCEEDED(hr));
211     LONG count = upper - lower + 1;
212     // SafeArrays may have negative lower bounds, so check for wraparound.
213     DCHECK_GT(count, 0);
214     return static_cast<size_t>(count);
215   }
216 
217   // Returns the internal pointer.
Get()218   SAFEARRAY* Get() const { return safearray_; }
219 
220   // Forbid comparison of ScopedSafearray types.  You should never have the same
221   // SAFEARRAY owned by two different scoped_ptrs.
222   bool operator==(const ScopedSafearray& safearray2) const = delete;
223   bool operator!=(const ScopedSafearray& safearray2) const = delete;
224 
225  private:
226   // RAW_PTR_EXCLUSION: #addr-of
227   RAW_PTR_EXCLUSION SAFEARRAY* safearray_;
228 };
229 
230 }  // namespace win
231 }  // namespace base
232 
233 #endif  // BASE_WIN_SCOPED_SAFEARRAY_H_
234