Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 1 | // Copyright 2018 The Chromium Authors. All rights reserved. |
| 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_VECTOR_H_ |
| 6 | #define BASE_WIN_VECTOR_H_ |
| 7 | |
| 8 | #include <ivectorchangedeventargs.h> |
| 9 | #include <windows.foundation.collections.h> |
| 10 | #include <wrl/implements.h> |
| 11 | |
| 12 | #include <algorithm> |
| 13 | #include <iterator> |
| 14 | #include <utility> |
| 15 | #include <vector> |
| 16 | |
| 17 | #include "base/base_export.h" |
Hans Wennborg | 7b53371 | 2020-06-22 20:52:27 | [diff] [blame] | 18 | #include "base/check_op.h" |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 19 | #include "base/containers/flat_map.h" |
Jan Wilken Dörrie | 8f95347 | 2019-07-23 11:29:31 | [diff] [blame] | 20 | #include "base/win/winrt_foundation_helpers.h" |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 21 | |
| 22 | namespace base { |
| 23 | namespace win { |
| 24 | |
| 25 | template <typename T> |
| 26 | class Vector; |
| 27 | |
| 28 | namespace internal { |
| 29 | |
Jan Wilken Dörrie | 8f95347 | 2019-07-23 11:29:31 | [diff] [blame] | 30 | // Template tricks needed to dispatch to the correct implementation. |
| 31 | // See base/win/winrt_foundation_helpers.h for explanation. |
| 32 | |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 33 | template <typename T> |
Julien Racle | cd8a64b | 2019-08-15 16:08:43 | [diff] [blame] | 34 | using VectorComplex = |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 35 | typename ABI::Windows::Foundation::Collections::IVector<T>::T_complex; |
| 36 | |
| 37 | template <typename T> |
Julien Racle | cd8a64b | 2019-08-15 16:08:43 | [diff] [blame] | 38 | using VectorLogical = LogicalType<VectorComplex<T>>; |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 39 | |
| 40 | template <typename T> |
Julien Racle | cd8a64b | 2019-08-15 16:08:43 | [diff] [blame] | 41 | using VectorAbi = AbiType<VectorComplex<T>>; |
Jan Wilken Dörrie | 8f95347 | 2019-07-23 11:29:31 | [diff] [blame] | 42 | |
| 43 | template <typename T> |
Julien Racle | cd8a64b | 2019-08-15 16:08:43 | [diff] [blame] | 44 | using VectorStorage = StorageType<VectorComplex<T>>; |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 45 | |
Julien Racle | a2070296 | 2019-04-10 17:19:45 | [diff] [blame] | 46 | template <typename T> |
| 47 | class VectorIterator |
| 48 | : public Microsoft::WRL::RuntimeClass< |
| 49 | Microsoft::WRL::RuntimeClassFlags< |
| 50 | Microsoft::WRL::WinRtClassicComMix | |
| 51 | Microsoft::WRL::InhibitRoOriginateError>, |
Julien Racle | cd8a64b | 2019-08-15 16:08:43 | [diff] [blame] | 52 | ABI::Windows::Foundation::Collections::IIterator<VectorLogical<T>>> { |
Julien Racle | a2070296 | 2019-04-10 17:19:45 | [diff] [blame] | 53 | public: |
Julien Racle | cd8a64b | 2019-08-15 16:08:43 | [diff] [blame] | 54 | using LogicalT = VectorLogical<T>; |
| 55 | using AbiT = VectorAbi<T>; |
Julien Racle | a2070296 | 2019-04-10 17:19:45 | [diff] [blame] | 56 | |
| 57 | explicit VectorIterator( |
| 58 | Microsoft::WRL::ComPtr< |
| 59 | ABI::Windows::Foundation::Collections::IVectorView<LogicalT>> view) |
| 60 | : view_(std::move(view)) {} |
| 61 | |
| 62 | // ABI::Windows::Foundation::Collections::IIterator: |
| 63 | IFACEMETHODIMP get_Current(AbiT* current) override { |
| 64 | return view_->GetAt(current_index_, current); |
| 65 | } |
| 66 | |
| 67 | IFACEMETHODIMP get_HasCurrent(boolean* has_current) override { |
| 68 | *has_current = FALSE; |
| 69 | unsigned size; |
| 70 | HRESULT hr = view_->get_Size(&size); |
| 71 | if (SUCCEEDED(hr)) { |
Sunggook Chue | 9451401 | 2019-11-20 01:10:51 | [diff] [blame] | 72 | if (current_index_ < size) { |
Julien Racle | a2070296 | 2019-04-10 17:19:45 | [diff] [blame] | 73 | *has_current = TRUE; |
| 74 | } |
| 75 | } |
| 76 | return hr; |
| 77 | } |
| 78 | |
| 79 | IFACEMETHODIMP MoveNext(boolean* has_current) override { |
Sunggook Chue | 9451401 | 2019-11-20 01:10:51 | [diff] [blame] | 80 | *has_current = FALSE; |
Richard Knoll | 55ea472 | 2020-06-26 14:43:57 | [diff] [blame] | 81 | unsigned size; |
| 82 | HRESULT hr = view_->get_Size(&size); |
Sunggook Chue | 9451401 | 2019-11-20 01:10:51 | [diff] [blame] | 83 | if (FAILED(hr)) |
| 84 | return hr; |
| 85 | |
Richard Knoll | 55ea472 | 2020-06-26 14:43:57 | [diff] [blame] | 86 | // Check if we're already past the last item. |
| 87 | if (current_index_ >= size) |
| 88 | return E_BOUNDS; |
| 89 | |
| 90 | // Move to the next item. |
| 91 | current_index_++; |
| 92 | |
| 93 | // Set |has_current| to TRUE if we're still on a valid item. |
| 94 | if (current_index_ < size) |
| 95 | *has_current = TRUE; |
| 96 | |
| 97 | return hr; |
Julien Racle | a2070296 | 2019-04-10 17:19:45 | [diff] [blame] | 98 | } |
| 99 | |
| 100 | IFACEMETHODIMP GetMany(unsigned capacity, |
| 101 | AbiT* value, |
| 102 | unsigned* actual) override { |
| 103 | return view_->GetMany(current_index_, capacity, value, actual); |
| 104 | } |
| 105 | |
| 106 | private: |
| 107 | Microsoft::WRL::ComPtr< |
| 108 | ABI::Windows::Foundation::Collections::IVectorView<LogicalT>> |
| 109 | view_; |
| 110 | unsigned current_index_ = 0; |
| 111 | }; |
| 112 | |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 113 | class BASE_EXPORT VectorChangedEventArgs |
| 114 | : public Microsoft::WRL::RuntimeClass< |
| 115 | Microsoft::WRL::RuntimeClassFlags< |
| 116 | Microsoft::WRL::WinRtClassicComMix | |
| 117 | Microsoft::WRL::InhibitRoOriginateError>, |
| 118 | ABI::Windows::Foundation::Collections::IVectorChangedEventArgs> { |
| 119 | public: |
| 120 | VectorChangedEventArgs( |
| 121 | ABI::Windows::Foundation::Collections::CollectionChange change, |
| 122 | unsigned int index) |
| 123 | : change_(change), index_(index) {} |
| 124 | |
| 125 | ~VectorChangedEventArgs() override = default; |
| 126 | |
| 127 | // ABI::Windows::Foundation::Collections::IVectorChangedEventArgs: |
| 128 | IFACEMETHODIMP get_CollectionChange( |
| 129 | ABI::Windows::Foundation::Collections::CollectionChange* value) override; |
| 130 | IFACEMETHODIMP get_Index(unsigned int* value) override; |
| 131 | |
| 132 | private: |
| 133 | const ABI::Windows::Foundation::Collections::CollectionChange change_; |
| 134 | const unsigned int index_; |
| 135 | }; |
| 136 | |
| 137 | template <typename T> |
| 138 | class VectorView |
| 139 | : public Microsoft::WRL::RuntimeClass< |
| 140 | Microsoft::WRL::RuntimeClassFlags< |
| 141 | Microsoft::WRL::WinRtClassicComMix | |
| 142 | Microsoft::WRL::InhibitRoOriginateError>, |
Julien Racle | cd8a64b | 2019-08-15 16:08:43 | [diff] [blame] | 143 | ABI::Windows::Foundation::Collections::IVectorView<VectorLogical<T>>, |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 144 | ABI::Windows::Foundation::Collections::VectorChangedEventHandler< |
Julien Racle | cd8a64b | 2019-08-15 16:08:43 | [diff] [blame] | 145 | VectorLogical<T>>> { |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 146 | public: |
Julien Racle | cd8a64b | 2019-08-15 16:08:43 | [diff] [blame] | 147 | using LogicalT = VectorLogical<T>; |
| 148 | using AbiT = VectorAbi<T>; |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 149 | |
| 150 | explicit VectorView(Microsoft::WRL::ComPtr<Vector<LogicalT>> vector) |
| 151 | : vector_(std::move(vector)) { |
| 152 | vector_->add_VectorChanged(this, &vector_changed_token_); |
| 153 | } |
| 154 | |
Robert Liao | a37b8e6 | 2019-12-23 05:28:20 | [diff] [blame] | 155 | ~VectorView() override { |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 156 | if (vector_) |
| 157 | vector_->remove_VectorChanged(vector_changed_token_); |
| 158 | } |
| 159 | |
| 160 | // ABI::Windows::Foundation::Collections::IVectorView: |
| 161 | IFACEMETHODIMP GetAt(unsigned index, AbiT* item) override { |
| 162 | return vector_ ? vector_->GetAt(index, item) : E_CHANGED_STATE; |
| 163 | } |
| 164 | |
| 165 | IFACEMETHODIMP get_Size(unsigned* size) override { |
| 166 | return vector_ ? vector_->get_Size(size) : E_CHANGED_STATE; |
| 167 | } |
| 168 | |
| 169 | IFACEMETHODIMP IndexOf(AbiT value, unsigned* index, boolean* found) override { |
| 170 | return vector_ ? vector_->IndexOf(std::move(value), index, found) |
| 171 | : E_CHANGED_STATE; |
| 172 | } |
| 173 | |
| 174 | IFACEMETHODIMP GetMany(unsigned start_index, |
| 175 | unsigned capacity, |
| 176 | AbiT* value, |
| 177 | unsigned* actual) override { |
| 178 | return vector_ ? vector_->GetMany(start_index, capacity, value, actual) |
| 179 | : E_CHANGED_STATE; |
| 180 | } |
| 181 | |
| 182 | // ABI::Windows::Foundation::Collections::VectorChangedEventHandler: |
| 183 | IFACEMETHODIMP Invoke( |
| 184 | ABI::Windows::Foundation::Collections::IObservableVector<LogicalT>* |
| 185 | sender, |
| 186 | ABI::Windows::Foundation::Collections::IVectorChangedEventArgs* e) |
| 187 | override { |
| 188 | DCHECK_EQ(vector_.Get(), sender); |
| 189 | vector_.Reset(); |
| 190 | sender->remove_VectorChanged(vector_changed_token_); |
| 191 | return S_OK; |
| 192 | } |
| 193 | |
| 194 | private: |
| 195 | Microsoft::WRL::ComPtr<Vector<LogicalT>> vector_; |
| 196 | EventRegistrationToken vector_changed_token_; |
| 197 | }; |
| 198 | |
| 199 | } // namespace internal |
| 200 | |
| 201 | // This file provides an implementation of Windows::Foundation::IVector. It |
| 202 | // functions as a thin wrapper around an std::vector, and dispatches method |
| 203 | // calls to either the corresponding std::vector API or appropriate |
| 204 | // std::algorithms. Furthermore, it notifies its observers whenever its |
jdoerrie | ce19c6d | 2018-07-03 09:42:51 | [diff] [blame] | 205 | // observable state changes. A base::win::Vector can be constructed for any type |
| 206 | // T, and is implicitly constructible from a std::vector. In the case where T is |
| 207 | // a pointer derived from IUnknown, the std::vector needs to be of type |
| 208 | // Microsoft::WRL::ComPtr<T>. This enforces proper reference counting and |
| 209 | // improves safety. |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 210 | template <typename T> |
| 211 | class Vector |
| 212 | : public Microsoft::WRL::RuntimeClass< |
| 213 | Microsoft::WRL::RuntimeClassFlags< |
| 214 | Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>, |
Julien Racle | cd8a64b | 2019-08-15 16:08:43 | [diff] [blame] | 215 | ABI::Windows::Foundation::Collections::IVector< |
| 216 | internal::VectorLogical<T>>, |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 217 | ABI::Windows::Foundation::Collections::IObservableVector< |
Julien Racle | cd8a64b | 2019-08-15 16:08:43 | [diff] [blame] | 218 | internal::VectorLogical<T>>, |
Julien Racle | a2070296 | 2019-04-10 17:19:45 | [diff] [blame] | 219 | ABI::Windows::Foundation::Collections::IIterable< |
Julien Racle | cd8a64b | 2019-08-15 16:08:43 | [diff] [blame] | 220 | internal::VectorLogical<T>>> { |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 221 | public: |
Julien Racle | cd8a64b | 2019-08-15 16:08:43 | [diff] [blame] | 222 | using LogicalT = internal::VectorLogical<T>; |
| 223 | using AbiT = internal::VectorAbi<T>; |
| 224 | using StorageT = internal::VectorStorage<T>; |
jdoerrie | ce19c6d | 2018-07-03 09:42:51 | [diff] [blame] | 225 | |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 226 | Vector() = default; |
jdoerrie | ce19c6d | 2018-07-03 09:42:51 | [diff] [blame] | 227 | explicit Vector(const std::vector<StorageT>& vector) : vector_(vector) {} |
| 228 | explicit Vector(std::vector<StorageT>&& vector) |
| 229 | : vector_(std::move(vector)) {} |
| 230 | |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 231 | // ABI::Windows::Foundation::Collections::IVector: |
| 232 | IFACEMETHODIMP GetAt(unsigned index, AbiT* item) override { |
| 233 | if (index >= vector_.size()) |
| 234 | return E_BOUNDS; |
jdoerrie | ce19c6d | 2018-07-03 09:42:51 | [diff] [blame] | 235 | return internal::CopyTo(vector_[index], item); |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 236 | } |
| 237 | |
| 238 | IFACEMETHODIMP get_Size(unsigned* size) override { |
| 239 | *size = vector_.size(); |
| 240 | return S_OK; |
| 241 | } |
| 242 | |
| 243 | IFACEMETHODIMP GetView( |
| 244 | ABI::Windows::Foundation::Collections::IVectorView<LogicalT>** view) |
| 245 | override { |
| 246 | return Microsoft::WRL::Make<internal::VectorView<LogicalT>>(this).CopyTo( |
| 247 | view); |
| 248 | } |
| 249 | |
| 250 | IFACEMETHODIMP IndexOf(AbiT value, unsigned* index, boolean* found) override { |
jdoerrie | ce19c6d | 2018-07-03 09:42:51 | [diff] [blame] | 251 | auto iter = std::find_if(vector_.begin(), vector_.end(), |
| 252 | [&value](const StorageT& elem) { |
| 253 | return internal::IsEqual(elem, value); |
| 254 | }); |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 255 | *index = iter != vector_.end() ? std::distance(vector_.begin(), iter) : 0; |
| 256 | *found = iter != vector_.end(); |
| 257 | return S_OK; |
| 258 | } |
| 259 | |
| 260 | IFACEMETHODIMP SetAt(unsigned index, AbiT item) override { |
| 261 | if (index >= vector_.size()) |
| 262 | return E_BOUNDS; |
| 263 | |
| 264 | vector_[index] = std::move(item); |
| 265 | NotifyVectorChanged( |
| 266 | ABI::Windows::Foundation::Collections::CollectionChange_ItemChanged, |
| 267 | index); |
| 268 | return S_OK; |
| 269 | } |
| 270 | |
| 271 | IFACEMETHODIMP InsertAt(unsigned index, AbiT item) override { |
| 272 | if (index > vector_.size()) |
| 273 | return E_BOUNDS; |
| 274 | |
| 275 | vector_.insert(std::next(vector_.begin(), index), std::move(item)); |
| 276 | NotifyVectorChanged( |
| 277 | ABI::Windows::Foundation::Collections::CollectionChange_ItemInserted, |
| 278 | index); |
| 279 | return S_OK; |
| 280 | } |
| 281 | |
| 282 | IFACEMETHODIMP RemoveAt(unsigned index) override { |
| 283 | if (index >= vector_.size()) |
| 284 | return E_BOUNDS; |
| 285 | |
| 286 | vector_.erase(std::next(vector_.begin(), index)); |
| 287 | NotifyVectorChanged( |
| 288 | ABI::Windows::Foundation::Collections::CollectionChange_ItemRemoved, |
| 289 | index); |
| 290 | return S_OK; |
| 291 | } |
| 292 | |
| 293 | IFACEMETHODIMP Append(AbiT item) override { |
| 294 | vector_.push_back(std::move(item)); |
| 295 | NotifyVectorChanged( |
| 296 | ABI::Windows::Foundation::Collections::CollectionChange_ItemInserted, |
| 297 | vector_.size() - 1); |
| 298 | return S_OK; |
| 299 | } |
| 300 | |
| 301 | IFACEMETHODIMP RemoveAtEnd() override { |
| 302 | if (vector_.empty()) |
| 303 | return E_BOUNDS; |
| 304 | |
| 305 | vector_.pop_back(); |
| 306 | NotifyVectorChanged( |
| 307 | ABI::Windows::Foundation::Collections::CollectionChange_ItemRemoved, |
| 308 | vector_.size()); |
| 309 | return S_OK; |
| 310 | } |
| 311 | |
| 312 | IFACEMETHODIMP Clear() override { |
| 313 | vector_.clear(); |
| 314 | NotifyVectorChanged( |
| 315 | ABI::Windows::Foundation::Collections::CollectionChange_Reset, 0); |
| 316 | return S_OK; |
| 317 | } |
| 318 | |
| 319 | IFACEMETHODIMP GetMany(unsigned start_index, |
| 320 | unsigned capacity, |
| 321 | AbiT* value, |
| 322 | unsigned* actual) override { |
| 323 | if (start_index > vector_.size()) |
| 324 | return E_BOUNDS; |
| 325 | |
| 326 | *actual = std::min<unsigned>(vector_.size() - start_index, capacity); |
jdoerrie | ce19c6d | 2018-07-03 09:42:51 | [diff] [blame] | 327 | return internal::CopyN(std::next(vector_.begin(), start_index), *actual, |
| 328 | value); |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 329 | } |
| 330 | |
| 331 | IFACEMETHODIMP ReplaceAll(unsigned count, AbiT* value) override { |
| 332 | vector_.assign(value, std::next(value, count)); |
| 333 | NotifyVectorChanged( |
| 334 | ABI::Windows::Foundation::Collections::CollectionChange_Reset, 0); |
| 335 | return S_OK; |
| 336 | } |
| 337 | |
| 338 | // ABI::Windows::Foundation::Collections::IObservableVector: |
| 339 | IFACEMETHODIMP add_VectorChanged( |
| 340 | ABI::Windows::Foundation::Collections::VectorChangedEventHandler< |
| 341 | LogicalT>* handler, |
| 342 | EventRegistrationToken* token) override { |
| 343 | token->value = handler_id_++; |
| 344 | handlers_.emplace_hint(handlers_.end(), token->value, handler); |
| 345 | return S_OK; |
| 346 | } |
| 347 | |
| 348 | IFACEMETHODIMP remove_VectorChanged(EventRegistrationToken token) override { |
| 349 | handlers_.erase(token.value); |
| 350 | return S_OK; |
| 351 | } |
| 352 | |
| 353 | void NotifyVectorChanged( |
| 354 | ABI::Windows::Foundation::Collections::CollectionChange change, |
| 355 | unsigned int index) { |
| 356 | auto args = |
| 357 | Microsoft::WRL::Make<internal::VectorChangedEventArgs>(change, index); |
| 358 | |
| 359 | // Invoking the handlers could result in mutations to the map, thus we make |
| 360 | // a copy beforehand. |
| 361 | auto handlers = handlers_; |
| 362 | for (auto& handler : handlers) |
| 363 | handler.second->Invoke(this, args.Get()); |
| 364 | } |
| 365 | |
Julien Racle | a2070296 | 2019-04-10 17:19:45 | [diff] [blame] | 366 | // ABI::Windows::Foundation::Collections::IIterable: |
| 367 | IFACEMETHODIMP First( |
Julien Racle | 2ed8c25 | 2019-08-08 15:56:50 | [diff] [blame] | 368 | ABI::Windows::Foundation::Collections::IIterator<LogicalT>** first) |
| 369 | override { |
Julien Racle | a2070296 | 2019-04-10 17:19:45 | [diff] [blame] | 370 | Microsoft::WRL::ComPtr< |
| 371 | ABI::Windows::Foundation::Collections::IVectorView<LogicalT>> |
| 372 | view; |
| 373 | HRESULT hr = GetView(&view); |
| 374 | if (SUCCEEDED(hr)) { |
| 375 | return Microsoft::WRL::Make<internal::VectorIterator<LogicalT>>(view) |
| 376 | .CopyTo(first); |
| 377 | } else { |
| 378 | return hr; |
| 379 | } |
| 380 | } |
| 381 | |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 382 | const std::vector<AbiT>& vector_for_testing() { return vector_; } |
| 383 | |
| 384 | private: |
| 385 | ~Vector() override { |
| 386 | // Handlers should not outlive the Vector. Furthermore, they must ensure |
Lei Zhang | 20b21af8 | 2020-08-10 18:31:58 | [diff] [blame] | 387 | // they are unregistered before the handler is destroyed. This implies |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 388 | // there should be no handlers left when the Vector is destructed. |
| 389 | DCHECK(handlers_.empty()); |
| 390 | } |
| 391 | |
jdoerrie | ce19c6d | 2018-07-03 09:42:51 | [diff] [blame] | 392 | std::vector<StorageT> vector_; |
Jan Wilken Dörrie | 2222967 | 2018-06-09 05:40:00 | [diff] [blame] | 393 | base::flat_map<int64_t, |
| 394 | ABI::Windows::Foundation::Collections:: |
| 395 | VectorChangedEventHandler<LogicalT>*> |
| 396 | handlers_; |
| 397 | int64_t handler_id_ = 0; |
| 398 | }; |
| 399 | |
| 400 | } // namespace win |
| 401 | } // namespace base |
| 402 | |
| 403 | #endif // BASE_WIN_VECTOR_H_ |