Import flat_map and flat_set from chromium/base/
These implementations have been copied from Chromium and adapted to build and run in WebRTC's environment. Bug: webrtc:12689 Change-Id: Id8ff5d86b00827102a6be9d613fad7864130d013 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/224661 Commit-Queue: Victor Boivie <boivie@webrtc.org> Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Cr-Commit-Position: refs/heads/master@{#34425}
This commit is contained in:
parent
93f9f35a8d
commit
fd954fcec7
@ -1399,6 +1399,7 @@ if (rtc_include_tests) {
|
|||||||
"../test:fileutils",
|
"../test:fileutils",
|
||||||
"../test:test_main",
|
"../test:test_main",
|
||||||
"../test:test_support",
|
"../test:test_support",
|
||||||
|
"containers:unittests",
|
||||||
"memory:unittests",
|
"memory:unittests",
|
||||||
"synchronization:mutex",
|
"synchronization:mutex",
|
||||||
"task_utils:to_queued_task",
|
"task_utils:to_queued_task",
|
||||||
|
|||||||
59
rtc_base/containers/BUILD.gn
Normal file
59
rtc_base/containers/BUILD.gn
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Use of this source code is governed by a BSD-style license
|
||||||
|
# that can be found in the LICENSE file in the root of the source
|
||||||
|
# tree. An additional intellectual property rights grant can be found
|
||||||
|
# in the file PATENTS. All contributing project authors may
|
||||||
|
# be found in the AUTHORS file in the root of the source tree.
|
||||||
|
|
||||||
|
import("../../webrtc.gni")
|
||||||
|
|
||||||
|
rtc_library("flat_containers_internal") {
|
||||||
|
sources = [
|
||||||
|
"as_const.h",
|
||||||
|
"flat_tree.cc",
|
||||||
|
"flat_tree.h",
|
||||||
|
"identity.h",
|
||||||
|
"invoke.h",
|
||||||
|
"move_only_int.h",
|
||||||
|
"not_fn.h",
|
||||||
|
"void_t.h",
|
||||||
|
]
|
||||||
|
deps = [
|
||||||
|
"..:checks",
|
||||||
|
"../system:no_unique_address",
|
||||||
|
]
|
||||||
|
absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container" ]
|
||||||
|
visibility = [ ":*" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_source_set("flat_set") {
|
||||||
|
sources = [ "flat_set.h" ]
|
||||||
|
deps = [ ":flat_containers_internal" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_source_set("flat_map") {
|
||||||
|
sources = [ "flat_map.h" ]
|
||||||
|
deps = [
|
||||||
|
":flat_containers_internal",
|
||||||
|
"..:checks",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_library("unittests") {
|
||||||
|
testonly = true
|
||||||
|
sources = [
|
||||||
|
"flat_map_unittest.cc",
|
||||||
|
"flat_set_unittest.cc",
|
||||||
|
"flat_tree_unittest.cc",
|
||||||
|
]
|
||||||
|
deps = [
|
||||||
|
":flat_containers_internal",
|
||||||
|
":flat_map",
|
||||||
|
":flat_set",
|
||||||
|
"../../test:test_support",
|
||||||
|
"//testing/gmock:gmock",
|
||||||
|
"//testing/gtest:gtest",
|
||||||
|
]
|
||||||
|
absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container" ]
|
||||||
|
}
|
||||||
32
rtc_base/containers/as_const.h
Normal file
32
rtc_base/containers/as_const.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This implementation is borrowed from Chromium.
|
||||||
|
|
||||||
|
#ifndef RTC_BASE_CONTAINERS_AS_CONST_H_
|
||||||
|
#define RTC_BASE_CONTAINERS_AS_CONST_H_
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// C++14 implementation of C++17's std::as_const():
|
||||||
|
// https://en.cppreference.com/w/cpp/utility/as_const
|
||||||
|
template <typename T>
|
||||||
|
constexpr std::add_const_t<T>& as_const(T& t) noexcept {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void as_const(const T&& t) = delete;
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // RTC_BASE_CONTAINERS_AS_CONST_H_
|
||||||
364
rtc_base/containers/flat_map.h
Normal file
364
rtc_base/containers/flat_map.h
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This implementation is borrowed from Chromium.
|
||||||
|
|
||||||
|
#ifndef RTC_BASE_CONTAINERS_FLAT_MAP_H_
|
||||||
|
#define RTC_BASE_CONTAINERS_FLAT_MAP_H_
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <tuple>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "rtc_base/checks.h"
|
||||||
|
#include "rtc_base/containers/flat_tree.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
namespace flat_containers_internal {
|
||||||
|
|
||||||
|
// An implementation of the flat_tree GetKeyFromValue template parameter that
|
||||||
|
// extracts the key as the first element of a pair.
|
||||||
|
struct GetFirst {
|
||||||
|
template <class Key, class Mapped>
|
||||||
|
constexpr const Key& operator()(const std::pair<Key, Mapped>& p) const {
|
||||||
|
return p.first;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace flat_containers_internal
|
||||||
|
|
||||||
|
// flat_map is a container with a std::map-like interface that stores its
|
||||||
|
// contents in a sorted container, by default a vector.
|
||||||
|
//
|
||||||
|
// Its implementation mostly tracks the corresponding standardization proposal
|
||||||
|
// https://wg21.link/P0429, except that the storage of keys and values is not
|
||||||
|
// split.
|
||||||
|
//
|
||||||
|
// PROS
|
||||||
|
//
|
||||||
|
// - Good memory locality.
|
||||||
|
// - Low overhead, especially for smaller maps.
|
||||||
|
// - Performance is good for more workloads than you might expect (see
|
||||||
|
// //base/containers/README.md in Chromium repository)
|
||||||
|
// - Supports C++14 map interface.
|
||||||
|
//
|
||||||
|
// CONS
|
||||||
|
//
|
||||||
|
// - Inserts and removals are O(n).
|
||||||
|
//
|
||||||
|
// IMPORTANT NOTES
|
||||||
|
//
|
||||||
|
// - Iterators are invalidated across mutations. This means that the following
|
||||||
|
// line of code has undefined behavior since adding a new element could
|
||||||
|
// resize the container, invalidating all iterators:
|
||||||
|
// container["new element"] = it.second;
|
||||||
|
// - If possible, construct a flat_map in one operation by inserting into
|
||||||
|
// a container and moving that container into the flat_map constructor.
|
||||||
|
//
|
||||||
|
// QUICK REFERENCE
|
||||||
|
//
|
||||||
|
// Most of the core functionality is inherited from flat_tree. Please see
|
||||||
|
// flat_tree.h for more details for most of these functions. As a quick
|
||||||
|
// reference, the functions available are:
|
||||||
|
//
|
||||||
|
// Constructors (inputs need not be sorted):
|
||||||
|
// flat_map(const flat_map&);
|
||||||
|
// flat_map(flat_map&&);
|
||||||
|
// flat_map(InputIterator first, InputIterator last,
|
||||||
|
// const Compare& compare = Compare());
|
||||||
|
// flat_map(const container_type& items,
|
||||||
|
// const Compare& compare = Compare());
|
||||||
|
// flat_map(container_type&& items,
|
||||||
|
// const Compare& compare = Compare()); // Re-use storage.
|
||||||
|
// flat_map(std::initializer_list<value_type> ilist,
|
||||||
|
// const Compare& comp = Compare());
|
||||||
|
//
|
||||||
|
// Constructors (inputs need to be sorted):
|
||||||
|
// flat_map(sorted_unique_t,
|
||||||
|
// InputIterator first, InputIterator last,
|
||||||
|
// const Compare& compare = Compare());
|
||||||
|
// flat_map(sorted_unique_t,
|
||||||
|
// const container_type& items,
|
||||||
|
// const Compare& compare = Compare());
|
||||||
|
// flat_map(sorted_unique_t,
|
||||||
|
// container_type&& items,
|
||||||
|
// const Compare& compare = Compare()); // Re-use storage.
|
||||||
|
// flat_map(sorted_unique_t,
|
||||||
|
// std::initializer_list<value_type> ilist,
|
||||||
|
// const Compare& comp = Compare());
|
||||||
|
//
|
||||||
|
// Assignment functions:
|
||||||
|
// flat_map& operator=(const flat_map&);
|
||||||
|
// flat_map& operator=(flat_map&&);
|
||||||
|
// flat_map& operator=(initializer_list<value_type>);
|
||||||
|
//
|
||||||
|
// Memory management functions:
|
||||||
|
// void reserve(size_t);
|
||||||
|
// size_t capacity() const;
|
||||||
|
// void shrink_to_fit();
|
||||||
|
//
|
||||||
|
// Size management functions:
|
||||||
|
// void clear();
|
||||||
|
// size_t size() const;
|
||||||
|
// size_t max_size() const;
|
||||||
|
// bool empty() const;
|
||||||
|
//
|
||||||
|
// Iterator functions:
|
||||||
|
// iterator begin();
|
||||||
|
// const_iterator begin() const;
|
||||||
|
// const_iterator cbegin() const;
|
||||||
|
// iterator end();
|
||||||
|
// const_iterator end() const;
|
||||||
|
// const_iterator cend() const;
|
||||||
|
// reverse_iterator rbegin();
|
||||||
|
// const reverse_iterator rbegin() const;
|
||||||
|
// const_reverse_iterator crbegin() const;
|
||||||
|
// reverse_iterator rend();
|
||||||
|
// const_reverse_iterator rend() const;
|
||||||
|
// const_reverse_iterator crend() const;
|
||||||
|
//
|
||||||
|
// Insert and accessor functions:
|
||||||
|
// mapped_type& operator[](const key_type&);
|
||||||
|
// mapped_type& operator[](key_type&&);
|
||||||
|
// mapped_type& at(const K&);
|
||||||
|
// const mapped_type& at(const K&) const;
|
||||||
|
// pair<iterator, bool> insert(const value_type&);
|
||||||
|
// pair<iterator, bool> insert(value_type&&);
|
||||||
|
// iterator insert(const_iterator hint, const value_type&);
|
||||||
|
// iterator insert(const_iterator hint, value_type&&);
|
||||||
|
// void insert(InputIterator first, InputIterator last);
|
||||||
|
// pair<iterator, bool> insert_or_assign(K&&, M&&);
|
||||||
|
// iterator insert_or_assign(const_iterator hint, K&&, M&&);
|
||||||
|
// pair<iterator, bool> emplace(Args&&...);
|
||||||
|
// iterator emplace_hint(const_iterator, Args&&...);
|
||||||
|
// pair<iterator, bool> try_emplace(K&&, Args&&...);
|
||||||
|
// iterator try_emplace(const_iterator hint, K&&, Args&&...);
|
||||||
|
|
||||||
|
// Underlying type functions:
|
||||||
|
// container_type extract() &&;
|
||||||
|
// void replace(container_type&&);
|
||||||
|
//
|
||||||
|
// Erase functions:
|
||||||
|
// iterator erase(iterator);
|
||||||
|
// iterator erase(const_iterator);
|
||||||
|
// iterator erase(const_iterator first, const_iterator& last);
|
||||||
|
// template <class K> size_t erase(const K& key);
|
||||||
|
//
|
||||||
|
// Comparators (see std::map documentation).
|
||||||
|
// key_compare key_comp() const;
|
||||||
|
// value_compare value_comp() const;
|
||||||
|
//
|
||||||
|
// Search functions:
|
||||||
|
// template <typename K> size_t count(const K&) const;
|
||||||
|
// template <typename K> iterator find(const K&);
|
||||||
|
// template <typename K> const_iterator find(const K&) const;
|
||||||
|
// template <typename K> bool contains(const K&) const;
|
||||||
|
// template <typename K> pair<iterator, iterator> equal_range(const K&);
|
||||||
|
// template <typename K> iterator lower_bound(const K&);
|
||||||
|
// template <typename K> const_iterator lower_bound(const K&) const;
|
||||||
|
// template <typename K> iterator upper_bound(const K&);
|
||||||
|
// template <typename K> const_iterator upper_bound(const K&) const;
|
||||||
|
//
|
||||||
|
// General functions:
|
||||||
|
// void swap(flat_map&);
|
||||||
|
//
|
||||||
|
// Non-member operators:
|
||||||
|
// bool operator==(const flat_map&, const flat_map);
|
||||||
|
// bool operator!=(const flat_map&, const flat_map);
|
||||||
|
// bool operator<(const flat_map&, const flat_map);
|
||||||
|
// bool operator>(const flat_map&, const flat_map);
|
||||||
|
// bool operator>=(const flat_map&, const flat_map);
|
||||||
|
// bool operator<=(const flat_map&, const flat_map);
|
||||||
|
//
|
||||||
|
template <class Key,
|
||||||
|
class Mapped,
|
||||||
|
class Compare = std::less<>,
|
||||||
|
class Container = std::vector<std::pair<Key, Mapped>>>
|
||||||
|
class flat_map : public ::webrtc::flat_containers_internal::flat_tree<
|
||||||
|
Key,
|
||||||
|
flat_containers_internal::GetFirst,
|
||||||
|
Compare,
|
||||||
|
Container> {
|
||||||
|
private:
|
||||||
|
using tree = typename ::webrtc::flat_containers_internal::
|
||||||
|
flat_tree<Key, flat_containers_internal::GetFirst, Compare, Container>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using key_type = typename tree::key_type;
|
||||||
|
using mapped_type = Mapped;
|
||||||
|
using value_type = typename tree::value_type;
|
||||||
|
using reference = typename Container::reference;
|
||||||
|
using const_reference = typename Container::const_reference;
|
||||||
|
using size_type = typename Container::size_type;
|
||||||
|
using difference_type = typename Container::difference_type;
|
||||||
|
using iterator = typename tree::iterator;
|
||||||
|
using const_iterator = typename tree::const_iterator;
|
||||||
|
using reverse_iterator = typename tree::reverse_iterator;
|
||||||
|
using const_reverse_iterator = typename tree::const_reverse_iterator;
|
||||||
|
using container_type = typename tree::container_type;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// Lifetime and assignments.
|
||||||
|
//
|
||||||
|
// Note: we explicitly bring operator= in because otherwise
|
||||||
|
// flat_map<...> x;
|
||||||
|
// x = {...};
|
||||||
|
// Would first create a flat_map and then move assign it. This most likely
|
||||||
|
// would be optimized away but still affects our debug builds.
|
||||||
|
|
||||||
|
using tree::tree;
|
||||||
|
using tree::operator=;
|
||||||
|
|
||||||
|
// Out-of-bound calls to at() will CHECK.
|
||||||
|
template <class K>
|
||||||
|
mapped_type& at(const K& key);
|
||||||
|
template <class K>
|
||||||
|
const mapped_type& at(const K& key) const;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// Map-specific insert operations.
|
||||||
|
//
|
||||||
|
// Normal insert() functions are inherited from flat_tree.
|
||||||
|
//
|
||||||
|
// Assume that every operation invalidates iterators and references.
|
||||||
|
// Insertion of one element can take O(size).
|
||||||
|
|
||||||
|
mapped_type& operator[](const key_type& key);
|
||||||
|
mapped_type& operator[](key_type&& key);
|
||||||
|
|
||||||
|
template <class K, class M>
|
||||||
|
std::pair<iterator, bool> insert_or_assign(K&& key, M&& obj);
|
||||||
|
template <class K, class M>
|
||||||
|
iterator insert_or_assign(const_iterator hint, K&& key, M&& obj);
|
||||||
|
|
||||||
|
template <class K, class... Args>
|
||||||
|
std::enable_if_t<std::is_constructible<key_type, K&&>::value,
|
||||||
|
std::pair<iterator, bool>>
|
||||||
|
try_emplace(K&& key, Args&&... args);
|
||||||
|
|
||||||
|
template <class K, class... Args>
|
||||||
|
std::enable_if_t<std::is_constructible<key_type, K&&>::value, iterator>
|
||||||
|
try_emplace(const_iterator hint, K&& key, Args&&... args);
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// General operations.
|
||||||
|
//
|
||||||
|
// Assume that swap invalidates iterators and references.
|
||||||
|
|
||||||
|
void swap(flat_map& other) noexcept;
|
||||||
|
|
||||||
|
friend void swap(flat_map& lhs, flat_map& rhs) noexcept { lhs.swap(rhs); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Lookups.
|
||||||
|
|
||||||
|
template <class Key, class Mapped, class Compare, class Container>
|
||||||
|
template <class K>
|
||||||
|
auto flat_map<Key, Mapped, Compare, Container>::at(const K& key)
|
||||||
|
-> mapped_type& {
|
||||||
|
iterator found = tree::find(key);
|
||||||
|
RTC_CHECK(found != tree::end());
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Key, class Mapped, class Compare, class Container>
|
||||||
|
template <class K>
|
||||||
|
auto flat_map<Key, Mapped, Compare, Container>::at(const K& key) const
|
||||||
|
-> const mapped_type& {
|
||||||
|
const_iterator found = tree::find(key);
|
||||||
|
RTC_CHECK(found != tree::cend());
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Insert operations.
|
||||||
|
|
||||||
|
template <class Key, class Mapped, class Compare, class Container>
|
||||||
|
auto flat_map<Key, Mapped, Compare, Container>::operator[](const key_type& key)
|
||||||
|
-> mapped_type& {
|
||||||
|
iterator found = tree::lower_bound(key);
|
||||||
|
if (found == tree::end() || tree::key_comp()(key, found->first))
|
||||||
|
found = tree::unsafe_emplace(found, key, mapped_type());
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Key, class Mapped, class Compare, class Container>
|
||||||
|
auto flat_map<Key, Mapped, Compare, Container>::operator[](key_type&& key)
|
||||||
|
-> mapped_type& {
|
||||||
|
iterator found = tree::lower_bound(key);
|
||||||
|
if (found == tree::end() || tree::key_comp()(key, found->first))
|
||||||
|
found = tree::unsafe_emplace(found, std::move(key), mapped_type());
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Key, class Mapped, class Compare, class Container>
|
||||||
|
template <class K, class M>
|
||||||
|
auto flat_map<Key, Mapped, Compare, Container>::insert_or_assign(K&& key,
|
||||||
|
M&& obj)
|
||||||
|
-> std::pair<iterator, bool> {
|
||||||
|
auto result =
|
||||||
|
tree::emplace_key_args(key, std::forward<K>(key), std::forward<M>(obj));
|
||||||
|
if (!result.second)
|
||||||
|
result.first->second = std::forward<M>(obj);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Key, class Mapped, class Compare, class Container>
|
||||||
|
template <class K, class M>
|
||||||
|
auto flat_map<Key, Mapped, Compare, Container>::insert_or_assign(
|
||||||
|
const_iterator hint,
|
||||||
|
K&& key,
|
||||||
|
M&& obj) -> iterator {
|
||||||
|
auto result = tree::emplace_hint_key_args(hint, key, std::forward<K>(key),
|
||||||
|
std::forward<M>(obj));
|
||||||
|
if (!result.second)
|
||||||
|
result.first->second = std::forward<M>(obj);
|
||||||
|
return result.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Key, class Mapped, class Compare, class Container>
|
||||||
|
template <class K, class... Args>
|
||||||
|
auto flat_map<Key, Mapped, Compare, Container>::try_emplace(K&& key,
|
||||||
|
Args&&... args)
|
||||||
|
-> std::enable_if_t<std::is_constructible<key_type, K&&>::value,
|
||||||
|
std::pair<iterator, bool>> {
|
||||||
|
return tree::emplace_key_args(
|
||||||
|
key, std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(std::forward<K>(key)),
|
||||||
|
std::forward_as_tuple(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Key, class Mapped, class Compare, class Container>
|
||||||
|
template <class K, class... Args>
|
||||||
|
auto flat_map<Key, Mapped, Compare, Container>::try_emplace(const_iterator hint,
|
||||||
|
K&& key,
|
||||||
|
Args&&... args)
|
||||||
|
-> std::enable_if_t<std::is_constructible<key_type, K&&>::value, iterator> {
|
||||||
|
return tree::emplace_hint_key_args(
|
||||||
|
hint, key, std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(std::forward<K>(key)),
|
||||||
|
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||||
|
.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// General operations.
|
||||||
|
|
||||||
|
template <class Key, class Mapped, class Compare, class Container>
|
||||||
|
void flat_map<Key, Mapped, Compare, Container>::swap(flat_map& other) noexcept {
|
||||||
|
tree::swap(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // RTC_BASE_CONTAINERS_FLAT_MAP_H_
|
||||||
433
rtc_base/containers/flat_map_unittest.cc
Normal file
433
rtc_base/containers/flat_map_unittest.cc
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This implementation is borrowed from Chromium.
|
||||||
|
|
||||||
|
#include "rtc_base/containers/flat_map.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "rtc_base/containers/move_only_int.h"
|
||||||
|
#include "test/gmock.h"
|
||||||
|
#include "test/gtest.h"
|
||||||
|
|
||||||
|
// A flat_map is basically a interface to flat_tree. So several basic
|
||||||
|
// operations are tested to make sure things are set up properly, but the bulk
|
||||||
|
// of the tests are in flat_tree_unittests.cc.
|
||||||
|
|
||||||
|
using ::testing::ElementsAre;
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct Unsortable {
|
||||||
|
int value;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator==(const Unsortable& lhs, const Unsortable& rhs) {
|
||||||
|
return lhs.value == rhs.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const Unsortable& lhs, const Unsortable& rhs) = delete;
|
||||||
|
bool operator<=(const Unsortable& lhs, const Unsortable& rhs) = delete;
|
||||||
|
bool operator>(const Unsortable& lhs, const Unsortable& rhs) = delete;
|
||||||
|
bool operator>=(const Unsortable& lhs, const Unsortable& rhs) = delete;
|
||||||
|
|
||||||
|
TEST(FlatMap, IncompleteType) {
|
||||||
|
struct A {
|
||||||
|
using Map = flat_map<A, A>;
|
||||||
|
int data;
|
||||||
|
Map set_with_incomplete_type;
|
||||||
|
Map::iterator it;
|
||||||
|
Map::const_iterator cit;
|
||||||
|
|
||||||
|
// We do not declare operator< because clang complains that it's unused.
|
||||||
|
};
|
||||||
|
|
||||||
|
A a;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatMap, RangeConstructor) {
|
||||||
|
flat_map<int, int>::value_type input_vals[] = {
|
||||||
|
{1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 2}, {2, 3}, {3, 1}, {3, 2}, {3, 3}};
|
||||||
|
|
||||||
|
flat_map<int, int> first(std::begin(input_vals), std::end(input_vals));
|
||||||
|
EXPECT_THAT(first, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 1),
|
||||||
|
std::make_pair(3, 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatMap, MoveConstructor) {
|
||||||
|
using pair = std::pair<MoveOnlyInt, MoveOnlyInt>;
|
||||||
|
|
||||||
|
flat_map<MoveOnlyInt, MoveOnlyInt> original;
|
||||||
|
original.insert(pair(MoveOnlyInt(1), MoveOnlyInt(1)));
|
||||||
|
original.insert(pair(MoveOnlyInt(2), MoveOnlyInt(2)));
|
||||||
|
original.insert(pair(MoveOnlyInt(3), MoveOnlyInt(3)));
|
||||||
|
original.insert(pair(MoveOnlyInt(4), MoveOnlyInt(4)));
|
||||||
|
|
||||||
|
flat_map<MoveOnlyInt, MoveOnlyInt> moved(std::move(original));
|
||||||
|
|
||||||
|
EXPECT_EQ(1U, moved.count(MoveOnlyInt(1)));
|
||||||
|
EXPECT_EQ(1U, moved.count(MoveOnlyInt(2)));
|
||||||
|
EXPECT_EQ(1U, moved.count(MoveOnlyInt(3)));
|
||||||
|
EXPECT_EQ(1U, moved.count(MoveOnlyInt(4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatMap, VectorConstructor) {
|
||||||
|
using IntPair = std::pair<int, int>;
|
||||||
|
using IntMap = flat_map<int, int>;
|
||||||
|
std::vector<IntPair> vect{{1, 1}, {1, 2}, {2, 1}};
|
||||||
|
IntMap map(std::move(vect));
|
||||||
|
EXPECT_THAT(map, ElementsAre(IntPair(1, 1), IntPair(2, 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatMap, InitializerListConstructor) {
|
||||||
|
flat_map<int, int> cont(
|
||||||
|
{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {1, 2}, {10, 10}, {8, 8}});
|
||||||
|
EXPECT_THAT(cont, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2),
|
||||||
|
std::make_pair(3, 3), std::make_pair(4, 4),
|
||||||
|
std::make_pair(5, 5), std::make_pair(8, 8),
|
||||||
|
std::make_pair(10, 10)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatMap, SortedRangeConstructor) {
|
||||||
|
using PairType = std::pair<int, Unsortable>;
|
||||||
|
using MapType = flat_map<int, Unsortable>;
|
||||||
|
MapType::value_type input_vals[] = {{1, {1}}, {2, {1}}, {3, {1}}};
|
||||||
|
MapType map(sorted_unique, std::begin(input_vals), std::end(input_vals));
|
||||||
|
EXPECT_THAT(
|
||||||
|
map, ElementsAre(PairType(1, {1}), PairType(2, {1}), PairType(3, {1})));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatMap, SortedCopyFromVectorConstructor) {
|
||||||
|
using PairType = std::pair<int, Unsortable>;
|
||||||
|
using MapType = flat_map<int, Unsortable>;
|
||||||
|
std::vector<PairType> vect{{1, {1}}, {2, {1}}};
|
||||||
|
MapType map(sorted_unique, vect);
|
||||||
|
EXPECT_THAT(map, ElementsAre(PairType(1, {1}), PairType(2, {1})));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatMap, SortedMoveFromVectorConstructor) {
|
||||||
|
using PairType = std::pair<int, Unsortable>;
|
||||||
|
using MapType = flat_map<int, Unsortable>;
|
||||||
|
std::vector<PairType> vect{{1, {1}}, {2, {1}}};
|
||||||
|
MapType map(sorted_unique, std::move(vect));
|
||||||
|
EXPECT_THAT(map, ElementsAre(PairType(1, {1}), PairType(2, {1})));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatMap, SortedInitializerListConstructor) {
|
||||||
|
using PairType = std::pair<int, Unsortable>;
|
||||||
|
flat_map<int, Unsortable> map(
|
||||||
|
sorted_unique,
|
||||||
|
{{1, {1}}, {2, {2}}, {3, {3}}, {4, {4}}, {5, {5}}, {8, {8}}, {10, {10}}});
|
||||||
|
EXPECT_THAT(map,
|
||||||
|
ElementsAre(PairType(1, {1}), PairType(2, {2}), PairType(3, {3}),
|
||||||
|
PairType(4, {4}), PairType(5, {5}), PairType(8, {8}),
|
||||||
|
PairType(10, {10})));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatMap, InitializerListAssignment) {
|
||||||
|
flat_map<int, int> cont;
|
||||||
|
cont = {{1, 1}, {2, 2}};
|
||||||
|
EXPECT_THAT(cont, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatMap, InsertFindSize) {
|
||||||
|
flat_map<int, int> s;
|
||||||
|
s.insert(std::make_pair(1, 1));
|
||||||
|
s.insert(std::make_pair(1, 1));
|
||||||
|
s.insert(std::make_pair(2, 2));
|
||||||
|
|
||||||
|
EXPECT_EQ(2u, s.size());
|
||||||
|
EXPECT_EQ(std::make_pair(1, 1), *s.find(1));
|
||||||
|
EXPECT_EQ(std::make_pair(2, 2), *s.find(2));
|
||||||
|
EXPECT_EQ(s.end(), s.find(7));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatMap, CopySwap) {
|
||||||
|
flat_map<int, int> original;
|
||||||
|
original.insert({1, 1});
|
||||||
|
original.insert({2, 2});
|
||||||
|
EXPECT_THAT(original,
|
||||||
|
ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2)));
|
||||||
|
|
||||||
|
flat_map<int, int> copy(original);
|
||||||
|
EXPECT_THAT(copy, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2)));
|
||||||
|
|
||||||
|
copy.erase(copy.begin());
|
||||||
|
copy.insert({10, 10});
|
||||||
|
EXPECT_THAT(copy, ElementsAre(std::make_pair(2, 2), std::make_pair(10, 10)));
|
||||||
|
|
||||||
|
original.swap(copy);
|
||||||
|
EXPECT_THAT(original,
|
||||||
|
ElementsAre(std::make_pair(2, 2), std::make_pair(10, 10)));
|
||||||
|
EXPECT_THAT(copy, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// operator[](const Key&)
|
||||||
|
TEST(FlatMap, SubscriptConstKey) {
|
||||||
|
flat_map<std::string, int> m;
|
||||||
|
|
||||||
|
// Default construct elements that don't exist yet.
|
||||||
|
int& s = m["a"];
|
||||||
|
EXPECT_EQ(0, s);
|
||||||
|
EXPECT_EQ(1u, m.size());
|
||||||
|
|
||||||
|
// The returned mapped reference should refer into the map.
|
||||||
|
s = 22;
|
||||||
|
EXPECT_EQ(22, m["a"]);
|
||||||
|
|
||||||
|
// Overwrite existing elements.
|
||||||
|
m["a"] = 44;
|
||||||
|
EXPECT_EQ(44, m["a"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// operator[](Key&&)
|
||||||
|
TEST(FlatMap, SubscriptMoveOnlyKey) {
|
||||||
|
flat_map<MoveOnlyInt, int> m;
|
||||||
|
|
||||||
|
// Default construct elements that don't exist yet.
|
||||||
|
int& s = m[MoveOnlyInt(1)];
|
||||||
|
EXPECT_EQ(0, s);
|
||||||
|
EXPECT_EQ(1u, m.size());
|
||||||
|
|
||||||
|
// The returned mapped reference should refer into the map.
|
||||||
|
s = 22;
|
||||||
|
EXPECT_EQ(22, m[MoveOnlyInt(1)]);
|
||||||
|
|
||||||
|
// Overwrite existing elements.
|
||||||
|
m[MoveOnlyInt(1)] = 44;
|
||||||
|
EXPECT_EQ(44, m[MoveOnlyInt(1)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mapped& at(const Key&)
|
||||||
|
// const Mapped& at(const Key&) const
|
||||||
|
TEST(FlatMap, AtFunction) {
|
||||||
|
flat_map<int, std::string> m = {{1, "a"}, {2, "b"}};
|
||||||
|
|
||||||
|
// Basic Usage.
|
||||||
|
EXPECT_EQ("a", m.at(1));
|
||||||
|
EXPECT_EQ("b", m.at(2));
|
||||||
|
|
||||||
|
// Const reference works.
|
||||||
|
const std::string& const_ref = webrtc::as_const(m).at(1);
|
||||||
|
EXPECT_EQ("a", const_ref);
|
||||||
|
|
||||||
|
// Reference works, can operate on the string.
|
||||||
|
m.at(1)[0] = 'x';
|
||||||
|
EXPECT_EQ("x", m.at(1));
|
||||||
|
|
||||||
|
// Out-of-bounds will CHECK.
|
||||||
|
EXPECT_DEATH_IF_SUPPORTED(m.at(-1), "");
|
||||||
|
EXPECT_DEATH_IF_SUPPORTED({ m.at(-1)[0] = 'z'; }, "");
|
||||||
|
|
||||||
|
// Heterogeneous look-up works.
|
||||||
|
flat_map<std::string, int> m2 = {{"a", 1}, {"b", 2}};
|
||||||
|
EXPECT_EQ(1, m2.at(absl::string_view("a")));
|
||||||
|
EXPECT_EQ(2, webrtc::as_const(m2).at(absl::string_view("b")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert_or_assign(K&&, M&&)
|
||||||
|
TEST(FlatMap, InsertOrAssignMoveOnlyKey) {
|
||||||
|
flat_map<MoveOnlyInt, MoveOnlyInt> m;
|
||||||
|
|
||||||
|
// Initial insertion should return an iterator to the element and set the
|
||||||
|
// second pair member to |true|. The inserted key and value should be moved
|
||||||
|
// from.
|
||||||
|
MoveOnlyInt key(1);
|
||||||
|
MoveOnlyInt val(22);
|
||||||
|
auto result = m.insert_or_assign(std::move(key), std::move(val));
|
||||||
|
EXPECT_EQ(1, result.first->first.data());
|
||||||
|
EXPECT_EQ(22, result.first->second.data());
|
||||||
|
EXPECT_TRUE(result.second);
|
||||||
|
EXPECT_EQ(1u, m.size());
|
||||||
|
EXPECT_EQ(0, key.data()); // moved from
|
||||||
|
EXPECT_EQ(0, val.data()); // moved from
|
||||||
|
|
||||||
|
// Second call with same key should result in an assignment, overwriting the
|
||||||
|
// old value. Assignment should be indicated by setting the second pair member
|
||||||
|
// to |false|. Only the inserted value should be moved from, the key should be
|
||||||
|
// left intact.
|
||||||
|
key = MoveOnlyInt(1);
|
||||||
|
val = MoveOnlyInt(44);
|
||||||
|
result = m.insert_or_assign(std::move(key), std::move(val));
|
||||||
|
EXPECT_EQ(1, result.first->first.data());
|
||||||
|
EXPECT_EQ(44, result.first->second.data());
|
||||||
|
EXPECT_FALSE(result.second);
|
||||||
|
EXPECT_EQ(1u, m.size());
|
||||||
|
EXPECT_EQ(1, key.data()); // not moved from
|
||||||
|
EXPECT_EQ(0, val.data()); // moved from
|
||||||
|
|
||||||
|
// Check that random insertion results in sorted range.
|
||||||
|
flat_map<MoveOnlyInt, int> map;
|
||||||
|
for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) {
|
||||||
|
map.insert_or_assign(MoveOnlyInt(i), i);
|
||||||
|
EXPECT_TRUE(absl::c_is_sorted(map));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert_or_assign(const_iterator hint, K&&, M&&)
|
||||||
|
TEST(FlatMap, InsertOrAssignMoveOnlyKeyWithHint) {
|
||||||
|
flat_map<MoveOnlyInt, MoveOnlyInt> m;
|
||||||
|
|
||||||
|
// Initial insertion should return an iterator to the element. The inserted
|
||||||
|
// key and value should be moved from.
|
||||||
|
MoveOnlyInt key(1);
|
||||||
|
MoveOnlyInt val(22);
|
||||||
|
auto result = m.insert_or_assign(m.end(), std::move(key), std::move(val));
|
||||||
|
EXPECT_EQ(1, result->first.data());
|
||||||
|
EXPECT_EQ(22, result->second.data());
|
||||||
|
EXPECT_EQ(1u, m.size());
|
||||||
|
EXPECT_EQ(0, key.data()); // moved from
|
||||||
|
EXPECT_EQ(0, val.data()); // moved from
|
||||||
|
|
||||||
|
// Second call with same key should result in an assignment, overwriting the
|
||||||
|
// old value. Only the inserted value should be moved from, the key should be
|
||||||
|
// left intact.
|
||||||
|
key = MoveOnlyInt(1);
|
||||||
|
val = MoveOnlyInt(44);
|
||||||
|
result = m.insert_or_assign(m.end(), std::move(key), std::move(val));
|
||||||
|
EXPECT_EQ(1, result->first.data());
|
||||||
|
EXPECT_EQ(44, result->second.data());
|
||||||
|
EXPECT_EQ(1u, m.size());
|
||||||
|
EXPECT_EQ(1, key.data()); // not moved from
|
||||||
|
EXPECT_EQ(0, val.data()); // moved from
|
||||||
|
|
||||||
|
// Check that random insertion results in sorted range.
|
||||||
|
flat_map<MoveOnlyInt, int> map;
|
||||||
|
for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) {
|
||||||
|
map.insert_or_assign(map.end(), MoveOnlyInt(i), i);
|
||||||
|
EXPECT_TRUE(absl::c_is_sorted(map));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try_emplace(K&&, Args&&...)
|
||||||
|
TEST(FlatMap, TryEmplaceMoveOnlyKey) {
|
||||||
|
flat_map<MoveOnlyInt, std::pair<MoveOnlyInt, MoveOnlyInt>> m;
|
||||||
|
|
||||||
|
// Trying to emplace into an empty map should succeed. Insertion should return
|
||||||
|
// an iterator to the element and set the second pair member to |true|. The
|
||||||
|
// inserted key and value should be moved from.
|
||||||
|
MoveOnlyInt key(1);
|
||||||
|
MoveOnlyInt val1(22);
|
||||||
|
MoveOnlyInt val2(44);
|
||||||
|
// Test piecewise construction of mapped_type.
|
||||||
|
auto result = m.try_emplace(std::move(key), std::move(val1), std::move(val2));
|
||||||
|
EXPECT_EQ(1, result.first->first.data());
|
||||||
|
EXPECT_EQ(22, result.first->second.first.data());
|
||||||
|
EXPECT_EQ(44, result.first->second.second.data());
|
||||||
|
EXPECT_TRUE(result.second);
|
||||||
|
EXPECT_EQ(1u, m.size());
|
||||||
|
EXPECT_EQ(0, key.data()); // moved from
|
||||||
|
EXPECT_EQ(0, val1.data()); // moved from
|
||||||
|
EXPECT_EQ(0, val2.data()); // moved from
|
||||||
|
|
||||||
|
// Second call with same key should result in a no-op, returning an iterator
|
||||||
|
// to the existing element and returning false as the second pair member.
|
||||||
|
// Key and values that were attempted to be inserted should be left intact.
|
||||||
|
key = MoveOnlyInt(1);
|
||||||
|
auto paired_val = std::make_pair(MoveOnlyInt(33), MoveOnlyInt(55));
|
||||||
|
// Test construction of mapped_type from pair.
|
||||||
|
result = m.try_emplace(std::move(key), std::move(paired_val));
|
||||||
|
EXPECT_EQ(1, result.first->first.data());
|
||||||
|
EXPECT_EQ(22, result.first->second.first.data());
|
||||||
|
EXPECT_EQ(44, result.first->second.second.data());
|
||||||
|
EXPECT_FALSE(result.second);
|
||||||
|
EXPECT_EQ(1u, m.size());
|
||||||
|
EXPECT_EQ(1, key.data()); // not moved from
|
||||||
|
EXPECT_EQ(33, paired_val.first.data()); // not moved from
|
||||||
|
EXPECT_EQ(55, paired_val.second.data()); // not moved from
|
||||||
|
|
||||||
|
// Check that random insertion results in sorted range.
|
||||||
|
flat_map<MoveOnlyInt, int> map;
|
||||||
|
for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) {
|
||||||
|
map.try_emplace(MoveOnlyInt(i), i);
|
||||||
|
EXPECT_TRUE(absl::c_is_sorted(map));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try_emplace(const_iterator hint, K&&, Args&&...)
|
||||||
|
TEST(FlatMap, TryEmplaceMoveOnlyKeyWithHint) {
|
||||||
|
flat_map<MoveOnlyInt, std::pair<MoveOnlyInt, MoveOnlyInt>> m;
|
||||||
|
|
||||||
|
// Trying to emplace into an empty map should succeed. Insertion should return
|
||||||
|
// an iterator to the element. The inserted key and value should be moved
|
||||||
|
// from.
|
||||||
|
MoveOnlyInt key(1);
|
||||||
|
MoveOnlyInt val1(22);
|
||||||
|
MoveOnlyInt val2(44);
|
||||||
|
// Test piecewise construction of mapped_type.
|
||||||
|
auto result =
|
||||||
|
m.try_emplace(m.end(), std::move(key), std::move(val1), std::move(val2));
|
||||||
|
EXPECT_EQ(1, result->first.data());
|
||||||
|
EXPECT_EQ(22, result->second.first.data());
|
||||||
|
EXPECT_EQ(44, result->second.second.data());
|
||||||
|
EXPECT_EQ(1u, m.size());
|
||||||
|
EXPECT_EQ(0, key.data()); // moved from
|
||||||
|
EXPECT_EQ(0, val1.data()); // moved from
|
||||||
|
EXPECT_EQ(0, val2.data()); // moved from
|
||||||
|
|
||||||
|
// Second call with same key should result in a no-op, returning an iterator
|
||||||
|
// to the existing element. Key and values that were attempted to be inserted
|
||||||
|
// should be left intact.
|
||||||
|
key = MoveOnlyInt(1);
|
||||||
|
val1 = MoveOnlyInt(33);
|
||||||
|
val2 = MoveOnlyInt(55);
|
||||||
|
auto paired_val = std::make_pair(MoveOnlyInt(33), MoveOnlyInt(55));
|
||||||
|
// Test construction of mapped_type from pair.
|
||||||
|
result = m.try_emplace(m.end(), std::move(key), std::move(paired_val));
|
||||||
|
EXPECT_EQ(1, result->first.data());
|
||||||
|
EXPECT_EQ(22, result->second.first.data());
|
||||||
|
EXPECT_EQ(44, result->second.second.data());
|
||||||
|
EXPECT_EQ(1u, m.size());
|
||||||
|
EXPECT_EQ(1, key.data()); // not moved from
|
||||||
|
EXPECT_EQ(33, paired_val.first.data()); // not moved from
|
||||||
|
EXPECT_EQ(55, paired_val.second.data()); // not moved from
|
||||||
|
|
||||||
|
// Check that random insertion results in sorted range.
|
||||||
|
flat_map<MoveOnlyInt, int> map;
|
||||||
|
for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) {
|
||||||
|
map.try_emplace(map.end(), MoveOnlyInt(i), i);
|
||||||
|
EXPECT_TRUE(absl::c_is_sorted(map));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatMap, UsingTransparentCompare) {
|
||||||
|
using ExplicitInt = MoveOnlyInt;
|
||||||
|
flat_map<ExplicitInt, int> m;
|
||||||
|
const auto& m1 = m;
|
||||||
|
int x = 0;
|
||||||
|
|
||||||
|
// Check if we can use lookup functions without converting to key_type.
|
||||||
|
// Correctness is checked in flat_tree tests.
|
||||||
|
m.count(x);
|
||||||
|
m1.count(x);
|
||||||
|
m.find(x);
|
||||||
|
m1.find(x);
|
||||||
|
m.equal_range(x);
|
||||||
|
m1.equal_range(x);
|
||||||
|
m.lower_bound(x);
|
||||||
|
m1.lower_bound(x);
|
||||||
|
m.upper_bound(x);
|
||||||
|
m1.upper_bound(x);
|
||||||
|
m.erase(x);
|
||||||
|
|
||||||
|
// Check if we broke overload resolution.
|
||||||
|
m.emplace(ExplicitInt(0), 0);
|
||||||
|
m.emplace(ExplicitInt(1), 0);
|
||||||
|
m.erase(m.begin());
|
||||||
|
m.erase(m.cbegin());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace webrtc
|
||||||
166
rtc_base/containers/flat_set.h
Normal file
166
rtc_base/containers/flat_set.h
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This implementation is borrowed from Chromium.
|
||||||
|
|
||||||
|
#ifndef RTC_BASE_CONTAINERS_FLAT_SET_H_
|
||||||
|
#define RTC_BASE_CONTAINERS_FLAT_SET_H_
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "rtc_base/containers/flat_tree.h"
|
||||||
|
#include "rtc_base/containers/identity.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// flat_set is a container with a std::set-like interface that stores its
|
||||||
|
// contents in a sorted container, by default a vector.
|
||||||
|
//
|
||||||
|
// Its implementation mostly tracks the corresponding standardization proposal
|
||||||
|
// https://wg21.link/P1222.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// PROS
|
||||||
|
//
|
||||||
|
// - Good memory locality.
|
||||||
|
// - Low overhead, especially for smaller sets.
|
||||||
|
// - Performance is good for more workloads than you might expect (see
|
||||||
|
// //base/containers/README.md in Chromium repository)
|
||||||
|
// - Supports C++14 set interface.
|
||||||
|
//
|
||||||
|
// CONS
|
||||||
|
//
|
||||||
|
// - Inserts and removals are O(n).
|
||||||
|
//
|
||||||
|
// IMPORTANT NOTES
|
||||||
|
//
|
||||||
|
// - Iterators are invalidated across mutations.
|
||||||
|
// - If possible, construct a flat_set in one operation by inserting into
|
||||||
|
// a container and moving that container into the flat_set constructor.
|
||||||
|
// - For multiple removals use base::EraseIf() which is O(n) rather than
|
||||||
|
// O(n * removed_items).
|
||||||
|
//
|
||||||
|
// QUICK REFERENCE
|
||||||
|
//
|
||||||
|
// Most of the core functionality is inherited from flat_tree. Please see
|
||||||
|
// flat_tree.h for more details for most of these functions. As a quick
|
||||||
|
// reference, the functions available are:
|
||||||
|
//
|
||||||
|
// Constructors (inputs need not be sorted):
|
||||||
|
// flat_set(const flat_set&);
|
||||||
|
// flat_set(flat_set&&);
|
||||||
|
// flat_set(InputIterator first, InputIterator last,
|
||||||
|
// const Compare& compare = Compare());
|
||||||
|
// flat_set(const container_type& items,
|
||||||
|
// const Compare& compare = Compare());
|
||||||
|
// flat_set(container_type&& items,
|
||||||
|
// const Compare& compare = Compare()); // Re-use storage.
|
||||||
|
// flat_set(std::initializer_list<value_type> ilist,
|
||||||
|
// const Compare& comp = Compare());
|
||||||
|
//
|
||||||
|
// Constructors (inputs need to be sorted):
|
||||||
|
// flat_set(sorted_unique_t,
|
||||||
|
// InputIterator first, InputIterator last,
|
||||||
|
// const Compare& compare = Compare());
|
||||||
|
// flat_set(sorted_unique_t,
|
||||||
|
// const container_type& items,
|
||||||
|
// const Compare& compare = Compare());
|
||||||
|
// flat_set(sorted_unique_t,
|
||||||
|
// container_type&& items,
|
||||||
|
// const Compare& compare = Compare()); // Re-use storage.
|
||||||
|
// flat_set(sorted_unique_t,
|
||||||
|
// std::initializer_list<value_type> ilist,
|
||||||
|
// const Compare& comp = Compare());
|
||||||
|
//
|
||||||
|
// Assignment functions:
|
||||||
|
// flat_set& operator=(const flat_set&);
|
||||||
|
// flat_set& operator=(flat_set&&);
|
||||||
|
// flat_set& operator=(initializer_list<Key>);
|
||||||
|
//
|
||||||
|
// Memory management functions:
|
||||||
|
// void reserve(size_t);
|
||||||
|
// size_t capacity() const;
|
||||||
|
// void shrink_to_fit();
|
||||||
|
//
|
||||||
|
// Size management functions:
|
||||||
|
// void clear();
|
||||||
|
// size_t size() const;
|
||||||
|
// size_t max_size() const;
|
||||||
|
// bool empty() const;
|
||||||
|
//
|
||||||
|
// Iterator functions:
|
||||||
|
// iterator begin();
|
||||||
|
// const_iterator begin() const;
|
||||||
|
// const_iterator cbegin() const;
|
||||||
|
// iterator end();
|
||||||
|
// const_iterator end() const;
|
||||||
|
// const_iterator cend() const;
|
||||||
|
// reverse_iterator rbegin();
|
||||||
|
// const reverse_iterator rbegin() const;
|
||||||
|
// const_reverse_iterator crbegin() const;
|
||||||
|
// reverse_iterator rend();
|
||||||
|
// const_reverse_iterator rend() const;
|
||||||
|
// const_reverse_iterator crend() const;
|
||||||
|
//
|
||||||
|
// Insert and accessor functions:
|
||||||
|
// pair<iterator, bool> insert(const key_type&);
|
||||||
|
// pair<iterator, bool> insert(key_type&&);
|
||||||
|
// void insert(InputIterator first, InputIterator last);
|
||||||
|
// iterator insert(const_iterator hint, const key_type&);
|
||||||
|
// iterator insert(const_iterator hint, key_type&&);
|
||||||
|
// pair<iterator, bool> emplace(Args&&...);
|
||||||
|
// iterator emplace_hint(const_iterator, Args&&...);
|
||||||
|
//
|
||||||
|
// Underlying type functions:
|
||||||
|
// container_type extract() &&;
|
||||||
|
// void replace(container_type&&);
|
||||||
|
//
|
||||||
|
// Erase functions:
|
||||||
|
// iterator erase(iterator);
|
||||||
|
// iterator erase(const_iterator);
|
||||||
|
// iterator erase(const_iterator first, const_iterator& last);
|
||||||
|
// template <typename K> size_t erase(const K& key);
|
||||||
|
//
|
||||||
|
// Comparators (see std::set documentation).
|
||||||
|
// key_compare key_comp() const;
|
||||||
|
// value_compare value_comp() const;
|
||||||
|
//
|
||||||
|
// Search functions:
|
||||||
|
// template <typename K> size_t count(const K&) const;
|
||||||
|
// template <typename K> iterator find(const K&);
|
||||||
|
// template <typename K> const_iterator find(const K&) const;
|
||||||
|
// template <typename K> bool contains(const K&) const;
|
||||||
|
// template <typename K> pair<iterator, iterator> equal_range(K&);
|
||||||
|
// template <typename K> iterator lower_bound(const K&);
|
||||||
|
// template <typename K> const_iterator lower_bound(const K&) const;
|
||||||
|
// template <typename K> iterator upper_bound(const K&);
|
||||||
|
// template <typename K> const_iterator upper_bound(const K&) const;
|
||||||
|
//
|
||||||
|
// General functions:
|
||||||
|
// void swap(flat_set&);
|
||||||
|
//
|
||||||
|
// Non-member operators:
|
||||||
|
// bool operator==(const flat_set&, const flat_set);
|
||||||
|
// bool operator!=(const flat_set&, const flat_set);
|
||||||
|
// bool operator<(const flat_set&, const flat_set);
|
||||||
|
// bool operator>(const flat_set&, const flat_set);
|
||||||
|
// bool operator>=(const flat_set&, const flat_set);
|
||||||
|
// bool operator<=(const flat_set&, const flat_set);
|
||||||
|
//
|
||||||
|
template <class Key,
|
||||||
|
class Compare = std::less<>,
|
||||||
|
class Container = std::vector<Key>>
|
||||||
|
using flat_set = typename ::webrtc::flat_containers_internal::
|
||||||
|
flat_tree<Key, webrtc::identity, Compare, Container>;
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // RTC_BASE_CONTAINERS_FLAT_SET_H_
|
||||||
129
rtc_base/containers/flat_set_unittest.cc
Normal file
129
rtc_base/containers/flat_set_unittest.cc
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This implementation is borrowed from Chromium.
|
||||||
|
|
||||||
|
#include "rtc_base/containers/flat_set.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "rtc_base/containers/move_only_int.h"
|
||||||
|
#include "test/gmock.h"
|
||||||
|
#include "test/gtest.h"
|
||||||
|
|
||||||
|
// A flat_set is basically a interface to flat_tree. So several basic
|
||||||
|
// operations are tested to make sure things are set up properly, but the bulk
|
||||||
|
// of the tests are in flat_tree_unittests.cc.
|
||||||
|
|
||||||
|
using ::testing::ElementsAre;
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(FlatSet, IncompleteType) {
|
||||||
|
struct A {
|
||||||
|
using Set = flat_set<A>;
|
||||||
|
int data;
|
||||||
|
Set set_with_incomplete_type;
|
||||||
|
Set::iterator it;
|
||||||
|
Set::const_iterator cit;
|
||||||
|
|
||||||
|
// We do not declare operator< because clang complains that it's unused.
|
||||||
|
};
|
||||||
|
|
||||||
|
A a;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatSet, RangeConstructor) {
|
||||||
|
flat_set<int>::value_type input_vals[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
|
||||||
|
|
||||||
|
flat_set<int> cont(std::begin(input_vals), std::end(input_vals));
|
||||||
|
EXPECT_THAT(cont, ElementsAre(1, 2, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatSet, MoveConstructor) {
|
||||||
|
int input_range[] = {1, 2, 3, 4};
|
||||||
|
|
||||||
|
flat_set<MoveOnlyInt> original(std::begin(input_range),
|
||||||
|
std::end(input_range));
|
||||||
|
flat_set<MoveOnlyInt> moved(std::move(original));
|
||||||
|
|
||||||
|
EXPECT_EQ(1U, moved.count(MoveOnlyInt(1)));
|
||||||
|
EXPECT_EQ(1U, moved.count(MoveOnlyInt(2)));
|
||||||
|
EXPECT_EQ(1U, moved.count(MoveOnlyInt(3)));
|
||||||
|
EXPECT_EQ(1U, moved.count(MoveOnlyInt(4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatSet, InitializerListConstructor) {
|
||||||
|
flat_set<int> cont({1, 2, 3, 4, 5, 6, 10, 8});
|
||||||
|
EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatSet, InsertFindSize) {
|
||||||
|
flat_set<int> s;
|
||||||
|
s.insert(1);
|
||||||
|
s.insert(1);
|
||||||
|
s.insert(2);
|
||||||
|
|
||||||
|
EXPECT_EQ(2u, s.size());
|
||||||
|
EXPECT_EQ(1, *s.find(1));
|
||||||
|
EXPECT_EQ(2, *s.find(2));
|
||||||
|
EXPECT_EQ(s.end(), s.find(7));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatSet, CopySwap) {
|
||||||
|
flat_set<int> original;
|
||||||
|
original.insert(1);
|
||||||
|
original.insert(2);
|
||||||
|
EXPECT_THAT(original, ElementsAre(1, 2));
|
||||||
|
|
||||||
|
flat_set<int> copy(original);
|
||||||
|
EXPECT_THAT(copy, ElementsAre(1, 2));
|
||||||
|
|
||||||
|
copy.erase(copy.begin());
|
||||||
|
copy.insert(10);
|
||||||
|
EXPECT_THAT(copy, ElementsAre(2, 10));
|
||||||
|
|
||||||
|
original.swap(copy);
|
||||||
|
EXPECT_THAT(original, ElementsAre(2, 10));
|
||||||
|
EXPECT_THAT(copy, ElementsAre(1, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlatSet, UsingTransparentCompare) {
|
||||||
|
using ExplicitInt = webrtc::MoveOnlyInt;
|
||||||
|
flat_set<ExplicitInt> s;
|
||||||
|
const auto& s1 = s;
|
||||||
|
int x = 0;
|
||||||
|
|
||||||
|
// Check if we can use lookup functions without converting to key_type.
|
||||||
|
// Correctness is checked in flat_tree tests.
|
||||||
|
s.count(x);
|
||||||
|
s1.count(x);
|
||||||
|
s.find(x);
|
||||||
|
s1.find(x);
|
||||||
|
s.equal_range(x);
|
||||||
|
s1.equal_range(x);
|
||||||
|
s.lower_bound(x);
|
||||||
|
s1.lower_bound(x);
|
||||||
|
s.upper_bound(x);
|
||||||
|
s1.upper_bound(x);
|
||||||
|
s.erase(x);
|
||||||
|
|
||||||
|
// Check if we broke overload resolution.
|
||||||
|
s.emplace(0);
|
||||||
|
s.emplace(1);
|
||||||
|
s.erase(s.begin());
|
||||||
|
s.erase(s.cbegin());
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
} // namespace webrtc
|
||||||
19
rtc_base/containers/flat_tree.cc
Normal file
19
rtc_base/containers/flat_tree.cc
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This implementation is borrowed from Chromium.
|
||||||
|
|
||||||
|
#include "rtc_base/containers/flat_tree.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
sorted_unique_t sorted_unique;
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
1102
rtc_base/containers/flat_tree.h
Normal file
1102
rtc_base/containers/flat_tree.h
Normal file
File diff suppressed because it is too large
Load Diff
1484
rtc_base/containers/flat_tree_unittest.cc
Normal file
1484
rtc_base/containers/flat_tree_unittest.cc
Normal file
File diff suppressed because it is too large
Load Diff
36
rtc_base/containers/identity.h
Normal file
36
rtc_base/containers/identity.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This implementation is borrowed from Chromium.
|
||||||
|
|
||||||
|
#ifndef RTC_BASE_CONTAINERS_IDENTITY_H_
|
||||||
|
#define RTC_BASE_CONTAINERS_IDENTITY_H_
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// Implementation of C++20's std::identity.
|
||||||
|
//
|
||||||
|
// Reference:
|
||||||
|
// - https://en.cppreference.com/w/cpp/utility/functional/identity
|
||||||
|
// - https://wg21.link/func.identity
|
||||||
|
struct identity {
|
||||||
|
template <typename T>
|
||||||
|
constexpr T&& operator()(T&& t) const noexcept {
|
||||||
|
return std::forward<T>(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
using is_transparent = void;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // RTC_BASE_CONTAINERS_IDENTITY_H_
|
||||||
162
rtc_base/containers/invoke.h
Normal file
162
rtc_base/containers/invoke.h
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This implementation is borrowed from Chromium.
|
||||||
|
|
||||||
|
#ifndef RTC_BASE_CONTAINERS_INVOKE_H_
|
||||||
|
#define RTC_BASE_CONTAINERS_INVOKE_H_
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
namespace invoke_internal {
|
||||||
|
|
||||||
|
// Helper struct and alias to deduce the class type from a member function
|
||||||
|
// pointer or member object pointer.
|
||||||
|
template <typename DecayedF>
|
||||||
|
struct member_pointer_class {};
|
||||||
|
|
||||||
|
template <typename ReturnT, typename ClassT>
|
||||||
|
struct member_pointer_class<ReturnT ClassT::*> {
|
||||||
|
using type = ClassT;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename DecayedF>
|
||||||
|
using member_pointer_class_t = typename member_pointer_class<DecayedF>::type;
|
||||||
|
|
||||||
|
// Utility struct to detect specializations of std::reference_wrapper.
|
||||||
|
template <typename T>
|
||||||
|
struct is_reference_wrapper : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
|
||||||
|
|
||||||
|
// Small helpers used below in invoke_internal::invoke to make the SFINAE more
|
||||||
|
// concise.
|
||||||
|
template <typename F>
|
||||||
|
const bool& IsMemFunPtr =
|
||||||
|
std::is_member_function_pointer<std::decay_t<F>>::value;
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
const bool& IsMemObjPtr = std::is_member_object_pointer<std::decay_t<F>>::value;
|
||||||
|
|
||||||
|
template <typename F,
|
||||||
|
typename T,
|
||||||
|
typename MemPtrClass = member_pointer_class_t<std::decay_t<F>>>
|
||||||
|
const bool& IsMemPtrToBaseOf =
|
||||||
|
std::is_base_of<MemPtrClass, std::decay_t<T>>::value;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const bool& IsRefWrapper = is_reference_wrapper<std::decay_t<T>>::value;
|
||||||
|
|
||||||
|
template <bool B>
|
||||||
|
using EnableIf = std::enable_if_t<B, bool>;
|
||||||
|
|
||||||
|
// Invokes a member function pointer on a reference to an object of a suitable
|
||||||
|
// type. Covers bullet 1 of the INVOKE definition.
|
||||||
|
//
|
||||||
|
// Reference: https://wg21.link/func.require#1.1
|
||||||
|
template <typename F,
|
||||||
|
typename T1,
|
||||||
|
typename... Args,
|
||||||
|
EnableIf<IsMemFunPtr<F> && IsMemPtrToBaseOf<F, T1>> = true>
|
||||||
|
constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) {
|
||||||
|
return (std::forward<T1>(t1).*f)(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invokes a member function pointer on a std::reference_wrapper to an object of
|
||||||
|
// a suitable type. Covers bullet 2 of the INVOKE definition.
|
||||||
|
//
|
||||||
|
// Reference: https://wg21.link/func.require#1.2
|
||||||
|
template <typename F,
|
||||||
|
typename T1,
|
||||||
|
typename... Args,
|
||||||
|
EnableIf<IsMemFunPtr<F> && IsRefWrapper<T1>> = true>
|
||||||
|
constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) {
|
||||||
|
return (t1.get().*f)(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invokes a member function pointer on a pointer-like type to an object of a
|
||||||
|
// suitable type. Covers bullet 3 of the INVOKE definition.
|
||||||
|
//
|
||||||
|
// Reference: https://wg21.link/func.require#1.3
|
||||||
|
template <typename F,
|
||||||
|
typename T1,
|
||||||
|
typename... Args,
|
||||||
|
EnableIf<IsMemFunPtr<F> && !IsMemPtrToBaseOf<F, T1> &&
|
||||||
|
!IsRefWrapper<T1>> = true>
|
||||||
|
constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) {
|
||||||
|
return ((*std::forward<T1>(t1)).*f)(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invokes a member object pointer on a reference to an object of a suitable
|
||||||
|
// type. Covers bullet 4 of the INVOKE definition.
|
||||||
|
//
|
||||||
|
// Reference: https://wg21.link/func.require#1.4
|
||||||
|
template <typename F,
|
||||||
|
typename T1,
|
||||||
|
EnableIf<IsMemObjPtr<F> && IsMemPtrToBaseOf<F, T1>> = true>
|
||||||
|
constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) {
|
||||||
|
return std::forward<T1>(t1).*f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invokes a member object pointer on a std::reference_wrapper to an object of
|
||||||
|
// a suitable type. Covers bullet 5 of the INVOKE definition.
|
||||||
|
//
|
||||||
|
// Reference: https://wg21.link/func.require#1.5
|
||||||
|
template <typename F,
|
||||||
|
typename T1,
|
||||||
|
EnableIf<IsMemObjPtr<F> && IsRefWrapper<T1>> = true>
|
||||||
|
constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) {
|
||||||
|
return t1.get().*f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invokes a member object pointer on a pointer-like type to an object of a
|
||||||
|
// suitable type. Covers bullet 6 of the INVOKE definition.
|
||||||
|
//
|
||||||
|
// Reference: https://wg21.link/func.require#1.6
|
||||||
|
template <typename F,
|
||||||
|
typename T1,
|
||||||
|
EnableIf<IsMemObjPtr<F> && !IsMemPtrToBaseOf<F, T1> &&
|
||||||
|
!IsRefWrapper<T1>> = true>
|
||||||
|
constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) {
|
||||||
|
return (*std::forward<T1>(t1)).*f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invokes a regular function or function object. Covers bullet 7 of the INVOKE
|
||||||
|
// definition.
|
||||||
|
//
|
||||||
|
// Reference: https://wg21.link/func.require#1.7
|
||||||
|
template <typename F, typename... Args>
|
||||||
|
constexpr decltype(auto) InvokeImpl(F&& f, Args&&... args) {
|
||||||
|
return std::forward<F>(f)(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace invoke_internal
|
||||||
|
|
||||||
|
// Implementation of C++17's std::invoke. This is not based on implementation
|
||||||
|
// referenced in original std::invoke proposal, but rather a manual
|
||||||
|
// implementation, so that it can be constexpr.
|
||||||
|
//
|
||||||
|
// References:
|
||||||
|
// - https://wg21.link/n4169#implementability
|
||||||
|
// - https://en.cppreference.com/w/cpp/utility/functional/invoke
|
||||||
|
// - https://wg21.link/func.invoke
|
||||||
|
template <typename F, typename... Args>
|
||||||
|
constexpr decltype(auto) invoke(F&& f, Args&&... args) {
|
||||||
|
return invoke_internal::InvokeImpl(std::forward<F>(f),
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // RTC_BASE_CONTAINERS_INVOKE_H_
|
||||||
74
rtc_base/containers/move_only_int.h
Normal file
74
rtc_base/containers/move_only_int.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This implementation is borrowed from Chromium.
|
||||||
|
|
||||||
|
#ifndef RTC_BASE_CONTAINERS_MOVE_ONLY_INT_H_
|
||||||
|
#define RTC_BASE_CONTAINERS_MOVE_ONLY_INT_H_
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// A move-only class that holds an integer. This is designed for testing
|
||||||
|
// containers. See also CopyOnlyInt.
|
||||||
|
class MoveOnlyInt {
|
||||||
|
public:
|
||||||
|
explicit MoveOnlyInt(int data = 1) : data_(data) {}
|
||||||
|
MoveOnlyInt(const MoveOnlyInt& other) = delete;
|
||||||
|
MoveOnlyInt& operator=(const MoveOnlyInt& other) = delete;
|
||||||
|
MoveOnlyInt(MoveOnlyInt&& other) : data_(other.data_) { other.data_ = 0; }
|
||||||
|
~MoveOnlyInt() { data_ = 0; }
|
||||||
|
|
||||||
|
MoveOnlyInt& operator=(MoveOnlyInt&& other) {
|
||||||
|
data_ = other.data_;
|
||||||
|
other.data_ = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {
|
||||||
|
return lhs.data_ == rhs.data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {
|
||||||
|
return !operator==(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator<(const MoveOnlyInt& lhs, int rhs) {
|
||||||
|
return lhs.data_ < rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator<(int lhs, const MoveOnlyInt& rhs) {
|
||||||
|
return lhs < rhs.data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator<(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {
|
||||||
|
return lhs.data_ < rhs.data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator>(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {
|
||||||
|
return rhs < lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator<=(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {
|
||||||
|
return !(rhs < lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator>=(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {
|
||||||
|
return !(lhs < rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int data() const { return data_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
volatile int data_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // RTC_BASE_CONTAINERS_MOVE_ONLY_INT_H_
|
||||||
64
rtc_base/containers/not_fn.h
Normal file
64
rtc_base/containers/not_fn.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This implementation is borrowed from Chromium.
|
||||||
|
|
||||||
|
#ifndef RTC_BASE_CONTAINERS_NOT_FN_H_
|
||||||
|
#define RTC_BASE_CONTAINERS_NOT_FN_H_
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "rtc_base/containers/invoke.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
namespace not_fn_internal {
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
struct NotFnImpl {
|
||||||
|
F f;
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
constexpr decltype(auto) operator()(Args&&... args) & noexcept {
|
||||||
|
return !webrtc::invoke(f, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
constexpr decltype(auto) operator()(Args&&... args) const& noexcept {
|
||||||
|
return !webrtc::invoke(f, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
constexpr decltype(auto) operator()(Args&&... args) && noexcept {
|
||||||
|
return !webrtc::invoke(std::move(f), std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
constexpr decltype(auto) operator()(Args&&... args) const&& noexcept {
|
||||||
|
return !webrtc::invoke(std::move(f), std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace not_fn_internal
|
||||||
|
|
||||||
|
// Implementation of C++17's std::not_fn.
|
||||||
|
//
|
||||||
|
// Reference:
|
||||||
|
// - https://en.cppreference.com/w/cpp/utility/functional/not_fn
|
||||||
|
// - https://wg21.link/func.not.fn
|
||||||
|
template <typename F>
|
||||||
|
constexpr not_fn_internal::NotFnImpl<std::decay_t<F>> not_fn(F&& f) {
|
||||||
|
return {std::forward<F>(f)};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // RTC_BASE_CONTAINERS_NOT_FN_H_
|
||||||
36
rtc_base/containers/void_t.h
Normal file
36
rtc_base/containers/void_t.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This implementation is borrowed from Chromium.
|
||||||
|
|
||||||
|
#ifndef RTC_BASE_CONTAINERS_VOID_T_H_
|
||||||
|
#define RTC_BASE_CONTAINERS_VOID_T_H_
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace void_t_internal {
|
||||||
|
// Implementation detail of webrtc::void_t below.
|
||||||
|
template <typename...>
|
||||||
|
struct make_void {
|
||||||
|
using type = void;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace void_t_internal
|
||||||
|
|
||||||
|
// webrtc::void_t is an implementation of std::void_t from C++17.
|
||||||
|
//
|
||||||
|
// We use |webrtc::void_t_internal::make_void| as a helper struct to avoid a
|
||||||
|
// C++14 defect:
|
||||||
|
// http://en.cppreference.com/w/cpp/types/void_t
|
||||||
|
// http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1558
|
||||||
|
template <typename... Ts>
|
||||||
|
using void_t = typename ::webrtc::void_t_internal::make_void<Ts...>::type;
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // RTC_BASE_CONTAINERS_VOID_T_H_
|
||||||
Loading…
x
Reference in New Issue
Block a user