diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake
index 9145d8b..cd633a1 100644
--- a/CMake/AbseilDll.cmake
+++ b/CMake/AbseilDll.cmake
@@ -95,6 +95,8 @@
   "container/internal/raw_hash_set.h"
   "container/internal/raw_hash_set_resize_impl.h"
   "container/internal/tracked.h"
+  "container/linked_hash_map.h"
+  "container/linked_hash_set.h"
   "container/node_hash_map.h"
   "container/node_hash_set.h"
   "crc/crc32c.cc"
@@ -177,6 +179,7 @@
   "log/internal/conditions.cc"
   "log/internal/conditions.h"
   "log/internal/config.h"
+  "log/internal/container.h"
   "log/internal/fnmatch.h"
   "log/internal/fnmatch.cc"
   "log/internal/globals.cc"
@@ -213,6 +216,7 @@
   "log/vlog_is_on.h"
   "memory/memory.h"
   "meta/type_traits.h"
+  "meta/internal/requires.h"
   "numeric/bits.h"
   "numeric/int128.cc"
   "numeric/int128.h"
@@ -327,6 +331,9 @@
   "strings/internal/cordz_update_tracker.h"
   "strings/internal/damerau_levenshtein_distance.h"
   "strings/internal/damerau_levenshtein_distance.cc"
+  "strings/internal/generic_printer.cc"
+  "strings/internal/generic_printer.h"
+  "strings/internal/generic_printer_internal.h"
   "strings/internal/stl_type_traits.h"
   "strings/internal/string_constant.h"
   "strings/internal/stringify_sink.h"
@@ -345,8 +352,6 @@
   "strings/str_replace.h"
   "strings/str_split.cc"
   "strings/str_split.h"
-  "strings/string_view.cc"
-  "strings/string_view.h"
   "strings/strip.h"
   "strings/substitute.cc"
   "strings/substitute.h"
@@ -446,6 +451,7 @@
   "types/variant.h"
   "utility/utility.h"
   "debugging/leak_check.cc"
+  "strings/string_view.h"
 )
 
 if(MSVC)
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index ade1a01..1486722 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -964,6 +964,9 @@
     hdrs = ["internal/iterator_traits.h"],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl:__subpackages__",
+    ],
     deps = [
         ":config",
         "//absl/meta:type_traits",
diff --git a/absl/base/config.h b/absl/base/config.h
index e4518ca..39d60e3 100644
--- a/absl/base/config.h
+++ b/absl/base/config.h
@@ -526,20 +526,11 @@
 #define ABSL_USES_STD_ANY 1
 #define ABSL_HAVE_STD_OPTIONAL 1
 #define ABSL_USES_STD_OPTIONAL 1
+#define ABSL_HAVE_STD_STRING_VIEW 1
+#define ABSL_USES_STD_STRING_VIEW 1
 #define ABSL_HAVE_STD_VARIANT 1
 #define ABSL_USES_STD_VARIANT 1
 
-// ABSL_HAVE_STD_STRING_VIEW
-//
-// Deprecated: always defined to 1.
-// std::string_view was added in C++17, which means all versions of C++
-// supported by Abseil have it.
-#ifdef ABSL_HAVE_STD_STRING_VIEW
-#error "ABSL_HAVE_STD_STRING_VIEW cannot be directly set."
-#else
-#define ABSL_HAVE_STD_STRING_VIEW 1
-#endif
-
 // ABSL_HAVE_STD_ORDERING
 //
 // Checks whether C++20 std::{partial,weak,strong}_ordering are available.
@@ -556,20 +547,6 @@
 #define ABSL_HAVE_STD_ORDERING 1
 #endif
 
-// ABSL_USES_STD_STRING_VIEW
-//
-// Indicates whether absl::string_view is an alias for std::string_view.
-#if !defined(ABSL_OPTION_USE_STD_STRING_VIEW)
-#error options.h is misconfigured.
-#elif ABSL_OPTION_USE_STD_STRING_VIEW == 0
-#undef ABSL_USES_STD_STRING_VIEW
-#elif ABSL_OPTION_USE_STD_STRING_VIEW == 1 || \
-    ABSL_OPTION_USE_STD_STRING_VIEW == 2
-#define ABSL_USES_STD_STRING_VIEW 1
-#else
-#error options.h is misconfigured.
-#endif
-
 // ABSL_USES_STD_ORDERING
 //
 // Indicates whether absl::{partial,weak,strong}_ordering are aliases for the
diff --git a/absl/base/internal/iterator_traits.h b/absl/base/internal/iterator_traits.h
index 472c436..5fa4df8 100644
--- a/absl/base/internal/iterator_traits.h
+++ b/absl/base/internal/iterator_traits.h
@@ -61,6 +61,10 @@
     std::is_convertible<IteratorConcept<Iterator>, IteratorTag>;
 
 template <typename Iterator>
+using IsAtLeastInputIterator =
+    IsAtLeastIterator<std::input_iterator_tag, Iterator>;
+
+template <typename Iterator>
 using IsAtLeastForwardIterator =
     IsAtLeastIterator<std::forward_iterator_tag, Iterator>;
 
diff --git a/absl/base/options.h b/absl/base/options.h
index 71bafb3..6f48e75 100644
--- a/absl/base/options.h
+++ b/absl/base/options.h
@@ -73,32 +73,6 @@
 // Type Compatibility Options
 // -----------------------------------------------------------------------------
 
-// ABSL_OPTION_USE_STD_STRING_VIEW
-//
-// This option controls whether absl::string_view is implemented as an alias to
-// std::string_view, or as an independent implementation.
-//
-// A value of 0 means to use Abseil's implementation.  This requires only C++11
-// support, and is expected to work on every toolchain we support.
-//
-// A value of 1 means to use an alias to std::string_view.  This requires that
-// all code using Abseil is built in C++17 mode or later.
-//
-// A value of 2 means to detect the C++ version being used to compile Abseil,
-// and use an alias only if a working std::string_view is available.  This
-// option is useful when you are building your program from source.  It should
-// not be used otherwise -- for example, if you are distributing Abseil in a
-// binary package manager -- since in mode 2, absl::string_view will name a
-// different type, with a different mangled name and binary layout, depending on
-// the compiler flags passed by the end user.  For more info, see
-// https://abseil.io/about/design/dropin-types.
-//
-// User code should not inspect this macro.  To check in the preprocessor if
-// absl::string_view is a typedef of std::string_view, use the feature macro
-// ABSL_USES_STD_STRING_VIEW.
-
-#define ABSL_OPTION_USE_STD_STRING_VIEW 2
-
 // ABSL_OPTION_USE_STD_ORDERING
 //
 // This option controls whether absl::{partial,weak,strong}_ordering are
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index ab6533f..15fb0a5 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -308,7 +308,7 @@
 cc_test(
     name = "flat_hash_set_test",
     srcs = ["flat_hash_set_test.cc"],
-    copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"],
+    copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     tags = ["no_test_loonix"],
     deps = [
@@ -388,7 +388,7 @@
 cc_test(
     name = "node_hash_set_test",
     srcs = ["node_hash_set_test.cc"],
-    copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"],
+    copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     tags = ["no_test_loonix"],
     deps = [
@@ -496,6 +496,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":hash_policy_testing",
+        "//absl/base:config",
         "//absl/base:no_destructor",
         "//absl/memory",
         "//absl/meta:type_traits",
@@ -942,9 +943,13 @@
     hdrs = ["internal/unordered_map_constructor_test.h"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl/container:__pkg__",
+    ],
     deps = [
         ":hash_generator_testing",
         ":hash_policy_testing",
+        "//absl/base:config",
         "@googletest//:gtest",
     ],
 )
@@ -955,6 +960,9 @@
     hdrs = ["internal/unordered_map_lookup_test.h"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl/container:__pkg__",
+    ],
     deps = [
         ":hash_generator_testing",
         ":hash_policy_testing",
@@ -968,6 +976,9 @@
     hdrs = ["internal/unordered_map_modifiers_test.h"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl/container:__pkg__",
+    ],
     deps = [
         ":hash_generator_testing",
         ":hash_policy_testing",
@@ -981,9 +992,13 @@
     hdrs = ["internal/unordered_set_constructor_test.h"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl/container:__pkg__",
+    ],
     deps = [
         ":hash_generator_testing",
         ":hash_policy_testing",
+        "//absl/base:config",
         "//absl/meta:type_traits",
         "@googletest//:gtest",
     ],
@@ -995,6 +1010,9 @@
     hdrs = ["internal/unordered_set_members_test.h"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl/container:__pkg__",
+    ],
     deps = [
         "//absl/meta:type_traits",
         "@googletest//:gtest",
@@ -1007,6 +1025,9 @@
     hdrs = ["internal/unordered_map_members_test.h"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl/container:__pkg__",
+    ],
     deps = [
         "//absl/meta:type_traits",
         "@googletest//:gtest",
@@ -1019,6 +1040,9 @@
     hdrs = ["internal/unordered_set_lookup_test.h"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl/container:__pkg__",
+    ],
     deps = [
         ":hash_generator_testing",
         ":hash_policy_testing",
@@ -1032,6 +1056,9 @@
     hdrs = ["internal/unordered_set_modifiers_test.h"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl/container:__pkg__",
+    ],
     deps = [
         ":hash_generator_testing",
         ":hash_policy_testing",
@@ -1200,3 +1227,125 @@
         "@google_benchmark//:benchmark_main",
     ],
 )
+
+cc_library(
+    name = "heterogeneous_lookup_testing",
+    testonly = True,
+    hdrs = ["internal/heterogeneous_lookup_testing.h"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl/container:__pkg__",
+    ],
+    deps = [
+        "//absl/base:config",
+        "//absl/container:test_instance_tracker",
+        "@googletest//:gtest",
+    ],
+)
+
+cc_library(
+    name = "linked_hash_set",
+    hdrs = ["linked_hash_set.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":common",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/container:flat_hash_set",
+    ],
+)
+
+cc_test(
+    name = "linked_hash_set_test",
+    srcs = ["linked_hash_set_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":heterogeneous_lookup_testing",
+        ":linked_hash_set",
+        "//absl/base:config",
+        "//absl/container:hash_generator_testing",
+        "//absl/container:hash_policy_testing",
+        "//absl/container:test_instance_tracker",
+        "//absl/container:unordered_set_constructor_test",
+        "//absl/container:unordered_set_lookup_test",
+        "//absl/container:unordered_set_members_test",
+        "//absl/container:unordered_set_modifiers_test",
+        "//absl/strings:string_view",
+        "@googletest//:gtest",
+        "@googletest//:gtest_main",
+    ],
+)
+
+cc_binary(
+    name = "linked_hash_set_benchmark",
+    testonly = True,
+    srcs = ["linked_hash_set_benchmark.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    tags = ["benchmark"],
+    visibility = ["//visibility:private"],
+    deps = [
+        ":linked_hash_set",
+        "//absl/functional:function_ref",
+        "//absl/strings:str_format",
+        "//absl/strings:string_view",
+        "@google_benchmark//:benchmark_main",
+    ],
+)
+
+cc_library(
+    name = "linked_hash_map",
+    hdrs = ["linked_hash_map.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":common",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/base:throw_delegate",
+        "//absl/container:flat_hash_set",
+    ],
+)
+
+cc_test(
+    name = "linked_hash_map_test",
+    srcs = ["linked_hash_map_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":heterogeneous_lookup_testing",
+        ":linked_hash_map",
+        "//absl/base:config",
+        "//absl/base:exception_testing",
+        "//absl/container:hash_generator_testing",
+        "//absl/container:hash_policy_testing",
+        "//absl/container:test_instance_tracker",
+        "//absl/container:unordered_map_constructor_test",
+        "//absl/container:unordered_map_lookup_test",
+        "//absl/container:unordered_map_members_test",
+        "//absl/container:unordered_map_modifiers_test",
+        "//absl/strings:string_view",
+        "@googletest//:gtest",
+        "@googletest//:gtest_main",
+    ],
+)
+
+cc_binary(
+    name = "linked_hash_map_benchmark",
+    testonly = True,
+    srcs = ["linked_hash_map_benchmark.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    tags = ["benchmark"],
+    visibility = ["//visibility:private"],
+    deps = [
+        ":linked_hash_map",
+        "//absl/functional:function_ref",
+        "//absl/strings:str_format",
+        "//absl/strings:string_view",
+        "@google_benchmark//:benchmark_main",
+    ],
+)
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt
index adce9a9..f1ea9e2 100644
--- a/absl/container/CMakeLists.txt
+++ b/absl/container/CMakeLists.txt
@@ -349,7 +349,6 @@
     "flat_hash_set_test.cc"
   COPTS
     ${ABSL_TEST_COPTS}
-    "-DUNORDERED_SET_CXX17"
   DEPS
     absl::check
     absl::config
@@ -432,7 +431,6 @@
     "node_hash_set_test.cc"
   COPTS
     ${ABSL_TEST_COPTS}
-    "-DUNORDERED_SET_CXX17"
   DEPS
     absl::hash_generator_testing
     absl::hash_policy_testing
@@ -541,6 +539,7 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
+    absl::config
     absl::hash_policy_testing
     absl::memory
     absl::meta
@@ -950,6 +949,7 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
+    absl::config
     absl::hash_generator_testing
     absl::hash_policy_testing
     GTest::gmock
@@ -1009,6 +1009,7 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
+    absl::config
     absl::hash_generator_testing
     absl::hash_policy_testing
     GTest::gmock
@@ -1104,3 +1105,100 @@
     absl::hashtablez_sampler
     GTest::gmock_main
 )
+
+absl_cc_library(
+  NAME
+    heterogeneous_lookup_testing
+  HDRS
+    "internal/heterogeneous_lookup_testing.h"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::config
+    absl::test_instance_tracker
+    GTest::gmock
+)
+
+absl_cc_library(
+  NAME
+    linked_hash_set
+  HDRS
+    "linked_hash_set.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+     absl::container_common
+     absl::config
+     absl::core_headers
+     absl::flat_hash_set
+)
+
+absl_cc_test(
+  NAME
+    linked_hash_set_test
+  SRCS
+    "linked_hash_set_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::linked_hash_set
+    absl::config
+    absl::heterogeneous_lookup_testing
+    absl::hash_generator_testing
+    absl::hash_policy_testing
+    absl::string_view
+    absl::test_instance_tracker
+    absl::unordered_set_constructor_test
+    absl::unordered_set_lookup_test
+    absl::unordered_set_members_test
+    absl::unordered_set_modifiers_test
+    GTest::gmock_main
+)
+
+absl_cc_library(
+  NAME
+    linked_hash_map
+  HDRS
+    "linked_hash_map.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+     absl::container_common
+     absl::config
+     absl::core_headers
+     absl::flat_hash_set
+     absl::throw_delegate
+)
+
+absl_cc_test(
+  NAME
+    linked_hash_map_test
+  SRCS
+    "linked_hash_map_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::linked_hash_map
+    absl::config
+    absl::exception_testing
+    absl::heterogeneous_lookup_testing
+    absl::hash_generator_testing
+    absl::hash_policy_testing
+    absl::string_view
+    absl::test_instance_tracker
+    absl::unordered_set_constructor_test
+    absl::unordered_set_lookup_test
+    absl::unordered_set_members_test
+    absl::unordered_set_modifiers_test
+    GTest::gmock_main
+)
diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h
index 131f622..0746f72 100644
--- a/absl/container/btree_map.h
+++ b/absl/container/btree_map.h
@@ -57,18 +57,44 @@
 #ifndef ABSL_CONTAINER_BTREE_MAP_H_
 #define ABSL_CONTAINER_BTREE_MAP_H_
 
+#include <functional>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
 #include "absl/base/attributes.h"
 #include "absl/container/internal/btree.h"  // IWYU pragma: export
 #include "absl/container/internal/btree_container.h"  // IWYU pragma: export
+#include "absl/container/internal/common.h"
+#include "absl/container/internal/container_memory.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
 namespace container_internal {
 
+template <typename Key, typename Data, typename... Params>
+struct map_params_impl;
+
+template <typename Key, typename Data>
+struct btree_map_defaults {
+  using Compare = std::less<Key>;
+  using Alloc = std::allocator<std::pair<const Key, Data>>;
+  using TargetNodeSize = std::integral_constant<int, 256>;
+  using IsMulti = std::false_type;
+};
+
 template <typename Key, typename Data, typename Compare, typename Alloc,
           int TargetNodeSize, bool IsMulti>
-struct map_params;
+using map_params = typename ApplyWithoutDefaultSuffix<
+    map_params_impl,
+    TypeList<void, void, typename btree_map_defaults<Key, Data>::Compare,
+             typename btree_map_defaults<Key, Data>::Alloc,
+             typename btree_map_defaults<Key, Data>::TargetNodeSize,
+             typename btree_map_defaults<Key, Data>::IsMulti>,
+    TypeList<Key, Data, Compare, Alloc,
+             std::integral_constant<int, TargetNodeSize>,
+             std::integral_constant<bool, IsMulti>>>::type;
 
 }  // namespace container_internal
 
@@ -855,11 +881,20 @@
 
 // A parameters structure for holding the type parameters for a btree_map.
 // Compare and Alloc should be nothrow copy-constructible.
-template <typename Key, typename Data, typename Compare, typename Alloc,
-          int TargetNodeSize, bool IsMulti>
-struct map_params : common_params<Key, Compare, Alloc, TargetNodeSize, IsMulti,
-                                  /*IsMap=*/true, map_slot_policy<Key, Data>> {
-  using super_type = typename map_params::common_params;
+template <typename Key, typename Data, typename... Params>
+struct map_params_impl
+    : common_params<
+          Key,
+          GetFromListOr<typename btree_map_defaults<Key, Data>::Compare, 0,
+                        Params...>,
+          GetFromListOr<typename btree_map_defaults<Key, Data>::Alloc, 1,
+                        Params...>,
+          GetFromListOr<typename btree_map_defaults<Key, Data>::TargetNodeSize,
+                        2, Params...>::value,
+          GetFromListOr<typename btree_map_defaults<Key, Data>::IsMulti, 3,
+                        Params...>::value,
+          /*IsMap=*/true, map_slot_policy<Key, Data>> {
+  using super_type = typename map_params_impl::common_params;
   using mapped_type = Data;
   // This type allows us to move keys when it is safe to do so. It is safe
   // for maps in which value_type and mutable_value_type are layout compatible.
@@ -868,6 +903,21 @@
   using value_type = typename super_type::value_type;
   using init_type = typename super_type::init_type;
 
+  static_assert(
+      std::is_same_v<
+          map_params<
+              Key, Data,
+              GetFromListOr<typename btree_map_defaults<Key, Data>::Compare, 0,
+                            Params...>,
+              GetFromListOr<typename btree_map_defaults<Key, Data>::Alloc, 1,
+                            Params...>,
+              GetFromListOr<
+                  typename btree_map_defaults<Key, Data>::TargetNodeSize, 2,
+                  Params...>::value,
+              GetFromListOr<typename btree_map_defaults<Key, Data>::IsMulti, 3,
+                            Params...>::value>,
+          map_params_impl>);
+
   template <typename V>
   static auto key(const V &value ABSL_ATTRIBUTE_LIFETIME_BOUND)
       -> decltype((value.first)) {
diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h
index 44a39cf..991cb89 100644
--- a/absl/container/btree_set.h
+++ b/absl/container/btree_set.h
@@ -56,9 +56,15 @@
 #ifndef ABSL_CONTAINER_BTREE_SET_H_
 #define ABSL_CONTAINER_BTREE_SET_H_
 
+#include <functional>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
 #include "absl/base/attributes.h"
 #include "absl/container/internal/btree.h"  // IWYU pragma: export
 #include "absl/container/internal/btree_container.h"  // IWYU pragma: export
+#include "absl/container/internal/common.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -68,9 +74,27 @@
 template <typename Key>
 struct set_slot_policy;
 
+template <typename Key, typename...Params>
+struct set_params_impl;
+
+template <typename Key>
+struct btree_set_defaults {
+  using Compare = std::less<Key>;
+  using Alloc = std::allocator<Key>;
+  using TargetNodeSize = std::integral_constant<int, 256>;
+  using IsMulti = std::false_type;
+};
+
 template <typename Key, typename Compare, typename Alloc, int TargetNodeSize,
           bool IsMulti>
-struct set_params;
+using set_params = typename ApplyWithoutDefaultSuffix<
+    set_params_impl,
+    TypeList<void, typename btree_set_defaults<Key>::Compare,
+             typename btree_set_defaults<Key>::Alloc,
+             typename btree_set_defaults<Key>::TargetNodeSize,
+             typename btree_set_defaults<Key>::IsMulti>,
+    TypeList<Key, Compare, Alloc, std::integral_constant<int, TargetNodeSize>,
+             std::integral_constant<bool, IsMulti>>>::type;
 
 }  // namespace container_internal
 
@@ -803,12 +827,34 @@
 
 // A parameters structure for holding the type parameters for a btree_set.
 // Compare and Alloc should be nothrow copy-constructible.
-template <typename Key, typename Compare, typename Alloc, int TargetNodeSize,
-          bool IsMulti>
-struct set_params : common_params<Key, Compare, Alloc, TargetNodeSize, IsMulti,
-                                  /*IsMap=*/false, set_slot_policy<Key>> {
+template <typename Key, typename... Params>
+struct set_params_impl
+    : common_params<
+          Key,
+          GetFromListOr<typename btree_set_defaults<Key>::Compare, 0,
+                        Params...>,
+          GetFromListOr<typename btree_set_defaults<Key>::Alloc, 1, Params...>,
+          GetFromListOr<typename btree_set_defaults<Key>::TargetNodeSize, 2,
+                        Params...>::value,
+          GetFromListOr<typename btree_set_defaults<Key>::IsMulti, 3,
+                        Params...>::value,
+          /*IsMap=*/false, set_slot_policy<Key>> {
   using value_type = Key;
-  using slot_type = typename set_params::common_params::slot_type;
+  using slot_type = typename set_params_impl::common_params::slot_type;
+
+  static_assert(
+      std::is_same_v<
+          set_params<
+              Key,
+              GetFromListOr<typename btree_set_defaults<Key>::Compare, 0,
+                            Params...>,
+              GetFromListOr<typename btree_set_defaults<Key>::Alloc, 1,
+                            Params...>,
+              GetFromListOr<typename btree_set_defaults<Key>::TargetNodeSize, 2,
+                            Params...>::value,
+              GetFromListOr<typename btree_set_defaults<Key>::IsMulti, 3,
+                            Params...>::value>,
+          set_params_impl>);
 
   template <typename V>
   static const V &key(const V &value) {
diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc
index a6438f5..0cf3ed3 100644
--- a/absl/container/btree_test.cc
+++ b/absl/container/btree_test.cc
@@ -3544,6 +3544,109 @@
   TestBasicFunctionality(set_type());
 }
 
+// Alias whose only purpose is to have the same length as set_params for better
+// alignment in the test below.
+template <typename... T>
+using set_p_impl = set_params_impl<T...>;
+
+TEST(BtreeTest, SetParamsStripsDefaults) {
+  using K = int;
+  using DA = btree_set_defaults<int>::Compare;
+  using DB = btree_set_defaults<int>::Alloc;
+  using DC = btree_set_defaults<int>::TargetNodeSize;
+  using DD = btree_set_defaults<int>::IsMulti;
+
+  using XA = std::greater<int>;
+  struct XB {};
+  using XC = std::integral_constant<int, 100>;
+  using XD = std::true_type;
+
+  EXPECT_TRUE((std::is_same_v<set_params<K, XA, XB, XC{}, XD{}>,
+                              set_p_impl<K, XA, XB, XC, XD>>));
+  EXPECT_TRUE((std::is_same_v<set_params<K, XA, XB, XC{}, DD{}>,
+                              set_p_impl<K, XA, XB, XC>>));
+  EXPECT_TRUE((std::is_same_v<set_params<K, XA, XB, DC{}, XD{}>,
+                              set_p_impl<K, XA, XB, DC, XD>>));
+  EXPECT_TRUE((std::is_same_v<set_params<K, XA, XB, DC{}, DD{}>,
+                              set_p_impl<K, XA, XB>>));
+  EXPECT_TRUE((std::is_same_v<set_params<K, XA, DB, XC{}, XD{}>,
+                              set_p_impl<K, XA, DB, XC, XD>>));
+  EXPECT_TRUE((std::is_same_v<set_params<K, XA, DB, XC{}, DD{}>,
+                              set_p_impl<K, XA, DB, XC>>));
+  EXPECT_TRUE((std::is_same_v<set_params<K, XA, DB, DC{}, XD{}>,
+                              set_p_impl<K, XA, DB, DC, XD>>));
+  EXPECT_TRUE((std::is_same_v<set_params<K, XA, DB, DC{}, DD{}>,
+                              set_p_impl<K, XA>>));
+  EXPECT_TRUE((std::is_same_v<set_params<K, DA, XB, XC{}, XD{}>,
+                              set_p_impl<K, DA, XB, XC, XD>>));
+  EXPECT_TRUE((std::is_same_v<set_params<K, DA, XB, XC{}, DD{}>,
+                              set_p_impl<K, DA, XB, XC>>));
+  EXPECT_TRUE((std::is_same_v<set_params<K, DA, XB, DC{}, XD{}>,
+                              set_p_impl<K, DA, XB, DC, XD>>));
+  EXPECT_TRUE((std::is_same_v<set_params<K, DA, XB, DC{}, DD{}>,
+                              set_p_impl<K, DA, XB>>));
+  EXPECT_TRUE((std::is_same_v<set_params<K, DA, DB, XC{}, XD{}>,
+                              set_p_impl<K, DA, DB, XC, XD>>));
+  EXPECT_TRUE((std::is_same_v<set_params<K, DA, DB, XC{}, DD{}>,
+                              set_p_impl<K, DA, DB, XC>>));
+  EXPECT_TRUE((std::is_same_v<set_params<K, DA, DB, DC{}, XD{}>,
+                              set_p_impl<K, DA, DB, DC, XD>>));
+  EXPECT_TRUE((std::is_same_v<set_params<K, DA, DB, DC{}, DD{}>,
+                              set_p_impl<K>>));
+}
+
+// Alias whose only purpose is to have the same length as map_params for better
+// alignment in the test below.
+template <typename... T>
+using map_p_impl = map_params_impl<T...>;
+
+TEST(BtreeTest, MapParamsStripsDefaults) {
+  using K = int;
+  using V = double;
+  using DA = btree_map_defaults<int, double>::Compare;
+  using DB = btree_map_defaults<int, double>::Alloc;
+  using DC = btree_map_defaults<int, double>::TargetNodeSize;
+  using DD = btree_map_defaults<int, double>::IsMulti;
+
+  using XA = std::greater<int>;
+  struct XB {};
+  using XC = std::integral_constant<int, 100>;
+  using XD = std::true_type;
+
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, XB, XC{}, XD{}>,
+                              map_p_impl<K, V, XA, XB, XC, XD>>));
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, XB, XC{}, DD{}>,
+                              map_p_impl<K, V, XA, XB, XC>>));
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, XB, DC{}, XD{}>,
+                              map_p_impl<K, V, XA, XB, DC, XD>>));
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, XB, DC{}, DD{}>,
+                              map_p_impl<K, V, XA, XB>>));
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, DB, XC{}, XD{}>,
+                              map_p_impl<K, V, XA, DB, XC, XD>>));
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, DB, XC{}, DD{}>,
+                              map_p_impl<K, V, XA, DB, XC>>));
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, DB, DC{}, XD{}>,
+                              map_p_impl<K, V, XA, DB, DC, XD>>));
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, DB, DC{}, DD{}>,
+                              map_p_impl<K, V, XA>>));
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, XB, XC{}, XD{}>,
+                              map_p_impl<K, V, DA, XB, XC, XD>>));
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, XB, XC{}, DD{}>,
+                              map_p_impl<K, V, DA, XB, XC>>));
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, XB, DC{}, XD{}>,
+                              map_p_impl<K, V, DA, XB, DC, XD>>));
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, XB, DC{}, DD{}>,
+                              map_p_impl<K, V, DA, XB>>));
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, DB, XC{}, XD{}>,
+                              map_p_impl<K, V, DA, DB, XC, XD>>));
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, DB, XC{}, DD{}>,
+                              map_p_impl<K, V, DA, DB, XC>>));
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, DB, DC{}, XD{}>,
+                              map_p_impl<K, V, DA, DB, DC, XD>>));
+  EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, DB, DC{}, DD{}>,
+                              map_p_impl<K, V>>));
+}
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h
index 7d6fd21..7ce3353 100644
--- a/absl/container/flat_hash_map.h
+++ b/absl/container/flat_hash_map.h
@@ -127,13 +127,17 @@
 //   if (result != ducks.end()) {
 //     std::cout << "Result: " << result->second << std::endl;
 //   }
-template <class K, class V, class Hash = DefaultHashContainerHash<K>,
-          class Eq = DefaultHashContainerEq<K>,
-          class Allocator = std::allocator<std::pair<const K, V>>>
+template <
+    class K, class V,
+    class Hash =
+        typename container_internal::FlatHashMapPolicy<K, V>::DefaultHash,
+    class Eq = typename container_internal::FlatHashMapPolicy<K, V>::DefaultEq,
+    class Allocator =
+        typename container_internal::FlatHashMapPolicy<K, V>::DefaultAlloc>
 class ABSL_ATTRIBUTE_OWNER flat_hash_map
-    : public absl::container_internal::raw_hash_map<
+    : public absl::container_internal::InstantiateRawHashMap<
           absl::container_internal::FlatHashMapPolicy<K, V>, Hash, Eq,
-          Allocator> {
+          Allocator>::type {
   using Base = typename flat_hash_map::raw_hash_map;
 
  public:
@@ -637,6 +641,10 @@
   using mapped_type = V;
   using init_type = std::pair</*non const*/ key_type, mapped_type>;
 
+  using DefaultHash = DefaultHashContainerHash<K>;
+  using DefaultEq = DefaultHashContainerEq<K>;
+  using DefaultAlloc = std::allocator<std::pair<const K, V>>;
+
   template <class Allocator, class... Args>
   static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
     slot_policy::construct(alloc, slot, std::forward<Args>(args)...);
diff --git a/absl/container/flat_hash_map_test.cc b/absl/container/flat_hash_map_test.cc
index e1d9382..73f28c7 100644
--- a/absl/container/flat_hash_map_test.cc
+++ b/absl/container/flat_hash_map_test.cc
@@ -39,8 +39,7 @@
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
 namespace {
-using ::absl::container_internal::hash_internal::Enum;
-using ::absl::container_internal::hash_internal::EnumClass;
+
 using ::testing::_;
 using ::testing::IsEmpty;
 using ::testing::Pair;
diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h
index 95e4533..a469fa0 100644
--- a/absl/container/flat_hash_set.h
+++ b/absl/container/flat_hash_set.h
@@ -124,12 +124,16 @@
 //   if (ducks.contains("dewey")) {
 //     std::cout << "We found dewey!" << std::endl;
 //   }
-template <class T, class Hash = DefaultHashContainerHash<T>,
-          class Eq = DefaultHashContainerEq<T>,
-          class Allocator = std::allocator<T>>
+template <
+    class T,
+    class Hash = typename container_internal::FlatHashSetPolicy<T>::DefaultHash,
+    class Eq = typename container_internal::FlatHashSetPolicy<T>::DefaultEq,
+    class Allocator =
+        typename container_internal::FlatHashSetPolicy<T>::DefaultAlloc>
 class ABSL_ATTRIBUTE_OWNER flat_hash_set
-    : public absl::container_internal::raw_hash_set<
-          absl::container_internal::FlatHashSetPolicy<T>, Hash, Eq, Allocator> {
+    : public absl::container_internal::InstantiateRawHashSet<
+          absl::container_internal::FlatHashSetPolicy<T>, Hash, Eq,
+          Allocator>::type {
   using Base = typename flat_hash_set::raw_hash_set;
 
  public:
@@ -535,6 +539,10 @@
   using init_type = T;
   using constant_iterators = std::true_type;
 
+  using DefaultHash = DefaultHashContainerHash<T>;
+  using DefaultEq = DefaultHashContainerEq<T>;
+  using DefaultAlloc = std::allocator<T>;
+
   template <class Allocator, class... Args>
   static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
     absl::allocator_traits<Allocator>::construct(*alloc, slot,
diff --git a/absl/container/flat_hash_set_test.cc b/absl/container/flat_hash_set_test.cc
index ca069b4..9b6a6d1 100644
--- a/absl/container/flat_hash_set_test.cc
+++ b/absl/container/flat_hash_set_test.cc
@@ -43,8 +43,6 @@
 namespace container_internal {
 namespace {
 
-using ::absl::container_internal::hash_internal::Enum;
-using ::absl::container_internal::hash_internal::EnumClass;
 using ::testing::IsEmpty;
 using ::testing::Pointee;
 using ::testing::UnorderedElementsAre;
diff --git a/absl/container/internal/common.h b/absl/container/internal/common.h
index 5ef6c56..3e263a3 100644
--- a/absl/container/internal/common.h
+++ b/absl/container/internal/common.h
@@ -15,7 +15,10 @@
 #ifndef ABSL_CONTAINER_INTERNAL_COMMON_H_
 #define ABSL_CONTAINER_INTERNAL_COMMON_H_
 
+#include <algorithm>
 #include <cassert>
+#include <cstddef>
+#include <tuple>
 #include <type_traits>
 
 #include "absl/meta/type_traits.h"
@@ -243,6 +246,54 @@
   NodeType node;
 };
 
+// Utilities to strip redundant template parameters from the underlying
+// implementation types.
+// We use a variadic pack (ie Params...) to specify required prefix of types for
+// non-default types, and then we use GetFromListOr to select the provided types
+// or the default ones otherwise.
+//
+// These default types do not contribute information for debugging and just
+// bloat the binary.
+// Removing the redundant tail types reduces mangled names and stringified
+// function names like __PRETTY_FUNCTION__.
+//
+// How to use:
+//  1. Define a template with `typename ...Params`
+//  2. Instantiate it via `ApplyWithoutDefaultSuffix<>` to only pass the minimal
+//     set of types.
+//  3. Inside the template use `GetFromListOr` to map back from the existing
+//     `Params` list to the actual types, filling the gaps when types are
+//     missing.
+
+template <typename Or, size_t N, typename... Params>
+using GetFromListOr = std::tuple_element_t<(std::min)(N, sizeof...(Params)),
+                                           std::tuple<Params..., Or>>;
+
+template <typename... T>
+struct TypeList {
+  template <template <typename...> class Template>
+  using Apply = Template<T...>;
+};
+
+// Evaluate to `Template<TPrefix...>` where the last type in the list (if any)
+// is different from the corresponding one in the default list.
+// Eg
+//   ApplyWithoutDefaultSuffix<Template, TypeList<a, b, c>, TypeList<a, X, c>>
+// evaluates to
+//   Template<a, X>
+template <template <typename...> class Template, typename D, typename T,
+          typename L = TypeList<>, typename = void>
+struct ApplyWithoutDefaultSuffix {
+  using type = typename L::template Apply<Template>;
+};
+template <template <typename...> class Template, typename D, typename... Ds,
+          typename T, typename... Ts, typename... L>
+struct ApplyWithoutDefaultSuffix<
+    Template, TypeList<D, Ds...>, TypeList<T, Ts...>, TypeList<L...>,
+    std::enable_if_t<!std::is_same_v<TypeList<D, Ds...>, TypeList<T, Ts...>>>>
+    : ApplyWithoutDefaultSuffix<Template, TypeList<Ds...>, TypeList<Ts...>,
+                       TypeList<L..., T>> {};
+
 }  // namespace container_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/container/internal/hash_generator_testing.cc b/absl/container/internal/hash_generator_testing.cc
index be20e21..4ae58da 100644
--- a/absl/container/internal/hash_generator_testing.cc
+++ b/absl/container/internal/hash_generator_testing.cc
@@ -26,7 +26,6 @@
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
-namespace hash_internal {
 
 std::string Generator<std::string>::operator()() const {
   absl::InsecureBitGen gen;
@@ -50,7 +49,6 @@
   return res;
 }
 
-}  // namespace hash_internal
 }  // namespace container_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/container/internal/hash_generator_testing.h b/absl/container/internal/hash_generator_testing.h
index 14c878e..4c5d87b 100644
--- a/absl/container/internal/hash_generator_testing.h
+++ b/absl/container/internal/hash_generator_testing.h
@@ -31,6 +31,7 @@
 #include <utility>
 #include <vector>
 
+#include "absl/base/config.h"
 #include "absl/container/internal/hash_policy_testing.h"
 #include "absl/memory/memory.h"
 #include "absl/meta/type_traits.h"
@@ -40,7 +41,6 @@
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
-namespace hash_internal {
 namespace generator_internal {
 
 template <class Container, class = void>
@@ -165,7 +165,6 @@
   }
 };
 
-}  // namespace hash_internal
 }  // namespace container_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/container/internal/hash_policy_testing.h b/absl/container/internal/hash_policy_testing.h
index e9f5757..86ea96a 100644
--- a/absl/container/internal/hash_policy_testing.h
+++ b/absl/container/internal/hash_policy_testing.h
@@ -170,18 +170,4 @@
 ABSL_NAMESPACE_END
 }  // namespace absl
 
-// ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS is false for glibcxx versions
-// where the unordered containers are missing certain constructors that
-// take allocator arguments. This test is defined ad-hoc for the platforms
-// we care about (notably Crosstool 17) because libstdcxx's useless
-// versioning scheme precludes a more principled solution.
-// From GCC-4.9 Changelog: (src: https://gcc.gnu.org/gcc-4.9/changes.html)
-// "the unordered associative containers in <unordered_map> and <unordered_set>
-// meet the allocator-aware container requirements;"
-#if defined(__GLIBCXX__) && __GLIBCXX__ <= 20140425
-#define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 0
-#else
-#define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 1
-#endif
-
 #endif  // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_
diff --git a/absl/container/internal/heterogeneous_lookup_testing.h b/absl/container/internal/heterogeneous_lookup_testing.h
new file mode 100644
index 0000000..b8cfae3
--- /dev/null
+++ b/absl/container/internal/heterogeneous_lookup_testing.h
@@ -0,0 +1,80 @@
+// Copyright 2025 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_CONTAINER_INTERNAL_HETEROGENEOUS_LOOKUP_TESTING_H_
+#define ABSL_CONTAINER_INTERNAL_HETEROGENEOUS_LOOKUP_TESTING_H_
+
+#include <cstddef>
+#include <ostream>
+
+#include "gmock/gmock.h"
+#include "absl/base/config.h"
+#include "absl/container/internal/test_instance_tracker.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace container_internal {
+
+// An expensive class that is convertible to CheapType to demonstrate
+// heterogeneous lookups.
+class ExpensiveType : public absl::test_internal::CopyableMovableInstance {
+ public:
+  explicit ExpensiveType(int value)
+      : absl::test_internal::CopyableMovableInstance(value) {}
+
+  friend std::ostream& operator<<(std::ostream& os, const ExpensiveType& a) {
+    return os << a.value();
+  }
+};
+
+class CheapType {
+ public:
+  explicit CheapType(const int value) : value_(value) {}
+
+  explicit operator ExpensiveType() const { return ExpensiveType(value_); }
+
+  int value() const { return value_; }
+
+ private:
+  int value_;
+};
+
+struct HeterogeneousHash {
+  using is_transparent = void;
+  size_t operator()(const ExpensiveType& a) const { return a.value(); }
+  size_t operator()(const CheapType& a) const { return a.value(); }
+};
+
+struct HeterogeneousEqual {
+  using is_transparent = void;
+  bool operator()(const ExpensiveType& a, const ExpensiveType& b) const {
+    return a.value() == b.value();
+  }
+  bool operator()(const ExpensiveType& a, const CheapType& b) const {
+    return a.value() == b.value();
+  }
+  bool operator()(const CheapType& a, const ExpensiveType& b) const {
+    return a.value() == b.value();
+  }
+};
+
+MATCHER_P(HasExpensiveValue, n, "") {
+  return ::testing::ExplainMatchResult(n, arg.value(), result_listener);
+}
+
+}  // namespace container_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_CONTAINER_INTERNAL_HETEROGENEOUS_LOOKUP_TESTING_H_
diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h
index 85adf87..9338f16 100644
--- a/absl/container/internal/raw_hash_map.h
+++ b/absl/container/internal/raw_hash_map.h
@@ -31,8 +31,20 @@
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
 
-template <class Policy, class Hash, class Eq, class Alloc>
-class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc> {
+template <class Policy, class... Params>
+class raw_hash_map;
+
+template <typename Policy, typename Hash, typename Eq, typename Alloc>
+struct InstantiateRawHashMap {
+  using type = typename ApplyWithoutDefaultSuffix<
+      raw_hash_map,
+      TypeList<int, typename Policy::DefaultHash, typename Policy::DefaultEq,
+               typename Policy::DefaultAlloc>,
+      TypeList<Policy, Hash, Eq, Alloc>>::type;
+};
+
+template <class Policy, class... Params>
+class raw_hash_map : public raw_hash_set<Policy, Params...> {
   // P is Policy. It's passed as a template argument to support maps that have
   // incomplete types as values, as in unordered_map<K, IncompleteType>.
   // MappedReference<> may be a non-reference type.
@@ -45,6 +57,10 @@
   using MappedConstReference = decltype(P::value(
       std::addressof(std::declval<typename raw_hash_map::const_reference>())));
 
+  using Hash = typename raw_hash_map::raw_hash_set::hasher;
+  using Eq = typename raw_hash_map::raw_hash_set::key_equal;
+  using Alloc = typename raw_hash_map::raw_hash_set::allocator_type;
+
   template <class K>
   using key_arg =
       typename KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>::
@@ -327,6 +343,13 @@
   }
 
  private:
+  static_assert(
+      std::is_same_v<
+          typename InstantiateRawHashMap<Policy, Hash, Eq, Alloc>::type,
+          raw_hash_map>,
+      "Redundant template parameters were passed. Use InstantiateRawHashMap<> "
+      "instead");
+
   template <class K, class V>
   std::pair<iterator, bool> insert_or_assign_impl(K&& k, V&& v)
       ABSL_ATTRIBUTE_LIFETIME_BOUND {
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h
index 1d209e0..d307e2d 100644
--- a/absl/container/internal/raw_hash_set.h
+++ b/absl/container/internal/raw_hash_set.h
@@ -1138,7 +1138,7 @@
   HeapOrSoo heap_or_soo_;
 };
 
-template <class Policy, class Hash, class Eq, class Alloc>
+template <class Policy, class... Params>
 class raw_hash_set;
 
 // Returns the next valid capacity after `n`.
@@ -1760,32 +1760,62 @@
     Group::NonIterableBitMaskType mask_empty, FindInfo target_group,
     absl::FunctionRef<size_t(size_t)> recompute_hash);
 
+template <typename Policy, typename Hash, typename Eq, typename Alloc>
+struct InstantiateRawHashSet {
+  using type = typename ApplyWithoutDefaultSuffix<
+      raw_hash_set,
+      TypeList<void, typename Policy::DefaultHash, typename Policy::DefaultEq,
+               typename Policy::DefaultAlloc>,
+      TypeList<Policy, Hash, Eq, Alloc>>::type;
+};
+
 // A SwissTable.
 //
 // Policy: a policy defines how to perform different operations on
 // the slots of the hashtable (see hash_policy_traits.h for the full interface
 // of policy).
 //
+// Params...: a variadic list of parameters that allows us to omit default
+//            types. This reduces the mangled name of the class and the size of
+//            debug strings like __PRETTY_FUNCTION__. Default types do not give
+//            any new information.
+//
 // Hash: a (possibly polymorphic) functor that hashes keys of the hashtable. The
 // functor should accept a key and return size_t as hash. For best performance
 // it is important that the hash function provides high entropy across all bits
 // of the hash.
+// This is the first element in `Params...` if it exists, or Policy::DefaultHash
+// otherwise.
 //
 // Eq: a (possibly polymorphic) functor that compares two keys for equality. It
 // should accept two (of possibly different type) keys and return a bool: true
 // if they are equal, false if they are not. If two keys compare equal, then
 // their hash values as defined by Hash MUST be equal.
+// This is the second element in `Params...` if it exists, or Policy::DefaultEq
+// otherwise.
 //
 // Allocator: an Allocator
 // [https://en.cppreference.com/w/cpp/named_req/Allocator] with which
 // the storage of the hashtable will be allocated and the elements will be
 // constructed and destroyed.
-template <class Policy, class Hash, class Eq, class Alloc>
+// This is the third element in `Params...` if it exists, or
+// Policy::DefaultAlloc otherwise.
+template <class Policy, class... Params>
 class raw_hash_set {
   using PolicyTraits = hash_policy_traits<Policy>;
+  using Hash = GetFromListOr<typename Policy::DefaultHash, 0, Params...>;
+  using Eq = GetFromListOr<typename Policy::DefaultEq, 1, Params...>;
+  using Alloc = GetFromListOr<typename Policy::DefaultAlloc, 2, Params...>;
   using KeyArgImpl =
       KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>;
 
+  static_assert(
+      std::is_same_v<
+          typename InstantiateRawHashSet<Policy, Hash, Eq, Alloc>::type,
+          raw_hash_set>,
+      "Redundant template parameters were passed. Use InstantiateRawHashSet<> "
+      "instead");
+
  public:
   using init_type = typename PolicyTraits::init_type;
   using key_type = typename PolicyTraits::key_type;
@@ -2640,8 +2670,11 @@
 
   // Moves elements from `src` into `this`.
   // If the element already exists in `this`, it is left unmodified in `src`.
-  template <typename H, typename E>
-  void merge(raw_hash_set<Policy, H, E, Alloc>& src) {  // NOLINT
+  template <
+      typename... Params2,
+      typename = std::enable_if_t<std::is_same_v<
+          Alloc, typename raw_hash_set<Policy, Params2...>::allocator_type>>>
+  void merge(raw_hash_set<Policy, Params2...>& src) {  // NOLINT
     AssertNotDebugCapacity();
     src.AssertNotDebugCapacity();
     assert(this != &src);
@@ -2665,8 +2698,11 @@
     }
   }
 
-  template <typename H, typename E>
-  void merge(raw_hash_set<Policy, H, E, Alloc>&& src) {
+  template <
+      typename... Params2,
+      typename = std::enable_if_t<std::is_same_v<
+          Alloc, typename raw_hash_set<Policy, Params2...>::allocator_type>>>
+  void merge(raw_hash_set<Policy, Params2...>&& src) {  // NOLINT
     merge(src);
   }
 
@@ -3622,19 +3658,19 @@
 };
 
 // Erases all elements that satisfy the predicate `pred` from the container `c`.
-template <typename P, typename H, typename E, typename A, typename Predicate>
-typename raw_hash_set<P, H, E, A>::size_type EraseIf(
-    Predicate& pred, raw_hash_set<P, H, E, A>* c) {
+template <typename P, typename... Params, typename Predicate>
+typename raw_hash_set<P, Params...>::size_type EraseIf(
+    Predicate& pred, raw_hash_set<P, Params...>* c) {
   return HashtableFreeFunctionsAccess::EraseIf(pred, c);
 }
 
 // Calls `cb` for all elements in the container `c`.
-template <typename P, typename H, typename E, typename A, typename Callback>
-void ForEach(Callback& cb, raw_hash_set<P, H, E, A>* c) {
+template <typename P, typename... Params, typename Callback>
+void ForEach(Callback& cb, raw_hash_set<P, Params...>* c) {
   return HashtableFreeFunctionsAccess::ForEach(cb, c);
 }
-template <typename P, typename H, typename E, typename A, typename Callback>
-void ForEach(Callback& cb, const raw_hash_set<P, H, E, A>* c) {
+template <typename P, typename... Params, typename Callback>
+void ForEach(Callback& cb, const raw_hash_set<P, Params...>* c) {
   return HashtableFreeFunctionsAccess::ForEach(cb, c);
 }
 
diff --git a/absl/container/internal/raw_hash_set_allocator_test.cc b/absl/container/internal/raw_hash_set_allocator_test.cc
index b268d9e..c4bff60 100644
--- a/absl/container/internal/raw_hash_set_allocator_test.cc
+++ b/absl/container/internal/raw_hash_set_allocator_test.cc
@@ -142,6 +142,10 @@
   using init_type = Tracked<int32_t>;
   using key_type = int32_t;
 
+  using DefaultHash = void;
+  using DefaultEq = void;
+  using DefaultAlloc = void;
+
   template <class allocator_type, class... Args>
   static void construct(allocator_type* alloc, slot_type* slot,
                         Args&&... args) {
diff --git a/absl/container/internal/raw_hash_set_benchmark.cc b/absl/container/internal/raw_hash_set_benchmark.cc
index f3e32fd..589f4f5 100644
--- a/absl/container/internal/raw_hash_set_benchmark.cc
+++ b/absl/container/internal/raw_hash_set_benchmark.cc
@@ -51,6 +51,10 @@
   using key_type = int64_t;
   using init_type = int64_t;
 
+  using DefaultHash = void;
+  using DefaultEq = void;
+  using DefaultAlloc = void;
+
   static void construct(void*, int64_t* slot, int64_t v) { *slot = v; }
   static void destroy(void*, int64_t*) {}
   static void transfer(void*, int64_t* new_slot, int64_t* old_slot) {
@@ -97,6 +101,10 @@
   using key_type = std::string;
   using init_type = std::pair<std::string, std::string>;
 
+  using DefaultHash = void;
+  using DefaultEq = void;
+  using DefaultAlloc = void;
+
   template <class allocator_type, class... Args>
   static void construct(allocator_type* alloc, slot_type* slot, Args... args) {
     std::allocator_traits<allocator_type>::construct(
diff --git a/absl/container/internal/raw_hash_set_probe_benchmark.cc b/absl/container/internal/raw_hash_set_probe_benchmark.cc
index 2712160..a09f7d9 100644
--- a/absl/container/internal/raw_hash_set_probe_benchmark.cc
+++ b/absl/container/internal/raw_hash_set_probe_benchmark.cc
@@ -58,6 +58,10 @@
   using key_type = T;
   using init_type = T;
 
+  using DefaultHash = void;
+  using DefaultEq = void;
+  using DefaultAlloc = void;
+
   template <class allocator_type, class Arg>
   static void construct(allocator_type* alloc, slot_type* slot,
                         const Arg& arg) {
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc
index e8de41a..0b5ad8f 100644
--- a/absl/container/internal/raw_hash_set_test.cc
+++ b/absl/container/internal/raw_hash_set_test.cc
@@ -382,6 +382,10 @@
   using key_type = T;
   using init_type = T;
 
+  using DefaultHash = hash_default_hash<T>;
+  using DefaultEq = std::equal_to<T>;
+  using DefaultAlloc = std::allocator<T>;
+
   template <class Allocator, class... Args>
   static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
     absl::allocator_traits<Allocator>::construct(*alloc, slot,
@@ -529,6 +533,10 @@
   using key_type = std::string;
   using init_type = std::pair<std::string, std::string>;
 
+  using DefaultHash = void;
+  using DefaultEq = void;
+  using DefaultAlloc = void;
+
   template <class allocator_type, class... Args>
   static void construct(allocator_type* alloc, slot_type* slot, Args... args) {
     std::allocator_traits<allocator_type>::construct(
@@ -581,9 +589,9 @@
 
 template <typename T, bool kTransferable = false, bool kSoo = false,
           class Alloc = std::allocator<T>>
-struct ValueTable
-    : raw_hash_set<ValuePolicy<T, kTransferable, kSoo>, hash_default_hash<T>,
-                   std::equal_to<T>, Alloc> {
+struct ValueTable : InstantiateRawHashSet<ValuePolicy<T, kTransferable, kSoo>,
+                                          hash_default_hash<T>,
+                                          std::equal_to<T>, Alloc>::type {
   using Base = typename ValueTable::raw_hash_set;
   using Base::Base;
 };
@@ -782,6 +790,34 @@
                        std::equal_to<absl::string_view>, std::allocator<int>>));
 }
 
+TEST(InstantiateRawHashSetTest, VerifyTypes) {
+  using P = ValuePolicy<int>;
+  using DA = typename P::DefaultHash;
+  using DB = typename P::DefaultEq;
+  using DC = typename P::DefaultAlloc;
+
+  struct A {};
+  struct B {};
+  struct C {};
+
+  EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, A, B, C>::type,
+                              raw_hash_set<P, A, B, C>>));
+  EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, A, B, DC>::type,
+                              raw_hash_set<P, A, B>>));
+  EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, A, DB, C>::type,
+                              raw_hash_set<P, A, DB, C>>));
+  EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, A, DB, DC>::type,
+                              raw_hash_set<P, A>>));
+  EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, DA, B, C>::type,
+                              raw_hash_set<P, DA, B, C>>));
+  EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, DA, B, DC>::type,
+                              raw_hash_set<P, DA, B>>));
+  EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, DA, DB, C>::type,
+                              raw_hash_set<P, DA, DB, C>>));
+  EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, DA, DB, DC>::type,
+                              raw_hash_set<P>>));
+}
+
 template <class TableType>
 class SooTest : public testing::Test {};
 
@@ -1144,6 +1180,10 @@
   using key_type = DecomposeType;
   using init_type = DecomposeType;
 
+  using DefaultHash = void;
+  using DefaultEq = void;
+  using DefaultAlloc = void;
+
   template <typename T>
   static void construct(void*, DecomposeType* slot, T&& v) {
     ::new (slot) DecomposeType(std::forward<T>(v));
@@ -2430,8 +2470,9 @@
     }
   };
 
-  struct Table : raw_hash_set<ValuePolicy<Value>, H, std::equal_to<Value>,
-                              std::allocator<Value>> {
+  struct Table
+      : InstantiateRawHashSet<ValuePolicy<Value>, H, std::equal_to<Value>,
+                              std::allocator<Value>>::type {
     using Base = typename Table::raw_hash_set;
     using Base::Base;
   };
diff --git a/absl/container/internal/unordered_map_constructor_test.h b/absl/container/internal/unordered_map_constructor_test.h
index 7e84dc2..1076aea 100644
--- a/absl/container/internal/unordered_map_constructor_test.h
+++ b/absl/container/internal/unordered_map_constructor_test.h
@@ -21,6 +21,7 @@
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/base/config.h"
 #include "absl/container/internal/hash_generator_testing.h"
 #include "absl/container/internal/hash_policy_testing.h"
 
@@ -85,28 +86,8 @@
   EXPECT_GE(m.bucket_count(), 123);
 }
 
-template <typename T>
-struct is_std_unordered_map : std::false_type {};
-
-template <typename... T>
-struct is_std_unordered_map<std::unordered_map<T...>> : std::true_type {};
-
-#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17)
-using has_cxx14_std_apis = std::true_type;
-#else
-using has_cxx14_std_apis = std::false_type;
-#endif
-
-template <typename T>
-using expect_cxx14_apis =
-    absl::disjunction<absl::negation<is_std_unordered_map<T>>,
-                      has_cxx14_std_apis>;
-
 template <typename TypeParam>
-void BucketCountAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void BucketCountAllocTest(std::true_type) {
+void BucketCountAllocTest() {
   using A = typename TypeParam::allocator_type;
   A alloc(0);
   TypeParam m(123, alloc);
@@ -117,14 +98,11 @@
 }
 
 TYPED_TEST_P(ConstructorTest, BucketCountAlloc) {
-  BucketCountAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+  BucketCountAllocTest<TypeParam>();
 }
 
 template <typename TypeParam>
-void BucketCountHashAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void BucketCountHashAllocTest(std::true_type) {
+void BucketCountHashAllocTest() {
   using H = typename TypeParam::hasher;
   using A = typename TypeParam::allocator_type;
   H hasher;
@@ -138,25 +116,11 @@
 }
 
 TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) {
-  BucketCountHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+  BucketCountHashAllocTest<TypeParam>();
 }
 
-#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
-using has_alloc_std_constructors = std::true_type;
-#else
-using has_alloc_std_constructors = std::false_type;
-#endif
-
-template <typename T>
-using expect_alloc_constructors =
-    absl::disjunction<absl::negation<is_std_unordered_map<T>>,
-                      has_alloc_std_constructors>;
-
 template <typename TypeParam>
-void AllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void AllocTest(std::true_type) {
+void AllocTest() {
   using A = typename TypeParam::allocator_type;
   A alloc(0);
   TypeParam m(alloc);
@@ -165,12 +129,10 @@
   EXPECT_THAT(m, ::testing::UnorderedElementsAre());
 }
 
-TYPED_TEST_P(ConstructorTest, Alloc) {
-  AllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
-}
+TYPED_TEST_P(ConstructorTest, Alloc) { AllocTest<TypeParam>(); }
 
 TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
   using A = typename TypeParam::allocator_type;
@@ -178,8 +140,7 @@
   E equal;
   A alloc(0);
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::UniqueGenerator<T>());
+  std::generate_n(std::back_inserter(values), 10, UniqueGenerator<T>());
   TypeParam m(values.begin(), values.end(), 123, hasher, equal, alloc);
   EXPECT_EQ(m.hash_function(), hasher);
   EXPECT_EQ(m.key_eq(), equal);
@@ -189,16 +150,12 @@
 }
 
 template <typename TypeParam>
-void InputIteratorBucketAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InputIteratorBucketAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+void InputIteratorBucketAllocTest() {
+  using T = GeneratedType<TypeParam>;
   using A = typename TypeParam::allocator_type;
   A alloc(0);
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::UniqueGenerator<T>());
+  std::generate_n(std::back_inserter(values), 10, UniqueGenerator<T>());
   TypeParam m(values.begin(), values.end(), 123, alloc);
   EXPECT_EQ(m.get_allocator(), alloc);
   EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
@@ -206,22 +163,18 @@
 }
 
 TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) {
-  InputIteratorBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+  InputIteratorBucketAllocTest<TypeParam>();
 }
 
 template <typename TypeParam>
-void InputIteratorBucketHashAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InputIteratorBucketHashAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+void InputIteratorBucketHashAllocTest() {
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using A = typename TypeParam::allocator_type;
   H hasher;
   A alloc(0);
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::UniqueGenerator<T>());
+  std::generate_n(std::back_inserter(values), 10, UniqueGenerator<T>());
   TypeParam m(values.begin(), values.end(), 123, hasher, alloc);
   EXPECT_EQ(m.hash_function(), hasher);
   EXPECT_EQ(m.get_allocator(), alloc);
@@ -230,18 +183,18 @@
 }
 
 TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) {
-  InputIteratorBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+  InputIteratorBucketHashAllocTest<TypeParam>();
 }
 
 TYPED_TEST_P(ConstructorTest, CopyConstructor) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
   using A = typename TypeParam::allocator_type;
   H hasher;
   E equal;
   A alloc(0);
-  hash_internal::UniqueGenerator<T> gen;
+  UniqueGenerator<T> gen;
   TypeParam m(123, hasher, equal, alloc);
   for (size_t i = 0; i != 10; ++i) m.insert(gen());
   TypeParam n(m);
@@ -252,18 +205,15 @@
 }
 
 template <typename TypeParam>
-void CopyConstructorAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void CopyConstructorAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+void CopyConstructorAllocTest() {
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
   using A = typename TypeParam::allocator_type;
   H hasher;
   E equal;
   A alloc(0);
-  hash_internal::UniqueGenerator<T> gen;
+  UniqueGenerator<T> gen;
   TypeParam m(123, hasher, equal, alloc);
   for (size_t i = 0; i != 10; ++i) m.insert(gen());
   TypeParam n(m, A(11));
@@ -274,20 +224,20 @@
 }
 
 TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) {
-  CopyConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
+  CopyConstructorAllocTest<TypeParam>();
 }
 
 // TODO(alkis): Test non-propagating allocators on copy constructors.
 
 TYPED_TEST_P(ConstructorTest, MoveConstructor) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
   using A = typename TypeParam::allocator_type;
   H hasher;
   E equal;
   A alloc(0);
-  hash_internal::UniqueGenerator<T> gen;
+  UniqueGenerator<T> gen;
   TypeParam m(123, hasher, equal, alloc);
   for (size_t i = 0; i != 10; ++i) m.insert(gen());
   TypeParam t(m);
@@ -299,18 +249,15 @@
 }
 
 template <typename TypeParam>
-void MoveConstructorAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void MoveConstructorAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+void MoveConstructorAllocTest() {
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
   using A = typename TypeParam::allocator_type;
   H hasher;
   E equal;
   A alloc(0);
-  hash_internal::UniqueGenerator<T> gen;
+  UniqueGenerator<T> gen;
   TypeParam m(123, hasher, equal, alloc);
   for (size_t i = 0; i != 10; ++i) m.insert(gen());
   TypeParam t(m);
@@ -322,14 +269,14 @@
 }
 
 TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) {
-  MoveConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
+  MoveConstructorAllocTest<TypeParam>();
 }
 
 // TODO(alkis): Test non-propagating allocators on move constructors.
 
 TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::UniqueGenerator<T> gen;
+  using T = GeneratedType<TypeParam>;
+  UniqueGenerator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
@@ -346,13 +293,10 @@
 }
 
 template <typename TypeParam>
-void InitializerListBucketAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InitializerListBucketAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+void InitializerListBucketAllocTest() {
+  using T = GeneratedType<TypeParam>;
   using A = typename TypeParam::allocator_type;
-  hash_internal::UniqueGenerator<T> gen;
+  UniqueGenerator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   A alloc(0);
   TypeParam m(values, 123, alloc);
@@ -362,20 +306,17 @@
 }
 
 TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) {
-  InitializerListBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+  InitializerListBucketAllocTest<TypeParam>();
 }
 
 template <typename TypeParam>
-void InitializerListBucketHashAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InitializerListBucketHashAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+void InitializerListBucketHashAllocTest() {
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using A = typename TypeParam::allocator_type;
   H hasher;
   A alloc(0);
-  hash_internal::UniqueGenerator<T> gen;
+  UniqueGenerator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   TypeParam m(values, 123, hasher, alloc);
   EXPECT_EQ(m.hash_function(), hasher);
@@ -385,18 +326,18 @@
 }
 
 TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) {
-  InitializerListBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+  InitializerListBucketHashAllocTest<TypeParam>();
 }
 
 TYPED_TEST_P(ConstructorTest, Assignment) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
   using A = typename TypeParam::allocator_type;
   H hasher;
   E equal;
   A alloc(0);
-  hash_internal::UniqueGenerator<T> gen;
+  UniqueGenerator<T> gen;
   TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
   TypeParam n;
   n = m;
@@ -409,14 +350,14 @@
 // (it depends on traits).
 
 TYPED_TEST_P(ConstructorTest, MoveAssignment) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
   using A = typename TypeParam::allocator_type;
   H hasher;
   E equal;
   A alloc(0);
-  hash_internal::UniqueGenerator<T> gen;
+  UniqueGenerator<T> gen;
   TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
   TypeParam t(m);
   TypeParam n;
@@ -427,8 +368,8 @@
 }
 
 TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::UniqueGenerator<T> gen;
+  using T = GeneratedType<TypeParam>;
+  UniqueGenerator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   TypeParam m;
   m = values;
@@ -436,8 +377,8 @@
 }
 
 TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::UniqueGenerator<T> gen;
+  using T = GeneratedType<TypeParam>;
+  UniqueGenerator<T> gen;
   TypeParam m({gen(), gen(), gen()});
   TypeParam n({gen()});
   n = m;
@@ -445,8 +386,8 @@
 }
 
 TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::UniqueGenerator<T> gen;
+  using T = GeneratedType<TypeParam>;
+  UniqueGenerator<T> gen;
   TypeParam m({gen(), gen(), gen()});
   TypeParam t(m);
   TypeParam n({gen()});
@@ -455,8 +396,8 @@
 }
 
 TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::UniqueGenerator<T> gen;
+  using T = GeneratedType<TypeParam>;
+  UniqueGenerator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   TypeParam m;
   m = values;
@@ -464,8 +405,8 @@
 }
 
 TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::UniqueGenerator<T> gen;
+  using T = GeneratedType<TypeParam>;
+  UniqueGenerator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   TypeParam m(values);
   m = *&m;  // Avoid -Wself-assign
diff --git a/absl/container/internal/unordered_map_lookup_test.h b/absl/container/internal/unordered_map_lookup_test.h
index 3713cd9..ba037c0 100644
--- a/absl/container/internal/unordered_map_lookup_test.h
+++ b/absl/container/internal/unordered_map_lookup_test.h
@@ -30,10 +30,9 @@
 TYPED_TEST_SUITE_P(LookupTest);
 
 TYPED_TEST_P(LookupTest, At) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m(values.begin(), values.end());
   for (const auto& p : values) {
     const auto& val = m.at(p.first);
@@ -42,11 +41,10 @@
 }
 
 TYPED_TEST_P(LookupTest, OperatorBracket) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using V = typename TypeParam::mapped_type;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m;
   for (const auto& p : values) {
     auto& val = m[p.first];
@@ -58,10 +56,9 @@
 }
 
 TYPED_TEST_P(LookupTest, Count) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m;
   for (const auto& p : values)
     EXPECT_EQ(0, m.count(p.first)) << ::testing::PrintToString(p.first);
@@ -72,10 +69,9 @@
 
 TYPED_TEST_P(LookupTest, Find) {
   using std::get;
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m;
   for (const auto& p : values)
     EXPECT_TRUE(m.end() == m.find(p.first))
@@ -90,10 +86,9 @@
 
 TYPED_TEST_P(LookupTest, EqualRange) {
   using std::get;
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m;
   for (const auto& p : values) {
     auto r = m.equal_range(p.first);
diff --git a/absl/container/internal/unordered_map_members_test.h b/absl/container/internal/unordered_map_members_test.h
index 7d48cdb..e9f4979 100644
--- a/absl/container/internal/unordered_map_members_test.h
+++ b/absl/container/internal/unordered_map_members_test.h
@@ -36,10 +36,10 @@
   EXPECT_TRUE((std::is_same<std::pair<const typename TypeParam::key_type,
                                       typename TypeParam::mapped_type>,
                             typename TypeParam::value_type>()));
-  EXPECT_TRUE((absl::conjunction<
-               absl::negation<std::is_signed<typename TypeParam::size_type>>,
+  EXPECT_TRUE((std::conjunction<
+               std::negation<std::is_signed<typename TypeParam::size_type>>,
                std::is_integral<typename TypeParam::size_type>>()));
-  EXPECT_TRUE((absl::conjunction<
+  EXPECT_TRUE((std::conjunction<
                std::is_signed<typename TypeParam::difference_type>,
                std::is_integral<typename TypeParam::difference_type>>()));
   EXPECT_TRUE((std::is_convertible<
diff --git a/absl/container/internal/unordered_map_modifiers_test.h b/absl/container/internal/unordered_map_modifiers_test.h
index 4d9ab30..a0b01a0 100644
--- a/absl/container/internal/unordered_map_modifiers_test.h
+++ b/absl/container/internal/unordered_map_modifiers_test.h
@@ -32,10 +32,9 @@
 TYPED_TEST_SUITE_P(ModifiersTest);
 
 TYPED_TEST_P(ModifiersTest, Clear) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m(values.begin(), values.end());
   ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
   m.clear();
@@ -44,63 +43,61 @@
 }
 
 TYPED_TEST_P(ModifiersTest, Insert) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using V = typename TypeParam::mapped_type;
-  T val = hash_internal::Generator<T>()();
+  T val = Generator<T>()();
   TypeParam m;
   auto p = m.insert(val);
   EXPECT_TRUE(p.second);
   EXPECT_EQ(val, *p.first);
-  T val2 = {val.first, hash_internal::Generator<V>()()};
+  T val2 = {val.first, Generator<V>()()};
   p = m.insert(val2);
   EXPECT_FALSE(p.second);
   EXPECT_EQ(val, *p.first);
 }
 
 TYPED_TEST_P(ModifiersTest, InsertHint) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using V = typename TypeParam::mapped_type;
-  T val = hash_internal::Generator<T>()();
+  T val = Generator<T>()();
   TypeParam m;
   auto it = m.insert(m.end(), val);
   EXPECT_TRUE(it != m.end());
   EXPECT_EQ(val, *it);
-  T val2 = {val.first, hash_internal::Generator<V>()()};
+  T val2 = {val.first, Generator<V>()()};
   it = m.insert(it, val2);
   EXPECT_TRUE(it != m.end());
   EXPECT_EQ(val, *it);
 }
 
 TYPED_TEST_P(ModifiersTest, InsertRange) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m;
   m.insert(values.begin(), values.end());
   ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
 }
 
 TYPED_TEST_P(ModifiersTest, InsertWithinCapacity) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using V = typename TypeParam::mapped_type;
-  T val = hash_internal::Generator<T>()();
+  T val = Generator<T>()();
   TypeParam m;
   m.reserve(10);
   const size_t original_capacity = m.bucket_count();
   m.insert(val);
   EXPECT_EQ(m.bucket_count(), original_capacity);
-  T val2 = {val.first, hash_internal::Generator<V>()()};
+  T val2 = {val.first, Generator<V>()()};
   m.insert(val2);
   EXPECT_EQ(m.bucket_count(), original_capacity);
 }
 
 TYPED_TEST_P(ModifiersTest, InsertRangeWithinCapacity) {
 #if !defined(__GLIBCXX__)
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> base_values;
-  std::generate_n(std::back_inserter(base_values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(base_values), 10, Generator<T>());
   std::vector<T> values;
   while (values.size() != 100) {
     std::copy_n(base_values.begin(), 10, std::back_inserter(values));
@@ -114,106 +111,98 @@
 }
 
 TYPED_TEST_P(ModifiersTest, InsertOrAssign) {
-#ifdef UNORDERED_MAP_CXX17
   using std::get;
   using K = typename TypeParam::key_type;
   using V = typename TypeParam::mapped_type;
-  K k = hash_internal::Generator<K>()();
-  V val = hash_internal::Generator<V>()();
+  K k = Generator<K>()();
+  V val = Generator<V>()();
   TypeParam m;
   auto p = m.insert_or_assign(k, val);
   EXPECT_TRUE(p.second);
   EXPECT_EQ(k, get<0>(*p.first));
   EXPECT_EQ(val, get<1>(*p.first));
-  V val2 = hash_internal::Generator<V>()();
+  V val2 = Generator<V>()();
   p = m.insert_or_assign(k, val2);
   EXPECT_FALSE(p.second);
   EXPECT_EQ(k, get<0>(*p.first));
   EXPECT_EQ(val2, get<1>(*p.first));
-#endif
 }
 
 TYPED_TEST_P(ModifiersTest, InsertOrAssignHint) {
-#ifdef UNORDERED_MAP_CXX17
   using std::get;
   using K = typename TypeParam::key_type;
   using V = typename TypeParam::mapped_type;
-  K k = hash_internal::Generator<K>()();
-  V val = hash_internal::Generator<V>()();
+  K k = Generator<K>()();
+  V val = Generator<V>()();
   TypeParam m;
   auto it = m.insert_or_assign(m.end(), k, val);
   EXPECT_TRUE(it != m.end());
   EXPECT_EQ(k, get<0>(*it));
   EXPECT_EQ(val, get<1>(*it));
-  V val2 = hash_internal::Generator<V>()();
+  V val2 = Generator<V>()();
   it = m.insert_or_assign(it, k, val2);
   EXPECT_EQ(k, get<0>(*it));
   EXPECT_EQ(val2, get<1>(*it));
-#endif
 }
 
 TYPED_TEST_P(ModifiersTest, Emplace) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using V = typename TypeParam::mapped_type;
-  T val = hash_internal::Generator<T>()();
+  T val = Generator<T>()();
   TypeParam m;
   // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps
   // with test traits/policy.
   auto p = m.emplace(val);
   EXPECT_TRUE(p.second);
   EXPECT_EQ(val, *p.first);
-  T val2 = {val.first, hash_internal::Generator<V>()()};
+  T val2 = {val.first, Generator<V>()()};
   p = m.emplace(val2);
   EXPECT_FALSE(p.second);
   EXPECT_EQ(val, *p.first);
 }
 
 TYPED_TEST_P(ModifiersTest, EmplaceHint) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using V = typename TypeParam::mapped_type;
-  T val = hash_internal::Generator<T>()();
+  T val = Generator<T>()();
   TypeParam m;
   // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps
   // with test traits/policy.
   auto it = m.emplace_hint(m.end(), val);
   EXPECT_EQ(val, *it);
-  T val2 = {val.first, hash_internal::Generator<V>()()};
+  T val2 = {val.first, Generator<V>()()};
   it = m.emplace_hint(it, val2);
   EXPECT_EQ(val, *it);
 }
 
 TYPED_TEST_P(ModifiersTest, TryEmplace) {
-#ifdef UNORDERED_MAP_CXX17
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using V = typename TypeParam::mapped_type;
-  T val = hash_internal::Generator<T>()();
+  T val = Generator<T>()();
   TypeParam m;
   // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps
   // with test traits/policy.
   auto p = m.try_emplace(val.first, val.second);
   EXPECT_TRUE(p.second);
   EXPECT_EQ(val, *p.first);
-  T val2 = {val.first, hash_internal::Generator<V>()()};
+  T val2 = {val.first, Generator<V>()()};
   p = m.try_emplace(val2.first, val2.second);
   EXPECT_FALSE(p.second);
   EXPECT_EQ(val, *p.first);
-#endif
 }
 
 TYPED_TEST_P(ModifiersTest, TryEmplaceHint) {
-#ifdef UNORDERED_MAP_CXX17
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using V = typename TypeParam::mapped_type;
-  T val = hash_internal::Generator<T>()();
+  T val = Generator<T>()();
   TypeParam m;
   // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps
   // with test traits/policy.
   auto it = m.try_emplace(m.end(), val.first, val.second);
   EXPECT_EQ(val, *it);
-  T val2 = {val.first, hash_internal::Generator<V>()()};
+  T val2 = {val.first, Generator<V>()()};
   it = m.try_emplace(it, val2.first, val2.second);
   EXPECT_EQ(val, *it);
-#endif
 }
 
 template <class V>
@@ -236,11 +225,10 @@
 };
 
 TYPED_TEST_P(ModifiersTest, Erase) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using std::get;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m(values.begin(), values.end());
   ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
   auto& first = *m.begin();
@@ -255,10 +243,9 @@
 }
 
 TYPED_TEST_P(ModifiersTest, EraseRange) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m(values.begin(), values.end());
   ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
   auto it = m.erase(m.begin(), m.end());
@@ -267,10 +254,9 @@
 }
 
 TYPED_TEST_P(ModifiersTest, EraseKey) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m(values.begin(), values.end());
   ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
   EXPECT_EQ(1, m.erase(values[0].first));
@@ -280,11 +266,11 @@
 }
 
 TYPED_TEST_P(ModifiersTest, Swap) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> v1;
   std::vector<T> v2;
-  std::generate_n(std::back_inserter(v1), 5, hash_internal::Generator<T>());
-  std::generate_n(std::back_inserter(v2), 5, hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(v1), 5, Generator<T>());
+  std::generate_n(std::back_inserter(v2), 5, Generator<T>());
   TypeParam m1(v1.begin(), v1.end());
   TypeParam m2(v2.begin(), v2.end());
   EXPECT_THAT(items(m1), ::testing::UnorderedElementsAreArray(v1));
@@ -327,20 +313,18 @@
 // Test that we do not move from rvalue arguments if an insertion does not
 // happen.
 TYPED_TEST_P(UniquePtrModifiersTest, TryEmplace) {
-#ifdef UNORDERED_MAP_CXX17
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using V = typename TypeParam::mapped_type;
-  T val = hash_internal::Generator<T>()();
+  T val = Generator<T>()();
   TypeParam m;
   auto p = m.try_emplace(val.first, std::move(val.second));
   EXPECT_TRUE(p.second);
   // A moved from std::unique_ptr is guaranteed to be nullptr.
   EXPECT_EQ(val.second, nullptr);
-  T val2 = {val.first, hash_internal::Generator<V>()()};
+  T val2 = {val.first, Generator<V>()()};
   p = m.try_emplace(val2.first, std::move(val2.second));
   EXPECT_FALSE(p.second);
   EXPECT_NE(val2.second, nullptr);
-#endif
 }
 
 REGISTER_TYPED_TEST_SUITE_P(UniquePtrModifiersTest, TryEmplace);
diff --git a/absl/container/internal/unordered_set_constructor_test.h b/absl/container/internal/unordered_set_constructor_test.h
index af1116e..7038a0c 100644
--- a/absl/container/internal/unordered_set_constructor_test.h
+++ b/absl/container/internal/unordered_set_constructor_test.h
@@ -21,6 +21,7 @@
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/base/config.h"
 #include "absl/container/internal/hash_generator_testing.h"
 #include "absl/container/internal/hash_policy_testing.h"
 #include "absl/meta/type_traits.h"
@@ -94,28 +95,8 @@
   EXPECT_GE(cm.bucket_count(), 123);
 }
 
-template <typename T>
-struct is_std_unordered_set : std::false_type {};
-
-template <typename... T>
-struct is_std_unordered_set<std::unordered_set<T...>> : std::true_type {};
-
-#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17)
-using has_cxx14_std_apis = std::true_type;
-#else
-using has_cxx14_std_apis = std::false_type;
-#endif
-
-template <typename T>
-using expect_cxx14_apis =
-    absl::disjunction<absl::negation<is_std_unordered_set<T>>,
-                      has_cxx14_std_apis>;
-
 template <typename TypeParam>
-void BucketCountAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void BucketCountAllocTest(std::true_type) {
+void BucketCountAllocTest() {
   using A = typename TypeParam::allocator_type;
   A alloc(0);
   TypeParam m(123, alloc);
@@ -126,14 +107,11 @@
 }
 
 TYPED_TEST_P(ConstructorTest, BucketCountAlloc) {
-  BucketCountAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+  BucketCountAllocTest<TypeParam>();
 }
 
 template <typename TypeParam>
-void BucketCountHashAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void BucketCountHashAllocTest(std::true_type) {
+void BucketCountHashAllocTest() {
   using H = typename TypeParam::hasher;
   using A = typename TypeParam::allocator_type;
   H hasher;
@@ -147,25 +125,11 @@
 }
 
 TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) {
-  BucketCountHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+  BucketCountHashAllocTest<TypeParam>();
 }
 
-#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
-using has_alloc_std_constructors = std::true_type;
-#else
-using has_alloc_std_constructors = std::false_type;
-#endif
-
-template <typename T>
-using expect_alloc_constructors =
-    absl::disjunction<absl::negation<is_std_unordered_set<T>>,
-                      has_alloc_std_constructors>;
-
 template <typename TypeParam>
-void AllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void AllocTest(std::true_type) {
+void AllocTest() {
   using A = typename TypeParam::allocator_type;
   A alloc(0);
   TypeParam m(alloc);
@@ -174,12 +138,10 @@
   EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
 }
 
-TYPED_TEST_P(ConstructorTest, Alloc) {
-  AllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
-}
+TYPED_TEST_P(ConstructorTest, Alloc) { AllocTest<TypeParam>(); }
 
 TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
   using A = typename TypeParam::allocator_type;
@@ -187,8 +149,7 @@
   E equal;
   A alloc(0);
   std::vector<T> values;
-  for (size_t i = 0; i != 10; ++i)
-    values.push_back(hash_internal::Generator<T>()());
+  for (size_t i = 0; i != 10; ++i) values.push_back(Generator<T>()());
   TypeParam m(values.begin(), values.end(), 123, hasher, equal, alloc);
   EXPECT_EQ(m.hash_function(), hasher);
   EXPECT_EQ(m.key_eq(), equal);
@@ -198,16 +159,12 @@
 }
 
 template <typename TypeParam>
-void InputIteratorBucketAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InputIteratorBucketAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+void InputIteratorBucketAllocTest() {
+  using T = GeneratedType<TypeParam>;
   using A = typename TypeParam::allocator_type;
   A alloc(0);
   std::vector<T> values;
-  for (size_t i = 0; i != 10; ++i)
-    values.push_back(hash_internal::Generator<T>()());
+  for (size_t i = 0; i != 10; ++i) values.push_back(Generator<T>()());
   TypeParam m(values.begin(), values.end(), 123, alloc);
   EXPECT_EQ(m.get_allocator(), alloc);
   EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
@@ -215,22 +172,18 @@
 }
 
 TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) {
-  InputIteratorBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+  InputIteratorBucketAllocTest<TypeParam>();
 }
 
 template <typename TypeParam>
-void InputIteratorBucketHashAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InputIteratorBucketHashAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+void InputIteratorBucketHashAllocTest() {
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using A = typename TypeParam::allocator_type;
   H hasher;
   A alloc(0);
   std::vector<T> values;
-  for (size_t i = 0; i != 10; ++i)
-    values.push_back(hash_internal::Generator<T>()());
+  for (size_t i = 0; i != 10; ++i) values.push_back(Generator<T>()());
   TypeParam m(values.begin(), values.end(), 123, hasher, alloc);
   EXPECT_EQ(m.hash_function(), hasher);
   EXPECT_EQ(m.get_allocator(), alloc);
@@ -239,11 +192,11 @@
 }
 
 TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) {
-  InputIteratorBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+  InputIteratorBucketHashAllocTest<TypeParam>();
 }
 
 TYPED_TEST_P(ConstructorTest, CopyConstructor) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
   using A = typename TypeParam::allocator_type;
@@ -251,7 +204,7 @@
   E equal;
   A alloc(0);
   TypeParam m(123, hasher, equal, alloc);
-  for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
+  for (size_t i = 0; i != 10; ++i) m.insert(Generator<T>()());
   TypeParam n(m);
   EXPECT_EQ(m.hash_function(), n.hash_function());
   EXPECT_EQ(m.key_eq(), n.key_eq());
@@ -261,11 +214,8 @@
 }
 
 template <typename TypeParam>
-void CopyConstructorAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void CopyConstructorAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+void CopyConstructorAllocTest() {
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
   using A = typename TypeParam::allocator_type;
@@ -273,7 +223,7 @@
   E equal;
   A alloc(0);
   TypeParam m(123, hasher, equal, alloc);
-  for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
+  for (size_t i = 0; i != 10; ++i) m.insert(Generator<T>()());
   TypeParam n(m, A(11));
   EXPECT_EQ(m.hash_function(), n.hash_function());
   EXPECT_EQ(m.key_eq(), n.key_eq());
@@ -282,13 +232,13 @@
 }
 
 TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) {
-  CopyConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
+  CopyConstructorAllocTest<TypeParam>();
 }
 
 // TODO(alkis): Test non-propagating allocators on copy constructors.
 
 TYPED_TEST_P(ConstructorTest, MoveConstructor) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
   using A = typename TypeParam::allocator_type;
@@ -296,7 +246,7 @@
   E equal;
   A alloc(0);
   TypeParam m(123, hasher, equal, alloc);
-  for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
+  for (size_t i = 0; i != 10; ++i) m.insert(Generator<T>()());
   TypeParam t(m);
   TypeParam n(std::move(t));
   EXPECT_EQ(m.hash_function(), n.hash_function());
@@ -306,11 +256,8 @@
 }
 
 template <typename TypeParam>
-void MoveConstructorAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void MoveConstructorAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+void MoveConstructorAllocTest() {
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
   using A = typename TypeParam::allocator_type;
@@ -318,7 +265,7 @@
   E equal;
   A alloc(0);
   TypeParam m(123, hasher, equal, alloc);
-  for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
+  for (size_t i = 0; i != 10; ++i) m.insert(Generator<T>()());
   TypeParam t(m);
   TypeParam n(std::move(t), A(1));
   EXPECT_EQ(m.hash_function(), n.hash_function());
@@ -328,14 +275,14 @@
 }
 
 TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) {
-  MoveConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
+  MoveConstructorAllocTest<TypeParam>();
 }
 
 // TODO(alkis): Test non-propagating allocators on move constructors.
 
 TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
+  using T = GeneratedType<TypeParam>;
+  Generator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
@@ -352,13 +299,10 @@
 }
 
 template <typename TypeParam>
-void InitializerListBucketAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InitializerListBucketAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+void InitializerListBucketAllocTest() {
+  using T = GeneratedType<TypeParam>;
   using A = typename TypeParam::allocator_type;
-  hash_internal::Generator<T> gen;
+  Generator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   A alloc(0);
   TypeParam m(values, 123, alloc);
@@ -368,20 +312,17 @@
 }
 
 TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) {
-  InitializerListBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+  InitializerListBucketAllocTest<TypeParam>();
 }
 
 template <typename TypeParam>
-void InitializerListBucketHashAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InitializerListBucketHashAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+void InitializerListBucketHashAllocTest() {
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using A = typename TypeParam::allocator_type;
   H hasher;
   A alloc(0);
-  hash_internal::Generator<T> gen;
+  Generator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   TypeParam m(values, 123, hasher, alloc);
   EXPECT_EQ(m.hash_function(), hasher);
@@ -391,18 +332,18 @@
 }
 
 TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) {
-  InitializerListBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
+  InitializerListBucketHashAllocTest<TypeParam>();
 }
 
 TYPED_TEST_P(ConstructorTest, CopyAssignment) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
   using A = typename TypeParam::allocator_type;
   H hasher;
   E equal;
   A alloc(0);
-  hash_internal::Generator<T> gen;
+  Generator<T> gen;
   TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
   TypeParam n;
   n = m;
@@ -415,14 +356,14 @@
 // (it depends on traits).
 
 TYPED_TEST_P(ConstructorTest, MoveAssignment) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
   using A = typename TypeParam::allocator_type;
   H hasher;
   E equal;
   A alloc(0);
-  hash_internal::Generator<T> gen;
+  Generator<T> gen;
   TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
   TypeParam t(m);
   TypeParam n;
@@ -433,8 +374,8 @@
 }
 
 TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
+  using T = GeneratedType<TypeParam>;
+  Generator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   TypeParam m;
   m = values;
@@ -442,8 +383,8 @@
 }
 
 TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
+  using T = GeneratedType<TypeParam>;
+  Generator<T> gen;
   TypeParam m({gen(), gen(), gen()});
   TypeParam n({gen()});
   n = m;
@@ -451,8 +392,8 @@
 }
 
 TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
+  using T = GeneratedType<TypeParam>;
+  Generator<T> gen;
   TypeParam m({gen(), gen(), gen()});
   TypeParam t(m);
   TypeParam n({gen()});
@@ -461,8 +402,8 @@
 }
 
 TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
+  using T = GeneratedType<TypeParam>;
+  Generator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   TypeParam m;
   m = values;
@@ -470,8 +411,8 @@
 }
 
 TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
+  using T = GeneratedType<TypeParam>;
+  Generator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   TypeParam m(values);
   m = *&m;  // Avoid -Wself-assign.
diff --git a/absl/container/internal/unordered_set_lookup_test.h b/absl/container/internal/unordered_set_lookup_test.h
index b35f766..dc63118 100644
--- a/absl/container/internal/unordered_set_lookup_test.h
+++ b/absl/container/internal/unordered_set_lookup_test.h
@@ -30,10 +30,9 @@
 TYPED_TEST_SUITE_P(LookupTest);
 
 TYPED_TEST_P(LookupTest, Count) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m;
   for (const auto& v : values)
     EXPECT_EQ(0, m.count(v)) << ::testing::PrintToString(v);
@@ -43,10 +42,9 @@
 }
 
 TYPED_TEST_P(LookupTest, Find) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m;
   for (const auto& v : values)
     EXPECT_TRUE(m.end() == m.find(v)) << ::testing::PrintToString(v);
@@ -65,10 +63,9 @@
 }
 
 TYPED_TEST_P(LookupTest, EqualRange) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m;
   for (const auto& v : values) {
     auto r = m.equal_range(v);
diff --git a/absl/container/internal/unordered_set_members_test.h b/absl/container/internal/unordered_set_members_test.h
index 4c5e104..b416fef 100644
--- a/absl/container/internal/unordered_set_members_test.h
+++ b/absl/container/internal/unordered_set_members_test.h
@@ -35,10 +35,10 @@
 TYPED_TEST_P(MembersTest, Typedefs) {
   EXPECT_TRUE((std::is_same<typename TypeParam::key_type,
                             typename TypeParam::value_type>()));
-  EXPECT_TRUE((absl::conjunction<
-               absl::negation<std::is_signed<typename TypeParam::size_type>>,
+  EXPECT_TRUE((std::conjunction<
+               std::negation<std::is_signed<typename TypeParam::size_type>>,
                std::is_integral<typename TypeParam::size_type>>()));
-  EXPECT_TRUE((absl::conjunction<
+  EXPECT_TRUE((std::conjunction<
                std::is_signed<typename TypeParam::difference_type>,
                std::is_integral<typename TypeParam::difference_type>>()));
   EXPECT_TRUE((std::is_convertible<
diff --git a/absl/container/internal/unordered_set_modifiers_test.h b/absl/container/internal/unordered_set_modifiers_test.h
index d8864bb..b647642 100644
--- a/absl/container/internal/unordered_set_modifiers_test.h
+++ b/absl/container/internal/unordered_set_modifiers_test.h
@@ -30,10 +30,9 @@
 TYPED_TEST_SUITE_P(ModifiersTest);
 
 TYPED_TEST_P(ModifiersTest, Clear) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m(values.begin(), values.end());
   ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
   m.clear();
@@ -42,8 +41,8 @@
 }
 
 TYPED_TEST_P(ModifiersTest, Insert) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  T val = hash_internal::Generator<T>()();
+  using T = GeneratedType<TypeParam>;
+  T val = Generator<T>()();
   TypeParam m;
   auto p = m.insert(val);
   EXPECT_TRUE(p.second);
@@ -53,8 +52,8 @@
 }
 
 TYPED_TEST_P(ModifiersTest, InsertHint) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  T val = hash_internal::Generator<T>()();
+  using T = GeneratedType<TypeParam>;
+  T val = Generator<T>()();
   TypeParam m;
   auto it = m.insert(m.end(), val);
   EXPECT_TRUE(it != m.end());
@@ -65,18 +64,17 @@
 }
 
 TYPED_TEST_P(ModifiersTest, InsertRange) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m;
   m.insert(values.begin(), values.end());
   ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
 }
 
 TYPED_TEST_P(ModifiersTest, InsertWithinCapacity) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  T val = hash_internal::Generator<T>()();
+  using T = GeneratedType<TypeParam>;
+  T val = Generator<T>()();
   TypeParam m;
   m.reserve(10);
   const size_t original_capacity = m.bucket_count();
@@ -88,10 +86,9 @@
 
 TYPED_TEST_P(ModifiersTest, InsertRangeWithinCapacity) {
 #if !defined(__GLIBCXX__)
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> base_values;
-  std::generate_n(std::back_inserter(base_values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(base_values), 10, Generator<T>());
   std::vector<T> values;
   while (values.size() != 100) {
     values.insert(values.end(), base_values.begin(), base_values.end());
@@ -105,8 +102,8 @@
 }
 
 TYPED_TEST_P(ModifiersTest, Emplace) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  T val = hash_internal::Generator<T>()();
+  using T = GeneratedType<TypeParam>;
+  T val = Generator<T>()();
   TypeParam m;
   // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps
   // with test traits/policy.
@@ -119,8 +116,8 @@
 }
 
 TYPED_TEST_P(ModifiersTest, EmplaceHint) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  T val = hash_internal::Generator<T>()();
+  using T = GeneratedType<TypeParam>;
+  T val = Generator<T>()();
   TypeParam m;
   // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps
   // with test traits/policy.
@@ -150,10 +147,9 @@
 };
 
 TYPED_TEST_P(ModifiersTest, Erase) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m(values.begin(), values.end());
   ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
   std::vector<T> values2;
@@ -167,10 +163,9 @@
 }
 
 TYPED_TEST_P(ModifiersTest, EraseRange) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m(values.begin(), values.end());
   ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
   auto it = m.erase(m.begin(), m.end());
@@ -179,10 +174,9 @@
 }
 
 TYPED_TEST_P(ModifiersTest, EraseKey) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(values), 10, Generator<T>());
   TypeParam m(values.begin(), values.end());
   ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
   EXPECT_EQ(1, m.erase(values[0]));
@@ -192,11 +186,11 @@
 }
 
 TYPED_TEST_P(ModifiersTest, Swap) {
-  using T = hash_internal::GeneratedType<TypeParam>;
+  using T = GeneratedType<TypeParam>;
   std::vector<T> v1;
   std::vector<T> v2;
-  std::generate_n(std::back_inserter(v1), 5, hash_internal::Generator<T>());
-  std::generate_n(std::back_inserter(v2), 5, hash_internal::Generator<T>());
+  std::generate_n(std::back_inserter(v1), 5, Generator<T>());
+  std::generate_n(std::back_inserter(v2), 5, Generator<T>());
   TypeParam m1(v1.begin(), v1.end());
   TypeParam m2(v2.begin(), v2.end());
   EXPECT_THAT(keys(m1), ::testing::UnorderedElementsAreArray(v1));
diff --git a/absl/container/linked_hash_map.h b/absl/container/linked_hash_map.h
new file mode 100644
index 0000000..cec0ada
--- /dev/null
+++ b/absl/container/linked_hash_map.h
@@ -0,0 +1,660 @@
+// Copyright 2025 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: linked_hash_map.h
+// -----------------------------------------------------------------------------
+//
+// This is a simple insertion-ordered map. It provides O(1) amortized
+// insertions and lookups, as well as iteration over the map in the insertion
+// order.
+//
+// This class is thread-compatible.
+//
+// Iterators point into the list and should be stable in the face of
+// mutations, except for an iterator pointing to an element that was just
+// deleted.
+//
+// This class supports heterogeneous lookups.
+
+#ifndef ABSL_CONTAINER_LINKED_HASH_MAP_H_
+#define ABSL_CONTAINER_LINKED_HASH_MAP_H_
+
+#include <cassert>
+#include <cstddef>
+#include <initializer_list>
+#include <list>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/internal/throw_delegate.h"
+#include "absl/base/optimization.h"
+#include "absl/container/flat_hash_set.h"
+#include "absl/container/internal/common.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+template <typename Key, typename Value,
+          typename KeyHash = typename absl::flat_hash_set<Key>::hasher,
+          typename KeyEq =
+              typename absl::flat_hash_set<Key, KeyHash>::key_equal,
+          typename Alloc = std::allocator<std::pair<const Key, Value>>>
+class linked_hash_map {
+  using KeyArgImpl = absl::container_internal::KeyArg<
+      absl::container_internal::IsTransparent<KeyEq>::value &&
+      absl::container_internal::IsTransparent<KeyHash>::value>;
+
+ public:
+  using key_type = Key;
+  using mapped_type = Value;
+  using hasher = KeyHash;
+  using key_equal = KeyEq;
+  using value_type = std::pair<const key_type, mapped_type>;
+  using allocator_type = Alloc;
+  using difference_type = ptrdiff_t;
+
+ private:
+  template <class K>
+  using key_arg = typename KeyArgImpl::template type<K, key_type>;
+
+  using ListType = std::list<value_type, Alloc>;
+
+  template <class Fn>
+  class Wrapped {
+    template <typename K>
+    static const K& ToKey(const K& k) {
+      return k;
+    }
+    static const key_type& ToKey(typename ListType::const_iterator it) {
+      return it->first;
+    }
+    static const key_type& ToKey(typename ListType::iterator it) {
+      return it->first;
+    }
+
+    Fn fn_;
+
+    friend linked_hash_map;
+
+   public:
+    using is_transparent = void;
+
+    Wrapped() = default;
+    explicit Wrapped(Fn fn) : fn_(std::move(fn)) {}
+
+    template <class... Args>
+    auto operator()(Args&&... args) const
+        -> decltype(this->fn_(ToKey(args)...)) {
+      return fn_(ToKey(args)...);
+    }
+  };
+  using SetType =
+      absl::flat_hash_set<typename ListType::iterator, Wrapped<hasher>,
+                          Wrapped<key_equal>, Alloc>;
+
+  class NodeHandle {
+   public:
+    using key_type = linked_hash_map::key_type;
+    using mapped_type = linked_hash_map::mapped_type;
+    using allocator_type = linked_hash_map::allocator_type;
+
+    constexpr NodeHandle() noexcept = default;
+    NodeHandle(NodeHandle&& nh) noexcept = default;
+    ~NodeHandle() = default;
+    NodeHandle& operator=(NodeHandle&& node) noexcept = default;
+    bool empty() const noexcept { return list_.empty(); }
+    explicit operator bool() const noexcept { return !empty(); }
+    allocator_type get_allocator() const { return list_.get_allocator(); }
+    const key_type& key() const { return list_.front().first; }
+    mapped_type& mapped() { return list_.front().second; }
+    void swap(NodeHandle& nh) noexcept { list_.swap(nh.list_); }
+
+   private:
+    friend linked_hash_map;
+
+    explicit NodeHandle(ListType list) : list_(std::move(list)) {}
+    ListType list_;
+  };
+
+  template <class Iterator, class NodeType>
+  struct InsertReturnType {
+    Iterator position;
+    bool inserted;
+    NodeType node;
+  };
+
+ public:
+  using iterator = typename ListType::iterator;
+  using const_iterator = typename ListType::const_iterator;
+  using reverse_iterator = typename ListType::reverse_iterator;
+  using const_reverse_iterator = typename ListType::const_reverse_iterator;
+  using reference = typename ListType::reference;
+  using const_reference = typename ListType::const_reference;
+  using size_type = typename ListType::size_type;
+  using pointer = typename std::allocator_traits<allocator_type>::pointer;
+  using const_pointer =
+      typename std::allocator_traits<allocator_type>::const_pointer;
+  using node_type = NodeHandle;
+  using insert_return_type = InsertReturnType<iterator, node_type>;
+
+  linked_hash_map() {}
+
+  explicit linked_hash_map(size_t bucket_count, const hasher& hash = hasher(),
+                           const key_equal& eq = key_equal(),
+                           const allocator_type& alloc = allocator_type())
+      : set_(bucket_count, Wrapped<hasher>(hash), Wrapped<key_equal>(eq),
+             alloc),
+        list_(alloc) {}
+
+  linked_hash_map(size_t bucket_count, const hasher& hash,
+                  const allocator_type& alloc)
+      : linked_hash_map(bucket_count, hash, key_equal(), alloc) {}
+
+  linked_hash_map(size_t bucket_count, const allocator_type& alloc)
+      : linked_hash_map(bucket_count, hasher(), key_equal(), alloc) {}
+
+  explicit linked_hash_map(const allocator_type& alloc)
+      : linked_hash_map(0, hasher(), key_equal(), alloc) {}
+
+  template <class InputIt>
+  linked_hash_map(InputIt first, InputIt last, size_t bucket_count = 0,
+                  const hasher& hash = hasher(),
+                  const key_equal& eq = key_equal(),
+                  const allocator_type& alloc = allocator_type())
+      : linked_hash_map(bucket_count, hash, eq, alloc) {
+    insert(first, last);
+  }
+
+  template <class InputIt>
+  linked_hash_map(InputIt first, InputIt last, size_t bucket_count,
+                  const hasher& hash, const allocator_type& alloc)
+      : linked_hash_map(first, last, bucket_count, hash, key_equal(), alloc) {}
+
+  template <class InputIt>
+  linked_hash_map(InputIt first, InputIt last, size_t bucket_count,
+                  const allocator_type& alloc)
+      : linked_hash_map(first, last, bucket_count, hasher(), key_equal(),
+                        alloc) {}
+
+  template <class InputIt>
+  linked_hash_map(InputIt first, InputIt last, const allocator_type& alloc)
+      : linked_hash_map(first, last, /*bucket_count=*/0, hasher(), key_equal(),
+                        alloc) {}
+
+  linked_hash_map(std::initializer_list<value_type> init,
+                  size_t bucket_count = 0, const hasher& hash = hasher(),
+                  const key_equal& eq = key_equal(),
+                  const allocator_type& alloc = allocator_type())
+      : linked_hash_map(init.begin(), init.end(), bucket_count, hash, eq,
+                        alloc) {}
+
+  linked_hash_map(std::initializer_list<value_type> init, size_t bucket_count,
+                  const hasher& hash, const allocator_type& alloc)
+      : linked_hash_map(init, bucket_count, hash, key_equal(), alloc) {}
+
+  linked_hash_map(std::initializer_list<value_type> init, size_t bucket_count,
+                  const allocator_type& alloc)
+      : linked_hash_map(init, bucket_count, hasher(), key_equal(), alloc) {}
+
+  linked_hash_map(std::initializer_list<value_type> init,
+                  const allocator_type& alloc)
+      : linked_hash_map(init, /*bucket_count=*/0, hasher(), key_equal(),
+                        alloc) {}
+
+  linked_hash_map(const linked_hash_map& other)
+      : linked_hash_map(other.bucket_count(), other.hash_function(),
+                        other.key_eq(), other.get_allocator()) {
+    CopyFrom(other);
+  }
+
+  linked_hash_map(const linked_hash_map& other, const allocator_type& alloc)
+      : linked_hash_map(other.bucket_count(), other.hash_function(),
+                        other.key_eq(), alloc) {
+    CopyFrom(other);
+  }
+
+  linked_hash_map(linked_hash_map&& other) noexcept
+      : set_(std::move(other.set_)), list_(std::move(other.list_)) {
+    // Since the list and set must agree for other to end up "valid",
+    // explicitly clear them.
+    other.set_.clear();
+    other.list_.clear();
+  }
+
+  linked_hash_map(linked_hash_map&& other, const allocator_type& alloc)
+      : linked_hash_map(0, other.hash_function(), other.key_eq(), alloc) {
+    if (get_allocator() == other.get_allocator()) {
+      *this = std::move(other);
+    } else {
+      CopyFrom(std::move(other));
+    }
+  }
+
+  linked_hash_map& operator=(const linked_hash_map& other) {
+    if (this == &other) return *this;
+    // Make a new set, with other's hash/eq/alloc.
+    set_ = SetType(other.bucket_count(), other.set_.hash_function(),
+                   other.set_.key_eq(), other.get_allocator());
+    // Copy the list, with other's allocator.
+    list_ = ListType(other.get_allocator());
+    CopyFrom(other);
+    return *this;
+  }
+
+  linked_hash_map& operator=(linked_hash_map&& other) noexcept {
+    // underlying containers will handle progagate_on_container_move details
+    set_ = std::move(other.set_);
+    list_ = std::move(other.list_);
+    other.set_.clear();
+    other.list_.clear();
+    return *this;
+  }
+
+  linked_hash_map& operator=(std::initializer_list<value_type> values) {
+    clear();
+    insert(values.begin(), values.end());
+    return *this;
+  }
+
+  // Derive size_ from set_, as list::size might be O(N).
+  size_type size() const { return set_.size(); }
+  size_type max_size() const noexcept { return ~size_type{}; }
+  bool empty() const { return set_.empty(); }
+
+  // Iteration is list-like, in insertion order.
+  // These are all forwarded.
+  iterator begin() { return list_.begin(); }
+  iterator end() { return list_.end(); }
+  const_iterator begin() const { return list_.begin(); }
+  const_iterator end() const { return list_.end(); }
+  const_iterator cbegin() const { return list_.cbegin(); }
+  const_iterator cend() const { return list_.cend(); }
+  reverse_iterator rbegin() { return list_.rbegin(); }
+  reverse_iterator rend() { return list_.rend(); }
+  const_reverse_iterator rbegin() const { return list_.rbegin(); }
+  const_reverse_iterator rend() const { return list_.rend(); }
+  const_reverse_iterator crbegin() const { return list_.crbegin(); }
+  const_reverse_iterator crend() const { return list_.crend(); }
+  reference front() { return list_.front(); }
+  reference back() { return list_.back(); }
+  const_reference front() const { return list_.front(); }
+  const_reference back() const { return list_.back(); }
+
+  void pop_front() { erase(begin()); }
+  void pop_back() { erase(std::prev(end())); }
+
+  ABSL_ATTRIBUTE_REINITIALIZES void clear() {
+    set_.clear();
+    list_.clear();
+  }
+
+  void reserve(size_t n) { set_.reserve(n); }
+  size_t capacity() const { return set_.capacity(); }
+  size_t bucket_count() const { return set_.bucket_count(); }
+  float load_factor() const { return set_.load_factor(); }
+
+  hasher hash_function() const { return set_.hash_function().fn_; }
+  key_equal key_eq() const { return set_.key_eq().fn_; }
+  allocator_type get_allocator() const { return list_.get_allocator(); }
+
+  template <class K = key_type>
+  size_type erase(const key_arg<K>& key) {
+    auto found = set_.find(key);
+    if (found == set_.end()) return 0;
+    auto list_it = *found;
+    // Erase set entry first since it refers to the list element.
+    set_.erase(found);
+    list_.erase(list_it);
+    return 1;
+  }
+
+  iterator erase(const_iterator position) {
+    auto found = set_.find(position);
+    assert(*found == position);
+    set_.erase(found);
+    return list_.erase(position);
+  }
+
+  iterator erase(iterator position) {
+    return erase(static_cast<const_iterator>(position));
+  }
+
+  iterator erase(iterator first, iterator last) {
+    while (first != last) first = erase(first);
+    return first;
+  }
+
+  iterator erase(const_iterator first, const_iterator last) {
+    while (first != last) first = erase(first);
+    if (first == end()) return end();
+    return *set_.find(first);
+  }
+
+  template <class K = key_type>
+  iterator find(const key_arg<K>& key) {
+    auto found = set_.find(key);
+    if (found == set_.end()) return end();
+    return *found;
+  }
+
+  template <class K = key_type>
+  const_iterator find(const key_arg<K>& key) const {
+    auto found = set_.find(key);
+    if (found == set_.end()) return end();
+    return *found;
+  }
+
+  template <class K = key_type>
+  size_type count(const key_arg<K>& key) const {
+    return contains(key) ? 1 : 0;
+  }
+  template <class K = key_type>
+  bool contains(const key_arg<K>& key) const {
+    return set_.contains(key);
+  }
+
+  template <class K = key_type>
+  mapped_type& at(const key_arg<K>& key) {
+    auto it = find(key);
+    if (ABSL_PREDICT_FALSE(it == end())) {
+      absl::base_internal::ThrowStdOutOfRange("absl::linked_hash_map::at");
+    }
+    return it->second;
+  }
+
+  template <class K = key_type>
+  const mapped_type& at(const key_arg<K>& key) const {
+    return const_cast<linked_hash_map*>(this)->at(key);
+  }
+
+  template <class K = key_type>
+  std::pair<iterator, iterator> equal_range(const key_arg<K>& key) {
+    auto iter = set_.find(key);
+    if (iter == set_.end()) return {end(), end()};
+    return {*iter, std::next(*iter)};
+  }
+
+  template <class K = key_type>
+  std::pair<const_iterator, const_iterator> equal_range(
+      const key_arg<K>& key) const {
+    auto iter = set_.find(key);
+    if (iter == set_.end()) return {end(), end()};
+    return {*iter, std::next(*iter)};
+  }
+
+  template <class K = key_type>
+  mapped_type& operator[](const key_arg<K>& key) {
+    return LazyEmplaceInternal(key).first->second;
+  }
+
+  template <class K = key_type, K* = nullptr>
+  mapped_type& operator[](key_arg<K>&& key) {
+    return LazyEmplaceInternal(std::forward<key_arg<K>>(key)).first->second;
+  }
+
+  std::pair<iterator, bool> insert(const value_type& v) {
+    return InsertInternal(v);
+  }
+  std::pair<iterator, bool> insert(value_type&& v) {
+    return InsertInternal(std::move(v));
+  }
+
+  iterator insert(const_iterator, const value_type& v) {
+    return insert(v).first;
+  }
+  iterator insert(const_iterator, value_type&& v) {
+    return insert(std::move(v)).first;
+  }
+
+  void insert(std::initializer_list<value_type> ilist) {
+    insert(ilist.begin(), ilist.end());
+  }
+
+  template <class InputIt>
+  void insert(InputIt first, InputIt last) {
+    for (; first != last; ++first) insert(*first);
+  }
+
+  insert_return_type insert(node_type&& node) {
+    if (node.empty()) return {end(), false, node_type()};
+    if (auto [set_itr, inserted] = set_.emplace(node.list_.begin()); inserted) {
+      list_.splice(list_.end(), node.list_);
+      return {*set_itr, true, node_type()};
+    } else {
+      return {*set_itr, false, std::move(node)};
+    }
+  }
+
+  iterator insert(const_iterator, node_type&& node) {
+    return insert(std::move(node)).first;
+  }
+
+  // The last two template parameters ensure that both arguments are rvalues
+  // (lvalue arguments are handled by the overloads below). This is necessary
+  // for supporting bitfield arguments.
+  //
+  //   union { int n : 1; };
+  //   linked_hash_map<int, int> m;
+  //   m.insert_or_assign(n, n);
+  template <class K = key_type, class V = mapped_type, K* = nullptr,
+            V* = nullptr>
+  std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, V&& v) {
+    return InsertOrAssignInternal(std::forward<key_arg<K>>(k),
+                                  std::forward<V>(v));
+  }
+
+  template <class K = key_type, class V = mapped_type, K* = nullptr>
+  std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, const V& v) {
+    return InsertOrAssignInternal(std::forward<key_arg<K>>(k), v);
+  }
+
+  template <class K = key_type, class V = mapped_type, V* = nullptr>
+  std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, V&& v) {
+    return InsertOrAssignInternal(k, std::forward<V>(v));
+  }
+
+  template <class K = key_type, class V = mapped_type>
+  std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, const V& v) {
+    return InsertOrAssignInternal(k, v);
+  }
+
+  template <class K = key_type, class V = mapped_type, K* = nullptr,
+            V* = nullptr>
+  iterator insert_or_assign(const_iterator, key_arg<K>&& k, V&& v) {
+    return insert_or_assign(std::forward<key_arg<K>>(k), std::forward<V>(v))
+        .first;
+  }
+
+  template <class K = key_type, class V = mapped_type, K* = nullptr>
+  iterator insert_or_assign(const_iterator, key_arg<K>&& k, const V& v) {
+    return insert_or_assign(std::forward<key_arg<K>>(k), v).first;
+  }
+
+  template <class K = key_type, class V = mapped_type, V* = nullptr>
+  iterator insert_or_assign(const_iterator, const key_arg<K>& k, V&& v) {
+    return insert_or_assign(k, std::forward<V>(v)).first;
+  }
+
+  template <class K = key_type, class V = mapped_type>
+  iterator insert_or_assign(const_iterator, const key_arg<K>& k, const V& v) {
+    return insert_or_assign(k, v).first;
+  }
+
+  template <typename... Args>
+  std::pair<iterator, bool> emplace(Args&&... args) {
+    ListType node_donor;
+    auto list_iter =
+        node_donor.emplace(node_donor.end(), std::forward<Args>(args)...);
+    auto ins = set_.insert(list_iter);
+    if (!ins.second) return {*ins.first, false};
+    list_.splice(list_.end(), node_donor, list_iter);
+    return {list_iter, true};
+  }
+
+  template <class K = key_type, class... Args, K* = nullptr>
+  iterator try_emplace(const_iterator, key_arg<K>&& k, Args&&... args) {
+    return try_emplace(std::forward<key_arg<K>>(k), std::forward<Args>(args)...)
+        .first;
+  }
+
+  template <typename... Args>
+  iterator emplace_hint(const_iterator, Args&&... args) {
+    return emplace(std::forward<Args>(args)...).first;
+  }
+
+  template <class K = key_type, typename... Args, K* = nullptr>
+  std::pair<iterator, bool> try_emplace(key_arg<K>&& key, Args&&... args) {
+    return LazyEmplaceInternal(std::forward<key_arg<K>>(key),
+                               std::forward<Args>(args)...);
+  }
+
+  template <typename H, typename E>
+  void merge(linked_hash_map<Key, Value, H, E, Alloc>& src) {
+    auto itr = src.list_.begin();
+    while (itr != src.list_.end()) {
+      if (contains(itr->first)) {
+        ++itr;
+      } else {
+        insert(src.extract(itr++));
+      }
+    }
+  }
+
+  template <typename H, typename E>
+  void merge(linked_hash_map<Key, Value, H, E, Alloc>&& src) {
+    merge(src);
+  }
+
+  node_type extract(const_iterator position) {
+    set_.erase(position->first);
+    ListType extracted_node_list;
+    extracted_node_list.splice(extracted_node_list.end(), list_, position);
+    return node_type(std::move(extracted_node_list));
+  }
+
+  template <class K = key_type,
+            std::enable_if_t<!std::is_same_v<K, iterator>, int> = 0>
+  node_type extract(const key_arg<K>& key) {
+    auto node = set_.extract(key);
+    if (node.empty()) return node_type();
+    ListType extracted_node_list;
+    extracted_node_list.splice(extracted_node_list.end(), list_, node.value());
+    return node_type(std::move(extracted_node_list));
+  }
+
+  template <typename H, typename E>
+  void splice(const_iterator, linked_hash_map<Key, Value, H, E, Alloc>& list,
+              const_iterator it) {
+    if (&list == this) {
+      list_.splice(list_.end(), list.list_, it);
+    } else {
+      insert(list.extract(it));
+    }
+  }
+
+  template <class K = key_type, typename... Args>
+  std::pair<iterator, bool> try_emplace(const key_arg<K>& key, Args&&... args) {
+    return LazyEmplaceInternal(key, std::forward<Args>(args)...);
+  }
+
+  template <class K = key_type, typename... Args>
+  iterator try_emplace(const_iterator, const key_arg<K>& key, Args&&... args) {
+    return LazyEmplaceInternal(key, std::forward<Args>(args)...).first;
+  }
+
+  void swap(linked_hash_map& other) noexcept {
+    using std::swap;
+    swap(set_, other.set_);
+    swap(list_, other.list_);
+  }
+
+  friend bool operator==(const linked_hash_map& a, const linked_hash_map& b) {
+    if (a.size() != b.size()) return false;
+    const linked_hash_map* outer = &a;
+    const linked_hash_map* inner = &b;
+    if (outer->capacity() > inner->capacity()) std::swap(outer, inner);
+    for (const value_type& elem : *outer) {
+      auto it = inner->find(elem.first);
+      if (it == inner->end()) return false;
+      if (it->second != elem.second) return false;
+    }
+
+    return true;
+  }
+
+  friend bool operator!=(const linked_hash_map& a, const linked_hash_map& b) {
+    return !(a == b);
+  }
+
+  void rehash(size_t n) { set_.rehash(n); }
+
+ private:
+  template <typename Other>
+  void CopyFrom(Other&& other) {
+    for (auto& elem : other.list_) {
+      set_.insert(list_.insert(list_.end(), std::move(elem)));
+    }
+    assert(set_.size() == list_.size());
+  }
+
+  template <typename U>
+  std::pair<iterator, bool> InsertInternal(U&& pair) {  // NOLINT(build/c++11)
+    bool constructed = false;
+    auto set_iter = set_.lazy_emplace(pair.first, [&](const auto& ctor) {
+      constructed = true;
+      ctor(list_.emplace(list_.end(), std::forward<U>(pair)));
+    });
+    return {*set_iter, constructed};
+  }
+
+  template <class K, class V>
+  std::pair<iterator, bool> InsertOrAssignInternal(K&& k, V&& v) {
+    auto [it, inserted] =
+        LazyEmplaceInternal(std::forward<K>(k), std::forward<V>(v));
+    if (!inserted) it->second = std::forward<V>(v);
+    return {it, inserted};
+  }
+
+  template <typename K, typename... Args>
+  std::pair<iterator, bool> LazyEmplaceInternal(K&& key, Args&&... args) {
+    bool constructed = false;
+    auto set_iter = set_.lazy_emplace(
+        key, [this, &constructed, &key, &args...](const auto& ctor) {
+          auto list_iter =
+              list_.emplace(list_.end(), std::piecewise_construct,
+                            std::forward_as_tuple(std::forward<K>(key)),
+                            std::forward_as_tuple(std::forward<Args>(args)...));
+          constructed = true;
+          ctor(list_iter);
+        });
+    return {*set_iter, constructed};
+  }
+
+  // The set component, used for speedy lookups.
+  SetType set_;
+
+  // The list component, used for maintaining insertion order.
+  ListType list_;
+};
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_CONTAINER_LINKED_HASH_MAP_H_
diff --git a/absl/container/linked_hash_map_benchmark.cc b/absl/container/linked_hash_map_benchmark.cc
new file mode 100644
index 0000000..5a0db38
--- /dev/null
+++ b/absl/container/linked_hash_map_benchmark.cc
@@ -0,0 +1,140 @@
+// Copyright 2025 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <algorithm>
+#include <cstddef>
+#include <string>
+#include <vector>
+
+#include "absl/container/linked_hash_map.h"
+#include "absl/functional/function_ref.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+struct BenchmarkData {
+  std::vector<std::string> sample;
+  size_t str_bytes = 0;
+  explicit BenchmarkData(int size,
+                         absl::FunctionRef<std::string(int)> factory) {
+    sample.reserve(size);
+    for (int i = 0; i < size; ++i) {
+      sample.push_back(factory(i));
+      str_bytes += sample.back().size();
+    }
+  }
+};
+
+void BenchmarkTryEmplaceStrings(benchmark::State& state,
+                                absl::FunctionRef<std::string(int)> factory) {
+  BenchmarkData data(state.range(0), factory);
+
+  // Make a batch around 1Mi bytes.
+  const size_t batch_size =
+      std::max(size_t{1}, size_t{1000000} / data.str_bytes);
+  std::vector<absl::linked_hash_map<std::string, int>> sets(batch_size);
+
+  while (state.KeepRunningBatch(batch_size)) {
+    state.PauseTiming();
+    for (auto& set : sets) set.clear();
+    state.ResumeTiming();
+    for (auto& set : sets) {
+      for (const auto& str : data.sample) {
+        benchmark::DoNotOptimize(set.try_emplace(str));
+      }
+    }
+  }
+
+  state.SetItemsProcessed(state.iterations() * state.range(0));
+  state.SetBytesProcessed(state.iterations() * data.str_bytes);
+}
+
+void BenchmarkInsertOrAssignStrings(
+    benchmark::State& state, absl::FunctionRef<std::string(int)> factory) {
+  BenchmarkData data(state.range(0), factory);
+
+  // Make a batch around 1Mi bytes.
+  const size_t batch_size = std::max(size_t{1}, 1000000 / data.str_bytes);
+  std::vector<absl::linked_hash_map<std::string, int>> sets(batch_size);
+
+  while (state.KeepRunningBatch(batch_size)) {
+    state.PauseTiming();
+    for (auto& set : sets) set.clear();
+    state.ResumeTiming();
+    for (auto& set : sets) {
+      for (const auto& str : data.sample) {
+        benchmark::DoNotOptimize(set.insert_or_assign(str, 0));
+      }
+    }
+  }
+
+  state.SetItemsProcessed(state.iterations() * state.range(0));
+  state.SetBytesProcessed(state.iterations() * data.str_bytes);
+}
+
+constexpr absl::string_view kFormatShort = "%10d";
+constexpr absl::string_view kFormatLong =
+    "a longer string that exceeds the SSO %10d";
+
+void BM_TryEmplaceShortStrings_Hit(benchmark::State& state) {
+  BenchmarkTryEmplaceStrings(
+      state, [](int i) { return absl::StrFormat(kFormatShort, i); });
+}
+BENCHMARK(BM_TryEmplaceShortStrings_Hit)->Range(1, 1 << 16);
+
+void BM_TryEmplaceLongStrings_Hit(benchmark::State& state) {
+  BenchmarkTryEmplaceStrings(
+      state, [](int i) { return absl::StrFormat(kFormatLong, i); });
+}
+BENCHMARK(BM_TryEmplaceLongStrings_Hit)->Range(1, 1 << 16);
+
+void BM_TryEmplaceShortStrings_Miss(benchmark::State& state) {
+  BenchmarkTryEmplaceStrings(
+      state, [](int i) { return absl::StrFormat(kFormatShort, i % 20); });
+}
+BENCHMARK(BM_TryEmplaceShortStrings_Miss)->Range(1, 1 << 16);
+
+void BM_TryEmplaceLongStrings_Miss(benchmark::State& state) {
+  BenchmarkTryEmplaceStrings(
+      state, [](int i) { return absl::StrFormat(kFormatLong, i % 20); });
+}
+BENCHMARK(BM_TryEmplaceLongStrings_Miss)->Range(1, 1 << 16);
+
+void BM_InsertOrAssignShortStrings_Hit(benchmark::State& state) {
+  BenchmarkInsertOrAssignStrings(
+      state, [](int i) { return absl::StrFormat(kFormatShort, i); });
+}
+BENCHMARK(BM_InsertOrAssignShortStrings_Hit)->Range(1, 1 << 16);
+
+void BM_InsertOrAssignLongStrings_Hit(benchmark::State& state) {
+  BenchmarkInsertOrAssignStrings(
+      state, [](int i) { return absl::StrFormat(kFormatLong, i); });
+}
+BENCHMARK(BM_InsertOrAssignLongStrings_Hit)->Range(1, 1 << 16);
+
+void BM_InsertOrAssignShortStrings_Miss(benchmark::State& state) {
+  BenchmarkInsertOrAssignStrings(
+      state, [](int i) { return absl::StrFormat(kFormatShort, i % 20); });
+}
+BENCHMARK(BM_InsertOrAssignShortStrings_Miss)->Range(1, 1 << 16);
+
+void BM_InsertOrAssignLongStrings_Miss(benchmark::State& state) {
+  BenchmarkInsertOrAssignStrings(
+      state, [](int i) { return absl::StrFormat(kFormatLong, i % 20); });
+}
+BENCHMARK(BM_InsertOrAssignLongStrings_Miss)->Range(1, 1 << 16);
+
+}  // namespace
diff --git a/absl/container/linked_hash_map_test.cc b/absl/container/linked_hash_map_test.cc
new file mode 100644
index 0000000..54d42fe
--- /dev/null
+++ b/absl/container/linked_hash_map_test.cc
@@ -0,0 +1,964 @@
+// Copyright 2025 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/container/linked_hash_map.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/base/internal/exception_testing.h"
+#include "absl/container/internal/hash_generator_testing.h"
+#include "absl/container/internal/hash_policy_testing.h"
+#include "absl/container/internal/heterogeneous_lookup_testing.h"
+#include "absl/container/internal/test_instance_tracker.h"
+#include "absl/container/internal/unordered_map_constructor_test.h"
+#include "absl/container/internal/unordered_map_lookup_test.h"
+#include "absl/container/internal/unordered_map_members_test.h"
+#include "absl/container/internal/unordered_map_modifiers_test.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace container_internal {
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::Pair;
+using ::testing::Pointee;
+
+template <class K, class V>
+using Map = linked_hash_map<K, V, StatefulTestingHash, StatefulTestingEqual,
+                            Alloc<std::pair<const K, V>>>;
+
+static_assert(!std::is_standard_layout<NonStandardLayout>(), "");
+
+using MapTypes =
+    ::testing::Types<Map<int, int>, Map<std::string, int>,
+                     Map<Enum, std::string>, Map<EnumClass, int>,
+                     Map<int, NonStandardLayout>, Map<NonStandardLayout, int>>;
+
+INSTANTIATE_TYPED_TEST_SUITE_P(LinkedHashMap, ConstructorTest, MapTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(LinkedHashMap, LookupTest, MapTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(LinkedHashMap, MembersTest, MapTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(LinkedHashMap, ModifiersTest, MapTypes);
+
+// Tests that the range constructor works.
+TEST(LinkedHashMapTest, RangeConstruct) {
+  const std::pair<int, int> items[] = {{1, 2}, {2, 3}, {3, 4}};
+  EXPECT_THAT((linked_hash_map<int, int>(std::begin(items), std::end(items))),
+              ElementsAre(Pair(1, 2), Pair(2, 3), Pair(3, 4)));
+}
+
+// Tests that copying works.
+TEST(LinkedHashMapTest, Copy) {
+  linked_hash_map<int, int> m{{2, 12}, {3, 13}};
+  auto copy = m;
+
+  EXPECT_TRUE(copy.contains(2));
+  auto found = copy.find(2);
+  ASSERT_TRUE(found != copy.end());
+  for (auto iter = copy.begin(); iter != copy.end(); ++iter) {
+    if (iter == found) return;
+  }
+  FAIL() << "Copied map's find method returned an invalid iterator.";
+}
+
+// Tests that assignment works.
+TEST(LinkedHashMapTest, Assign) {
+  linked_hash_map<int, int> m{{2, 12}, {3, 13}};
+  linked_hash_map<int, int> n{{4, 14}};
+
+  n = m;
+  EXPECT_TRUE(n.contains(2));
+  auto found = n.find(2);
+  ASSERT_TRUE(found != n.end());
+  for (auto iter = n.begin(); iter != n.end(); ++iter) {
+    if (iter == found) return;
+  }
+  FAIL() << "Assigned map's find method returned an invalid iterator.";
+}
+
+// Tests that move constructor works.
+TEST(LinkedHashMapTest, Move) {
+  // Use unique_ptr as an example of a non-copyable type.
+  linked_hash_map<int, std::unique_ptr<int>> m;
+  m[2] = std::make_unique<int>(12);
+  m[3] = std::make_unique<int>(13);
+  linked_hash_map<int, std::unique_ptr<int>> n = std::move(m);
+  EXPECT_THAT(n, ElementsAre(Pair(2, Pointee(12)), Pair(3, Pointee(13))));
+}
+
+TEST(LinkedHashMapTest, CanInsertMoveOnly) {
+  linked_hash_map<int, std::unique_ptr<int>> m;
+  struct Data {
+    int k, v;
+  };
+  const Data data[] = {{1, 123}, {3, 345}, {2, 234}, {4, 456}};
+  for (const auto& kv : data)
+    m.insert({kv.k, std::make_unique<int>(int{kv.v})});
+  EXPECT_TRUE(m.contains(2));
+  auto found = m.find(2);
+  ASSERT_TRUE(found != m.end());
+  EXPECT_EQ(234, *found->second);
+}
+
+TEST(LinkedHashMapTest, CanEmplaceMoveOnly) {
+  linked_hash_map<int, std::unique_ptr<int>> m;
+  struct Data {
+    int k, v;
+  };
+  const Data data[] = {{1, 123}, {3, 345}, {2, 234}, {4, 456}};
+  for (const auto& kv : data) {
+    m.emplace(std::piecewise_construct, std::make_tuple(kv.k),
+              std::make_tuple(new int{kv.v}));
+  }
+  EXPECT_TRUE(m.contains(2));
+  auto found = m.find(2);
+  ASSERT_TRUE(found != m.end());
+  EXPECT_EQ(234, *found->second);
+}
+
+struct NoCopy {
+  explicit NoCopy(int x) : x(x) {}
+  NoCopy(const NoCopy&) = delete;
+  NoCopy& operator=(const NoCopy&) = delete;
+  NoCopy(NoCopy&&) = delete;
+  NoCopy& operator=(NoCopy&&) = delete;
+  int x;
+};
+
+TEST(LinkedHashMapTest, CanEmplaceNoMoveNoCopy) {
+  linked_hash_map<int, NoCopy> m;
+  struct Data {
+    int k, v;
+  };
+  const Data data[] = {{1, 123}, {3, 345}, {2, 234}, {4, 456}};
+  for (const auto& kv : data) {
+    m.emplace(std::piecewise_construct, std::make_tuple(kv.k),
+              std::make_tuple(kv.v));
+  }
+  EXPECT_TRUE(m.contains(2));
+  auto found = m.find(2);
+  ASSERT_TRUE(found != m.end());
+  EXPECT_EQ(234, found->second.x);
+}
+
+TEST(LinkedHashMapTest, ConstKeys) {
+  linked_hash_map<int, int> m;
+  m.insert(std::make_pair(1, 2));
+  // Test that keys are const in iteration.
+  std::pair<const int, int>& p = *m.begin();
+  EXPECT_EQ(1, p.first);
+}
+
+// Tests that iteration from begin() to end() works
+TEST(LinkedHashMapTest, Iteration) {
+  linked_hash_map<int, int> m;
+  EXPECT_TRUE(m.begin() == m.end());
+
+  m.insert(std::make_pair(2, 12));
+  m.insert(std::make_pair(1, 11));
+  m.insert(std::make_pair(3, 13));
+
+  linked_hash_map<int, int>::iterator i = m.begin();
+  ASSERT_TRUE(m.begin() == i);
+  ASSERT_TRUE(m.end() != i);
+  EXPECT_EQ(2, i->first);
+  EXPECT_EQ(12, i->second);
+
+  ++i;
+  ASSERT_TRUE(m.end() != i);
+  EXPECT_EQ(1, i->first);
+  EXPECT_EQ(11, i->second);
+
+  ++i;
+  ASSERT_TRUE(m.end() != i);
+  EXPECT_EQ(3, i->first);
+  EXPECT_EQ(13, i->second);
+
+  ++i;  // Should be the end of the line.
+  ASSERT_TRUE(m.end() == i);
+}
+
+// Tests that reverse iteration from rbegin() to rend() works
+TEST(LinkedHashMapTest, ReverseIteration) {
+  linked_hash_map<int, int> m;
+  EXPECT_TRUE(m.rbegin() == m.rend());
+
+  m.insert(std::make_pair(2, 12));
+  m.insert(std::make_pair(1, 11));
+  m.insert(std::make_pair(3, 13));
+
+  linked_hash_map<int, int>::reverse_iterator i = m.rbegin();
+  ASSERT_TRUE(m.rbegin() == i);
+  ASSERT_TRUE(m.rend() != i);
+  EXPECT_EQ(3, i->first);
+  EXPECT_EQ(13, i->second);
+
+  ++i;
+  ASSERT_TRUE(m.rend() != i);
+  EXPECT_EQ(1, i->first);
+  EXPECT_EQ(11, i->second);
+
+  ++i;
+  ASSERT_TRUE(m.rend() != i);
+  EXPECT_EQ(2, i->first);
+  EXPECT_EQ(12, i->second);
+
+  ++i;  // Should be the end of the line.
+  ASSERT_TRUE(m.rend() == i);
+}
+
+// Tests that clear() works
+TEST(LinkedHashMapTest, Clear) {
+  linked_hash_map<int, int> m;
+  m.insert(std::make_pair(2, 12));
+  m.insert(std::make_pair(1, 11));
+  m.insert(std::make_pair(3, 13));
+
+  ASSERT_EQ(3, m.size());
+
+  m.clear();
+
+  EXPECT_EQ(0, m.size());
+
+  m.clear();  // Make sure we can call it on an empty map.
+
+  EXPECT_EQ(0, m.size());
+}
+
+// Tests that size() works.
+TEST(LinkedHashMapTest, Size) {
+  linked_hash_map<int, int> m;
+  EXPECT_EQ(0, m.size());
+  m.insert(std::make_pair(2, 12));
+  EXPECT_EQ(1, m.size());
+  m.insert(std::make_pair(1, 11));
+  EXPECT_EQ(2, m.size());
+  m.insert(std::make_pair(3, 13));
+  EXPECT_EQ(3, m.size());
+  m.clear();
+  EXPECT_EQ(0, m.size());
+}
+
+// Tests empty()
+TEST(LinkedHashMapTest, Empty) {
+  linked_hash_map<int, int> m;
+  ASSERT_TRUE(m.empty());
+  m.insert(std::make_pair(2, 12));
+  ASSERT_FALSE(m.empty());
+  m.clear();
+  ASSERT_TRUE(m.empty());
+}
+
+TEST(LinkedHashMapTest, Erase) {
+  linked_hash_map<int, int> m;
+  ASSERT_EQ(0, m.size());
+  EXPECT_EQ(0, m.erase(2));  // Nothing to erase yet
+
+  m.insert(std::make_pair(2, 12));
+  ASSERT_EQ(1, m.size());
+  EXPECT_EQ(1, m.erase(2));
+  EXPECT_EQ(0, m.size());
+
+  EXPECT_EQ(0, m.erase(2));  // Make sure nothing bad happens if we repeat.
+  EXPECT_EQ(0, m.size());
+}
+
+TEST(LinkedHashMapTest, Erase2) {
+  linked_hash_map<int, int> m;
+  ASSERT_EQ(0, m.size());
+  EXPECT_EQ(0, m.erase(2));  // Nothing to erase yet
+
+  m.insert(std::make_pair(2, 12));
+  m.insert(std::make_pair(1, 11));
+  m.insert(std::make_pair(3, 13));
+  m.insert(std::make_pair(4, 14));
+  ASSERT_EQ(4, m.size());
+
+  // Erase middle two
+  EXPECT_EQ(1, m.erase(1));
+  EXPECT_EQ(1, m.erase(3));
+
+  EXPECT_EQ(2, m.size());
+
+  // Make sure we can still iterate over everything that's left.
+  linked_hash_map<int, int>::iterator it = m.begin();
+  ASSERT_TRUE(it != m.end());
+  EXPECT_EQ(12, it->second);
+  ++it;
+  ASSERT_TRUE(it != m.end());
+  EXPECT_EQ(14, it->second);
+  ++it;
+  ASSERT_TRUE(it == m.end());
+
+  EXPECT_EQ(0, m.erase(1));  // Make sure nothing bad happens if we repeat.
+  ASSERT_EQ(2, m.size());
+
+  EXPECT_EQ(1, m.erase(2));
+  EXPECT_EQ(1, m.erase(4));
+  ASSERT_EQ(0, m.size());
+
+  EXPECT_EQ(0, m.erase(1));  // Make sure nothing bad happens if we repeat.
+  ASSERT_EQ(0, m.size());
+}
+
+// Test that erase(iter,iter) and erase(iter) compile and work.
+TEST(LinkedHashMapTest, Erase3) {
+  linked_hash_map<int, int> m;
+
+  m.insert(std::make_pair(1, 11));
+  m.insert(std::make_pair(2, 12));
+  m.insert(std::make_pair(3, 13));
+  m.insert(std::make_pair(4, 14));
+
+  // Erase middle two
+  linked_hash_map<int, int>::iterator it2 = m.find(2);
+  linked_hash_map<int, int>::iterator it4 = m.find(4);
+  EXPECT_EQ(m.erase(it2, it4), m.find(4));
+  EXPECT_EQ(2, m.size());
+
+  // Make sure we can still iterate over everything that's left.
+  linked_hash_map<int, int>::iterator it = m.begin();
+  ASSERT_TRUE(it != m.end());
+  EXPECT_EQ(11, it->second);
+  ++it;
+  ASSERT_TRUE(it != m.end());
+  EXPECT_EQ(14, it->second);
+  ++it;
+  ASSERT_TRUE(it == m.end());
+
+  // Erase first one using an iterator.
+  EXPECT_EQ(m.erase(m.begin()), m.find(4));
+
+  // Only the last element should be left.
+  it = m.begin();
+  ASSERT_TRUE(it != m.end());
+  EXPECT_EQ(14, it->second);
+  ++it;
+  ASSERT_TRUE(it == m.end());
+}
+
+// Test all types of insertion
+TEST(LinkedHashMapTest, Insertion) {
+  linked_hash_map<int, int> m;
+  ASSERT_EQ(0, m.size());
+  std::pair<linked_hash_map<int, int>::iterator, bool> result;
+
+  result = m.insert(std::make_pair(2, 12));
+  ASSERT_EQ(1, m.size());
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(2, result.first->first);
+  EXPECT_EQ(12, result.first->second);
+
+  result = m.insert(std::make_pair(1, 11));
+  ASSERT_EQ(2, m.size());
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(1, result.first->first);
+  EXPECT_EQ(11, result.first->second);
+
+  result = m.insert(std::make_pair(3, 13));
+  linked_hash_map<int, int>::iterator result_iterator = result.first;
+  ASSERT_EQ(3, m.size());
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(3, result.first->first);
+  EXPECT_EQ(13, result.first->second);
+
+  result = m.insert(std::make_pair(3, 13));
+  EXPECT_EQ(3, m.size());
+  EXPECT_FALSE(result.second) << "No insertion should have occurred.";
+  EXPECT_TRUE(result_iterator == result.first)
+      << "Duplicate insertion should have given us the original iterator.";
+
+  std::vector<std::pair<int, int>> v = {{3, 13}, {4, 14}, {5, 15}};
+  m.insert(v.begin(), v.end());
+  // Expect 4 and 5 inserted, 3 not inserted.
+  EXPECT_EQ(5, m.size());
+  EXPECT_EQ(14, m.at(4));
+  EXPECT_EQ(15, m.at(5));
+}
+
+static std::pair<const int, int> Pair(int i, int j) { return {i, j}; }
+
+// Test front accessors.
+TEST(LinkedHashMapTest, Front) {
+  linked_hash_map<int, int> m;
+
+  m.insert(std::make_pair(2, 12));
+  m.insert(std::make_pair(1, 11));
+  m.insert(std::make_pair(3, 13));
+
+  EXPECT_EQ(3, m.size());
+  EXPECT_EQ(Pair(2, 12), m.front());
+  m.pop_front();
+  EXPECT_EQ(2, m.size());
+  EXPECT_EQ(Pair(1, 11), m.front());
+  m.pop_front();
+  EXPECT_EQ(1, m.size());
+  EXPECT_EQ(Pair(3, 13), m.front());
+  m.pop_front();
+  EXPECT_TRUE(m.empty());
+}
+
+// Test back accessors.
+TEST(LinkedHashMapTest, Back) {
+  linked_hash_map<int, int> m;
+
+  m.insert(std::make_pair(2, 12));
+  m.insert(std::make_pair(1, 11));
+  m.insert(std::make_pair(3, 13));
+
+  EXPECT_EQ(3, m.size());
+  EXPECT_EQ(Pair(3, 13), m.back());
+  m.pop_back();
+  EXPECT_EQ(2, m.size());
+  EXPECT_EQ(Pair(1, 11), m.back());
+  m.pop_back();
+  EXPECT_EQ(1, m.size());
+  EXPECT_EQ(Pair(2, 12), m.back());
+  m.pop_back();
+  EXPECT_TRUE(m.empty());
+}
+
+TEST(LinkedHashMapTest, Find) {
+  linked_hash_map<int, int> m;
+
+  EXPECT_TRUE(m.end() == m.find(1))
+      << "We shouldn't find anything in an empty map.";
+
+  m.insert(std::make_pair(2, 12));
+  EXPECT_TRUE(m.end() == m.find(1))
+      << "We shouldn't find an element that doesn't exist in the map.";
+
+  std::pair<linked_hash_map<int, int>::iterator, bool> result =
+      m.insert(std::make_pair(1, 11));
+  ASSERT_TRUE(result.second);
+  ASSERT_TRUE(m.end() != result.first);
+  EXPECT_TRUE(result.first == m.find(1))
+      << "We should have found an element we know exists in the map.";
+  EXPECT_EQ(11, result.first->second);
+
+  // Check that a follow-up insertion doesn't affect our original
+  m.insert(std::make_pair(3, 13));
+  linked_hash_map<int, int>::iterator it = m.find(1);
+  ASSERT_TRUE(m.end() != it);
+  EXPECT_EQ(11, it->second);
+
+  m.clear();
+  EXPECT_TRUE(m.end() == m.find(1))
+      << "We shouldn't find anything in a map that we've cleared.";
+}
+
+TEST(LinkedHashMapTest, Contains) {
+  linked_hash_map<int, int> m;
+
+  EXPECT_FALSE(m.contains(1)) << "An empty map shouldn't contain anything.";
+
+  m.insert(std::make_pair(2, 12));
+  EXPECT_FALSE(m.contains(1))
+      << "The map shouldn't contain an element that doesn't exist.";
+
+  m.insert(std::make_pair(1, 11));
+  EXPECT_TRUE(m.contains(1))
+      << "The map should contain an element that we know exists.";
+
+  m.clear();
+  EXPECT_FALSE(m.contains(1))
+      << "A map that we've cleared shouldn't contain anything.";
+}
+
+TEST(LinkedHashMapTest, At) {
+  linked_hash_map<int, int> m = {{1, 2}};
+  EXPECT_EQ(2, m.at(1));
+  ABSL_BASE_INTERNAL_EXPECT_FAIL(m.at(3), std::out_of_range,
+                                 "linked_hash_map::at");
+
+  const linked_hash_map<int, int> cm = {{1, 2}};
+  EXPECT_EQ(2, cm.at(1));
+  ABSL_BASE_INTERNAL_EXPECT_FAIL(cm.at(3), std::out_of_range,
+                                 "linked_hash_map::at");
+}
+
+TEST(LinkedHashMapTest, Swap) {
+  linked_hash_map<int, int> m1;
+  linked_hash_map<int, int> m2;
+  m1.insert(std::make_pair(1, 1));
+  m1.insert(std::make_pair(2, 2));
+  m2.insert(std::make_pair(3, 3));
+  ASSERT_EQ(2, m1.size());
+  ASSERT_EQ(1, m2.size());
+  m1.swap(m2);
+  ASSERT_EQ(1, m1.size());
+  ASSERT_EQ(2, m2.size());
+}
+
+TEST(LinkedHashMapTest, InitializerList) {
+  linked_hash_map<int, int> m{{1, 2}, {3, 4}};
+  ASSERT_EQ(2, m.size());
+  EXPECT_TRUE(m.contains(1));
+  linked_hash_map<int, int>::iterator it = m.find(1);
+  ASSERT_TRUE(m.end() != it);
+  EXPECT_EQ(2, it->second);
+  EXPECT_TRUE(m.contains(3));
+  it = m.find(3);
+  ASSERT_TRUE(m.end() != it);
+  EXPECT_EQ(4, it->second);
+}
+
+TEST(LinkedHashMapTest, CustomHashAndEquality) {
+  struct CustomIntHash {
+    size_t operator()(int x) const { return x; }
+  };
+  struct CustomIntEq {
+    bool operator()(int x, int y) const { return x == y; }
+  };
+  linked_hash_map<int, int, CustomIntHash, CustomIntEq> m;
+  m.insert(std::make_pair(1, 1));
+  EXPECT_TRUE(m.contains(1));
+  EXPECT_EQ(1, m[1]);
+}
+
+TEST(LinkedHashMapTest, EqualRange) {
+  linked_hash_map<int, int> m{{3, 11}, {1, 13}};
+  const auto& const_m = m;
+
+  EXPECT_THAT(m.equal_range(2), testing::Pair(m.end(), m.end()));
+  EXPECT_THAT(const_m.equal_range(2),
+              testing::Pair(const_m.end(), const_m.end()));
+
+  EXPECT_THAT(m.equal_range(1), testing::Pair(m.find(1), ++m.find(1)));
+  EXPECT_THAT(const_m.equal_range(1),
+              testing::Pair(const_m.find(1), ++const_m.find(1)));
+}
+
+TEST(LinkedHashMapTest, ReserveWorks) {
+  linked_hash_map<int, int> m;
+  EXPECT_EQ(0, m.size());
+  EXPECT_EQ(0.0, m.load_factor());
+  m.reserve(10);
+  EXPECT_LE(10, m.capacity());
+  EXPECT_EQ(0, m.size());
+  EXPECT_EQ(0.0, m.load_factor());
+  m.emplace(1, 1);
+  m.emplace(2, 2);
+  EXPECT_LE(10, m.capacity());
+  EXPECT_EQ(2, m.size());
+  EXPECT_LT(0.0, m.load_factor());
+}
+
+// We require key types to have hash and equals.
+class CopyableMovableType
+    : public absl::test_internal::CopyableMovableInstance {
+ public:
+  using CopyableMovableInstance::CopyableMovableInstance;
+
+  bool operator==(const CopyableMovableType& o) const {
+    return value() == o.value();
+  }
+
+  template <typename H>
+  friend H AbslHashValue(H h, const CopyableMovableType& c) {
+    return H::combine(std::move(h), c.value());
+  }
+};
+
+TEST(LinkedHashMapTest, RValueOperatorAt) {
+  absl::test_internal::InstanceTracker tracker;
+
+  linked_hash_map<CopyableMovableType, int> map;
+  map[CopyableMovableType(1)] = 1;
+  EXPECT_EQ(tracker.copies(), 0);
+  CopyableMovableType c(2);
+  map[std::move(c)] = 2;
+  EXPECT_EQ(tracker.copies(), 0);
+  EXPECT_THAT(map, ElementsAre(Pair(CopyableMovableType(1), 1),
+                               Pair(CopyableMovableType(2), 2)));
+}
+
+TEST(LinkedHashMapTest, HeterogeneousTests) {
+  absl::test_internal::InstanceTracker tracker;
+
+  using MapType = linked_hash_map<ExpensiveType, int, HeterogeneousHash,
+                                  HeterogeneousEqual>;
+  MapType map;
+  ExpensiveType one(1);
+  map[one] = 1;
+  // Two instances: 'one' var and an instance in the map.
+  EXPECT_EQ(2, tracker.instances());
+  EXPECT_EQ(1, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+
+  tracker.ResetCopiesMovesSwaps();
+  map[one] = 5;
+  // No construction since key==1 exists.
+  EXPECT_EQ(2, tracker.instances());
+  EXPECT_EQ(0, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+
+  tracker.ResetCopiesMovesSwaps();
+  map[CheapType(1)] = 10;
+  // No construction since key==1 exists.
+  EXPECT_EQ(2, tracker.instances());
+  EXPECT_EQ(0, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+
+  tracker.ResetCopiesMovesSwaps();
+  map[CheapType(2)] = 20;
+  // Construction since key==2 doesn't exist in the map.
+  EXPECT_EQ(3, tracker.instances());
+  EXPECT_EQ(0, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+  EXPECT_THAT(map, ElementsAre(Pair(HasExpensiveValue(1), 10),
+                               Pair(HasExpensiveValue(2), 20)));
+
+  // find
+  auto itr = map.find(CheapType(1));
+  tracker.ResetCopiesMovesSwaps();
+  ASSERT_NE(itr, map.end());
+  EXPECT_EQ(10, itr->second);
+  // contains
+  EXPECT_TRUE(map.contains(CheapType(2)));
+  // count
+  EXPECT_EQ(1, map.count(CheapType(2)));
+  // equal_range
+  auto eq_itr_pair = map.equal_range(CheapType(2));
+  ASSERT_NE(eq_itr_pair.first, map.end());
+  EXPECT_EQ(20, eq_itr_pair.first->second);
+  // No construction for find, contains, count or equal_range.
+  EXPECT_EQ(3, tracker.instances());
+  EXPECT_EQ(0, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+
+  // insert
+  auto three = MapType::value_type(CheapType(3), 30);
+  tracker.ResetCopiesMovesSwaps();
+  map.insert(three);
+  // Two instances: 'three' var and an instance in the map.
+  EXPECT_EQ(5, tracker.instances());
+  EXPECT_EQ(1, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+
+  tracker.ResetCopiesMovesSwaps();
+  map.insert(three);
+  // No additional construction since key==3 exists.
+  EXPECT_EQ(5, tracker.instances());
+  EXPECT_EQ(0, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+  EXPECT_THAT(map, ElementsAre(Pair(HasExpensiveValue(1), 10),
+                               Pair(HasExpensiveValue(2), 20),
+                               Pair(HasExpensiveValue(3), 30)));
+
+  // Test std::move() using operator[] and insert().
+  ExpensiveType four(4);
+  tracker.ResetCopiesMovesSwaps();
+  map[std::move(four)] = 40;
+  // Two constructions (regular and move).
+  EXPECT_EQ(7, tracker.instances());
+  EXPECT_EQ(0, tracker.copies());
+  EXPECT_EQ(1, tracker.moves());
+
+  auto five = MapType::value_type(CheapType(5), 50);
+  tracker.ResetCopiesMovesSwaps();
+  map.insert(std::move(five));
+  // Two constructions (regular and move).
+  EXPECT_EQ(9, tracker.instances());
+  EXPECT_EQ(1, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+  EXPECT_THAT(map, ElementsAre(Pair(HasExpensiveValue(1), 10),
+                               Pair(HasExpensiveValue(2), 20),
+                               Pair(HasExpensiveValue(3), 30),
+                               Pair(HasExpensiveValue(4), 40),
+                               Pair(HasExpensiveValue(5), 50)));
+
+  // Insert using std::pair.
+  tracker.ResetCopiesMovesSwaps();
+  map.insert(std::make_pair(ExpensiveType(6), 60));
+  EXPECT_EQ(10, tracker.instances());
+  EXPECT_EQ(1, tracker.copies());
+  EXPECT_EQ(2, tracker.moves());
+
+  // Heterogeneous erase.
+  map.erase(CheapType(1));
+  // No construction and instance reduced by one.
+  tracker.ResetCopiesMovesSwaps();
+  EXPECT_EQ(9, tracker.instances());
+  EXPECT_EQ(0, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+  EXPECT_THAT(map, ElementsAre(Pair(HasExpensiveValue(2), 20),
+                               Pair(HasExpensiveValue(3), 30),
+                               Pair(HasExpensiveValue(4), 40),
+                               Pair(HasExpensiveValue(5), 50),
+                               Pair(HasExpensiveValue(6), 60)));
+}
+
+TEST(LinkedHashMapTest, HeterogeneousStringViewLookup) {
+  linked_hash_map<std::string, int> map;
+  map["foo"] = 1;
+  map["bar"] = 2;
+  map["blah"] = 3;
+
+  {
+    absl::string_view lookup("foo");
+    auto itr = map.find(lookup);
+    ASSERT_NE(itr, map.end());
+    EXPECT_EQ(1, itr->second);
+  }
+
+  // Not found.
+  {
+    absl::string_view lookup("foobar");
+    EXPECT_EQ(map.end(), map.find(lookup));
+  }
+
+  {
+    absl::string_view lookup("blah");
+    auto itr = map.find(lookup);
+    ASSERT_NE(itr, map.end());
+    EXPECT_EQ(3, itr->second);
+  }
+}
+
+TEST(LinkedHashMapTest, UniversalReferenceWorks) {
+  linked_hash_map<std::string, int> map;
+  std::string s = "very very very very very long string";
+  map[s] = 1;
+  EXPECT_EQ(s, "very very very very very long string");
+}
+
+TEST(LinkedHashMap, ExtractInsert) {
+  linked_hash_map<int, int> m = {{1, 7}, {2, 9}};
+  auto node = m.extract(1);
+  EXPECT_TRUE(node);
+  EXPECT_EQ(node.key(), 1);
+  EXPECT_EQ(node.mapped(), 7);
+  EXPECT_THAT(m, ElementsAre(Pair(2, 9)));
+  EXPECT_FALSE(m.contains(1));
+  EXPECT_TRUE(m.contains(2));
+
+  node.mapped() = 17;
+  m.insert(std::move(node));
+  EXPECT_FALSE(node);
+  EXPECT_THAT(m, ElementsAre(Pair(2, 9), Pair(1, 17)));
+  EXPECT_TRUE(m.contains(2));
+  EXPECT_TRUE(m.contains(1));
+
+  node = m.extract(m.find(1));
+  EXPECT_TRUE(node);
+  EXPECT_EQ(node.key(), 1);
+  EXPECT_EQ(node.mapped(), 17);
+  EXPECT_THAT(m, ElementsAre(Pair(2, 9)));
+}
+
+TEST(LinkedHashMap, Merge) {
+  linked_hash_map<int, int> m = {{1, 7}, {3, 6}};
+  linked_hash_map<int, int> src = {{1, 10}, {2, 9}, {4, 16}};
+
+  m.merge(src);
+
+  EXPECT_THAT(m, ElementsAre(Pair(1, 7), Pair(3, 6), Pair(2, 9), Pair(4, 16)));
+  EXPECT_THAT(src, ElementsAre(Pair(1, 10)));
+  for (int i : {2, 9, 4}) {
+    EXPECT_FALSE(src.contains(i));
+  }
+}
+
+TEST(LinkedHashMap, Splice) {
+  linked_hash_map<int, int> m = {{1, 7}, {3, 6}};
+  linked_hash_map<int, int> src = {{1, 10}, {2, 9}, {4, 16}};
+  m.splice(m.end(), m, m.begin());
+
+  EXPECT_THAT(m, ElementsAre(Pair(3, 6), Pair(1, 7)));
+
+  m.splice(m.end(), src, ++src.begin());
+  EXPECT_THAT(m, ElementsAre(Pair(3, 6), Pair(1, 7), Pair(2, 9)));
+  EXPECT_THAT(src, ElementsAre(Pair(1, 10), Pair(4, 16)));
+}
+
+TEST(LinkedHashMap, EraseRange) {
+  linked_hash_map<int, int> map = {{1, 1},  {2, 4},  {3, 9},  {4, 16}, {5, 25},
+                                   {6, 36}, {7, 49}, {8, 64}, {9, 81}};
+  auto start = map.find(3);
+  auto end = map.find(8);
+  auto itr = map.erase(start, end);
+  ASSERT_NE(itr, map.end());
+  EXPECT_THAT(*itr, Pair(8, 64));
+  EXPECT_THAT(map,
+              ElementsAre(Pair(1, 1), Pair(2, 4), Pair(8, 64), Pair(9, 81)));
+  for (int i : {1, 2, 8, 9}) {
+    EXPECT_TRUE(map.contains(i));
+  }
+  for (int i : {3, 4, 5, 6, 7}) {
+    EXPECT_FALSE(map.contains(i));
+  }
+}
+
+TEST(LinkedHashMap, InsertInitializerList) {
+  linked_hash_map<int, int> m;
+  m.insert({{1, 7}, {2, 9}, {3, 29}});
+  EXPECT_THAT(m, ElementsAre(Pair(1, 7), Pair(2, 9), Pair(3, 29)));
+}
+
+TEST(LinkedHashMap, InsertOrAssign) {
+  linked_hash_map<int, int> m;
+  {
+    auto [itr, elem_inserted] = m.insert_or_assign(10, 20);
+    EXPECT_THAT(*itr, Pair(10, 20));
+    EXPECT_EQ(elem_inserted, true);
+  }
+  {
+    auto [itr, elem_inserted] = m.insert_or_assign(10, 30);
+    EXPECT_THAT(*itr, Pair(10, 30));
+    EXPECT_EQ(elem_inserted, false);
+  }
+}
+
+TEST(LinkedHashMap, TryEmplace) {
+  linked_hash_map<int, std::pair<int, std::string>> m;
+  {
+    auto [itr, elem_inserted] = m.try_emplace(10, 20, "string");
+    EXPECT_THAT(*itr, Pair(10, Pair(20, "string")));
+    EXPECT_EQ(elem_inserted, true);
+  }
+  {
+    absl::test_internal::InstanceTracker tracker;
+    std::string s = "some string";
+    auto [itr, elem_inserted] = m.try_emplace(10, 2, std::move(s));
+    EXPECT_THAT(*itr, Pair(10, Pair(20, "string")));
+    EXPECT_EQ(elem_inserted, false);
+    EXPECT_THAT(tracker.moves(), 0);
+  }
+}
+
+TEST(LinkedHashMapTest, TryEmplace) {
+  linked_hash_map<int, std::tuple<int, std::string, std::unique_ptr<float>>>
+      map;
+  auto result = map.try_emplace(3, 2, "foo", new float(1.0));
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(std::get<0>(result.first->second), 2);
+  EXPECT_EQ(std::get<1>(result.first->second), "foo");
+  EXPECT_EQ(*std::get<2>(result.first->second), 1.0);
+
+  // Ptr should not be moved.
+  auto ptr = std::make_unique<float>(3.0);
+  auto result2 = map.try_emplace(3, 22, "foo-bar", std::move(ptr));
+  EXPECT_FALSE(result2.second);
+  EXPECT_EQ(std::get<0>(result.first->second), 2);
+  EXPECT_EQ(std::get<1>(result.first->second), "foo");
+  EXPECT_EQ(*std::get<2>(result.first->second), 1.0);
+  EXPECT_NE(ptr.get(), nullptr);
+}
+
+struct CountedHash {
+  explicit CountedHash(int* count) : count(count) {}
+  size_t operator()(int value) const {
+    ++(*count);
+    return value;
+  }
+  int* count = nullptr;
+};
+
+// Makes a map too big for small object optimization.  Counts the number of
+// hashes in `count`, but leaves `count` set to 0.
+linked_hash_map<int, std::string, CountedHash> MakeNonSmallMap(int* count) {
+  const int kFirstKey = -1000;
+  linked_hash_map<int, std::string, CountedHash> m(0, CountedHash(count));
+  for (int i = kFirstKey; i < kFirstKey + 100; ++i) {
+    m[i] = "foo";
+  }
+  *count = 0;
+  return m;
+}
+
+constexpr bool BuildHasDebugModeRehashes() {
+#if !defined(NDEBUG) || defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+    defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER)
+  return true;
+#else
+  return false;
+#endif
+}
+
+TEST(LinkedHashMapTest, HashCountInOptBuilds) {
+  if (BuildHasDebugModeRehashes()) {
+    GTEST_SKIP() << "Only run under NDEBUG: `assert` statements and sanitizer "
+                    "rehashing may cause redundant hashing.";
+  }
+
+  using Map = linked_hash_map<int, std::string, CountedHash>;
+  {
+    int count = 0;
+    Map m = MakeNonSmallMap(&count);
+    m.insert({1, "foo"});
+    EXPECT_EQ(count, 1);
+    m.erase(1);
+    EXPECT_EQ(count, 2);
+  }
+  {
+    int count = 0;
+    Map m = MakeNonSmallMap(&count);
+    m[2] = "bar";
+    EXPECT_EQ(count, 1);
+  }
+  {
+    int count = 0;
+    Map m = MakeNonSmallMap(&count);
+    m.insert({3, "baz"});
+    EXPECT_EQ(count, 1);
+    auto node = m.extract(3);
+    EXPECT_EQ(count, 2);
+    m.insert(std::move(node));
+    EXPECT_EQ(count, 3);
+  }
+  {
+    int count = 0;
+    Map m = MakeNonSmallMap(&count);
+    m.insert_or_assign(4, "qux");
+    EXPECT_EQ(count, 1);
+  }
+  {
+    int count = 0;
+    Map m = MakeNonSmallMap(&count);
+    m.emplace(5, "vog");
+    EXPECT_EQ(count, 1);
+  }
+  {
+    int count = 0;
+    Map m = MakeNonSmallMap(&count);
+    m.try_emplace(6, 'x', 42);
+    EXPECT_EQ(count, 1);
+  }
+  {
+    int src_count = 0, dst_count = 0;
+    Map src = MakeNonSmallMap(&src_count);
+    Map dst = MakeNonSmallMap(&dst_count);
+    src[7] = "siete";
+    dst.merge(src);
+    EXPECT_LE(src_count, 200);
+    EXPECT_LE(dst_count, 200);
+  }
+}
+
+}  // namespace
+}  // namespace container_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/container/linked_hash_set.h b/absl/container/linked_hash_set.h
new file mode 100644
index 0000000..f874cd1
--- /dev/null
+++ b/absl/container/linked_hash_set.h
@@ -0,0 +1,524 @@
+// Copyright 2025 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: linked_hash_set.h
+// -----------------------------------------------------------------------------
+//
+// This is a simple insertion-ordered set. It provides O(1) amortized
+// insertions and lookups, as well as iteration over the set in the insertion
+// order.
+//
+// This class is thread-compatible.
+//
+// Iterators point into the list and should be stable in the face of
+// mutations, except for an iterator pointing to an element that was just
+// deleted.
+//
+// This class supports heterogeneous lookups.
+
+#ifndef ABSL_CONTAINER_LINKED_HASH_SET_H_
+#define ABSL_CONTAINER_LINKED_HASH_SET_H_
+
+#include <cassert>
+#include <cstddef>
+#include <initializer_list>
+#include <list>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/container/flat_hash_set.h"
+#include "absl/container/internal/common.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+template <
+    typename Key, typename KeyHash = typename absl::flat_hash_set<Key>::hasher,
+    typename KeyEq = typename absl::flat_hash_set<Key, KeyHash>::key_equal,
+    typename Alloc = std::allocator<Key>>
+class linked_hash_set {
+  using KeyArgImpl = absl::container_internal::KeyArg<
+      absl::container_internal::IsTransparent<KeyEq>::value &&
+      absl::container_internal::IsTransparent<KeyHash>::value>;
+
+ public:
+  using key_type = Key;
+  using hasher = KeyHash;
+  using key_equal = KeyEq;
+  using value_type = key_type;
+  using allocator_type = Alloc;
+  using difference_type = ptrdiff_t;
+
+ private:
+  template <class K>
+  using key_arg = typename KeyArgImpl::template type<K, key_type>;
+
+  using ListType = std::list<key_type, Alloc>;
+
+  template <class Fn>
+  class Wrapped {
+    template <typename K>
+    static const K& ToKey(const K& k) {
+      return k;
+    }
+    static const key_type& ToKey(typename ListType::const_iterator it) {
+      return *it;
+    }
+    static const key_type& ToKey(typename ListType::iterator it) { return *it; }
+
+    Fn fn_;
+
+    friend linked_hash_set;
+
+   public:
+    using is_transparent = void;
+
+    Wrapped() = default;
+    explicit Wrapped(Fn fn) : fn_(std::move(fn)) {}
+
+    template <class... Args>
+    auto operator()(Args&&... args) const
+        -> decltype(this->fn_(ToKey(args)...)) {
+      return fn_(ToKey(args)...);
+    }
+  };
+  using SetType =
+      absl::flat_hash_set<typename ListType::iterator, Wrapped<hasher>,
+                          Wrapped<key_equal>, Alloc>;
+
+  class NodeHandle {
+   public:
+    using allocator_type = linked_hash_set::allocator_type;
+    using value_type = linked_hash_set::value_type;
+
+    constexpr NodeHandle() noexcept = default;
+    NodeHandle(NodeHandle&& nh) noexcept = default;
+    ~NodeHandle() = default;
+    NodeHandle& operator=(NodeHandle&& node) noexcept = default;
+    bool empty() const noexcept { return list_.empty(); }
+    explicit operator bool() const noexcept { return !empty(); }
+    allocator_type get_allocator() const { return list_.get_allocator(); }
+    value_type& value() { return list_.front(); }
+    void swap(NodeHandle& nh) noexcept { list_.swap(nh.list_); }
+
+   private:
+    friend linked_hash_set;
+
+    explicit NodeHandle(ListType list) : list_(std::move(list)) {}
+    ListType list_;
+  };
+
+  template <class Iterator, class NodeType>
+  struct InsertReturnType {
+    Iterator position;
+    bool inserted;
+    NodeType node;
+  };
+
+ public:
+  using iterator = typename ListType::const_iterator;
+  using const_iterator = typename ListType::const_iterator;
+  using reverse_iterator = typename ListType::const_reverse_iterator;
+  using const_reverse_iterator = typename ListType::const_reverse_iterator;
+  using reference = typename ListType::reference;
+  using const_reference = typename ListType::const_reference;
+  using pointer = typename std::allocator_traits<allocator_type>::pointer;
+  using const_pointer =
+      typename std::allocator_traits<allocator_type>::const_pointer;
+  using size_type = typename ListType::size_type;
+  using node_type = NodeHandle;
+  using insert_return_type = InsertReturnType<iterator, node_type>;
+
+  linked_hash_set() {}
+
+  explicit linked_hash_set(size_t bucket_count, const hasher& hash = hasher(),
+                           const key_equal& eq = key_equal(),
+                           const allocator_type& alloc = allocator_type())
+      : set_(bucket_count, Wrapped<hasher>(hash), Wrapped<key_equal>(eq),
+             alloc),
+        list_(alloc) {}
+
+  linked_hash_set(size_t bucket_count, const hasher& hash,
+                  const allocator_type& alloc)
+      : linked_hash_set(bucket_count, hash, key_equal(), alloc) {}
+
+  linked_hash_set(size_t bucket_count, const allocator_type& alloc)
+      : linked_hash_set(bucket_count, hasher(), key_equal(), alloc) {}
+
+  explicit linked_hash_set(const allocator_type& alloc)
+      : linked_hash_set(0, hasher(), key_equal(), alloc) {}
+
+  template <class InputIt>
+  linked_hash_set(InputIt first, InputIt last, size_t bucket_count = 0,
+                  const hasher& hash = hasher(),
+                  const key_equal& eq = key_equal(),
+                  const allocator_type& alloc = allocator_type())
+      : linked_hash_set(bucket_count, hash, eq, alloc) {
+    insert(first, last);
+  }
+
+  template <class InputIter>
+  linked_hash_set(InputIter first, InputIter last, size_t bucket_count,
+                  const hasher& hash, const allocator_type& alloc)
+      : linked_hash_set(first, last, bucket_count, hash, key_equal(), alloc) {}
+
+  template <class InputIter>
+  linked_hash_set(InputIter first, InputIter last, size_t bucket_count,
+                  const allocator_type& alloc)
+      : linked_hash_set(first, last, bucket_count, hasher(), key_equal(),
+                        alloc) {}
+
+  template <class InputIt>
+  linked_hash_set(InputIt first, InputIt last, const allocator_type& alloc)
+      : linked_hash_set(first, last, /*bucket_count=*/0, hasher(), key_equal(),
+                        alloc) {}
+
+  linked_hash_set(std::initializer_list<key_type> init, size_t bucket_count = 0,
+                  const hasher& hash = hasher(),
+                  const key_equal& eq = key_equal(),
+                  const allocator_type& alloc = allocator_type())
+      : linked_hash_set(init.begin(), init.end(), bucket_count, hash, eq,
+                        alloc) {}
+
+  linked_hash_set(std::initializer_list<key_type> init, size_t bucket_count,
+                  const allocator_type& alloc)
+      : linked_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {}
+
+  linked_hash_set(std::initializer_list<key_type> init, size_t bucket_count,
+                  const hasher& hash, const allocator_type& alloc)
+      : linked_hash_set(init, bucket_count, hash, key_equal(), alloc) {}
+
+  linked_hash_set(std::initializer_list<key_type> init,
+                  const allocator_type& alloc)
+      : linked_hash_set(init, /*bucket_count=*/0, hasher(), key_equal(),
+                        alloc) {}
+
+  linked_hash_set(const linked_hash_set& other)
+      : linked_hash_set(other.bucket_count(), other.hash_function(),
+                        other.key_eq(), other.get_allocator()) {
+    CopyFrom(other);
+  }
+
+  linked_hash_set(const linked_hash_set& other, const allocator_type& alloc)
+      : linked_hash_set(other.bucket_count(), other.hash_function(),
+                        other.key_eq(), alloc) {
+    CopyFrom(other);
+  }
+
+  linked_hash_set(linked_hash_set&& other) noexcept
+      : set_(std::move(other.set_)), list_(std::move(other.list_)) {
+    // Since the list and set must agree for other to end up "valid",
+    // explicitly clear them.
+    other.set_.clear();
+    other.list_.clear();
+  }
+
+  linked_hash_set(linked_hash_set&& other, const allocator_type& alloc)
+      : linked_hash_set(0, other.hash_function(), other.key_eq(), alloc) {
+    if (get_allocator() == other.get_allocator()) {
+      *this = std::move(other);
+    } else {
+      CopyFrom(std::move(other));
+    }
+  }
+
+  linked_hash_set& operator=(const linked_hash_set& other) {
+    if (this == &other) return *this;
+    // Make a new set, with other's hash/eq/alloc.
+    set_ = SetType(other.bucket_count(), other.set_.hash_function(),
+                   other.set_.key_eq(), other.get_allocator());
+    // Copy the list, with other's allocator.
+    list_ = ListType(other.get_allocator());
+    CopyFrom(other);
+    return *this;
+  }
+
+  linked_hash_set& operator=(linked_hash_set&& other) noexcept {
+    set_ = std::move(other.set_);
+    list_ = std::move(other.list_);
+    other.set_.clear();
+    other.list_.clear();
+    return *this;
+  }
+
+  linked_hash_set& operator=(std::initializer_list<key_type> values) {
+    clear();
+    insert(values.begin(), values.end());
+    return *this;
+  }
+
+  // Derive size from set_, as list::size might be O(N).
+  size_type size() const { return set_.size(); }
+  size_type max_size() const noexcept { return ~size_type{}; }
+  bool empty() const { return set_.empty(); }
+
+  // Iteration is list-like, in insertion order.
+  // These are all forwarded.
+  iterator begin() { return list_.begin(); }
+  iterator end() { return list_.end(); }
+  const_iterator begin() const { return list_.begin(); }
+  const_iterator end() const { return list_.end(); }
+  const_iterator cbegin() const { return list_.cbegin(); }
+  const_iterator cend() const { return list_.cend(); }
+  reverse_iterator rbegin() { return list_.rbegin(); }
+  reverse_iterator rend() { return list_.rend(); }
+  const_reverse_iterator rbegin() const { return list_.rbegin(); }
+  const_reverse_iterator rend() const { return list_.rend(); }
+  const_reverse_iterator crbegin() const { return list_.crbegin(); }
+  const_reverse_iterator crend() const { return list_.crend(); }
+  reference front() { return list_.front(); }
+  reference back() { return list_.back(); }
+  const_reference front() const { return list_.front(); }
+  const_reference back() const { return list_.back(); }
+
+  void pop_front() { erase(begin()); }
+  void pop_back() { erase(std::prev(end())); }
+
+  ABSL_ATTRIBUTE_REINITIALIZES void clear() {
+    set_.clear();
+    list_.clear();
+  }
+
+  void reserve(size_t n) { set_.reserve(n); }
+  size_t bucket_count() const { return set_.bucket_count(); }
+  size_t capacity() const { return set_.capacity(); }
+  float load_factor() const { return set_.load_factor(); }
+
+  hasher hash_function() const { return set_.hash_function().fn_; }
+  key_equal key_eq() const { return set_.key_eq().fn_; }
+  allocator_type get_allocator() const { return list_.get_allocator(); }
+
+  template <typename K = key_type>
+  size_type erase(const key_arg<K>& key) {
+    auto found = set_.find(key);
+    if (found == set_.end()) return 0;
+    auto list_it = *found;
+    // Erase set entry first since it refers to the list element.
+    set_.erase(found);
+    list_.erase(list_it);
+    return 1;
+  }
+
+  iterator erase(const_iterator position) {
+    auto found = set_.find(position);
+    assert(*found == position);
+    set_.erase(found);
+    return list_.erase(position);
+  }
+
+  iterator erase(const_iterator first, const_iterator last) {
+    while (first != last) first = erase(first);
+    return first;
+  }
+
+  template <typename K = key_type>
+  iterator find(const key_arg<K>& key) {
+    auto found = set_.find(key);
+    if (found == set_.end()) return end();
+    return *found;
+  }
+
+  template <typename K = key_type>
+  const_iterator find(const key_arg<K>& key) const {
+    auto found = set_.find(key);
+    if (found == set_.end()) return end();
+    return *found;
+  }
+
+  template <typename K = key_type>
+  size_t count(const key_arg<K>& key) const {
+    return contains(key) ? 1 : 0;
+  }
+  template <typename K = key_type>
+  bool contains(const key_arg<K>& key) const {
+    return set_.contains(key);
+  }
+
+  template <typename K = key_type>
+  std::pair<iterator, iterator> equal_range(const key_arg<K>& key) {
+    auto iter = set_.find(key);
+    if (iter == set_.end()) return {end(), end()};
+    return {*iter, std::next(*iter)};
+  }
+
+  template <typename K = key_type>
+  std::pair<const_iterator, const_iterator> equal_range(
+      const key_arg<K>& key) const {
+    auto iter = set_.find(key);
+    if (iter == set_.end()) return {end(), end()};
+    return {*iter, std::next(*iter)};
+  }
+
+  template <typename K = key_type>
+  std::pair<iterator, bool> insert(const key_arg<K>& k) {
+    return InsertInternal(list_.end(), k);
+  }
+  template <typename K = key_type, K* = nullptr>
+  std::pair<iterator, bool> insert(key_arg<K>&& k) {
+    return InsertInternal(list_.end(), std::move(k));
+  }
+
+  template <typename K = key_type,
+            std::enable_if_t<
+                !std::is_convertible_v<const key_arg<K>&, const_iterator> &&
+                    !std::is_convertible_v<const key_arg<K>&, iterator>,
+                int> = 0>
+  iterator insert(const_iterator hint, const key_arg<K>& k) {
+    return InsertInternal(hint, k).first;
+  }
+  template <
+      typename K = key_type, K* = nullptr,
+      std::enable_if_t<!std::is_convertible_v<key_arg<K>&&, const_iterator> &&
+                           !std::is_convertible_v<key_arg<K>&&, iterator>,
+                       int> = 0>
+  iterator insert(const_iterator hint, key_arg<K>&& k) {
+    return InsertInternal(hint, std::move(k)).first;
+  }
+
+  void insert(std::initializer_list<key_type> ilist) {
+    insert(ilist.begin(), ilist.end());
+  }
+
+  template <class InputIt>
+  void insert(InputIt first, InputIt last) {
+    for (; first != last; ++first) insert(*first);
+  }
+
+  insert_return_type insert(node_type&& node) {
+    if (node.empty()) return {end(), false, node_type()};
+    if (auto [set_itr, inserted] = set_.emplace(node.list_.begin()); inserted) {
+      list_.splice(list_.end(), node.list_);
+      return {*set_itr, true, node_type()};
+    } else {
+      return {*set_itr, false, std::move(node)};
+    }
+  }
+
+  iterator insert(const_iterator, node_type&& node) {
+    return insert(std::move(node)).first;
+  }
+
+  template <typename... Args>
+  std::pair<iterator, bool> emplace(Args&&... args) {
+    return EmplaceInternal(list_.end(), std::forward<Args>(args)...);
+  }
+
+  template <typename... Args>
+  iterator emplace_hint(const_iterator hint, Args&&... args) {
+    return EmplaceInternal(hint, std::forward<Args>(args)...).first;
+  }
+
+  template <typename H, typename E>
+  void merge(linked_hash_set<Key, H, E, Alloc>& src) {
+    auto itr = src.list_.begin();
+    while (itr != src.list_.end()) {
+      if (contains(*itr)) {
+        ++itr;
+      } else {
+        insert(src.extract(itr++));
+      }
+    }
+  }
+
+  template <typename H, typename E>
+  void merge(linked_hash_set<Key, H, E, Alloc>&& src) {
+    merge(src);
+  }
+
+  node_type extract(const_iterator position) {
+    set_.erase(position);
+    ListType extracted_node_list;
+    extracted_node_list.splice(extracted_node_list.end(), list_, position);
+    return node_type(std::move(extracted_node_list));
+  }
+
+  template <class K = key_type, typename std::enable_if_t<
+                                    !std::is_same<K, iterator>::value, int> = 0>
+  node_type extract(const key_arg<K>& key) {
+    auto node = set_.extract(key);
+    if (node.empty()) return node_type();
+    ListType extracted_node_list;
+    extracted_node_list.splice(extracted_node_list.end(), list_, node.value());
+    return node_type(std::move(extracted_node_list));
+  }
+
+  void swap(linked_hash_set& other) noexcept {
+    using std::swap;
+    swap(set_, other.set_);
+    swap(list_, other.list_);
+  }
+
+  friend bool operator==(const linked_hash_set& a, const linked_hash_set& b) {
+    if (a.size() != b.size()) return false;
+    const linked_hash_set* outer = &a;
+    const linked_hash_set* inner = &b;
+    if (outer->capacity() > inner->capacity()) std::swap(outer, inner);
+    for (const value_type& elem : *outer)
+      if (!inner->contains(elem)) return false;
+    return true;
+  }
+
+  friend bool operator!=(const linked_hash_set& a, const linked_hash_set& b) {
+    return !(a == b);
+  }
+
+  void rehash(size_t n) { set_.rehash(n); }
+
+ private:
+  template <typename Other>
+  void CopyFrom(Other&& other) {
+    for (auto& elem : other.list_) {
+      set_.insert(list_.insert(list_.end(), std::move(elem)));
+    }
+    assert(set_.size() == list_.size());
+  }
+
+  template <typename... Args>
+  std::pair<iterator, bool> EmplaceInternal(const_iterator hint,
+                                            Args&&... args) {
+    ListType node_donor;
+    auto list_iter =
+        node_donor.emplace(node_donor.end(), std::forward<Args>(args)...);
+    auto ins = set_.insert(list_iter);
+    if (!ins.second) return {*ins.first, false};
+    list_.splice(hint, node_donor, list_iter);
+    return {list_iter, true};
+  }
+
+  template <typename U>
+  std::pair<iterator, bool> InsertInternal(const_iterator hint,
+                                           U&& key) {  // NOLINT(build/c++11)
+    bool constructed = false;
+    auto set_iter = set_.lazy_emplace(key, [&](const auto& ctor) {
+      constructed = true;
+      ctor(list_.emplace(hint, std::forward<U>(key)));
+    });
+    return {*set_iter, constructed};
+  }
+
+  // The set component, used for speedy lookups.
+  SetType set_;
+
+  // The list component, used for maintaining insertion order.
+  ListType list_;
+};
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_CONTAINER_LINKED_HASH_SET_H_
diff --git a/absl/container/linked_hash_set_benchmark.cc b/absl/container/linked_hash_set_benchmark.cc
new file mode 100644
index 0000000..e790e7d
--- /dev/null
+++ b/absl/container/linked_hash_set_benchmark.cc
@@ -0,0 +1,84 @@
+// Copyright 2025 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <algorithm>
+#include <cstddef>
+#include <string>
+#include <vector>
+
+#include "absl/container/linked_hash_set.h"
+#include "absl/functional/function_ref.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+void BenchmarkInsertStrings(benchmark::State& state,
+                            absl::FunctionRef<std::string(int)> factory) {
+  std::vector<std::string> sample;
+  size_t str_bytes = 0;
+  for (int i = 0; i < state.range(0); ++i) {
+    sample.push_back(factory(i));
+    str_bytes += sample.back().size();
+  }
+
+  // Make a batch around 1Mi bytes.
+  const size_t batch_size = std::max(size_t{1}, size_t{1000000} / str_bytes);
+  std::vector<absl::linked_hash_set<std::string>> sets(batch_size);
+
+  while (state.KeepRunningBatch(batch_size)) {
+    state.PauseTiming();
+    for (auto& set : sets) set.clear();
+    state.ResumeTiming();
+    for (auto& set : sets) {
+      for (const auto& str : sample) {
+        benchmark::DoNotOptimize(set.insert(str));
+      }
+    }
+  }
+
+  state.SetItemsProcessed(state.iterations() * state.range(0));
+  state.SetBytesProcessed(state.iterations() * str_bytes);
+}
+
+constexpr absl::string_view kFormatShort = "%10d";
+constexpr absl::string_view kFormatLong =
+    "a longer string that exceeds the SSO %10d";
+
+void BM_InsertShortStrings_Hit(benchmark::State& state) {
+  BenchmarkInsertStrings(
+      state, [](int i) { return absl::StrFormat(kFormatShort, i); });
+}
+BENCHMARK(BM_InsertShortStrings_Hit)->Range(1, 1 << 16);
+
+void BM_InsertLongStrings_Hit(benchmark::State& state) {
+  BenchmarkInsertStrings(state,
+                         [](int i) { return absl::StrFormat(kFormatLong, i); });
+}
+BENCHMARK(BM_InsertLongStrings_Hit)->Range(1, 1 << 16);
+
+void BM_InsertShortStrings_Miss(benchmark::State& state) {
+  BenchmarkInsertStrings(
+      state, [](int i) { return absl::StrFormat(kFormatShort, i % 20); });
+}
+BENCHMARK(BM_InsertShortStrings_Miss)->Range(1, 1 << 16);
+
+void BM_InsertLongStrings_Miss(benchmark::State& state) {
+  BenchmarkInsertStrings(
+      state, [](int i) { return absl::StrFormat(kFormatLong, i % 20); });
+}
+BENCHMARK(BM_InsertLongStrings_Miss)->Range(1, 1 << 16);
+
+}  // namespace
diff --git a/absl/container/linked_hash_set_test.cc b/absl/container/linked_hash_set_test.cc
new file mode 100644
index 0000000..b641b97
--- /dev/null
+++ b/absl/container/linked_hash_set_test.cc
@@ -0,0 +1,917 @@
+// Copyright 2025 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/container/linked_hash_set.h"
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/container/internal/hash_generator_testing.h"
+#include "absl/container/internal/hash_policy_testing.h"
+#include "absl/container/internal/heterogeneous_lookup_testing.h"
+#include "absl/container/internal/test_instance_tracker.h"
+#include "absl/container/internal/unordered_set_constructor_test.h"
+#include "absl/container/internal/unordered_set_lookup_test.h"
+#include "absl/container/internal/unordered_set_members_test.h"
+#include "absl/container/internal/unordered_set_modifiers_test.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace container_internal {
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::ElementsAreArray;
+using ::testing::Pointee;
+
+template <class T>
+using Set =
+    linked_hash_set<T, StatefulTestingHash, StatefulTestingEqual, Alloc<T>>;
+
+using SetTypes =
+    ::testing::Types<Set<int>, Set<std::string>, Set<Enum>, Set<EnumClass>>;
+
+INSTANTIATE_TYPED_TEST_SUITE_P(LinkedHashSet, ConstructorTest, SetTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(LinkedHashSet, LookupTest, SetTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(LinkedHashSet, MembersTest, SetTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(LinkedHashSet, ModifiersTest, SetTypes);
+
+// Tests that the range constructor works.
+TEST(LinkedHashSetTest, RangeConstruct) {
+  const auto items = {1, 2, 3};
+  EXPECT_THAT(linked_hash_set<int>(items.begin(), items.end()),
+              ElementsAre(1, 2, 3));
+}
+
+// Tests that copying works.
+TEST(LinkedHashSetTest, Copy) {
+  linked_hash_set<int> m{4, 8, 15, 16, 23, 42};
+  auto copy = m;
+
+  auto found = copy.find(8);
+  ASSERT_TRUE(found != copy.end());
+  for (auto iter = copy.begin(); iter != copy.end(); ++iter) {
+    if (iter == found) return;
+  }
+  FAIL() << "Copied set's find method returned an invalid iterator.";
+}
+
+// Tests that assignment works.
+TEST(LinkedHashSetTest, Assign) {
+  linked_hash_set<int> m{2, 3};
+  linked_hash_set<int> n{4};
+
+  n = m;
+  EXPECT_TRUE(n.contains(2));
+  auto found = n.find(2);
+  ASSERT_TRUE(found != n.end());
+  for (auto iter = n.begin(); iter != n.end(); ++iter) {
+    if (iter == found) return;
+  }
+  FAIL() << "Assigned set's find method returned an invalid iterator.";
+}
+
+// Tests that move constructor works.
+TEST(LinkedHashSetTest, Move) {
+  // Use unique_ptr as an example of a non-copyable type.
+  linked_hash_set<std::unique_ptr<int>> m;
+  m.insert(std::make_unique<int>(2));
+  m.insert(std::make_unique<int>(3));
+  linked_hash_set<std::unique_ptr<int>> n = std::move(m);
+  EXPECT_THAT(n, ElementsAre(Pointee(2), Pointee(3)));
+}
+
+struct IntUniquePtrHash {
+  size_t operator()(const std::unique_ptr<int>& p) const {
+    return static_cast<size_t>(*p);
+  }
+};
+
+struct IntUniquePtrEq {
+  size_t operator()(const std::unique_ptr<int>& a,
+                    const std::unique_ptr<int>& b) const {
+    return *a == *b;
+  }
+};
+
+// Pretty artificial for a set, but unique_ptr is a convenient move-only type.
+TEST(LinkedHashSetTest, CanInsertMoveOnly) {
+  linked_hash_set<std::unique_ptr<int>, IntUniquePtrHash, IntUniquePtrEq> s;
+  std::vector<int> data = {4, 8, 15, 16, 23, 42};
+  for (int x : data) s.insert(std::make_unique<int>(x));
+  EXPECT_EQ(s.size(), data.size());
+  for (const std::unique_ptr<int>& elt : s) {
+    EXPECT_TRUE(s.contains(elt));
+    EXPECT_TRUE(s.find(elt) != s.end());
+  }
+}
+
+TEST(LinkedHashSetTest, CanMoveMoveOnly) {
+  linked_hash_set<std::unique_ptr<int>, IntUniquePtrHash, IntUniquePtrEq> s;
+  std::vector<int> data = {4, 8, 15, 16, 23, 42};
+  for (int x : data) s.insert(std::make_unique<int>(x));
+  linked_hash_set<std::unique_ptr<int>, IntUniquePtrHash, IntUniquePtrEq> ss =
+      std::move(s);
+  EXPECT_EQ(ss.size(), data.size());
+}
+
+TEST(LinkedHashSetTest, CanEmplaceMoveOnly) {
+  linked_hash_set<std::unique_ptr<int>, IntUniquePtrHash, IntUniquePtrEq> s;
+  std::vector<int> data = {4, 8, 15, 16, 23, 42};
+  for (const int x : data) {
+    s.emplace(new int{x});
+  }
+  EXPECT_EQ(s.size(), data.size());
+  for (const std::unique_ptr<int>& elt : s) {
+    EXPECT_TRUE(s.contains(elt));
+    EXPECT_TRUE(s.find(elt) != s.end());
+  }
+}
+
+TEST(LinkedHashSetTest, CanInsertTransparent) {
+  linked_hash_set<std::string> s;
+  s.insert(absl::string_view("foo"));
+  s.insert(absl::string_view("bar"));
+  s.insert(absl::string_view("foo"));
+  EXPECT_THAT(s, ElementsAre("foo", "bar"));
+}
+
+// Tests that iteration from begin() to end() works
+TEST(LinkedHashSetTest, Iteration) {
+  linked_hash_set<int> m;
+  EXPECT_TRUE(m.begin() == m.end());
+
+  m.insert(2);
+  m.insert(1);
+  m.insert(3);
+
+  linked_hash_set<int>::iterator i = m.begin();
+  ASSERT_TRUE(m.begin() == i);
+  ASSERT_TRUE(m.end() != i);
+  EXPECT_EQ(2, *i);
+
+  ++i;
+  ASSERT_TRUE(m.end() != i);
+  EXPECT_EQ(1, *i);
+
+  ++i;
+  ASSERT_TRUE(m.end() != i);
+  EXPECT_EQ(3, *i);
+
+  ++i;
+  ASSERT_TRUE(m.end() == i);
+}
+
+// Tests that reverse iteration from rbegin() to rend() works
+TEST(LinkedHashSetTest, ReverseIteration) {
+  linked_hash_set<int> m;
+  EXPECT_TRUE(m.rbegin() == m.rend());
+
+  m.insert(2);
+  m.insert(1);
+  m.insert(3);
+
+  linked_hash_set<int>::reverse_iterator i = m.rbegin();
+  ASSERT_TRUE(m.rbegin() == i);
+  ASSERT_TRUE(m.rend() != i);
+  EXPECT_EQ(3, *i);
+
+  ++i;
+  ASSERT_TRUE(m.rend() != i);
+  EXPECT_EQ(1, *i);
+
+  ++i;
+  ASSERT_TRUE(m.rend() != i);
+  EXPECT_EQ(2, *i);
+
+  ++i;
+  ASSERT_TRUE(m.rend() == i);
+}
+
+// Tests that clear() works
+TEST(LinkedHashSetTest, Clear) {
+  linked_hash_set<int> m{2, 1, 3};
+  ASSERT_EQ(3, m.size());
+
+  m.clear();
+  EXPECT_EQ(0, m.size());
+  EXPECT_FALSE(m.contains(1));
+  EXPECT_TRUE(m.find(1) == m.end());
+
+  // Make sure we can call it on an empty set.
+  m.clear();
+  EXPECT_EQ(0, m.size());
+}
+
+// Tests that size() works.
+TEST(LinkedHashSetTest, Size) {
+  linked_hash_set<int> m;
+  EXPECT_EQ(0, m.size());
+  m.insert(2);
+  EXPECT_EQ(1, m.size());
+  m.insert(11);
+  EXPECT_EQ(2, m.size());
+  m.insert(0);
+  EXPECT_EQ(3, m.size());
+  m.insert(0);
+  EXPECT_EQ(3, m.size());
+  m.clear();
+  EXPECT_EQ(0, m.size());
+}
+
+// Tests empty()
+TEST(LinkedHashSetTest, Empty) {
+  linked_hash_set<int> m;
+  ASSERT_TRUE(m.empty());
+  m.insert(2);
+  ASSERT_FALSE(m.empty());
+  m.clear();
+  ASSERT_TRUE(m.empty());
+}
+
+TEST(LinkedHashSetTest, Erase) {
+  linked_hash_set<int> m;
+  ASSERT_EQ(0, m.size());
+  EXPECT_EQ(0, m.erase(2));  // Nothing to erase yet
+
+  m.insert(2);
+  ASSERT_EQ(1, m.size());
+  EXPECT_EQ(1, m.erase(2));
+  EXPECT_EQ(0, m.size());
+  EXPECT_TRUE(m.empty());
+
+  EXPECT_EQ(0, m.erase(2));  // Make sure nothing bad happens if we repeat.
+  EXPECT_EQ(0, m.size());
+  EXPECT_TRUE(m.empty());
+}
+
+TEST(LinkedHashSetTest, Erase2) {
+  linked_hash_set<int> m;
+  ASSERT_EQ(0, m.size());
+  EXPECT_EQ(0, m.erase(2));  // Nothing to erase yet
+
+  m.insert(2);
+  m.insert(1);
+  m.insert(3);
+  m.insert(4);
+  ASSERT_EQ(4, m.size());
+
+  // Erase middle two
+  EXPECT_EQ(1, m.erase(1));
+  EXPECT_EQ(1, m.erase(3));
+
+  EXPECT_EQ(2, m.size());
+
+  // Make sure we can still iterate over everything that's left.
+  linked_hash_set<int>::iterator it = m.begin();
+  ASSERT_TRUE(it != m.end());
+  EXPECT_EQ(2, *it);
+  ++it;
+  ASSERT_TRUE(it != m.end());
+  EXPECT_EQ(4, *it);
+  ++it;
+  ASSERT_TRUE(it == m.end());
+
+  EXPECT_EQ(0, m.erase(1));  // Make sure nothing bad happens if we repeat.
+  ASSERT_EQ(2, m.size());
+
+  EXPECT_EQ(1, m.erase(2));
+  EXPECT_EQ(1, m.erase(4));
+  ASSERT_EQ(0, m.size());
+  EXPECT_TRUE(m.empty());
+
+  EXPECT_EQ(0, m.erase(1));  // Make sure nothing bad happens if we repeat.
+  ASSERT_EQ(0, m.size());
+  EXPECT_TRUE(m.empty());
+}
+
+// Test that erase(iter,iter) and erase(iter) compile and work.
+TEST(LinkedHashSetTest, Erase3) {
+  linked_hash_set<int> m;
+
+  m.insert(1);
+  m.insert(2);
+  m.insert(3);
+  m.insert(4);
+
+  // Erase middle two
+  linked_hash_set<int>::iterator it2 = m.find(2);
+  linked_hash_set<int>::iterator it4 = m.find(4);
+  EXPECT_EQ(m.erase(it2, it4), m.find(4));
+  EXPECT_FALSE(m.contains(2));
+  EXPECT_TRUE(m.find(2) == m.end());
+  EXPECT_FALSE(m.contains(3));
+  EXPECT_TRUE(m.find(3) == m.end());
+  EXPECT_EQ(2, m.size());
+
+  // Make sure we can still iterate over everything that's left.
+  linked_hash_set<int>::iterator it = m.begin();
+  ASSERT_TRUE(it != m.end());
+  EXPECT_EQ(1, *it);
+  ++it;
+  ASSERT_TRUE(it != m.end());
+  EXPECT_EQ(4, *it);
+  ++it;
+  ASSERT_TRUE(it == m.end());
+
+  // Erase first one using an iterator.
+  EXPECT_EQ(m.erase(m.begin()), m.find(4));
+  EXPECT_FALSE(m.contains(1));
+  EXPECT_TRUE(m.find(1) == m.end());
+
+  // Only the last element should be left.
+  EXPECT_TRUE(m.contains(4));
+  it = m.begin();
+  ASSERT_TRUE(it != m.end());
+  EXPECT_EQ(4, *it);
+  ++it;
+  ASSERT_TRUE(it == m.end());
+}
+
+// Test all types of insertion
+TEST(LinkedHashSetTest, Insertion) {
+  linked_hash_set<int> m;
+  ASSERT_EQ(0, m.size());
+  std::pair<linked_hash_set<int>::iterator, bool> result;
+
+  result = m.insert(2);
+  ASSERT_EQ(1, m.size());
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(2, *result.first);
+  EXPECT_TRUE(m.contains(2));
+  EXPECT_TRUE(m.find(2) != m.end());
+
+  result = m.insert(1);
+  ASSERT_EQ(2, m.size());
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(1, *result.first);
+  EXPECT_TRUE(m.contains(1));
+  EXPECT_TRUE(m.find(1) != m.end());
+
+  result = m.insert(3);
+  linked_hash_set<int>::iterator result_iterator = result.first;
+  ASSERT_EQ(3, m.size());
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(3, *result.first);
+  EXPECT_TRUE(m.contains(3));
+  EXPECT_TRUE(m.find(3) != m.end());
+
+  result = m.insert(3);
+  EXPECT_EQ(3, m.size());
+  EXPECT_FALSE(result.second) << "No insertion should have occurred.";
+  EXPECT_TRUE(result_iterator == result.first)
+      << "Duplicate insertion should have given us the original iterator.";
+  EXPECT_TRUE(m.contains(3));
+  EXPECT_TRUE(m.find(3) != m.end());
+
+  std::vector<int> v = {3, 4, 5};
+  m.insert(v.begin(), v.end());
+  // Expect 4 and 5 inserted, 3 not inserted.
+  EXPECT_EQ(5, m.size());
+  EXPECT_TRUE(m.contains(4));
+  EXPECT_NE(m.find(4), m.end());
+  EXPECT_TRUE(m.contains(5));
+  EXPECT_NE(m.find(5), m.end());
+}
+
+TEST(LinkedHashSetTest, HintedInsertionMoveable) {
+  linked_hash_set<int> m = {1, 3};
+  m.insert(m.find(3), 2);
+  EXPECT_THAT(m, ElementsAre(1, 2, 3));
+}
+
+TEST(LinkedHashSetTest, HintedInsertionReference) {
+  linked_hash_set<int> m = {1, 3};
+  const int val = 2;
+  m.insert(m.find(3), val);
+  EXPECT_THAT(m, ElementsAre(1, 2, 3));
+}
+
+TEST(LinkedHashSetTest, HintedEmplaceMoveable) {
+  linked_hash_set<int> m = {1, 3};
+  m.emplace_hint(m.find(3), 2);
+  EXPECT_THAT(m, ElementsAre(1, 2, 3));
+}
+
+TEST(LinkedHashSetTest, HintedEmplaceReference) {
+  linked_hash_set<int> m = {1, 3};
+  const int val = 2;
+  m.emplace_hint(m.find(3), val);
+  EXPECT_THAT(m, ElementsAre(1, 2, 3));
+}
+
+// Test front accessors.
+TEST(LinkedHashSetTest, Front) {
+  linked_hash_set<int> m;
+
+  m.insert(222);
+  m.insert(111);
+  m.insert(333);
+
+  EXPECT_EQ(3, m.size());
+  EXPECT_EQ(222, m.front());
+  m.pop_front();
+  EXPECT_EQ(2, m.size());
+  EXPECT_EQ(111, m.front());
+  m.pop_front();
+  EXPECT_EQ(1, m.size());
+  EXPECT_EQ(333, m.front());
+  m.pop_front();
+  EXPECT_TRUE(m.empty());
+}
+
+// Test back accessors.
+TEST(LinkedHashSetTest, Back) {
+  linked_hash_set<int> m;
+
+  m.insert(222);
+  m.insert(111);
+  m.insert(333);
+
+  EXPECT_EQ(3, m.size());
+  EXPECT_EQ(333, m.back());
+  m.pop_back();
+  EXPECT_EQ(2, m.size());
+  EXPECT_EQ(111, m.back());
+  m.pop_back();
+  EXPECT_EQ(1, m.size());
+  EXPECT_EQ(222, m.back());
+  m.pop_back();
+  EXPECT_TRUE(m.empty());
+}
+
+TEST(LinkedHashSetTest, Find) {
+  linked_hash_set<int> m;
+
+  EXPECT_TRUE(m.end() == m.find(1))
+      << "We shouldn't find anything in an empty set.";
+
+  m.insert(2);
+  EXPECT_TRUE(m.end() == m.find(1))
+      << "We shouldn't find an element that doesn't exist in the set.";
+
+  std::pair<linked_hash_set<int>::iterator, bool> result = m.insert(1);
+  ASSERT_TRUE(result.second);
+  ASSERT_TRUE(m.end() != result.first);
+  EXPECT_TRUE(result.first == m.find(1))
+      << "We should have found an element we know exists in the set.";
+  EXPECT_EQ(1, *result.first);
+
+  // Check that a follow-up insertion doesn't affect our original
+  m.insert(3);
+  linked_hash_set<int>::iterator it = m.find(1);
+  ASSERT_TRUE(m.end() != it);
+  EXPECT_EQ(1, *it);
+
+  m.clear();
+  EXPECT_TRUE(m.end() == m.find(1))
+      << "We shouldn't find anything in a set that we've cleared.";
+}
+
+TEST(LinkedHashSetTest, Contains) {
+  linked_hash_set<int> m;
+
+  EXPECT_FALSE(m.contains(1)) << "The empty set shouldn't contain anything.";
+
+  m.insert(2);
+  EXPECT_FALSE(m.contains(1))
+      << "contains() should not return true for an element that doesn't exist "
+      << "in the set.";
+
+  m.insert(1);
+  EXPECT_TRUE(m.contains(1))
+      << "contains() should return true for an element we know exists in the "
+      << "set.";
+
+  m.clear();
+  EXPECT_FALSE(m.contains(1))
+      << "A set that we've cleared shouldn't contain anything.";
+}
+
+TEST(LinkedHashSetTest, Swap) {
+  linked_hash_set<int> m1;
+  linked_hash_set<int> m2;
+  m1.insert(1);
+  m1.insert(2);
+  m2.insert(3);
+  ASSERT_EQ(2, m1.size());
+  ASSERT_EQ(1, m2.size());
+  m1.swap(m2);
+  ASSERT_EQ(1, m1.size());
+  ASSERT_EQ(2, m2.size());
+}
+
+TEST(LinkedHashSetTest, InitializerList) {
+  linked_hash_set<int> m{1, 3};
+  ASSERT_EQ(2, m.size());
+  EXPECT_TRUE(m.contains(1));
+  linked_hash_set<int>::iterator it = m.find(1);
+  ASSERT_TRUE(m.end() != it);
+  EXPECT_EQ(1, *it);
+  it = m.find(3);
+  EXPECT_TRUE(m.contains(3));
+  ASSERT_TRUE(m.end() != it);
+  EXPECT_EQ(3, *it);
+}
+
+TEST(LinkedHashSetTest, CustomHashAndEquality) {
+  struct CustomIntHash {
+    size_t operator()(int x) const { return 0; }
+  };
+  struct CustomIntEq {
+    bool operator()(int x, int y) const { return abs(x) == abs(y); }
+  };
+  linked_hash_set<int, CustomIntHash, CustomIntEq> m;
+  m.insert(1);
+  EXPECT_EQ(1, m.size());
+  m.insert(2);
+  EXPECT_EQ(2, m.size());
+  EXPECT_FALSE(m.insert(-2).second);
+  EXPECT_EQ(2, m.size());
+  EXPECT_TRUE(m.contains(-1));
+  EXPECT_TRUE(m.find(-1) != m.end());
+}
+
+TEST(LinkedHashSetTest, EqualRange) {
+  linked_hash_set<int> m{3, 1};
+  const auto& const_m = m;
+
+  EXPECT_THAT(m.equal_range(2), testing::Pair(m.end(), m.end()));
+  EXPECT_THAT(const_m.equal_range(2),
+              testing::Pair(const_m.end(), const_m.end()));
+
+  EXPECT_THAT(m.equal_range(1), testing::Pair(m.find(1), ++m.find(1)));
+  EXPECT_THAT(const_m.equal_range(1),
+              testing::Pair(const_m.find(1), ++const_m.find(1)));
+}
+
+TEST(LinkedHashSetTest, ReserveWorks) {
+  linked_hash_set<int> m;
+  EXPECT_EQ(0, m.size());
+  EXPECT_EQ(0.0, m.load_factor());
+  m.reserve(10);
+  EXPECT_LE(10, m.capacity());
+  EXPECT_EQ(0, m.size());
+  EXPECT_EQ(0.0, m.load_factor());
+  m.insert(1);
+  m.insert(2);
+  EXPECT_LE(10, m.capacity());
+  EXPECT_EQ(2, m.size());
+  EXPECT_LT(0.0, m.load_factor());
+}
+
+TEST(LinkedHashSetTest, HeterogeneousTests) {
+  absl::test_internal::InstanceTracker tracker;
+
+  linked_hash_set<ExpensiveType, HeterogeneousHash, HeterogeneousEqual> set;
+  ExpensiveType one(1);
+  tracker.ResetCopiesMovesSwaps();
+  set.insert(one);
+  // Two instances: 'one' var and an instance in the set.
+  EXPECT_EQ(2, tracker.instances());
+  EXPECT_EQ(1, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+
+  tracker.ResetCopiesMovesSwaps();
+  set.insert(one);
+  // No construction since key==1 exists.
+  EXPECT_EQ(2, tracker.instances());
+  EXPECT_EQ(0, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+
+  tracker.ResetCopiesMovesSwaps();
+  set.emplace(CheapType(1));
+  // No construction since key==1 exists.
+  EXPECT_EQ(2, tracker.instances());
+  EXPECT_EQ(0, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+
+  tracker.ResetCopiesMovesSwaps();
+  set.emplace(CheapType(2));
+  // Construction since key==2 doesn't exist in the set.
+  EXPECT_EQ(3, tracker.instances());
+  EXPECT_EQ(0, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+  EXPECT_THAT(set, ElementsAre(HasExpensiveValue(1), HasExpensiveValue(2)));
+
+  // find
+  tracker.ResetCopiesMovesSwaps();
+  auto itr = set.find(CheapType(1));
+  ASSERT_NE(itr, set.end());
+  EXPECT_EQ(1, itr->value());
+  // contains
+  EXPECT_TRUE(set.contains(CheapType(2)));
+  // count
+  EXPECT_EQ(1, set.count(CheapType(2)));
+  // equal_range
+  auto eq_itr_pair = set.equal_range(CheapType(2));
+  ASSERT_NE(eq_itr_pair.first, set.end());
+  EXPECT_EQ(2, eq_itr_pair.first->value());
+  // No construction for find, contains, count or equal_range.
+  EXPECT_EQ(3, tracker.instances());
+  EXPECT_EQ(0, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+
+  // emplace
+  tracker.ResetCopiesMovesSwaps();
+  set.emplace(3);
+  // Just one construction.
+  EXPECT_EQ(4, tracker.instances());
+  EXPECT_EQ(0, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+
+  tracker.ResetCopiesMovesSwaps();
+  set.emplace(3);
+  // No additional construction since key==3 exists.
+  EXPECT_EQ(4, tracker.instances());
+  EXPECT_EQ(0, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+  EXPECT_THAT(set, ElementsAre(HasExpensiveValue(1), HasExpensiveValue(2),
+                               HasExpensiveValue(3)));
+
+  // Test std::move() using insert().
+  ExpensiveType four(4);
+  tracker.ResetCopiesMovesSwaps();
+  set.insert(std::move(four));
+  // Two constructions (regular and move).
+  EXPECT_EQ(6, tracker.instances());
+  EXPECT_EQ(0, tracker.copies());
+  EXPECT_EQ(1, tracker.moves());
+
+  EXPECT_THAT(set, ElementsAre(HasExpensiveValue(1), HasExpensiveValue(2),
+                               HasExpensiveValue(3), HasExpensiveValue(4)));
+
+  tracker.ResetCopiesMovesSwaps();
+  set.erase(CheapType(1));
+  // No construction and instance reduced by one.
+  EXPECT_EQ(5, tracker.instances());
+  EXPECT_EQ(0, tracker.copies());
+  EXPECT_EQ(0, tracker.moves());
+  EXPECT_THAT(set, ElementsAre(HasExpensiveValue(2), HasExpensiveValue(3),
+                               HasExpensiveValue(4)));
+}
+
+TEST(LinkedHashSetTest, HeterogeneousStringViewLookup) {
+  linked_hash_set<std::string> set;
+  set.insert("foo");
+  set.insert("bar");
+  set.insert("blah");
+
+  {
+    absl::string_view lookup("foo");
+    auto itr = set.find(lookup);
+    ASSERT_NE(itr, set.end());
+    EXPECT_EQ("foo", *itr);
+  }
+
+  // Not found.
+  {
+    absl::string_view lookup("foobar");
+    EXPECT_EQ(set.end(), set.find(lookup));
+  }
+
+  {
+    absl::string_view lookup("blah");
+    auto itr = set.find(lookup);
+    ASSERT_NE(itr, set.end());
+    EXPECT_EQ("blah", *itr);
+  }
+}
+
+TEST(LinkedHashSetTest, EmplaceString) {
+  std::vector<std::string> v = {"a", "b"};
+  linked_hash_set<absl::string_view> hs(v.begin(), v.end());
+  EXPECT_THAT(hs, ElementsAreArray(v));
+}
+
+TEST(LinkedHashSetTest, BitfieldArgument) {
+  union {
+    int n : 1;
+  };
+  n = 0;
+  linked_hash_set<int> s = {n};
+  s.insert(n);
+  s.insert(s.end(), n);
+  s.insert({n});
+  s.erase(n);
+  s.count(n);
+  s.find(n);
+  s.contains(n);
+  s.equal_range(n);
+}
+
+TEST(LinkedHashSetTest, MergeExtractInsert) {
+  struct Hash {
+    size_t operator()(const std::unique_ptr<int>& p) const { return *p; }
+  };
+  struct Eq {
+    bool operator()(const std::unique_ptr<int>& a,
+                    const std::unique_ptr<int>& b) const {
+      return *a == *b;
+    }
+  };
+  linked_hash_set<std::unique_ptr<int>, Hash, Eq> set1, set2;
+  set1.insert(std::make_unique<int>(7));
+  set1.insert(std::make_unique<int>(17));
+
+  set2.insert(std::make_unique<int>(7));
+  set2.insert(std::make_unique<int>(19));
+
+  EXPECT_THAT(set1, ElementsAre(Pointee(7), Pointee(17)));
+  EXPECT_THAT(set2, ElementsAre(Pointee(7), Pointee(19)));
+
+  set1.merge(set2);
+
+  EXPECT_THAT(set1, ElementsAre(Pointee(7), Pointee(17), Pointee(19)));
+  EXPECT_THAT(set2, ElementsAre(Pointee(7)));
+
+  auto node = set1.extract(std::make_unique<int>(7));
+  EXPECT_TRUE(node);
+  EXPECT_THAT(node.value(), Pointee(7));
+  EXPECT_THAT(set1, ElementsAre(Pointee(17), Pointee(19)));
+
+  auto insert_result = set2.insert(std::move(node));
+  EXPECT_FALSE(node);
+  EXPECT_FALSE(insert_result.inserted);
+  EXPECT_TRUE(insert_result.node);
+  EXPECT_THAT(insert_result.node.value(), Pointee(7));
+  EXPECT_EQ(**insert_result.position, 7);
+  EXPECT_NE(insert_result.position->get(), insert_result.node.value().get());
+  EXPECT_THAT(set2, ElementsAre(Pointee(7)));
+
+  node = set1.extract(std::make_unique<int>(17));
+  EXPECT_TRUE(node);
+  EXPECT_THAT(node.value(), Pointee(17));
+  EXPECT_THAT(set1, ElementsAre(Pointee(19)));
+
+  node.value() = std::make_unique<int>(23);
+
+  insert_result = set2.insert(std::move(node));
+  EXPECT_FALSE(node);
+  EXPECT_TRUE(insert_result.inserted);
+  EXPECT_FALSE(insert_result.node);
+  EXPECT_EQ(**insert_result.position, 23);
+  EXPECT_THAT(set2, ElementsAre(Pointee(7), Pointee(23)));
+}
+
+TEST(LinkedHashSet, ExtractInsert) {
+  linked_hash_set<int> s = {1, 7, 2, 9};
+  auto node = s.extract(1);
+  EXPECT_TRUE(node);
+  EXPECT_EQ(node.value(), 1);
+  EXPECT_THAT(s, ElementsAre(7, 2, 9));
+  EXPECT_FALSE(s.contains(1));
+
+  node.value() = 17;
+  s.insert(std::move(node));
+  EXPECT_FALSE(node);
+  EXPECT_THAT(s, ElementsAre(7, 2, 9, 17));
+  EXPECT_TRUE(s.contains(17));
+
+  node = s.extract(s.find(9));
+  EXPECT_TRUE(node);
+  EXPECT_EQ(node.value(), 9);
+  EXPECT_THAT(s, ElementsAre(7, 2, 17));
+  EXPECT_FALSE(s.contains(9));
+}
+
+TEST(LinkedHashSet, Merge) {
+  linked_hash_set<int> m = {1, 7, 3, 6, 10};
+  linked_hash_set<int> src = {1, 2, 9, 10, 4, 16};
+
+  m.merge(src);
+
+  EXPECT_THAT(m, ElementsAre(1, 7, 3, 6, 10, 2, 9, 4, 16));
+  for (int i : {1, 7, 3, 6, 10, 2, 9, 4, 16}) {
+    EXPECT_TRUE(m.contains(i));
+  }
+  EXPECT_THAT(src, ElementsAre(1, 10));
+  for (int i : {1, 10}) {
+    EXPECT_TRUE(src.contains(i));
+  }
+  for (int i : {2, 9, 4, 16}) {
+    EXPECT_FALSE(src.contains(i));
+  }
+}
+
+TEST(LinkedHashSet, EraseRange) {
+  linked_hash_set<int> set = {1, 2, 3, 4, 5, 25, 36, 7, 8, 9, 81};
+  auto start = set.find(3);
+  auto end = set.find(8);
+  auto itr = set.erase(start, end);
+  ASSERT_NE(itr, set.end());
+  EXPECT_THAT(*itr, 8);
+  EXPECT_THAT(set, ElementsAre(1, 2, 8, 9, 81));
+  for (int i : {1, 2, 8, 9, 81}) {
+    EXPECT_TRUE(set.contains(i));
+  }
+  for (int i : {3, 4, 5, 25, 36, 7}) {
+    EXPECT_FALSE(set.contains(i));
+  }
+}
+
+TEST(LinkedHashSet, InsertInitializerList) {
+  linked_hash_set<int> set;
+  set.insert({1, 7, 2, 9, 3, 29});
+  EXPECT_THAT(set, ElementsAre(1, 7, 2, 9, 3, 29));
+  for (int i : {1, 7, 2, 9, 3, 29}) {
+    EXPECT_TRUE(set.contains(i));
+  }
+}
+
+struct CountedHash {
+  explicit CountedHash(int* count) : count(count) {}
+  size_t operator()(int value) const {
+    ++(*count);
+    return value;
+  }
+  int* count = nullptr;
+};
+
+// Makes a set too big for small object optimization.  Counts the number of
+// hashes in `count`, but leaves `count` set to 0.
+linked_hash_set<int, CountedHash> MakeNonSmallSet(int* count) {
+  const int kFirstKey = -1000;
+  linked_hash_set<int, CountedHash> s(0, CountedHash(count));
+  for (int i = kFirstKey; i < kFirstKey + 100; ++i) {
+    s.insert(i);
+  }
+  *count = 0;
+  return s;
+}
+
+constexpr bool BuildHasDebugModeRehashes() {
+#if !defined(NDEBUG) || defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+    defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER)
+  return true;
+#else
+  return false;
+#endif
+}
+
+TEST(LinkedHashSetTest, HashCountInOptBuilds) {
+  if (BuildHasDebugModeRehashes()) {
+    GTEST_SKIP() << "Only run under NDEBUG: `assert` statements and sanitizer "
+                    "rehashing may cause redundant hashing.";
+  }
+
+  using Set = linked_hash_set<int, CountedHash>;
+  {
+    int count = 0;
+    Set s = MakeNonSmallSet(&count);
+    s.insert(1);
+    EXPECT_EQ(count, 1);
+    s.erase(1);
+    EXPECT_EQ(count, 2);
+  }
+  {
+    int count = 0;
+    Set s = MakeNonSmallSet(&count);
+    s.insert(3);
+    EXPECT_EQ(count, 1);
+    auto node = s.extract(3);
+    EXPECT_EQ(count, 2);
+    s.insert(std::move(node));
+    EXPECT_EQ(count, 3);
+  }
+  {
+    int count = 0;
+    Set s = MakeNonSmallSet(&count);
+    s.emplace(5);
+    EXPECT_EQ(count, 1);
+  }
+  {
+    int src_count = 0, dst_count = 0;
+    Set src = MakeNonSmallSet(&src_count);
+    Set dst = MakeNonSmallSet(&dst_count);
+    src.insert(7);
+    dst.merge(src);
+    EXPECT_LE(src_count, 200);
+    EXPECT_LE(dst_count, 200);
+  }
+}
+
+}  // namespace
+}  // namespace container_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h
index b24db36..580a044 100644
--- a/absl/container/node_hash_map.h
+++ b/absl/container/node_hash_map.h
@@ -122,13 +122,18 @@
 //   if (result != ducks.end()) {
 //     std::cout << "Result: " << result->second << std::endl;
 //   }
-template <class Key, class Value, class Hash = DefaultHashContainerHash<Key>,
-          class Eq = DefaultHashContainerEq<Key>,
-          class Alloc = std::allocator<std::pair<const Key, Value>>>
+template <
+    class Key, class Value,
+    class Hash =
+        typename container_internal::NodeHashMapPolicy<Key, Value>::DefaultHash,
+    class Eq =
+        typename container_internal::NodeHashMapPolicy<Key, Value>::DefaultEq,
+    class Alloc = typename container_internal::NodeHashMapPolicy<
+        Key, Value>::DefaultAlloc>
 class ABSL_ATTRIBUTE_OWNER node_hash_map
-    : public absl::container_internal::raw_hash_map<
+    : public absl::container_internal::InstantiateRawHashMap<
           absl::container_internal::NodeHashMapPolicy<Key, Value>, Hash, Eq,
-          Alloc> {
+          Alloc>::type {
   using Base = typename node_hash_map::raw_hash_map;
 
  public:
@@ -629,6 +634,10 @@
   using mapped_type = Value;
   using init_type = std::pair</*non const*/ key_type, mapped_type>;
 
+  using DefaultHash = DefaultHashContainerHash<Key>;
+  using DefaultEq = DefaultHashContainerEq<Key>;
+  using DefaultAlloc = std::allocator<std::pair<const Key, Value>>;
+
   template <class Allocator, class... Args>
   static value_type* new_element(Allocator* alloc, Args&&... args) {
     using PairAlloc = typename absl::allocator_traits<
diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h
index 508248c..f69c6ab 100644
--- a/absl/container/node_hash_set.h
+++ b/absl/container/node_hash_set.h
@@ -118,11 +118,16 @@
 //   if (ducks.contains("dewey")) {
 //     std::cout << "We found dewey!" << std::endl;
 //   }
-template <class T, class Hash = DefaultHashContainerHash<T>,
-          class Eq = DefaultHashContainerEq<T>, class Alloc = std::allocator<T>>
+template <
+    class T,
+    class Hash = typename container_internal::NodeHashSetPolicy<T>::DefaultHash,
+    class Eq = typename container_internal::NodeHashSetPolicy<T>::DefaultEq,
+    class Alloc =
+        typename container_internal::NodeHashSetPolicy<T>::DefaultAlloc>
 class ABSL_ATTRIBUTE_OWNER node_hash_set
-    : public absl::container_internal::raw_hash_set<
-          absl::container_internal::NodeHashSetPolicy<T>, Hash, Eq, Alloc> {
+    : public absl::container_internal::InstantiateRawHashSet<
+          absl::container_internal::NodeHashSetPolicy<T>, Hash, Eq,
+          Alloc>::type {
   using Base = typename node_hash_set::raw_hash_set;
 
  public:
@@ -529,6 +534,10 @@
   using init_type = T;
   using constant_iterators = std::true_type;
 
+  using DefaultHash = DefaultHashContainerHash<T>;
+  using DefaultEq = DefaultHashContainerEq<T>;
+  using DefaultAlloc = std::allocator<T>;
+
   template <class Allocator, class... Args>
   static T* new_element(Allocator* alloc, Args&&... args) {
     using ValueAlloc =
diff --git a/absl/container/node_hash_set_test.cc b/absl/container/node_hash_set_test.cc
index e616ac1..e1f5bd9 100644
--- a/absl/container/node_hash_set_test.cc
+++ b/absl/container/node_hash_set_test.cc
@@ -35,8 +35,7 @@
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
 namespace {
-using ::absl::container_internal::hash_internal::Enum;
-using ::absl::container_internal::hash_internal::EnumClass;
+
 using ::testing::IsEmpty;
 using ::testing::Pointee;
 using ::testing::UnorderedElementsAre;
diff --git a/absl/debugging/stacktrace.cc b/absl/debugging/stacktrace.cc
index aee065d..1a770af 100644
--- a/absl/debugging/stacktrace.cc
+++ b/absl/debugging/stacktrace.cc
@@ -53,17 +53,17 @@
 #if defined(ABSL_STACKTRACE_INL_HEADER)
 #include ABSL_STACKTRACE_INL_HEADER
 #else
-# error Cannot calculate stack trace: will need to write for your environment
+#error Cannot calculate stack trace: will need to write for your environment
 
-# include "absl/debugging/internal/stacktrace_aarch64-inl.inc"
-# include "absl/debugging/internal/stacktrace_arm-inl.inc"
-# include "absl/debugging/internal/stacktrace_emscripten-inl.inc"
-# include "absl/debugging/internal/stacktrace_generic-inl.inc"
-# include "absl/debugging/internal/stacktrace_powerpc-inl.inc"
-# include "absl/debugging/internal/stacktrace_riscv-inl.inc"
-# include "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
-# include "absl/debugging/internal/stacktrace_win32-inl.inc"
-# include "absl/debugging/internal/stacktrace_x86-inl.inc"
+#include "absl/debugging/internal/stacktrace_aarch64-inl.inc"
+#include "absl/debugging/internal/stacktrace_arm-inl.inc"
+#include "absl/debugging/internal/stacktrace_emscripten-inl.inc"
+#include "absl/debugging/internal/stacktrace_generic-inl.inc"
+#include "absl/debugging/internal/stacktrace_powerpc-inl.inc"
+#include "absl/debugging/internal/stacktrace_riscv-inl.inc"
+#include "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
+#include "absl/debugging/internal/stacktrace_win32-inl.inc"
+#include "absl/debugging/internal/stacktrace_x86-inl.inc"
 #endif
 
 namespace absl {
diff --git a/absl/flags/flag.h b/absl/flags/flag.h
index e052d5f..4c328e3 100644
--- a/absl/flags/flag.h
+++ b/absl/flags/flag.h
@@ -241,7 +241,8 @@
     /* default value argument. That keeps temporaries alive */               \
     /* long enough for NonConst to work correctly.          */               \
     static constexpr absl::string_view Value(                                \
-        absl::string_view absl_flag_help = ABSL_FLAG_IMPL_FLAGHELP(txt)) {   \
+        absl::string_view absl_flag_help ABSL_ATTRIBUTE_LIFETIME_BOUND  =     \
+            ABSL_FLAG_IMPL_FLAGHELP(txt)) {                                  \
       return absl_flag_help;                                                 \
     }                                                                        \
     static std::string NonConst() { return std::string(Value()); }           \
diff --git a/absl/flags/marshalling.cc b/absl/flags/marshalling.cc
index ca4a130..acdc880 100644
--- a/absl/flags/marshalling.cc
+++ b/absl/flags/marshalling.cc
@@ -45,22 +45,7 @@
 // AbslParseFlag specializations for boolean type.
 
 bool AbslParseFlag(absl::string_view text, bool* dst, std::string*) {
-  const char* kTrue[] = {"1", "t", "true", "y", "yes"};
-  const char* kFalse[] = {"0", "f", "false", "n", "no"};
-  static_assert(sizeof(kTrue) == sizeof(kFalse), "true_false_equal");
-
-  text = absl::StripAsciiWhitespace(text);
-
-  for (size_t i = 0; i < ABSL_ARRAYSIZE(kTrue); ++i) {
-    if (absl::EqualsIgnoreCase(text, kTrue[i])) {
-      *dst = true;
-      return true;
-    } else if (absl::EqualsIgnoreCase(text, kFalse[i])) {
-      *dst = false;
-      return true;
-    }
-  }
-  return false;  // didn't match a legal input
+  return SimpleAtob(absl::StripAsciiWhitespace(text), dst);
 }
 
 // --------------------------------------------------------------------
diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc
index df2a179..4961930 100644
--- a/absl/flags/parse.cc
+++ b/absl/flags/parse.cc
@@ -206,8 +206,10 @@
 
   std::string line;
   bool success = true;
+  int line_number = 0;
 
   while (std::getline(flag_file, line)) {
+    line_number++;
     absl::string_view stripped = absl::StripLeadingAsciiWhitespace(line);
 
     if (stripped.empty() || stripped[0] == '#') {
@@ -229,8 +231,8 @@
     }
 
     flags_internal::ReportUsageError(
-        absl::StrCat("Unexpected line in the flagfile ", flag_file_name, ": ",
-                     line),
+        absl::StrCat("Unexpected line ", line_number, " in the flagfile ",
+                     flag_file_name),
         true);
 
     success = false;
diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc
index 9997069..08eb81a 100644
--- a/absl/flags/parse_test.cc
+++ b/absl/flags/parse_test.cc
@@ -827,7 +827,7 @@
                       flagfile_flag),
   };
   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args5),
-               "Unexpected line in the flagfile .*: \\*bin\\*");
+               "Unexpected line 2 in the flagfile .*");
 }
 
 // --------------------------------------------------------------------
diff --git a/absl/hash/hash_benchmark.cc b/absl/hash/hash_benchmark.cc
index 73b037d..b39ef75 100644
--- a/absl/hash/hash_benchmark.cc
+++ b/absl/hash/hash_benchmark.cc
@@ -338,6 +338,16 @@
 }();
 }  // namespace
 
+struct PodPairInt64 {
+  int64_t a;
+  int64_t b;
+
+  template <typename H>
+  friend H AbslHashValue(H h, const PodPairInt64& p) {
+    return H::combine(std::move(h), p.a, p.b);
+  }
+};
+
 template <class T>
 struct PodRand {
   static_assert(std::is_pod<T>::value, "");
@@ -378,6 +388,7 @@
 
 MAKE_LATENCY_BENCHMARK(AbslHash, Int32, PodRand<int32_t>)
 MAKE_LATENCY_BENCHMARK(AbslHash, Int64, PodRand<int64_t>)
+MAKE_LATENCY_BENCHMARK(AbslHash, PairInt64, PodRand<PodPairInt64>)
 MAKE_LATENCY_BENCHMARK(AbslHash, String3, StringRand<3>)
 MAKE_LATENCY_BENCHMARK(AbslHash, String5, StringRand<5>)
 MAKE_LATENCY_BENCHMARK(AbslHash, String9, StringRand<9>)
diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc
index 500d1e0..89e0470 100644
--- a/absl/hash/hash_test.cc
+++ b/absl/hash/hash_test.cc
@@ -1284,7 +1284,7 @@
   constexpr char kMinChar = 0;
   constexpr char kMaxChar = 64;
   // These sizes cover the different hashing cases.
-  for (size_t size : {8u, 16u, 32u, 64u}) {
+  for (size_t size : {8u, 16u, 32u, 64u, 128u}) {
     for (size_t b = 0; b < size - 1; ++b) {
       absl::flat_hash_set<std::string> set;
       std::string s(size, '\0');
diff --git a/absl/hash/internal/hash.cc b/absl/hash/internal/hash.cc
index 6b02f19..23caae9 100644
--- a/absl/hash/internal/hash.cc
+++ b/absl/hash/internal/hash.cc
@@ -26,7 +26,6 @@
 #include "absl/base/prefetch.h"
 #include "absl/hash/internal/city.h"
 
-
 #ifdef ABSL_AES_INTERNAL_HAVE_X86_SIMD
 #error ABSL_AES_INTERNAL_HAVE_X86_SIMD cannot be directly set
 #elif defined(__SSE4_2__) && defined(__AES__)
@@ -46,18 +45,20 @@
 
 namespace {
 
-uint64_t Mix32Bytes(const uint8_t* ptr, uint64_t current_state) {
-  uint64_t a = absl::base_internal::UnalignedLoad64(ptr);
-  uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8);
-  uint64_t c = absl::base_internal::UnalignedLoad64(ptr + 16);
-  uint64_t d = absl::base_internal::UnalignedLoad64(ptr + 24);
-
-  uint64_t cs0 = Mix(a ^ kStaticRandomData[1], b ^ current_state);
-  uint64_t cs1 = Mix(c ^ kStaticRandomData[2], d ^ current_state);
-  return cs0 ^ cs1;
+void PrefetchFutureDataToLocalCache(const uint8_t* ptr) {
+  PrefetchToLocalCache(ptr + 5 * ABSL_CACHELINE_SIZE);
 }
 
 #ifdef ABSL_AES_INTERNAL_HAVE_X86_SIMD
+uint64_t Mix4x16Vectors(__m128i a, __m128i b, __m128i c, __m128i d) {
+  // res128 = encrypt(a + c, d) + decrypt(b - d, a)
+  auto res128 = _mm_add_epi64(_mm_aesenc_si128(_mm_add_epi64(a, c), d),
+                              _mm_aesdec_si128(_mm_sub_epi64(b, d), a));
+  auto x64 = static_cast<uint64_t>(_mm_cvtsi128_si64(res128));
+  auto y64 = static_cast<uint64_t>(_mm_extract_epi64(res128, 1));
+  return x64 ^ y64;
+}
+
 uint64_t LowLevelHash33To64(uint64_t seed, const uint8_t* ptr, size_t len) {
   assert(len > 32);
   assert(len <= 64);
@@ -84,13 +85,82 @@
 
   // We perform another round of encryption to mix bits between two halves of
   // the input.
-  auto res128 = _mm_add_epi64(_mm_aesenc_si128(_mm_add_epi64(na, nc), nd),
-                              _mm_aesdec_si128(_mm_sub_epi64(nb, nd), na));
-  auto x64 = static_cast<uint64_t>(_mm_cvtsi128_si64(res128));
-  auto y64 = static_cast<uint64_t>(_mm_extract_epi64(res128, 1));
-  return x64 ^ y64;
+  return Mix4x16Vectors(na, nb, nc, nd);
+}
+
+[[maybe_unused]] ABSL_ATTRIBUTE_NOINLINE uint64_t
+LowLevelHashLenGt64(uint64_t seed, const void* data, size_t len) {
+  assert(len > 64);
+  const uint8_t* ptr = static_cast<const uint8_t*>(data);
+  const uint8_t* last_32_ptr = ptr + len - 32;
+
+  // If we have more than 64 bytes, we're going to handle chunks of 64
+  // bytes at a time. We're going to build up four separate hash states
+  // which we will then hash together. This avoids short dependency chains.
+  __m128i state0 =
+      _mm_set_epi64x(static_cast<int64_t>(seed), static_cast<int64_t>(len));
+  __m128i state1 = state0;
+  __m128i state2 = state1;
+  __m128i state3 = state2;
+
+  // Mixing two 128-bit vectors at a time with corresponding states.
+  // All variables are mixed slightly differently to avoid hash collision
+  // due to trivial byte rotation.
+  // We combine state and data with _mm_add_epi64/_mm_sub_epi64 before applying
+  // AES encryption to make hash function dependent on the order of the blocks.
+  // See comments in LowLevelHash33To64 for more considerations.
+  auto mix_ab = [&state0,
+                 &state1](const uint8_t* p) ABSL_ATTRIBUTE_ALWAYS_INLINE {
+    // i128 a = *p;
+    // i128 b = *(p + 16);
+    // state0 = decrypt(state0 + a, state0);
+    // state1 = decrypt(state1 - b, state1);
+    auto a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
+    auto b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + 16));
+    state0 = _mm_aesdec_si128(_mm_add_epi64(state0, a), state0);
+    state1 = _mm_aesdec_si128(_mm_sub_epi64(state1, b), state1);
+  };
+  auto mix_cd = [&state2,
+                 &state3](const uint8_t* p) ABSL_ATTRIBUTE_ALWAYS_INLINE {
+    // i128 c = *p;
+    // i128 d = *(p + 16);
+    // state2 = encrypt(state2 + c, state2);
+    // state3 = encrypt(state3 - d, state3);
+    auto c = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
+    auto d = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + 16));
+    state2 = _mm_aesenc_si128(_mm_add_epi64(state2, c), state2);
+    state3 = _mm_aesenc_si128(_mm_sub_epi64(state3, d), state3);
+  };
+
+  do {
+    PrefetchFutureDataToLocalCache(ptr);
+    mix_ab(ptr);
+    mix_cd(ptr + 32);
+
+    ptr += 64;
+    len -= 64;
+  } while (len > 64);
+
+  // We now have a data `ptr` with at most 64 bytes.
+  if (len > 32) {
+    mix_ab(ptr);
+  }
+  mix_cd(last_32_ptr);
+
+  return Mix4x16Vectors(state0, state1, state2, state3);
 }
 #else
+uint64_t Mix32Bytes(const uint8_t* ptr, uint64_t current_state) {
+  uint64_t a = absl::base_internal::UnalignedLoad64(ptr);
+  uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8);
+  uint64_t c = absl::base_internal::UnalignedLoad64(ptr + 16);
+  uint64_t d = absl::base_internal::UnalignedLoad64(ptr + 24);
+
+  uint64_t cs0 = Mix(a ^ kStaticRandomData[1], b ^ current_state);
+  uint64_t cs1 = Mix(c ^ kStaticRandomData[2], d ^ current_state);
+  return cs0 ^ cs1;
+}
+
 uint64_t LowLevelHash33To64(uint64_t seed, const uint8_t* ptr, size_t len) {
   assert(len > 32);
   assert(len <= 64);
@@ -98,7 +168,6 @@
   const uint8_t* last_32_ptr = ptr + len - 32;
   return Mix32Bytes(last_32_ptr, Mix32Bytes(ptr, current_state));
 }
-#endif  // ABSL_AES_INTERNAL_HAVE_X86_SIMD
 
 [[maybe_unused]] ABSL_ATTRIBUTE_NOINLINE uint64_t
 LowLevelHashLenGt64(uint64_t seed, const void* data, size_t len) {
@@ -114,7 +183,7 @@
   uint64_t duplicated_state2 = current_state;
 
   do {
-    PrefetchToLocalCache(ptr + 5 * ABSL_CACHELINE_SIZE);
+    PrefetchFutureDataToLocalCache(ptr);
 
     uint64_t a = absl::base_internal::UnalignedLoad64(ptr);
     uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8);
@@ -148,6 +217,7 @@
   // safely read from `ptr + len - 32`.
   return Mix32Bytes(last_32_ptr, current_state);
 }
+#endif  // ABSL_AES_INTERNAL_HAVE_X86_SIMD
 
 [[maybe_unused]] uint64_t LowLevelHashLenGt32(uint64_t seed, const void* data,
                                               size_t len) {
diff --git a/absl/hash/internal/low_level_hash_test.cc b/absl/hash/internal/low_level_hash_test.cc
index cdd279b..d054337 100644
--- a/absl/hash/internal/low_level_hash_test.cc
+++ b/absl/hash/internal/low_level_hash_test.cc
@@ -380,28 +380,28 @@
       0xe4c78173c7ea537b, 0x0bbdc2bcabdb50b1, 0xd9aa134df2d87623,
       0x6c4907c9477a9409, 0xc3e418a5dbda52e5, 0x4d24f3e9d0dda93a,
       0xcdb565a363dbe45f, 0xa95f228c8ee57478, 0x6b8f00bab5130227,
-      0x2d05a0f44818b67a, 0xa64b55b071afbbea, 0xa205bfe6c724ce4d,
-      0x69dd26ca8ac21744, 0xef80e2ff2f6a9bc0, 0xde266c0baa202c20,
-      0xfa3463080ac74c50, 0x379d968a40125c2b, 0x4cbbd0a7b3c7d648,
-      0xc92afd93f4c665d2, 0x6e28f5adb7ae38dc, 0x7c689c9c237be35e,
-      0xaea41b29bd9d0f73, 0x832cef631d77e59f, 0x70cac8e87bc37dd3,
-      0x8e8c98bbde68e764, 0xd6117aeb3ddedded, 0xd796ab808e766240,
-      0x8953d0ea1a7d9814, 0xa212eba4281b391c, 0x21a555a8939ce597,
-      0x809d31660f6d81a8, 0x2356524b20ab400f, 0x5bc611e1e49d0478,
-      0xba9c065e2f385ce2, 0xb0a0fd12f4e83899, 0x14d076a35b1ff2ca,
-      0x8acd0bb8cf9a93c0, 0xe62e8ec094039ee4, 0x38a536a7072bdc61,
-      0xca256297602524f8, 0xfc62ebfb3530caeb, 0x8d8b0c05520569f6,
-      0xbbaca65cf154c59d, 0x3739b5ada7e338d3, 0xdb9ea31f47365340,
-      0x410b5c9c1da56755, 0x7e0abc03dbd10283, 0x136f87be70ed442e,
-      0x6b727d4feddbe1e9, 0x074ebb21183b01df, 0x3fe92185b1985484,
-      0xc5d8efd3c68305ca, 0xd9bada21b17e272e, 0x64d73133e1360f83,
-      0xeb8563aa993e21f9, 0xe5e8da50cceab28f, 0x7a6f92eb3223d2f3,
-      0xbdaf98370ea9b31b, 0x1682a84457f077bc, 0x4abd2d33b6e3be37,
-      0xb35bc81a7c9d4c04, 0x3e5bde3fb7cfe63d, 0xff3abe6e2ffec974,
-      0xb8116dd26cf6feec, 0x7a77a6e4ed0cf081, 0xb71eec2d5a184316,
-      0x6fa932f77b4da817, 0x795f79b33909b2c4, 0x1b8755ef6b5eb34e,
-      0x2255b72d7d6b2d79, 0xf2bdafafa90bd50a, 0x442a578f02cb1fc8,
-      0xc25aefe55ecf83db, 0x3114c056f9c5a676,
+      0x2d05a0f44818b67a, 0xd6bf7d990b5f44cb, 0xa3608bdb4712861a,
+      0xf20c33e5e355330b, 0xbc86e1b13130180d, 0x0848221b397b839a,
+      0x17cc0acf44a7e210, 0xc18c6dc584fe0f62, 0x896c7858a59f991d,
+      0xeab1e6d7d2856ed7, 0x7e4b2d99c23edc51, 0x9aeeeb7fa46e7cf0,
+      0x161b9f2e3611790f, 0x5f82aae18d971b36, 0x8d0dd9965881e162,
+      0x56700ea26285895a, 0xcd919c86c29a053e, 0x3e5d589282d9a722,
+      0x92caee9f48a66604, 0x7e1a2fd9b06f14b0, 0xce1d5293f95b0178,
+      0x8101361290e70a11, 0x570e3e9c9eafc1c6, 0x77b6241926a7a568,
+      0x313e5cb34f346699, 0xab8ebeab0514b82b, 0x6e0a43763a310408,
+      0x761b76ec22b2e440, 0x4238c84a9ec00528, 0xb9ea1f6d4d5552af,
+      0xd21f8f110b9dc060, 0xb3d3842b69ac3689, 0xd0a88aa1dcf59869,
+      0xf3f69f637b123403, 0xf5f34b1068cac7da, 0xe69a08d604774abf,
+      0x57648d3a73332437, 0x9762947f5013d00d, 0x35c5d734a0015922,
+      0xbee2fe5a104ce209, 0xedb060efa6efca34, 0x5ccf0f4786d97bc2,
+      0x1ef8ed72e80d7bef, 0x58522deb49c5e30f, 0xde97cd2a6f8bd13b,
+      0x3fae37c6f9855d09, 0xea99ae786feca261, 0x8c6d1d46670b0943,
+      0x84658b2a232c7bfb, 0x7058b7a7968de394, 0x0d44fba68e25aa8f,
+      0xc7f687020f8eb00b, 0xbf9671e1196153d6, 0x1009be891b7f83e7,
+      0x4f9457fb4aa12865, 0x30a49d9563643b32, 0x0302e2c5b46d5a3a,
+      0x77553f42fb0bfbf7, 0x26b95e89f0077110, 0x76ce68ebe01191ba,
+      0x724110fb509e4376, 0xebe74b016b5cfb88, 0x3b0fe11dcf175fc9,
+      0x20b737b9c0490538, 0x0db21c429b45fd17,
   };
 #else
   constexpr uint64_t kGolden[kNumGoldenOutputs] = {
diff --git a/absl/log/CMakeLists.txt b/absl/log/CMakeLists.txt
index 51f399e..9d097ab 100644
--- a/absl/log/CMakeLists.txt
+++ b/absl/log/CMakeLists.txt
@@ -1204,3 +1204,36 @@
     absl::log_internal_fnmatch
     GTest::gmock_main
 )
+
+absl_cc_library(
+  NAME
+    log_internal_container
+  HDRS
+    "internal/container.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::config
+    absl::requires_internal
+    absl::strings
+)
+
+absl_cc_test(
+  NAME
+    internal_container_test
+  SRCS
+    "internal/container_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::config
+    absl::log_internal_container
+    absl::span
+    absl::strings
+    absl::str_format
+    GTest::gmock_main
+)
diff --git a/absl/log/internal/BUILD.bazel b/absl/log/internal/BUILD.bazel
index bb20a95..32ae277 100644
--- a/absl/log/internal/BUILD.bazel
+++ b/absl/log/internal/BUILD.bazel
@@ -548,3 +548,34 @@
         "@google_benchmark//:benchmark_main",
     ],
 )
+
+cc_library(
+    name = "container",
+    hdrs = ["container.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl:__subpackages__",
+    ],
+    deps = [
+        "//absl/base:config",
+        "//absl/meta:requires",
+        "//absl/strings",
+    ],
+)
+
+cc_test(
+    name = "container_test",
+    srcs = ["container_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":container",
+        "//absl/base:config",
+        "//absl/strings",
+        "//absl/strings:str_format",
+        "//absl/types:span",
+        "@googletest//:gtest",
+        "@googletest//:gtest_main",
+    ],
+)
diff --git a/absl/log/internal/container.h b/absl/log/internal/container.h
new file mode 100644
index 0000000..1144652
--- /dev/null
+++ b/absl/log/internal/container.h
@@ -0,0 +1,312 @@
+// Copyright 2025 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// The typical use looks like this:
+//
+//   LOG(INFO) << LogContainer(container);
+//
+// By default, LogContainer() uses the LogShortUpTo100 policy: comma-space
+// separation, no newlines, and with limit of 100 items.
+//
+// Policies can be specified:
+//
+//   LOG(INFO) << LogContainer(container, LogMultiline());
+//
+// The above example will print the container using newlines between elements,
+// enclosed in [] braces.
+//
+// See below for further details on policies.
+
+#ifndef ABSL_LOG_INTERNAL_CONTAINER_H_
+#define ABSL_LOG_INTERNAL_CONTAINER_H_
+
+#include <cstdint>
+#include <limits>
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <type_traits>
+
+#include "absl/base/config.h"
+#include "absl/meta/internal/requires.h"
+#include "absl/strings/str_cat.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+
+// Several policy classes below determine how LogRangeToStream will
+// format a range of items.  A Policy class should have these methods:
+//
+// Called to print an individual container element.
+//   void Log(ostream &out, const ElementT &element) const;
+//
+// Called before printing the set of elements:
+//   void LogOpening(ostream &out) const;
+//
+// Called after printing the set of elements:
+//   void LogClosing(ostream &out) const;
+//
+// Called before printing the first element:
+//   void LogFirstSeparator(ostream &out) const;
+//
+// Called before printing the remaining elements:
+//   void LogSeparator(ostream &out) const;
+//
+// Returns the maximum number of elements to print:
+//   int64 MaxElements() const;
+//
+// Called to print an indication that MaximumElements() was reached:
+//   void LogEllipsis(ostream &out) const;
+
+namespace internal {
+
+struct LogBase {
+  template <typename ElementT>
+  void Log(std::ostream &out, const ElementT &element) const {  // NOLINT
+    // Fallback to `AbslStringify` if the type does not have `operator<<`.
+    if constexpr (meta_internal::Requires<std::ostream, ElementT>(
+                      [](auto&& x, auto&& y) -> decltype(x << y) {})) {
+      out << element;
+    } else {
+      out << absl::StrCat(element);
+    }
+  }
+  void LogEllipsis(std::ostream &out) const {  // NOLINT
+    out << "...";
+  }
+};
+
+struct LogShortBase : public LogBase {
+  void LogOpening(std::ostream &out) const { out << "["; }        // NOLINT
+  void LogClosing(std::ostream &out) const { out << "]"; }        // NOLINT
+  void LogFirstSeparator(std::ostream &out) const { out << ""; }  // NOLINT
+  void LogSeparator(std::ostream &out) const { out << ", "; }     // NOLINT
+};
+
+struct LogMultilineBase : public LogBase {
+  void LogOpening(std::ostream &out) const { out << "["; }          // NOLINT
+  void LogClosing(std::ostream &out) const { out << "\n]"; }        // NOLINT
+  void LogFirstSeparator(std::ostream &out) const { out << "\n"; }  // NOLINT
+  void LogSeparator(std::ostream &out) const { out << "\n"; }       // NOLINT
+};
+
+struct LogLegacyBase : public LogBase {
+  void LogOpening(std::ostream &out) const { out << ""; }         // NOLINT
+  void LogClosing(std::ostream &out) const { out << ""; }         // NOLINT
+  void LogFirstSeparator(std::ostream &out) const { out << ""; }  // NOLINT
+  void LogSeparator(std::ostream &out) const { out << " "; }      // NOLINT
+};
+
+}  // namespace internal
+
+// LogShort uses [] braces and separates items with comma-spaces.  For
+// example "[1, 2, 3]".
+struct LogShort : public internal::LogShortBase {
+  int64_t MaxElements() const { return (std::numeric_limits<int64_t>::max)(); }
+};
+
+// LogShortUpToN(max_elements) formats the same as LogShort but prints no more
+// than the max_elements elements.
+class LogShortUpToN : public internal::LogShortBase {
+ public:
+  explicit LogShortUpToN(int64_t max_elements) : max_elements_(max_elements) {}
+  int64_t MaxElements() const { return max_elements_; }
+
+ private:
+  int64_t max_elements_;
+};
+
+// LogShortUpTo100 formats the same as LogShort but prints no more
+// than 100 elements.
+struct LogShortUpTo100 : public LogShortUpToN {
+  LogShortUpTo100() : LogShortUpToN(100) {}
+};
+
+// LogMultiline uses [] braces and separates items with
+// newlines.  For example "[
+// 1
+// 2
+// 3
+// ]".
+struct LogMultiline : public internal::LogMultilineBase {
+  int64_t MaxElements() const { return (std::numeric_limits<int64_t>::max)(); }
+};
+
+// LogMultilineUpToN(max_elements) formats the same as LogMultiline but
+// prints no more than max_elements elements.
+class LogMultilineUpToN : public internal::LogMultilineBase {
+ public:
+  explicit LogMultilineUpToN(int64_t max_elements)
+      : max_elements_(max_elements) {}
+  int64_t MaxElements() const { return max_elements_; }
+
+ private:
+  int64_t max_elements_;
+};
+
+// LogMultilineUpTo100 formats the same as LogMultiline but
+// prints no more than 100 elements.
+struct LogMultilineUpTo100 : public LogMultilineUpToN {
+  LogMultilineUpTo100() : LogMultilineUpToN(100) {}
+};
+
+// The legacy behavior of LogSequence() does not use braces and
+// separates items with spaces.  For example "1 2 3".
+struct LogLegacyUpTo100 : public internal::LogLegacyBase {
+  int64_t MaxElements() const { return 100; }
+};
+struct LogLegacy : public internal::LogLegacyBase {
+  int64_t MaxElements() const { return (std::numeric_limits<int64_t>::max)(); }
+};
+
+// The default policy for new code.
+typedef LogShortUpTo100 LogDefault;
+
+// LogRangeToStream should be used to define operator<< for
+// STL and STL-like containers.  For example, see stl_logging.h.
+template <typename IteratorT, typename PolicyT>
+inline void LogRangeToStream(std::ostream &out,  // NOLINT
+                             IteratorT begin, IteratorT end,
+                             const PolicyT &policy) {
+  policy.LogOpening(out);
+  for (int64_t i = 0; begin != end && i < policy.MaxElements(); ++i, ++begin) {
+    if (i == 0) {
+      policy.LogFirstSeparator(out);
+    } else {
+      policy.LogSeparator(out);
+    }
+    policy.Log(out, *begin);
+  }
+  if (begin != end) {
+    policy.LogSeparator(out);
+    policy.LogEllipsis(out);
+  }
+  policy.LogClosing(out);
+}
+
+namespace detail {
+
+// RangeLogger is a helper class for LogRange and LogContainer; do not use it
+// directly.  This object captures iterators into the argument of the LogRange
+// and LogContainer functions, so its lifetime should be confined to a single
+// logging statement.  Objects of this type should not be assigned to local
+// variables.
+template <typename IteratorT, typename PolicyT>
+class RangeLogger {
+ public:
+  RangeLogger(const IteratorT &begin, const IteratorT &end,
+              const PolicyT &policy)
+      : begin_(begin), end_(end), policy_(policy) {}
+
+  friend std::ostream &operator<<(std::ostream &out, const RangeLogger &range) {
+    LogRangeToStream<IteratorT, PolicyT>(out, range.begin_, range.end_,
+                                         range.policy_);
+    return out;
+  }
+
+  // operator<< above is generally recommended. However, some situations may
+  // require a string, so a convenience str() method is provided as well.
+  std::string str() const {
+    std::stringstream ss;
+    ss << *this;
+    return ss.str();
+  }
+
+ private:
+  IteratorT begin_;
+  IteratorT end_;
+  PolicyT policy_;
+};
+
+template <typename E>
+class EnumLogger {
+ public:
+  explicit EnumLogger(E e) : e_(e) {}
+
+  friend std::ostream &operator<<(std::ostream &out, const EnumLogger &v) {
+    using I = typename std::underlying_type<E>::type;
+    return out << static_cast<I>(v.e_);
+  }
+
+ private:
+  E e_;
+};
+
+}  // namespace detail
+
+// Log a range using "policy".  For example:
+//
+//   LOG(INFO) << LogRange(start_pos, end_pos, LogMultiline());
+//
+// The above example will print the range using newlines between
+// elements, enclosed in [] braces.
+template <typename IteratorT, typename PolicyT>
+detail::RangeLogger<IteratorT, PolicyT> LogRange(const IteratorT &begin,
+                                                 const IteratorT &end,
+                                                 const PolicyT &policy) {
+  return detail::RangeLogger<IteratorT, PolicyT>(begin, end, policy);
+}
+
+// Log a range.  For example:
+//
+//   LOG(INFO) << LogRange(start_pos, end_pos);
+//
+// By default, Range() uses the LogShortUpTo100 policy: comma-space
+// separation, no newlines, and with limit of 100 items.
+template <typename IteratorT>
+detail::RangeLogger<IteratorT, LogDefault> LogRange(const IteratorT &begin,
+                                                    const IteratorT &end) {
+  return LogRange(begin, end, LogDefault());
+}
+
+// Log a container using "policy".  For example:
+//
+//   LOG(INFO) << LogContainer(container, LogMultiline());
+//
+// The above example will print the container using newlines between
+// elements, enclosed in [] braces.
+template <typename ContainerT, typename PolicyT>
+auto LogContainer(const ContainerT& container, const PolicyT& policy)
+    -> decltype(LogRange(container.begin(), container.end(), policy)) {
+  return LogRange(container.begin(), container.end(), policy);
+}
+
+// Log a container.  For example:
+//
+//   LOG(INFO) << LogContainer(container);
+//
+// By default, Container() uses the LogShortUpTo100 policy: comma-space
+// separation, no newlines, and with limit of 100 items.
+template <typename ContainerT>
+auto LogContainer(const ContainerT& container)
+    -> decltype(LogContainer(container, LogDefault())) {
+  return LogContainer(container, LogDefault());
+}
+
+// Log a (possibly scoped) enum.  For example:
+//
+//   enum class Color { kRed, kGreen, kBlue };
+//   LOG(INFO) << LogEnum(kRed);
+template <typename E>
+detail::EnumLogger<E> LogEnum(E e) {
+  static_assert(std::is_enum<E>::value, "must be an enum");
+  return detail::EnumLogger<E>(e);
+}
+
+}  // namespace log_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_LOG_INTERNAL_CONTAINER_H_
diff --git a/absl/log/internal/container_test.cc b/absl/log/internal/container_test.cc
new file mode 100644
index 0000000..0a5a058
--- /dev/null
+++ b/absl/log/internal/container_test.cc
@@ -0,0 +1,254 @@
+// Copyright 2025 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/log/internal/container.h"
+
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <ostream>
+#include <set>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/str_join.h"
+#include "absl/types/span.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+namespace {
+
+class ContainerLoggingTest : public ::testing::Test {
+ protected:
+  ContainerLoggingTest() : stream_(new std::stringstream) {}
+  std::ostream& stream() { return *stream_; }
+  std::string logged() {
+    std::string r = stream_->str();
+    stream_ = std::make_unique<std::stringstream>();
+    return r;
+  }
+
+ private:
+  std::unique_ptr<std::stringstream> stream_;
+};
+
+TEST_F(ContainerLoggingTest, ShortRange) {
+  std::vector<std::string> words = {"hi", "hello"};
+  LogRangeToStream(stream(), words.begin(), words.end(), LogMultiline());
+  EXPECT_EQ("[\nhi\nhello\n]", logged());
+}
+
+TEST_F(ContainerLoggingTest, LegacyRange) {
+  std::vector<int> lengths = {1, 2};
+  LogRangeToStream(stream(), lengths.begin(), lengths.end(),
+                   LogLegacyUpTo100());
+  EXPECT_EQ("1 2", logged());
+}
+
+TEST_F(ContainerLoggingTest, ToString) {
+  std::vector<int> lengths = {1, 2, 3, 4, 5};
+  EXPECT_EQ(LogContainer(lengths).str(), "[1, 2, 3, 4, 5]");
+}
+
+class UserDefFriend {
+ public:
+  explicit UserDefFriend(int i) : i_(i) {}
+
+ private:
+  friend std::ostream& operator<<(std::ostream& str, const UserDefFriend& i) {
+    return str << i.i_;
+  }
+  int i_;
+};
+
+TEST_F(ContainerLoggingTest, RangeOfUserDefined) {
+  std::vector<UserDefFriend> ints = {UserDefFriend(1), UserDefFriend(2),
+                                     UserDefFriend(3)};
+  LogRangeToStream(stream(), ints.begin(), ints.begin() + 1, LogDefault());
+  LogRangeToStream(stream(), ints.begin() + 1, ints.begin() + 2,
+                   LogMultiline());
+  LogRangeToStream(stream(), ints.begin() + 2, ints.begin() + 3, LogDefault());
+  LogRangeToStream(stream(), ints.begin(), ints.begin(), LogMultiline());
+
+  EXPECT_EQ("[1][\n2\n][3][\n]", logged());
+}
+
+TEST_F(ContainerLoggingTest, FullContainer) {
+  std::vector<int> ints;
+  std::vector<int> ints100;
+  std::vector<int> ints123;
+  int64_t max_elements = 123;
+  std::string expected1;
+  std::string expected2;
+  std::string expected3;
+  std::string expected4;
+  std::string expected5;
+  std::string expected6;
+  std::string expected7;
+  std::string expected8;
+  std::string expected9;
+  for (int i = 0; i < 1000; ++i) {
+    ints.push_back(i);
+    if (i < 100) {
+      ints100.push_back(i);
+    }
+    if (i < max_elements) {
+      ints123.push_back(i);
+    }
+  }
+  expected1 = "[\n" + absl::StrJoin(ints, "\n") + "\n]";
+  expected2 = "[" + absl::StrJoin(ints, ", ") + "]";
+  expected3 = "[\n" + absl::StrJoin(ints100, "\n") + "\n...\n]";
+  expected4 = "[" + absl::StrJoin(ints100, ", ") + ", ...]";
+  expected5 = absl::StrJoin(ints100, " ") + " ...";
+  expected6 = "[\n" + absl::StrJoin(ints, "\n") + "\n]";
+  expected7 = "[\n" + absl::StrJoin(ints123, "\n") + "\n...\n]";
+  expected8 = "[" + absl::StrJoin(ints, ", ") + "]";
+  expected9 = "[" + absl::StrJoin(ints123, ", ") + ", ...]";
+
+  LogRangeToStream(stream(), ints.begin(), ints.end(), LogMultiline());
+  EXPECT_EQ(expected1, logged());
+  LogRangeToStream(stream(), ints.begin(), ints.end(), LogShort());
+  EXPECT_EQ(expected2, logged());
+  LogRangeToStream(stream(), ints.begin(), ints.end(), LogMultilineUpTo100());
+  EXPECT_EQ(expected3, logged());
+  LogRangeToStream(stream(), ints.begin(), ints.end(), LogShortUpTo100());
+  EXPECT_EQ(expected4, logged());
+  LogRangeToStream(stream(), ints.begin(), ints.end(), LogLegacyUpTo100());
+  EXPECT_EQ(expected5, logged());
+
+  LogRangeToStream(stream(), ints.begin(), ints.end(),
+                   LogMultilineUpToN(ints.size()));
+  EXPECT_EQ(expected6, logged());
+  LogRangeToStream(stream(), ints.begin(), ints.end(),
+                   LogMultilineUpToN(max_elements));
+  EXPECT_EQ(expected7, logged());
+  LogRangeToStream(stream(), ints.begin(), ints.end(),
+                   LogShortUpToN(ints.size()));
+  EXPECT_EQ(expected8, logged());
+  LogRangeToStream(stream(), ints.begin(), ints.end(),
+                   LogShortUpToN(max_elements));
+  EXPECT_EQ(expected9, logged());
+}
+
+TEST_F(ContainerLoggingTest, LogContainer) {
+  std::set<int> ints = {1, 2, 3};
+  stream() << LogContainer(ints, LogMultiline());
+  EXPECT_EQ("[\n1\n2\n3\n]", logged());
+
+  stream() << LogContainer(ints);
+  EXPECT_EQ("[1, 2, 3]", logged());
+
+  stream() << LogContainer(std::vector<int>(ints.begin(), ints.end()),
+                           LogLegacyUpTo100());
+  EXPECT_EQ("1 2 3", logged());
+}
+
+TEST_F(ContainerLoggingTest, LogMutableSpan) {
+  std::vector<int> ints = {1, 2, 3};
+  absl::Span<int> int_span(ints);
+  stream() << LogContainer(int_span);
+  EXPECT_EQ("[1, 2, 3]", logged());
+}
+
+TEST_F(ContainerLoggingTest, LogRange) {
+  std::set<int> ints = {1, 2, 3};
+  stream() << LogRange(ints.begin(), ints.end(), LogMultiline());
+  EXPECT_EQ("[\n1\n2\n3\n]", logged());
+
+  stream() << LogRange(ints.begin(), ints.end());
+  EXPECT_EQ("[1, 2, 3]", logged());
+}
+
+// Some class with a custom Stringify
+class C {
+ public:
+  explicit C(int x) : x_(x) {}
+
+ private:
+  // This is intentionally made private for the purposes of the test;
+  //` AbslStringify` isn't meant to be called directly, and instead invoked
+  // via `StrCat` and friends.
+  template <typename Sink>
+  friend void AbslStringify(Sink& sink, const C& p) {
+    absl::Format(&sink, "C(%d)", p.x_);
+  }
+
+  int x_;
+};
+
+TEST_F(ContainerLoggingTest, LogContainerWithCustomStringify) {
+  std::vector<C> c = {C(1), C(2), C(3)};
+  stream() << LogContainer(c);
+  EXPECT_EQ("[C(1), C(2), C(3)]", logged());
+}
+
+class LogEnumTest : public ContainerLoggingTest {
+ protected:
+  enum Unscoped { kUnscoped0, kUnscoped1, kUnscoped2 };
+
+  enum StreamableUnscoped {
+    kStreamableUnscoped0,
+    kStreamableUnscoped1,
+    kStreamableUnscoped2
+  };
+
+  enum class Scoped { k0, k1, k2 };
+
+  enum class StreamableScoped { k0, k1, k2 };
+
+  friend std::ostream& operator<<(std::ostream& os, StreamableUnscoped v) {
+    return os << LogEnum(v);
+  }
+
+  friend std::ostream& operator<<(std::ostream& os, StreamableScoped v) {
+    return os << LogEnum(v);
+  }
+};
+
+TEST_F(LogEnumTest, Unscoped) {
+  stream() << LogEnum(kUnscoped0) << "," << LogEnum(kUnscoped1) << ","
+           << LogEnum(kUnscoped2);
+  EXPECT_EQ("0,1,2", logged());
+}
+
+TEST_F(LogEnumTest, StreamableUnscoped) {
+  stream() << kStreamableUnscoped0 << "," << kStreamableUnscoped1 << ","
+           << kStreamableUnscoped2;
+  EXPECT_EQ("0,1,2", logged());
+}
+
+TEST_F(LogEnumTest, Scoped) {
+  stream() << LogEnum(Scoped::k0) << "," << LogEnum(Scoped::k1) << ","
+           << LogEnum(Scoped::k2);
+  EXPECT_EQ("0,1,2", logged());
+}
+
+TEST_F(LogEnumTest, StreamableScoped) {
+  // Test using LogEnum to implement an operator<<.
+  stream() << StreamableScoped::k0 << "," << StreamableScoped::k1 << ","
+           << StreamableScoped::k2;
+  EXPECT_EQ("0,1,2", logged());
+}
+
+}  // namespace
+}  // namespace log_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/meta/BUILD.bazel b/absl/meta/BUILD.bazel
index f6efa42..26468c6 100644
--- a/absl/meta/BUILD.bazel
+++ b/absl/meta/BUILD.bazel
@@ -35,6 +35,57 @@
 licenses(["notice"])
 
 cc_library(
+    name = "constexpr_testing",
+    testonly = 1,
+    hdrs = ["internal/constexpr_testing.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl:__subpackages__",
+    ],
+    deps = [
+        "//absl/base:config",
+    ],
+)
+
+cc_test(
+    name = "constexpr_testing_test",
+    srcs = ["internal/constexpr_testing_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":constexpr_testing",
+        "@googletest//:gtest",
+        "@googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "requires",
+    hdrs = ["internal/requires.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl:__subpackages__",
+    ],
+    deps = [
+        "//absl/base:config",
+    ],
+)
+
+cc_test(
+    name = "requires_test",
+    srcs = ["internal/requires_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":requires",
+        "@googletest//:gtest",
+        "@googletest//:gtest_main",
+    ],
+)
+
+cc_library(
     name = "type_traits",
     hdrs = ["type_traits.h"],
     copts = ABSL_DEFAULT_COPTS,
diff --git a/absl/meta/CMakeLists.txt b/absl/meta/CMakeLists.txt
index d509114..c98c360 100644
--- a/absl/meta/CMakeLists.txt
+++ b/absl/meta/CMakeLists.txt
@@ -16,6 +16,52 @@
 
 absl_cc_library(
   NAME
+    constexpr_testing_internal
+  HDRS
+    "internal/constexpr_testing.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::config
+)
+
+absl_cc_test(
+  NAME
+    constexpr_testing_test
+  SRCS
+    "internal/constexpr_testing_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::constexpr_testing_internal
+    GTest::gmock_main
+)
+
+absl_cc_library(
+  NAME
+    requires_internal
+  HDRS
+    "internal/requires.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::config
+)
+
+absl_cc_test(
+  NAME
+    requires_test
+  SRCS
+    "internal/requires_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::requires_internal
+    GTest::gmock_main
+)
+
+absl_cc_library(
+  NAME
     type_traits
   HDRS
     "type_traits.h"
diff --git a/absl/meta/internal/constexpr_testing.h b/absl/meta/internal/constexpr_testing.h
new file mode 100644
index 0000000..eddf64b
--- /dev/null
+++ b/absl/meta/internal/constexpr_testing.h
@@ -0,0 +1,73 @@
+// Copyright 2025 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_META_INTERNAL_CONSTEXPR_TESTING_H_
+#define ABSL_META_INTERNAL_CONSTEXPR_TESTING_H_
+
+#include <type_traits>
+
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace meta_internal {
+
+// HasConstexprEvaluation([] { ... }) will evaluate to `true` if the
+// lambda can be evaluated in a constant expression and `false`
+// otherwise.
+// The return type of the lambda is not relevant, as long as the whole
+// evaluation works in a constant expression.
+template <typename F>
+constexpr bool HasConstexprEvaluation(F f);
+
+/// Implementation details below ///
+
+namespace internal_constexpr_evaluation {
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wuninitialized"
+#endif
+// This will give a constexpr instance of `F`.
+// This works for captureless lambdas because they have no state and the copy
+// constructor does not look at the input reference.
+template <typename F>
+constexpr F default_instance = default_instance<F>;
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+template <typename F>
+constexpr std::integral_constant<bool, (default_instance<F>(), true)> Tester(
+    int) {
+  return {};
+}
+
+template <typename S>
+constexpr std::false_type Tester(char) {
+  return {};
+}
+
+}  // namespace internal_constexpr_evaluation
+
+template <typename F>
+constexpr bool HasConstexprEvaluation(F) {
+  return internal_constexpr_evaluation::Tester<F>(0);
+}
+
+}  // namespace meta_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_META_INTERNAL_CONSTEXPR_TESTING_H_
diff --git a/absl/meta/internal/constexpr_testing_test.cc b/absl/meta/internal/constexpr_testing_test.cc
new file mode 100644
index 0000000..50c8c53
--- /dev/null
+++ b/absl/meta/internal/constexpr_testing_test.cc
@@ -0,0 +1,40 @@
+// Copyright 2025 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/meta/internal/constexpr_testing.h"
+
+#include <map>
+#include <string_view>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+TEST(ConstexprTesting, Basic) {
+  using absl::meta_internal::HasConstexprEvaluation;
+
+  EXPECT_TRUE(HasConstexprEvaluation([] {}));
+  static constexpr int const_global = 7;
+  EXPECT_TRUE(HasConstexprEvaluation([] { return const_global; }));
+  EXPECT_TRUE(HasConstexprEvaluation([] { return 0; }));
+  EXPECT_TRUE(HasConstexprEvaluation([] { return std::string_view{}; }));
+
+  static int nonconst_global;
+  EXPECT_FALSE(HasConstexprEvaluation([] { return nonconst_global; }));
+  EXPECT_FALSE(HasConstexprEvaluation([] { std::abort(); }));
+  EXPECT_FALSE(HasConstexprEvaluation([] { return std::map<int, int>(); }));
+}
+
+}  // namespace
diff --git a/absl/meta/internal/requires.h b/absl/meta/internal/requires.h
new file mode 100644
index 0000000..2166e99
--- /dev/null
+++ b/absl/meta/internal/requires.h
@@ -0,0 +1,67 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_META_INTERNAL_REQUIRES_H_
+#define ABSL_META_INTERNAL_REQUIRES_H_
+
+#include <type_traits>
+
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace meta_internal {
+
+// C++17 port of the C++20 `requires` expressions.
+// It allows easy inline test of properties of types in template code.
+// https://en.cppreference.com/w/cpp/language/constraints#Requires_expressions
+//
+// Example usage:
+//
+// if constexpr (Requires<T>([](auto&& x) -> decltype(x.foo()) {})) {
+//   // T has foo()
+//   return t.foo();
+// } else if constexpr (Requires<T>([](auto&& x) -> decltype(Bar(x)) {})) {
+//   // Can call Bar with T
+//   return Bar(t);
+// } else if constexpr (Requires<T, U>(
+//     // Can test expression with multiple inputs
+//     [](auto&& x, auto&& y) -> decltype(x + y) {})) {
+//   return t + t2;
+// }
+//
+// The `Requires` function takes a list of types and a generic lambda where all
+// arguments are of type `auto&&`. The lambda is never actually invoked and the
+// body must be empty.
+// When used this way, `Requires` returns whether the expression inside
+// `decltype` is well-formed, when the lambda parameters have the types that
+// are specified by the corresponding template arguments.
+//
+// NOTE: C++17 does not allow lambdas in template parameters, which means that
+// code like the following is _not_ valid in C++17:
+//
+//  template <typename T,
+//            typename = std::enable_if_t<gtl::Requires<T>(
+//              [] (auto&& v) -> decltype(<expr>) {})>>
+//
+template <typename... T, typename F>
+constexpr bool Requires(F) {
+  return std::is_invocable_v<F, T...>;
+}
+
+}  // namespace meta_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_META_INTERNAL_REQUIRES_H_
diff --git a/absl/meta/internal/requires_test.cc b/absl/meta/internal/requires_test.cc
new file mode 100644
index 0000000..046d8f3
--- /dev/null
+++ b/absl/meta/internal/requires_test.cc
@@ -0,0 +1,66 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/meta/internal/requires.h"
+
+#include <string>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+namespace {
+
+TEST(RequiresTest, SimpleLambdasWork) {
+  static_assert(absl::meta_internal::Requires([] {}));
+  static_assert(absl::meta_internal::Requires<int>([](auto&&) {}));
+  static_assert(
+      absl::meta_internal::Requires<int, char>([](auto&&, auto&&) {}));
+}
+
+template <typename T>
+inline constexpr bool has_cstr =
+    absl::meta_internal::Requires<T>([](auto&& x) -> decltype(x.c_str()) {});
+
+template <typename T, typename U>
+inline constexpr bool have_plus = absl::meta_internal::Requires<T, U>(
+    [](auto&& x, auto&& y) -> decltype(x + y) {});
+
+TEST(RequiresTest, CanTestProperties) {
+  static_assert(has_cstr<std::string>);
+  static_assert(!has_cstr<std::vector<int>>);
+
+  static_assert(have_plus<int, double>);
+  static_assert(have_plus<std::string, std::string>);
+  static_assert(!have_plus<std::string, double>);
+}
+
+TEST(RequiresTest, WorksWithUnmovableTypes) {
+  struct S {
+    S(const S&) = delete;
+    int foo() { return 0; }
+  };
+  static_assert(
+      absl::meta_internal::Requires<S>([](auto&& x) -> decltype(x.foo()) {}));
+  static_assert(
+      !absl::meta_internal::Requires<S>([](auto&& x) -> decltype(x.bar()) {}));
+}
+
+TEST(RequiresTest, WorksWithArrays) {
+  static_assert(
+      absl::meta_internal::Requires<int[2]>([](auto&& x) -> decltype(x[1]) {}));
+  static_assert(
+      !absl::meta_internal::Requires<int[2]>([](auto&& x) -> decltype(-x) {}));
+}
+
+}  // namespace
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index 9dde5e5..a1e5021 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -36,16 +36,13 @@
 
 cc_library(
     name = "string_view",
-    srcs = ["string_view.cc"],
     hdrs = ["string_view.h"],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
-        "//absl/base",
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:nullability",
-        "//absl/base:throw_delegate",
     ],
 )
 
@@ -410,23 +407,6 @@
     ],
 )
 
-cc_binary(
-    name = "string_view_benchmark",
-    testonly = True,
-    srcs = ["string_view_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":string_view",
-        ":strings",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/random",
-        "@google_benchmark//:benchmark_main",
-    ],
-)
-
 cc_test(
     name = "string_view_test",
     size = "small",
@@ -1571,3 +1551,43 @@
         "@googletest//:gtest_main",
     ],
 )
+
+cc_library(
+    name = "generic_printer",
+    srcs = [
+        "internal/generic_printer.cc",
+        "internal/generic_printer_internal.h",
+    ],
+    hdrs = ["internal/generic_printer.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl:__subpackages__",
+    ],
+    deps = [
+        ":str_format",
+        ":strings",
+        "//absl/base:config",
+        "//absl/log/internal:container",
+        "//absl/meta:requires",
+    ],
+)
+
+cc_test(
+    name = "generic_printer_test",
+    srcs = ["internal/generic_printer_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":generic_printer",
+        ":strings",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/container:flat_hash_map",
+        "//absl/log",
+        "//absl/status",
+        "//absl/status:statusor",
+        "@googletest//:gtest",
+        "@googletest//:gtest_main",
+    ],
+)
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index 2b97fb5..a03943d 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -19,16 +19,12 @@
     string_view
   HDRS
     "string_view.h"
-  SRCS
-    "string_view.cc"
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
-    absl::base
     absl::config
     absl::core_headers
     absl::nullability
-    absl::throw_delegate
   PUBLIC
 )
 
@@ -1260,3 +1256,40 @@
     absl::strings
     GTest::gmock_main
 )
+
+absl_cc_library(
+  NAME
+    generic_printer_internal
+  SRCS
+    "internal/generic_printer.cc"
+    "internal/generic_printer_internal.h"
+  HDRS
+    "internal/generic_printer.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::config
+    absl::strings
+    absl::str_format
+    absl::log_internal_container
+    absl::requires_internal
+)
+
+absl_cc_test(
+  NAME
+    generic_printer_test
+  SRCS
+    "internal/generic_printer_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::base
+    absl::config
+    absl::flat_hash_map
+    absl::generic_printer_internal
+    absl::log
+    absl::status
+    absl::statusor
+    absl::strings
+    GTest::gmock_main
+)
diff --git a/absl/strings/escaping.cc b/absl/strings/escaping.cc
index 89aa603..2f8cbc1 100644
--- a/absl/strings/escaping.cc
+++ b/absl/strings/escaping.cc
@@ -32,8 +32,8 @@
 #include "absl/base/nullability.h"
 #include "absl/strings/ascii.h"
 #include "absl/strings/charset.h"
+#include "absl/strings/internal/append_and_overwrite.h"
 #include "absl/strings/internal/escaping.h"
-#include "absl/strings/internal/resize_uninitialized.h"
 #include "absl/strings/internal/utf8.h"
 #include "absl/strings/numbers.h"
 #include "absl/strings/resize_and_overwrite.h"
@@ -446,22 +446,22 @@
 
   // We keep 3 slop bytes so that we can call `little_endian::Store32`
   // invariably regardless of the length of the escaped character.
-  constexpr size_t slop_bytes = 3;
+  constexpr size_t kSlopBytes = 3;
   size_t cur_dest_len = dest->size();
-  size_t new_dest_len = cur_dest_len + escaped_len + slop_bytes;
-  ABSL_INTERNAL_CHECK(new_dest_len > cur_dest_len, "std::string size overflow");
-  strings_internal::AppendUninitializedTraits<std::string>::Append(
-      dest, escaped_len + slop_bytes);
-  char* append_ptr = &(*dest)[cur_dest_len];
-
-  for (char c : src) {
-    unsigned char uc = static_cast<unsigned char>(c);
-    size_t char_len = kCEscapedLen[uc];
-    uint32_t little_endian_uint32 = kCEscapedLittleEndianUint32Array[uc];
-    little_endian::Store32(append_ptr, little_endian_uint32);
-    append_ptr += char_len;
-  }
-  dest->resize(new_dest_len - slop_bytes);
+  size_t append_buf_len = cur_dest_len + escaped_len + kSlopBytes;
+  ABSL_INTERNAL_CHECK(append_buf_len > cur_dest_len,
+                      "std::string size overflow");
+  strings_internal::StringAppendAndOverwrite(
+      *dest, append_buf_len, [src, escaped_len](char* append_ptr, size_t) {
+        for (char c : src) {
+          unsigned char uc = static_cast<unsigned char>(c);
+          size_t char_len = kCEscapedLen[uc];
+          uint32_t little_endian_uint32 = kCEscapedLittleEndianUint32Array[uc];
+          little_endian::Store32(append_ptr, little_endian_uint32);
+          append_ptr += char_len;
+        }
+        return escaped_len;
+      });
 }
 
 // Reverses the mapping in Base64EscapeInternal; see that method's
diff --git a/absl/strings/internal/generic_printer.cc b/absl/strings/internal/generic_printer.cc
new file mode 100644
index 0000000..16ca228
--- /dev/null
+++ b/absl/strings/internal/generic_printer.cc
@@ -0,0 +1,107 @@
+// Copyright 2025 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/strings/internal/generic_printer.h"
+
+#include <cstddef>
+#include <cstdlib>
+#include <ostream>
+#include <string>
+
+#include "absl/base/config.h"
+#include "absl/strings/ascii.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/str_format.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace internal_generic_printer {
+
+// Out-of-line helper for PrintAsStringWithEscaping.
+std::ostream& PrintEscapedString(std::ostream& os, absl::string_view v) {
+  return os << "\"" << absl::CHexEscape(v) << "\"";
+}
+
+// Retuns a string representation of 'v', shortened if possible.
+template <class T, class F>
+std::string TryShorten(T v, F strtox) {
+  std::string printed =
+      absl::StrFormat("%.*g", std::numeric_limits<T>::max_digits10 / 2, v);
+  T parsed = strtox(printed.data());
+  if (parsed != v) {
+    printed =
+        absl::StrFormat("%.*g", std::numeric_limits<T>::max_digits10 + 1, v);
+  }
+  return printed;
+}
+
+// Out-of-line helpers for floating point values. These don't necessarily
+// ensure that values are precise, but rather that they are wide enough to
+// represent distinct values. go/c++17std/numeric.limits.members.html
+std::ostream& PrintPreciseFP(std::ostream& os, float v) {
+  return os << TryShorten(v, [](const char* buf) {
+           char* unused;
+           return std::strtof(buf, &unused);
+         }) << "f";
+}
+std::ostream& PrintPreciseFP(std::ostream& os, double v) {
+  return os << TryShorten(v, [](const char* buf) {
+           char* unused;
+           return std::strtod(buf, &unused);
+         });
+}
+std::ostream& PrintPreciseFP(std::ostream& os, long double v) {
+  return os << TryShorten(v, [](const char* buf) {
+           char* unused;
+           return std::strtold(buf, &unused);
+         }) << "L";
+}
+
+// Prints a nibble of 'v' in hexadecimal.
+inline char hexnib(int v) {
+  return static_cast<char>((v < 10 ? '0' : ('a' - 10)) + v);
+}
+
+template <typename T>
+static std::ostream& PrintCharImpl(std::ostream& os, T v) {
+  // Specialization for chars: print as 'c' if printable, otherwise
+  // hex-escaped.
+  return (absl::ascii_isprint(static_cast<unsigned char>(v))
+              ? (os << (v == '\'' ? "'\\" : "'") << v)
+              : (os << "'\\x" << hexnib((v >> 4) & 0xf) << hexnib(v & 0xf)))
+         << "' (0x" << hexnib((v >> 4) & 0xf) << hexnib(v & 0xf) << " "
+         << static_cast<int>(v) << ")";
+}
+
+std::ostream& PrintChar(std::ostream& os, char c) {
+  return PrintCharImpl(os, c);
+}
+
+std::ostream& PrintChar(std::ostream& os, signed char c) {
+  return PrintCharImpl(os, c);
+}
+
+std::ostream& PrintChar(std::ostream& os, unsigned char c) {
+  return PrintCharImpl(os, c);
+}
+
+std::ostream& PrintByte(std::ostream& os, std::byte b) {
+  auto v = std::to_integer<int>(b);
+  os << "0x" << hexnib((v >> 4) & 0xf) << hexnib(v & 0xf);
+  return os;
+}
+
+}  // namespace internal_generic_printer
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/generic_printer.h b/absl/strings/internal/generic_printer.h
new file mode 100644
index 0000000..ed60155
--- /dev/null
+++ b/absl/strings/internal/generic_printer.h
@@ -0,0 +1,115 @@
+// Copyright 2025 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_STRINGS_INTERNAL_GENERIC_PRINTER_H_
+#define ABSL_STRINGS_INTERNAL_GENERIC_PRINTER_H_
+
+#include "absl/strings/internal/generic_printer_internal.h"  // IWYU pragma: export
+
+// Helpers for printing objects in generic code.
+//
+// These functions help with streaming in generic code. It may be desirable, for
+// example, to log values from generic functions; however, operator<< may not be
+// overloaded for all types.
+//
+// The helpers in this library use, in order of precedence:
+//
+//  1. std::string and std::string_view are quoted and escaped. (The specific
+//     format is not guaranteed to be stable.)
+//  2. A defined AbslStringify() method.
+//  3. absl::log_internal::LogContainer, if the object is a STL container.
+//  4. For std::tuple, std::pair, and std::optional, recursively calls
+//     GenericPrint for each element.
+//  5. Floating point values are printed with enough precision for round-trip.
+//  6. char values are quoted, with non-printable values escaped, and the
+//     char's numeric value is included.
+//  7. A defined operator<< overload (which should be found by ADL).
+//  8. A defined DebugString() const method.
+//  9. A fallback value with basic information otherwise.
+//
+// Note that the fallback value means that if no formatting conversion is
+// defined, you will not see a compile-time error. This also means that
+// GenericPrint() can safely be used in generic template contexts, and can
+// format any types needed (even though the output will vary).
+//
+// Example usage:
+//
+//   // All values after GenericPrint() are formatted:
+//   LOG(INFO) << GenericPrint()
+//             << int_var         // <- printed normally
+//             << float_var       // <- sufficient precision for round-trip
+//             << " unchanged literal text ";
+//
+//   // Just one value is formatted:
+//   LOG(INFO) << GenericPrint(string("this is quoted and escaped\t\n"))
+//             << GenericPrint("but not this, ");
+//             << string("and not this.");
+//
+// To make a type loggable with GenericPrint, prefer defining operator<< as a
+// friend function. For example:
+//
+//   class TypeToLog {
+//    public:
+//     string ToString() const;  // Many types already implement this.
+//                               // Define out-of-line if it is complex.
+//     friend std::ostream& operator<<(std::ostream& os, const TypeToLog& v) {
+//       return os << v.ToString();  // OK to define in-line instead, if simple.
+//     }
+//   };
+//
+// (Defining operator<< as an inline friend free function allows it to be found
+// by Argument-Dependent Lookup, or ADL, which is the mechanism typically used
+// for operator overload resolution. An inline friend function is the tightest
+// scope possible for overloading the left-hand side of an operator.)
+
+#include <ostream>
+#include <utility>
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace strings_internal {
+
+// Helper for logging values in generic code.
+//
+// Example usage:
+//
+//   template <typename T>
+//   void GenericFunction() {
+//     T v1, v2;
+//     VLOG(1) << GenericPrint() << v1 << v2;  // GenericPrint everything
+//     VLOG(1) << GenericPrint(v1) << v2;  // GenericPrint just v1
+//   }
+//
+inline constexpr internal_generic_printer::GenericPrintAdapterFactory
+    GenericPrint{};
+
+// Generic printer type: this class can be used, for example, as a template
+// argument to allow users to provide alternative printing strategies.
+//
+// For example, to allow callers to provide a custom strategy:
+//
+//   template <typename T, typename PrinterT = GenericPrinter<T>>
+//   void GenericFunction() {
+//     T value;
+//     VLOG(1) << PrinterT{value};
+//   }
+//
+template <typename T>
+using GenericPrinter = internal_generic_printer::GenericPrinter<T>;
+
+}  // namespace strings_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_GENERIC_PRINTER_H_
diff --git a/absl/strings/internal/generic_printer_internal.h b/absl/strings/internal/generic_printer_internal.h
new file mode 100644
index 0000000..1a2d0bd
--- /dev/null
+++ b/absl/strings/internal/generic_printer_internal.h
@@ -0,0 +1,423 @@
+// Copyright 2025 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_STRINGS_INTERNAL_GENERIC_PRINTER_INTERNAL_H_
+#define ABSL_STRINGS_INTERNAL_GENERIC_PRINTER_INTERNAL_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <ostream>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <variant>
+#include <vector>
+
+#include "absl/base/config.h"
+#include "absl/log/internal/container.h"
+#include "absl/meta/internal/requires.h"
+#include "absl/strings/has_absl_stringify.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+
+// NOTE: we do not want to expand the dependencies of this library. All
+// compatibility detection must be done in a generic way, without having to
+// include the headers of other libraries.
+
+// Internal implementation details: see generic_printer.h.
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace internal_generic_printer {
+
+template <typename T>
+std::ostream& GenericPrintImpl(std::ostream& os, const T& v);
+
+// Scope blocker: we must always use ADL in our predicates below.
+struct Anonymous;
+std::ostream& operator<<(const Anonymous&, const Anonymous&) = delete;
+
+// Logging policy for LogContainer. Our SFINAE overload will not fail
+// if the contained type cannot be printed, so make sure to circle back to
+// GenericPrinter.
+struct ContainerLogPolicy {
+  void LogOpening(std::ostream& os) const { os << "["; }
+  void LogClosing(std::ostream& os) const { os << "]"; }
+  void LogFirstSeparator(std::ostream& os) const { os << ""; }
+  void LogSeparator(std::ostream& os) const { os << ", "; }
+  int64_t MaxElements() const { return 15; }
+  template <typename T>
+  void Log(std::ostream& os, const T& element) const {
+    internal_generic_printer::GenericPrintImpl(os, element);
+  }
+  void LogEllipsis(std::ostream& os) const { os << "..."; }
+};
+
+// Out-of-line helper for PrintAsStringWithEscaping.
+std::ostream& PrintEscapedString(std::ostream& os, absl::string_view v);
+
+// Trait to recognize a string, possibly with a custom allocator.
+template <class T>
+inline constexpr bool is_any_string = false;
+template <class A>
+inline constexpr bool
+    is_any_string<std::basic_string<char, std::char_traits<char>, A>> = true;
+
+// Trait to recognize a supported pointer. Below are documented reasons why
+// raw pointers and std::shared_ptr are not currently supported.
+// See also http://b/239459272#comment9 and the discussion in the comment
+// threads of cl/485200996.
+//
+// The tl;dr:
+// 1. Pointers that logically represent an object (or nullptr) are safe to print
+//    out.
+// 2. Pointers that may represent some other concept (delineating memory bounds,
+//    overridden managed-memory deleters to mimic RAII, ...) may be unsafe to
+//    print out.
+//
+// raw pointers:
+//  - A pointer one-past the last element of an array
+//  - Non-null but non-dereferenceable: https://gcc.godbolt.org/z/sqsqGvKbP
+//
+// `std::shared_ptr`:
+//  - "Aliasing" / similar to raw pointers: https://gcc.godbolt.org/z/YbWPzvhae
+template <class T, class = void>
+inline constexpr bool is_supported_ptr = false;
+// `std::unique_ptr` has the same theoretical risks as raw pointers, but those
+// risks are far less likely (typically requiring a custom deleter), and the
+// benefits of supporting unique_ptr outweigh the costs.
+//  - Note: `std::unique_ptr<T[]>` cannot safely be and is not dereferenced.
+template <class A, class... Deleter>
+inline constexpr bool is_supported_ptr<std::unique_ptr<A, Deleter...>> = true;
+// `ArenaSafeUniquePtr` is at least as safe as `std::unique_ptr`.
+template <class T>
+inline constexpr bool is_supported_ptr<
+    T,
+    // Check for `ArenaSafeUniquePtr` without having to include its header here.
+    // This does match any type named `ArenaSafeUniquePtr`, regardless of the
+    // namespace it is defined in, but it's pretty plausible that any such type
+    // would be a fork.
+    decltype(T().~ArenaSafeUniquePtr())> = true;
+
+// Specialization for floats: print floating point types using their
+// max_digits10 precision. This ensures each distinct underlying values
+// can be represented uniquely, even though it's not (strictly speaking)
+// the most precise representation.
+std::ostream& PrintPreciseFP(std::ostream& os, float v);
+std::ostream& PrintPreciseFP(std::ostream& os, double v);
+std::ostream& PrintPreciseFP(std::ostream& os, long double v);
+
+std::ostream& PrintChar(std::ostream& os, char c);
+std::ostream& PrintChar(std::ostream& os, signed char c);
+std::ostream& PrintChar(std::ostream& os, unsigned char c);
+
+std::ostream& PrintByte(std::ostream& os, std::byte b);
+
+template <class... Ts>
+std::ostream& PrintTuple(std::ostream& os, const std::tuple<Ts...>& tuple) {
+  absl::string_view sep = "";
+  const auto print_one = [&](const auto& v) {
+    os << sep;
+    (GenericPrintImpl)(os, v);
+    sep = ", ";
+  };
+  os << "<";
+  std::apply([&](const auto&... v) { (print_one(v), ...); }, tuple);
+  os << ">";
+  return os;
+}
+
+template <typename T, typename U>
+std::ostream& PrintPair(std::ostream& os, const std::pair<T, U>& p) {
+  os << "<";
+  (GenericPrintImpl)(os, p.first);
+  os << ", ";
+  (GenericPrintImpl)(os, p.second);
+  os << ">";
+  return os;
+}
+
+template <typename T>
+std::ostream& PrintOptionalLike(std::ostream& os, const T& v) {
+  if (v.has_value()) {
+    os << "<";
+    (GenericPrintImpl)(os, *v);
+    os << ">";
+  } else {
+    (GenericPrintImpl)(os, std::nullopt);
+  }
+  return os;
+}
+
+template <typename... Ts>
+std::ostream& PrintVariant(std::ostream& os, const std::variant<Ts...>& v) {
+  os << "(";
+  os << "'(index = " << v.index() << ")' ";
+
+  // NOTE(derekbailey): This may throw a std::bad_variant_access if the variant
+  // is "valueless", which only occurs if exceptions are thrown. This is
+  // non-relevant when not using exceptions, but it is worth mentioning if that
+  // invariant is ever changed.
+  std::visit([&](const auto& arg) { (GenericPrintImpl)(os, arg); }, v);
+  os << ")";
+  return os;
+}
+
+template <typename StatusOrLike>
+std::ostream& PrintStatusOrLike(std::ostream& os, const StatusOrLike& v) {
+  os << "<";
+  if (v.ok()) {
+    os << "OK: ";
+    (GenericPrintImpl)(os, *v);
+  } else {
+    (GenericPrintImpl)(os, v.status());
+  }
+  os << ">";
+  return os;
+}
+
+template <typename SmartPointer>
+std::ostream& PrintSmartPointerContents(std::ostream& os,
+                                        const SmartPointer& v) {
+  os << "<";
+  if (v == nullptr) {
+    (GenericPrintImpl)(os, nullptr);
+  } else {
+    // Cast to void* so that every type (e.g. `char*`) is printed as an address.
+    os << absl::implicit_cast<const void*>(v.get()) << " pointing to ";
+
+    if constexpr (meta_internal::Requires<SmartPointer>(
+                      [](auto&& p) -> decltype(p[0]) {})) {
+      // e.g. std::unique_ptr<int[]>, which only has operator[]
+      os << "an array";
+    } else if constexpr (std::is_object_v<
+                             typename SmartPointer::element_type>) {
+      (GenericPrintImpl)(os, *v);
+    } else {
+      // e.g. std::unique_ptr<void, MyCustomDeleter>
+      os << "a non-object type";
+    }
+  }
+  os << ">";
+  return os;
+}
+
+template <typename T>
+std::ostream& GenericPrintImpl(std::ostream& os, const T& v) {
+  if constexpr (is_any_string<T> || std::is_same_v<T, absl::string_view>) {
+    // Specialization for strings: prints with plausible quoting and escaping.
+    return PrintEscapedString(os, v);
+  } else if constexpr (absl::HasAbslStringify<T>::value) {
+    // If someone has specified `AbslStringify`, we should prefer that.
+    return os << absl::StrCat(v);
+  } else if constexpr (meta_internal::Requires<const T>(
+                           [&](auto&& w)
+                               -> decltype((
+                                   PrintTuple)(std::declval<std::ostream&>(),
+                                               w)) {})) {
+    // For tuples, use `< elem0, ..., elemN >`.
+    return (PrintTuple)(os, v);
+
+  } else if constexpr (meta_internal::Requires<const T>(
+                           [&](auto&& w)
+                               -> decltype((
+                                   PrintPair)(std::declval<std::ostream&>(),
+                                              w)) {})) {
+    // For pairs, use `< first, second >`.
+    return (PrintPair)(os, v);
+
+  } else if constexpr (meta_internal::Requires<const T>(
+                           [&](auto&& w)
+                               -> decltype((
+                                   PrintVariant)(std::declval<std::ostream&>(),
+                                                 w)) {})) {
+    // For std::variant, use `std::visit(v)`
+    return (PrintVariant)(os, v);
+  } else if constexpr (is_supported_ptr<T>) {
+    return (PrintSmartPointerContents)(os, v);
+  } else if constexpr (meta_internal::Requires<const T>(
+                           [&](auto&& w) -> decltype(w.ok(), w.status(), *w) {
+                           })) {
+    return (PrintStatusOrLike)(os, v);
+  } else if constexpr (meta_internal::Requires<const T>(
+                           [&](auto&& w) -> decltype(w.has_value(), *w) {})) {
+    return (PrintOptionalLike)(os, v);
+  } else if constexpr (std::is_same_v<T, std::nullopt_t>) {
+    // Specialization for nullopt.
+    return os << "nullopt";
+
+  } else if constexpr (std::is_same_v<T, std::nullptr_t>) {
+    // Specialization for nullptr.
+    return os << "nullptr";
+
+  } else if constexpr (std::is_floating_point_v<T>) {
+    // For floating point print with enough precision for a roundtrip.
+    return PrintPreciseFP(os, v);
+
+  } else if constexpr (std::is_same_v<T, char> ||
+                       std::is_same_v<T, signed char> ||
+                       std::is_same_v<T, unsigned char>) {
+    // Chars are printed as the char (if a printable char) and the integral
+    // representation in hex and decimal.
+    return PrintChar(os, v);
+
+  } else if constexpr (std::is_same_v<T, std::byte>) {
+    return PrintByte(os, v);
+
+  } else if constexpr (std::is_same_v<T, bool> ||
+                       std::is_same_v<T,
+                                      typename std::vector<bool>::reference> ||
+                       std::is_same_v<
+                           T, typename std::vector<bool>::const_reference>) {
+    return os << (v ? "true" : "false");
+
+  } else if constexpr (meta_internal::Requires<const T>(
+                           [&](auto&& w)
+                               -> decltype(ProtobufInternalGetEnumDescriptor(
+                                   w)) {})) {
+    os << static_cast<std::underlying_type_t<T>>(v);
+    if (auto* desc =
+            ProtobufInternalGetEnumDescriptor(T{})->FindValueByNumber(v)) {
+      os << "(" << desc->name() << ")";
+    }
+    return os;
+  } else if constexpr (!std::is_enum_v<T> &&
+                       meta_internal::Requires<const T>(
+                           [&](auto&& w) -> decltype(absl::StrCat(w)) {})) {
+    return os << absl::StrCat(v);
+
+  } else if constexpr (meta_internal::Requires<const T>(
+                           [&](auto&& w)
+                               -> decltype(std::declval<std::ostream&>()
+                                           << log_internal::LogContainer(w)) {
+                           })) {
+    // For containers, use `[ elem0, ..., elemN ]` with a max of 15.
+    return os << log_internal::LogContainer(v, ContainerLogPolicy());
+
+  } else if constexpr (meta_internal::Requires<const T>(
+                           [&](auto&& w)
+                               -> decltype(std::declval<std::ostream&>() << w) {
+                           })) {
+    // Streaming
+    return os << v;
+
+  } else if constexpr (meta_internal::Requires<const T>(
+                           [&](auto&& w)
+                               -> decltype(std::declval<std::ostream&>()
+                                           << w.DebugString()) {})) {
+    // DebugString
+    return os << v.DebugString();
+
+  } else if constexpr (std::is_enum_v<T>) {
+    // In case the underlying type is some kind of char, we have to recurse.
+    return GenericPrintImpl(os, static_cast<std::underlying_type_t<T>>(v));
+
+  } else {
+    // Default: we don't have anything to stream the value.
+    return os << "[unprintable value of size " << sizeof(T) << " @" << &v
+              << "]";
+  }
+}
+
+// GenericPrinter always has a valid operator<<. It defers to the disjuction
+// above.
+template <typename T>
+class GenericPrinter {
+ public:
+  explicit GenericPrinter(const T& value) : value_(value) {}
+
+ private:
+  friend std::ostream& operator<<(std::ostream& os, const GenericPrinter& gp) {
+    return internal_generic_printer::GenericPrintImpl(os, gp.value_);
+  }
+  const T& value_;
+};
+
+struct GenericPrintStreamAdapter {
+  template <class StreamT>
+  struct Impl {
+    // Stream operator: this object will adapt to the underlying stream type,
+    // but only if the Impl is an rvalue. For example, this works:
+    //   std::cout << GenericPrint() << foo;
+    // but not:
+    //   auto adapter = (std::cout << GenericPrint());
+    //   adapter << foo;
+    template <typename T>
+    Impl&& operator<<(const T& value) && {
+      os << internal_generic_printer::GenericPrinter<T>(value);
+      return std::move(*this);
+    }
+
+    // Inhibit using a stack variable for the adapter:
+    template <typename T>
+    Impl& operator<<(const T& value) & = delete;
+
+    // Detects a Flush() method, for LogMessage compatibility.
+    template <typename T>
+    class HasFlushMethod {
+     private:
+      template <typename C>
+      static std::true_type Test(decltype(&C::Flush));
+      template <typename C>
+      static std::false_type Test(...);
+
+     public:
+      static constexpr bool value = decltype(Test<T>(nullptr))::value;
+    };
+
+    // LogMessage compatibility requires a Flush() method.
+    void Flush() {
+      if constexpr (HasFlushMethod<StreamT>::value) {
+        os.Flush();
+      }
+    }
+
+    StreamT& os;
+  };
+
+  // If Impl is evaluated on the RHS of an 'operator&&', and 'lhs && Impl.os'
+  // implicitly converts to void, then it's fine for Impl to do so, too. This
+  // will create precisely as many objects as 'lhs && Impl.os', so we should
+  // both observe any side effects, and avoid observing multiple side
+  // effects. (See absl::log_internal::Voidify for an example of why this might
+  // be useful.)
+  template <typename LHS, typename RHS>
+  friend auto operator&&(LHS&& lhs, Impl<RHS>&& rhs)
+      -> decltype(lhs && rhs.os) {
+    return lhs && rhs.os;
+  }
+
+  template <class StreamT>
+  friend Impl<StreamT> operator<<(StreamT& os, GenericPrintStreamAdapter&&) {
+    return Impl<StreamT>{os};
+  }
+};
+
+struct GenericPrintAdapterFactory {
+  internal_generic_printer::GenericPrintStreamAdapter operator()() const {
+    return internal_generic_printer::GenericPrintStreamAdapter{};
+  }
+  template <typename T>
+  internal_generic_printer::GenericPrinter<T> operator()(const T& value) const {
+    return internal_generic_printer::GenericPrinter<T>{value};
+  }
+};
+
+}  // namespace internal_generic_printer
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_GENERIC_PRINTER_INTERNAL_H_
diff --git a/absl/strings/internal/generic_printer_test.cc b/absl/strings/internal/generic_printer_test.cc
new file mode 100644
index 0000000..4093228
--- /dev/null
+++ b/absl/strings/internal/generic_printer_test.cc
@@ -0,0 +1,685 @@
+// Copyright 2025 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/strings/internal/generic_printer.h"
+
+#include <array>
+#include <cstdint>
+#include <limits>
+#include <map>
+#include <memory>
+#include <optional>
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <variant>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/container/flat_hash_map.h"
+#include "absl/status/status.h"
+#include "absl/status/statusor.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/substitute.h"
+
+namespace generic_logging_test {
+struct NotStreamable {};
+}  // namespace generic_logging_test
+
+static std::ostream& operator<<(std::ostream& os,
+                                const generic_logging_test::NotStreamable&) {
+  return os << "This overload should NOT be found by GenericPrint.";
+}
+
+// Types to test selection logic for streamable and non-streamable types.
+namespace generic_logging_test {
+struct Streamable {
+  int x;
+  friend std::ostream& operator<<(std::ostream& os, const Streamable& l) {
+    return os << "Streamable{" << l.x << "}";
+  }
+};
+}  // namespace generic_logging_test
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace strings_internal {
+namespace {
+
+using ::testing::AllOf;
+using ::testing::AnyOf;
+using ::testing::ContainsRegex;
+using ::testing::EndsWith;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::MatchesRegex;
+
+struct AbslStringifiable {
+  template <typename S>
+  friend void AbslStringify(S& sink, const AbslStringifiable&) {
+    sink.Append("AbslStringifiable!");
+  }
+};
+
+auto IsUnprintable() {
+#ifdef GTEST_USES_SIMPLE_RE
+  return HasSubstr("unprintable value of size");
+#else
+  return ContainsRegex(
+      "\\[unprintable value of size [0-9]+ @(0x)?[0-9a-fA-F]+\\]");
+#endif
+}
+
+auto HasExactlyNInstancesOf(int n, absl::string_view me) {
+#ifdef GTEST_USES_SIMPLE_RE
+  (void)n;
+  return HasSubstr(me);
+#else
+  absl::string_view value_m_times = "(.*$0){$1}.*";
+
+  return AllOf(MatchesRegex(absl::Substitute(value_m_times, me, n)),
+               Not(MatchesRegex(absl::Substitute(value_m_times, me, n + 1))));
+#endif
+}
+
+template <typename T>
+std::string GenericPrintToString(const T& v) {
+  std::stringstream ss;
+  ss << GenericPrint(v);
+  {
+    std::stringstream ss2;
+    ss2 << GenericPrint() << v;
+    EXPECT_EQ(ss.str(), ss2.str());
+  }
+  return ss.str();
+}
+
+TEST(GenericPrinterTest, Bool) {
+  EXPECT_EQ("true", GenericPrintToString(true));
+  EXPECT_EQ("false", GenericPrintToString(false));
+}
+
+TEST(GenericPrinterTest, VectorOfBool) {
+  std::vector<bool> v{true, false, true};
+  const auto& cv = v;
+  EXPECT_EQ("[true, false, true]", GenericPrintToString(v));
+  EXPECT_EQ("true", GenericPrintToString(v[0]));
+  EXPECT_EQ("true", GenericPrintToString(cv[0]));
+}
+
+TEST(GenericPrinterTest, CharLiterals) {
+  EXPECT_EQ(R"(a"\b)", GenericPrintToString(R"(a"\b)"));
+}
+
+TEST(GenericPrinterTest, Builtin) {
+  EXPECT_EQ("123", GenericPrintToString(123));
+}
+
+TEST(GenericPrinterTest, AbslStringifiable) {
+  EXPECT_EQ("AbslStringifiable!", GenericPrintToString(AbslStringifiable{}));
+}
+
+TEST(GenericPrinterTest, Nullptr) {
+  EXPECT_EQ("nullptr", GenericPrintToString(nullptr));
+}
+
+TEST(GenericPrinterTest, Chars) {
+  EXPECT_EQ(R"('\x0a' (0x0a 10))", GenericPrintToString('\x0a'));
+  EXPECT_EQ(R"(' ' (0x20 32))", GenericPrintToString(' '));
+  EXPECT_EQ(R"('~' (0x7e 126))", GenericPrintToString('~'));
+  EXPECT_EQ(R"('\'' (0x27 39))", GenericPrintToString('\''));
+}
+
+TEST(GenericPrinterTest, SignedChars) {
+  EXPECT_EQ(R"('\x0a' (0x0a 10))",
+            GenericPrintToString(static_cast<signed char>('\x0a')));
+  EXPECT_EQ(R"(' ' (0x20 32))",
+            GenericPrintToString(static_cast<signed char>(' ')));
+  EXPECT_EQ(R"('~' (0x7e 126))",
+            GenericPrintToString(static_cast<signed char>('~')));
+  EXPECT_EQ(R"('\'' (0x27 39))",
+            GenericPrintToString(static_cast<signed char>('\'')));
+}
+
+TEST(GenericPrinterTest, UnsignedChars) {
+  EXPECT_EQ(R"('\x0a' (0x0a 10))",
+            GenericPrintToString(static_cast<unsigned char>('\x0a')));
+  EXPECT_EQ(R"(' ' (0x20 32))",
+            GenericPrintToString(static_cast<unsigned char>(' ')));
+  EXPECT_EQ(R"('~' (0x7e 126))",
+            GenericPrintToString(static_cast<unsigned char>('~')));
+  EXPECT_EQ(R"('\'' (0x27 39))",
+            GenericPrintToString(static_cast<unsigned char>('\'')));
+}
+
+TEST(GenericPrinterTest, Bytes) {
+  EXPECT_EQ("0x00", GenericPrintToString(static_cast<std::byte>(0)));
+  EXPECT_EQ("0x7f", GenericPrintToString(static_cast<std::byte>(0x7F)));
+  EXPECT_EQ("0xff", GenericPrintToString(static_cast<std::byte>(0xFF)));
+}
+
+TEST(GenericPrinterTest, Strings) {
+  const std::string expected_quotes = R"("a\"\\b")";
+  EXPECT_EQ(expected_quotes, GenericPrintToString(std::string(R"(a"\b)")));
+  const std::string expected_nonprintable = R"("\x00\xcd\n\xab")";
+  EXPECT_EQ(expected_nonprintable,
+            GenericPrintToString(absl::string_view("\0\315\n\xAB", 4)));
+}
+
+TEST(GenericPrinterTest, PreciseFloat) {
+  // Instead of testing exactly how the values are formatted, just check that
+  // they are distinct.
+
+  // Ensure concise output for exact values:
+  EXPECT_EQ("1f", GenericPrintToString(1.f));
+  EXPECT_EQ("1.1f", GenericPrintToString(1.1f));
+
+  // Plausible real-world values:
+  float f = 10.0000095f;
+  EXPECT_NE(GenericPrintToString(f), GenericPrintToString(10.0000105f));
+  // Smallest increment for a real-world value:
+  EXPECT_NE(GenericPrintToString(f),
+            GenericPrintToString(std::nextafter(f, 11)));
+  // The two smallest (finite) values possible:
+  EXPECT_NE(GenericPrintToString(std::numeric_limits<float>::lowest()),
+            GenericPrintToString(
+                std::nextafter(std::numeric_limits<float>::lowest(), 1)));
+  // Ensure the value has the correct type suffix:
+  EXPECT_THAT(GenericPrintToString(0.f), EndsWith("f"));
+}
+
+TEST(GenericPrinterTest, PreciseDouble) {
+  EXPECT_EQ("1", GenericPrintToString(1.));
+  EXPECT_EQ("1.1", GenericPrintToString(1.1));
+  double d = 10.000000000000002;
+  EXPECT_NE(GenericPrintToString(d), GenericPrintToString(10.000000000000004));
+  EXPECT_NE(GenericPrintToString(d),
+            GenericPrintToString(std::nextafter(d, 11)));
+  EXPECT_NE(GenericPrintToString(std::numeric_limits<double>::lowest()),
+            GenericPrintToString(
+                std::nextafter(std::numeric_limits<double>::lowest(), 1)));
+  EXPECT_THAT(GenericPrintToString(0.), EndsWith("0"));
+}
+
+TEST(GenericPrinterTest, PreciseLongDouble) {
+  EXPECT_EQ("1L", GenericPrintToString(1.L));
+  EXPECT_EQ("1.1L", GenericPrintToString(1.1L));
+  long double ld = 10.0000000000000000000000000000002;
+  EXPECT_NE(GenericPrintToString(ld),
+            GenericPrintToString(10.0000000000000000000000000000004));
+  EXPECT_NE(GenericPrintToString(ld),
+            GenericPrintToString(std::nextafter(ld, 11)));
+  EXPECT_NE(GenericPrintToString(std::numeric_limits<long double>::lowest()),
+            GenericPrintToString(
+                std::nextafter(std::numeric_limits<long double>::lowest(), 1)));
+  EXPECT_THAT(GenericPrintToString(0.L), EndsWith("L"));
+}
+
+TEST(GenericPrinterTest, StreamableLvalue) {
+  generic_logging_test::Streamable x{234};
+  EXPECT_EQ("Streamable{234}", GenericPrintToString(x));
+}
+
+TEST(GenericPrinterTest, StreamableXvalue) {
+  EXPECT_EQ("Streamable{345}",
+            GenericPrintToString(generic_logging_test::Streamable{345}));
+}
+
+TEST(GenericPrinterTest, NotStreamableWithoutGenericPrint) {
+  ::generic_logging_test::NotStreamable x;
+  std::stringstream ss;
+  ::operator<<(ss, x);
+  EXPECT_EQ(ss.str(), "This overload should NOT be found by GenericPrint.");
+}
+
+TEST(GenericPrinterTest, NotStreamableLvalue) {
+  generic_logging_test::NotStreamable x;
+  EXPECT_THAT(GenericPrintToString(x), IsUnprintable());
+}
+
+TEST(GenericPrinterTest, NotStreamableXvalue) {
+  EXPECT_THAT(GenericPrintToString(generic_logging_test::NotStreamable{}),
+              IsUnprintable());
+}
+
+TEST(GenericPrinterTest, DebugString) {
+  struct WithDebugString {
+    std::string val;
+    std::string DebugString() const {
+      return absl::StrCat("WithDebugString{", val, "}");
+    }
+  };
+  EXPECT_EQ("WithDebugString{foo}",
+            GenericPrintToString(WithDebugString{"foo"}));
+}
+
+TEST(GenericPrinterTest, Vector) {
+  std::vector<int> v = {4, 5, 6};
+  EXPECT_THAT(GenericPrintToString(v), MatchesRegex(".*4,? 5,? 6.*"));
+}
+
+TEST(GenericPrinterTest, StreamableVector) {
+  std::vector<generic_logging_test::Streamable> v = {{7}, {8}, {9}};
+  EXPECT_THAT(GenericPrintToString(v),
+              MatchesRegex(".*Streamable.7.,? Streamable.8.,? Streamable.9.*"));
+}
+
+TEST(GenericPrinterTest, Map) {
+  absl::flat_hash_map<
+      std::string, absl::flat_hash_map<std::string, std::pair<double, double>>>
+      v = {{"A", {{"B", {.5, .25}}}}};
+
+  EXPECT_THAT(GenericPrintToString(v), R"([<"A", [<"B", <0.5, 0.25>>]>])");
+
+  std::map<std::string, std::map<std::string, std::pair<double, double>>> v2 = {
+      {"A", {{"B", {.5, .25}}}}};
+
+  EXPECT_THAT(GenericPrintToString(v2), R"([<"A", [<"B", <0.5, 0.25>>]>])");
+}
+
+TEST(GenericPrinterTest, StreamAdapter) {
+  std::stringstream ss;
+  static_assert(
+      std::is_same<
+          typename std::remove_reference<decltype(ss << GenericPrint())>::type,
+          internal_generic_printer::GenericPrintStreamAdapter::Impl<
+              std::stringstream>>::value,
+      "expected ostream << GenericPrint() to yield adapter impl");
+
+  ss << GenericPrint() << "again, " << "back-up, " << "cue, "
+     << "double-u, " << "eye, "
+     << "four: " << generic_logging_test::NotStreamable{};
+  EXPECT_THAT(
+      ss.str(),
+      MatchesRegex(
+          "again, back-up, cue, double-u, eye, four: .unprintable value.*"));
+}
+
+TEST(GenericPrinterTest, NotStreamableVector) {
+  std::vector<generic_logging_test::NotStreamable> v = {{}, {}, {}};
+#ifdef GTEST_USES_SIMPLE_RE
+  EXPECT_THAT(GenericPrintToString(v), HasSubstr("unprintable"));
+#else
+  EXPECT_THAT(GenericPrintToString(v), MatchesRegex(".*(unprintable.*){3}.*"));
+#endif
+}
+
+struct CustomContainer : public std::array<int, 4> {
+  template <typename Sink>
+  friend void AbslStringify(Sink& sink, const CustomContainer& c) {
+    absl::Format(&sink, "%d %d", c[0], c[1]);
+  }
+};
+
+// Checks that AbslStringify (go/totw/215) is respected for container-like
+// types.
+TEST(GenericPrinterTest, ContainerLikeCustomLogging) {
+  CustomContainer c = {1, 2, 3, 4};
+  EXPECT_EQ(GenericPrintToString(c), "1 2");
+}
+
+// Test helper: this function demonstrates customizable printing logic:
+// 'GenericPrinter<T>' can be nominated as a default template argument.
+template <typename T, typename Printer = GenericPrinter<T>>
+std::string SpecializablePrint(const T& v) {
+  std::stringstream ss;
+  ss << Printer{v};
+  return ss.str();
+}
+
+TEST(GenericPrinterTest, DefaultPrinter) {
+  EXPECT_EQ("123", SpecializablePrint(123));
+}
+
+// Example of custom printing logic. This doesn't actually test anything in
+// GenericPrinter, but it's a working example of customizing printing logic (as
+// opposed to the comments in generic_printer.h).
+struct CustomPrinter {
+  explicit CustomPrinter(int) {}
+  friend std::ostream& operator<<(std::ostream& os, CustomPrinter&&) {
+    return os << "custom printer";
+  }
+};
+
+TEST(GenericPrinterTest, CustomPrinter) {
+  EXPECT_EQ("custom printer", (SpecializablePrint<int, CustomPrinter>(123)));
+}
+
+TEST(GenricPrinterTest, Nullopt) {
+  EXPECT_EQ("nullopt", GenericPrintToString(std::nullopt));
+}
+
+TEST(GenericPrinterTest, Optional) {
+  EXPECT_EQ("nullopt", GenericPrintToString(std::optional<int>()));
+  EXPECT_EQ("nullopt", GenericPrintToString(std::optional<int>(std::nullopt)));
+  EXPECT_EQ("<3>", GenericPrintToString(std::make_optional(3)));
+  EXPECT_EQ("<Streamable{3}>", GenericPrintToString(std::make_optional(
+                                   generic_logging_test::Streamable{3})));
+}
+
+TEST(GenericPrinterTest, Tuple) {
+  EXPECT_EQ("<1, two, 3>", GenericPrintToString(std::make_tuple(1, "two", 3)));
+}
+
+TEST(GenericPrinterTest, EmptyTuple) {
+  EXPECT_EQ("<>", GenericPrintToString(std::make_tuple()));
+}
+
+TEST(GenericPrinterTest, TupleWithStreamableMember) {
+  EXPECT_EQ("<1, two, Streamable{3}>",
+            GenericPrintToString(std::make_tuple(
+                1, "two", generic_logging_test::Streamable{3})));
+}
+
+TEST(GenericPrinterTest, Variant) {
+  EXPECT_EQ(R"(('(index = 0)' "cow"))",
+            GenericPrintToString(std::variant<std::string, float>("cow")));
+
+  EXPECT_EQ("('(index = 1)' 1.1f)",
+            GenericPrintToString(std::variant<std::string, float>(1.1F)));
+}
+
+TEST(GenericPrinterTest, VariantMonostate) {
+  EXPECT_THAT(GenericPrintToString(std::variant<std::monostate, std::string>()),
+              IsUnprintable());
+}
+
+TEST(GenericPrinterTest, VariantNonStreamable) {
+  EXPECT_EQ(R"(('(index = 0)' "cow"))",
+            GenericPrintToString(
+                std::variant<std::string, generic_logging_test::NotStreamable>(
+                    "cow")));
+
+  EXPECT_THAT(
+      GenericPrintToString(
+          std::variant<std::string, generic_logging_test::NotStreamable>(
+              generic_logging_test::NotStreamable{})),
+      IsUnprintable());
+}
+
+TEST(GenericPrinterTest, VariantNestedVariant) {
+  EXPECT_EQ(
+      "('(index = 1)' ('(index = 1)' 1.1f))",
+      GenericPrintToString(std::variant<std::string, std::variant<int, float>>(
+          std::variant<int, float>(1.1F))));
+}
+
+TEST(GenericPrinterTest, VariantInPlace) {
+  EXPECT_EQ("('(index = 0)' 17)", GenericPrintToString(std::variant<int, int>(
+                                      std::in_place_index<0>, 17)));
+
+  EXPECT_EQ("('(index = 1)' 17)", GenericPrintToString(std::variant<int, int>(
+                                      std::in_place_index<1>, 17)));
+}
+
+TEST(GenericPrinterTest, StatusOrLikeOkPrintsValue) {
+  EXPECT_EQ(R"(<OK: "cow">)",
+            GenericPrintToString(absl::StatusOr<std::string>("cow")));
+
+  EXPECT_EQ(R"(<OK: 1.1f>)", GenericPrintToString(absl::StatusOr<float>(1.1F)));
+}
+
+TEST(GenericPrinterTest, StatusOrLikeNonOkPrintsStatus) {
+  EXPECT_THAT(
+      GenericPrintToString(absl::StatusOr<float>(
+          absl::InvalidArgumentError("my error message"))),
+      AllOf(HasSubstr("my error message"), HasSubstr("INVALID_ARGUMENT")));
+
+  EXPECT_THAT(GenericPrintToString(
+                  absl::StatusOr<int>(absl::AbortedError("other message"))),
+              AllOf(HasSubstr("other message"), HasSubstr("ABORTED")));
+}
+
+TEST(GenericPrinterTest, StatusOrLikeNonStreamableValueUnprintable) {
+  EXPECT_THAT(
+      GenericPrintToString(absl::StatusOr<generic_logging_test::NotStreamable>(
+          generic_logging_test::NotStreamable{})),
+      IsUnprintable());
+}
+
+TEST(GenericPrinterTest, StatusOrLikeNonStreamableErrorStillPrintable) {
+  EXPECT_THAT(
+      GenericPrintToString(absl::StatusOr<generic_logging_test::NotStreamable>(
+          absl::AbortedError("other message"))),
+      AllOf(HasSubstr("other message"), HasSubstr("ABORTED")));
+}
+
+TEST(GenericPrinterTest, IsSupportedPointer) {
+  using internal_generic_printer::is_supported_ptr;
+
+  EXPECT_TRUE(is_supported_ptr<std::unique_ptr<std::string>>);
+  EXPECT_TRUE(is_supported_ptr<std::unique_ptr<int[]>>);
+  EXPECT_TRUE((is_supported_ptr<std::unique_ptr<void, void (*)(void*)>>));
+
+  EXPECT_FALSE(is_supported_ptr<int*>);
+  EXPECT_FALSE(is_supported_ptr<std::shared_ptr<int>>);
+  EXPECT_FALSE(is_supported_ptr<std::weak_ptr<int>>);
+}
+
+TEST(GenericPrinterTest, SmartPointerPrintsNullptrForAllNullptrs) {
+  std::unique_ptr<std::string> up;
+
+  EXPECT_EQ("<nullptr>", GenericPrintToString(up));
+}
+
+TEST(GenericPrinterTest, SmartPointerPrintsValueIfNonNull) {
+  EXPECT_THAT(GenericPrintToString(std::make_unique<int>(5)),
+              HasSubstr("pointing to 5"));
+}
+
+TEST(GenericPrinterTest, SmartPointerPrintsAddressOfPointee) {
+  auto i = std::make_unique<int>(5);
+  auto c = std::make_unique<char>('z');
+  char memory[] = "abcdefg";
+  auto cp = std::make_unique<char*>(memory);
+
+  EXPECT_THAT(GenericPrintToString(i),
+              AnyOf(Eq(absl::StrFormat("<%016X pointing to 5>",
+                                       reinterpret_cast<intptr_t>(&*i))),
+                    Eq(absl::StrFormat("<%#x pointing to 5>",
+                                       reinterpret_cast<intptr_t>(&*i)))));
+
+  EXPECT_THAT(
+      GenericPrintToString(c),
+      AnyOf(HasSubstr(absl::StrFormat("<%016X pointing to 'z'",
+                                      reinterpret_cast<intptr_t>(&*c))),
+            HasSubstr(absl::StrFormat("<%#x pointing to 'z'",
+                                      reinterpret_cast<intptr_t>(&*c)))));
+
+  EXPECT_THAT(GenericPrintToString(cp),
+              AnyOf(Eq(absl::StrFormat("<%016X pointing to abcdefg>",
+                                       reinterpret_cast<intptr_t>(&*cp))),
+                    Eq(absl::StrFormat("<%#x pointing to abcdefg>",
+                                       reinterpret_cast<intptr_t>(&*cp)))));
+}
+
+TEST(GenericPrinterTest, SmartPointerToArrayOnlyPrintsAddressAndHelpText) {
+  auto empty = std::make_unique<int[]>(0);
+  auto nonempty = std::make_unique<int[]>(5);
+  nonempty[0] = 12345;
+  nonempty[4] = 54321;
+  // NOTE: ArenaSafeUniquePtr is not meant to support array-type template
+  // parameters, so we skip testing that here.
+  // http://g/c-users/J-AEFrFHssY/UMMFzCkdBAAJ, b/265984185.
+
+  EXPECT_THAT(
+      GenericPrintToString(nonempty),
+      AllOf(AnyOf(HasSubstr(absl::StrFormat(
+                      "%016X", reinterpret_cast<intptr_t>(nonempty.get()))),
+                  HasSubstr(absl::StrFormat(
+                      "%#x", reinterpret_cast<intptr_t>(nonempty.get())))),
+            HasSubstr("array"), Not(HasSubstr("to 54321")),
+            Not(HasSubstr("to 12345"))));
+
+  EXPECT_THAT(
+      GenericPrintToString(empty),
+      AllOf(AnyOf(HasSubstr(absl::StrFormat(
+                      "%016X", reinterpret_cast<intptr_t>(empty.get()))),
+                  HasSubstr(absl::StrFormat(
+                      "%#x", reinterpret_cast<intptr_t>(empty.get())))),
+            HasSubstr("array")));
+}
+
+TEST(GenericPrinterTest, SmartPointerToNonObjectType) {
+  auto int_ptr_deleter = [](void* data) {
+    int* p = static_cast<int*>(data);
+    delete p;
+  };
+
+  std::unique_ptr<void, decltype(int_ptr_deleter)> void_ptr(new int(959),
+                                                            int_ptr_deleter);
+
+  EXPECT_THAT(GenericPrintToString(void_ptr),
+              HasSubstr("pointing to a non-object type"));
+}
+
+TEST(GenericPrinterTest, PrintsCustomDeleterSmartPointer) {
+  // Delete `p` (if not nullptr) only on the 4th time the deleter is used.
+  auto four_deleter = [](std::string* p) {
+    static int counter = 0;
+    if (p == nullptr) return;  // skip calls to moved-from destructors.
+    if (++counter >= 4) delete p;
+  };
+
+  // Have four `unique_ptr`s "manage" the same string-pointer, with only the
+  // final (4th) call to the deleter deleting the string pointer.
+  auto* unique_string = new std::string("unique string");
+  std::vector<std::unique_ptr<std::string, decltype(four_deleter)>> test_ptrs;
+  for (int i = 0; i < 4; ++i) {
+    test_ptrs.emplace_back(unique_string, four_deleter);
+  }
+
+  EXPECT_THAT(GenericPrintToString(test_ptrs),
+              HasExactlyNInstancesOf(4, "unique string"));
+}
+
+// Ensure that GenericPrint is robust to recursion when a type's operator<<
+// calls into GenericPrint internally.
+struct CustomRecursive {
+  std::unique_ptr<CustomRecursive> next;
+  int val = 0;
+
+  friend std::ostream& operator<<(std::ostream& os, const CustomRecursive& cr) {
+    return os << "custom print: next = " << GenericPrintToString(cr.next);
+  }
+};
+
+TEST(GenericPrinterTest, DISABLED_CustomPrintOverloadRecursionDetected) {
+  auto r1 = std::make_unique<CustomRecursive>();
+  r1->val = 1;
+  auto& r2 = r1->next = std::make_unique<CustomRecursive>();
+  r2->val = 2;
+  r2->next = std::move(r1);
+
+  EXPECT_THAT(GenericPrintToString(*r2),
+              AllOf(HasExactlyNInstancesOf(2, "custom print"),
+                    HasExactlyNInstancesOf(1, "<recursive>")));
+
+  r2->next = nullptr;  // break the cycle
+}
+// <end DISABLED_ test section>
+
+enum CStyleEnum { kValue0, kValue1 };
+TEST(GenericPrinterTest, Enum) {
+  EXPECT_EQ("1", GenericPrintToString(kValue1));
+}
+
+enum class CppStyleEnum { kValue0, kValue1, kValue2 };
+TEST(GenericPrinterTest, EnumClass) {
+  EXPECT_EQ("2", GenericPrintToString(CppStyleEnum::kValue2));
+}
+
+enum class CharBasedEnum : char { kValueA = 'A', kValue1 = '\x01' };
+TEST(GenericPrinterTest, CharBasedEnum) {
+  EXPECT_EQ("'A' (0x41 65)", GenericPrintToString(CharBasedEnum::kValueA));
+  EXPECT_EQ("'\\x01' (0x01 1)", GenericPrintToString(CharBasedEnum::kValue1));
+}
+
+enum class WideBasedEnum : uint64_t {
+  kValue = std::numeric_limits<uint64_t>::max()
+};
+TEST(GenericPrinterTest, WideBasedEnum) {
+  EXPECT_EQ(absl::StrCat(std::numeric_limits<uint64_t>::max()),
+            GenericPrintToString(WideBasedEnum::kValue));
+}
+
+enum CStyleEnumWithStringify { kValueA = 0, kValueB = 2 };
+template <typename Sink>
+void AbslStringify(Sink& sink, CStyleEnumWithStringify e) {
+  switch (e) {
+    case CStyleEnumWithStringify::kValueA:
+      sink.Append("A");
+      return;
+    case CStyleEnumWithStringify::kValueB:
+      sink.Append("B");
+      return;
+  }
+  sink.Append("??");
+}
+TEST(GenericPrinterTest, CStyleEnumWithStringify) {
+  EXPECT_EQ("A", GenericPrintToString(CStyleEnumWithStringify::kValueA));
+  EXPECT_EQ("??",
+            GenericPrintToString(static_cast<CStyleEnumWithStringify>(1)));
+}
+
+enum class CppStyleEnumWithStringify { kValueA, kValueB, kValueC };
+template <typename Sink>
+void AbslStringify(Sink& sink, CppStyleEnumWithStringify e) {
+  switch (e) {
+    case CppStyleEnumWithStringify::kValueA:
+      sink.Append("A");
+      return;
+    case CppStyleEnumWithStringify::kValueB:
+      sink.Append("B");
+      return;
+    case CppStyleEnumWithStringify::kValueC:
+      sink.Append("C");
+      return;
+  }
+  sink.Append("??");
+}
+TEST(GenericPrinterTest, CppStyleEnumWithStringify) {
+  EXPECT_EQ("A", GenericPrintToString(CppStyleEnumWithStringify::kValueA));
+  EXPECT_EQ("??",
+            GenericPrintToString(static_cast<CppStyleEnumWithStringify>(17)));
+}
+
+enum class CharBasedEnumWithStringify : char { kValueA = 'A', kValueB = 'B' };
+template <typename Sink>
+void AbslStringify(Sink& sink, CharBasedEnumWithStringify e) {
+  switch (e) {
+    case CharBasedEnumWithStringify::kValueA:
+      sink.Append("charA");
+      return;
+    case CharBasedEnumWithStringify::kValueB:
+      sink.Append("charB");
+      return;
+  }
+  sink.Append("??");
+}
+TEST(GenericPrinterTest, CharBasedEnumWithStringify) {
+  EXPECT_EQ("charA", GenericPrintToString(CharBasedEnumWithStringify::kValueA));
+  EXPECT_EQ("??",
+            GenericPrintToString(static_cast<CharBasedEnumWithStringify>('W')));
+}
+
+}  // namespace
+}  // namespace strings_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/resize_uninitialized.h b/absl/strings/internal/resize_uninitialized.h
index 49859dc..7e55e5a 100644
--- a/absl/strings/internal/resize_uninitialized.h
+++ b/absl/strings/internal/resize_uninitialized.h
@@ -68,18 +68,6 @@
   ResizeUninitializedTraits<string_type>::Resize(s, new_size);
 }
 
-// Used to ensure exponential growth so that the amortized complexity of
-// increasing the string size by a small amount is O(1), in contrast to
-// O(str->size()) in the case of precise growth.
-template <typename string_type>
-void STLStringReserveAmortized(string_type* s, size_t new_size) {
-  const size_t cap = s->capacity();
-  if (new_size > cap) {
-    // Make sure to always grow by at least a factor of 2x.
-    s->reserve((std::max)(new_size, 2 * cap));
-  }
-}
-
 // In this type trait, we look for an __append_default_init member function, and
 // we use it if available, otherwise, we use append.
 template <typename string_type, typename = void>
diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc
index 60e43e0..b6a8e42 100644
--- a/absl/strings/numbers.cc
+++ b/absl/strings/numbers.cc
@@ -242,6 +242,18 @@
   return tens;
 }
 
+
+// Encodes v to buffer as 16 digits padded with leading zeros.
+// Pre-condition: v must be < 10^16.
+inline char* EncodePadded16(uint64_t v, char* absl_nonnull buffer) {
+  constexpr uint64_t k1e8 = 100000000;
+  uint32_t hi = static_cast<uint32_t>(v / k1e8);
+  uint32_t lo = static_cast<uint32_t>(v % k1e8);
+  little_endian::Store64(buffer, PrepareEightDigits(hi) + kEightZeroBytes);
+  little_endian::Store64(buffer + 8, PrepareEightDigits(lo) + kEightZeroBytes);
+  return buffer + 16;
+}
+
 inline ABSL_ATTRIBUTE_ALWAYS_INLINE char* absl_nonnull EncodeFullU32(
     uint32_t n, char* absl_nonnull out_str) {
   if (n < 10) {
@@ -265,19 +277,19 @@
   return out_str + sizeof(bottom);
 }
 
-inline ABSL_ATTRIBUTE_ALWAYS_INLINE char* EncodeFullU64(uint64_t i,
-                                                        char* buffer) {
+inline ABSL_ATTRIBUTE_ALWAYS_INLINE char* absl_nonnull EncodeFullU64(
+    uint64_t i, char* absl_nonnull buffer) {
   if (i <= std::numeric_limits<uint32_t>::max()) {
     return EncodeFullU32(static_cast<uint32_t>(i), buffer);
   }
   uint32_t mod08;
   if (i < 1'0000'0000'0000'0000ull) {
     uint32_t div08 = static_cast<uint32_t>(i / 100'000'000ull);
-    mod08 =  static_cast<uint32_t>(i % 100'000'000ull);
+    mod08 = static_cast<uint32_t>(i % 100'000'000ull);
     buffer = EncodeFullU32(div08, buffer);
   } else {
     uint64_t div08 = i / 100'000'000ull;
-    mod08 =  static_cast<uint32_t>(i % 100'000'000ull);
+    mod08 = static_cast<uint32_t>(i % 100'000'000ull);
     uint32_t div016 = static_cast<uint32_t>(div08 / 100'000'000ull);
     uint32_t div08mod08 = static_cast<uint32_t>(div08 % 100'000'000ull);
     uint64_t mid_result = PrepareEightDigits(div08mod08) + kEightZeroBytes;
@@ -290,6 +302,30 @@
   return buffer + sizeof(mod_result);
 }
 
+inline ABSL_ATTRIBUTE_ALWAYS_INLINE char* absl_nonnull EncodeFullU128(
+    uint128 i, char* absl_nonnull buffer) {
+  if (absl::Uint128High64(i) == 0) {
+    return EncodeFullU64(absl::Uint128Low64(i), buffer);
+  }
+  // We divide the number into 16-digit chunks because `EncodePadded16` is
+  // optimized to handle 16 digits at a time (as two 8-digit chunks).
+  constexpr uint64_t k1e16 = uint64_t{10'000'000'000'000'000};
+  uint128 high = i / k1e16;
+  uint64_t low = absl::Uint128Low64(i % k1e16);
+  uint64_t mid = absl::Uint128Low64(high % k1e16);
+  high /= k1e16;
+
+  if (high == 0) {
+    buffer = EncodeFullU64(mid, buffer);
+    buffer = EncodePadded16(low, buffer);
+  } else {
+    buffer = EncodeFullU64(absl::Uint128Low64(high), buffer);
+    buffer = EncodePadded16(mid, buffer);
+    buffer = EncodePadded16(low, buffer);
+  }
+  return buffer;
+}
+
 }  // namespace
 
 void numbers_internal::PutTwoDigits(uint32_t i, char* absl_nonnull buf) {
@@ -345,6 +381,25 @@
   return buffer;
 }
 
+char* absl_nonnull numbers_internal::FastIntToBuffer(
+    uint128 i, char* absl_nonnull buffer) {
+  buffer = EncodeFullU128(i, buffer);
+  *buffer = '\0';
+  return buffer;
+}
+
+char* absl_nonnull numbers_internal::FastIntToBuffer(
+    int128 i, char* absl_nonnull buffer) {
+  uint128 u = static_cast<uint128>(i);
+  if (i < 0) {
+    *buffer++ = '-';
+    u = -u;
+  }
+  buffer = EncodeFullU128(u, buffer);
+  *buffer = '\0';
+  return buffer;
+}
+
 // Given a 128-bit number expressed as a pair of uint64_t, high half first,
 // return that number multiplied by the given 32-bit value.  If the result is
 // too large to fit in a 128-bit number, divide it by 2 until it fits.
diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h
index 9c67974..fa552af 100644
--- a/absl/strings/numbers.h
+++ b/absl/strings/numbers.h
@@ -174,8 +174,9 @@
 bool safe_strtou128_base(absl::string_view text,
                          absl::uint128* absl_nonnull value, int base);
 
-static const int kFastToBufferSize = 32;
-static const int kSixDigitsToBufferSize = 16;
+inline constexpr int kFastToBuffer128Size = 41;
+inline constexpr int kFastToBufferSize = 32;
+inline constexpr int kSixDigitsToBufferSize = 16;
 
 // Helper function for fast formatting of floating-point values.
 // The result is the same as printf's "%g", a.k.a. "%.6g"; that is, six
@@ -188,7 +189,8 @@
 // WARNING: These functions may write more characters than necessary, because
 // they are intended for speed. All functions take an output buffer
 // as an argument and return a pointer to the last byte they wrote, which is the
-// terminating '\0'. At most `kFastToBufferSize` bytes are written.
+// terminating '\0'. The maximum size written is `kFastToBufferSize` for 64-bit
+// integers or less, and `kFastToBuffer128Size` for 128-bit integers.
 char* absl_nonnull FastIntToBuffer(int32_t i, char* absl_nonnull buffer)
     ABSL_INTERNAL_NEED_MIN_SIZE(buffer, kFastToBufferSize);
 char* absl_nonnull FastIntToBuffer(uint32_t n, char* absl_nonnull out_str)
@@ -197,25 +199,36 @@
     ABSL_INTERNAL_NEED_MIN_SIZE(buffer, kFastToBufferSize);
 char* absl_nonnull FastIntToBuffer(uint64_t i, char* absl_nonnull buffer)
     ABSL_INTERNAL_NEED_MIN_SIZE(buffer, kFastToBufferSize);
+char* absl_nonnull FastIntToBuffer(int128 i, char* absl_nonnull buffer)
+    ABSL_INTERNAL_NEED_MIN_SIZE(buffer, kFastToBuffer128Size);
+char* absl_nonnull FastIntToBuffer(uint128 i, char* absl_nonnull buffer)
+    ABSL_INTERNAL_NEED_MIN_SIZE(buffer, kFastToBuffer128Size);
 
-// For enums and integer types that are not an exact match for the types above,
-// use templates to call the appropriate one of the four overloads above.
+// For enums and integer types that are up to 128 bits and are not an exact
+// match for the types above, use templates to call the appropriate one of the
+// four overloads above.
 template <typename int_type>
-char* absl_nonnull FastIntToBuffer(int_type i, char* absl_nonnull buffer)
-    ABSL_INTERNAL_NEED_MIN_SIZE(buffer, kFastToBufferSize) {
-  static_assert(sizeof(i) <= 64 / 8,
-                "FastIntToBuffer works only with 64-bit-or-less integers.");
+char* absl_nonnull FastIntToBuffer(int_type i,
+                                          char* absl_nonnull buffer)
+    ABSL_INTERNAL_NEED_MIN_SIZE(
+        buffer, (sizeof(int_type) > 8 ? kFastToBuffer128Size
+                                      : kFastToBufferSize)) {
   // These conditions are constexpr bools to suppress MSVC warning C4127.
   constexpr bool kIsSigned = is_signed<int_type>();
   constexpr bool kUse64Bit = sizeof(i) > 32 / 8;
+  constexpr bool kUse128Bit = sizeof(i) > 64 / 8;
   if (kIsSigned) {
-    if (kUse64Bit) {
+    if constexpr (kUse128Bit) {
+      return FastIntToBuffer(static_cast<int128>(i), buffer);
+    } else if constexpr (kUse64Bit) {
       return FastIntToBuffer(static_cast<int64_t>(i), buffer);
     } else {
       return FastIntToBuffer(static_cast<int32_t>(i), buffer);
     }
   } else {
-    if (kUse64Bit) {
+    if constexpr (kUse128Bit) {
+      return FastIntToBuffer(static_cast<uint128>(i), buffer);
+    } else if constexpr (kUse64Bit) {
       return FastIntToBuffer(static_cast<uint64_t>(i), buffer);
     } else {
       return FastIntToBuffer(static_cast<uint32_t>(i), buffer);
diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc
index ca13da0..2eaa8c7 100644
--- a/absl/strings/numbers_test.cc
+++ b/absl/strings/numbers_test.cc
@@ -159,6 +159,8 @@
 
 typedef MyInteger<int64_t> MyInt64;
 typedef MyInteger<uint64_t> MyUInt64;
+typedef MyInteger<absl::uint128> MyUInt128;
+typedef MyInteger<absl::int128> MyInt128;
 
 void CheckInt32(int32_t x) {
   char buffer[absl::numbers_internal::kFastToBufferSize];
@@ -212,6 +214,32 @@
   EXPECT_EQ(expected, std::string(&buffer[1], my_actual)) << " Input " << x;
 }
 
+void CheckUInt128(absl::uint128 x) {
+  char buffer[absl::numbers_internal::kFastToBuffer128Size];
+  char* actual = absl::numbers_internal::FastIntToBuffer(x, buffer);
+  std::string s;
+  absl::strings_internal::OStringStream strm(&s);
+  strm << x;
+  EXPECT_EQ(s, std::string(buffer, actual)) << " Input " << s;
+
+  char* my_actual =
+      absl::numbers_internal::FastIntToBuffer(MyUInt128(x), buffer);
+  EXPECT_EQ(s, std::string(buffer, my_actual)) << " Input " << s;
+}
+
+void CheckInt128(absl::int128 x) {
+  char buffer[absl::numbers_internal::kFastToBuffer128Size];
+  char* actual = absl::numbers_internal::FastIntToBuffer(x, buffer);
+  std::string s;
+  absl::strings_internal::OStringStream strm(&s);
+  strm << x;
+  EXPECT_EQ(s, std::string(buffer, actual)) << " Input " << s;
+
+  char* my_actual =
+      absl::numbers_internal::FastIntToBuffer(MyInt128(x), buffer);
+  EXPECT_EQ(s, std::string(buffer, my_actual)) << " Input " << s;
+}
+
 void CheckHex64(uint64_t v) {
   char expected[16 + 1];
   std::string actual = absl::StrCat(absl::Hex(v, absl::kZeroPad16));
@@ -251,6 +279,34 @@
   CheckUInt64(uint64_t{1000000000000000000});
   CheckUInt64(uint64_t{1199999999999999999});
   CheckUInt64(std::numeric_limits<uint64_t>::max());
+  CheckUInt128(0);
+  CheckUInt128(1);
+  CheckUInt128(9);
+  CheckUInt128(10);
+  CheckUInt128(99);
+  CheckUInt128(100);
+  CheckUInt128(std::numeric_limits<uint64_t>::max());
+  CheckUInt128(absl::uint128(std::numeric_limits<uint64_t>::max()) + 1);
+  CheckUInt128(absl::MakeUint128(1, 0));
+  absl::uint128 k1e16 = 10000000000000000ULL;
+  CheckUInt128(k1e16 - 1);
+  CheckUInt128(k1e16);
+  CheckUInt128(k1e16 + 1);
+  CheckUInt128(k1e16 * k1e16 - 1);
+  CheckUInt128(k1e16 * k1e16);
+  CheckUInt128(k1e16 * k1e16 + 1);
+  CheckUInt128(absl::Uint128Max() - 1);
+  CheckUInt128(absl::Uint128Max());
+
+  CheckInt128(0);
+  CheckInt128(1);
+  CheckInt128(-1);
+  CheckInt128(10);
+  CheckInt128(-10);
+  CheckInt128(absl::Int128Max());
+  CheckInt128(absl::Int128Min());
+  CheckInt128(absl::MakeInt128(-1, 1));
+  CheckInt128(absl::MakeInt128(-1, std::numeric_limits<uint64_t>::max()));
 
   for (int i = 0; i < 10000; i++) {
     CheckHex64(i);
diff --git a/absl/strings/string_view.cc b/absl/strings/string_view.cc
deleted file mode 100644
index 33bd1bb..0000000
--- a/absl/strings/string_view.cc
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/string_view.h"
-
-#ifndef ABSL_USES_STD_STRING_VIEW
-
-#include <algorithm>
-#include <climits>
-#include <cstring>
-#include <ostream>
-
-#include "absl/base/nullability.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace {
-
-// This is significantly faster for case-sensitive matches with very
-// few possible matches.
-const char* absl_nullable memmatch(const char* absl_nullable phaystack,
-                                   size_t haylen,
-                                   const char* absl_nullable pneedle,
-                                   size_t neelen) {
-  if (0 == neelen) {
-    return phaystack;  // even if haylen is 0
-  }
-  if (haylen < neelen) return nullptr;
-
-  const char* match;
-  const char* hayend = phaystack + haylen - neelen + 1;
-  // A static cast is used here as memchr returns a const void *, and pointer
-  // arithmetic is not allowed on pointers to void.
-  while (
-      (match = static_cast<const char*>(memchr(
-           phaystack, pneedle[0], static_cast<size_t>(hayend - phaystack))))) {
-    if (memcmp(match, pneedle, neelen) == 0)
-      return match;
-    else
-      phaystack = match + 1;
-  }
-  return nullptr;
-}
-
-void WritePadding(std::ostream& o, size_t pad) {
-  char fill_buf[32];
-  memset(fill_buf, o.fill(), sizeof(fill_buf));
-  while (pad) {
-    size_t n = std::min(pad, sizeof(fill_buf));
-    o.write(fill_buf, static_cast<std::streamsize>(n));
-    pad -= n;
-  }
-}
-
-class LookupTable {
- public:
-  // For each character in wanted, sets the index corresponding
-  // to the ASCII code of that character. This is used by
-  // the find_.*_of methods below to tell whether or not a character is in
-  // the lookup table in constant time.
-  explicit LookupTable(string_view wanted) {
-    for (char c : wanted) {
-      table_[Index(c)] = true;
-    }
-  }
-  bool operator[](char c) const { return table_[Index(c)]; }
-
- private:
-  static unsigned char Index(char c) { return static_cast<unsigned char>(c); }
-  bool table_[UCHAR_MAX + 1] = {};
-};
-
-}  // namespace
-
-std::ostream& operator<<(std::ostream& o, string_view piece) {
-  std::ostream::sentry sentry(o);
-  if (sentry) {
-    size_t lpad = 0;
-    size_t rpad = 0;
-    if (static_cast<size_t>(o.width()) > piece.size()) {
-      size_t pad = static_cast<size_t>(o.width()) - piece.size();
-      if ((o.flags() & o.adjustfield) == o.left) {
-        rpad = pad;
-      } else {
-        lpad = pad;
-      }
-    }
-    if (lpad) WritePadding(o, lpad);
-    o.write(piece.data(), static_cast<std::streamsize>(piece.size()));
-    if (rpad) WritePadding(o, rpad);
-    o.width(0);
-  }
-  return o;
-}
-
-string_view::size_type string_view::find(string_view s,
-                                         size_type pos) const noexcept {
-  if (empty() || pos > length_) {
-    if (empty() && pos == 0 && s.empty()) return 0;
-    return npos;
-  }
-  const char* result = memmatch(ptr_ + pos, length_ - pos, s.ptr_, s.length_);
-  return result ? static_cast<size_type>(result - ptr_) : npos;
-}
-
-string_view::size_type string_view::find(char c, size_type pos) const noexcept {
-  if (empty() || pos >= length_) {
-    return npos;
-  }
-  const char* result =
-      static_cast<const char*>(memchr(ptr_ + pos, c, length_ - pos));
-  return result != nullptr ? static_cast<size_type>(result - ptr_) : npos;
-}
-
-string_view::size_type string_view::rfind(string_view s,
-                                          size_type pos) const noexcept {
-  if (length_ < s.length_) return npos;
-  if (s.empty()) return std::min(length_, pos);
-  const char* last = ptr_ + std::min(length_ - s.length_, pos) + s.length_;
-  const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_);
-  return result != last ? static_cast<size_type>(result - ptr_) : npos;
-}
-
-// Search range is [0..pos] inclusive.  If pos == npos, search everything.
-string_view::size_type string_view::rfind(char c,
-                                          size_type pos) const noexcept {
-  // Note: memrchr() is not available on Windows.
-  if (empty()) return npos;
-  for (size_type i = std::min(pos, length_ - 1);; --i) {
-    if (ptr_[i] == c) {
-      return i;
-    }
-    if (i == 0) break;
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_first_of(
-    string_view s, size_type pos) const noexcept {
-  if (empty() || s.empty()) {
-    return npos;
-  }
-  // Avoid the cost of LookupTable() for a single-character search.
-  if (s.length_ == 1) return find_first_of(s.ptr_[0], pos);
-  LookupTable tbl(s);
-  for (size_type i = pos; i < length_; ++i) {
-    if (tbl[ptr_[i]]) {
-      return i;
-    }
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_first_not_of(
-    string_view s, size_type pos) const noexcept {
-  if (empty()) return npos;
-  // Avoid the cost of LookupTable() for a single-character search.
-  if (s.length_ == 1) return find_first_not_of(s.ptr_[0], pos);
-  LookupTable tbl(s);
-  for (size_type i = pos; i < length_; ++i) {
-    if (!tbl[ptr_[i]]) {
-      return i;
-    }
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_first_not_of(
-    char c, size_type pos) const noexcept {
-  if (empty()) return npos;
-  for (; pos < length_; ++pos) {
-    if (ptr_[pos] != c) {
-      return pos;
-    }
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_last_of(string_view s,
-                                                 size_type pos) const noexcept {
-  if (empty() || s.empty()) return npos;
-  // Avoid the cost of LookupTable() for a single-character search.
-  if (s.length_ == 1) return find_last_of(s.ptr_[0], pos);
-  LookupTable tbl(s);
-  for (size_type i = std::min(pos, length_ - 1);; --i) {
-    if (tbl[ptr_[i]]) {
-      return i;
-    }
-    if (i == 0) break;
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_last_not_of(
-    string_view s, size_type pos) const noexcept {
-  if (empty()) return npos;
-  size_type i = std::min(pos, length_ - 1);
-  if (s.empty()) return i;
-  // Avoid the cost of LookupTable() for a single-character search.
-  if (s.length_ == 1) return find_last_not_of(s.ptr_[0], pos);
-  LookupTable tbl(s);
-  for (;; --i) {
-    if (!tbl[ptr_[i]]) {
-      return i;
-    }
-    if (i == 0) break;
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_last_not_of(
-    char c, size_type pos) const noexcept {
-  if (empty()) return npos;
-  size_type i = std::min(pos, length_ - 1);
-  for (;; --i) {
-    if (ptr_[i] != c) {
-      return i;
-    }
-    if (i == 0) break;
-  }
-  return npos;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#else
-
-// https://github.com/abseil/abseil-cpp/issues/1465
-// CMake builds on Apple platforms error when libraries are empty.
-// Our CMake configuration can avoid this error on header-only libraries,
-// but since this library is conditionally empty, including a single
-// variable is an easy workaround.
-#ifdef __APPLE__
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-extern const char kAvoidEmptyStringViewLibraryWarning;
-const char kAvoidEmptyStringViewLibraryWarning = 0;
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-#endif  // __APPLE__
-
-#endif  // ABSL_USES_STD_STRING_VIEW
diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h
index 358570e..9daa149 100644
--- a/absl/strings/string_view.h
+++ b/absl/strings/string_view.h
@@ -1,4 +1,3 @@
-//
 // Copyright 2017 The Abseil Authors.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,784 +16,24 @@
 // File: string_view.h
 // -----------------------------------------------------------------------------
 //
-// This file contains the definition of the `absl::string_view` class. A
-// `string_view` points to a contiguous span of characters, often part or all of
-// another `std::string`, double-quoted string literal, character array, or even
-// another `string_view`.
-//
-// This `absl::string_view` abstraction is designed to be a drop-in
-// replacement for the C++17 `std::string_view` abstraction.
+// Historical note: Abseil once provided an implementation of
+// `absl::string_view` as a polyfill for `std::string_view` prior to C++17. Now
+// that C++17 is required, `absl::string_view` is an alias for
+// `std::string_view`
+
 #ifndef ABSL_STRINGS_STRING_VIEW_H_
 #define ABSL_STRINGS_STRING_VIEW_H_
 
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <cstring>
-#include <iosfwd>
-#include <iterator>
-#include <limits>
-#include <memory>
-#include <string>
-#include <type_traits>
+#include <string_view>
 
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
-#include "absl/base/internal/throw_delegate.h"
-#include "absl/base/macros.h"
 #include "absl/base/nullability.h"
-#include "absl/base/optimization.h"
-#include "absl/base/port.h"
-
-#ifdef ABSL_USES_STD_STRING_VIEW
-
-#include <string_view>  // IWYU pragma: export
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-using string_view = std::string_view;
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#else  // ABSL_USES_STD_STRING_VIEW
-
-#if ABSL_HAVE_ATTRIBUTE(diagnose_if)
-#define ABSL_INTERNAL_DIAGNOSE_IF_NULLPTR(x) \
-  __attribute__((diagnose_if(                \
-      x == nullptr,                          \
-      "null passed to a callee that requires a non-null argument", "error")))
-#else
-#define ABSL_INTERNAL_DIAGNOSE_IF_NULLPTR(x)
-#endif
-
-#if ABSL_HAVE_BUILTIN(__builtin_memcmp) ||        \
-    (defined(__GNUC__) && !defined(__clang__)) || \
-    (defined(_MSC_VER) && _MSC_VER >= 1928)
-#define ABSL_INTERNAL_STRING_VIEW_MEMCMP __builtin_memcmp
-#else  // ABSL_HAVE_BUILTIN(__builtin_memcmp)
-#define ABSL_INTERNAL_STRING_VIEW_MEMCMP memcmp
-#endif  // ABSL_HAVE_BUILTIN(__builtin_memcmp)
-
-// If `std::ranges` is available, mark `string_view` as satisfying the
-// `view` and `borrowed_range` concepts, just like `std::string_view`.
-#ifdef __has_include
-#if __has_include(<version>)
-#include <version>
-#endif
-#endif
-
-#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L
-#include <ranges>  // NOLINT(build/c++20)
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-class string_view;
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-template <>
-// NOLINTNEXTLINE(build/c++20)
-inline constexpr bool std::ranges::enable_view<absl::string_view> = true;
-template <>
-// NOLINTNEXTLINE(build/c++20)
-inline constexpr bool std::ranges::enable_borrowed_range<absl::string_view> =
-    true;
-#endif
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
-// absl::string_view
-//
-// A `string_view` provides a lightweight view into the string data provided by
-// a `std::string`, double-quoted string literal, character array, or even
-// another `string_view`. A `string_view` does *not* own the string to which it
-// points, and that data cannot be modified through the view.
-//
-// You can use `string_view` as a function or method parameter anywhere a
-// parameter can receive a double-quoted string literal, `const char*`,
-// `std::string`, or another `absl::string_view` argument with no need to copy
-// the string data. Systematic use of `string_view` within function arguments
-// reduces data copies and `strlen()` calls.
-//
-// Because of its small size, prefer passing `string_view` by value:
-//
-//   void MyFunction(absl::string_view arg);
-//
-// If circumstances require, you may also pass one by const reference:
-//
-//   void MyFunction(const absl::string_view& arg);  // not preferred
-//
-// Passing by value generates slightly smaller code for many architectures.
-//
-// In either case, the source data of the `string_view` must outlive the
-// `string_view` itself.
-//
-// A `string_view` is also suitable for local variables if you know that the
-// lifetime of the underlying object is longer than the lifetime of your
-// `string_view` variable. However, beware of binding a `string_view` to a
-// temporary value:
-//
-//   // BAD use of string_view: lifetime problem
-//   absl::string_view sv = obj.ReturnAString();
-//
-//   // GOOD use of string_view: str outlives sv
-//   std::string str = obj.ReturnAString();
-//   absl::string_view sv = str;
-//
-// Due to lifetime issues, a `string_view` is sometimes a poor choice for a
-// return value and usually a poor choice for a data member. If you do use a
-// `string_view` this way, it is your responsibility to ensure that the object
-// pointed to by the `string_view` outlives the `string_view`.
-//
-// A `string_view` may represent a whole string or just part of a string. For
-// example, when splitting a string, `std::vector<absl::string_view>` is a
-// natural data type for the output.
-//
-// For another example, a Cord is a non-contiguous, potentially very
-// long string-like object.  The Cord class has an interface that iteratively
-// provides string_view objects that point to the successive pieces of a Cord
-// object.
-//
-// When constructed from a source which is NUL-terminated, the `string_view`
-// itself will not include the NUL-terminator unless a specific size (including
-// the NUL) is passed to the constructor. As a result, common idioms that work
-// on NUL-terminated strings do not work on `string_view` objects. If you write
-// code that scans a `string_view`, you must check its length rather than test
-// for nul, for example. Note, however, that nuls may still be embedded within
-// a `string_view` explicitly.
-//
-// You may create a null `string_view` in two ways:
-//
-//   absl::string_view sv;
-//   absl::string_view sv(nullptr, 0);
-//
-// For the above, `sv.data() == nullptr`, `sv.length() == 0`, and
-// `sv.empty() == true`. Also, if you create a `string_view` with a non-null
-// pointer then `sv.data() != nullptr`. Thus, you can use `string_view()` to
-// signal an undefined value that is different from other `string_view` values
-// in a similar fashion to how `const char* p1 = nullptr;` is different from
-// `const char* p2 = "";`. However, in practice, it is not recommended to rely
-// on this behavior.
-//
-// Be careful not to confuse a null `string_view` with an empty one. A null
-// `string_view` is an empty `string_view`, but some empty `string_view`s are
-// not null. Prefer checking for emptiness over checking for null.
-//
-// There are many ways to create an empty string_view:
-//
-//   const char* nullcp = nullptr;
-//   // string_view.size() will return 0 in all cases.
-//   absl::string_view();
-//   absl::string_view(nullcp, 0);
-//   absl::string_view("");
-//   absl::string_view("", 0);
-//   absl::string_view("abcdef", 0);
-//   absl::string_view("abcdef" + 6, 0);
-//
-// All empty `string_view` objects whether null or not, are equal:
-//
-//   absl::string_view() == absl::string_view("", 0)
-//   absl::string_view(nullptr, 0) == absl::string_view("abcdef"+6, 0)
-class ABSL_ATTRIBUTE_VIEW string_view {
- public:
-  using traits_type = std::char_traits<char>;
-  using value_type = char;
-  using pointer = char* absl_nullable;
-  using const_pointer = const char* absl_nullable;
-  using reference = char&;
-  using const_reference = const char&;
-  using const_iterator = const char* absl_nullable;
-  using iterator = const_iterator;
-  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-  using reverse_iterator = const_reverse_iterator;
-  using size_type = size_t;
-  using difference_type = std::ptrdiff_t;
-  using absl_internal_is_view = std::true_type;
-
-  static constexpr size_type npos = static_cast<size_type>(-1);
-
-  // Null `string_view` constructor
-  constexpr string_view() noexcept : ptr_(nullptr), length_(0) {}
-
-  // Implicit constructors
-
-  template <typename Allocator>
-  string_view(  // NOLINT(runtime/explicit)
-      const std::basic_string<char, std::char_traits<char>, Allocator>& str
-          ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept
-      // This is implemented in terms of `string_view(p, n)` so `str.size()`
-      // doesn't need to be reevaluated after `ptr_` is set.
-      // The length check is also skipped since it is unnecessary and causes
-      // code bloat.
-      : string_view(str.data(), str.size(), SkipCheckLengthTag{}) {}
-
-  // Implicit constructor of a `string_view` from NUL-terminated `str`. When
-  // accepting possibly null strings, use `absl::NullSafeStringView(str)`
-  // instead (see below).
-  // The length check is skipped since it is unnecessary and causes code bloat.
-  constexpr string_view(  // NOLINT(runtime/explicit)
-      const char* absl_nonnull str) ABSL_INTERNAL_DIAGNOSE_IF_NULLPTR(str)
-      : ptr_(str), length_(str ? StrlenInternal(str) : 0) {
-    ABSL_HARDENING_ASSERT(str != nullptr);
-  }
-
-  // Constructor of a `string_view` from a `const char*` and length.
-  constexpr string_view(const char* absl_nullable data, size_type len)
-      : ptr_(data), length_(CheckLengthInternal(len)) {
-    ABSL_ASSERT(data != nullptr || len == 0);
-  }
-
-#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
-  template <std::contiguous_iterator It, std::sized_sentinel_for<It> End>
-    requires(std::is_same_v<std::iter_value_t<It>, value_type> &&
-             !std::is_convertible_v<End, size_type>)
-  constexpr string_view(It begin, End end)
-      : ptr_(std::to_address(begin)), length_(end - begin) {
-    ABSL_HARDENING_ASSERT(end >= begin);
-  }
-#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
-
-  // Deleted constructor from std::nullptr_t from C++23.
-  string_view(std::nullptr_t) = delete;
-
-  constexpr string_view(const string_view&) noexcept = default;
-  string_view& operator=(const string_view&) noexcept = default;
-
-  // Iterators
-
-  // string_view::begin()
-  //
-  // Returns an iterator pointing to the first character at the beginning of the
-  // `string_view`, or `end()` if the `string_view` is empty.
-  constexpr const_iterator begin() const noexcept { return ptr_; }
-
-  // string_view::end()
-  //
-  // Returns an iterator pointing just beyond the last character at the end of
-  // the `string_view`. This iterator acts as a placeholder; attempting to
-  // access it results in undefined behavior.
-  constexpr const_iterator end() const noexcept { return ptr_ + length_; }
-
-  // string_view::cbegin()
-  //
-  // Returns a const iterator pointing to the first character at the beginning
-  // of the `string_view`, or `end()` if the `string_view` is empty.
-  constexpr const_iterator cbegin() const noexcept { return begin(); }
-
-  // string_view::cend()
-  //
-  // Returns a const iterator pointing just beyond the last character at the end
-  // of the `string_view`. This pointer acts as a placeholder; attempting to
-  // access its element results in undefined behavior.
-  constexpr const_iterator cend() const noexcept { return end(); }
-
-  // string_view::rbegin()
-  //
-  // Returns a reverse iterator pointing to the last character at the end of the
-  // `string_view`, or `rend()` if the `string_view` is empty.
-  const_reverse_iterator rbegin() const noexcept {
-    return const_reverse_iterator(end());
-  }
-
-  // string_view::rend()
-  //
-  // Returns a reverse iterator pointing just before the first character at the
-  // beginning of the `string_view`. This pointer acts as a placeholder;
-  // attempting to access its element results in undefined behavior.
-  const_reverse_iterator rend() const noexcept {
-    return const_reverse_iterator(begin());
-  }
-
-  // string_view::crbegin()
-  //
-  // Returns a const reverse iterator pointing to the last character at the end
-  // of the `string_view`, or `crend()` if the `string_view` is empty.
-  const_reverse_iterator crbegin() const noexcept { return rbegin(); }
-
-  // string_view::crend()
-  //
-  // Returns a const reverse iterator pointing just before the first character
-  // at the beginning of the `string_view`. This pointer acts as a placeholder;
-  // attempting to access its element results in undefined behavior.
-  const_reverse_iterator crend() const noexcept { return rend(); }
-
-  // Capacity Utilities
-
-  // string_view::size()
-  //
-  // Returns the number of characters in the `string_view`.
-  constexpr size_type size() const noexcept { return length_; }
-
-  // string_view::length()
-  //
-  // Returns the number of characters in the `string_view`. Alias for `size()`.
-  constexpr size_type length() const noexcept { return size(); }
-
-  // string_view::max_size()
-  //
-  // Returns the maximum number of characters the `string_view` can hold.
-  constexpr size_type max_size() const noexcept { return kMaxSize; }
-
-  // string_view::empty()
-  //
-  // Checks if the `string_view` is empty (refers to no characters).
-  constexpr bool empty() const noexcept { return length_ == 0; }
-
-  // string_view::operator[]
-  //
-  // Returns the ith element of the `string_view` using the array operator.
-  // Note that this operator does not perform any bounds checking.
-  constexpr const_reference operator[](size_type i) const {
-    ABSL_HARDENING_ASSERT(i < size());
-    return ptr_[i];
-  }
-
-  // string_view::at()
-  //
-  // Returns the ith element of the `string_view`. Bounds checking is performed,
-  // and an exception of type `std::out_of_range` will be thrown on invalid
-  // access.
-  constexpr const_reference at(size_type i) const {
-    if (ABSL_PREDICT_FALSE(i >= size())) {
-      base_internal::ThrowStdOutOfRange("absl::string_view::at");
-    }
-    return ptr_[i];
-  }
-
-  // string_view::front()
-  //
-  // Returns the first element of a `string_view`.
-  constexpr const_reference front() const {
-    ABSL_HARDENING_ASSERT(!empty());
-    return ptr_[0];
-  }
-
-  // string_view::back()
-  //
-  // Returns the last element of a `string_view`.
-  constexpr const_reference back() const {
-    ABSL_HARDENING_ASSERT(!empty());
-    return ptr_[size() - 1];
-  }
-
-  // string_view::data()
-  //
-  // Returns a pointer to the underlying character array (which is of course
-  // stored elsewhere). Note that `string_view::data()` may contain embedded nul
-  // characters, but the returned buffer may or may not be NUL-terminated;
-  // therefore, do not pass `data()` to a routine that expects a NUL-terminated
-  // string.
-  constexpr const_pointer data() const noexcept { return ptr_; }
-
-  // Modifiers
-
-  // string_view::remove_prefix()
-  //
-  // Removes the first `n` characters from the `string_view`. Note that the
-  // underlying string is not changed, only the view.
-  constexpr void remove_prefix(size_type n) {
-    ABSL_HARDENING_ASSERT(n <= length_);
-    ptr_ += n;
-    length_ -= n;
-  }
-
-  // string_view::remove_suffix()
-  //
-  // Removes the last `n` characters from the `string_view`. Note that the
-  // underlying string is not changed, only the view.
-  constexpr void remove_suffix(size_type n) {
-    ABSL_HARDENING_ASSERT(n <= length_);
-    length_ -= n;
-  }
-
-  // string_view::swap()
-  //
-  // Swaps this `string_view` with another `string_view`.
-  constexpr void swap(string_view& s) noexcept {
-    auto t = *this;
-    *this = s;
-    s = t;
-  }
-
-  // Explicit conversion operators
-
-  // Converts to `std::basic_string`.
-  template <typename A>
-  explicit operator std::basic_string<char, traits_type, A>() const {
-    if (!data()) return {};
-    return std::basic_string<char, traits_type, A>(data(), size());
-  }
-
-  // string_view::copy()
-  //
-  // Copies the contents of the `string_view` at offset `pos` and length `n`
-  // into `buf`.
-  size_type copy(char* absl_nonnull buf, size_type n, size_type pos = 0) const {
-    if (ABSL_PREDICT_FALSE(pos > length_)) {
-      base_internal::ThrowStdOutOfRange("absl::string_view::copy");
-    }
-    size_type rlen = (std::min)(length_ - pos, n);
-    if (rlen > 0) {
-      const char* start = ptr_ + pos;
-      traits_type::copy(buf, start, rlen);
-    }
-    return rlen;
-  }
-
-  // string_view::substr()
-  //
-  // Returns a "substring" of the `string_view` (at offset `pos` and length
-  // `n`) as another string_view. This function throws `std::out_of_bounds` if
-  // `pos > size`.
-  // Use absl::ClippedSubstr if you need a truncating substr operation.
-  constexpr string_view substr(size_type pos = 0, size_type n = npos) const {
-    if (ABSL_PREDICT_FALSE(pos > length_)) {
-      base_internal::ThrowStdOutOfRange("absl::string_view::substr");
-    }
-    return string_view(ptr_ + pos, (std::min)(n, length_ - pos));
-  }
-
-  // string_view::compare()
-  //
-  // Performs a lexicographical comparison between this `string_view` and
-  // another `string_view` `x`, returning a negative value if `*this` is less
-  // than `x`, 0 if `*this` is equal to `x`, and a positive value if `*this`
-  // is greater than `x`.
-  constexpr int compare(string_view x) const noexcept {
-    return CompareImpl(length_, x.length_,
-                       (std::min)(length_, x.length_) == 0
-                           ? 0
-                           : ABSL_INTERNAL_STRING_VIEW_MEMCMP(
-                                 ptr_, x.ptr_, (std::min)(length_, x.length_)));
-  }
-
-  // Overload of `string_view::compare()` for comparing a substring of the
-  // 'string_view` and another `absl::string_view`.
-  constexpr int compare(size_type pos1, size_type count1, string_view v) const {
-    return substr(pos1, count1).compare(v);
-  }
-
-  // Overload of `string_view::compare()` for comparing a substring of the
-  // `string_view` and a substring of another `absl::string_view`.
-  constexpr int compare(size_type pos1, size_type count1, string_view v,
-                        size_type pos2, size_type count2) const {
-    return substr(pos1, count1).compare(v.substr(pos2, count2));
-  }
-
-  // Overload of `string_view::compare()` for comparing a `string_view` and a
-  // a different C-style string `s`.
-  constexpr int compare(const char* absl_nonnull s) const {
-    return compare(string_view(s));
-  }
-
-  // Overload of `string_view::compare()` for comparing a substring of the
-  // `string_view` and a different string C-style string `s`.
-  constexpr int compare(size_type pos1, size_type count1,
-                        const char* absl_nonnull s) const {
-    return substr(pos1, count1).compare(string_view(s));
-  }
-
-  // Overload of `string_view::compare()` for comparing a substring of the
-  // `string_view` and a substring of a different C-style string `s`.
-  constexpr int compare(size_type pos1, size_type count1,
-                        const char* absl_nonnull s, size_type count2) const {
-    return substr(pos1, count1).compare(string_view(s, count2));
-  }
-
-  // Find Utilities
-
-  // string_view::find()
-  //
-  // Finds the first occurrence of the substring `s` within the `string_view`,
-  // returning the position of the first character's match, or `npos` if no
-  // match was found.
-  size_type find(string_view s, size_type pos = 0) const noexcept;
-
-  // Overload of `string_view::find()` for finding the given character `c`
-  // within the `string_view`.
-  size_type find(char c, size_type pos = 0) const noexcept;
-
-  // Overload of `string_view::find()` for finding a substring of a different
-  // C-style string `s` within the `string_view`.
-  size_type find(const char* absl_nonnull s, size_type pos,
-                 size_type count) const {
-    return find(string_view(s, count), pos);
-  }
-
-  // Overload of `string_view::find()` for finding a different C-style string
-  // `s` within the `string_view`.
-  size_type find(const char* absl_nonnull s, size_type pos = 0) const {
-    return find(string_view(s), pos);
-  }
-
-  // string_view::rfind()
-  //
-  // Finds the last occurrence of a substring `s` within the `string_view`,
-  // returning the position of the first character's match, or `npos` if no
-  // match was found.
-  size_type rfind(string_view s, size_type pos = npos) const noexcept;
-
-  // Overload of `string_view::rfind()` for finding the last given character `c`
-  // within the `string_view`.
-  size_type rfind(char c, size_type pos = npos) const noexcept;
-
-  // Overload of `string_view::rfind()` for finding a substring of a different
-  // C-style string `s` within the `string_view`.
-  size_type rfind(const char* absl_nonnull s, size_type pos,
-                  size_type count) const {
-    return rfind(string_view(s, count), pos);
-  }
-
-  // Overload of `string_view::rfind()` for finding a different C-style string
-  // `s` within the `string_view`.
-  size_type rfind(const char* absl_nonnull s, size_type pos = npos) const {
-    return rfind(string_view(s), pos);
-  }
-
-  // string_view::find_first_of()
-  //
-  // Finds the first occurrence of any of the characters in `s` within the
-  // `string_view`, returning the start position of the match, or `npos` if no
-  // match was found.
-  size_type find_first_of(string_view s, size_type pos = 0) const noexcept;
-
-  // Overload of `string_view::find_first_of()` for finding a character `c`
-  // within the `string_view`.
-  size_type find_first_of(char c, size_type pos = 0) const noexcept {
-    return find(c, pos);
-  }
-
-  // Overload of `string_view::find_first_of()` for finding a substring of a
-  // different C-style string `s` within the `string_view`.
-  size_type find_first_of(const char* absl_nonnull s, size_type pos,
-                          size_type count) const {
-    return find_first_of(string_view(s, count), pos);
-  }
-
-  // Overload of `string_view::find_first_of()` for finding a different C-style
-  // string `s` within the `string_view`.
-  size_type find_first_of(const char* absl_nonnull s, size_type pos = 0) const {
-    return find_first_of(string_view(s), pos);
-  }
-
-  // string_view::find_last_of()
-  //
-  // Finds the last occurrence of any of the characters in `s` within the
-  // `string_view`, returning the start position of the match, or `npos` if no
-  // match was found.
-  size_type find_last_of(string_view s, size_type pos = npos) const noexcept;
-
-  // Overload of `string_view::find_last_of()` for finding a character `c`
-  // within the `string_view`.
-  size_type find_last_of(char c, size_type pos = npos) const noexcept {
-    return rfind(c, pos);
-  }
-
-  // Overload of `string_view::find_last_of()` for finding a substring of a
-  // different C-style string `s` within the `string_view`.
-  size_type find_last_of(const char* absl_nonnull s, size_type pos,
-                         size_type count) const {
-    return find_last_of(string_view(s, count), pos);
-  }
-
-  // Overload of `string_view::find_last_of()` for finding a different C-style
-  // string `s` within the `string_view`.
-  size_type find_last_of(const char* absl_nonnull s,
-                         size_type pos = npos) const {
-    return find_last_of(string_view(s), pos);
-  }
-
-  // string_view::find_first_not_of()
-  //
-  // Finds the first occurrence of any of the characters not in `s` within the
-  // `string_view`, returning the start position of the first non-match, or
-  // `npos` if no non-match was found.
-  size_type find_first_not_of(string_view s, size_type pos = 0) const noexcept;
-
-  // Overload of `string_view::find_first_not_of()` for finding a character
-  // that is not `c` within the `string_view`.
-  size_type find_first_not_of(char c, size_type pos = 0) const noexcept;
-
-  // Overload of `string_view::find_first_not_of()` for finding a substring of a
-  // different C-style string `s` within the `string_view`.
-  size_type find_first_not_of(const char* absl_nonnull s, size_type pos,
-                              size_type count) const {
-    return find_first_not_of(string_view(s, count), pos);
-  }
-
-  // Overload of `string_view::find_first_not_of()` for finding a different
-  // C-style string `s` within the `string_view`.
-  size_type find_first_not_of(const char* absl_nonnull s,
-                              size_type pos = 0) const {
-    return find_first_not_of(string_view(s), pos);
-  }
-
-  // string_view::find_last_not_of()
-  //
-  // Finds the last occurrence of any of the characters not in `s` within the
-  // `string_view`, returning the start position of the last non-match, or
-  // `npos` if no non-match was found.
-  size_type find_last_not_of(string_view s,
-                             size_type pos = npos) const noexcept;
-
-  // Overload of `string_view::find_last_not_of()` for finding a character
-  // that is not `c` within the `string_view`.
-  size_type find_last_not_of(char c, size_type pos = npos) const noexcept;
-
-  // Overload of `string_view::find_last_not_of()` for finding a substring of a
-  // different C-style string `s` within the `string_view`.
-  size_type find_last_not_of(const char* absl_nonnull s, size_type pos,
-                             size_type count) const {
-    return find_last_not_of(string_view(s, count), pos);
-  }
-
-  // Overload of `string_view::find_last_not_of()` for finding a different
-  // C-style string `s` within the `string_view`.
-  size_type find_last_not_of(const char* absl_nonnull s,
-                             size_type pos = npos) const {
-    return find_last_not_of(string_view(s), pos);
-  }
-
-#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
-  // string_view::starts_with()
-  //
-  // Returns true if the `string_view` starts with the prefix `s`.
-  //
-  // This method only exists when targeting at least C++20.
-  // If support for C++ prior to C++20 is required, use `absl::StartsWith()`
-  // from `//absl/strings/match.h` for compatibility.
-  constexpr bool starts_with(string_view s) const noexcept {
-    return s.empty() ||
-           (size() >= s.size() &&
-            ABSL_INTERNAL_STRING_VIEW_MEMCMP(data(), s.data(), s.size()) == 0);
-  }
-
-  // Overload of `string_view::starts_with()` that returns true if `c` is the
-  // first character of the `string_view`.
-  constexpr bool starts_with(char c) const noexcept {
-    return !empty() && front() == c;
-  }
-
-  // Overload of `string_view::starts_with()` that returns true if the
-  // `string_view` starts with the C-style prefix `s`.
-  constexpr bool starts_with(const char* absl_nonnull s) const {
-    return starts_with(string_view(s));
-  }
-
-  // string_view::ends_with()
-  //
-  // Returns true if the `string_view` ends with the suffix `s`.
-  //
-  // This method only exists when targeting at least C++20.
-  // If support for C++ prior to C++20 is required, use `absl::EndsWith()`
-  // from `//absl/strings/match.h` for compatibility.
-  constexpr bool ends_with(string_view s) const noexcept {
-    return s.empty() || (size() >= s.size() && ABSL_INTERNAL_STRING_VIEW_MEMCMP(
-                                                   data() + (size() - s.size()),
-                                                   s.data(), s.size()) == 0);
-  }
-
-  // Overload of `string_view::ends_with()` that returns true if `c` is the
-  // last character of the `string_view`.
-  constexpr bool ends_with(char c) const noexcept {
-    return !empty() && back() == c;
-  }
-
-  // Overload of `string_view::ends_with()` that returns true if the
-  // `string_view` ends with the C-style suffix `s`.
-  constexpr bool ends_with(const char* absl_nonnull s) const {
-    return ends_with(string_view(s));
-  }
-#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
-
- private:
-  // The constructor from std::string delegates to this constructor.
-  // See the comment on that constructor for the rationale.
-  struct SkipCheckLengthTag {};
-  string_view(const char* absl_nullable data, size_type len,
-              SkipCheckLengthTag) noexcept
-      : ptr_(data), length_(len) {}
-
-  static constexpr size_type kMaxSize =
-      (std::numeric_limits<difference_type>::max)();
-
-  static constexpr size_type CheckLengthInternal(size_type len) {
-    ABSL_HARDENING_ASSERT(len <= kMaxSize);
-    return len;
-  }
-
-  static constexpr size_type StrlenInternal(const char* absl_nonnull str) {
-#if defined(_MSC_VER) && !defined(__clang__)
-    // MSVC 2017+ can evaluate this at compile-time.
-    const char* begin = str;
-    while (*str != '\0') ++str;
-    return str - begin;
-#elif ABSL_HAVE_BUILTIN(__builtin_strlen) || \
-    (defined(__GNUC__) && !defined(__clang__))
-    // GCC has __builtin_strlen according to
-    // https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Other-Builtins.html, but
-    // ABSL_HAVE_BUILTIN doesn't detect that, so we use the extra checks above.
-    // __builtin_strlen is constexpr.
-    return __builtin_strlen(str);
-#else
-    return str ? strlen(str) : 0;
-#endif
-  }
-
-  static constexpr int CompareImpl(size_type length_a, size_type length_b,
-                                   int compare_result) {
-    return compare_result == 0 ? static_cast<int>(length_a > length_b) -
-                                     static_cast<int>(length_a < length_b)
-                               : (compare_result < 0 ? -1 : 1);
-  }
-
-  const char* absl_nullable ptr_;
-  size_type length_;
-};
-
-// This large function is defined inline so that in a fairly common case where
-// one of the arguments is a literal, the compiler can elide a lot of the
-// following comparisons.
-constexpr bool operator==(string_view x, string_view y) noexcept {
-  return x.size() == y.size() &&
-         (x.empty() ||
-          ABSL_INTERNAL_STRING_VIEW_MEMCMP(x.data(), y.data(), x.size()) == 0);
-}
-
-constexpr bool operator!=(string_view x, string_view y) noexcept {
-  return !(x == y);
-}
-
-constexpr bool operator<(string_view x, string_view y) noexcept {
-  return x.compare(y) < 0;
-}
-
-constexpr bool operator>(string_view x, string_view y) noexcept {
-  return y < x;
-}
-
-constexpr bool operator<=(string_view x, string_view y) noexcept {
-  return !(y < x);
-}
-
-constexpr bool operator>=(string_view x, string_view y) noexcept {
-  return !(x < y);
-}
-
-// IO Insertion Operator
-std::ostream& operator<<(std::ostream& o, string_view piece);
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#undef ABSL_INTERNAL_DIAGNOSE_IF_NULLPTR
-#undef ABSL_INTERNAL_STRING_VIEW_MEMCMP
-
-#endif  // ABSL_USES_STD_STRING_VIEW
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
+using std::string_view;
 
 // ClippedSubstr()
 //
diff --git a/absl/strings/string_view_benchmark.cc b/absl/strings/string_view_benchmark.cc
deleted file mode 100644
index 546f6ed..0000000
--- a/absl/strings/string_view_benchmark.cc
+++ /dev/null
@@ -1,380 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <map>
-#include <random>
-#include <string>
-#include <unordered_set>
-#include <vector>
-
-#include "absl/base/attributes.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
-#include "absl/random/random.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/string_view.h"
-#include "benchmark/benchmark.h"
-
-namespace {
-
-void BM_StringViewFromString(benchmark::State& state) {
-  std::string s(state.range(0), 'x');
-  std::string* ps = &s;
-  struct SV {
-    SV() = default;
-    explicit SV(const std::string& s) : sv(s) {}
-    absl::string_view sv;
-  } sv;
-  SV* psv = &sv;
-  benchmark::DoNotOptimize(ps);
-  benchmark::DoNotOptimize(psv);
-  for (auto _ : state) {
-    new (psv) SV(*ps);
-    benchmark::DoNotOptimize(sv);
-  }
-}
-BENCHMARK(BM_StringViewFromString)->Arg(12)->Arg(128);
-
-// Provide a forcibly out-of-line wrapper for operator== that can be used in
-// benchmarks to measure the impact of inlining.
-ABSL_ATTRIBUTE_NOINLINE
-bool NonInlinedEq(absl::string_view a, absl::string_view b) { return a == b; }
-
-// We use functions that cannot be inlined to perform the comparison loops so
-// that inlining of the operator== can't optimize away *everything*.
-ABSL_ATTRIBUTE_NOINLINE
-void DoEqualityComparisons(benchmark::State& state, absl::string_view a,
-                           absl::string_view b) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a == b);
-  }
-}
-
-void BM_EqualIdentical(benchmark::State& state) {
-  std::string x(state.range(0), 'a');
-  DoEqualityComparisons(state, x, x);
-}
-BENCHMARK(BM_EqualIdentical)->DenseRange(0, 3)->Range(4, 1 << 10);
-
-void BM_EqualSame(benchmark::State& state) {
-  std::string x(state.range(0), 'a');
-  std::string y = x;
-  DoEqualityComparisons(state, x, y);
-}
-BENCHMARK(BM_EqualSame)
-    ->DenseRange(0, 10)
-    ->Arg(20)
-    ->Arg(40)
-    ->Arg(70)
-    ->Arg(110)
-    ->Range(160, 4096);
-
-void BM_EqualDifferent(benchmark::State& state) {
-  const int len = state.range(0);
-  std::string x(len, 'a');
-  std::string y = x;
-  if (len > 0) {
-    y[len - 1] = 'b';
-  }
-  DoEqualityComparisons(state, x, y);
-}
-BENCHMARK(BM_EqualDifferent)->DenseRange(0, 3)->Range(4, 1 << 10);
-
-// This benchmark is intended to check that important simplifications can be
-// made with absl::string_view comparisons against constant strings. The idea is
-// that if constant strings cause redundant components of the comparison, the
-// compiler should detect and eliminate them. Here we use 8 different strings,
-// each with the same size. Provided our comparison makes the implementation
-// inline-able by the compiler, it should fold all of these away into a single
-// size check once per loop iteration.
-ABSL_ATTRIBUTE_NOINLINE
-void DoConstantSizeInlinedEqualityComparisons(benchmark::State& state,
-                                              absl::string_view a) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a == "aaa");
-    benchmark::DoNotOptimize(a == "bbb");
-    benchmark::DoNotOptimize(a == "ccc");
-    benchmark::DoNotOptimize(a == "ddd");
-    benchmark::DoNotOptimize(a == "eee");
-    benchmark::DoNotOptimize(a == "fff");
-    benchmark::DoNotOptimize(a == "ggg");
-    benchmark::DoNotOptimize(a == "hhh");
-  }
-}
-void BM_EqualConstantSizeInlined(benchmark::State& state) {
-  std::string x(state.range(0), 'a');
-  DoConstantSizeInlinedEqualityComparisons(state, x);
-}
-// We only need to check for size of 3, and <> 3 as this benchmark only has to
-// do with size differences.
-BENCHMARK(BM_EqualConstantSizeInlined)->DenseRange(2, 4);
-
-// This benchmark exists purely to give context to the above timings: this is
-// what they would look like if the compiler is completely unable to simplify
-// between two comparisons when they are comparing against constant strings.
-ABSL_ATTRIBUTE_NOINLINE
-void DoConstantSizeNonInlinedEqualityComparisons(benchmark::State& state,
-                                                 absl::string_view a) {
-  for (auto _ : state) {
-    // Force these out-of-line to compare with the above function.
-    benchmark::DoNotOptimize(NonInlinedEq(a, "aaa"));
-    benchmark::DoNotOptimize(NonInlinedEq(a, "bbb"));
-    benchmark::DoNotOptimize(NonInlinedEq(a, "ccc"));
-    benchmark::DoNotOptimize(NonInlinedEq(a, "ddd"));
-    benchmark::DoNotOptimize(NonInlinedEq(a, "eee"));
-    benchmark::DoNotOptimize(NonInlinedEq(a, "fff"));
-    benchmark::DoNotOptimize(NonInlinedEq(a, "ggg"));
-    benchmark::DoNotOptimize(NonInlinedEq(a, "hhh"));
-  }
-}
-
-void BM_EqualConstantSizeNonInlined(benchmark::State& state) {
-  std::string x(state.range(0), 'a');
-  DoConstantSizeNonInlinedEqualityComparisons(state, x);
-}
-// We only need to check for size of 3, and <> 3 as this benchmark only has to
-// do with size differences.
-BENCHMARK(BM_EqualConstantSizeNonInlined)->DenseRange(2, 4);
-
-void BM_CompareSame(benchmark::State& state) {
-  const int len = state.range(0);
-  std::string x;
-  for (int i = 0; i < len; i++) {
-    x += 'a';
-  }
-  std::string y = x;
-  absl::string_view a = x;
-  absl::string_view b = y;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a);
-    benchmark::DoNotOptimize(b);
-    benchmark::DoNotOptimize(a.compare(b));
-  }
-}
-BENCHMARK(BM_CompareSame)->DenseRange(0, 3)->Range(4, 1 << 10);
-
-void BM_CompareFirstOneLess(benchmark::State& state) {
-  const int len = state.range(0);
-  std::string x(len, 'a');
-  std::string y = x;
-  y.back() = 'b';
-  absl::string_view a = x;
-  absl::string_view b = y;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a);
-    benchmark::DoNotOptimize(b);
-    benchmark::DoNotOptimize(a.compare(b));
-  }
-}
-BENCHMARK(BM_CompareFirstOneLess)->DenseRange(1, 3)->Range(4, 1 << 10);
-
-void BM_CompareSecondOneLess(benchmark::State& state) {
-  const int len = state.range(0);
-  std::string x(len, 'a');
-  std::string y = x;
-  x.back() = 'b';
-  absl::string_view a = x;
-  absl::string_view b = y;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a);
-    benchmark::DoNotOptimize(b);
-    benchmark::DoNotOptimize(a.compare(b));
-  }
-}
-BENCHMARK(BM_CompareSecondOneLess)->DenseRange(1, 3)->Range(4, 1 << 10);
-
-void BM_find_string_view_len_one(benchmark::State& state) {
-  std::string haystack(state.range(0), '0');
-  absl::string_view s(haystack);
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(s.find("x"));  // not present; length 1
-  }
-}
-BENCHMARK(BM_find_string_view_len_one)->Range(1, 1 << 20);
-
-void BM_find_string_view_len_two(benchmark::State& state) {
-  std::string haystack(state.range(0), '0');
-  absl::string_view s(haystack);
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(s.find("xx"));  // not present; length 2
-  }
-}
-BENCHMARK(BM_find_string_view_len_two)->Range(1, 1 << 20);
-
-void BM_find_one_char(benchmark::State& state) {
-  std::string haystack(state.range(0), '0');
-  absl::string_view s(haystack);
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(s.find('x'));  // not present
-  }
-}
-BENCHMARK(BM_find_one_char)->Range(1, 1 << 20);
-
-void BM_rfind_one_char(benchmark::State& state) {
-  std::string haystack(state.range(0), '0');
-  absl::string_view s(haystack);
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(s.rfind('x'));  // not present
-  }
-}
-BENCHMARK(BM_rfind_one_char)->Range(1, 1 << 20);
-
-void BM_worst_case_find_first_of(benchmark::State& state, int haystack_len) {
-  const int needle_len = state.range(0);
-  std::string needle;
-  for (int i = 0; i < needle_len; ++i) {
-    needle += 'a' + i;
-  }
-  std::string haystack(haystack_len, '0');  // 1000 zeros.
-
-  absl::string_view s(haystack);
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(s.find_first_of(needle));
-  }
-}
-
-void BM_find_first_of_short(benchmark::State& state) {
-  BM_worst_case_find_first_of(state, 10);
-}
-
-void BM_find_first_of_medium(benchmark::State& state) {
-  BM_worst_case_find_first_of(state, 100);
-}
-
-void BM_find_first_of_long(benchmark::State& state) {
-  BM_worst_case_find_first_of(state, 1000);
-}
-
-BENCHMARK(BM_find_first_of_short)->DenseRange(0, 4)->Arg(8)->Arg(16)->Arg(32);
-BENCHMARK(BM_find_first_of_medium)->DenseRange(0, 4)->Arg(8)->Arg(16)->Arg(32);
-BENCHMARK(BM_find_first_of_long)->DenseRange(0, 4)->Arg(8)->Arg(16)->Arg(32);
-
-struct EasyMap : public std::map<absl::string_view, uint64_t> {
-  explicit EasyMap(size_t) {}
-};
-
-// This templated benchmark helper function is intended to stress operator== or
-// operator< in a realistic test.  It surely isn't entirely realistic, but it's
-// a start.  The test creates a map of type Map, a template arg, and populates
-// it with table_size key/value pairs. Each key has WordsPerKey words.  After
-// creating the map, a number of lookups are done in random order.  Some keys
-// are used much more frequently than others in this phase of the test.
-template <typename Map, int WordsPerKey>
-void StringViewMapBenchmark(benchmark::State& state) {
-  const int table_size = state.range(0);
-  const double kFractionOfKeysThatAreHot = 0.2;
-  const int kNumLookupsOfHotKeys = 20;
-  const int kNumLookupsOfColdKeys = 1;
-  const char* words[] = {"the",   "quick",  "brown",    "fox",      "jumped",
-                         "over",  "the",    "lazy",     "dog",      "and",
-                         "found", "a",      "large",    "mushroom", "and",
-                         "a",     "couple", "crickets", "eating",   "pie"};
-  // Create some keys that consist of words in random order.
-  absl::InsecureBitGen rng;
-  std::vector<std::string> keys(table_size);
-  std::vector<int> all_indices;
-  const int kBlockSize = 1 << 12;
-  std::unordered_set<std::string> t(kBlockSize);
-  std::uniform_int_distribution<int> uniform(0, ABSL_ARRAYSIZE(words) - 1);
-  for (int i = 0; i < table_size; i++) {
-    all_indices.push_back(i);
-    do {
-      keys[i].clear();
-      for (int j = 0; j < WordsPerKey; j++) {
-        absl::StrAppend(&keys[i], j > 0 ? " " : "", words[uniform(rng)]);
-      }
-    } while (!t.insert(keys[i]).second);
-  }
-
-  // Create a list of strings to lookup: a permutation of the array of
-  // keys we just created, with repeats.  "Hot" keys get repeated more.
-  std::shuffle(all_indices.begin(), all_indices.end(), rng);
-  const int num_hot = table_size * kFractionOfKeysThatAreHot;
-  const int num_cold = table_size - num_hot;
-  std::vector<int> hot_indices(all_indices.begin(),
-                               all_indices.begin() + num_hot);
-  std::vector<int> indices;
-  for (int i = 0; i < kNumLookupsOfColdKeys; i++) {
-    indices.insert(indices.end(), all_indices.begin(), all_indices.end());
-  }
-  for (int i = 0; i < kNumLookupsOfHotKeys - kNumLookupsOfColdKeys; i++) {
-    indices.insert(indices.end(), hot_indices.begin(), hot_indices.end());
-  }
-  std::shuffle(indices.begin(), indices.end(), rng);
-  ABSL_RAW_CHECK(
-      num_cold * kNumLookupsOfColdKeys + num_hot * kNumLookupsOfHotKeys ==
-          indices.size(),
-      "");
-  // After constructing the array we probe it with absl::string_views built from
-  // test_strings.  This means operator== won't see equal pointers, so
-  // it'll have to check for equal lengths and equal characters.
-  std::vector<std::string> test_strings(indices.size());
-  for (int i = 0; i < indices.size(); i++) {
-    test_strings[i] = keys[indices[i]];
-  }
-
-  // Run the benchmark. It includes map construction but is mostly
-  // map lookups.
-  for (auto _ : state) {
-    Map h(table_size);
-    for (int i = 0; i < table_size; i++) {
-      h[keys[i]] = i * 2;
-    }
-    ABSL_RAW_CHECK(h.size() == table_size, "");
-    uint64_t sum = 0;
-    for (int i = 0; i < indices.size(); i++) {
-      sum += h[test_strings[i]];
-    }
-    benchmark::DoNotOptimize(sum);
-  }
-}
-
-void BM_StdMap_4(benchmark::State& state) {
-  StringViewMapBenchmark<EasyMap, 4>(state);
-}
-BENCHMARK(BM_StdMap_4)->Range(1 << 10, 1 << 16);
-
-void BM_StdMap_8(benchmark::State& state) {
-  StringViewMapBenchmark<EasyMap, 8>(state);
-}
-BENCHMARK(BM_StdMap_8)->Range(1 << 10, 1 << 16);
-
-void BM_CopyToStringNative(benchmark::State& state) {
-  std::string src(state.range(0), 'x');
-  absl::string_view sv(src);
-  std::string dst;
-  for (auto _ : state) {
-    dst.assign(sv.begin(), sv.end());
-  }
-}
-BENCHMARK(BM_CopyToStringNative)->Range(1 << 3, 1 << 12);
-
-void BM_AppendToStringNative(benchmark::State& state) {
-  std::string src(state.range(0), 'x');
-  absl::string_view sv(src);
-  std::string dst;
-  for (auto _ : state) {
-    dst.clear();
-    dst.insert(dst.end(), sv.begin(), sv.end());
-  }
-}
-BENCHMARK(BM_AppendToStringNative)->Range(1 << 3, 1 << 12);
-
-}  // namespace
diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc
index 390173f..d6cec6f 100644
--- a/absl/strings/string_view_test.cc
+++ b/absl/strings/string_view_test.cc
@@ -14,8 +14,6 @@
 
 #include "absl/strings/string_view.h"
 
-#include <stdlib.h>
-
 #include <array>
 #include <cstddef>
 #include <cstdlib>
@@ -35,740 +33,8 @@
 #include "absl/base/config.h"
 #include "absl/meta/type_traits.h"
 
-#ifdef __has_include
-#if __has_include(<version>)
-#include <version>  // NOLINT(misc-include-cleaner)
-#endif
-#endif
-
-#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L
-#include <ranges>  // NOLINT(build/c++20)
-#endif
-
-#if defined(ABSL_USES_STD_STRING_VIEW) || defined(__ANDROID__)
-// We don't control the death messaging when using std::string_view.
-// Android assert messages only go to system log, so death tests cannot inspect
-// the message for matching.
-#define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
-  EXPECT_DEATH_IF_SUPPORTED(statement, ".*")
-#else
-#define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
-  EXPECT_DEATH_IF_SUPPORTED(statement, regex)
-#endif
-
 namespace {
 
-static_assert(!absl::type_traits_internal::IsOwner<absl::string_view>::value &&
-                  absl::type_traits_internal::IsView<absl::string_view>::value,
-              "string_view is a view, not an owner");
-
-static_assert(absl::type_traits_internal::IsLifetimeBoundAssignment<
-                  absl::string_view, std::string>::value,
-              "lifetimebound assignment not detected");
-
-#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L
-// NOLINTNEXTLINE(build/c++20)
-static_assert(std::ranges::enable_view<absl::string_view>,
-              "std::ranges::view not enabled");
-// NOLINTNEXTLINE(build/c++20)
-static_assert(std::ranges::enable_borrowed_range<absl::string_view>,
-              "std::ranges::borrowed_range not enabled");
-#endif
-
-// A minimal allocator that uses malloc().
-template <typename T>
-struct Mallocator {
-  typedef T value_type;
-  typedef size_t size_type;
-  typedef ptrdiff_t difference_type;
-  typedef T* pointer;
-  typedef const T* const_pointer;
-  typedef T& reference;
-  typedef const T& const_reference;
-
-  size_type max_size() const {
-    return size_t(std::numeric_limits<size_type>::max()) / sizeof(value_type);
-  }
-  template <typename U>
-  struct rebind {
-    typedef Mallocator<U> other;
-  };
-  Mallocator() = default;
-  template <class U>
-  Mallocator(const Mallocator<U>&) {}  // NOLINT(runtime/explicit)
-
-  T* allocate(size_t n) { return static_cast<T*>(std::malloc(n * sizeof(T))); }
-  void deallocate(T* p, size_t) { std::free(p); }
-};
-template <typename T, typename U>
-bool operator==(const Mallocator<T>&, const Mallocator<U>&) {
-  return true;
-}
-template <typename T, typename U>
-bool operator!=(const Mallocator<T>&, const Mallocator<U>&) {
-  return false;
-}
-
-TEST(StringViewTest, Ctor) {
-  {
-    // Null.
-    absl::string_view s10;
-    EXPECT_TRUE(s10.data() == nullptr);
-    EXPECT_EQ(0u, s10.length());
-  }
-
-  {
-    // const char* without length.
-    const char* hello = "hello";
-    absl::string_view s20(hello);
-    EXPECT_TRUE(s20.data() == hello);
-    EXPECT_EQ(5u, s20.length());
-
-    // const char* with length.
-    absl::string_view s21(hello, 4);
-    EXPECT_TRUE(s21.data() == hello);
-    EXPECT_EQ(4u, s21.length());
-
-    // Not recommended, but valid C++
-    absl::string_view s22(hello, 6);
-    EXPECT_TRUE(s22.data() == hello);
-    EXPECT_EQ(6u, s22.length());
-  }
-
-  {
-    // std::string.
-    std::string hola = "hola";
-    absl::string_view s30(hola);
-    EXPECT_TRUE(s30.data() == hola.data());
-    EXPECT_EQ(4u, s30.length());
-
-    // std::string with embedded '\0'.
-    hola.push_back('\0');
-    hola.append("h2");
-    hola.push_back('\0');
-    absl::string_view s31(hola);
-    EXPECT_TRUE(s31.data() == hola.data());
-    EXPECT_EQ(8u, s31.length());
-  }
-
-#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
-  {
-    // Iterator constructor
-    std::string str = "hello";
-    absl::string_view s1(str.begin(), str.end());
-    EXPECT_EQ(s1, "hello");
-
-    std::array<char, 3> arr = { '1', '2', '3' };
-    absl::string_view s2(arr.begin(), arr.end());
-    EXPECT_EQ(s2, "123");
-
-    const char carr[] = "carr";
-    absl::string_view s3(carr, carr + strlen(carr));
-    EXPECT_EQ(s3, "carr");
-  }
-#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
-
-  {
-    using mstring =
-        std::basic_string<char, std::char_traits<char>, Mallocator<char>>;
-    mstring str1("BUNGIE-JUMPING!");
-    const mstring str2("SLEEPING!");
-
-    absl::string_view s1(str1);
-    s1.remove_prefix(strlen("BUNGIE-JUM"));
-
-    absl::string_view s2(str2);
-    s2.remove_prefix(strlen("SLEE"));
-
-    EXPECT_EQ(s1, s2);
-    EXPECT_EQ(s1, "PING!");
-  }
-
-  // TODO(mec): absl::string_view(const absl::string_view&);
-}
-
-TEST(StringViewTest, Swap) {
-  absl::string_view a("a");
-  absl::string_view b("bbb");
-  EXPECT_TRUE(noexcept(a.swap(b)));
-  a.swap(b);
-  EXPECT_EQ(a, "bbb");
-  EXPECT_EQ(b, "a");
-  a.swap(b);
-  EXPECT_EQ(a, "a");
-  EXPECT_EQ(b, "bbb");
-}
-
-TEST(StringViewTest, STLComparator) {
-  std::string s1("foo");
-  std::string s2("bar");
-  std::string s3("baz");
-
-  absl::string_view p1(s1);
-  absl::string_view p2(s2);
-  absl::string_view p3(s3);
-
-  typedef std::map<absl::string_view, int> TestMap;
-  TestMap map;
-
-  map.insert(std::make_pair(p1, 0));
-  map.insert(std::make_pair(p2, 1));
-  map.insert(std::make_pair(p3, 2));
-  EXPECT_EQ(map.size(), 3u);
-
-  TestMap::const_iterator iter = map.begin();
-  EXPECT_EQ(iter->second, 1);
-  ++iter;
-  EXPECT_EQ(iter->second, 2);
-  ++iter;
-  EXPECT_EQ(iter->second, 0);
-  ++iter;
-  EXPECT_TRUE(iter == map.end());
-
-  TestMap::iterator new_iter = map.find("zot");
-  EXPECT_TRUE(new_iter == map.end());
-
-  new_iter = map.find("bar");
-  EXPECT_TRUE(new_iter != map.end());
-
-  map.erase(new_iter);
-  EXPECT_EQ(map.size(), 2u);
-
-  iter = map.begin();
-  EXPECT_EQ(iter->second, 2);
-  ++iter;
-  EXPECT_EQ(iter->second, 0);
-  ++iter;
-  EXPECT_TRUE(iter == map.end());
-}
-
-#define COMPARE(result, op, x, y)                                      \
-  EXPECT_EQ(result, absl::string_view((x)) op absl::string_view((y))); \
-  EXPECT_EQ(result, absl::string_view((x)).compare(absl::string_view((y))) op 0)
-
-TEST(StringViewTest, ComparisonOperators) {
-  COMPARE(true, ==, "",   "");
-  COMPARE(true, ==, "", absl::string_view());
-  COMPARE(true, ==, absl::string_view(), "");
-  COMPARE(true, ==, "a",  "a");
-  COMPARE(true, ==, "aa", "aa");
-  COMPARE(false, ==, "a",  "");
-  COMPARE(false, ==, "",   "a");
-  COMPARE(false, ==, "a",  "b");
-  COMPARE(false, ==, "a",  "aa");
-  COMPARE(false, ==, "aa", "a");
-
-  COMPARE(false, !=, "",   "");
-  COMPARE(false, !=, "a",  "a");
-  COMPARE(false, !=, "aa", "aa");
-  COMPARE(true, !=, "a",  "");
-  COMPARE(true, !=, "",   "a");
-  COMPARE(true, !=, "a",  "b");
-  COMPARE(true, !=, "a",  "aa");
-  COMPARE(true, !=, "aa", "a");
-
-  COMPARE(true, <, "a",  "b");
-  COMPARE(true, <, "a",  "aa");
-  COMPARE(true, <, "aa", "b");
-  COMPARE(true, <, "aa", "bb");
-  COMPARE(false, <, "a",  "a");
-  COMPARE(false, <, "b",  "a");
-  COMPARE(false, <, "aa", "a");
-  COMPARE(false, <, "b",  "aa");
-  COMPARE(false, <, "bb", "aa");
-
-  COMPARE(true, <=, "a",  "a");
-  COMPARE(true, <=, "a",  "b");
-  COMPARE(true, <=, "a",  "aa");
-  COMPARE(true, <=, "aa", "b");
-  COMPARE(true, <=, "aa", "bb");
-  COMPARE(false, <=, "b",  "a");
-  COMPARE(false, <=, "aa", "a");
-  COMPARE(false, <=, "b",  "aa");
-  COMPARE(false, <=, "bb", "aa");
-
-  COMPARE(false, >=, "a",  "b");
-  COMPARE(false, >=, "a",  "aa");
-  COMPARE(false, >=, "aa", "b");
-  COMPARE(false, >=, "aa", "bb");
-  COMPARE(true, >=, "a",  "a");
-  COMPARE(true, >=, "b",  "a");
-  COMPARE(true, >=, "aa", "a");
-  COMPARE(true, >=, "b",  "aa");
-  COMPARE(true, >=, "bb", "aa");
-
-  COMPARE(false, >, "a",  "a");
-  COMPARE(false, >, "a",  "b");
-  COMPARE(false, >, "a",  "aa");
-  COMPARE(false, >, "aa", "b");
-  COMPARE(false, >, "aa", "bb");
-  COMPARE(true, >, "b",  "a");
-  COMPARE(true, >, "aa", "a");
-  COMPARE(true, >, "b",  "aa");
-  COMPARE(true, >, "bb", "aa");
-}
-
-TEST(StringViewTest, ComparisonOperatorsByCharacterPosition) {
-  std::string x;
-  for (size_t i = 0; i < 256; i++) {
-    x += 'a';
-    std::string y = x;
-    COMPARE(true, ==, x, y);
-    for (size_t j = 0; j < i; j++) {
-      std::string z = x;
-      z[j] = 'b';       // Differs in position 'j'
-      COMPARE(false, ==, x, z);
-      COMPARE(true, <, x, z);
-      COMPARE(true, >, z, x);
-      if (j + 1 < i) {
-        z[j + 1] = 'A';  // Differs in position 'j+1' as well
-        COMPARE(false, ==, x, z);
-        COMPARE(true, <, x, z);
-        COMPARE(true, >, z, x);
-        z[j + 1] = 'z';  // Differs in position 'j+1' as well
-        COMPARE(false, ==, x, z);
-        COMPARE(true, <, x, z);
-        COMPARE(true, >, z, x);
-      }
-    }
-  }
-}
-#undef COMPARE
-
-// Sadly, our users often confuse std::string::npos with
-// absl::string_view::npos; So much so that we test here that they are the same.
-// They need to both be unsigned, and both be the maximum-valued integer of
-// their type.
-
-template <typename T>
-struct is_type {
-  template <typename U>
-  static bool same(U) {
-    return false;
-  }
-  static bool same(T) { return true; }
-};
-
-TEST(StringViewTest, NposMatchesStdStringView) {
-  EXPECT_EQ(absl::string_view::npos, std::string::npos);
-
-  EXPECT_TRUE(is_type<size_t>::same(absl::string_view::npos));
-  EXPECT_FALSE(is_type<size_t>::same(""));
-
-  // Make sure absl::string_view::npos continues to be a header constant.
-  char test[absl::string_view::npos & 1] = {0};
-  EXPECT_EQ(0, test[0]);
-}
-
-TEST(StringViewTest, STL1) {
-  const absl::string_view a("abcdefghijklmnopqrstuvwxyz");
-  const absl::string_view b("abc");
-  const absl::string_view c("xyz");
-  const absl::string_view d("foobar");
-  const absl::string_view e;
-  std::string temp("123");
-  temp += '\0';
-  temp += "456";
-  const absl::string_view f(temp);
-
-  EXPECT_EQ(a[6], 'g');
-  EXPECT_EQ(b[0], 'a');
-  EXPECT_EQ(c[2], 'z');
-  EXPECT_EQ(f[3], '\0');
-  EXPECT_EQ(f[5], '5');
-
-  EXPECT_EQ(*d.data(), 'f');
-  EXPECT_EQ(d.data()[5], 'r');
-  EXPECT_TRUE(e.data() == nullptr);
-
-  EXPECT_EQ(*a.begin(), 'a');
-  EXPECT_EQ(*(b.begin() + 2), 'c');
-  EXPECT_EQ(*(c.end() - 1), 'z');
-
-  EXPECT_EQ(*a.rbegin(), 'z');
-  EXPECT_EQ(*(b.rbegin() + 2), 'a');
-  EXPECT_EQ(*(c.rend() - 1), 'x');
-  EXPECT_TRUE(a.rbegin() + 26 == a.rend());
-
-  EXPECT_EQ(a.size(), 26u);
-  EXPECT_EQ(b.size(), 3u);
-  EXPECT_EQ(c.size(), 3u);
-  EXPECT_EQ(d.size(), 6u);
-  EXPECT_EQ(e.size(), 0u);
-  EXPECT_EQ(f.size(), 7u);
-
-  EXPECT_TRUE(!d.empty());
-  EXPECT_TRUE(d.begin() != d.end());
-  EXPECT_TRUE(d.begin() + 6 == d.end());
-
-  EXPECT_TRUE(e.empty());
-  EXPECT_TRUE(e.begin() == e.end());
-
-  char buf[4] = { '%', '%', '%', '%' };
-  EXPECT_EQ(a.copy(buf, 4), 4u);
-  EXPECT_EQ(buf[0], a[0]);
-  EXPECT_EQ(buf[1], a[1]);
-  EXPECT_EQ(buf[2], a[2]);
-  EXPECT_EQ(buf[3], a[3]);
-  EXPECT_EQ(a.copy(buf, 3, 7), 3u);
-  EXPECT_EQ(buf[0], a[7]);
-  EXPECT_EQ(buf[1], a[8]);
-  EXPECT_EQ(buf[2], a[9]);
-  EXPECT_EQ(buf[3], a[3]);
-  EXPECT_EQ(c.copy(buf, 99), 3u);
-  EXPECT_EQ(buf[0], c[0]);
-  EXPECT_EQ(buf[1], c[1]);
-  EXPECT_EQ(buf[2], c[2]);
-  EXPECT_EQ(buf[3], a[3]);
-#ifdef ABSL_HAVE_EXCEPTIONS
-  EXPECT_THROW(a.copy(buf, 1, 27), std::out_of_range);
-#else
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(a.copy(buf, 1, 27), "absl::string_view::copy");
-#endif
-}
-
-// Separated from STL1() because some compilers produce an overly
-// large stack frame for the combined function.
-TEST(StringViewTest, STL2) {
-  const absl::string_view a("abcdefghijklmnopqrstuvwxyz");
-  const absl::string_view b("abc");
-  const absl::string_view c("xyz");
-  absl::string_view d("foobar");
-  const absl::string_view e;
-  const absl::string_view f(
-      "123"
-      "\0"
-      "456",
-      7);
-
-  d = absl::string_view();
-  EXPECT_EQ(d.size(), 0u);
-  EXPECT_TRUE(d.empty());
-  EXPECT_TRUE(d.data() == nullptr);
-  EXPECT_TRUE(d.begin() == d.end());
-
-  EXPECT_EQ(a.find(b), 0u);
-  EXPECT_EQ(a.find(b, 1), absl::string_view::npos);
-  EXPECT_EQ(a.find(c), 23u);
-  EXPECT_EQ(a.find(c, 9), 23u);
-  EXPECT_EQ(a.find(c, absl::string_view::npos), absl::string_view::npos);
-  EXPECT_EQ(b.find(c), absl::string_view::npos);
-  EXPECT_EQ(b.find(c, absl::string_view::npos), absl::string_view::npos);
-  EXPECT_EQ(a.find(d), 0u);
-  EXPECT_EQ(a.find(e), 0u);
-  EXPECT_EQ(a.find(d, 12), 12u);
-  EXPECT_EQ(a.find(e, 17), 17u);
-  absl::string_view g("xx not found bb");
-  EXPECT_EQ(a.find(g), absl::string_view::npos);
-  // empty string nonsense
-  EXPECT_EQ(d.find(b), absl::string_view::npos);
-  EXPECT_EQ(e.find(b), absl::string_view::npos);
-  EXPECT_EQ(d.find(b, 4), absl::string_view::npos);
-  EXPECT_EQ(e.find(b, 7), absl::string_view::npos);
-
-  size_t empty_search_pos = std::string().find(std::string());
-  EXPECT_EQ(d.find(d), empty_search_pos);
-  EXPECT_EQ(d.find(e), empty_search_pos);
-  EXPECT_EQ(e.find(d), empty_search_pos);
-  EXPECT_EQ(e.find(e), empty_search_pos);
-  EXPECT_EQ(d.find(d, 4), std::string().find(std::string(), 4));
-  EXPECT_EQ(d.find(e, 4), std::string().find(std::string(), 4));
-  EXPECT_EQ(e.find(d, 4), std::string().find(std::string(), 4));
-  EXPECT_EQ(e.find(e, 4), std::string().find(std::string(), 4));
-
-  EXPECT_EQ(a.find('a'), 0u);
-  EXPECT_EQ(a.find('c'), 2u);
-  EXPECT_EQ(a.find('z'), 25u);
-  EXPECT_EQ(a.find('$'), absl::string_view::npos);
-  EXPECT_EQ(a.find('\0'), absl::string_view::npos);
-  EXPECT_EQ(f.find('\0'), 3u);
-  EXPECT_EQ(f.find('3'), 2u);
-  EXPECT_EQ(f.find('5'), 5u);
-  EXPECT_EQ(g.find('o'), 4u);
-  EXPECT_EQ(g.find('o', 4), 4u);
-  EXPECT_EQ(g.find('o', 5), 8u);
-  EXPECT_EQ(a.find('b', 5), absl::string_view::npos);
-  // empty string nonsense
-  EXPECT_EQ(d.find('\0'), absl::string_view::npos);
-  EXPECT_EQ(e.find('\0'), absl::string_view::npos);
-  EXPECT_EQ(d.find('\0', 4), absl::string_view::npos);
-  EXPECT_EQ(e.find('\0', 7), absl::string_view::npos);
-  EXPECT_EQ(d.find('x'), absl::string_view::npos);
-  EXPECT_EQ(e.find('x'), absl::string_view::npos);
-  EXPECT_EQ(d.find('x', 4), absl::string_view::npos);
-  EXPECT_EQ(e.find('x', 7), absl::string_view::npos);
-
-  EXPECT_EQ(a.find(b.data(), 1, 0), 1u);
-  EXPECT_EQ(a.find(c.data(), 9, 0), 9u);
-  EXPECT_EQ(a.find(c.data(), absl::string_view::npos, 0),
-            absl::string_view::npos);
-  EXPECT_EQ(b.find(c.data(), absl::string_view::npos, 0),
-            absl::string_view::npos);
-  // empty string nonsense
-  EXPECT_EQ(d.find(b.data(), 4, 0), absl::string_view::npos);
-  EXPECT_EQ(e.find(b.data(), 7, 0), absl::string_view::npos);
-
-  EXPECT_EQ(a.find(b.data(), 1), absl::string_view::npos);
-  EXPECT_EQ(a.find(c.data(), 9), 23u);
-  EXPECT_EQ(a.find(c.data(), absl::string_view::npos), absl::string_view::npos);
-  EXPECT_EQ(b.find(c.data(), absl::string_view::npos), absl::string_view::npos);
-  // empty string nonsense
-  EXPECT_EQ(d.find(b.data(), 4), absl::string_view::npos);
-  EXPECT_EQ(e.find(b.data(), 7), absl::string_view::npos);
-
-  EXPECT_EQ(a.rfind(b), 0u);
-  EXPECT_EQ(a.rfind(b, 1), 0u);
-  EXPECT_EQ(a.rfind(c), 23u);
-  EXPECT_EQ(a.rfind(c, 22), absl::string_view::npos);
-  EXPECT_EQ(a.rfind(c, 1), absl::string_view::npos);
-  EXPECT_EQ(a.rfind(c, 0), absl::string_view::npos);
-  EXPECT_EQ(b.rfind(c), absl::string_view::npos);
-  EXPECT_EQ(b.rfind(c, 0), absl::string_view::npos);
-  EXPECT_EQ(a.rfind(d), std::string(a).rfind(std::string()));
-  EXPECT_EQ(a.rfind(e), std::string(a).rfind(std::string()));
-  EXPECT_EQ(a.rfind(d, 12), 12u);
-  EXPECT_EQ(a.rfind(e, 17), 17u);
-  EXPECT_EQ(a.rfind(g), absl::string_view::npos);
-  EXPECT_EQ(d.rfind(b), absl::string_view::npos);
-  EXPECT_EQ(e.rfind(b), absl::string_view::npos);
-  EXPECT_EQ(d.rfind(b, 4), absl::string_view::npos);
-  EXPECT_EQ(e.rfind(b, 7), absl::string_view::npos);
-  // empty string nonsense
-  EXPECT_EQ(d.rfind(d, 4), std::string().rfind(std::string()));
-  EXPECT_EQ(e.rfind(d, 7), std::string().rfind(std::string()));
-  EXPECT_EQ(d.rfind(e, 4), std::string().rfind(std::string()));
-  EXPECT_EQ(e.rfind(e, 7), std::string().rfind(std::string()));
-  EXPECT_EQ(d.rfind(d), std::string().rfind(std::string()));
-  EXPECT_EQ(e.rfind(d), std::string().rfind(std::string()));
-  EXPECT_EQ(d.rfind(e), std::string().rfind(std::string()));
-  EXPECT_EQ(e.rfind(e), std::string().rfind(std::string()));
-
-  EXPECT_EQ(g.rfind('o'), 8u);
-  EXPECT_EQ(g.rfind('q'), absl::string_view::npos);
-  EXPECT_EQ(g.rfind('o', 8), 8u);
-  EXPECT_EQ(g.rfind('o', 7), 4u);
-  EXPECT_EQ(g.rfind('o', 3), absl::string_view::npos);
-  EXPECT_EQ(f.rfind('\0'), 3u);
-  EXPECT_EQ(f.rfind('\0', 12), 3u);
-  EXPECT_EQ(f.rfind('3'), 2u);
-  EXPECT_EQ(f.rfind('5'), 5u);
-  // empty string nonsense
-  EXPECT_EQ(d.rfind('o'), absl::string_view::npos);
-  EXPECT_EQ(e.rfind('o'), absl::string_view::npos);
-  EXPECT_EQ(d.rfind('o', 4), absl::string_view::npos);
-  EXPECT_EQ(e.rfind('o', 7), absl::string_view::npos);
-
-  EXPECT_EQ(a.rfind(b.data(), 1, 0), 1u);
-  EXPECT_EQ(a.rfind(c.data(), 22, 0), 22u);
-  EXPECT_EQ(a.rfind(c.data(), 1, 0), 1u);
-  EXPECT_EQ(a.rfind(c.data(), 0, 0), 0u);
-  EXPECT_EQ(b.rfind(c.data(), 0, 0), 0u);
-  EXPECT_EQ(d.rfind(b.data(), 4, 0), 0u);
-  EXPECT_EQ(e.rfind(b.data(), 7, 0), 0u);
-}
-
-// Continued from STL2
-TEST(StringViewTest, STL2FindFirst) {
-  const absl::string_view a("abcdefghijklmnopqrstuvwxyz");
-  const absl::string_view b("abc");
-  const absl::string_view c("xyz");
-  absl::string_view d("foobar");
-  const absl::string_view e;
-  const absl::string_view f(
-      "123"
-      "\0"
-      "456",
-      7);
-  absl::string_view g("xx not found bb");
-
-  d = absl::string_view();
-  EXPECT_EQ(a.find_first_of(b), 0u);
-  EXPECT_EQ(a.find_first_of(b, 0), 0u);
-  EXPECT_EQ(a.find_first_of(b, 1), 1u);
-  EXPECT_EQ(a.find_first_of(b, 2), 2u);
-  EXPECT_EQ(a.find_first_of(b, 3), absl::string_view::npos);
-  EXPECT_EQ(a.find_first_of(c), 23u);
-  EXPECT_EQ(a.find_first_of(c, 23), 23u);
-  EXPECT_EQ(a.find_first_of(c, 24), 24u);
-  EXPECT_EQ(a.find_first_of(c, 25), 25u);
-  EXPECT_EQ(a.find_first_of(c, 26), absl::string_view::npos);
-  EXPECT_EQ(g.find_first_of(b), 13u);
-  EXPECT_EQ(g.find_first_of(c), 0u);
-  EXPECT_EQ(a.find_first_of(f), absl::string_view::npos);
-  EXPECT_EQ(f.find_first_of(a), absl::string_view::npos);
-  // empty string nonsense
-  EXPECT_EQ(a.find_first_of(d), absl::string_view::npos);
-  EXPECT_EQ(a.find_first_of(e), absl::string_view::npos);
-  EXPECT_EQ(d.find_first_of(b), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_of(b), absl::string_view::npos);
-  EXPECT_EQ(d.find_first_of(d), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_of(d), absl::string_view::npos);
-  EXPECT_EQ(d.find_first_of(e), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_of(e), absl::string_view::npos);
-
-  EXPECT_EQ(a.find_first_not_of(b), 3u);
-  EXPECT_EQ(a.find_first_not_of(c), 0u);
-  EXPECT_EQ(b.find_first_not_of(a), absl::string_view::npos);
-  EXPECT_EQ(c.find_first_not_of(a), absl::string_view::npos);
-  EXPECT_EQ(f.find_first_not_of(a), 0u);
-  EXPECT_EQ(a.find_first_not_of(f), 0u);
-  EXPECT_EQ(a.find_first_not_of(d), 0u);
-  EXPECT_EQ(a.find_first_not_of(e), 0u);
-  // empty string nonsense
-  EXPECT_EQ(a.find_first_not_of(d), 0u);
-  EXPECT_EQ(a.find_first_not_of(e), 0u);
-  EXPECT_EQ(a.find_first_not_of(d, 1), 1u);
-  EXPECT_EQ(a.find_first_not_of(e, 1), 1u);
-  EXPECT_EQ(a.find_first_not_of(d, a.size() - 1), a.size() - 1);
-  EXPECT_EQ(a.find_first_not_of(e, a.size() - 1), a.size() - 1);
-  EXPECT_EQ(a.find_first_not_of(d, a.size()), absl::string_view::npos);
-  EXPECT_EQ(a.find_first_not_of(e, a.size()), absl::string_view::npos);
-  EXPECT_EQ(a.find_first_not_of(d, absl::string_view::npos),
-            absl::string_view::npos);
-  EXPECT_EQ(a.find_first_not_of(e, absl::string_view::npos),
-            absl::string_view::npos);
-  EXPECT_EQ(d.find_first_not_of(a), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_not_of(a), absl::string_view::npos);
-  EXPECT_EQ(d.find_first_not_of(d), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_not_of(d), absl::string_view::npos);
-  EXPECT_EQ(d.find_first_not_of(e), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_not_of(e), absl::string_view::npos);
-
-  absl::string_view h("====");
-  EXPECT_EQ(h.find_first_not_of('='), absl::string_view::npos);
-  EXPECT_EQ(h.find_first_not_of('=', 3), absl::string_view::npos);
-  EXPECT_EQ(h.find_first_not_of('\0'), 0u);
-  EXPECT_EQ(g.find_first_not_of('x'), 2u);
-  EXPECT_EQ(f.find_first_not_of('\0'), 0u);
-  EXPECT_EQ(f.find_first_not_of('\0', 3), 4u);
-  EXPECT_EQ(f.find_first_not_of('\0', 2), 2u);
-  // empty string nonsense
-  EXPECT_EQ(d.find_first_not_of('x'), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_not_of('x'), absl::string_view::npos);
-  EXPECT_EQ(d.find_first_not_of('\0'), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_not_of('\0'), absl::string_view::npos);
-}
-
-// Continued from STL2
-TEST(StringViewTest, STL2FindLast) {
-  const absl::string_view a("abcdefghijklmnopqrstuvwxyz");
-  const absl::string_view b("abc");
-  const absl::string_view c("xyz");
-  absl::string_view d("foobar");
-  const absl::string_view e;
-  const absl::string_view f(
-      "123"
-      "\0"
-      "456",
-      7);
-  absl::string_view g("xx not found bb");
-  absl::string_view h("====");
-  absl::string_view i("56");
-
-  d = absl::string_view();
-  EXPECT_EQ(h.find_last_of(a), absl::string_view::npos);
-  EXPECT_EQ(g.find_last_of(a), g.size() - 1);
-  EXPECT_EQ(a.find_last_of(b), 2u);
-  EXPECT_EQ(a.find_last_of(c), a.size() - 1);
-  EXPECT_EQ(f.find_last_of(i), 6u);
-  EXPECT_EQ(a.find_last_of('a'), 0u);
-  EXPECT_EQ(a.find_last_of('b'), 1u);
-  EXPECT_EQ(a.find_last_of('z'), 25u);
-  EXPECT_EQ(a.find_last_of('a', 5), 0u);
-  EXPECT_EQ(a.find_last_of('b', 5), 1u);
-  EXPECT_EQ(a.find_last_of('b', 0), absl::string_view::npos);
-  EXPECT_EQ(a.find_last_of('z', 25), 25u);
-  EXPECT_EQ(a.find_last_of('z', 24), absl::string_view::npos);
-  EXPECT_EQ(f.find_last_of(i, 5), 5u);
-  EXPECT_EQ(f.find_last_of(i, 6), 6u);
-  EXPECT_EQ(f.find_last_of(a, 4), absl::string_view::npos);
-  // empty string nonsense
-  EXPECT_EQ(f.find_last_of(d), absl::string_view::npos);
-  EXPECT_EQ(f.find_last_of(e), absl::string_view::npos);
-  EXPECT_EQ(f.find_last_of(d, 4), absl::string_view::npos);
-  EXPECT_EQ(f.find_last_of(e, 4), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_of(d), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_of(e), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_of(d), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_of(e), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_of(f), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_of(f), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_of(d, 4), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_of(e, 4), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_of(d, 4), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_of(e, 4), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_of(f, 4), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_of(f, 4), absl::string_view::npos);
-
-  EXPECT_EQ(a.find_last_not_of(b), a.size() - 1);
-  EXPECT_EQ(a.find_last_not_of(c), 22u);
-  EXPECT_EQ(b.find_last_not_of(a), absl::string_view::npos);
-  EXPECT_EQ(b.find_last_not_of(b), absl::string_view::npos);
-  EXPECT_EQ(f.find_last_not_of(i), 4u);
-  EXPECT_EQ(a.find_last_not_of(c, 24), 22u);
-  EXPECT_EQ(a.find_last_not_of(b, 3), 3u);
-  EXPECT_EQ(a.find_last_not_of(b, 2), absl::string_view::npos);
-  // empty string nonsense
-  EXPECT_EQ(f.find_last_not_of(d), f.size() - 1);
-  EXPECT_EQ(f.find_last_not_of(e), f.size() - 1);
-  EXPECT_EQ(f.find_last_not_of(d, 4), 4u);
-  EXPECT_EQ(f.find_last_not_of(e, 4), 4u);
-  EXPECT_EQ(d.find_last_not_of(d), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_not_of(e), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of(d), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of(e), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_not_of(f), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of(f), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_not_of(d, 4), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_not_of(e, 4), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of(d, 4), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of(e, 4), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_not_of(f, 4), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of(f, 4), absl::string_view::npos);
-
-  EXPECT_EQ(h.find_last_not_of('x'), h.size() - 1);
-  EXPECT_EQ(h.find_last_not_of('='), absl::string_view::npos);
-  EXPECT_EQ(b.find_last_not_of('c'), 1u);
-  EXPECT_EQ(h.find_last_not_of('x', 2), 2u);
-  EXPECT_EQ(h.find_last_not_of('=', 2), absl::string_view::npos);
-  EXPECT_EQ(b.find_last_not_of('b', 1), 0u);
-  // empty string nonsense
-  EXPECT_EQ(d.find_last_not_of('x'), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of('x'), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_not_of('\0'), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of('\0'), absl::string_view::npos);
-}
-
-// Continued from STL2
-TEST(StringViewTest, STL2Substr) {
-  const absl::string_view a("abcdefghijklmnopqrstuvwxyz");
-  const absl::string_view b("abc");
-  const absl::string_view c("xyz");
-  absl::string_view d("foobar");
-  const absl::string_view e;
-
-  d = absl::string_view();
-  EXPECT_EQ(a.substr(0, 3), b);
-  EXPECT_EQ(a.substr(23), c);
-  EXPECT_EQ(a.substr(23, 3), c);
-  EXPECT_EQ(a.substr(23, 99), c);
-  EXPECT_EQ(a.substr(0), a);
-  EXPECT_EQ(a.substr(), a);
-  EXPECT_EQ(a.substr(3, 2), "de");
-  // empty string nonsense
-  EXPECT_EQ(d.substr(0, 99), e);
-  // use of npos
-  EXPECT_EQ(a.substr(0, absl::string_view::npos), a);
-  EXPECT_EQ(a.substr(23, absl::string_view::npos), c);
-  // throw exception
-#ifdef ABSL_HAVE_EXCEPTIONS
-  EXPECT_THROW((void)a.substr(99, 2), std::out_of_range);
-#else
-  ABSL_EXPECT_DEATH_IF_SUPPORTED((void)a.substr(99, 2),
-                                 "absl::string_view::substr");
-#endif
-}
-
 TEST(StringViewTest, TruncSubstr) {
   const absl::string_view hi("hi");
   EXPECT_EQ("", absl::ClippedSubstr(hi, 0, 0));
@@ -780,272 +46,6 @@
   EXPECT_EQ("", absl::ClippedSubstr(hi, 3, 2));  // truncation
 }
 
-TEST(StringViewTest, UTF8) {
-  std::string utf8 = "\u00E1";
-  std::string utf8_twice = utf8 + " " + utf8;
-  size_t utf8_len = strlen(utf8.data());
-  EXPECT_EQ(utf8_len, absl::string_view(utf8_twice).find_first_of(" "));
-  EXPECT_EQ(utf8_len, absl::string_view(utf8_twice).find_first_of(" \t"));
-}
-
-TEST(StringViewTest, FindConformance) {
-  struct {
-    std::string haystack;
-    std::string needle;
-  } specs[] = {
-    {"", ""},
-    {"", "a"},
-    {"a", ""},
-    {"a", "a"},
-    {"a", "b"},
-    {"aa", ""},
-    {"aa", "a"},
-    {"aa", "b"},
-    {"ab", "a"},
-    {"ab", "b"},
-    {"abcd", ""},
-    {"abcd", "a"},
-    {"abcd", "d"},
-    {"abcd", "ab"},
-    {"abcd", "bc"},
-    {"abcd", "cd"},
-    {"abcd", "abcd"},
-  };
-  for (const auto& s : specs) {
-    SCOPED_TRACE(s.haystack);
-    SCOPED_TRACE(s.needle);
-    std::string st = s.haystack;
-    absl::string_view sp = s.haystack;
-    for (size_t i = 0; i <= sp.size(); ++i) {
-      size_t pos = (i == sp.size()) ? absl::string_view::npos : i;
-      SCOPED_TRACE(pos);
-      EXPECT_EQ(sp.find(s.needle, pos),
-                st.find(s.needle, pos));
-      EXPECT_EQ(sp.rfind(s.needle, pos),
-                st.rfind(s.needle, pos));
-      EXPECT_EQ(sp.find_first_of(s.needle, pos),
-                st.find_first_of(s.needle, pos));
-      EXPECT_EQ(sp.find_first_not_of(s.needle, pos),
-                st.find_first_not_of(s.needle, pos));
-      EXPECT_EQ(sp.find_last_of(s.needle, pos),
-                st.find_last_of(s.needle, pos));
-      EXPECT_EQ(sp.find_last_not_of(s.needle, pos),
-                st.find_last_not_of(s.needle, pos));
-    }
-  }
-}
-
-TEST(StringViewTest, Remove) {
-  absl::string_view a("foobar");
-  std::string s1("123");
-  s1 += '\0';
-  s1 += "456";
-  absl::string_view e;
-  std::string s2;
-
-  // remove_prefix
-  absl::string_view c(a);
-  c.remove_prefix(3);
-  EXPECT_EQ(c, "bar");
-  c = a;
-  c.remove_prefix(0);
-  EXPECT_EQ(c, a);
-  c.remove_prefix(c.size());
-  EXPECT_EQ(c, e);
-
-  // remove_suffix
-  c = a;
-  c.remove_suffix(3);
-  EXPECT_EQ(c, "foo");
-  c = a;
-  c.remove_suffix(0);
-  EXPECT_EQ(c, a);
-  c.remove_suffix(c.size());
-  EXPECT_EQ(c, e);
-}
-
-TEST(StringViewTest, Set) {
-  absl::string_view a("foobar");
-  absl::string_view empty;
-  absl::string_view b;
-
-  // set
-  b = absl::string_view("foobar", 6);
-  EXPECT_EQ(b, a);
-  b = absl::string_view("foobar", 0);
-  EXPECT_EQ(b, empty);
-  b = absl::string_view("foobar", 7);
-  EXPECT_NE(b, a);
-
-  b = absl::string_view("foobar");
-  EXPECT_EQ(b, a);
-}
-
-TEST(StringViewTest, FrontBack) {
-  static const char arr[] = "abcd";
-  const absl::string_view csp(arr, 4);
-  EXPECT_EQ(&arr[0], &csp.front());
-  EXPECT_EQ(&arr[3], &csp.back());
-}
-
-TEST(StringViewTest, FrontBackSingleChar) {
-  static const char c = 'a';
-  const absl::string_view csp(&c, 1);
-  EXPECT_EQ(&c, &csp.front());
-  EXPECT_EQ(&c, &csp.back());
-}
-
-TEST(StringViewTest, FrontBackEmpty) {
-#ifndef ABSL_USES_STD_STRING_VIEW
-#if !defined(NDEBUG) || ABSL_OPTION_HARDENED
-  // Abseil's string_view implementation has debug assertions that check that
-  // front() and back() are not called on an empty string_view.
-  absl::string_view sv;
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(sv.front(), "");
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(sv.back(), "");
-#endif
-#endif
-}
-
-TEST(StringViewTest, DefaultConstructor) {
-  absl::string_view s;
-  EXPECT_EQ(s.data(), nullptr);
-  EXPECT_EQ(s.size(), 0u);
-}
-
-TEST(StringViewTest, Comparisons2) {
-  // The `compare` member has 6 overloads (v: string_view, s: const char*):
-  //  (1) compare(v)
-  //  (2) compare(pos1, count1, v)
-  //  (3) compare(pos1, count1, v, pos2, count2)
-  //  (4) compare(s)
-  //  (5) compare(pos1, count1, s)
-  //  (6) compare(pos1, count1, s, count2)
-
-  absl::string_view abc("abcdefghijklmnopqrstuvwxyz");
-
-  // check comparison operations on strings longer than 4 bytes.
-  EXPECT_EQ(abc, absl::string_view("abcdefghijklmnopqrstuvwxyz"));
-  EXPECT_EQ(abc.compare(absl::string_view("abcdefghijklmnopqrstuvwxyz")), 0);
-
-  EXPECT_LT(abc, absl::string_view("abcdefghijklmnopqrstuvwxzz"));
-  EXPECT_LT(abc.compare(absl::string_view("abcdefghijklmnopqrstuvwxzz")), 0);
-
-  EXPECT_GT(abc, absl::string_view("abcdefghijklmnopqrstuvwxyy"));
-  EXPECT_GT(abc.compare(absl::string_view("abcdefghijklmnopqrstuvwxyy")), 0);
-
-  // The "substr" variants of `compare`.
-  absl::string_view digits("0123456789");
-  auto npos = absl::string_view::npos;
-
-  // Taking string_view
-  EXPECT_EQ(digits.compare(3, npos, absl::string_view("3456789")), 0);  // 2
-  EXPECT_EQ(digits.compare(3, 4, absl::string_view("3456")), 0);        // 2
-  EXPECT_EQ(digits.compare(10, 0, absl::string_view()), 0);             // 2
-  EXPECT_EQ(digits.compare(3, 4, absl::string_view("0123456789"), 3, 4),
-            0);  // 3
-  EXPECT_LT(digits.compare(3, 4, absl::string_view("0123456789"), 3, 5),
-            0);  // 3
-  EXPECT_LT(digits.compare(0, npos, absl::string_view("0123456789"), 3, 5),
-            0);  // 3
-  // Taking const char*
-  EXPECT_EQ(digits.compare(3, 4, "3456"), 0);                 // 5
-  EXPECT_EQ(digits.compare(3, npos, "3456789"), 0);           // 5
-  EXPECT_EQ(digits.compare(10, 0, ""), 0);                    // 5
-  EXPECT_EQ(digits.compare(3, 4, "0123456789", 3, 4), 0);     // 6
-  EXPECT_LT(digits.compare(3, 4, "0123456789", 3, 5), 0);     // 6
-  EXPECT_LT(digits.compare(0, npos, "0123456789", 3, 5), 0);  // 6
-}
-
-TEST(StringViewTest, At) {
-  absl::string_view abc = "abc";
-  EXPECT_EQ(abc.at(0), 'a');
-  EXPECT_EQ(abc.at(1), 'b');
-  EXPECT_EQ(abc.at(2), 'c');
-#ifdef ABSL_HAVE_EXCEPTIONS
-  EXPECT_THROW((void)abc.at(3), std::out_of_range);
-#else
-  ABSL_EXPECT_DEATH_IF_SUPPORTED((void)abc.at(3), "absl::string_view::at");
-#endif
-}
-
-#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
-TEST(StringViewTest, StartsWith) {
-  const absl::string_view a("foobar");
-  const absl::string_view b("123\0abc", 7);
-  const absl::string_view e;
-  EXPECT_TRUE(a.starts_with(a));
-  EXPECT_TRUE(a.starts_with("foo"));
-  EXPECT_TRUE(a.starts_with('f'));
-  EXPECT_TRUE(a.starts_with(e));
-  EXPECT_TRUE(b.starts_with(b));
-  EXPECT_TRUE(b.starts_with('1'));
-  EXPECT_TRUE(b.starts_with(e));
-  EXPECT_TRUE(e.starts_with(""));
-  EXPECT_FALSE(a.starts_with(b));
-  EXPECT_FALSE(b.starts_with(a));
-  EXPECT_FALSE(e.starts_with(a));
-  EXPECT_FALSE(a.starts_with('r'));
-  EXPECT_FALSE(a.starts_with('\0'));
-  EXPECT_FALSE(e.starts_with('r'));
-  EXPECT_FALSE(e.starts_with('\0'));
-
-  // Test that constexpr compiles.
-  constexpr absl::string_view kFooBar("foobar");
-  constexpr absl::string_view kFoo("foo");
-  constexpr absl::string_view kBar("bar");
-  constexpr bool k1 = kFooBar.starts_with(kFoo);
-  EXPECT_TRUE(k1);
-  constexpr bool k2 = kFooBar.starts_with(kBar);
-  EXPECT_FALSE(k2);
-  constexpr bool k3 = kFooBar.starts_with('f');
-  EXPECT_TRUE(k3);
-  constexpr bool k4 = kFooBar.starts_with("fo");
-  EXPECT_TRUE(k4);
-}
-
-TEST(StringViewTest, EndsWith) {
-  const absl::string_view a("foobar");
-  const absl::string_view b("123\0abc", 7);
-  const absl::string_view e;
-  EXPECT_TRUE(a.ends_with(a));
-  EXPECT_TRUE(a.ends_with('r'));
-  EXPECT_TRUE(a.ends_with("bar"));
-  EXPECT_TRUE(a.ends_with(e));
-  EXPECT_TRUE(b.ends_with(b));
-  EXPECT_TRUE(b.ends_with('c'));
-  EXPECT_TRUE(b.ends_with(e));
-  EXPECT_TRUE(e.ends_with(""));
-  EXPECT_FALSE(a.ends_with(b));
-  EXPECT_FALSE(b.ends_with(a));
-  EXPECT_FALSE(e.ends_with(a));
-  EXPECT_FALSE(a.ends_with('f'));
-  EXPECT_FALSE(a.ends_with('\0'));
-  EXPECT_FALSE(e.ends_with('r'));
-  EXPECT_FALSE(e.ends_with('\0'));
-
-  // Test that constexpr compiles.
-  constexpr absl::string_view kFooBar("foobar");
-  constexpr absl::string_view kFoo("foo");
-  constexpr absl::string_view kBar("bar");
-  constexpr bool k1 = kFooBar.ends_with(kFoo);
-  EXPECT_FALSE(k1);
-  constexpr bool k2 = kFooBar.ends_with(kBar);
-  EXPECT_TRUE(k2);
-  constexpr bool k3 = kFooBar.ends_with('r');
-  EXPECT_TRUE(k3);
-  constexpr bool k4 = kFooBar.ends_with("ar");
-  EXPECT_TRUE(k4);
-}
-#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
-
-struct MyCharAlloc : std::allocator<char> {};
-
-TEST(StringViewTest, ExplicitConversionOperator) {
-  absl::string_view sp = "hi";
-  EXPECT_EQ(sp, std::string(sp));
-}
-
 TEST(StringViewTest, NullSafeStringView) {
   {
     absl::string_view s = absl::NullSafeStringView(nullptr);
@@ -1083,317 +83,4 @@
   }
 }
 
-TEST(StringViewTest, ConstexprCompiles) {
-  constexpr absl::string_view sp;
-  // With `-Wnonnull` turned on, there is no way to test the defensive null
-  // check in the `string_view(const char*)` constructor in a constexpr context,
-  // as the argument needs to be constexpr. The compiler will therefore always
-  // know at compile time that the argument is nullptr and complain because the
-  // parameter is annotated nonnull. We hence turn the warning off for this
-  // test.
-  constexpr absl::string_view cstr_len("cstr", 4);
-
-#if defined(ABSL_USES_STD_STRING_VIEW)
-  // In libstdc++ (as of 7.2), `std::string_view::string_view(const char*)`
-  // calls `std::char_traits<char>::length(const char*)` to get the string
-  // length, but it is not marked constexpr yet. See GCC bug:
-  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78156
-  // Also, there is a LWG issue that adds constexpr to length() which was just
-  // resolved 2017-06-02. See
-  // http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2232
-  // TODO(zhangxy): Update the condition when libstdc++ adopts the constexpr
-  // length().
-#if !defined(__GLIBCXX__)
-#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR 1
-#endif  // !__GLIBCXX__
-
-#else  // ABSL_USES_STD_STRING_VIEW
-
-// This duplicates the check for __builtin_strlen in the header.
-#if ABSL_HAVE_BUILTIN(__builtin_strlen) || \
-    (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR 1
-#elif defined(__GNUC__)  // GCC or clang
-#error GCC/clang should have constexpr string_view.
-#endif
-
-// MSVC 2017+ should be able to construct a constexpr string_view from a cstr.
-#if defined(_MSC_VER)
-#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR 1
-#endif
-
-#endif  // ABSL_USES_STD_STRING_VIEW
-
-#ifdef ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR
-  constexpr absl::string_view cstr_strlen("foo");
-  EXPECT_EQ(cstr_strlen.length(), 3u);
-  constexpr absl::string_view cstr_strlen2 = "bar";
-  EXPECT_EQ(cstr_strlen2, "bar");
-
-#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \
-    (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_COMPARISON 1
-#endif
-#ifdef ABSL_HAVE_CONSTEXPR_STRING_VIEW_COMPARISON
-  constexpr absl::string_view foo = "foo";
-  constexpr absl::string_view bar = "bar";
-  constexpr bool foo_eq_bar = foo == bar;
-  constexpr bool foo_ne_bar = foo != bar;
-  constexpr bool foo_lt_bar = foo < bar;
-  constexpr bool foo_le_bar = foo <= bar;
-  constexpr bool foo_gt_bar = foo > bar;
-  constexpr bool foo_ge_bar = foo >= bar;
-  constexpr int foo_compare_bar = foo.compare(bar);
-  EXPECT_FALSE(foo_eq_bar);
-  EXPECT_TRUE(foo_ne_bar);
-  EXPECT_FALSE(foo_lt_bar);
-  EXPECT_FALSE(foo_le_bar);
-  EXPECT_TRUE(foo_gt_bar);
-  EXPECT_TRUE(foo_ge_bar);
-  EXPECT_GT(foo_compare_bar, 0);
-#endif
-#endif
-
-  constexpr absl::string_view::iterator const_begin_empty = sp.begin();
-  constexpr absl::string_view::iterator const_end_empty = sp.end();
-  EXPECT_EQ(const_begin_empty, const_end_empty);
-
-  constexpr absl::string_view::iterator const_begin = cstr_len.begin();
-  constexpr absl::string_view::iterator const_end = cstr_len.end();
-  constexpr absl::string_view::size_type const_size = cstr_len.size();
-  constexpr absl::string_view::size_type const_length = cstr_len.length();
-  static_assert(const_begin + const_size == const_end,
-                "pointer arithmetic check");
-  static_assert(const_begin + const_length == const_end,
-                "pointer arithmetic check");
-#ifndef _MSC_VER
-  // MSVC has bugs doing constexpr pointer arithmetic.
-  // https://developercommunity.visualstudio.com/content/problem/482192/bad-pointer-arithmetic-in-constepxr-2019-rc1-svc1.html
-  EXPECT_EQ(const_begin + const_size, const_end);
-  EXPECT_EQ(const_begin + const_length, const_end);
-#endif
-
-  constexpr bool isempty = sp.empty();
-  EXPECT_TRUE(isempty);
-
-  constexpr const char c = cstr_len[2];
-  EXPECT_EQ(c, 't');
-
-  constexpr const char cfront = cstr_len.front();
-  constexpr const char cback = cstr_len.back();
-  EXPECT_EQ(cfront, 'c');
-  EXPECT_EQ(cback, 'r');
-
-  constexpr const char* np = sp.data();
-  constexpr const char* cstr_ptr = cstr_len.data();
-  EXPECT_EQ(np, nullptr);
-  EXPECT_NE(cstr_ptr, nullptr);
-
-  constexpr size_t sp_npos = sp.npos;
-  EXPECT_EQ(sp_npos, static_cast<size_t>(-1));
-
-#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
-  {
-    static constexpr std::array<char, 3> arr = { '1', '2', '3' };
-    constexpr absl::string_view s2(arr.begin(), arr.end());
-    EXPECT_EQ(s2, "123");
-
-    static constexpr char carr[] = "carr";
-    constexpr absl::string_view s3(carr, carr + 4);
-    EXPECT_EQ(s3, "carr");
-  }
-#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
-}
-
-constexpr char ConstexprMethodsHelper() {
-#if defined(__cplusplus) && __cplusplus >= 201402L
-  absl::string_view str("123", 3);
-  str.remove_prefix(1);
-  str.remove_suffix(1);
-  absl::string_view bar;
-  str.swap(bar);
-  return bar.front();
-#else
-  return '2';
-#endif
-}
-
-TEST(StringViewTest, ConstexprMethods) {
-  // remove_prefix, remove_suffix, swap
-  static_assert(ConstexprMethodsHelper() == '2', "");
-
-  // substr
-  constexpr absl::string_view foobar("foobar", 6);
-  constexpr absl::string_view foo = foobar.substr(0, 3);
-  constexpr absl::string_view bar = foobar.substr(3);
-  EXPECT_EQ(foo, "foo");
-  EXPECT_EQ(bar, "bar");
-}
-
-TEST(StringViewTest, Noexcept) {
-  EXPECT_TRUE((std::is_nothrow_constructible<absl::string_view,
-                                             const std::string&>::value));
-  EXPECT_TRUE((std::is_nothrow_constructible<absl::string_view,
-                                             const std::string&>::value));
-  EXPECT_TRUE(std::is_nothrow_constructible<absl::string_view>::value);
-  constexpr absl::string_view sp;
-  EXPECT_TRUE(noexcept(sp.begin()));
-  EXPECT_TRUE(noexcept(sp.end()));
-  EXPECT_TRUE(noexcept(sp.cbegin()));
-  EXPECT_TRUE(noexcept(sp.cend()));
-  EXPECT_TRUE(noexcept(sp.rbegin()));
-  EXPECT_TRUE(noexcept(sp.rend()));
-  EXPECT_TRUE(noexcept(sp.crbegin()));
-  EXPECT_TRUE(noexcept(sp.crend()));
-  EXPECT_TRUE(noexcept(sp.size()));
-  EXPECT_TRUE(noexcept(sp.length()));
-  EXPECT_TRUE(noexcept(sp.empty()));
-  EXPECT_TRUE(noexcept(sp.data()));
-  EXPECT_TRUE(noexcept(sp.compare(sp)));
-  EXPECT_TRUE(noexcept(sp.find(sp)));
-  EXPECT_TRUE(noexcept(sp.find('f')));
-  EXPECT_TRUE(noexcept(sp.rfind(sp)));
-  EXPECT_TRUE(noexcept(sp.rfind('f')));
-  EXPECT_TRUE(noexcept(sp.find_first_of(sp)));
-  EXPECT_TRUE(noexcept(sp.find_first_of('f')));
-  EXPECT_TRUE(noexcept(sp.find_last_of(sp)));
-  EXPECT_TRUE(noexcept(sp.find_last_of('f')));
-  EXPECT_TRUE(noexcept(sp.find_first_not_of(sp)));
-  EXPECT_TRUE(noexcept(sp.find_first_not_of('f')));
-  EXPECT_TRUE(noexcept(sp.find_last_not_of(sp)));
-  EXPECT_TRUE(noexcept(sp.find_last_not_of('f')));
-}
-
-TEST(StringViewTest, BoundsCheck) {
-#ifndef ABSL_USES_STD_STRING_VIEW
-#if !defined(NDEBUG) || ABSL_OPTION_HARDENED
-  // Abseil's string_view implementation has bounds-checking in debug mode.
-  absl::string_view h = "hello";
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(h[5], "");
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(h[static_cast<size_t>(-1)], "");
-#endif
-#endif
-}
-
-TEST(ComparisonOpsTest, StringCompareNotAmbiguous) {
-  EXPECT_EQ("hello", std::string("hello"));
-  EXPECT_LT("hello", std::string("world"));
-}
-
-TEST(ComparisonOpsTest, HeterogeneousStringViewEquals) {
-  EXPECT_EQ(absl::string_view("hello"), std::string("hello"));
-  EXPECT_EQ("hello", absl::string_view("hello"));
-}
-
-TEST(FindOneCharTest, EdgeCases) {
-  absl::string_view a("xxyyyxx");
-
-  // Set a = "xyyyx".
-  a.remove_prefix(1);
-  a.remove_suffix(1);
-
-  EXPECT_EQ(0u, a.find('x'));
-  EXPECT_EQ(0u, a.find('x', 0));
-  EXPECT_EQ(4u, a.find('x', 1));
-  EXPECT_EQ(4u, a.find('x', 4));
-  EXPECT_EQ(absl::string_view::npos, a.find('x', 5));
-
-  EXPECT_EQ(4u, a.rfind('x'));
-  EXPECT_EQ(4u, a.rfind('x', 5));
-  EXPECT_EQ(4u, a.rfind('x', 4));
-  EXPECT_EQ(0u, a.rfind('x', 3));
-  EXPECT_EQ(0u, a.rfind('x', 0));
-
-  // Set a = "yyy".
-  a.remove_prefix(1);
-  a.remove_suffix(1);
-
-  EXPECT_EQ(absl::string_view::npos, a.find('x'));
-  EXPECT_EQ(absl::string_view::npos, a.rfind('x'));
-}
-
-#ifndef ABSL_HAVE_THREAD_SANITIZER  // Allocates too much memory for tsan.
-TEST(HugeStringView, TwoPointTwoGB) {
-  if (sizeof(size_t) <= 4)
-    return;
-  // Try a huge string piece.
-  const size_t size = size_t{2200} * 1000 * 1000;
-  std::string s(size, 'a');
-  absl::string_view sp(s);
-  EXPECT_EQ(size, sp.length());
-  sp.remove_prefix(1);
-  EXPECT_EQ(size - 1, sp.length());
-  sp.remove_suffix(2);
-  EXPECT_EQ(size - 1 - 2, sp.length());
-}
-#endif  // ABSL_HAVE_THREAD_SANITIZER
-
-#if !defined(NDEBUG) && !defined(ABSL_USES_STD_STRING_VIEW)
-TEST(NonNegativeLenTest, NonNegativeLen) {
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(
-      absl::string_view("xyz", static_cast<size_t>(-1)), "len <= kMaxSize");
-}
-
-TEST(LenExceedsMaxSizeTest, LenExceedsMaxSize) {
-  auto max_size = absl::string_view().max_size();
-
-  // This should construct ok (although the view itself is obviously invalid).
-  absl::string_view ok_view("", max_size);
-
-  // Adding one to the max should trigger an assertion.
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(absl::string_view("", max_size + 1),
-                                 "len <= kMaxSize");
-}
-#endif  // !defined(NDEBUG) && !defined(ABSL_USES_STD_STRING_VIEW)
-
-class StringViewStreamTest : public ::testing::Test {
- public:
-  // Set negative 'width' for right justification.
-  template <typename T>
-  std::string Pad(const T& s, int width, char fill = 0) {
-    std::ostringstream oss;
-    if (fill != 0) {
-      oss << std::setfill(fill);
-    }
-    if (width < 0) {
-      width = -width;
-      oss << std::right;
-    }
-    oss << std::setw(width) << s;
-    return oss.str();
-  }
-};
-
-TEST_F(StringViewStreamTest, Padding) {
-  std::string s("hello");
-  absl::string_view sp(s);
-  for (int w = -64; w < 64; ++w) {
-    SCOPED_TRACE(w);
-    EXPECT_EQ(Pad(s, w), Pad(sp, w));
-  }
-  for (int w = -64; w < 64; ++w) {
-    SCOPED_TRACE(w);
-    EXPECT_EQ(Pad(s, w, '#'), Pad(sp, w, '#'));
-  }
-}
-
-TEST_F(StringViewStreamTest, ResetsWidth) {
-  // Width should reset after one formatted write.
-  // If we weren't resetting width after formatting the string_view,
-  // we'd have width=5 carrying over to the printing of the "]",
-  // creating "[###hi####]".
-  std::string s = "hi";
-  absl::string_view sp = s;
-  {
-    std::ostringstream oss;
-    oss << "[" << std::setfill('#') << std::setw(5) << s << "]";
-    ASSERT_EQ("[###hi]", oss.str());
-  }
-  {
-    std::ostringstream oss;
-    oss << "[" << std::setfill('#') << std::setw(5) << sp << "]";
-    EXPECT_EQ("[###hi]", oss.str());
-  }
-}
-
 }  // namespace
diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel
index 9c6b31a..b3b55e7 100644
--- a/absl/synchronization/BUILD.bazel
+++ b/absl/synchronization/BUILD.bazel
@@ -72,9 +72,7 @@
         "//absl/base:core_headers",
         "//absl/base:raw_logging_internal",
         "//absl/time",
-    ] + select({
-        "//conditions:default": [],
-    }),
+    ],
 )
 
 cc_test(
diff --git a/absl/synchronization/internal/kernel_timeout.h b/absl/synchronization/internal/kernel_timeout.h
index 06404a7..c24fa86 100644
--- a/absl/synchronization/internal/kernel_timeout.h
+++ b/absl/synchronization/internal/kernel_timeout.h
@@ -129,8 +129,8 @@
   std::chrono::nanoseconds ToChronoDuration() const;
 
   // Returns true if steady (aka monotonic) clocks are supported by the system.
-  // This method exists because go/btm requires synchronized clocks, and
-  // thus requires we use the system (aka walltime) clock.
+  // This currently returns true on all platforms, but we have encountered
+  // platforms that once lacked steady clock support.
   static constexpr bool SupportsSteadyClock() { return true; }
 
  private:
diff --git a/absl/synchronization/internal/kernel_timeout_test.cc b/absl/synchronization/internal/kernel_timeout_test.cc
index 33962f8..811246c 100644
--- a/absl/synchronization/internal/kernel_timeout_test.cc
+++ b/absl/synchronization/internal/kernel_timeout_test.cc
@@ -24,12 +24,22 @@
 #include "absl/time/time.h"
 #include "gtest/gtest.h"
 
-// Test go/btm support by randomizing the value of clock_gettime() for
-// CLOCK_MONOTONIC. This works by overriding a weak symbol in glibc.
+#if 0  // All supported platforms currently have steady clocks.
+#define ABSL_INTERNAL_KERNEL_TIMEOUT_SUPPORTS_STEADY_CLOCK 0
+#else
+#define ABSL_INTERNAL_KERNEL_TIMEOUT_SUPPORTS_STEADY_CLOCK 1
+#endif
+
+static_assert(
+    absl::synchronization_internal::KernelTimeout::SupportsSteadyClock() ==
+    static_cast<bool>(ABSL_INTERNAL_KERNEL_TIMEOUT_SUPPORTS_STEADY_CLOCK));
+
+// Randomizing the value of clock_gettime() for CLOCK_MONOTONIC.
+// This works by overriding a weak symbol in glibc.
 // We should be resistant to this randomization when !SupportsSteadyClock().
-#if defined(__GOOGLE_GRTE_VERSION__) &&      \
-    !defined(ABSL_HAVE_ADDRESS_SANITIZER) && \
-    !defined(ABSL_HAVE_MEMORY_SANITIZER) &&  \
+#if !ABSL_INTERNAL_KERNEL_TIMEOUT_SUPPORTS_STEADY_CLOCK && \
+    !defined(ABSL_HAVE_ADDRESS_SANITIZER) &&               \
+    !defined(ABSL_HAVE_MEMORY_SANITIZER) &&                \
     !defined(ABSL_HAVE_THREAD_SANITIZER)
 extern "C" int __clock_gettime(clockid_t c, struct timespec* ts);
 
diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version
index ef468ad..cb3be9a 100644
--- a/absl/time/internal/cctz/testdata/version
+++ b/absl/time/internal/cctz/testdata/version
@@ -1 +1 @@
-2025b
+2025c
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada b/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
index 18d0d14..0d8c993 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel b/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
index 18d0d14..0d8c993 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana b/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
index 18d0d14..0d8c993 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
index 18d0d14..0d8c993 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab b/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab
index 402c015..4ae3523 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab
@@ -3,22 +3,22 @@
 # This file is in the public domain, so clarified as of
 # 2009-05-17 by Arthur David Olson.
 #
-# From Paul Eggert (2023-09-06):
+# From Paul Eggert (2025-07-01):
 # This file contains a table of two-letter country codes.  Columns are
-# separated by a single tab.  Lines beginning with '#' are comments.
+# separated by a single tab.  Lines beginning with ‘#’ are comments.
 # All text uses UTF-8 encoding.  The columns of the table are as follows:
 #
 # 1.  ISO 3166-1 alpha-2 country code, current as of
-#     ISO/TC 46 N1108 (2023-04-05).  See: ISO/TC 46 Documents
+#     ISO/TC 46 N1127 (2024-02-29).  See: ISO/TC 46 Documents
 #     https://www.iso.org/committee/48750.html?view=documents
 # 2.  The usual English name for the coded region.  This sometimes
 #     departs from ISO-listed names, sometimes so that sorted subsets
-#     of names are useful (e.g., "Samoa (American)" and "Samoa
-#     (western)" rather than "American Samoa" and "Samoa"),
+#     of names are useful (e.g., “Samoa (American)” and “Samoa
+#     (western)” rather than “American Samoa” and “Samoa”),
 #     sometimes to avoid confusion among non-experts (e.g.,
-#     "Czech Republic" and "Turkey" rather than "Czechia" and "Türkiye"),
-#     and sometimes to omit needless detail or churn (e.g., "Netherlands"
-#     rather than "Netherlands (the)" or "Netherlands (Kingdom of the)").
+#     “Czech Republic” and “Turkey” rather than “Czechia” and “Türkiye”),
+#     and sometimes to omit needless detail or churn (e.g., “Netherlands”
+#     rather than “Netherlands (the)” or “Netherlands (Kingdom of the)”).
 #
 # The table is sorted by country code.
 #
@@ -71,7 +71,7 @@
 CF	Central African Rep.
 CG	Congo (Rep.)
 CH	Switzerland
-CI	Côte d'Ivoire
+CI	Côte d’Ivoire
 CK	Cook Islands
 CL	Chile
 CM	Cameroon
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
index 36535bd..cd43e3d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
@@ -2,15 +2,15 @@
 #
 # This file is in the public domain.
 #
-# From Paul Eggert (2018-06-27):
+# From Paul Eggert (2025-05-15):
 # This file contains a table where each row stands for a timezone where
 # civil timestamps have agreed since 1970.  Columns are separated by
-# a single tab.  Lines beginning with '#' are comments.  All text uses
+# a single tab.  Lines beginning with ‘#’ are comments.  All text uses
 # UTF-8 encoding.  The columns of the table are as follows:
 #
 # 1.  The countries that overlap the timezone, as a comma-separated list
-#     of ISO 3166 2-character country codes.  See the file 'iso3166.tab'.
-# 2.  Latitude and longitude of the timezone's principal location
+#     of ISO 3166 2-character country codes.
+# 2.  Latitude and longitude of the timezone’s principal location
 #     in ISO 6709 sign-degrees-minutes-seconds format,
 #     either ±DDMM±DDDMM or ±DDMMSS±DDDMMSS,
 #     first latitude (+ is north), then longitude (+ is east).
@@ -197,7 +197,7 @@
 KZ	+5312+06337	Asia/Qostanay	Qostanay/Kostanay/Kustanay
 KZ	+5017+05710	Asia/Aqtobe	Aqtöbe/Aktobe
 KZ	+4431+05016	Asia/Aqtau	Mangghystaū/Mankistau
-KZ	+4707+05156	Asia/Atyrau	Atyraū/Atirau/Gur'yev
+KZ	+4707+05156	Asia/Atyrau	Atyraū/Atirau/Gur’yev
 KZ	+5113+05121	Asia/Oral	West Kazakhstan
 LB	+3353+03530	Asia/Beirut
 LK	+0656+07951	Asia/Colombo
@@ -245,7 +245,7 @@
 PF	-1732-14934	Pacific/Tahiti	Society Islands
 PF	-0900-13930	Pacific/Marquesas	Marquesas Islands
 PF	-2308-13457	Pacific/Gambier	Gambier Islands
-PG,AQ,FM	-0930+14710	Pacific/Port_Moresby	Papua New Guinea (most areas), Chuuk, Yap, Dumont d'Urville
+PG,AQ,FM	-0930+14710	Pacific/Port_Moresby	Papua New Guinea (most areas), Chuuk, Yap, Dumont d’Urville
 PG	-0613+15534	Pacific/Bougainville	Bougainville
 PH	+143512+1205804	Asia/Manila
 PK	+2452+06703	Asia/Karachi
@@ -265,7 +265,7 @@
 RS,BA,HR,ME,MK,SI	+4450+02030	Europe/Belgrade
 RU	+5443+02030	Europe/Kaliningrad	MSK-01 - Kaliningrad
 RU	+554521+0373704	Europe/Moscow	MSK+00 - Moscow area
-# Mention RU and UA alphabetically.  See "territorial claims" above.
+# Mention RU and UA alphabetically.  See “territorial claims” above.
 RU,UA	+4457+03406	Europe/Simferopol	Crimea
 RU	+5836+04939	Europe/Kirov	MSK+00 - Kirov
 RU	+4844+04425	Europe/Volgograd	MSK+00 - Volgograd
@@ -353,20 +353,20 @@
 # The next section contains experimental tab-separated comments for
 # use by user agents like tzselect that identify continents and oceans.
 #
-# For example, the comment "#@AQ<tab>Antarctica/" means the country code
+# For example, the comment ‘#@AQ<tab>Antarctica/’ means the country code
 # AQ is in the continent Antarctica regardless of the Zone name,
 # so Pacific/Auckland should be listed under Antarctica as well as
-# under the Pacific because its line's country codes include AQ.
+# under the Pacific because its line’s country codes include AQ.
 #
 # If more than one country code is affected each is listed separated
-# by commas, e.g., #@IS,SH<tab>Atlantic/".  If a country code is in
+# by commas, e.g., ‘#@IS,SH<tab>Atlantic/’.  If a country code is in
 # more than one continent or ocean, each is listed separated by
-# commas, e.g., the second column of "#@CY,TR<tab>Asia/,Europe/".
+# commas, e.g., the second column of ‘#@CY,TR<tab>Asia/,Europe/’.
 #
 # These experimental comments are present only for country codes where
 # the continent or ocean is not already obvious from the Zone name.
 # For example, there is no such comment for RU since it already
-# corresponds to Zone names starting with both "Europe/" and "Asia/".
+# corresponds to Zone names starting with both ‘Europe/’ and ‘Asia/’.
 #
 #@AQ	Antarctica/
 #@IS,SH	Atlantic/
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab b/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab
index 093f0a0..1d64b39 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab
@@ -5,12 +5,12 @@
 # From Paul Eggert (2023-12-18):
 # This file contains a table where each row stands for a timezone
 # where civil timestamps are predicted to agree from now on.
-# This file is like zone1970.tab (see zone1970.tab's comments),
+# This file is like zone1970.tab (see zone1970.tab’s comments),
 # but with the following changes:
 #
 # 1.  Each timezone corresponds to a set of clocks that are planned
 #     to agree from now on.  This is a larger set of clocks than in
-#     zone1970.tab, where each timezone's clocks must agree from 1970 on.
+#     zone1970.tab, where each timezone’s clocks must agree from 1970 on.
 # 2.  The first column is irrelevant and ignored.
 # 3.  The table is sorted in a different way:
 #     first by standard time UTC offset;
@@ -29,19 +29,19 @@
 #XX	coordinates	TZ	comments
 #
 # -11 - SST
-XX	-1416-17042	Pacific/Pago_Pago	Midway; Samoa ("SST")
+XX	-1416-17042	Pacific/Pago_Pago	Midway; Samoa (SST)
 #
 # -11
 XX	-1901-16955	Pacific/Niue	Niue
 #
 # -10 - HST
-XX	+211825-1575130	Pacific/Honolulu	Hawaii ("HST")
+XX	+211825-1575130	Pacific/Honolulu	Hawaii (HST)
 #
 # -10
 XX	-1732-14934	Pacific/Tahiti	Tahiti; Cook Islands
 #
 # -10/-09 - HST / HDT (North America DST)
-XX	+515248-1763929	America/Adak	western Aleutians in Alaska ("HST/HDT")
+XX	+515248-1763929	America/Adak	western Aleutians in Alaska (HST/HDT)
 #
 # -09:30
 XX	-0900-13930	Pacific/Marquesas	Marquesas
@@ -50,58 +50,58 @@
 XX	-2308-13457	Pacific/Gambier	Gambier
 #
 # -09/-08 - AKST/AKDT (North America DST)
-XX	+611305-1495401	America/Anchorage	most of Alaska ("AKST/AKDT")
+XX	+611305-1495401	America/Anchorage	most of Alaska (AKST/AKDT)
 #
 # -08
 XX	-2504-13005	Pacific/Pitcairn	Pitcairn
 #
 # -08/-07 - PST/PDT (North America DST)
-XX	+340308-1181434	America/Los_Angeles	Pacific ("PST/PDT") - US & Canada; Mexico near US border
+XX	+340308-1181434	America/Los_Angeles	Pacific (PST/PDT) - US & Canada; Mexico near US border
 #
 # -07 - MST
-XX	+332654-1120424	America/Phoenix	Mountain Standard ("MST") - Arizona; western Mexico; Yukon
+XX	+332654-1120424	America/Phoenix	Mountain Standard (MST) - Arizona; western Mexico; Yukon
 #
 # -07/-06 - MST/MDT (North America DST)
-XX	+394421-1045903	America/Denver	Mountain ("MST/MDT") - US & Canada; Mexico near US border
+XX	+394421-1045903	America/Denver	Mountain (MST/MDT) - US & Canada; Mexico near US border
 #
 # -06
 XX	-0054-08936	Pacific/Galapagos	Galápagos
 #
 # -06 - CST
-XX	+1924-09909	America/Mexico_City	Central Standard ("CST") - Saskatchewan; central Mexico; Central America
+XX	+1924-09909	America/Mexico_City	Central Standard (CST) - Saskatchewan; central Mexico; Central America
 #
 # -06/-05 (Chile DST)
 XX	-2709-10926	Pacific/Easter	Easter Island
 #
 # -06/-05 - CST/CDT (North America DST)
-XX	+415100-0873900	America/Chicago	Central ("CST/CDT") - US & Canada; Mexico near US border
+XX	+415100-0873900	America/Chicago	Central (CST/CDT) - US & Canada; Mexico near US border
 #
 # -05
 XX	-1203-07703	America/Lima	eastern South America
 #
 # -05 - EST
-XX	+175805-0764736	America/Jamaica	Eastern Standard ("EST") - Caymans; Jamaica; eastern Mexico; Panama
+XX	+175805-0764736	America/Jamaica	Eastern Standard (EST) - Caymans; Jamaica; eastern Mexico; Panama
 #
 # -05/-04 - CST/CDT (Cuba DST)
 XX	+2308-08222	America/Havana	Cuba
 #
 # -05/-04 - EST/EDT (North America DST)
-XX	+404251-0740023	America/New_York	Eastern ("EST/EDT") - US & Canada
+XX	+404251-0740023	America/New_York	Eastern (EST/EDT) - US & Canada
 #
 # -04
 XX	+1030-06656	America/Caracas	western South America
 #
 # -04 - AST
-XX	+1828-06954	America/Santo_Domingo	Atlantic Standard ("AST") - eastern Caribbean
+XX	+1828-06954	America/Santo_Domingo	Atlantic Standard (AST) - eastern Caribbean
 #
 # -04/-03 (Chile DST)
 XX	-3327-07040	America/Santiago	most of Chile
 #
 # -04/-03 - AST/ADT (North America DST)
-XX	+4439-06336	America/Halifax	Atlantic ("AST/ADT") - Canada; Bermuda
+XX	+4439-06336	America/Halifax	Atlantic (AST/ADT) - Canada; Bermuda
 #
 # -03:30/-02:30 - NST/NDT (North America DST)
-XX	+4734-05243	America/St_Johns	Newfoundland ("NST/NDT")
+XX	+4734-05243	America/St_Johns	Newfoundland (NST/NDT)
 #
 # -03
 XX	-2332-04637	America/Sao_Paulo	eastern and southern South America
@@ -122,43 +122,43 @@
 XX	+3744-02540	Atlantic/Azores	Azores
 #
 # +00 - GMT
-XX	+0519-00402	Africa/Abidjan	far western Africa; Iceland ("GMT")
+XX	+0519-00402	Africa/Abidjan	far western Africa; Iceland (GMT)
 #
 # +00/+01 - GMT/BST (EU DST)
-XX	+513030-0000731	Europe/London	United Kingdom ("GMT/BST")
+XX	+513030-0000731	Europe/London	United Kingdom (GMT/BST)
 #
 # +00/+01 - WET/WEST (EU DST)
-XX	+3843-00908	Europe/Lisbon	western Europe ("WET/WEST")
+XX	+3843-00908	Europe/Lisbon	western Europe (WET/WEST)
 #
 # +00/+02 - Troll DST
 XX	-720041+0023206	Antarctica/Troll	Troll Station in Antarctica
 #
 # +01 - CET
-XX	+3647+00303	Africa/Algiers	Algeria, Tunisia ("CET")
+XX	+3647+00303	Africa/Algiers	Algeria, Tunisia (CET)
 #
 # +01 - WAT
-XX	+0627+00324	Africa/Lagos	western Africa ("WAT")
+XX	+0627+00324	Africa/Lagos	western Africa (WAT)
 #
 # +01/+00 - IST/GMT (EU DST in reverse)
-XX	+5320-00615	Europe/Dublin	Ireland ("IST/GMT")
+XX	+5320-00615	Europe/Dublin	Ireland (IST/GMT)
 #
 # +01/+00 - (Morocco DST)
 XX	+3339-00735	Africa/Casablanca	Morocco
 #
 # +01/+02 - CET/CEST (EU DST)
-XX	+4852+00220	Europe/Paris	central Europe ("CET/CEST")
+XX	+4852+00220	Europe/Paris	central Europe (CET/CEST)
 #
 # +02 - CAT
-XX	-2558+03235	Africa/Maputo	central Africa ("CAT")
+XX	-2558+03235	Africa/Maputo	central Africa (CAT)
 #
 # +02 - EET
-XX	+3254+01311	Africa/Tripoli	Libya; Kaliningrad ("EET")
+XX	+3254+01311	Africa/Tripoli	Libya; Kaliningrad (EET)
 #
 # +02 - SAST
-XX	-2615+02800	Africa/Johannesburg	southern Africa ("SAST")
+XX	-2615+02800	Africa/Johannesburg	southern Africa (SAST)
 #
 # +02/+03 - EET/EEST (EU DST)
-XX	+3758+02343	Europe/Athens	eastern Europe ("EET/EEST")
+XX	+3758+02343	Europe/Athens	eastern Europe (EET/EEST)
 #
 # +02/+03 - EET/EEST (Egypt DST)
 XX	+3003+03115	Africa/Cairo	Egypt
@@ -179,10 +179,10 @@
 XX	+4101+02858	Europe/Istanbul	Near East; Belarus
 #
 # +03 - EAT
-XX	-0117+03649	Africa/Nairobi	eastern Africa ("EAT")
+XX	-0117+03649	Africa/Nairobi	eastern Africa (EAT)
 #
 # +03 - MSK
-XX	+554521+0373704	Europe/Moscow	Moscow ("MSK")
+XX	+554521+0373704	Europe/Moscow	Moscow (MSK)
 #
 # +03:30
 XX	+3540+05126	Asia/Tehran	Iran
@@ -197,13 +197,13 @@
 XX	+4120+06918	Asia/Tashkent	Russia; Kazakhstan; Tajikistan; Turkmenistan; Uzbekistan; Maldives
 #
 # +05 - PKT
-XX	+2452+06703	Asia/Karachi	Pakistan ("PKT")
+XX	+2452+06703	Asia/Karachi	Pakistan (PKT)
 #
 # +05:30
 XX	+0656+07951	Asia/Colombo	Sri Lanka
 #
 # +05:30 - IST
-XX	+2232+08822	Asia/Kolkata	India ("IST")
+XX	+2232+08822	Asia/Kolkata	India (IST)
 #
 # +05:45
 XX	+2743+08519	Asia/Kathmandu	Nepal
@@ -218,25 +218,25 @@
 XX	+1345+10031	Asia/Bangkok	Russia; Indochina; Christmas Island
 #
 # +07 - WIB
-XX	-0610+10648	Asia/Jakarta	Indonesia ("WIB")
+XX	-0610+10648	Asia/Jakarta	Indonesia (WIB)
 #
 # +08
 XX	+0117+10351	Asia/Singapore	Russia; Brunei; Malaysia; Singapore; Concordia
 #
 # +08 - AWST
-XX	-3157+11551	Australia/Perth	Western Australia ("AWST")
+XX	-3157+11551	Australia/Perth	Western Australia (AWST)
 #
 # +08 - CST
-XX	+3114+12128	Asia/Shanghai	China ("CST")
+XX	+3114+12128	Asia/Shanghai	China (CST)
 #
 # +08 - HKT
-XX	+2217+11409	Asia/Hong_Kong	Hong Kong ("HKT")
+XX	+2217+11409	Asia/Hong_Kong	Hong Kong (HKT)
 #
 # +08 - PHT
-XX	+143512+1205804	Asia/Manila	Philippines ("PHT")
+XX	+143512+1205804	Asia/Manila	Philippines (PHT)
 #
 # +08 - WITA
-XX	-0507+11924	Asia/Makassar	Indonesia ("WITA")
+XX	-0507+11924	Asia/Makassar	Indonesia (WITA)
 #
 # +08:45
 XX	-3143+12852	Australia/Eucla	Eucla
@@ -245,31 +245,31 @@
 XX	+5203+11328	Asia/Chita	Russia; Palau; East Timor
 #
 # +09 - JST
-XX	+353916+1394441	Asia/Tokyo	Japan ("JST"); Eyre Bird Observatory
+XX	+353916+1394441	Asia/Tokyo	Japan (JST); Eyre Bird Observatory
 #
 # +09 - KST
-XX	+3733+12658	Asia/Seoul	Korea ("KST")
+XX	+3733+12658	Asia/Seoul	Korea (KST)
 #
 # +09 - WIT
-XX	-0232+14042	Asia/Jayapura	Indonesia ("WIT")
+XX	-0232+14042	Asia/Jayapura	Indonesia (WIT)
 #
 # +09:30 - ACST
-XX	-1228+13050	Australia/Darwin	Northern Territory ("ACST")
+XX	-1228+13050	Australia/Darwin	Northern Territory (ACST)
 #
 # +09:30/+10:30 - ACST/ACDT (Australia DST)
-XX	-3455+13835	Australia/Adelaide	South Australia ("ACST/ACDT")
+XX	-3455+13835	Australia/Adelaide	South Australia (ACST/ACDT)
 #
 # +10
-XX	+4310+13156	Asia/Vladivostok	Russia; Yap; Chuuk; Papua New Guinea; Dumont d'Urville
+XX	+4310+13156	Asia/Vladivostok	Russia; Yap; Chuuk; Papua New Guinea; Dumont d’Urville
 #
 # +10 - AEST
-XX	-2728+15302	Australia/Brisbane	Queensland ("AEST")
+XX	-2728+15302	Australia/Brisbane	Queensland (AEST)
 #
 # +10 - ChST
-XX	+1328+14445	Pacific/Guam	Mariana Islands ("ChST")
+XX	+1328+14445	Pacific/Guam	Mariana Islands (ChST)
 #
 # +10/+11 - AEST/AEDT (Australia DST)
-XX	-3352+15113	Australia/Sydney	southeast Australia ("AEST/AEDT")
+XX	-3352+15113	Australia/Sydney	southeast Australia (AEST/AEDT)
 #
 # +10:30/+11
 XX	-3133+15905	Australia/Lord_Howe	Lord Howe Island
@@ -284,7 +284,7 @@
 XX	+5301+15839	Asia/Kamchatka	Russia; Tuvalu; Fiji; etc.
 #
 # +12/+13 (New Zealand DST)
-XX	-3652+17446	Pacific/Auckland	New Zealand ("NZST/NZDT")
+XX	-3652+17446	Pacific/Auckland	New Zealand (NZST/NZDT)
 #
 # +12:45/+13:45 (Chatham DST)
 XX	-4357-17633	Pacific/Chatham	Chatham Islands
diff --git a/ci/absl_alternate_options.h b/ci/absl_alternate_options.h
index 20bf010..d5567f3 100644
--- a/ci/absl_alternate_options.h
+++ b/ci/absl_alternate_options.h
@@ -20,7 +20,6 @@
 #ifndef ABSL_CI_ABSL_ALTERNATE_OPTIONS_H_
 #define ABSL_CI_ABSL_ALTERNATE_OPTIONS_H_
 
-#define ABSL_OPTION_USE_STD_STRING_VIEW 0
 #define ABSL_OPTION_USE_STD_ORDERING 0
 #define ABSL_OPTION_USE_INLINE_NAMESPACE 1
 #define ABSL_OPTION_INLINE_NAMESPACE_NAME ns