// Copyright 2019 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_MAP_H_ #define BASE_WIN_MAP_H_ #include <windows.foundation.collections.h> #include <wrl/implements.h> #include <map> #include "base/check_op.h" #include "base/notreached.h" #include "base/stl_util.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 { *replaced = !InsertOrAssign(map_, key, std::move(value)).second; 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); // NOLINT(modernize-use-nullptr): AbiK may not be a pointer. 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_