1 // Copyright 2019 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_WIN_MAP_H_ 6 #define BASE_WIN_MAP_H_ 7 8 #include <windows.foundation.collections.h> 9 #include <wrl/implements.h> 10 11 #include <map> 12 #include <utility> 13 14 #include "base/check_op.h" 15 #include "base/containers/contains.h" 16 #include "base/notimplemented.h" 17 #include "base/win/vector.h" 18 #include "base/win/winrt_foundation_helpers.h" 19 20 namespace base { 21 namespace win { 22 23 template <typename K, typename V> 24 class Map; 25 26 namespace internal { 27 28 // Template tricks needed to dispatch to the correct implementation. 29 // See base/win/winrt_foundation_helpers.h for explanation. 30 31 template <typename K, typename V> 32 using ComplexK = 33 typename ABI::Windows::Foundation::Collections::IMap<K, V>::K_complex; 34 35 template <typename K, typename V> 36 using ComplexV = 37 typename ABI::Windows::Foundation::Collections::IMap<K, V>::V_complex; 38 39 template <typename K, typename V> 40 using LogicalK = LogicalType<ComplexK<K, V>>; 41 42 template <typename K, typename V> 43 using LogicalV = LogicalType<ComplexV<K, V>>; 44 45 template <typename K, typename V> 46 using AbiK = AbiType<ComplexK<K, V>>; 47 48 template <typename K, typename V> 49 using AbiV = AbiType<ComplexV<K, V>>; 50 51 template <typename K, typename V> 52 using StorageK = StorageType<ComplexK<K, V>>; 53 54 template <typename K, typename V> 55 using StorageV = StorageType<ComplexV<K, V>>; 56 57 template <typename K, typename V> 58 class KeyValuePair : public Microsoft::WRL::RuntimeClass< 59 Microsoft::WRL::RuntimeClassFlags< 60 Microsoft::WRL::WinRtClassicComMix | 61 Microsoft::WRL::InhibitRoOriginateError>, 62 ABI::Windows::Foundation::Collections:: 63 IKeyValuePair<LogicalK<K, V>, LogicalV<K, V>>> { 64 public: 65 using AbiK = AbiK<K, V>; 66 using AbiV = AbiV<K, V>; 67 using StorageK = StorageK<K, V>; 68 using StorageV = StorageV<K, V>; 69 KeyValuePair(StorageK key,StorageV value)70 KeyValuePair(StorageK key, StorageV value) 71 : key_(std::move(key)), value_(std::move(value)) {} 72 73 // ABI::Windows::Foundation::Collections::IKeyValuePair: get_Key(AbiK * key)74 IFACEMETHODIMP get_Key(AbiK* key) { return CopyTo(key_, key); } 75 get_Value(AbiV * value)76 IFACEMETHODIMP get_Value(AbiV* value) { return CopyTo(value_, value); } 77 78 private: 79 StorageK key_; 80 StorageV value_; 81 }; 82 83 template <typename K> 84 class MapChangedEventArgs 85 : public Microsoft::WRL::RuntimeClass< 86 Microsoft::WRL::RuntimeClassFlags< 87 Microsoft::WRL::WinRtClassicComMix | 88 Microsoft::WRL::InhibitRoOriginateError>, 89 ABI::Windows::Foundation::Collections::IMapChangedEventArgs<K>> { 90 public: MapChangedEventArgs(ABI::Windows::Foundation::Collections::CollectionChange change,K key)91 MapChangedEventArgs( 92 ABI::Windows::Foundation::Collections::CollectionChange change, 93 K key) 94 : change_(change), key_(std::move(key)) {} 95 96 ~MapChangedEventArgs() override = default; 97 98 // ABI::Windows::Foundation::Collections::IMapChangedEventArgs: get_CollectionChange(ABI::Windows::Foundation::Collections::CollectionChange * value)99 IFACEMETHODIMP get_CollectionChange( 100 ABI::Windows::Foundation::Collections::CollectionChange* value) override { 101 *value = change_; 102 return S_OK; 103 } 104 get_Key(K * value)105 IFACEMETHODIMP get_Key(K* value) override { 106 *value = key_; 107 return S_OK; 108 } 109 110 private: 111 const ABI::Windows::Foundation::Collections::CollectionChange change_; 112 K key_; 113 }; 114 115 } // namespace internal 116 117 // This file provides an implementation of Windows::Foundation::IMap. It 118 // functions as a thin wrapper around an std::map, and dispatches 119 // method calls to either the corresponding std::map API or 120 // appropriate std algorithms. Furthermore, it notifies its observers whenever 121 // its observable state changes, and is iterable. Please notice also that if the 122 // map is modified while iterating over it, iterator methods will return 123 // E_CHANGED_STATE. A base::win::Map can be constructed for any types <K,V>, and 124 // is implicitly constructible from a std::map. In the case where K or V is a 125 // pointer derived from IUnknown, the std::map needs to be of type 126 // Microsoft::WRL::ComPtr<K> or Microsoft::WRL::ComPtr<V>. This enforces proper 127 // reference counting and improves safety. 128 template <typename K, typename V> 129 class Map 130 : public Microsoft::WRL::RuntimeClass< 131 Microsoft::WRL::RuntimeClassFlags< 132 Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>, 133 ABI::Windows::Foundation::Collections::IMap<internal::LogicalK<K, V>, 134 internal::LogicalV<K, V>>, 135 ABI::Windows::Foundation::Collections::IObservableMap< 136 internal::LogicalK<K, V>, 137 internal::LogicalV<K, V>>, 138 ABI::Windows::Foundation::Collections::IIterable< 139 ABI::Windows::Foundation::Collections::IKeyValuePair< 140 internal::LogicalK<K, V>, 141 internal::LogicalV<K, V>>*>> { 142 public: 143 using LogicalK = internal::LogicalK<K, V>; 144 using LogicalV = internal::LogicalV<K, V>; 145 using AbiK = internal::AbiK<K, V>; 146 using AbiV = internal::AbiV<K, V>; 147 using StorageK = internal::StorageK<K, V>; 148 using StorageV = internal::StorageV<K, V>; 149 150 private: 151 class MapView; 152 153 // Iterates over base::win::Map. 154 // Its methods return E_CHANGED_STATE is the map is modified. 155 // TODO(https://crbug.com/987533): Refactor MapIterator to leverage 156 // std::map::iterator. 157 class MapIterator 158 : public Microsoft::WRL::RuntimeClass< 159 Microsoft::WRL::RuntimeClassFlags< 160 Microsoft::WRL::WinRtClassicComMix | 161 Microsoft::WRL::InhibitRoOriginateError>, 162 ABI::Windows::Foundation::Collections::IIterator< 163 ABI::Windows::Foundation::Collections::IKeyValuePair< 164 internal::LogicalK<K, V>, 165 internal::LogicalV<K, V>>*>> { 166 public: MapIterator(Microsoft::WRL::ComPtr<MapView> view)167 explicit MapIterator(Microsoft::WRL::ComPtr<MapView> view) 168 : view_(std::move(view)) { 169 DCHECK(view_->ValidState()); 170 ConvertMapToVectorIterator(); 171 } 172 173 // ABI::Windows::Foundation::Collections::IIterator: get_Current(ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK,LogicalV> ** current)174 IFACEMETHODIMP get_Current( 175 ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK, 176 LogicalV>** 177 current) override { 178 return view_->ValidState() ? iterator_->get_Current(current) 179 : E_CHANGED_STATE; 180 } 181 get_HasCurrent(boolean * has_current)182 IFACEMETHODIMP get_HasCurrent(boolean* has_current) override { 183 return view_->ValidState() ? iterator_->get_HasCurrent(has_current) 184 : E_CHANGED_STATE; 185 } 186 MoveNext(boolean * has_current)187 IFACEMETHODIMP MoveNext(boolean* has_current) override { 188 return view_->ValidState() ? iterator_->MoveNext(has_current) 189 : E_CHANGED_STATE; 190 } 191 GetMany(unsigned capacity,ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK,LogicalV> ** value,unsigned * actual)192 IFACEMETHODIMP GetMany( 193 unsigned capacity, 194 ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK, 195 LogicalV>** value, 196 unsigned* actual) override { 197 return view_->ValidState() ? iterator_->GetMany(capacity, value, actual) 198 : E_CHANGED_STATE; 199 } 200 201 private: 202 // Helper for iteration: ConvertMapToVectorIterator()203 void ConvertMapToVectorIterator() { 204 // Create a vector that will hold Map's key-value pairs. 205 auto vector = Microsoft::WRL::Make< 206 Vector<ABI::Windows::Foundation::Collections::IKeyValuePair< 207 LogicalK, LogicalV>*>>(); 208 209 // Fill the vector with container data. 210 for (const auto& pair : view_->get_map()) { 211 auto key_value_pair = 212 Microsoft::WRL::Make<internal::KeyValuePair<AbiK, AbiV>>( 213 pair.first, pair.second); 214 vector->Append(key_value_pair.Get()); 215 } 216 217 // Return an iterator to that vector. 218 // Iterator is immutable (wraps an IVectorView) and Vector's lifecycle is 219 // ensured cause the view holds a reference to the vector, and iterator 220 // holds a reference to the view. 221 HRESULT hr = vector->First(&iterator_); 222 DCHECK(SUCCEEDED(hr)); 223 } 224 225 Microsoft::WRL::ComPtr<MapView> view_; 226 Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IIterator< 227 ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK, 228 LogicalV>*>> 229 iterator_; 230 }; 231 232 class MapView 233 : public Microsoft::WRL::RuntimeClass< 234 Microsoft::WRL::RuntimeClassFlags< 235 Microsoft::WRL::WinRtClassicComMix | 236 Microsoft::WRL::InhibitRoOriginateError>, 237 ABI::Windows::Foundation::Collections:: 238 IMapView<internal::LogicalK<K, V>, internal::LogicalV<K, V>>, 239 ABI::Windows::Foundation::Collections::IIterable< 240 ABI::Windows::Foundation::Collections::IKeyValuePair< 241 internal::LogicalK<K, V>, 242 internal::LogicalV<K, V>>*>, 243 ABI::Windows::Foundation::Collections::MapChangedEventHandler< 244 internal::LogicalK<K, V>, 245 internal::LogicalV<K, V>>> { 246 public: MapView(Microsoft::WRL::ComPtr<Map<LogicalK,LogicalV>> map)247 explicit MapView(Microsoft::WRL::ComPtr<Map<LogicalK, LogicalV>> map) 248 : map_(std::move(map)) { 249 map_->add_MapChanged(this, &map_changed_token_); 250 } 251 ~MapView()252 ~MapView() override { 253 if (map_) 254 map_->remove_MapChanged(map_changed_token_); 255 } 256 257 // ABI::Windows::Foundation::Collections::IMapView: Lookup(AbiK key,AbiV * value)258 IFACEMETHODIMP Lookup(AbiK key, AbiV* value) override { 259 return map_ ? map_->Lookup(key, value) : E_CHANGED_STATE; 260 } 261 get_Size(unsigned int * size)262 IFACEMETHODIMP get_Size(unsigned int* size) override { 263 return map_ ? map_->get_Size(size) : E_CHANGED_STATE; 264 } 265 HasKey(AbiK key,boolean * found)266 IFACEMETHODIMP HasKey(AbiK key, boolean* found) override { 267 return map_ ? map_->HasKey(key, found) : E_CHANGED_STATE; 268 } 269 Split(ABI::Windows::Foundation::Collections::IMapView<LogicalK,LogicalV> ** first_partition,ABI::Windows::Foundation::Collections::IMapView<LogicalK,LogicalV> ** second_partition)270 IFACEMETHODIMP Split( 271 ABI::Windows::Foundation::Collections::IMapView<LogicalK, LogicalV>** 272 first_partition, 273 ABI::Windows::Foundation::Collections::IMapView<LogicalK, LogicalV>** 274 second_partition) override { 275 NOTIMPLEMENTED(); 276 return E_NOTIMPL; 277 } 278 279 // ABI::Windows::Foundation::Collections::IIterable: First(ABI::Windows::Foundation::Collections::IIterator<ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK,LogicalV> * > ** first)280 IFACEMETHODIMP First( 281 ABI::Windows::Foundation::Collections::IIterator< 282 ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK, 283 LogicalV>*>** 284 first) override { 285 return map_ ? map_->First(first) : E_CHANGED_STATE; 286 } 287 288 // ABI::Windows::Foundation::Collections::MapChangedEventHandler: Invoke(ABI::Windows::Foundation::Collections::IObservableMap<LogicalK,LogicalV> * sender,ABI::Windows::Foundation::Collections::IMapChangedEventArgs<LogicalK> * e)289 IFACEMETHODIMP Invoke( 290 ABI::Windows::Foundation::Collections::IObservableMap<LogicalK, 291 LogicalV>* sender, 292 ABI::Windows::Foundation::Collections::IMapChangedEventArgs<LogicalK>* 293 e) override { 294 DCHECK_EQ(map_.Get(), sender); 295 map_.Reset(); 296 sender->remove_MapChanged(map_changed_token_); 297 return S_OK; 298 } 299 300 // Accessor used in MapIterator for iterating over Map's container. 301 // Will remain valid during the entire iteration. get_map()302 const std::map<StorageK, StorageV, internal::Less>& get_map() { 303 DCHECK(map_); 304 return map_->map_; 305 } 306 ValidState()307 bool ValidState() const { return map_; } 308 309 private: 310 Microsoft::WRL::ComPtr<Map<LogicalK, LogicalV>> map_; 311 EventRegistrationToken map_changed_token_; 312 }; 313 314 public: 315 Map() = default; Map(const std::map<StorageK,StorageV,internal::Less> & map)316 explicit Map(const std::map<StorageK, StorageV, internal::Less>& map) 317 : map_(map) {} Map(std::map<StorageK,StorageV,internal::Less> && map)318 explicit Map(std::map<StorageK, StorageV, internal::Less>&& map) 319 : map_(std::move(map)) {} 320 321 // ABI::Windows::Foundation::Collections::IMap: Lookup(AbiK key,AbiV * value)322 IFACEMETHODIMP Lookup(AbiK key, AbiV* value) override { 323 auto it = map_.find(key); 324 if (it == map_.cend()) 325 return E_BOUNDS; 326 327 return internal::CopyTo(it->second, value); 328 } 329 get_Size(unsigned int * size)330 IFACEMETHODIMP get_Size(unsigned int* size) override { 331 *size = map_.size(); 332 return S_OK; 333 } 334 HasKey(AbiK key,boolean * found)335 IFACEMETHODIMP HasKey(AbiK key, boolean* found) override { 336 *found = Contains(map_, key); 337 return S_OK; 338 } 339 GetView(ABI::Windows::Foundation::Collections::IMapView<LogicalK,LogicalV> ** view)340 IFACEMETHODIMP GetView( 341 ABI::Windows::Foundation::Collections::IMapView<LogicalK, LogicalV>** 342 view) override { 343 return Microsoft::WRL::Make<MapView>(this).CopyTo(view); 344 } 345 Insert(AbiK key,AbiV value,boolean * replaced)346 IFACEMETHODIMP Insert(AbiK key, AbiV value, boolean* replaced) override { 347 auto [it, inserted] = map_.insert_or_assign(key, std::move(value)); 348 *replaced = !inserted; 349 NotifyMapChanged(*replaced ? ABI::Windows::Foundation::Collections:: 350 CollectionChange_ItemChanged 351 : ABI::Windows::Foundation::Collections:: 352 CollectionChange_ItemInserted, 353 key); 354 return S_OK; 355 } 356 Remove(AbiK key)357 IFACEMETHODIMP Remove(AbiK key) override { 358 if (!map_.erase(key)) 359 return E_BOUNDS; 360 361 NotifyMapChanged( 362 ABI::Windows::Foundation::Collections::CollectionChange_ItemRemoved, 363 key); 364 return S_OK; 365 } 366 Clear()367 IFACEMETHODIMP Clear() override { 368 map_.clear(); 369 NotifyMapChanged( 370 ABI::Windows::Foundation::Collections::CollectionChange_Reset, 0); 371 return S_OK; 372 } 373 374 // ABI::Windows::Foundation::Collections::IObservableMap: add_MapChanged(ABI::Windows::Foundation::Collections::MapChangedEventHandler<LogicalK,LogicalV> * handler,EventRegistrationToken * token)375 IFACEMETHODIMP add_MapChanged( 376 ABI::Windows::Foundation::Collections::MapChangedEventHandler<LogicalK, 377 LogicalV>* 378 handler, 379 EventRegistrationToken* token) override { 380 token->value = handler_id_++; 381 handlers_.emplace_hint(handlers_.end(), token->value, handler); 382 return S_OK; 383 } 384 remove_MapChanged(EventRegistrationToken token)385 IFACEMETHODIMP remove_MapChanged(EventRegistrationToken token) override { 386 return handlers_.erase(token.value) ? S_OK : E_BOUNDS; 387 } 388 389 // ABI::Windows::Foundation::Collections::IIterable: First(ABI::Windows::Foundation::Collections::IIterator<ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK,LogicalV> * > ** first)390 IFACEMETHODIMP First( 391 ABI::Windows::Foundation::Collections::IIterator< 392 ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK, 393 LogicalV>*>** 394 first) override { 395 return Microsoft::WRL::Make<MapIterator>( 396 Microsoft::WRL::Make<MapView>(this)) 397 .CopyTo(first); 398 } 399 400 private: ~Map()401 ~Map() override { 402 // Handlers should not outlive the Map. Furthermore, they must ensure 403 // they are unregistered before the handler is destroyed. This implies 404 // there should be no handlers left when the Map is destructed. 405 DCHECK(handlers_.empty()); 406 } 407 NotifyMapChanged(ABI::Windows::Foundation::Collections::CollectionChange change,AbiK key)408 void NotifyMapChanged( 409 ABI::Windows::Foundation::Collections::CollectionChange change, 410 AbiK key) { 411 auto args = 412 Microsoft::WRL::Make<internal::MapChangedEventArgs<AbiK>>(change, key); 413 414 // Invoking the handlers could result in mutations to the map, thus we make 415 // a copy beforehand. 416 auto handlers = handlers_; 417 for (auto& handler : handlers) 418 handler.second->Invoke(this, args.Get()); 419 } 420 421 std::map<StorageK, StorageV, internal::Less> map_; 422 base::flat_map<int64_t, 423 ABI::Windows::Foundation::Collections:: 424 MapChangedEventHandler<LogicalK, LogicalV>*> 425 handlers_; 426 int64_t handler_id_ = 0; 427 }; 428 429 } // namespace win 430 } // namespace base 431 432 #endif // BASE_WIN_MAP_H_ 433