// 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 #include #include #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 class Map; namespace internal { // Template tricks needed to dispatch to the correct implementation. // See base/win/winrt_foundation_helpers.h for explanation. template using ComplexK = typename ABI::Windows::Foundation::Collections::IMap::K_complex; template using ComplexV = typename ABI::Windows::Foundation::Collections::IMap::V_complex; template using LogicalK = LogicalType>; template using LogicalV = LogicalType>; template using AbiK = AbiType>; template using AbiV = AbiType>; template using StorageK = StorageType>; template using StorageV = StorageType>; template class KeyValuePair : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::WinRtClassicComMix | Microsoft::WRL::InhibitRoOriginateError>, ABI::Windows::Foundation::Collections:: IKeyValuePair, LogicalV>> { public: using AbiK = AbiK; using AbiV = AbiV; using StorageK = StorageK; using StorageV = StorageV; 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 class MapChangedEventArgs : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::WinRtClassicComMix | Microsoft::WRL::InhibitRoOriginateError>, ABI::Windows::Foundation::Collections::IMapChangedEventArgs> { 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 , 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 or Microsoft::WRL::ComPtr. This enforces proper // reference counting and improves safety. template class Map : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>, ABI::Windows::Foundation::Collections::IMap, internal::LogicalV>, ABI::Windows::Foundation::Collections::IObservableMap< internal::LogicalK, internal::LogicalV>, ABI::Windows::Foundation::Collections::IIterable< ABI::Windows::Foundation::Collections::IKeyValuePair< internal::LogicalK, internal::LogicalV>*>> { public: using LogicalK = internal::LogicalK; using LogicalV = internal::LogicalV; using AbiK = internal::AbiK; using AbiV = internal::AbiV; using StorageK = internal::StorageK; using StorageV = internal::StorageV; 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, internal::LogicalV>*>> { public: explicit MapIterator(Microsoft::WRL::ComPtr view) : view_(std::move(view)) { DCHECK(view_->ValidState()); ConvertMapToVectorIterator(); } // ABI::Windows::Foundation::Collections::IIterator: IFACEMETHODIMP get_Current( ABI::Windows::Foundation::Collections::IKeyValuePair** 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** 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*>>(); // Fill the vector with container data. for (const auto& pair : view_->get_map()) { auto key_value_pair = Microsoft::WRL::Make>( 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 view_; Microsoft::WRL::ComPtr*>> iterator_; }; class MapView : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::WinRtClassicComMix | Microsoft::WRL::InhibitRoOriginateError>, ABI::Windows::Foundation::Collections:: IMapView, internal::LogicalV>, ABI::Windows::Foundation::Collections::IIterable< ABI::Windows::Foundation::Collections::IKeyValuePair< internal::LogicalK, internal::LogicalV>*>, ABI::Windows::Foundation::Collections::MapChangedEventHandler< internal::LogicalK, internal::LogicalV>> { public: explicit MapView(Microsoft::WRL::ComPtr> 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** first_partition, ABI::Windows::Foundation::Collections::IMapView** second_partition) override { NOTIMPLEMENTED(); return E_NOTIMPL; } // ABI::Windows::Foundation::Collections::IIterable: IFACEMETHODIMP First( ABI::Windows::Foundation::Collections::IIterator< ABI::Windows::Foundation::Collections::IKeyValuePair*>** first) override { return map_ ? map_->First(first) : E_CHANGED_STATE; } // ABI::Windows::Foundation::Collections::MapChangedEventHandler: IFACEMETHODIMP Invoke( ABI::Windows::Foundation::Collections::IObservableMap* sender, ABI::Windows::Foundation::Collections::IMapChangedEventArgs* 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& get_map() { DCHECK(map_); return map_->map_; } bool ValidState() const { return map_; } private: Microsoft::WRL::ComPtr> map_; EventRegistrationToken map_changed_token_; }; public: Map() = default; explicit Map(const std::map& map) : map_(map) {} explicit Map(std::map&& 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** view) override { return Microsoft::WRL::Make(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* 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*>** first) override { return Microsoft::WRL::Make( Microsoft::WRL::Make(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>(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 map_; base::flat_map*> handlers_; int64_t handler_id_ = 0; }; } // namespace win } // namespace base #endif // BASE_WIN_MAP_H_