// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_WIN_VECTOR_H_ #define BASE_WIN_VECTOR_H_ #include #include #include #include #include #include #include #include "base/base_export.h" #include "base/check_op.h" #include "base/containers/flat_map.h" #include "base/win/winrt_foundation_helpers.h" namespace base { namespace win { template class Vector; namespace internal { // Template tricks needed to dispatch to the correct implementation. // See base/win/winrt_foundation_helpers.h for explanation. template using VectorComplex = typename ABI::Windows::Foundation::Collections::IVector::T_complex; template using VectorLogical = LogicalType>; template using VectorAbi = AbiType>; template using VectorStorage = StorageType>; template class VectorIterator : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::WinRtClassicComMix | Microsoft::WRL::InhibitRoOriginateError>, ABI::Windows::Foundation::Collections::IIterator>> { public: using LogicalT = VectorLogical; using AbiT = VectorAbi; explicit VectorIterator( Microsoft::WRL::ComPtr< ABI::Windows::Foundation::Collections::IVectorView> view) : view_(std::move(view)) {} // ABI::Windows::Foundation::Collections::IIterator: IFACEMETHODIMP get_Current(AbiT* current) override { return view_->GetAt(current_index_, current); } IFACEMETHODIMP get_HasCurrent(boolean* has_current) override { *has_current = FALSE; unsigned size; HRESULT hr = view_->get_Size(&size); if (SUCCEEDED(hr)) { if (current_index_ < size) { *has_current = TRUE; } } return hr; } IFACEMETHODIMP MoveNext(boolean* has_current) override { *has_current = FALSE; unsigned size; HRESULT hr = view_->get_Size(&size); if (FAILED(hr)) return hr; // Check if we're already past the last item. if (current_index_ >= size) return E_BOUNDS; // Move to the next item. current_index_++; // Set |has_current| to TRUE if we're still on a valid item. if (current_index_ < size) *has_current = TRUE; return hr; } IFACEMETHODIMP GetMany(unsigned capacity, AbiT* value, unsigned* actual) override { return view_->GetMany(current_index_, capacity, value, actual); } private: Microsoft::WRL::ComPtr< ABI::Windows::Foundation::Collections::IVectorView> view_; unsigned current_index_ = 0; }; class BASE_EXPORT VectorChangedEventArgs : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::WinRtClassicComMix | Microsoft::WRL::InhibitRoOriginateError>, ABI::Windows::Foundation::Collections::IVectorChangedEventArgs> { public: VectorChangedEventArgs( ABI::Windows::Foundation::Collections::CollectionChange change, unsigned int index) : change_(change), index_(index) {} ~VectorChangedEventArgs() override = default; // ABI::Windows::Foundation::Collections::IVectorChangedEventArgs: IFACEMETHODIMP get_CollectionChange( ABI::Windows::Foundation::Collections::CollectionChange* value) override; IFACEMETHODIMP get_Index(unsigned int* value) override; private: const ABI::Windows::Foundation::Collections::CollectionChange change_; const unsigned int index_; }; template class VectorView : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::WinRtClassicComMix | Microsoft::WRL::InhibitRoOriginateError>, ABI::Windows::Foundation::Collections::IVectorView>, ABI::Windows::Foundation::Collections::VectorChangedEventHandler< VectorLogical>> { public: using LogicalT = VectorLogical; using AbiT = VectorAbi; explicit VectorView(Microsoft::WRL::ComPtr> vector) : vector_(std::move(vector)) { vector_->add_VectorChanged(this, &vector_changed_token_); } ~VectorView() override { if (vector_) vector_->remove_VectorChanged(vector_changed_token_); } // ABI::Windows::Foundation::Collections::IVectorView: IFACEMETHODIMP GetAt(unsigned index, AbiT* item) override { return vector_ ? vector_->GetAt(index, item) : E_CHANGED_STATE; } IFACEMETHODIMP get_Size(unsigned* size) override { return vector_ ? vector_->get_Size(size) : E_CHANGED_STATE; } IFACEMETHODIMP IndexOf(AbiT value, unsigned* index, boolean* found) override { return vector_ ? vector_->IndexOf(std::move(value), index, found) : E_CHANGED_STATE; } IFACEMETHODIMP GetMany(unsigned start_index, unsigned capacity, AbiT* value, unsigned* actual) override { return vector_ ? vector_->GetMany(start_index, capacity, value, actual) : E_CHANGED_STATE; } // ABI::Windows::Foundation::Collections::VectorChangedEventHandler: IFACEMETHODIMP Invoke( ABI::Windows::Foundation::Collections::IObservableVector* sender, ABI::Windows::Foundation::Collections::IVectorChangedEventArgs* e) override { DCHECK_EQ(vector_.Get(), sender); vector_.Reset(); sender->remove_VectorChanged(vector_changed_token_); return S_OK; } private: Microsoft::WRL::ComPtr> vector_; EventRegistrationToken vector_changed_token_; }; } // namespace internal // This file provides an implementation of Windows::Foundation::IVector. It // functions as a thin wrapper around an std::vector, and dispatches method // calls to either the corresponding std::vector API or appropriate // std::algorithms. Furthermore, it notifies its observers whenever its // observable state changes. A base::win::Vector can be constructed for any type // T, and is implicitly constructible from a std::vector. In the case where T is // a pointer derived from IUnknown, the std::vector needs to be of type // Microsoft::WRL::ComPtr. This enforces proper reference counting and // improves safety. template class Vector : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>, ABI::Windows::Foundation::Collections::IVector< internal::VectorLogical>, ABI::Windows::Foundation::Collections::IObservableVector< internal::VectorLogical>, ABI::Windows::Foundation::Collections::IIterable< internal::VectorLogical>> { public: using LogicalT = internal::VectorLogical; using AbiT = internal::VectorAbi; using StorageT = internal::VectorStorage; Vector() = default; explicit Vector(const std::vector& vector) : vector_(vector) {} explicit Vector(std::vector&& vector) : vector_(std::move(vector)) {} // ABI::Windows::Foundation::Collections::IVector: IFACEMETHODIMP GetAt(unsigned index, AbiT* item) override { if (index >= vector_.size()) return E_BOUNDS; return internal::CopyTo(vector_[index], item); } IFACEMETHODIMP get_Size(unsigned* size) override { *size = vector_.size(); return S_OK; } IFACEMETHODIMP GetView( ABI::Windows::Foundation::Collections::IVectorView** view) override { return Microsoft::WRL::Make>(this).CopyTo( view); } IFACEMETHODIMP IndexOf(AbiT value, unsigned* index, boolean* found) override { auto iter = std::find_if(vector_.begin(), vector_.end(), [&value](const StorageT& elem) { return internal::IsEqual(elem, value); }); *index = iter != vector_.end() ? std::distance(vector_.begin(), iter) : 0; *found = iter != vector_.end(); return S_OK; } IFACEMETHODIMP SetAt(unsigned index, AbiT item) override { if (index >= vector_.size()) return E_BOUNDS; vector_[index] = std::move(item); NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange_ItemChanged, index); return S_OK; } IFACEMETHODIMP InsertAt(unsigned index, AbiT item) override { if (index > vector_.size()) return E_BOUNDS; vector_.insert(std::next(vector_.begin(), index), std::move(item)); NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange_ItemInserted, index); return S_OK; } IFACEMETHODIMP RemoveAt(unsigned index) override { if (index >= vector_.size()) return E_BOUNDS; vector_.erase(std::next(vector_.begin(), index)); NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange_ItemRemoved, index); return S_OK; } IFACEMETHODIMP Append(AbiT item) override { vector_.push_back(std::move(item)); NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange_ItemInserted, vector_.size() - 1); return S_OK; } IFACEMETHODIMP RemoveAtEnd() override { if (vector_.empty()) return E_BOUNDS; vector_.pop_back(); NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange_ItemRemoved, vector_.size()); return S_OK; } IFACEMETHODIMP Clear() override { vector_.clear(); NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange_Reset, 0); return S_OK; } IFACEMETHODIMP GetMany(unsigned start_index, unsigned capacity, AbiT* value, unsigned* actual) override { if (start_index > vector_.size()) return E_BOUNDS; *actual = std::min(vector_.size() - start_index, capacity); return internal::CopyN(std::next(vector_.begin(), start_index), *actual, value); } IFACEMETHODIMP ReplaceAll(unsigned count, AbiT* value) override { vector_.assign(value, std::next(value, count)); NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange_Reset, 0); return S_OK; } // ABI::Windows::Foundation::Collections::IObservableVector: IFACEMETHODIMP add_VectorChanged( ABI::Windows::Foundation::Collections::VectorChangedEventHandler< LogicalT>* handler, EventRegistrationToken* token) override { token->value = handler_id_++; handlers_.emplace_hint(handlers_.end(), token->value, handler); return S_OK; } IFACEMETHODIMP remove_VectorChanged(EventRegistrationToken token) override { handlers_.erase(token.value); return S_OK; } void NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange change, unsigned int index) { auto args = Microsoft::WRL::Make(change, index); // Invoking the handlers could result in mutations to the map, thus we make // a copy beforehand. auto handlers = handlers_; for (auto& handler : handlers) handler.second->Invoke(this, args.Get()); } // ABI::Windows::Foundation::Collections::IIterable: IFACEMETHODIMP First( ABI::Windows::Foundation::Collections::IIterator** first) override { Microsoft::WRL::ComPtr< ABI::Windows::Foundation::Collections::IVectorView> view; HRESULT hr = GetView(&view); if (SUCCEEDED(hr)) { return Microsoft::WRL::Make>(view) .CopyTo(first); } else { return hr; } } const std::vector& vector_for_testing() { return vector_; } private: ~Vector() override { // Handlers should not outlive the Vector. Furthermore, they must ensure // they are unregistered before the handler is destroyed. This implies // there should be no handlers left when the Vector is destructed. DCHECK(handlers_.empty()); } std::vector vector_; base::flat_map*> handlers_; int64_t handler_id_ = 0; }; } // namespace win } // namespace base #endif // BASE_WIN_VECTOR_H_