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