433 lines
15 KiB
C++
433 lines
15 KiB
C++
// 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_
|