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}
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
index 7ea069a..8dc89fa 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
@@ -1399,6 +1399,7 @@
         "../test:fileutils",
         "../test:test_main",
         "../test:test_support",
+        "containers:unittests",
         "memory:unittests",
         "synchronization:mutex",
         "task_utils:to_queued_task",
diff --git a/rtc_base/containers/BUILD.gn b/rtc_base/containers/BUILD.gn
new file mode 100644
index 0000000..f303e70
--- /dev/null
+++ b/rtc_base/containers/BUILD.gn
@@ -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" ]
+}
diff --git a/rtc_base/containers/as_const.h b/rtc_base/containers/as_const.h
new file mode 100644
index 0000000..a41b3bc
--- /dev/null
+++ b/rtc_base/containers/as_const.h
@@ -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_
diff --git a/rtc_base/containers/flat_map.h b/rtc_base/containers/flat_map.h
new file mode 100644
index 0000000..9fa8056
--- /dev/null
+++ b/rtc_base/containers/flat_map.h
@@ -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_
diff --git a/rtc_base/containers/flat_map_unittest.cc b/rtc_base/containers/flat_map_unittest.cc
new file mode 100644
index 0000000..3ece2ff
--- /dev/null
+++ b/rtc_base/containers/flat_map_unittest.cc
@@ -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
diff --git a/rtc_base/containers/flat_set.h b/rtc_base/containers/flat_set.h
new file mode 100644
index 0000000..5c43179
--- /dev/null
+++ b/rtc_base/containers/flat_set.h
@@ -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_
diff --git a/rtc_base/containers/flat_set_unittest.cc b/rtc_base/containers/flat_set_unittest.cc
new file mode 100644
index 0000000..831afed
--- /dev/null
+++ b/rtc_base/containers/flat_set_unittest.cc
@@ -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
diff --git a/rtc_base/containers/flat_tree.cc b/rtc_base/containers/flat_tree.cc
new file mode 100644
index 0000000..9e86db1
--- /dev/null
+++ b/rtc_base/containers/flat_tree.cc
@@ -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
diff --git a/rtc_base/containers/flat_tree.h b/rtc_base/containers/flat_tree.h
new file mode 100644
index 0000000..046eef1
--- /dev/null
+++ b/rtc_base/containers/flat_tree.h
@@ -0,0 +1,1102 @@
+/*
+ *  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_TREE_H_
+#define RTC_BASE_CONTAINERS_FLAT_TREE_H_
+
+#include <algorithm>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "absl/algorithm/container.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/containers/as_const.h"
+#include "rtc_base/containers/not_fn.h"
+#include "rtc_base/containers/void_t.h"
+#include "rtc_base/system/no_unique_address.h"
+
+namespace webrtc {
+// Tag type that allows skipping the sort_and_unique step when constructing a
+// flat_tree in case the underlying container is already sorted and has no
+// duplicate elements.
+struct sorted_unique_t {
+  constexpr sorted_unique_t() = default;
+};
+extern sorted_unique_t sorted_unique;
+
+namespace flat_containers_internal {
+
+// Helper functions used in RTC_DCHECKs below to make sure that inputs tagged
+// with sorted_unique are indeed sorted and unique.
+template <typename Range, typename Comp>
+constexpr bool is_sorted_and_unique(const Range& range, Comp comp) {
+  // Being unique implies that there are no adjacent elements that
+  // compare equal. So this checks that each element is strictly less
+  // than the element after it.
+  return absl::c_adjacent_find(range, webrtc::not_fn(comp)) == std::end(range);
+}
+
+// This is a convenience trait inheriting from std::true_type if Iterator is at
+// least a ForwardIterator and thus supports multiple passes over a range.
+template <class Iterator>
+using is_multipass =
+    std::is_base_of<std::forward_iterator_tag,
+                    typename std::iterator_traits<Iterator>::iterator_category>;
+
+// Uses SFINAE to detect whether type has is_transparent member.
+template <typename T, typename = void>
+struct IsTransparentCompare : std::false_type {};
+template <typename T>
+struct IsTransparentCompare<T, void_t<typename T::is_transparent>>
+    : std::true_type {};
+
+// Helper inspired by C++20's std::to_array to convert a C-style array to a
+// std::array. As opposed to the C++20 version this implementation does not
+// provide an overload for rvalues and does not strip cv qualifers from the
+// returned std::array::value_type. The returned value_type needs to be
+// specified explicitly, allowing the construction of std::arrays with const
+// elements.
+//
+// Reference: https://en.cppreference.com/w/cpp/container/array/to_array
+template <typename U, typename T, size_t N, size_t... I>
+constexpr std::array<U, N> ToArrayImpl(const T (&data)[N],
+                                       std::index_sequence<I...>) {
+  return {{data[I]...}};
+}
+
+template <typename U, typename T, size_t N>
+constexpr std::array<U, N> ToArray(const T (&data)[N]) {
+  return ToArrayImpl<U>(data, std::make_index_sequence<N>());
+}
+
+// std::pair's operator= is not constexpr prior to C++20. Thus we need this
+// small helper to invoke operator= on the .first and .second member explicitly.
+template <typename T>
+constexpr void Assign(T& lhs, T&& rhs) {
+  lhs = std::move(rhs);
+}
+
+template <typename T, typename U>
+constexpr void Assign(std::pair<T, U>& lhs, std::pair<T, U>&& rhs) {
+  Assign(lhs.first, std::move(rhs.first));
+  Assign(lhs.second, std::move(rhs.second));
+}
+
+// constexpr swap implementation. std::swap is not constexpr prior to C++20.
+template <typename T>
+constexpr void Swap(T& lhs, T& rhs) {
+  T tmp = std::move(lhs);
+  Assign(lhs, std::move(rhs));
+  Assign(rhs, std::move(tmp));
+}
+
+// constexpr prev implementation. std::prev is not constexpr prior to C++17.
+template <typename BidirIt>
+constexpr BidirIt Prev(BidirIt it) {
+  return --it;
+}
+
+// constexpr next implementation. std::next is not constexpr prior to C++17.
+template <typename InputIt>
+constexpr InputIt Next(InputIt it) {
+  return ++it;
+}
+
+// constexpr sort implementation. std::sort is not constexpr prior to C++20.
+// While insertion sort has a quadratic worst case complexity, it was chosen
+// because it has linear complexity for nearly sorted data, is stable, and
+// simple to implement.
+template <typename BidirIt, typename Compare>
+constexpr void InsertionSort(BidirIt first, BidirIt last, const Compare& comp) {
+  if (first == last)
+    return;
+
+  for (auto it = Next(first); it != last; ++it) {
+    for (auto curr = it; curr != first && comp(*curr, *Prev(curr)); --curr)
+      Swap(*curr, *Prev(curr));
+  }
+}
+
+// Implementation -------------------------------------------------------------
+
+// Implementation for the sorted associative flat_set and flat_map using a
+// sorted vector as the backing store. Do not use directly.
+//
+// The use of "value" in this is like std::map uses, meaning it's the thing
+// contained (in the case of map it's a <Kay, Mapped> pair). The Key is how
+// things are looked up. In the case of a set, Key == Value. In the case of
+// a map, the Key is a component of a Value.
+//
+// The helper class GetKeyFromValue provides the means to extract a key from a
+// value for comparison purposes. It should implement:
+//   const Key& operator()(const Value&).
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+class flat_tree {
+ public:
+  // --------------------------------------------------------------------------
+  // Types.
+  //
+  using key_type = Key;
+  using key_compare = KeyCompare;
+  using value_type = typename Container::value_type;
+
+  // Wraps the templated key comparison to compare values.
+  struct value_compare {
+    constexpr bool operator()(const value_type& left,
+                              const value_type& right) const {
+      GetKeyFromValue extractor;
+      return comp(extractor(left), extractor(right));
+    }
+
+    RTC_NO_UNIQUE_ADDRESS key_compare comp;
+  };
+
+  using pointer = typename Container::pointer;
+  using const_pointer = typename Container::const_pointer;
+  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 Container::iterator;
+  using const_iterator = typename Container::const_iterator;
+  using reverse_iterator = typename Container::reverse_iterator;
+  using const_reverse_iterator = typename Container::const_reverse_iterator;
+  using container_type = Container;
+
+  // --------------------------------------------------------------------------
+  // Lifetime.
+  //
+  // Constructors that take range guarantee O(N * log^2(N)) + O(N) complexity
+  // and take O(N * log(N)) + O(N) if extra memory is available (N is a range
+  // length).
+  //
+  // Assume that move constructors invalidate iterators and references.
+  //
+  // The constructors that take ranges, lists, and vectors do not require that
+  // the input be sorted.
+  //
+  // When passing the webrtc::sorted_unique tag as the first argument no sort
+  // and unique step takes places. This is useful if the underlying container
+  // already has the required properties.
+
+  flat_tree() = default;
+  flat_tree(const flat_tree&) = default;
+  flat_tree(flat_tree&&) = default;
+
+  explicit flat_tree(const key_compare& comp);
+
+  template <class InputIterator>
+  flat_tree(InputIterator first,
+            InputIterator last,
+            const key_compare& comp = key_compare());
+
+  flat_tree(const container_type& items,
+            const key_compare& comp = key_compare());
+
+  explicit flat_tree(container_type&& items,
+                     const key_compare& comp = key_compare());
+
+  flat_tree(std::initializer_list<value_type> ilist,
+            const key_compare& comp = key_compare());
+
+  template <class InputIterator>
+  flat_tree(sorted_unique_t,
+            InputIterator first,
+            InputIterator last,
+            const key_compare& comp = key_compare());
+
+  flat_tree(sorted_unique_t,
+            const container_type& items,
+            const key_compare& comp = key_compare());
+
+  constexpr flat_tree(sorted_unique_t,
+                      container_type&& items,
+                      const key_compare& comp = key_compare());
+
+  flat_tree(sorted_unique_t,
+            std::initializer_list<value_type> ilist,
+            const key_compare& comp = key_compare());
+
+  ~flat_tree() = default;
+
+  // --------------------------------------------------------------------------
+  // Assignments.
+  //
+  // Assume that move assignment invalidates iterators and references.
+
+  flat_tree& operator=(const flat_tree&) = default;
+  flat_tree& operator=(flat_tree&&) = default;
+  // Takes the first if there are duplicates in the initializer list.
+  flat_tree& operator=(std::initializer_list<value_type> ilist);
+
+  // --------------------------------------------------------------------------
+  // Memory management.
+  //
+  // Beware that shrink_to_fit() simply forwards the request to the
+  // container_type and its implementation is free to optimize otherwise and
+  // leave capacity() to be greater that its size.
+  //
+  // reserve() and shrink_to_fit() invalidate iterators and references.
+
+  void reserve(size_type new_capacity);
+  size_type capacity() const;
+  void shrink_to_fit();
+
+  // --------------------------------------------------------------------------
+  // Size management.
+  //
+  // clear() leaves the capacity() of the flat_tree unchanged.
+
+  void clear();
+
+  constexpr size_type size() const;
+  constexpr size_type max_size() const;
+  constexpr bool empty() const;
+
+  // --------------------------------------------------------------------------
+  // Iterators.
+  //
+  // Iterators follow the ordering defined by the key comparator used in
+  // construction of the flat_tree.
+
+  iterator begin();
+  constexpr const_iterator begin() const;
+  const_iterator cbegin() const;
+
+  iterator end();
+  constexpr 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 operations.
+  //
+  // Assume that every operation invalidates iterators and references.
+  // Insertion of one element can take O(size). Capacity of flat_tree grows in
+  // an implementation-defined manner.
+  //
+  // NOTE: Prefer to build a new flat_tree from a std::vector (or similar)
+  // instead of calling insert() repeatedly.
+
+  std::pair<iterator, bool> insert(const value_type& val);
+  std::pair<iterator, bool> insert(value_type&& val);
+
+  iterator insert(const_iterator position_hint, const value_type& x);
+  iterator insert(const_iterator position_hint, value_type&& x);
+
+  // This method inserts the values from the range [first, last) into the
+  // current tree.
+  template <class InputIterator>
+  void insert(InputIterator first, InputIterator last);
+
+  template <class... Args>
+  std::pair<iterator, bool> emplace(Args&&... args);
+
+  template <class... Args>
+  iterator emplace_hint(const_iterator position_hint, Args&&... args);
+
+  // --------------------------------------------------------------------------
+  // Underlying type operations.
+  //
+  // Assume that either operation invalidates iterators and references.
+
+  // Extracts the container_type and returns it to the caller. Ensures that
+  // `this` is `empty()` afterwards.
+  container_type extract() &&;
+
+  // Replaces the container_type with `body`. Expects that `body` is sorted
+  // and has no repeated elements with regard to value_comp().
+  void replace(container_type&& body);
+
+  // --------------------------------------------------------------------------
+  // Erase operations.
+  //
+  // Assume that every operation invalidates iterators and references.
+  //
+  // erase(position), erase(first, last) can take O(size).
+  // erase(key) may take O(size) + O(log(size)).
+  //
+  // Prefer webrtc::EraseIf() or some other variation on erase(remove(), end())
+  // idiom when deleting multiple non-consecutive elements.
+
+  iterator erase(iterator position);
+  // Artificially templatized to break ambiguity if `iterator` and
+  // `const_iterator` are the same type.
+  template <typename DummyT = void>
+  iterator erase(const_iterator position);
+  iterator erase(const_iterator first, const_iterator last);
+  template <typename K>
+  size_type erase(const K& key);
+
+  // --------------------------------------------------------------------------
+  // Comparators.
+
+  constexpr key_compare key_comp() const;
+  constexpr value_compare value_comp() const;
+
+  // --------------------------------------------------------------------------
+  // Search operations.
+  //
+  // Search operations have O(log(size)) complexity.
+
+  template <typename K>
+  size_type count(const K& key) const;
+
+  template <typename K>
+  iterator find(const K& key);
+
+  template <typename K>
+  const_iterator find(const K& key) const;
+
+  template <typename K>
+  bool contains(const K& key) const;
+
+  template <typename K>
+  std::pair<iterator, iterator> equal_range(const K& key);
+
+  template <typename K>
+  std::pair<const_iterator, const_iterator> equal_range(const K& key) const;
+
+  template <typename K>
+  iterator lower_bound(const K& key);
+
+  template <typename K>
+  const_iterator lower_bound(const K& key) const;
+
+  template <typename K>
+  iterator upper_bound(const K& key);
+
+  template <typename K>
+  const_iterator upper_bound(const K& key) const;
+
+  // --------------------------------------------------------------------------
+  // General operations.
+  //
+  // Assume that swap invalidates iterators and references.
+  //
+  // Implementation note: currently we use operator==() and operator<() on
+  // std::vector, because they have the same contract we need, so we use them
+  // directly for brevity and in case it is more optimal than calling equal()
+  // and lexicograhpical_compare(). If the underlying container type is changed,
+  // this code may need to be modified.
+
+  void swap(flat_tree& other) noexcept;
+
+  friend bool operator==(const flat_tree& lhs, const flat_tree& rhs) {
+    return lhs.body_ == rhs.body_;
+  }
+
+  friend bool operator!=(const flat_tree& lhs, const flat_tree& rhs) {
+    return !(lhs == rhs);
+  }
+
+  friend bool operator<(const flat_tree& lhs, const flat_tree& rhs) {
+    return lhs.body_ < rhs.body_;
+  }
+
+  friend bool operator>(const flat_tree& lhs, const flat_tree& rhs) {
+    return rhs < lhs;
+  }
+
+  friend bool operator>=(const flat_tree& lhs, const flat_tree& rhs) {
+    return !(lhs < rhs);
+  }
+
+  friend bool operator<=(const flat_tree& lhs, const flat_tree& rhs) {
+    return !(lhs > rhs);
+  }
+
+  friend void swap(flat_tree& lhs, flat_tree& rhs) noexcept { lhs.swap(rhs); }
+
+ protected:
+  // Emplaces a new item into the tree that is known not to be in it. This
+  // is for implementing map operator[].
+  template <class... Args>
+  iterator unsafe_emplace(const_iterator position, Args&&... args);
+
+  // Attempts to emplace a new element with key |key|. Only if |key| is not yet
+  // present, construct value_type from |args| and insert it. Returns an
+  // iterator to the element with key |key| and a bool indicating whether an
+  // insertion happened.
+  template <class K, class... Args>
+  std::pair<iterator, bool> emplace_key_args(const K& key, Args&&... args);
+
+  // Similar to |emplace_key_args|, but checks |hint| first as a possible
+  // insertion position.
+  template <class K, class... Args>
+  std::pair<iterator, bool> emplace_hint_key_args(const_iterator hint,
+                                                  const K& key,
+                                                  Args&&... args);
+
+ private:
+  // Helper class for e.g. lower_bound that can compare a value on the left
+  // to a key on the right.
+  struct KeyValueCompare {
+    // The key comparison object must outlive this class.
+    explicit KeyValueCompare(const key_compare& comp) : comp_(comp) {}
+
+    template <typename T, typename U>
+    bool operator()(const T& lhs, const U& rhs) const {
+      return comp_(extract_if_value_type(lhs), extract_if_value_type(rhs));
+    }
+
+   private:
+    const key_type& extract_if_value_type(const value_type& v) const {
+      GetKeyFromValue extractor;
+      return extractor(v);
+    }
+
+    template <typename K>
+    const K& extract_if_value_type(const K& k) const {
+      return k;
+    }
+
+    const key_compare& comp_;
+  };
+
+  iterator const_cast_it(const_iterator c_it) {
+    auto distance = std::distance(cbegin(), c_it);
+    return std::next(begin(), distance);
+  }
+
+  // This method is inspired by both std::map::insert(P&&) and
+  // std::map::insert_or_assign(const K&, V&&). It inserts val if an equivalent
+  // element is not present yet, otherwise it overwrites. It returns an iterator
+  // to the modified element and a flag indicating whether insertion or
+  // assignment happened.
+  template <class V>
+  std::pair<iterator, bool> insert_or_assign(V&& val) {
+    auto position = lower_bound(GetKeyFromValue()(val));
+
+    if (position == end() || value_comp()(val, *position))
+      return {body_.emplace(position, std::forward<V>(val)), true};
+
+    *position = std::forward<V>(val);
+    return {position, false};
+  }
+
+  // This method is similar to insert_or_assign, with the following differences:
+  // - Instead of searching [begin(), end()) it only searches [first, last).
+  // - In case no equivalent element is found, val is appended to the end of the
+  //   underlying body and an iterator to the next bigger element in [first,
+  //   last) is returned.
+  template <class V>
+  std::pair<iterator, bool> append_or_assign(iterator first,
+                                             iterator last,
+                                             V&& val) {
+    auto position = std::lower_bound(first, last, val, value_comp());
+
+    if (position == last || value_comp()(val, *position)) {
+      // emplace_back might invalidate position, which is why distance needs to
+      // be cached.
+      const difference_type distance = std::distance(begin(), position);
+      body_.emplace_back(std::forward<V>(val));
+      return {std::next(begin(), distance), true};
+    }
+
+    *position = std::forward<V>(val);
+    return {position, false};
+  }
+
+  // This method is similar to insert, with the following differences:
+  // - Instead of searching [begin(), end()) it only searches [first, last).
+  // - In case no equivalent element is found, val is appended to the end of the
+  //   underlying body and an iterator to the next bigger element in [first,
+  //   last) is returned.
+  template <class V>
+  std::pair<iterator, bool> append_unique(iterator first,
+                                          iterator last,
+                                          V&& val) {
+    auto position = std::lower_bound(first, last, val, value_comp());
+
+    if (position == last || value_comp()(val, *position)) {
+      // emplace_back might invalidate position, which is why distance needs to
+      // be cached.
+      const difference_type distance = std::distance(begin(), position);
+      body_.emplace_back(std::forward<V>(val));
+      return {std::next(begin(), distance), true};
+    }
+
+    return {position, false};
+  }
+
+  void sort_and_unique(iterator first, iterator last) {
+    // Preserve stability for the unique code below.
+    std::stable_sort(first, last, value_comp());
+
+    // lhs is already <= rhs due to sort, therefore !(lhs < rhs) <=> lhs == rhs.
+    auto equal_comp = webrtc::not_fn(value_comp());
+    erase(std::unique(first, last, equal_comp), last);
+  }
+
+  void sort_and_unique() { sort_and_unique(begin(), end()); }
+
+  // To support comparators that may not be possible to default-construct, we
+  // have to store an instance of Compare. Since Compare commonly is stateless,
+  // we use the RTC_NO_UNIQUE_ADDRESS attribute to save space.
+  RTC_NO_UNIQUE_ADDRESS key_compare comp_;
+  // Declare after |key_compare_comp_| to workaround GCC ICE. For details
+  // see https://crbug.com/1156268
+  container_type body_;
+
+  // If the compare is not transparent we want to construct key_type once.
+  template <typename K>
+  using KeyTypeOrK = typename std::
+      conditional<IsTransparentCompare<key_compare>::value, K, key_type>::type;
+};
+
+// ----------------------------------------------------------------------------
+// Lifetime.
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::flat_tree(
+    const KeyCompare& comp)
+    : comp_(comp) {}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <class InputIterator>
+flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::flat_tree(
+    InputIterator first,
+    InputIterator last,
+    const KeyCompare& comp)
+    : comp_(comp), body_(first, last) {
+  sort_and_unique();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::flat_tree(
+    const container_type& items,
+    const KeyCompare& comp)
+    : comp_(comp), body_(items) {
+  sort_and_unique();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::flat_tree(
+    container_type&& items,
+    const KeyCompare& comp)
+    : comp_(comp), body_(std::move(items)) {
+  sort_and_unique();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::flat_tree(
+    std::initializer_list<value_type> ilist,
+    const KeyCompare& comp)
+    : flat_tree(std::begin(ilist), std::end(ilist), comp) {}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <class InputIterator>
+flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::flat_tree(
+    sorted_unique_t,
+    InputIterator first,
+    InputIterator last,
+    const KeyCompare& comp)
+    : comp_(comp), body_(first, last) {
+  RTC_DCHECK(is_sorted_and_unique(*this, value_comp()));
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::flat_tree(
+    sorted_unique_t,
+    const container_type& items,
+    const KeyCompare& comp)
+    : comp_(comp), body_(items) {
+  RTC_DCHECK(is_sorted_and_unique(*this, value_comp()));
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+constexpr flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::flat_tree(
+    sorted_unique_t,
+    container_type&& items,
+    const KeyCompare& comp)
+    : comp_(comp), body_(std::move(items)) {
+  RTC_DCHECK(is_sorted_and_unique(*this, value_comp()));
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::flat_tree(
+    sorted_unique_t,
+    std::initializer_list<value_type> ilist,
+    const KeyCompare& comp)
+    : flat_tree(sorted_unique, std::begin(ilist), std::end(ilist), comp) {}
+
+// ----------------------------------------------------------------------------
+// Assignments.
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::operator=(
+    std::initializer_list<value_type> ilist) -> flat_tree& {
+  body_ = ilist;
+  sort_and_unique();
+  return *this;
+}
+
+// ----------------------------------------------------------------------------
+// Memory management.
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+void flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::reserve(
+    size_type new_capacity) {
+  body_.reserve(new_capacity);
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::capacity() const
+    -> size_type {
+  return body_.capacity();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+void flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::shrink_to_fit() {
+  body_.shrink_to_fit();
+}
+
+// ----------------------------------------------------------------------------
+// Size management.
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+void flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::clear() {
+  body_.clear();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+constexpr auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::size()
+    const -> size_type {
+  return body_.size();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+constexpr auto
+flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::max_size() const
+    -> size_type {
+  return body_.max_size();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+constexpr bool flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::empty()
+    const {
+  return body_.empty();
+}
+
+// ----------------------------------------------------------------------------
+// Iterators.
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::begin()
+    -> iterator {
+  return body_.begin();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+constexpr auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::begin()
+    const -> const_iterator {
+  return std::begin(body_);
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::cbegin() const
+    -> const_iterator {
+  return body_.cbegin();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::end() -> iterator {
+  return body_.end();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+constexpr auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::end()
+    const -> const_iterator {
+  return std::end(body_);
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::cend() const
+    -> const_iterator {
+  return body_.cend();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::rbegin()
+    -> reverse_iterator {
+  return body_.rbegin();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::rbegin() const
+    -> const_reverse_iterator {
+  return body_.rbegin();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::crbegin() const
+    -> const_reverse_iterator {
+  return body_.crbegin();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::rend()
+    -> reverse_iterator {
+  return body_.rend();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::rend() const
+    -> const_reverse_iterator {
+  return body_.rend();
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::crend() const
+    -> const_reverse_iterator {
+  return body_.crend();
+}
+
+// ----------------------------------------------------------------------------
+// Insert operations.
+//
+// Currently we use position_hint the same way as eastl or boost:
+// https://github.com/electronicarts/EASTL/blob/master/include/EASTL/vector_set.h#L493
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::insert(
+    const value_type& val) -> std::pair<iterator, bool> {
+  return emplace_key_args(GetKeyFromValue()(val), val);
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::insert(
+    value_type&& val) -> std::pair<iterator, bool> {
+  return emplace_key_args(GetKeyFromValue()(val), std::move(val));
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::insert(
+    const_iterator position_hint,
+    const value_type& val) -> iterator {
+  return emplace_hint_key_args(position_hint, GetKeyFromValue()(val), val)
+      .first;
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::insert(
+    const_iterator position_hint,
+    value_type&& val) -> iterator {
+  return emplace_hint_key_args(position_hint, GetKeyFromValue()(val),
+                               std::move(val))
+      .first;
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <class InputIterator>
+void flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::insert(
+    InputIterator first,
+    InputIterator last) {
+  if (first == last)
+    return;
+
+  // Dispatch to single element insert if the input range contains a single
+  // element.
+  if (is_multipass<InputIterator>() && std::next(first) == last) {
+    insert(end(), *first);
+    return;
+  }
+
+  // Provide a convenience lambda to obtain an iterator pointing past the last
+  // old element. This needs to be dymanic due to possible re-allocations.
+  auto middle = [this, size = size()] { return std::next(begin(), size); };
+
+  // For batch updates initialize the first insertion point.
+  difference_type pos_first_new = size();
+
+  // Loop over the input range while appending new values and overwriting
+  // existing ones, if applicable. Keep track of the first insertion point.
+  for (; first != last; ++first) {
+    std::pair<iterator, bool> result = append_unique(begin(), middle(), *first);
+    if (result.second) {
+      pos_first_new =
+          std::min(pos_first_new, std::distance(begin(), result.first));
+    }
+  }
+
+  // The new elements might be unordered and contain duplicates, so post-process
+  // the just inserted elements and merge them with the rest, inserting them at
+  // the previously found spot.
+  sort_and_unique(middle(), end());
+  std::inplace_merge(std::next(begin(), pos_first_new), middle(), end(),
+                     value_comp());
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <class... Args>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::emplace(
+    Args&&... args) -> std::pair<iterator, bool> {
+  return insert(value_type(std::forward<Args>(args)...));
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <class... Args>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::emplace_hint(
+    const_iterator position_hint,
+    Args&&... args) -> iterator {
+  return insert(position_hint, value_type(std::forward<Args>(args)...));
+}
+
+// ----------------------------------------------------------------------------
+// Underlying type operations.
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::
+    extract() && -> container_type {
+  return std::exchange(body_, container_type());
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+void flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::replace(
+    container_type&& body) {
+  // Ensure that `body` is sorted and has no repeated elements according to
+  // `value_comp()`.
+  RTC_DCHECK(is_sorted_and_unique(body, value_comp()));
+  body_ = std::move(body);
+}
+
+// ----------------------------------------------------------------------------
+// Erase operations.
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::erase(
+    iterator position) -> iterator {
+  RTC_CHECK(position != body_.end());
+  return body_.erase(position);
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <typename DummyT>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::erase(
+    const_iterator position) -> iterator {
+  RTC_CHECK(position != body_.end());
+  return body_.erase(position);
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <typename K>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::erase(const K& val)
+    -> size_type {
+  auto eq_range = equal_range(val);
+  auto res = std::distance(eq_range.first, eq_range.second);
+  erase(eq_range.first, eq_range.second);
+  return res;
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::erase(
+    const_iterator first,
+    const_iterator last) -> iterator {
+  return body_.erase(first, last);
+}
+
+// ----------------------------------------------------------------------------
+// Comparators.
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+constexpr auto
+flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::key_comp() const
+    -> key_compare {
+  return comp_;
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+constexpr auto
+flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::value_comp() const
+    -> value_compare {
+  return value_compare{comp_};
+}
+
+// ----------------------------------------------------------------------------
+// Search operations.
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <typename K>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::count(
+    const K& key) const -> size_type {
+  auto eq_range = equal_range(key);
+  return std::distance(eq_range.first, eq_range.second);
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <typename K>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::find(const K& key)
+    -> iterator {
+  return const_cast_it(webrtc::as_const(*this).find(key));
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <typename K>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::find(
+    const K& key) const -> const_iterator {
+  auto eq_range = equal_range(key);
+  return (eq_range.first == eq_range.second) ? end() : eq_range.first;
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <typename K>
+bool flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::contains(
+    const K& key) const {
+  auto lower = lower_bound(key);
+  return lower != end() && !comp_(key, GetKeyFromValue()(*lower));
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <typename K>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::equal_range(
+    const K& key) -> std::pair<iterator, iterator> {
+  auto res = webrtc::as_const(*this).equal_range(key);
+  return {const_cast_it(res.first), const_cast_it(res.second)};
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <typename K>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::equal_range(
+    const K& key) const -> std::pair<const_iterator, const_iterator> {
+  auto lower = lower_bound(key);
+
+  KeyValueCompare comp(comp_);
+  if (lower == end() || comp(key, *lower))
+    return {lower, lower};
+
+  return {lower, std::next(lower)};
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <typename K>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::lower_bound(
+    const K& key) -> iterator {
+  return const_cast_it(webrtc::as_const(*this).lower_bound(key));
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <typename K>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::lower_bound(
+    const K& key) const -> const_iterator {
+  static_assert(std::is_convertible<const KeyTypeOrK<K>&, const K&>::value,
+                "Requested type cannot be bound to the container's key_type "
+                "which is required for a non-transparent compare.");
+
+  const KeyTypeOrK<K>& key_ref = key;
+
+  KeyValueCompare comp(comp_);
+  return absl::c_lower_bound(*this, key_ref, comp);
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <typename K>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::upper_bound(
+    const K& key) -> iterator {
+  return const_cast_it(webrtc::as_const(*this).upper_bound(key));
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <typename K>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::upper_bound(
+    const K& key) const -> const_iterator {
+  static_assert(std::is_convertible<const KeyTypeOrK<K>&, const K&>::value,
+                "Requested type cannot be bound to the container's key_type "
+                "which is required for a non-transparent compare.");
+
+  const KeyTypeOrK<K>& key_ref = key;
+
+  KeyValueCompare comp(comp_);
+  return absl::c_upper_bound(*this, key_ref, comp);
+}
+
+// ----------------------------------------------------------------------------
+// General operations.
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+void flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::swap(
+    flat_tree& other) noexcept {
+  std::swap(*this, other);
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <class... Args>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::unsafe_emplace(
+    const_iterator position,
+    Args&&... args) -> iterator {
+  return body_.emplace(position, std::forward<Args>(args)...);
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <class K, class... Args>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::emplace_key_args(
+    const K& key,
+    Args&&... args) -> std::pair<iterator, bool> {
+  auto lower = lower_bound(key);
+  if (lower == end() || comp_(key, GetKeyFromValue()(*lower)))
+    return {unsafe_emplace(lower, std::forward<Args>(args)...), true};
+  return {lower, false};
+}
+
+template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
+template <class K, class... Args>
+auto flat_tree<Key, GetKeyFromValue, KeyCompare, Container>::
+    emplace_hint_key_args(const_iterator hint, const K& key, Args&&... args)
+        -> std::pair<iterator, bool> {
+  KeyValueCompare comp(comp_);
+  if ((hint == begin() || comp(*std::prev(hint), key))) {
+    if (hint == end() || comp(key, *hint)) {
+      // *(hint - 1) < key < *hint => key did not exist and hint is correct.
+      return {unsafe_emplace(hint, std::forward<Args>(args)...), true};
+    }
+    if (!comp(*hint, key)) {
+      // key == *hint => no-op, return correct hint.
+      return {const_cast_it(hint), false};
+    }
+  }
+  // hint was not helpful, dispatch to hintless version.
+  return emplace_key_args(key, std::forward<Args>(args)...);
+}
+
+}  // namespace flat_containers_internal
+
+// ----------------------------------------------------------------------------
+// Free functions.
+
+// Erases all elements that match predicate. It has O(size) complexity.
+template <class Key,
+          class GetKeyFromValue,
+          class KeyCompare,
+          class Container,
+          typename Predicate>
+size_t EraseIf(
+    webrtc::flat_containers_internal::
+        flat_tree<Key, GetKeyFromValue, KeyCompare, Container>& container,
+    Predicate pred) {
+  auto it = std::remove_if(container.begin(), container.end(), pred);
+  size_t removed = std::distance(it, container.end());
+  container.erase(it, container.end());
+  return removed;
+}
+
+}  // namespace webrtc
+
+#endif  // RTC_BASE_CONTAINERS_FLAT_TREE_H_
diff --git a/rtc_base/containers/flat_tree_unittest.cc b/rtc_base/containers/flat_tree_unittest.cc
new file mode 100644
index 0000000..9bb803d
--- /dev/null
+++ b/rtc_base/containers/flat_tree_unittest.cc
@@ -0,0 +1,1484 @@
+/*
+ *  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"
+
+// Following tests are ported and extended tests from libcpp for std::set.
+// They can be found here:
+// https://github.com/llvm/llvm-project/tree/main/libcxx/test/std/containers/associative/set
+//
+// Not ported tests:
+// * No tests with PrivateConstructor and std::less<> changed to std::less<T>
+//   These tests have to do with C++14 std::less<>
+//   http://en.cppreference.com/w/cpp/utility/functional/less_void
+//   and add support for templated versions of lookup functions.
+//   Because we use same implementation, we figured that it's OK just to check
+//   compilation and this is what we do in flat_set_unittest/flat_map_unittest.
+// * No tests for max_size()
+//   Has to do with allocator support.
+// * No tests with DefaultOnly.
+//   Standard containers allocate each element in the separate node on the heap
+//   and then manipulate these nodes. Flat containers store their elements in
+//   contiguous memory and move them around, type is required to be movable.
+// * No tests for N3644.
+//   This proposal suggests that all default constructed iterators compare
+//   equal. Currently we use std::vector iterators and they don't implement
+//   this.
+// * No tests with min_allocator and no tests counting allocations.
+//   Flat sets currently don't support allocators.
+
+#include <array>
+#include <deque>
+#include <forward_list>
+#include <functional>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "rtc_base/containers/identity.h"
+#include "rtc_base/containers/move_only_int.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace flat_containers_internal {
+namespace {
+
+template <class It>
+class InputIterator {
+ public:
+  using iterator_category = std::input_iterator_tag;
+  using value_type = typename std::iterator_traits<It>::value_type;
+  using difference_type = typename std::iterator_traits<It>::difference_type;
+  using pointer = It;
+  using reference = typename std::iterator_traits<It>::reference;
+
+  InputIterator() : it_() {}
+  explicit InputIterator(It it) : it_(it) {}
+
+  reference operator*() const { return *it_; }
+  pointer operator->() const { return it_; }
+
+  InputIterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  InputIterator operator++(int) {
+    InputIterator tmp(*this);
+    ++(*this);
+    return tmp;
+  }
+
+  friend bool operator==(const InputIterator& lhs, const InputIterator& rhs) {
+    return lhs.it_ == rhs.it_;
+  }
+  friend bool operator!=(const InputIterator& lhs, const InputIterator& rhs) {
+    return !(lhs == rhs);
+  }
+
+ private:
+  It it_;
+};
+
+template <typename It>
+InputIterator<It> MakeInputIterator(It it) {
+  return InputIterator<It>(it);
+}
+
+class Emplaceable {
+ public:
+  Emplaceable() : Emplaceable(0, 0.0) {}
+  Emplaceable(int i, double d) : int_(i), double_(d) {}
+  Emplaceable(Emplaceable&& other) : int_(other.int_), double_(other.double_) {
+    other.int_ = 0;
+    other.double_ = 0.0;
+  }
+  Emplaceable(const Emplaceable&) = delete;
+  Emplaceable& operator=(const Emplaceable&) = delete;
+
+  Emplaceable& operator=(Emplaceable&& other) {
+    int_ = other.int_;
+    other.int_ = 0;
+    double_ = other.double_;
+    other.double_ = 0.0;
+    return *this;
+  }
+
+  friend bool operator==(const Emplaceable& lhs, const Emplaceable& rhs) {
+    return std::tie(lhs.int_, lhs.double_) == std::tie(rhs.int_, rhs.double_);
+  }
+
+  friend bool operator<(const Emplaceable& lhs, const Emplaceable& rhs) {
+    return std::tie(lhs.int_, lhs.double_) < std::tie(rhs.int_, rhs.double_);
+  }
+
+ private:
+  int int_;
+  double double_;
+};
+
+struct TemplateConstructor {
+  template <typename T>
+  explicit TemplateConstructor(const T&) {}
+
+  friend bool operator<(const TemplateConstructor&,
+                        const TemplateConstructor&) {
+    return false;
+  }
+};
+
+class NonDefaultConstructibleCompare {
+ public:
+  explicit NonDefaultConstructibleCompare(int) {}
+
+  template <typename T>
+  bool operator()(const T& lhs, const T& rhs) const {
+    return std::less<T>()(lhs, rhs);
+  }
+};
+
+template <class PairType>
+struct LessByFirst {
+  bool operator()(const PairType& lhs, const PairType& rhs) const {
+    return lhs.first < rhs.first;
+  }
+};
+
+// Common test trees.
+template <typename ContainerT>
+using TypedTree = flat_tree<typename ContainerT::value_type,
+                            identity,
+                            std::less<>,
+                            ContainerT>;
+using IntTree = TypedTree<std::vector<int>>;
+using IntPair = std::pair<int, int>;
+using IntPairTree =
+    flat_tree<IntPair, identity, LessByFirst<IntPair>, std::vector<IntPair>>;
+using MoveOnlyTree =
+    flat_tree<MoveOnlyInt, identity, std::less<>, std::vector<MoveOnlyInt>>;
+using EmplaceableTree =
+    flat_tree<Emplaceable, identity, std::less<>, std::vector<Emplaceable>>;
+using ReversedTree =
+    flat_tree<int, identity, std::greater<int>, std::vector<int>>;
+
+using TreeWithStrangeCompare =
+    flat_tree<int, identity, NonDefaultConstructibleCompare, std::vector<int>>;
+
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+
+template <typename T>
+class FlatTreeTest : public testing::Test {};
+TYPED_TEST_SUITE_P(FlatTreeTest);
+
+TEST(FlatTree, IsMultipass) {
+  static_assert(!is_multipass<std::istream_iterator<int>>(),
+                "InputIterator is not multipass");
+  static_assert(!is_multipass<std::ostream_iterator<int>>(),
+                "OutputIterator is not multipass");
+
+  static_assert(is_multipass<std::forward_list<int>::iterator>(),
+                "ForwardIterator is multipass");
+  static_assert(is_multipass<std::list<int>::iterator>(),
+                "BidirectionalIterator is multipass");
+  static_assert(is_multipass<std::vector<int>::iterator>(),
+                "RandomAccessIterator is multipass");
+}
+
+// Tests that the compiler generated move operators propagrate noexcept
+// specifiers.
+TEST(FlatTree, NoExcept) {
+  struct MoveThrows {
+    MoveThrows(MoveThrows&&) noexcept(false) {}
+    MoveThrows& operator=(MoveThrows&&) noexcept(false) { return *this; }
+  };
+
+  using MoveThrowsTree =
+      flat_tree<MoveThrows, identity, std::less<>, std::array<MoveThrows, 1>>;
+
+  static_assert(std::is_nothrow_move_constructible<IntTree>::value,
+                "Error: IntTree is not nothrow move constructible");
+  static_assert(std::is_nothrow_move_assignable<IntTree>::value,
+                "Error: IntTree is not nothrow move assignable");
+
+  static_assert(!std::is_nothrow_move_constructible<MoveThrowsTree>::value,
+                "Error: MoveThrowsTree is nothrow move constructible");
+  static_assert(!std::is_nothrow_move_assignable<MoveThrowsTree>::value,
+                "Error: MoveThrowsTree is nothrow move assignable");
+}
+
+// ----------------------------------------------------------------------------
+// Class.
+
+// Check that flat_tree and its iterators can be instantiated with an
+// incomplete type.
+
+TEST(FlatTree, IncompleteType) {
+  struct A {
+    using Tree = flat_tree<A, identity, std::less<A>, std::vector<A>>;
+    int data;
+    Tree set_with_incomplete_type;
+    Tree::iterator it;
+    Tree::const_iterator cit;
+
+    // We do not declare operator< because clang complains that it's unused.
+  };
+
+  A a;
+}
+
+TEST(FlatTree, Stability) {
+  using Pair = std::pair<int, int>;
+
+  using Tree = flat_tree<Pair, identity, LessByFirst<Pair>, std::vector<Pair>>;
+
+  // Constructors are stable.
+  Tree cont({{0, 0}, {1, 0}, {0, 1}, {2, 0}, {0, 2}, {1, 1}});
+
+  auto AllOfSecondsAreZero = [&cont] {
+    return absl::c_all_of(cont,
+                          [](const Pair& elem) { return elem.second == 0; });
+  };
+
+  EXPECT_TRUE(AllOfSecondsAreZero()) << "constructor should be stable";
+
+  // Should not replace existing.
+  cont.insert(Pair(0, 2));
+  cont.insert(Pair(1, 2));
+  cont.insert(Pair(2, 2));
+
+  EXPECT_TRUE(AllOfSecondsAreZero()) << "insert should be stable";
+
+  cont.insert(Pair(3, 0));
+  cont.insert(Pair(3, 2));
+
+  EXPECT_TRUE(AllOfSecondsAreZero()) << "insert should be stable";
+}
+
+// ----------------------------------------------------------------------------
+// Types.
+
+// key_type
+// key_compare
+// value_type
+// value_compare
+// pointer
+// const_pointer
+// reference
+// const_reference
+// size_type
+// difference_type
+// iterator
+// const_iterator
+// reverse_iterator
+// const_reverse_iterator
+
+TEST(FlatTree, Types) {
+  // These are guaranteed to be portable.
+  static_assert((std::is_same<int, IntTree::key_type>::value), "");
+  static_assert((std::is_same<int, IntTree::value_type>::value), "");
+  static_assert((std::is_same<std::less<>, IntTree::key_compare>::value), "");
+  static_assert((std::is_same<int&, IntTree::reference>::value), "");
+  static_assert((std::is_same<const int&, IntTree::const_reference>::value),
+                "");
+  static_assert((std::is_same<int*, IntTree::pointer>::value), "");
+  static_assert((std::is_same<const int*, IntTree::const_pointer>::value), "");
+}
+
+// ----------------------------------------------------------------------------
+// Lifetime.
+
+// flat_tree()
+// flat_tree(const Compare& comp)
+
+TYPED_TEST_P(FlatTreeTest, DefaultConstructor) {
+  {
+    TypedTree<TypeParam> cont;
+    EXPECT_THAT(cont, ElementsAre());
+  }
+
+  {
+    TreeWithStrangeCompare cont(NonDefaultConstructibleCompare(0));
+    EXPECT_THAT(cont, ElementsAre());
+  }
+}
+
+// flat_tree(const flat_tree& x)
+
+TYPED_TEST_P(FlatTreeTest, CopyConstructor) {
+  TypedTree<TypeParam> original({1, 2, 3, 4});
+  TypedTree<TypeParam> copied(original);
+
+  EXPECT_THAT(copied, ElementsAre(1, 2, 3, 4));
+
+  EXPECT_THAT(copied, ElementsAre(1, 2, 3, 4));
+  EXPECT_THAT(original, ElementsAre(1, 2, 3, 4));
+  EXPECT_EQ(original, copied);
+}
+
+// flat_tree(flat_tree&& x)
+
+TEST(FlatTree, MoveConstructor) {
+  int input_range[] = {1, 2, 3, 4};
+
+  MoveOnlyTree original(std::begin(input_range), std::end(input_range));
+  MoveOnlyTree 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)));
+}
+
+// flat_tree(InputIterator first,
+//           InputIterator last,
+//           const Compare& comp = Compare())
+
+TEST(FlatTree, RangeConstructor) {
+  {
+    IntPair input_vals[] = {{1, 1}, {1, 2}, {2, 1}, {2, 2}, {1, 3},
+                            {2, 3}, {3, 1}, {3, 2}, {3, 3}};
+
+    IntPairTree first_of(MakeInputIterator(std::begin(input_vals)),
+                         MakeInputIterator(std::end(input_vals)));
+    EXPECT_THAT(first_of,
+                ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1)));
+  }
+  {
+    TreeWithStrangeCompare::value_type input_vals[] = {1, 1, 1, 2, 2,
+                                                       2, 3, 3, 3};
+
+    TreeWithStrangeCompare cont(MakeInputIterator(std::begin(input_vals)),
+                                MakeInputIterator(std::end(input_vals)),
+                                NonDefaultConstructibleCompare(0));
+    EXPECT_THAT(cont, ElementsAre(1, 2, 3));
+  }
+}
+
+// flat_tree(const container_type&)
+
+TYPED_TEST_P(FlatTreeTest, ContainerCopyConstructor) {
+  TypeParam items = {1, 2, 3, 4};
+  TypedTree<TypeParam> tree(items);
+
+  EXPECT_THAT(tree, ElementsAre(1, 2, 3, 4));
+  EXPECT_THAT(items, ElementsAre(1, 2, 3, 4));
+}
+
+// flat_tree(container_type&&)
+
+TEST(FlatTree, ContainerMoveConstructor) {
+  using Pair = std::pair<int, MoveOnlyInt>;
+
+  // Construct an unsorted vector with a duplicate item in it. Sorted by the
+  // first item, the second allows us to test for stability. Using a move
+  // only type to ensure the vector is not copied.
+  std::vector<Pair> storage;
+  storage.push_back(Pair(2, MoveOnlyInt(0)));
+  storage.push_back(Pair(1, MoveOnlyInt(0)));
+  storage.push_back(Pair(2, MoveOnlyInt(1)));
+
+  using Tree = flat_tree<Pair, identity, LessByFirst<Pair>, std::vector<Pair>>;
+  Tree tree(std::move(storage));
+
+  // The list should be two items long, with only the first "2" saved.
+  ASSERT_EQ(2u, tree.size());
+  const Pair& zeroth = *tree.begin();
+  ASSERT_EQ(1, zeroth.first);
+  ASSERT_EQ(0, zeroth.second.data());
+
+  const Pair& first = *(tree.begin() + 1);
+  ASSERT_EQ(2, first.first);
+  ASSERT_EQ(0, first.second.data());
+}
+
+// flat_tree(std::initializer_list<value_type> ilist,
+//           const Compare& comp = Compare())
+
+TYPED_TEST_P(FlatTreeTest, InitializerListConstructor) {
+  {
+    TypedTree<TypeParam> cont({1, 2, 3, 4, 5, 6, 10, 8});
+    EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
+  }
+  {
+    TypedTree<TypeParam> cont({1, 2, 3, 4, 5, 6, 10, 8});
+    EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
+  }
+  {
+    TreeWithStrangeCompare cont({1, 2, 3, 4, 5, 6, 10, 8},
+                                NonDefaultConstructibleCompare(0));
+    EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
+  }
+  {
+    IntPairTree first_of({{1, 1}, {2, 1}, {1, 2}});
+    EXPECT_THAT(first_of, ElementsAre(IntPair(1, 1), IntPair(2, 1)));
+  }
+}
+
+// flat_tree(sorted_unique_t,
+//           InputIterator first,
+//           InputIterator last,
+//           const Compare& comp = Compare())
+
+TEST(FlatTree, SortedUniqueRangeConstructor) {
+  {
+    IntPair input_vals[] = {{1, 1}, {2, 1}, {3, 1}};
+
+    IntPairTree first_of(sorted_unique,
+                         MakeInputIterator(std::begin(input_vals)),
+                         MakeInputIterator(std::end(input_vals)));
+    EXPECT_THAT(first_of,
+                ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1)));
+  }
+  {
+    TreeWithStrangeCompare::value_type input_vals[] = {1, 2, 3};
+
+    TreeWithStrangeCompare cont(sorted_unique,
+                                MakeInputIterator(std::begin(input_vals)),
+                                MakeInputIterator(std::end(input_vals)),
+                                NonDefaultConstructibleCompare(0));
+    EXPECT_THAT(cont, ElementsAre(1, 2, 3));
+  }
+}
+
+// flat_tree(sorted_unique_t, const container_type&)
+
+TYPED_TEST_P(FlatTreeTest, SortedUniqueContainerCopyConstructor) {
+  TypeParam items = {1, 2, 3, 4};
+  TypedTree<TypeParam> tree(sorted_unique, items);
+
+  EXPECT_THAT(tree, ElementsAre(1, 2, 3, 4));
+  EXPECT_THAT(items, ElementsAre(1, 2, 3, 4));
+}
+
+// flat_tree(sorted_unique_t, std::vector<value_type>&&)
+
+TEST(FlatTree, SortedUniqueVectorMoveConstructor) {
+  using Pair = std::pair<int, MoveOnlyInt>;
+
+  std::vector<Pair> storage;
+  storage.push_back(Pair(1, MoveOnlyInt(0)));
+  storage.push_back(Pair(2, MoveOnlyInt(0)));
+
+  using Tree = flat_tree<Pair, identity, LessByFirst<Pair>, std::vector<Pair>>;
+  Tree tree(sorted_unique, std::move(storage));
+
+  ASSERT_EQ(2u, tree.size());
+  const Pair& zeroth = *tree.begin();
+  ASSERT_EQ(1, zeroth.first);
+  ASSERT_EQ(0, zeroth.second.data());
+
+  const Pair& first = *(tree.begin() + 1);
+  ASSERT_EQ(2, first.first);
+  ASSERT_EQ(0, first.second.data());
+}
+
+// flat_tree(sorted_unique_t,
+//           std::initializer_list<value_type> ilist,
+//           const Compare& comp = Compare())
+
+TYPED_TEST_P(FlatTreeTest, SortedUniqueInitializerListConstructor) {
+  {
+    TypedTree<TypeParam> cont(sorted_unique, {1, 2, 3, 4, 5, 6, 8, 10});
+    EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
+  }
+  {
+    TypedTree<TypeParam> cont(sorted_unique, {1, 2, 3, 4, 5, 6, 8, 10});
+    EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
+  }
+  {
+    TreeWithStrangeCompare cont(sorted_unique, {1, 2, 3, 4, 5, 6, 8, 10},
+                                NonDefaultConstructibleCompare(0));
+    EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
+  }
+  {
+    IntPairTree first_of(sorted_unique, {{1, 1}, {2, 1}});
+    EXPECT_THAT(first_of, ElementsAre(IntPair(1, 1), IntPair(2, 1)));
+  }
+}
+
+// ----------------------------------------------------------------------------
+// Assignments.
+
+// flat_tree& operator=(const flat_tree&)
+
+TYPED_TEST_P(FlatTreeTest, CopyAssignable) {
+  TypedTree<TypeParam> original({1, 2, 3, 4});
+  TypedTree<TypeParam> copied;
+  copied = original;
+
+  EXPECT_THAT(copied, ElementsAre(1, 2, 3, 4));
+  EXPECT_THAT(original, ElementsAre(1, 2, 3, 4));
+  EXPECT_EQ(original, copied);
+}
+
+// flat_tree& operator=(flat_tree&&)
+
+TEST(FlatTree, MoveAssignable) {
+  int input_range[] = {1, 2, 3, 4};
+
+  MoveOnlyTree original(std::begin(input_range), std::end(input_range));
+  MoveOnlyTree moved;
+  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)));
+}
+
+// flat_tree& operator=(std::initializer_list<value_type> ilist)
+
+TYPED_TEST_P(FlatTreeTest, InitializerListAssignable) {
+  TypedTree<TypeParam> cont({0});
+  cont = {1, 2, 3, 4, 5, 6, 10, 8};
+
+  EXPECT_EQ(0U, cont.count(0));
+  EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
+}
+
+// --------------------------------------------------------------------------
+// Memory management.
+
+// void reserve(size_type new_capacity)
+
+TEST(FlatTreeTest, Reserve) {
+  IntTree cont({1, 2, 3});
+
+  cont.reserve(5);
+  EXPECT_LE(5U, cont.capacity());
+}
+
+// size_type capacity() const
+
+TEST(FlatTreeTest, Capacity) {
+  IntTree cont({1, 2, 3});
+
+  EXPECT_LE(cont.size(), cont.capacity());
+  cont.reserve(5);
+  EXPECT_LE(cont.size(), cont.capacity());
+}
+
+// void shrink_to_fit()
+
+TEST(FlatTreeTest, ShrinkToFit) {
+  IntTree cont({1, 2, 3});
+
+  IntTree::size_type capacity_before = cont.capacity();
+  cont.shrink_to_fit();
+  EXPECT_GE(capacity_before, cont.capacity());
+}
+
+// ----------------------------------------------------------------------------
+// Size management.
+
+// void clear()
+
+TYPED_TEST_P(FlatTreeTest, Clear) {
+  TypedTree<TypeParam> cont({1, 2, 3, 4, 5, 6, 7, 8});
+  cont.clear();
+  EXPECT_THAT(cont, ElementsAre());
+}
+
+// size_type size() const
+
+TYPED_TEST_P(FlatTreeTest, Size) {
+  TypedTree<TypeParam> cont;
+
+  EXPECT_EQ(0U, cont.size());
+  cont.insert(2);
+  EXPECT_EQ(1U, cont.size());
+  cont.insert(1);
+  EXPECT_EQ(2U, cont.size());
+  cont.insert(3);
+  EXPECT_EQ(3U, cont.size());
+  cont.erase(cont.begin());
+  EXPECT_EQ(2U, cont.size());
+  cont.erase(cont.begin());
+  EXPECT_EQ(1U, cont.size());
+  cont.erase(cont.begin());
+  EXPECT_EQ(0U, cont.size());
+}
+
+// bool empty() const
+
+TYPED_TEST_P(FlatTreeTest, Empty) {
+  TypedTree<TypeParam> cont;
+
+  EXPECT_TRUE(cont.empty());
+  cont.insert(1);
+  EXPECT_FALSE(cont.empty());
+  cont.clear();
+  EXPECT_TRUE(cont.empty());
+}
+
+// ----------------------------------------------------------------------------
+// Iterators.
+
+// iterator begin()
+// const_iterator begin() const
+// iterator end()
+// const_iterator end() const
+//
+// reverse_iterator rbegin()
+// const_reverse_iterator rbegin() const
+// reverse_iterator rend()
+// const_reverse_iterator rend() const
+//
+// const_iterator cbegin() const
+// const_iterator cend() const
+// const_reverse_iterator crbegin() const
+// const_reverse_iterator crend() const
+
+TYPED_TEST_P(FlatTreeTest, Iterators) {
+  TypedTree<TypeParam> cont({1, 2, 3, 4, 5, 6, 7, 8});
+
+  auto size =
+      static_cast<typename TypedTree<TypeParam>::difference_type>(cont.size());
+
+  EXPECT_EQ(size, std::distance(cont.begin(), cont.end()));
+  EXPECT_EQ(size, std::distance(cont.cbegin(), cont.cend()));
+  EXPECT_EQ(size, std::distance(cont.rbegin(), cont.rend()));
+  EXPECT_EQ(size, std::distance(cont.crbegin(), cont.crend()));
+
+  {
+    auto it = cont.begin();
+    auto c_it = cont.cbegin();
+    EXPECT_EQ(it, c_it);
+    for (int j = 1; it != cont.end(); ++it, ++c_it, ++j) {
+      EXPECT_EQ(j, *it);
+      EXPECT_EQ(j, *c_it);
+    }
+  }
+  {
+    auto rit = cont.rbegin();
+    auto c_rit = cont.crbegin();
+    EXPECT_EQ(rit, c_rit);
+    for (int j = static_cast<int>(size); rit != cont.rend();
+         ++rit, ++c_rit, --j) {
+      EXPECT_EQ(j, *rit);
+      EXPECT_EQ(j, *c_rit);
+    }
+  }
+}
+
+// ----------------------------------------------------------------------------
+// Insert operations.
+
+// pair<iterator, bool> insert(const value_type& val)
+
+TYPED_TEST_P(FlatTreeTest, InsertLValue) {
+  TypedTree<TypeParam> cont;
+
+  int value = 2;
+  std::pair<typename TypedTree<TypeParam>::iterator, bool> result =
+      cont.insert(value);
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(cont.begin(), result.first);
+  EXPECT_EQ(1U, cont.size());
+  EXPECT_EQ(2, *result.first);
+
+  value = 1;
+  result = cont.insert(value);
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(cont.begin(), result.first);
+  EXPECT_EQ(2U, cont.size());
+  EXPECT_EQ(1, *result.first);
+
+  value = 3;
+  result = cont.insert(value);
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(std::prev(cont.end()), result.first);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, *result.first);
+
+  value = 3;
+  result = cont.insert(value);
+  EXPECT_FALSE(result.second);
+  EXPECT_EQ(std::prev(cont.end()), result.first);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, *result.first);
+}
+
+// pair<iterator, bool> insert(value_type&& val)
+
+TEST(FlatTree, InsertRValue) {
+  MoveOnlyTree cont;
+
+  std::pair<MoveOnlyTree::iterator, bool> result = cont.insert(MoveOnlyInt(2));
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(cont.begin(), result.first);
+  EXPECT_EQ(1U, cont.size());
+  EXPECT_EQ(2, result.first->data());
+
+  result = cont.insert(MoveOnlyInt(1));
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(cont.begin(), result.first);
+  EXPECT_EQ(2U, cont.size());
+  EXPECT_EQ(1, result.first->data());
+
+  result = cont.insert(MoveOnlyInt(3));
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(std::prev(cont.end()), result.first);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, result.first->data());
+
+  result = cont.insert(MoveOnlyInt(3));
+  EXPECT_FALSE(result.second);
+  EXPECT_EQ(std::prev(cont.end()), result.first);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, result.first->data());
+}
+
+// iterator insert(const_iterator position_hint, const value_type& val)
+
+TYPED_TEST_P(FlatTreeTest, InsertPositionLValue) {
+  TypedTree<TypeParam> cont;
+
+  auto result = cont.insert(cont.cend(), 2);
+  EXPECT_EQ(cont.begin(), result);
+  EXPECT_EQ(1U, cont.size());
+  EXPECT_EQ(2, *result);
+
+  result = cont.insert(cont.cend(), 1);
+  EXPECT_EQ(cont.begin(), result);
+  EXPECT_EQ(2U, cont.size());
+  EXPECT_EQ(1, *result);
+
+  result = cont.insert(cont.cend(), 3);
+  EXPECT_EQ(std::prev(cont.end()), result);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, *result);
+
+  result = cont.insert(cont.cend(), 3);
+  EXPECT_EQ(std::prev(cont.end()), result);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, *result);
+}
+
+// iterator insert(const_iterator position_hint, value_type&& val)
+
+TEST(FlatTree, InsertPositionRValue) {
+  MoveOnlyTree cont;
+
+  auto result = cont.insert(cont.cend(), MoveOnlyInt(2));
+  EXPECT_EQ(cont.begin(), result);
+  EXPECT_EQ(1U, cont.size());
+  EXPECT_EQ(2, result->data());
+
+  result = cont.insert(cont.cend(), MoveOnlyInt(1));
+  EXPECT_EQ(cont.begin(), result);
+  EXPECT_EQ(2U, cont.size());
+  EXPECT_EQ(1, result->data());
+
+  result = cont.insert(cont.cend(), MoveOnlyInt(3));
+  EXPECT_EQ(std::prev(cont.end()), result);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, result->data());
+
+  result = cont.insert(cont.cend(), MoveOnlyInt(3));
+  EXPECT_EQ(std::prev(cont.end()), result);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, result->data());
+}
+
+// template <class InputIterator>
+//   void insert(InputIterator first, InputIterator last);
+
+TEST(FlatTree, InsertIterIter) {
+  struct GetKeyFromIntIntPair {
+    const int& operator()(const std::pair<int, int>& p) const {
+      return p.first;
+    }
+  };
+
+  using IntIntMap = flat_tree<int, GetKeyFromIntIntPair, std::less<int>,
+                              std::vector<IntPair>>;
+
+  {
+    IntIntMap cont;
+    IntPair int_pairs[] = {{3, 1}, {1, 1}, {4, 1}, {2, 1}};
+    cont.insert(std::begin(int_pairs), std::end(int_pairs));
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
+                                  IntPair(4, 1)));
+  }
+
+  {
+    IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
+    std::vector<IntPair> int_pairs;
+    cont.insert(std::begin(int_pairs), std::end(int_pairs));
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
+                                  IntPair(4, 1)));
+  }
+
+  {
+    IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
+    IntPair int_pairs[] = {{1, 1}};
+    cont.insert(std::begin(int_pairs), std::end(int_pairs));
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
+                                  IntPair(4, 1)));
+  }
+
+  {
+    IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
+    IntPair int_pairs[] = {{5, 1}};
+    cont.insert(std::begin(int_pairs), std::end(int_pairs));
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
+                                  IntPair(4, 1), IntPair(5, 1)));
+  }
+
+  {
+    IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
+    IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}};
+    cont.insert(std::begin(int_pairs), std::end(int_pairs));
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
+                                  IntPair(4, 1)));
+  }
+
+  {
+    IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
+    IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}, {7, 2}, {6, 2},
+                           {8, 2}, {5, 2}, {5, 3}, {6, 3}, {7, 3}, {8, 3}};
+    cont.insert(std::begin(int_pairs), std::end(int_pairs));
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
+                                  IntPair(4, 1), IntPair(5, 2), IntPair(6, 2),
+                                  IntPair(7, 2), IntPair(8, 2)));
+  }
+}
+
+// template <class... Args>
+// pair<iterator, bool> emplace(Args&&... args)
+
+TYPED_TEST_P(FlatTreeTest, Emplace) {
+  {
+    EmplaceableTree cont;
+
+    std::pair<EmplaceableTree::iterator, bool> result = cont.emplace();
+    EXPECT_TRUE(result.second);
+    EXPECT_EQ(cont.begin(), result.first);
+    EXPECT_EQ(1U, cont.size());
+    EXPECT_EQ(Emplaceable(), *cont.begin());
+
+    result = cont.emplace(2, 3.5);
+    EXPECT_TRUE(result.second);
+    EXPECT_EQ(std::next(cont.begin()), result.first);
+    EXPECT_EQ(2U, cont.size());
+    EXPECT_EQ(Emplaceable(2, 3.5), *result.first);
+
+    result = cont.emplace(2, 3.5);
+    EXPECT_FALSE(result.second);
+    EXPECT_EQ(std::next(cont.begin()), result.first);
+    EXPECT_EQ(2U, cont.size());
+    EXPECT_EQ(Emplaceable(2, 3.5), *result.first);
+  }
+  {
+    TypedTree<TypeParam> cont;
+
+    std::pair<typename TypedTree<TypeParam>::iterator, bool> result =
+        cont.emplace(2);
+    EXPECT_TRUE(result.second);
+    EXPECT_EQ(cont.begin(), result.first);
+    EXPECT_EQ(1U, cont.size());
+    EXPECT_EQ(2, *result.first);
+  }
+}
+
+// template <class... Args>
+// iterator emplace_hint(const_iterator position_hint, Args&&... args)
+
+TYPED_TEST_P(FlatTreeTest, EmplacePosition) {
+  {
+    EmplaceableTree cont;
+
+    auto result = cont.emplace_hint(cont.cend());
+    EXPECT_EQ(cont.begin(), result);
+    EXPECT_EQ(1U, cont.size());
+    EXPECT_EQ(Emplaceable(), *cont.begin());
+
+    result = cont.emplace_hint(cont.cend(), 2, 3.5);
+    EXPECT_EQ(std::next(cont.begin()), result);
+    EXPECT_EQ(2U, cont.size());
+    EXPECT_EQ(Emplaceable(2, 3.5), *result);
+
+    result = cont.emplace_hint(cont.cbegin(), 2, 3.5);
+    EXPECT_EQ(std::next(cont.begin()), result);
+    EXPECT_EQ(2U, cont.size());
+    EXPECT_EQ(Emplaceable(2, 3.5), *result);
+  }
+  {
+    TypedTree<TypeParam> cont;
+
+    auto result = cont.emplace_hint(cont.cend(), 2);
+    EXPECT_EQ(cont.begin(), result);
+    EXPECT_EQ(1U, cont.size());
+    EXPECT_EQ(2, *result);
+  }
+}
+
+// ----------------------------------------------------------------------------
+// Underlying type operations.
+
+// underlying_type extract() &&
+TYPED_TEST_P(FlatTreeTest, Extract) {
+  TypedTree<TypeParam> cont;
+  cont.emplace(3);
+  cont.emplace(1);
+  cont.emplace(2);
+  cont.emplace(4);
+
+  TypeParam body = std::move(cont).extract();
+  EXPECT_THAT(cont, IsEmpty());
+  EXPECT_THAT(body, ElementsAre(1, 2, 3, 4));
+}
+
+// replace(underlying_type&&)
+TYPED_TEST_P(FlatTreeTest, Replace) {
+  TypeParam body = {1, 2, 3, 4};
+  TypedTree<TypeParam> cont;
+  cont.replace(std::move(body));
+
+  EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4));
+}
+
+// ----------------------------------------------------------------------------
+// Erase operations.
+
+// iterator erase(const_iterator position_hint)
+
+TYPED_TEST_P(FlatTreeTest, ErasePosition) {
+  {
+    TypedTree<TypeParam> cont({1, 2, 3, 4, 5, 6, 7, 8});
+
+    auto it = cont.erase(std::next(cont.cbegin(), 3));
+    EXPECT_EQ(std::next(cont.begin(), 3), it);
+    EXPECT_THAT(cont, ElementsAre(1, 2, 3, 5, 6, 7, 8));
+
+    it = cont.erase(std::next(cont.cbegin(), 0));
+    EXPECT_EQ(cont.begin(), it);
+    EXPECT_THAT(cont, ElementsAre(2, 3, 5, 6, 7, 8));
+
+    it = cont.erase(std::next(cont.cbegin(), 5));
+    EXPECT_EQ(cont.end(), it);
+    EXPECT_THAT(cont, ElementsAre(2, 3, 5, 6, 7));
+
+    it = cont.erase(std::next(cont.cbegin(), 1));
+    EXPECT_EQ(std::next(cont.begin()), it);
+    EXPECT_THAT(cont, ElementsAre(2, 5, 6, 7));
+
+    it = cont.erase(std::next(cont.cbegin(), 2));
+    EXPECT_EQ(std::next(cont.begin(), 2), it);
+    EXPECT_THAT(cont, ElementsAre(2, 5, 7));
+
+    it = cont.erase(std::next(cont.cbegin(), 2));
+    EXPECT_EQ(std::next(cont.begin(), 2), it);
+    EXPECT_THAT(cont, ElementsAre(2, 5));
+
+    it = cont.erase(std::next(cont.cbegin(), 0));
+    EXPECT_EQ(std::next(cont.begin(), 0), it);
+    EXPECT_THAT(cont, ElementsAre(5));
+
+    it = cont.erase(cont.cbegin());
+    EXPECT_EQ(cont.begin(), it);
+    EXPECT_EQ(cont.end(), it);
+  }
+  //  This is LWG #2059.
+  //  There is a potential ambiguity between erase with an iterator and erase
+  //  with a key, if key has a templated constructor.
+  {
+    using T = TemplateConstructor;
+
+    flat_tree<T, identity, std::less<>, std::vector<T>> cont;
+    T v(0);
+
+    auto it = cont.find(v);
+    if (it != cont.end())
+      cont.erase(it);
+  }
+}
+
+// iterator erase(const_iterator first, const_iterator last)
+
+TYPED_TEST_P(FlatTreeTest, EraseRange) {
+  TypedTree<TypeParam> cont({1, 2, 3, 4, 5, 6, 7, 8});
+
+  auto it =
+      cont.erase(std::next(cont.cbegin(), 5), std::next(cont.cbegin(), 5));
+  EXPECT_EQ(std::next(cont.begin(), 5), it);
+  EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 7, 8));
+
+  it = cont.erase(std::next(cont.cbegin(), 3), std::next(cont.cbegin(), 4));
+  EXPECT_EQ(std::next(cont.begin(), 3), it);
+  EXPECT_THAT(cont, ElementsAre(1, 2, 3, 5, 6, 7, 8));
+
+  it = cont.erase(std::next(cont.cbegin(), 2), std::next(cont.cbegin(), 5));
+  EXPECT_EQ(std::next(cont.begin(), 2), it);
+  EXPECT_THAT(cont, ElementsAre(1, 2, 7, 8));
+
+  it = cont.erase(std::next(cont.cbegin(), 0), std::next(cont.cbegin(), 2));
+  EXPECT_EQ(std::next(cont.begin(), 0), it);
+  EXPECT_THAT(cont, ElementsAre(7, 8));
+
+  it = cont.erase(cont.cbegin(), cont.cend());
+  EXPECT_EQ(cont.begin(), it);
+  EXPECT_EQ(cont.end(), it);
+}
+
+// size_type erase(const key_type& key)
+
+TYPED_TEST_P(FlatTreeTest, EraseKey) {
+  TypedTree<TypeParam> cont({1, 2, 3, 4, 5, 6, 7, 8});
+
+  EXPECT_EQ(0U, cont.erase(9));
+  EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 7, 8));
+
+  EXPECT_EQ(1U, cont.erase(4));
+  EXPECT_THAT(cont, ElementsAre(1, 2, 3, 5, 6, 7, 8));
+
+  EXPECT_EQ(1U, cont.erase(1));
+  EXPECT_THAT(cont, ElementsAre(2, 3, 5, 6, 7, 8));
+
+  EXPECT_EQ(1U, cont.erase(8));
+  EXPECT_THAT(cont, ElementsAre(2, 3, 5, 6, 7));
+
+  EXPECT_EQ(1U, cont.erase(3));
+  EXPECT_THAT(cont, ElementsAre(2, 5, 6, 7));
+
+  EXPECT_EQ(1U, cont.erase(6));
+  EXPECT_THAT(cont, ElementsAre(2, 5, 7));
+
+  EXPECT_EQ(1U, cont.erase(7));
+  EXPECT_THAT(cont, ElementsAre(2, 5));
+
+  EXPECT_EQ(1U, cont.erase(2));
+  EXPECT_THAT(cont, ElementsAre(5));
+
+  EXPECT_EQ(1U, cont.erase(5));
+  EXPECT_THAT(cont, ElementsAre());
+}
+
+TYPED_TEST_P(FlatTreeTest, EraseEndDeath) {
+  {
+    TypedTree<TypeParam> tree;
+    ASSERT_DEATH_IF_SUPPORTED(tree.erase(tree.cend()), "");
+  }
+
+  {
+    TypedTree<TypeParam> tree = {1, 2, 3, 4};
+    ASSERT_DEATH_IF_SUPPORTED(tree.erase(tree.find(5)), "");
+  }
+}
+
+// ----------------------------------------------------------------------------
+// Comparators.
+
+// key_compare key_comp() const
+
+TEST(FlatTree, KeyComp) {
+  ReversedTree cont({1, 2, 3, 4, 5});
+
+  EXPECT_TRUE(absl::c_is_sorted(cont, cont.key_comp()));
+  int new_elements[] = {6, 7, 8, 9, 10};
+  std::copy(std::begin(new_elements), std::end(new_elements),
+            std::inserter(cont, cont.end()));
+  EXPECT_TRUE(absl::c_is_sorted(cont, cont.key_comp()));
+}
+
+// value_compare value_comp() const
+
+TEST(FlatTree, ValueComp) {
+  ReversedTree cont({1, 2, 3, 4, 5});
+
+  EXPECT_TRUE(absl::c_is_sorted(cont, cont.value_comp()));
+  int new_elements[] = {6, 7, 8, 9, 10};
+  std::copy(std::begin(new_elements), std::end(new_elements),
+            std::inserter(cont, cont.end()));
+  EXPECT_TRUE(absl::c_is_sorted(cont, cont.value_comp()));
+}
+
+// ----------------------------------------------------------------------------
+// Search operations.
+
+// size_type count(const key_type& key) const
+
+TYPED_TEST_P(FlatTreeTest, Count) {
+  const TypedTree<TypeParam> cont({5, 6, 7, 8, 9, 10, 11, 12});
+
+  EXPECT_EQ(1U, cont.count(5));
+  EXPECT_EQ(1U, cont.count(6));
+  EXPECT_EQ(1U, cont.count(7));
+  EXPECT_EQ(1U, cont.count(8));
+  EXPECT_EQ(1U, cont.count(9));
+  EXPECT_EQ(1U, cont.count(10));
+  EXPECT_EQ(1U, cont.count(11));
+  EXPECT_EQ(1U, cont.count(12));
+  EXPECT_EQ(0U, cont.count(4));
+}
+
+// iterator find(const key_type& key)
+// const_iterator find(const key_type& key) const
+
+TYPED_TEST_P(FlatTreeTest, Find) {
+  {
+    TypedTree<TypeParam> cont({5, 6, 7, 8, 9, 10, 11, 12});
+
+    EXPECT_EQ(cont.begin(), cont.find(5));
+    EXPECT_EQ(std::next(cont.begin()), cont.find(6));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.find(7));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.find(8));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.find(9));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.find(10));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.find(11));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.find(12));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.find(4));
+  }
+  {
+    const TypedTree<TypeParam> cont({5, 6, 7, 8, 9, 10, 11, 12});
+
+    EXPECT_EQ(cont.begin(), cont.find(5));
+    EXPECT_EQ(std::next(cont.begin()), cont.find(6));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.find(7));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.find(8));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.find(9));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.find(10));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.find(11));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.find(12));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.find(4));
+  }
+}
+
+// bool contains(const key_type& key) const
+
+TYPED_TEST_P(FlatTreeTest, Contains) {
+  const TypedTree<TypeParam> cont({5, 6, 7, 8, 9, 10, 11, 12});
+
+  EXPECT_TRUE(cont.contains(5));
+  EXPECT_TRUE(cont.contains(6));
+  EXPECT_TRUE(cont.contains(7));
+  EXPECT_TRUE(cont.contains(8));
+  EXPECT_TRUE(cont.contains(9));
+  EXPECT_TRUE(cont.contains(10));
+  EXPECT_TRUE(cont.contains(11));
+  EXPECT_TRUE(cont.contains(12));
+  EXPECT_FALSE(cont.contains(4));
+}
+
+// pair<iterator, iterator> equal_range(const key_type& key)
+// pair<const_iterator, const_iterator> equal_range(const key_type& key) const
+
+TYPED_TEST_P(FlatTreeTest, EqualRange) {
+  {
+    TypedTree<TypeParam> cont({5, 7, 9, 11, 13, 15, 17, 19});
+
+    std::pair<typename TypedTree<TypeParam>::iterator,
+              typename TypedTree<TypeParam>::iterator>
+        result = cont.equal_range(5);
+    EXPECT_EQ(std::next(cont.begin(), 0), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.second);
+    result = cont.equal_range(7);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.second);
+    result = cont.equal_range(9);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.second);
+    result = cont.equal_range(11);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.second);
+    result = cont.equal_range(13);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.second);
+    result = cont.equal_range(15);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.second);
+    result = cont.equal_range(17);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.second);
+    result = cont.equal_range(19);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 8), result.second);
+    result = cont.equal_range(4);
+    EXPECT_EQ(std::next(cont.begin(), 0), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 0), result.second);
+    result = cont.equal_range(6);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.second);
+    result = cont.equal_range(8);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.second);
+    result = cont.equal_range(10);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.second);
+    result = cont.equal_range(12);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.second);
+    result = cont.equal_range(14);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.second);
+    result = cont.equal_range(16);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.second);
+    result = cont.equal_range(18);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.second);
+    result = cont.equal_range(20);
+    EXPECT_EQ(std::next(cont.begin(), 8), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 8), result.second);
+  }
+  {
+    const TypedTree<TypeParam> cont({5, 7, 9, 11, 13, 15, 17, 19});
+
+    std::pair<typename TypedTree<TypeParam>::const_iterator,
+              typename TypedTree<TypeParam>::const_iterator>
+        result = cont.equal_range(5);
+    EXPECT_EQ(std::next(cont.begin(), 0), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.second);
+    result = cont.equal_range(7);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.second);
+    result = cont.equal_range(9);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.second);
+    result = cont.equal_range(11);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.second);
+    result = cont.equal_range(13);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.second);
+    result = cont.equal_range(15);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.second);
+    result = cont.equal_range(17);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.second);
+    result = cont.equal_range(19);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 8), result.second);
+    result = cont.equal_range(4);
+    EXPECT_EQ(std::next(cont.begin(), 0), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 0), result.second);
+    result = cont.equal_range(6);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.second);
+    result = cont.equal_range(8);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.second);
+    result = cont.equal_range(10);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.second);
+    result = cont.equal_range(12);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.second);
+    result = cont.equal_range(14);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.second);
+    result = cont.equal_range(16);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.second);
+    result = cont.equal_range(18);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.second);
+    result = cont.equal_range(20);
+    EXPECT_EQ(std::next(cont.begin(), 8), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 8), result.second);
+  }
+}
+
+//       iterator lower_bound(const key_type& key);
+// const_iterator lower_bound(const key_type& key) const;
+
+TYPED_TEST_P(FlatTreeTest, LowerBound) {
+  {
+    TypedTree<TypeParam> cont({5, 7, 9, 11, 13, 15, 17, 19});
+
+    EXPECT_EQ(cont.begin(), cont.lower_bound(5));
+    EXPECT_EQ(std::next(cont.begin()), cont.lower_bound(7));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.lower_bound(9));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.lower_bound(11));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.lower_bound(13));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.lower_bound(15));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.lower_bound(17));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.lower_bound(19));
+    EXPECT_EQ(std::next(cont.begin(), 0), cont.lower_bound(4));
+    EXPECT_EQ(std::next(cont.begin(), 1), cont.lower_bound(6));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.lower_bound(8));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.lower_bound(10));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.lower_bound(12));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.lower_bound(14));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.lower_bound(16));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.lower_bound(18));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.lower_bound(20));
+  }
+  {
+    const TypedTree<TypeParam> cont({5, 7, 9, 11, 13, 15, 17, 19});
+
+    EXPECT_EQ(cont.begin(), cont.lower_bound(5));
+    EXPECT_EQ(std::next(cont.begin()), cont.lower_bound(7));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.lower_bound(9));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.lower_bound(11));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.lower_bound(13));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.lower_bound(15));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.lower_bound(17));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.lower_bound(19));
+    EXPECT_EQ(std::next(cont.begin(), 0), cont.lower_bound(4));
+    EXPECT_EQ(std::next(cont.begin(), 1), cont.lower_bound(6));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.lower_bound(8));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.lower_bound(10));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.lower_bound(12));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.lower_bound(14));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.lower_bound(16));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.lower_bound(18));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.lower_bound(20));
+  }
+}
+
+// iterator upper_bound(const key_type& key)
+// const_iterator upper_bound(const key_type& key) const
+
+TYPED_TEST_P(FlatTreeTest, UpperBound) {
+  {
+    TypedTree<TypeParam> cont({5, 7, 9, 11, 13, 15, 17, 19});
+
+    EXPECT_EQ(std::next(cont.begin(), 1), cont.upper_bound(5));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.upper_bound(7));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.upper_bound(9));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.upper_bound(11));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.upper_bound(13));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.upper_bound(15));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.upper_bound(17));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.upper_bound(19));
+    EXPECT_EQ(std::next(cont.begin(), 0), cont.upper_bound(4));
+    EXPECT_EQ(std::next(cont.begin(), 1), cont.upper_bound(6));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.upper_bound(8));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.upper_bound(10));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.upper_bound(12));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.upper_bound(14));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.upper_bound(16));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.upper_bound(18));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.upper_bound(20));
+  }
+  {
+    const TypedTree<TypeParam> cont({5, 7, 9, 11, 13, 15, 17, 19});
+
+    EXPECT_EQ(std::next(cont.begin(), 1), cont.upper_bound(5));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.upper_bound(7));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.upper_bound(9));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.upper_bound(11));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.upper_bound(13));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.upper_bound(15));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.upper_bound(17));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.upper_bound(19));
+    EXPECT_EQ(std::next(cont.begin(), 0), cont.upper_bound(4));
+    EXPECT_EQ(std::next(cont.begin(), 1), cont.upper_bound(6));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.upper_bound(8));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.upper_bound(10));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.upper_bound(12));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.upper_bound(14));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.upper_bound(16));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.upper_bound(18));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.upper_bound(20));
+  }
+}
+
+// ----------------------------------------------------------------------------
+// General operations.
+
+// void swap(flat_tree& other)
+// void swap(flat_tree& lhs, flat_tree& rhs)
+
+TYPED_TEST_P(FlatTreeTest, Swap) {
+  TypedTree<TypeParam> x({1, 2, 3});
+  TypedTree<TypeParam> y({4});
+  swap(x, y);
+  EXPECT_THAT(x, ElementsAre(4));
+  EXPECT_THAT(y, ElementsAre(1, 2, 3));
+
+  y.swap(x);
+  EXPECT_THAT(x, ElementsAre(1, 2, 3));
+  EXPECT_THAT(y, ElementsAre(4));
+}
+
+// bool operator==(const flat_tree& lhs, const flat_tree& rhs)
+// bool operator!=(const flat_tree& lhs, const flat_tree& rhs)
+// bool operator<(const flat_tree& lhs, const flat_tree& rhs)
+// bool operator>(const flat_tree& lhs, const flat_tree& rhs)
+// bool operator<=(const flat_tree& lhs, const flat_tree& rhs)
+// bool operator>=(const flat_tree& lhs, const flat_tree& rhs)
+
+TEST(FlatTree, Comparison) {
+  // Provided comparator does not participate in comparison.
+  ReversedTree biggest({3});
+  ReversedTree smallest({1});
+  ReversedTree middle({1, 2});
+
+  EXPECT_EQ(biggest, biggest);
+  EXPECT_NE(biggest, smallest);
+  EXPECT_LT(smallest, middle);
+  EXPECT_LE(smallest, middle);
+  EXPECT_LE(middle, middle);
+  EXPECT_GT(biggest, middle);
+  EXPECT_GE(biggest, middle);
+  EXPECT_GE(biggest, biggest);
+}
+
+TYPED_TEST_P(FlatTreeTest, SupportsEraseIf) {
+  TypedTree<TypeParam> x;
+  EXPECT_EQ(0u, EraseIf(x, [](int) { return false; }));
+  EXPECT_THAT(x, ElementsAre());
+
+  x = {1, 2, 3};
+  EXPECT_EQ(1u, EraseIf(x, [](int elem) { return !(elem & 1); }));
+  EXPECT_THAT(x, ElementsAre(1, 3));
+
+  x = {1, 2, 3, 4};
+  EXPECT_EQ(2u, EraseIf(x, [](int elem) { return elem & 1; }));
+  EXPECT_THAT(x, ElementsAre(2, 4));
+}
+
+REGISTER_TYPED_TEST_SUITE_P(FlatTreeTest,
+                            DefaultConstructor,
+                            CopyConstructor,
+                            ContainerCopyConstructor,
+                            InitializerListConstructor,
+                            SortedUniqueContainerCopyConstructor,
+                            SortedUniqueInitializerListConstructor,
+                            CopyAssignable,
+                            InitializerListAssignable,
+                            Clear,
+                            Size,
+                            Empty,
+                            Iterators,
+                            InsertLValue,
+                            InsertPositionLValue,
+                            Emplace,
+                            EmplacePosition,
+                            Extract,
+                            Replace,
+                            ErasePosition,
+                            EraseRange,
+                            EraseKey,
+                            EraseEndDeath,
+                            Count,
+                            Find,
+                            Contains,
+                            EqualRange,
+                            LowerBound,
+                            UpperBound,
+                            Swap,
+                            SupportsEraseIf);
+
+using IntSequenceContainers =
+    ::testing::Types<std::deque<int>, std::vector<int>>;
+INSTANTIATE_TYPED_TEST_SUITE_P(My, FlatTreeTest, IntSequenceContainers);
+
+}  // namespace
+}  // namespace flat_containers_internal
+}  // namespace webrtc
diff --git a/rtc_base/containers/identity.h b/rtc_base/containers/identity.h
new file mode 100644
index 0000000..2959293
--- /dev/null
+++ b/rtc_base/containers/identity.h
@@ -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_
diff --git a/rtc_base/containers/invoke.h b/rtc_base/containers/invoke.h
new file mode 100644
index 0000000..5d17a70
--- /dev/null
+++ b/rtc_base/containers/invoke.h
@@ -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_
diff --git a/rtc_base/containers/move_only_int.h b/rtc_base/containers/move_only_int.h
new file mode 100644
index 0000000..8f745aa
--- /dev/null
+++ b/rtc_base/containers/move_only_int.h
@@ -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_
diff --git a/rtc_base/containers/not_fn.h b/rtc_base/containers/not_fn.h
new file mode 100644
index 0000000..39cfd27
--- /dev/null
+++ b/rtc_base/containers/not_fn.h
@@ -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_
diff --git a/rtc_base/containers/void_t.h b/rtc_base/containers/void_t.h
new file mode 100644
index 0000000..62c57d4
--- /dev/null
+++ b/rtc_base/containers/void_t.h
@@ -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_