| // Copyright 2019 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_MAP_H_ |
| #define BASE_WIN_MAP_H_ |
| |
| #include <windows.foundation.collections.h> |
| #include <wrl/implements.h> |
| |
| #include <map> |
| #include <utility> |
| |
| #include "base/check_op.h" |
| #include "base/containers/contains.h" |
| #include "base/notreached.h" |
| #include "base/win/vector.h" |
| #include "base/win/winrt_foundation_helpers.h" |
| |
| namespace base { |
| namespace win { |
| |
| template <typename K, typename V> |
| class Map; |
| |
| namespace internal { |
| |
| // Template tricks needed to dispatch to the correct implementation. |
| // See base/win/winrt_foundation_helpers.h for explanation. |
| |
| template <typename K, typename V> |
| using ComplexK = |
| typename ABI::Windows::Foundation::Collections::IMap<K, V>::K_complex; |
| |
| template <typename K, typename V> |
| using ComplexV = |
| typename ABI::Windows::Foundation::Collections::IMap<K, V>::V_complex; |
| |
| template <typename K, typename V> |
| using LogicalK = LogicalType<ComplexK<K, V>>; |
| |
| template <typename K, typename V> |
| using LogicalV = LogicalType<ComplexV<K, V>>; |
| |
| template <typename K, typename V> |
| using AbiK = AbiType<ComplexK<K, V>>; |
| |
| template <typename K, typename V> |
| using AbiV = AbiType<ComplexV<K, V>>; |
| |
| template <typename K, typename V> |
| using StorageK = StorageType<ComplexK<K, V>>; |
| |
| template <typename K, typename V> |
| using StorageV = StorageType<ComplexV<K, V>>; |
| |
| template <typename K, typename V> |
| class KeyValuePair : public Microsoft::WRL::RuntimeClass< |
| Microsoft::WRL::RuntimeClassFlags< |
| Microsoft::WRL::WinRtClassicComMix | |
| Microsoft::WRL::InhibitRoOriginateError>, |
| ABI::Windows::Foundation::Collections:: |
| IKeyValuePair<LogicalK<K, V>, LogicalV<K, V>>> { |
| public: |
| using AbiK = AbiK<K, V>; |
| using AbiV = AbiV<K, V>; |
| using StorageK = StorageK<K, V>; |
| using StorageV = StorageV<K, V>; |
| |
| KeyValuePair(StorageK key, StorageV value) |
| : key_(std::move(key)), value_(std::move(value)) {} |
| |
| // ABI::Windows::Foundation::Collections::IKeyValuePair: |
| IFACEMETHODIMP get_Key(AbiK* key) { return CopyTo(key_, key); } |
| |
| IFACEMETHODIMP get_Value(AbiV* value) { return CopyTo(value_, value); } |
| |
| private: |
| StorageK key_; |
| StorageV value_; |
| }; |
| |
| template <typename K> |
| class MapChangedEventArgs |
| : public Microsoft::WRL::RuntimeClass< |
| Microsoft::WRL::RuntimeClassFlags< |
| Microsoft::WRL::WinRtClassicComMix | |
| Microsoft::WRL::InhibitRoOriginateError>, |
| ABI::Windows::Foundation::Collections::IMapChangedEventArgs<K>> { |
| public: |
| MapChangedEventArgs( |
| ABI::Windows::Foundation::Collections::CollectionChange change, |
| K key) |
| : change_(change), key_(std::move(key)) {} |
| |
| ~MapChangedEventArgs() override = default; |
| |
| // ABI::Windows::Foundation::Collections::IMapChangedEventArgs: |
| IFACEMETHODIMP get_CollectionChange( |
| ABI::Windows::Foundation::Collections::CollectionChange* value) override { |
| *value = change_; |
| return S_OK; |
| } |
| |
| IFACEMETHODIMP get_Key(K* value) override { |
| *value = key_; |
| return S_OK; |
| } |
| |
| private: |
| const ABI::Windows::Foundation::Collections::CollectionChange change_; |
| K key_; |
| }; |
| |
| } // namespace internal |
| |
| // This file provides an implementation of Windows::Foundation::IMap. It |
| // functions as a thin wrapper around an std::map, and dispatches |
| // method calls to either the corresponding std::map API or |
| // appropriate std algorithms. Furthermore, it notifies its observers whenever |
| // its observable state changes, and is iterable. Please notice also that if the |
| // map is modified while iterating over it, iterator methods will return |
| // E_CHANGED_STATE. A base::win::Map can be constructed for any types <K,V>, and |
| // is implicitly constructible from a std::map. In the case where K or V is a |
| // pointer derived from IUnknown, the std::map needs to be of type |
| // Microsoft::WRL::ComPtr<K> or Microsoft::WRL::ComPtr<V>. This enforces proper |
| // reference counting and improves safety. |
| template <typename K, typename V> |
| class Map |
| : public Microsoft::WRL::RuntimeClass< |
| Microsoft::WRL::RuntimeClassFlags< |
| Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>, |
| ABI::Windows::Foundation::Collections::IMap<internal::LogicalK<K, V>, |
| internal::LogicalV<K, V>>, |
| ABI::Windows::Foundation::Collections::IObservableMap< |
| internal::LogicalK<K, V>, |
| internal::LogicalV<K, V>>, |
| ABI::Windows::Foundation::Collections::IIterable< |
| ABI::Windows::Foundation::Collections::IKeyValuePair< |
| internal::LogicalK<K, V>, |
| internal::LogicalV<K, V>>*>> { |
| public: |
| using LogicalK = internal::LogicalK<K, V>; |
| using LogicalV = internal::LogicalV<K, V>; |
| using AbiK = internal::AbiK<K, V>; |
| using AbiV = internal::AbiV<K, V>; |
| using StorageK = internal::StorageK<K, V>; |
| using StorageV = internal::StorageV<K, V>; |
| |
| private: |
| class MapView; |
| |
| // Iterates over base::win::Map. |
| // Its methods return E_CHANGED_STATE is the map is modified. |
| // TODO(https://crbug.com/987533): Refactor MapIterator to leverage |
| // std::map::iterator. |
| class MapIterator |
| : public Microsoft::WRL::RuntimeClass< |
| Microsoft::WRL::RuntimeClassFlags< |
| Microsoft::WRL::WinRtClassicComMix | |
| Microsoft::WRL::InhibitRoOriginateError>, |
| ABI::Windows::Foundation::Collections::IIterator< |
| ABI::Windows::Foundation::Collections::IKeyValuePair< |
| internal::LogicalK<K, V>, |
| internal::LogicalV<K, V>>*>> { |
| public: |
| explicit MapIterator(Microsoft::WRL::ComPtr<MapView> view) |
| : view_(std::move(view)) { |
| DCHECK(view_->ValidState()); |
| ConvertMapToVectorIterator(); |
| } |
| |
| // ABI::Windows::Foundation::Collections::IIterator: |
| IFACEMETHODIMP get_Current( |
| ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK, |
| LogicalV>** |
| current) override { |
| return view_->ValidState() ? iterator_->get_Current(current) |
| : E_CHANGED_STATE; |
| } |
| |
| IFACEMETHODIMP get_HasCurrent(boolean* has_current) override { |
| return view_->ValidState() ? iterator_->get_HasCurrent(has_current) |
| : E_CHANGED_STATE; |
| } |
| |
| IFACEMETHODIMP MoveNext(boolean* has_current) override { |
| return view_->ValidState() ? iterator_->MoveNext(has_current) |
| : E_CHANGED_STATE; |
| } |
| |
| IFACEMETHODIMP GetMany( |
| unsigned capacity, |
| ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK, |
| LogicalV>** value, |
| unsigned* actual) override { |
| return view_->ValidState() ? iterator_->GetMany(capacity, value, actual) |
| : E_CHANGED_STATE; |
| } |
| |
| private: |
| // Helper for iteration: |
| void ConvertMapToVectorIterator() { |
| // Create a vector that will hold Map's key-value pairs. |
| auto vector = Microsoft::WRL::Make< |
| Vector<ABI::Windows::Foundation::Collections::IKeyValuePair< |
| LogicalK, LogicalV>*>>(); |
| |
| // Fill the vector with container data. |
| for (const auto& pair : view_->get_map()) { |
| auto key_value_pair = |
| Microsoft::WRL::Make<internal::KeyValuePair<AbiK, AbiV>>( |
| pair.first, pair.second); |
| vector->Append(key_value_pair.Get()); |
| } |
| |
| // Return an iterator to that vector. |
| // Iterator is immutable (wraps an IVectorView) and Vector's lifecycle is |
| // ensured cause the view holds a reference to the vector, and iterator |
| // holds a reference to the view. |
| HRESULT hr = vector->First(&iterator_); |
| DCHECK(SUCCEEDED(hr)); |
| } |
| |
| Microsoft::WRL::ComPtr<MapView> view_; |
| Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IIterator< |
| ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK, |
| LogicalV>*>> |
| iterator_; |
| }; |
| |
| class MapView |
| : public Microsoft::WRL::RuntimeClass< |
| Microsoft::WRL::RuntimeClassFlags< |
| Microsoft::WRL::WinRtClassicComMix | |
| Microsoft::WRL::InhibitRoOriginateError>, |
| ABI::Windows::Foundation::Collections:: |
| IMapView<internal::LogicalK<K, V>, internal::LogicalV<K, V>>, |
| ABI::Windows::Foundation::Collections::IIterable< |
| ABI::Windows::Foundation::Collections::IKeyValuePair< |
| internal::LogicalK<K, V>, |
| internal::LogicalV<K, V>>*>, |
| ABI::Windows::Foundation::Collections::MapChangedEventHandler< |
| internal::LogicalK<K, V>, |
| internal::LogicalV<K, V>>> { |
| public: |
| explicit MapView(Microsoft::WRL::ComPtr<Map<LogicalK, LogicalV>> map) |
| : map_(std::move(map)) { |
| map_->add_MapChanged(this, &map_changed_token_); |
| } |
| |
| ~MapView() override { |
| if (map_) |
| map_->remove_MapChanged(map_changed_token_); |
| } |
| |
| // ABI::Windows::Foundation::Collections::IMapView: |
| IFACEMETHODIMP Lookup(AbiK key, AbiV* value) override { |
| return map_ ? map_->Lookup(key, value) : E_CHANGED_STATE; |
| } |
| |
| IFACEMETHODIMP get_Size(unsigned int* size) override { |
| return map_ ? map_->get_Size(size) : E_CHANGED_STATE; |
| } |
| |
| IFACEMETHODIMP HasKey(AbiK key, boolean* found) override { |
| return map_ ? map_->HasKey(key, found) : E_CHANGED_STATE; |
| } |
| |
| IFACEMETHODIMP Split( |
| ABI::Windows::Foundation::Collections::IMapView<LogicalK, LogicalV>** |
| first_partition, |
| ABI::Windows::Foundation::Collections::IMapView<LogicalK, LogicalV>** |
| second_partition) override { |
| NOTIMPLEMENTED(); |
| return E_NOTIMPL; |
| } |
| |
| // ABI::Windows::Foundation::Collections::IIterable: |
| IFACEMETHODIMP First( |
| ABI::Windows::Foundation::Collections::IIterator< |
| ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK, |
| LogicalV>*>** |
| first) override { |
| return map_ ? map_->First(first) : E_CHANGED_STATE; |
| } |
| |
| // ABI::Windows::Foundation::Collections::MapChangedEventHandler: |
| IFACEMETHODIMP Invoke( |
| ABI::Windows::Foundation::Collections::IObservableMap<LogicalK, |
| LogicalV>* sender, |
| ABI::Windows::Foundation::Collections::IMapChangedEventArgs<LogicalK>* |
| e) override { |
| DCHECK_EQ(map_.Get(), sender); |
| map_.Reset(); |
| sender->remove_MapChanged(map_changed_token_); |
| return S_OK; |
| } |
| |
| // Accessor used in MapIterator for iterating over Map's container. |
| // Will remain valid during the entire iteration. |
| const std::map<StorageK, StorageV, internal::Less>& get_map() { |
| DCHECK(map_); |
| return map_->map_; |
| } |
| |
| bool ValidState() const { return map_; } |
| |
| private: |
| Microsoft::WRL::ComPtr<Map<LogicalK, LogicalV>> map_; |
| EventRegistrationToken map_changed_token_; |
| }; |
| |
| public: |
| Map() = default; |
| explicit Map(const std::map<StorageK, StorageV, internal::Less>& map) |
| : map_(map) {} |
| explicit Map(std::map<StorageK, StorageV, internal::Less>&& map) |
| : map_(std::move(map)) {} |
| |
| // ABI::Windows::Foundation::Collections::IMap: |
| IFACEMETHODIMP Lookup(AbiK key, AbiV* value) override { |
| auto it = map_.find(key); |
| if (it == map_.cend()) |
| return E_BOUNDS; |
| |
| return internal::CopyTo(it->second, value); |
| } |
| |
| IFACEMETHODIMP get_Size(unsigned int* size) override { |
| *size = map_.size(); |
| return S_OK; |
| } |
| |
| IFACEMETHODIMP HasKey(AbiK key, boolean* found) override { |
| *found = Contains(map_, key); |
| return S_OK; |
| } |
| |
| IFACEMETHODIMP GetView( |
| ABI::Windows::Foundation::Collections::IMapView<LogicalK, LogicalV>** |
| view) override { |
| return Microsoft::WRL::Make<MapView>(this).CopyTo(view); |
| } |
| |
| IFACEMETHODIMP Insert(AbiK key, AbiV value, boolean* replaced) override { |
| auto [it, inserted] = map_.insert_or_assign(key, std::move(value)); |
| *replaced = !inserted; |
| NotifyMapChanged(*replaced ? ABI::Windows::Foundation::Collections:: |
| CollectionChange_ItemChanged |
| : ABI::Windows::Foundation::Collections:: |
| CollectionChange_ItemInserted, |
| key); |
| return S_OK; |
| } |
| |
| IFACEMETHODIMP Remove(AbiK key) override { |
| if (!map_.erase(key)) |
| return E_BOUNDS; |
| |
| NotifyMapChanged( |
| ABI::Windows::Foundation::Collections::CollectionChange_ItemRemoved, |
| key); |
| return S_OK; |
| } |
| |
| IFACEMETHODIMP Clear() override { |
| map_.clear(); |
| NotifyMapChanged( |
| ABI::Windows::Foundation::Collections::CollectionChange_Reset, 0); |
| return S_OK; |
| } |
| |
| // ABI::Windows::Foundation::Collections::IObservableMap: |
| IFACEMETHODIMP add_MapChanged( |
| ABI::Windows::Foundation::Collections::MapChangedEventHandler<LogicalK, |
| LogicalV>* |
| handler, |
| EventRegistrationToken* token) override { |
| token->value = handler_id_++; |
| handlers_.emplace_hint(handlers_.end(), token->value, handler); |
| return S_OK; |
| } |
| |
| IFACEMETHODIMP remove_MapChanged(EventRegistrationToken token) override { |
| return handlers_.erase(token.value) ? S_OK : E_BOUNDS; |
| } |
| |
| // ABI::Windows::Foundation::Collections::IIterable: |
| IFACEMETHODIMP First( |
| ABI::Windows::Foundation::Collections::IIterator< |
| ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK, |
| LogicalV>*>** |
| first) override { |
| return Microsoft::WRL::Make<MapIterator>( |
| Microsoft::WRL::Make<MapView>(this)) |
| .CopyTo(first); |
| } |
| |
| private: |
| ~Map() override { |
| // Handlers should not outlive the Map. Furthermore, they must ensure |
| // they are unregistered before the handler is destroyed. This implies |
| // there should be no handlers left when the Map is destructed. |
| DCHECK(handlers_.empty()); |
| } |
| |
| void NotifyMapChanged( |
| ABI::Windows::Foundation::Collections::CollectionChange change, |
| AbiK key) { |
| auto args = |
| Microsoft::WRL::Make<internal::MapChangedEventArgs<AbiK>>(change, key); |
| |
| // 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()); |
| } |
| |
| std::map<StorageK, StorageV, internal::Less> map_; |
| base::flat_map<int64_t, |
| ABI::Windows::Foundation::Collections:: |
| MapChangedEventHandler<LogicalK, LogicalV>*> |
| handlers_; |
| int64_t handler_id_ = 0; |
| }; |
| |
| } // namespace win |
| } // namespace base |
| |
| #endif // BASE_WIN_MAP_H_ |