// Copyright 2018 The Chromium Authors
// 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 <ivectorchangedeventargs.h>
#include <windows.foundation.collections.h>
#include <wrl/implements.h>
#include <algorithm>
#include <iterator>
#include <utility>
#include <vector>
#include "base/base_export.h"
#include "base/check_op.h"
#include "base/containers/flat_map.h"
#include "base/ranges/algorithm.h"
#include "base/win/winrt_foundation_helpers.h"
namespace base {
namespace win {
template <typename T>
class Vector;
namespace internal {
// Template tricks needed to dispatch to the correct implementation.
// See base/win/winrt_foundation_helpers.h for explanation.
template <typename T>
using VectorComplex =
typename ABI::Windows::Foundation::Collections::IVector<T>::T_complex;
template <typename T>
using VectorLogical = LogicalType<VectorComplex<T>>;
template <typename T>
using VectorAbi = AbiType<VectorComplex<T>>;
template <typename T>
using VectorStorage = StorageType<VectorComplex<T>>;
template <typename T>
class VectorIterator
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<
Microsoft::WRL::WinRtClassicComMix |
Microsoft::WRL::InhibitRoOriginateError>,
ABI::Windows::Foundation::Collections::IIterator<VectorLogical<T>>> {
public:
using LogicalT = VectorLogical<T>;
using AbiT = VectorAbi<T>;
explicit VectorIterator(
Microsoft::WRL::ComPtr<
ABI::Windows::Foundation::Collections::IVectorView<LogicalT>> 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<LogicalT>>
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 <typename T>
class VectorView
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<
Microsoft::WRL::WinRtClassicComMix |
Microsoft::WRL::InhibitRoOriginateError>,
ABI::Windows::Foundation::Collections::IVectorView<VectorLogical<T>>,
ABI::Windows::Foundation::Collections::VectorChangedEventHandler<
VectorLogical<T>>> {
public:
using LogicalT = VectorLogical<T>;
using AbiT = VectorAbi<T>;
explicit VectorView(Microsoft::WRL::ComPtr<Vector<LogicalT>> 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<LogicalT>*
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<LogicalT>> 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<T>. This enforces proper reference counting and
// improves safety.
template <typename T>
class Vector
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<
Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>,
ABI::Windows::Foundation::Collections::IVector<
internal::VectorLogical<T>>,
ABI::Windows::Foundation::Collections::IObservableVector<
internal::VectorLogical<T>>,
ABI::Windows::Foundation::Collections::IIterable<
internal::VectorLogical<T>>> {
public:
using LogicalT = internal::VectorLogical<T>;
using AbiT = internal::VectorAbi<T>;
using StorageT = internal::VectorStorage<T>;
Vector() = default;
explicit Vector(const std::vector<StorageT>& vector) : vector_(vector) {}
explicit Vector(std::vector<StorageT>&& 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<LogicalT>** view)
override {
return Microsoft::WRL::Make<internal::VectorView<LogicalT>>(this).CopyTo(
view);
}
IFACEMETHODIMP IndexOf(AbiT value, unsigned* index, boolean* found) override {
auto iter = base::ranges::find_if(vector_, [&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<unsigned>(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<internal::VectorChangedEventArgs>(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<LogicalT>** first)
override {
Microsoft::WRL::ComPtr<
ABI::Windows::Foundation::Collections::IVectorView<LogicalT>>
view;
HRESULT hr = GetView(&view);
if (SUCCEEDED(hr)) {
return Microsoft::WRL::Make<internal::VectorIterator<LogicalT>>(view)
.CopyTo(first);
} else {
return hr;
}
}
const std::vector<AbiT>& 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<StorageT> vector_;
base::flat_map<int64_t,
ABI::Windows::Foundation::Collections::
VectorChangedEventHandler<LogicalT>*>
handlers_;
int64_t handler_id_ = 0;
};
} // namespace win
} // namespace base
#endif // BASE_WIN_VECTOR_H_