Merge tag '20211102.0' am: 594fdb7756 am: 7e85dffa51 am: 9a87b0bded am: 305d5b130d

Original change: https://android-review.googlesource.com/c/platform/external/abseil-cpp/+/1931218

Change-Id: Id19c321576ce5d00d7e0e2d391e530aa3739e755
diff --git a/Android.bp b/Android.bp
index 05de691..ba0a7ae 100644
--- a/Android.bp
+++ b/Android.bp
@@ -39,9 +39,7 @@
     name: "libabsl_base",
     srcs: [
         "absl/base/internal/cycleclock.cc",
-        "absl/base/internal/exponential_biased.cc",
         "absl/base/internal/low_level_alloc.cc",
-        "absl/base/internal/periodic_sampler.cc",
         "absl/base/internal/raw_logging.cc",
         "absl/base/internal/spinlock.cc",
         "absl/base/internal/spinlock_wait.cc",
@@ -104,7 +102,6 @@
     srcs: [
         "absl/hash/internal/city.cc",
         "absl/hash/internal/hash.cc",
-        "absl/hash/internal/wyhash.cc",
     ],
 }
 
diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake
index 253c73f..fa323ff 100644
--- a/CMake/AbseilDll.cmake
+++ b/CMake/AbseilDll.cmake
@@ -17,8 +17,6 @@
   "base/internal/dynamic_annotations.h"
   "base/internal/endian.h"
   "base/internal/errno_saver.h"
-  "base/internal/exponential_biased.cc"
-  "base/internal/exponential_biased.h"
   "base/internal/fast_type_id.h"
   "base/internal/hide_ptr.h"
   "base/internal/identity.h"
@@ -28,8 +26,6 @@
   "base/internal/low_level_alloc.h"
   "base/internal/low_level_scheduling.h"
   "base/internal/per_thread_tls.h"
-  "base/internal/periodic_sampler.cc"
-  "base/internal/periodic_sampler.h"
   "base/internal/pretty_function.h"
   "base/internal/raw_logging.cc"
   "base/internal/raw_logging.h"
@@ -124,8 +120,8 @@
   "hash/internal/hash.h"
   "hash/internal/hash.cc"
   "hash/internal/spy_hash_state.h"
-  "hash/internal/wyhash.h"
-  "hash/internal/wyhash.cc"
+  "hash/internal/low_level_hash.h"
+  "hash/internal/low_level_hash.cc"
   "memory/memory.h"
   "meta/type_traits.h"
   "numeric/bits.h"
@@ -133,6 +129,11 @@
   "numeric/int128.h"
   "numeric/internal/bits.h"
   "numeric/internal/representation.h"
+  "profiling/internal/exponential_biased.cc"
+  "profiling/internal/exponential_biased.h"
+  "profiling/internal/periodic_sampler.cc"
+  "profiling/internal/periodic_sampler.h"
+  "profiling/internal/sample_recorder.h"
   "random/bernoulli_distribution.h"
   "random/beta_distribution.h"
   "random/bit_gen_ref.h"
@@ -197,16 +198,35 @@
   "strings/cord.h"
   "strings/escaping.cc"
   "strings/escaping.h"
-  "strings/internal/cord_internal.cc"
-  "strings/internal/cord_internal.h"
-  "strings/internal/cord_rep_flat.h"
-  "strings/internal/cord_rep_ring.cc"
-  "strings/internal/cord_rep_ring.h"
-  "strings/internal/cord_rep_ring_reader.h"
   "strings/internal/charconv_bigint.cc"
   "strings/internal/charconv_bigint.h"
   "strings/internal/charconv_parse.cc"
   "strings/internal/charconv_parse.h"
+  "strings/internal/cord_internal.cc"
+  "strings/internal/cord_internal.h"
+  "strings/internal/cord_rep_consume.h"
+  "strings/internal/cord_rep_consume.cc"
+  "strings/internal/cord_rep_btree.cc"
+  "strings/internal/cord_rep_btree.h"
+  "strings/internal/cord_rep_btree_navigator.cc"
+  "strings/internal/cord_rep_btree_navigator.h"
+  "strings/internal/cord_rep_btree_reader.cc"
+  "strings/internal/cord_rep_btree_reader.h"
+  "strings/internal/cord_rep_flat.h"
+  "strings/internal/cord_rep_ring.cc"
+  "strings/internal/cord_rep_ring.h"
+  "strings/internal/cord_rep_ring_reader.h"
+  "strings/internal/cordz_functions.cc"
+  "strings/internal/cordz_functions.h"
+  "strings/internal/cordz_handle.cc"
+  "strings/internal/cordz_handle.h"
+  "strings/internal/cordz_info.cc"
+  "strings/internal/cordz_info.h"
+  "strings/internal/cordz_sample_token.cc"
+  "strings/internal/cordz_sample_token.h"
+  "strings/internal/cordz_statistics.h"
+  "strings/internal/cordz_update_scope.h"
+  "strings/internal/cordz_update_tracker.h"
   "strings/internal/stl_type_traits.h"
   "strings/internal/string_constant.h"
   "strings/match.cc"
@@ -438,6 +458,7 @@
   "raw_hash_set"
   "layout"
   "tracked"
+  "sample_recorder"
 )
 
 function(absl_internal_dll_contains)
diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake
index 54fb8df..f2ce567 100644
--- a/CMake/AbseilHelpers.cmake
+++ b/CMake/AbseilHelpers.cmake
@@ -141,7 +141,8 @@
   endif()
 
   # Generate a pkg-config file for every library:
-  if(_build_type STREQUAL "static" OR _build_type STREQUAL "shared")
+  if((_build_type STREQUAL "static" OR _build_type STREQUAL "shared")
+     AND ABSL_ENABLE_INSTALL)
     if(NOT ABSL_CC_LIB_TESTONLY)
       if(absl_VERSION)
         set(PC_VERSION "${absl_VERSION}")
@@ -170,8 +171,8 @@
       FILE(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" CONTENT "\
 prefix=${CMAKE_INSTALL_PREFIX}\n\
 exec_prefix=\${prefix}\n\
-libdir=\${prefix}/${CMAKE_INSTALL_LIBDIR}\n\
-includedir=\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}\n\
+libdir=${CMAKE_INSTALL_FULL_LIBDIR}\n\
+includedir=${CMAKE_INSTALL_FULL_INCLUDEDIR}\n\
 \n\
 Name: absl_${_NAME}\n\
 Description: Abseil ${_NAME} library\n\
@@ -253,9 +254,23 @@
       set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/internal)
     endif()
 
-    # INTERFACE libraries can't have the CXX_STANDARD property set
-    set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD})
-    set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
+    if(ABSL_PROPAGATE_CXX_STD)
+      # Abseil libraries require C++11 as the current minimum standard.
+      # Top-level application CMake projects should ensure a consistent C++
+      # standard for all compiled sources by setting CMAKE_CXX_STANDARD.
+      target_compile_features(${_NAME} PUBLIC cxx_std_11)
+    else()
+      # Note: This is legacy (before CMake 3.8) behavior. Setting the
+      # target-level CXX_STANDARD property to ABSL_CXX_STANDARD (which is
+      # initialized by CMAKE_CXX_STANDARD) should have no real effect, since
+      # that is the default value anyway.
+      #
+      # CXX_STANDARD_REQUIRED does guard against the top-level CMake project
+      # not having enabled CMAKE_CXX_STANDARD_REQUIRED (which prevents
+      # "decaying" to an older standard if the requested one isn't available).
+      set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD})
+      set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
+    endif()
 
     # When being installed, we lose the absl_ prefix.  We want to put it back
     # to have properly named lib files.  This is a no-op when we are not being
@@ -263,7 +278,7 @@
     if(ABSL_ENABLE_INSTALL)
       set_target_properties(${_NAME} PROPERTIES
         OUTPUT_NAME "absl_${_NAME}"
-        SOVERSION "2103.0.1"
+        SOVERSION "2111.0.0"
       )
     endif()
   else()
@@ -286,6 +301,16 @@
         ${ABSL_DEFAULT_LINKOPTS}
     )
     target_compile_definitions(${_NAME} INTERFACE ${ABSL_CC_LIB_DEFINES})
+
+    if(ABSL_PROPAGATE_CXX_STD)
+      # Abseil libraries require C++11 as the current minimum standard.
+      # Top-level application CMake projects should ensure a consistent C++
+      # standard for all compiled sources by setting CMAKE_CXX_STANDARD.
+      target_compile_features(${_NAME} INTERFACE cxx_std_11)
+
+      # (INTERFACE libraries can't have the CXX_STANDARD property set, so there
+      # is no legacy behavior else case).
+    endif()
   endif()
 
   # TODO currently we don't install googletest alongside abseil sources, so
@@ -335,8 +360,8 @@
 #     "awesome_test.cc"
 #   DEPS
 #     absl::awesome
-#     gmock
-#     gtest_main
+#     GTest::gmock
+#     GTest::gtest_main
 # )
 function(absl_cc_test)
   if(NOT BUILD_TESTING)
@@ -389,8 +414,23 @@
   # Add all Abseil targets to a folder in the IDE for organization.
   set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/test)
 
-  set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD})
-  set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
+  if(ABSL_PROPAGATE_CXX_STD)
+    # Abseil libraries require C++11 as the current minimum standard.
+    # Top-level application CMake projects should ensure a consistent C++
+    # standard for all compiled sources by setting CMAKE_CXX_STANDARD.
+    target_compile_features(${_NAME} PUBLIC cxx_std_11)
+  else()
+    # Note: This is legacy (before CMake 3.8) behavior. Setting the
+    # target-level CXX_STANDARD property to ABSL_CXX_STANDARD (which is
+    # initialized by CMAKE_CXX_STANDARD) should have no real effect, since
+    # that is the default value anyway.
+    #
+    # CXX_STANDARD_REQUIRED does guard against the top-level CMake project
+    # not having enabled CMAKE_CXX_STANDARD_REQUIRED (which prevents
+    # "decaying" to an older standard if the requested one isn't available).
+    set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD})
+    set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
+  endif()
 
   add_test(NAME ${_NAME} COMMAND ${_NAME})
 endfunction()
diff --git a/CMake/README.md b/CMake/README.md
index 5eee817..f8b27e6 100644
--- a/CMake/README.md
+++ b/CMake/README.md
@@ -34,15 +34,16 @@
 4. Add the **absl::** target you wish to use to the
 [`target_link_libraries()`](https://cmake.org/cmake/help/latest/command/target_link_libraries.html)
 section of your executable or of your library.<br>
-Here is a short CMakeLists.txt example of a project file using Abseil.
+Here is a short CMakeLists.txt example of an application project using Abseil.
 
 ```cmake
-cmake_minimum_required(VERSION 3.5)
-project(my_project)
+cmake_minimum_required(VERSION 3.8.2)
+project(my_app_project)
 
 # Pick the C++ standard to compile with.
 # Abseil currently supports C++11, C++14, and C++17.
 set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 add_subdirectory(abseil-cpp)
 
@@ -50,6 +51,44 @@
 target_link_libraries(my_exe absl::base absl::synchronization absl::strings)
 ```
 
+Note that if you are developing a library designed for use by other clients, you
+should instead leave `CMAKE_CXX_STANDARD` unset (or only set if being built as
+the current top-level CMake project) and configure the minimum required C++
+standard at the target level. If you require a later minimum C++ standard than
+Abseil does, it's a good idea to also enforce that `CMAKE_CXX_STANDARD` (which
+will control Abseil library targets) is set to at least that minimum. For
+example:
+
+```cmake
+cmake_minimum_required(VERSION 3.8.2)
+project(my_lib_project)
+
+# Leave C++ standard up to the root application, so set it only if this is the
+# current top-level CMake project.
+if(CMAKE_SOURCE_DIR STREQUAL my_lib_project_SOURCE_DIR)
+  set(CMAKE_CXX_STANDARD 17)
+  set(CMAKE_CXX_STANDARD_REQUIRED ON)
+endif()
+
+add_subdirectory(abseil-cpp)
+
+add_library(my_lib source.cpp)
+target_link_libraries(my_lib absl::base absl::synchronization absl::strings)
+
+# Enforce that my_lib requires C++17. Important to document for clients that they
+# must set CMAKE_CXX_STANDARD to 17 or higher for proper Abseil ABI compatibility
+# (since otherwise, Abseil library targets could be compiled with a lower C++
+# standard than my_lib).
+target_compile_features(my_lib PUBLIC cxx_std_17)
+if(CMAKE_CXX_STANDARD LESS 17)
+  message(FATAL_ERROR
+      "my_lib_project requires CMAKE_CXX_STANDARD >= 17 (got: ${CMAKE_CXX_STANDARD})")
+endif()
+```
+
+Then the top-level application project that uses your library is responsible for
+setting a consistent `CMAKE_CXX_STANDARD` that is sufficiently high.
+
 ### Running Abseil Tests with CMake
 
 Use the `-DBUILD_TESTING=ON` flag to run Abseil tests.
@@ -99,3 +138,48 @@
 absl::time
 absl::utility
 ```
+
+## Traditional CMake Set-Up
+
+For larger projects, it may make sense to use the traditional CMake set-up where you build and install projects separately.
+
+First, you'd need to build and install Google Test:
+```
+cmake -S /source/googletest -B /build/googletest -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/installation/dir -DBUILD_GMOCK=ON
+cmake --build /build/googletest --target install
+```
+
+Then you need to configure and build Abseil. Make sure you enable `ABSL_USE_EXTERNAL_GOOGLETEST` and `ABSL_FIND_GOOGLETEST`. You also need to enable `ABSL_ENABLE_INSTALL` so that you can install Abseil itself.
+```
+cmake -S /source/abseil-cpp -B /build/abseil-cpp -DCMAKE_PREFIX_PATH=/installation/dir -DCMAKE_INSTALL_PREFIX=/installation/dir -DABSL_ENABLE_INSTALL=ON -DABSL_USE_EXTERNAL_GOOGLETEST=ON -DABSL_FIND_GOOGLETEST=ON
+cmake --build /temporary/build/abseil-cpp
+```
+
+(`CMAKE_PREFIX_PATH` is where you already have Google Test installed; `CMAKE_INSTALL_PREFIX` is where you want to have Abseil installed; they can be different.)
+
+Run the tests:
+```
+ctest --test-dir /temporary/build/abseil-cpp
+```
+
+And finally install:
+```
+cmake --build /temporary/build/abseil-cpp --target install
+```
+
+# CMake Option Synposis
+
+## Enable Standard CMake Installation
+
+`-DABSL_ENABLE_INSTALL=ON`
+
+## Google Test Options
+
+`-DBUILD_TESTING=ON` must be set to enable testing
+
+- Have Abseil download and build Google Test for you: `-DABSL_USE_EXTERNAL_GOOGLETEST=OFF` (default)
+  - Download and build latest Google Test: `-DABSL_USE_GOOGLETEST_HEAD=ON`
+  - Download specific Google Test version (ZIP archive): `-DABSL_GOOGLETEST_DOWNLOAD_URL=https://.../version.zip`
+  - Use Google Test from specific local directory: `-DABSL_LOCAL_GOOGLETEST_DIR=/path/to/googletest`
+- Use Google Test included elsewhere in your project: `-DABSL_USE_EXTERNAL_GOOGLETEST=ON`
+- Use standard CMake `find_package(CTest)` to find installed Google Test: `-DABSL_USE_EXTERNAL_GOOGLETEST=ON -DABSL_FIND_GOOGLETEST=ON`
diff --git a/CMake/install_test_project/CMakeLists.txt b/CMake/install_test_project/CMakeLists.txt
index 06b797e..b865b2e 100644
--- a/CMake/install_test_project/CMakeLists.txt
+++ b/CMake/install_test_project/CMakeLists.txt
@@ -18,10 +18,8 @@
 cmake_minimum_required(VERSION 3.5)
 project(absl_cmake_testing CXX)
 
-set(CMAKE_CXX_STANDARD 11)
-
 add_executable(simple simple.cc)
 
 find_package(absl REQUIRED)
 
-target_link_libraries(simple absl::strings)
+target_link_libraries(simple absl::strings absl::config)
diff --git a/CMake/install_test_project/simple.cc b/CMake/install_test_project/simple.cc
index e9e3529..7daa7f0 100644
--- a/CMake/install_test_project/simple.cc
+++ b/CMake/install_test_project/simple.cc
@@ -14,8 +14,17 @@
 // limitations under the License.
 
 #include <iostream>
+#include "absl/base/config.h"
 #include "absl/strings/substitute.h"
 
+#if !defined(ABSL_LTS_RELEASE_VERSION) || ABSL_LTS_RELEASE_VERSION != 99998877
+#error ABSL_LTS_RELEASE_VERSION is not set correctly.
+#endif
+
+#if !defined(ABSL_LTS_RELEASE_PATCH_LEVEL) || ABSL_LTS_RELEASE_PATCH_LEVEL != 0
+#error ABSL_LTS_RELEASE_PATCH_LEVEL is not set correctly.
+#endif
+
 int main(int argc, char** argv) {
   for (int i = 0; i < argc; ++i) {
     std::cout << absl::Substitute("Arg $0: $1\n", i, argv[i]);
diff --git a/CMake/install_test_project/test.sh b/CMake/install_test_project/test.sh
index a3d3977..5a78c92 100755
--- a/CMake/install_test_project/test.sh
+++ b/CMake/install_test_project/test.sh
@@ -19,10 +19,9 @@
 # Fail on any error. Treat unset variables an error. Print commands as executed.
 set -euox pipefail
 
-source ci/cmake_common.sh
-
 absl_dir=/abseil-cpp
 absl_build_dir=/buildfs
+googletest_builddir=/googletest_builddir
 project_dir="${absl_dir}"/CMake/install_test_project
 project_build_dir=/buildfs/project-build
 
@@ -31,13 +30,30 @@
   build_shared_libs="ON"
 fi
 
+# Build and install GoogleTest
+mkdir "${googletest_builddir}"
+pushd "${googletest_builddir}"
+curl -L "${ABSL_GOOGLETEST_DOWNLOAD_URL}" --output "${ABSL_GOOGLETEST_COMMIT}".zip
+unzip "${ABSL_GOOGLETEST_COMMIT}".zip
+pushd "googletest-${ABSL_GOOGLETEST_COMMIT}"
+mkdir build
+pushd build
+cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS="${build_shared_libs}" ..
+make -j $(nproc)
+make install
+ldconfig
+popd
+popd
+popd
+
 # Run the LTS transformations
 ./create_lts.py 99998877
 
-# Install Abseil
+# Build and install Abseil
 pushd "${absl_build_dir}"
 cmake "${absl_dir}" \
-  -DABSL_GOOGLETEST_DOWNLOAD_URL="${ABSL_GOOGLETEST_DOWNLOAD_URL}" \
+  -DABSL_USE_EXTERNAL_GOOGLETEST=ON \
+  -DABSL_FIND_GOOGLETEST=ON  \
   -DCMAKE_BUILD_TYPE=Release \
   -DBUILD_TESTING=ON \
   -DBUILD_SHARED_LIBS="${build_shared_libs}"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3a73f70..750a475 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -41,11 +41,16 @@
   cmake_policy(SET CMP0077 NEW)
 endif (POLICY CMP0077)
 
+# Allow the user to specify the MSVC runtime
+if (POLICY CMP0091)
+  cmake_policy(SET CMP0091 NEW)
+endif (POLICY CMP0091)
+
 # Set BUILD_TESTING to OFF by default.
 # This must come before the project() and include(CTest) lines.
 OPTION(BUILD_TESTING "Build tests" OFF)
 
-project(absl LANGUAGES CXX VERSION 20210324)
+project(absl LANGUAGES CXX VERSION 20211102)
 include(CTest)
 
 # Output directory is correct by default for most build setups. However, when
@@ -62,6 +67,13 @@
   option(ABSL_ENABLE_INSTALL "Enable install rule" ON)
 endif()
 
+option(ABSL_PROPAGATE_CXX_STD
+  "Use CMake C++ standard meta features (e.g. cxx_std_11) that propagate to targets that link to Abseil"
+  OFF)  # TODO: Default to ON for CMake 3.8 and greater.
+if((${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.8) AND (NOT ABSL_PROPAGATE_CXX_STD))
+  message(WARNING "A future Abseil release will default ABSL_PROPAGATE_CXX_STD to ON for CMake 3.8 and up. We recommend enabling this option to ensure your project still builds correctly.")
+endif()
+
 list(APPEND CMAKE_MODULE_PATH
   ${CMAKE_CURRENT_LIST_DIR}/CMake
   ${CMAKE_CURRENT_LIST_DIR}/absl/copts
@@ -97,9 +109,18 @@
 ## pthread
 find_package(Threads REQUIRED)
 
+include(CMakeDependentOption)
+
 option(ABSL_USE_EXTERNAL_GOOGLETEST
   "If ON, Abseil will assume that the targets for GoogleTest are already provided by the including project. This makes sense when Abseil is used with add_subproject." OFF)
 
+cmake_dependent_option(ABSL_FIND_GOOGLETEST
+  "If ON, Abseil will use find_package(GTest) rather than assuming that GoogleTest is already provided by the including project."
+  ON
+  "ABSL_USE_EXTERNAL_GOOGLETEST"
+  OFF)
+
+
 option(ABSL_USE_GOOGLETEST_HEAD
   "If ON, abseil will download HEAD from GoogleTest at config time." OFF)
 
@@ -111,7 +132,15 @@
 
 if(BUILD_TESTING)
   ## check targets
-  if (NOT ABSL_USE_EXTERNAL_GOOGLETEST)
+  if (ABSL_USE_EXTERNAL_GOOGLETEST)
+    if (ABSL_FIND_GOOGLETEST)
+      find_package(GTest REQUIRED)
+    else()
+      if (NOT TARGET gtest AND NOT TARGET GTest::gtest)
+        message(FATAL_ERROR "ABSL_USE_EXTERNAL_GOOGLETEST is ON and ABSL_FIND_GOOGLETEST is OFF, which means that the top-level project must build the Google Test project. However, the target gtest was not found.")
+      endif()
+    endif()
+  else()
     set(absl_gtest_build_dir ${CMAKE_BINARY_DIR}/googletest-build)
     if(ABSL_USE_GOOGLETEST_HEAD AND ABSL_GOOGLETEST_DOWNLOAD_URL)
       message(FATAL_ERROR "Do not set both ABSL_USE_GOOGLETEST_HEAD and ABSL_GOOGLETEST_DOWNLOAD_URL")
@@ -129,16 +158,18 @@
     include(CMake/Googletest/DownloadGTest.cmake)
   endif()
 
-  check_target(gtest)
-  check_target(gtest_main)
-  check_target(gmock)
+  if (NOT ABSL_FIND_GOOGLETEST)
+    # When Google Test is included directly rather than through find_package, the aliases are missing.
+    add_library(GTest::gtest ALIAS gtest)
+    add_library(GTest::gtest_main ALIAS gtest_main)
+    add_library(GTest::gmock ALIAS gmock)
+    add_library(GTest::gmock_main ALIAS gmock_main)
+  endif()
 
-  list(APPEND ABSL_TEST_COMMON_LIBRARIES
-    gtest_main
-    gtest
-    gmock
-    ${CMAKE_THREAD_LIBS_INIT}
-  )
+  check_target(GTest::gtest)
+  check_target(GTest::gtest_main)
+  check_target(GTest::gmock)
+  check_target(GTest::gmock_main)
 endif()
 
 add_subdirectory(absl)
diff --git a/FAQ.md b/FAQ.md
index 78028fc..fbd92ce 100644
--- a/FAQ.md
+++ b/FAQ.md
@@ -27,7 +27,10 @@
   file](https://docs.bazel.build/versions/master/guide.html#bazelrc)
 
 If you are using CMake as the build system, you'll need to add a line like
-`set(CMAKE_CXX_STANDARD 17)` to your top level `CMakeLists.txt` file. See the
+`set(CMAKE_CXX_STANDARD 17)` to your top level `CMakeLists.txt` file. If you
+are developing a library designed to be used by other clients, you should
+instead leave `CMAKE_CXX_STANDARD` unset and configure the minimum C++ standard
+required by each of your library targets via `target_compile_features`. See the
 [CMake build
 instructions](https://github.com/abseil/abseil-cpp/blob/master/CMake/README.md)
 for more information.
diff --git a/METADATA b/METADATA
index 23f8fb2..a36d767 100644
--- a/METADATA
+++ b/METADATA
@@ -12,7 +12,7 @@
     type: GIT
     value: "https://github.com/abseil/abseil-cpp"
   }
-  version: "20210324.2"
-  last_upgrade_date { year: 2021 month: 07 day: 23 }
+  version: "20211102.0"
+  last_upgrade_date { year: 2021 month: 12 day: 21 }
 }
 
diff --git a/README.md b/README.md
index 264c4b3..db3a7b4 100644
--- a/README.md
+++ b/README.md
@@ -92,6 +92,9 @@
   available within C++14 and C++17 versions of the C++ `<type_traits>` library.
 * [`numeric`](absl/numeric/)
   <br /> The `numeric` library contains C++11-compatible 128-bit integers.
+* [`profiling`](absl/profiling/)
+  <br /> The `profiling` library contains utility code for profiling C++
+  entities.  It is currently a private dependency of other Abseil libraries.
 * [`status`](absl/status/)
   <br /> The `status` contains abstractions for error handling, specifically
   `absl::Status` and `absl::StatusOr<T>`.
diff --git a/WORKSPACE b/WORKSPACE
index 258d23b..c9aa8ca 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -15,31 +15,30 @@
 #
 
 workspace(name = "com_google_absl")
+
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
 
 # GoogleTest/GoogleMock framework. Used by most unit-tests.
 http_archive(
-    name = "com_google_googletest",
+    name = "com_google_googletest",  # 2021-07-09T13:28:13Z
+    sha256 = "12ef65654dc01ab40f6f33f9d02c04f2097d2cd9fbe48dc6001b29543583b0ad",
+    strip_prefix = "googletest-8d51ffdfab10b3fba636ae69bc03da4b54f8c235",
     # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh.
-    urls = ["https://github.com/google/googletest/archive/8567b09290fe402cf01923e2131c5635b8ed851b.zip"],  # 2020-06-12T22:24:28Z
-    strip_prefix = "googletest-8567b09290fe402cf01923e2131c5635b8ed851b",
-    sha256 = "9a8a166eb6a56c7b3d7b19dc2c946fe4778fd6f21c7a12368ad3b836d8f1be48",
+    urls = ["https://github.com/google/googletest/archive/8d51ffdfab10b3fba636ae69bc03da4b54f8c235.zip"],
 )
 
 # Google benchmark.
 http_archive(
-    name = "com_github_google_benchmark",
-    urls = ["https://github.com/google/benchmark/archive/bf585a2789e30585b4e3ce6baf11ef2750b54677.zip"],  # 2020-11-26T11:14:03Z
-    strip_prefix = "benchmark-bf585a2789e30585b4e3ce6baf11ef2750b54677",
-    sha256 = "2a778d821997df7d8646c9c59b8edb9a573a6e04c534c01892a40aa524a7b68c",
+    name = "com_github_google_benchmark",  # 2021-09-20T09:19:51Z
+    sha256 = "62e2f2e6d8a744d67e4bbc212fcfd06647080de4253c97ad5c6749e09faf2cb0",
+    strip_prefix = "benchmark-0baacde3618ca617da95375e0af13ce1baadea47",
+    urls = ["https://github.com/google/benchmark/archive/0baacde3618ca617da95375e0af13ce1baadea47.zip"],
 )
 
-# C++ rules for Bazel.
+# Bazel platform rules.
 http_archive(
-    name = "rules_cc",
-    sha256 = "9a446e9dd9c1bb180c86977a8dc1e9e659550ae732ae58bd2e8fd51e15b2c91d",
-    strip_prefix = "rules_cc-262ebec3c2296296526740db4aefce68c80de7fa",
-    urls = [
-        "https://github.com/bazelbuild/rules_cc/archive/262ebec3c2296296526740db4aefce68c80de7fa.zip",
-    ],
+    name = "platforms",
+    sha256 = "b601beaf841244de5c5a50d2b2eddd34839788000fa1be4260ce6603ca0d8eb7",
+    strip_prefix = "platforms-98939346da932eef0b54cf808622f5bb0928f00b",
+    urls = ["https://github.com/bazelbuild/platforms/archive/98939346da932eef0b54cf808622f5bb0928f00b.zip"],
 )
diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel
index c9d4a2d..d799b7f 100644
--- a/absl/BUILD.bazel
+++ b/absl/BUILD.bazel
@@ -44,14 +44,14 @@
 config_setting(
     name = "osx",
     constraint_values = [
-        "@bazel_tools//platforms:osx",
+        "@platforms//os:osx",
     ],
 )
 
 config_setting(
     name = "ios",
     constraint_values = [
-        "@bazel_tools//platforms:ios",
+        "@platforms//os:ios",
     ],
 )
 
@@ -70,3 +70,11 @@
     },
     visibility = [":__subpackages__"],
 )
+
+config_setting(
+    name = "fuchsia",
+    values = {
+        "cpu": "fuchsia",
+    },
+    visibility = [":__subpackages__"],
+)
diff --git a/absl/CMakeLists.txt b/absl/CMakeLists.txt
index a41e1ee..b171584 100644
--- a/absl/CMakeLists.txt
+++ b/absl/CMakeLists.txt
@@ -25,6 +25,7 @@
 add_subdirectory(memory)
 add_subdirectory(meta)
 add_subdirectory(numeric)
+add_subdirectory(profiling)
 add_subdirectory(random)
 add_subdirectory(status)
 add_subdirectory(strings)
diff --git a/absl/algorithm/BUILD.bazel b/absl/algorithm/BUILD.bazel
index a3002b7..afc5263 100644
--- a/absl/algorithm/BUILD.bazel
+++ b/absl/algorithm/BUILD.bazel
@@ -14,7 +14,6 @@
 # limitations under the License.
 #
 
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt
index 56cd0fb..609d858 100644
--- a/absl/algorithm/CMakeLists.txt
+++ b/absl/algorithm/CMakeLists.txt
@@ -35,7 +35,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::algorithm
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -65,5 +65,5 @@
     absl::core_headers
     absl::memory
     absl::span
-    gmock_main
+    GTest::gmock_main
 )
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h
index 6398438..c38a4a6 100644
--- a/absl/algorithm/container.h
+++ b/absl/algorithm/container.h
@@ -905,11 +905,11 @@
 
 // Overload of c_sort() for performing a `comp` comparison other than the
 // default `operator<`.
-template <typename C, typename Compare>
-void c_sort(C& c, Compare&& comp) {
+template <typename C, typename LessThan>
+void c_sort(C& c, LessThan&& comp) {
   std::sort(container_algorithm_internal::c_begin(c),
             container_algorithm_internal::c_end(c),
-            std::forward<Compare>(comp));
+            std::forward<LessThan>(comp));
 }
 
 // c_stable_sort()
@@ -925,11 +925,11 @@
 
 // Overload of c_stable_sort() for performing a `comp` comparison other than the
 // default `operator<`.
-template <typename C, typename Compare>
-void c_stable_sort(C& c, Compare&& comp) {
+template <typename C, typename LessThan>
+void c_stable_sort(C& c, LessThan&& comp) {
   std::stable_sort(container_algorithm_internal::c_begin(c),
                    container_algorithm_internal::c_end(c),
-                   std::forward<Compare>(comp));
+                   std::forward<LessThan>(comp));
 }
 
 // c_is_sorted()
@@ -944,11 +944,11 @@
 
 // c_is_sorted() overload for performing a `comp` comparison other than the
 // default `operator<`.
-template <typename C, typename Compare>
-bool c_is_sorted(const C& c, Compare&& comp) {
+template <typename C, typename LessThan>
+bool c_is_sorted(const C& c, LessThan&& comp) {
   return std::is_sorted(container_algorithm_internal::c_begin(c),
                         container_algorithm_internal::c_end(c),
-                        std::forward<Compare>(comp));
+                        std::forward<LessThan>(comp));
 }
 
 // c_partial_sort()
@@ -966,14 +966,14 @@
 
 // Overload of c_partial_sort() for performing a `comp` comparison other than
 // the default `operator<`.
-template <typename RandomAccessContainer, typename Compare>
+template <typename RandomAccessContainer, typename LessThan>
 void c_partial_sort(
     RandomAccessContainer& sequence,
     container_algorithm_internal::ContainerIter<RandomAccessContainer> middle,
-    Compare&& comp) {
+    LessThan&& comp) {
   std::partial_sort(container_algorithm_internal::c_begin(sequence), middle,
                     container_algorithm_internal::c_end(sequence),
-                    std::forward<Compare>(comp));
+                    std::forward<LessThan>(comp));
 }
 
 // c_partial_sort_copy()
@@ -994,15 +994,15 @@
 
 // Overload of c_partial_sort_copy() for performing a `comp` comparison other
 // than the default `operator<`.
-template <typename C, typename RandomAccessContainer, typename Compare>
+template <typename C, typename RandomAccessContainer, typename LessThan>
 container_algorithm_internal::ContainerIter<RandomAccessContainer>
 c_partial_sort_copy(const C& sequence, RandomAccessContainer& result,
-                    Compare&& comp) {
+                    LessThan&& comp) {
   return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence),
                                 container_algorithm_internal::c_end(sequence),
                                 container_algorithm_internal::c_begin(result),
                                 container_algorithm_internal::c_end(result),
-                                std::forward<Compare>(comp));
+                                std::forward<LessThan>(comp));
 }
 
 // c_is_sorted_until()
@@ -1018,12 +1018,12 @@
 
 // Overload of c_is_sorted_until() for performing a `comp` comparison other than
 // the default `operator<`.
-template <typename C, typename Compare>
+template <typename C, typename LessThan>
 container_algorithm_internal::ContainerIter<C> c_is_sorted_until(
-    C& c, Compare&& comp) {
+    C& c, LessThan&& comp) {
   return std::is_sorted_until(container_algorithm_internal::c_begin(c),
                               container_algorithm_internal::c_end(c),
-                              std::forward<Compare>(comp));
+                              std::forward<LessThan>(comp));
 }
 
 // c_nth_element()
@@ -1043,14 +1043,14 @@
 
 // Overload of c_nth_element() for performing a `comp` comparison other than
 // the default `operator<`.
-template <typename RandomAccessContainer, typename Compare>
+template <typename RandomAccessContainer, typename LessThan>
 void c_nth_element(
     RandomAccessContainer& sequence,
     container_algorithm_internal::ContainerIter<RandomAccessContainer> nth,
-    Compare&& comp) {
+    LessThan&& comp) {
   std::nth_element(container_algorithm_internal::c_begin(sequence), nth,
                    container_algorithm_internal::c_end(sequence),
-                   std::forward<Compare>(comp));
+                   std::forward<LessThan>(comp));
 }
 
 //------------------------------------------------------------------------------
@@ -1072,12 +1072,12 @@
 
 // Overload of c_lower_bound() for performing a `comp` comparison other than
 // the default `operator<`.
-template <typename Sequence, typename T, typename Compare>
+template <typename Sequence, typename T, typename LessThan>
 container_algorithm_internal::ContainerIter<Sequence> c_lower_bound(
-    Sequence& sequence, T&& value, Compare&& comp) {
+    Sequence& sequence, T&& value, LessThan&& comp) {
   return std::lower_bound(container_algorithm_internal::c_begin(sequence),
                           container_algorithm_internal::c_end(sequence),
-                          std::forward<T>(value), std::forward<Compare>(comp));
+                          std::forward<T>(value), std::forward<LessThan>(comp));
 }
 
 // c_upper_bound()
@@ -1095,12 +1095,12 @@
 
 // Overload of c_upper_bound() for performing a `comp` comparison other than
 // the default `operator<`.
-template <typename Sequence, typename T, typename Compare>
+template <typename Sequence, typename T, typename LessThan>
 container_algorithm_internal::ContainerIter<Sequence> c_upper_bound(
-    Sequence& sequence, T&& value, Compare&& comp) {
+    Sequence& sequence, T&& value, LessThan&& comp) {
   return std::upper_bound(container_algorithm_internal::c_begin(sequence),
                           container_algorithm_internal::c_end(sequence),
-                          std::forward<T>(value), std::forward<Compare>(comp));
+                          std::forward<T>(value), std::forward<LessThan>(comp));
 }
 
 // c_equal_range()
@@ -1118,12 +1118,12 @@
 
 // Overload of c_equal_range() for performing a `comp` comparison other than
 // the default `operator<`.
-template <typename Sequence, typename T, typename Compare>
+template <typename Sequence, typename T, typename LessThan>
 container_algorithm_internal::ContainerIterPairType<Sequence, Sequence>
-c_equal_range(Sequence& sequence, T&& value, Compare&& comp) {
+c_equal_range(Sequence& sequence, T&& value, LessThan&& comp) {
   return std::equal_range(container_algorithm_internal::c_begin(sequence),
                           container_algorithm_internal::c_end(sequence),
-                          std::forward<T>(value), std::forward<Compare>(comp));
+                          std::forward<T>(value), std::forward<LessThan>(comp));
 }
 
 // c_binary_search()
@@ -1140,12 +1140,12 @@
 
 // Overload of c_binary_search() for performing a `comp` comparison other than
 // the default `operator<`.
-template <typename Sequence, typename T, typename Compare>
-bool c_binary_search(Sequence&& sequence, T&& value, Compare&& comp) {
+template <typename Sequence, typename T, typename LessThan>
+bool c_binary_search(Sequence&& sequence, T&& value, LessThan&& comp) {
   return std::binary_search(container_algorithm_internal::c_begin(sequence),
                             container_algorithm_internal::c_end(sequence),
                             std::forward<T>(value),
-                            std::forward<Compare>(comp));
+                            std::forward<LessThan>(comp));
 }
 
 //------------------------------------------------------------------------------
@@ -1166,14 +1166,14 @@
 
 // Overload of c_merge() for performing a `comp` comparison other than
 // the default `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare>
+template <typename C1, typename C2, typename OutputIterator, typename LessThan>
 OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result,
-                       Compare&& comp) {
+                       LessThan&& comp) {
   return std::merge(container_algorithm_internal::c_begin(c1),
                     container_algorithm_internal::c_end(c1),
                     container_algorithm_internal::c_begin(c2),
                     container_algorithm_internal::c_end(c2), result,
-                    std::forward<Compare>(comp));
+                    std::forward<LessThan>(comp));
 }
 
 // c_inplace_merge()
@@ -1189,13 +1189,13 @@
 
 // Overload of c_inplace_merge() for performing a merge using a `comp` other
 // than `operator<`.
-template <typename C, typename Compare>
+template <typename C, typename LessThan>
 void c_inplace_merge(C& c,
                      container_algorithm_internal::ContainerIter<C> middle,
-                     Compare&& comp) {
+                     LessThan&& comp) {
   std::inplace_merge(container_algorithm_internal::c_begin(c), middle,
                      container_algorithm_internal::c_end(c),
-                     std::forward<Compare>(comp));
+                     std::forward<LessThan>(comp));
 }
 
 // c_includes()
@@ -1213,13 +1213,13 @@
 
 // Overload of c_includes() for performing a merge using a `comp` other than
 // `operator<`.
-template <typename C1, typename C2, typename Compare>
-bool c_includes(const C1& c1, const C2& c2, Compare&& comp) {
+template <typename C1, typename C2, typename LessThan>
+bool c_includes(const C1& c1, const C2& c2, LessThan&& comp) {
   return std::includes(container_algorithm_internal::c_begin(c1),
                        container_algorithm_internal::c_end(c1),
                        container_algorithm_internal::c_begin(c2),
                        container_algorithm_internal::c_end(c2),
-                       std::forward<Compare>(comp));
+                       std::forward<LessThan>(comp));
 }
 
 // c_set_union()
@@ -1243,7 +1243,7 @@
 
 // Overload of c_set_union() for performing a merge using a `comp` other than
 // `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare,
+template <typename C1, typename C2, typename OutputIterator, typename LessThan,
           typename = typename std::enable_if<
               !container_algorithm_internal::IsUnorderedContainer<C1>::value,
               void>::type,
@@ -1251,18 +1251,18 @@
               !container_algorithm_internal::IsUnorderedContainer<C2>::value,
               void>::type>
 OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output,
-                           Compare&& comp) {
+                           LessThan&& comp) {
   return std::set_union(container_algorithm_internal::c_begin(c1),
                         container_algorithm_internal::c_end(c1),
                         container_algorithm_internal::c_begin(c2),
                         container_algorithm_internal::c_end(c2), output,
-                        std::forward<Compare>(comp));
+                        std::forward<LessThan>(comp));
 }
 
 // c_set_intersection()
 //
 // Container-based version of the <algorithm> `std::set_intersection()` function
-// to return an iterator containing the intersection of two containers.
+// to return an iterator containing the intersection of two sorted containers.
 template <typename C1, typename C2, typename OutputIterator,
           typename = typename std::enable_if<
               !container_algorithm_internal::IsUnorderedContainer<C1>::value,
@@ -1272,6 +1272,11 @@
               void>::type>
 OutputIterator c_set_intersection(const C1& c1, const C2& c2,
                                   OutputIterator output) {
+  // In debug builds, ensure that both containers are sorted with respect to the
+  // default comparator. std::set_intersection requires the containers be sorted
+  // using operator<.
+  assert(absl::c_is_sorted(c1));
+  assert(absl::c_is_sorted(c2));
   return std::set_intersection(container_algorithm_internal::c_begin(c1),
                                container_algorithm_internal::c_end(c1),
                                container_algorithm_internal::c_begin(c2),
@@ -1280,7 +1285,7 @@
 
 // Overload of c_set_intersection() for performing a merge using a `comp` other
 // than `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare,
+template <typename C1, typename C2, typename OutputIterator, typename LessThan,
           typename = typename std::enable_if<
               !container_algorithm_internal::IsUnorderedContainer<C1>::value,
               void>::type,
@@ -1288,12 +1293,17 @@
               !container_algorithm_internal::IsUnorderedContainer<C2>::value,
               void>::type>
 OutputIterator c_set_intersection(const C1& c1, const C2& c2,
-                                  OutputIterator output, Compare&& comp) {
+                                  OutputIterator output, LessThan&& comp) {
+  // In debug builds, ensure that both containers are sorted with respect to the
+  // default comparator. std::set_intersection requires the containers be sorted
+  // using the same comparator.
+  assert(absl::c_is_sorted(c1, comp));
+  assert(absl::c_is_sorted(c2, comp));
   return std::set_intersection(container_algorithm_internal::c_begin(c1),
                                container_algorithm_internal::c_end(c1),
                                container_algorithm_internal::c_begin(c2),
                                container_algorithm_internal::c_end(c2), output,
-                               std::forward<Compare>(comp));
+                               std::forward<LessThan>(comp));
 }
 
 // c_set_difference()
@@ -1318,7 +1328,7 @@
 
 // Overload of c_set_difference() for performing a merge using a `comp` other
 // than `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare,
+template <typename C1, typename C2, typename OutputIterator, typename LessThan,
           typename = typename std::enable_if<
               !container_algorithm_internal::IsUnorderedContainer<C1>::value,
               void>::type,
@@ -1326,12 +1336,12 @@
               !container_algorithm_internal::IsUnorderedContainer<C2>::value,
               void>::type>
 OutputIterator c_set_difference(const C1& c1, const C2& c2,
-                                OutputIterator output, Compare&& comp) {
+                                OutputIterator output, LessThan&& comp) {
   return std::set_difference(container_algorithm_internal::c_begin(c1),
                              container_algorithm_internal::c_end(c1),
                              container_algorithm_internal::c_begin(c2),
                              container_algorithm_internal::c_end(c2), output,
-                             std::forward<Compare>(comp));
+                             std::forward<LessThan>(comp));
 }
 
 // c_set_symmetric_difference()
@@ -1357,7 +1367,7 @@
 
 // Overload of c_set_symmetric_difference() for performing a merge using a
 // `comp` other than `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare,
+template <typename C1, typename C2, typename OutputIterator, typename LessThan,
           typename = typename std::enable_if<
               !container_algorithm_internal::IsUnorderedContainer<C1>::value,
               void>::type,
@@ -1366,13 +1376,13 @@
               void>::type>
 OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2,
                                           OutputIterator output,
-                                          Compare&& comp) {
+                                          LessThan&& comp) {
   return std::set_symmetric_difference(
       container_algorithm_internal::c_begin(c1),
       container_algorithm_internal::c_end(c1),
       container_algorithm_internal::c_begin(c2),
       container_algorithm_internal::c_end(c2), output,
-      std::forward<Compare>(comp));
+      std::forward<LessThan>(comp));
 }
 
 //------------------------------------------------------------------------------
@@ -1391,11 +1401,11 @@
 
 // Overload of c_push_heap() for performing a push operation on a heap using a
 // `comp` other than `operator<`.
-template <typename RandomAccessContainer, typename Compare>
-void c_push_heap(RandomAccessContainer& sequence, Compare&& comp) {
+template <typename RandomAccessContainer, typename LessThan>
+void c_push_heap(RandomAccessContainer& sequence, LessThan&& comp) {
   std::push_heap(container_algorithm_internal::c_begin(sequence),
                  container_algorithm_internal::c_end(sequence),
-                 std::forward<Compare>(comp));
+                 std::forward<LessThan>(comp));
 }
 
 // c_pop_heap()
@@ -1410,11 +1420,11 @@
 
 // Overload of c_pop_heap() for performing a pop operation on a heap using a
 // `comp` other than `operator<`.
-template <typename RandomAccessContainer, typename Compare>
-void c_pop_heap(RandomAccessContainer& sequence, Compare&& comp) {
+template <typename RandomAccessContainer, typename LessThan>
+void c_pop_heap(RandomAccessContainer& sequence, LessThan&& comp) {
   std::pop_heap(container_algorithm_internal::c_begin(sequence),
                 container_algorithm_internal::c_end(sequence),
-                std::forward<Compare>(comp));
+                std::forward<LessThan>(comp));
 }
 
 // c_make_heap()
@@ -1429,11 +1439,11 @@
 
 // Overload of c_make_heap() for performing heap comparisons using a
 // `comp` other than `operator<`
-template <typename RandomAccessContainer, typename Compare>
-void c_make_heap(RandomAccessContainer& sequence, Compare&& comp) {
+template <typename RandomAccessContainer, typename LessThan>
+void c_make_heap(RandomAccessContainer& sequence, LessThan&& comp) {
   std::make_heap(container_algorithm_internal::c_begin(sequence),
                  container_algorithm_internal::c_end(sequence),
-                 std::forward<Compare>(comp));
+                 std::forward<LessThan>(comp));
 }
 
 // c_sort_heap()
@@ -1448,11 +1458,11 @@
 
 // Overload of c_sort_heap() for performing heap comparisons using a
 // `comp` other than `operator<`
-template <typename RandomAccessContainer, typename Compare>
-void c_sort_heap(RandomAccessContainer& sequence, Compare&& comp) {
+template <typename RandomAccessContainer, typename LessThan>
+void c_sort_heap(RandomAccessContainer& sequence, LessThan&& comp) {
   std::sort_heap(container_algorithm_internal::c_begin(sequence),
                  container_algorithm_internal::c_end(sequence),
-                 std::forward<Compare>(comp));
+                 std::forward<LessThan>(comp));
 }
 
 // c_is_heap()
@@ -1467,11 +1477,11 @@
 
 // Overload of c_is_heap() for performing heap comparisons using a
 // `comp` other than `operator<`
-template <typename RandomAccessContainer, typename Compare>
-bool c_is_heap(const RandomAccessContainer& sequence, Compare&& comp) {
+template <typename RandomAccessContainer, typename LessThan>
+bool c_is_heap(const RandomAccessContainer& sequence, LessThan&& comp) {
   return std::is_heap(container_algorithm_internal::c_begin(sequence),
                       container_algorithm_internal::c_end(sequence),
-                      std::forward<Compare>(comp));
+                      std::forward<LessThan>(comp));
 }
 
 // c_is_heap_until()
@@ -1487,12 +1497,12 @@
 
 // Overload of c_is_heap_until() for performing heap comparisons using a
 // `comp` other than `operator<`
-template <typename RandomAccessContainer, typename Compare>
+template <typename RandomAccessContainer, typename LessThan>
 container_algorithm_internal::ContainerIter<RandomAccessContainer>
-c_is_heap_until(RandomAccessContainer& sequence, Compare&& comp) {
+c_is_heap_until(RandomAccessContainer& sequence, LessThan&& comp) {
   return std::is_heap_until(container_algorithm_internal::c_begin(sequence),
                             container_algorithm_internal::c_end(sequence),
-                            std::forward<Compare>(comp));
+                            std::forward<LessThan>(comp));
 }
 
 //------------------------------------------------------------------------------
@@ -1513,12 +1523,12 @@
 
 // Overload of c_min_element() for performing a `comp` comparison other than
 // `operator<`.
-template <typename Sequence, typename Compare>
+template <typename Sequence, typename LessThan>
 container_algorithm_internal::ContainerIter<Sequence> c_min_element(
-    Sequence& sequence, Compare&& comp) {
+    Sequence& sequence, LessThan&& comp) {
   return std::min_element(container_algorithm_internal::c_begin(sequence),
                           container_algorithm_internal::c_end(sequence),
-                          std::forward<Compare>(comp));
+                          std::forward<LessThan>(comp));
 }
 
 // c_max_element()
@@ -1535,12 +1545,12 @@
 
 // Overload of c_max_element() for performing a `comp` comparison other than
 // `operator<`.
-template <typename Sequence, typename Compare>
+template <typename Sequence, typename LessThan>
 container_algorithm_internal::ContainerIter<Sequence> c_max_element(
-    Sequence& sequence, Compare&& comp) {
+    Sequence& sequence, LessThan&& comp) {
   return std::max_element(container_algorithm_internal::c_begin(sequence),
                           container_algorithm_internal::c_end(sequence),
-                          std::forward<Compare>(comp));
+                          std::forward<LessThan>(comp));
 }
 
 // c_minmax_element()
@@ -1558,12 +1568,12 @@
 
 // Overload of c_minmax_element() for performing `comp` comparisons other than
 // `operator<`.
-template <typename C, typename Compare>
+template <typename C, typename LessThan>
 container_algorithm_internal::ContainerIterPairType<C, C>
-c_minmax_element(C& c, Compare&& comp) {
+c_minmax_element(C& c, LessThan&& comp) {
   return std::minmax_element(container_algorithm_internal::c_begin(c),
                              container_algorithm_internal::c_end(c),
-                             std::forward<Compare>(comp));
+                             std::forward<LessThan>(comp));
 }
 
 //------------------------------------------------------------------------------
@@ -1588,15 +1598,15 @@
 
 // Overload of c_lexicographical_compare() for performing a lexicographical
 // comparison using a `comp` operator instead of `operator<`.
-template <typename Sequence1, typename Sequence2, typename Compare>
+template <typename Sequence1, typename Sequence2, typename LessThan>
 bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2,
-                               Compare&& comp) {
+                               LessThan&& comp) {
   return std::lexicographical_compare(
       container_algorithm_internal::c_begin(sequence1),
       container_algorithm_internal::c_end(sequence1),
       container_algorithm_internal::c_begin(sequence2),
       container_algorithm_internal::c_end(sequence2),
-      std::forward<Compare>(comp));
+      std::forward<LessThan>(comp));
 }
 
 // c_next_permutation()
@@ -1612,11 +1622,11 @@
 
 // Overload of c_next_permutation() for performing a lexicographical
 // comparison using a `comp` operator instead of `operator<`.
-template <typename C, typename Compare>
-bool c_next_permutation(C& c, Compare&& comp) {
+template <typename C, typename LessThan>
+bool c_next_permutation(C& c, LessThan&& comp) {
   return std::next_permutation(container_algorithm_internal::c_begin(c),
                                container_algorithm_internal::c_end(c),
-                               std::forward<Compare>(comp));
+                               std::forward<LessThan>(comp));
 }
 
 // c_prev_permutation()
@@ -1632,11 +1642,11 @@
 
 // Overload of c_prev_permutation() for performing a lexicographical
 // comparison using a `comp` operator instead of `operator<`.
-template <typename C, typename Compare>
-bool c_prev_permutation(C& c, Compare&& comp) {
+template <typename C, typename LessThan>
+bool c_prev_permutation(C& c, LessThan&& comp) {
   return std::prev_permutation(container_algorithm_internal::c_begin(c),
                                container_algorithm_internal::c_end(c),
-                               std::forward<Compare>(comp));
+                               std::forward<LessThan>(comp));
 }
 
 //------------------------------------------------------------------------------
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index 65ff0dd..4769efd 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -14,7 +14,6 @@
 # limitations under the License.
 #
 
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
@@ -594,75 +593,6 @@
 )
 
 cc_library(
-    name = "exponential_biased",
-    srcs = ["internal/exponential_biased.cc"],
-    hdrs = ["internal/exponential_biased.h"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
-    deps = [
-        ":config",
-        ":core_headers",
-    ],
-)
-
-cc_test(
-    name = "exponential_biased_test",
-    size = "small",
-    srcs = ["internal/exponential_biased_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":exponential_biased",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "periodic_sampler",
-    srcs = ["internal/periodic_sampler.cc"],
-    hdrs = ["internal/periodic_sampler.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":core_headers",
-        ":exponential_biased",
-    ],
-)
-
-cc_test(
-    name = "periodic_sampler_test",
-    size = "small",
-    srcs = ["internal/periodic_sampler_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":core_headers",
-        ":periodic_sampler",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_binary(
-    name = "periodic_sampler_benchmark",
-    testonly = 1,
-    srcs = ["internal/periodic_sampler_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":core_headers",
-        ":periodic_sampler",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_library(
     name = "scoped_set_env",
     testonly = 1,
     srcs = ["internal/scoped_set_env.cc"],
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index 981b8cc..c7233cb 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -230,7 +230,7 @@
     ${ABSL_DEFAULT_COPTS}
   DEPS
     absl::config
-    gtest
+    GTest::gtest
   TESTONLY
 )
 
@@ -259,7 +259,7 @@
     absl::meta
     absl::strings
     absl::utility
-    gtest
+    GTest::gtest
   TESTONLY
 )
 
@@ -273,7 +273,7 @@
   DEPS
     absl::exception_safety_testing
     absl::memory
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_library(
@@ -300,8 +300,8 @@
     absl::atomic_hook_test_helper
     absl::atomic_hook
     absl::core_headers
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -314,7 +314,7 @@
   DEPS
     absl::base
     absl::core_headers
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -327,8 +327,8 @@
   DEPS
     absl::errno_saver
     absl::strerror
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -342,7 +342,7 @@
     absl::base
     absl::config
     absl::throw_delegate
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -357,7 +357,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::base_internal
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -371,8 +371,8 @@
     absl::base_internal
     absl::memory
     absl::strings
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_library(
@@ -388,7 +388,7 @@
     absl::base_internal
     absl::core_headers
     absl::synchronization
-    gtest
+    GTest::gtest
   TESTONLY
 )
 
@@ -406,7 +406,7 @@
     absl::config
     absl::core_headers
     absl::synchronization
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_library(
@@ -435,7 +435,7 @@
     absl::base
     absl::config
     absl::endian
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -448,7 +448,7 @@
   DEPS
     absl::config
     absl::synchronization
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -462,7 +462,7 @@
     absl::base
     absl::core_headers
     absl::synchronization
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -475,7 +475,7 @@
   DEPS
     absl::raw_logging_internal
     absl::strings
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -488,7 +488,7 @@
   DEPS
     absl::base
     absl::synchronization
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -516,61 +516,7 @@
     absl::core_headers
     absl::synchronization
     Threads::Threads
-    gtest_main
-)
-
-absl_cc_library(
-  NAME
-    exponential_biased
-  SRCS
-    "internal/exponential_biased.cc"
-  HDRS
-    "internal/exponential_biased.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-)
-
-absl_cc_test(
-  NAME
-    exponential_biased_test
-  SRCS
-    "internal/exponential_biased_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::exponential_biased
-    absl::strings
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    periodic_sampler
-  SRCS
-    "internal/periodic_sampler.cc"
-  HDRS
-    "internal/periodic_sampler.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::core_headers
-    absl::exponential_biased
-)
-
-absl_cc_test(
-  NAME
-    periodic_sampler_test
-  SRCS
-    "internal/periodic_sampler_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::core_headers
-    absl::periodic_sampler
-    gmock_main
+    GTest::gtest_main
 )
 
 absl_cc_library(
@@ -596,7 +542,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::scoped_set_env
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -620,8 +566,8 @@
     absl::flags_marshalling
     absl::log_severity
     absl::strings
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_library(
@@ -651,8 +597,8 @@
   DEPS
     absl::strerror
     absl::strings
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_library(
@@ -677,7 +623,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::fast_type_id
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -690,5 +636,5 @@
   DEPS
     absl::core_headers
     absl::optional
-    gtest_main
+    GTest::gtest_main
 )
diff --git a/absl/base/attributes.h b/absl/base/attributes.h
index cf2cb55..e390782 100644
--- a/absl/base/attributes.h
+++ b/absl/base/attributes.h
@@ -131,14 +131,14 @@
 // ABSL_ATTRIBUTE_WEAK
 //
 // Tags a function as weak for the purposes of compilation and linking.
-// Weak attributes currently do not work properly in LLVM's Windows backend,
-// so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598
+// Weak attributes did not work properly in LLVM's Windows backend before
+// 9.0.0, so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598
 // for further information.
 // The MinGW compiler doesn't complain about the weak attribute until the link
 // step, presumably because Windows doesn't use ELF binaries.
 #if (ABSL_HAVE_ATTRIBUTE(weak) ||                   \
      (defined(__GNUC__) && !defined(__clang__))) && \
-    !(defined(__llvm__) && defined(_WIN32)) && !defined(__MINGW32__)
+    (!defined(_WIN32) || __clang_major__ < 9) && !defined(__MINGW32__)
 #undef ABSL_ATTRIBUTE_WEAK
 #define ABSL_ATTRIBUTE_WEAK __attribute__((weak))
 #define ABSL_HAVE_ATTRIBUTE_WEAK 1
@@ -281,10 +281,7 @@
 // ABSL_ATTRIBUTE_RETURNS_NONNULL
 //
 // Tells the compiler that a particular function never returns a null pointer.
-#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) || \
-    (defined(__GNUC__) && \
-     (__GNUC__ > 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \
-     !defined(__clang__))
+#if ABSL_HAVE_ATTRIBUTE(returns_nonnull)
 #define ABSL_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull))
 #else
 #define ABSL_ATTRIBUTE_RETURNS_NONNULL
@@ -321,8 +318,16 @@
 // `__start_ ## name` and `__stop_ ## name` symbols to bracket the section.
 // This functionality is supported by GNU linker.
 #ifndef ABSL_ATTRIBUTE_SECTION_VARIABLE
+#ifdef _AIX
+// __attribute__((section(#name))) on AIX is achived by using the `.csect` psudo
+// op which includes an additional integer as part of its syntax indcating
+// alignment. If data fall under different alignments then you might get a
+// compilation error indicating a `Section type conflict`.
+#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name)
+#else
 #define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name)))
 #endif
+#endif
 
 // ABSL_DECLARE_ATTRIBUTE_SECTION_VARS
 //
@@ -524,6 +529,13 @@
 // ABSL_ATTRIBUTE_UNUSED
 //
 // Prevents the compiler from complaining about variables that appear unused.
+//
+// For code or headers that are assured to only build with C++17 and up, prefer
+// just using the standard '[[maybe_unused]]' directly over this macro.
+//
+// Due to differences in positioning requirements between the old, compiler
+// specific __attribute__ syntax and the now standard [[maybe_unused]], this
+// macro does not attempt to take advantage of '[[maybe_unused]]'.
 #if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__))
 #undef ABSL_ATTRIBUTE_UNUSED
 #define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__))
@@ -544,13 +556,19 @@
 // ABSL_ATTRIBUTE_PACKED
 //
 // Instructs the compiler not to use natural alignment for a tagged data
-// structure, but instead to reduce its alignment to 1. This attribute can
-// either be applied to members of a structure or to a structure in its
-// entirety. Applying this attribute (judiciously) to a structure in its
-// entirety to optimize the memory footprint of very commonly-used structs is
-// fine. Do not apply this attribute to a structure in its entirety if the
-// purpose is to control the offsets of the members in the structure. Instead,
-// apply this attribute only to structure members that need it.
+// structure, but instead to reduce its alignment to 1.
+//
+// Therefore, DO NOT APPLY THIS ATTRIBUTE TO STRUCTS CONTAINING ATOMICS. Doing
+// so can cause atomic variables to be mis-aligned and silently violate
+// atomicity on x86.
+//
+// This attribute can either be applied to members of a structure or to a
+// structure in its entirety. Applying this attribute (judiciously) to a
+// structure in its entirety to optimize the memory footprint of very
+// commonly-used structs is fine. Do not apply this attribute to a structure in
+// its entirety if the purpose is to control the offsets of the members in the
+// structure. Instead, apply this attribute only to structure members that need
+// it.
 //
 // When applying ABSL_ATTRIBUTE_PACKED only to specific structure members the
 // natural alignment of structure members not annotated is preserved. Aligned
@@ -595,31 +613,24 @@
 //    case 42:
 //      ...
 //
-// Notes: when compiled with clang in C++11 mode, the ABSL_FALLTHROUGH_INTENDED
-// macro is expanded to the [[clang::fallthrough]] attribute, which is analysed
-// when  performing switch labels fall-through diagnostic
-// (`-Wimplicit-fallthrough`). See clang documentation on language extensions
-// for details:
+// Notes: When supported, GCC and Clang can issue a warning on switch labels
+// with unannotated fallthrough using the warning `-Wimplicit-fallthrough`. See
+// clang documentation on language extensions for details:
 // https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough
 //
-// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro
-// has no effect on diagnostics. In any case this macro has no effect on runtime
+// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro has
+// no effect on diagnostics. In any case this macro has no effect on runtime
 // behavior and performance of code.
 
 #ifdef ABSL_FALLTHROUGH_INTENDED
 #error "ABSL_FALLTHROUGH_INTENDED should not be defined."
-#endif
-
-// TODO(zhangxy): Use c++17 standard [[fallthrough]] macro, when supported.
-#if defined(__clang__) && defined(__has_warning)
-#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
+#elif ABSL_HAVE_CPP_ATTRIBUTE(fallthrough)
+#define ABSL_FALLTHROUGH_INTENDED [[fallthrough]]
+#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::fallthrough)
 #define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]]
-#endif
-#elif defined(__GNUC__) && __GNUC__ >= 7
+#elif ABSL_HAVE_CPP_ATTRIBUTE(gnu::fallthrough)
 #define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]]
-#endif
-
-#ifndef ABSL_FALLTHROUGH_INTENDED
+#else
 #define ABSL_FALLTHROUGH_INTENDED \
   do {                            \
   } while (0)
@@ -699,4 +710,26 @@
 #define ABSL_ATTRIBUTE_PURE_FUNCTION
 #endif
 
+// ABSL_ATTRIBUTE_LIFETIME_BOUND indicates that a resource owned by a function
+// parameter or implicit object parameter is retained by the return value of the
+// annotated function (or, for a parameter of a constructor, in the value of the
+// constructed object). This attribute causes warnings to be produced if a
+// temporary object does not live long enough.
+//
+// When applied to a reference parameter, the referenced object is assumed to be
+// retained by the return value of the function. When applied to a non-reference
+// parameter (for example, a pointer or a class type), all temporaries
+// referenced by the parameter are assumed to be retained by the return value of
+// the function.
+//
+// See also the upstream documentation:
+// https://clang.llvm.org/docs/AttributeReference.html#lifetimebound
+#if ABSL_HAVE_CPP_ATTRIBUTE(clang::lifetimebound)
+#define ABSL_ATTRIBUTE_LIFETIME_BOUND [[clang::lifetimebound]]
+#elif ABSL_HAVE_ATTRIBUTE(lifetimebound)
+#define ABSL_ATTRIBUTE_LIFETIME_BOUND __attribute__((lifetimebound))
+#else
+#define ABSL_ATTRIBUTE_LIFETIME_BOUND
+#endif
+
 #endif  // ABSL_BASE_ATTRIBUTES_H_
diff --git a/absl/base/config.h b/absl/base/config.h
index 9544996..585485c 100644
--- a/absl/base/config.h
+++ b/absl/base/config.h
@@ -66,6 +66,35 @@
 #include "absl/base/options.h"
 #include "absl/base/policy_checks.h"
 
+// Abseil long-term support (LTS) releases will define
+// `ABSL_LTS_RELEASE_VERSION` to the integer representing the date string of the
+// LTS release version, and will define `ABSL_LTS_RELEASE_PATCH_LEVEL` to the
+// integer representing the patch-level for that release.
+//
+// For example, for LTS release version "20300401.2", this would give us
+// ABSL_LTS_RELEASE_VERSION == 20300401 && ABSL_LTS_RELEASE_PATCH_LEVEL == 2
+//
+// These symbols will not be defined in non-LTS code.
+//
+// Abseil recommends that clients live-at-head. Therefore, if you are using
+// these symbols to assert a minimum version requirement, we recommend you do it
+// as
+//
+// #if defined(ABSL_LTS_RELEASE_VERSION) && ABSL_LTS_RELEASE_VERSION < 20300401
+// #error Project foo requires Abseil LTS version >= 20300401
+// #endif
+//
+// The `defined(ABSL_LTS_RELEASE_VERSION)` part of the check excludes
+// live-at-head clients from the minimum version assertion.
+//
+// See https://abseil.io/about/releases for more information on Abseil release
+// management.
+//
+// LTS releases can be obtained from
+// https://github.com/abseil/abseil-cpp/releases.
+#define ABSL_LTS_RELEASE_VERSION 20211102
+#define ABSL_LTS_RELEASE_PATCH_LEVEL 0
+
 // Helper macro to convert a CPP variable to a string literal.
 #define ABSL_INTERNAL_DO_TOKEN_STR(x) #x
 #define ABSL_INTERNAL_TOKEN_STR(x) ABSL_INTERNAL_DO_TOKEN_STR(x)
@@ -166,6 +195,22 @@
 #define ABSL_HAVE_FEATURE(f) 0
 #endif
 
+// Portable check for GCC minimum version:
+// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
+#if defined(__GNUC__) && defined(__GNUC_MINOR__)
+#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) \
+  (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y))
+#else
+#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) 0
+#endif
+
+#if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__)
+#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) \
+  (__clang_major__ > (x) || __clang_major__ == (x) && __clang_minor__ >= (y))
+#else
+#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) 0
+#endif
+
 // ABSL_HAVE_TLS is defined to 1 when __thread should be supported.
 // We assume __thread is supported on Linux when compiled with Clang or compiled
 // against libstdc++ with _GLIBCXX_HAVE_TLS defined.
@@ -183,10 +228,9 @@
 // gcc >= 4.8.1 using libstdc++, and Visual Studio.
 #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
 #error ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set
-#elif defined(_LIBCPP_VERSION) ||                                        \
-    (!defined(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && \
-     (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) ||        \
-    defined(_MSC_VER)
+#elif defined(_LIBCPP_VERSION) || defined(_MSC_VER) || \
+    (!defined(__clang__) && defined(__GLIBCXX__) &&    \
+     ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(4, 8))
 #define ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1
 #endif
 
@@ -199,16 +243,17 @@
 //
 // Checks whether `std::is_trivially_copy_assignable<T>` is supported.
 
-// Notes: Clang with libc++ supports these features, as does gcc >= 5.1 with
-// either libc++ or libstdc++, and Visual Studio (but not NVCC).
+// Notes: Clang with libc++ supports these features, as does gcc >= 7.4 with
+// libstdc++, or gcc >= 8.2 with libc++, and Visual Studio (but not NVCC).
 #if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE)
 #error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set
 #elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE)
 #error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set
-#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) ||        \
-    (!defined(__clang__) && defined(__GNUC__) &&                 \
-     (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 4)) && \
-     (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) ||      \
+#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) ||                    \
+    (!defined(__clang__) &&                                                  \
+     ((ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(7, 4) && defined(__GLIBCXX__)) || \
+      (ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(8, 2) &&                          \
+       defined(_LIBCPP_VERSION)))) ||                                        \
     (defined(_MSC_VER) && !defined(__NVCC__))
 #define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1
 #define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1
@@ -222,7 +267,7 @@
 #if ABSL_INTERNAL_HAS_KEYWORD(__builtin_LINE) && \
     ABSL_INTERNAL_HAS_KEYWORD(__builtin_FILE)
 #define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1
-#elif defined(__GNUC__) && __GNUC__ >= 5
+#elif ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 0)
 #define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1
 #endif
 #endif
@@ -319,25 +364,21 @@
 // For further details, consult the compiler's documentation.
 #ifdef ABSL_HAVE_EXCEPTIONS
 #error ABSL_HAVE_EXCEPTIONS cannot be directly set.
-
-#elif defined(__clang__)
-
-#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6)
+#elif ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(3, 6)
 // Clang >= 3.6
 #if ABSL_HAVE_FEATURE(cxx_exceptions)
 #define ABSL_HAVE_EXCEPTIONS 1
 #endif  // ABSL_HAVE_FEATURE(cxx_exceptions)
-#else
+#elif defined(__clang__)
 // Clang < 3.6
 // http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro
 #if defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions)
 #define ABSL_HAVE_EXCEPTIONS 1
 #endif  // defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions)
-#endif  // __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6)
-
 // Handle remaining special cases and default to exceptions being supported.
-#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) &&    \
-    !(defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__cpp_exceptions)) && \
+#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \
+    !(ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 0) &&                        \
+      !defined(__cpp_exceptions)) &&                                      \
     !(defined(_MSC_VER) && !defined(_CPPUNWIND))
 #define ABSL_HAVE_EXCEPTIONS 1
 #endif
@@ -369,10 +410,11 @@
 // POSIX.1-2001.
 #ifdef ABSL_HAVE_MMAP
 #error ABSL_HAVE_MMAP cannot be directly set
-#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) ||   \
-    defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \
-    defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \
-    defined(__ASYLO__) || defined(__myriad2__)
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
+    defined(_AIX) || defined(__ros__) || defined(__native_client__) ||    \
+    defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__) ||    \
+    defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) ||       \
+    defined(__HAIKU__)
 #define ABSL_HAVE_MMAP 1
 #endif
 
@@ -383,7 +425,7 @@
 #ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM
 #error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set
 #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
-    defined(__ros__)
+    defined(_AIX) || defined(__ros__)
 #define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1
 #endif
 
@@ -690,10 +732,6 @@
 // a compiler instrumentation module and a run-time library.
 #ifdef ABSL_HAVE_MEMORY_SANITIZER
 #error "ABSL_HAVE_MEMORY_SANITIZER cannot be directly set."
-#elif defined(MEMORY_SANITIZER)
-// The MEMORY_SANITIZER macro is deprecated but we will continue to honor it
-// for now.
-#define ABSL_HAVE_MEMORY_SANITIZER 1
 #elif defined(__SANITIZE_MEMORY__)
 #define ABSL_HAVE_MEMORY_SANITIZER 1
 #elif !defined(__native_client__) && ABSL_HAVE_FEATURE(memory_sanitizer)
@@ -705,10 +743,6 @@
 // ThreadSanitizer (TSan) is a fast data race detector.
 #ifdef ABSL_HAVE_THREAD_SANITIZER
 #error "ABSL_HAVE_THREAD_SANITIZER cannot be directly set."
-#elif defined(THREAD_SANITIZER)
-// The THREAD_SANITIZER macro is deprecated but we will continue to honor it
-// for now.
-#define ABSL_HAVE_THREAD_SANITIZER 1
 #elif defined(__SANITIZE_THREAD__)
 #define ABSL_HAVE_THREAD_SANITIZER 1
 #elif ABSL_HAVE_FEATURE(thread_sanitizer)
@@ -720,10 +754,6 @@
 // AddressSanitizer (ASan) is a fast memory error detector.
 #ifdef ABSL_HAVE_ADDRESS_SANITIZER
 #error "ABSL_HAVE_ADDRESS_SANITIZER cannot be directly set."
-#elif defined(ADDRESS_SANITIZER)
-// The ADDRESS_SANITIZER macro is deprecated but we will continue to honor it
-// for now.
-#define ABSL_HAVE_ADDRESS_SANITIZER 1
 #elif defined(__SANITIZE_ADDRESS__)
 #define ABSL_HAVE_ADDRESS_SANITIZER 1
 #elif ABSL_HAVE_FEATURE(address_sanitizer)
diff --git a/absl/base/dynamic_annotations.h b/absl/base/dynamic_annotations.h
index 880cbf6..3ea7c15 100644
--- a/absl/base/dynamic_annotations.h
+++ b/absl/base/dynamic_annotations.h
@@ -433,31 +433,6 @@
 
 #endif
 
-#ifdef __cplusplus
-#ifdef ABSL_HAVE_THREAD_SANITIZER
-ABSL_INTERNAL_BEGIN_EXTERN_C
-int RunningOnValgrind();
-double ValgrindSlowdown();
-ABSL_INTERNAL_END_EXTERN_C
-#else
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-ABSL_DEPRECATED(
-    "Don't use this interface. It is misleading and is being deleted.")
-ABSL_ATTRIBUTE_ALWAYS_INLINE inline int RunningOnValgrind() { return 0; }
-ABSL_DEPRECATED(
-    "Don't use this interface. It is misleading and is being deleted.")
-ABSL_ATTRIBUTE_ALWAYS_INLINE inline double ValgrindSlowdown() { return 1.0; }
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-using absl::base_internal::RunningOnValgrind;
-using absl::base_internal::ValgrindSlowdown;
-#endif
-#endif
-
 // -------------------------------------------------------------------------
 // Address sanitizer annotations
 
@@ -471,7 +446,7 @@
   __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid)
 #define ABSL_ADDRESS_SANITIZER_REDZONE(name) \
   struct {                                   \
-    char x[8] __attribute__((aligned(8)));   \
+    alignas(8) char x[8];                    \
   } name
 
 #else
diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h
index 6ba89d0..77a5aec 100644
--- a/absl/base/internal/exception_safety_testing.h
+++ b/absl/base/internal/exception_safety_testing.h
@@ -536,7 +536,22 @@
   }
 
   // Memory management operators
-  // Args.. allows us to overload regular and placement new in one shot
+  static void* operator new(size_t s) noexcept(
+      IsSpecified(TypeSpec::kNoThrowNew)) {
+    if (!IsSpecified(TypeSpec::kNoThrowNew)) {
+      exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true);
+    }
+    return ::operator new(s);
+  }
+
+  static void* operator new[](size_t s) noexcept(
+      IsSpecified(TypeSpec::kNoThrowNew)) {
+    if (!IsSpecified(TypeSpec::kNoThrowNew)) {
+      exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true);
+    }
+    return ::operator new[](s);
+  }
+
   template <typename... Args>
   static void* operator new(size_t s, Args&&... args) noexcept(
       IsSpecified(TypeSpec::kNoThrowNew)) {
@@ -557,12 +572,6 @@
 
   // Abseil doesn't support throwing overloaded operator delete.  These are
   // provided so a throwing operator-new can clean up after itself.
-  //
-  // We provide both regular and templated operator delete because if only the
-  // templated version is provided as we did with operator new, the compiler has
-  // no way of knowing which overload of operator delete to call. See
-  // https://en.cppreference.com/w/cpp/memory/new/operator_delete and
-  // https://en.cppreference.com/w/cpp/language/delete for the gory details.
   void operator delete(void* p) noexcept { ::operator delete(p); }
 
   template <typename... Args>
@@ -726,9 +735,8 @@
 
   ThrowingAllocator select_on_container_copy_construction() noexcept(
       IsSpecified(AllocSpec::kNoThrowAllocate)) {
-    auto& out = *this;
     ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
-    return out;
+    return *this;
   }
 
   template <typename U>
diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h
index c73b5e0..ac40daf 100644
--- a/absl/base/internal/spinlock.h
+++ b/absl/base/internal/spinlock.h
@@ -16,13 +16,15 @@
 
 //  Most users requiring mutual exclusion should use Mutex.
 //  SpinLock is provided for use in two situations:
-//   - for use in code that Mutex itself depends on
+//   - for use by Abseil internal code that Mutex itself depends on
 //   - for async signal safety (see below)
 
 // SpinLock is async signal safe.  If a spinlock is used within a signal
 // handler, all code that acquires the lock must ensure that the signal cannot
 // arrive while they are holding the lock.  Typically, this is done by blocking
 // the signal.
+//
+// Threads waiting on a SpinLock may be woken in an arbitrary order.
 
 #ifndef ABSL_BASE_INTERNAL_SPINLOCK_H_
 #define ABSL_BASE_INTERNAL_SPINLOCK_H_
diff --git a/absl/base/internal/spinlock_wait.h b/absl/base/internal/spinlock_wait.h
index 579bd09..9a1adcd 100644
--- a/absl/base/internal/spinlock_wait.h
+++ b/absl/base/internal/spinlock_wait.h
@@ -39,6 +39,8 @@
 // satisfying 0<=i<n && trans[i].done, atomically make the transition,
 // then return the old value of *w.   Make any other atomic transitions
 // where !trans[i].done, but continue waiting.
+//
+// Wakeups for threads blocked on SpinLockWait do not respect priorities.
 uint32_t SpinLockWait(std::atomic<uint32_t> *w, int n,
                       const SpinLockWaitTransition trans[],
                       SchedulingMode scheduling_mode);
diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc
index 4a3b205..a7cfb46 100644
--- a/absl/base/internal/sysinfo.cc
+++ b/absl/base/internal/sysinfo.cc
@@ -61,9 +61,78 @@
 ABSL_NAMESPACE_BEGIN
 namespace base_internal {
 
+namespace {
+
+#if defined(_WIN32)
+
+// Returns number of bits set in `bitMask`
+DWORD Win32CountSetBits(ULONG_PTR bitMask) {
+  for (DWORD bitSetCount = 0; ; ++bitSetCount) {
+    if (bitMask == 0) return bitSetCount;
+    bitMask &= bitMask - 1;
+  }
+}
+
+// Returns the number of logical CPUs using GetLogicalProcessorInformation(), or
+// 0 if the number of processors is not available or can not be computed.
+// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformation
+int Win32NumCPUs() {
+#pragma comment(lib, "kernel32.lib")
+  using Info = SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
+
+  DWORD info_size = sizeof(Info);
+  Info* info(static_cast<Info*>(malloc(info_size)));
+  if (info == nullptr) return 0;
+
+  bool success = GetLogicalProcessorInformation(info, &info_size);
+  if (!success && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+    free(info);
+    info = static_cast<Info*>(malloc(info_size));
+    if (info == nullptr) return 0;
+    success = GetLogicalProcessorInformation(info, &info_size);
+  }
+
+  DWORD logicalProcessorCount = 0;
+  if (success) {
+    Info* ptr = info;
+    DWORD byteOffset = 0;
+    while (byteOffset + sizeof(Info) <= info_size) {
+      switch (ptr->Relationship) {
+        case RelationProcessorCore:
+          logicalProcessorCount += Win32CountSetBits(ptr->ProcessorMask);
+          break;
+
+        case RelationNumaNode:
+        case RelationCache:
+        case RelationProcessorPackage:
+          // Ignore other entries
+          break;
+
+        default:
+          // Ignore unknown entries
+          break;
+      }
+      byteOffset += sizeof(Info);
+      ptr++;
+    }
+  }
+  free(info);
+  return logicalProcessorCount;
+}
+
+#endif
+
+}  // namespace
+
+
 static int GetNumCPUs() {
 #if defined(__myriad2__)
   return 1;
+#elif defined(_WIN32)
+  const unsigned hardware_concurrency = Win32NumCPUs();
+  return hardware_concurrency ? hardware_concurrency : 1;
+#elif defined(_AIX)
+  return sysconf(_SC_NPROCESSORS_ONLN);
 #else
   // Other possibilities:
   //  - Read /sys/devices/system/cpu/online and use cpumask_parse()
diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h
index 6e25b92..659694b 100644
--- a/absl/base/internal/thread_identity.h
+++ b/absl/base/internal/thread_identity.h
@@ -188,25 +188,25 @@
 // May be chosen at compile time via: -DABSL_FORCE_THREAD_IDENTITY_MODE=<mode
 // index>
 #ifdef ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC
-#error ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC cannot be direcly set
+#error ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC cannot be directly set
 #else
 #define ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC 0
 #endif
 
 #ifdef ABSL_THREAD_IDENTITY_MODE_USE_TLS
-#error ABSL_THREAD_IDENTITY_MODE_USE_TLS cannot be direcly set
+#error ABSL_THREAD_IDENTITY_MODE_USE_TLS cannot be directly set
 #else
 #define ABSL_THREAD_IDENTITY_MODE_USE_TLS 1
 #endif
 
 #ifdef ABSL_THREAD_IDENTITY_MODE_USE_CPP11
-#error ABSL_THREAD_IDENTITY_MODE_USE_CPP11 cannot be direcly set
+#error ABSL_THREAD_IDENTITY_MODE_USE_CPP11 cannot be directly set
 #else
 #define ABSL_THREAD_IDENTITY_MODE_USE_CPP11 2
 #endif
 
 #ifdef ABSL_THREAD_IDENTITY_MODE
-#error ABSL_THREAD_IDENTITY_MODE cannot be direcly set
+#error ABSL_THREAD_IDENTITY_MODE cannot be directly set
 #elif defined(ABSL_FORCE_THREAD_IDENTITY_MODE)
 #define ABSL_THREAD_IDENTITY_MODE ABSL_FORCE_THREAD_IDENTITY_MODE
 #elif defined(_WIN32) && !defined(__MINGW32__)
diff --git a/absl/base/internal/unscaledcycleclock.cc b/absl/base/internal/unscaledcycleclock.cc
index 1545288..4d352bd 100644
--- a/absl/base/internal/unscaledcycleclock.cc
+++ b/absl/base/internal/unscaledcycleclock.cc
@@ -87,6 +87,10 @@
 double UnscaledCycleClock::Frequency() {
 #ifdef __GLIBC__
   return __ppc_get_timebase_freq();
+#elif defined(_AIX)
+  // This is the same constant value as returned by
+  // __ppc_get_timebase_freq().
+  return static_cast<double>(512000000);
 #elif defined(__FreeBSD__)
   static once_flag init_timebase_frequency_once;
   static double timebase_frequency = 0.0;
@@ -119,6 +123,18 @@
   return aarch64_timer_frequency;
 }
 
+#elif defined(__riscv)
+
+int64_t UnscaledCycleClock::Now() {
+  int64_t virtual_timer_value;
+  asm volatile("rdcycle %0" : "=r"(virtual_timer_value));
+  return virtual_timer_value;
+}
+
+double UnscaledCycleClock::Frequency() {
+  return base_internal::NominalCPUFrequency();
+}
+
 #elif defined(_M_IX86) || defined(_M_X64)
 
 #pragma intrinsic(__rdtsc)
diff --git a/absl/base/internal/unscaledcycleclock.h b/absl/base/internal/unscaledcycleclock.h
index 82f2c87..681ff8f 100644
--- a/absl/base/internal/unscaledcycleclock.h
+++ b/absl/base/internal/unscaledcycleclock.h
@@ -46,8 +46,8 @@
 
 // The following platforms have an implementation of a hardware counter.
 #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \
-  defined(__powerpc__) || defined(__ppc__) || \
-  defined(_M_IX86) || defined(_M_X64)
+    defined(__powerpc__) || defined(__ppc__) || defined(__riscv) ||     \
+    defined(_M_IX86) || defined(_M_X64)
 #define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 1
 #else
 #define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 0
@@ -80,8 +80,8 @@
 
 // This macro can be used to test if UnscaledCycleClock::Frequency()
 // is NominalCPUFrequency() on a particular platform.
-#if  (defined(__i386__) || defined(__x86_64__) || \
-      defined(_M_IX86) || defined(_M_X64))
+#if (defined(__i386__) || defined(__x86_64__) || defined(__riscv) || \
+     defined(_M_IX86) || defined(_M_X64))
 #define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
 #endif
 
diff --git a/absl/base/log_severity_test.cc b/absl/base/log_severity_test.cc
index 2c6872b..55b26d1 100644
--- a/absl/base/log_severity_test.cc
+++ b/absl/base/log_severity_test.cc
@@ -52,9 +52,9 @@
               Eq("absl::LogSeverity(4)"));
 }
 
-static_assert(
-    absl::flags_internal::FlagUseOneWordStorage<absl::LogSeverity>::value,
-    "Flags of type absl::LogSeverity ought to be lock-free.");
+static_assert(absl::flags_internal::FlagUseValueAndInitBitStorage<
+                  absl::LogSeverity>::value,
+              "Flags of type absl::LogSeverity ought to be lock-free.");
 
 using ParseFlagFromOutOfRangeIntegerTest = TestWithParam<int64_t>;
 INSTANTIATE_TEST_SUITE_P(
diff --git a/absl/base/options.h b/absl/base/options.h
index eca879a..56b4e36 100644
--- a/absl/base/options.h
+++ b/absl/base/options.h
@@ -206,7 +206,7 @@
 // allowed.
 
 #define ABSL_OPTION_USE_INLINE_NAMESPACE 1
-#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20210324
+#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20211102
 
 // ABSL_OPTION_HARDENED
 //
diff --git a/absl/cleanup/BUILD.bazel b/absl/cleanup/BUILD.bazel
index 5cca898..2154d9f 100644
--- a/absl/cleanup/BUILD.bazel
+++ b/absl/cleanup/BUILD.bazel
@@ -12,7 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
diff --git a/absl/cleanup/CMakeLists.txt b/absl/cleanup/CMakeLists.txt
index a2dd78a..26a6d0d 100644
--- a/absl/cleanup/CMakeLists.txt
+++ b/absl/cleanup/CMakeLists.txt
@@ -51,5 +51,5 @@
     absl::cleanup
     absl::config
     absl::utility
-    gmock_main
+    GTest::gmock_main
 )
diff --git a/absl/cleanup/cleanup.h b/absl/cleanup/cleanup.h
index 61b53d5..960ccd0 100644
--- a/absl/cleanup/cleanup.h
+++ b/absl/cleanup/cleanup.h
@@ -86,25 +86,25 @@
                 "Callbacks that return values are not supported.");
 
  public:
-  Cleanup(Callback callback)  // NOLINT
-      : storage_(std::move(callback), /* is_callback_engaged = */ true) {}
+  Cleanup(Callback callback) : storage_(std::move(callback)) {}  // NOLINT
 
   Cleanup(Cleanup&& other) = default;
 
   void Cancel() && {
     ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged());
-    storage_.DisengageCallback();
+    storage_.DestroyCallback();
   }
 
   void Invoke() && {
     ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged());
-    storage_.DisengageCallback();
     storage_.InvokeCallback();
+    storage_.DestroyCallback();
   }
 
   ~Cleanup() {
     if (storage_.IsCallbackEngaged()) {
       storage_.InvokeCallback();
+      storage_.DestroyCallback();
     }
   }
 
diff --git a/absl/cleanup/cleanup_test.cc b/absl/cleanup/cleanup_test.cc
index 792595d..46b8858 100644
--- a/absl/cleanup/cleanup_test.cc
+++ b/absl/cleanup/cleanup_test.cc
@@ -264,4 +264,48 @@
   EXPECT_FALSE(called);  // Destructor shouldn't invoke the callback
 }
 
+int DestructionCount = 0;
+
+struct DestructionCounter {
+  void operator()() {}
+
+  ~DestructionCounter() { ++DestructionCount; }
+};
+
+TYPED_TEST(CleanupTest, DestructorDestroys) {
+  {
+    auto cleanup =
+        absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter()));
+    DestructionCount = 0;
+  }
+
+  EXPECT_EQ(DestructionCount, 1);  // Engaged cleanup destroys
+}
+
+TYPED_TEST(CleanupTest, CancelDestroys) {
+  {
+    auto cleanup =
+        absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter()));
+    DestructionCount = 0;
+
+    std::move(cleanup).Cancel();
+    EXPECT_EQ(DestructionCount, 1);  // Cancel destroys
+  }
+
+  EXPECT_EQ(DestructionCount, 1);  // Canceled cleanup does not double destroy
+}
+
+TYPED_TEST(CleanupTest, InvokeDestroys) {
+  {
+    auto cleanup =
+        absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter()));
+    DestructionCount = 0;
+
+    std::move(cleanup).Invoke();
+    EXPECT_EQ(DestructionCount, 1);  // Invoke destroys
+  }
+
+  EXPECT_EQ(DestructionCount, 1);  // Invoked cleanup does not double destroy
+}
+
 }  // namespace
diff --git a/absl/cleanup/internal/cleanup.h b/absl/cleanup/internal/cleanup.h
index b4c4073..2783fcb 100644
--- a/absl/cleanup/internal/cleanup.h
+++ b/absl/cleanup/internal/cleanup.h
@@ -15,10 +15,12 @@
 #ifndef ABSL_CLEANUP_INTERNAL_CLEANUP_H_
 #define ABSL_CLEANUP_INTERNAL_CLEANUP_H_
 
+#include <new>
 #include <type_traits>
 #include <utility>
 
 #include "absl/base/internal/invoke.h"
+#include "absl/base/macros.h"
 #include "absl/base/thread_annotations.h"
 #include "absl/utility/utility.h"
 
@@ -45,14 +47,22 @@
  public:
   Storage() = delete;
 
-  Storage(Callback callback, bool is_callback_engaged)
-      : callback_(std::move(callback)),
-        is_callback_engaged_(is_callback_engaged) {}
+  explicit Storage(Callback callback) {
+    // Placement-new into a character buffer is used for eager destruction when
+    // the cleanup is invoked or cancelled. To ensure this optimizes well, the
+    // behavior is implemented locally instead of using an absl::optional.
+    ::new (GetCallbackBuffer()) Callback(std::move(callback));
+    is_callback_engaged_ = true;
+  }
 
-  Storage(Storage&& other)
-      : callback_(std::move(other.callback_)),
-        is_callback_engaged_(
-            absl::exchange(other.is_callback_engaged_, false)) {}
+  Storage(Storage&& other) {
+    ABSL_HARDENING_ASSERT(other.IsCallbackEngaged());
+
+    ::new (GetCallbackBuffer()) Callback(std::move(other.GetCallback()));
+    is_callback_engaged_ = true;
+
+    other.DestroyCallback();
+  }
 
   Storage(const Storage& other) = delete;
 
@@ -60,17 +70,26 @@
 
   Storage& operator=(const Storage& other) = delete;
 
+  void* GetCallbackBuffer() { return static_cast<void*>(+callback_buffer_); }
+
+  Callback& GetCallback() {
+    return *reinterpret_cast<Callback*>(GetCallbackBuffer());
+  }
+
   bool IsCallbackEngaged() const { return is_callback_engaged_; }
 
-  void DisengageCallback() { is_callback_engaged_ = false; }
+  void DestroyCallback() {
+    is_callback_engaged_ = false;
+    GetCallback().~Callback();
+  }
 
   void InvokeCallback() ABSL_NO_THREAD_SAFETY_ANALYSIS {
-    std::move(callback_)();
+    std::move(GetCallback())();
   }
 
  private:
-  Callback callback_;
   bool is_callback_engaged_;
+  alignas(Callback) char callback_buffer_[sizeof(Callback)];
 };
 
 }  // namespace cleanup_internal
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index f22fdc6..ffaee19 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -14,7 +14,6 @@
 # limitations under the License.
 #
 
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
@@ -510,9 +509,10 @@
         ":have_sse",
         "//absl/base",
         "//absl/base:core_headers",
-        "//absl/base:exponential_biased",
         "//absl/debugging:stacktrace",
         "//absl/memory",
+        "//absl/profiling:exponential_biased",
+        "//absl/profiling:sample_recorder",
         "//absl/synchronization",
         "//absl/utility",
     ],
@@ -526,6 +526,7 @@
         ":hashtablez_sampler",
         ":have_sse",
         "//absl/base:core_headers",
+        "//absl/profiling:sample_recorder",
         "//absl/synchronization",
         "//absl/synchronization:thread_pool",
         "//absl/time",
@@ -598,7 +599,6 @@
         ":hashtable_debug_hooks",
         ":hashtablez_sampler",
         ":have_sse",
-        ":layout",
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:endian",
@@ -876,6 +876,22 @@
     ],
 )
 
+cc_test(
+    name = "sample_element_size_test",
+    srcs = ["sample_element_size_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    tags = NOTEST_TAGS_NONMOBILE,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":flat_hash_map",
+        ":flat_hash_set",
+        ":node_hash_map",
+        ":node_hash_set",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
 cc_library(
     name = "btree",
     srcs = [
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt
index 2d7d0e6..78584d2 100644
--- a/absl/container/CMakeLists.txt
+++ b/absl/container/CMakeLists.txt
@@ -80,7 +80,7 @@
     absl::strings
     absl::test_instance_tracker
     absl::type_traits
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -109,7 +109,7 @@
     absl::optional
     absl::test_instance_tracker
     absl::utility
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -144,7 +144,7 @@
     absl::exception_testing
     absl::hash_testing
     absl::memory
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -158,7 +158,7 @@
     absl::fixed_array
     absl::config
     absl::exception_safety_testing
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -222,7 +222,7 @@
     absl::memory
     absl::raw_logging_internal
     absl::strings
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -236,7 +236,7 @@
     absl::inlined_vector
     absl::config
     absl::exception_safety_testing
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -262,7 +262,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::test_instance_tracker
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -297,7 +297,7 @@
     absl::unordered_map_modifiers_test
     absl::any
     absl::raw_logging_internal
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -335,7 +335,7 @@
     absl::memory
     absl::raw_logging_internal
     absl::strings
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -370,7 +370,7 @@
     absl::unordered_map_lookup_test
     absl::unordered_map_members_test
     absl::unordered_map_modifiers_test
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -404,7 +404,7 @@
     absl::unordered_set_lookup_test
     absl::unordered_set_members_test
     absl::unordered_set_modifiers_test
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -433,7 +433,7 @@
     absl::container_memory
     absl::strings
     absl::test_instance_tracker
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -465,7 +465,7 @@
     absl::hash
     absl::random_random
     absl::strings
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -507,7 +507,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::hash_policy_testing
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -531,7 +531,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::hash_policy_traits
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -548,6 +548,7 @@
     absl::base
     absl::exponential_biased
     absl::have_sse
+    absl::sample_recorder
     absl::synchronization
 )
 
@@ -561,7 +562,7 @@
   DEPS
     absl::hashtablez_sampler
     absl::have_sse
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -618,7 +619,7 @@
   DEPS
     absl::hash_policy_traits
     absl::node_hash_policy
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -666,7 +667,6 @@
     absl::hash_policy_traits
     absl::hashtable_debug_hooks
     absl::have_sse
-    absl::layout
     absl::memory
     absl::meta
     absl::optional
@@ -693,7 +693,7 @@
     absl::core_headers
     absl::raw_logging_internal
     absl::strings
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -707,7 +707,7 @@
     absl::raw_hash_set
     absl::tracked
     absl::core_headers
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -740,7 +740,7 @@
     absl::core_headers
     absl::raw_logging_internal
     absl::span
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -765,7 +765,7 @@
   DEPS
     absl::hash_generator_testing
     absl::hash_policy_testing
-    gmock
+    GTest::gmock
   TESTONLY
 )
 
@@ -779,7 +779,7 @@
   DEPS
     absl::hash_generator_testing
     absl::hash_policy_testing
-    gmock
+    GTest::gmock
   TESTONLY
 )
 
@@ -792,7 +792,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::type_traits
-    gmock
+    GTest::gmock
   TESTONLY
 )
 
@@ -806,7 +806,7 @@
   DEPS
     absl::hash_generator_testing
     absl::hash_policy_testing
-    gmock
+    GTest::gmock
   TESTONLY
 )
 
@@ -820,7 +820,7 @@
   DEPS
     absl::hash_generator_testing
     absl::hash_policy_testing
-    gmock
+    GTest::gmock
   TESTONLY
 )
 
@@ -834,7 +834,7 @@
   DEPS
     absl::hash_generator_testing
     absl::hash_policy_testing
-    gmock
+    GTest::gmock
   TESTONLY
 )
 
@@ -847,7 +847,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::type_traits
-    gmock
+    GTest::gmock
   TESTONLY
 )
 
@@ -861,7 +861,7 @@
   DEPS
     absl::hash_generator_testing
     absl::hash_policy_testing
-    gmock
+    GTest::gmock
   TESTONLY
 )
 
@@ -877,7 +877,7 @@
     absl::unordered_set_lookup_test
     absl::unordered_set_members_test
     absl::unordered_set_modifiers_test
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -892,5 +892,20 @@
     absl::unordered_map_lookup_test
     absl::unordered_map_members_test
     absl::unordered_map_modifiers_test
-    gmock_main
+    GTest::gmock_main
+)
+
+absl_cc_test(
+  NAME
+    sample_element_size_test
+  SRCS
+    "sample_element_size_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::flat_hash_map
+    absl::flat_hash_set
+    absl::node_hash_map
+    absl::node_hash_set
+    GTest::gmock_main
 )
diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h
index ea49d44..f0a8d4a 100644
--- a/absl/container/btree_map.h
+++ b/absl/container/btree_map.h
@@ -366,8 +366,8 @@
   // Determines whether an element comparing equal to the given `key` exists
   // within the `btree_map`, returning `true` if so or `false` otherwise.
   //
-  // Supports heterogeneous lookup, provided that the map is provided a
-  // compatible heterogeneous comparator.
+  // Supports heterogeneous lookup, provided that the map has a compatible
+  // heterogeneous comparator.
   using Base::contains;
 
   // btree_map::count()
@@ -378,8 +378,8 @@
   // the `btree_map`. Note that this function will return either `1` or `0`
   // since duplicate elements are not allowed within a `btree_map`.
   //
-  // Supports heterogeneous lookup, provided that the map is provided a
-  // compatible heterogeneous comparator.
+  // Supports heterogeneous lookup, provided that the map has a compatible
+  // heterogeneous comparator.
   using Base::count;
 
   // btree_map::equal_range()
@@ -395,10 +395,34 @@
   //
   // Finds an element with the passed `key` within the `btree_map`.
   //
-  // Supports heterogeneous lookup, provided that the map is provided a
-  // compatible heterogeneous comparator.
+  // Supports heterogeneous lookup, provided that the map has a compatible
+  // heterogeneous comparator.
   using Base::find;
 
+  // btree_map::lower_bound()
+  //
+  // template <typename K> iterator lower_bound(const K& key):
+  // template <typename K> const_iterator lower_bound(const K& key) const:
+  //
+  // Finds the first element with a key that is not less than `key` within the
+  // `btree_map`.
+  //
+  // Supports heterogeneous lookup, provided that the map has a compatible
+  // heterogeneous comparator.
+  using Base::lower_bound;
+
+  // btree_map::upper_bound()
+  //
+  // template <typename K> iterator upper_bound(const K& key):
+  // template <typename K> const_iterator upper_bound(const K& key) const:
+  //
+  // Finds the first element with a key that is greater than `key` within the
+  // `btree_map`.
+  //
+  // Supports heterogeneous lookup, provided that the map has a compatible
+  // heterogeneous comparator.
+  using Base::upper_bound;
+
   // btree_map::operator[]()
   //
   // Returns a reference to the value mapped to the passed key within the
@@ -669,9 +693,8 @@
 
   // btree_multimap::merge()
   //
-  // Extracts elements from a given `source` btree_multimap into this
-  // `btree_multimap`. If the destination `btree_multimap` already contains an
-  // element with an equivalent key, that element is not extracted.
+  // Extracts all elements from a given `source` btree_multimap into this
+  // `btree_multimap`.
   using Base::merge;
 
   // btree_multimap::swap(btree_multimap& other)
@@ -691,8 +714,8 @@
   // Determines whether an element comparing equal to the given `key` exists
   // within the `btree_multimap`, returning `true` if so or `false` otherwise.
   //
-  // Supports heterogeneous lookup, provided that the map is provided a
-  // compatible heterogeneous comparator.
+  // Supports heterogeneous lookup, provided that the map has a compatible
+  // heterogeneous comparator.
   using Base::contains;
 
   // btree_multimap::count()
@@ -702,8 +725,8 @@
   // Returns the number of elements comparing equal to the given `key` within
   // the `btree_multimap`.
   //
-  // Supports heterogeneous lookup, provided that the map is provided a
-  // compatible heterogeneous comparator.
+  // Supports heterogeneous lookup, provided that the map has a compatible
+  // heterogeneous comparator.
   using Base::count;
 
   // btree_multimap::equal_range()
@@ -720,10 +743,34 @@
   //
   // Finds an element with the passed `key` within the `btree_multimap`.
   //
-  // Supports heterogeneous lookup, provided that the map is provided a
-  // compatible heterogeneous comparator.
+  // Supports heterogeneous lookup, provided that the map has a compatible
+  // heterogeneous comparator.
   using Base::find;
 
+  // btree_multimap::lower_bound()
+  //
+  // template <typename K> iterator lower_bound(const K& key):
+  // template <typename K> const_iterator lower_bound(const K& key) const:
+  //
+  // Finds the first element with a key that is not less than `key` within the
+  // `btree_multimap`.
+  //
+  // Supports heterogeneous lookup, provided that the map has a compatible
+  // heterogeneous comparator.
+  using Base::lower_bound;
+
+  // btree_multimap::upper_bound()
+  //
+  // template <typename K> iterator upper_bound(const K& key):
+  // template <typename K> const_iterator upper_bound(const K& key) const:
+  //
+  // Finds the first element with a key that is greater than `key` within the
+  // `btree_multimap`.
+  //
+  // Supports heterogeneous lookup, provided that the map has a compatible
+  // heterogeneous comparator.
+  using Base::upper_bound;
+
   // btree_multimap::get_allocator()
   //
   // Returns the allocator function associated with this `btree_multimap`.
diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h
index 21ef0a0..8973900 100644
--- a/absl/container/btree_set.h
+++ b/absl/container/btree_set.h
@@ -300,8 +300,8 @@
   // Determines whether an element comparing equal to the given `key` exists
   // within the `btree_set`, returning `true` if so or `false` otherwise.
   //
-  // Supports heterogeneous lookup, provided that the set is provided a
-  // compatible heterogeneous comparator.
+  // Supports heterogeneous lookup, provided that the set has a compatible
+  // heterogeneous comparator.
   using Base::contains;
 
   // btree_set::count()
@@ -312,8 +312,8 @@
   // the `btree_set`. Note that this function will return either `1` or `0`
   // since duplicate elements are not allowed within a `btree_set`.
   //
-  // Supports heterogeneous lookup, provided that the set is provided a
-  // compatible heterogeneous comparator.
+  // Supports heterogeneous lookup, provided that the set has a compatible
+  // heterogeneous comparator.
   using Base::count;
 
   // btree_set::equal_range()
@@ -330,10 +330,32 @@
   //
   // Finds an element with the passed `key` within the `btree_set`.
   //
-  // Supports heterogeneous lookup, provided that the set is provided a
-  // compatible heterogeneous comparator.
+  // Supports heterogeneous lookup, provided that the set has a compatible
+  // heterogeneous comparator.
   using Base::find;
 
+  // btree_set::lower_bound()
+  //
+  // template <typename K> iterator lower_bound(const K& key):
+  // template <typename K> const_iterator lower_bound(const K& key) const:
+  //
+  // Finds the first element that is not less than `key` within the `btree_set`.
+  //
+  // Supports heterogeneous lookup, provided that the set has a compatible
+  // heterogeneous comparator.
+  using Base::lower_bound;
+
+  // btree_set::upper_bound()
+  //
+  // template <typename K> iterator upper_bound(const K& key):
+  // template <typename K> const_iterator upper_bound(const K& key) const:
+  //
+  // Finds the first element that is greater than `key` within the `btree_set`.
+  //
+  // Supports heterogeneous lookup, provided that the set has a compatible
+  // heterogeneous comparator.
+  using Base::upper_bound;
+
   // btree_set::get_allocator()
   //
   // Returns the allocator function associated with this `btree_set`.
@@ -582,9 +604,8 @@
 
   // btree_multiset::merge()
   //
-  // Extracts elements from a given `source` btree_multiset into this
-  // `btree_multiset`. If the destination `btree_multiset` already contains an
-  // element with an equivalent key, that element is not extracted.
+  // Extracts all elements from a given `source` btree_multiset into this
+  // `btree_multiset`.
   using Base::merge;
 
   // btree_multiset::swap(btree_multiset& other)
@@ -604,8 +625,8 @@
   // Determines whether an element comparing equal to the given `key` exists
   // within the `btree_multiset`, returning `true` if so or `false` otherwise.
   //
-  // Supports heterogeneous lookup, provided that the set is provided a
-  // compatible heterogeneous comparator.
+  // Supports heterogeneous lookup, provided that the set has a compatible
+  // heterogeneous comparator.
   using Base::contains;
 
   // btree_multiset::count()
@@ -615,8 +636,8 @@
   // Returns the number of elements comparing equal to the given `key` within
   // the `btree_multiset`.
   //
-  // Supports heterogeneous lookup, provided that the set is provided a
-  // compatible heterogeneous comparator.
+  // Supports heterogeneous lookup, provided that the set has a compatible
+  // heterogeneous comparator.
   using Base::count;
 
   // btree_multiset::equal_range()
@@ -633,10 +654,34 @@
   //
   // Finds an element with the passed `key` within the `btree_multiset`.
   //
-  // Supports heterogeneous lookup, provided that the set is provided a
-  // compatible heterogeneous comparator.
+  // Supports heterogeneous lookup, provided that the set has a compatible
+  // heterogeneous comparator.
   using Base::find;
 
+  // btree_multiset::lower_bound()
+  //
+  // template <typename K> iterator lower_bound(const K& key):
+  // template <typename K> const_iterator lower_bound(const K& key) const:
+  //
+  // Finds the first element that is not less than `key` within the
+  // `btree_multiset`.
+  //
+  // Supports heterogeneous lookup, provided that the set has a compatible
+  // heterogeneous comparator.
+  using Base::lower_bound;
+
+  // btree_multiset::upper_bound()
+  //
+  // template <typename K> iterator upper_bound(const K& key):
+  // template <typename K> const_iterator upper_bound(const K& key) const:
+  //
+  // Finds the first element that is greater than `key` within the
+  // `btree_multiset`.
+  //
+  // Supports heterogeneous lookup, provided that the set has a compatible
+  // heterogeneous comparator.
+  using Base::upper_bound;
+
   // btree_multiset::get_allocator()
   //
   // Returns the allocator function associated with this `btree_multiset`.
diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc
index 74337df..d27cf27 100644
--- a/absl/container/btree_test.cc
+++ b/absl/container/btree_test.cc
@@ -595,7 +595,7 @@
   using V = typename remove_pair_const<typename T::value_type>::type;
   const std::vector<V> random_values = GenerateValuesWithSeed<V>(
       absl::GetFlag(FLAGS_test_values), 4 * absl::GetFlag(FLAGS_test_values),
-      testing::GTEST_FLAG(random_seed));
+      GTEST_FLAG_GET(random_seed));
 
   unique_checker<T, C> container;
 
@@ -619,7 +619,7 @@
   using V = typename remove_pair_const<typename T::value_type>::type;
   const std::vector<V> random_values = GenerateValuesWithSeed<V>(
       absl::GetFlag(FLAGS_test_values), 4 * absl::GetFlag(FLAGS_test_values),
-      testing::GTEST_FLAG(random_seed));
+      GTEST_FLAG_GET(random_seed));
 
   multi_checker<T, C> container;
 
@@ -1708,10 +1708,25 @@
   EXPECT_EQ(split_set, expected_set);
 }
 
-// We can't use EXPECT_EQ/etc. to compare absl::weak_ordering because they
-// convert literal 0 to int and absl::weak_ordering can only be compared with
-// literal 0. Defining this function allows for avoiding ClangTidy warnings.
-bool Identity(const bool b) { return b; }
+TEST(Btree, KeyComp) {
+  absl::btree_set<int> s;
+  EXPECT_TRUE(s.key_comp()(1, 2));
+  EXPECT_FALSE(s.key_comp()(2, 2));
+  EXPECT_FALSE(s.key_comp()(2, 1));
+
+  absl::btree_map<int, int> m1;
+  EXPECT_TRUE(m1.key_comp()(1, 2));
+  EXPECT_FALSE(m1.key_comp()(2, 2));
+  EXPECT_FALSE(m1.key_comp()(2, 1));
+
+  // Even though we internally adapt the comparator of `m2` to be three-way and
+  // heterogeneous, the comparator we expose through key_comp() is the original
+  // unadapted comparator.
+  absl::btree_map<std::string, int> m2;
+  EXPECT_TRUE(m2.key_comp()("a", "b"));
+  EXPECT_FALSE(m2.key_comp()("b", "b"));
+  EXPECT_FALSE(m2.key_comp()("b", "a"));
+}
 
 TEST(Btree, ValueComp) {
   absl::btree_set<int> s;
@@ -1724,13 +1739,13 @@
   EXPECT_FALSE(m1.value_comp()(std::make_pair(2, 0), std::make_pair(2, 0)));
   EXPECT_FALSE(m1.value_comp()(std::make_pair(2, 0), std::make_pair(1, 0)));
 
+  // Even though we internally adapt the comparator of `m2` to be three-way and
+  // heterogeneous, the comparator we expose through value_comp() is based on
+  // the original unadapted comparator.
   absl::btree_map<std::string, int> m2;
-  EXPECT_TRUE(Identity(
-      m2.value_comp()(std::make_pair("a", 0), std::make_pair("b", 0)) < 0));
-  EXPECT_TRUE(Identity(
-      m2.value_comp()(std::make_pair("b", 0), std::make_pair("b", 0)) == 0));
-  EXPECT_TRUE(Identity(
-      m2.value_comp()(std::make_pair("b", 0), std::make_pair("a", 0)) > 0));
+  EXPECT_TRUE(m2.value_comp()(std::make_pair("a", 0), std::make_pair("b", 0)));
+  EXPECT_FALSE(m2.value_comp()(std::make_pair("b", 0), std::make_pair("b", 0)));
+  EXPECT_FALSE(m2.value_comp()(std::make_pair("b", 0), std::make_pair("a", 0)));
 }
 
 TEST(Btree, DefaultConstruction) {
@@ -2893,6 +2908,46 @@
   EXPECT_EQ(bytes_used2, original_bytes_used);
 }
 
+bool IntCmp(const int a, const int b) { return a < b; }
+
+TEST(Btree, SupportsFunctionPtrComparator) {
+  absl::btree_set<int, decltype(IntCmp) *> set(IntCmp);
+  set.insert({1, 2, 3});
+  EXPECT_THAT(set, ElementsAre(1, 2, 3));
+  EXPECT_TRUE(set.key_comp()(1, 2));
+  EXPECT_TRUE(set.value_comp()(1, 2));
+
+  absl::btree_map<int, int, decltype(IntCmp) *> map(&IntCmp);
+  map[1] = 1;
+  EXPECT_THAT(map, ElementsAre(Pair(1, 1)));
+  EXPECT_TRUE(map.key_comp()(1, 2));
+  EXPECT_TRUE(map.value_comp()(std::make_pair(1, 1), std::make_pair(2, 2)));
+}
+
+template <typename Compare>
+struct TransparentPassThroughComp {
+  using is_transparent = void;
+
+  // This will fail compilation if we attempt a comparison that Compare does not
+  // support, and the failure will happen inside the function implementation so
+  // it can't be avoided by using SFINAE on this comparator.
+  template <typename T, typename U>
+  bool operator()(const T &lhs, const U &rhs) const {
+    return Compare()(lhs, rhs);
+  }
+};
+
+TEST(Btree,
+     SupportsTransparentComparatorThatDoesNotImplementAllVisibleOperators) {
+  absl::btree_set<MultiKey, TransparentPassThroughComp<MultiKeyComp>> set;
+  set.insert(MultiKey{1, 2});
+  EXPECT_TRUE(set.contains(1));
+}
+
+TEST(Btree, ConstructImplicitlyWithUnadaptedComparator) {
+  absl::btree_set<MultiKey, MultiKeyComp> set = {{}, MultiKeyComp{}};
+}
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h
index fcb3e54..839ba0b 100644
--- a/absl/container/fixed_array.h
+++ b/absl/container/fixed_array.h
@@ -73,11 +73,6 @@
 // uninitialized (e.g. int, int[4], double), and others default-constructed.
 // This matches the behavior of c-style arrays and `std::array`, but not
 // `std::vector`.
-//
-// Note that `FixedArray` does not provide a public allocator; if it requires a
-// heap allocation, it will do so with global `::operator new[]()` and
-// `::operator delete[]()`, even if T provides class-scope overrides for these
-// operators.
 template <typename T, size_t N = kFixedArrayUseDefault,
           typename A = std::allocator<T>>
 class FixedArray {
diff --git a/absl/container/flat_hash_map_test.cc b/absl/container/flat_hash_map_test.cc
index 89ec60c..8dda1d3 100644
--- a/absl/container/flat_hash_map_test.cc
+++ b/absl/container/flat_hash_map_test.cc
@@ -282,6 +282,32 @@
 }
 #endif
 
+TEST(FlatHashMap, Reserve) {
+  // Verify that if we reserve(size() + n) then we can perform n insertions
+  // without a rehash, i.e., without invalidating any references.
+  for (size_t trial = 0; trial < 20; ++trial) {
+    for (size_t initial = 3; initial < 100; ++initial) {
+      // Fill in `initial` entries, then erase 2 of them, then reserve space for
+      // two inserts and check for reference stability while doing the inserts.
+      flat_hash_map<size_t, size_t> map;
+      for (size_t i = 0; i < initial; ++i) {
+        map[i] = i;
+      }
+      map.erase(0);
+      map.erase(1);
+      map.reserve(map.size() + 2);
+      size_t& a2 = map[2];
+      // In the event of a failure, asan will complain in one of these two
+      // assignments.
+      map[initial] = a2;
+      map[initial + 1] = a2;
+      // Fail even when not under asan:
+      size_t& a2new = map[2];
+      EXPECT_EQ(&a2, &a2new);
+    }
+  }
+}
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h
index 7c18234..df9e099 100644
--- a/absl/container/inlined_vector.h
+++ b/absl/container/inlined_vector.h
@@ -72,37 +72,43 @@
 
   using Storage = inlined_vector_internal::Storage<T, N, A>;
 
-  using AllocatorTraits = typename Storage::AllocatorTraits;
-  using RValueReference = typename Storage::RValueReference;
-  using MoveIterator = typename Storage::MoveIterator;
-  using IsMemcpyOk = typename Storage::IsMemcpyOk;
+  template <typename TheA>
+  using AllocatorTraits = inlined_vector_internal::AllocatorTraits<TheA>;
+  template <typename TheA>
+  using MoveIterator = inlined_vector_internal::MoveIterator<TheA>;
+  template <typename TheA>
+  using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk<TheA>;
 
-  template <typename Iterator>
+  template <typename TheA, typename Iterator>
   using IteratorValueAdapter =
-      typename Storage::template IteratorValueAdapter<Iterator>;
-  using CopyValueAdapter = typename Storage::CopyValueAdapter;
-  using DefaultValueAdapter = typename Storage::DefaultValueAdapter;
+      inlined_vector_internal::IteratorValueAdapter<TheA, Iterator>;
+  template <typename TheA>
+  using CopyValueAdapter = inlined_vector_internal::CopyValueAdapter<TheA>;
+  template <typename TheA>
+  using DefaultValueAdapter =
+      inlined_vector_internal::DefaultValueAdapter<TheA>;
 
   template <typename Iterator>
   using EnableIfAtLeastForwardIterator = absl::enable_if_t<
-      inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value>;
+      inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
   template <typename Iterator>
   using DisableIfAtLeastForwardIterator = absl::enable_if_t<
-      !inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value>;
+      !inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
 
  public:
-  using allocator_type = typename Storage::allocator_type;
-  using value_type = typename Storage::value_type;
-  using pointer = typename Storage::pointer;
-  using const_pointer = typename Storage::const_pointer;
-  using size_type = typename Storage::size_type;
-  using difference_type = typename Storage::difference_type;
-  using reference = typename Storage::reference;
-  using const_reference = typename Storage::const_reference;
-  using iterator = typename Storage::iterator;
-  using const_iterator = typename Storage::const_iterator;
-  using reverse_iterator = typename Storage::reverse_iterator;
-  using const_reverse_iterator = typename Storage::const_reverse_iterator;
+  using allocator_type = A;
+  using value_type = inlined_vector_internal::ValueType<A>;
+  using pointer = inlined_vector_internal::Pointer<A>;
+  using const_pointer = inlined_vector_internal::ConstPointer<A>;
+  using size_type = inlined_vector_internal::SizeType<A>;
+  using difference_type = inlined_vector_internal::DifferenceType<A>;
+  using reference = inlined_vector_internal::Reference<A>;
+  using const_reference = inlined_vector_internal::ConstReference<A>;
+  using iterator = inlined_vector_internal::Iterator<A>;
+  using const_iterator = inlined_vector_internal::ConstIterator<A>;
+  using reverse_iterator = inlined_vector_internal::ReverseIterator<A>;
+  using const_reverse_iterator =
+      inlined_vector_internal::ConstReverseIterator<A>;
 
   // ---------------------------------------------------------------------------
   // InlinedVector Constructors and Destructor
@@ -111,28 +117,28 @@
   // Creates an empty inlined vector with a value-initialized allocator.
   InlinedVector() noexcept(noexcept(allocator_type())) : storage_() {}
 
-  // Creates an empty inlined vector with a copy of `alloc`.
-  explicit InlinedVector(const allocator_type& alloc) noexcept
-      : storage_(alloc) {}
+  // Creates an empty inlined vector with a copy of `allocator`.
+  explicit InlinedVector(const allocator_type& allocator) noexcept
+      : storage_(allocator) {}
 
   // Creates an inlined vector with `n` copies of `value_type()`.
   explicit InlinedVector(size_type n,
-                         const allocator_type& alloc = allocator_type())
-      : storage_(alloc) {
-    storage_.Initialize(DefaultValueAdapter(), n);
+                         const allocator_type& allocator = allocator_type())
+      : storage_(allocator) {
+    storage_.Initialize(DefaultValueAdapter<A>(), n);
   }
 
   // Creates an inlined vector with `n` copies of `v`.
   InlinedVector(size_type n, const_reference v,
-                const allocator_type& alloc = allocator_type())
-      : storage_(alloc) {
-    storage_.Initialize(CopyValueAdapter(v), n);
+                const allocator_type& allocator = allocator_type())
+      : storage_(allocator) {
+    storage_.Initialize(CopyValueAdapter<A>(std::addressof(v)), n);
   }
 
   // Creates an inlined vector with copies of the elements of `list`.
   InlinedVector(std::initializer_list<value_type> list,
-                const allocator_type& alloc = allocator_type())
-      : InlinedVector(list.begin(), list.end(), alloc) {}
+                const allocator_type& allocator = allocator_type())
+      : InlinedVector(list.begin(), list.end(), allocator) {}
 
   // Creates an inlined vector with elements constructed from the provided
   // forward iterator range [`first`, `last`).
@@ -141,35 +147,36 @@
   // this constructor with two integral arguments and a call to the above
   // `InlinedVector(size_type, const_reference)` constructor.
   template <typename ForwardIterator,
-            EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr>
+            EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
   InlinedVector(ForwardIterator first, ForwardIterator last,
-                const allocator_type& alloc = allocator_type())
-      : storage_(alloc) {
-    storage_.Initialize(IteratorValueAdapter<ForwardIterator>(first),
+                const allocator_type& allocator = allocator_type())
+      : storage_(allocator) {
+    storage_.Initialize(IteratorValueAdapter<A, ForwardIterator>(first),
                         std::distance(first, last));
   }
 
   // Creates an inlined vector with elements constructed from the provided input
   // iterator range [`first`, `last`).
   template <typename InputIterator,
-            DisableIfAtLeastForwardIterator<InputIterator>* = nullptr>
+            DisableIfAtLeastForwardIterator<InputIterator> = 0>
   InlinedVector(InputIterator first, InputIterator last,
-                const allocator_type& alloc = allocator_type())
-      : storage_(alloc) {
+                const allocator_type& allocator = allocator_type())
+      : storage_(allocator) {
     std::copy(first, last, std::back_inserter(*this));
   }
 
   // Creates an inlined vector by copying the contents of `other` using
   // `other`'s allocator.
   InlinedVector(const InlinedVector& other)
-      : InlinedVector(other, *other.storage_.GetAllocPtr()) {}
+      : InlinedVector(other, other.storage_.GetAllocator()) {}
 
-  // Creates an inlined vector by copying the contents of `other` using `alloc`.
-  InlinedVector(const InlinedVector& other, const allocator_type& alloc)
-      : storage_(alloc) {
+  // Creates an inlined vector by copying the contents of `other` using the
+  // provided `allocator`.
+  InlinedVector(const InlinedVector& other, const allocator_type& allocator)
+      : storage_(allocator) {
     if (other.empty()) {
       // Empty; nothing to do.
-    } else if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) {
+    } else if (IsMemcpyOk<A>::value && !other.storage_.GetIsAllocated()) {
       // Memcpy-able and do not need allocation.
       storage_.MemcpyFrom(other.storage_);
     } else {
@@ -194,23 +201,23 @@
   InlinedVector(InlinedVector&& other) noexcept(
       absl::allocator_is_nothrow<allocator_type>::value ||
       std::is_nothrow_move_constructible<value_type>::value)
-      : storage_(*other.storage_.GetAllocPtr()) {
-    if (IsMemcpyOk::value) {
+      : storage_(other.storage_.GetAllocator()) {
+    if (IsMemcpyOk<A>::value) {
       storage_.MemcpyFrom(other.storage_);
 
       other.storage_.SetInlinedSize(0);
     } else if (other.storage_.GetIsAllocated()) {
-      storage_.SetAllocatedData(other.storage_.GetAllocatedData(),
-                                other.storage_.GetAllocatedCapacity());
+      storage_.SetAllocation({other.storage_.GetAllocatedData(),
+                              other.storage_.GetAllocatedCapacity()});
       storage_.SetAllocatedSize(other.storage_.GetSize());
 
       other.storage_.SetInlinedSize(0);
     } else {
-      IteratorValueAdapter<MoveIterator> other_values(
-          MoveIterator(other.storage_.GetInlinedData()));
+      IteratorValueAdapter<A, MoveIterator<A>> other_values(
+          MoveIterator<A>(other.storage_.GetInlinedData()));
 
-      inlined_vector_internal::ConstructElements(
-          storage_.GetAllocPtr(), storage_.GetInlinedData(), &other_values,
+      inlined_vector_internal::ConstructElements<A>(
+          storage_.GetAllocator(), storage_.GetInlinedData(), other_values,
           other.storage_.GetSize());
 
       storage_.SetInlinedSize(other.storage_.GetSize());
@@ -218,30 +225,32 @@
   }
 
   // Creates an inlined vector by moving in the contents of `other` with a copy
-  // of `alloc`.
+  // of `allocator`.
   //
-  // NOTE: if `other`'s allocator is not equal to `alloc`, even if `other`
+  // NOTE: if `other`'s allocator is not equal to `allocator`, even if `other`
   // contains allocated memory, this move constructor will still allocate. Since
   // allocation is performed, this constructor can only be `noexcept` if the
   // specified allocator is also `noexcept`.
-  InlinedVector(InlinedVector&& other, const allocator_type& alloc) noexcept(
-      absl::allocator_is_nothrow<allocator_type>::value)
-      : storage_(alloc) {
-    if (IsMemcpyOk::value) {
+  InlinedVector(
+      InlinedVector&& other,
+      const allocator_type& allocator)
+      noexcept(absl::allocator_is_nothrow<allocator_type>::value)
+      : storage_(allocator) {
+    if (IsMemcpyOk<A>::value) {
       storage_.MemcpyFrom(other.storage_);
 
       other.storage_.SetInlinedSize(0);
-    } else if ((*storage_.GetAllocPtr() == *other.storage_.GetAllocPtr()) &&
+    } else if ((storage_.GetAllocator() == other.storage_.GetAllocator()) &&
                other.storage_.GetIsAllocated()) {
-      storage_.SetAllocatedData(other.storage_.GetAllocatedData(),
-                                other.storage_.GetAllocatedCapacity());
+      storage_.SetAllocation({other.storage_.GetAllocatedData(),
+                              other.storage_.GetAllocatedCapacity()});
       storage_.SetAllocatedSize(other.storage_.GetSize());
 
       other.storage_.SetInlinedSize(0);
     } else {
-      storage_.Initialize(
-          IteratorValueAdapter<MoveIterator>(MoveIterator(other.data())),
-          other.size());
+      storage_.Initialize(IteratorValueAdapter<A, MoveIterator<A>>(
+                              MoveIterator<A>(other.data())),
+                          other.size());
     }
   }
 
@@ -442,7 +451,7 @@
   // `InlinedVector::get_allocator()`
   //
   // Returns a copy of the inlined vector's allocator.
-  allocator_type get_allocator() const { return *storage_.GetAllocPtr(); }
+  allocator_type get_allocator() const { return storage_.GetAllocator(); }
 
   // ---------------------------------------------------------------------------
   // InlinedVector Member Mutators
@@ -476,16 +485,16 @@
   // unspecified state.
   InlinedVector& operator=(InlinedVector&& other) {
     if (ABSL_PREDICT_TRUE(this != std::addressof(other))) {
-      if (IsMemcpyOk::value || other.storage_.GetIsAllocated()) {
-        inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(),
-                                                 size());
+      if (IsMemcpyOk<A>::value || other.storage_.GetIsAllocated()) {
+        inlined_vector_internal::DestroyElements<A>(storage_.GetAllocator(),
+                                                    data(), size());
         storage_.DeallocateIfAllocated();
         storage_.MemcpyFrom(other.storage_);
 
         other.storage_.SetInlinedSize(0);
       } else {
-        storage_.Assign(IteratorValueAdapter<MoveIterator>(
-                            MoveIterator(other.storage_.GetInlinedData())),
+        storage_.Assign(IteratorValueAdapter<A, MoveIterator<A>>(
+                            MoveIterator<A>(other.storage_.GetInlinedData())),
                         other.size());
       }
     }
@@ -497,7 +506,7 @@
   //
   // Replaces the contents of the inlined vector with `n` copies of `v`.
   void assign(size_type n, const_reference v) {
-    storage_.Assign(CopyValueAdapter(v), n);
+    storage_.Assign(CopyValueAdapter<A>(std::addressof(v)), n);
   }
 
   // Overload of `InlinedVector::assign(...)` that replaces the contents of the
@@ -511,9 +520,9 @@
   //
   // NOTE: this overload is for iterators that are "forward" category or better.
   template <typename ForwardIterator,
-            EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr>
+            EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
   void assign(ForwardIterator first, ForwardIterator last) {
-    storage_.Assign(IteratorValueAdapter<ForwardIterator>(first),
+    storage_.Assign(IteratorValueAdapter<A, ForwardIterator>(first),
                     std::distance(first, last));
   }
 
@@ -522,7 +531,7 @@
   //
   // NOTE: this overload is for iterators that are "input" category.
   template <typename InputIterator,
-            DisableIfAtLeastForwardIterator<InputIterator>* = nullptr>
+            DisableIfAtLeastForwardIterator<InputIterator> = 0>
   void assign(InputIterator first, InputIterator last) {
     size_type i = 0;
     for (; i < size() && first != last; ++i, static_cast<void>(++first)) {
@@ -541,7 +550,7 @@
   // is larger than `size()`, new elements are value-initialized.
   void resize(size_type n) {
     ABSL_HARDENING_ASSERT(n <= max_size());
-    storage_.Resize(DefaultValueAdapter(), n);
+    storage_.Resize(DefaultValueAdapter<A>(), n);
   }
 
   // Overload of `InlinedVector::resize(...)` that resizes the inlined vector to
@@ -551,7 +560,7 @@
   // is larger than `size()`, new elements are copied-constructed from `v`.
   void resize(size_type n, const_reference v) {
     ABSL_HARDENING_ASSERT(n <= max_size());
-    storage_.Resize(CopyValueAdapter(v), n);
+    storage_.Resize(CopyValueAdapter<A>(std::addressof(v)), n);
   }
 
   // `InlinedVector::insert(...)`
@@ -564,7 +573,7 @@
 
   // Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using
   // move semantics, returning an `iterator` to the newly inserted element.
-  iterator insert(const_iterator pos, RValueReference v) {
+  iterator insert(const_iterator pos, value_type&& v) {
     return emplace(pos, std::move(v));
   }
 
@@ -577,7 +586,8 @@
 
     if (ABSL_PREDICT_TRUE(n != 0)) {
       value_type dealias = v;
-      return storage_.Insert(pos, CopyValueAdapter(dealias), n);
+      return storage_.Insert(pos, CopyValueAdapter<A>(std::addressof(dealias)),
+                             n);
     } else {
       return const_cast<iterator>(pos);
     }
@@ -596,14 +606,15 @@
   //
   // NOTE: this overload is for iterators that are "forward" category or better.
   template <typename ForwardIterator,
-            EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr>
+            EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
   iterator insert(const_iterator pos, ForwardIterator first,
                   ForwardIterator last) {
     ABSL_HARDENING_ASSERT(pos >= begin());
     ABSL_HARDENING_ASSERT(pos <= end());
 
     if (ABSL_PREDICT_TRUE(first != last)) {
-      return storage_.Insert(pos, IteratorValueAdapter<ForwardIterator>(first),
+      return storage_.Insert(pos,
+                             IteratorValueAdapter<A, ForwardIterator>(first),
                              std::distance(first, last));
     } else {
       return const_cast<iterator>(pos);
@@ -616,7 +627,7 @@
   //
   // NOTE: this overload is for iterators that are "input" category.
   template <typename InputIterator,
-            DisableIfAtLeastForwardIterator<InputIterator>* = nullptr>
+            DisableIfAtLeastForwardIterator<InputIterator> = 0>
   iterator insert(const_iterator pos, InputIterator first, InputIterator last) {
     ABSL_HARDENING_ASSERT(pos >= begin());
     ABSL_HARDENING_ASSERT(pos <= end());
@@ -640,8 +651,8 @@
 
     value_type dealias(std::forward<Args>(args)...);
     return storage_.Insert(pos,
-                           IteratorValueAdapter<MoveIterator>(
-                               MoveIterator(std::addressof(dealias))),
+                           IteratorValueAdapter<A, MoveIterator<A>>(
+                               MoveIterator<A>(std::addressof(dealias))),
                            1);
   }
 
@@ -661,7 +672,7 @@
 
   // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()`
   // using move semantics.
-  void push_back(RValueReference v) {
+  void push_back(value_type&& v) {
     static_cast<void>(emplace_back(std::move(v)));
   }
 
@@ -671,7 +682,7 @@
   void pop_back() noexcept {
     ABSL_HARDENING_ASSERT(!empty());
 
-    AllocatorTraits::destroy(*storage_.GetAllocPtr(), data() + (size() - 1));
+    AllocatorTraits<A>::destroy(storage_.GetAllocator(), data() + (size() - 1));
     storage_.SubtractSize(1);
   }
 
@@ -710,8 +721,8 @@
   // Destroys all elements in the inlined vector, setting the size to `0` and
   // deallocating any held memory.
   void clear() noexcept {
-    inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(),
-                                             size());
+    inlined_vector_internal::DestroyElements<A>(storage_.GetAllocator(), data(),
+                                                size());
     storage_.DeallocateIfAllocated();
 
     storage_.SetInlinedSize(0);
@@ -724,15 +735,12 @@
 
   // `InlinedVector::shrink_to_fit()`
   //
-  // Reduces memory usage by freeing unused memory. After being called, calls to
-  // `capacity()` will be equal to `max(N, size())`.
+  // Attempts to reduce memory usage by moving elements to (or keeping elements
+  // in) the smallest available buffer sufficient for containing `size()`
+  // elements.
   //
-  // If `size() <= N` and the inlined vector contains allocated memory, the
-  // elements will all be moved to the inlined space and the allocated memory
-  // will be deallocated.
-  //
-  // If `size() > N` and `size() < capacity()`, the elements will be moved to a
-  // smaller allocation.
+  // If `size()` is sufficiently small, the elements will be moved into (or kept
+  // in) the inlined space.
   void shrink_to_fit() {
     if (storage_.GetIsAllocated()) {
       storage_.ShrinkToFit();
diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h
index 0bb3836..f636c5f 100644
--- a/absl/container/internal/btree.h
+++ b/absl/container/internal/btree.h
@@ -88,7 +88,12 @@
 
   // Compatibility constructor.
   StringBtreeDefaultLess(std::less<std::string>) {}  // NOLINT
-  StringBtreeDefaultLess(std::less<string_view>) {}  // NOLINT
+  StringBtreeDefaultLess(std::less<absl::string_view>) {}  // NOLINT
+
+  // Allow converting to std::less for use in key_comp()/value_comp().
+  explicit operator std::less<std::string>() const { return {}; }
+  explicit operator std::less<absl::string_view>() const { return {}; }
+  explicit operator std::less<absl::Cord>() const { return {}; }
 
   absl::weak_ordering operator()(absl::string_view lhs,
                                  absl::string_view rhs) const {
@@ -115,7 +120,12 @@
   StringBtreeDefaultGreater() = default;
 
   StringBtreeDefaultGreater(std::greater<std::string>) {}  // NOLINT
-  StringBtreeDefaultGreater(std::greater<string_view>) {}  // NOLINT
+  StringBtreeDefaultGreater(std::greater<absl::string_view>) {}  // NOLINT
+
+  // Allow converting to std::greater for use in key_comp()/value_comp().
+  explicit operator std::greater<std::string>() const { return {}; }
+  explicit operator std::greater<absl::string_view>() const { return {}; }
+  explicit operator std::greater<absl::Cord>() const { return {}; }
 
   absl::weak_ordering operator()(absl::string_view lhs,
                                  absl::string_view rhs) const {
@@ -217,6 +227,8 @@
 template <typename Key, typename Compare, typename Alloc, int TargetNodeSize,
           bool Multi, typename SlotPolicy>
 struct common_params {
+  using original_key_compare = Compare;
+
   // If Compare is a common comparator for a string-like type, then we adapt it
   // to use heterogeneous lookup and to be a key-compare-to comparator.
   using key_compare = typename key_compare_to_adapter<Compare>::type;
@@ -317,16 +329,21 @@
   using value_type = typename super_type::value_type;
   using init_type = typename super_type::init_type;
 
-  using key_compare = typename super_type::key_compare;
-  // Inherit from key_compare for empty base class optimization.
-  struct value_compare : private key_compare {
-    value_compare() = default;
-    explicit value_compare(const key_compare &cmp) : key_compare(cmp) {}
+  using original_key_compare = typename super_type::original_key_compare;
+  // Reference: https://en.cppreference.com/w/cpp/container/map/value_compare
+  class value_compare {
+    template <typename Params>
+    friend class btree;
 
-    template <typename T, typename U>
-    auto operator()(const T &left, const U &right) const
-        -> decltype(std::declval<key_compare>()(left.first, right.first)) {
-      return key_compare::operator()(left.first, right.first);
+   protected:
+    explicit value_compare(original_key_compare c) : comp(std::move(c)) {}
+
+    original_key_compare comp;  // NOLINT
+
+   public:
+    auto operator()(const value_type &lhs, const value_type &rhs) const
+        -> decltype(comp(lhs.first, rhs.first)) {
+      return comp(lhs.first, rhs.first);
     }
   };
   using is_map_container = std::true_type;
@@ -392,7 +409,8 @@
                                   set_slot_policy<Key>> {
   using value_type = Key;
   using slot_type = typename set_params::common_params::slot_type;
-  using value_compare = typename set_params::common_params::key_compare;
+  using value_compare =
+      typename set_params::common_params::original_key_compare;
   using is_map_container = std::false_type;
 
   template <typename V>
@@ -484,8 +502,8 @@
                        std::is_same<std::greater<key_type>,
                                     key_compare>::value)>;
 
-  // This class is organized by gtl::Layout as if it had the following
-  // structure:
+  // This class is organized by absl::container_internal::Layout as if it had
+  // the following structure:
   //   // A pointer to the node's parent.
   //   btree_node *parent;
   //
@@ -1129,6 +1147,7 @@
   using size_type = typename Params::size_type;
   using difference_type = typename Params::difference_type;
   using key_compare = typename Params::key_compare;
+  using original_key_compare = typename Params::original_key_compare;
   using value_compare = typename Params::value_compare;
   using allocator_type = typename Params::allocator_type;
   using reference = typename Params::reference;
@@ -1338,7 +1357,9 @@
     return compare_internal::compare_result_as_less_than(key_comp()(a, b));
   }
 
-  value_compare value_comp() const { return value_compare(key_comp()); }
+  value_compare value_comp() const {
+    return value_compare(original_key_compare(key_comp()));
+  }
 
   // Verifies the structure of the btree.
   void verify() const;
diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h
index 03be708..a99668c 100644
--- a/absl/container/internal/btree_container.h
+++ b/absl/container/internal/btree_container.h
@@ -20,6 +20,7 @@
 #include <iterator>
 #include <utility>
 
+#include "absl/base/attributes.h"
 #include "absl/base/internal/throw_delegate.h"
 #include "absl/container/internal/btree.h"  // IWYU pragma: export
 #include "absl/container/internal/common.h"
@@ -51,7 +52,7 @@
   using value_type = typename Tree::value_type;
   using size_type = typename Tree::size_type;
   using difference_type = typename Tree::difference_type;
-  using key_compare = typename Tree::key_compare;
+  using key_compare = typename Tree::original_key_compare;
   using value_compare = typename Tree::value_compare;
   using allocator_type = typename Tree::allocator_type;
   using reference = typename Tree::reference;
@@ -176,7 +177,7 @@
   }
 
   // Utility routines.
-  void clear() { tree_.clear(); }
+  ABSL_ATTRIBUTE_REINITIALIZES void clear() { tree_.clear(); }
   void swap(btree_container &other) { tree_.swap(other.tree_); }
   void verify() const { tree_.verify(); }
 
@@ -214,7 +215,7 @@
   allocator_type get_allocator() const { return tree_.get_allocator(); }
 
   // The key comparator used by the btree.
-  key_compare key_comp() const { return tree_.key_comp(); }
+  key_compare key_comp() const { return key_compare(tree_.key_comp()); }
   value_compare value_comp() const { return tree_.value_comp(); }
 
   // Support absl::Hash.
@@ -247,7 +248,7 @@
   using key_type = typename Tree::key_type;
   using value_type = typename Tree::value_type;
   using size_type = typename Tree::size_type;
-  using key_compare = typename Tree::key_compare;
+  using key_compare = typename Tree::original_key_compare;
   using allocator_type = typename Tree::allocator_type;
   using iterator = typename Tree::iterator;
   using const_iterator = typename Tree::const_iterator;
@@ -398,7 +399,7 @@
   using key_type = typename Tree::key_type;
   using mapped_type = typename params_type::mapped_type;
   using value_type = typename Tree::value_type;
-  using key_compare = typename Tree::key_compare;
+  using key_compare = typename Tree::original_key_compare;
   using allocator_type = typename Tree::allocator_type;
   using iterator = typename Tree::iterator;
   using const_iterator = typename Tree::const_iterator;
@@ -543,7 +544,7 @@
   using key_type = typename Tree::key_type;
   using value_type = typename Tree::value_type;
   using size_type = typename Tree::size_type;
-  using key_compare = typename Tree::key_compare;
+  using key_compare = typename Tree::original_key_compare;
   using allocator_type = typename Tree::allocator_type;
   using iterator = typename Tree::iterator;
   using const_iterator = typename Tree::const_iterator;
diff --git a/absl/container/internal/hash_function_defaults.h b/absl/container/internal/hash_function_defaults.h
index 0683422..250e662 100644
--- a/absl/container/internal/hash_function_defaults.h
+++ b/absl/container/internal/hash_function_defaults.h
@@ -78,24 +78,26 @@
   }
 };
 
+struct StringEq {
+  using is_transparent = void;
+  bool operator()(absl::string_view lhs, absl::string_view rhs) const {
+    return lhs == rhs;
+  }
+  bool operator()(const absl::Cord& lhs, const absl::Cord& rhs) const {
+    return lhs == rhs;
+  }
+  bool operator()(const absl::Cord& lhs, absl::string_view rhs) const {
+    return lhs == rhs;
+  }
+  bool operator()(absl::string_view lhs, const absl::Cord& rhs) const {
+    return lhs == rhs;
+  }
+};
+
 // Supports heterogeneous lookup for string-like elements.
 struct StringHashEq {
   using Hash = StringHash;
-  struct Eq {
-    using is_transparent = void;
-    bool operator()(absl::string_view lhs, absl::string_view rhs) const {
-      return lhs == rhs;
-    }
-    bool operator()(const absl::Cord& lhs, const absl::Cord& rhs) const {
-      return lhs == rhs;
-    }
-    bool operator()(const absl::Cord& lhs, absl::string_view rhs) const {
-      return lhs == rhs;
-    }
-    bool operator()(absl::string_view lhs, const absl::Cord& rhs) const {
-      return lhs == rhs;
-    }
-  };
+  using Eq = StringEq;
 };
 
 template <>
diff --git a/absl/container/internal/hash_generator_testing.h b/absl/container/internal/hash_generator_testing.h
index 6869fe4..f1f555a 100644
--- a/absl/container/internal/hash_generator_testing.h
+++ b/absl/container/internal/hash_generator_testing.h
@@ -21,11 +21,13 @@
 #include <stdint.h>
 
 #include <algorithm>
+#include <cassert>
 #include <iosfwd>
 #include <random>
 #include <tuple>
 #include <type_traits>
 #include <utility>
+#include <vector>
 
 #include "absl/container/internal/hash_policy_testing.h"
 #include "absl/memory/memory.h"
@@ -153,6 +155,25 @@
                                   typename Container::value_type,
                                   typename Container::key_type>::type>&>()());
 
+// Naive wrapper that performs a linear search of previous values.
+// Beware this is O(SQR), which is reasonable for smaller kMaxValues.
+template <class T, size_t kMaxValues = 64, class E = void>
+struct UniqueGenerator {
+  Generator<T, E> gen;
+  std::vector<T> values;
+
+  T operator()() {
+    assert(values.size() < kMaxValues);
+    for (;;) {
+      T value = gen();
+      if (std::find(values.begin(), values.end(), value) == values.end()) {
+        values.push_back(value);
+        return value;
+      }
+    }
+  }
+};
+
 }  // namespace hash_internal
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc
index 5a29bed..40cce04 100644
--- a/absl/container/internal/hashtablez_sampler.cc
+++ b/absl/container/internal/hashtablez_sampler.cc
@@ -21,10 +21,11 @@
 #include <limits>
 
 #include "absl/base/attributes.h"
-#include "absl/base/internal/exponential_biased.h"
 #include "absl/container/internal/have_sse.h"
 #include "absl/debugging/stacktrace.h"
 #include "absl/memory/memory.h"
+#include "absl/profiling/internal/exponential_biased.h"
+#include "absl/profiling/internal/sample_recorder.h"
 #include "absl/synchronization/mutex.h"
 
 namespace absl {
@@ -37,10 +38,9 @@
     false
 };
 ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_sample_parameter{1 << 10};
-ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_max_samples{1 << 20};
 
 #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-ABSL_PER_THREAD_TLS_KEYWORD absl::base_internal::ExponentialBiased
+ABSL_PER_THREAD_TLS_KEYWORD absl::profiling_internal::ExponentialBiased
     g_exponential_biased_generator;
 #endif
 
@@ -50,16 +50,14 @@
 ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0;
 #endif  // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
 
-HashtablezSampler& HashtablezSampler::Global() {
+HashtablezSampler& GlobalHashtablezSampler() {
   static auto* sampler = new HashtablezSampler();
   return *sampler;
 }
 
-HashtablezSampler::DisposeCallback HashtablezSampler::SetDisposeCallback(
-    DisposeCallback f) {
-  return dispose_.exchange(f, std::memory_order_relaxed);
-}
-
+// TODO(bradleybear): The comments at this constructors declaration say that the
+// fields are not initialized, but this definition does initialize the fields.
+// Something needs to be cleaned up.
 HashtablezInfo::HashtablezInfo() { PrepareForSampling(); }
 HashtablezInfo::~HashtablezInfo() = default;
 
@@ -73,6 +71,7 @@
   hashes_bitwise_or.store(0, std::memory_order_relaxed);
   hashes_bitwise_and.store(~size_t{}, std::memory_order_relaxed);
   hashes_bitwise_xor.store(0, std::memory_order_relaxed);
+  max_reserve.store(0, std::memory_order_relaxed);
 
   create_time = absl::Now();
   // The inliner makes hardcoded skip_count difficult (especially when combined
@@ -80,93 +79,6 @@
   // instead.
   depth = absl::GetStackTrace(stack, HashtablezInfo::kMaxStackDepth,
                               /* skip_count= */ 0);
-  dead = nullptr;
-}
-
-HashtablezSampler::HashtablezSampler()
-    : dropped_samples_(0), size_estimate_(0), all_(nullptr), dispose_(nullptr) {
-  absl::MutexLock l(&graveyard_.init_mu);
-  graveyard_.dead = &graveyard_;
-}
-
-HashtablezSampler::~HashtablezSampler() {
-  HashtablezInfo* s = all_.load(std::memory_order_acquire);
-  while (s != nullptr) {
-    HashtablezInfo* next = s->next;
-    delete s;
-    s = next;
-  }
-}
-
-void HashtablezSampler::PushNew(HashtablezInfo* sample) {
-  sample->next = all_.load(std::memory_order_relaxed);
-  while (!all_.compare_exchange_weak(sample->next, sample,
-                                     std::memory_order_release,
-                                     std::memory_order_relaxed)) {
-  }
-}
-
-void HashtablezSampler::PushDead(HashtablezInfo* sample) {
-  if (auto* dispose = dispose_.load(std::memory_order_relaxed)) {
-    dispose(*sample);
-  }
-
-  absl::MutexLock graveyard_lock(&graveyard_.init_mu);
-  absl::MutexLock sample_lock(&sample->init_mu);
-  sample->dead = graveyard_.dead;
-  graveyard_.dead = sample;
-}
-
-HashtablezInfo* HashtablezSampler::PopDead() {
-  absl::MutexLock graveyard_lock(&graveyard_.init_mu);
-
-  // The list is circular, so eventually it collapses down to
-  //   graveyard_.dead == &graveyard_
-  // when it is empty.
-  HashtablezInfo* sample = graveyard_.dead;
-  if (sample == &graveyard_) return nullptr;
-
-  absl::MutexLock sample_lock(&sample->init_mu);
-  graveyard_.dead = sample->dead;
-  sample->PrepareForSampling();
-  return sample;
-}
-
-HashtablezInfo* HashtablezSampler::Register() {
-  int64_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed);
-  if (size > g_hashtablez_max_samples.load(std::memory_order_relaxed)) {
-    size_estimate_.fetch_sub(1, std::memory_order_relaxed);
-    dropped_samples_.fetch_add(1, std::memory_order_relaxed);
-    return nullptr;
-  }
-
-  HashtablezInfo* sample = PopDead();
-  if (sample == nullptr) {
-    // Resurrection failed.  Hire a new warlock.
-    sample = new HashtablezInfo();
-    PushNew(sample);
-  }
-
-  return sample;
-}
-
-void HashtablezSampler::Unregister(HashtablezInfo* sample) {
-  PushDead(sample);
-  size_estimate_.fetch_sub(1, std::memory_order_relaxed);
-}
-
-int64_t HashtablezSampler::Iterate(
-    const std::function<void(const HashtablezInfo& stack)>& f) {
-  HashtablezInfo* s = all_.load(std::memory_order_acquire);
-  while (s != nullptr) {
-    absl::MutexLock l(&s->init_mu);
-    if (s->dead == nullptr) {
-      f(*s);
-    }
-    s = s->next;
-  }
-
-  return dropped_samples_.load(std::memory_order_relaxed);
 }
 
 static bool ShouldForceSampling() {
@@ -189,10 +101,12 @@
   return state == kForce;
 }
 
-HashtablezInfo* SampleSlow(int64_t* next_sample) {
+HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size) {
   if (ABSL_PREDICT_FALSE(ShouldForceSampling())) {
     *next_sample = 1;
-    return HashtablezSampler::Global().Register();
+    HashtablezInfo* result = GlobalHashtablezSampler().Register();
+    result->inline_element_size = inline_element_size;
+    return result;
   }
 
 #if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
@@ -214,15 +128,17 @@
   // that case.
   if (first) {
     if (ABSL_PREDICT_TRUE(--*next_sample > 0)) return nullptr;
-    return SampleSlow(next_sample);
+    return SampleSlow(next_sample, inline_element_size);
   }
 
-  return HashtablezSampler::Global().Register();
+  HashtablezInfo* result = GlobalHashtablezSampler().Register();
+  result->inline_element_size = inline_element_size;
+  return result;
 #endif
 }
 
 void UnsampleSlow(HashtablezInfo* info) {
-  HashtablezSampler::Global().Unregister(info);
+  GlobalHashtablezSampler().Unregister(info);
 }
 
 void RecordInsertSlow(HashtablezInfo* info, size_t hash,
@@ -262,7 +178,7 @@
 
 void SetHashtablezMaxSamples(int32_t max) {
   if (max > 0) {
-    g_hashtablez_max_samples.store(max, std::memory_order_release);
+    GlobalHashtablezSampler().SetMaxSamples(max);
   } else {
     ABSL_RAW_LOG(ERROR, "Invalid hashtablez max samples: %lld",
                  static_cast<long long>(max));  // NOLINT(runtime/int)
diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h
index 85685f7..91fcdb3 100644
--- a/absl/container/internal/hashtablez_sampler.h
+++ b/absl/container/internal/hashtablez_sampler.h
@@ -47,6 +47,7 @@
 #include "absl/base/internal/per_thread_tls.h"
 #include "absl/base/optimization.h"
 #include "absl/container/internal/have_sse.h"
+#include "absl/profiling/internal/sample_recorder.h"
 #include "absl/synchronization/mutex.h"
 #include "absl/utility/utility.h"
 
@@ -57,7 +58,7 @@
 // Stores information about a sampled hashtable.  All mutations to this *must*
 // be made through `Record*` functions below.  All reads from this *must* only
 // occur in the callback to `HashtablezSampler::Iterate`.
-struct HashtablezInfo {
+struct HashtablezInfo : public profiling_internal::Sample<HashtablezInfo> {
   // Constructs the object but does not fill in any fields.
   HashtablezInfo();
   ~HashtablezInfo();
@@ -79,14 +80,7 @@
   std::atomic<size_t> hashes_bitwise_or;
   std::atomic<size_t> hashes_bitwise_and;
   std::atomic<size_t> hashes_bitwise_xor;
-
-  // `HashtablezSampler` maintains intrusive linked lists for all samples.  See
-  // comments on `HashtablezSampler::all_` for details on these.  `init_mu`
-  // guards the ability to restore the sample to a pristine state.  This
-  // prevents races with sampling and resurrecting an object.
-  absl::Mutex init_mu;
-  HashtablezInfo* next;
-  HashtablezInfo* dead ABSL_GUARDED_BY(init_mu);
+  std::atomic<size_t> max_reserve;
 
   // All of the fields below are set by `PrepareForSampling`, they must not be
   // mutated in `Record*` functions.  They are logically `const` in that sense.
@@ -97,6 +91,7 @@
   absl::Time create_time;
   int32_t depth;
   void* stack[kMaxStackDepth];
+  size_t inline_element_size;
 };
 
 inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) {
@@ -114,6 +109,18 @@
       std::memory_order_relaxed);
 }
 
+inline void RecordReservationSlow(HashtablezInfo* info,
+                                  size_t target_capacity) {
+  info->max_reserve.store(
+      (std::max)(info->max_reserve.load(std::memory_order_relaxed),
+                 target_capacity),
+      std::memory_order_relaxed);
+}
+
+inline void RecordClearedReservationSlow(HashtablezInfo* info) {
+  info->max_reserve.store(0, std::memory_order_relaxed);
+}
+
 inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size,
                                      size_t capacity) {
   info->size.store(size, std::memory_order_relaxed);
@@ -137,7 +144,7 @@
       std::memory_order_relaxed);
 }
 
-HashtablezInfo* SampleSlow(int64_t* next_sample);
+HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size);
 void UnsampleSlow(HashtablezInfo* info);
 
 #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
@@ -177,6 +184,16 @@
     RecordRehashSlow(info_, total_probe_length);
   }
 
+  inline void RecordReservation(size_t target_capacity) {
+    if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
+    RecordReservationSlow(info_, target_capacity);
+  }
+
+  inline void RecordClearedReservation() {
+    if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
+    RecordClearedReservationSlow(info_);
+  }
+
   inline void RecordInsert(size_t hash, size_t distance_from_desired) {
     if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
     RecordInsertSlow(info_, hash, distance_from_desired);
@@ -206,6 +223,8 @@
 
   inline void RecordStorageChanged(size_t /*size*/, size_t /*capacity*/) {}
   inline void RecordRehash(size_t /*total_probe_length*/) {}
+  inline void RecordReservation(size_t /*target_capacity*/) {}
+  inline void RecordClearedReservation() {}
   inline void RecordInsert(size_t /*hash*/, size_t /*distance_from_desired*/) {}
   inline void RecordErase() {}
 
@@ -220,84 +239,24 @@
 
 // Returns an RAII sampling handle that manages registration and unregistation
 // with the global sampler.
-inline HashtablezInfoHandle Sample() {
+inline HashtablezInfoHandle Sample(
+    size_t inline_element_size ABSL_ATTRIBUTE_UNUSED) {
 #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
   if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) {
     return HashtablezInfoHandle(nullptr);
   }
-  return HashtablezInfoHandle(SampleSlow(&global_next_sample));
+  return HashtablezInfoHandle(
+      SampleSlow(&global_next_sample, inline_element_size));
 #else
   return HashtablezInfoHandle(nullptr);
 #endif  // !ABSL_PER_THREAD_TLS
 }
 
-// Holds samples and their associated stack traces with a soft limit of
-// `SetHashtablezMaxSamples()`.
-//
-// Thread safe.
-class HashtablezSampler {
- public:
-  // Returns a global Sampler.
-  static HashtablezSampler& Global();
+using HashtablezSampler =
+    ::absl::profiling_internal::SampleRecorder<HashtablezInfo>;
 
-  HashtablezSampler();
-  ~HashtablezSampler();
-
-  // Registers for sampling.  Returns an opaque registration info.
-  HashtablezInfo* Register();
-
-  // Unregisters the sample.
-  void Unregister(HashtablezInfo* sample);
-
-  // The dispose callback will be called on all samples the moment they are
-  // being unregistered. Only affects samples that are unregistered after the
-  // callback has been set.
-  // Returns the previous callback.
-  using DisposeCallback = void (*)(const HashtablezInfo&);
-  DisposeCallback SetDisposeCallback(DisposeCallback f);
-
-  // Iterates over all the registered `StackInfo`s.  Returning the number of
-  // samples that have been dropped.
-  int64_t Iterate(const std::function<void(const HashtablezInfo& stack)>& f);
-
- private:
-  void PushNew(HashtablezInfo* sample);
-  void PushDead(HashtablezInfo* sample);
-  HashtablezInfo* PopDead();
-
-  std::atomic<size_t> dropped_samples_;
-  std::atomic<size_t> size_estimate_;
-
-  // Intrusive lock free linked lists for tracking samples.
-  //
-  // `all_` records all samples (they are never removed from this list) and is
-  // terminated with a `nullptr`.
-  //
-  // `graveyard_.dead` is a circular linked list.  When it is empty,
-  // `graveyard_.dead == &graveyard`.  The list is circular so that
-  // every item on it (even the last) has a non-null dead pointer.  This allows
-  // `Iterate` to determine if a given sample is live or dead using only
-  // information on the sample itself.
-  //
-  // For example, nodes [A, B, C, D, E] with [A, C, E] alive and [B, D] dead
-  // looks like this (G is the Graveyard):
-  //
-  //           +---+    +---+    +---+    +---+    +---+
-  //    all -->| A |--->| B |--->| C |--->| D |--->| E |
-  //           |   |    |   |    |   |    |   |    |   |
-  //   +---+   |   | +->|   |-+  |   | +->|   |-+  |   |
-  //   | G |   +---+ |  +---+ |  +---+ |  +---+ |  +---+
-  //   |   |         |        |        |        |
-  //   |   | --------+        +--------+        |
-  //   +---+                                    |
-  //     ^                                      |
-  //     +--------------------------------------+
-  //
-  std::atomic<HashtablezInfo*> all_;
-  HashtablezInfo graveyard_;
-
-  std::atomic<DisposeCallback> dispose_;
-};
+// Returns a global Sampler.
+HashtablezSampler& GlobalHashtablezSampler();
 
 // Enables or disables sampling for Swiss tables.
 void SetHashtablezEnabled(bool enabled);
diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc
index 5f4c83b..449619a 100644
--- a/absl/container/internal/hashtablez_sampler_test.cc
+++ b/absl/container/internal/hashtablez_sampler_test.cc
@@ -22,6 +22,7 @@
 #include "gtest/gtest.h"
 #include "absl/base/attributes.h"
 #include "absl/container/internal/have_sse.h"
+#include "absl/profiling/internal/sample_recorder.h"
 #include "absl/synchronization/blocking_counter.h"
 #include "absl/synchronization/internal/thread_pool.h"
 #include "absl/synchronization/mutex.h"
@@ -77,10 +78,12 @@
 
 TEST(HashtablezInfoTest, PrepareForSampling) {
   absl::Time test_start = absl::Now();
+  const size_t test_element_size = 17;
   HashtablezInfo info;
   absl::MutexLock l(&info.init_mu);
   info.PrepareForSampling();
 
+  info.inline_element_size = test_element_size;
   EXPECT_EQ(info.capacity.load(), 0);
   EXPECT_EQ(info.size.load(), 0);
   EXPECT_EQ(info.num_erases.load(), 0);
@@ -90,7 +93,9 @@
   EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
   EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
   EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
+  EXPECT_EQ(info.max_reserve.load(), 0);
   EXPECT_GE(info.create_time, test_start);
+  EXPECT_EQ(info.inline_element_size, test_element_size);
 
   info.capacity.store(1, std::memory_order_relaxed);
   info.size.store(1, std::memory_order_relaxed);
@@ -100,6 +105,7 @@
   info.hashes_bitwise_or.store(1, std::memory_order_relaxed);
   info.hashes_bitwise_and.store(1, std::memory_order_relaxed);
   info.hashes_bitwise_xor.store(1, std::memory_order_relaxed);
+  info.max_reserve.store(1, std::memory_order_relaxed);
   info.create_time = test_start - absl::Hours(20);
 
   info.PrepareForSampling();
@@ -112,6 +118,8 @@
   EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
   EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
   EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
+  EXPECT_EQ(info.max_reserve.load(), 0);
+  EXPECT_EQ(info.inline_element_size, test_element_size);
   EXPECT_GE(info.create_time, test_start);
 }
 
@@ -150,9 +158,11 @@
 }
 
 TEST(HashtablezInfoTest, RecordErase) {
+  const size_t test_element_size = 29;
   HashtablezInfo info;
   absl::MutexLock l(&info.init_mu);
   info.PrepareForSampling();
+  info.inline_element_size = test_element_size;
   EXPECT_EQ(info.num_erases.load(), 0);
   EXPECT_EQ(info.size.load(), 0);
   RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength);
@@ -160,12 +170,15 @@
   RecordEraseSlow(&info);
   EXPECT_EQ(info.size.load(), 0);
   EXPECT_EQ(info.num_erases.load(), 1);
+  EXPECT_EQ(info.inline_element_size, test_element_size);
 }
 
 TEST(HashtablezInfoTest, RecordRehash) {
+  const size_t test_element_size = 31;
   HashtablezInfo info;
   absl::MutexLock l(&info.init_mu);
   info.PrepareForSampling();
+  info.inline_element_size = test_element_size;
   RecordInsertSlow(&info, 0x1, 0);
   RecordInsertSlow(&info, 0x2, kProbeLength);
   RecordInsertSlow(&info, 0x4, kProbeLength);
@@ -184,16 +197,34 @@
   EXPECT_EQ(info.total_probe_length.load(), 3);
   EXPECT_EQ(info.num_erases.load(), 0);
   EXPECT_EQ(info.num_rehashes.load(), 1);
+  EXPECT_EQ(info.inline_element_size, test_element_size);
+}
+
+TEST(HashtablezInfoTest, RecordReservation) {
+  HashtablezInfo info;
+  absl::MutexLock l(&info.init_mu);
+  info.PrepareForSampling();
+  RecordReservationSlow(&info, 3);
+  EXPECT_EQ(info.max_reserve.load(), 3);
+
+  RecordReservationSlow(&info, 2);
+  // High watermark does not change
+  EXPECT_EQ(info.max_reserve.load(), 3);
+
+  RecordReservationSlow(&info, 10);
+  // High watermark does change
+  EXPECT_EQ(info.max_reserve.load(), 10);
 }
 
 #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
 TEST(HashtablezSamplerTest, SmallSampleParameter) {
+  const size_t test_element_size = 31;
   SetHashtablezEnabled(true);
   SetHashtablezSampleParameter(100);
 
   for (int i = 0; i < 1000; ++i) {
     int64_t next_sample = 0;
-    HashtablezInfo* sample = SampleSlow(&next_sample);
+    HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size);
     EXPECT_GT(next_sample, 0);
     EXPECT_NE(sample, nullptr);
     UnsampleSlow(sample);
@@ -201,12 +232,13 @@
 }
 
 TEST(HashtablezSamplerTest, LargeSampleParameter) {
+  const size_t test_element_size = 31;
   SetHashtablezEnabled(true);
   SetHashtablezSampleParameter(std::numeric_limits<int32_t>::max());
 
   for (int i = 0; i < 1000; ++i) {
     int64_t next_sample = 0;
-    HashtablezInfo* sample = SampleSlow(&next_sample);
+    HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size);
     EXPECT_GT(next_sample, 0);
     EXPECT_NE(sample, nullptr);
     UnsampleSlow(sample);
@@ -214,13 +246,14 @@
 }
 
 TEST(HashtablezSamplerTest, Sample) {
+  const size_t test_element_size = 31;
   SetHashtablezEnabled(true);
   SetHashtablezSampleParameter(100);
   int64_t num_sampled = 0;
   int64_t total = 0;
   double sample_rate = 0.0;
   for (int i = 0; i < 1000000; ++i) {
-    HashtablezInfoHandle h = Sample();
+    HashtablezInfoHandle h = Sample(test_element_size);
     ++total;
     if (HashtablezInfoHandlePeer::IsSampled(h)) {
       ++num_sampled;
@@ -232,7 +265,7 @@
 }
 
 TEST(HashtablezSamplerTest, Handle) {
-  auto& sampler = HashtablezSampler::Global();
+  auto& sampler = GlobalHashtablezSampler();
   HashtablezInfoHandle h(sampler.Register());
   auto* info = HashtablezInfoHandlePeer::GetInfo(&h);
   info->hashes_bitwise_and.store(0x12345678, std::memory_order_relaxed);
diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h
index b8aec45..1d7d6cd 100644
--- a/absl/container/internal/inlined_vector.h
+++ b/absl/container/internal/inlined_vector.h
@@ -21,8 +21,11 @@
 #include <iterator>
 #include <limits>
 #include <memory>
+#include <new>
+#include <type_traits>
 #include <utility>
 
+#include "absl/base/attributes.h"
 #include "absl/base/macros.h"
 #include "absl/container/internal/compressed_tuple.h"
 #include "absl/memory/memory.h"
@@ -36,116 +39,132 @@
 // GCC does not deal very well with the below code
 #if !defined(__clang__) && defined(__GNUC__)
 #pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
 #endif
 
+template <typename A>
+using AllocatorTraits = std::allocator_traits<A>;
+template <typename A>
+using ValueType = typename AllocatorTraits<A>::value_type;
+template <typename A>
+using SizeType = typename AllocatorTraits<A>::size_type;
+template <typename A>
+using Pointer = typename AllocatorTraits<A>::pointer;
+template <typename A>
+using ConstPointer = typename AllocatorTraits<A>::const_pointer;
+template <typename A>
+using SizeType = typename AllocatorTraits<A>::size_type;
+template <typename A>
+using DifferenceType = typename AllocatorTraits<A>::difference_type;
+template <typename A>
+using Reference = ValueType<A>&;
+template <typename A>
+using ConstReference = const ValueType<A>&;
+template <typename A>
+using Iterator = Pointer<A>;
+template <typename A>
+using ConstIterator = ConstPointer<A>;
+template <typename A>
+using ReverseIterator = typename std::reverse_iterator<Iterator<A>>;
+template <typename A>
+using ConstReverseIterator = typename std::reverse_iterator<ConstIterator<A>>;
+template <typename A>
+using MoveIterator = typename std::move_iterator<Iterator<A>>;
+
 template <typename Iterator>
 using IsAtLeastForwardIterator = std::is_convertible<
     typename std::iterator_traits<Iterator>::iterator_category,
     std::forward_iterator_tag>;
 
-template <typename AllocatorType,
-          typename ValueType =
-              typename absl::allocator_traits<AllocatorType>::value_type>
+template <typename A>
 using IsMemcpyOk =
-    absl::conjunction<std::is_same<AllocatorType, std::allocator<ValueType>>,
-                      absl::is_trivially_copy_constructible<ValueType>,
-                      absl::is_trivially_copy_assignable<ValueType>,
-                      absl::is_trivially_destructible<ValueType>>;
+    absl::conjunction<std::is_same<A, std::allocator<ValueType<A>>>,
+                      absl::is_trivially_copy_constructible<ValueType<A>>,
+                      absl::is_trivially_copy_assignable<ValueType<A>>,
+                      absl::is_trivially_destructible<ValueType<A>>>;
 
-template <typename AllocatorType, typename Pointer, typename SizeType>
-void DestroyElements(AllocatorType* alloc_ptr, Pointer destroy_first,
-                     SizeType destroy_size) {
-  using AllocatorTraits = absl::allocator_traits<AllocatorType>;
+template <typename T>
+struct TypeIdentity {
+  using type = T;
+};
 
+// Used for function arguments in template functions to prevent ADL by forcing
+// callers to explicitly specify the template parameter.
+template <typename T>
+using NoTypeDeduction = typename TypeIdentity<T>::type;
+
+template <typename A>
+void DestroyElements(NoTypeDeduction<A>& allocator, Pointer<A> destroy_first,
+                     SizeType<A> destroy_size) {
   if (destroy_first != nullptr) {
-    for (auto i = destroy_size; i != 0;) {
+    for (SizeType<A> i = destroy_size; i != 0;) {
       --i;
-      AllocatorTraits::destroy(*alloc_ptr, destroy_first + i);
+      AllocatorTraits<A>::destroy(allocator, destroy_first + i);
     }
-
-#if !defined(NDEBUG)
-    {
-      using ValueType = typename AllocatorTraits::value_type;
-
-      // Overwrite unused memory with `0xab` so we can catch uninitialized
-      // usage.
-      //
-      // Cast to `void*` to tell the compiler that we don't care that we might
-      // be scribbling on a vtable pointer.
-      void* memory_ptr = destroy_first;
-      auto memory_size = destroy_size * sizeof(ValueType);
-      std::memset(memory_ptr, 0xab, memory_size);
-    }
-#endif  // !defined(NDEBUG)
   }
 }
 
-// If kUseMemcpy is true, memcpy(dst, src, n); else do nothing.
-// Useful to avoid compiler warnings when memcpy() is used for T values
-// that are not trivially copyable in non-reachable code.
-template <bool kUseMemcpy>
-inline void MemcpyIfAllowed(void* dst, const void* src, size_t n);
+template <typename A>
+struct Allocation {
+  Pointer<A> data;
+  SizeType<A> capacity;
+};
 
-// memcpy when allowed.
-template <>
-inline void MemcpyIfAllowed<true>(void* dst, const void* src, size_t n) {
-  memcpy(dst, src, n);
-}
+template <typename A,
+          bool IsOverAligned =
+              (alignof(ValueType<A>) > ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT)>
+struct MallocAdapter {
+  static Allocation<A> Allocate(A& allocator, SizeType<A> requested_capacity) {
+    return {AllocatorTraits<A>::allocate(allocator, requested_capacity),
+            requested_capacity};
+  }
 
-// Do nothing for types that are not memcpy-able. This function is only
-// called from non-reachable branches.
-template <>
-inline void MemcpyIfAllowed<false>(void*, const void*, size_t) {}
+  static void Deallocate(A& allocator, Pointer<A> pointer,
+                         SizeType<A> capacity) {
+    AllocatorTraits<A>::deallocate(allocator, pointer, capacity);
+  }
+};
 
-template <typename AllocatorType, typename Pointer, typename ValueAdapter,
-          typename SizeType>
-void ConstructElements(AllocatorType* alloc_ptr, Pointer construct_first,
-                       ValueAdapter* values_ptr, SizeType construct_size) {
-  for (SizeType i = 0; i < construct_size; ++i) {
-    ABSL_INTERNAL_TRY {
-      values_ptr->ConstructNext(alloc_ptr, construct_first + i);
-    }
+template <typename A, typename ValueAdapter>
+void ConstructElements(NoTypeDeduction<A>& allocator,
+                       Pointer<A> construct_first, ValueAdapter& values,
+                       SizeType<A> construct_size) {
+  for (SizeType<A> i = 0; i < construct_size; ++i) {
+    ABSL_INTERNAL_TRY { values.ConstructNext(allocator, construct_first + i); }
     ABSL_INTERNAL_CATCH_ANY {
-      inlined_vector_internal::DestroyElements(alloc_ptr, construct_first, i);
+      DestroyElements<A>(allocator, construct_first, i);
       ABSL_INTERNAL_RETHROW;
     }
   }
 }
 
-template <typename Pointer, typename ValueAdapter, typename SizeType>
-void AssignElements(Pointer assign_first, ValueAdapter* values_ptr,
-                    SizeType assign_size) {
-  for (SizeType i = 0; i < assign_size; ++i) {
-    values_ptr->AssignNext(assign_first + i);
+template <typename A, typename ValueAdapter>
+void AssignElements(Pointer<A> assign_first, ValueAdapter& values,
+                    SizeType<A> assign_size) {
+  for (SizeType<A> i = 0; i < assign_size; ++i) {
+    values.AssignNext(assign_first + i);
   }
 }
 
-template <typename AllocatorType>
+template <typename A>
 struct StorageView {
-  using AllocatorTraits = absl::allocator_traits<AllocatorType>;
-  using Pointer = typename AllocatorTraits::pointer;
-  using SizeType = typename AllocatorTraits::size_type;
-
-  Pointer data;
-  SizeType size;
-  SizeType capacity;
+  Pointer<A> data;
+  SizeType<A> size;
+  SizeType<A> capacity;
 };
 
-template <typename AllocatorType, typename Iterator>
+template <typename A, typename Iterator>
 class IteratorValueAdapter {
-  using AllocatorTraits = absl::allocator_traits<AllocatorType>;
-  using Pointer = typename AllocatorTraits::pointer;
-
  public:
   explicit IteratorValueAdapter(const Iterator& it) : it_(it) {}
 
-  void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) {
-    AllocatorTraits::construct(*alloc_ptr, construct_at, *it_);
+  void ConstructNext(A& allocator, Pointer<A> construct_at) {
+    AllocatorTraits<A>::construct(allocator, construct_at, *it_);
     ++it_;
   }
 
-  void AssignNext(Pointer assign_at) {
+  void AssignNext(Pointer<A> assign_at) {
     *assign_at = *it_;
     ++it_;
   }
@@ -154,166 +173,123 @@
   Iterator it_;
 };
 
-template <typename AllocatorType>
+template <typename A>
 class CopyValueAdapter {
-  using AllocatorTraits = absl::allocator_traits<AllocatorType>;
-  using ValueType = typename AllocatorTraits::value_type;
-  using Pointer = typename AllocatorTraits::pointer;
-  using ConstPointer = typename AllocatorTraits::const_pointer;
-
  public:
-  explicit CopyValueAdapter(const ValueType& v) : ptr_(std::addressof(v)) {}
+  explicit CopyValueAdapter(ConstPointer<A> p) : ptr_(p) {}
 
-  void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) {
-    AllocatorTraits::construct(*alloc_ptr, construct_at, *ptr_);
+  void ConstructNext(A& allocator, Pointer<A> construct_at) {
+    AllocatorTraits<A>::construct(allocator, construct_at, *ptr_);
   }
 
-  void AssignNext(Pointer assign_at) { *assign_at = *ptr_; }
+  void AssignNext(Pointer<A> assign_at) { *assign_at = *ptr_; }
 
  private:
-  ConstPointer ptr_;
+  ConstPointer<A> ptr_;
 };
 
-template <typename AllocatorType>
+template <typename A>
 class DefaultValueAdapter {
-  using AllocatorTraits = absl::allocator_traits<AllocatorType>;
-  using ValueType = typename AllocatorTraits::value_type;
-  using Pointer = typename AllocatorTraits::pointer;
-
  public:
   explicit DefaultValueAdapter() {}
 
-  void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) {
-    AllocatorTraits::construct(*alloc_ptr, construct_at);
+  void ConstructNext(A& allocator, Pointer<A> construct_at) {
+    AllocatorTraits<A>::construct(allocator, construct_at);
   }
 
-  void AssignNext(Pointer assign_at) { *assign_at = ValueType(); }
+  void AssignNext(Pointer<A> assign_at) { *assign_at = ValueType<A>(); }
 };
 
-template <typename AllocatorType>
+template <typename A>
 class AllocationTransaction {
-  using AllocatorTraits = absl::allocator_traits<AllocatorType>;
-  using Pointer = typename AllocatorTraits::pointer;
-  using SizeType = typename AllocatorTraits::size_type;
-
  public:
-  explicit AllocationTransaction(AllocatorType* alloc_ptr)
-      : alloc_data_(*alloc_ptr, nullptr) {}
+  explicit AllocationTransaction(A& allocator)
+      : allocator_data_(allocator, nullptr), capacity_(0) {}
 
   ~AllocationTransaction() {
     if (DidAllocate()) {
-      AllocatorTraits::deallocate(GetAllocator(), GetData(), GetCapacity());
+      MallocAdapter<A>::Deallocate(GetAllocator(), GetData(), GetCapacity());
     }
   }
 
   AllocationTransaction(const AllocationTransaction&) = delete;
   void operator=(const AllocationTransaction&) = delete;
 
-  AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); }
-  Pointer& GetData() { return alloc_data_.template get<1>(); }
-  SizeType& GetCapacity() { return capacity_; }
+  A& GetAllocator() { return allocator_data_.template get<0>(); }
+  Pointer<A>& GetData() { return allocator_data_.template get<1>(); }
+  SizeType<A>& GetCapacity() { return capacity_; }
 
   bool DidAllocate() { return GetData() != nullptr; }
-  Pointer Allocate(SizeType capacity) {
-    GetData() = AllocatorTraits::allocate(GetAllocator(), capacity);
-    GetCapacity() = capacity;
-    return GetData();
+
+  Pointer<A> Allocate(SizeType<A> requested_capacity) {
+    Allocation<A> result =
+        MallocAdapter<A>::Allocate(GetAllocator(), requested_capacity);
+    GetData() = result.data;
+    GetCapacity() = result.capacity;
+    return result.data;
   }
 
+  ABSL_MUST_USE_RESULT Allocation<A> Release() && {
+    Allocation<A> result = {GetData(), GetCapacity()};
+    Reset();
+    return result;
+  }
+
+ private:
   void Reset() {
     GetData() = nullptr;
     GetCapacity() = 0;
   }
 
- private:
-  container_internal::CompressedTuple<AllocatorType, Pointer> alloc_data_;
-  SizeType capacity_ = 0;
+  container_internal::CompressedTuple<A, Pointer<A>> allocator_data_;
+  SizeType<A> capacity_;
 };
 
-template <typename AllocatorType>
+template <typename A>
 class ConstructionTransaction {
-  using AllocatorTraits = absl::allocator_traits<AllocatorType>;
-  using Pointer = typename AllocatorTraits::pointer;
-  using SizeType = typename AllocatorTraits::size_type;
-
  public:
-  explicit ConstructionTransaction(AllocatorType* alloc_ptr)
-      : alloc_data_(*alloc_ptr, nullptr) {}
+  explicit ConstructionTransaction(A& allocator)
+      : allocator_data_(allocator, nullptr), size_(0) {}
 
   ~ConstructionTransaction() {
     if (DidConstruct()) {
-      inlined_vector_internal::DestroyElements(std::addressof(GetAllocator()),
-                                               GetData(), GetSize());
+      DestroyElements<A>(GetAllocator(), GetData(), GetSize());
     }
   }
 
   ConstructionTransaction(const ConstructionTransaction&) = delete;
   void operator=(const ConstructionTransaction&) = delete;
 
-  AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); }
-  Pointer& GetData() { return alloc_data_.template get<1>(); }
-  SizeType& GetSize() { return size_; }
+  A& GetAllocator() { return allocator_data_.template get<0>(); }
+  Pointer<A>& GetData() { return allocator_data_.template get<1>(); }
+  SizeType<A>& GetSize() { return size_; }
 
   bool DidConstruct() { return GetData() != nullptr; }
   template <typename ValueAdapter>
-  void Construct(Pointer data, ValueAdapter* values_ptr, SizeType size) {
-    inlined_vector_internal::ConstructElements(std::addressof(GetAllocator()),
-                                               data, values_ptr, size);
+  void Construct(Pointer<A> data, ValueAdapter& values, SizeType<A> size) {
+    ConstructElements<A>(GetAllocator(), data, values, size);
     GetData() = data;
     GetSize() = size;
   }
-  void Commit() {
+  void Commit() && {
     GetData() = nullptr;
     GetSize() = 0;
   }
 
  private:
-  container_internal::CompressedTuple<AllocatorType, Pointer> alloc_data_;
-  SizeType size_ = 0;
+  container_internal::CompressedTuple<A, Pointer<A>> allocator_data_;
+  SizeType<A> size_;
 };
 
 template <typename T, size_t N, typename A>
 class Storage {
  public:
-  using AllocatorTraits = absl::allocator_traits<A>;
-  using allocator_type = typename AllocatorTraits::allocator_type;
-  using value_type = typename AllocatorTraits::value_type;
-  using pointer = typename AllocatorTraits::pointer;
-  using const_pointer = typename AllocatorTraits::const_pointer;
-  using size_type = typename AllocatorTraits::size_type;
-  using difference_type = typename AllocatorTraits::difference_type;
-
-  using reference = value_type&;
-  using const_reference = const value_type&;
-  using RValueReference = value_type&&;
-  using iterator = pointer;
-  using const_iterator = const_pointer;
-  using reverse_iterator = std::reverse_iterator<iterator>;
-  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-  using MoveIterator = std::move_iterator<iterator>;
-  using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk<allocator_type>;
-
-  using StorageView = inlined_vector_internal::StorageView<allocator_type>;
-
-  template <typename Iterator>
-  using IteratorValueAdapter =
-      inlined_vector_internal::IteratorValueAdapter<allocator_type, Iterator>;
-  using CopyValueAdapter =
-      inlined_vector_internal::CopyValueAdapter<allocator_type>;
-  using DefaultValueAdapter =
-      inlined_vector_internal::DefaultValueAdapter<allocator_type>;
-
-  using AllocationTransaction =
-      inlined_vector_internal::AllocationTransaction<allocator_type>;
-  using ConstructionTransaction =
-      inlined_vector_internal::ConstructionTransaction<allocator_type>;
-
-  static size_type NextCapacity(size_type current_capacity) {
+  static SizeType<A> NextCapacity(SizeType<A> current_capacity) {
     return current_capacity * 2;
   }
 
-  static size_type ComputeCapacity(size_type current_capacity,
-                                   size_type requested_capacity) {
+  static SizeType<A> ComputeCapacity(SizeType<A> current_capacity,
+                                     SizeType<A> requested_capacity) {
     return (std::max)(NextCapacity(current_capacity), requested_capacity);
   }
 
@@ -321,15 +297,15 @@
   // Storage Constructors and Destructor
   // ---------------------------------------------------------------------------
 
-  Storage() : metadata_(allocator_type(), /* size and is_allocated */ 0) {}
+  Storage() : metadata_(A(), /* size and is_allocated */ 0) {}
 
-  explicit Storage(const allocator_type& alloc)
-      : metadata_(alloc, /* size and is_allocated */ 0) {}
+  explicit Storage(const A& allocator)
+      : metadata_(allocator, /* size and is_allocated */ 0) {}
 
   ~Storage() {
     if (GetSizeAndIsAllocated() == 0) {
       // Empty and not allocated; nothing to do.
-    } else if (IsMemcpyOk::value) {
+    } else if (IsMemcpyOk<A>::value) {
       // No destructors need to be run; just deallocate if necessary.
       DeallocateIfAllocated();
     } else {
@@ -341,52 +317,48 @@
   // Storage Member Accessors
   // ---------------------------------------------------------------------------
 
-  size_type& GetSizeAndIsAllocated() { return metadata_.template get<1>(); }
+  SizeType<A>& GetSizeAndIsAllocated() { return metadata_.template get<1>(); }
 
-  const size_type& GetSizeAndIsAllocated() const {
+  const SizeType<A>& GetSizeAndIsAllocated() const {
     return metadata_.template get<1>();
   }
 
-  size_type GetSize() const { return GetSizeAndIsAllocated() >> 1; }
+  SizeType<A> GetSize() const { return GetSizeAndIsAllocated() >> 1; }
 
   bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; }
 
-  pointer GetAllocatedData() { return data_.allocated.allocated_data; }
+  Pointer<A> GetAllocatedData() { return data_.allocated.allocated_data; }
 
-  const_pointer GetAllocatedData() const {
+  ConstPointer<A> GetAllocatedData() const {
     return data_.allocated.allocated_data;
   }
 
-  pointer GetInlinedData() {
-    return reinterpret_cast<pointer>(
+  Pointer<A> GetInlinedData() {
+    return reinterpret_cast<Pointer<A>>(
         std::addressof(data_.inlined.inlined_data[0]));
   }
 
-  const_pointer GetInlinedData() const {
-    return reinterpret_cast<const_pointer>(
+  ConstPointer<A> GetInlinedData() const {
+    return reinterpret_cast<ConstPointer<A>>(
         std::addressof(data_.inlined.inlined_data[0]));
   }
 
-  size_type GetAllocatedCapacity() const {
+  SizeType<A> GetAllocatedCapacity() const {
     return data_.allocated.allocated_capacity;
   }
 
-  size_type GetInlinedCapacity() const { return static_cast<size_type>(N); }
+  SizeType<A> GetInlinedCapacity() const { return static_cast<SizeType<A>>(N); }
 
-  StorageView MakeStorageView() {
-    return GetIsAllocated()
-               ? StorageView{GetAllocatedData(), GetSize(),
-                             GetAllocatedCapacity()}
-               : StorageView{GetInlinedData(), GetSize(), GetInlinedCapacity()};
+  StorageView<A> MakeStorageView() {
+    return GetIsAllocated() ? StorageView<A>{GetAllocatedData(), GetSize(),
+                                             GetAllocatedCapacity()}
+                            : StorageView<A>{GetInlinedData(), GetSize(),
+                                             GetInlinedCapacity()};
   }
 
-  allocator_type* GetAllocPtr() {
-    return std::addressof(metadata_.template get<0>());
-  }
+  A& GetAllocator() { return metadata_.template get<0>(); }
 
-  const allocator_type* GetAllocPtr() const {
-    return std::addressof(metadata_.template get<0>());
-  }
+  const A& GetAllocator() const { return metadata_.template get<0>(); }
 
   // ---------------------------------------------------------------------------
   // Storage Member Mutators
@@ -395,74 +367,67 @@
   ABSL_ATTRIBUTE_NOINLINE void InitFrom(const Storage& other);
 
   template <typename ValueAdapter>
-  void Initialize(ValueAdapter values, size_type new_size);
+  void Initialize(ValueAdapter values, SizeType<A> new_size);
 
   template <typename ValueAdapter>
-  void Assign(ValueAdapter values, size_type new_size);
+  void Assign(ValueAdapter values, SizeType<A> new_size);
 
   template <typename ValueAdapter>
-  void Resize(ValueAdapter values, size_type new_size);
+  void Resize(ValueAdapter values, SizeType<A> new_size);
 
   template <typename ValueAdapter>
-  iterator Insert(const_iterator pos, ValueAdapter values,
-                  size_type insert_count);
+  Iterator<A> Insert(ConstIterator<A> pos, ValueAdapter values,
+                     SizeType<A> insert_count);
 
   template <typename... Args>
-  reference EmplaceBack(Args&&... args);
+  Reference<A> EmplaceBack(Args&&... args);
 
-  iterator Erase(const_iterator from, const_iterator to);
+  Iterator<A> Erase(ConstIterator<A> from, ConstIterator<A> to);
 
-  void Reserve(size_type requested_capacity);
+  void Reserve(SizeType<A> requested_capacity);
 
   void ShrinkToFit();
 
   void Swap(Storage* other_storage_ptr);
 
   void SetIsAllocated() {
-    GetSizeAndIsAllocated() |= static_cast<size_type>(1);
+    GetSizeAndIsAllocated() |= static_cast<SizeType<A>>(1);
   }
 
   void UnsetIsAllocated() {
-    GetSizeAndIsAllocated() &= ((std::numeric_limits<size_type>::max)() - 1);
+    GetSizeAndIsAllocated() &= ((std::numeric_limits<SizeType<A>>::max)() - 1);
   }
 
-  void SetSize(size_type size) {
+  void SetSize(SizeType<A> size) {
     GetSizeAndIsAllocated() =
-        (size << 1) | static_cast<size_type>(GetIsAllocated());
+        (size << 1) | static_cast<SizeType<A>>(GetIsAllocated());
   }
 
-  void SetAllocatedSize(size_type size) {
-    GetSizeAndIsAllocated() = (size << 1) | static_cast<size_type>(1);
+  void SetAllocatedSize(SizeType<A> size) {
+    GetSizeAndIsAllocated() = (size << 1) | static_cast<SizeType<A>>(1);
   }
 
-  void SetInlinedSize(size_type size) {
-    GetSizeAndIsAllocated() = size << static_cast<size_type>(1);
+  void SetInlinedSize(SizeType<A> size) {
+    GetSizeAndIsAllocated() = size << static_cast<SizeType<A>>(1);
   }
 
-  void AddSize(size_type count) {
-    GetSizeAndIsAllocated() += count << static_cast<size_type>(1);
+  void AddSize(SizeType<A> count) {
+    GetSizeAndIsAllocated() += count << static_cast<SizeType<A>>(1);
   }
 
-  void SubtractSize(size_type count) {
+  void SubtractSize(SizeType<A> count) {
     assert(count <= GetSize());
 
-    GetSizeAndIsAllocated() -= count << static_cast<size_type>(1);
+    GetSizeAndIsAllocated() -= count << static_cast<SizeType<A>>(1);
   }
 
-  void SetAllocatedData(pointer data, size_type capacity) {
-    data_.allocated.allocated_data = data;
-    data_.allocated.allocated_capacity = capacity;
-  }
-
-  void AcquireAllocatedData(AllocationTransaction* allocation_tx_ptr) {
-    SetAllocatedData(allocation_tx_ptr->GetData(),
-                     allocation_tx_ptr->GetCapacity());
-
-    allocation_tx_ptr->Reset();
+  void SetAllocation(Allocation<A> allocation) {
+    data_.allocated.allocated_data = allocation.data;
+    data_.allocated.allocated_capacity = allocation.capacity;
   }
 
   void MemcpyFrom(const Storage& other_storage) {
-    assert(IsMemcpyOk::value || other_storage.GetIsAllocated());
+    assert(IsMemcpyOk<A>::value || other_storage.GetIsAllocated());
 
     GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated();
     data_ = other_storage.data_;
@@ -470,24 +435,23 @@
 
   void DeallocateIfAllocated() {
     if (GetIsAllocated()) {
-      AllocatorTraits::deallocate(*GetAllocPtr(), GetAllocatedData(),
-                                  GetAllocatedCapacity());
+      MallocAdapter<A>::Deallocate(GetAllocator(), GetAllocatedData(),
+                                   GetAllocatedCapacity());
     }
   }
 
  private:
   ABSL_ATTRIBUTE_NOINLINE void DestroyContents();
 
-  using Metadata =
-      container_internal::CompressedTuple<allocator_type, size_type>;
+  using Metadata = container_internal::CompressedTuple<A, SizeType<A>>;
 
   struct Allocated {
-    pointer allocated_data;
-    size_type allocated_capacity;
+    Pointer<A> allocated_data;
+    SizeType<A> allocated_capacity;
   };
 
   struct Inlined {
-    alignas(value_type) char inlined_data[sizeof(value_type[N])];
+    alignas(ValueType<A>) char inlined_data[sizeof(ValueType<A>[N])];
   };
 
   union Data {
@@ -496,7 +460,7 @@
   };
 
   template <typename... Args>
-  ABSL_ATTRIBUTE_NOINLINE reference EmplaceBackSlow(Args&&... args);
+  ABSL_ATTRIBUTE_NOINLINE Reference<A> EmplaceBackSlow(Args&&... args);
 
   Metadata metadata_;
   Data data_;
@@ -504,17 +468,17 @@
 
 template <typename T, size_t N, typename A>
 void Storage<T, N, A>::DestroyContents() {
-  pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData();
-  inlined_vector_internal::DestroyElements(GetAllocPtr(), data, GetSize());
+  Pointer<A> data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData();
+  DestroyElements<A>(GetAllocator(), data, GetSize());
   DeallocateIfAllocated();
 }
 
 template <typename T, size_t N, typename A>
 void Storage<T, N, A>::InitFrom(const Storage& other) {
-  const auto n = other.GetSize();
+  const SizeType<A> n = other.GetSize();
   assert(n > 0);  // Empty sources handled handled in caller.
-  const_pointer src;
-  pointer dst;
+  ConstPointer<A> src;
+  Pointer<A> dst;
   if (!other.GetIsAllocated()) {
     dst = GetInlinedData();
     src = other.GetInlinedData();
@@ -522,43 +486,48 @@
     // Because this is only called from the `InlinedVector` constructors, it's
     // safe to take on the allocation with size `0`. If `ConstructElements(...)`
     // throws, deallocation will be automatically handled by `~Storage()`.
-    size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), n);
-    dst = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity);
-    SetAllocatedData(dst, new_capacity);
+    SizeType<A> requested_capacity = ComputeCapacity(GetInlinedCapacity(), n);
+    Allocation<A> allocation =
+        MallocAdapter<A>::Allocate(GetAllocator(), requested_capacity);
+    SetAllocation(allocation);
+    dst = allocation.data;
     src = other.GetAllocatedData();
   }
-  if (IsMemcpyOk::value) {
-    MemcpyIfAllowed<IsMemcpyOk::value>(dst, src, sizeof(dst[0]) * n);
+  if (IsMemcpyOk<A>::value) {
+    std::memcpy(reinterpret_cast<char*>(dst),
+                reinterpret_cast<const char*>(src), n * sizeof(ValueType<A>));
   } else {
-    auto values = IteratorValueAdapter<const_pointer>(src);
-    inlined_vector_internal::ConstructElements(GetAllocPtr(), dst, &values, n);
+    auto values = IteratorValueAdapter<A, ConstPointer<A>>(src);
+    ConstructElements<A>(GetAllocator(), dst, values, n);
   }
   GetSizeAndIsAllocated() = other.GetSizeAndIsAllocated();
 }
 
 template <typename T, size_t N, typename A>
 template <typename ValueAdapter>
-auto Storage<T, N, A>::Initialize(ValueAdapter values, size_type new_size)
+auto Storage<T, N, A>::Initialize(ValueAdapter values, SizeType<A> new_size)
     -> void {
   // Only callable from constructors!
   assert(!GetIsAllocated());
   assert(GetSize() == 0);
 
-  pointer construct_data;
+  Pointer<A> construct_data;
   if (new_size > GetInlinedCapacity()) {
     // Because this is only called from the `InlinedVector` constructors, it's
     // safe to take on the allocation with size `0`. If `ConstructElements(...)`
     // throws, deallocation will be automatically handled by `~Storage()`.
-    size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), new_size);
-    construct_data = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity);
-    SetAllocatedData(construct_data, new_capacity);
+    SizeType<A> requested_capacity =
+        ComputeCapacity(GetInlinedCapacity(), new_size);
+    Allocation<A> allocation =
+        MallocAdapter<A>::Allocate(GetAllocator(), requested_capacity);
+    construct_data = allocation.data;
+    SetAllocation(allocation);
     SetIsAllocated();
   } else {
     construct_data = GetInlinedData();
   }
 
-  inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data,
-                                             &values, new_size);
+  ConstructElements<A>(GetAllocator(), construct_data, values, new_size);
 
   // Since the initial size was guaranteed to be `0` and the allocated bit is
   // already correct for either case, *adding* `new_size` gives us the correct
@@ -568,18 +537,20 @@
 
 template <typename T, size_t N, typename A>
 template <typename ValueAdapter>
-auto Storage<T, N, A>::Assign(ValueAdapter values, size_type new_size) -> void {
-  StorageView storage_view = MakeStorageView();
+auto Storage<T, N, A>::Assign(ValueAdapter values, SizeType<A> new_size)
+    -> void {
+  StorageView<A> storage_view = MakeStorageView();
 
-  AllocationTransaction allocation_tx(GetAllocPtr());
+  AllocationTransaction<A> allocation_tx(GetAllocator());
 
-  absl::Span<value_type> assign_loop;
-  absl::Span<value_type> construct_loop;
-  absl::Span<value_type> destroy_loop;
+  absl::Span<ValueType<A>> assign_loop;
+  absl::Span<ValueType<A>> construct_loop;
+  absl::Span<ValueType<A>> destroy_loop;
 
   if (new_size > storage_view.capacity) {
-    size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size);
-    construct_loop = {allocation_tx.Allocate(new_capacity), new_size};
+    SizeType<A> requested_capacity =
+        ComputeCapacity(storage_view.capacity, new_size);
+    construct_loop = {allocation_tx.Allocate(requested_capacity), new_size};
     destroy_loop = {storage_view.data, storage_view.size};
   } else if (new_size > storage_view.size) {
     assign_loop = {storage_view.data, storage_view.size};
@@ -590,18 +561,16 @@
     destroy_loop = {storage_view.data + new_size, storage_view.size - new_size};
   }
 
-  inlined_vector_internal::AssignElements(assign_loop.data(), &values,
-                                          assign_loop.size());
+  AssignElements<A>(assign_loop.data(), values, assign_loop.size());
 
-  inlined_vector_internal::ConstructElements(
-      GetAllocPtr(), construct_loop.data(), &values, construct_loop.size());
+  ConstructElements<A>(GetAllocator(), construct_loop.data(), values,
+                       construct_loop.size());
 
-  inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(),
-                                           destroy_loop.size());
+  DestroyElements<A>(GetAllocator(), destroy_loop.data(), destroy_loop.size());
 
   if (allocation_tx.DidAllocate()) {
     DeallocateIfAllocated();
-    AcquireAllocatedData(&allocation_tx);
+    SetAllocation(std::move(allocation_tx).Release());
     SetIsAllocated();
   }
 
@@ -610,19 +579,18 @@
 
 template <typename T, size_t N, typename A>
 template <typename ValueAdapter>
-auto Storage<T, N, A>::Resize(ValueAdapter values, size_type new_size) -> void {
-  StorageView storage_view = MakeStorageView();
-  auto* const base = storage_view.data;
-  const size_type size = storage_view.size;
-  auto* alloc = GetAllocPtr();
+auto Storage<T, N, A>::Resize(ValueAdapter values, SizeType<A> new_size)
+    -> void {
+  StorageView<A> storage_view = MakeStorageView();
+  Pointer<A> const base = storage_view.data;
+  const SizeType<A> size = storage_view.size;
+  A& alloc = GetAllocator();
   if (new_size <= size) {
     // Destroy extra old elements.
-    inlined_vector_internal::DestroyElements(alloc, base + new_size,
-                                             size - new_size);
+    DestroyElements<A>(alloc, base + new_size, size - new_size);
   } else if (new_size <= storage_view.capacity) {
     // Construct new elements in place.
-    inlined_vector_internal::ConstructElements(alloc, base + size, &values,
-                                               new_size - size);
+    ConstructElements<A>(alloc, base + size, values, new_size - size);
   } else {
     // Steps:
     //  a. Allocate new backing store.
@@ -631,21 +599,22 @@
     //  d. Destroy all elements in old backing store.
     // Use transactional wrappers for the first two steps so we can roll
     // back if necessary due to exceptions.
-    AllocationTransaction allocation_tx(alloc);
-    size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size);
-    pointer new_data = allocation_tx.Allocate(new_capacity);
+    AllocationTransaction<A> allocation_tx(alloc);
+    SizeType<A> requested_capacity =
+        ComputeCapacity(storage_view.capacity, new_size);
+    Pointer<A> new_data = allocation_tx.Allocate(requested_capacity);
 
-    ConstructionTransaction construction_tx(alloc);
-    construction_tx.Construct(new_data + size, &values, new_size - size);
+    ConstructionTransaction<A> construction_tx(alloc);
+    construction_tx.Construct(new_data + size, values, new_size - size);
 
-    IteratorValueAdapter<MoveIterator> move_values((MoveIterator(base)));
-    inlined_vector_internal::ConstructElements(alloc, new_data, &move_values,
-                                               size);
+    IteratorValueAdapter<A, MoveIterator<A>> move_values(
+        (MoveIterator<A>(base)));
+    ConstructElements<A>(alloc, new_data, move_values, size);
 
-    inlined_vector_internal::DestroyElements(alloc, base, size);
-    construction_tx.Commit();
+    DestroyElements<A>(alloc, base, size);
+    std::move(construction_tx).Commit();
     DeallocateIfAllocated();
-    AcquireAllocatedData(&allocation_tx);
+    SetAllocation(std::move(allocation_tx).Release());
     SetIsAllocated();
   }
   SetSize(new_size);
@@ -653,76 +622,76 @@
 
 template <typename T, size_t N, typename A>
 template <typename ValueAdapter>
-auto Storage<T, N, A>::Insert(const_iterator pos, ValueAdapter values,
-                              size_type insert_count) -> iterator {
-  StorageView storage_view = MakeStorageView();
+auto Storage<T, N, A>::Insert(ConstIterator<A> pos, ValueAdapter values,
+                              SizeType<A> insert_count) -> Iterator<A> {
+  StorageView<A> storage_view = MakeStorageView();
 
-  size_type insert_index =
-      std::distance(const_iterator(storage_view.data), pos);
-  size_type insert_end_index = insert_index + insert_count;
-  size_type new_size = storage_view.size + insert_count;
+  SizeType<A> insert_index =
+      std::distance(ConstIterator<A>(storage_view.data), pos);
+  SizeType<A> insert_end_index = insert_index + insert_count;
+  SizeType<A> new_size = storage_view.size + insert_count;
 
   if (new_size > storage_view.capacity) {
-    AllocationTransaction allocation_tx(GetAllocPtr());
-    ConstructionTransaction construction_tx(GetAllocPtr());
-    ConstructionTransaction move_construciton_tx(GetAllocPtr());
+    AllocationTransaction<A> allocation_tx(GetAllocator());
+    ConstructionTransaction<A> construction_tx(GetAllocator());
+    ConstructionTransaction<A> move_construction_tx(GetAllocator());
 
-    IteratorValueAdapter<MoveIterator> move_values(
-        MoveIterator(storage_view.data));
+    IteratorValueAdapter<A, MoveIterator<A>> move_values(
+        MoveIterator<A>(storage_view.data));
 
-    size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size);
-    pointer new_data = allocation_tx.Allocate(new_capacity);
+    SizeType<A> requested_capacity =
+        ComputeCapacity(storage_view.capacity, new_size);
+    Pointer<A> new_data = allocation_tx.Allocate(requested_capacity);
 
-    construction_tx.Construct(new_data + insert_index, &values, insert_count);
+    construction_tx.Construct(new_data + insert_index, values, insert_count);
 
-    move_construciton_tx.Construct(new_data, &move_values, insert_index);
+    move_construction_tx.Construct(new_data, move_values, insert_index);
 
-    inlined_vector_internal::ConstructElements(
-        GetAllocPtr(), new_data + insert_end_index, &move_values,
-        storage_view.size - insert_index);
+    ConstructElements<A>(GetAllocator(), new_data + insert_end_index,
+                         move_values, storage_view.size - insert_index);
 
-    inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data,
-                                             storage_view.size);
+    DestroyElements<A>(GetAllocator(), storage_view.data, storage_view.size);
 
-    construction_tx.Commit();
-    move_construciton_tx.Commit();
+    std::move(construction_tx).Commit();
+    std::move(move_construction_tx).Commit();
     DeallocateIfAllocated();
-    AcquireAllocatedData(&allocation_tx);
+    SetAllocation(std::move(allocation_tx).Release());
 
     SetAllocatedSize(new_size);
-    return iterator(new_data + insert_index);
+    return Iterator<A>(new_data + insert_index);
   } else {
-    size_type move_construction_destination_index =
+    SizeType<A> move_construction_destination_index =
         (std::max)(insert_end_index, storage_view.size);
 
-    ConstructionTransaction move_construction_tx(GetAllocPtr());
+    ConstructionTransaction<A> move_construction_tx(GetAllocator());
 
-    IteratorValueAdapter<MoveIterator> move_construction_values(
-        MoveIterator(storage_view.data +
-                     (move_construction_destination_index - insert_count)));
-    absl::Span<value_type> move_construction = {
+    IteratorValueAdapter<A, MoveIterator<A>> move_construction_values(
+        MoveIterator<A>(storage_view.data +
+                        (move_construction_destination_index - insert_count)));
+    absl::Span<ValueType<A>> move_construction = {
         storage_view.data + move_construction_destination_index,
         new_size - move_construction_destination_index};
 
-    pointer move_assignment_values = storage_view.data + insert_index;
-    absl::Span<value_type> move_assignment = {
+    Pointer<A> move_assignment_values = storage_view.data + insert_index;
+    absl::Span<ValueType<A>> move_assignment = {
         storage_view.data + insert_end_index,
         move_construction_destination_index - insert_end_index};
 
-    absl::Span<value_type> insert_assignment = {move_assignment_values,
-                                                move_construction.size()};
+    absl::Span<ValueType<A>> insert_assignment = {move_assignment_values,
+                                                  move_construction.size()};
 
-    absl::Span<value_type> insert_construction = {
+    absl::Span<ValueType<A>> insert_construction = {
         insert_assignment.data() + insert_assignment.size(),
         insert_count - insert_assignment.size()};
 
     move_construction_tx.Construct(move_construction.data(),
-                                   &move_construction_values,
+                                   move_construction_values,
                                    move_construction.size());
 
-    for (pointer destination = move_assignment.data() + move_assignment.size(),
-                 last_destination = move_assignment.data(),
-                 source = move_assignment_values + move_assignment.size();
+    for (Pointer<A>
+             destination = move_assignment.data() + move_assignment.size(),
+             last_destination = move_assignment.data(),
+             source = move_assignment_values + move_assignment.size();
          ;) {
       --destination;
       --source;
@@ -730,30 +699,29 @@
       *destination = std::move(*source);
     }
 
-    inlined_vector_internal::AssignElements(insert_assignment.data(), &values,
-                                            insert_assignment.size());
+    AssignElements<A>(insert_assignment.data(), values,
+                      insert_assignment.size());
 
-    inlined_vector_internal::ConstructElements(
-        GetAllocPtr(), insert_construction.data(), &values,
-        insert_construction.size());
+    ConstructElements<A>(GetAllocator(), insert_construction.data(), values,
+                         insert_construction.size());
 
-    move_construction_tx.Commit();
+    std::move(move_construction_tx).Commit();
 
     AddSize(insert_count);
-    return iterator(storage_view.data + insert_index);
+    return Iterator<A>(storage_view.data + insert_index);
   }
 }
 
 template <typename T, size_t N, typename A>
 template <typename... Args>
-auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> reference {
-  StorageView storage_view = MakeStorageView();
-  const auto n = storage_view.size;
+auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> Reference<A> {
+  StorageView<A> storage_view = MakeStorageView();
+  const SizeType<A> n = storage_view.size;
   if (ABSL_PREDICT_TRUE(n != storage_view.capacity)) {
     // Fast path; new element fits.
-    pointer last_ptr = storage_view.data + n;
-    AllocatorTraits::construct(*GetAllocPtr(), last_ptr,
-                               std::forward<Args>(args)...);
+    Pointer<A> last_ptr = storage_view.data + n;
+    AllocatorTraits<A>::construct(GetAllocator(), last_ptr,
+                                  std::forward<Args>(args)...);
     AddSize(1);
     return *last_ptr;
   }
@@ -763,87 +731,83 @@
 
 template <typename T, size_t N, typename A>
 template <typename... Args>
-auto Storage<T, N, A>::EmplaceBackSlow(Args&&... args) -> reference {
-  StorageView storage_view = MakeStorageView();
-  AllocationTransaction allocation_tx(GetAllocPtr());
-  IteratorValueAdapter<MoveIterator> move_values(
-      MoveIterator(storage_view.data));
-  size_type new_capacity = NextCapacity(storage_view.capacity);
-  pointer construct_data = allocation_tx.Allocate(new_capacity);
-  pointer last_ptr = construct_data + storage_view.size;
+auto Storage<T, N, A>::EmplaceBackSlow(Args&&... args) -> Reference<A> {
+  StorageView<A> storage_view = MakeStorageView();
+  AllocationTransaction<A> allocation_tx(GetAllocator());
+  IteratorValueAdapter<A, MoveIterator<A>> move_values(
+      MoveIterator<A>(storage_view.data));
+  SizeType<A> requested_capacity = NextCapacity(storage_view.capacity);
+  Pointer<A> construct_data = allocation_tx.Allocate(requested_capacity);
+  Pointer<A> last_ptr = construct_data + storage_view.size;
 
   // Construct new element.
-  AllocatorTraits::construct(*GetAllocPtr(), last_ptr,
-                             std::forward<Args>(args)...);
+  AllocatorTraits<A>::construct(GetAllocator(), last_ptr,
+                                std::forward<Args>(args)...);
   // Move elements from old backing store to new backing store.
   ABSL_INTERNAL_TRY {
-    inlined_vector_internal::ConstructElements(
-        GetAllocPtr(), allocation_tx.GetData(), &move_values,
-        storage_view.size);
+    ConstructElements<A>(GetAllocator(), allocation_tx.GetData(), move_values,
+                         storage_view.size);
   }
   ABSL_INTERNAL_CATCH_ANY {
-    AllocatorTraits::destroy(*GetAllocPtr(), last_ptr);
+    AllocatorTraits<A>::destroy(GetAllocator(), last_ptr);
     ABSL_INTERNAL_RETHROW;
   }
   // Destroy elements in old backing store.
-  inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data,
-                                           storage_view.size);
+  DestroyElements<A>(GetAllocator(), storage_view.data, storage_view.size);
 
   DeallocateIfAllocated();
-  AcquireAllocatedData(&allocation_tx);
+  SetAllocation(std::move(allocation_tx).Release());
   SetIsAllocated();
   AddSize(1);
   return *last_ptr;
 }
 
 template <typename T, size_t N, typename A>
-auto Storage<T, N, A>::Erase(const_iterator from, const_iterator to)
-    -> iterator {
-  StorageView storage_view = MakeStorageView();
+auto Storage<T, N, A>::Erase(ConstIterator<A> from, ConstIterator<A> to)
+    -> Iterator<A> {
+  StorageView<A> storage_view = MakeStorageView();
 
-  size_type erase_size = std::distance(from, to);
-  size_type erase_index =
-      std::distance(const_iterator(storage_view.data), from);
-  size_type erase_end_index = erase_index + erase_size;
+  SizeType<A> erase_size = std::distance(from, to);
+  SizeType<A> erase_index =
+      std::distance(ConstIterator<A>(storage_view.data), from);
+  SizeType<A> erase_end_index = erase_index + erase_size;
 
-  IteratorValueAdapter<MoveIterator> move_values(
-      MoveIterator(storage_view.data + erase_end_index));
+  IteratorValueAdapter<A, MoveIterator<A>> move_values(
+      MoveIterator<A>(storage_view.data + erase_end_index));
 
-  inlined_vector_internal::AssignElements(storage_view.data + erase_index,
-                                          &move_values,
-                                          storage_view.size - erase_end_index);
+  AssignElements<A>(storage_view.data + erase_index, move_values,
+                    storage_view.size - erase_end_index);
 
-  inlined_vector_internal::DestroyElements(
-      GetAllocPtr(), storage_view.data + (storage_view.size - erase_size),
-      erase_size);
+  DestroyElements<A>(GetAllocator(),
+                     storage_view.data + (storage_view.size - erase_size),
+                     erase_size);
 
   SubtractSize(erase_size);
-  return iterator(storage_view.data + erase_index);
+  return Iterator<A>(storage_view.data + erase_index);
 }
 
 template <typename T, size_t N, typename A>
-auto Storage<T, N, A>::Reserve(size_type requested_capacity) -> void {
-  StorageView storage_view = MakeStorageView();
+auto Storage<T, N, A>::Reserve(SizeType<A> requested_capacity) -> void {
+  StorageView<A> storage_view = MakeStorageView();
 
   if (ABSL_PREDICT_FALSE(requested_capacity <= storage_view.capacity)) return;
 
-  AllocationTransaction allocation_tx(GetAllocPtr());
+  AllocationTransaction<A> allocation_tx(GetAllocator());
 
-  IteratorValueAdapter<MoveIterator> move_values(
-      MoveIterator(storage_view.data));
+  IteratorValueAdapter<A, MoveIterator<A>> move_values(
+      MoveIterator<A>(storage_view.data));
 
-  size_type new_capacity =
+  SizeType<A> new_requested_capacity =
       ComputeCapacity(storage_view.capacity, requested_capacity);
-  pointer new_data = allocation_tx.Allocate(new_capacity);
+  Pointer<A> new_data = allocation_tx.Allocate(new_requested_capacity);
 
-  inlined_vector_internal::ConstructElements(GetAllocPtr(), new_data,
-                                             &move_values, storage_view.size);
+  ConstructElements<A>(GetAllocator(), new_data, move_values,
+                       storage_view.size);
 
-  inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data,
-                                           storage_view.size);
+  DestroyElements<A>(GetAllocator(), storage_view.data, storage_view.size);
 
   DeallocateIfAllocated();
-  AcquireAllocatedData(&allocation_tx);
+  SetAllocation(std::move(allocation_tx).Release());
   SetIsAllocated();
 }
 
@@ -852,41 +816,44 @@
   // May only be called on allocated instances!
   assert(GetIsAllocated());
 
-  StorageView storage_view{GetAllocatedData(), GetSize(),
-                           GetAllocatedCapacity()};
+  StorageView<A> storage_view{GetAllocatedData(), GetSize(),
+                              GetAllocatedCapacity()};
 
   if (ABSL_PREDICT_FALSE(storage_view.size == storage_view.capacity)) return;
 
-  AllocationTransaction allocation_tx(GetAllocPtr());
+  AllocationTransaction<A> allocation_tx(GetAllocator());
 
-  IteratorValueAdapter<MoveIterator> move_values(
-      MoveIterator(storage_view.data));
+  IteratorValueAdapter<A, MoveIterator<A>> move_values(
+      MoveIterator<A>(storage_view.data));
 
-  pointer construct_data;
+  Pointer<A> construct_data;
   if (storage_view.size > GetInlinedCapacity()) {
-    size_type new_capacity = storage_view.size;
-    construct_data = allocation_tx.Allocate(new_capacity);
+    SizeType<A> requested_capacity = storage_view.size;
+    construct_data = allocation_tx.Allocate(requested_capacity);
+    if (allocation_tx.GetCapacity() >= storage_view.capacity) {
+      // Already using the smallest available heap allocation.
+      return;
+    }
   } else {
     construct_data = GetInlinedData();
   }
 
   ABSL_INTERNAL_TRY {
-    inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data,
-                                               &move_values, storage_view.size);
+    ConstructElements<A>(GetAllocator(), construct_data, move_values,
+                         storage_view.size);
   }
   ABSL_INTERNAL_CATCH_ANY {
-    SetAllocatedData(storage_view.data, storage_view.capacity);
+    SetAllocation({storage_view.data, storage_view.capacity});
     ABSL_INTERNAL_RETHROW;
   }
 
-  inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data,
-                                           storage_view.size);
+  DestroyElements<A>(GetAllocator(), storage_view.data, storage_view.size);
 
-  AllocatorTraits::deallocate(*GetAllocPtr(), storage_view.data,
-                              storage_view.capacity);
+  MallocAdapter<A>::Deallocate(GetAllocator(), storage_view.data,
+                               storage_view.capacity);
 
   if (allocation_tx.DidAllocate()) {
-    AcquireAllocatedData(&allocation_tx);
+    SetAllocation(std::move(allocation_tx).Release());
   } else {
     UnsetIsAllocated();
   }
@@ -904,58 +871,56 @@
     Storage* large_ptr = other_storage_ptr;
     if (small_ptr->GetSize() > large_ptr->GetSize()) swap(small_ptr, large_ptr);
 
-    for (size_type i = 0; i < small_ptr->GetSize(); ++i) {
+    for (SizeType<A> i = 0; i < small_ptr->GetSize(); ++i) {
       swap(small_ptr->GetInlinedData()[i], large_ptr->GetInlinedData()[i]);
     }
 
-    IteratorValueAdapter<MoveIterator> move_values(
-        MoveIterator(large_ptr->GetInlinedData() + small_ptr->GetSize()));
+    IteratorValueAdapter<A, MoveIterator<A>> move_values(
+        MoveIterator<A>(large_ptr->GetInlinedData() + small_ptr->GetSize()));
 
-    inlined_vector_internal::ConstructElements(
-        large_ptr->GetAllocPtr(),
-        small_ptr->GetInlinedData() + small_ptr->GetSize(), &move_values,
-        large_ptr->GetSize() - small_ptr->GetSize());
+    ConstructElements<A>(large_ptr->GetAllocator(),
+                         small_ptr->GetInlinedData() + small_ptr->GetSize(),
+                         move_values,
+                         large_ptr->GetSize() - small_ptr->GetSize());
 
-    inlined_vector_internal::DestroyElements(
-        large_ptr->GetAllocPtr(),
-        large_ptr->GetInlinedData() + small_ptr->GetSize(),
-        large_ptr->GetSize() - small_ptr->GetSize());
+    DestroyElements<A>(large_ptr->GetAllocator(),
+                       large_ptr->GetInlinedData() + small_ptr->GetSize(),
+                       large_ptr->GetSize() - small_ptr->GetSize());
   } else {
     Storage* allocated_ptr = this;
     Storage* inlined_ptr = other_storage_ptr;
     if (!allocated_ptr->GetIsAllocated()) swap(allocated_ptr, inlined_ptr);
 
-    StorageView allocated_storage_view{allocated_ptr->GetAllocatedData(),
-                                       allocated_ptr->GetSize(),
-                                       allocated_ptr->GetAllocatedCapacity()};
+    StorageView<A> allocated_storage_view{
+        allocated_ptr->GetAllocatedData(), allocated_ptr->GetSize(),
+        allocated_ptr->GetAllocatedCapacity()};
 
-    IteratorValueAdapter<MoveIterator> move_values(
-        MoveIterator(inlined_ptr->GetInlinedData()));
+    IteratorValueAdapter<A, MoveIterator<A>> move_values(
+        MoveIterator<A>(inlined_ptr->GetInlinedData()));
 
     ABSL_INTERNAL_TRY {
-      inlined_vector_internal::ConstructElements(
-          inlined_ptr->GetAllocPtr(), allocated_ptr->GetInlinedData(),
-          &move_values, inlined_ptr->GetSize());
+      ConstructElements<A>(inlined_ptr->GetAllocator(),
+                           allocated_ptr->GetInlinedData(), move_values,
+                           inlined_ptr->GetSize());
     }
     ABSL_INTERNAL_CATCH_ANY {
-      allocated_ptr->SetAllocatedData(allocated_storage_view.data,
-                                      allocated_storage_view.capacity);
+      allocated_ptr->SetAllocation(
+          {allocated_storage_view.data, allocated_storage_view.capacity});
       ABSL_INTERNAL_RETHROW;
     }
 
-    inlined_vector_internal::DestroyElements(inlined_ptr->GetAllocPtr(),
-                                             inlined_ptr->GetInlinedData(),
-                                             inlined_ptr->GetSize());
+    DestroyElements<A>(inlined_ptr->GetAllocator(),
+                       inlined_ptr->GetInlinedData(), inlined_ptr->GetSize());
 
-    inlined_ptr->SetAllocatedData(allocated_storage_view.data,
-                                  allocated_storage_view.capacity);
+    inlined_ptr->SetAllocation(
+        {allocated_storage_view.data, allocated_storage_view.capacity});
   }
 
   swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated());
-  swap(*GetAllocPtr(), *other_storage_ptr->GetAllocPtr());
+  swap(GetAllocator(), other_storage_ptr->GetAllocator());
 }
 
-// End ignore "maybe-uninitialized"
+// End ignore "array-bounds" and "maybe-uninitialized"
 #if !defined(__clang__) && defined(__GNUC__)
 #pragma GCC diagnostic pop
 #endif
diff --git a/absl/container/internal/layout_test.cc b/absl/container/internal/layout_test.cc
index 1d7158f..54e5d5b 100644
--- a/absl/container/internal/layout_test.cc
+++ b/absl/container/internal/layout_test.cc
@@ -1350,7 +1350,13 @@
 TEST(Layout, OverAligned) {
   constexpr size_t M = alignof(max_align_t);
   constexpr Layout<unsigned char, Aligned<unsigned char, 2 * M>> x(1, 3);
+#ifdef __GNUC__
+  // Using __attribute__ ((aligned ())) instead of alignas to bypass a gcc bug:
+  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89357
+  __attribute__((aligned(2 * M))) unsigned char p[x.AllocSize()];
+#else
   alignas(2 * M) unsigned char p[x.AllocSize()];
+#endif
   EXPECT_EQ(2 * M + 3, x.AllocSize());
   EXPECT_THAT(x.Pointers(p), Tuple(p + 0, p + 2 * M));
 }
diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h
index 0a02757..c7df2ef 100644
--- a/absl/container/internal/raw_hash_map.h
+++ b/absl/container/internal/raw_hash_map.h
@@ -51,8 +51,9 @@
   using key_arg = typename KeyArgImpl::template type<K, key_type>;
 
   static_assert(!std::is_reference<key_type>::value, "");
-  // TODO(alkis): remove this assertion and verify that reference mapped_type is
-  // supported.
+
+  // TODO(b/187807849): Evaluate whether to support reference mapped_type and
+  // remove this assertion if/when it is supported.
   static_assert(!std::is_reference<mapped_type>::value, "");
 
   using iterator = typename raw_hash_map::raw_hash_set::iterator;
diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc
index bfef071..687bcb8 100644
--- a/absl/container/internal/raw_hash_set.cc
+++ b/absl/container/internal/raw_hash_set.cc
@@ -23,6 +23,12 @@
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
 
+alignas(16) ABSL_CONST_INIT ABSL_DLL const ctrl_t kEmptyGroup[16] = {
+    ctrl_t::kSentinel, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
+    ctrl_t::kEmpty,    ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
+    ctrl_t::kEmpty,    ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
+    ctrl_t::kEmpty,    ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty};
+
 constexpr size_t Group::kWidth;
 
 // Returns "random" seed.
@@ -37,24 +43,24 @@
   return value ^ static_cast<size_t>(reinterpret_cast<uintptr_t>(&counter));
 }
 
-bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl) {
+bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl) {
   // To avoid problems with weak hashes and single bit tests, we use % 13.
   // TODO(kfm,sbenza): revisit after we do unconditional mixing
   return (H1(hash, ctrl) ^ RandomSeed()) % 13 > 6;
 }
 
-void ConvertDeletedToEmptyAndFullToDeleted(
-    ctrl_t* ctrl, size_t capacity) {
-  assert(ctrl[capacity] == kSentinel);
+void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity) {
+  assert(ctrl[capacity] == ctrl_t::kSentinel);
   assert(IsValidCapacity(capacity));
-  for (ctrl_t* pos = ctrl; pos != ctrl + capacity + 1; pos += Group::kWidth) {
+  for (ctrl_t* pos = ctrl; pos < ctrl + capacity; pos += Group::kWidth) {
     Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos);
   }
   // Copy the cloned ctrl bytes.
-  std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth);
-  ctrl[capacity] = kSentinel;
+  std::memcpy(ctrl + capacity + 1, ctrl, NumClonedBytes());
+  ctrl[capacity] = ctrl_t::kSentinel;
 }
-
+// Extern template instantiotion for inline function.
+template FindInfo find_first_non_full(const ctrl_t*, size_t, size_t);
 
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h
index 8615de8..12682b3 100644
--- a/absl/container/internal/raw_hash_set.h
+++ b/absl/container/internal/raw_hash_set.h
@@ -87,6 +87,17 @@
 //
 // This probing function guarantees that after N probes, all the groups of the
 // table will be probed exactly once.
+//
+// The control state and slot array are stored contiguously in a shared heap
+// allocation. The layout of this allocation is: `capacity()` control bytes,
+// one sentinel control byte, `Group::kWidth - 1` cloned control bytes,
+// <possible padding>, `capacity()` slots. The sentinel control byte is used in
+// iteration so we know when we reach the end of the table. The cloned control
+// bytes at the end of the table are cloned from the beginning of the table so
+// groups that begin near the end of the table can see a full group. In cases in
+// which there are more than `capacity()` cloned control bytes, the extra bytes
+// are `kEmpty`, and these ensure that we always see at least one empty slot and
+// can stop an unsuccessful search.
 
 #ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
 #define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
@@ -112,7 +123,6 @@
 #include "absl/container/internal/hashtable_debug_hooks.h"
 #include "absl/container/internal/hashtablez_sampler.h"
 #include "absl/container/internal/have_sse.h"
-#include "absl/container/internal/layout.h"
 #include "absl/memory/memory.h"
 #include "absl/meta/type_traits.h"
 #include "absl/numeric/bits.h"
@@ -252,48 +262,53 @@
   T mask_;
 };
 
-using ctrl_t = signed char;
 using h2_t = uint8_t;
 
 // The values here are selected for maximum performance. See the static asserts
-// below for details.
-enum Ctrl : ctrl_t {
+// below for details. We use an enum class so that when strict aliasing is
+// enabled, the compiler knows ctrl_t doesn't alias other types.
+enum class ctrl_t : int8_t {
   kEmpty = -128,   // 0b10000000
   kDeleted = -2,   // 0b11111110
   kSentinel = -1,  // 0b11111111
 };
 static_assert(
-    kEmpty & kDeleted & kSentinel & 0x80,
+    (static_cast<int8_t>(ctrl_t::kEmpty) &
+     static_cast<int8_t>(ctrl_t::kDeleted) &
+     static_cast<int8_t>(ctrl_t::kSentinel) & 0x80) != 0,
     "Special markers need to have the MSB to make checking for them efficient");
-static_assert(kEmpty < kSentinel && kDeleted < kSentinel,
-              "kEmpty and kDeleted must be smaller than kSentinel to make the "
-              "SIMD test of IsEmptyOrDeleted() efficient");
-static_assert(kSentinel == -1,
-              "kSentinel must be -1 to elide loading it from memory into SIMD "
-              "registers (pcmpeqd xmm, xmm)");
-static_assert(kEmpty == -128,
-              "kEmpty must be -128 to make the SIMD check for its "
+static_assert(
+    ctrl_t::kEmpty < ctrl_t::kSentinel && ctrl_t::kDeleted < ctrl_t::kSentinel,
+    "ctrl_t::kEmpty and ctrl_t::kDeleted must be smaller than "
+    "ctrl_t::kSentinel to make the SIMD test of IsEmptyOrDeleted() efficient");
+static_assert(
+    ctrl_t::kSentinel == static_cast<ctrl_t>(-1),
+    "ctrl_t::kSentinel must be -1 to elide loading it from memory into SIMD "
+    "registers (pcmpeqd xmm, xmm)");
+static_assert(ctrl_t::kEmpty == static_cast<ctrl_t>(-128),
+              "ctrl_t::kEmpty must be -128 to make the SIMD check for its "
               "existence efficient (psignb xmm, xmm)");
-static_assert(~kEmpty & ~kDeleted & kSentinel & 0x7F,
-              "kEmpty and kDeleted must share an unset bit that is not shared "
-              "by kSentinel to make the scalar test for MatchEmptyOrDeleted() "
-              "efficient");
-static_assert(kDeleted == -2,
-              "kDeleted must be -2 to make the implementation of "
+static_assert(
+    (~static_cast<int8_t>(ctrl_t::kEmpty) &
+     ~static_cast<int8_t>(ctrl_t::kDeleted) &
+     static_cast<int8_t>(ctrl_t::kSentinel) & 0x7F) != 0,
+    "ctrl_t::kEmpty and ctrl_t::kDeleted must share an unset bit that is not "
+    "shared by ctrl_t::kSentinel to make the scalar test for "
+    "MatchEmptyOrDeleted() efficient");
+static_assert(ctrl_t::kDeleted == static_cast<ctrl_t>(-2),
+              "ctrl_t::kDeleted must be -2 to make the implementation of "
               "ConvertSpecialToEmptyAndFullToDeleted efficient");
 
 // A single block of empty control bytes for tables without any slots allocated.
 // This enables removing a branch in the hot path of find().
+ABSL_DLL extern const ctrl_t kEmptyGroup[16];
 inline ctrl_t* EmptyGroup() {
-  alignas(16) static constexpr ctrl_t empty_group[] = {
-      kSentinel, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,
-      kEmpty,    kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty};
-  return const_cast<ctrl_t*>(empty_group);
+  return const_cast<ctrl_t*>(kEmptyGroup);
 }
 
 // Mixes a randomly generated per-process seed with `hash` and `ctrl` to
 // randomize insertion order within groups.
-bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl);
+bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl);
 
 // Returns a hash seed.
 //
@@ -309,12 +324,12 @@
 inline size_t H1(size_t hash, const ctrl_t* ctrl) {
   return (hash >> 7) ^ HashSeed(ctrl);
 }
-inline ctrl_t H2(size_t hash) { return hash & 0x7F; }
+inline h2_t H2(size_t hash) { return hash & 0x7F; }
 
-inline bool IsEmpty(ctrl_t c) { return c == kEmpty; }
-inline bool IsFull(ctrl_t c) { return c >= 0; }
-inline bool IsDeleted(ctrl_t c) { return c == kDeleted; }
-inline bool IsEmptyOrDeleted(ctrl_t c) { return c < kSentinel; }
+inline bool IsEmpty(ctrl_t c) { return c == ctrl_t::kEmpty; }
+inline bool IsFull(ctrl_t c) { return c >= static_cast<ctrl_t>(0); }
+inline bool IsDeleted(ctrl_t c) { return c == ctrl_t::kDeleted; }
+inline bool IsEmptyOrDeleted(ctrl_t c) { return c < ctrl_t::kSentinel; }
 
 #if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2
 
@@ -351,24 +366,24 @@
   // Returns a bitmask representing the positions of empty slots.
   BitMask<uint32_t, kWidth> MatchEmpty() const {
 #if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3
-    // This only works because kEmpty is -128.
+    // This only works because ctrl_t::kEmpty is -128.
     return BitMask<uint32_t, kWidth>(
         _mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl)));
 #else
-    return Match(static_cast<h2_t>(kEmpty));
+    return Match(static_cast<h2_t>(ctrl_t::kEmpty));
 #endif
   }
 
   // Returns a bitmask representing the positions of empty or deleted slots.
   BitMask<uint32_t, kWidth> MatchEmptyOrDeleted() const {
-    auto special = _mm_set1_epi8(kSentinel);
+    auto special = _mm_set1_epi8(static_cast<int8_t>(ctrl_t::kSentinel));
     return BitMask<uint32_t, kWidth>(
         _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)));
   }
 
   // Returns the number of trailing empty or deleted elements in the group.
   uint32_t CountLeadingEmptyOrDeleted() const {
-    auto special = _mm_set1_epi8(kSentinel);
+    auto special = _mm_set1_epi8(static_cast<int8_t>(ctrl_t::kSentinel));
     return TrailingZeros(static_cast<uint32_t>(
         _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1));
   }
@@ -403,7 +418,7 @@
     //
     // Caveat: there are false positives but:
     // - they only occur if there is a real match
-    // - they never occur on kEmpty, kDeleted, kSentinel
+    // - they never occur on ctrl_t::kEmpty, ctrl_t::kDeleted, ctrl_t::kSentinel
     // - they will be handled gracefully by subsequent checks in code
     //
     // Example:
@@ -448,6 +463,10 @@
 using Group = GroupPortableImpl;
 #endif
 
+// The number of cloned control bytes that we copy from the beginning to the
+// end of the control bytes array.
+constexpr size_t NumClonedBytes() { return Group::kWidth - 1; }
+
 template <class Policy, class Hash, class Eq, class Alloc>
 class raw_hash_set;
 
@@ -455,8 +474,8 @@
 
 // PRECONDITION:
 //   IsValidCapacity(capacity)
-//   ctrl[capacity] == kSentinel
-//   ctrl[i] != kSentinel for all i < capacity
+//   ctrl[capacity] == ctrl_t::kSentinel
+//   ctrl[i] != ctrl_t::kSentinel for all i < capacity
 // Applies mapping for every byte in ctrl:
 //   DELETED -> EMPTY
 //   EMPTY -> EMPTY
@@ -498,6 +517,22 @@
   return growth + static_cast<size_t>((static_cast<int64_t>(growth) - 1) / 7);
 }
 
+template <class InputIter>
+size_t SelectBucketCountForIterRange(InputIter first, InputIter last,
+                                     size_t bucket_count) {
+  if (bucket_count != 0) {
+    return bucket_count;
+  }
+  using InputIterCategory =
+      typename std::iterator_traits<InputIter>::iterator_category;
+  if (std::is_base_of<std::random_access_iterator_tag,
+                      InputIterCategory>::value) {
+    return GrowthToLowerboundCapacity(
+        static_cast<size_t>(std::distance(first, last)));
+  }
+  return 0;
+}
+
 inline void AssertIsFull(ctrl_t* ctrl) {
   ABSL_HARDENING_ASSERT((ctrl != nullptr && IsFull(*ctrl)) &&
                         "Invalid operation on iterator. The element might have "
@@ -525,27 +560,29 @@
 //  This is important to make 1 a valid capacity.
 //
 //  - In small mode only the first `capacity()` control bytes after the
-//  sentinel are valid. The rest contain dummy kEmpty values that do not
+//  sentinel are valid. The rest contain dummy ctrl_t::kEmpty values that do not
 //  represent a real slot. This is important to take into account on
 //  find_first_non_full(), where we never try ShouldInsertBackwards() for
 //  small tables.
 inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; }
 
-inline probe_seq<Group::kWidth> probe(ctrl_t* ctrl, size_t hash,
+inline probe_seq<Group::kWidth> probe(const ctrl_t* ctrl, size_t hash,
                                       size_t capacity) {
   return probe_seq<Group::kWidth>(H1(hash, ctrl), capacity);
 }
 
 // Probes the raw_hash_set with the probe sequence for hash and returns the
 // pointer to the first empty or deleted slot.
-// NOTE: this function must work with tables having both kEmpty and kDelete
-// in one group. Such tables appears during drop_deletes_without_resize.
+// NOTE: this function must work with tables having both ctrl_t::kEmpty and
+// ctrl_t::kDeleted in one group. Such tables appears during
+// drop_deletes_without_resize.
 //
 // This function is very useful when insertions happen and:
 // - the input is already a set
 // - there are enough slots
 // - the element with the hash is not in the table
-inline FindInfo find_first_non_full(ctrl_t* ctrl, size_t hash,
+template <typename = void>
+inline FindInfo find_first_non_full(const ctrl_t* ctrl, size_t hash,
                                     size_t capacity) {
   auto seq = probe(ctrl, hash, capacity);
   while (true) {
@@ -564,10 +601,60 @@
       return {seq.offset(mask.LowestBitSet()), seq.index()};
     }
     seq.next();
-    assert(seq.index() < capacity && "full table!");
+    assert(seq.index() <= capacity && "full table!");
   }
 }
 
+// Extern template for inline function keep possibility of inlining.
+// When compiler decided to not inline, no symbols will be added to the
+// corresponding translation unit.
+extern template FindInfo find_first_non_full(const ctrl_t*, size_t, size_t);
+
+// Reset all ctrl bytes back to ctrl_t::kEmpty, except the sentinel.
+inline void ResetCtrl(size_t capacity, ctrl_t* ctrl, const void* slot,
+                      size_t slot_size) {
+  std::memset(ctrl, static_cast<int8_t>(ctrl_t::kEmpty),
+              capacity + 1 + NumClonedBytes());
+  ctrl[capacity] = ctrl_t::kSentinel;
+  SanitizerPoisonMemoryRegion(slot, slot_size * capacity);
+}
+
+// Sets the control byte, and if `i < NumClonedBytes()`, set the cloned byte
+// at the end too.
+inline void SetCtrl(size_t i, ctrl_t h, size_t capacity, ctrl_t* ctrl,
+                    const void* slot, size_t slot_size) {
+  assert(i < capacity);
+
+  auto* slot_i = static_cast<const char*>(slot) + i * slot_size;
+  if (IsFull(h)) {
+    SanitizerUnpoisonMemoryRegion(slot_i, slot_size);
+  } else {
+    SanitizerPoisonMemoryRegion(slot_i, slot_size);
+  }
+
+  ctrl[i] = h;
+  ctrl[((i - NumClonedBytes()) & capacity) + (NumClonedBytes() & capacity)] = h;
+}
+
+inline void SetCtrl(size_t i, h2_t h, size_t capacity, ctrl_t* ctrl,
+                    const void* slot, size_t slot_size) {
+  SetCtrl(i, static_cast<ctrl_t>(h), capacity, ctrl, slot, slot_size);
+}
+
+// The allocated block consists of `capacity + 1 + NumClonedBytes()` control
+// bytes followed by `capacity` slots, which must be aligned to `slot_align`.
+// SlotOffset returns the offset of the slots into the allocated block.
+inline size_t SlotOffset(size_t capacity, size_t slot_align) {
+  assert(IsValidCapacity(capacity));
+  const size_t num_control_bytes = capacity + 1 + NumClonedBytes();
+  return (num_control_bytes + slot_align - 1) & (~slot_align + 1);
+}
+
+// Returns the size of the allocated block. See also above comment.
+inline size_t AllocSize(size_t capacity, size_t slot_size, size_t slot_align) {
+  return SlotOffset(capacity, slot_align) + capacity * slot_size;
+}
+
 // 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).
@@ -624,13 +711,6 @@
   auto KeyTypeCanBeHashed(const Hash& h, const key_type& k) -> decltype(h(k));
   auto KeyTypeCanBeEq(const Eq& eq, const key_type& k) -> decltype(eq(k, k));
 
-  using Layout = absl::container_internal::Layout<ctrl_t, slot_type>;
-
-  static Layout MakeLayout(size_t capacity) {
-    assert(IsValidCapacity(capacity));
-    return Layout(capacity + Group::kWidth + 1, capacity);
-  }
-
   using AllocTraits = absl::allocator_traits<allocator_type>;
   using SlotAlloc = typename absl::allocator_traits<
       allocator_type>::template rebind_alloc<slot_type>;
@@ -733,7 +813,7 @@
         ctrl_ += shift;
         slot_ += shift;
       }
-      if (ABSL_PREDICT_FALSE(*ctrl_ == kSentinel)) ctrl_ = nullptr;
+      if (ABSL_PREDICT_FALSE(*ctrl_ == ctrl_t::kSentinel)) ctrl_ = nullptr;
     }
 
     ctrl_t* ctrl_ = nullptr;
@@ -814,7 +894,8 @@
   raw_hash_set(InputIter first, InputIter last, size_t bucket_count = 0,
                const hasher& hash = hasher(), const key_equal& eq = key_equal(),
                const allocator_type& alloc = allocator_type())
-      : raw_hash_set(bucket_count, hash, eq, alloc) {
+      : raw_hash_set(SelectBucketCountForIterRange(first, last, bucket_count),
+                     hash, eq, alloc) {
     insert(first, last);
   }
 
@@ -902,7 +983,8 @@
     for (const auto& v : that) {
       const size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, v);
       auto target = find_first_non_full(ctrl_, hash, capacity_);
-      set_ctrl(target.offset, H2(hash));
+      SetCtrl(target.offset, H2(hash), capacity_, ctrl_, slots_,
+              sizeof(slot_type));
       emplace_at(target.offset, v);
       infoz().RecordInsert(hash, target.probe_length);
     }
@@ -998,6 +1080,8 @@
     // past that we simply deallocate the array.
     if (capacity_ > 127) {
       destroy_slots();
+
+      infoz().RecordClearedReservation();
     } else if (capacity_) {
       for (size_t i = 0; i != capacity_; ++i) {
         if (IsFull(ctrl_[i])) {
@@ -1005,7 +1089,7 @@
         }
       }
       size_ = 0;
-      reset_ctrl();
+      ResetCtrl(capacity_, ctrl_, slots_, sizeof(slot_type));
       reset_growth_left();
     }
     assert(empty());
@@ -1311,21 +1395,31 @@
     if (n == 0 && size_ == 0) {
       destroy_slots();
       infoz().RecordStorageChanged(0, 0);
+      infoz().RecordClearedReservation();
       return;
     }
+
     // bitor is a faster way of doing `max` here. We will round up to the next
     // power-of-2-minus-1, so bitor is good enough.
     auto m = NormalizeCapacity(n | GrowthToLowerboundCapacity(size()));
     // n == 0 unconditionally rehashes as per the standard.
     if (n == 0 || m > capacity_) {
       resize(m);
+
+      // This is after resize, to ensure that we have completed the allocation
+      // and have potentially sampled the hashtable.
+      infoz().RecordReservation(n);
     }
   }
 
   void reserve(size_t n) {
-    size_t m = GrowthToLowerboundCapacity(n);
-    if (m > capacity_) {
+    if (n > size() + growth_left()) {
+      size_t m = GrowthToLowerboundCapacity(n);
       resize(NormalizeCapacity(m));
+
+      // This is after resize, to ensure that we have completed the allocation
+      // and have potentially sampled the hashtable.
+      infoz().RecordReservation(n);
     }
   }
 
@@ -1352,6 +1446,7 @@
   void prefetch(const key_arg<K>& key) const {
     (void)key;
 #if defined(__GNUC__)
+    prefetch_heap_block();
     auto seq = probe(ctrl_, hash_ref()(key), capacity_);
     __builtin_prefetch(static_cast<const void*>(ctrl_ + seq.offset()));
     __builtin_prefetch(static_cast<const void*>(slots_ + seq.offset()));
@@ -1378,11 +1473,12 @@
       }
       if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return end();
       seq.next();
-      assert(seq.index() < capacity_ && "full table!");
+      assert(seq.index() <= capacity_ && "full table!");
     }
   }
   template <class K = key_type>
   iterator find(const key_arg<K>& key) {
+    prefetch_heap_block();
     return find(key, hash_ref()(key));
   }
 
@@ -1392,6 +1488,7 @@
   }
   template <class K = key_type>
   const_iterator find(const key_arg<K>& key) const {
+    prefetch_heap_block();
     return find(key, hash_ref()(key));
   }
 
@@ -1526,7 +1623,8 @@
         static_cast<size_t>(empty_after.TrailingZeros() +
                             empty_before.LeadingZeros()) < Group::kWidth;
 
-    set_ctrl(index, was_never_full ? kEmpty : kDeleted);
+    SetCtrl(index, was_never_full ? ctrl_t::kEmpty : ctrl_t::kDeleted,
+            capacity_, ctrl_, slots_, sizeof(slot_type));
     growth_left() += was_never_full;
     infoz().RecordErase();
   }
@@ -1545,15 +1643,16 @@
     // bound more carefully.
     if (std::is_same<SlotAlloc, std::allocator<slot_type>>::value &&
         slots_ == nullptr) {
-      infoz() = Sample();
+      infoz() = Sample(sizeof(slot_type));
     }
 
-    auto layout = MakeLayout(capacity_);
-    char* mem = static_cast<char*>(
-        Allocate<Layout::Alignment()>(&alloc_ref(), layout.AllocSize()));
-    ctrl_ = reinterpret_cast<ctrl_t*>(layout.template Pointer<0>(mem));
-    slots_ = layout.template Pointer<1>(mem);
-    reset_ctrl();
+    char* mem = static_cast<char*>(Allocate<alignof(slot_type)>(
+        &alloc_ref(),
+        AllocSize(capacity_, sizeof(slot_type), alignof(slot_type))));
+    ctrl_ = reinterpret_cast<ctrl_t*>(mem);
+    slots_ = reinterpret_cast<slot_type*>(
+        mem + SlotOffset(capacity_, alignof(slot_type)));
+    ResetCtrl(capacity_, ctrl_, slots_, sizeof(slot_type));
     reset_growth_left();
     infoz().RecordStorageChanged(size_, capacity_);
   }
@@ -1565,10 +1664,12 @@
         PolicyTraits::destroy(&alloc_ref(), slots_ + i);
       }
     }
-    auto layout = MakeLayout(capacity_);
+
     // Unpoison before returning the memory to the allocator.
     SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_);
-    Deallocate<Layout::Alignment()>(&alloc_ref(), ctrl_, layout.AllocSize());
+    Deallocate<alignof(slot_type)>(
+        &alloc_ref(), ctrl_,
+        AllocSize(capacity_, sizeof(slot_type), alignof(slot_type)));
     ctrl_ = EmptyGroup();
     slots_ = nullptr;
     size_ = 0;
@@ -1592,16 +1693,16 @@
         auto target = find_first_non_full(ctrl_, hash, capacity_);
         size_t new_i = target.offset;
         total_probe_length += target.probe_length;
-        set_ctrl(new_i, H2(hash));
+        SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type));
         PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i);
       }
     }
     if (old_capacity) {
       SanitizerUnpoisonMemoryRegion(old_slots,
                                     sizeof(slot_type) * old_capacity);
-      auto layout = MakeLayout(old_capacity);
-      Deallocate<Layout::Alignment()>(&alloc_ref(), old_ctrl,
-                                      layout.AllocSize());
+      Deallocate<alignof(slot_type)>(
+          &alloc_ref(), old_ctrl,
+          AllocSize(old_capacity, sizeof(slot_type), alignof(slot_type)));
     }
     infoz().RecordRehash(total_probe_length);
   }
@@ -1631,35 +1732,35 @@
     slot_type* slot = reinterpret_cast<slot_type*>(&raw);
     for (size_t i = 0; i != capacity_; ++i) {
       if (!IsDeleted(ctrl_[i])) continue;
-      size_t hash = PolicyTraits::apply(HashElement{hash_ref()},
-                                        PolicyTraits::element(slots_ + i));
-      auto target = find_first_non_full(ctrl_, hash, capacity_);
-      size_t new_i = target.offset;
+      const size_t hash = PolicyTraits::apply(
+          HashElement{hash_ref()}, PolicyTraits::element(slots_ + i));
+      const FindInfo target = find_first_non_full(ctrl_, hash, capacity_);
+      const size_t new_i = target.offset;
       total_probe_length += target.probe_length;
 
       // Verify if the old and new i fall within the same group wrt the hash.
       // If they do, we don't need to move the object as it falls already in the
       // best probe we can.
-      const auto probe_index = [&](size_t pos) {
-        return ((pos - probe(ctrl_, hash, capacity_).offset()) & capacity_) /
-               Group::kWidth;
+      const size_t probe_offset = probe(ctrl_, hash, capacity_).offset();
+      const auto probe_index = [probe_offset, this](size_t pos) {
+        return ((pos - probe_offset) & capacity_) / Group::kWidth;
       };
 
       // Element doesn't move.
       if (ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) {
-        set_ctrl(i, H2(hash));
+        SetCtrl(i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type));
         continue;
       }
       if (IsEmpty(ctrl_[new_i])) {
         // Transfer element to the empty spot.
-        // set_ctrl poisons/unpoisons the slots so we have to call it at the
+        // SetCtrl poisons/unpoisons the slots so we have to call it at the
         // right time.
-        set_ctrl(new_i, H2(hash));
+        SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type));
         PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slots_ + i);
-        set_ctrl(i, kEmpty);
+        SetCtrl(i, ctrl_t::kEmpty, capacity_, ctrl_, slots_, sizeof(slot_type));
       } else {
         assert(IsDeleted(ctrl_[new_i]));
-        set_ctrl(new_i, H2(hash));
+        SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type));
         // Until we are done rehashing, DELETED marks previously FULL slots.
         // Swap i and new_i elements.
         PolicyTraits::transfer(&alloc_ref(), slot, slots_ + i);
@@ -1675,8 +1776,50 @@
   void rehash_and_grow_if_necessary() {
     if (capacity_ == 0) {
       resize(1);
-    } else if (size() <= CapacityToGrowth(capacity()) / 2) {
+    } else if (capacity_ > Group::kWidth &&
+               // Do these calcuations in 64-bit to avoid overflow.
+               size() * uint64_t{32} <= capacity_ * uint64_t{25}) {
       // Squash DELETED without growing if there is enough capacity.
+      //
+      // Rehash in place if the current size is <= 25/32 of capacity_.
+      // Rationale for such a high factor: 1) drop_deletes_without_resize() is
+      // faster than resize, and 2) it takes quite a bit of work to add
+      // tombstones.  In the worst case, seems to take approximately 4
+      // insert/erase pairs to create a single tombstone and so if we are
+      // rehashing because of tombstones, we can afford to rehash-in-place as
+      // long as we are reclaiming at least 1/8 the capacity without doing more
+      // than 2X the work.  (Where "work" is defined to be size() for rehashing
+      // or rehashing in place, and 1 for an insert or erase.)  But rehashing in
+      // place is faster per operation than inserting or even doubling the size
+      // of the table, so we actually afford to reclaim even less space from a
+      // resize-in-place.  The decision is to rehash in place if we can reclaim
+      // at about 1/8th of the usable capacity (specifically 3/28 of the
+      // capacity) which means that the total cost of rehashing will be a small
+      // fraction of the total work.
+      //
+      // Here is output of an experiment using the BM_CacheInSteadyState
+      // benchmark running the old case (where we rehash-in-place only if we can
+      // reclaim at least 7/16*capacity_) vs. this code (which rehashes in place
+      // if we can recover 3/32*capacity_).
+      //
+      // Note that although in the worst-case number of rehashes jumped up from
+      // 15 to 190, but the number of operations per second is almost the same.
+      //
+      // Abridged output of running BM_CacheInSteadyState benchmark from
+      // raw_hash_set_benchmark.   N is the number of insert/erase operations.
+      //
+      //      | OLD (recover >= 7/16        | NEW (recover >= 3/32)
+      // size |    N/s LoadFactor NRehashes |    N/s LoadFactor NRehashes
+      //  448 | 145284       0.44        18 | 140118       0.44        19
+      //  493 | 152546       0.24        11 | 151417       0.48        28
+      //  538 | 151439       0.26        11 | 151152       0.53        38
+      //  583 | 151765       0.28        11 | 150572       0.57        50
+      //  628 | 150241       0.31        11 | 150853       0.61        66
+      //  672 | 149602       0.33        12 | 150110       0.66        90
+      //  717 | 149998       0.35        12 | 149531       0.70       129
+      //  762 | 149836       0.37        13 | 148559       0.74       190
+      //  807 | 149736       0.39        14 | 151107       0.39        14
+      //  852 | 150204       0.42        15 | 151019       0.42        15
       drop_deletes_without_resize();
     } else {
       // Otherwise grow the container.
@@ -1696,7 +1839,7 @@
       }
       if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return false;
       seq.next();
-      assert(seq.index() < capacity_ && "full table!");
+      assert(seq.index() <= capacity_ && "full table!");
     }
     return false;
   }
@@ -1716,6 +1859,7 @@
  protected:
   template <class K>
   std::pair<size_t, bool> find_or_prepare_insert(const K& key) {
+    prefetch_heap_block();
     auto hash = hash_ref()(key);
     auto seq = probe(ctrl_, hash, capacity_);
     while (true) {
@@ -1728,7 +1872,7 @@
       }
       if (ABSL_PREDICT_TRUE(g.MatchEmpty())) break;
       seq.next();
-      assert(seq.index() < capacity_ && "full table!");
+      assert(seq.index() <= capacity_ && "full table!");
     }
     return {prepare_insert(hash), true};
   }
@@ -1742,7 +1886,8 @@
     }
     ++size_;
     growth_left() -= IsEmpty(ctrl_[target.offset]);
-    set_ctrl(target.offset, H2(hash));
+    SetCtrl(target.offset, H2(hash), capacity_, ctrl_, slots_,
+            sizeof(slot_type));
     infoz().RecordInsert(hash, target.probe_length);
     return target.offset;
   }
@@ -1771,35 +1916,21 @@
  private:
   friend struct RawHashSetTestOnlyAccess;
 
-  // Reset all ctrl bytes back to kEmpty, except the sentinel.
-  void reset_ctrl() {
-    std::memset(ctrl_, kEmpty, capacity_ + Group::kWidth);
-    ctrl_[capacity_] = kSentinel;
-    SanitizerPoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_);
-  }
-
   void reset_growth_left() {
     growth_left() = CapacityToGrowth(capacity()) - size_;
   }
 
-  // Sets the control byte, and if `i < Group::kWidth`, set the cloned byte at
-  // the end too.
-  void set_ctrl(size_t i, ctrl_t h) {
-    assert(i < capacity_);
-
-    if (IsFull(h)) {
-      SanitizerUnpoisonObject(slots_ + i);
-    } else {
-      SanitizerPoisonObject(slots_ + i);
-    }
-
-    ctrl_[i] = h;
-    ctrl_[((i - Group::kWidth) & capacity_) + 1 +
-          ((Group::kWidth - 1) & capacity_)] = h;
-  }
-
   size_t& growth_left() { return settings_.template get<0>(); }
 
+  void prefetch_heap_block() const {
+    // Prefetch the heap-allocated memory region to resolve potential TLB
+    // misses.  This is intended to overlap with execution of calculating the
+    // hash for a key.
+#if defined(__GNUC__)
+    __builtin_prefetch(static_cast<const void*>(ctrl_), 0, 1);
+#endif  // __GNUC__
+  }
+
   HashtablezInfoHandle& infoz() { return settings_.template get<1>(); }
 
   hasher& hash_ref() { return settings_.template get<2>(); }
@@ -1814,10 +1945,10 @@
   // TODO(alkis): Investigate removing some of these fields:
   // - ctrl/slots can be derived from each other
   // - size can be moved into the slot array
-  ctrl_t* ctrl_ = EmptyGroup();    // [(capacity + 1) * ctrl_t]
-  slot_type* slots_ = nullptr;     // [capacity * slot_type]
-  size_t size_ = 0;                // number of full slots
-  size_t capacity_ = 0;            // total number of slots
+  ctrl_t* ctrl_ = EmptyGroup();  // [(capacity + 1 + NumClonedBytes()) * ctrl_t]
+  slot_type* slots_ = nullptr;   // [capacity * slot_type]
+  size_t size_ = 0;              // number of full slots
+  size_t capacity_ = 0;          // total number of slots
   absl::container_internal::CompressedTuple<size_t /* growth_left */,
                                             HashtablezInfoHandle, hasher,
                                             key_equal, allocator_type>
@@ -1827,11 +1958,12 @@
 
 // Erases all elements that satisfy the predicate `pred` from the container `c`.
 template <typename P, typename H, typename E, typename A, typename Predicate>
-void EraseIf(Predicate pred, raw_hash_set<P, H, E, A>* c) {
+void EraseIf(Predicate& pred, raw_hash_set<P, H, E, A>* c) {
   for (auto it = c->begin(), last = c->end(); it != last;) {
-    auto copy_it = it++;
-    if (pred(*copy_it)) {
-      c->erase(copy_it);
+    if (pred(*it)) {
+      c->erase(it++);
+    } else {
+      ++it;
     }
   }
 }
@@ -1866,8 +1998,7 @@
   static size_t AllocatedByteSize(const Set& c) {
     size_t capacity = c.capacity_;
     if (capacity == 0) return 0;
-    auto layout = Set::MakeLayout(capacity);
-    size_t m = layout.AllocSize();
+    size_t m = AllocSize(capacity, sizeof(Slot), alignof(Slot));
 
     size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr));
     if (per_slot != ~size_t{}) {
@@ -1885,8 +2016,8 @@
   static size_t LowerBoundAllocatedByteSize(size_t size) {
     size_t capacity = GrowthToLowerboundCapacity(size);
     if (capacity == 0) return 0;
-    auto layout = Set::MakeLayout(NormalizeCapacity(capacity));
-    size_t m = layout.AllocSize();
+    size_t m =
+        AllocSize(NormalizeCapacity(capacity), sizeof(Slot), alignof(Slot));
     size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr));
     if (per_slot != ~size_t{}) {
       m += per_slot * size;
diff --git a/absl/container/internal/raw_hash_set_benchmark.cc b/absl/container/internal/raw_hash_set_benchmark.cc
index f9be2c5..c886d3a 100644
--- a/absl/container/internal/raw_hash_set_benchmark.cc
+++ b/absl/container/internal/raw_hash_set_benchmark.cc
@@ -254,6 +254,23 @@
 }
 BENCHMARK(BM_CopyAssign)->Range(128, 4096);
 
+void BM_RangeCtor(benchmark::State& state) {
+  std::random_device rd;
+  std::mt19937 rng(rd());
+  std::uniform_int_distribution<uint64_t> dist(0, ~uint64_t{});
+  std::vector<int> values;
+  const size_t desired_size = state.range(0);
+  while (values.size() < desired_size) {
+    values.emplace_back(dist(rng));
+  }
+
+  for (auto unused : state) {
+    IntTable t{values.begin(), values.end()};
+    benchmark::DoNotOptimize(t);
+  }
+}
+BENCHMARK(BM_RangeCtor)->Range(128, 65536);
+
 void BM_NoOpReserveIntTable(benchmark::State& state) {
   IntTable t;
   t.reserve(100000);
@@ -298,9 +315,17 @@
 }
 BENCHMARK(BM_ReserveStringTable)->Range(128, 4096);
 
+// Like std::iota, except that ctrl_t doesn't support operator++.
+template <typename CtrlIter>
+void Iota(CtrlIter begin, CtrlIter end, int value) {
+  for (; begin != end; ++begin, ++value) {
+    *begin = static_cast<ctrl_t>(value);
+  }
+}
+
 void BM_Group_Match(benchmark::State& state) {
   std::array<ctrl_t, Group::kWidth> group;
-  std::iota(group.begin(), group.end(), -4);
+  Iota(group.begin(), group.end(), -4);
   Group g{group.data()};
   h2_t h = 1;
   for (auto _ : state) {
@@ -312,7 +337,7 @@
 
 void BM_Group_MatchEmpty(benchmark::State& state) {
   std::array<ctrl_t, Group::kWidth> group;
-  std::iota(group.begin(), group.end(), -4);
+  Iota(group.begin(), group.end(), -4);
   Group g{group.data()};
   for (auto _ : state) ::benchmark::DoNotOptimize(g.MatchEmpty());
 }
@@ -320,7 +345,7 @@
 
 void BM_Group_MatchEmptyOrDeleted(benchmark::State& state) {
   std::array<ctrl_t, Group::kWidth> group;
-  std::iota(group.begin(), group.end(), -4);
+  Iota(group.begin(), group.end(), -4);
   Group g{group.data()};
   for (auto _ : state) ::benchmark::DoNotOptimize(g.MatchEmptyOrDeleted());
 }
@@ -328,7 +353,7 @@
 
 void BM_Group_CountLeadingEmptyOrDeleted(benchmark::State& state) {
   std::array<ctrl_t, Group::kWidth> group;
-  std::iota(group.begin(), group.end(), -2);
+  Iota(group.begin(), group.end(), -2);
   Group g{group.data()};
   for (auto _ : state)
     ::benchmark::DoNotOptimize(g.CountLeadingEmptyOrDeleted());
@@ -337,7 +362,7 @@
 
 void BM_Group_MatchFirstEmptyOrDeleted(benchmark::State& state) {
   std::array<ctrl_t, Group::kWidth> group;
-  std::iota(group.begin(), group.end(), -2);
+  Iota(group.begin(), group.end(), -2);
   Group g{group.data()};
   for (auto _ : state) ::benchmark::DoNotOptimize(*g.MatchEmptyOrDeleted());
 }
@@ -346,8 +371,11 @@
 void BM_DropDeletes(benchmark::State& state) {
   constexpr size_t capacity = (1 << 20) - 1;
   std::vector<ctrl_t> ctrl(capacity + 1 + Group::kWidth);
-  ctrl[capacity] = kSentinel;
-  std::vector<ctrl_t> pattern = {kEmpty, 2, kDeleted, 2, kEmpty, 1, kDeleted};
+  ctrl[capacity] = ctrl_t::kSentinel;
+  std::vector<ctrl_t> pattern = {ctrl_t::kEmpty,   static_cast<ctrl_t>(2),
+                                 ctrl_t::kDeleted, static_cast<ctrl_t>(2),
+                                 ctrl_t::kEmpty,   static_cast<ctrl_t>(1),
+                                 ctrl_t::kDeleted};
   for (size_t i = 0; i != capacity; ++i) {
     ctrl[i] = pattern[i % pattern.size()];
   }
@@ -378,6 +406,12 @@
   return table->find(key) != table->end();
 }
 
+auto CodegenAbslRawHashSetInt64Insert(absl::container_internal::IntTable* table,
+                                      int64_t key)
+    -> decltype(table->insert(key)) {
+  return table->insert(key);
+}
+
 bool CodegenAbslRawHashSetInt64Contains(
     absl::container_internal::IntTable* table, int64_t key) {
   return table->contains(key);
@@ -391,6 +425,7 @@
 int odr =
     (::benchmark::DoNotOptimize(std::make_tuple(
          &CodegenAbslRawHashSetInt64Find, &CodegenAbslRawHashSetInt64FindNeEnd,
+         &CodegenAbslRawHashSetInt64Insert,
          &CodegenAbslRawHashSetInt64Contains,
          &CodegenAbslRawHashSetInt64Iterate)),
      1);
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc
index 7dac65a..362b3ca 100644
--- a/absl/container/internal/raw_hash_set_test.cc
+++ b/absl/container/internal/raw_hash_set_test.cc
@@ -58,6 +58,9 @@
 using ::testing::Pair;
 using ::testing::UnorderedElementsAre;
 
+// Convenience function to static cast to ctrl_t.
+ctrl_t CtrlT(int i) { return static_cast<ctrl_t>(i); }
+
 TEST(Util, NormalizeCapacity) {
   EXPECT_EQ(1, NormalizeCapacity(0));
   EXPECT_EQ(1, NormalizeCapacity(1));
@@ -170,15 +173,19 @@
 
 TEST(Group, Match) {
   if (Group::kWidth == 16) {
-    ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
-                      7,      5, 3,        1, 1,      1, 1,         1};
+    ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted,  CtrlT(3),
+                      ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
+                      CtrlT(7),       CtrlT(5), CtrlT(3),          CtrlT(1),
+                      CtrlT(1),       CtrlT(1), CtrlT(1),          CtrlT(1)};
     EXPECT_THAT(Group{group}.Match(0), ElementsAre());
     EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 11, 12, 13, 14, 15));
     EXPECT_THAT(Group{group}.Match(3), ElementsAre(3, 10));
     EXPECT_THAT(Group{group}.Match(5), ElementsAre(5, 9));
     EXPECT_THAT(Group{group}.Match(7), ElementsAre(7, 8));
   } else if (Group::kWidth == 8) {
-    ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
+    ctrl_t group[] = {ctrl_t::kEmpty,    CtrlT(1), CtrlT(2),
+                      ctrl_t::kDeleted,  CtrlT(2), CtrlT(1),
+                      ctrl_t::kSentinel, CtrlT(1)};
     EXPECT_THAT(Group{group}.Match(0), ElementsAre());
     EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 5, 7));
     EXPECT_THAT(Group{group}.Match(2), ElementsAre(2, 4));
@@ -189,11 +196,15 @@
 
 TEST(Group, MatchEmpty) {
   if (Group::kWidth == 16) {
-    ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
-                      7,      5, 3,        1, 1,      1, 1,         1};
+    ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted,  CtrlT(3),
+                      ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
+                      CtrlT(7),       CtrlT(5), CtrlT(3),          CtrlT(1),
+                      CtrlT(1),       CtrlT(1), CtrlT(1),          CtrlT(1)};
     EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0, 4));
   } else if (Group::kWidth == 8) {
-    ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
+    ctrl_t group[] = {ctrl_t::kEmpty,    CtrlT(1), CtrlT(2),
+                      ctrl_t::kDeleted,  CtrlT(2), CtrlT(1),
+                      ctrl_t::kSentinel, CtrlT(1)};
     EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0));
   } else {
     FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
@@ -202,11 +213,15 @@
 
 TEST(Group, MatchEmptyOrDeleted) {
   if (Group::kWidth == 16) {
-    ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
-                      7,      5, 3,        1, 1,      1, 1,         1};
+    ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted,  CtrlT(3),
+                      ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
+                      CtrlT(7),       CtrlT(5), CtrlT(3),          CtrlT(1),
+                      CtrlT(1),       CtrlT(1), CtrlT(1),          CtrlT(1)};
     EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 2, 4));
   } else if (Group::kWidth == 8) {
-    ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
+    ctrl_t group[] = {ctrl_t::kEmpty,    CtrlT(1), CtrlT(2),
+                      ctrl_t::kDeleted,  CtrlT(2), CtrlT(1),
+                      ctrl_t::kSentinel, CtrlT(1)};
     EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 3));
   } else {
     FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
@@ -217,28 +232,32 @@
   constexpr size_t kCapacity = 63;
   constexpr size_t kGroupWidth = container_internal::Group::kWidth;
   std::vector<ctrl_t> ctrl(kCapacity + 1 + kGroupWidth);
-  ctrl[kCapacity] = kSentinel;
-  std::vector<ctrl_t> pattern = {kEmpty, 2, kDeleted, 2, kEmpty, 1, kDeleted};
+  ctrl[kCapacity] = ctrl_t::kSentinel;
+  std::vector<ctrl_t> pattern = {
+      ctrl_t::kEmpty, CtrlT(2), ctrl_t::kDeleted, CtrlT(2),
+      ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted};
   for (size_t i = 0; i != kCapacity; ++i) {
     ctrl[i] = pattern[i % pattern.size()];
     if (i < kGroupWidth - 1)
       ctrl[i + kCapacity + 1] = pattern[i % pattern.size()];
   }
   ConvertDeletedToEmptyAndFullToDeleted(ctrl.data(), kCapacity);
-  ASSERT_EQ(ctrl[kCapacity], kSentinel);
-  for (size_t i = 0; i < kCapacity + 1 + kGroupWidth; ++i) {
+  ASSERT_EQ(ctrl[kCapacity], ctrl_t::kSentinel);
+  for (size_t i = 0; i < kCapacity + kGroupWidth; ++i) {
     ctrl_t expected = pattern[i % (kCapacity + 1) % pattern.size()];
-    if (i == kCapacity) expected = kSentinel;
-    if (expected == kDeleted) expected = kEmpty;
-    if (IsFull(expected)) expected = kDeleted;
+    if (i == kCapacity) expected = ctrl_t::kSentinel;
+    if (expected == ctrl_t::kDeleted) expected = ctrl_t::kEmpty;
+    if (IsFull(expected)) expected = ctrl_t::kDeleted;
     EXPECT_EQ(ctrl[i], expected)
-        << i << " " << int{pattern[i % pattern.size()]};
+        << i << " " << static_cast<int>(pattern[i % pattern.size()]);
   }
 }
 
 TEST(Group, CountLeadingEmptyOrDeleted) {
-  const std::vector<ctrl_t> empty_examples = {kEmpty, kDeleted};
-  const std::vector<ctrl_t> full_examples = {0, 1, 2, 3, 5, 9, 127, kSentinel};
+  const std::vector<ctrl_t> empty_examples = {ctrl_t::kEmpty, ctrl_t::kDeleted};
+  const std::vector<ctrl_t> full_examples = {
+      CtrlT(0), CtrlT(1), CtrlT(2),   CtrlT(3),
+      CtrlT(5), CtrlT(9), CtrlT(127), ctrl_t::kSentinel};
 
   for (ctrl_t empty : empty_examples) {
     std::vector<ctrl_t> e(Group::kWidth, empty);
@@ -294,6 +313,7 @@
 };
 
 using IntPolicy = ValuePolicy<int64_t>;
+using Uint8Policy = ValuePolicy<uint8_t>;
 
 class StringPolicy {
   template <class F, class K, class V,
@@ -374,6 +394,13 @@
   using Base::Base;
 };
 
+struct Uint8Table
+    : raw_hash_set<Uint8Policy, container_internal::hash_default_hash<uint8_t>,
+                   std::equal_to<uint8_t>, std::allocator<uint8_t>> {
+  using Base = typename Uint8Table::raw_hash_set;
+  using Base::Base;
+};
+
 template <typename T>
 struct CustomAlloc : std::allocator<T> {
   CustomAlloc() {}
@@ -541,6 +568,37 @@
   EXPECT_TRUE(t.empty());
 }
 
+TEST(Table, InsertWithinCapacity) {
+  IntTable t;
+  t.reserve(10);
+  const size_t original_capacity = t.capacity();
+  const auto addr = [&](int i) {
+    return reinterpret_cast<uintptr_t>(&*t.find(i));
+  };
+  // Inserting an element does not change capacity.
+  t.insert(0);
+  EXPECT_THAT(t.capacity(), original_capacity);
+  const uintptr_t original_addr_0 = addr(0);
+  // Inserting another element does not rehash.
+  t.insert(1);
+  EXPECT_THAT(t.capacity(), original_capacity);
+  EXPECT_THAT(addr(0), original_addr_0);
+  // Inserting lots of duplicate elements does not rehash.
+  for (int i = 0; i < 100; ++i) {
+    t.insert(i % 10);
+  }
+  EXPECT_THAT(t.capacity(), original_capacity);
+  EXPECT_THAT(addr(0), original_addr_0);
+  // Inserting a range of duplicate elements does not rehash.
+  std::vector<int> dup_range;
+  for (int i = 0; i < 100; ++i) {
+    dup_range.push_back(i % 10);
+  }
+  t.insert(dup_range.begin(), dup_range.end());
+  EXPECT_THAT(t.capacity(), original_capacity);
+  EXPECT_THAT(addr(0), original_addr_0);
+}
+
 TEST(Table, LazyEmplace) {
   StringTable t;
   bool called = false;
@@ -588,28 +646,53 @@
 }
 
 int decompose_constructed;
+int decompose_copy_constructed;
+int decompose_copy_assigned;
+int decompose_move_constructed;
+int decompose_move_assigned;
 struct DecomposeType {
-  DecomposeType(int i) : i(i) {  // NOLINT
+  DecomposeType(int i = 0) : i(i) {  // NOLINT
     ++decompose_constructed;
   }
 
   explicit DecomposeType(const char* d) : DecomposeType(*d) {}
 
+  DecomposeType(const DecomposeType& other) : i(other.i) {
+    ++decompose_copy_constructed;
+  }
+  DecomposeType& operator=(const DecomposeType& other) {
+    ++decompose_copy_assigned;
+    i = other.i;
+    return *this;
+  }
+  DecomposeType(DecomposeType&& other) : i(other.i) {
+    ++decompose_move_constructed;
+  }
+  DecomposeType& operator=(DecomposeType&& other) {
+    ++decompose_move_assigned;
+    i = other.i;
+    return *this;
+  }
+
   int i;
 };
 
 struct DecomposeHash {
   using is_transparent = void;
-  size_t operator()(DecomposeType a) const { return a.i; }
+  size_t operator()(const DecomposeType& a) const { return a.i; }
   size_t operator()(int a) const { return a; }
   size_t operator()(const char* a) const { return *a; }
 };
 
 struct DecomposeEq {
   using is_transparent = void;
-  bool operator()(DecomposeType a, DecomposeType b) const { return a.i == b.i; }
-  bool operator()(DecomposeType a, int b) const { return a.i == b; }
-  bool operator()(DecomposeType a, const char* b) const { return a.i == *b; }
+  bool operator()(const DecomposeType& a, const DecomposeType& b) const {
+    return a.i == b.i;
+  }
+  bool operator()(const DecomposeType& a, int b) const { return a.i == b; }
+  bool operator()(const DecomposeType& a, const char* b) const {
+    return a.i == *b;
+  }
 };
 
 struct DecomposePolicy {
@@ -619,9 +702,9 @@
 
   template <typename T>
   static void construct(void*, DecomposeType* slot, T&& v) {
-    *slot = DecomposeType(std::forward<T>(v));
+    ::new (slot) DecomposeType(std::forward<T>(v));
   }
-  static void destroy(void*, DecomposeType*) {}
+  static void destroy(void*, DecomposeType* slot) { slot->~DecomposeType(); }
   static DecomposeType& element(slot_type* slot) { return *slot; }
 
   template <class F, class T>
@@ -636,8 +719,13 @@
   const int one = 1;
   const char* three_p = "3";
   const auto& three = three_p;
+  const int elem_vector_count = 256;
+  std::vector<DecomposeType> elem_vector(elem_vector_count, DecomposeType{0});
+  std::iota(elem_vector.begin(), elem_vector.end(), 0);
 
-  raw_hash_set<DecomposePolicy, Hash, Eq, std::allocator<int>> set1;
+  using DecomposeSet =
+      raw_hash_set<DecomposePolicy, Hash, Eq, std::allocator<int>>;
+  DecomposeSet set1;
 
   decompose_constructed = 0;
   int expected_constructed = 0;
@@ -695,20 +783,72 @@
     expected_constructed += construct_three;
     EXPECT_EQ(expected_constructed, decompose_constructed);
   }
+
+  decompose_copy_constructed = 0;
+  decompose_copy_assigned = 0;
+  decompose_move_constructed = 0;
+  decompose_move_assigned = 0;
+  int expected_copy_constructed = 0;
+  int expected_move_constructed = 0;
+  {  // raw_hash_set(first, last) with random-access iterators
+    DecomposeSet set2(elem_vector.begin(), elem_vector.end());
+    // Expect exactly one copy-constructor call for each element if no
+    // rehashing is done.
+    expected_copy_constructed += elem_vector_count;
+    EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed);
+    EXPECT_EQ(expected_move_constructed, decompose_move_constructed);
+    EXPECT_EQ(0, decompose_move_assigned);
+    EXPECT_EQ(0, decompose_copy_assigned);
+  }
+
+  {  // raw_hash_set(first, last) with forward iterators
+    std::list<DecomposeType> elem_list(elem_vector.begin(), elem_vector.end());
+    expected_copy_constructed = decompose_copy_constructed;
+    DecomposeSet set2(elem_list.begin(), elem_list.end());
+    // Expect exactly N elements copied into set, expect at most 2*N elements
+    // moving internally for all resizing needed (for a growth factor of 2).
+    expected_copy_constructed += elem_vector_count;
+    EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed);
+    expected_move_constructed += elem_vector_count;
+    EXPECT_LT(expected_move_constructed, decompose_move_constructed);
+    expected_move_constructed += elem_vector_count;
+    EXPECT_GE(expected_move_constructed, decompose_move_constructed);
+    EXPECT_EQ(0, decompose_move_assigned);
+    EXPECT_EQ(0, decompose_copy_assigned);
+    expected_copy_constructed = decompose_copy_constructed;
+    expected_move_constructed = decompose_move_constructed;
+  }
+
+  {  // insert(first, last)
+    DecomposeSet set2;
+    set2.insert(elem_vector.begin(), elem_vector.end());
+    // Expect exactly N elements copied into set, expect at most 2*N elements
+    // moving internally for all resizing needed (for a growth factor of 2).
+    const int expected_new_elements = elem_vector_count;
+    const int expected_max_element_moves = 2 * elem_vector_count;
+    expected_copy_constructed += expected_new_elements;
+    EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed);
+    expected_move_constructed += expected_max_element_moves;
+    EXPECT_GE(expected_move_constructed, decompose_move_constructed);
+    EXPECT_EQ(0, decompose_move_assigned);
+    EXPECT_EQ(0, decompose_copy_assigned);
+    expected_copy_constructed = decompose_copy_constructed;
+    expected_move_constructed = decompose_move_constructed;
+  }
 }
 
 TEST(Table, Decompose) {
   TestDecompose<DecomposeHash, DecomposeEq>(false);
 
   struct TransparentHashIntOverload {
-    size_t operator()(DecomposeType a) const { return a.i; }
+    size_t operator()(const DecomposeType& a) const { return a.i; }
     size_t operator()(int a) const { return a; }
   };
   struct TransparentEqIntOverload {
-    bool operator()(DecomposeType a, DecomposeType b) const {
+    bool operator()(const DecomposeType& a, const DecomposeType& b) const {
       return a.i == b.i;
     }
-    bool operator()(DecomposeType a, int b) const { return a.i == b; }
+    bool operator()(const DecomposeType& a, int b) const { return a.i == b; }
   };
   TestDecompose<TransparentHashIntOverload, DecomposeEq>(true);
   TestDecompose<TransparentHashIntOverload, TransparentEqIntOverload>(true);
@@ -750,7 +890,7 @@
   const size_t capacity = t.capacity();
 
   // Remove elements from all groups except the first and the last one.
-  // All elements removed from full groups will be marked as kDeleted.
+  // All elements removed from full groups will be marked as ctrl_t::kDeleted.
   const size_t erase_begin = Group::kWidth / 2;
   const size_t erase_end = (t.size() / Group::kWidth - 1) * Group::kWidth;
   for (size_t i = erase_begin; i < erase_end; ++i) {
@@ -1898,7 +2038,7 @@
   SetHashtablezEnabled(true);
   SetHashtablezSampleParameter(100);
 
-  auto& sampler = HashtablezSampler::Global();
+  auto& sampler = GlobalHashtablezSampler();
   size_t start_size = 0;
   std::unordered_set<const HashtablezInfo*> preexisting_info;
   start_size += sampler.Iterate([&](const HashtablezInfo& info) {
@@ -1909,16 +2049,33 @@
   std::vector<IntTable> tables;
   for (int i = 0; i < 1000000; ++i) {
     tables.emplace_back();
+
+    const bool do_reserve = (i % 10 > 5);
+    const bool do_rehash = !do_reserve && (i % 10 > 0);
+
+    if (do_reserve) {
+      // Don't reserve on all tables.
+      tables.back().reserve(10 * (i % 10));
+    }
+
     tables.back().insert(1);
     tables.back().insert(i % 5);
+
+    if (do_rehash) {
+      // Rehash some other tables.
+      tables.back().rehash(10 * (i % 10));
+    }
   }
   size_t end_size = 0;
   std::unordered_map<size_t, int> observed_checksums;
+  std::unordered_map<ssize_t, int> reservations;
   end_size += sampler.Iterate([&](const HashtablezInfo& info) {
     if (preexisting_info.count(&info) == 0) {
       observed_checksums[info.hashes_bitwise_xor.load(
           std::memory_order_relaxed)]++;
+      reservations[info.max_reserve.load(std::memory_order_relaxed)]++;
     }
+    EXPECT_EQ(info.inline_element_size, sizeof(int64_t));
     ++end_size;
   });
 
@@ -1928,6 +2085,15 @@
   for (const auto& [_, count] : observed_checksums) {
     EXPECT_NEAR((100 * count) / static_cast<double>(tables.size()), 0.2, 0.05);
   }
+
+  EXPECT_EQ(reservations.size(), 10);
+  for (const auto& [reservation, count] : reservations) {
+    EXPECT_GE(reservation, 0);
+    EXPECT_LT(reservation, 100);
+
+    EXPECT_NEAR((100 * count) / static_cast<double>(tables.size()), 0.1, 0.05)
+        << reservation;
+  }
 }
 #endif  // ABSL_INTERNAL_HASHTABLEZ_SAMPLE
 
@@ -1936,7 +2102,7 @@
   SetHashtablezEnabled(true);
   SetHashtablezSampleParameter(100);
 
-  auto& sampler = HashtablezSampler::Global();
+  auto& sampler = GlobalHashtablezSampler();
   size_t start_size = 0;
   start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; });
 
@@ -1978,6 +2144,36 @@
 }
 #endif  // ABSL_HAVE_ADDRESS_SANITIZER
 
+TEST(Table, AlignOne) {
+  // We previously had a bug in which we were copying a control byte over the
+  // first slot when alignof(value_type) is 1. We test repeated
+  // insertions/erases and verify that the behavior is correct.
+  Uint8Table t;
+  std::unordered_set<uint8_t> verifier;  // NOLINT
+
+  // Do repeated insertions/erases from the table.
+  for (int64_t i = 0; i < 100000; ++i) {
+    SCOPED_TRACE(i);
+    const uint8_t u = (i * -i) & 0xFF;
+    auto it = t.find(u);
+    auto verifier_it = verifier.find(u);
+    if (it == t.end()) {
+      ASSERT_EQ(verifier_it, verifier.end());
+      t.insert(u);
+      verifier.insert(u);
+    } else {
+      ASSERT_NE(verifier_it, verifier.end());
+      t.erase(it);
+      verifier.erase(verifier_it);
+    }
+  }
+
+  EXPECT_EQ(t.size(), verifier.size());
+  for (uint8_t u : t) {
+    EXPECT_EQ(verifier.count(u), 1);
+  }
+}
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/internal/unordered_map_constructor_test.h b/absl/container/internal/unordered_map_constructor_test.h
index 3f90ad7..c1d20f3 100644
--- a/absl/container/internal/unordered_map_constructor_test.h
+++ b/absl/container/internal/unordered_map_constructor_test.h
@@ -179,7 +179,7 @@
   A alloc(0);
   std::vector<T> values;
   std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+                  hash_internal::UniqueGenerator<T>());
   TypeParam m(values.begin(), values.end(), 123, hasher, equal, alloc);
   EXPECT_EQ(m.hash_function(), hasher);
   EXPECT_EQ(m.key_eq(), equal);
@@ -198,7 +198,7 @@
   A alloc(0);
   std::vector<T> values;
   std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+                  hash_internal::UniqueGenerator<T>());
   TypeParam m(values.begin(), values.end(), 123, alloc);
   EXPECT_EQ(m.get_allocator(), alloc);
   EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
@@ -221,7 +221,7 @@
   A alloc(0);
   std::vector<T> values;
   std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
+                  hash_internal::UniqueGenerator<T>());
   TypeParam m(values.begin(), values.end(), 123, hasher, alloc);
   EXPECT_EQ(m.hash_function(), hasher);
   EXPECT_EQ(m.get_allocator(), alloc);
@@ -241,8 +241,9 @@
   H hasher;
   E equal;
   A alloc(0);
+  hash_internal::UniqueGenerator<T> gen;
   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(gen());
   TypeParam n(m);
   EXPECT_EQ(m.hash_function(), n.hash_function());
   EXPECT_EQ(m.key_eq(), n.key_eq());
@@ -262,8 +263,9 @@
   H hasher;
   E equal;
   A alloc(0);
+  hash_internal::UniqueGenerator<T> gen;
   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(gen());
   TypeParam n(m, A(11));
   EXPECT_EQ(m.hash_function(), n.hash_function());
   EXPECT_EQ(m.key_eq(), n.key_eq());
@@ -285,8 +287,9 @@
   H hasher;
   E equal;
   A alloc(0);
+  hash_internal::UniqueGenerator<T> gen;
   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(gen());
   TypeParam t(m);
   TypeParam n(std::move(t));
   EXPECT_EQ(m.hash_function(), n.hash_function());
@@ -307,8 +310,9 @@
   H hasher;
   E equal;
   A alloc(0);
+  hash_internal::UniqueGenerator<T> gen;
   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(gen());
   TypeParam t(m);
   TypeParam n(std::move(t), A(1));
   EXPECT_EQ(m.hash_function(), n.hash_function());
@@ -325,7 +329,7 @@
 
 TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) {
   using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
+  hash_internal::UniqueGenerator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   using H = typename TypeParam::hasher;
   using E = typename TypeParam::key_equal;
@@ -348,7 +352,7 @@
 void InitializerListBucketAllocTest(std::true_type) {
   using T = hash_internal::GeneratedType<TypeParam>;
   using A = typename TypeParam::allocator_type;
-  hash_internal::Generator<T> gen;
+  hash_internal::UniqueGenerator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   A alloc(0);
   TypeParam m(values, 123, alloc);
@@ -371,7 +375,7 @@
   using A = typename TypeParam::allocator_type;
   H hasher;
   A alloc(0);
-  hash_internal::Generator<T> gen;
+  hash_internal::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);
@@ -392,7 +396,7 @@
   H hasher;
   E equal;
   A alloc(0);
-  hash_internal::Generator<T> gen;
+  hash_internal::UniqueGenerator<T> gen;
   TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
   TypeParam n;
   n = m;
@@ -412,7 +416,7 @@
   H hasher;
   E equal;
   A alloc(0);
-  hash_internal::Generator<T> gen;
+  hash_internal::UniqueGenerator<T> gen;
   TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
   TypeParam t(m);
   TypeParam n;
@@ -424,7 +428,7 @@
 
 TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) {
   using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
+  hash_internal::UniqueGenerator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   TypeParam m;
   m = values;
@@ -433,7 +437,7 @@
 
 TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) {
   using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
+  hash_internal::UniqueGenerator<T> gen;
   TypeParam m({gen(), gen(), gen()});
   TypeParam n({gen()});
   n = m;
@@ -442,7 +446,7 @@
 
 TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) {
   using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
+  hash_internal::UniqueGenerator<T> gen;
   TypeParam m({gen(), gen(), gen()});
   TypeParam t(m);
   TypeParam n({gen()});
@@ -452,7 +456,7 @@
 
 TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) {
   using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
+  hash_internal::UniqueGenerator<T> gen;
   std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
   TypeParam m;
   m = values;
@@ -461,7 +465,7 @@
 
 TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) {
   using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
+  hash_internal::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_modifiers_test.h b/absl/container/internal/unordered_map_modifiers_test.h
index 8c9ca77..d354393 100644
--- a/absl/container/internal/unordered_map_modifiers_test.h
+++ b/absl/container/internal/unordered_map_modifiers_test.h
@@ -81,6 +81,38 @@
   ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
 }
 
+TYPED_TEST_P(ModifiersTest, InsertWithinCapacity) {
+  using T = hash_internal::GeneratedType<TypeParam>;
+  using V = typename TypeParam::mapped_type;
+  T val = hash_internal::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>()()};
+  m.insert(val2);
+  EXPECT_EQ(m.bucket_count(), original_capacity);
+}
+
+TYPED_TEST_P(ModifiersTest, InsertRangeWithinCapacity) {
+#if !defined(__GLIBCXX__)
+  using T = hash_internal::GeneratedType<TypeParam>;
+  std::vector<T> base_values;
+  std::generate_n(std::back_inserter(base_values), 10,
+                  hash_internal::Generator<T>());
+  std::vector<T> values;
+  while (values.size() != 100) {
+    std::copy_n(base_values.begin(), 10, std::back_inserter(values));
+  }
+  TypeParam m;
+  m.reserve(10);
+  const size_t original_capacity = m.bucket_count();
+  m.insert(values.begin(), values.end());
+  EXPECT_EQ(m.bucket_count(), original_capacity);
+#endif
+}
+
 TYPED_TEST_P(ModifiersTest, InsertOrAssign) {
 #ifdef UNORDERED_MAP_CXX17
   using std::get;
@@ -266,9 +298,10 @@
 // TODO(alkis): Write tests for merge.
 
 REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint,
-                           InsertRange, InsertOrAssign, InsertOrAssignHint,
-                           Emplace, EmplaceHint, TryEmplace, TryEmplaceHint,
-                           Erase, EraseRange, EraseKey, Swap);
+                           InsertRange, InsertWithinCapacity,
+                           InsertRangeWithinCapacity, InsertOrAssign,
+                           InsertOrAssignHint, Emplace, EmplaceHint, TryEmplace,
+                           TryEmplaceHint, Erase, EraseRange, EraseKey, Swap);
 
 template <typename Type>
 struct is_unique_ptr : std::false_type {};
diff --git a/absl/container/internal/unordered_set_modifiers_test.h b/absl/container/internal/unordered_set_modifiers_test.h
index 26be58d..6e473e4 100644
--- a/absl/container/internal/unordered_set_modifiers_test.h
+++ b/absl/container/internal/unordered_set_modifiers_test.h
@@ -74,6 +74,36 @@
   ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
 }
 
+TYPED_TEST_P(ModifiersTest, InsertWithinCapacity) {
+  using T = hash_internal::GeneratedType<TypeParam>;
+  T val = hash_internal::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);
+  m.insert(val);
+  EXPECT_EQ(m.bucket_count(), original_capacity);
+}
+
+TYPED_TEST_P(ModifiersTest, InsertRangeWithinCapacity) {
+#if !defined(__GLIBCXX__)
+  using T = hash_internal::GeneratedType<TypeParam>;
+  std::vector<T> base_values;
+  std::generate_n(std::back_inserter(base_values), 10,
+                  hash_internal::Generator<T>());
+  std::vector<T> values;
+  while (values.size() != 100) {
+    values.insert(values.end(), base_values.begin(), base_values.end());
+  }
+  TypeParam m;
+  m.reserve(10);
+  const size_t original_capacity = m.bucket_count();
+  m.insert(values.begin(), values.end());
+  EXPECT_EQ(m.bucket_count(), original_capacity);
+#endif
+}
+
 TYPED_TEST_P(ModifiersTest, Emplace) {
   using T = hash_internal::GeneratedType<TypeParam>;
   T val = hash_internal::Generator<T>()();
@@ -180,8 +210,9 @@
 // TODO(alkis): Write tests for merge.
 
 REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint,
-                           InsertRange, Emplace, EmplaceHint, Erase, EraseRange,
-                           EraseKey, Swap);
+                           InsertRange, InsertWithinCapacity,
+                           InsertRangeWithinCapacity, Emplace, EmplaceHint,
+                           Erase, EraseRange, EraseKey, Swap);
 
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/sample_element_size_test.cc b/absl/container/sample_element_size_test.cc
new file mode 100644
index 0000000..b23626b
--- /dev/null
+++ b/absl/container/sample_element_size_test.cc
@@ -0,0 +1,114 @@
+// 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 "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/container/flat_hash_map.h"
+#include "absl/container/flat_hash_set.h"
+#include "absl/container/node_hash_map.h"
+#include "absl/container/node_hash_set.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace container_internal {
+namespace {
+
+#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
+// Create some tables of type `Table`, then look at all the new
+// `HashtablezInfo`s to make sure that the `inline_element_size ==
+// expected_element_size`.  The `inline_element_size` is the amount of memory
+// allocated for each slot of a hash table, that is `sizeof(slot_type)`.  Add
+// the new `HashtablezInfo`s to `preexisting_info`.  Store all the new tables
+// into `tables`.
+template <class Table>
+void TestInlineElementSize(
+    HashtablezSampler& sampler,
+    // clang-tidy gives a false positive on this declaration.  This unordered
+    // set cannot be flat_hash_set, however, since that would introduce a mutex
+    // deadlock.
+    std::unordered_set<const HashtablezInfo*>& preexisting_info,  // NOLINT
+    std::vector<Table>& tables, const typename Table::value_type& elt,
+    size_t expected_element_size) {
+  for (int i = 0; i < 10; ++i) {
+    // We create a new table and must store it somewhere so that when we store
+    // a pointer to the resulting `HashtablezInfo` into `preexisting_info`
+    // that we aren't storing a dangling pointer.
+    tables.emplace_back();
+    // We must insert an element to get a hashtablez to instantiate.
+    tables.back().insert(elt);
+  }
+  size_t new_count = 0;
+  sampler.Iterate([&](const HashtablezInfo& info) {
+    if (preexisting_info.insert(&info).second) {
+      EXPECT_EQ(info.inline_element_size, expected_element_size);
+      ++new_count;
+    }
+  });
+  // Make sure we actually did get a new hashtablez.
+  EXPECT_GT(new_count, 0);
+}
+
+struct bigstruct {
+  char a[1000];
+  friend bool operator==(const bigstruct& x, const bigstruct& y) {
+    return memcmp(x.a, y.a, sizeof(x.a)) == 0;
+  }
+  template <typename H>
+  friend H AbslHashValue(H h, const bigstruct& c) {
+    return H::combine_contiguous(std::move(h), c.a, sizeof(c.a));
+  }
+};
+#endif
+
+TEST(FlatHashMap, SampleElementSize) {
+#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
+  // Enable sampling even if the prod default is off.
+  SetHashtablezEnabled(true);
+  SetHashtablezSampleParameter(1);
+
+  auto& sampler = GlobalHashtablezSampler();
+  std::vector<flat_hash_map<int, bigstruct>> flat_map_tables;
+  std::vector<flat_hash_set<bigstruct>> flat_set_tables;
+  std::vector<node_hash_map<int, bigstruct>> node_map_tables;
+  std::vector<node_hash_set<bigstruct>> node_set_tables;
+
+  // It takes thousands of new tables after changing the sampling parameters
+  // before you actually get some instrumentation.  And if you must actually
+  // put something into those tables.
+  for (int i = 0; i < 10000; ++i) {
+    flat_map_tables.emplace_back();
+    flat_map_tables.back()[i] = bigstruct{};
+  }
+
+  // clang-tidy gives a false positive on this declaration.  This unordered set
+  // cannot be a flat_hash_set, however, since that would introduce a mutex
+  // deadlock.
+  std::unordered_set<const HashtablezInfo*> preexisting_info;  // NOLINT
+  sampler.Iterate(
+      [&](const HashtablezInfo& info) { preexisting_info.insert(&info); });
+  TestInlineElementSize(sampler, preexisting_info, flat_map_tables,
+                        {0, bigstruct{}}, sizeof(int) + sizeof(bigstruct));
+  TestInlineElementSize(sampler, preexisting_info, node_map_tables,
+                        {0, bigstruct{}}, sizeof(void*));
+  TestInlineElementSize(sampler, preexisting_info, flat_set_tables,  //
+                        bigstruct{}, sizeof(bigstruct));
+  TestInlineElementSize(sampler, preexisting_info, node_set_tables,  //
+                        bigstruct{}, sizeof(void*));
+#endif
+}
+
+}  // namespace
+}  // namespace container_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/copts/GENERATED_AbseilCopts.cmake b/absl/copts/GENERATED_AbseilCopts.cmake
index 51742c9..a4ab1aa 100644
--- a/absl/copts/GENERATED_AbseilCopts.cmake
+++ b/absl/copts/GENERATED_AbseilCopts.cmake
@@ -71,12 +71,13 @@
     "-Wformat-security"
     "-Wgnu-redeclared-enum"
     "-Winfinite-recursion"
+    "-Winvalid-constexpr"
     "-Wliteral-conversion"
     "-Wmissing-declarations"
     "-Woverlength-strings"
     "-Wpointer-arith"
     "-Wself-assign"
-    "-Wshadow"
+    "-Wshadow-all"
     "-Wstring-conversion"
     "-Wtautological-overlap-compare"
     "-Wundef"
@@ -93,6 +94,7 @@
     "-Wno-implicit-int-conversion"
     "-Wno-shorten-64-to-32"
     "-Wno-sign-conversion"
+    "-Wno-unknown-warning-option"
     "-DNOMINMAX"
 )
 
diff --git a/absl/copts/GENERATED_copts.bzl b/absl/copts/GENERATED_copts.bzl
index 6707488..a6efc98 100644
--- a/absl/copts/GENERATED_copts.bzl
+++ b/absl/copts/GENERATED_copts.bzl
@@ -72,12 +72,13 @@
     "-Wformat-security",
     "-Wgnu-redeclared-enum",
     "-Winfinite-recursion",
+    "-Winvalid-constexpr",
     "-Wliteral-conversion",
     "-Wmissing-declarations",
     "-Woverlength-strings",
     "-Wpointer-arith",
     "-Wself-assign",
-    "-Wshadow",
+    "-Wshadow-all",
     "-Wstring-conversion",
     "-Wtautological-overlap-compare",
     "-Wundef",
@@ -94,6 +95,7 @@
     "-Wno-implicit-int-conversion",
     "-Wno-shorten-64-to-32",
     "-Wno-sign-conversion",
+    "-Wno-unknown-warning-option",
     "-DNOMINMAX",
 ]
 
diff --git a/absl/copts/configure_copts.bzl b/absl/copts/configure_copts.bzl
index 669a906..40d5849 100644
--- a/absl/copts/configure_copts.bzl
+++ b/absl/copts/configure_copts.bzl
@@ -50,6 +50,7 @@
     ":cpu_x64_windows": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS,
     ":cpu_k8": ABSL_RANDOM_HWAES_X64_FLAGS,
     ":cpu_ppc": ["-mcrypto"],
+    ":cpu_aarch64": ABSL_RANDOM_HWAES_ARM64_FLAGS,
 
     # Supported by default or unsupported.
     "//conditions:default": [],
@@ -70,6 +71,7 @@
         "darwin",
         "x64_windows_msvc",
         "x64_windows",
+        "aarch64",
     ]
     for cpu in cpu_configs:
         native.config_setting(
diff --git a/absl/copts/copts.py b/absl/copts/copts.py
index cf52981..0d6c1ec 100644
--- a/absl/copts/copts.py
+++ b/absl/copts/copts.py
@@ -87,12 +87,13 @@
         "-Wformat-security",
         "-Wgnu-redeclared-enum",
         "-Winfinite-recursion",
+        "-Winvalid-constexpr",
         "-Wliteral-conversion",
         "-Wmissing-declarations",
         "-Woverlength-strings",
         "-Wpointer-arith",
         "-Wself-assign",
-        "-Wshadow",
+        "-Wshadow-all",
         "-Wstring-conversion",
         "-Wtautological-overlap-compare",
         "-Wundef",
@@ -111,6 +112,9 @@
         "-Wno-implicit-int-conversion",
         "-Wno-shorten-64-to-32",
         "-Wno-sign-conversion",
+        # Disable warnings on unknown warning flags (when warning flags are
+        # unknown on older compiler versions)
+        "-Wno-unknown-warning-option",
         # Don't define min and max macros (Build on Windows using clang)
         "-DNOMINMAX",
     ],
diff --git a/absl/copts/generate_copts.py b/absl/copts/generate_copts.py
index 0e5dc9f..34be2fc 100755
--- a/absl/copts/generate_copts.py
+++ b/absl/copts/generate_copts.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 """Generate Abseil compile compile option configs.
 
 Usage: <path_to_absl>/copts/generate_copts.py
diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel
index 5385bcb..3c4ea8d 100644
--- a/absl/debugging/BUILD.bazel
+++ b/absl/debugging/BUILD.bazel
@@ -14,7 +14,6 @@
 # limitations under the License.
 #
 
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
@@ -34,8 +33,10 @@
         "internal/stacktrace_aarch64-inl.inc",
         "internal/stacktrace_arm-inl.inc",
         "internal/stacktrace_config.h",
+        "internal/stacktrace_emscripten-inl.inc",
         "internal/stacktrace_generic-inl.inc",
         "internal/stacktrace_powerpc-inl.inc",
+        "internal/stacktrace_riscv-inl.inc",
         "internal/stacktrace_unimplemented-inl.inc",
         "internal/stacktrace_win32-inl.inc",
         "internal/stacktrace_x86-inl.inc",
@@ -57,6 +58,7 @@
         "symbolize.cc",
         "symbolize_darwin.inc",
         "symbolize_elf.inc",
+        "symbolize_emscripten.inc",
         "symbolize_unimplemented.inc",
         "symbolize_win32.inc",
     ],
@@ -180,6 +182,7 @@
     ],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = ["//visibility:private"],
     deps = [
         "//absl/base:config",
         "//absl/base:core_headers",
@@ -194,6 +197,7 @@
     srcs = ["internal/demangle.cc"],
     hdrs = ["internal/demangle.h"],
     copts = ABSL_DEFAULT_COPTS,
+    visibility = ["//visibility:private"],
     deps = [
         "//absl/base",
         "//absl/base:config",
@@ -344,6 +348,7 @@
     srcs = ["internal/stack_consumption_test.cc"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
+    tags = ["notsan"],
     deps = [
         ":stack_consumption",
         "//absl/base:core_headers",
diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt
index 074b44c..b16fa00 100644
--- a/absl/debugging/CMakeLists.txt
+++ b/absl/debugging/CMakeLists.txt
@@ -22,8 +22,10 @@
     "internal/stacktrace_aarch64-inl.inc"
     "internal/stacktrace_arm-inl.inc"
     "internal/stacktrace_config.h"
+    "internal/stacktrace_emscripten-inl.inc"
     "internal/stacktrace_generic-inl.inc"
     "internal/stacktrace_powerpc-inl.inc"
+    "internal/stacktrace_riscv-inl.inc"
     "internal/stacktrace_unimplemented-inl.inc"
     "internal/stacktrace_win32-inl.inc"
     "internal/stacktrace_x86-inl.inc"
@@ -48,6 +50,7 @@
     "symbolize.cc"
     "symbolize_darwin.inc"
     "symbolize_elf.inc"
+    "symbolize_emscripten.inc"
     "symbolize_unimplemented.inc"
     "symbolize_win32.inc"
   COPTS
@@ -87,7 +90,7 @@
     absl::memory
     absl::raw_logging_internal
     absl::strings
-    gmock
+    GTest::gmock
 )
 
 absl_cc_library(
@@ -141,7 +144,7 @@
     absl::strings
     absl::raw_logging_internal
     Threads::Threads
-    gmock
+    GTest::gmock
 )
 
 absl_cc_library(
@@ -194,7 +197,7 @@
     absl::core_headers
     absl::memory
     absl::raw_logging_internal
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -261,7 +264,7 @@
   DEPS
     absl::leak_check_api_enabled_for_testing
     absl::base
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -275,7 +278,7 @@
   DEPS
     absl::leak_check_api_disabled_for_testing
     absl::base
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -292,7 +295,7 @@
     absl::leak_check_disable
     absl::base
     absl::raw_logging_internal
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -322,7 +325,7 @@
     absl::stack_consumption
     absl::core_headers
     absl::raw_logging_internal
-    gmock_main
+    GTest::gmock_main
 )
 
 # component target
diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc
index 3ddebd7..689e597 100644
--- a/absl/debugging/failure_signal_handler.cc
+++ b/absl/debugging/failure_signal_handler.cc
@@ -367,6 +367,7 @@
   // goes after this point.
   if (fsh_options.writerfn != nullptr) {
     WriteFailureInfo(signo, ucontext, my_cpu, fsh_options.writerfn);
+    fsh_options.writerfn(nullptr);
   }
 
   if (fsh_options.call_previous_handler) {
diff --git a/absl/debugging/failure_signal_handler.h b/absl/debugging/failure_signal_handler.h
index 0c0f585..500115c 100644
--- a/absl/debugging/failure_signal_handler.h
+++ b/absl/debugging/failure_signal_handler.h
@@ -90,7 +90,7 @@
   // If non-null, indicates a pointer to a callback function that will be called
   // upon failure, with a string argument containing failure data. This function
   // may be used as a hook to write failure data to a secondary location, such
-  // as a log file. This function may also be called with null data, as a hint
+  // as a log file. This function will also be called with null data, as a hint
   // to flush any buffered data before the program may be terminated. Consider
   // flushing any buffered data in all calls to this function.
   //
diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc
index 5cd5632..93ae327 100644
--- a/absl/debugging/internal/demangle.cc
+++ b/absl/debugging/internal/demangle.cc
@@ -1617,6 +1617,7 @@
 //              ::= <2-ary operator-name> <expression> <expression>
 //              ::= <3-ary operator-name> <expression> <expression> <expression>
 //              ::= cl <expression>+ E
+//              ::= cp <simple-id> <expression>* E # Clang-specific.
 //              ::= cv <type> <expression>      # type (expression)
 //              ::= cv <type> _ <expression>* E # type (expr-list)
 //              ::= st <type>
@@ -1639,14 +1640,23 @@
     return true;
   }
 
-  // Object/function call expression.
   ParseState copy = state->parse_state;
+
+  // Object/function call expression.
   if (ParseTwoCharToken(state, "cl") && OneOrMore(ParseExpression, state) &&
       ParseOneCharToken(state, 'E')) {
     return true;
   }
   state->parse_state = copy;
 
+  // Clang-specific "cp <simple-id> <expression>* E"
+  //   https://clang.llvm.org/doxygen/ItaniumMangle_8cpp_source.html#l04338
+  if (ParseTwoCharToken(state, "cp") && ParseSimpleId(state) &&
+      ZeroOrMore(ParseExpression, state) && ParseOneCharToken(state, 'E')) {
+    return true;
+  }
+  state->parse_state = copy;
+
   // Function-param expression (level 0).
   if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) &&
       Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) {
diff --git a/absl/debugging/internal/elf_mem_image.cc b/absl/debugging/internal/elf_mem_image.cc
index 24cc013..29a2818 100644
--- a/absl/debugging/internal/elf_mem_image.cc
+++ b/absl/debugging/internal/elf_mem_image.cc
@@ -22,6 +22,7 @@
 #include <string.h>
 #include <cassert>
 #include <cstddef>
+#include "absl/base/config.h"
 #include "absl/base/internal/raw_logging.h"
 
 // From binutils/include/elf/common.h (this doesn't appear to be documented
@@ -43,11 +44,11 @@
 
 namespace {
 
-#if __WORDSIZE == 32
+#if __SIZEOF_POINTER__ == 4
 const int kElfClass = ELFCLASS32;
 int ElfBind(const ElfW(Sym) *symbol) { return ELF32_ST_BIND(symbol->st_info); }
 int ElfType(const ElfW(Sym) *symbol) { return ELF32_ST_TYPE(symbol->st_info); }
-#elif __WORDSIZE == 64
+#elif __SIZEOF_POINTER__ == 8
 const int kElfClass = ELFCLASS64;
 int ElfBind(const ElfW(Sym) *symbol) { return ELF64_ST_BIND(symbol->st_info); }
 int ElfType(const ElfW(Sym) *symbol) { return ELF64_ST_TYPE(symbol->st_info); }
@@ -175,17 +176,17 @@
   }
   switch (base_as_char[EI_DATA]) {
     case ELFDATA2LSB: {
-      if (__LITTLE_ENDIAN != __BYTE_ORDER) {
-        assert(false);
-        return;
-      }
+#ifndef ABSL_IS_LITTLE_ENDIAN
+      assert(false);
+      return;
+#endif
       break;
     }
     case ELFDATA2MSB: {
-      if (__BIG_ENDIAN != __BYTE_ORDER) {
-        assert(false);
-        return;
-      }
+#ifndef ABSL_IS_BIG_ENDIAN
+      assert(false);
+      return;
+#endif
       break;
     }
     default: {
@@ -221,7 +222,7 @@
       reinterpret_cast<ElfW(Dyn) *>(dynamic_program_header->p_vaddr +
                                     relocation);
   for (; dynamic_entry->d_tag != DT_NULL; ++dynamic_entry) {
-    const ElfW(Xword) value = dynamic_entry->d_un.d_val + relocation;
+    const auto value = dynamic_entry->d_un.d_val + relocation;
     switch (dynamic_entry->d_tag) {
       case DT_HASH:
         hash_ = reinterpret_cast<ElfW(Word) *>(value);
diff --git a/absl/debugging/internal/elf_mem_image.h b/absl/debugging/internal/elf_mem_image.h
index 46bfade..a894bd4 100644
--- a/absl/debugging/internal/elf_mem_image.h
+++ b/absl/debugging/internal/elf_mem_image.h
@@ -31,8 +31,8 @@
 #error ABSL_HAVE_ELF_MEM_IMAGE cannot be directly set
 #endif
 
-#if defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) && \
-    !defined(__asmjs__) && !defined(__wasm__)
+#if defined(__ELF__) && !defined(__native_client__) && !defined(__asmjs__) && \
+    !defined(__wasm__)
 #define ABSL_HAVE_ELF_MEM_IMAGE 1
 #endif
 
@@ -40,6 +40,10 @@
 
 #include <link.h>  // for ElfW
 
+#if defined(__FreeBSD__) && !defined(ElfW)
+#define ElfW(x) __ElfN(x)
+#endif
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace debugging_internal {
diff --git a/absl/debugging/internal/stack_consumption.cc b/absl/debugging/internal/stack_consumption.cc
index e3dd51c..5134864 100644
--- a/absl/debugging/internal/stack_consumption.cc
+++ b/absl/debugging/internal/stack_consumption.cc
@@ -43,7 +43,7 @@
 // unspecified. Therefore, instead we hardcode the direction of the
 // stack on platforms we know about.
 #if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \
-    defined(__aarch64__)
+    defined(__aarch64__) || defined(__riscv)
 constexpr bool kStackGrowsDown = true;
 #else
 #error Need to define kStackGrowsDown
diff --git a/absl/debugging/internal/stack_consumption.h b/absl/debugging/internal/stack_consumption.h
index 2b5e715..f41b64c 100644
--- a/absl/debugging/internal/stack_consumption.h
+++ b/absl/debugging/internal/stack_consumption.h
@@ -26,7 +26,7 @@
 #error ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION cannot be set directly
 #elif !defined(__APPLE__) && !defined(_WIN32) &&                     \
     (defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \
-     defined(__aarch64__))
+     defined(__aarch64__) || defined(__riscv))
 #define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1
 
 namespace absl {
diff --git a/absl/debugging/internal/stacktrace_config.h b/absl/debugging/internal/stacktrace_config.h
index cca410d..ff21b71 100644
--- a/absl/debugging/internal/stacktrace_config.h
+++ b/absl/debugging/internal/stacktrace_config.h
@@ -35,7 +35,11 @@
 // Thread local support required for UnwindImpl.
 #define ABSL_STACKTRACE_INL_HEADER \
   "absl/debugging/internal/stacktrace_generic-inl.inc"
-#endif
+#endif  // defined(ABSL_HAVE_THREAD_LOCAL)
+
+#elif defined(__EMSCRIPTEN__)
+#define ABSL_STACKTRACE_INL_HEADER \
+  "absl/debugging/internal/stacktrace_emscripten-inl.inc"
 
 #elif defined(__linux__) && !defined(__ANDROID__)
 
@@ -51,7 +55,7 @@
 // Note: When using glibc this may require -funwind-tables to function properly.
 #define ABSL_STACKTRACE_INL_HEADER \
   "absl/debugging/internal/stacktrace_generic-inl.inc"
-#endif
+#endif  // __has_include(<execinfo.h>)
 #elif defined(__i386__) || defined(__x86_64__)
 #define ABSL_STACKTRACE_INL_HEADER \
   "absl/debugging/internal/stacktrace_x86-inl.inc"
@@ -61,15 +65,18 @@
 #elif defined(__aarch64__)
 #define ABSL_STACKTRACE_INL_HEADER \
   "absl/debugging/internal/stacktrace_aarch64-inl.inc"
+#elif defined(__riscv)
+#define ABSL_STACKTRACE_INL_HEADER \
+  "absl/debugging/internal/stacktrace_riscv-inl.inc"
 #elif defined(__has_include)
 #if __has_include(<execinfo.h>)
 // Note: When using glibc this may require -funwind-tables to function properly.
 #define ABSL_STACKTRACE_INL_HEADER \
   "absl/debugging/internal/stacktrace_generic-inl.inc"
-#endif
-#endif
+#endif  // __has_include(<execinfo.h>)
+#endif  // defined(__has_include)
 
-#endif
+#endif  // defined(__linux__) && !defined(__ANDROID__)
 
 // Fallback to the empty implementation.
 #if !defined(ABSL_STACKTRACE_INL_HEADER)
diff --git a/absl/debugging/internal/stacktrace_emscripten-inl.inc b/absl/debugging/internal/stacktrace_emscripten-inl.inc
new file mode 100644
index 0000000..0f44451
--- /dev/null
+++ b/absl/debugging/internal/stacktrace_emscripten-inl.inc
@@ -0,0 +1,110 @@
+// 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.
+//
+// Portable implementation - just use glibc
+//
+// Note:  The glibc implementation may cause a call to malloc.
+// This can cause a deadlock in HeapProfiler.
+
+#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_
+#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_
+
+#include <emscripten.h>
+
+#include <atomic>
+#include <cstring>
+
+#include "absl/base/attributes.h"
+#include "absl/debugging/stacktrace.h"
+
+extern "C" {
+uintptr_t emscripten_stack_snapshot();
+uint32_t emscripten_stack_unwind_buffer(uintptr_t pc, void *buffer,
+                                        uint32_t depth);
+}
+
+// Sometimes, we can try to get a stack trace from within a stack
+// trace, which can cause a self-deadlock.
+// Protect against such reentrant call by failing to get a stack trace.
+//
+// We use __thread here because the code here is extremely low level -- it is
+// called while collecting stack traces from within malloc and mmap, and thus
+// can not call anything which might call malloc or mmap itself.
+static __thread int recursive = 0;
+
+// The stack trace function might be invoked very early in the program's
+// execution (e.g. from the very first malloc).
+// As such, we suppress usage of backtrace during this early stage of execution.
+static std::atomic<bool> disable_stacktraces(true);  // Disabled until healthy.
+// Waiting until static initializers run seems to be late enough.
+// This file is included into stacktrace.cc so this will only run once.
+ABSL_ATTRIBUTE_UNUSED static int stacktraces_enabler = []() {
+  // Check if we can even create stacktraces. If not, bail early and leave
+  // disable_stacktraces set as-is.
+  // clang-format off
+  if (!EM_ASM_INT({ return (typeof wasmOffsetConverter !== 'undefined'); })) {
+    return 0;
+  }
+  // clang-format on
+  disable_stacktraces.store(false, std::memory_order_relaxed);
+  return 0;
+}();
+
+template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
+static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
+                      const void *ucp, int *min_dropped_frames) {
+  if (recursive || disable_stacktraces.load(std::memory_order_relaxed)) {
+    return 0;
+  }
+  ++recursive;
+
+  static_cast<void>(ucp);  // Unused.
+  constexpr int kStackLength = 64;
+  void *stack[kStackLength];
+
+  int size;
+  uintptr_t pc = emscripten_stack_snapshot();
+  size = emscripten_stack_unwind_buffer(pc, stack, kStackLength);
+
+  int result_count = size - skip_count;
+  if (result_count < 0) result_count = 0;
+  if (result_count > max_depth) result_count = max_depth;
+  for (int i = 0; i < result_count; i++) result[i] = stack[i + skip_count];
+
+  if (IS_STACK_FRAMES) {
+    // No implementation for finding out the stack frame sizes yet.
+    memset(sizes, 0, sizeof(*sizes) * result_count);
+  }
+  if (min_dropped_frames != nullptr) {
+    if (size - skip_count - max_depth > 0) {
+      *min_dropped_frames = size - skip_count - max_depth;
+    } else {
+      *min_dropped_frames = 0;
+    }
+  }
+
+  --recursive;
+
+  return result_count;
+}
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace debugging_internal {
+bool StackTraceWorksForTest() { return true; }
+}  // namespace debugging_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_
diff --git a/absl/debugging/internal/stacktrace_riscv-inl.inc b/absl/debugging/internal/stacktrace_riscv-inl.inc
new file mode 100644
index 0000000..8cbc785
--- /dev/null
+++ b/absl/debugging/internal/stacktrace_riscv-inl.inc
@@ -0,0 +1,234 @@
+// Copyright 2021 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_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_
+#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_
+
+// Generate stack trace for riscv
+
+#include <sys/ucontext.h>
+
+#include "absl/base/config.h"
+#if defined(__linux__)
+#include <sys/mman.h>
+#include <ucontext.h>
+#include <unistd.h>
+#endif
+
+#include <atomic>
+#include <cassert>
+#include <cstdint>
+#include <iostream>
+
+#include "absl/base/attributes.h"
+#include "absl/debugging/internal/address_is_readable.h"
+#include "absl/debugging/internal/vdso_support.h"
+#include "absl/debugging/stacktrace.h"
+
+static const uintptr_t kUnknownFrameSize = 0;
+
+#if defined(__linux__)
+// Returns the address of the VDSO __kernel_rt_sigreturn function, if present.
+static const unsigned char *GetKernelRtSigreturnAddress() {
+  constexpr uintptr_t kImpossibleAddress = 0;
+  ABSL_CONST_INIT static std::atomic<uintptr_t> memoized(kImpossibleAddress);
+  uintptr_t address = memoized.load(std::memory_order_relaxed);
+  if (address != kImpossibleAddress) {
+    return reinterpret_cast<const unsigned char *>(address);
+  }
+
+  address = reinterpret_cast<uintptr_t>(nullptr);
+
+#if ABSL_HAVE_VDSO_SUPPORT
+  absl::debugging_internal::VDSOSupport vdso;
+  if (vdso.IsPresent()) {
+    absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info;
+    // Symbol versioning pulled from arch/riscv/kernel/vdso/vdso.lds at v5.10.
+    auto lookup = [&](int type) {
+      return vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_4.15", type,
+                               &symbol_info);
+    };
+    if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) ||
+        symbol_info.address == nullptr) {
+      // Unexpected: VDSO is present, yet the expected symbol is missing or
+      // null.
+      assert(false && "VDSO is present, but doesn't have expected symbol");
+    } else {
+      if (reinterpret_cast<uintptr_t>(symbol_info.address) !=
+          kImpossibleAddress) {
+        address = reinterpret_cast<uintptr_t>(symbol_info.address);
+      } else {
+        assert(false && "VDSO returned invalid address");
+      }
+    }
+  }
+#endif
+
+  memoized.store(address, std::memory_order_relaxed);
+  return reinterpret_cast<const unsigned char *>(address);
+}
+#endif  // __linux__
+
+// Compute the size of a stack frame in [low..high).  We assume that low < high.
+// Return size of kUnknownFrameSize.
+template <typename T>
+static inline uintptr_t ComputeStackFrameSize(const T *low, const T *high) {
+  const char *low_char_ptr = reinterpret_cast<const char *>(low);
+  const char *high_char_ptr = reinterpret_cast<const char *>(high);
+  return low < high ? high_char_ptr - low_char_ptr : kUnknownFrameSize;
+}
+
+// Given a pointer to a stack frame, locate and return the calling stackframe,
+// or return null if no stackframe can be found. Perform sanity checks (the
+// strictness of which is controlled by the boolean parameter
+// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
+template <bool STRICT_UNWINDING, bool WITH_CONTEXT>
+ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS  // May read random elements from stack.
+ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY   // May read random elements from stack.
+static void ** NextStackFrame(void **old_frame_pointer, const void *uc) {
+  //               .
+  //               .
+  //               .
+  //   +-> +----------------+
+  //   |   | return address |
+  //   |   |   previous fp  |
+  //   |   |      ...       |
+  //   |   +----------------+ <-+
+  //   |   | return address |   |
+  //   +---|-  previous fp  |   |
+  //       |      ...       |   |
+  // $fp ->|----------------+   |
+  //       | return address |   |
+  //       |   previous fp -|---+
+  // $sp ->|      ...       |
+  //       +----------------+
+  void **new_frame_pointer = reinterpret_cast<void **>(old_frame_pointer[-2]);
+  bool check_frame_size = true;
+
+#if defined(__linux__)
+  if (WITH_CONTEXT && uc != nullptr) {
+    // Check to see if next frame's return address is __kernel_rt_sigreturn.
+    if (old_frame_pointer[-1] == GetKernelRtSigreturnAddress()) {
+      const ucontext_t *ucv = static_cast<const ucontext_t *>(uc);
+      // old_frame_pointer is not suitable for unwinding, look at ucontext to
+      // discover frame pointer before signal.
+      //
+      // RISCV ELF psABI has the frame pointer at x8/fp/s0.
+      // -- RISCV psABI Table 18.2
+      void **const pre_signal_frame_pointer =
+          reinterpret_cast<void **>(ucv->uc_mcontext.__gregs[8]);
+
+      // Check the alleged frame pointer is actually readable. This is to
+      // prevent "double fault" in case we hit the first fault due to stack
+      // corruption.
+      if (!absl::debugging_internal::AddressIsReadable(
+              pre_signal_frame_pointer))
+        return nullptr;
+
+      // Alleged frame pointer is readable, use it for further unwinding.
+      new_frame_pointer = pre_signal_frame_pointer;
+
+      // Skip frame size check if we return from a signal.  We may be using an
+      // alterate stack for signals.
+      check_frame_size = false;
+    }
+  }
+#endif
+
+  // The RISCV ELF psABI mandates that the stack pointer is always 16-byte
+  // aligned.
+  // FIXME(abdulras) this doesn't hold for ILP32E which only mandates a 4-byte
+  // alignment.
+  if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 15) != 0)
+    return nullptr;
+
+  // Check frame size.  In strict mode, we assume frames to be under 100,000
+  // bytes.  In non-strict mode, we relax the limit to 1MB.
+  if (check_frame_size) {
+    const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
+    const uintptr_t frame_size =
+        ComputeStackFrameSize(old_frame_pointer, new_frame_pointer);
+    if (frame_size == kUnknownFrameSize || frame_size > max_size)
+      return nullptr;
+  }
+
+  return new_frame_pointer;
+}
+
+template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
+ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS  // May read random elements from stack.
+ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY   // May read random elements from stack.
+static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
+                      const void *ucp, int *min_dropped_frames) {
+#if defined(__GNUC__)
+  void **frame_pointer = reinterpret_cast<void **>(__builtin_frame_address(0));
+#else
+#error reading stack pointer not yet supported on this platform
+#endif
+
+  skip_count++;  // Skip the frame for this function.
+  int n = 0;
+
+  // The `frame_pointer` that is computed here points to the top of the frame.
+  // The two words preceding the address are the return address and the previous
+  // frame pointer.  To find a PC value associated with the current frame, we
+  // need to go down a level in the call chain.  So we remember the return
+  // address of the last frame seen.  This does not work for the first stack
+  // frame, which belongs to `UnwindImp()` but we skip the frame for
+  // `UnwindImp()` anyway.
+  void *prev_return_address = nullptr;
+
+  while (frame_pointer && n < max_depth) {
+    // The absl::GetStackFrames routine si called when we are in some
+    // informational context (the failure signal handler for example).  Use the
+    // non-strict unwinding rules to produce a stack trace that is as complete
+    // as possible (even if it contains a few bogus entries in some rare cases).
+    void **next_frame_pointer =
+        NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp);
+
+    if (skip_count > 0) {
+      skip_count--;
+    } else {
+      result[n] = prev_return_address;
+      if (IS_STACK_FRAMES) {
+        sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer);
+      }
+      n++;
+    }
+    prev_return_address = frame_pointer[-1];
+    frame_pointer = next_frame_pointer;
+  }
+  if (min_dropped_frames != nullptr) {
+    // Implementation detail: we clamp the max of frames we are willing to
+    // count, so as not to spend too much time in the loop below.
+    const int kMaxUnwind = 200;
+    int j = 0;
+    for (; frame_pointer != nullptr && j < kMaxUnwind; j++) {
+      frame_pointer =
+          NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp);
+    }
+    *min_dropped_frames = j;
+  }
+  return n;
+}
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace debugging_internal {
+bool StackTraceWorksForTest() { return true; }
+}  // namespace debugging_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif
diff --git a/absl/debugging/internal/stacktrace_x86-inl.inc b/absl/debugging/internal/stacktrace_x86-inl.inc
index bc320ff..847a547 100644
--- a/absl/debugging/internal/stacktrace_x86-inl.inc
+++ b/absl/debugging/internal/stacktrace_x86-inl.inc
@@ -27,6 +27,7 @@
 
 #include <cassert>
 #include <cstdint>
+#include <limits>
 
 #include "absl/base/macros.h"
 #include "absl/base/port.h"
@@ -132,9 +133,8 @@
     const uintptr_t bp = 0;
     const uintptr_t sp = 0;
 #endif
-    // Sanity-check that the base pointer is valid.  It should be as long as
-    // SHRINK_WRAP_FRAME_POINTER is not set, but it's possible that some code in
-    // the process is compiled with --copt=-fomit-frame-pointer or
+    // Sanity-check that the base pointer is valid. It's possible that some
+    // code in the process is compiled with --copt=-fomit-frame-pointer or
     // --copt=-momit-leaf-frame-pointer.
     //
     // TODO(bcmills): -momit-leaf-frame-pointer is currently the default
@@ -159,7 +159,8 @@
 template <bool STRICT_UNWINDING, bool WITH_CONTEXT>
 ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS  // May read random elements from stack.
 ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY   // May read random elements from stack.
-static void **NextStackFrame(void **old_fp, const void *uc) {
+static void **NextStackFrame(void **old_fp, const void *uc,
+                             size_t stack_low, size_t stack_high) {
   void **new_fp = (void **)*old_fp;
 
 #if defined(__linux__) && defined(__i386__)
@@ -247,7 +248,7 @@
   // using an alternate signal stack.
   //
   // TODO(bcmills): The GetFP call should be completely unnecessary when
-  // SHRINK_WRAP_FRAME_POINTER is set (because we should be back in the thread's
+  // ENABLE_COMBINED_UNWINDER is set (because we should be back in the thread's
   // stack by this point), but it is empirically still needed (e.g. when the
   // stack includes a call to abort).  unw_get_reg returns UNW_EBADREG for some
   // frames.  Figure out why GetValidFrameAddr and/or libunwind isn't doing what
@@ -258,6 +259,18 @@
     // at a greater address that the current one.
     if (new_fp_u <= old_fp_u) return nullptr;
     if (new_fp_u - old_fp_u > kMaxFrameBytes) return nullptr;
+
+    if (stack_low < old_fp_u && old_fp_u <= stack_high) {
+      // Old BP was in the expected stack region...
+      if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) {
+        // ... but new BP is outside of expected stack region.
+        // It is most likely bogus.
+        return nullptr;
+      }
+    } else {
+      // We may be here if we are executing in a co-routine with a
+      // separate stack. We can't do safety checks in this case.
+    }
   } else {
     if (new_fp == nullptr) return nullptr;  // skip AddressIsReadable() below
     // In the non-strict mode, allow discontiguous stack frames.
@@ -297,13 +310,17 @@
   int n = 0;
   void **fp = reinterpret_cast<void **>(__builtin_frame_address(0));
 
+  size_t stack_low = getpagesize();  // Assume that the first page is not stack.
+  size_t stack_high = std::numeric_limits<size_t>::max() - sizeof(void *);
+
   while (fp && n < max_depth) {
     if (*(fp + 1) == reinterpret_cast<void *>(0)) {
       // In 64-bit code, we often see a frame that
       // points to itself and has a return address of 0.
       break;
     }
-    void **next_fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp);
+    void **next_fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(
+        fp, ucp, stack_low, stack_high);
     if (skip_count > 0) {
       skip_count--;
     } else {
@@ -326,7 +343,8 @@
     const int kMaxUnwind = 1000;
     int j = 0;
     for (; fp != nullptr && j < kMaxUnwind; j++) {
-      fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp);
+      fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp, stack_low,
+                                                             stack_high);
     }
     *min_dropped_frames = j;
   }
diff --git a/absl/debugging/internal/symbolize.h b/absl/debugging/internal/symbolize.h
index 4f26130..27d5e65 100644
--- a/absl/debugging/internal/symbolize.h
+++ b/absl/debugging/internal/symbolize.h
@@ -28,8 +28,8 @@
 
 #ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE
 #error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set
-#elif defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) && \
-    !defined(__asmjs__) && !defined(__wasm__)
+#elif defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) \
+      && !defined(__asmjs__) && !defined(__wasm__)
 #define ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE 1
 
 #include <elf.h>
@@ -68,6 +68,12 @@
 #define ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE 1
 #endif
 
+#ifdef ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE
+#error ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE cannot be directly set
+#elif defined(__EMSCRIPTEN__)
+#define ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE 1
+#endif
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace debugging_internal {
diff --git a/absl/debugging/internal/vdso_support.cc b/absl/debugging/internal/vdso_support.cc
index 6be16d9..977a9f6 100644
--- a/absl/debugging/internal/vdso_support.cc
+++ b/absl/debugging/internal/vdso_support.cc
@@ -20,12 +20,25 @@
 
 #ifdef ABSL_HAVE_VDSO_SUPPORT     // defined in vdso_support.h
 
+#if !defined(__has_include)
+#define __has_include(header) 0
+#endif
+
 #include <errno.h>
 #include <fcntl.h>
+#if __has_include(<syscall.h>)
+#include <syscall.h>
+#elif __has_include(<sys/syscall.h>)
 #include <sys/syscall.h>
+#endif
 #include <unistd.h>
 
-#if __GLIBC_PREREQ(2, 16)  // GLIBC-2.16 implements getauxval.
+#if defined(__GLIBC__) && \
+    (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16))
+#define ABSL_HAVE_GETAUXVAL
+#endif
+
+#ifdef ABSL_HAVE_GETAUXVAL
 #include <sys/auxv.h>
 #endif
 
@@ -37,6 +50,11 @@
 #define AT_SYSINFO_EHDR 33  // for crosstoolv10
 #endif
 
+#if defined(__FreeBSD__)
+using Elf64_auxv_t = Elf64_Auxinfo;
+using Elf32_auxv_t = Elf32_Auxinfo;
+#endif
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace debugging_internal {
@@ -65,7 +83,7 @@
 // the operation should be idempotent.
 const void *VDSOSupport::Init() {
   const auto kInvalidBase = debugging_internal::ElfMemImage::kInvalidBase;
-#if __GLIBC_PREREQ(2, 16)
+#ifdef ABSL_HAVE_GETAUXVAL
   if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) {
     errno = 0;
     const void *const sysinfo_ehdr =
@@ -74,7 +92,7 @@
       vdso_base_.store(sysinfo_ehdr, std::memory_order_relaxed);
     }
   }
-#endif  // __GLIBC_PREREQ(2, 16)
+#endif  // ABSL_HAVE_GETAUXVAL
   if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) {
     int fd = open("/proc/self/auxv", O_RDONLY);
     if (fd == -1) {
diff --git a/absl/debugging/stacktrace.cc b/absl/debugging/stacktrace.cc
index 1f7c7d8..ff8069f 100644
--- a/absl/debugging/stacktrace.cc
+++ b/absl/debugging/stacktrace.cc
@@ -49,8 +49,10 @@
 
 # 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"
diff --git a/absl/debugging/symbolize.cc b/absl/debugging/symbolize.cc
index 5e4a25d..f1abdfd 100644
--- a/absl/debugging/symbolize.cc
+++ b/absl/debugging/symbolize.cc
@@ -31,6 +31,8 @@
 #include "absl/debugging/symbolize_win32.inc"
 #elif defined(__APPLE__)
 #include "absl/debugging/symbolize_darwin.inc"
+#elif defined(__EMSCRIPTEN__)
+#include "absl/debugging/symbolize_emscripten.inc"
 #else
 #include "absl/debugging/symbolize_unimplemented.inc"
 #endif
diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc
index f4d5727..3ff343d 100644
--- a/absl/debugging/symbolize_elf.inc
+++ b/absl/debugging/symbolize_elf.inc
@@ -77,6 +77,10 @@
 #include "absl/debugging/internal/vdso_support.h"
 #include "absl/strings/string_view.h"
 
+#if defined(__FreeBSD__) && !defined(ElfW)
+#define ElfW(x) __ElfN(x)
+#endif
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
@@ -701,6 +705,16 @@
       const char *start_address =
           ComputeOffset(original_start_address, relocation);
 
+#ifdef __arm__
+      // ARM functions are always aligned to multiples of two bytes; the
+      // lowest-order bit in start_address is ignored by the CPU and indicates
+      // whether the function contains ARM (0) or Thumb (1) code. We don't care
+      // about what encoding is being used; we just want the real start address
+      // of the function.
+      start_address = reinterpret_cast<const char *>(
+          reinterpret_cast<uintptr_t>(start_address) & ~1);
+#endif
+
       if (deref_function_descriptor_pointer &&
           InSection(original_start_address, opd)) {
         // The opd section is mapped into memory.  Just dereference
diff --git a/absl/debugging/symbolize_emscripten.inc b/absl/debugging/symbolize_emscripten.inc
new file mode 100644
index 0000000..c226c45
--- /dev/null
+++ b/absl/debugging/symbolize_emscripten.inc
@@ -0,0 +1,72 @@
+// Copyright 2020 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 <cxxabi.h>
+#include <emscripten.h>
+
+#include <algorithm>
+#include <cstring>
+
+#include "absl/base/internal/raw_logging.h"
+#include "absl/debugging/internal/demangle.h"
+#include "absl/strings/numbers.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+
+extern "C" {
+const char* emscripten_pc_get_function(const void* pc);
+}
+
+// clang-format off
+EM_JS(bool, HaveOffsetConverter, (),
+      { return typeof wasmOffsetConverter !== 'undefined'; });
+// clang-format on
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+void InitializeSymbolizer(const char*) {
+  if (!HaveOffsetConverter()) {
+    ABSL_RAW_LOG(INFO,
+                 "Symbolization unavailable. Rebuild with -sWASM=1 "
+                 "and -sUSE_OFFSET_CONVERTER=1.");
+  }
+}
+
+bool Symbolize(const void* pc, char* out, int out_size) {
+  // Check if we have the offset converter necessary for pc_get_function.
+  // Without it, the program will abort().
+  if (!HaveOffsetConverter()) {
+    return false;
+  }
+  const char* func_name = emscripten_pc_get_function(pc);
+  if (func_name == nullptr) {
+    return false;
+  }
+
+  strncpy(out, func_name, out_size);
+
+  if (out[out_size - 1] != '\0') {
+    // strncpy() does not '\0' terminate when it truncates.
+    static constexpr char kEllipsis[] = "...";
+    int ellipsis_size = std::min<int>(sizeof(kEllipsis) - 1, out_size - 1);
+    memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size);
+    out[out_size - 1] = '\0';
+  }
+
+  return true;
+}
+
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc
index a2dd495..c710a3d 100644
--- a/absl/debugging/symbolize_test.cc
+++ b/absl/debugging/symbolize_test.cc
@@ -146,8 +146,22 @@
   return TrySymbolizeWithLimit(pc, sizeof(try_symbolize_buffer));
 }
 
-#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) || \
-    defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE)
+#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) ||    \
+    defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE) || \
+    defined(ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE)
+
+// Test with a return address.
+void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() {
+#if defined(ABSL_HAVE_ATTRIBUTE_NOINLINE)
+  void *return_address = __builtin_return_address(0);
+  const char *symbol = TrySymbolize(return_address);
+  ABSL_RAW_CHECK(symbol != nullptr, "TestWithReturnAddress failed");
+  ABSL_RAW_CHECK(strcmp(symbol, "main") == 0, "TestWithReturnAddress failed");
+  std::cout << "TestWithReturnAddress passed" << std::endl;
+#endif
+}
+
+#ifndef ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE
 
 TEST(Symbolize, Cached) {
   // Compilers should give us pointers to them.
@@ -418,6 +432,7 @@
   close(fd);
 }
 #endif  // !ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE
+#endif  // !ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE
 
 // x86 specific tests.  Uses some inline assembler.
 extern "C" {
@@ -466,17 +481,46 @@
 }
 }
 
-// Test with a return address.
-void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() {
+#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target)
+// Test that we correctly identify bounds of Thumb functions on ARM.
+//
+// Thumb functions have the lowest-order bit set in their addresses in the ELF
+// symbol table. This requires some extra logic to properly compute function
+// bounds. To test this logic, nudge a Thumb function right up against an ARM
+// function and try to symbolize the ARM function.
+//
+// A naive implementation will simply use the Thumb function's entry point as
+// written in the symbol table and will therefore treat the Thumb function as
+// extending one byte further in the instruction stream than it actually does.
+// When asked to symbolize the start of the ARM function, it will identify an
+// overlap between the Thumb and ARM functions, and it will return the name of
+// the Thumb function.
+//
+// A correct implementation, on the other hand, will null out the lowest-order
+// bit in the Thumb function's entry point. It will correctly compute the end of
+// the Thumb function, it will find no overlap between the Thumb and ARM
+// functions, and it will return the name of the ARM function.
+
+__attribute__((target("thumb"))) int ArmThumbOverlapThumb(int x) {
+  return x * x * x;
+}
+
+__attribute__((target("arm"))) int ArmThumbOverlapArm(int x) {
+  return x * x * x;
+}
+
+void ABSL_ATTRIBUTE_NOINLINE TestArmThumbOverlap() {
 #if defined(ABSL_HAVE_ATTRIBUTE_NOINLINE)
-  void *return_address = __builtin_return_address(0);
-  const char *symbol = TrySymbolize(return_address);
-  ABSL_RAW_CHECK(symbol != nullptr, "TestWithReturnAddress failed");
-  ABSL_RAW_CHECK(strcmp(symbol, "main") == 0, "TestWithReturnAddress failed");
-  std::cout << "TestWithReturnAddress passed" << std::endl;
+  const char *symbol = TrySymbolize((void *)&ArmThumbOverlapArm);
+  ABSL_RAW_CHECK(symbol != nullptr, "TestArmThumbOverlap failed");
+  ABSL_RAW_CHECK(strcmp("ArmThumbOverlapArm()", symbol) == 0,
+                 "TestArmThumbOverlap failed");
+  std::cout << "TestArmThumbOverlap passed" << std::endl;
 #endif
 }
 
+#endif  // defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target)
+
 #elif defined(_WIN32)
 #if !defined(ABSL_CONSUME_DLL)
 
@@ -519,7 +563,6 @@
 
 #endif  // !defined(ABSL_CONSUME_DLL)
 #else  // Symbolizer unimplemented
-
 TEST(Symbolize, Unimplemented) {
   char buf[64];
   EXPECT_FALSE(absl::Symbolize((void *)(&nonstatic_func), buf, sizeof(buf)));
@@ -551,6 +594,9 @@
   TestWithPCInsideInlineFunction();
   TestWithPCInsideNonInlineFunction();
   TestWithReturnAddress();
+#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target)
+  TestArmThumbOverlap();
+#endif
 #endif
 
   return RUN_ALL_TESTS();
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel
index 147249e..d20deab 100644
--- a/absl/flags/BUILD.bazel
+++ b/absl/flags/BUILD.bazel
@@ -14,7 +14,6 @@
 # limitations under the License.
 #
 
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
@@ -217,6 +216,7 @@
     name = "flag",
     srcs = [
         "flag.cc",
+        "internal/flag_msvc.inc",
     ],
     hdrs = [
         "declare.h",
@@ -259,6 +259,7 @@
         ":reflection",
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/container:flat_hash_map",
         "//absl/strings",
     ],
 )
diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt
index caac69c..7f3298e 100644
--- a/absl/flags/CMakeLists.txt
+++ b/absl/flags/CMakeLists.txt
@@ -202,6 +202,7 @@
   HDRS
     "declare.h"
     "flag.h"
+    "internal/flag_msvc.inc"
   COPTS
     ${ABSL_DEFAULT_COPTS}
   LINKOPTS
@@ -239,6 +240,7 @@
     absl::flags_private_handle_accessor
     absl::flags_program_name
     absl::flags_reflection
+    absl::flat_hash_map
     absl::strings
     absl::synchronization
 )
@@ -309,7 +311,7 @@
     absl::flags_reflection
     absl::memory
     absl::strings
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -321,7 +323,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::flags_config
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -341,7 +343,7 @@
     absl::flags_reflection
     absl::strings
     absl::time
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -353,7 +355,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::flags_marshalling
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -372,7 +374,7 @@
     absl::scoped_set_env
     absl::span
     absl::strings
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -384,7 +386,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::flags_path_util
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -397,7 +399,7 @@
   DEPS
     absl::flags_program_name
     absl::strings
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -414,7 +416,7 @@
     absl::flags_usage
     absl::memory
     absl::strings
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -428,7 +430,7 @@
     absl::base
     absl::flags_internal
     absl::time
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -443,7 +445,7 @@
     absl::flags_path_util
     absl::flags_program_name
     absl::strings
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -462,5 +464,5 @@
     absl::flags_reflection
     absl::flags_usage
     absl::strings
-    gtest
+    GTest::gtest
 )
diff --git a/absl/flags/flag.h b/absl/flags/flag.h
index f09580b..a724ccc 100644
--- a/absl/flags/flag.h
+++ b/absl/flags/flag.h
@@ -71,101 +71,7 @@
 template <typename T>
 using Flag = flags_internal::Flag<T>;
 #else
-// MSVC debug builds do not implement initialization with constexpr constructors
-// correctly. To work around this we add a level of indirection, so that the
-// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias
-// to that class) and dynamically allocates an instance when necessary. We also
-// forward all calls to internal::Flag methods via trampoline methods. In this
-// setup the `absl::Flag` class does not have constructor and virtual methods,
-// all the data members are public and thus MSVC is able to initialize it at
-// link time. To deal with multiple threads accessing the flag for the first
-// time concurrently we use an atomic boolean indicating if flag object is
-// initialized. We also employ the double-checked locking pattern where the
-// second level of protection is a global Mutex, so if two threads attempt to
-// construct the flag concurrently only one wins.
-// This solution is based on a recomendation here:
-// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454
-
-namespace flags_internal {
-absl::Mutex* GetGlobalConstructionGuard();
-}  // namespace flags_internal
-
-template <typename T>
-class Flag {
- public:
-  // No constructor and destructor to ensure this is an aggregate type.
-  // Visual Studio 2015 still requires the constructor for class to be
-  // constexpr initializable.
-#if _MSC_VER <= 1900
-  constexpr Flag(const char* name, const char* filename,
-                 const flags_internal::HelpGenFunc help_gen,
-                 const flags_internal::FlagDfltGenFunc default_value_gen)
-      : name_(name),
-        filename_(filename),
-        help_gen_(help_gen),
-        default_value_gen_(default_value_gen),
-        inited_(false),
-        impl_(nullptr) {}
-#endif
-
-  flags_internal::Flag<T>& GetImpl() const {
-    if (!inited_.load(std::memory_order_acquire)) {
-      absl::MutexLock l(flags_internal::GetGlobalConstructionGuard());
-
-      if (inited_.load(std::memory_order_acquire)) {
-        return *impl_;
-      }
-
-      impl_ = new flags_internal::Flag<T>(
-          name_, filename_,
-          {flags_internal::FlagHelpMsg(help_gen_),
-           flags_internal::FlagHelpKind::kGenFunc},
-          {flags_internal::FlagDefaultSrc(default_value_gen_),
-           flags_internal::FlagDefaultKind::kGenFunc});
-      inited_.store(true, std::memory_order_release);
-    }
-
-    return *impl_;
-  }
-
-  // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API.
-  // See https://abseil.io/docs/cpp/guides/flags
-  bool IsRetired() const { return GetImpl().IsRetired(); }
-  absl::string_view Name() const { return GetImpl().Name(); }
-  std::string Help() const { return GetImpl().Help(); }
-  bool IsModified() const { return GetImpl().IsModified(); }
-  bool IsSpecifiedOnCommandLine() const {
-    return GetImpl().IsSpecifiedOnCommandLine();
-  }
-  std::string Filename() const { return GetImpl().Filename(); }
-  std::string DefaultValue() const { return GetImpl().DefaultValue(); }
-  std::string CurrentValue() const { return GetImpl().CurrentValue(); }
-  template <typename U>
-  inline bool IsOfType() const {
-    return GetImpl().template IsOfType<U>();
-  }
-  T Get() const {
-    return flags_internal::FlagImplPeer::InvokeGet<T>(GetImpl());
-  }
-  void Set(const T& v) {
-    flags_internal::FlagImplPeer::InvokeSet(GetImpl(), v);
-  }
-  void InvokeCallback() { GetImpl().InvokeCallback(); }
-
-  const CommandLineFlag& Reflect() const {
-    return flags_internal::FlagImplPeer::InvokeReflect(GetImpl());
-  }
-
-  // The data members are logically private, but they need to be public for
-  // this to be an aggregate type.
-  const char* name_;
-  const char* filename_;
-  const flags_internal::HelpGenFunc help_gen_;
-  const flags_internal::FlagDfltGenFunc default_value_gen_;
-
-  mutable std::atomic<bool> inited_;
-  mutable flags_internal::Flag<T>* impl_;
-};
+#include "absl/flags/internal/flag_msvc.inc"
 #endif
 
 // GetFlag()
@@ -265,6 +171,8 @@
 //
 //   ABSL_FLAG(T, name, default_value, help).OnUpdate(callback);
 //
+// `callback` should be convertible to `void (*)()`.
+//
 // After any setting of the flag value, the callback will be called at least
 // once. A rapid sequence of changes may be merged together into the same
 // callback. No concurrent calls to the callback will be made for the same
@@ -279,7 +187,6 @@
 // Note: ABSL_FLAG.OnUpdate() does not have a public definition. Hence, this
 // comment serves as its API documentation.
 
-
 // -----------------------------------------------------------------------------
 // Implementation details below this section
 // -----------------------------------------------------------------------------
@@ -334,8 +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 v = ABSL_FLAG_IMPL_FLAGHELP(txt)) {                \
-      return v;                                                              \
+        absl::string_view absl_flag_help = ABSL_FLAG_IMPL_FLAGHELP(txt)) {   \
+      return absl_flag_help;                                                 \
     }                                                                        \
     static std::string NonConst() { return std::string(Value()); }           \
   };                                                                         \
@@ -347,8 +254,8 @@
 #define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value)     \
   struct AbslFlagDefaultGenFor##name {                                        \
     Type value = absl::flags_internal::InitDefaultValue<Type>(default_value); \
-    static void Gen(void* p) {                                                \
-      new (p) Type(AbslFlagDefaultGenFor##name{}.value);                      \
+    static void Gen(void* absl_flag_default_loc) {                            \
+      new (absl_flag_default_loc) Type(AbslFlagDefaultGenFor##name{}.value);  \
     }                                                                         \
   };
 
diff --git a/absl/flags/flag_benchmark.cc b/absl/flags/flag_benchmark.cc
index 57584f8..fc572d9 100644
--- a/absl/flags/flag_benchmark.cc
+++ b/absl/flags/flag_benchmark.cc
@@ -101,7 +101,39 @@
   A(AbslDuration)            \
   A(UDT)
 
-#define FLAG_DEF(T) ABSL_FLAG(T, T##_flag, {}, "");
+#define REPLICATE_0(A, T, name, index) A(T, name, index)
+#define REPLICATE_1(A, T, name, index) \
+  REPLICATE_0(A, T, name, index##0) REPLICATE_0(A, T, name, index##1)
+#define REPLICATE_2(A, T, name, index) \
+  REPLICATE_1(A, T, name, index##0) REPLICATE_1(A, T, name, index##1)
+#define REPLICATE_3(A, T, name, index) \
+  REPLICATE_2(A, T, name, index##0) REPLICATE_2(A, T, name, index##1)
+#define REPLICATE_4(A, T, name, index) \
+  REPLICATE_3(A, T, name, index##0) REPLICATE_3(A, T, name, index##1)
+#define REPLICATE_5(A, T, name, index) \
+  REPLICATE_4(A, T, name, index##0) REPLICATE_4(A, T, name, index##1)
+#define REPLICATE_6(A, T, name, index) \
+  REPLICATE_5(A, T, name, index##0) REPLICATE_5(A, T, name, index##1)
+#define REPLICATE_7(A, T, name, index) \
+  REPLICATE_6(A, T, name, index##0) REPLICATE_6(A, T, name, index##1)
+#define REPLICATE_8(A, T, name, index) \
+  REPLICATE_7(A, T, name, index##0) REPLICATE_7(A, T, name, index##1)
+#define REPLICATE_9(A, T, name, index) \
+  REPLICATE_8(A, T, name, index##0) REPLICATE_8(A, T, name, index##1)
+#if defined(_MSC_VER)
+#define REPLICATE(A, T, name) \
+  REPLICATE_7(A, T, name, 0) REPLICATE_7(A, T, name, 1)
+#define SINGLE_FLAG(T) FLAGS_##T##_flag_00000000
+#else
+#define REPLICATE(A, T, name) \
+  REPLICATE_9(A, T, name, 0) REPLICATE_9(A, T, name, 1)
+#define SINGLE_FLAG(T) FLAGS_##T##_flag_0000000000
+#endif
+#define REPLICATE_ALL(A, T, name) \
+  REPLICATE_9(A, T, name, 0) REPLICATE_9(A, T, name, 1)
+
+#define COUNT(T, name, index) +1
+constexpr size_t kNumFlags = 0 REPLICATE(COUNT, _, _);
 
 #if defined(__clang__) && defined(__linux__)
 // Force the flags used for benchmarks into a separate ELF section.
@@ -110,38 +142,87 @@
 // benchmark results more reproducible across unrelated code changes.
 #pragma clang section data = ".benchmark_flags"
 #endif
+#define DEFINE_FLAG(T, name, index) ABSL_FLAG(T, name##_##index, {}, "");
+#define FLAG_DEF(T) REPLICATE(DEFINE_FLAG, T, T##_flag);
 BENCHMARKED_TYPES(FLAG_DEF)
 #if defined(__clang__) && defined(__linux__)
 #pragma clang section data = ""
 #endif
 // Register thousands of flags to bloat up the size of the registry.
 // This mimics real life production binaries.
-#define DEFINE_FLAG_0(name) ABSL_FLAG(int, name, 0, "");
-#define DEFINE_FLAG_1(name) DEFINE_FLAG_0(name##0) DEFINE_FLAG_0(name##1)
-#define DEFINE_FLAG_2(name) DEFINE_FLAG_1(name##0) DEFINE_FLAG_1(name##1)
-#define DEFINE_FLAG_3(name) DEFINE_FLAG_2(name##0) DEFINE_FLAG_2(name##1)
-#define DEFINE_FLAG_4(name) DEFINE_FLAG_3(name##0) DEFINE_FLAG_3(name##1)
-#define DEFINE_FLAG_5(name) DEFINE_FLAG_4(name##0) DEFINE_FLAG_4(name##1)
-#define DEFINE_FLAG_6(name) DEFINE_FLAG_5(name##0) DEFINE_FLAG_5(name##1)
-#define DEFINE_FLAG_7(name) DEFINE_FLAG_6(name##0) DEFINE_FLAG_6(name##1)
-#define DEFINE_FLAG_8(name) DEFINE_FLAG_7(name##0) DEFINE_FLAG_7(name##1)
-#define DEFINE_FLAG_9(name) DEFINE_FLAG_8(name##0) DEFINE_FLAG_8(name##1)
-#define DEFINE_FLAG_10(name) DEFINE_FLAG_9(name##0) DEFINE_FLAG_9(name##1)
-#define DEFINE_FLAG_11(name) DEFINE_FLAG_10(name##0) DEFINE_FLAG_10(name##1)
-#define DEFINE_FLAG_12(name) DEFINE_FLAG_11(name##0) DEFINE_FLAG_11(name##1)
-DEFINE_FLAG_12(bloat_flag_);
+#define BLOAT_FLAG(_unused1, _unused2, index) \
+  ABSL_FLAG(int, bloat_flag_##index, 0, "");
+REPLICATE_ALL(BLOAT_FLAG, _, _)
 
 namespace {
 
-#define BM_GetFlag(T)                                            \
-  void BM_GetFlag_##T(benchmark::State& state) {                 \
-    for (auto _ : state) {                                       \
-      benchmark::DoNotOptimize(absl::GetFlag(FLAGS_##T##_flag)); \
-    }                                                            \
-  }                                                              \
-  BENCHMARK(BM_GetFlag_##T)->ThreadRange(1, 16);
+#define FLAG_PTR(T, name, index) &FLAGS_##name##_##index,
+#define FLAG_PTR_ARR(T)                              \
+  static constexpr absl::Flag<T>* FlagPtrs_##T[] = { \
+      REPLICATE(FLAG_PTR, T, T##_flag)};
+BENCHMARKED_TYPES(FLAG_PTR_ARR)
 
-BENCHMARKED_TYPES(BM_GetFlag)
+#define BM_SingleGetFlag(T)                                    \
+  void BM_SingleGetFlag_##T(benchmark::State& state) {         \
+    for (auto _ : state) {                                     \
+      benchmark::DoNotOptimize(absl::GetFlag(SINGLE_FLAG(T))); \
+    }                                                          \
+  }                                                            \
+  BENCHMARK(BM_SingleGetFlag_##T)->ThreadRange(1, 16);
+
+BENCHMARKED_TYPES(BM_SingleGetFlag)
+
+template <typename T>
+struct Accumulator {
+  using type = T;
+};
+template <>
+struct Accumulator<String> {
+  using type = size_t;
+};
+template <>
+struct Accumulator<VectorOfStrings> {
+  using type = size_t;
+};
+template <>
+struct Accumulator<OptionalInt> {
+  using type = bool;
+};
+template <>
+struct Accumulator<OptionalString> {
+  using type = bool;
+};
+template <>
+struct Accumulator<UDT> {
+  using type = bool;
+};
+
+template <typename T>
+void Accumulate(typename Accumulator<T>::type& a, const T& f) {
+  a += f;
+}
+void Accumulate(bool& a, bool f) { a = a || f; }
+void Accumulate(size_t& a, const std::string& f) { a += f.size(); }
+void Accumulate(size_t& a, const std::vector<std::string>& f) { a += f.size(); }
+void Accumulate(bool& a, const OptionalInt& f) { a |= f.has_value(); }
+void Accumulate(bool& a, const OptionalString& f) { a |= f.has_value(); }
+void Accumulate(bool& a, const UDT& f) {
+  a |= reinterpret_cast<int64_t>(&f) & 0x1;
+}
+
+#define BM_ManyGetFlag(T)                            \
+  void BM_ManyGetFlag_##T(benchmark::State& state) { \
+    Accumulator<T>::type res = {};                   \
+    while (state.KeepRunningBatch(kNumFlags)) {      \
+      for (auto* flag_ptr : FlagPtrs_##T) {          \
+        Accumulate(res, absl::GetFlag(*flag_ptr));   \
+      }                                              \
+    }                                                \
+    benchmark::DoNotOptimize(res);                   \
+  }                                                  \
+  BENCHMARK(BM_ManyGetFlag_##T)->ThreadRange(1, 8);
+
+BENCHMARKED_TYPES(BM_ManyGetFlag)
 
 void BM_ThreadedFindCommandLineFlag(benchmark::State& state) {
   char dummy[] = "dummy";
@@ -150,17 +231,18 @@
   // is finalized.
   absl::ParseCommandLine(1, argv);
 
-  for (auto s : state) {
-    benchmark::DoNotOptimize(
-        absl::FindCommandLineFlag("bloat_flag_010101010101"));
+  while (state.KeepRunningBatch(kNumFlags)) {
+    for (auto* flag_ptr : FlagPtrs_bool) {
+      benchmark::DoNotOptimize(absl::FindCommandLineFlag(flag_ptr->Name()));
+    }
   }
 }
 BENCHMARK(BM_ThreadedFindCommandLineFlag)->ThreadRange(1, 16);
 
 }  // namespace
 
-#define InvokeGetFlag(T)                                               \
-  T AbslInvokeGetFlag##T() { return absl::GetFlag(FLAGS_##T##_flag); } \
+#define InvokeGetFlag(T)                                             \
+  T AbslInvokeGetFlag##T() { return absl::GetFlag(SINGLE_FLAG(T)); } \
   int odr##T = (benchmark::DoNotOptimize(AbslInvokeGetFlag##T), 1);
 
 BENCHMARKED_TYPES(InvokeGetFlag)
diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc
index 6912b54..6e974a5 100644
--- a/absl/flags/flag_test.cc
+++ b/absl/flags/flag_test.cc
@@ -61,6 +61,7 @@
 struct UDT {
   UDT() = default;
   UDT(const UDT&) = default;
+  UDT& operator=(const UDT&) = default;
 };
 bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; }
 std::string AbslUnparseFlag(const UDT&) { return ""; }
@@ -102,9 +103,9 @@
 
 TEST_F(FlagTest, Traits) {
   EXPECT_EQ(flags::StorageKind<int>(),
-            flags::FlagValueStorageKind::kOneWordAtomic);
+            flags::FlagValueStorageKind::kValueAndInitBit);
   EXPECT_EQ(flags::StorageKind<bool>(),
-            flags::FlagValueStorageKind::kOneWordAtomic);
+            flags::FlagValueStorageKind::kValueAndInitBit);
   EXPECT_EQ(flags::StorageKind<double>(),
             flags::FlagValueStorageKind::kOneWordAtomic);
   EXPECT_EQ(flags::StorageKind<int64_t>(),
@@ -723,6 +724,8 @@
 namespace {
 
 TEST_F(FlagTest, TestCustomUDT) {
+  EXPECT_EQ(flags::StorageKind<CustomUDT>(),
+            flags::FlagValueStorageKind::kOneWordAtomic);
   EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(1, 1));
   absl::SetFlag(&FLAGS_test_flag_custom_udt, CustomUDT(2, 3));
   EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(2, 3));
@@ -943,3 +946,34 @@
 }
 
 }  // namespace
+
+// --------------------------------------------------------------------
+
+namespace {
+
+enum TestE { A = 1, B = 2, C = 3 };
+
+struct EnumWrapper {
+  EnumWrapper() : e(A) {}
+
+  TestE e;
+};
+
+bool AbslParseFlag(absl::string_view, EnumWrapper*, std::string*) {
+  return true;
+}
+std::string AbslUnparseFlag(const EnumWrapper&) { return ""; }
+
+}  // namespace
+
+ABSL_FLAG(EnumWrapper, test_enum_wrapper_flag, {}, "help");
+
+TEST_F(FlagTest, TesTypeWrappingEnum) {
+  EnumWrapper value = absl::GetFlag(FLAGS_test_enum_wrapper_flag);
+  EXPECT_EQ(value.e, A);
+
+  value.e = B;
+  absl::SetFlag(&FLAGS_test_enum_wrapper_flag, value);
+  value = absl::GetFlag(FLAGS_test_enum_wrapper_flag);
+  EXPECT_EQ(value.e, B);
+}
diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc
index f83c1fe..1515022 100644
--- a/absl/flags/internal/flag.cc
+++ b/absl/flags/internal/flag.cc
@@ -145,12 +145,7 @@
   auto def_kind = static_cast<FlagDefaultKind>(def_kind_);
 
   switch (ValueStorageKind()) {
-    case FlagValueStorageKind::kAlignedBuffer:
-      // For this storage kind the default_value_ always points to gen_func
-      // during initialization.
-      assert(def_kind == FlagDefaultKind::kGenFunc);
-      (*default_value_.gen_func)(AlignedBufferValue());
-      break;
+    case FlagValueStorageKind::kValueAndInitBit:
     case FlagValueStorageKind::kOneWordAtomic: {
       alignas(int64_t) std::array<char, sizeof(int64_t)> buf{};
       if (def_kind == FlagDefaultKind::kGenFunc) {
@@ -159,6 +154,12 @@
         assert(def_kind != FlagDefaultKind::kDynamicValue);
         std::memcpy(buf.data(), &default_value_, Sizeof(op_));
       }
+      if (ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit) {
+        // We presume here the memory layout of FlagValueAndInitBit struct.
+        uint8_t initialized = 1;
+        std::memcpy(buf.data() + Sizeof(op_), &initialized,
+                    sizeof(initialized));
+      }
       OneWordValue().store(absl::bit_cast<int64_t>(buf),
                            std::memory_order_release);
       break;
@@ -170,6 +171,12 @@
       (*default_value_.gen_func)(AtomicBufferValue());
       break;
     }
+    case FlagValueStorageKind::kAlignedBuffer:
+      // For this storage kind the default_value_ always points to gen_func
+      // during initialization.
+      assert(def_kind == FlagDefaultKind::kGenFunc);
+      (*default_value_.gen_func)(AlignedBufferValue());
+      break;
   }
   seq_lock_.MarkInitialized();
 }
@@ -226,12 +233,10 @@
 
 void FlagImpl::StoreValue(const void* src) {
   switch (ValueStorageKind()) {
-    case FlagValueStorageKind::kAlignedBuffer:
-      Copy(op_, src, AlignedBufferValue());
-      seq_lock_.IncrementModificationCount();
-      break;
+    case FlagValueStorageKind::kValueAndInitBit:
     case FlagValueStorageKind::kOneWordAtomic: {
-      int64_t one_word_val = 0;
+      // Load the current value to avoid setting 'init' bit manualy.
+      int64_t one_word_val = OneWordValue().load(std::memory_order_acquire);
       std::memcpy(&one_word_val, src, Sizeof(op_));
       OneWordValue().store(one_word_val, std::memory_order_release);
       seq_lock_.IncrementModificationCount();
@@ -241,6 +246,10 @@
       seq_lock_.Write(AtomicBufferValue(), src, Sizeof(op_));
       break;
     }
+    case FlagValueStorageKind::kAlignedBuffer:
+      Copy(op_, src, AlignedBufferValue());
+      seq_lock_.IncrementModificationCount();
+      break;
   }
   modified_ = true;
   InvokeCallback();
@@ -280,10 +289,7 @@
 std::string FlagImpl::CurrentValue() const {
   auto* guard = DataGuard();  // Make sure flag initialized
   switch (ValueStorageKind()) {
-    case FlagValueStorageKind::kAlignedBuffer: {
-      absl::MutexLock l(guard);
-      return flags_internal::Unparse(op_, AlignedBufferValue());
-    }
+    case FlagValueStorageKind::kValueAndInitBit:
     case FlagValueStorageKind::kOneWordAtomic: {
       const auto one_word_val =
           absl::bit_cast<std::array<char, sizeof(int64_t)>>(
@@ -296,6 +302,10 @@
       ReadSequenceLockedData(cloned.get());
       return flags_internal::Unparse(op_, cloned.get());
     }
+    case FlagValueStorageKind::kAlignedBuffer: {
+      absl::MutexLock l(guard);
+      return flags_internal::Unparse(op_, AlignedBufferValue());
+    }
   }
 
   return "";
@@ -341,11 +351,7 @@
   bool modified = modified_;
   bool on_command_line = on_command_line_;
   switch (ValueStorageKind()) {
-    case FlagValueStorageKind::kAlignedBuffer: {
-      return absl::make_unique<FlagState>(
-          *this, flags_internal::Clone(op_, AlignedBufferValue()), modified,
-          on_command_line, ModificationCount());
-    }
+    case FlagValueStorageKind::kValueAndInitBit:
     case FlagValueStorageKind::kOneWordAtomic: {
       return absl::make_unique<FlagState>(
           *this, OneWordValue().load(std::memory_order_acquire), modified,
@@ -361,6 +367,11 @@
       return absl::make_unique<FlagState>(*this, cloned, modified,
                                           on_command_line, ModificationCount());
     }
+    case FlagValueStorageKind::kAlignedBuffer: {
+      return absl::make_unique<FlagState>(
+          *this, flags_internal::Clone(op_, AlignedBufferValue()), modified,
+          on_command_line, ModificationCount());
+    }
   }
   return nullptr;
 }
@@ -372,13 +383,14 @@
   }
 
   switch (ValueStorageKind()) {
-    case FlagValueStorageKind::kAlignedBuffer:
-    case FlagValueStorageKind::kSequenceLocked:
-      StoreValue(flag_state.value_.heap_allocated);
-      break;
+    case FlagValueStorageKind::kValueAndInitBit:
     case FlagValueStorageKind::kOneWordAtomic:
       StoreValue(&flag_state.value_.one_word);
       break;
+    case FlagValueStorageKind::kSequenceLocked:
+    case FlagValueStorageKind::kAlignedBuffer:
+      StoreValue(flag_state.value_.heap_allocated);
+      break;
   }
 
   modified_ = flag_state.modified_;
@@ -407,7 +419,8 @@
 }
 
 std::atomic<int64_t>& FlagImpl::OneWordValue() const {
-  assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic);
+  assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic ||
+         ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit);
   return OffsetValue<FlagOneWordValue>()->value;
 }
 
@@ -433,11 +446,7 @@
 void FlagImpl::Read(void* dst) const {
   auto* guard = DataGuard();  // Make sure flag initialized
   switch (ValueStorageKind()) {
-    case FlagValueStorageKind::kAlignedBuffer: {
-      absl::MutexLock l(guard);
-      flags_internal::CopyConstruct(op_, AlignedBufferValue(), dst);
-      break;
-    }
+    case FlagValueStorageKind::kValueAndInitBit:
     case FlagValueStorageKind::kOneWordAtomic: {
       const int64_t one_word_val =
           OneWordValue().load(std::memory_order_acquire);
@@ -448,9 +457,31 @@
       ReadSequenceLockedData(dst);
       break;
     }
+    case FlagValueStorageKind::kAlignedBuffer: {
+      absl::MutexLock l(guard);
+      flags_internal::CopyConstruct(op_, AlignedBufferValue(), dst);
+      break;
+    }
   }
 }
 
+int64_t FlagImpl::ReadOneWord() const {
+  assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic ||
+         ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit);
+  auto* guard = DataGuard();  // Make sure flag initialized
+  (void)guard;
+  return OneWordValue().load(std::memory_order_acquire);
+}
+
+bool FlagImpl::ReadOneBool() const {
+  assert(ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit);
+  auto* guard = DataGuard();  // Make sure flag initialized
+  (void)guard;
+  return absl::bit_cast<FlagValueAndInitBit<bool>>(
+             OneWordValue().load(std::memory_order_acquire))
+      .value;
+}
+
 void FlagImpl::ReadSequenceLockedData(void* dst) const {
   int size = Sizeof(op_);
   // Attempt to read using the sequence lock.
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index e6bade0..124a2f1 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -29,6 +29,7 @@
 
 #include "absl/base/attributes.h"
 #include "absl/base/call_once.h"
+#include "absl/base/casts.h"
 #include "absl/base/config.h"
 #include "absl/base/optimization.h"
 #include "absl/base/thread_annotations.h"
@@ -289,7 +290,7 @@
 
 template <typename ValueT, typename GenT,
           typename std::enable_if<std::is_integral<ValueT>::value, int>::type =
-              (GenT{}, 0)>
+              ((void)GenT{}, 0)>
 constexpr FlagDefaultArg DefaultArg(int) {
   return {FlagDefaultSrc(GenT{}.value), FlagDefaultKind::kOneWord};
 }
@@ -305,48 +306,71 @@
 constexpr int64_t UninitializedFlagValue() { return 0xababababababababll; }
 
 template <typename T>
+using FlagUseValueAndInitBitStorage = std::integral_constant<
+    bool, absl::type_traits_internal::is_trivially_copyable<T>::value &&
+              std::is_default_constructible<T>::value && (sizeof(T) < 8)>;
+
+template <typename T>
 using FlagUseOneWordStorage = std::integral_constant<
     bool, absl::type_traits_internal::is_trivially_copyable<T>::value &&
               (sizeof(T) <= 8)>;
 
 template <class T>
-using FlagShouldUseSequenceLock = std::integral_constant<
+using FlagUseSequenceLockStorage = std::integral_constant<
     bool, absl::type_traits_internal::is_trivially_copyable<T>::value &&
               (sizeof(T) > 8)>;
 
 enum class FlagValueStorageKind : uint8_t {
-  kAlignedBuffer = 0,
+  kValueAndInitBit = 0,
   kOneWordAtomic = 1,
   kSequenceLocked = 2,
+  kAlignedBuffer = 3,
 };
 
 template <typename T>
 static constexpr FlagValueStorageKind StorageKind() {
-  return FlagUseOneWordStorage<T>::value ? FlagValueStorageKind::kOneWordAtomic
-         : FlagShouldUseSequenceLock<T>::value
+  return FlagUseValueAndInitBitStorage<T>::value
+             ? FlagValueStorageKind::kValueAndInitBit
+         : FlagUseOneWordStorage<T>::value
+             ? FlagValueStorageKind::kOneWordAtomic
+         : FlagUseSequenceLockStorage<T>::value
              ? FlagValueStorageKind::kSequenceLocked
              : FlagValueStorageKind::kAlignedBuffer;
 }
 
 struct FlagOneWordValue {
-  constexpr FlagOneWordValue() : value(UninitializedFlagValue()) {}
-
+  constexpr explicit FlagOneWordValue(int64_t v) : value(v) {}
   std::atomic<int64_t> value;
 };
 
+template <typename T>
+struct alignas(8) FlagValueAndInitBit {
+  T value;
+  // Use an int instead of a bool to guarantee that a non-zero value has
+  // a bit set.
+  uint8_t init;
+};
+
 template <typename T,
           FlagValueStorageKind Kind = flags_internal::StorageKind<T>()>
 struct FlagValue;
 
 template <typename T>
-struct FlagValue<T, FlagValueStorageKind::kAlignedBuffer> {
-  bool Get(const SequenceLock&, T&) const { return false; }
-
-  alignas(T) char value[sizeof(T)];
+struct FlagValue<T, FlagValueStorageKind::kValueAndInitBit> : FlagOneWordValue {
+  constexpr FlagValue() : FlagOneWordValue(0) {}
+  bool Get(const SequenceLock&, T& dst) const {
+    int64_t storage = value.load(std::memory_order_acquire);
+    if (ABSL_PREDICT_FALSE(storage == 0)) {
+      return false;
+    }
+    dst = absl::bit_cast<FlagValueAndInitBit<T>>(storage).value;
+    return true;
+  }
 };
 
 template <typename T>
 struct FlagValue<T, FlagValueStorageKind::kOneWordAtomic> : FlagOneWordValue {
+  constexpr FlagValue() : FlagOneWordValue(UninitializedFlagValue()) {}
   bool Get(const SequenceLock&, T& dst) const {
     int64_t one_word_val = value.load(std::memory_order_acquire);
     if (ABSL_PREDICT_FALSE(one_word_val == UninitializedFlagValue())) {
@@ -370,6 +394,13 @@
       std::atomic<uint64_t>) std::atomic<uint64_t> value_words[kNumWords];
 };
 
+template <typename T>
+struct FlagValue<T, FlagValueStorageKind::kAlignedBuffer> {
+  bool Get(const SequenceLock&, T&) const { return false; }
+
+  alignas(T) char value[sizeof(T)];
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 // Flag callback auxiliary structs.
 
@@ -415,7 +446,27 @@
         data_guard_{} {}
 
   // Constant access methods
+  int64_t ReadOneWord() const ABSL_LOCKS_EXCLUDED(*DataGuard());
+  bool ReadOneBool() const ABSL_LOCKS_EXCLUDED(*DataGuard());
   void Read(void* dst) const override ABSL_LOCKS_EXCLUDED(*DataGuard());
+  void Read(bool* value) const ABSL_LOCKS_EXCLUDED(*DataGuard()) {
+    *value = ReadOneBool();
+  }
+  template <typename T,
+            absl::enable_if_t<flags_internal::StorageKind<T>() ==
+                                  FlagValueStorageKind::kOneWordAtomic,
+                              int> = 0>
+  void Read(T* value) const ABSL_LOCKS_EXCLUDED(*DataGuard()) {
+    int64_t v = ReadOneWord();
+    std::memcpy(value, static_cast<const void*>(&v), sizeof(T));
+  }
+  template <typename T,
+            typename std::enable_if<flags_internal::StorageKind<T>() ==
+                                        FlagValueStorageKind::kValueAndInitBit,
+                                    int>::type = 0>
+  void Read(T* value) const ABSL_LOCKS_EXCLUDED(*DataGuard()) {
+    *value = absl::bit_cast<FlagValueAndInitBit<T>>(ReadOneWord()).value;
+  }
 
   // Mutating access methods
   void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard());
diff --git a/absl/flags/internal/flag_msvc.inc b/absl/flags/internal/flag_msvc.inc
new file mode 100644
index 0000000..c31bd27
--- /dev/null
+++ b/absl/flags/internal/flag_msvc.inc
@@ -0,0 +1,116 @@
+//
+//  Copyright 2021 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.
+
+// Do not include this file directly.
+// Include absl/flags/flag.h instead.
+
+// MSVC debug builds do not implement initialization with constexpr constructors
+// correctly. To work around this we add a level of indirection, so that the
+// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias
+// to that class) and dynamically allocates an instance when necessary. We also
+// forward all calls to internal::Flag methods via trampoline methods. In this
+// setup the `absl::Flag` class does not have constructor and virtual methods,
+// all the data members are public and thus MSVC is able to initialize it at
+// link time. To deal with multiple threads accessing the flag for the first
+// time concurrently we use an atomic boolean indicating if flag object is
+// initialized. We also employ the double-checked locking pattern where the
+// second level of protection is a global Mutex, so if two threads attempt to
+// construct the flag concurrently only one wins.
+//
+// This solution is based on a recomendation here:
+// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454
+
+namespace flags_internal {
+absl::Mutex* GetGlobalConstructionGuard();
+}  // namespace flags_internal
+
+// Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API.
+// See https://abseil.io/docs/cpp/guides/flags
+template <typename T>
+class Flag {
+ public:
+  // No constructor and destructor to ensure this is an aggregate type.
+  // Visual Studio 2015 still requires the constructor for class to be
+  // constexpr initializable.
+#if _MSC_VER <= 1900
+  constexpr Flag(const char* name, const char* filename,
+                 const flags_internal::HelpGenFunc help_gen,
+                 const flags_internal::FlagDfltGenFunc default_value_gen)
+      : name_(name),
+        filename_(filename),
+        help_gen_(help_gen),
+        default_value_gen_(default_value_gen),
+        inited_(false),
+        impl_(nullptr) {}
+#endif
+
+  flags_internal::Flag<T>& GetImpl() const {
+    if (!inited_.load(std::memory_order_acquire)) {
+      absl::MutexLock l(flags_internal::GetGlobalConstructionGuard());
+
+      if (inited_.load(std::memory_order_acquire)) {
+        return *impl_;
+      }
+
+      impl_ = new flags_internal::Flag<T>(
+          name_, filename_,
+          {flags_internal::FlagHelpMsg(help_gen_),
+           flags_internal::FlagHelpKind::kGenFunc},
+          {flags_internal::FlagDefaultSrc(default_value_gen_),
+           flags_internal::FlagDefaultKind::kGenFunc});
+      inited_.store(true, std::memory_order_release);
+    }
+
+    return *impl_;
+  }
+
+  // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API.
+  // See https://abseil.io/docs/cpp/guides/flags
+  bool IsRetired() const { return GetImpl().IsRetired(); }
+  absl::string_view Name() const { return GetImpl().Name(); }
+  std::string Help() const { return GetImpl().Help(); }
+  bool IsModified() const { return GetImpl().IsModified(); }
+  bool IsSpecifiedOnCommandLine() const {
+    return GetImpl().IsSpecifiedOnCommandLine();
+  }
+  std::string Filename() const { return GetImpl().Filename(); }
+  std::string DefaultValue() const { return GetImpl().DefaultValue(); }
+  std::string CurrentValue() const { return GetImpl().CurrentValue(); }
+  template <typename U>
+  inline bool IsOfType() const {
+    return GetImpl().template IsOfType<U>();
+  }
+  T Get() const {
+    return flags_internal::FlagImplPeer::InvokeGet<T>(GetImpl());
+  }
+  void Set(const T& v) {
+    flags_internal::FlagImplPeer::InvokeSet(GetImpl(), v);
+  }
+  void InvokeCallback() { GetImpl().InvokeCallback(); }
+
+  const CommandLineFlag& Reflect() const {
+    return flags_internal::FlagImplPeer::InvokeReflect(GetImpl());
+  }
+
+  // The data members are logically private, but they need to be public for
+  // this to be an aggregate type.
+  const char* name_;
+  const char* filename_;
+  const flags_internal::HelpGenFunc help_gen_;
+  const flags_internal::FlagDfltGenFunc default_value_gen_;
+
+  mutable std::atomic<bool> inited_;
+  mutable flags_internal::Flag<T>* impl_;
+};
diff --git a/absl/flags/internal/sequence_lock.h b/absl/flags/internal/sequence_lock.h
index 807b2a7..36318ab 100644
--- a/absl/flags/internal/sequence_lock.h
+++ b/absl/flags/internal/sequence_lock.h
@@ -49,7 +49,7 @@
 // The memory reads and writes protected by this lock must use the provided
 // `TryRead()` and `Write()` functions. These functions behave similarly to
 // `memcpy()`, with one oddity: the protected data must be an array of
-// `std::atomic<int64>`. This is to comply with the C++ standard, which
+// `std::atomic<uint64>`. This is to comply with the C++ standard, which
 // considers data races on non-atomic objects to be undefined behavior. See "Can
 // Seqlocks Get Along With Programming Language Memory Models?"[1] by Hans J.
 // Boehm for more details.
diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc
index a588c7f..949709e 100644
--- a/absl/flags/internal/usage.cc
+++ b/absl/flags/internal/usage.cc
@@ -245,7 +245,7 @@
         << XMLElement("usage", program_usage_message) << '\n';
   }
 
-  // Map of package name to
+  // Ordered map of package name to
   //   map of file name to
   //     vector of flags in the file.
   // This map is used to output matching flags grouped by package and file
@@ -273,20 +273,26 @@
 
   absl::string_view package_separator;  // controls blank lines between packages
   absl::string_view file_separator;     // controls blank lines between files
-  for (const auto& package : matching_flags) {
+  for (auto& package : matching_flags) {
     if (format == HelpFormat::kHumanReadable) {
       out << package_separator;
       package_separator = "\n\n";
     }
 
     file_separator = "";
-    for (const auto& flags_in_file : package.second) {
+    for (auto& flags_in_file : package.second) {
       if (format == HelpFormat::kHumanReadable) {
         out << file_separator << "  Flags from " << flags_in_file.first
             << ":\n";
         file_separator = "\n";
       }
 
+      std::sort(std::begin(flags_in_file.second),
+                std::end(flags_in_file.second),
+                [](const CommandLineFlag* lhs, const CommandLineFlag* rhs) {
+                  return lhs->Name() < rhs->Name();
+                });
+
       for (const auto* flag : flags_in_file.second) {
         flags_internal::FlagHelp(out, *flag, format);
       }
diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc
index b5c2487..044d71c 100644
--- a/absl/flags/internal/usage_test.cc
+++ b/absl/flags/internal/usage_test.cc
@@ -45,6 +45,7 @@
 struct UDT {
   UDT() = default;
   UDT(const UDT&) = default;
+  UDT& operator=(const UDT&) = default;
 };
 bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; }
 std::string AbslUnparseFlag(const UDT&) { return "UDT{}"; }
diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc
index 41bc0bc..8dc91db 100644
--- a/absl/flags/parse_test.cc
+++ b/absl/flags/parse_test.cc
@@ -46,6 +46,7 @@
 struct UDT {
   UDT() = default;
   UDT(const UDT&) = default;
+  UDT& operator=(const UDT&) = default;
   UDT(int v) : value(v) {}  // NOLINT
 
   int value;
diff --git a/absl/flags/reflection.cc b/absl/flags/reflection.cc
index 0c76110..dbce403 100644
--- a/absl/flags/reflection.cc
+++ b/absl/flags/reflection.cc
@@ -18,11 +18,11 @@
 #include <assert.h>
 
 #include <atomic>
-#include <map>
 #include <string>
 
 #include "absl/base/config.h"
 #include "absl/base/thread_annotations.h"
+#include "absl/container/flat_hash_map.h"
 #include "absl/flags/commandlineflag.h"
 #include "absl/flags/internal/private_handle_accessor.h"
 #include "absl/flags/internal/registry.h"
@@ -68,7 +68,7 @@
   friend void FinalizeRegistry();
 
   // The map from name to flag, for FindFlag().
-  using FlagMap = std::map<absl::string_view, CommandLineFlag*>;
+  using FlagMap = absl::flat_hash_map<absl::string_view, CommandLineFlag*>;
   using FlagIterator = FlagMap::iterator;
   using FlagConstIterator = FlagMap::const_iterator;
   FlagMap flags_;
@@ -204,6 +204,10 @@
   for (const auto& f : registry.flags_) {
     registry.flat_flags_.push_back(f.second);
   }
+  std::sort(std::begin(registry.flat_flags_), std::end(registry.flat_flags_),
+            [](const CommandLineFlag* lhs, const CommandLineFlag* rhs) {
+              return lhs->Name() < rhs->Name();
+            });
   registry.flags_.clear();
   registry.finalized_flags_.store(true, std::memory_order_release);
 }
diff --git a/absl/functional/BUILD.bazel b/absl/functional/BUILD.bazel
index ebd9b99..f9f2b9c 100644
--- a/absl/functional/BUILD.bazel
+++ b/absl/functional/BUILD.bazel
@@ -14,7 +14,6 @@
 # limitations under the License.
 #
 
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
@@ -60,6 +59,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         "//absl/base:base_internal",
+        "//absl/base:core_headers",
         "//absl/meta:type_traits",
     ],
 )
diff --git a/absl/functional/CMakeLists.txt b/absl/functional/CMakeLists.txt
index cda914f..338ddc6 100644
--- a/absl/functional/CMakeLists.txt
+++ b/absl/functional/CMakeLists.txt
@@ -39,7 +39,7 @@
   DEPS
     absl::bind_front
     absl::memory
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -53,6 +53,7 @@
     ${ABSL_DEFAULT_COPTS}
   DEPS
     absl::base_internal
+    absl::core_headers
     absl::meta
   PUBLIC
 )
@@ -68,5 +69,5 @@
     absl::function_ref
     absl::memory
     absl::test_instance_tracker
-    gmock_main
+    GTest::gmock_main
 )
diff --git a/absl/functional/function_ref.h b/absl/functional/function_ref.h
index 6e03ac2..824e3ce 100644
--- a/absl/functional/function_ref.h
+++ b/absl/functional/function_ref.h
@@ -50,6 +50,7 @@
 #include <functional>
 #include <type_traits>
 
+#include "absl/base/attributes.h"
 #include "absl/functional/internal/function_ref.h"
 #include "absl/meta/type_traits.h"
 
@@ -98,7 +99,8 @@
  public:
   // Constructs a FunctionRef from any invokable type.
   template <typename F, typename = EnableIfCompatible<const F&>>
-  FunctionRef(const F& f)  // NOLINT(runtime/explicit)
+  // NOLINTNEXTLINE(runtime/explicit)
+  FunctionRef(const F& f ABSL_ATTRIBUTE_LIFETIME_BOUND)
       : invoker_(&absl::functional_internal::InvokeObject<F, R, Args...>) {
     absl::functional_internal::AssertNonNull(f);
     ptr_.obj = &f;
@@ -122,6 +124,7 @@
   // To help prevent subtle lifetime bugs, FunctionRef is not assignable.
   // Typically, it should only be used as an argument type.
   FunctionRef& operator=(const FunctionRef& rhs) = delete;
+  FunctionRef(const FunctionRef& rhs) = default;
 
   // Call the underlying object.
   R operator()(Args... args) const {
diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel
index 4b2c220..f0640d3 100644
--- a/absl/hash/BUILD.bazel
+++ b/absl/hash/BUILD.bazel
@@ -14,7 +14,6 @@
 # limitations under the License.
 #
 
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
@@ -37,7 +36,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":city",
-        ":wyhash",
+        ":low_level_hash",
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:endian",
@@ -143,27 +142,28 @@
 )
 
 cc_library(
-    name = "wyhash",
-    srcs = ["internal/wyhash.cc"],
-    hdrs = ["internal/wyhash.h"],
+    name = "low_level_hash",
+    srcs = ["internal/low_level_hash.cc"],
+    hdrs = ["internal/low_level_hash.h"],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     visibility = ["//visibility:private"],
     deps = [
         "//absl/base:config",
         "//absl/base:endian",
+        "//absl/numeric:bits",
         "//absl/numeric:int128",
     ],
 )
 
 cc_test(
-    name = "wyhash_test",
-    srcs = ["internal/wyhash_test.cc"],
+    name = "low_level_hash_test",
+    srcs = ["internal/low_level_hash_test.cc"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     visibility = ["//visibility:private"],
     deps = [
-        ":wyhash",
+        ":low_level_hash",
         "//absl/strings",
         "@com_google_googletest//:gtest_main",
     ],
diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt
index b43bfa5..5916ae3 100644
--- a/absl/hash/CMakeLists.txt
+++ b/absl/hash/CMakeLists.txt
@@ -36,7 +36,7 @@
     absl::optional
     absl::variant
     absl::utility
-    absl::wyhash
+    absl::low_level_hash
   PUBLIC
 )
 
@@ -52,7 +52,7 @@
     absl::meta
     absl::strings
     absl::variant
-    gmock
+    GTest::gmock
   TESTONLY
 )
 
@@ -72,7 +72,7 @@
     absl::spy_hash_state
     absl::meta
     absl::int128
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -113,19 +113,20 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::city
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
   NAME
-    wyhash
+    low_level_hash
   HDRS
-    "internal/wyhash.h"
+    "internal/low_level_hash.h"
   SRCS
-    "internal/wyhash.cc"
+    "internal/low_level_hash.cc"
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
+    absl::bits
     absl::config
     absl::endian
     absl::int128
@@ -133,13 +134,13 @@
 
 absl_cc_test(
   NAME
-    wyhash_test
+    low_level_hash_test
   SRCS
-    "internal/wyhash_test.cc"
+    "internal/low_level_hash_test.cc"
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
-    absl::wyhash
+    absl::low_level_hash
     absl::strings
-    gmock_main
+    GTest::gmock_main
 )
diff --git a/absl/hash/hash.h b/absl/hash/hash.h
index 5de132c..8282ea5 100644
--- a/absl/hash/hash.h
+++ b/absl/hash/hash.h
@@ -73,6 +73,8 @@
 #ifndef ABSL_HASH_HASH_H_
 #define ABSL_HASH_HASH_H_
 
+#include <tuple>
+
 #include "absl/hash/internal/hash.h"
 
 namespace absl {
@@ -214,6 +216,26 @@
 template <typename T>
 using Hash = absl::hash_internal::Hash<T>;
 
+// HashOf
+//
+// absl::HashOf() is a helper that generates a hash from the values of its
+// arguments.  It dispatches to absl::Hash directly, as follows:
+//  * HashOf(t) == absl::Hash<T>{}(t)
+//  * HashOf(a, b, c) == HashOf(std::make_tuple(a, b, c))
+//
+// HashOf(a1, a2, ...) == HashOf(b1, b2, ...) is guaranteed when
+//  * The argument lists have pairwise identical C++ types
+//  * a1 == b1 && a2 == b2 && ...
+//
+// The requirement that the arguments match in both type and value is critical.
+// It means that `a == b` does not necessarily imply `HashOf(a) == HashOf(b)` if
+// `a` and `b` have different types. For example, `HashOf(2) != HashOf(2.0)`.
+template <int&... ExplicitArgumentBarrier, typename... Types>
+size_t HashOf(const Types&... values) {
+  auto tuple = std::tie(values...);
+  return absl::Hash<decltype(tuple)>{}(tuple);
+}
+
 // HashState
 //
 // A type erased version of the hash state concept, for use in user-defined
diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc
index 1d2e6cf..b3ddebd 100644
--- a/absl/hash/hash_test.cc
+++ b/absl/hash/hash_test.cc
@@ -973,4 +973,39 @@
             absl::Hash<ValueWithBoolConversion>()(ValueWithBoolConversion{1}));
 }
 
+TEST(HashOf, MatchesHashForSingleArgument) {
+  std::string s = "forty two";
+  int i = 42;
+  double d = 42.0;
+  std::tuple<int, int> t{4, 2};
+
+  EXPECT_EQ(absl::HashOf(s), absl::Hash<std::string>{}(s));
+  EXPECT_EQ(absl::HashOf(i), absl::Hash<int>{}(i));
+  EXPECT_EQ(absl::HashOf(d), absl::Hash<double>{}(d));
+  EXPECT_EQ(absl::HashOf(t), (absl::Hash<std::tuple<int, int>>{}(t)));
+}
+
+TEST(HashOf, MatchesHashOfTupleForMultipleArguments) {
+  std::string hello = "hello";
+  std::string world = "world";
+
+  EXPECT_EQ(absl::HashOf(), absl::HashOf(std::make_tuple()));
+  EXPECT_EQ(absl::HashOf(hello), absl::HashOf(std::make_tuple(hello)));
+  EXPECT_EQ(absl::HashOf(hello, world),
+            absl::HashOf(std::make_tuple(hello, world)));
+}
+
+template <typename T>
+std::true_type HashOfExplicitParameter(decltype(absl::HashOf<T>(0))) {
+  return {};
+}
+template <typename T>
+std::false_type HashOfExplicitParameter(size_t) {
+  return {};
+}
+
+TEST(HashOf, CantPassExplicitTemplateParameters) {
+  EXPECT_FALSE(HashOfExplicitParameter<int>(0));
+}
+
 }  // namespace
diff --git a/absl/hash/internal/hash.cc b/absl/hash/internal/hash.cc
index 1433eb9..11451e5 100644
--- a/absl/hash/internal/hash.cc
+++ b/absl/hash/internal/hash.cc
@@ -18,13 +18,12 @@
 ABSL_NAMESPACE_BEGIN
 namespace hash_internal {
 
-uint64_t HashState::CombineLargeContiguousImpl32(uint64_t state,
-                                                 const unsigned char* first,
-                                                 size_t len) {
+uint64_t MixingHashState::CombineLargeContiguousImpl32(
+    uint64_t state, const unsigned char* first, size_t len) {
   while (len >= PiecewiseChunkSize()) {
-    state =
-        Mix(state, absl::hash_internal::CityHash32(reinterpret_cast<const char*>(first),
-                                         PiecewiseChunkSize()));
+    state = Mix(state,
+                hash_internal::CityHash32(reinterpret_cast<const char*>(first),
+                                          PiecewiseChunkSize()));
     len -= PiecewiseChunkSize();
     first += PiecewiseChunkSize();
   }
@@ -33,9 +32,8 @@
                                std::integral_constant<int, 4>{});
 }
 
-uint64_t HashState::CombineLargeContiguousImpl64(uint64_t state,
-                                                 const unsigned char* first,
-                                                 size_t len) {
+uint64_t MixingHashState::CombineLargeContiguousImpl64(
+    uint64_t state, const unsigned char* first, size_t len) {
   while (len >= PiecewiseChunkSize()) {
     state = Mix(state, Hash64(first, PiecewiseChunkSize()));
     len -= PiecewiseChunkSize();
@@ -46,23 +44,24 @@
                                std::integral_constant<int, 8>{});
 }
 
-ABSL_CONST_INIT const void* const HashState::kSeed = &kSeed;
+ABSL_CONST_INIT const void* const MixingHashState::kSeed = &kSeed;
 
-// The salt array used by Wyhash. This array is NOT the mechanism used to make
-// absl::Hash non-deterministic between program invocations.  See `Seed()` for
-// that mechanism.
+// The salt array used by LowLevelHash. This array is NOT the mechanism used to
+// make absl::Hash non-deterministic between program invocations.  See `Seed()`
+// for that mechanism.
 //
 // Any random values are fine. These values are just digits from the decimal
 // part of pi.
 // https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number
-constexpr uint64_t kWyhashSalt[5] = {
+constexpr uint64_t kHashSalt[5] = {
     uint64_t{0x243F6A8885A308D3}, uint64_t{0x13198A2E03707344},
     uint64_t{0xA4093822299F31D0}, uint64_t{0x082EFA98EC4E6C89},
     uint64_t{0x452821E638D01377},
 };
 
-uint64_t HashState::WyhashImpl(const unsigned char* data, size_t len) {
-  return Wyhash(data, len, Seed(), kWyhashSalt);
+uint64_t MixingHashState::LowLevelHashImpl(const unsigned char* data,
+                                           size_t len) {
+  return LowLevelHash(data, len, Seed(), kHashSalt);
 }
 
 }  // namespace hash_internal
diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h
index 7fb0af0..b1e33ca 100644
--- a/absl/hash/internal/hash.h
+++ b/absl/hash/internal/hash.h
@@ -21,6 +21,7 @@
 
 #include <algorithm>
 #include <array>
+#include <bitset>
 #include <cmath>
 #include <cstring>
 #include <deque>
@@ -42,14 +43,14 @@
 #include "absl/base/internal/unaligned_access.h"
 #include "absl/base/port.h"
 #include "absl/container/fixed_array.h"
-#include "absl/hash/internal/wyhash.h"
+#include "absl/hash/internal/city.h"
+#include "absl/hash/internal/low_level_hash.h"
 #include "absl/meta/type_traits.h"
 #include "absl/numeric/int128.h"
 #include "absl/strings/string_view.h"
 #include "absl/types/optional.h"
 #include "absl/types/variant.h"
 #include "absl/utility/utility.h"
-#include "absl/hash/internal/city.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -379,7 +380,7 @@
 // This SFINAE gets MSVC confused under some conditions. Let's just disable it
 // for now.
 H
-#else  // _MSC_VER
+#else   // _MSC_VER
 typename std::enable_if<absl::conjunction<is_hashable<Ts>...>::value, H>::type
 #endif  // _MSC_VER
 AbslHashValue(H hash_state, const std::tuple<Ts...>& t) {
@@ -489,8 +490,9 @@
 
 // AbslHashValue for hashing std::vector
 //
-// Do not use this for vector<bool>. It does not have a .data(), and a fallback
-// for std::hash<> is most likely faster.
+// Do not use this for vector<bool> on platforms that have a working
+// implementation of std::hash. It does not have a .data(), and a fallback for
+// std::hash<> is most likely faster.
 template <typename H, typename T, typename Allocator>
 typename std::enable_if<is_hashable<T>::value && !std::is_same<T, bool>::value,
                         H>::type
@@ -500,6 +502,27 @@
                     vector.size());
 }
 
+#if defined(ABSL_IS_BIG_ENDIAN) && \
+    (defined(__GLIBCXX__) || defined(__GLIBCPP__))
+// AbslHashValue for hashing std::vector<bool>
+//
+// std::hash in libstdc++ does not work correctly with vector<bool> on Big
+// Endian platforms therefore we need to implement a custom AbslHashValue for
+// it. More details on the bug:
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102531
+template <typename H, typename T, typename Allocator>
+typename std::enable_if<is_hashable<T>::value && std::is_same<T, bool>::value,
+                        H>::type
+AbslHashValue(H hash_state, const std::vector<T, Allocator>& vector) {
+  typename H::AbslInternalPiecewiseCombiner combiner;
+  for (const auto& i : vector) {
+    unsigned char c = static_cast<unsigned char>(i);
+    hash_state = combiner.add_buffer(std::move(hash_state), &c, sizeof(c));
+  }
+  return H::combine(combiner.finalize(std::move(hash_state)), vector.size());
+}
+#endif
+
 // -----------------------------------------------------------------------------
 // AbslHashValue for Ordered Associative Containers
 // -----------------------------------------------------------------------------
@@ -592,9 +615,28 @@
 // AbslHashValue for Other Types
 // -----------------------------------------------------------------------------
 
-// AbslHashValue for hashing std::bitset is not defined, for the same reason as
-// for vector<bool> (see std::vector above): It does not expose the raw bytes,
-// and a fallback to std::hash<> is most likely faster.
+// AbslHashValue for hashing std::bitset is not defined on Little Endian
+// platforms, for the same reason as for vector<bool> (see std::vector above):
+// It does not expose the raw bytes, and a fallback to std::hash<> is most
+// likely faster.
+
+#if defined(ABSL_IS_BIG_ENDIAN) && \
+    (defined(__GLIBCXX__) || defined(__GLIBCPP__))
+// AbslHashValue for hashing std::bitset
+//
+// std::hash in libstdc++ does not work correctly with std::bitset on Big Endian
+// platforms therefore we need to implement a custom AbslHashValue for it. More
+// details on the bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102531
+template <typename H, size_t N>
+H AbslHashValue(H hash_state, const std::bitset<N>& set) {
+  typename H::AbslInternalPiecewiseCombiner combiner;
+  for (int i = 0; i < N; i++) {
+    unsigned char c = static_cast<unsigned char>(set[i]);
+    hash_state = combiner.add_buffer(std::move(hash_state), &c, sizeof(c));
+  }
+  return H::combine(combiner.finalize(std::move(hash_state)), N);
+}
+#endif
 
 // -----------------------------------------------------------------------------
 
@@ -714,8 +756,8 @@
 struct is_hashable
     : std::integral_constant<bool, HashSelect::template Apply<T>::value> {};
 
-// HashState
-class ABSL_DLL HashState : public HashStateBase<HashState> {
+// MixingHashState
+class ABSL_DLL MixingHashState : public HashStateBase<MixingHashState> {
   // absl::uint128 is not an alias or a thin wrapper around the intrinsic.
   // We use the intrinsic when available to improve performance.
 #ifdef ABSL_HAVE_INTRINSIC_INT128
@@ -734,22 +776,23 @@
 
  public:
   // Move only
-  HashState(HashState&&) = default;
-  HashState& operator=(HashState&&) = default;
+  MixingHashState(MixingHashState&&) = default;
+  MixingHashState& operator=(MixingHashState&&) = default;
 
-  // HashState::combine_contiguous()
+  // MixingHashState::combine_contiguous()
   //
   // Fundamental base case for hash recursion: mixes the given range of bytes
   // into the hash state.
-  static HashState combine_contiguous(HashState hash_state,
-                                      const unsigned char* first, size_t size) {
-    return HashState(
+  static MixingHashState combine_contiguous(MixingHashState hash_state,
+                                            const unsigned char* first,
+                                            size_t size) {
+    return MixingHashState(
         CombineContiguousImpl(hash_state.state_, first, size,
                               std::integral_constant<int, sizeof(size_t)>{}));
   }
-  using HashState::HashStateBase::combine_contiguous;
+  using MixingHashState::HashStateBase::combine_contiguous;
 
-  // HashState::hash()
+  // MixingHashState::hash()
   //
   // For performance reasons in non-opt mode, we specialize this for
   // integral types.
@@ -761,24 +804,24 @@
     return static_cast<size_t>(Mix(Seed(), static_cast<uint64_t>(value)));
   }
 
-  // Overload of HashState::hash()
+  // Overload of MixingHashState::hash()
   template <typename T, absl::enable_if_t<!IntegralFastPath<T>::value, int> = 0>
   static size_t hash(const T& value) {
-    return static_cast<size_t>(combine(HashState{}, value).state_);
+    return static_cast<size_t>(combine(MixingHashState{}, value).state_);
   }
 
  private:
   // Invoked only once for a given argument; that plus the fact that this is
   // move-only ensures that there is only one non-moved-from object.
-  HashState() : state_(Seed()) {}
+  MixingHashState() : state_(Seed()) {}
 
   // Workaround for MSVC bug.
   // We make the type copyable to fix the calling convention, even though we
   // never actually copy it. Keep it private to not affect the public API of the
   // type.
-  HashState(const HashState&) = default;
+  MixingHashState(const MixingHashState&) = default;
 
-  explicit HashState(uint64_t state) : state_(state) {}
+  explicit MixingHashState(uint64_t state) : state_(state) {}
 
   // Implementation of the base case for combine_contiguous where we actually
   // mix the bytes into the state.
@@ -793,7 +836,6 @@
                                         std::integral_constant<int, 8>
                                         /* sizeof_size_t */);
 
-
   // Slow dispatch path for calls to CombineContiguousImpl with a size argument
   // larger than PiecewiseChunkSize().  Has the same effect as calling
   // CombineContiguousImpl() repeatedly with the chunk stride size.
@@ -856,8 +898,15 @@
   }
 
   ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Mix(uint64_t state, uint64_t v) {
+#if defined(__aarch64__)
+    // On AArch64, calculating a 128-bit product is inefficient, because it
+    // requires a sequence of two instructions to calculate the upper and lower
+    // halves of the result.
+    using MultType = uint64_t;
+#else
     using MultType =
         absl::conditional_t<sizeof(size_t) == 4, uint64_t, uint128>;
+#endif
     // We do the addition in 64-bit space to make sure the 128-bit
     // multiplication is fast. If we were to do it as MultType the compiler has
     // to assume that the high word is non-zero and needs to perform 2
@@ -867,16 +916,16 @@
     return static_cast<uint64_t>(m ^ (m >> (sizeof(m) * 8 / 2)));
   }
 
-  // An extern to avoid bloat on a direct call to Wyhash() with fixed values for
-  // both the seed and salt parameters.
-  static uint64_t WyhashImpl(const unsigned char* data, size_t len);
+  // An extern to avoid bloat on a direct call to LowLevelHash() with fixed
+  // values for both the seed and salt parameters.
+  static uint64_t LowLevelHashImpl(const unsigned char* data, size_t len);
 
   ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Hash64(const unsigned char* data,
                                                       size_t len) {
 #ifdef ABSL_HAVE_INTRINSIC_INT128
-    return WyhashImpl(data, len);
+    return LowLevelHashImpl(data, len);
 #else
-    return absl::hash_internal::CityHash64(reinterpret_cast<const char*>(data), len);
+    return hash_internal::CityHash64(reinterpret_cast<const char*>(data), len);
 #endif
   }
 
@@ -911,8 +960,8 @@
   uint64_t state_;
 };
 
-// HashState::CombineContiguousImpl()
-inline uint64_t HashState::CombineContiguousImpl(
+// MixingHashState::CombineContiguousImpl()
+inline uint64_t MixingHashState::CombineContiguousImpl(
     uint64_t state, const unsigned char* first, size_t len,
     std::integral_constant<int, 4> /* sizeof_size_t */) {
   // For large values we use CityHash, for small ones we just use a
@@ -922,7 +971,7 @@
     if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) {
       return CombineLargeContiguousImpl32(state, first, len);
     }
-    v = absl::hash_internal::CityHash32(reinterpret_cast<const char*>(first), len);
+    v = hash_internal::CityHash32(reinterpret_cast<const char*>(first), len);
   } else if (len >= 4) {
     v = Read4To8(first, len);
   } else if (len > 0) {
@@ -934,12 +983,12 @@
   return Mix(state, v);
 }
 
-// Overload of HashState::CombineContiguousImpl()
-inline uint64_t HashState::CombineContiguousImpl(
+// Overload of MixingHashState::CombineContiguousImpl()
+inline uint64_t MixingHashState::CombineContiguousImpl(
     uint64_t state, const unsigned char* first, size_t len,
     std::integral_constant<int, 8> /* sizeof_size_t */) {
-  // For large values we use Wyhash or CityHash depending on the platform, for
-  // small ones we just use a multiplicative hash.
+  // For large values we use LowLevelHash or CityHash depending on the platform,
+  // for small ones we just use a multiplicative hash.
   uint64_t v;
   if (len > 16) {
     if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) {
@@ -976,7 +1025,9 @@
 
 template <typename T>
 struct HashImpl {
-  size_t operator()(const T& value) const { return HashState::hash(value); }
+  size_t operator()(const T& value) const {
+    return MixingHashState::hash(value);
+  }
 };
 
 template <typename T>
diff --git a/absl/hash/internal/wyhash.cc b/absl/hash/internal/low_level_hash.cc
similarity index 75%
rename from absl/hash/internal/wyhash.cc
rename to absl/hash/internal/low_level_hash.cc
index 642bde4..6f9cb9c 100644
--- a/absl/hash/internal/wyhash.cc
+++ b/absl/hash/internal/low_level_hash.cc
@@ -12,23 +12,35 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "absl/hash/internal/wyhash.h"
+#include "absl/hash/internal/low_level_hash.h"
 
 #include "absl/base/internal/unaligned_access.h"
+#include "absl/numeric/bits.h"
 #include "absl/numeric/int128.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace hash_internal {
 
-static uint64_t WyhashMix(uint64_t v0, uint64_t v1) {
+static uint64_t Mix(uint64_t v0, uint64_t v1) {
+#if !defined(__aarch64__)
+  // The default bit-mixer uses 64x64->128-bit multiplication.
   absl::uint128 p = v0;
   p *= v1;
   return absl::Uint128Low64(p) ^ absl::Uint128High64(p);
+#else
+  // The default bit-mixer above would perform poorly on some ARM microarchs,
+  // where calculating a 128-bit product requires a sequence of two
+  // instructions with a high combined latency and poor throughput.
+  // Instead, we mix bits using only 64-bit arithmetic, which is faster.
+  uint64_t p = v0 ^ absl::rotl(v1, 40);
+  p *= v1 ^ absl::rotl(v0, 39);
+  return p ^ (p >> 11);
+#endif
 }
 
-uint64_t Wyhash(const void* data, size_t len, uint64_t seed,
-                const uint64_t salt[]) {
+uint64_t LowLevelHash(const void* data, size_t len, uint64_t seed,
+                      const uint64_t salt[]) {
   const uint8_t* ptr = static_cast<const uint8_t*>(data);
   uint64_t starting_length = static_cast<uint64_t>(len);
   uint64_t current_state = seed ^ salt[0];
@@ -49,12 +61,12 @@
       uint64_t g = absl::base_internal::UnalignedLoad64(ptr + 48);
       uint64_t h = absl::base_internal::UnalignedLoad64(ptr + 56);
 
-      uint64_t cs0 = WyhashMix(a ^ salt[1], b ^ current_state);
-      uint64_t cs1 = WyhashMix(c ^ salt[2], d ^ current_state);
+      uint64_t cs0 = Mix(a ^ salt[1], b ^ current_state);
+      uint64_t cs1 = Mix(c ^ salt[2], d ^ current_state);
       current_state = (cs0 ^ cs1);
 
-      uint64_t ds0 = WyhashMix(e ^ salt[3], f ^ duplicated_state);
-      uint64_t ds1 = WyhashMix(g ^ salt[4], h ^ duplicated_state);
+      uint64_t ds0 = Mix(e ^ salt[3], f ^ duplicated_state);
+      uint64_t ds1 = Mix(g ^ salt[4], h ^ duplicated_state);
       duplicated_state = (ds0 ^ ds1);
 
       ptr += 64;
@@ -70,7 +82,7 @@
     uint64_t a = absl::base_internal::UnalignedLoad64(ptr);
     uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8);
 
-    current_state = WyhashMix(a ^ salt[1], b ^ current_state);
+    current_state = Mix(a ^ salt[1], b ^ current_state);
 
     ptr += 16;
     len -= 16;
@@ -101,9 +113,9 @@
     b = 0;
   }
 
-  uint64_t w = WyhashMix(a ^ salt[1], b ^ current_state);
+  uint64_t w = Mix(a ^ salt[1], b ^ current_state);
   uint64_t z = salt[1] ^ starting_length;
-  return WyhashMix(w, z);
+  return Mix(w, z);
 }
 
 }  // namespace hash_internal
diff --git a/absl/hash/internal/low_level_hash.h b/absl/hash/internal/low_level_hash.h
new file mode 100644
index 0000000..439968a
--- /dev/null
+++ b/absl/hash/internal/low_level_hash.h
@@ -0,0 +1,50 @@
+// Copyright 2020 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.
+//
+// This file provides the Google-internal implementation of LowLevelHash.
+//
+// LowLevelHash is a fast hash function for hash tables, the fastest we've
+// currently (late 2020) found that passes the SMHasher tests. The algorithm
+// relies on intrinsic 128-bit multiplication for speed. This is not meant to be
+// secure - just fast.
+//
+// It is closely based on a version of wyhash, but does not maintain or
+// guarantee future compatibility with it.
+
+#ifndef ABSL_HASH_INTERNAL_LOW_LEVEL_HASH_H_
+#define ABSL_HASH_INTERNAL_LOW_LEVEL_HASH_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace hash_internal {
+
+// Hash function for a byte array. A 64-bit seed and a set of five 64-bit
+// integers are hashed into the result.
+//
+// To allow all hashable types (including string_view and Span) to depend on
+// this algorithm, we keep the API low-level, with as few dependencies as
+// possible.
+uint64_t LowLevelHash(const void* data, size_t len, uint64_t seed,
+                      const uint64_t salt[5]);
+
+}  // namespace hash_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_HASH_INTERNAL_LOW_LEVEL_HASH_H_
diff --git a/absl/hash/internal/low_level_hash_test.cc b/absl/hash/internal/low_level_hash_test.cc
new file mode 100644
index 0000000..ae930b3
--- /dev/null
+++ b/absl/hash/internal/low_level_hash_test.cc
@@ -0,0 +1,580 @@
+// Copyright 2020 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/hash/internal/low_level_hash.h"
+
+#include <cinttypes>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/strings/escaping.h"
+
+#define UPDATE_GOLDEN 0
+
+namespace {
+
+static const uint64_t kSalt[5] = {0xa0761d6478bd642f, 0xe7037ed1a0b428dbl,
+                                  0x8ebc6af09c88c6e3, 0x589965cc75374cc3l,
+                                  0x1d8e4e27c47d124f};
+
+TEST(LowLevelHashTest, VerifyGolden) {
+  constexpr size_t kNumGoldenOutputs = 134;
+  static struct {
+    absl::string_view base64_data;
+    uint64_t seed;
+  } cases[] = {
+      {"", uint64_t{0xec42b7ab404b8acb}},
+      {"ICAg", uint64_t{0}},
+      {"YWFhYQ==", uint64_t{0}},
+      {"AQID", uint64_t{0}},
+      {"AQIDBA==", uint64_t{0}},
+      {"dGhpcmRfcGFydHl8d3loYXNofDY0", uint64_t{0}},
+      {"Zw==", uint64_t{0xeeee074043a3ee0f}},
+      {"xmk=", uint64_t{0x857902089c393de}},
+      {"c1H/", uint64_t{0x993df040024ca3af}},
+      {"SuwpzQ==", uint64_t{0xc4e4c2acea740e96}},
+      {"uqvy++M=", uint64_t{0x6a214b3db872d0cf}},
+      {"RnzCVPgb", uint64_t{0x44343db6a89dba4d}},
+      {"6OeNdlouYw==", uint64_t{0x77b5d6d1ae1dd483}},
+      {"M5/JmmYyDbc=", uint64_t{0x89ab8ecb44d221f1}},
+      {"MVijWiVdBRdY", uint64_t{0x60244b17577ca81b}},
+      {"6V7Uq7LNxpu0VA==", uint64_t{0x59a08dcee0717067}},
+      {"EQ6CdEEhPdyHcOk=", uint64_t{0xf5f20db3ade57396}},
+      {"PqFB4fxnPgF+l+rc", uint64_t{0xbf8dee0751ad3efb}},
+      {"a5aPOFwq7LA7+zKvPA==", uint64_t{0x6b7a06b268d63e30}},
+      {"VOwY21wCGv5D+/qqOvs=", uint64_t{0xb8c37f0ae0f54c82}},
+      {"KdHmBTx8lHXYvmGJ+Vy7", uint64_t{0x9fcbed0c38e50eef}},
+      {"qJkPlbHr8bMF7/cA6aE65Q==", uint64_t{0x2af4bade1d8e3a1d}},
+      {"ygvL0EhHZL0fIx6oHHtkxRQ=", uint64_t{0x714e3aa912da2f2c}},
+      {"c1rFXkt5YztwZCQRngncqtSs", uint64_t{0xf5ee75e3cbb82c1c}},
+      {"8hsQrzszzeNQSEcVXLtvIhm6mw==", uint64_t{0x620e7007321b93b9}},
+      {"ffUL4RocfyP4KfikGxO1yk7omDI=", uint64_t{0xc08528cac2e551fc}},
+      {"OOB5TT00vF9Od/rLbAWshiErqhpV", uint64_t{0x6a1debf9cc3ad39}},
+      {"or5wtXM7BFzTNpSzr+Lw5J5PMhVJ/Q==", uint64_t{0x7e0a3c88111fc226}},
+      {"gk6pCHDUsoopVEiaCrzVDhioRKxb844=", uint64_t{0x1301fef15df39edb}},
+      {"TNctmwlC5QbEM6/No4R/La3UdkfeMhzs", uint64_t{0x64e181f3d5817ab}},
+      {"SsQw9iAjhWz7sgcE9OwLuSC6hsM+BfHs2Q==", uint64_t{0xafafc44961078ecb}},
+      {"ZzO3mVCj4xTT2TT3XqDyEKj2BZQBvrS8RHg=", uint64_t{0x4f7bb45549250094}},
+      {"+klp5iPQGtppan5MflEls0iEUzqU+zGZkDJX", uint64_t{0xa30061abaa2818c}},
+      {"RO6bvOnlJc8I9eniXlNgqtKy0IX6VNg16NRmgg==",
+       uint64_t{0xd902ee3e44a5705f}},
+      {"ZJjZqId1ZXBaij9igClE3nyliU5XWdNRrayGlYA=", uint64_t{0x316d36da516f583}},
+      {"7BfkhfGMDGbxfMB8uyL85GbaYQtjr2K8g7RpLzr/",
+       uint64_t{0x402d83f9f834f616}},
+      {"rycWk6wHH7htETQtje9PidS2YzXBx+Qkg2fY7ZYS7A==",
+       uint64_t{0x9c604164c016b72c}},
+      {"RTkC2OUK+J13CdGllsH0H5WqgspsSa6QzRZouqx6pvI=",
+       uint64_t{0x3f4507e01f9e73ba}},
+      {"tKjKmbLCNyrLCM9hycOAXm4DKNpM12oZ7dLTmUx5iwAi",
+       uint64_t{0xc3fe0d5be8d2c7c7}},
+      {"VprUGNH+5NnNRaORxgH/ySrZFQFDL+4VAodhfBNinmn8cg==",
+       uint64_t{0x531858a40bfa7ea1}},
+      {"gc1xZaY+q0nPcUvOOnWnT3bqfmT/geth/f7Dm2e/DemMfk4=",
+       uint64_t{0x86689478a7a7e8fa}},
+      {"Mr35fIxqx1ukPAL0su1yFuzzAU3wABCLZ8+ZUFsXn47UmAph",
+       uint64_t{0x4ec948b8e7f27288}},
+      {"A9G8pw2+m7+rDtWYAdbl8tb2fT7FFo4hLi2vAsa5Y8mKH3CX3g==",
+       uint64_t{0xce46c7213c10032}},
+      {"DFaJGishGwEHDdj9ixbCoaTjz9KS0phLNWHVVdFsM93CvPft3hM=",
+       uint64_t{0xf63e96ee6f32a8b6}},
+      {"7+Ugx+Kr3aRNgYgcUxru62YkTDt5Hqis+2po81hGBkcrJg4N0uuy",
+       uint64_t{0x1cfe85e65fc5225}},
+      {"H2w6O8BUKqu6Tvj2xxaecxEI2wRgIgqnTTG1WwOgDSINR13Nm4d4Vg==",
+       uint64_t{0x45c474f1cee1d2e8}},
+      {"1XBMnIbqD5jy65xTDaf6WtiwtdtQwv1dCVoqpeKj+7cTR1SaMWMyI04=",
+       uint64_t{0x6e024e14015f329c}},
+      {"znZbdXG2TSFrKHEuJc83gPncYpzXGbAebUpP0XxzH0rpe8BaMQ17nDbt",
+       uint64_t{0x760c40502103ae1c}},
+      {"ylu8Atu13j1StlcC1MRMJJXIl7USgDDS22HgVv0WQ8hx/8pNtaiKB17hCQ==",
+       uint64_t{0x17fd05c3c560c320}},
+      {"M6ZVVzsd7vAvbiACSYHioH/440dp4xG2mLlBnxgiqEvI/aIEGpD0Sf4VS0g=",
+       uint64_t{0x8b34200a6f8e90d9}},
+      {"li3oFSXLXI+ubUVGJ4blP6mNinGKLHWkvGruun85AhVn6iuMtocbZPVhqxzn",
+       uint64_t{0x6be89e50818bdf69}},
+      {"kFuQHuUCqBF3Tc3hO4dgdIp223ShaCoog48d5Do5zMqUXOh5XpGK1t5XtxnfGA==",
+       uint64_t{0xfb389773315b47d8}},
+      {"jWmOad0v0QhXVJd1OdGuBZtDYYS8wBVHlvOeTQx9ZZnm8wLEItPMeihj72E0nWY=",
+       uint64_t{0x4f2512a23f61efee}},
+      {"z+DHU52HaOQdW4JrZwDQAebEA6rm13Zg/9lPYA3txt3NjTBqFZlOMvTRnVzRbl23",
+       uint64_t{0x59ccd92fc16c6fda}},
+      {"MmBiGDfYeTayyJa/tVycg+rN7f9mPDFaDc+23j0TlW9094er0ADigsl4QX7V3gG/qw==",
+       uint64_t{0x25c5a7f5bd330919}},
+      {"774RK+9rOL4iFvs1q2qpo/JVc/I39buvNjqEFDtDvyoB0FXxPI2vXqOrk08VPfIHkmU=",
+       uint64_t{0x51df4174d34c97d7}},
+      {"+slatXiQ7/2lK0BkVUI1qzNxOOLP3I1iK6OfHaoxgqT63FpzbElwEXSwdsryq3UlHK0I",
+       uint64_t{0x80ce6d76f89cb57}},
+      {"64mVTbQ47dHjHlOHGS/hjJwr/"
+       "K2frCNpn87exOqMzNUVYiPKmhCbfS7vBUce5tO6Ec9osQ==",
+       uint64_t{0x20961c911965f684}},
+      {"fIsaG1r530SFrBqaDj1kqE0AJnvvK8MNEZbII2Yw1OK77v0V59xabIh0B5axaz/"
+       "+a2V5WpA=",
+       uint64_t{0x4e5b926ec83868e7}},
+      {"PGih0zDEOWCYGxuHGDFu9Ivbff/"
+       "iE7BNUq65tycTR2R76TerrXALRosnzaNYO5fjFhTi+CiS",
+       uint64_t{0x3927b30b922eecef}},
+      {"RnpA/"
+       "zJnEnnLjmICORByRVb9bCOgxF44p3VMiW10G7PvW7IhwsWajlP9kIwNA9FjAD2GoQHk2Q="
+       "=",
+       uint64_t{0xbd0291284a49b61c}},
+      {"qFklMceaTHqJpy2qavJE+EVBiNFOi6OxjOA3LeIcBop1K7w8xQi3TrDk+"
+       "BrWPRIbfprszSaPfrI=",
+       uint64_t{0x73a77c575bcc956}},
+      {"cLbfUtLl3EcQmITWoTskUR8da/VafRDYF/ylPYwk7/"
+       "zazk6ssyrzxMN3mmSyvrXR2yDGNZ3WDrTT",
+       uint64_t{0x766a0e2ade6d09a6}},
+      {"s/"
+       "Jf1+"
+       "FbsbCpXWPTUSeWyMH6e4CvTFvPE5Fs6Z8hvFITGyr0dtukHzkI84oviVLxhM1xMxrMAy1db"
+       "w==",
+       uint64_t{0x2599f4f905115869}},
+      {"FvyQ00+j7nmYZVQ8hI1Edxd0AWplhTfWuFGiu34AK5X8u2hLX1bE97sZM0CmeLe+"
+       "7LgoUT1fJ/axybE=",
+       uint64_t{0xd8256e5444d21e53}},
+      {"L8ncxMaYLBH3g9buPu8hfpWZNlOF7nvWLNv9IozH07uQsIBWSKxoPy8+"
+       "LW4tTuzC6CIWbRGRRD1sQV/4",
+       uint64_t{0xf664a91333fb8dfd}},
+      {"CDK0meI07yrgV2kQlZZ+"
+       "wuVqhc2NmzqeLH7bmcA6kchsRWFPeVF5Wqjjaj556ABeUoUr3yBmfU3kWOakkg==",
+       uint64_t{0x9625b859be372cd1}},
+      {"d23/vc5ONh/"
+       "HkMiq+gYk4gaCNYyuFKwUkvn46t+dfVcKfBTYykr4kdvAPNXGYLjM4u1YkAEFpJP+"
+       "nX7eOvs=",
+       uint64_t{0x7b99940782e29898}},
+      {"NUR3SRxBkxTSbtQORJpu/GdR6b/h6sSGfsMj/KFd99ahbh+9r7LSgSGmkGVB/"
+       "mGoT0pnMTQst7Lv2q6QN6Vm",
+       uint64_t{0x4fe12fa5383b51a8}},
+      {"2BOFlcI3Z0RYDtS9T9Ie9yJoXlOdigpPeeT+CRujb/"
+       "O39Ih5LPC9hP6RQk1kYESGyaLZZi3jtabHs7DiVx/VDg==",
+       uint64_t{0xe2ccb09ac0f5b4b6}},
+      {"FF2HQE1FxEvWBpg6Z9zAMH+Zlqx8S1JD/"
+       "wIlViL6ZDZY63alMDrxB0GJQahmAtjlm26RGLnjW7jmgQ4Ie3I+014=",
+       uint64_t{0x7d0a37adbd7b753b}},
+      {"tHmO7mqVL/PX11nZrz50Hc+M17Poj5lpnqHkEN+4bpMx/"
+       "YGbkrGOaYjoQjgmt1X2QyypK7xClFrjeWrCMdlVYtbW",
+       uint64_t{0xd3ae96ef9f7185f2}},
+      {"/WiHi9IQcxRImsudkA/KOTqGe8/"
+       "gXkhKIHkjddv5S9hi02M049dIK3EUyAEjkjpdGLUs+BN0QzPtZqjIYPOgwsYE9g==",
+       uint64_t{0x4fb88ea63f79a0d8}},
+      {"qds+1ExSnU11L4fTSDz/QE90g4Jh6ioqSh3KDOTOAo2pQGL1k/"
+       "9CCC7J23YF27dUTzrWsCQA2m4epXoCc3yPHb3xElA=",
+       uint64_t{0xed564e259bb5ebe9}},
+      {"8FVYHx40lSQPTHheh08Oq0/"
+       "pGm2OlG8BEf8ezvAxHuGGdgCkqpXIueJBF2mQJhTfDy5NncO8ntS7vaKs7sCNdDaNGOEi",
+       uint64_t{0x3e3256b60c428000}},
+      {"4ZoEIrJtstiCkeew3oRzmyJHVt/pAs2pj0HgHFrBPztbQ10NsQ/"
+       "lM6DM439QVxpznnBSiHMgMQJhER+70l72LqFTO1JiIQ==",
+       uint64_t{0xfb05bad59ec8705}},
+      {"hQPtaYI+wJyxXgwD5n8jGIKFKaFA/"
+       "P83KqCKZfPthnjwdOFysqEOYwAaZuaaiv4cDyi9TyS8hk5cEbNP/jrI7q6pYGBLbsM=",
+       uint64_t{0xafdc251dbf97b5f8}},
+      {"S4gpMSKzMD7CWPsSfLeYyhSpfWOntyuVZdX1xSBjiGvsspwOZcxNKCRIOqAA0moUfOh3I5+"
+       "juQV4rsqYElMD/gWfDGpsWZKQ",
+       uint64_t{0x10ec9c92ddb5dcbc}},
+      {"oswxop+"
+       "bthuDLT4j0PcoSKby4LhF47ZKg8K17xxHf74UsGCzTBbOz0MM8hQEGlyqDT1iUiAYnaPaUp"
+       "L2mRK0rcIUYA4qLt5uOw==",
+       uint64_t{0x9a767d5822c7dac4}},
+      {"0II/"
+       "697p+"
+       "BtLSjxj5989OXI004TogEb94VUnDzOVSgMXie72cuYRvTFNIBgtXlKfkiUjeqVpd4a+"
+       "n5bxNOD1TGrjQtzKU5r7obo=",
+       uint64_t{0xee46254080d6e2db}},
+      {"E84YZW2qipAlMPmctrg7TKlwLZ68l4L+c0xRDUfyyFrA4MAti0q9sHq3TDFviH0Y+"
+       "Kq3tEE5srWFA8LM9oomtmvm5PYxoaarWPLc",
+       uint64_t{0xbbb669588d8bf398}},
+      {"x3pa4HIElyZG0Nj7Vdy9IdJIR4izLmypXw5PCmZB5y68QQ4uRaVVi3UthsoJROvbjDJkP2D"
+       "Q6L/eN8pFeLFzNPKBYzcmuMOb5Ull7w==",
+       uint64_t{0xdc2afaa529beef44}},
+      {"jVDKGYIuWOP/"
+       "QKLdd2wi8B2VJA8Wh0c8PwrXJVM8FOGM3voPDVPyDJOU6QsBDPseoR8uuKd19OZ/"
+       "zAvSCB+zlf6upAsBlheUKgCfKww=",
+       uint64_t{0xf1f67391d45013a8}},
+      {"mkquunhmYe1aR2wmUz4vcvLEcKBoe6H+kjUok9VUn2+eTSkWs4oDDtJvNCWtY5efJwg/"
+       "j4PgjRYWtqnrCkhaqJaEvkkOwVfgMIwF3e+d",
+       uint64_t{0x16fce2b8c65a3429}},
+      {"fRelvKYonTQ+s+rnnvQw+JzGfFoPixtna0vzcSjiDqX5s2Kg2//"
+       "UGrK+AVCyMUhO98WoB1DDbrsOYSw2QzrcPe0+3ck9sePvb+Q/IRaHbw==",
+       uint64_t{0xf4b096699f49fe67}},
+      {"DUwXFJzagljo44QeJ7/"
+       "6ZKw4QXV18lhkYT2jglMr8WB3CHUU4vdsytvw6AKv42ZcG6fRkZkq9fpnmXy6xG0aO3WPT1"
+       "eHuyFirAlkW+zKtwg=",
+       uint64_t{0xca584c4bc8198682}},
+      {"cYmZCrOOBBongNTr7e4nYn52uQUy2mfe48s50JXx2AZ6cRAt/"
+       "xRHJ5QbEoEJOeOHsJyM4nbzwFm++SlT6gFZZHJpkXJ92JkR86uS/eV1hJUR",
+       uint64_t{0xed269fc3818b6aad}},
+      {"EXeHBDfhwzAKFhsMcH9+2RHwV+mJaN01+9oacF6vgm8mCXRd6jeN9U2oAb0of5c5cO4i+"
+       "Vb/LlHZSMI490SnHU0bejhSCC2gsC5d2K30ER3iNA==",
+       uint64_t{0x33f253cbb8fe66a8}},
+      {"FzkzRYoNjkxFhZDso94IHRZaJUP61nFYrh5MwDwv9FNoJ5jyNCY/"
+       "eazPZk+tbmzDyJIGw2h3GxaWZ9bSlsol/vK98SbkMKCQ/wbfrXRLcDzdd/8=",
+       uint64_t{0xd0b76b2c1523d99c}},
+      {"Re4aXISCMlYY/XsX7zkIFR04ta03u4zkL9dVbLXMa/q6hlY/CImVIIYRN3VKP4pnd0AUr/"
+       "ugkyt36JcstAInb4h9rpAGQ7GMVOgBniiMBZ/MGU7H",
+       uint64_t{0xfd28f0811a2a237f}},
+      {"ueLyMcqJXX+MhO4UApylCN9WlTQ+"
+       "ltJmItgG7vFUtqs2qNwBMjmAvr5u0sAKd8jpzV0dDPTwchbIeAW5zbtkA2NABJV6hFM48ib"
+       "4/J3A5mseA3cS8w==",
+       uint64_t{0x6261fb136482e84}},
+      {"6Si7Yi11L+jZMkwaN+GUuzXMrlvEqviEkGOilNq0h8TdQyYKuFXzkYc/"
+       "q74gP3pVCyiwz9KpVGMM9vfnq36riMHRknkmhQutxLZs5fbmOgEO69HglCU=",
+       uint64_t{0x458efc750bca7c3a}},
+      {"Q6AbOofGuTJOegPh9Clm/"
+       "9crtUMQqylKrTc1fhfJo1tqvpXxhU4k08kntL1RG7woRnFrVh2UoMrL1kjin+s9CanT+"
+       "y4hHwLqRranl9FjvxfVKm3yvg68",
+       uint64_t{0xa7e69ff84e5e7c27}},
+      {"ieQEbIPvqY2YfIjHnqfJiO1/MIVRk0RoaG/WWi3kFrfIGiNLCczYoklgaecHMm/"
+       "1sZ96AjO+a5stQfZbJQwS7Sc1ODABEdJKcTsxeW2hbh9A6CFzpowP1A==",
+       uint64_t{0x3c59bfd0c29efe9e}},
+      {"zQUv8hFB3zh2GGl3KTvCmnfzE+"
+       "SUgQPVaSVIELFX5H9cE3FuVFGmymkPQZJLAyzC90Cmi8GqYCvPqTuAAB//"
+       "XTJxy4bCcVArgZG9zJXpjowpNBfr3ngWrSE=",
+       uint64_t{0x10befacc6afd298d}},
+      {"US4hcC1+op5JKGC7eIs8CUgInjKWKlvKQkapulxW262E/"
+       "B2ye79QxOexf188u2mFwwe3WTISJHRZzS61IwljqAWAWoBAqkUnW8SHmIDwHUP31J0p5sGd"
+       "P47L",
+       uint64_t{0x41d5320b0a38efa7}},
+      {"9bHUWFna2LNaGF6fQLlkx1Hkt24nrkLE2CmFdWgTQV3FFbUe747SSqYw6ebpTa07MWSpWRP"
+       "sHesVo2B9tqHbe7eQmqYebPDFnNqrhSdZwFm9arLQVs+7a3Ic6A==",
+       uint64_t{0x58db1c7450fe17f3}},
+      {"Kb3DpHRUPhtyqgs3RuXjzA08jGb59hjKTOeFt1qhoINfYyfTt2buKhD6YVffRCPsgK9SeqZ"
+       "qRPJSyaqsa0ovyq1WnWW8jI/NhvAkZTVHUrX2pC+cD3OPYT05Dag=",
+       uint64_t{0x6098c055a335b7a6}},
+      {"gzxyMJIPlU+bJBwhFUCHSofZ/"
+       "319LxqMoqnt3+L6h2U2+ZXJCSsYpE80xmR0Ta77Jq54o92SMH87HV8dGOaCTuAYF+"
+       "lDL42SY1P316Cl0sZTS2ow3ZqwGbcPNs/1",
+       uint64_t{0x1bbacec67845a801}},
+      {"uR7V0TW+FGVMpsifnaBAQ3IGlr1wx5sKd7TChuqRe6OvUXTlD4hKWy8S+"
+       "8yyOw8lQabism19vOQxfmocEOW/"
+       "vzY0pEa87qHrAZy4s9fH2Bltu8vaOIe+agYohhYORQ==",
+       uint64_t{0xc419cfc7442190}},
+      {"1UR5eoo2aCwhacjZHaCh9bkOsITp6QunUxHQ2SfeHv0imHetzt/"
+       "Z70mhyWZBalv6eAx+YfWKCUib2SHDtz/"
+       "A2dc3hqUWX5VfAV7FQsghPUAtu6IiRatq4YSLpDvKZBQ=",
+       uint64_t{0xc95e510d94ba270c}},
+      {"opubR7H63BH7OtY+Avd7QyQ25UZ8kLBdFDsBTwZlY6gA/"
+       "u+x+"
+       "czC9AaZMgmQrUy15DH7YMGsvdXnviTtI4eVI4aF1H9Rl3NXMKZgwFOsdTfdcZeeHVRzBBKX"
+       "8jUfh1il",
+       uint64_t{0xff1ae05c98089c3f}},
+      {"DC0kXcSXtfQ9FbSRwirIn5tgPri0sbzHSa78aDZVDUKCMaBGyFU6BmrulywYX8yzvwprdLs"
+       "oOwTWN2wMjHlPDqrvVHNEjnmufRDblW+nSS+xtKNs3N5xsxXdv6JXDrAB/Q==",
+       uint64_t{0x90c02b8dceced493}},
+      {"BXRBk+3wEP3Lpm1y75wjoz+PgB0AMzLe8tQ1AYU2/"
+       "oqrQB2YMC6W+9QDbcOfkGbeH+b7IBkt/"
+       "gwCMw2HaQsRFEsurXtcQ3YwRuPz5XNaw5NAvrNa67Fm7eRzdE1+hWLKtA8=",
+       uint64_t{0x9f8a76697ab1aa36}},
+      {"RRBSvEGYnzR9E45Aps/+WSnpCo/X7gJLO4DRnUqFrJCV/kzWlusLE/"
+       "6ZU6RoUf2ROwcgEvUiXTGjLs7ts3t9SXnJHxC1KiOzxHdYLMhVvgNd3hVSAXODpKFSkVXND"
+       "55G2L1W",
+       uint64_t{0x6ba1bf3d811a531d}},
+      {"jeh6Qazxmdi57pa9S3XSnnZFIRrnc6s8QLrah5OX3SB/V2ErSPoEAumavzQPkdKF1/"
+       "SfvmdL+qgF1C+Yawy562QaFqwVGq7+tW0yxP8FStb56ZRgNI4IOmI30s1Ei7iops9Uuw==",
+       uint64_t{0x6a418974109c67b4}},
+      {"6QO5nnDrY2/"
+       "wrUXpltlKy2dSBcmK15fOY092CR7KxAjNfaY+"
+       "aAmtWbbzQk3MjBg03x39afSUN1fkrWACdyQKRaGxgwq6MGNxI6W+8DLWJBHzIXrntrE/"
+       "ml6fnNXEpxplWJ1vEs4=",
+       uint64_t{0x8472f1c2b3d230a3}},
+      {"0oPxeEHhqhcFuwonNfLd5jF3RNATGZS6NPoS0WklnzyokbTqcl4BeBkMn07+fDQv83j/"
+       "BpGUwcWO05f3+DYzocfnizpFjLJemFGsls3gxcBYxcbqWYev51tG3lN9EvRE+X9+Pwww",
+       uint64_t{0x5e06068f884e73a7}},
+      {"naSBSjtOKgAOg8XVbR5cHAW3Y+QL4Pb/JO9/"
+       "oy6L08wvVRZqo0BrssMwhzBP401Um7A4ppAupbQeJFdMrysY34AuSSNvtNUy5VxjNECwiNt"
+       "gwYHw7yakDUv8WvonctmnoSPKENegQg==",
+       uint64_t{0x55290b1a8f170f59}},
+      {"vPyl8DxVeRe1OpilKb9KNwpGkQRtA94UpAHetNh+"
+       "95V7nIW38v7PpzhnTWIml5kw3So1Si0TXtIUPIbsu32BNhoH7QwFvLM+"
+       "JACgSpc5e3RjsL6Qwxxi11npwxRmRUqATDeMUfRAjxg=",
+       uint64_t{0x5501cfd83dfe706a}},
+      {"QC9i2GjdTMuNC1xQJ74ngKfrlA4w3o58FhvNCltdIpuMhHP1YsDA78scQPLbZ3OCUgeQguY"
+       "f/vw6zAaVKSgwtaykqg5ka/4vhz4hYqWU5ficdXqClHl+zkWEY26slCNYOM5nnDlly8Cj",
+       uint64_t{0xe43ed13d13a66990}},
+      {"7CNIgQhAHX27nxI0HeB5oUTnTdgKpRDYDKwRcXfSFGP1XeT9nQF6WKCMjL1tBV6x7KuJ91G"
+       "Zz11F4c+8s+MfqEAEpd4FHzamrMNjGcjCyrVtU6y+7HscMVzr7Q/"
+       "ODLcPEFztFnwjvCjmHw==",
+       uint64_t{0xdf43bc375cf5283f}},
+      {"Qa/hC2RPXhANSospe+gUaPfjdK/yhQvfm4cCV6/pdvCYWPv8p1kMtKOX3h5/"
+       "8oZ31fsmx4Axphu5qXJokuhZKkBUJueuMpxRyXpwSWz2wELx5glxF7CM0Fn+"
+       "OevnkhUn5jsPlG2r5jYlVn8=",
+       uint64_t{0x8112b806d288d7b5}},
+      {"kUw/0z4l3a89jTwN5jpG0SHY5km/"
+       "IVhTjgM5xCiPRLncg40aqWrJ5vcF891AOq5hEpSq0bUCJUMFXgct7kvnys905HjerV7Vs1G"
+       "y84tgVJ70/2+pAZTsB/PzNOE/G6sOj4+GbTzkQu819OLB",
+       uint64_t{0xd52a18abb001cb46}},
+      {"VDdfSDbO8Tdj3T5W0XM3EI7iHh5xpIutiM6dvcJ/fhe23V/srFEkDy5iZf/"
+       "VnA9kfi2C79ENnFnbOReeuZW1b3MUXB9lgC6U4pOTuC+"
+       "jHK3Qnpyiqzj7h3ISJSuo2pob7vY6VHZo6Fn7exEqHg==",
+       uint64_t{0xe12b76a2433a1236}},
+      {"Ldfvy3ORdquM/R2fIkhH/ONi69mcP1AEJ6n/"
+       "oropwecAsLJzQSgezSY8bEiEs0VnFTBBsW+RtZY6tDj03fnb3amNUOq1b7jbqyQkL9hpl+"
+       "2Z2J8IaVSeownWl+bQcsR5/xRktIMckC5AtF4YHfU=",
+       uint64_t{0x175bf7319cf1fa00}},
+      {"BrbNpb42+"
+       "VzZAjJw6QLirXzhweCVRfwlczzZ0VX2xluskwBqyfnGovz5EuX79JJ31VNXa5hTkAyQat3l"
+       "YKRADTdAdwE5PqM1N7YaMqqsqoAAAeuYVXuk5eWCykYmClNdSspegwgCuT+403JigBzi",
+       uint64_t{0xd63d57b3f67525ae}},
+      {"gB3NGHJJvVcuPyF0ZSvHwnWSIfmaI7La24VMPQVoIIWF7Z74NltPZZpx2f+cocESM+"
+       "ILzQW9p+BC8x5IWz7N4Str2WLGKMdgmaBfNkEhSHQDU0IJEOnpUt0HmjhFaBlx0/"
+       "LTmhua+rQ6Wup8ezLwfg==",
+       uint64_t{0x933faea858832b73}},
+      {"hTKHlRxx6Pl4gjG+6ksvvj0CWFicUg3WrPdSJypDpq91LUWRni2KF6+"
+       "81ZoHBFhEBrCdogKqeK+hy9bLDnx7g6rAFUjtn1+cWzQ2YjiOpz4+"
+       "ROBB7lnwjyTGWzJD1rXtlso1g2qVH8XJVigC5M9AIxM=",
+       uint64_t{0x53d061e5f8e7c04f}},
+      {"IWQBelSQnhrr0F3BhUpXUIDauhX6f95Qp+A0diFXiUK7irwPG1oqBiqHyK/SH/"
+       "9S+"
+       "rln9DlFROAmeFdH0OCJi2tFm4afxYzJTFR4HnR4cG4x12JqHaZLQx6iiu6CE3rtWBVz99oA"
+       "wCZUOEXIsLU24o2Y",
+       uint64_t{0xdb4124556dd515e0}},
+      {"TKo+l+"
+       "1dOXdLvIrFqeLaHdm0HZnbcdEgOoLVcGRiCbAMR0j5pIFw8D36tefckAS1RCFOH5IgP8yiF"
+       "T0Gd0a2hI3+"
+       "fTKA7iK96NekxWeoeqzJyctc6QsoiyBlkZerRxs5RplrxoeNg29kKDTM0K94mnhD9g==",
+       uint64_t{0x4fb31a0dd681ee71}},
+      {"YU4e7G6EfQYvxCFoCrrT0EFgVLHFfOWRTJQJ5gxM3G2b+"
+       "1kJf9YPrpsxF6Xr6nYtS8reEEbDoZJYqnlk9lXSkVArm88Cqn6d25VCx3+"
+       "49MqC0trIlXtb7SXUUhwpJK16T0hJUfPH7s5cMZXc6YmmbFuBNPE=",
+       uint64_t{0x27cc72eefa138e4c}},
+      {"/I/"
+       "eImMwPo1U6wekNFD1Jxjk9XQVi1D+"
+       "FPdqcHifYXQuP5aScNQfxMAmaPR2XhuOQhADV5tTVbBKwCDCX4E3jcDNHzCiPvViZF1W27t"
+       "xaf2BbFQdwKrNCmrtzcluBFYu0XZfc7RU1RmxK/RtnF1qHsq/O4pp",
+       uint64_t{0x44bc2dfba4bd3ced}},
+      {"CJTT9WGcY2XykTdo8KodRIA29qsqY0iHzWZRjKHb9alwyJ7RZAE3V5Juv4MY3MeYEr1EPCC"
+       "MxO7yFXqT8XA8YTjaMp3bafRt17Pw8JC4iKJ1zN+WWKOESrj+"
+       "3aluGQqn8z1EzqY4PH7rLG575PYeWsP98BugdA==",
+       uint64_t{0x242da1e3a439bed8}},
+      {"ZlhyQwLhXQyIUEnMH/"
+       "AEW27vh9xrbNKJxpWGtrEmKhd+nFqAfbeNBQjW0SfG1YI0xQkQMHXjuTt4P/"
+       "EpZRtA47ibZDVS8TtaxwyBjuIDwqcN09eCtpC+Ls+"
+       "vWDTLmBeDM3u4hmzz4DQAYsLiZYSJcldg9Q3wszw=",
+       uint64_t{0xdc559c746e35c139}},
+      {"v2KU8y0sCrBghmnm8lzGJlwo6D6ObccAxCf10heoDtYLosk4ztTpLlpSFEyu23MLA1tJkcg"
+       "Rko04h19QMG0mOw/"
+       "wc93EXAweriBqXfvdaP85sZABwiKO+6rtS9pacRVpYYhHJeVTQ5NzrvBvi1huxAr+"
+       "xswhVMfL",
+       uint64_t{0xd0b0350275b9989}},
+      {"QhKlnIS6BuVCTQsnoE67E/"
+       "yrgogE8EwO7xLaEGei26m0gEU4OksefJgppDh3X0x0Cs78Dr9IHK5b977CmZlrTRmwhlP8p"
+       "M+UzXPNRNIZuN3ntOum/QhUWP8SGpirheXENWsXMQ/"
+       "nxtxakyEtrNkKk471Oov9juP8oQ==",
+       uint64_t{0xb04489e41d17730c}},
+      {"/ZRMgnoRt+Uo6fUPr9FqQvKX7syhgVqWu+"
+       "WUSsiQ68UlN0efSP6Eced5gJZL6tg9gcYJIkhjuQNITU0Q3TjVAnAcobgbJikCn6qZ6pRxK"
+       "BY4MTiAlfGD3T7R7hwJwx554MAy++Zb/YUFlnCaCJiwQMnowF7aQzwYFCo=",
+       uint64_t{0x2217285eb4572156}},
+      {"NB7tU5fNE8nI+SXGfipc7sRkhnSkUF1krjeo6k+8FITaAtdyz+"
+       "o7mONgXmGLulBPH9bEwyYhKNVY0L+njNQrZ9YC2aXsFD3PdZsxAFaBT3VXEzh+"
+       "NGBTjDASNL3mXyS8Yv1iThGfHoY7T4aR0NYGJ+k+pR6f+KrPC96M",
+       uint64_t{0x12c2e8e68aede73b}},
+      {"8T6wrqCtEO6/rwxF6lvMeyuigVOLwPipX/FULvwyu+1wa5sQGav/"
+       "2FsLHUVn6cGSi0LlFwLewGHPFJDLR0u4t7ZUyM//"
+       "x6da0sWgOa5hzDqjsVGmjxEHXiaXKW3i4iSZNuxoNbMQkIbVML+"
+       "DkYu9ND0O2swg4itGeVSzXA==",
+       uint64_t{0x4d612125bdc4fd00}},
+      {"Ntf1bMRdondtMv1CYr3G80iDJ4WSAlKy5H34XdGruQiCrnRGDBa+"
+       "eUi7vKp4gp3BBcVGl8eYSasVQQjn7MLvb3BjtXx6c/"
+       "bCL7JtpzQKaDnPr9GWRxpBXVxKREgMM7d8lm35EODv0w+"
+       "hQLfVSh8OGs7fsBb68nNWPLeeSOo=",
+       uint64_t{0x81826b553954464e}},
+      {"VsSAw72Ro6xks02kaiLuiTEIWBC5bgqr4WDnmP8vglXzAhixk7td926rm9jNimL+"
+       "kroPSygZ9gl63aF5DCPOACXmsbmhDrAQuUzoh9ZKhWgElLQsrqo1KIjWoZT5b5QfVUXY9lS"
+       "IBg3U75SqORoTPq7HalxxoIT5diWOcJQi",
+       uint64_t{0xc2e5d345dc0ddd2d}},
+      {"j+loZ+C87+"
+       "bJxNVebg94gU0mSLeDulcHs84tQT7BZM2rzDSLiCNxUedHr1ZWJ9ejTiBa0dqy2I2ABc++"
+       "xzOLcv+//YfibtjKtYggC6/3rv0XCc7xu6d/"
+       "O6xO+XOBhOWAQ+IHJVHf7wZnDxIXB8AUHsnjEISKj7823biqXjyP3g==",
+       uint64_t{0x3da6830a9e32631e}},
+      {"f3LlpcPElMkspNtDq5xXyWU62erEaKn7RWKlo540gR6mZsNpK1czV/"
+       "sOmqaq8XAQLEn68LKj6/"
+       "cFkJukxRzCa4OF1a7cCAXYFp9+wZDu0bw4y63qbpjhdCl8GO6Z2lkcXy7KOzbPE01ukg7+"
+       "gN+7uKpoohgAhIwpAKQXmX5xtd0=",
+       uint64_t{0xc9ae5c8759b4877a}},
+  };
+
+#if defined(ABSL_IS_BIG_ENDIAN)
+  constexpr uint64_t kGolden[kNumGoldenOutputs] = {
+      0xe5a40d39ab796423, 0x1766974bf7527d81, 0x5c3bbbe230db17a8,
+      0xa6630143a7e6aa6f, 0x17645cb7318b86b,  0x218b175f30ba61f8,
+      0xa6564b468248c683, 0xef192f401b116e1c, 0xbe8dc0c54617639d,
+      0xe7b01610fc22dbb8, 0x99d9f694404af913, 0xf4eecd37464b45c5,
+      0x7d2c653d63596d9b, 0x3f15c8544ec5393a, 0x6b9dc0c1704f796c,
+      0xf1ded7a7eae5ed5a, 0x2db2fd7c6dd4641b, 0x151ca2d3d4cd33ab,
+      0xa5af5994ac2ccd64, 0x2b2a4ca3191d2fce, 0xf89e68c9364e7c05,
+      0x71724c70b799c21,  0x70536fabfd157369, 0xdee92794c3c3082b,
+      0xac033a6743d3b3eb, 0xed2956b506cd5151, 0xbd669644755264b6,
+      0x6ab1ff5d5f549a63, 0xf6bd551a2e3e04e,  0x7b5a8cef6875ea73,
+      0x22bccf4d4db0a91c, 0x4f2bc07754c7c7eb, 0xfb6b8342a86725db,
+      0x13a1a0d4c5854da,  0x5f6e44655f7dedac, 0x54a9198dff2bdf85,
+      0xdb17e6915d4e4042, 0xa69926cf5c3b89f,  0xf77f031bfd74c096,
+      0x1d6f916fdd50ec3c, 0x334ac76013ade393, 0x99370f899111de15,
+      0x352457a03ada6de,  0x341974d4f42d854d, 0xda89ab02872aeb5,
+      0x6ec2b74e143b10d9, 0x6f284c0b5cd60522, 0xf9670de353438f88,
+      0xde920913adf0a2b4, 0xb7a07d7c0c17a8ec, 0x879a69f558ba3a98,
+      0x360cf6d802df20f9, 0x53530f8046673738, 0xbd8f5f2bcf35e483,
+      0x3f171f047144b983, 0x644d04e820823465, 0x50e44773a20b2702,
+      0xe584ed4c05c745dd, 0x9a825c85b95ab6c0, 0xbce2931deb74e775,
+      0x10468e9e705c7cfe, 0x12e01de3104141e2, 0x5c11ae2ee3713abd,
+      0x6ac5ffb0860319e6, 0xc1e6da1849d30fc9, 0xa0e4d247a458b447,
+      0x4530d4615c32b89b, 0x116aa09107a76505, 0xf941339d00d9bb73,
+      0x573a0fc1615afb33, 0xa975c81dc868b258, 0x3ab2c5250ab54bda,
+      0x37f99f208a3e3b11, 0x4b49b0ff706689d,  0x30bafa0b8f0a87fe,
+      0xea6787a65cc20cdd, 0x55861729f1fc3ab8, 0xea38e009c5be9b72,
+      0xcb8522cba33c3c66, 0x352e77653fe306f3, 0xe0bb760793bac064,
+      0xf66ec59322662956, 0x637aa320455d56f8, 0x46ee546be5824a89,
+      0x9e6842421e83d8a4, 0xf98ac2bc96b9fb8c, 0xf2c1002fd9a70b99,
+      0x4c2b62b1e39e9405, 0x3248555fa3ade9c4, 0xd4d04c37f6417c21,
+      0xf40cd506b1bf5653, 0x6c45d6005c760d2f, 0x61d88a7e61ff0d7e,
+      0x131591e8a53cc967, 0xdae85cb9bc29bab6, 0xe98835334905e626,
+      0x7cce50a2b66b8754, 0x5b0b3d0c5ac498ae, 0xd35a218c974d1756,
+      0xfce436ddc1d003c,  0xd183901de90bb741, 0x9378f8f34974a66,
+      0x21f11ae0a0402368, 0xf2fbd7c94ef89cb6, 0xc329c69d0f0d080b,
+      0xf2841cba16216a61, 0x47aba97b44916df1, 0x724d4e00a8019fcf,
+      0x2df9005c2a728d63, 0xc788892a1a5d7515, 0x9e993a65f9df0480,
+      0x76876721ff49f969, 0xbe7a796cfba15bf5, 0xa4c8bd54586f5488,
+      0xb390a325275501ab, 0x893f11317427ccf1, 0x92f2bb57da5695b9,
+      0x30985b90da88269f, 0x2c690e268e086de8, 0x1c02df6097997196,
+      0x1f9778f8bbdf6455, 0x7d57378c7bf8416d, 0xba8582a5f8d84d38,
+      0xe8ca43b85050be4e, 0x5048cf6bed8a5d9f, 0xfbc5ba80917d0ea4,
+      0x8011026525bf1691, 0x26b8dc6aed9fb50d, 0x191f5bfee77c1fe3,
+      0xdd497891465a2cc1, 0x6f1fe8c57a33072e, 0x2c9f4ec078c460c0,
+      0x9a725bde8f6a1437, 0x6ce545fa3ef61e4d,
+  };
+#elif defined(__aarch64__)
+  constexpr uint64_t kGolden[kNumGoldenOutputs] = {
+      0x45c0aadee165dcbe, 0x25ed8587f6f20d06, 0x5f23ae668ce7926d,
+      0xfef74d1da0846719, 0x54478408e68cb7d4, 0xee27ddaf88c6fe68,
+      0xb7ac7031e81867ca, 0xf1168f818ec6c36d, 0x1dd0b734a83b019a,
+      0xd6ae30d4142b54fe, 0xcd860c721ccb80fb, 0x068acf8493794756,
+      0xd4ada0be58681307, 0x13ffe0f64ca540ed, 0xffc1d7a3401aec02,
+      0xd81c4d865cf95fb9, 0x1dd0793acede62e0, 0xa6722abbca8fe4cf,
+      0x5453d3e4111a7e40, 0xf29b3e3204c9dcd2, 0x23be2980e43117f7,
+      0x74e2ccbc286f08eb, 0x19ef7c0f9496003a, 0xbfbf1c3e49b27987,
+      0x6e6c179eb4a82c70, 0x07f4e184216bc4fc, 0xf17fbc4254927554,
+      0xe57696b70a45b1b6, 0x6d3b144631b320e8, 0xccf8729792c75a2d,
+      0xe832495b41fa980b, 0x5c96cfdc7b227d34, 0xc4dca234ef4e43f4,
+      0x5fc801abf9abe307, 0xe41e3c5076d88f4d, 0x522346200ddec3c3,
+      0x72bed1946fd7aaa4, 0x0ac1f84dcc335f96, 0x3af78db5e0a47670,
+      0x6100ebf1481f1caf, 0xf5fd10037fc651a3, 0xa01227d8944665f3,
+      0x7217681c4bbc9420, 0x4adee538e3eb10d1, 0x35e1761ad96de9a7,
+      0x8b370aef9613bfba, 0x824506f749eeaf59, 0x85e805fa04423991,
+      0xb61e9c33283c3de7, 0xc79721bbcb039ed6, 0x04e1c19a3a1e6639,
+      0x6aaf6346b68dd638, 0x601a4b496be6d0c4, 0x3ece355f91c41787,
+      0xd2fc8998448d7888, 0xd7529804f843efa9, 0xabdcc38a288536aa,
+      0xdd323e48a9718648, 0x2090279c0030a52a, 0xe2f90faca88a3cd1,
+      0x3e0c4e92fc50e4aa, 0xa26d308798e801dd, 0x432eefeedee8c02e,
+      0xca4ce494614b77df, 0xbba82911e838066d, 0x4b00821016adee4b,
+      0x4cf6e526dfb5a20f, 0x5b8466495142cba2, 0xe28ac1406e88a68c,
+      0x8511e5f9d3100999, 0x05acbfe02798890b, 0x74c249c7ce4a8425,
+      0xdbe7468d09bc34bc, 0x11079ab10e3b9b58, 0xb7788dec9032035a,
+      0xb7e8daa786513f80, 0x34c3288831f46b45, 0x014cce5f0c21ecc6,
+      0xc6a8f7b024551a28, 0x49784e902e207fd8, 0x4720d32af0b55158,
+      0x8df3ec5de0c1da00, 0xf4db677b2c9e6853, 0xaa419abea78d312d,
+      0x181e0f91bd757443, 0xa8c45136fada083b, 0x91303b93f5f0582c,
+      0x883b95c6ddc62a08, 0x93186a8875fe952b, 0xd94f533928e957e2,
+      0x6ba343003e10c172, 0xc8623b620c715d6a, 0x8ca0c512e180e244,
+      0xdc9b74c2536b6216, 0x8eb5fdc61b295d96, 0x2ad83966b37c95ba,
+      0xb90bf154ac5edec9, 0x902cf847b326cfb3, 0x7b02d0c0ca7808ca,
+      0x492f310d003ea15f, 0x3eb6497a47c95990, 0x5d46b0ced31428b7,
+      0x081afa67d1986157, 0x043482ec286b20eb, 0xc103c8f18c1a2a53,
+      0xe8e9995a81481e83, 0x6bb3295822bc90b5, 0xeec75297a3fa5672,
+      0x591c8440c4857412, 0x74947f455aaf24ad, 0xcf0e571586ec77a9,
+      0x0c2553ea8c0400ad, 0x380219118066255f, 0x7595adb88b15ebe2,
+      0xb33c00696c64ae23, 0xa143516ddd7c9857, 0x39179af229248d26,
+      0x65d387a6f2ee2079, 0x89f8a9b21cd2f195, 0xbfef032d25df92e6,
+      0x6b7e18a36c69da71, 0x4b3b15f6c28974e6, 0x032a75917f6c544c,
+      0xe3b97ecca6d287cd, 0xa4a563110d3cda81, 0x35e09e8134f4e7f1,
+      0xc9419dd03a9a390e, 0x7b86fae9000fd329, 0x1e044f8d54fe74c3,
+      0x9c4991d7a47e9666, 0xfb485f3a1df4fdb6, 0xb11519969eeb94ff,
+      0x3224ea1c44caeb8d, 0x86570bbd7cc6b80d,
+  };
+#else
+  constexpr uint64_t kGolden[kNumGoldenOutputs] = {
+      0xe5a40d39ab796423, 0x1766974bf7527d81, 0x5c3bbbe230db17a8,
+      0xa6630143a7e6aa6f, 0x8787cb2d04b0c984, 0x33603654ff574ac2,
+      0xa6564b468248c683, 0xef192f401b116e1c, 0xbe8dc0c54617639d,
+      0x93d7f665b5521c8e, 0x646d70bb42445f28, 0x96a7b1e3cc9bd426,
+      0x76020289ab0790c4, 0x39f842e4133b9b44, 0x2b8d7047be4bcaab,
+      0x99628abef6716a97, 0x4432e02ba42b2740, 0x74d810efcad7918a,
+      0x88c84e986002507f, 0x4f99acf193cf39b9, 0xd90e7a3655891e37,
+      0x3bb378b1d4df8fcf, 0xf78e94045c052d47, 0x26da0b2130da6b40,
+      0x30b4d426af8c6986, 0x5413b4aaf3baaeae, 0x756ab265370a1597,
+      0xdaf5f4b7d09814fb, 0x8f874ae37742b75e, 0x8fecd03956121ce8,
+      0x229c292ea7a08285, 0x0bb4bf0692d14bae, 0x207b24ca3bdac1db,
+      0x64f6cd6745d3825b, 0xa2b2e1656b58df1e, 0x0d01d30d9ee7a148,
+      0x1cb4cd00ab804e3b, 0x4697f2637fd90999, 0x8383a756b5688c07,
+      0x695c29cb3696a975, 0xda2e5a5a5e971521, 0x7935d4befa056b2b,
+      0x38dd541ca95420fe, 0xcc06c7a4963f967f, 0xbf0f6f66e232fb20,
+      0xf7efb32d373fe71a, 0xe2e64634b1c12660, 0x285b8fd1638e306d,
+      0x658e8a4e3b714d6c, 0xf391fb968e0eb398, 0x744a9ea0cc144bf2,
+      0x12636f2be11012f1, 0x29c57de825948f80, 0x58c6f99ab0d1c021,
+      0x13e7b5a7b82fe3bb, 0x10fbc87901e02b63, 0xa24c9184901b748b,
+      0xcac4fd4c5080e581, 0xc38bdb7483ba68e1, 0xdb2a8069b2ceaffa,
+      0xdf9fe91d0d1c7887, 0xe83f49e96e2e6a08, 0x0c69e61b62ca2b62,
+      0xb4a4f3f85f8298fe, 0x167a1b39e1e95f41, 0xf8a2a5649855ee41,
+      0x27992565b595c498, 0x3e08cca5b71f9346, 0xad406b10c770a6d2,
+      0xd1713ce6e552bcf2, 0x753b287194c73ad3, 0x5ae41a95f600af1c,
+      0x4a61163b86a8bb4c, 0x42eeaa79e760c7e4, 0x698df622ef465b0a,
+      0x157583111e1a6026, 0xaa1388f078e793e0, 0xf10d68d0f3309360,
+      0x2af056184457a3de, 0x6d0058e1590b2489, 0x638f287f68817f12,
+      0xc46b71fecefd5467, 0x2c8e94679d964e0a, 0x8612b797ce22503a,
+      0x59f929babfba7170, 0x9527556923fb49a0, 0x1039ab644f5e150b,
+      0x7816c83f3aa05e6d, 0xf51d2f564518c619, 0x67d494cff03ac004,
+      0x2802d636ced1cfbb, 0xf64e20bad771cb12, 0x0b9a6cf84a83e15e,
+      0x8da6630319609301, 0x40946a86e2a996f3, 0xcab7f5997953fa76,
+      0x39129ca0e04fc465, 0x5238221fd685e1b8, 0x175130c407dbcaab,
+      0x02f20e7536c0b0df, 0x2742cb488a04ad56, 0xd6afb593879ff93b,
+      0xf50ad64caac0ca7f, 0x2ade95c4261364ae, 0x5c4f3299faacd07a,
+      0xfffe3bff0ae5e9bc, 0x1db785c0005166e4, 0xea000d962ad18418,
+      0xe42aef38359362d9, 0xc8e95657348a3891, 0xc162eca864f238c6,
+      0xbe1fb373e20579ad, 0x628a1d4f40aa6ffd, 0xa87bdb7456340f90,
+      0x5960ef3ba982c801, 0x5026586df9a431ec, 0xfe4b8a20fdf0840b,
+      0xdcb761867da7072f, 0xc10d4653667275b7, 0x727720deec13110b,
+      0x710b009662858dc9, 0xfbf8f7a3ecac1eb7, 0xb6fc4fcd0722e3df,
+      0x7cb86dcc55104aac, 0x19e71e9b45c3a51e, 0x51de38573c2bea48,
+      0xa73ab6996d6df158, 0x55ef2b8c930817b2, 0xb2850bf5fae87157,
+      0xecf3de1acd04651f, 0xcc0a40552559ff32, 0xc385c374f20315b1,
+      0xb90208a4c7234183, 0x58aa1ca7a4c075d9,
+  };
+#endif
+
+#if UPDATE_GOLDEN
+  (void)kGolden;  // Silence warning.
+  for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
+    std::string str;
+    ASSERT_TRUE(absl::Base64Unescape(cases[i].base64_data, &str));
+    uint64_t h = absl::hash_internal::LowLevelHash(str.data(), str.size(),
+                                                   cases[i].seed, kSalt);
+    printf("0x%016" PRIx64 ", ", h);
+    if (i % 3 == 2) {
+      printf("\n");
+    }
+  }
+  printf("\n\n\n");
+  EXPECT_FALSE(true);
+#else
+  for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
+    SCOPED_TRACE(::testing::Message()
+                 << "i = " << i << "; input = " << cases[i].base64_data);
+    std::string str;
+    ASSERT_TRUE(absl::Base64Unescape(cases[i].base64_data, &str));
+    EXPECT_EQ(absl::hash_internal::LowLevelHash(str.data(), str.size(),
+                                                cases[i].seed, kSalt),
+              kGolden[i]);
+  }
+#endif
+}
+
+}  // namespace
diff --git a/absl/hash/internal/wyhash.h b/absl/hash/internal/wyhash.h
deleted file mode 100644
index 4aff4e9..0000000
--- a/absl/hash/internal/wyhash.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2020 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.
-//
-// This file provides the Google-internal implementation of the Wyhash
-// algorithm.
-//
-// Wyhash is a fast hash function for hash tables, the fastest we've currently
-// (late 2020) found that passes the SMHasher tests. The algorithm relies on
-// intrinsic 128-bit multiplication for speed. This is not meant to be secure -
-// just fast.
-
-#ifndef ABSL_HASH_INTERNAL_WYHASH_H_
-#define ABSL_HASH_INTERNAL_WYHASH_H_
-
-#include <stdint.h>
-#include <stdlib.h>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace hash_internal {
-
-// Hash function for a byte array. A 64-bit seed and a set of five 64-bit
-// integers are hashed into the result.
-//
-// To allow all hashable types (including string_view and Span) to depend on
-// this algoritm, we keep the API low-level, with as few dependencies as
-// possible.
-uint64_t Wyhash(const void* data, size_t len, uint64_t seed,
-                const uint64_t salt[5]);
-
-}  // namespace hash_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_HASH_INTERNAL_WYHASH_H_
diff --git a/absl/hash/internal/wyhash_test.cc b/absl/hash/internal/wyhash_test.cc
deleted file mode 100644
index 9fb06d2..0000000
--- a/absl/hash/internal/wyhash_test.cc
+++ /dev/null
@@ -1,486 +0,0 @@
-// Copyright 2020 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/hash/internal/wyhash.h"
-
-#include "absl/strings/escaping.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-namespace {
-
-static const uint64_t kCurrentSeed = 0;
-static const uint64_t kSalt[5] = {0xa0761d6478bd642f, 0xe7037ed1a0b428dbl,
-                                  0x8ebc6af09c88c6e3, 0x589965cc75374cc3l,
-                                  0x1d8e4e27c47d124f};
-
-// Note: We don't account for endianness, so the values here are only correct if
-// you're also running on a little endian platform.
-
-TEST(WyhashTest, EmptyString) {
-  const std::string s = "";
-  EXPECT_EQ(
-      absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt),
-      4808886099364463827);
-}
-
-TEST(WyhashTest, Spaces) {
-  const std::string s = "   ";
-  EXPECT_EQ(
-      absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt),
-      1686201463024549249);
-}
-
-TEST(WyhashTest, RepeatingString) {
-  const std::string s = "aaaa";
-  EXPECT_EQ(
-      absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt),
-      6646112255271966632);
-}
-
-TEST(WyhashTest, HexString) {
-  const std::string small = "\x01\x02\x03";
-  const std::string med = "\x01\x02\x03\x04";
-
-  EXPECT_EQ(absl::hash_internal::Wyhash(small.c_str(), small.length(),
-                                        kCurrentSeed, kSalt),
-            11989428023081740911ULL);
-  EXPECT_EQ(absl::hash_internal::Wyhash(med.c_str(), med.length(), kCurrentSeed,
-                                        kSalt),
-            9765997711188871556ULL);
-}
-
-TEST(WyhashTest, Words) {
-  const std::string s = "third_party|wyhash|64";
-  EXPECT_EQ(
-      absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt),
-      3702018632387611330);
-}
-
-TEST(WyhashTest, LongString) {
-  const std::string s =
-      "AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz"
-      "0123456789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOp"
-      "QrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEf"
-      "GhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz012345"
-      "6789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789";
-
-  EXPECT_EQ(
-      absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt),
-      9245411362605796064ULL);
-}
-
-TEST(WyhashTest, BigReference) {
-  struct ExpectedResult {
-    absl::string_view base64_data;
-    uint64_t seed;
-    uint64_t hash;
-  } expected_results[] = {
-      {"", uint64_t{0xec42b7ab404b8acb}, uint64_t{0xe5a40d39ab796423}},
-      {"Zw==", uint64_t{0xeeee074043a3ee0f}, uint64_t{0xa6564b468248c683}},
-      {"xmk=", uint64_t{0x857902089c393de}, uint64_t{0xef192f401b116e1c}},
-      {"c1H/", uint64_t{0x993df040024ca3af}, uint64_t{0xbe8dc0c54617639d}},
-      {"SuwpzQ==", uint64_t{0xc4e4c2acea740e96}, uint64_t{0x93d7f665b5521c8e}},
-      {"uqvy++M=", uint64_t{0x6a214b3db872d0cf}, uint64_t{0x646d70bb42445f28}},
-      {"RnzCVPgb", uint64_t{0x44343db6a89dba4d}, uint64_t{0x96a7b1e3cc9bd426}},
-      {"6OeNdlouYw==", uint64_t{0x77b5d6d1ae1dd483},
-       uint64_t{0x76020289ab0790c4}},
-      {"M5/JmmYyDbc=", uint64_t{0x89ab8ecb44d221f1},
-       uint64_t{0x39f842e4133b9b44}},
-      {"MVijWiVdBRdY", uint64_t{0x60244b17577ca81b},
-       uint64_t{0x2b8d7047be4bcaab}},
-      {"6V7Uq7LNxpu0VA==", uint64_t{0x59a08dcee0717067},
-       uint64_t{0x99628abef6716a97}},
-      {"EQ6CdEEhPdyHcOk=", uint64_t{0xf5f20db3ade57396},
-       uint64_t{0x4432e02ba42b2740}},
-      {"PqFB4fxnPgF+l+rc", uint64_t{0xbf8dee0751ad3efb},
-       uint64_t{0x74d810efcad7918a}},
-      {"a5aPOFwq7LA7+zKvPA==", uint64_t{0x6b7a06b268d63e30},
-       uint64_t{0x88c84e986002507f}},
-      {"VOwY21wCGv5D+/qqOvs=", uint64_t{0xb8c37f0ae0f54c82},
-       uint64_t{0x4f99acf193cf39b9}},
-      {"KdHmBTx8lHXYvmGJ+Vy7", uint64_t{0x9fcbed0c38e50eef},
-       uint64_t{0xd90e7a3655891e37}},
-      {"qJkPlbHr8bMF7/cA6aE65Q==", uint64_t{0x2af4bade1d8e3a1d},
-       uint64_t{0x3bb378b1d4df8fcf}},
-      {"ygvL0EhHZL0fIx6oHHtkxRQ=", uint64_t{0x714e3aa912da2f2c},
-       uint64_t{0xf78e94045c052d47}},
-      {"c1rFXkt5YztwZCQRngncqtSs", uint64_t{0xf5ee75e3cbb82c1c},
-       uint64_t{0x26da0b2130da6b40}},
-      {"8hsQrzszzeNQSEcVXLtvIhm6mw==", uint64_t{0x620e7007321b93b9},
-       uint64_t{0x30b4d426af8c6986}},
-      {"ffUL4RocfyP4KfikGxO1yk7omDI=", uint64_t{0xc08528cac2e551fc},
-       uint64_t{0x5413b4aaf3baaeae}},
-      {"OOB5TT00vF9Od/rLbAWshiErqhpV", uint64_t{0x6a1debf9cc3ad39},
-       uint64_t{0x756ab265370a1597}},
-      {"or5wtXM7BFzTNpSzr+Lw5J5PMhVJ/Q==", uint64_t{0x7e0a3c88111fc226},
-       uint64_t{0xdaf5f4b7d09814fb}},
-      {"gk6pCHDUsoopVEiaCrzVDhioRKxb844=", uint64_t{0x1301fef15df39edb},
-       uint64_t{0x8f874ae37742b75e}},
-      {"TNctmwlC5QbEM6/No4R/La3UdkfeMhzs", uint64_t{0x64e181f3d5817ab},
-       uint64_t{0x8fecd03956121ce8}},
-      {"SsQw9iAjhWz7sgcE9OwLuSC6hsM+BfHs2Q==", uint64_t{0xafafc44961078ecb},
-       uint64_t{0x229c292ea7a08285}},
-      {"ZzO3mVCj4xTT2TT3XqDyEKj2BZQBvrS8RHg=", uint64_t{0x4f7bb45549250094},
-       uint64_t{0xbb4bf0692d14bae}},
-      {"+klp5iPQGtppan5MflEls0iEUzqU+zGZkDJX", uint64_t{0xa30061abaa2818c},
-       uint64_t{0x207b24ca3bdac1db}},
-      {"RO6bvOnlJc8I9eniXlNgqtKy0IX6VNg16NRmgg==", uint64_t{0xd902ee3e44a5705f},
-       uint64_t{0x64f6cd6745d3825b}},
-      {"ZJjZqId1ZXBaij9igClE3nyliU5XWdNRrayGlYA=", uint64_t{0x316d36da516f583},
-       uint64_t{0xa2b2e1656b58df1e}},
-      {"7BfkhfGMDGbxfMB8uyL85GbaYQtjr2K8g7RpLzr/", uint64_t{0x402d83f9f834f616},
-       uint64_t{0xd01d30d9ee7a148}},
-      {"rycWk6wHH7htETQtje9PidS2YzXBx+Qkg2fY7ZYS7A==",
-       uint64_t{0x9c604164c016b72c}, uint64_t{0x1cb4cd00ab804e3b}},
-      {"RTkC2OUK+J13CdGllsH0H5WqgspsSa6QzRZouqx6pvI=",
-       uint64_t{0x3f4507e01f9e73ba}, uint64_t{0x4697f2637fd90999}},
-      {"tKjKmbLCNyrLCM9hycOAXm4DKNpM12oZ7dLTmUx5iwAi",
-       uint64_t{0xc3fe0d5be8d2c7c7}, uint64_t{0x8383a756b5688c07}},
-      {"VprUGNH+5NnNRaORxgH/ySrZFQFDL+4VAodhfBNinmn8cg==",
-       uint64_t{0x531858a40bfa7ea1}, uint64_t{0x695c29cb3696a975}},
-      {"gc1xZaY+q0nPcUvOOnWnT3bqfmT/geth/f7Dm2e/DemMfk4=",
-       uint64_t{0x86689478a7a7e8fa}, uint64_t{0xda2e5a5a5e971521}},
-      {"Mr35fIxqx1ukPAL0su1yFuzzAU3wABCLZ8+ZUFsXn47UmAph",
-       uint64_t{0x4ec948b8e7f27288}, uint64_t{0x7935d4befa056b2b}},
-      {"A9G8pw2+m7+rDtWYAdbl8tb2fT7FFo4hLi2vAsa5Y8mKH3CX3g==",
-       uint64_t{0xce46c7213c10032}, uint64_t{0x38dd541ca95420fe}},
-      {"DFaJGishGwEHDdj9ixbCoaTjz9KS0phLNWHVVdFsM93CvPft3hM=",
-       uint64_t{0xf63e96ee6f32a8b6}, uint64_t{0xcc06c7a4963f967f}},
-      {"7+Ugx+Kr3aRNgYgcUxru62YkTDt5Hqis+2po81hGBkcrJg4N0uuy",
-       uint64_t{0x1cfe85e65fc5225}, uint64_t{0xbf0f6f66e232fb20}},
-      {"H2w6O8BUKqu6Tvj2xxaecxEI2wRgIgqnTTG1WwOgDSINR13Nm4d4Vg==",
-       uint64_t{0x45c474f1cee1d2e8}, uint64_t{0xf7efb32d373fe71a}},
-      {"1XBMnIbqD5jy65xTDaf6WtiwtdtQwv1dCVoqpeKj+7cTR1SaMWMyI04=",
-       uint64_t{0x6e024e14015f329c}, uint64_t{0xe2e64634b1c12660}},
-      {"znZbdXG2TSFrKHEuJc83gPncYpzXGbAebUpP0XxzH0rpe8BaMQ17nDbt",
-       uint64_t{0x760c40502103ae1c}, uint64_t{0x285b8fd1638e306d}},
-      {"ylu8Atu13j1StlcC1MRMJJXIl7USgDDS22HgVv0WQ8hx/8pNtaiKB17hCQ==",
-       uint64_t{0x17fd05c3c560c320}, uint64_t{0x658e8a4e3b714d6c}},
-      {"M6ZVVzsd7vAvbiACSYHioH/440dp4xG2mLlBnxgiqEvI/aIEGpD0Sf4VS0g=",
-       uint64_t{0x8b34200a6f8e90d9}, uint64_t{0xf391fb968e0eb398}},
-      {"li3oFSXLXI+ubUVGJ4blP6mNinGKLHWkvGruun85AhVn6iuMtocbZPVhqxzn",
-       uint64_t{0x6be89e50818bdf69}, uint64_t{0x744a9ea0cc144bf2}},
-      {"kFuQHuUCqBF3Tc3hO4dgdIp223ShaCoog48d5Do5zMqUXOh5XpGK1t5XtxnfGA==",
-       uint64_t{0xfb389773315b47d8}, uint64_t{0x12636f2be11012f1}},
-      {"jWmOad0v0QhXVJd1OdGuBZtDYYS8wBVHlvOeTQx9ZZnm8wLEItPMeihj72E0nWY=",
-       uint64_t{0x4f2512a23f61efee}, uint64_t{0x29c57de825948f80}},
-      {"z+DHU52HaOQdW4JrZwDQAebEA6rm13Zg/9lPYA3txt3NjTBqFZlOMvTRnVzRbl23",
-       uint64_t{0x59ccd92fc16c6fda}, uint64_t{0x58c6f99ab0d1c021}},
-      {"MmBiGDfYeTayyJa/tVycg+rN7f9mPDFaDc+23j0TlW9094er0ADigsl4QX7V3gG/qw==",
-       uint64_t{0x25c5a7f5bd330919}, uint64_t{0x13e7b5a7b82fe3bb}},
-      {"774RK+9rOL4iFvs1q2qpo/JVc/I39buvNjqEFDtDvyoB0FXxPI2vXqOrk08VPfIHkmU=",
-       uint64_t{0x51df4174d34c97d7}, uint64_t{0x10fbc87901e02b63}},
-      {"+slatXiQ7/2lK0BkVUI1qzNxOOLP3I1iK6OfHaoxgqT63FpzbElwEXSwdsryq3UlHK0I",
-       uint64_t{0x80ce6d76f89cb57}, uint64_t{0xa24c9184901b748b}},
-      {"64mVTbQ47dHjHlOHGS/hjJwr/"
-       "K2frCNpn87exOqMzNUVYiPKmhCbfS7vBUce5tO6Ec9osQ==",
-       uint64_t{0x20961c911965f684}, uint64_t{0xcac4fd4c5080e581}},
-      {"fIsaG1r530SFrBqaDj1kqE0AJnvvK8MNEZbII2Yw1OK77v0V59xabIh0B5axaz/"
-       "+a2V5WpA=",
-       uint64_t{0x4e5b926ec83868e7}, uint64_t{0xc38bdb7483ba68e1}},
-      {"PGih0zDEOWCYGxuHGDFu9Ivbff/"
-       "iE7BNUq65tycTR2R76TerrXALRosnzaNYO5fjFhTi+CiS",
-       uint64_t{0x3927b30b922eecef}, uint64_t{0xdb2a8069b2ceaffa}},
-      {"RnpA/"
-       "zJnEnnLjmICORByRVb9bCOgxF44p3VMiW10G7PvW7IhwsWajlP9kIwNA9FjAD2GoQHk2Q="
-       "=",
-       uint64_t{0xbd0291284a49b61c}, uint64_t{0xdf9fe91d0d1c7887}},
-      {"qFklMceaTHqJpy2qavJE+EVBiNFOi6OxjOA3LeIcBop1K7w8xQi3TrDk+"
-       "BrWPRIbfprszSaPfrI=",
-       uint64_t{0x73a77c575bcc956}, uint64_t{0xe83f49e96e2e6a08}},
-      {"cLbfUtLl3EcQmITWoTskUR8da/VafRDYF/ylPYwk7/"
-       "zazk6ssyrzxMN3mmSyvrXR2yDGNZ3WDrTT",
-       uint64_t{0x766a0e2ade6d09a6}, uint64_t{0xc69e61b62ca2b62}},
-      {"s/"
-       "Jf1+"
-       "FbsbCpXWPTUSeWyMH6e4CvTFvPE5Fs6Z8hvFITGyr0dtukHzkI84oviVLxhM1xMxrMAy1db"
-       "w==",
-       uint64_t{0x2599f4f905115869}, uint64_t{0xb4a4f3f85f8298fe}},
-      {"FvyQ00+j7nmYZVQ8hI1Edxd0AWplhTfWuFGiu34AK5X8u2hLX1bE97sZM0CmeLe+"
-       "7LgoUT1fJ/axybE=",
-       uint64_t{0xd8256e5444d21e53}, uint64_t{0x167a1b39e1e95f41}},
-      {"L8ncxMaYLBH3g9buPu8hfpWZNlOF7nvWLNv9IozH07uQsIBWSKxoPy8+"
-       "LW4tTuzC6CIWbRGRRD1sQV/4",
-       uint64_t{0xf664a91333fb8dfd}, uint64_t{0xf8a2a5649855ee41}},
-      {"CDK0meI07yrgV2kQlZZ+"
-       "wuVqhc2NmzqeLH7bmcA6kchsRWFPeVF5Wqjjaj556ABeUoUr3yBmfU3kWOakkg==",
-       uint64_t{0x9625b859be372cd1}, uint64_t{0x27992565b595c498}},
-      {"d23/vc5ONh/"
-       "HkMiq+gYk4gaCNYyuFKwUkvn46t+dfVcKfBTYykr4kdvAPNXGYLjM4u1YkAEFpJP+"
-       "nX7eOvs=",
-       uint64_t{0x7b99940782e29898}, uint64_t{0x3e08cca5b71f9346}},
-      {"NUR3SRxBkxTSbtQORJpu/GdR6b/h6sSGfsMj/KFd99ahbh+9r7LSgSGmkGVB/"
-       "mGoT0pnMTQst7Lv2q6QN6Vm",
-       uint64_t{0x4fe12fa5383b51a8}, uint64_t{0xad406b10c770a6d2}},
-      {"2BOFlcI3Z0RYDtS9T9Ie9yJoXlOdigpPeeT+CRujb/"
-       "O39Ih5LPC9hP6RQk1kYESGyaLZZi3jtabHs7DiVx/VDg==",
-       uint64_t{0xe2ccb09ac0f5b4b6}, uint64_t{0xd1713ce6e552bcf2}},
-      {"FF2HQE1FxEvWBpg6Z9zAMH+Zlqx8S1JD/"
-       "wIlViL6ZDZY63alMDrxB0GJQahmAtjlm26RGLnjW7jmgQ4Ie3I+014=",
-       uint64_t{0x7d0a37adbd7b753b}, uint64_t{0x753b287194c73ad3}},
-      {"tHmO7mqVL/PX11nZrz50Hc+M17Poj5lpnqHkEN+4bpMx/"
-       "YGbkrGOaYjoQjgmt1X2QyypK7xClFrjeWrCMdlVYtbW",
-       uint64_t{0xd3ae96ef9f7185f2}, uint64_t{0x5ae41a95f600af1c}},
-      {"/WiHi9IQcxRImsudkA/KOTqGe8/"
-       "gXkhKIHkjddv5S9hi02M049dIK3EUyAEjkjpdGLUs+BN0QzPtZqjIYPOgwsYE9g==",
-       uint64_t{0x4fb88ea63f79a0d8}, uint64_t{0x4a61163b86a8bb4c}},
-      {"qds+1ExSnU11L4fTSDz/QE90g4Jh6ioqSh3KDOTOAo2pQGL1k/"
-       "9CCC7J23YF27dUTzrWsCQA2m4epXoCc3yPHb3xElA=",
-       uint64_t{0xed564e259bb5ebe9}, uint64_t{0x42eeaa79e760c7e4}},
-      {"8FVYHx40lSQPTHheh08Oq0/"
-       "pGm2OlG8BEf8ezvAxHuGGdgCkqpXIueJBF2mQJhTfDy5NncO8ntS7vaKs7sCNdDaNGOEi",
-       uint64_t{0x3e3256b60c428000}, uint64_t{0x698df622ef465b0a}},
-      {"4ZoEIrJtstiCkeew3oRzmyJHVt/pAs2pj0HgHFrBPztbQ10NsQ/"
-       "lM6DM439QVxpznnBSiHMgMQJhER+70l72LqFTO1JiIQ==",
-       uint64_t{0xfb05bad59ec8705}, uint64_t{0x157583111e1a6026}},
-      {"hQPtaYI+wJyxXgwD5n8jGIKFKaFA/"
-       "P83KqCKZfPthnjwdOFysqEOYwAaZuaaiv4cDyi9TyS8hk5cEbNP/jrI7q6pYGBLbsM=",
-       uint64_t{0xafdc251dbf97b5f8}, uint64_t{0xaa1388f078e793e0}},
-      {"S4gpMSKzMD7CWPsSfLeYyhSpfWOntyuVZdX1xSBjiGvsspwOZcxNKCRIOqAA0moUfOh3I5+"
-       "juQV4rsqYElMD/gWfDGpsWZKQ",
-       uint64_t{0x10ec9c92ddb5dcbc}, uint64_t{0xf10d68d0f3309360}},
-      {"oswxop+"
-       "bthuDLT4j0PcoSKby4LhF47ZKg8K17xxHf74UsGCzTBbOz0MM8hQEGlyqDT1iUiAYnaPaUp"
-       "L2mRK0rcIUYA4qLt5uOw==",
-       uint64_t{0x9a767d5822c7dac4}, uint64_t{0x2af056184457a3de}},
-      {"0II/"
-       "697p+"
-       "BtLSjxj5989OXI004TogEb94VUnDzOVSgMXie72cuYRvTFNIBgtXlKfkiUjeqVpd4a+"
-       "n5bxNOD1TGrjQtzKU5r7obo=",
-       uint64_t{0xee46254080d6e2db}, uint64_t{0x6d0058e1590b2489}},
-      {"E84YZW2qipAlMPmctrg7TKlwLZ68l4L+c0xRDUfyyFrA4MAti0q9sHq3TDFviH0Y+"
-       "Kq3tEE5srWFA8LM9oomtmvm5PYxoaarWPLc",
-       uint64_t{0xbbb669588d8bf398}, uint64_t{0x638f287f68817f12}},
-      {"x3pa4HIElyZG0Nj7Vdy9IdJIR4izLmypXw5PCmZB5y68QQ4uRaVVi3UthsoJROvbjDJkP2D"
-       "Q6L/eN8pFeLFzNPKBYzcmuMOb5Ull7w==",
-       uint64_t{0xdc2afaa529beef44}, uint64_t{0xc46b71fecefd5467}},
-      {"jVDKGYIuWOP/"
-       "QKLdd2wi8B2VJA8Wh0c8PwrXJVM8FOGM3voPDVPyDJOU6QsBDPseoR8uuKd19OZ/"
-       "zAvSCB+zlf6upAsBlheUKgCfKww=",
-       uint64_t{0xf1f67391d45013a8}, uint64_t{0x2c8e94679d964e0a}},
-      {"mkquunhmYe1aR2wmUz4vcvLEcKBoe6H+kjUok9VUn2+eTSkWs4oDDtJvNCWtY5efJwg/"
-       "j4PgjRYWtqnrCkhaqJaEvkkOwVfgMIwF3e+d",
-       uint64_t{0x16fce2b8c65a3429}, uint64_t{0x8612b797ce22503a}},
-      {"fRelvKYonTQ+s+rnnvQw+JzGfFoPixtna0vzcSjiDqX5s2Kg2//"
-       "UGrK+AVCyMUhO98WoB1DDbrsOYSw2QzrcPe0+3ck9sePvb+Q/IRaHbw==",
-       uint64_t{0xf4b096699f49fe67}, uint64_t{0x59f929babfba7170}},
-      {"DUwXFJzagljo44QeJ7/"
-       "6ZKw4QXV18lhkYT2jglMr8WB3CHUU4vdsytvw6AKv42ZcG6fRkZkq9fpnmXy6xG0aO3WPT1"
-       "eHuyFirAlkW+zKtwg=",
-       uint64_t{0xca584c4bc8198682}, uint64_t{0x9527556923fb49a0}},
-      {"cYmZCrOOBBongNTr7e4nYn52uQUy2mfe48s50JXx2AZ6cRAt/"
-       "xRHJ5QbEoEJOeOHsJyM4nbzwFm++SlT6gFZZHJpkXJ92JkR86uS/eV1hJUR",
-       uint64_t{0xed269fc3818b6aad}, uint64_t{0x1039ab644f5e150b}},
-      {"EXeHBDfhwzAKFhsMcH9+2RHwV+mJaN01+9oacF6vgm8mCXRd6jeN9U2oAb0of5c5cO4i+"
-       "Vb/LlHZSMI490SnHU0bejhSCC2gsC5d2K30ER3iNA==",
-       uint64_t{0x33f253cbb8fe66a8}, uint64_t{0x7816c83f3aa05e6d}},
-      {"FzkzRYoNjkxFhZDso94IHRZaJUP61nFYrh5MwDwv9FNoJ5jyNCY/"
-       "eazPZk+tbmzDyJIGw2h3GxaWZ9bSlsol/vK98SbkMKCQ/wbfrXRLcDzdd/8=",
-       uint64_t{0xd0b76b2c1523d99c}, uint64_t{0xf51d2f564518c619}},
-      {"Re4aXISCMlYY/XsX7zkIFR04ta03u4zkL9dVbLXMa/q6hlY/CImVIIYRN3VKP4pnd0AUr/"
-       "ugkyt36JcstAInb4h9rpAGQ7GMVOgBniiMBZ/MGU7H",
-       uint64_t{0xfd28f0811a2a237f}, uint64_t{0x67d494cff03ac004}},
-      {"ueLyMcqJXX+MhO4UApylCN9WlTQ+"
-       "ltJmItgG7vFUtqs2qNwBMjmAvr5u0sAKd8jpzV0dDPTwchbIeAW5zbtkA2NABJV6hFM48ib"
-       "4/J3A5mseA3cS8w==",
-       uint64_t{0x6261fb136482e84}, uint64_t{0x2802d636ced1cfbb}},
-      {"6Si7Yi11L+jZMkwaN+GUuzXMrlvEqviEkGOilNq0h8TdQyYKuFXzkYc/"
-       "q74gP3pVCyiwz9KpVGMM9vfnq36riMHRknkmhQutxLZs5fbmOgEO69HglCU=",
-       uint64_t{0x458efc750bca7c3a}, uint64_t{0xf64e20bad771cb12}},
-      {"Q6AbOofGuTJOegPh9Clm/"
-       "9crtUMQqylKrTc1fhfJo1tqvpXxhU4k08kntL1RG7woRnFrVh2UoMrL1kjin+s9CanT+"
-       "y4hHwLqRranl9FjvxfVKm3yvg68",
-       uint64_t{0xa7e69ff84e5e7c27}, uint64_t{0xb9a6cf84a83e15e}},
-      {"ieQEbIPvqY2YfIjHnqfJiO1/MIVRk0RoaG/WWi3kFrfIGiNLCczYoklgaecHMm/"
-       "1sZ96AjO+a5stQfZbJQwS7Sc1ODABEdJKcTsxeW2hbh9A6CFzpowP1A==",
-       uint64_t{0x3c59bfd0c29efe9e}, uint64_t{0x8da6630319609301}},
-      {"zQUv8hFB3zh2GGl3KTvCmnfzE+"
-       "SUgQPVaSVIELFX5H9cE3FuVFGmymkPQZJLAyzC90Cmi8GqYCvPqTuAAB//"
-       "XTJxy4bCcVArgZG9zJXpjowpNBfr3ngWrSE=",
-       uint64_t{0x10befacc6afd298d}, uint64_t{0x40946a86e2a996f3}},
-      {"US4hcC1+op5JKGC7eIs8CUgInjKWKlvKQkapulxW262E/"
-       "B2ye79QxOexf188u2mFwwe3WTISJHRZzS61IwljqAWAWoBAqkUnW8SHmIDwHUP31J0p5sGd"
-       "P47L",
-       uint64_t{0x41d5320b0a38efa7}, uint64_t{0xcab7f5997953fa76}},
-      {"9bHUWFna2LNaGF6fQLlkx1Hkt24nrkLE2CmFdWgTQV3FFbUe747SSqYw6ebpTa07MWSpWRP"
-       "sHesVo2B9tqHbe7eQmqYebPDFnNqrhSdZwFm9arLQVs+7a3Ic6A==",
-       uint64_t{0x58db1c7450fe17f3}, uint64_t{0x39129ca0e04fc465}},
-      {"Kb3DpHRUPhtyqgs3RuXjzA08jGb59hjKTOeFt1qhoINfYyfTt2buKhD6YVffRCPsgK9SeqZ"
-       "qRPJSyaqsa0ovyq1WnWW8jI/NhvAkZTVHUrX2pC+cD3OPYT05Dag=",
-       uint64_t{0x6098c055a335b7a6}, uint64_t{0x5238221fd685e1b8}},
-      {"gzxyMJIPlU+bJBwhFUCHSofZ/"
-       "319LxqMoqnt3+L6h2U2+ZXJCSsYpE80xmR0Ta77Jq54o92SMH87HV8dGOaCTuAYF+"
-       "lDL42SY1P316Cl0sZTS2ow3ZqwGbcPNs/1",
-       uint64_t{0x1bbacec67845a801}, uint64_t{0x175130c407dbcaab}},
-      {"uR7V0TW+FGVMpsifnaBAQ3IGlr1wx5sKd7TChuqRe6OvUXTlD4hKWy8S+"
-       "8yyOw8lQabism19vOQxfmocEOW/"
-       "vzY0pEa87qHrAZy4s9fH2Bltu8vaOIe+agYohhYORQ==",
-       uint64_t{0xc419cfc7442190}, uint64_t{0x2f20e7536c0b0df}},
-      {"1UR5eoo2aCwhacjZHaCh9bkOsITp6QunUxHQ2SfeHv0imHetzt/"
-       "Z70mhyWZBalv6eAx+YfWKCUib2SHDtz/"
-       "A2dc3hqUWX5VfAV7FQsghPUAtu6IiRatq4YSLpDvKZBQ=",
-       uint64_t{0xc95e510d94ba270c}, uint64_t{0x2742cb488a04ad56}},
-      {"opubR7H63BH7OtY+Avd7QyQ25UZ8kLBdFDsBTwZlY6gA/"
-       "u+x+"
-       "czC9AaZMgmQrUy15DH7YMGsvdXnviTtI4eVI4aF1H9Rl3NXMKZgwFOsdTfdcZeeHVRzBBKX"
-       "8jUfh1il",
-       uint64_t{0xff1ae05c98089c3f}, uint64_t{0xd6afb593879ff93b}},
-      {"DC0kXcSXtfQ9FbSRwirIn5tgPri0sbzHSa78aDZVDUKCMaBGyFU6BmrulywYX8yzvwprdLs"
-       "oOwTWN2wMjHlPDqrvVHNEjnmufRDblW+nSS+xtKNs3N5xsxXdv6JXDrAB/Q==",
-       uint64_t{0x90c02b8dceced493}, uint64_t{0xf50ad64caac0ca7f}},
-      {"BXRBk+3wEP3Lpm1y75wjoz+PgB0AMzLe8tQ1AYU2/"
-       "oqrQB2YMC6W+9QDbcOfkGbeH+b7IBkt/"
-       "gwCMw2HaQsRFEsurXtcQ3YwRuPz5XNaw5NAvrNa67Fm7eRzdE1+hWLKtA8=",
-       uint64_t{0x9f8a76697ab1aa36}, uint64_t{0x2ade95c4261364ae}},
-      {"RRBSvEGYnzR9E45Aps/+WSnpCo/X7gJLO4DRnUqFrJCV/kzWlusLE/"
-       "6ZU6RoUf2ROwcgEvUiXTGjLs7ts3t9SXnJHxC1KiOzxHdYLMhVvgNd3hVSAXODpKFSkVXND"
-       "55G2L1W",
-       uint64_t{0x6ba1bf3d811a531d}, uint64_t{0x5c4f3299faacd07a}},
-      {"jeh6Qazxmdi57pa9S3XSnnZFIRrnc6s8QLrah5OX3SB/V2ErSPoEAumavzQPkdKF1/"
-       "SfvmdL+qgF1C+Yawy562QaFqwVGq7+tW0yxP8FStb56ZRgNI4IOmI30s1Ei7iops9Uuw==",
-       uint64_t{0x6a418974109c67b4}, uint64_t{0xfffe3bff0ae5e9bc}},
-      {"6QO5nnDrY2/"
-       "wrUXpltlKy2dSBcmK15fOY092CR7KxAjNfaY+"
-       "aAmtWbbzQk3MjBg03x39afSUN1fkrWACdyQKRaGxgwq6MGNxI6W+8DLWJBHzIXrntrE/"
-       "ml6fnNXEpxplWJ1vEs4=",
-       uint64_t{0x8472f1c2b3d230a3}, uint64_t{0x1db785c0005166e4}},
-      {"0oPxeEHhqhcFuwonNfLd5jF3RNATGZS6NPoS0WklnzyokbTqcl4BeBkMn07+fDQv83j/"
-       "BpGUwcWO05f3+DYzocfnizpFjLJemFGsls3gxcBYxcbqWYev51tG3lN9EvRE+X9+Pwww",
-       uint64_t{0x5e06068f884e73a7}, uint64_t{0xea000d962ad18418}},
-      {"naSBSjtOKgAOg8XVbR5cHAW3Y+QL4Pb/JO9/"
-       "oy6L08wvVRZqo0BrssMwhzBP401Um7A4ppAupbQeJFdMrysY34AuSSNvtNUy5VxjNECwiNt"
-       "gwYHw7yakDUv8WvonctmnoSPKENegQg==",
-       uint64_t{0x55290b1a8f170f59}, uint64_t{0xe42aef38359362d9}},
-      {"vPyl8DxVeRe1OpilKb9KNwpGkQRtA94UpAHetNh+"
-       "95V7nIW38v7PpzhnTWIml5kw3So1Si0TXtIUPIbsu32BNhoH7QwFvLM+"
-       "JACgSpc5e3RjsL6Qwxxi11npwxRmRUqATDeMUfRAjxg=",
-       uint64_t{0x5501cfd83dfe706a}, uint64_t{0xc8e95657348a3891}},
-      {"QC9i2GjdTMuNC1xQJ74ngKfrlA4w3o58FhvNCltdIpuMhHP1YsDA78scQPLbZ3OCUgeQguY"
-       "f/vw6zAaVKSgwtaykqg5ka/4vhz4hYqWU5ficdXqClHl+zkWEY26slCNYOM5nnDlly8Cj",
-       uint64_t{0xe43ed13d13a66990}, uint64_t{0xc162eca864f238c6}},
-      {"7CNIgQhAHX27nxI0HeB5oUTnTdgKpRDYDKwRcXfSFGP1XeT9nQF6WKCMjL1tBV6x7KuJ91G"
-       "Zz11F4c+8s+MfqEAEpd4FHzamrMNjGcjCyrVtU6y+7HscMVzr7Q/"
-       "ODLcPEFztFnwjvCjmHw==",
-       uint64_t{0xdf43bc375cf5283f}, uint64_t{0xbe1fb373e20579ad}},
-      {"Qa/hC2RPXhANSospe+gUaPfjdK/yhQvfm4cCV6/pdvCYWPv8p1kMtKOX3h5/"
-       "8oZ31fsmx4Axphu5qXJokuhZKkBUJueuMpxRyXpwSWz2wELx5glxF7CM0Fn+"
-       "OevnkhUn5jsPlG2r5jYlVn8=",
-       uint64_t{0x8112b806d288d7b5}, uint64_t{0x628a1d4f40aa6ffd}},
-      {"kUw/0z4l3a89jTwN5jpG0SHY5km/"
-       "IVhTjgM5xCiPRLncg40aqWrJ5vcF891AOq5hEpSq0bUCJUMFXgct7kvnys905HjerV7Vs1G"
-       "y84tgVJ70/2+pAZTsB/PzNOE/G6sOj4+GbTzkQu819OLB",
-       uint64_t{0xd52a18abb001cb46}, uint64_t{0xa87bdb7456340f90}},
-      {"VDdfSDbO8Tdj3T5W0XM3EI7iHh5xpIutiM6dvcJ/fhe23V/srFEkDy5iZf/"
-       "VnA9kfi2C79ENnFnbOReeuZW1b3MUXB9lgC6U4pOTuC+"
-       "jHK3Qnpyiqzj7h3ISJSuo2pob7vY6VHZo6Fn7exEqHg==",
-       uint64_t{0xe12b76a2433a1236}, uint64_t{0x5960ef3ba982c801}},
-      {"Ldfvy3ORdquM/R2fIkhH/ONi69mcP1AEJ6n/"
-       "oropwecAsLJzQSgezSY8bEiEs0VnFTBBsW+RtZY6tDj03fnb3amNUOq1b7jbqyQkL9hpl+"
-       "2Z2J8IaVSeownWl+bQcsR5/xRktIMckC5AtF4YHfU=",
-       uint64_t{0x175bf7319cf1fa00}, uint64_t{0x5026586df9a431ec}},
-      {"BrbNpb42+"
-       "VzZAjJw6QLirXzhweCVRfwlczzZ0VX2xluskwBqyfnGovz5EuX79JJ31VNXa5hTkAyQat3l"
-       "YKRADTdAdwE5PqM1N7YaMqqsqoAAAeuYVXuk5eWCykYmClNdSspegwgCuT+403JigBzi",
-       uint64_t{0xd63d57b3f67525ae}, uint64_t{0xfe4b8a20fdf0840b}},
-      {"gB3NGHJJvVcuPyF0ZSvHwnWSIfmaI7La24VMPQVoIIWF7Z74NltPZZpx2f+cocESM+"
-       "ILzQW9p+BC8x5IWz7N4Str2WLGKMdgmaBfNkEhSHQDU0IJEOnpUt0HmjhFaBlx0/"
-       "LTmhua+rQ6Wup8ezLwfg==",
-       uint64_t{0x933faea858832b73}, uint64_t{0xdcb761867da7072f}},
-      {"hTKHlRxx6Pl4gjG+6ksvvj0CWFicUg3WrPdSJypDpq91LUWRni2KF6+"
-       "81ZoHBFhEBrCdogKqeK+hy9bLDnx7g6rAFUjtn1+cWzQ2YjiOpz4+"
-       "ROBB7lnwjyTGWzJD1rXtlso1g2qVH8XJVigC5M9AIxM=",
-       uint64_t{0x53d061e5f8e7c04f}, uint64_t{0xc10d4653667275b7}},
-      {"IWQBelSQnhrr0F3BhUpXUIDauhX6f95Qp+A0diFXiUK7irwPG1oqBiqHyK/SH/"
-       "9S+"
-       "rln9DlFROAmeFdH0OCJi2tFm4afxYzJTFR4HnR4cG4x12JqHaZLQx6iiu6CE3rtWBVz99oA"
-       "wCZUOEXIsLU24o2Y",
-       uint64_t{0xdb4124556dd515e0}, uint64_t{0x727720deec13110b}},
-      {"TKo+l+"
-       "1dOXdLvIrFqeLaHdm0HZnbcdEgOoLVcGRiCbAMR0j5pIFw8D36tefckAS1RCFOH5IgP8yiF"
-       "T0Gd0a2hI3+"
-       "fTKA7iK96NekxWeoeqzJyctc6QsoiyBlkZerRxs5RplrxoeNg29kKDTM0K94mnhD9g==",
-       uint64_t{0x4fb31a0dd681ee71}, uint64_t{0x710b009662858dc9}},
-      {"YU4e7G6EfQYvxCFoCrrT0EFgVLHFfOWRTJQJ5gxM3G2b+"
-       "1kJf9YPrpsxF6Xr6nYtS8reEEbDoZJYqnlk9lXSkVArm88Cqn6d25VCx3+"
-       "49MqC0trIlXtb7SXUUhwpJK16T0hJUfPH7s5cMZXc6YmmbFuBNPE=",
-       uint64_t{0x27cc72eefa138e4c}, uint64_t{0xfbf8f7a3ecac1eb7}},
-      {"/I/"
-       "eImMwPo1U6wekNFD1Jxjk9XQVi1D+"
-       "FPdqcHifYXQuP5aScNQfxMAmaPR2XhuOQhADV5tTVbBKwCDCX4E3jcDNHzCiPvViZF1W27t"
-       "xaf2BbFQdwKrNCmrtzcluBFYu0XZfc7RU1RmxK/RtnF1qHsq/O4pp",
-       uint64_t{0x44bc2dfba4bd3ced}, uint64_t{0xb6fc4fcd0722e3df}},
-      {"CJTT9WGcY2XykTdo8KodRIA29qsqY0iHzWZRjKHb9alwyJ7RZAE3V5Juv4MY3MeYEr1EPCC"
-       "MxO7yFXqT8XA8YTjaMp3bafRt17Pw8JC4iKJ1zN+WWKOESrj+"
-       "3aluGQqn8z1EzqY4PH7rLG575PYeWsP98BugdA==",
-       uint64_t{0x242da1e3a439bed8}, uint64_t{0x7cb86dcc55104aac}},
-      {"ZlhyQwLhXQyIUEnMH/"
-       "AEW27vh9xrbNKJxpWGtrEmKhd+nFqAfbeNBQjW0SfG1YI0xQkQMHXjuTt4P/"
-       "EpZRtA47ibZDVS8TtaxwyBjuIDwqcN09eCtpC+Ls+"
-       "vWDTLmBeDM3u4hmzz4DQAYsLiZYSJcldg9Q3wszw=",
-       uint64_t{0xdc559c746e35c139}, uint64_t{0x19e71e9b45c3a51e}},
-      {"v2KU8y0sCrBghmnm8lzGJlwo6D6ObccAxCf10heoDtYLosk4ztTpLlpSFEyu23MLA1tJkcg"
-       "Rko04h19QMG0mOw/"
-       "wc93EXAweriBqXfvdaP85sZABwiKO+6rtS9pacRVpYYhHJeVTQ5NzrvBvi1huxAr+"
-       "xswhVMfL",
-       uint64_t{0xd0b0350275b9989}, uint64_t{0x51de38573c2bea48}},
-      {"QhKlnIS6BuVCTQsnoE67E/"
-       "yrgogE8EwO7xLaEGei26m0gEU4OksefJgppDh3X0x0Cs78Dr9IHK5b977CmZlrTRmwhlP8p"
-       "M+UzXPNRNIZuN3ntOum/QhUWP8SGpirheXENWsXMQ/"
-       "nxtxakyEtrNkKk471Oov9juP8oQ==",
-       uint64_t{0xb04489e41d17730c}, uint64_t{0xa73ab6996d6df158}},
-      {"/ZRMgnoRt+Uo6fUPr9FqQvKX7syhgVqWu+"
-       "WUSsiQ68UlN0efSP6Eced5gJZL6tg9gcYJIkhjuQNITU0Q3TjVAnAcobgbJikCn6qZ6pRxK"
-       "BY4MTiAlfGD3T7R7hwJwx554MAy++Zb/YUFlnCaCJiwQMnowF7aQzwYFCo=",
-       uint64_t{0x2217285eb4572156}, uint64_t{0x55ef2b8c930817b2}},
-      {"NB7tU5fNE8nI+SXGfipc7sRkhnSkUF1krjeo6k+8FITaAtdyz+"
-       "o7mONgXmGLulBPH9bEwyYhKNVY0L+njNQrZ9YC2aXsFD3PdZsxAFaBT3VXEzh+"
-       "NGBTjDASNL3mXyS8Yv1iThGfHoY7T4aR0NYGJ+k+pR6f+KrPC96M",
-       uint64_t{0x12c2e8e68aede73b}, uint64_t{0xb2850bf5fae87157}},
-      {"8T6wrqCtEO6/rwxF6lvMeyuigVOLwPipX/FULvwyu+1wa5sQGav/"
-       "2FsLHUVn6cGSi0LlFwLewGHPFJDLR0u4t7ZUyM//"
-       "x6da0sWgOa5hzDqjsVGmjxEHXiaXKW3i4iSZNuxoNbMQkIbVML+"
-       "DkYu9ND0O2swg4itGeVSzXA==",
-       uint64_t{0x4d612125bdc4fd00}, uint64_t{0xecf3de1acd04651f}},
-      {"Ntf1bMRdondtMv1CYr3G80iDJ4WSAlKy5H34XdGruQiCrnRGDBa+"
-       "eUi7vKp4gp3BBcVGl8eYSasVQQjn7MLvb3BjtXx6c/"
-       "bCL7JtpzQKaDnPr9GWRxpBXVxKREgMM7d8lm35EODv0w+"
-       "hQLfVSh8OGs7fsBb68nNWPLeeSOo=",
-       uint64_t{0x81826b553954464e}, uint64_t{0xcc0a40552559ff32}},
-      {"VsSAw72Ro6xks02kaiLuiTEIWBC5bgqr4WDnmP8vglXzAhixk7td926rm9jNimL+"
-       "kroPSygZ9gl63aF5DCPOACXmsbmhDrAQuUzoh9ZKhWgElLQsrqo1KIjWoZT5b5QfVUXY9lS"
-       "IBg3U75SqORoTPq7HalxxoIT5diWOcJQi",
-       uint64_t{0xc2e5d345dc0ddd2d}, uint64_t{0xc385c374f20315b1}},
-      {"j+loZ+C87+"
-       "bJxNVebg94gU0mSLeDulcHs84tQT7BZM2rzDSLiCNxUedHr1ZWJ9ejTiBa0dqy2I2ABc++"
-       "xzOLcv+//YfibtjKtYggC6/3rv0XCc7xu6d/"
-       "O6xO+XOBhOWAQ+IHJVHf7wZnDxIXB8AUHsnjEISKj7823biqXjyP3g==",
-       uint64_t{0x3da6830a9e32631e}, uint64_t{0xb90208a4c7234183}},
-      {"f3LlpcPElMkspNtDq5xXyWU62erEaKn7RWKlo540gR6mZsNpK1czV/"
-       "sOmqaq8XAQLEn68LKj6/"
-       "cFkJukxRzCa4OF1a7cCAXYFp9+wZDu0bw4y63qbpjhdCl8GO6Z2lkcXy7KOzbPE01ukg7+"
-       "gN+7uKpoohgAhIwpAKQXmX5xtd0=",
-       uint64_t{0xc9ae5c8759b4877a}, uint64_t{0x58aa1ca7a4c075d9}},
-  };
-
-  for (const auto& expected_result : expected_results) {
-    std::string str;
-    ASSERT_TRUE(absl::Base64Unescape(expected_result.base64_data, &str));
-    EXPECT_EQ(absl::hash_internal::Wyhash(str.data(), str.size(),
-                                          expected_result.seed, kSalt),
-              expected_result.hash);
-  }
-}
-
-}  // namespace
diff --git a/absl/memory/BUILD.bazel b/absl/memory/BUILD.bazel
index d2824a0..c16bf8a 100644
--- a/absl/memory/BUILD.bazel
+++ b/absl/memory/BUILD.bazel
@@ -14,7 +14,6 @@
 # limitations under the License.
 #
 
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
diff --git a/absl/memory/CMakeLists.txt b/absl/memory/CMakeLists.txt
index 78fb7e1..9d50e1d 100644
--- a/absl/memory/CMakeLists.txt
+++ b/absl/memory/CMakeLists.txt
@@ -37,7 +37,7 @@
   DEPS
     absl::memory
     absl::core_headers
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -51,5 +51,5 @@
     absl::memory
     absl::config
     absl::exception_safety_testing
-    gmock_main
+    GTest::gmock_main
 )
diff --git a/absl/memory/memory.h b/absl/memory/memory.h
index 2b5ff62..d633260 100644
--- a/absl/memory/memory.h
+++ b/absl/memory/memory.h
@@ -420,7 +420,7 @@
 //
 // A C++11 compatible implementation of C++17's std::allocator_traits.
 //
-#if __cplusplus >= 201703L
+#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
 using std::allocator_traits;
 #else  // __cplusplus >= 201703L
 template <typename Alloc>
diff --git a/absl/meta/BUILD.bazel b/absl/meta/BUILD.bazel
index 5585fcc..fb37925 100644
--- a/absl/meta/BUILD.bazel
+++ b/absl/meta/BUILD.bazel
@@ -14,7 +14,6 @@
 # limitations under the License.
 #
 
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
diff --git a/absl/meta/CMakeLists.txt b/absl/meta/CMakeLists.txt
index 672ead2..9de4bd3 100644
--- a/absl/meta/CMakeLists.txt
+++ b/absl/meta/CMakeLists.txt
@@ -35,7 +35,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::type_traits
-    gmock_main
+    GTest::gmock_main
 )
 
 # component target
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h
index d5cb5f3..d886cb3 100644
--- a/absl/meta/type_traits.h
+++ b/absl/meta/type_traits.h
@@ -35,7 +35,7 @@
 #ifndef ABSL_META_TYPE_TRAITS_H_
 #define ABSL_META_TYPE_TRAITS_H_
 
-#include <stddef.h>
+#include <cstddef>
 #include <functional>
 #include <type_traits>
 
@@ -47,6 +47,14 @@
 #define ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION 1
 #endif
 
+// Defines the default alignment. `__STDCPP_DEFAULT_NEW_ALIGNMENT__` is a C++17
+// feature.
+#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
+#define ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT __STDCPP_DEFAULT_NEW_ALIGNMENT__
+#else  // defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
+#define ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT alignof(std::max_align_t)
+#endif  // defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
@@ -499,6 +507,27 @@
 #endif  // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
 };
 
+#if defined(__cpp_lib_remove_cvref) && __cpp_lib_remove_cvref >= 201711L
+template <typename T>
+using remove_cvref = std::remove_cvref<T>;
+
+template <typename T>
+using remove_cvref_t = typename std::remove_cvref<T>::type;
+#else
+// remove_cvref()
+//
+// C++11 compatible implementation of std::remove_cvref which was added in
+// C++20.
+template <typename T>
+struct remove_cvref {
+  using type =
+      typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+};
+
+template <typename T>
+using remove_cvref_t = typename remove_cvref<T>::type;
+#endif
+
 namespace type_traits_internal {
 // is_trivially_copyable()
 //
@@ -613,7 +642,8 @@
 
 namespace type_traits_internal {
 
-#if __cplusplus >= 201703L
+#if (defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L) || \
+    (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
 // std::result_of is deprecated (C++17) or removed (C++20)
 template<typename> struct result_of;
 template<typename F, typename... Args>
diff --git a/absl/meta/type_traits_test.cc b/absl/meta/type_traits_test.cc
index 1aafd0d..0ef5b66 100644
--- a/absl/meta/type_traits_test.cc
+++ b/absl/meta/type_traits_test.cc
@@ -942,6 +942,34 @@
       absl::type_traits_internal::is_trivially_copyable<Trivial&>::value);
 }
 
+TEST(TypeTraitsTest, TestRemoveCVRef) {
+  EXPECT_TRUE(
+      (std::is_same<typename absl::remove_cvref<int>::type, int>::value));
+  EXPECT_TRUE(
+      (std::is_same<typename absl::remove_cvref<int&>::type, int>::value));
+  EXPECT_TRUE(
+      (std::is_same<typename absl::remove_cvref<int&&>::type, int>::value));
+  EXPECT_TRUE((
+      std::is_same<typename absl::remove_cvref<const int&>::type, int>::value));
+  EXPECT_TRUE(
+      (std::is_same<typename absl::remove_cvref<int*>::type, int*>::value));
+  // Does not remove const in this case.
+  EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int*>::type,
+                            const int*>::value));
+  EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int[2]>::type,
+                            int[2]>::value));
+  EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int(&)[2]>::type,
+                            int[2]>::value));
+  EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int(&&)[2]>::type,
+                            int[2]>::value));
+  EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int[2]>::type,
+                            int[2]>::value));
+  EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int(&)[2]>::type,
+                            int[2]>::value));
+  EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int(&&)[2]>::type,
+                            int[2]>::value));
+}
+
 #define ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(trait_name, ...)          \
   EXPECT_TRUE((std::is_same<typename std::trait_name<__VA_ARGS__>::type, \
                             absl::trait_name##_t<__VA_ARGS__>>::value))
diff --git a/absl/numeric/BUILD.bazel b/absl/numeric/BUILD.bazel
index ea587bf..1f9e0f2 100644
--- a/absl/numeric/BUILD.bazel
+++ b/absl/numeric/BUILD.bazel
@@ -12,7 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
diff --git a/absl/numeric/CMakeLists.txt b/absl/numeric/CMakeLists.txt
index 781987d..26df5cf 100644
--- a/absl/numeric/CMakeLists.txt
+++ b/absl/numeric/CMakeLists.txt
@@ -38,7 +38,7 @@
     absl::bits
     absl::core_headers
     absl::random_random
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -73,7 +73,7 @@
     absl::core_headers
     absl::hash_testing
     absl::type_traits
-    gmock_main
+    GTest::gmock_main
 )
 
 # component target
diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc
index 5160df7..17d8874 100644
--- a/absl/numeric/int128.cc
+++ b/absl/numeric/int128.cc
@@ -138,28 +138,21 @@
 uint128::uint128(double v) : uint128(MakeUint128FromFloat(v)) {}
 uint128::uint128(long double v) : uint128(MakeUint128FromFloat(v)) {}
 
+#if !defined(ABSL_HAVE_INTRINSIC_INT128)
 uint128 operator/(uint128 lhs, uint128 rhs) {
-#if defined(ABSL_HAVE_INTRINSIC_INT128)
-  return static_cast<unsigned __int128>(lhs) /
-         static_cast<unsigned __int128>(rhs);
-#else  // ABSL_HAVE_INTRINSIC_INT128
   uint128 quotient = 0;
   uint128 remainder = 0;
   DivModImpl(lhs, rhs, &quotient, &remainder);
   return quotient;
-#endif  // ABSL_HAVE_INTRINSIC_INT128
 }
+
 uint128 operator%(uint128 lhs, uint128 rhs) {
-#if defined(ABSL_HAVE_INTRINSIC_INT128)
-  return static_cast<unsigned __int128>(lhs) %
-         static_cast<unsigned __int128>(rhs);
-#else  // ABSL_HAVE_INTRINSIC_INT128
   uint128 quotient = 0;
   uint128 remainder = 0;
   DivModImpl(lhs, rhs, &quotient, &remainder);
   return remainder;
-#endif  // ABSL_HAVE_INTRINSIC_INT128
 }
+#endif  // !defined(ABSL_HAVE_INTRINSIC_INT128)
 
 namespace {
 
diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h
index 0dd814a..c7ad96b 100644
--- a/absl/numeric/int128.h
+++ b/absl/numeric/int128.h
@@ -18,6 +18,10 @@
 // -----------------------------------------------------------------------------
 //
 // This header file defines 128-bit integer types, `uint128` and `int128`.
+//
+// TODO(absl-team): This module is inconsistent as many inline `uint128` methods
+// are defined in this file, while many inline `int128` methods are defined in
+// the `int128_*_intrinsic.inc` files.
 
 #ifndef ABSL_NUMERIC_INT128_H_
 #define ABSL_NUMERIC_INT128_H_
@@ -582,10 +586,10 @@
 
 // Arithmetic operators.
 
-uint128 operator<<(uint128 lhs, int amount);
-uint128 operator>>(uint128 lhs, int amount);
-uint128 operator+(uint128 lhs, uint128 rhs);
-uint128 operator-(uint128 lhs, uint128 rhs);
+constexpr uint128 operator<<(uint128 lhs, int amount);
+constexpr uint128 operator>>(uint128 lhs, int amount);
+constexpr uint128 operator+(uint128 lhs, uint128 rhs);
+constexpr uint128 operator-(uint128 lhs, uint128 rhs);
 uint128 operator*(uint128 lhs, uint128 rhs);
 uint128 operator/(uint128 lhs, uint128 rhs);
 uint128 operator%(uint128 lhs, uint128 rhs);
@@ -782,16 +786,19 @@
 
 // Comparison operators.
 
-inline bool operator==(uint128 lhs, uint128 rhs) {
+constexpr bool operator==(uint128 lhs, uint128 rhs) {
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+  return static_cast<unsigned __int128>(lhs) ==
+         static_cast<unsigned __int128>(rhs);
+#else
   return (Uint128Low64(lhs) == Uint128Low64(rhs) &&
           Uint128High64(lhs) == Uint128High64(rhs));
+#endif
 }
 
-inline bool operator!=(uint128 lhs, uint128 rhs) {
-  return !(lhs == rhs);
-}
+constexpr bool operator!=(uint128 lhs, uint128 rhs) { return !(lhs == rhs); }
 
-inline bool operator<(uint128 lhs, uint128 rhs) {
+constexpr bool operator<(uint128 lhs, uint128 rhs) {
 #ifdef ABSL_HAVE_INTRINSIC_INT128
   return static_cast<unsigned __int128>(lhs) <
          static_cast<unsigned __int128>(rhs);
@@ -802,118 +809,169 @@
 #endif
 }
 
-inline bool operator>(uint128 lhs, uint128 rhs) { return rhs < lhs; }
+constexpr bool operator>(uint128 lhs, uint128 rhs) { return rhs < lhs; }
 
-inline bool operator<=(uint128 lhs, uint128 rhs) { return !(rhs < lhs); }
+constexpr bool operator<=(uint128 lhs, uint128 rhs) { return !(rhs < lhs); }
 
-inline bool operator>=(uint128 lhs, uint128 rhs) { return !(lhs < rhs); }
+constexpr bool operator>=(uint128 lhs, uint128 rhs) { return !(lhs < rhs); }
 
 // Unary operators.
 
-inline uint128 operator-(uint128 val) {
-  uint64_t hi = ~Uint128High64(val);
-  uint64_t lo = ~Uint128Low64(val) + 1;
-  if (lo == 0) ++hi;  // carry
-  return MakeUint128(hi, lo);
+constexpr inline uint128 operator+(uint128 val) {
+  return val;
 }
 
-inline bool operator!(uint128 val) {
+constexpr inline int128 operator+(int128 val) {
+  return val;
+}
+
+constexpr uint128 operator-(uint128 val) {
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+  return -static_cast<unsigned __int128>(val);
+#else
+  return MakeUint128(
+      ~Uint128High64(val) + static_cast<unsigned long>(Uint128Low64(val) == 0),
+      ~Uint128Low64(val) + 1);
+#endif
+}
+
+constexpr inline bool operator!(uint128 val) {
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+  return !static_cast<unsigned __int128>(val);
+#else
   return !Uint128High64(val) && !Uint128Low64(val);
+#endif
 }
 
 // Logical operators.
 
-inline uint128 operator~(uint128 val) {
+constexpr inline uint128 operator~(uint128 val) {
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+  return ~static_cast<unsigned __int128>(val);
+#else
   return MakeUint128(~Uint128High64(val), ~Uint128Low64(val));
+#endif
 }
 
-inline uint128 operator|(uint128 lhs, uint128 rhs) {
+constexpr inline uint128 operator|(uint128 lhs, uint128 rhs) {
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+  return static_cast<unsigned __int128>(lhs) |
+         static_cast<unsigned __int128>(rhs);
+#else
   return MakeUint128(Uint128High64(lhs) | Uint128High64(rhs),
-                           Uint128Low64(lhs) | Uint128Low64(rhs));
+                     Uint128Low64(lhs) | Uint128Low64(rhs));
+#endif
 }
 
-inline uint128 operator&(uint128 lhs, uint128 rhs) {
+constexpr inline uint128 operator&(uint128 lhs, uint128 rhs) {
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+  return static_cast<unsigned __int128>(lhs) &
+         static_cast<unsigned __int128>(rhs);
+#else
   return MakeUint128(Uint128High64(lhs) & Uint128High64(rhs),
-                           Uint128Low64(lhs) & Uint128Low64(rhs));
+                     Uint128Low64(lhs) & Uint128Low64(rhs));
+#endif
 }
 
-inline uint128 operator^(uint128 lhs, uint128 rhs) {
+constexpr inline uint128 operator^(uint128 lhs, uint128 rhs) {
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+  return static_cast<unsigned __int128>(lhs) ^
+         static_cast<unsigned __int128>(rhs);
+#else
   return MakeUint128(Uint128High64(lhs) ^ Uint128High64(rhs),
-                           Uint128Low64(lhs) ^ Uint128Low64(rhs));
+                     Uint128Low64(lhs) ^ Uint128Low64(rhs));
+#endif
 }
 
 inline uint128& uint128::operator|=(uint128 other) {
-  hi_ |= other.hi_;
-  lo_ |= other.lo_;
+  *this = *this | other;
   return *this;
 }
 
 inline uint128& uint128::operator&=(uint128 other) {
-  hi_ &= other.hi_;
-  lo_ &= other.lo_;
+  *this = *this & other;
   return *this;
 }
 
 inline uint128& uint128::operator^=(uint128 other) {
-  hi_ ^= other.hi_;
-  lo_ ^= other.lo_;
+  *this = *this ^ other;
   return *this;
 }
 
 // Arithmetic operators.
 
-inline uint128 operator<<(uint128 lhs, int amount) {
+constexpr uint128 operator<<(uint128 lhs, int amount) {
 #ifdef ABSL_HAVE_INTRINSIC_INT128
   return static_cast<unsigned __int128>(lhs) << amount;
 #else
   // uint64_t shifts of >= 64 are undefined, so we will need some
   // special-casing.
-  if (amount < 64) {
-    if (amount != 0) {
-      return MakeUint128(
-          (Uint128High64(lhs) << amount) | (Uint128Low64(lhs) >> (64 - amount)),
-          Uint128Low64(lhs) << amount);
-    }
-    return lhs;
-  }
-  return MakeUint128(Uint128Low64(lhs) << (amount - 64), 0);
+  return amount >= 64 ? MakeUint128(Uint128Low64(lhs) << (amount - 64), 0)
+         : amount == 0 ? lhs
+                       : MakeUint128((Uint128High64(lhs) << amount) |
+                                         (Uint128Low64(lhs) >> (64 - amount)),
+                                     Uint128Low64(lhs) << amount);
 #endif
 }
 
-inline uint128 operator>>(uint128 lhs, int amount) {
+constexpr uint128 operator>>(uint128 lhs, int amount) {
 #ifdef ABSL_HAVE_INTRINSIC_INT128
   return static_cast<unsigned __int128>(lhs) >> amount;
 #else
   // uint64_t shifts of >= 64 are undefined, so we will need some
   // special-casing.
-  if (amount < 64) {
-    if (amount != 0) {
-      return MakeUint128(Uint128High64(lhs) >> amount,
-                         (Uint128Low64(lhs) >> amount) |
-                             (Uint128High64(lhs) << (64 - amount)));
-    }
-    return lhs;
-  }
-  return MakeUint128(0, Uint128High64(lhs) >> (amount - 64));
+  return amount >= 64 ? MakeUint128(0, Uint128High64(lhs) >> (amount - 64))
+         : amount == 0 ? lhs
+                       : MakeUint128(Uint128High64(lhs) >> amount,
+                                     (Uint128Low64(lhs) >> amount) |
+                                         (Uint128High64(lhs) << (64 - amount)));
 #endif
 }
 
-inline uint128 operator+(uint128 lhs, uint128 rhs) {
-  uint128 result = MakeUint128(Uint128High64(lhs) + Uint128High64(rhs),
-                               Uint128Low64(lhs) + Uint128Low64(rhs));
-  if (Uint128Low64(result) < Uint128Low64(lhs)) {  // check for carry
-    return MakeUint128(Uint128High64(result) + 1, Uint128Low64(result));
-  }
-  return result;
+#if !defined(ABSL_HAVE_INTRINSIC_INT128)
+namespace int128_internal {
+constexpr uint128 AddResult(uint128 result, uint128 lhs) {
+  // check for carry
+  return (Uint128Low64(result) < Uint128Low64(lhs))
+             ? MakeUint128(Uint128High64(result) + 1, Uint128Low64(result))
+             : result;
+}
+}  // namespace int128_internal
+#endif
+
+constexpr uint128 operator+(uint128 lhs, uint128 rhs) {
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+  return static_cast<unsigned __int128>(lhs) +
+         static_cast<unsigned __int128>(rhs);
+#else
+  return int128_internal::AddResult(
+      MakeUint128(Uint128High64(lhs) + Uint128High64(rhs),
+                  Uint128Low64(lhs) + Uint128Low64(rhs)),
+      lhs);
+#endif
 }
 
-inline uint128 operator-(uint128 lhs, uint128 rhs) {
-  uint128 result = MakeUint128(Uint128High64(lhs) - Uint128High64(rhs),
-                               Uint128Low64(lhs) - Uint128Low64(rhs));
-  if (Uint128Low64(lhs) < Uint128Low64(rhs)) {  // check for carry
-    return MakeUint128(Uint128High64(result) - 1, Uint128Low64(result));
-  }
-  return result;
+#if !defined(ABSL_HAVE_INTRINSIC_INT128)
+namespace int128_internal {
+constexpr uint128 SubstructResult(uint128 result, uint128 lhs, uint128 rhs) {
+  // check for carry
+  return (Uint128Low64(lhs) < Uint128Low64(rhs))
+             ? MakeUint128(Uint128High64(result) - 1, Uint128Low64(result))
+             : result;
+}
+}  // namespace int128_internal
+#endif
+
+constexpr uint128 operator-(uint128 lhs, uint128 rhs) {
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+  return static_cast<unsigned __int128>(lhs) -
+         static_cast<unsigned __int128>(rhs);
+#else
+  return int128_internal::SubstructResult(
+      MakeUint128(Uint128High64(lhs) - Uint128High64(rhs),
+                  Uint128Low64(lhs) - Uint128Low64(rhs)),
+      lhs, rhs);
+#endif
 }
 
 inline uint128 operator*(uint128 lhs, uint128 rhs) {
@@ -943,6 +1001,18 @@
 #endif  // ABSL_HAVE_INTRINSIC128
 }
 
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+inline uint128 operator/(uint128 lhs, uint128 rhs) {
+  return static_cast<unsigned __int128>(lhs) /
+         static_cast<unsigned __int128>(rhs);
+}
+
+inline uint128 operator%(uint128 lhs, uint128 rhs) {
+  return static_cast<unsigned __int128>(lhs) %
+         static_cast<unsigned __int128>(rhs);
+}
+#endif
+
 // Increment/decrement operators.
 
 inline uint128 uint128::operator++(int) {
@@ -1000,17 +1070,17 @@
 }
 
 // Arithmetic operators.
-
-int128 operator+(int128 lhs, int128 rhs);
-int128 operator-(int128 lhs, int128 rhs);
+constexpr int128 operator-(int128 v);
+constexpr int128 operator+(int128 lhs, int128 rhs);
+constexpr int128 operator-(int128 lhs, int128 rhs);
 int128 operator*(int128 lhs, int128 rhs);
 int128 operator/(int128 lhs, int128 rhs);
 int128 operator%(int128 lhs, int128 rhs);
-int128 operator|(int128 lhs, int128 rhs);
-int128 operator&(int128 lhs, int128 rhs);
-int128 operator^(int128 lhs, int128 rhs);
-int128 operator<<(int128 lhs, int amount);
-int128 operator>>(int128 lhs, int amount);
+constexpr int128 operator|(int128 lhs, int128 rhs);
+constexpr int128 operator&(int128 lhs, int128 rhs);
+constexpr int128 operator^(int128 lhs, int128 rhs);
+constexpr int128 operator<<(int128 lhs, int amount);
+constexpr int128 operator>>(int128 lhs, int amount);
 
 inline int128& int128::operator+=(int128 other) {
   *this = *this + other;
@@ -1062,6 +1132,9 @@
   return *this;
 }
 
+// Forward declaration for comparison operators.
+constexpr bool operator!=(int128 lhs, int128 rhs);
+
 namespace int128_internal {
 
 // Casts from unsigned to signed while preserving the underlying binary
diff --git a/absl/numeric/int128_have_intrinsic.inc b/absl/numeric/int128_have_intrinsic.inc
index d6c76dd..3945fa2 100644
--- a/absl/numeric/int128_have_intrinsic.inc
+++ b/absl/numeric/int128_have_intrinsic.inc
@@ -155,7 +155,7 @@
 #if defined(__clang__) && !defined(__ppc64__)
 inline int128::operator float() const { return static_cast<float>(v_); }
 
-inline int128::operator double () const { return static_cast<double>(v_); }
+inline int128::operator double() const { return static_cast<double>(v_); }
 
 inline int128::operator long double() const {
   return static_cast<long double>(v_);
@@ -163,8 +163,8 @@
 
 #else  // Clang on PowerPC
 // Forward declaration for conversion operators to floating point types.
-int128 operator-(int128 v);
-bool operator!=(int128 lhs, int128 rhs);
+constexpr int128 operator-(int128 v);
+constexpr bool operator!=(int128 lhs, int128 rhs);
 
 inline int128::operator float() const {
   // We must convert the absolute value and then negate as needed, because
@@ -199,51 +199,45 @@
 
 // Comparison operators.
 
-inline bool operator==(int128 lhs, int128 rhs) {
+constexpr bool operator==(int128 lhs, int128 rhs) {
   return static_cast<__int128>(lhs) == static_cast<__int128>(rhs);
 }
 
-inline bool operator!=(int128 lhs, int128 rhs) {
+constexpr bool operator!=(int128 lhs, int128 rhs) {
   return static_cast<__int128>(lhs) != static_cast<__int128>(rhs);
 }
 
-inline bool operator<(int128 lhs, int128 rhs) {
+constexpr bool operator<(int128 lhs, int128 rhs) {
   return static_cast<__int128>(lhs) < static_cast<__int128>(rhs);
 }
 
-inline bool operator>(int128 lhs, int128 rhs) {
+constexpr bool operator>(int128 lhs, int128 rhs) {
   return static_cast<__int128>(lhs) > static_cast<__int128>(rhs);
 }
 
-inline bool operator<=(int128 lhs, int128 rhs) {
+constexpr bool operator<=(int128 lhs, int128 rhs) {
   return static_cast<__int128>(lhs) <= static_cast<__int128>(rhs);
 }
 
-inline bool operator>=(int128 lhs, int128 rhs) {
+constexpr bool operator>=(int128 lhs, int128 rhs) {
   return static_cast<__int128>(lhs) >= static_cast<__int128>(rhs);
 }
 
 // Unary operators.
 
-inline int128 operator-(int128 v) {
-  return -static_cast<__int128>(v);
-}
+constexpr int128 operator-(int128 v) { return -static_cast<__int128>(v); }
 
-inline bool operator!(int128 v) {
-  return !static_cast<__int128>(v);
-}
+constexpr bool operator!(int128 v) { return !static_cast<__int128>(v); }
 
-inline int128 operator~(int128 val) {
-  return ~static_cast<__int128>(val);
-}
+constexpr int128 operator~(int128 val) { return ~static_cast<__int128>(val); }
 
 // Arithmetic operators.
 
-inline int128 operator+(int128 lhs, int128 rhs) {
+constexpr int128 operator+(int128 lhs, int128 rhs) {
   return static_cast<__int128>(lhs) + static_cast<__int128>(rhs);
 }
 
-inline int128 operator-(int128 lhs, int128 rhs) {
+constexpr int128 operator-(int128 lhs, int128 rhs) {
   return static_cast<__int128>(lhs) - static_cast<__int128>(rhs);
 }
 
@@ -281,22 +275,22 @@
   return *this;
 }
 
-inline int128 operator|(int128 lhs, int128 rhs) {
+constexpr int128 operator|(int128 lhs, int128 rhs) {
   return static_cast<__int128>(lhs) | static_cast<__int128>(rhs);
 }
 
-inline int128 operator&(int128 lhs, int128 rhs) {
+constexpr int128 operator&(int128 lhs, int128 rhs) {
   return static_cast<__int128>(lhs) & static_cast<__int128>(rhs);
 }
 
-inline int128 operator^(int128 lhs, int128 rhs) {
+constexpr int128 operator^(int128 lhs, int128 rhs) {
   return static_cast<__int128>(lhs) ^ static_cast<__int128>(rhs);
 }
 
-inline int128 operator<<(int128 lhs, int amount) {
+constexpr int128 operator<<(int128 lhs, int amount) {
   return static_cast<__int128>(lhs) << amount;
 }
 
-inline int128 operator>>(int128 lhs, int amount) {
+constexpr int128 operator>>(int128 lhs, int amount) {
   return static_cast<__int128>(lhs) >> amount;
 }
diff --git a/absl/numeric/int128_no_intrinsic.inc b/absl/numeric/int128_no_intrinsic.inc
index c753771..8834804 100644
--- a/absl/numeric/int128_no_intrinsic.inc
+++ b/absl/numeric/int128_no_intrinsic.inc
@@ -134,10 +134,6 @@
   return static_cast<unsigned long long>(lo_);           // NOLINT(runtime/int)
 }
 
-// Forward declaration for conversion operators to floating point types.
-int128 operator-(int128 v);
-bool operator!=(int128 lhs, int128 rhs);
-
 inline int128::operator float() const {
   // We must convert the absolute value and then negate as needed, because
   // floating point types are typically sign-magnitude. Otherwise, the
@@ -169,76 +165,80 @@
 
 // Comparison operators.
 
-inline bool operator==(int128 lhs, int128 rhs) {
+constexpr bool operator==(int128 lhs, int128 rhs) {
   return (Int128Low64(lhs) == Int128Low64(rhs) &&
           Int128High64(lhs) == Int128High64(rhs));
 }
 
-inline bool operator!=(int128 lhs, int128 rhs) {
-  return !(lhs == rhs);
-}
+constexpr bool operator!=(int128 lhs, int128 rhs) { return !(lhs == rhs); }
 
-inline bool operator<(int128 lhs, int128 rhs) {
+constexpr bool operator<(int128 lhs, int128 rhs) {
   return (Int128High64(lhs) == Int128High64(rhs))
              ? (Int128Low64(lhs) < Int128Low64(rhs))
              : (Int128High64(lhs) < Int128High64(rhs));
 }
 
-inline bool operator>(int128 lhs, int128 rhs) {
+constexpr bool operator>(int128 lhs, int128 rhs) {
   return (Int128High64(lhs) == Int128High64(rhs))
              ? (Int128Low64(lhs) > Int128Low64(rhs))
              : (Int128High64(lhs) > Int128High64(rhs));
 }
 
-inline bool operator<=(int128 lhs, int128 rhs) {
-  return !(lhs > rhs);
-}
+constexpr bool operator<=(int128 lhs, int128 rhs) { return !(lhs > rhs); }
 
-inline bool operator>=(int128 lhs, int128 rhs) {
-  return !(lhs < rhs);
-}
+constexpr bool operator>=(int128 lhs, int128 rhs) { return !(lhs < rhs); }
 
 // Unary operators.
 
-inline int128 operator-(int128 v) {
-  int64_t hi = ~Int128High64(v);
-  uint64_t lo = ~Int128Low64(v) + 1;
-  if (lo == 0) ++hi;  // carry
-  return MakeInt128(hi, lo);
+constexpr int128 operator-(int128 v) {
+  return MakeInt128(~Int128High64(v) + (Int128Low64(v) == 0),
+                    ~Int128Low64(v) + 1);
 }
 
-inline bool operator!(int128 v) {
+constexpr bool operator!(int128 v) {
   return !Int128Low64(v) && !Int128High64(v);
 }
 
-inline int128 operator~(int128 val) {
+constexpr int128 operator~(int128 val) {
   return MakeInt128(~Int128High64(val), ~Int128Low64(val));
 }
 
 // Arithmetic operators.
 
-inline int128 operator+(int128 lhs, int128 rhs) {
-  int128 result = MakeInt128(Int128High64(lhs) + Int128High64(rhs),
-                             Int128Low64(lhs) + Int128Low64(rhs));
-  if (Int128Low64(result) < Int128Low64(lhs)) {  // check for carry
-    return MakeInt128(Int128High64(result) + 1, Int128Low64(result));
-  }
-  return result;
+namespace int128_internal {
+constexpr int128 SignedAddResult(int128 result, int128 lhs) {
+  // check for carry
+  return (Int128Low64(result) < Int128Low64(lhs))
+             ? MakeInt128(Int128High64(result) + 1, Int128Low64(result))
+             : result;
+}
+}  // namespace int128_internal
+constexpr int128 operator+(int128 lhs, int128 rhs) {
+  return int128_internal::SignedAddResult(
+      MakeInt128(Int128High64(lhs) + Int128High64(rhs),
+                 Int128Low64(lhs) + Int128Low64(rhs)),
+      lhs);
 }
 
-inline int128 operator-(int128 lhs, int128 rhs) {
-  int128 result = MakeInt128(Int128High64(lhs) - Int128High64(rhs),
-                             Int128Low64(lhs) - Int128Low64(rhs));
-  if (Int128Low64(lhs) < Int128Low64(rhs)) {  // check for carry
-    return MakeInt128(Int128High64(result) - 1, Int128Low64(result));
-  }
-  return result;
+namespace int128_internal {
+constexpr int128 SignedSubstructResult(int128 result, int128 lhs, int128 rhs) {
+  // check for carry
+  return (Int128Low64(lhs) < Int128Low64(rhs))
+             ? MakeInt128(Int128High64(result) - 1, Int128Low64(result))
+             : result;
+}
+}  // namespace int128_internal
+constexpr int128 operator-(int128 lhs, int128 rhs) {
+  return int128_internal::SignedSubstructResult(
+      MakeInt128(Int128High64(lhs) - Int128High64(rhs),
+                 Int128Low64(lhs) - Int128Low64(rhs)),
+      lhs, rhs);
 }
 
 inline int128 operator*(int128 lhs, int128 rhs) {
-  uint128 result = uint128(lhs) * rhs;
-  return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(result)),
-                    Uint128Low64(result));
+  return MakeInt128(
+      int128_internal::BitCastToSigned(Uint128High64(uint128(lhs) * rhs)),
+      Uint128Low64(uint128(lhs) * rhs));
 }
 
 inline int128 int128::operator++(int) {
@@ -263,46 +263,49 @@
   return *this;
 }
 
-inline int128 operator|(int128 lhs, int128 rhs) {
+constexpr int128 operator|(int128 lhs, int128 rhs) {
   return MakeInt128(Int128High64(lhs) | Int128High64(rhs),
                     Int128Low64(lhs) | Int128Low64(rhs));
 }
 
-inline int128 operator&(int128 lhs, int128 rhs) {
+constexpr int128 operator&(int128 lhs, int128 rhs) {
   return MakeInt128(Int128High64(lhs) & Int128High64(rhs),
                     Int128Low64(lhs) & Int128Low64(rhs));
 }
 
-inline int128 operator^(int128 lhs, int128 rhs) {
+constexpr int128 operator^(int128 lhs, int128 rhs) {
   return MakeInt128(Int128High64(lhs) ^ Int128High64(rhs),
                     Int128Low64(lhs) ^ Int128Low64(rhs));
 }
 
-inline int128 operator<<(int128 lhs, int amount) {
-  // uint64_t shifts of >= 64 are undefined, so we need some special-casing.
-  if (amount < 64) {
-    if (amount != 0) {
-      return MakeInt128(
-          (Int128High64(lhs) << amount) |
-              static_cast<int64_t>(Int128Low64(lhs) >> (64 - amount)),
-          Int128Low64(lhs) << amount);
-    }
-    return lhs;
-  }
-  return MakeInt128(static_cast<int64_t>(Int128Low64(lhs) << (amount - 64)), 0);
+constexpr int128 operator<<(int128 lhs, int amount) {
+  // int64_t shifts of >= 64 are undefined, so we need some special-casing.
+  return amount >= 64
+             ? MakeInt128(
+                   static_cast<int64_t>(Int128Low64(lhs) << (amount - 64)), 0)
+         : amount == 0
+             ? lhs
+             : MakeInt128(
+                   (Int128High64(lhs) << amount) |
+                       static_cast<int64_t>(Int128Low64(lhs) >> (64 - amount)),
+                   Int128Low64(lhs) << amount);
 }
 
-inline int128 operator>>(int128 lhs, int amount) {
-  // uint64_t shifts of >= 64 are undefined, so we need some special-casing.
-  if (amount < 64) {
-    if (amount != 0) {
-      return MakeInt128(
-          Int128High64(lhs) >> amount,
-          (Int128Low64(lhs) >> amount) |
-              (static_cast<uint64_t>(Int128High64(lhs)) << (64 - amount)));
-    }
-    return lhs;
-  }
-  return MakeInt128(0,
-                    static_cast<uint64_t>(Int128High64(lhs) >> (amount - 64)));
+constexpr int128 operator>>(int128 lhs, int amount) {
+  // int64_t shifts of >= 64 are undefined, so we need some special-casing.
+  // The (Int128High64(lhs) >> 32) >> 32 "trick" causes the the most significant
+  // int64 to be inititialized with all zeros or all ones correctly. It takes
+  // into account whether the number is negative or positive, and whether the
+  // current architecture does arithmetic or logical right shifts for negative
+  // numbers.
+  return amount >= 64
+             ? MakeInt128(
+                   (Int128High64(lhs) >> 32) >> 32,
+                   static_cast<uint64_t>(Int128High64(lhs) >> (amount - 64)))
+         : amount == 0
+             ? lhs
+             : MakeInt128(Int128High64(lhs) >> amount,
+                          (Int128Low64(lhs) >> amount) |
+                              (static_cast<uint64_t>(Int128High64(lhs))
+                               << (64 - amount)));
 }
diff --git a/absl/numeric/int128_test.cc b/absl/numeric/int128_test.cc
index bc86c71..dd9425d 100644
--- a/absl/numeric/int128_test.cc
+++ b/absl/numeric/int128_test.cc
@@ -226,6 +226,11 @@
   EXPECT_EQ(test >>= 1, one);
   EXPECT_EQ(test <<= 1, two);
 
+  EXPECT_EQ(big, +big);
+  EXPECT_EQ(two, +two);
+  EXPECT_EQ(absl::Uint128Max(), +absl::Uint128Max());
+  EXPECT_EQ(zero, +zero);
+
   EXPECT_EQ(big, -(-big));
   EXPECT_EQ(two, -((-one) - 1));
   EXPECT_EQ(absl::Uint128Max(), -one);
@@ -234,6 +239,24 @@
   EXPECT_EQ(absl::Uint128Max(), absl::kuint128max);
 }
 
+TEST(Int128, RightShiftOfNegativeNumbers) {
+  absl::int128 minus_six = -6;
+  absl::int128 minus_three = -3;
+  absl::int128 minus_two = -2;
+  absl::int128 minus_one = -1;
+  if ((-6 >> 1) == -3) {
+    // Right shift is arithmetic (sign propagates)
+    EXPECT_EQ(minus_six >> 1, minus_three);
+    EXPECT_EQ(minus_six >> 2, minus_two);
+    EXPECT_EQ(minus_six >> 65, minus_one);
+  } else {
+    // Right shift is logical (zeros shifted in at MSB)
+    EXPECT_EQ(minus_six >> 1, absl::int128(absl::uint128(minus_six) >> 1));
+    EXPECT_EQ(minus_six >> 2, absl::int128(absl::uint128(minus_six) >> 2));
+    EXPECT_EQ(minus_six >> 65, absl::int128(absl::uint128(minus_six) >> 65));
+  }
+}
+
 TEST(Uint128, ConversionTests) {
   EXPECT_TRUE(absl::MakeUint128(1, 0));
 
@@ -769,6 +792,19 @@
   }
 }
 
+TEST(Int128, UnaryPlusTest) {
+  int64_t values64[] = {0, 1, 12345, 0x4000000000000000,
+                        std::numeric_limits<int64_t>::max()};
+  for (int64_t value : values64) {
+    SCOPED_TRACE(::testing::Message() << "value = " << value);
+
+    EXPECT_EQ(absl::int128(value), +absl::int128(value));
+    EXPECT_EQ(absl::int128(-value), +absl::int128(-value));
+    EXPECT_EQ(absl::MakeInt128(value, 0), +absl::MakeInt128(value, 0));
+    EXPECT_EQ(absl::MakeInt128(-value, 0), +absl::MakeInt128(-value, 0));
+  }
+}
+
 TEST(Int128, UnaryNegationTest) {
   int64_t values64[] = {0, 1, 12345, 0x4000000000000000,
                         std::numeric_limits<int64_t>::max()};
diff --git a/absl/profiling/BUILD.bazel b/absl/profiling/BUILD.bazel
new file mode 100644
index 0000000..496a06b
--- /dev/null
+++ b/absl/profiling/BUILD.bazel
@@ -0,0 +1,126 @@
+# Copyright 2021 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.
+
+load(
+    "//absl:copts/configure_copts.bzl",
+    "ABSL_DEFAULT_COPTS",
+    "ABSL_DEFAULT_LINKOPTS",
+    "ABSL_TEST_COPTS",
+)
+
+package(default_visibility = ["//visibility:private"])
+
+licenses(["notice"])
+
+cc_library(
+    name = "sample_recorder",
+    hdrs = ["internal/sample_recorder.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl:__subpackages__",
+    ],
+    deps = [
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/synchronization",
+        "//absl/time",
+    ],
+)
+
+cc_test(
+    name = "sample_recorder_test",
+    srcs = ["internal/sample_recorder_test.cc"],
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":sample_recorder",
+        "//absl/base:core_headers",
+        "//absl/synchronization",
+        "//absl/synchronization:thread_pool",
+        "//absl/time",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "exponential_biased",
+    srcs = ["internal/exponential_biased.cc"],
+    hdrs = ["internal/exponential_biased.h"],
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl:__subpackages__",
+    ],
+    deps = [
+        "//absl/base:config",
+        "//absl/base:core_headers",
+    ],
+)
+
+cc_test(
+    name = "exponential_biased_test",
+    size = "small",
+    srcs = ["internal/exponential_biased_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":exponential_biased",
+        "//absl/strings",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "periodic_sampler",
+    srcs = ["internal/periodic_sampler.cc"],
+    hdrs = ["internal/periodic_sampler.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//absl:__subpackages__",
+    ],
+    deps = [
+        ":exponential_biased",
+        "//absl/base:core_headers",
+    ],
+)
+
+cc_test(
+    name = "periodic_sampler_test",
+    size = "small",
+    srcs = ["internal/periodic_sampler_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":periodic_sampler",
+        "//absl/base:core_headers",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_binary(
+    name = "periodic_sampler_benchmark",
+    testonly = 1,
+    srcs = ["internal/periodic_sampler_benchmark.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    tags = ["benchmark"],
+    visibility = ["//visibility:private"],
+    deps = [
+        ":periodic_sampler",
+        "//absl/base:core_headers",
+        "@com_github_google_benchmark//:benchmark_main",
+    ],
+)
diff --git a/absl/profiling/CMakeLists.txt b/absl/profiling/CMakeLists.txt
new file mode 100644
index 0000000..9b3a710
--- /dev/null
+++ b/absl/profiling/CMakeLists.txt
@@ -0,0 +1,93 @@
+# Copyright 2021 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.
+
+absl_cc_library(
+  NAME
+    sample_recorder
+  HDRS
+    "internal/sample_recorder.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::base
+    absl::synchronization
+)
+
+absl_cc_test(
+  NAME
+    sample_recorder_test
+  SRCS
+    "internal/sample_recorder_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::sample_recorder
+    absl::time
+    GTest::gmock_main
+)
+
+absl_cc_library(
+  NAME
+    exponential_biased
+  SRCS
+    "internal/exponential_biased.cc"
+  HDRS
+    "internal/exponential_biased.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::config
+    absl::core_headers
+)
+
+absl_cc_test(
+  NAME
+    exponential_biased_test
+  SRCS
+    "internal/exponential_biased_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::exponential_biased
+    absl::strings
+    GTest::gmock_main
+)
+
+absl_cc_library(
+  NAME
+    periodic_sampler
+  SRCS
+    "internal/periodic_sampler.cc"
+  HDRS
+    "internal/periodic_sampler.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::core_headers
+    absl::exponential_biased
+)
+
+absl_cc_test(
+  NAME
+    periodic_sampler_test
+  SRCS
+    "internal/periodic_sampler_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::core_headers
+    absl::periodic_sampler
+    GTest::gmock_main
+)
+
diff --git a/absl/base/internal/exponential_biased.cc b/absl/profiling/internal/exponential_biased.cc
similarity index 95%
rename from absl/base/internal/exponential_biased.cc
rename to absl/profiling/internal/exponential_biased.cc
index 1b30c06..81d9a75 100644
--- a/absl/base/internal/exponential_biased.cc
+++ b/absl/profiling/internal/exponential_biased.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "absl/base/internal/exponential_biased.h"
+#include "absl/profiling/internal/exponential_biased.h"
 
 #include <stdint.h>
 
@@ -26,7 +26,7 @@
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
-namespace base_internal {
+namespace profiling_internal {
 
 // The algorithm generates a random number between 0 and 1 and applies the
 // inverse cumulative distribution function for an exponential. Specifically:
@@ -64,7 +64,7 @@
     // Assume huge values are bias neutral, retain bias for next call.
     return std::numeric_limits<int64_t>::max() / 2;
   }
-  double value = std::round(interval);
+  double value = std::rint(interval);
   bias_ = interval - value;
   return value;
 }
@@ -88,6 +88,6 @@
   initialized_ = true;
 }
 
-}  // namespace base_internal
+}  // namespace profiling_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/base/internal/exponential_biased.h b/absl/profiling/internal/exponential_biased.h
similarity index 94%
rename from absl/base/internal/exponential_biased.h
rename to absl/profiling/internal/exponential_biased.h
index 94f79a3..d31f778 100644
--- a/absl/base/internal/exponential_biased.h
+++ b/absl/profiling/internal/exponential_biased.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_
-#define ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_
+#ifndef ABSL_PROFILING_INTERNAL_EXPONENTIAL_BIASED_H_
+#define ABSL_PROFILING_INTERNAL_EXPONENTIAL_BIASED_H_
 
 #include <stdint.h>
 
@@ -22,7 +22,7 @@
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
-namespace base_internal {
+namespace profiling_internal {
 
 // ExponentialBiased provides a small and fast random number generator for a
 // rounded exponential distribution. This generator manages very little state,
@@ -66,7 +66,7 @@
 // Adjusting with rounding bias is relatively trivial:
 //
 //    double value = bias_ + exponential_distribution(mean)();
-//    double rounded_value = std::round(value);
+//    double rounded_value = std::rint(value);
 //    bias_ = value - rounded_value;
 //    return rounded_value;
 //
@@ -123,8 +123,8 @@
   return (prng_mult * rnd + prng_add) & prng_mod_mask;
 }
 
-}  // namespace base_internal
+}  // namespace profiling_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
 
-#endif  // ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_
+#endif  // ABSL_PROFILING_INTERNAL_EXPONENTIAL_BIASED_H_
diff --git a/absl/base/internal/exponential_biased_test.cc b/absl/profiling/internal/exponential_biased_test.cc
similarity index 97%
rename from absl/base/internal/exponential_biased_test.cc
rename to absl/profiling/internal/exponential_biased_test.cc
index 075583c..5675001 100644
--- a/absl/base/internal/exponential_biased_test.cc
+++ b/absl/profiling/internal/exponential_biased_test.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "absl/base/internal/exponential_biased.h"
+#include "absl/profiling/internal/exponential_biased.h"
 
 #include <stddef.h>
 
@@ -28,7 +28,7 @@
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
-namespace base_internal {
+namespace profiling_internal {
 
 MATCHER_P2(IsBetween, a, b,
            absl::StrCat(std::string(negation ? "isn't" : "is"), " between ", a,
@@ -194,6 +194,6 @@
   EXPECT_THAT(eb_stack.GetSkipCount(2), Ge(0));
 }
 
-}  // namespace base_internal
+}  // namespace profiling_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/base/internal/periodic_sampler.cc b/absl/profiling/internal/periodic_sampler.cc
similarity index 88%
rename from absl/base/internal/periodic_sampler.cc
rename to absl/profiling/internal/periodic_sampler.cc
index 520dabb..a738a82 100644
--- a/absl/base/internal/periodic_sampler.cc
+++ b/absl/profiling/internal/periodic_sampler.cc
@@ -12,15 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "absl/base/internal/periodic_sampler.h"
+#include "absl/profiling/internal/periodic_sampler.h"
 
 #include <atomic>
 
-#include "absl/base/internal/exponential_biased.h"
+#include "absl/profiling/internal/exponential_biased.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
-namespace base_internal {
+namespace profiling_internal {
 
 int64_t PeriodicSamplerBase::GetExponentialBiased(int period) noexcept {
   return rng_.GetStride(period);
@@ -48,6 +48,6 @@
   return true;
 }
 
-}  // namespace base_internal
+}  // namespace profiling_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/base/internal/periodic_sampler.h b/absl/profiling/internal/periodic_sampler.h
similarity index 95%
rename from absl/base/internal/periodic_sampler.h
rename to absl/profiling/internal/periodic_sampler.h
index f8a8679..54f0af4 100644
--- a/absl/base/internal/periodic_sampler.h
+++ b/absl/profiling/internal/periodic_sampler.h
@@ -12,19 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_
-#define ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_
+#ifndef ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_
+#define ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_
 
 #include <stdint.h>
 
 #include <atomic>
 
-#include "absl/base/internal/exponential_biased.h"
 #include "absl/base/optimization.h"
+#include "absl/profiling/internal/exponential_biased.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
-namespace base_internal {
+namespace profiling_internal {
 
 // PeriodicSamplerBase provides the basic period sampler implementation.
 //
@@ -149,7 +149,7 @@
   //   ICC   x64 (OK) : https://gcc.godbolt.org/z/ptTNfD
   //   MSVC  x64 (OK) : https://gcc.godbolt.org/z/76j4-5
   uint64_t stride_ = 0;
-  ExponentialBiased rng_;
+  absl::profiling_internal::ExponentialBiased rng_;
 };
 
 inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept {
@@ -204,8 +204,8 @@
 template <typename Tag, int default_period>
 std::atomic<int> PeriodicSampler<Tag, default_period>::period_(default_period);
 
-}  // namespace base_internal
+}  // namespace profiling_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
 
-#endif  // ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_
+#endif  // ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_
diff --git a/absl/base/internal/periodic_sampler_benchmark.cc b/absl/profiling/internal/periodic_sampler_benchmark.cc
similarity index 94%
rename from absl/base/internal/periodic_sampler_benchmark.cc
rename to absl/profiling/internal/periodic_sampler_benchmark.cc
index 5ad469c..8f0e557 100644
--- a/absl/base/internal/periodic_sampler_benchmark.cc
+++ b/absl/profiling/internal/periodic_sampler_benchmark.cc
@@ -12,12 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "absl/profiling/internal/periodic_sampler.h"
 #include "benchmark/benchmark.h"
-#include "absl/base/internal/periodic_sampler.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
-namespace base_internal {
+namespace profiling_internal {
 namespace {
 
 template <typename Sampler>
@@ -74,6 +74,6 @@
 BENCHMARK(BM_PeriodicSampler_Disabled);
 
 }  // namespace
-}  // namespace base_internal
+}  // namespace profiling_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/base/internal/periodic_sampler_test.cc b/absl/profiling/internal/periodic_sampler_test.cc
similarity index 97%
rename from absl/base/internal/periodic_sampler_test.cc
rename to absl/profiling/internal/periodic_sampler_test.cc
index 3b301e3..ef986f3 100644
--- a/absl/base/internal/periodic_sampler_test.cc
+++ b/absl/profiling/internal/periodic_sampler_test.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "absl/base/internal/periodic_sampler.h"
+#include "absl/profiling/internal/periodic_sampler.h"
 
 #include <thread>  // NOLINT(build/c++11)
 
@@ -23,7 +23,7 @@
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
-namespace base_internal {
+namespace profiling_internal {
 namespace {
 
 using testing::Eq;
@@ -172,6 +172,6 @@
 }
 
 }  // namespace
-}  // namespace base_internal
+}  // namespace profiling_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/profiling/internal/sample_recorder.h b/absl/profiling/internal/sample_recorder.h
new file mode 100644
index 0000000..5e04a9c
--- /dev/null
+++ b/absl/profiling/internal/sample_recorder.h
@@ -0,0 +1,230 @@
+// 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.
+//
+// -----------------------------------------------------------------------------
+// File: sample_recorder.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines a lock-free linked list for recording samples
+// collected from a random/stochastic process.
+//
+// This utility is internal-only. Use at your own risk.
+
+#ifndef ABSL_PROFILING_INTERNAL_SAMPLE_RECORDER_H_
+#define ABSL_PROFILING_INTERNAL_SAMPLE_RECORDER_H_
+
+#include <atomic>
+#include <cstddef>
+#include <functional>
+
+#include "absl/base/config.h"
+#include "absl/base/thread_annotations.h"
+#include "absl/synchronization/mutex.h"
+#include "absl/time/time.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace profiling_internal {
+
+// Sample<T> that has members required for linking samples in the linked list of
+// samples maintained by the SampleRecorder.  Type T defines the sampled data.
+template <typename T>
+struct Sample {
+  // Guards the ability to restore the sample to a pristine state.  This
+  // prevents races with sampling and resurrecting an object.
+  absl::Mutex init_mu;
+  T* next = nullptr;
+  T* dead ABSL_GUARDED_BY(init_mu) = nullptr;
+};
+
+// Holds samples and their associated stack traces with a soft limit of
+// `SetHashtablezMaxSamples()`.
+//
+// Thread safe.
+template <typename T>
+class SampleRecorder {
+ public:
+  SampleRecorder();
+  ~SampleRecorder();
+
+  // Registers for sampling.  Returns an opaque registration info.
+  T* Register();
+
+  // Unregisters the sample.
+  void Unregister(T* sample);
+
+  // The dispose callback will be called on all samples the moment they are
+  // being unregistered. Only affects samples that are unregistered after the
+  // callback has been set.
+  // Returns the previous callback.
+  using DisposeCallback = void (*)(const T&);
+  DisposeCallback SetDisposeCallback(DisposeCallback f);
+
+  // Iterates over all the registered `StackInfo`s.  Returning the number of
+  // samples that have been dropped.
+  int64_t Iterate(const std::function<void(const T& stack)>& f);
+
+  void SetMaxSamples(int32_t max);
+
+ private:
+  void PushNew(T* sample);
+  void PushDead(T* sample);
+  T* PopDead();
+
+  std::atomic<size_t> dropped_samples_;
+  std::atomic<size_t> size_estimate_;
+  std::atomic<int32_t> max_samples_{1 << 20};
+
+  // Intrusive lock free linked lists for tracking samples.
+  //
+  // `all_` records all samples (they are never removed from this list) and is
+  // terminated with a `nullptr`.
+  //
+  // `graveyard_.dead` is a circular linked list.  When it is empty,
+  // `graveyard_.dead == &graveyard`.  The list is circular so that
+  // every item on it (even the last) has a non-null dead pointer.  This allows
+  // `Iterate` to determine if a given sample is live or dead using only
+  // information on the sample itself.
+  //
+  // For example, nodes [A, B, C, D, E] with [A, C, E] alive and [B, D] dead
+  // looks like this (G is the Graveyard):
+  //
+  //           +---+    +---+    +---+    +---+    +---+
+  //    all -->| A |--->| B |--->| C |--->| D |--->| E |
+  //           |   |    |   |    |   |    |   |    |   |
+  //   +---+   |   | +->|   |-+  |   | +->|   |-+  |   |
+  //   | G |   +---+ |  +---+ |  +---+ |  +---+ |  +---+
+  //   |   |         |        |        |        |
+  //   |   | --------+        +--------+        |
+  //   +---+                                    |
+  //     ^                                      |
+  //     +--------------------------------------+
+  //
+  std::atomic<T*> all_;
+  T graveyard_;
+
+  std::atomic<DisposeCallback> dispose_;
+};
+
+template <typename T>
+typename SampleRecorder<T>::DisposeCallback
+SampleRecorder<T>::SetDisposeCallback(DisposeCallback f) {
+  return dispose_.exchange(f, std::memory_order_relaxed);
+}
+
+template <typename T>
+SampleRecorder<T>::SampleRecorder()
+    : dropped_samples_(0), size_estimate_(0), all_(nullptr), dispose_(nullptr) {
+  absl::MutexLock l(&graveyard_.init_mu);
+  graveyard_.dead = &graveyard_;
+}
+
+template <typename T>
+SampleRecorder<T>::~SampleRecorder() {
+  T* s = all_.load(std::memory_order_acquire);
+  while (s != nullptr) {
+    T* next = s->next;
+    delete s;
+    s = next;
+  }
+}
+
+template <typename T>
+void SampleRecorder<T>::PushNew(T* sample) {
+  sample->next = all_.load(std::memory_order_relaxed);
+  while (!all_.compare_exchange_weak(sample->next, sample,
+                                     std::memory_order_release,
+                                     std::memory_order_relaxed)) {
+  }
+}
+
+template <typename T>
+void SampleRecorder<T>::PushDead(T* sample) {
+  if (auto* dispose = dispose_.load(std::memory_order_relaxed)) {
+    dispose(*sample);
+  }
+
+  absl::MutexLock graveyard_lock(&graveyard_.init_mu);
+  absl::MutexLock sample_lock(&sample->init_mu);
+  sample->dead = graveyard_.dead;
+  graveyard_.dead = sample;
+}
+
+template <typename T>
+T* SampleRecorder<T>::PopDead() {
+  absl::MutexLock graveyard_lock(&graveyard_.init_mu);
+
+  // The list is circular, so eventually it collapses down to
+  //   graveyard_.dead == &graveyard_
+  // when it is empty.
+  T* sample = graveyard_.dead;
+  if (sample == &graveyard_) return nullptr;
+
+  absl::MutexLock sample_lock(&sample->init_mu);
+  graveyard_.dead = sample->dead;
+  sample->dead = nullptr;
+  sample->PrepareForSampling();
+  return sample;
+}
+
+template <typename T>
+T* SampleRecorder<T>::Register() {
+  int64_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed);
+  if (size > max_samples_.load(std::memory_order_relaxed)) {
+    size_estimate_.fetch_sub(1, std::memory_order_relaxed);
+    dropped_samples_.fetch_add(1, std::memory_order_relaxed);
+    return nullptr;
+  }
+
+  T* sample = PopDead();
+  if (sample == nullptr) {
+    // Resurrection failed.  Hire a new warlock.
+    sample = new T();
+    PushNew(sample);
+  }
+
+  return sample;
+}
+
+template <typename T>
+void SampleRecorder<T>::Unregister(T* sample) {
+  PushDead(sample);
+  size_estimate_.fetch_sub(1, std::memory_order_relaxed);
+}
+
+template <typename T>
+int64_t SampleRecorder<T>::Iterate(
+    const std::function<void(const T& stack)>& f) {
+  T* s = all_.load(std::memory_order_acquire);
+  while (s != nullptr) {
+    absl::MutexLock l(&s->init_mu);
+    if (s->dead == nullptr) {
+      f(*s);
+    }
+    s = s->next;
+  }
+
+  return dropped_samples_.load(std::memory_order_relaxed);
+}
+
+template <typename T>
+void SampleRecorder<T>::SetMaxSamples(int32_t max) {
+  max_samples_.store(max, std::memory_order_release);
+}
+
+}  // namespace profiling_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_PROFILING_INTERNAL_SAMPLE_RECORDER_H_
diff --git a/absl/profiling/internal/sample_recorder_test.cc b/absl/profiling/internal/sample_recorder_test.cc
new file mode 100644
index 0000000..ec6e0fa
--- /dev/null
+++ b/absl/profiling/internal/sample_recorder_test.cc
@@ -0,0 +1,171 @@
+// 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 "absl/profiling/internal/sample_recorder.h"
+
+#include <atomic>
+#include <random>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "absl/base/thread_annotations.h"
+#include "absl/synchronization/internal/thread_pool.h"
+#include "absl/synchronization/mutex.h"
+#include "absl/synchronization/notification.h"
+#include "absl/time/time.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace profiling_internal {
+
+namespace {
+using ::absl::synchronization_internal::ThreadPool;
+using ::testing::IsEmpty;
+using ::testing::UnorderedElementsAre;
+
+struct Info : public Sample<Info> {
+ public:
+  void PrepareForSampling() {}
+  std::atomic<size_t> size;
+  absl::Time create_time;
+};
+
+std::vector<size_t> GetSizes(SampleRecorder<Info>* s) {
+  std::vector<size_t> res;
+  s->Iterate([&](const Info& info) {
+    res.push_back(info.size.load(std::memory_order_acquire));
+  });
+  return res;
+}
+
+Info* Register(SampleRecorder<Info>* s, size_t size) {
+  auto* info = s->Register();
+  assert(info != nullptr);
+  info->size.store(size);
+  return info;
+}
+
+TEST(SampleRecorderTest, Registration) {
+  SampleRecorder<Info> sampler;
+  auto* info1 = Register(&sampler, 1);
+  EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1));
+
+  auto* info2 = Register(&sampler, 2);
+  EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1, 2));
+  info1->size.store(3);
+  EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(3, 2));
+
+  sampler.Unregister(info1);
+  sampler.Unregister(info2);
+}
+
+TEST(SampleRecorderTest, Unregistration) {
+  SampleRecorder<Info> sampler;
+  std::vector<Info*> infos;
+  for (size_t i = 0; i < 3; ++i) {
+    infos.push_back(Register(&sampler, i));
+  }
+  EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 1, 2));
+
+  sampler.Unregister(infos[1]);
+  EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2));
+
+  infos.push_back(Register(&sampler, 3));
+  infos.push_back(Register(&sampler, 4));
+  EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 3, 4));
+  sampler.Unregister(infos[3]);
+  EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 4));
+
+  sampler.Unregister(infos[0]);
+  sampler.Unregister(infos[2]);
+  sampler.Unregister(infos[4]);
+  EXPECT_THAT(GetSizes(&sampler), IsEmpty());
+}
+
+TEST(SampleRecorderTest, MultiThreaded) {
+  SampleRecorder<Info> sampler;
+  Notification stop;
+  ThreadPool pool(10);
+
+  for (int i = 0; i < 10; ++i) {
+    pool.Schedule([&sampler, &stop]() {
+      std::random_device rd;
+      std::mt19937 gen(rd());
+
+      std::vector<Info*> infoz;
+      while (!stop.HasBeenNotified()) {
+        if (infoz.empty()) {
+          infoz.push_back(sampler.Register());
+        }
+        switch (std::uniform_int_distribution<>(0, 2)(gen)) {
+          case 0: {
+            infoz.push_back(sampler.Register());
+            break;
+          }
+          case 1: {
+            size_t p =
+                std::uniform_int_distribution<>(0, infoz.size() - 1)(gen);
+            Info* info = infoz[p];
+            infoz[p] = infoz.back();
+            infoz.pop_back();
+            sampler.Unregister(info);
+            break;
+          }
+          case 2: {
+            absl::Duration oldest = absl::ZeroDuration();
+            sampler.Iterate([&](const Info& info) {
+              oldest = std::max(oldest, absl::Now() - info.create_time);
+            });
+            ASSERT_GE(oldest, absl::ZeroDuration());
+            break;
+          }
+        }
+      }
+    });
+  }
+  // The threads will hammer away.  Give it a little bit of time for tsan to
+  // spot errors.
+  absl::SleepFor(absl::Seconds(3));
+  stop.Notify();
+}
+
+TEST(SampleRecorderTest, Callback) {
+  SampleRecorder<Info> sampler;
+
+  auto* info1 = Register(&sampler, 1);
+  auto* info2 = Register(&sampler, 2);
+
+  static const Info* expected;
+
+  auto callback = [](const Info& info) {
+    // We can't use `info` outside of this callback because the object will be
+    // disposed as soon as we return from here.
+    EXPECT_EQ(&info, expected);
+  };
+
+  // Set the callback.
+  EXPECT_EQ(sampler.SetDisposeCallback(callback), nullptr);
+  expected = info1;
+  sampler.Unregister(info1);
+
+  // Unset the callback.
+  EXPECT_EQ(callback, sampler.SetDisposeCallback(nullptr));
+  expected = nullptr;  // no more calls.
+  sampler.Unregister(info2);
+}
+
+}  // namespace
+}  // namespace profiling_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel
index 66ffcbc..fdde78b 100644
--- a/absl/random/BUILD.bazel
+++ b/absl/random/BUILD.bazel
@@ -16,7 +16,6 @@
 
 # ABSL random-number generation libraries.
 
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt
index 3009a03..9d1c67f 100644
--- a/absl/random/CMakeLists.txt
+++ b/absl/random/CMakeLists.txt
@@ -62,8 +62,8 @@
     absl::random_random
     absl::random_internal_sequence_urbg
     absl::fast_type_id
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -119,8 +119,8 @@
     absl::type_traits
     absl::utility
     absl::variant
-    gmock
-    gtest
+    GTest::gmock
+    GTest::gtest
   TESTONLY
 )
 
@@ -136,8 +136,8 @@
   DEPS
     absl::random_mocking_bit_gen
     absl::random_random
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -153,8 +153,8 @@
     absl::random_bit_gen_ref
     absl::random_mocking_bit_gen
     absl::random_random
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_library(
@@ -245,8 +245,8 @@
     absl::random_random
     absl::random_internal_sequence_urbg
     absl::random_internal_pcg_engine
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -268,8 +268,8 @@
     absl::raw_logging_internal
     absl::strings
     absl::str_format
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -285,8 +285,8 @@
     absl::random_distributions
     absl::random_random
     absl::random_internal_distribution_test_util
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -301,8 +301,8 @@
     absl::random_distributions
     absl::random_random
     absl::raw_logging_internal
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -322,8 +322,8 @@
     absl::raw_logging_internal
     absl::strings
     absl::str_format
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -343,8 +343,8 @@
     absl::random_random
     absl::raw_logging_internal
     absl::strings
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -367,8 +367,8 @@
     absl::raw_logging_internal
     absl::strings
     absl::str_format
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -391,8 +391,8 @@
     absl::raw_logging_internal
     absl::strings
     absl::str_format
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -414,8 +414,8 @@
     absl::raw_logging_internal
     absl::strings
     absl::str_format
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -435,8 +435,8 @@
     absl::random_random
     absl::raw_logging_internal
     absl::strings
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -456,8 +456,8 @@
     absl::random_internal_sequence_urbg
     absl::random_random
     absl::strings
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -477,8 +477,8 @@
     absl::random_random
     absl::raw_logging_internal
     absl::strings
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -492,7 +492,7 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::random_random
-    gtest_main
+    GTest::gtest_main
 )
 
 absl_cc_test(
@@ -508,8 +508,8 @@
     absl::random_seed_sequences
     absl::random_internal_nonsecure_base
     absl::random_random
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -894,7 +894,7 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::random_internal_traits
-    gtest_main
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -911,7 +911,7 @@
     absl::bits
     absl::flags
     absl::random_internal_generate_real
-    gtest_main
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -926,7 +926,7 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::random_internal_distribution_test_util
-    gtest_main
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -941,7 +941,7 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::random_internal_fastmath
-    gtest_main
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -957,8 +957,8 @@
   DEPS
     absl::random_internal_explicit_seed_seq
     absl::random_seed_sequences
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -973,8 +973,8 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::random_internal_salted_seed_seq
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -990,7 +990,7 @@
   DEPS
     absl::core_headers
     absl::random_internal_distribution_test_util
-    gtest_main
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -1005,7 +1005,7 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::random_internal_fast_uniform_bits
-    gtest_main
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -1024,7 +1024,7 @@
     absl::random_distributions
     absl::random_seed_sequences
     absl::strings
-    gtest_main
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -1039,8 +1039,8 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::random_internal_seed_material
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -1057,7 +1057,7 @@
     absl::random_internal_pool_urbg
     absl::span
     absl::type_traits
-    gtest_main
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -1074,8 +1074,8 @@
     absl::random_internal_explicit_seed_seq
     absl::random_internal_pcg_engine
     absl::time
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -1094,8 +1094,8 @@
     absl::raw_logging_internal
     absl::strings
     absl::time
-    gmock
-    gtest_main
+    GTest::gmock
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -1111,7 +1111,7 @@
   DEPS
     absl::random_internal_randen
     absl::type_traits
-    gtest_main
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -1127,7 +1127,7 @@
   DEPS
     absl::endian
     absl::random_internal_randen_slow
-    gtest_main
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -1146,8 +1146,8 @@
     absl::random_internal_randen_hwaes_impl
     absl::raw_logging_internal
     absl::str_format
-    gmock
-    gtest
+    GTest::gmock
+    GTest::gtest
 )
 
 # Internal-only target, do not depend on directly.
@@ -1178,7 +1178,7 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::random_internal_uniform_helper
-    gtest_main
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -1193,7 +1193,7 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::random_internal_iostream_state_saver
-    gtest_main
+    GTest::gtest_main
 )
 
 # Internal-only target, do not depend on directly.
@@ -1210,5 +1210,5 @@
     absl::random_internal_wide_multiply
     absl::bits
     absl::int128
-    gtest_main
+    GTest::gtest_main
 )
diff --git a/absl/random/beta_distribution_test.cc b/absl/random/beta_distribution_test.cc
index 44cdfdd..d980c96 100644
--- a/absl/random/beta_distribution_test.cc
+++ b/absl/random/beta_distribution_test.cc
@@ -15,6 +15,7 @@
 #include "absl/random/beta_distribution.h"
 
 #include <algorithm>
+#include <cfloat>
 #include <cstddef>
 #include <cstdint>
 #include <iterator>
@@ -558,6 +559,14 @@
 // dependencies of the distribution change, such as RandU64ToDouble, then this
 // is also likely to change.
 TEST(BetaDistributionTest, AlgorithmBounds) {
+#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0
+  // We're using an x87-compatible FPU, and intermediate operations are
+  // performed with 80-bit floats. This produces slightly different results from
+  // what we expect below.
+  GTEST_SKIP()
+      << "Skipping the test because we detected x87 floating-point semantics";
+#endif
+
   {
     absl::random_internal::sequence_urbg urbg(
         {0x7fbe76c8b4395800ull, 0x8000000000000000ull});
diff --git a/absl/random/discrete_distribution_test.cc b/absl/random/discrete_distribution_test.cc
index 6d00700..415b14c 100644
--- a/absl/random/discrete_distribution_test.cc
+++ b/absl/random/discrete_distribution_test.cc
@@ -99,6 +99,7 @@
 }
 
 TEST(DiscreteDistributionTest, InitDiscreteDistribution) {
+  using testing::_;
   using testing::Pair;
 
   {
@@ -111,8 +112,8 @@
     // Each bucket is p=1/3, so bucket 0 will send half it's traffic
     // to bucket 2, while the rest will retain all of their traffic.
     EXPECT_THAT(q, testing::ElementsAre(Pair(0.5, 2),  //
-                                        Pair(1.0, 1),  //
-                                        Pair(1.0, 2)));
+                                        Pair(1.0, _),  //
+                                        Pair(1.0, _)));
   }
 
   {
@@ -135,7 +136,7 @@
 
     EXPECT_THAT(q, testing::ElementsAre(Pair(b0, 3),   //
                                         Pair(b1, 3),   //
-                                        Pair(1.0, 2),  //
+                                        Pair(1.0, _),  //
                                         Pair(b3, 2),   //
                                         Pair(b1, 3)));
   }
diff --git a/absl/random/distributions_test.cc b/absl/random/distributions_test.cc
index 5866a07..d3a5dd7 100644
--- a/absl/random/distributions_test.cc
+++ b/absl/random/distributions_test.cc
@@ -14,6 +14,7 @@
 
 #include "absl/random/distributions.h"
 
+#include <cfloat>
 #include <cmath>
 #include <cstdint>
 #include <random>
@@ -224,6 +225,15 @@
 TEST_F(RandomDistributionsTest, UniformNonsenseRanges) {
   // The ranges used in this test are undefined behavior.
   // The results are arbitrary and subject to future changes.
+
+#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0
+  // We're using an x87-compatible FPU, and intermediate operations can be
+  // performed with 80-bit floats. This produces slightly different results from
+  // what we expect below.
+  GTEST_SKIP()
+      << "Skipping the test because we detected x87 floating-point semantics";
+#endif
+
   absl::InsecureBitGen gen;
 
   // <uint>
diff --git a/absl/random/exponential_distribution_test.cc b/absl/random/exponential_distribution_test.cc
index af11d61..81a5d17 100644
--- a/absl/random/exponential_distribution_test.cc
+++ b/absl/random/exponential_distribution_test.cc
@@ -15,6 +15,7 @@
 #include "absl/random/exponential_distribution.h"
 
 #include <algorithm>
+#include <cfloat>
 #include <cmath>
 #include <cstddef>
 #include <cstdint>
@@ -384,6 +385,15 @@
 TEST(ExponentialDistributionTest, AlgorithmBounds) {
   // Relies on absl::uniform_real_distribution, so some of these comments
   // reference that.
+
+#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0
+  // We're using an x87-compatible FPU, and intermediate operations can be
+  // performed with 80-bit floats. This produces slightly different results from
+  // what we expect below.
+  GTEST_SKIP()
+      << "Skipping the test because we detected x87 floating-point semantics";
+#endif
+
   absl::exponential_distribution<double> dist;
 
   {
diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel
index 612b150..e93eebb 100644
--- a/absl/random/internal/BUILD.bazel
+++ b/absl/random/internal/BUILD.bazel
@@ -14,8 +14,6 @@
 # limitations under the License.
 #
 
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
-
 # Internal-only implementation classes for Abseil Random
 load(
     "//absl:copts/configure_copts.bzl",
@@ -82,6 +80,7 @@
     deps = [
         ":fast_uniform_bits",
         "//absl/base:core_headers",
+        "//absl/base:dynamic_annotations",
         "//absl/base:raw_logging_internal",
         "//absl/strings",
         "//absl/types:optional",
@@ -296,6 +295,8 @@
         ":platform",
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/base:endian",
+        "//absl/numeric:int128",
     ],
 )
 
@@ -336,6 +337,7 @@
         ":platform",
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/numeric:int128",
     ],
 )
 
@@ -622,7 +624,7 @@
     name = "randen_hwaes_test",
     size = "small",
     srcs = ["randen_hwaes_test.cc"],
-    copts = ABSL_TEST_COPTS,
+    copts = ABSL_TEST_COPTS + ABSL_RANDOM_RANDEN_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     tags = ABSL_RANDOM_NONPORTABLE_TAGS,
     deps = [
@@ -717,7 +719,6 @@
 
 cc_test(
     name = "iostream_state_saver_test",
-    size = "small",
     srcs = ["iostream_state_saver_test.cc"],
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
diff --git a/absl/random/internal/explicit_seed_seq.h b/absl/random/internal/explicit_seed_seq.h
index e3aa31a..25f7915 100644
--- a/absl/random/internal/explicit_seed_seq.h
+++ b/absl/random/internal/explicit_seed_seq.h
@@ -74,7 +74,7 @@
   template <typename OutIterator>
   void generate(OutIterator begin, OutIterator end) {
     for (size_t index = 0; begin != end; begin++) {
-      *begin = state_.empty() ? 0 : little_endian::FromHost32(state_[index++]);
+      *begin = state_.empty() ? 0 : state_[index++];
       if (index >= state_.size()) {
         index = 0;
       }
diff --git a/absl/random/internal/explicit_seed_seq_test.cc b/absl/random/internal/explicit_seed_seq_test.cc
index a55ad73..f867f61 100644
--- a/absl/random/internal/explicit_seed_seq_test.cc
+++ b/absl/random/internal/explicit_seed_seq_test.cc
@@ -24,6 +24,8 @@
 
 namespace {
 
+using ::absl::random_internal::ExplicitSeedSeq;
+
 template <typename Sseq>
 bool ConformsToInterface() {
   // Check that the SeedSequence can be default-constructed.
@@ -64,14 +66,14 @@
   EXPECT_TRUE(ConformsToInterface<std::seed_seq>());
 
   // Abseil classes
-  EXPECT_TRUE(ConformsToInterface<absl::random_internal::ExplicitSeedSeq>());
+  EXPECT_TRUE(ConformsToInterface<ExplicitSeedSeq>());
 }
 
 TEST(ExplicitSeedSeq, DefaultConstructorGeneratesZeros) {
   const size_t kNumBlocks = 128;
 
   uint32_t outputs[kNumBlocks];
-  absl::random_internal::ExplicitSeedSeq seq;
+  ExplicitSeedSeq seq;
   seq.generate(outputs, &outputs[kNumBlocks]);
 
   for (uint32_t& seed : outputs) {
@@ -87,8 +89,7 @@
   for (uint32_t& seed : seed_material) {
     seed = urandom();
   }
-  absl::random_internal::ExplicitSeedSeq seq(seed_material,
-                                             &seed_material[kNumBlocks]);
+  ExplicitSeedSeq seq(seed_material, &seed_material[kNumBlocks]);
 
   // Check that output is same as seed-material provided to constructor.
   {
@@ -133,11 +134,10 @@
   for (uint32_t& entry : entropy) {
     entry = urandom();
   }
-  absl::random_internal::ExplicitSeedSeq seq_from_entropy(std::begin(entropy),
-                                                          std::end(entropy));
+  ExplicitSeedSeq seq_from_entropy(std::begin(entropy), std::end(entropy));
   // Copy constructor.
   {
-    absl::random_internal::ExplicitSeedSeq seq_copy(seq_from_entropy);
+    ExplicitSeedSeq seq_copy(seq_from_entropy);
     EXPECT_EQ(seq_copy.size(), seq_from_entropy.size());
 
     std::vector<uint32_t> seeds_1;
@@ -155,8 +155,7 @@
     for (uint32_t& entry : entropy) {
       entry = urandom();
     }
-    absl::random_internal::ExplicitSeedSeq another_seq(std::begin(entropy),
-                                                       std::end(entropy));
+    ExplicitSeedSeq another_seq(std::begin(entropy), std::end(entropy));
 
     std::vector<uint32_t> seeds_1;
     seeds_1.resize(1000, 0);
@@ -202,3 +201,35 @@
     EXPECT_THAT(seeds_1, Each(Eq(0)));
   }
 }
+
+TEST(ExplicitSeedSeq, StdURBGGoldenTests) {
+  // Verify that for std::- URBG instances the results are stable across
+  // platforms (these should have deterministic output).
+  {
+    ExplicitSeedSeq seed_sequence{12, 34, 56};
+    std::minstd_rand rng(seed_sequence);
+
+    std::minstd_rand::result_type values[4] = {rng(), rng(), rng(), rng()};
+    EXPECT_THAT(values,
+                testing::ElementsAre(579252, 43785881, 464353103, 1501811174));
+  }
+
+  {
+    ExplicitSeedSeq seed_sequence{12, 34, 56};
+    std::mt19937 rng(seed_sequence);
+
+    std::mt19937::result_type values[4] = {rng(), rng(), rng(), rng()};
+    EXPECT_THAT(values, testing::ElementsAre(138416803, 151130212, 33817739,
+                                             138416803));
+  }
+
+  {
+    ExplicitSeedSeq seed_sequence{12, 34, 56};
+    std::mt19937_64 rng(seed_sequence);
+
+    std::mt19937_64::result_type values[4] = {rng(), rng(), rng(), rng()};
+    EXPECT_THAT(values,
+                testing::ElementsAre(19738651785169348, 1464811352364190456,
+                                     18054685302720800, 19738651785169348));
+  }
+}
diff --git a/absl/random/internal/generate_real.h b/absl/random/internal/generate_real.h
index 4f62873..d5fbb44 100644
--- a/absl/random/internal/generate_real.h
+++ b/absl/random/internal/generate_real.h
@@ -127,10 +127,8 @@
 
   // Construct the 32-bit or 64-bit IEEE 754 floating-point value from
   // the individual fields: sign, exp, mantissa(bits).
-  uint_type val =
-      (std::is_same<SignedTag, GeneratePositiveTag>::value ? 0u : sign) |
-      (static_cast<uint_type>(exp) << kExp) |
-      (static_cast<uint_type>(bits) & kMask);
+  uint_type val = sign | (static_cast<uint_type>(exp) << kExp) |
+                  (static_cast<uint_type>(bits) & kMask);
 
   // bit_cast to the output-type
   real_type result;
diff --git a/absl/random/internal/pool_urbg.cc b/absl/random/internal/pool_urbg.cc
index 5bee530..725100a 100644
--- a/absl/random/internal/pool_urbg.cc
+++ b/absl/random/internal/pool_urbg.cc
@@ -194,11 +194,10 @@
   // Not all the platforms that we build for have std::aligned_alloc, however
   // since we never free these objects, we can over allocate and munge the
   // pointers to the correct alignment.
-  void* memory = std::malloc(sizeof(RandenPoolEntry) + kAlignment);
-  auto x = reinterpret_cast<intptr_t>(memory);
+  intptr_t x = reinterpret_cast<intptr_t>(
+      new char[sizeof(RandenPoolEntry) + kAlignment]);
   auto y = x % kAlignment;
-  void* aligned =
-      (y == 0) ? memory : reinterpret_cast<void*>(x + kAlignment - y);
+  void* aligned = reinterpret_cast<void*>(y == 0 ? x : (x + kAlignment - y));
   return new (aligned) RandenPoolEntry();
 }
 
diff --git a/absl/random/internal/randen_engine.h b/absl/random/internal/randen_engine.h
index 92bb890..372c3ac 100644
--- a/absl/random/internal/randen_engine.h
+++ b/absl/random/internal/randen_engine.h
@@ -121,6 +121,13 @@
       const size_t requested_entropy = (entropy_size == 0) ? 8u : entropy_size;
       std::fill(std::begin(buffer) + requested_entropy, std::end(buffer), 0);
       seq.generate(std::begin(buffer), std::begin(buffer) + requested_entropy);
+#ifdef ABSL_IS_BIG_ENDIAN
+      // Randen expects the seed buffer to be in Little Endian; reverse it on
+      // Big Endian platforms.
+      for (sequence_result_type& e : buffer) {
+        e = absl::little_endian::FromHost(e);
+      }
+#endif
       // The Randen paper suggests preferentially initializing even-numbered
       // 128-bit vectors of the randen state (there are 16 such vectors).
       // The seed data is merged into the state offset by 128-bits, which
diff --git a/absl/random/internal/randen_hwaes.cc b/absl/random/internal/randen_hwaes.cc
index b5a3f90..fee6677 100644
--- a/absl/random/internal/randen_hwaes.cc
+++ b/absl/random/internal/randen_hwaes.cc
@@ -23,49 +23,20 @@
 #include <cstring>
 
 #include "absl/base/attributes.h"
+#include "absl/numeric/int128.h"
 #include "absl/random/internal/platform.h"
 #include "absl/random/internal/randen_traits.h"
 
 // ABSL_RANDEN_HWAES_IMPL indicates whether this file will contain
 // a hardware accelerated implementation of randen, or whether it
 // will contain stubs that exit the process.
-#if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32)
-// The platform.h directives are sufficient to indicate whether
-// we should build accelerated implementations for x86.
-#if (ABSL_HAVE_ACCELERATED_AES || ABSL_RANDOM_INTERNAL_AES_DISPATCH)
-#define ABSL_RANDEN_HWAES_IMPL 1
-#endif
-#elif defined(ABSL_ARCH_PPC)
-// The platform.h directives are sufficient to indicate whether
-// we should build accelerated implementations for PPC.
-//
-// NOTE: This has mostly been tested on 64-bit Power variants,
-// and not embedded cpus such as powerpc32-8540
 #if ABSL_HAVE_ACCELERATED_AES
+// The following plaforms have implemented RandenHwAes.
+#if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32) || \
+    defined(ABSL_ARCH_PPC) || defined(ABSL_ARCH_ARM) ||       \
+    defined(ABSL_ARCH_AARCH64)
 #define ABSL_RANDEN_HWAES_IMPL 1
 #endif
-#elif defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64)
-// ARM is somewhat more complicated. We might support crypto natively...
-#if ABSL_HAVE_ACCELERATED_AES || \
-    (defined(__ARM_NEON) && defined(__ARM_FEATURE_CRYPTO))
-#define ABSL_RANDEN_HWAES_IMPL 1
-
-#elif ABSL_RANDOM_INTERNAL_AES_DISPATCH && !defined(__APPLE__) && \
-    (defined(__GNUC__) && __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ > 9)
-// ...or, on GCC, we can use an ASM directive to
-// instruct the assember to allow crypto instructions.
-#define ABSL_RANDEN_HWAES_IMPL 1
-#define ABSL_RANDEN_HWAES_IMPL_CRYPTO_DIRECTIVE 1
-#endif
-#else
-// HWAES is unsupported by these architectures / platforms:
-//   __myriad2__
-//   __mips__
-//
-// Other architectures / platforms are unknown.
-//
-// See the Abseil documentation on supported macros at:
-// https://abseil.io/docs/cpp/platforms/macros
 #endif
 
 #if !defined(ABSL_RANDEN_HWAES_IMPL)
@@ -120,11 +91,6 @@
 
 using absl::random_internal::RandenTraits;
 
-// Randen operates on 128-bit vectors.
-struct alignas(16) u64x2 {
-  uint64_t data[2];
-};
-
 }  // namespace
 
 // TARGET_CRYPTO defines a crypto attribute for each architecture.
@@ -186,7 +152,7 @@
 }
 
 // Enables native loads in the round loop by pre-swapping.
-inline ABSL_TARGET_CRYPTO void SwapEndian(u64x2* state) {
+inline ABSL_TARGET_CRYPTO void SwapEndian(absl::uint128* state) {
   for (uint32_t block = 0; block < RandenTraits::kFeistelBlocks; ++block) {
     Vector128Store(ReverseBytes(Vector128Load(state + block)), state + block);
   }
@@ -196,22 +162,6 @@
 
 #elif defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64)
 
-// This asm directive will cause the file to be compiled with crypto extensions
-// whether or not the cpu-architecture supports it.
-#if ABSL_RANDEN_HWAES_IMPL_CRYPTO_DIRECTIVE
-asm(".arch_extension  crypto\n");
-
-// Override missing defines.
-#if !defined(__ARM_NEON)
-#define __ARM_NEON 1
-#endif
-
-#if !defined(__ARM_FEATURE_CRYPTO)
-#define __ARM_FEATURE_CRYPTO 1
-#endif
-
-#endif
-
 // Rely on the ARM NEON+Crypto advanced simd types, defined in <arm_neon.h>.
 // uint8x16_t is the user alias for underlying __simd128_uint8_t type.
 // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0073a/IHI0073A_arm_neon_intrinsics_ref.pdf
@@ -261,7 +211,7 @@
 
 #elif defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32)
 // On x86 we rely on the aesni instructions
-#include <wmmintrin.h>
+#include <immintrin.h>
 
 namespace {
 
@@ -270,7 +220,7 @@
 class Vector128 {
  public:
   // Convert from/to intrinsics.
-  inline explicit Vector128(const __m128i& Vector128) : data_(Vector128) {}
+  inline explicit Vector128(const __m128i& v) : data_(v) {}
 
   inline __m128i data() const { return data_; }
 
@@ -327,7 +277,7 @@
 
 // Block shuffles applies a shuffle to the entire state between AES rounds.
 // Improved odd-even shuffle from "New criterion for diffusion property".
-inline ABSL_TARGET_CRYPTO void BlockShuffle(u64x2* state) {
+inline ABSL_TARGET_CRYPTO void BlockShuffle(absl::uint128* state) {
   static_assert(RandenTraits::kFeistelBlocks == 16,
                 "Expecting 16 FeistelBlocks.");
 
@@ -374,8 +324,9 @@
 // per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in
 // parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel
 // XORs are 'free' (included in the second AES instruction).
-inline ABSL_TARGET_CRYPTO const u64x2* FeistelRound(
-    u64x2* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
+inline ABSL_TARGET_CRYPTO const absl::uint128* FeistelRound(
+    absl::uint128* state,
+    const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
   static_assert(RandenTraits::kFeistelBlocks == 16,
                 "Expecting 16 FeistelBlocks.");
 
@@ -436,7 +387,8 @@
 // 2^64 queries if the round function is a PRF. This is similar to the b=8 case
 // of Simpira v2, but more efficient than its generic construction for b=16.
 inline ABSL_TARGET_CRYPTO void Permute(
-    u64x2* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
+    absl::uint128* state,
+    const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
   // (Successfully unrolled; the first iteration jumps into the second half)
 #ifdef __clang__
 #pragma clang loop unroll_count(2)
@@ -473,10 +425,11 @@
   static_assert(RandenTraits::kStateBytes / sizeof(Vector128) == 16,
                 "Unexpected Randen kStateBlocks");
 
-  auto* state =
-      reinterpret_cast<u64x2 * ABSL_RANDOM_INTERNAL_RESTRICT>(state_void);
+  auto* state = reinterpret_cast<absl::uint128 * ABSL_RANDOM_INTERNAL_RESTRICT>(
+      state_void);
   const auto* seed =
-      reinterpret_cast<const u64x2 * ABSL_RANDOM_INTERNAL_RESTRICT>(seed_void);
+      reinterpret_cast<const absl::uint128 * ABSL_RANDOM_INTERNAL_RESTRICT>(
+          seed_void);
 
   Vector128 b1 = Vector128Load(state + 1);
   b1 ^= Vector128Load(seed + 0);
@@ -545,8 +498,8 @@
   static_assert(RandenTraits::kCapacityBytes == sizeof(Vector128),
                 "Capacity mismatch");
 
-  auto* state = reinterpret_cast<u64x2*>(state_void);
-  const auto* keys = reinterpret_cast<const u64x2*>(keys_void);
+  auto* state = reinterpret_cast<absl::uint128*>(state_void);
+  const auto* keys = reinterpret_cast<const absl::uint128*>(keys_void);
 
   const Vector128 prev_inner = Vector128Load(state);
 
diff --git a/absl/random/internal/randen_hwaes_test.cc b/absl/random/internal/randen_hwaes_test.cc
index 66ddb43..2348b55 100644
--- a/absl/random/internal/randen_hwaes_test.cc
+++ b/absl/random/internal/randen_hwaes_test.cc
@@ -27,44 +27,39 @@
 using absl::random_internal::RandenHwAes;
 using absl::random_internal::RandenTraits;
 
-// Local state parameters.
-constexpr size_t kSeedBytes =
-    RandenTraits::kStateBytes - RandenTraits::kCapacityBytes;
-constexpr size_t kStateSizeT = RandenTraits::kStateBytes / sizeof(uint64_t);
-constexpr size_t kSeedSizeT = kSeedBytes / sizeof(uint32_t);
-
-struct alignas(16) randen {
-  uint64_t state[kStateSizeT];
-  uint32_t seed[kSeedSizeT];
-};
-
 TEST(RandenHwAesTest, Default) {
   EXPECT_TRUE(absl::random_internal::CPUSupportsRandenHwAes());
 
-  constexpr uint64_t kGolden[] = {
-      0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977,
-      0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912,
-      0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6,
-      0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a,
-      0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37,
-      0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556,
-      0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42,
-      0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc,
-      0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f,
-      0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3,
-      0x026ff374c101da7e, 0x811ef0821c3de851,
+  constexpr uint8_t kGolden[] = {
+      0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d,
+      0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3,
+      0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f,
+      0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0,
+      0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff,
+      0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2,
+      0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5,
+      0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c,
+      0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56,
+      0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4,
+      0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91,
+      0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5,
+      0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d,
+      0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58,
+      0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e,
+      0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49,
+      0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0,
+      0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6,
+      0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61,
+      0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35,
+      0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c,
+      0x82, 0xf0, 0x1e, 0x81,
   };
 
-  alignas(16) randen d;
-  memset(d.state, 0, sizeof(d.state));
-  RandenHwAes::Generate(RandenHwAes::GetKeys(), d.state);
+  alignas(16) uint8_t state[RandenTraits::kStateBytes];
+  std::memset(state, 0, sizeof(state));
 
-  uint64_t* id = d.state;
-  for (const auto& elem : kGolden) {
-    auto a = absl::StrFormat("%#x", elem);
-    auto b = absl::StrFormat("%#x", *id++);
-    EXPECT_EQ(a, b);
-  }
+  RandenHwAes::Generate(RandenHwAes::GetKeys(), state);
+  EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state)));
 }
 
 }  // namespace
diff --git a/absl/random/internal/randen_slow.cc b/absl/random/internal/randen_slow.cc
index 4e5f3dc..9bfd2a4 100644
--- a/absl/random/internal/randen_slow.cc
+++ b/absl/random/internal/randen_slow.cc
@@ -19,6 +19,8 @@
 #include <cstring>
 
 #include "absl/base/attributes.h"
+#include "absl/base/internal/endian.h"
+#include "absl/numeric/int128.h"
 #include "absl/random/internal/platform.h"
 #include "absl/random/internal/randen_traits.h"
 
@@ -38,192 +40,193 @@
 namespace {
 
 // AES portions based on rijndael-alg-fst.c,
-// https://fastcrypto.org/front/misc/rijndael-alg-fst.c
+// https://fastcrypto.org/front/misc/rijndael-alg-fst.c, and modified for
+// platform-endianness.
 //
 // Implementation of
 // http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf
 constexpr uint32_t te0[256] = {
-    0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd,
-    0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d,
-    0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d,
-    0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,
-    0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7,
-    0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a,
-    0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4,
-    0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,
-    0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1,
-    0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d,
-    0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e,
-    0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,
-    0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e,
-    0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c,
-    0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46,
-    0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,
-    0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7,
-    0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81,
-    0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe,
-    0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,
-    0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a,
-    0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f,
-    0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2,
-    0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,
-    0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e,
-    0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c,
-    0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256,
-    0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4,
-    0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4,
-    0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7,
-    0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa,
-    0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,
-    0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1,
-    0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21,
-    0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42,
-    0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12,
-    0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158,
-    0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133,
-    0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22,
-    0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,
-    0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631,
-    0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11,
-    0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a,
+    0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6,
+    0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56,
+    0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f,
+    0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb,
+    0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453,
+    0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c,
+    0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551,
+    0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a,
+    0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637,
+    0x0f05050a, 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
+    0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d,
+    0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b,
+    0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd,
+    0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1,
+    0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d,
+    0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85,
+    0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a,
+    0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe,
+    0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d,
+    0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
+    0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5,
+    0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3,
+    0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755,
+    0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6,
+    0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
+    0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428,
+    0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264,
+    0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8,
+    0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531,
+    0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
+    0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac,
+    0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810,
+    0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657,
+    0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e,
+    0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c,
+    0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c,
+    0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199,
+    0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122,
+    0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c,
+    0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
+    0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7,
+    0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e,
+    0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c,
 };
 
 constexpr uint32_t te1[256] = {
-    0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b,
-    0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b,
-    0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282,
-    0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0,
-    0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4,
-    0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626,
-    0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5,
-    0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515,
-    0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696,
-    0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2,
-    0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383,
-    0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0,
-    0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3,
-    0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded,
-    0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb,
-    0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf,
-    0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d,
-    0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f,
-    0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3,
-    0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5,
-    0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff,
-    0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec,
-    0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7,
-    0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373,
-    0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a,
-    0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414,
-    0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232,
-    0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c,
-    0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595,
-    0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d,
-    0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656,
-    0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808,
-    0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6,
-    0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f,
-    0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e,
-    0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e,
-    0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1,
-    0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111,
-    0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e,
-    0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf,
-    0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6,
-    0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f,
-    0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616,
+    0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd,
+    0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d,
+    0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, 0xcaca8f45, 0x82821f9d,
+    0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b,
+    0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7,
+    0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a,
+    0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4,
+    0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f,
+    0x0404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1,
+    0x05050a0f, 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d,
+    0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, 0x83831d9e,
+    0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb,
+    0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e,
+    0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c,
+    0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46,
+    0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a,
+    0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7,
+    0x33336655, 0x85851194, 0x45458acf, 0xf9f9e910, 0x02020406, 0x7f7ffe81,
+    0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe,
+    0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104,
+    0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a,
+    0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f,
+    0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2,
+    0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695,
+    0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e,
+    0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c,
+    0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, 0xe0e0db3b, 0x32326456,
+    0x3a3a744e, 0x0a0a141e, 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4,
+    0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4,
+    0xe4e4d337, 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7,
+    0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa,
+    0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018,
+    0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1,
+    0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21,
+    0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42,
+    0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, 0x0e0e1c12,
+    0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958,
+    0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233,
+    0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22,
+    0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
+    0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, 0xe6e6d731,
+    0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, 0x2d2d5a77, 0x0f0f1e11,
+    0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a,
 };
 
 constexpr uint32_t te2[256] = {
-    0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b,
-    0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b,
-    0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82,
-    0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0,
-    0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4,
-    0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26,
-    0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5,
-    0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15,
-    0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796,
-    0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2,
-    0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83,
-    0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0,
-    0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3,
-    0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed,
-    0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb,
-    0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf,
-    0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d,
-    0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f,
-    0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3,
-    0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5,
-    0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff,
-    0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec,
-    0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7,
-    0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673,
-    0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a,
-    0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814,
-    0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432,
-    0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c,
-    0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195,
-    0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d,
-    0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56,
-    0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008,
-    0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6,
-    0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f,
-    0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e,
-    0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e,
-    0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1,
-    0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211,
-    0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e,
-    0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df,
-    0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6,
-    0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f,
-    0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16,
+    0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b,
+    0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b,
+    0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, 0xca8f45ca, 0x821f9d82,
+    0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0,
+    0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4,
+    0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26,
+    0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5,
+    0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15,
+    0x04080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196,
+    0x050a0f05, 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2,
+    0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, 0x831d9e83,
+    0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0,
+    0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3,
+    0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced,
+    0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb,
+    0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf,
+    0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d,
+    0x33665533, 0x85119485, 0x458acf45, 0xf9e910f9, 0x02040602, 0x7ffe817f,
+    0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3,
+    0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5,
+    0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff,
+    0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec,
+    0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7,
+    0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573,
+    0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a,
+    0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14,
+    0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, 0xe0db3be0, 0x32645632,
+    0x3a744e3a, 0x0a141e0a, 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c,
+    0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495,
+    0xe4d337e4, 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d,
+    0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56,
+    0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808,
+    0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6,
+    0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f,
+    0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e,
+    0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, 0x0e1c120e,
+    0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1,
+    0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311,
+    0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e,
+    0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
+    0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, 0xe6d731e6,
+    0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, 0x2d5a772d, 0x0f1e110f,
+    0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16,
 };
 
 constexpr uint32_t te3[256] = {
-    0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6,
-    0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56,
-    0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f,
-    0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb,
-    0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753,
-    0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c,
-    0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451,
-    0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a,
-    0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137,
-    0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf,
-    0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d,
-    0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b,
-    0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd,
-    0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1,
-    0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d,
-    0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85,
-    0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a,
-    0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe,
-    0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d,
-    0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1,
-    0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5,
-    0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3,
-    0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255,
-    0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6,
-    0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54,
-    0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28,
-    0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664,
-    0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8,
-    0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431,
-    0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da,
-    0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac,
-    0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810,
-    0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157,
-    0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e,
-    0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c,
-    0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c,
-    0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899,
-    0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322,
-    0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c,
-    0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5,
-    0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7,
-    0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e,
-    0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c,
+    0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b,
+    0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b,
+    0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, 0x8f45caca, 0x1f9d8282,
+    0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0,
+    0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4,
+    0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626,
+    0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5,
+    0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515,
+    0x080c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696,
+    0x0a0f0505, 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2,
+    0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383,
+    0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0,
+    0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3,
+    0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded,
+    0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb,
+    0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf,
+    0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d,
+    0x66553333, 0x11948585, 0x8acf4545, 0xe910f9f9, 0x04060202, 0xfe817f7f,
+    0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3,
+    0x80c04040, 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5,
+    0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff,
+    0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec,
+    0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7,
+    0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373,
+    0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a,
+    0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414,
+    0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, 0xdb3be0e0, 0x64563232,
+    0x744e3a3a, 0x141e0a0a, 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c,
+    0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595,
+    0xd337e4e4, 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d,
+    0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656,
+    0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808,
+    0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6,
+    0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f,
+    0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e,
+    0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, 0x1c120e0e,
+    0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1,
+    0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111,
+    0xd2bb6969, 0xa970d9d9, 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e,
+    0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
+    0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6,
+    0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f,
+    0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616,
 };
 
 // Software implementation of the Vector128 class, using uint32_t
@@ -235,45 +238,13 @@
 inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128
 Vector128Load(const void* from) {
   Vector128 result;
-  const uint8_t* src = reinterpret_cast<const uint8_t*>(from);
-  result.s[0] = static_cast<uint32_t>(src[0]) << 24 |
-                static_cast<uint32_t>(src[1]) << 16 |
-                static_cast<uint32_t>(src[2]) << 8 |
-                static_cast<uint32_t>(src[3]);
-  result.s[1] = static_cast<uint32_t>(src[4]) << 24 |
-                static_cast<uint32_t>(src[5]) << 16 |
-                static_cast<uint32_t>(src[6]) << 8 |
-                static_cast<uint32_t>(src[7]);
-  result.s[2] = static_cast<uint32_t>(src[8]) << 24 |
-                static_cast<uint32_t>(src[9]) << 16 |
-                static_cast<uint32_t>(src[10]) << 8 |
-                static_cast<uint32_t>(src[11]);
-  result.s[3] = static_cast<uint32_t>(src[12]) << 24 |
-                static_cast<uint32_t>(src[13]) << 16 |
-                static_cast<uint32_t>(src[14]) << 8 |
-                static_cast<uint32_t>(src[15]);
+  std::memcpy(result.s, from, sizeof(Vector128));
   return result;
 }
 
 inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Vector128Store(
     const Vector128& v, void* to) {
-  uint8_t* dst = reinterpret_cast<uint8_t*>(to);
-  dst[0] = static_cast<uint8_t>(v.s[0] >> 24);
-  dst[1] = static_cast<uint8_t>(v.s[0] >> 16);
-  dst[2] = static_cast<uint8_t>(v.s[0] >> 8);
-  dst[3] = static_cast<uint8_t>(v.s[0]);
-  dst[4] = static_cast<uint8_t>(v.s[1] >> 24);
-  dst[5] = static_cast<uint8_t>(v.s[1] >> 16);
-  dst[6] = static_cast<uint8_t>(v.s[1] >> 8);
-  dst[7] = static_cast<uint8_t>(v.s[1]);
-  dst[8] = static_cast<uint8_t>(v.s[2] >> 24);
-  dst[9] = static_cast<uint8_t>(v.s[2] >> 16);
-  dst[10] = static_cast<uint8_t>(v.s[2] >> 8);
-  dst[11] = static_cast<uint8_t>(v.s[2]);
-  dst[12] = static_cast<uint8_t>(v.s[3] >> 24);
-  dst[13] = static_cast<uint8_t>(v.s[3] >> 16);
-  dst[14] = static_cast<uint8_t>(v.s[3] >> 8);
-  dst[15] = static_cast<uint8_t>(v.s[3]);
+  std::memcpy(to, v.s, sizeof(Vector128));
 }
 
 // One round of AES. "round_key" is a public constant for breaking the
@@ -281,39 +252,57 @@
 inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128
 AesRound(const Vector128& state, const Vector128& round_key) {
   Vector128 result;
+#ifdef ABSL_IS_LITTLE_ENDIAN
   result.s[0] = round_key.s[0] ^                  //
-                te0[uint8_t(state.s[0] >> 24)] ^  //
-                te1[uint8_t(state.s[1] >> 16)] ^  //
-                te2[uint8_t(state.s[2] >> 8)] ^   //
-                te3[uint8_t(state.s[3])];
+                te0[uint8_t(state.s[0])] ^        //
+                te1[uint8_t(state.s[1] >> 8)] ^   //
+                te2[uint8_t(state.s[2] >> 16)] ^  //
+                te3[uint8_t(state.s[3] >> 24)];
   result.s[1] = round_key.s[1] ^                  //
-                te0[uint8_t(state.s[1] >> 24)] ^  //
-                te1[uint8_t(state.s[2] >> 16)] ^  //
-                te2[uint8_t(state.s[3] >> 8)] ^   //
-                te3[uint8_t(state.s[0])];
+                te0[uint8_t(state.s[1])] ^        //
+                te1[uint8_t(state.s[2] >> 8)] ^   //
+                te2[uint8_t(state.s[3] >> 16)] ^  //
+                te3[uint8_t(state.s[0] >> 24)];
   result.s[2] = round_key.s[2] ^                  //
-                te0[uint8_t(state.s[2] >> 24)] ^  //
-                te1[uint8_t(state.s[3] >> 16)] ^  //
-                te2[uint8_t(state.s[0] >> 8)] ^   //
-                te3[uint8_t(state.s[1])];
+                te0[uint8_t(state.s[2])] ^        //
+                te1[uint8_t(state.s[3] >> 8)] ^   //
+                te2[uint8_t(state.s[0] >> 16)] ^  //
+                te3[uint8_t(state.s[1] >> 24)];
   result.s[3] = round_key.s[3] ^                  //
-                te0[uint8_t(state.s[3] >> 24)] ^  //
-                te1[uint8_t(state.s[0] >> 16)] ^  //
-                te2[uint8_t(state.s[1] >> 8)] ^   //
-                te3[uint8_t(state.s[2])];
+                te0[uint8_t(state.s[3])] ^        //
+                te1[uint8_t(state.s[0] >> 8)] ^   //
+                te2[uint8_t(state.s[1] >> 16)] ^  //
+                te3[uint8_t(state.s[2] >> 24)];
+#else
+  result.s[0] = round_key.s[0] ^                  //
+                te0[uint8_t(state.s[0])] ^        //
+                te1[uint8_t(state.s[3] >> 8)] ^   //
+                te2[uint8_t(state.s[2] >> 16)] ^  //
+                te3[uint8_t(state.s[1] >> 24)];
+  result.s[1] = round_key.s[1] ^                  //
+                te0[uint8_t(state.s[1])] ^        //
+                te1[uint8_t(state.s[0] >> 8)] ^   //
+                te2[uint8_t(state.s[3] >> 16)] ^  //
+                te3[uint8_t(state.s[2] >> 24)];
+  result.s[2] = round_key.s[2] ^                  //
+                te0[uint8_t(state.s[2])] ^        //
+                te1[uint8_t(state.s[1] >> 8)] ^   //
+                te2[uint8_t(state.s[0] >> 16)] ^  //
+                te3[uint8_t(state.s[3] >> 24)];
+  result.s[3] = round_key.s[3] ^                  //
+                te0[uint8_t(state.s[3])] ^        //
+                te1[uint8_t(state.s[2] >> 8)] ^   //
+                te2[uint8_t(state.s[1] >> 16)] ^  //
+                te3[uint8_t(state.s[0] >> 24)];
+#endif
   return result;
 }
 
 using ::absl::random_internal::RandenTraits;
 
-// Randen operates on 128-bit vectors.
-struct alignas(16) u64x2 {
-  uint64_t data[2];
-};
-
 // The improved Feistel block shuffle function for 16 blocks.
 inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void BlockShuffle(
-    u64x2* state) {
+    absl::uint128* state) {
   static_assert(RandenTraits::kFeistelBlocks == 16,
                 "Feistel block shuffle only works for 16 blocks.");
 
@@ -323,31 +312,31 @@
   // The fully unrolled loop without the memcpy improves the speed by about
   // 30% over the equivalent:
 #if 0
-  u64x2 source[RandenTraits::kFeistelBlocks];
+  absl::uint128 source[RandenTraits::kFeistelBlocks];
   std::memcpy(source, state, sizeof(source));
   for (size_t i = 0; i < RandenTraits::kFeistelBlocks; i++) {
-    const u64x2 v0 = source[shuffle[i]];
+    const absl::uint128 v0 = source[shuffle[i]];
     state[i] = v0;
   }
   return;
 #endif
 
-  const u64x2 v0 = state[shuffle[0]];
-  const u64x2 v1 = state[shuffle[1]];
-  const u64x2 v2 = state[shuffle[2]];
-  const u64x2 v3 = state[shuffle[3]];
-  const u64x2 v4 = state[shuffle[4]];
-  const u64x2 v5 = state[shuffle[5]];
-  const u64x2 v6 = state[shuffle[6]];
-  const u64x2 v7 = state[shuffle[7]];
-  const u64x2 w0 = state[shuffle[8]];
-  const u64x2 w1 = state[shuffle[9]];
-  const u64x2 w2 = state[shuffle[10]];
-  const u64x2 w3 = state[shuffle[11]];
-  const u64x2 w4 = state[shuffle[12]];
-  const u64x2 w5 = state[shuffle[13]];
-  const u64x2 w6 = state[shuffle[14]];
-  const u64x2 w7 = state[shuffle[15]];
+  const absl::uint128 v0 = state[shuffle[0]];
+  const absl::uint128 v1 = state[shuffle[1]];
+  const absl::uint128 v2 = state[shuffle[2]];
+  const absl::uint128 v3 = state[shuffle[3]];
+  const absl::uint128 v4 = state[shuffle[4]];
+  const absl::uint128 v5 = state[shuffle[5]];
+  const absl::uint128 v6 = state[shuffle[6]];
+  const absl::uint128 v7 = state[shuffle[7]];
+  const absl::uint128 w0 = state[shuffle[8]];
+  const absl::uint128 w1 = state[shuffle[9]];
+  const absl::uint128 w2 = state[shuffle[10]];
+  const absl::uint128 w3 = state[shuffle[11]];
+  const absl::uint128 w4 = state[shuffle[12]];
+  const absl::uint128 w5 = state[shuffle[13]];
+  const absl::uint128 w6 = state[shuffle[14]];
+  const absl::uint128 w7 = state[shuffle[15]];
   state[0] = v0;
   state[1] = v1;
   state[2] = v2;
@@ -371,9 +360,9 @@
 // per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in
 // parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel
 // XORs are 'free' (included in the second AES instruction).
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE const u64x2* FeistelRound(
-    u64x2* ABSL_RANDOM_INTERNAL_RESTRICT state,
-    const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
+inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE const absl::uint128*
+FeistelRound(absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT state,
+             const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
   for (size_t branch = 0; branch < RandenTraits::kFeistelBlocks; branch += 4) {
     const Vector128 s0 = Vector128Load(state + branch);
     const Vector128 s1 = Vector128Load(state + branch + 1);
@@ -398,13 +387,31 @@
 // 2^64 queries if the round function is a PRF. This is similar to the b=8 case
 // of Simpira v2, but more efficient than its generic construction for b=16.
 inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Permute(
-    u64x2* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
+    absl::uint128* state,
+    const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
   for (size_t round = 0; round < RandenTraits::kFeistelRounds; ++round) {
     keys = FeistelRound(state, keys);
     BlockShuffle(state);
   }
 }
 
+// Enables native loads in the round loop by pre-swapping.
+inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void SwapEndian(
+    absl::uint128* state) {
+#ifdef ABSL_IS_BIG_ENDIAN
+  for (uint32_t block = 0; block < RandenTraits::kFeistelBlocks; ++block) {
+    uint64_t new_lo = absl::little_endian::ToHost64(
+        static_cast<uint64_t>(state[block] >> 64));
+    uint64_t new_hi = absl::little_endian::ToHost64(
+        static_cast<uint64_t>((state[block] << 64) >> 64));
+    state[block] = (static_cast<absl::uint128>(new_hi) << 64) | new_lo;
+  }
+#else
+  // Avoid warning about unused variable.
+  (void)state;
+#endif
+}
+
 }  // namespace
 
 namespace absl {
@@ -414,7 +421,11 @@
 const void* RandenSlow::GetKeys() {
   // Round keys for one AES per Feistel round and branch.
   // The canonical implementation uses first digits of Pi.
+#ifdef ABSL_IS_LITTLE_ENDIAN
   return kRandenRoundKeys;
+#else
+  return kRandenRoundKeysBE;
+#endif
 }
 
 void RandenSlow::Absorb(const void* seed_void, void* state_void) {
@@ -437,19 +448,22 @@
 }
 
 void RandenSlow::Generate(const void* keys_void, void* state_void) {
-  static_assert(RandenTraits::kCapacityBytes == sizeof(u64x2),
+  static_assert(RandenTraits::kCapacityBytes == sizeof(absl::uint128),
                 "Capacity mismatch");
 
-  auto* state = reinterpret_cast<u64x2*>(state_void);
-  const auto* keys = reinterpret_cast<const u64x2*>(keys_void);
+  auto* state = reinterpret_cast<absl::uint128*>(state_void);
+  const auto* keys = reinterpret_cast<const absl::uint128*>(keys_void);
 
-  const u64x2 prev_inner = state[0];
+  const absl::uint128 prev_inner = state[0];
+
+  SwapEndian(state);
 
   Permute(state, keys);
 
+  SwapEndian(state);
+
   // Ensure backtracking resistance.
-  state[0].data[0] ^= prev_inner.data[0];
-  state[0].data[1] ^= prev_inner.data[1];
+  *state ^= prev_inner;
 }
 
 }  // namespace random_internal
diff --git a/absl/random/internal/randen_slow_test.cc b/absl/random/internal/randen_slow_test.cc
index 4861ffa..ed60395 100644
--- a/absl/random/internal/randen_slow_test.cc
+++ b/absl/random/internal/randen_slow_test.cc
@@ -25,40 +25,37 @@
 using absl::random_internal::RandenSlow;
 using absl::random_internal::RandenTraits;
 
-// Local state parameters.
-constexpr size_t kSeedBytes =
-    RandenTraits::kStateBytes - RandenTraits::kCapacityBytes;
-constexpr size_t kStateSizeT = RandenTraits::kStateBytes / sizeof(uint64_t);
-constexpr size_t kSeedSizeT = kSeedBytes / sizeof(uint32_t);
-
-struct alignas(16) randen {
-  uint64_t state[kStateSizeT];
-  uint32_t seed[kSeedSizeT];
-};
-
 TEST(RandenSlowTest, Default) {
-  constexpr uint64_t kGolden[] = {
-      0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977,
-      0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912,
-      0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6,
-      0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a,
-      0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37,
-      0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556,
-      0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42,
-      0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc,
-      0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f,
-      0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3,
-      0x026ff374c101da7e, 0x811ef0821c3de851,
+  constexpr uint8_t kGolden[] = {
+      0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d,
+      0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3,
+      0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f,
+      0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0,
+      0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff,
+      0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2,
+      0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5,
+      0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c,
+      0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56,
+      0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4,
+      0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91,
+      0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5,
+      0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d,
+      0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58,
+      0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e,
+      0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49,
+      0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0,
+      0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6,
+      0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61,
+      0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35,
+      0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c,
+      0x82, 0xf0, 0x1e, 0x81,
   };
 
-  alignas(16) randen d;
-  std::memset(d.state, 0, sizeof(d.state));
-  RandenSlow::Generate(RandenSlow::GetKeys(), d.state);
+  alignas(16) uint8_t state[RandenTraits::kStateBytes];
+  std::memset(state, 0, sizeof(state));
 
-  uint64_t* id = d.state;
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(absl::little_endian::FromHost64(elem), *id++);
-  }
+  RandenSlow::Generate(RandenSlow::GetKeys(), state);
+  EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state)));
 }
 
 }  // namespace
diff --git a/absl/random/internal/randen_test.cc b/absl/random/internal/randen_test.cc
index c186fe0..92773b8 100644
--- a/absl/random/internal/randen_test.cc
+++ b/absl/random/internal/randen_test.cc
@@ -23,9 +23,6 @@
 
 using absl::random_internal::Randen;
 
-// Local state parameters.
-constexpr size_t kStateSizeT = Randen::kStateBytes / sizeof(uint64_t);
-
 TEST(RandenTest, CopyAndMove) {
   static_assert(std::is_copy_constructible<Randen>::value,
                 "Randen must be copy constructible");
@@ -41,30 +38,38 @@
 }
 
 TEST(RandenTest, Default) {
-  constexpr uint64_t kGolden[] = {
-      0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977,
-      0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912,
-      0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6,
-      0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a,
-      0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37,
-      0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556,
-      0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42,
-      0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc,
-      0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f,
-      0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3,
-      0x026ff374c101da7e, 0x811ef0821c3de851,
+  constexpr uint8_t kGolden[] = {
+      0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d,
+      0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3,
+      0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f,
+      0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0,
+      0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff,
+      0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2,
+      0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5,
+      0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c,
+      0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56,
+      0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4,
+      0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91,
+      0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5,
+      0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d,
+      0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58,
+      0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e,
+      0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49,
+      0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0,
+      0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6,
+      0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61,
+      0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35,
+      0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c,
+      0x82, 0xf0, 0x1e, 0x81,
   };
 
-  alignas(16) uint64_t state[kStateSizeT];
+  alignas(16) uint8_t state[Randen::kStateBytes];
   std::memset(state, 0, sizeof(state));
 
   Randen r;
   r.Generate(state);
 
-  auto id = std::begin(state);
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, *id++);
-  }
+  EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state)));
 }
 
 }  // namespace
diff --git a/absl/random/internal/seed_material.cc b/absl/random/internal/seed_material.cc
index 4d38a57..c03cad8 100644
--- a/absl/random/internal/seed_material.cc
+++ b/absl/random/internal/seed_material.cc
@@ -28,6 +28,7 @@
 #include <cstdlib>
 #include <cstring>
 
+#include "absl/base/dynamic_annotations.h"
 #include "absl/base/internal/raw_logging.h"
 #include "absl/strings/ascii.h"
 #include "absl/strings/escaping.h"
@@ -50,6 +51,18 @@
 
 #endif
 
+#if defined(__GLIBC__) && \
+    (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))
+// glibc >= 2.25 has getentropy()
+#define ABSL_RANDOM_USE_GET_ENTROPY 1
+#endif
+
+#if defined(__EMSCRIPTEN__)
+#include <sys/random.h>
+// Emscripten has getentropy, but it resides in a different header.
+#define ABSL_RANDOM_USE_GET_ENTROPY 1
+#endif
+
 #if defined(ABSL_RANDOM_USE_BCRYPT)
 #include <bcrypt.h>
 
@@ -122,8 +135,32 @@
 
 #else
 
+#if defined(ABSL_RANDOM_USE_GET_ENTROPY)
+// On *nix, use getentropy() if supported. Note that libc may support
+// getentropy(), but the kernel may not, in which case this function will return
+// false.
+bool ReadSeedMaterialFromGetEntropy(absl::Span<uint32_t> values) {
+  auto buffer = reinterpret_cast<uint8_t*>(values.data());
+  size_t buffer_size = sizeof(uint32_t) * values.size();
+  while (buffer_size > 0) {
+    // getentropy() has a maximum permitted length of 256.
+    size_t to_read = std::min<size_t>(buffer_size, 256);
+    int result = getentropy(buffer, to_read);
+    if (result < 0) {
+      return false;
+    }
+    // https://github.com/google/sanitizers/issues/1173
+    // MemorySanitizer can't see through getentropy().
+    ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(buffer, to_read);
+    buffer += to_read;
+    buffer_size -= to_read;
+  }
+  return true;
+}
+#endif  // defined(ABSL_RANDOM_GETENTROPY)
+
 // On *nix, read entropy from /dev/urandom.
-bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
+bool ReadSeedMaterialFromDevURandom(absl::Span<uint32_t> values) {
   const char kEntropyFile[] = "/dev/urandom";
 
   auto buffer = reinterpret_cast<uint8_t*>(values.data());
@@ -150,6 +187,17 @@
   return success;
 }
 
+bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
+#if defined(ABSL_RANDOM_USE_GET_ENTROPY)
+  if (ReadSeedMaterialFromGetEntropy(values)) {
+    return true;
+  }
+#endif
+  // Libc may support getentropy, but the kernel may not, so we still have
+  // to fallback to ReadSeedMaterialFromDevURandom().
+  return ReadSeedMaterialFromDevURandom(values);
+}
+
 #endif
 
 }  // namespace
diff --git a/absl/random/mocking_bit_gen_test.cc b/absl/random/mocking_bit_gen_test.cc
index f63b6e4..c713cea 100644
--- a/absl/random/mocking_bit_gen_test.cc
+++ b/absl/random/mocking_bit_gen_test.cc
@@ -15,6 +15,7 @@
 //
 #include "absl/random/mocking_bit_gen.h"
 
+#include <cmath>
 #include <numeric>
 #include <random>
 
@@ -328,8 +329,9 @@
 
   absl::MockingBitGen gen;
   ON_CALL(absl::MockPoisson<int>(), Call(gen, _))
-      .WillByDefault(
-          [](double lambda) { return static_cast<int>(lambda * 10); });
+      .WillByDefault([](double lambda) {
+        return static_cast<int>(std::rint(lambda * 10));
+      });
   EXPECT_EQ(absl::Poisson<int>(gen, 1.7), 17);
   EXPECT_EQ(absl::Poisson<int>(gen, 0.03), 0);
 }
diff --git a/absl/random/uniform_real_distribution_test.cc b/absl/random/uniform_real_distribution_test.cc
index 18bcd3b..035bd28 100644
--- a/absl/random/uniform_real_distribution_test.cc
+++ b/absl/random/uniform_real_distribution_test.cc
@@ -14,6 +14,7 @@
 
 #include "absl/random/uniform_real_distribution.h"
 
+#include <cfloat>
 #include <cmath>
 #include <cstdint>
 #include <iterator>
@@ -70,6 +71,14 @@
 TYPED_TEST_SUITE(UniformRealDistributionTest, RealTypes);
 
 TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) {
+#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0
+  // We're using an x87-compatible FPU, and intermediate operations are
+  // performed with 80-bit floats. This produces slightly different results from
+  // what we expect below.
+  GTEST_SKIP()
+      << "Skipping the test because we detected x87 floating-point semantics";
+#endif
+
   using param_type =
       typename absl::uniform_real_distribution<TypeParam>::param_type;
 
diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel
index 189bd73..bae5156 100644
--- a/absl/status/BUILD.bazel
+++ b/absl/status/BUILD.bazel
@@ -17,7 +17,6 @@
 # It will expand later to have utilities around `Status` like `StatusOr`,
 # `StatusBuilder` and macros.
 
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
@@ -48,6 +47,7 @@
         "//absl/container:inlined_vector",
         "//absl/debugging:stacktrace",
         "//absl/debugging:symbolize",
+        "//absl/functional:function_ref",
         "//absl/strings",
         "//absl/strings:cord",
         "//absl/strings:str_format",
@@ -78,6 +78,7 @@
     copts = ABSL_DEFAULT_COPTS,
     deps = [
         ":status",
+        "//absl/base",
         "//absl/base:core_headers",
         "//absl/base:raw_logging_internal",
         "//absl/meta:type_traits",
@@ -96,6 +97,7 @@
         ":statusor",
         "//absl/base",
         "//absl/memory",
+        "//absl/strings",
         "//absl/types:any",
         "//absl/utility",
         "@com_google_googletest//:gtest_main",
diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt
index f0d798a..f107c85 100644
--- a/absl/status/CMakeLists.txt
+++ b/absl/status/CMakeLists.txt
@@ -29,6 +29,7 @@
     absl::atomic_hook
     absl::config
     absl::core_headers
+    absl::function_ref
     absl::raw_logging_internal
     absl::inlined_vector
     absl::stacktrace
@@ -50,7 +51,7 @@
   DEPS
     absl::status
     absl::strings
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -64,6 +65,7 @@
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
+    absl::base
     absl::status
     absl::core_headers
     absl::raw_logging_internal
@@ -84,5 +86,5 @@
   DEPS
     absl::status
     absl::statusor
-    gmock_main
+    GTest::gmock_main
 )
diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h
index 99a2d96..ac12940 100644
--- a/absl/status/internal/status_internal.h
+++ b/absl/status/internal/status_internal.h
@@ -47,12 +47,12 @@
 
 // Reference-counted representation of Status data.
 struct StatusRep {
-  StatusRep(absl::StatusCode code, std::string message,
-            std::unique_ptr<status_internal::Payloads> payloads)
+  StatusRep(absl::StatusCode code_arg, absl::string_view message_arg,
+            std::unique_ptr<status_internal::Payloads> payloads_arg)
       : ref(int32_t{1}),
-        code(code),
-        message(std::move(message)),
-        payloads(std::move(payloads)) {}
+        code(code_arg),
+        message(message_arg),
+        payloads(std::move(payloads_arg)) {}
 
   std::atomic<int32_t> ref;
   absl::StatusCode code;
diff --git a/absl/status/status.cc b/absl/status/status.cc
index 51a0d26..bcf3413 100644
--- a/absl/status/status.cc
+++ b/absl/status/status.cc
@@ -161,7 +161,7 @@
 }
 
 void Status::ForEachPayload(
-    const std::function<void(absl::string_view, const absl::Cord&)>& visitor)
+    absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
     const {
   if (auto* payloads = GetPayloads()) {
     bool in_reverse =
@@ -207,19 +207,10 @@
   }
 }
 
-uintptr_t Status::NewRep(
-    absl::StatusCode code, absl::string_view msg,
-    std::unique_ptr<status_internal::Payloads> payloads) {
-  status_internal::StatusRep* rep = new status_internal::StatusRep(
-      code, std::string(msg.data(), msg.size()),
-      std::move(payloads));
-  return PointerToRep(rep);
-}
-
 Status::Status(absl::StatusCode code, absl::string_view msg)
     : rep_(CodeToInlinedRep(code)) {
   if (code != absl::StatusCode::kOk && !msg.empty()) {
-    rep_ = NewRep(code, msg, nullptr);
+    rep_ = PointerToRep(new status_internal::StatusRep(code, msg, nullptr));
   }
 }
 
@@ -238,9 +229,9 @@
 void Status::PrepareToModify() {
   ABSL_RAW_CHECK(!ok(), "PrepareToModify shouldn't be called on OK status.");
   if (IsInlined(rep_)) {
-    rep_ =
-        NewRep(static_cast<absl::StatusCode>(raw_code()), absl::string_view(),
-               nullptr);
+    rep_ = PointerToRep(new status_internal::StatusRep(
+        static_cast<absl::StatusCode>(raw_code()), absl::string_view(),
+        nullptr));
     return;
   }
 
@@ -251,8 +242,9 @@
     if (rep->payloads) {
       payloads = absl::make_unique<status_internal::Payloads>(*rep->payloads);
     }
-    rep_ = NewRep(rep->code, message(),
-                  std::move(payloads));
+    status_internal::StatusRep* const new_rep = new status_internal::StatusRep(
+        rep->code, message(), std::move(payloads));
+    rep_ = PointerToRep(new_rep);
     UnrefNonInlined(rep_i);
   }
 }
@@ -316,7 +308,7 @@
 }
 
 std::ostream& operator<<(std::ostream& os, const Status& x) {
-  os << x.ToString();
+  os << x.ToString(StatusToStringMode::kWithEverything);
   return os;
 }
 
diff --git a/absl/status/status.h b/absl/status/status.h
index 61486fe..39071e5 100644
--- a/absl/status/status.h
+++ b/absl/status/status.h
@@ -55,6 +55,7 @@
 #include <string>
 
 #include "absl/container/inlined_vector.h"
+#include "absl/functional/function_ref.h"
 #include "absl/status/internal/status_internal.h"
 #include "absl/strings/cord.h"
 #include "absl/strings/string_view.h"
@@ -80,7 +81,7 @@
 // `kFailedPrecondition` if both codes apply. Similarly prefer `kNotFound` or
 // `kAlreadyExists` over `kFailedPrecondition`.
 //
-// Because these errors may travel RPC boundaries, these codes are tied to the
+// Because these errors may cross RPC boundaries, these codes are tied to the
 // `google.rpc.Code` definitions within
 // https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
 // The string value of these RPC codes is denoted within each enum below.
@@ -114,10 +115,10 @@
   // StatusCode::kInvalidArgument
   //
   // kInvalidArgument (gRPC code "INVALID_ARGUMENT") indicates the caller
-  // specified an invalid argument, such a malformed filename. Note that such
-  // errors should be narrowly limited to indicate to the invalid nature of the
-  // arguments themselves. Errors with validly formed arguments that may cause
-  // errors with the state of the receiving system should be denoted with
+  // specified an invalid argument, such as a malformed filename. Note that use
+  // of such errors should be narrowly limited to indicate the invalid nature of
+  // the arguments themselves. Errors with validly formed arguments that may
+  // cause errors with the state of the receiving system should be denoted with
   // `kFailedPrecondition` instead.
   kInvalidArgument = 3,
 
@@ -137,14 +138,15 @@
   //
   // `kNotFound` is useful if a request should be denied for an entire class of
   // users, such as during a gradual feature rollout or undocumented allow list.
-  // If, instead, a request should be denied for specific sets of users, such as
-  // through user-based access control, use `kPermissionDenied` instead.
+  // If a request should be denied for specific sets of users, such as through
+  // user-based access control, use `kPermissionDenied` instead.
   kNotFound = 5,
 
   // StatusCode::kAlreadyExists
   //
-  // kAlreadyExists (gRPC code "ALREADY_EXISTS") indicates the entity that a
-  // caller attempted to create (such as file or directory) is already present.
+  // kAlreadyExists (gRPC code "ALREADY_EXISTS") indicates that the entity a
+  // caller attempted to create (such as a file or directory) is already
+  // present.
   kAlreadyExists = 6,
 
   // StatusCode::kPermissionDenied
@@ -183,7 +185,7 @@
   //      level (such as when a client-specified test-and-set fails, indicating
   //      the client should restart a read-modify-write sequence).
   //  (c) Use `kFailedPrecondition` if the client should not retry until
-  //      the system state has been explicitly fixed. For example, if an "rmdir"
+  //      the system state has been explicitly fixed. For example, if a "rmdir"
   //      fails because the directory is non-empty, `kFailedPrecondition`
   //      should be returned since the client should not retry unless
   //      the files are deleted from the directory.
@@ -283,7 +285,7 @@
 // absl::StatusToStringMode
 //
 // An `absl::StatusToStringMode` is an enumerated type indicating how
-// `absl::Status::ToString()` should construct the output string for an non-ok
+// `absl::Status::ToString()` should construct the output string for a non-ok
 // status.
 enum class StatusToStringMode : int {
   // ToString will not contain any extra data (such as payloads). It will only
@@ -293,6 +295,8 @@
   kWithPayload = 1 << 0,
   // ToString will include all the extra data this Status has.
   kWithEverything = ~kWithNoExtraData,
+  // Default mode used by ToString. Its exact value might change in the future.
+  kDefault = kWithPayload,
 };
 
 // absl::StatusToStringMode is specified as a bitmask type, which means the
@@ -343,7 +347,7 @@
 // API developers should construct their functions to return `absl::OkStatus()`
 // upon success, or an `absl::StatusCode` upon another type of error (e.g
 // an `absl::StatusCode::kInvalidArgument` error). The API provides convenience
-// functions to constuct each status code.
+// functions to construct each status code.
 //
 // Example:
 //
@@ -491,7 +495,7 @@
   // Returns the error message associated with this error code, if available.
   // Note that this message rarely describes the error code.  It is not unusual
   // for the error message to be the empty string. As a result, prefer
-  // `Status::ToString()` for debug logging.
+  // `operator<<` or `Status::ToString()` for debug logging.
   absl::string_view message() const;
 
   friend bool operator==(const Status&, const Status&);
@@ -509,7 +513,7 @@
   // result, and the payloads to be printed use the status payload printer
   // mechanism (which is internal).
   std::string ToString(
-      StatusToStringMode mode = StatusToStringMode::kWithPayload) const;
+      StatusToStringMode mode = StatusToStringMode::kDefault) const;
 
   // Status::IgnoreError()
   //
@@ -587,7 +591,7 @@
   // NOTE: Any mutation on the same 'absl::Status' object during visitation is
   // forbidden and could result in undefined behavior.
   void ForEachPayload(
-      const std::function<void(absl::string_view, const absl::Cord&)>& visitor)
+      absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
       const;
 
  private:
diff --git a/absl/status/status_test.cc b/absl/status/status_test.cc
index 0e1a43c..1b038f6 100644
--- a/absl/status/status_test.cc
+++ b/absl/status/status_test.cc
@@ -36,7 +36,9 @@
 // its creator, and its classifier.
 struct ErrorTest {
   absl::StatusCode code;
-  using Creator = absl::Status (*)(absl::string_view);
+  using Creator = absl::Status (*)(
+      absl::string_view
+  );
   using Classifier = bool (*)(const absl::Status&);
   Creator creator;
   Classifier classifier;
@@ -78,7 +80,9 @@
     // expected error code and message.
     std::string message =
         absl::StrCat("error code ", test.code, " test message");
-    absl::Status status = test.creator(message);
+    absl::Status status = test.creator(
+        message
+    );
     EXPECT_EQ(test.code, status.code());
     EXPECT_EQ(message, status.message());
 
diff --git a/absl/status/statusor.cc b/absl/status/statusor.cc
index b954b45..96642b3 100644
--- a/absl/status/statusor.cc
+++ b/absl/status/statusor.cc
@@ -16,6 +16,7 @@
 #include <cstdlib>
 #include <utility>
 
+#include "absl/base/call_once.h"
 #include "absl/base/internal/raw_logging.h"
 #include "absl/status/status.h"
 #include "absl/strings/str_cat.h"
@@ -26,13 +27,44 @@
 BadStatusOrAccess::BadStatusOrAccess(absl::Status status)
     : status_(std::move(status)) {}
 
-BadStatusOrAccess::~BadStatusOrAccess() = default;
+BadStatusOrAccess::BadStatusOrAccess(const BadStatusOrAccess& other)
+    : status_(other.status_) {}
+
+BadStatusOrAccess& BadStatusOrAccess::operator=(
+    const BadStatusOrAccess& other) {
+  // Ensure assignment is correct regardless of whether this->InitWhat() has
+  // already been called.
+  other.InitWhat();
+  status_ = other.status_;
+  what_ = other.what_;
+  return *this;
+}
+
+BadStatusOrAccess& BadStatusOrAccess::operator=(BadStatusOrAccess&& other) {
+  // Ensure assignment is correct regardless of whether this->InitWhat() has
+  // already been called.
+  other.InitWhat();
+  status_ = std::move(other.status_);
+  what_ = std::move(other.what_);
+  return *this;
+}
+
+BadStatusOrAccess::BadStatusOrAccess(BadStatusOrAccess&& other)
+    : status_(std::move(other.status_)) {}
+
 const char* BadStatusOrAccess::what() const noexcept {
-  return "Bad StatusOr access";
+  InitWhat();
+  return what_.c_str();
 }
 
 const absl::Status& BadStatusOrAccess::status() const { return status_; }
 
+void BadStatusOrAccess::InitWhat() const {
+  absl::call_once(init_what_, [this] {
+    what_ = absl::StrCat("Bad StatusOr access: ", status_.ToString());
+  });
+}
+
 namespace internal_statusor {
 
 void Helper::HandleInvalidStatusCtorArg(absl::Status* status) {
diff --git a/absl/status/statusor.h b/absl/status/statusor.h
index b7c55cc..c051fbb 100644
--- a/absl/status/statusor.h
+++ b/absl/status/statusor.h
@@ -44,6 +44,7 @@
 #include <utility>
 
 #include "absl/base/attributes.h"
+#include "absl/base/call_once.h"
 #include "absl/meta/type_traits.h"
 #include "absl/status/internal/statusor_internal.h"
 #include "absl/status/status.h"
@@ -72,13 +73,18 @@
 class BadStatusOrAccess : public std::exception {
  public:
   explicit BadStatusOrAccess(absl::Status status);
-  ~BadStatusOrAccess() override;
+  ~BadStatusOrAccess() override = default;
+
+  BadStatusOrAccess(const BadStatusOrAccess& other);
+  BadStatusOrAccess& operator=(const BadStatusOrAccess& other);
+  BadStatusOrAccess(BadStatusOrAccess&& other);
+  BadStatusOrAccess& operator=(BadStatusOrAccess&& other);
 
   // BadStatusOrAccess::what()
   //
   // Returns the associated explanatory string of the `absl::StatusOr<T>`
-  // object's error code. This function only returns the string literal "Bad
-  // StatusOr Access" for cases when evaluating general exceptions.
+  // object's error code. This function contains information about the failing
+  // status, but its exact formatting may change and should not be depended on.
   //
   // The pointer of this string is guaranteed to be valid until any non-const
   // function is invoked on the exception object.
@@ -91,7 +97,11 @@
   const absl::Status& status() const;
 
  private:
+  void InitWhat() const;
+
   absl::Status status_;
+  mutable absl::once_flag init_what_;
+  mutable std::string what_;
 };
 
 // Returned StatusOr objects may not be ignored.
@@ -419,8 +429,8 @@
   // if `T` can be constructed from a `U`. Can accept move or copy constructors.
   //
   // This constructor is explicit if `U` is not convertible to `T`. To avoid
-  // ambiguity, this constuctor is disabled if `U` is a `StatusOr<J>`, where `J`
-  // is convertible to `T`.
+  // ambiguity, this constructor is disabled if `U` is a `StatusOr<J>`, where
+  // `J` is convertible to `T`.
   template <
       typename U = T,
       absl::enable_if_t<
@@ -437,8 +447,7 @@
                               T, U&&>>>>>::value,
           int> = 0>
   StatusOr(U&& u)  // NOLINT
-      : StatusOr(absl::in_place, std::forward<U>(u)) {
-  }
+      : StatusOr(absl::in_place, std::forward<U>(u)) {}
 
   template <
       typename U = T,
@@ -457,8 +466,7 @@
               absl::negation<std::is_convertible<U&&, T>>>::value,
           int> = 0>
   explicit StatusOr(U&& u)  // NOLINT
-      : StatusOr(absl::in_place, std::forward<U>(u)) {
-  }
+      : StatusOr(absl::in_place, std::forward<U>(u)) {}
 
   // StatusOr<T>::ok()
   //
@@ -481,7 +489,7 @@
   // Returns a reference to the current `absl::Status` contained within the
   // `absl::StatusOr<T>`. If `absl::StatusOr<T>` contains a `T`, then this
   // function returns `absl::OkStatus()`.
-  const Status& status() const &;
+  const Status& status() const&;
   Status status() &&;
 
   // StatusOr<T>::value()
@@ -510,10 +518,10 @@
   //
   // The `std::move` on statusor instead of on the whole expression enables
   // warnings about possible uses of the statusor object after the move.
-  const T& value() const&;
-  T& value() &;
-  const T&& value() const&&;
-  T&& value() &&;
+  const T& value() const& ABSL_ATTRIBUTE_LIFETIME_BOUND;
+  T& value() & ABSL_ATTRIBUTE_LIFETIME_BOUND;
+  const T&& value() const&& ABSL_ATTRIBUTE_LIFETIME_BOUND;
+  T&& value() && ABSL_ATTRIBUTE_LIFETIME_BOUND;
 
   // StatusOr<T>:: operator*()
   //
@@ -525,10 +533,10 @@
   // `absl::StatusOr<T>`. Alternatively, see the `value()` member function for a
   // similar API that guarantees crashing or throwing an exception if there is
   // no current value.
-  const T& operator*() const&;
-  T& operator*() &;
-  const T&& operator*() const&&;
-  T&& operator*() &&;
+  const T& operator*() const& ABSL_ATTRIBUTE_LIFETIME_BOUND;
+  T& operator*() & ABSL_ATTRIBUTE_LIFETIME_BOUND;
+  const T&& operator*() const&& ABSL_ATTRIBUTE_LIFETIME_BOUND;
+  T&& operator*() && ABSL_ATTRIBUTE_LIFETIME_BOUND;
 
   // StatusOr<T>::operator->()
   //
@@ -537,8 +545,8 @@
   // REQUIRES: `this->ok() == true`, otherwise the behavior is undefined.
   //
   // Use `this->ok()` to verify that there is a current value.
-  const T* operator->() const;
-  T* operator->();
+  const T* operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
+  T* operator->() ABSL_ATTRIBUTE_LIFETIME_BOUND;
 
   // StatusOr<T>::value_or()
   //
@@ -661,7 +669,9 @@
     : Base(absl::in_place, ilist, std::forward<Args>(args)...) {}
 
 template <typename T>
-const Status& StatusOr<T>::status() const & { return this->status_; }
+const Status& StatusOr<T>::status() const& {
+  return this->status_;
+}
 template <typename T>
 Status StatusOr<T>::status() && {
   return ok() ? OkStatus() : std::move(this->status_);
diff --git a/absl/status/statusor_test.cc b/absl/status/statusor_test.cc
index c2e8fb7..7cae90e 100644
--- a/absl/status/statusor_test.cc
+++ b/absl/status/statusor_test.cc
@@ -17,6 +17,7 @@
 #include <array>
 #include <initializer_list>
 #include <memory>
+#include <string>
 #include <type_traits>
 #include <utility>
 
@@ -25,6 +26,7 @@
 #include "absl/base/casts.h"
 #include "absl/memory/memory.h"
 #include "absl/status/status.h"
+#include "absl/strings/string_view.h"
 #include "absl/types/any.h"
 #include "absl/utility/utility.h"
 
@@ -34,6 +36,7 @@
 using ::testing::AnyWith;
 using ::testing::ElementsAre;
 using ::testing::Field;
+using ::testing::HasSubstr;
 using ::testing::Ne;
 using ::testing::Not;
 using ::testing::Pointee;
@@ -257,9 +260,9 @@
 
 TEST(StatusOr, TestValueOrDieOverloadForConstTemporary) {
   static_assert(
-      std::is_same<const int&&,
-                   decltype(
-                       std::declval<const absl::StatusOr<int>&&>().value())>(),
+      std::is_same<
+          const int&&,
+          decltype(std::declval<const absl::StatusOr<int>&&>().value())>(),
       "value() for const temporaries should return const T&&");
 }
 
@@ -303,20 +306,57 @@
   EXPECT_NE(status.message(), "Some error");
 }
 
+TEST(BadStatusOrAccessTest, CopyConstructionWhatOk) {
+  absl::Status error =
+      absl::InternalError("some arbitrary message too big for the sso buffer");
+  absl::BadStatusOrAccess e1{error};
+  absl::BadStatusOrAccess e2{e1};
+  EXPECT_THAT(e1.what(), HasSubstr(error.ToString()));
+  EXPECT_THAT(e2.what(), HasSubstr(error.ToString()));
+}
+
+TEST(BadStatusOrAccessTest, CopyAssignmentWhatOk) {
+  absl::Status error =
+      absl::InternalError("some arbitrary message too big for the sso buffer");
+  absl::BadStatusOrAccess e1{error};
+  absl::BadStatusOrAccess e2{absl::InternalError("other")};
+  e2 = e1;
+  EXPECT_THAT(e1.what(), HasSubstr(error.ToString()));
+  EXPECT_THAT(e2.what(), HasSubstr(error.ToString()));
+}
+
+TEST(BadStatusOrAccessTest, MoveConstructionWhatOk) {
+  absl::Status error =
+      absl::InternalError("some arbitrary message too big for the sso buffer");
+  absl::BadStatusOrAccess e1{error};
+  absl::BadStatusOrAccess e2{std::move(e1)};
+  EXPECT_THAT(e2.what(), HasSubstr(error.ToString()));
+}
+
+TEST(BadStatusOrAccessTest, MoveAssignmentWhatOk) {
+  absl::Status error =
+      absl::InternalError("some arbitrary message too big for the sso buffer");
+  absl::BadStatusOrAccess e1{error};
+  absl::BadStatusOrAccess e2{absl::InternalError("other")};
+  e2 = std::move(e1);
+  EXPECT_THAT(e2.what(), HasSubstr(error.ToString()));
+}
+
 // Define `EXPECT_DEATH_OR_THROW` to test the behavior of `StatusOr::value`,
 // which either throws `BadStatusOrAccess` or `LOG(FATAL)` based on whether
 // exceptions are enabled.
 #ifdef ABSL_HAVE_EXCEPTIONS
-#define EXPECT_DEATH_OR_THROW(statement, status_)    \
-  EXPECT_THROW(                                      \
-      {                                              \
-        try {                                        \
-          statement;                                 \
-        } catch (const absl::BadStatusOrAccess& e) { \
-          EXPECT_EQ(e.status(), status_);            \
-          throw;                                     \
-        }                                            \
-      },                                             \
+#define EXPECT_DEATH_OR_THROW(statement, status_)                  \
+  EXPECT_THROW(                                                    \
+      {                                                            \
+        try {                                                      \
+          statement;                                               \
+        } catch (const absl::BadStatusOrAccess& e) {               \
+          EXPECT_EQ(e.status(), status_);                          \
+          EXPECT_THAT(e.what(), HasSubstr(e.status().ToString())); \
+          throw;                                                   \
+        }                                                          \
+      },                                                           \
       absl::BadStatusOrAccess);
 #else  // ABSL_HAVE_EXCEPTIONS
 #define EXPECT_DEATH_OR_THROW(statement, status) \
@@ -412,8 +452,6 @@
   EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled);
 }
 
-
-
 TEST(StatusOr, TestValueCtor) {
   const int kI = 4;
   const absl::StatusOr<int> thing(kI);
@@ -1300,8 +1338,6 @@
   EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown);
 }
 
-
-
 TEST(StatusOr, TestPointerStatusCtor) {
   absl::StatusOr<int*> thing(absl::CancelledError());
   EXPECT_FALSE(thing.ok());
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index 123b5ef..090fc58 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -13,7 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
@@ -269,10 +268,18 @@
     name = "cord_internal",
     srcs = [
         "internal/cord_internal.cc",
+        "internal/cord_rep_btree.cc",
+        "internal/cord_rep_btree_navigator.cc",
+        "internal/cord_rep_btree_reader.cc",
+        "internal/cord_rep_consume.cc",
         "internal/cord_rep_ring.cc",
     ],
     hdrs = [
         "internal/cord_internal.h",
+        "internal/cord_rep_btree.h",
+        "internal/cord_rep_btree_navigator.h",
+        "internal/cord_rep_btree_reader.h",
+        "internal/cord_rep_consume.h",
         "internal/cord_rep_flat.h",
         "internal/cord_rep_ring.h",
         "internal/cord_rep_ring_reader.h",
@@ -292,7 +299,92 @@
         "//absl/container:compressed_tuple",
         "//absl/container:inlined_vector",
         "//absl/container:layout",
+        "//absl/functional:function_ref",
         "//absl/meta:type_traits",
+        "//absl/types:span",
+    ],
+)
+
+cc_test(
+    name = "cord_internal_test",
+    srcs = ["internal/cord_internal_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":cord_internal",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "cord_rep_btree_test",
+    size = "medium",
+    srcs = ["internal/cord_rep_btree_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":cord_internal",
+        ":cord_rep_test_util",
+        ":strings",
+        "//absl/base:config",
+        "//absl/base:raw_logging_internal",
+        "//absl/cleanup",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "cord_rep_btree_navigator_test",
+    size = "medium",
+    srcs = ["internal/cord_rep_btree_navigator_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":cord_internal",
+        ":cord_rep_test_util",
+        ":strings",
+        "//absl/base:config",
+        "//absl/base:raw_logging_internal",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "cord_rep_btree_reader_test",
+    size = "medium",
+    srcs = ["internal/cord_rep_btree_reader_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":cord",
+        ":cord_internal",
+        ":cord_rep_test_util",
+        ":strings",
+        "//absl/base:config",
+        "//absl/base:raw_logging_internal",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "cordz_update_tracker",
+    hdrs = ["internal/cordz_update_tracker.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    visibility = [
+        "//absl:__subpackages__",
+    ],
+    deps = ["//absl/base:config"],
+)
+
+cc_test(
+    name = "cordz_update_tracker_test",
+    srcs = ["internal/cordz_update_tracker_test.cc"],
+    deps = [
+        ":cordz_update_tracker",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/synchronization",
+        "@com_google_googletest//:gtest_main",
     ],
 )
 
@@ -307,10 +399,16 @@
     copts = ABSL_DEFAULT_COPTS,
     deps = [
         ":cord_internal",
+        ":cordz_functions",
+        ":cordz_info",
+        ":cordz_statistics",
+        ":cordz_update_scope",
+        ":cordz_update_tracker",
         ":internal",
         ":str_format",
         ":strings",
         "//absl/base",
+        "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:endian",
         "//absl/base:raw_logging_internal",
@@ -323,6 +421,215 @@
 )
 
 cc_library(
+    name = "cordz_handle",
+    srcs = ["internal/cordz_handle.cc"],
+    hdrs = ["internal/cordz_handle.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    visibility = [
+        "//absl:__subpackages__",
+    ],
+    deps = [
+        "//absl/base",
+        "//absl/base:config",
+        "//absl/base:raw_logging_internal",
+        "//absl/synchronization",
+    ],
+)
+
+cc_library(
+    name = "cordz_info",
+    srcs = ["internal/cordz_info.cc"],
+    hdrs = ["internal/cordz_info.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    visibility = [
+        "//absl:__subpackages__",
+    ],
+    deps = [
+        ":cord_internal",
+        ":cordz_functions",
+        ":cordz_handle",
+        ":cordz_statistics",
+        ":cordz_update_tracker",
+        "//absl/base",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/base:raw_logging_internal",
+        "//absl/container:inlined_vector",
+        "//absl/debugging:stacktrace",
+        "//absl/synchronization",
+        "//absl/types:span",
+    ],
+)
+
+cc_library(
+    name = "cordz_update_scope",
+    hdrs = ["internal/cordz_update_scope.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    visibility = [
+        "//absl:__subpackages__",
+    ],
+    deps = [
+        ":cord_internal",
+        ":cordz_info",
+        ":cordz_update_tracker",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+    ],
+)
+
+cc_test(
+    name = "cordz_update_scope_test",
+    srcs = ["internal/cordz_update_scope_test.cc"],
+    copts = ABSL_DEFAULT_COPTS,
+    deps = [
+        ":cord_internal",
+        ":cordz_info",
+        ":cordz_test_helpers",
+        ":cordz_update_scope",
+        ":cordz_update_tracker",
+        "//absl/base:config",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "cordz_sample_token",
+    srcs = ["internal/cordz_sample_token.cc"],
+    hdrs = ["internal/cordz_sample_token.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    visibility = [
+        "//absl:__subpackages__",
+    ],
+    deps = [
+        ":cordz_handle",
+        ":cordz_info",
+        "//absl/base:config",
+    ],
+)
+
+cc_library(
+    name = "cordz_functions",
+    srcs = ["internal/cordz_functions.cc"],
+    hdrs = ["internal/cordz_functions.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    visibility = [
+        "//absl:__subpackages__",
+    ],
+    deps = [
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/base:raw_logging_internal",
+        "//absl/profiling:exponential_biased",
+    ],
+)
+
+cc_library(
+    name = "cordz_statistics",
+    hdrs = ["internal/cordz_statistics.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    visibility = [
+        "//absl:__subpackages__",
+    ],
+    deps = [
+        ":cordz_update_tracker",
+        "//absl/base:config",
+    ],
+)
+
+cc_test(
+    name = "cordz_functions_test",
+    srcs = [
+        "internal/cordz_functions_test.cc",
+    ],
+    deps = [
+        ":cordz_functions",
+        ":cordz_test_helpers",
+        "//absl/base:config",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "cordz_handle_test",
+    srcs = [
+        "internal/cordz_handle_test.cc",
+    ],
+    deps = [
+        ":cordz_handle",
+        "//absl/base:config",
+        "//absl/memory",
+        "//absl/random",
+        "//absl/random:distributions",
+        "//absl/synchronization",
+        "//absl/synchronization:thread_pool",
+        "//absl/time",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "cordz_info_test",
+    srcs = [
+        "internal/cordz_info_test.cc",
+    ],
+    deps = [
+        ":cord_internal",
+        ":cordz_handle",
+        ":cordz_info",
+        ":cordz_statistics",
+        ":cordz_test_helpers",
+        ":cordz_update_tracker",
+        ":strings",
+        "//absl/base:config",
+        "//absl/debugging:stacktrace",
+        "//absl/debugging:symbolize",
+        "//absl/types:span",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "cordz_info_statistics_test",
+    srcs = [
+        "internal/cordz_info_statistics_test.cc",
+    ],
+    deps = [
+        ":cord",
+        ":cord_internal",
+        ":cordz_info",
+        ":cordz_sample_token",
+        ":cordz_statistics",
+        ":cordz_update_scope",
+        ":cordz_update_tracker",
+        "//absl/base:config",
+        "//absl/synchronization",
+        "//absl/synchronization:thread_pool",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "cordz_sample_token_test",
+    srcs = [
+        "internal/cordz_sample_token_test.cc",
+    ],
+    deps = [
+        ":cord_internal",
+        ":cordz_handle",
+        ":cordz_info",
+        ":cordz_sample_token",
+        ":cordz_test_helpers",
+        "//absl/base:config",
+        "//absl/memory",
+        "//absl/random",
+        "//absl/synchronization",
+        "//absl/synchronization:thread_pool",
+        "//absl/time",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
     name = "cord_test_helpers",
     testonly = 1,
     hdrs = [
@@ -331,6 +638,41 @@
     copts = ABSL_DEFAULT_COPTS,
     deps = [
         ":cord",
+        ":cord_internal",
+        ":strings",
+        "//absl/base:config",
+    ],
+)
+
+cc_library(
+    name = "cord_rep_test_util",
+    testonly = 1,
+    hdrs = ["internal/cord_rep_test_util.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    deps = [
+        ":cord_internal",
+        ":strings",
+        "//absl/base:config",
+        "//absl/base:raw_logging_internal",
+    ],
+)
+
+cc_library(
+    name = "cordz_test_helpers",
+    testonly = 1,
+    hdrs = ["cordz_test_helpers.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    deps = [
+        ":cord",
+        ":cord_internal",
+        ":cordz_info",
+        ":cordz_sample_token",
+        ":cordz_statistics",
+        ":cordz_update_tracker",
+        ":strings",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -343,6 +685,8 @@
     deps = [
         ":cord",
         ":cord_test_helpers",
+        ":cordz_functions",
+        ":cordz_test_helpers",
         ":str_format",
         ":strings",
         "//absl/base",
@@ -351,6 +695,56 @@
         "//absl/base:endian",
         "//absl/base:raw_logging_internal",
         "//absl/container:fixed_array",
+        "//absl/random",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "cordz_test",
+    size = "medium",
+    srcs = ["cordz_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    tags = [
+        "benchmark",
+        "no_test_android_arm",
+        "no_test_android_arm64",
+        "no_test_android_x86",
+        "no_test_darwin_x86_64",
+        "no_test_ios_x86_64",
+        "no_test_loonix",
+        "no_test_msvc_x64",
+    ],
+    visibility = ["//visibility:private"],
+    deps = [
+        ":cord",
+        ":cord_test_helpers",
+        ":cordz_functions",
+        ":cordz_info",
+        ":cordz_sample_token",
+        ":cordz_statistics",
+        ":cordz_test_helpers",
+        ":cordz_update_tracker",
+        ":strings",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/base:raw_logging_internal",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "cord_rep_consume_test",
+    size = "medium",
+    srcs = ["internal/cord_rep_consume_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":cord_internal",
+        ":strings",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/debugging:leak_check",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -434,6 +828,7 @@
         ":strings",
         "//absl/base:core_headers",
         "//absl/base:dynamic_annotations",
+        "//absl/container:btree",
         "//absl/container:flat_hash_map",
         "//absl/container:node_hash_map",
         "@com_google_googletest//:gtest_main",
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index 3b7ae63..d6801fe 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -101,7 +101,7 @@
   DEPS
     absl::strings
     absl::base
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -115,7 +115,7 @@
     absl::strings
     absl::core_headers
     absl::fixed_array
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -128,7 +128,7 @@
   DEPS
     absl::strings
     absl::core_headers
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -142,7 +142,7 @@
   DEPS
     absl::strings
     absl::core_headers
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -156,7 +156,7 @@
     absl::strings_internal
     absl::base
     absl::core_headers
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -169,7 +169,7 @@
   DEPS
     absl::strings
     absl::type_traits
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -184,7 +184,7 @@
     absl::config
     absl::core_headers
     absl::dynamic_annotations
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -197,7 +197,7 @@
   DEPS
     absl::strings
     absl::core_headers
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -209,7 +209,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::strings
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -221,12 +221,12 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::strings
-    absl::base
     absl::core_headers
     absl::dynamic_annotations
+    absl::btree
     absl::flat_hash_map
     absl::node_hash_map
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -238,7 +238,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::strings_internal
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -253,7 +253,7 @@
     absl::base
     absl::core_headers
     absl::type_traits
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -268,7 +268,7 @@
     absl::base
     absl::core_headers
     absl::memory
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -281,7 +281,7 @@
   DEPS
     absl::strings
     absl::core_headers
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -301,7 +301,7 @@
     absl::random_random
     absl::random_distributions
     absl::strings_internal
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -314,7 +314,7 @@
   DEPS
     absl::strings
     absl::base
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -326,7 +326,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::strings_internal
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -340,7 +340,7 @@
     absl::strings
     absl::str_format
     absl::pow10_helper
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -355,7 +355,7 @@
     absl::strings
     absl::config
     absl::raw_logging_internal
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -370,7 +370,7 @@
   DEPS
     absl::strings
     absl::config
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -428,7 +428,7 @@
     absl::cord
     absl::strings
     absl::core_headers
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -442,7 +442,7 @@
     absl::str_format
     absl::str_format_internal
     absl::strings
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -455,7 +455,7 @@
   DEPS
     absl::str_format
     absl::str_format_internal
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -467,7 +467,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::str_format_internal
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -479,7 +479,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::str_format
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -494,7 +494,7 @@
     absl::str_format_internal
     absl::raw_logging_internal
     absl::int128
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -507,7 +507,7 @@
   DEPS
     absl::str_format_internal
     absl::cord
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -520,7 +520,7 @@
   DEPS
     absl::str_format_internal
     absl::core_headers
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -547,7 +547,281 @@
   DEPS
     absl::pow10_helper
     absl::str_format
-    gmock_main
+    GTest::gmock_main
+)
+
+absl_cc_library(
+  NAME
+    cord_internal
+  HDRS
+    "internal/cord_internal.h"
+    "internal/cord_rep_btree.h"
+    "internal/cord_rep_btree_navigator.h"
+    "internal/cord_rep_btree_reader.h"
+    "internal/cord_rep_consume.h"
+    "internal/cord_rep_flat.h"
+    "internal/cord_rep_ring.h"
+    "internal/cord_rep_ring_reader.h"
+  SRCS
+    "internal/cord_internal.cc"
+    "internal/cord_rep_btree.cc"
+    "internal/cord_rep_btree_navigator.cc"
+    "internal/cord_rep_btree_reader.cc"
+    "internal/cord_rep_consume.cc"
+    "internal/cord_rep_ring.cc"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::base_internal
+    absl::compressed_tuple
+    absl::config
+    absl::core_headers
+    absl::endian
+    absl::inlined_vector
+    absl::layout
+    absl::raw_logging_internal
+    absl::strings
+    absl::throw_delegate
+    absl::type_traits
+)
+
+absl_cc_library(
+  NAME
+    cordz_update_tracker
+  HDRS
+    "internal/cordz_update_tracker.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::config
+)
+
+absl_cc_test(
+  NAME
+    cordz_update_tracker_test
+  SRCS
+    "internal/cordz_update_tracker_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::config
+    absl::cordz_update_tracker
+    absl::core_headers
+    absl::synchronization
+    GTest::gmock_main
+)
+
+absl_cc_library(
+  NAME
+    cordz_functions
+  HDRS
+    "internal/cordz_functions.h"
+  SRCS
+    "internal/cordz_functions.cc"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::config
+    absl::core_headers
+    absl::exponential_biased
+    absl::raw_logging_internal
+)
+
+absl_cc_test(
+  NAME
+    cordz_functions_test
+  SRCS
+    "internal/cordz_functions_test.cc"
+  DEPS
+    absl::config
+    absl::cordz_functions
+    absl::cordz_test_helpers
+    GTest::gmock_main
+)
+
+absl_cc_library(
+  NAME
+    cordz_statistics
+  HDRS
+    "internal/cordz_statistics.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::config
+    absl::core_headers
+    absl::cordz_update_tracker
+    absl::synchronization
+)
+
+absl_cc_library(
+  NAME
+    cordz_handle
+  HDRS
+    "internal/cordz_handle.h"
+  SRCS
+    "internal/cordz_handle.cc"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::base
+    absl::config
+    absl::raw_logging_internal
+    absl::synchronization
+)
+
+absl_cc_test(
+  NAME
+    cordz_handle_test
+  SRCS
+    "internal/cordz_handle_test.cc"
+  DEPS
+    absl::config
+    absl::cordz_handle
+    absl::cordz_test_helpers
+    absl::memory
+    absl::random_random
+    absl::random_distributions
+    absl::synchronization
+    absl::time
+    GTest::gmock_main
+)
+
+absl_cc_library(
+  NAME
+    cordz_info
+  HDRS
+    "internal/cordz_info.h"
+  SRCS
+    "internal/cordz_info.cc"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::base
+    absl::config
+    absl::cord_internal
+    absl::cordz_functions
+    absl::cordz_handle
+    absl::cordz_statistics
+    absl::cordz_update_tracker
+    absl::core_headers
+    absl::inlined_vector
+    absl::span
+    absl::raw_logging_internal
+    absl::stacktrace
+    absl::synchronization
+)
+
+absl_cc_test(
+  NAME
+    cordz_info_test
+  SRCS
+    "internal/cordz_info_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::config
+    absl::cord_internal
+    absl::cordz_test_helpers
+    absl::cordz_handle
+    absl::cordz_info
+    absl::cordz_statistics
+    absl::cordz_test_helpers
+    absl::cordz_update_tracker
+    absl::span
+    absl::stacktrace
+    absl::symbolize
+    GTest::gmock_main
+)
+
+absl_cc_test(
+  NAME
+    cordz_info_statistics_test
+  SRCS
+    "internal/cordz_info_statistics_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::config
+    absl::cord
+    absl::cord_internal
+    absl::cordz_info
+    absl::cordz_sample_token
+    absl::cordz_statistics
+    absl::cordz_update_scope
+    absl::cordz_update_tracker
+    absl::thread_pool
+    GTest::gmock_main
+)
+
+absl_cc_library(
+  NAME
+    cordz_sample_token
+  HDRS
+    "internal/cordz_sample_token.h"
+  SRCS
+    "internal/cordz_sample_token.cc"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::config
+    absl::cordz_handle
+    absl::cordz_info
+)
+
+absl_cc_test(
+  NAME
+    cordz_sample_token_test
+  SRCS
+    "internal/cordz_sample_token_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::config
+    absl::cord_internal
+    absl::cordz_handle
+    absl::cordz_info
+    absl::cordz_info
+    absl::cordz_sample_token
+    absl::cordz_test_helpers
+    absl::memory
+    absl::random_random
+    absl::synchronization
+    absl::thread_pool
+    absl::time
+    GTest::gmock_main
+)
+
+absl_cc_library(
+  NAME
+    cordz_update_scope
+  HDRS
+    "internal/cordz_update_scope.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::config
+    absl::cord_internal
+    absl::cordz_info
+    absl::cordz_update_tracker
+    absl::core_headers
+)
+
+absl_cc_test(
+  NAME
+    cordz_update_scope_test
+  SRCS
+    "internal/cordz_update_scope_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::config
+    absl::cord_internal
+    absl::cordz_info
+    absl::cordz_test_helpers
+    absl::cordz_update_scope
+    absl::cordz_update_tracker
+    absl::core_headers
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -557,19 +831,16 @@
     "cord.h"
   SRCS
     "cord.cc"
-    "internal/cord_internal.cc"
-    "internal/cord_internal.h"
-    "internal/cord_rep_ring.h"
-    "internal/cord_rep_ring.cc"
-    "internal/cord_rep_ring_reader.h"
-    "internal/cord_rep_flat.h"
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
     absl::base
-    absl::base_internal
-    absl::compressed_tuple
     absl::config
+    absl::cord_internal
+    absl::cordz_functions
+    absl::cordz_info
+    absl::cordz_update_scope
+    absl::cordz_update_tracker
     absl::core_headers
     absl::endian
     absl::fixed_array
@@ -578,21 +849,57 @@
     absl::optional
     absl::raw_logging_internal
     absl::strings
-    absl::strings_internal
-    absl::throw_delegate
     absl::type_traits
   PUBLIC
 )
 
 absl_cc_library(
   NAME
+    cord_rep_test_util
+  HDRS
+    "internal/cord_rep_test_util.h"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::config
+    absl::cord_internal
+    absl::raw_logging_internal
+    absl::strings
+  TESTONLY
+)
+
+absl_cc_library(
+  NAME
     cord_test_helpers
   HDRS
     "cord_test_helpers.h"
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
+    absl::config
     absl::cord
+    absl::cord_internal
+    absl::strings
+  TESTONLY
+)
+
+absl_cc_library(
+  NAME
+    cordz_test_helpers
+  HDRS
+    "cordz_test_helpers.h"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::config
+    absl::cord
+    absl::cord_internal
+    absl::cordz_info
+    absl::cordz_sample_token
+    absl::cordz_statistics
+    absl::cordz_update_tracker
+    absl::core_headers
+    absl::strings
   TESTONLY
 )
 
@@ -609,11 +916,99 @@
     absl::strings
     absl::base
     absl::config
+    absl::cord_test_helpers
+    absl::cordz_test_helpers
     absl::core_headers
     absl::endian
+    absl::random_random
     absl::raw_logging_internal
     absl::fixed_array
-    gmock_main
+    GTest::gmock_main
+)
+
+absl_cc_test(
+  NAME
+    cord_rep_consume_test
+  SRCS
+    "internal/cord_rep_consume_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::base
+    absl::config
+    absl::cord_internal
+    absl::core_headers
+    absl::function_ref
+    absl::raw_logging_internal
+    absl::strings
+    GTest::gmock_main
+)
+
+absl_cc_test(
+  NAME
+    cord_internal_test
+  SRCS
+    "internal/cord_internal_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::cord_internal
+    GTest::gmock_main
+)
+
+absl_cc_test(
+  NAME
+    cord_rep_btree_test
+  SRCS
+    "internal/cord_rep_btree_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::base
+    absl::cleanup
+    absl::config
+    absl::cord_internal
+    absl::cord_rep_test_util
+    absl::core_headers
+    absl::raw_logging_internal
+    absl::strings
+    GTest::gmock_main
+)
+
+absl_cc_test(
+  NAME
+    cord_rep_btree_navigator_test
+  SRCS
+    "internal/cord_rep_btree_navigator_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::base
+    absl::config
+    absl::cord_internal
+    absl::cord_rep_test_util
+    absl::core_headers
+    absl::raw_logging_internal
+    absl::strings
+    GTest::gmock_main
+)
+
+absl_cc_test(
+  NAME
+    cord_rep_btree_reader_test
+  SRCS
+    "internal/cord_rep_btree_reader_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::base
+    absl::config
+    absl::cord_internal
+    absl::cord_rep_test_util
+    absl::core_headers
+    absl::raw_logging_internal
+    absl::strings
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -624,13 +1019,13 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
-    absl::config
-    absl::cord
-    absl::strings
     absl::base
+    absl::config
+    absl::cord_internal
     absl::core_headers
     absl::raw_logging_internal
-    gmock_main
+    absl::strings
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -641,9 +1036,33 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
-    absl::cord
-    absl::strings
     absl::base
+    absl::cord_internal
     absl::core_headers
-    gmock_main
+    absl::strings
+    GTest::gmock_main
+)
+
+absl_cc_test(
+  NAME
+    cordz_test
+  SRCS
+    "cordz_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::cord
+    absl::cord_test_helpers
+    absl::cordz_test_helpers
+    absl::cordz_functions
+    absl::cordz_info
+    absl::cordz_sample_token
+    absl::cordz_statistics
+    absl::cordz_update_tracker
+    absl::base
+    absl::config
+    absl::core_headers
+    absl::raw_logging_internal
+    absl::strings
+    GTest::gmock_main
 )
diff --git a/absl/strings/charconv.cc b/absl/strings/charconv.cc
index b8674c2..fefcfc9 100644
--- a/absl/strings/charconv.cc
+++ b/absl/strings/charconv.cc
@@ -111,7 +111,7 @@
     return sign ? -ldexp(mantissa, exponent) : ldexp(mantissa, exponent);
 #else
     constexpr uint64_t kMantissaMask =
-        (uint64_t(1) << (kTargetMantissaBits - 1)) - 1;
+        (uint64_t{1} << (kTargetMantissaBits - 1)) - 1;
     uint64_t dbl = static_cast<uint64_t>(sign) << 63;
     if (mantissa > kMantissaMask) {
       // Normal value.
@@ -151,7 +151,7 @@
     return sign ? -ldexpf(mantissa, exponent) : ldexpf(mantissa, exponent);
 #else
     constexpr uint32_t kMantissaMask =
-        (uint32_t(1) << (kTargetMantissaBits - 1)) - 1;
+        (uint32_t{1} << (kTargetMantissaBits - 1)) - 1;
     uint32_t flt = static_cast<uint32_t>(sign) << 31;
     if (mantissa > kMantissaMask) {
       // Normal value.
@@ -499,7 +499,7 @@
 template <typename FloatType>
 CalculatedFloat CalculatedFloatFromRawValues(uint64_t mantissa, int exponent) {
   CalculatedFloat result;
-  if (mantissa == uint64_t(1) << FloatTraits<FloatType>::kTargetMantissaBits) {
+  if (mantissa == uint64_t{1} << FloatTraits<FloatType>::kTargetMantissaBits) {
     mantissa >>= 1;
     exponent += 1;
   }
diff --git a/absl/strings/charconv.h b/absl/strings/charconv.h
index e04be32..7c50981 100644
--- a/absl/strings/charconv.h
+++ b/absl/strings/charconv.h
@@ -64,8 +64,9 @@
 // the result in `value`.
 //
 // The matching pattern format is almost the same as that of strtod(), except
-// that C locale is not respected, and an initial '+' character in the input
-// range will never be matched.
+// that (1) C locale is not respected, (2) an initial '+' character in the
+// input range will never be matched, and (3) leading whitespaces are not
+// ignored.
 //
 // If `fmt` is set, it must be one of the enumerator values of the chars_format.
 // (This is despite the fact that chars_format is a bitmask type.)  If set to
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc
index 9353375..854047c 100644
--- a/absl/strings/cord.cc
+++ b/absl/strings/cord.cc
@@ -36,8 +36,11 @@
 #include "absl/container/inlined_vector.h"
 #include "absl/strings/escaping.h"
 #include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_btree.h"
 #include "absl/strings/internal/cord_rep_flat.h"
-#include "absl/strings/internal/cord_rep_ring.h"
+#include "absl/strings/internal/cordz_statistics.h"
+#include "absl/strings/internal/cordz_update_scope.h"
+#include "absl/strings/internal/cordz_update_tracker.h"
 #include "absl/strings/internal/resize_uninitialized.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_format.h"
@@ -48,19 +51,15 @@
 ABSL_NAMESPACE_BEGIN
 
 using ::absl::cord_internal::CordRep;
+using ::absl::cord_internal::CordRepBtree;
 using ::absl::cord_internal::CordRepConcat;
 using ::absl::cord_internal::CordRepExternal;
 using ::absl::cord_internal::CordRepFlat;
-using ::absl::cord_internal::CordRepRing;
 using ::absl::cord_internal::CordRepSubstring;
-using ::absl::cord_internal::kMinFlatLength;
+using ::absl::cord_internal::CordzUpdateTracker;
+using ::absl::cord_internal::InlineData;
 using ::absl::cord_internal::kMaxFlatLength;
-
-using ::absl::cord_internal::CONCAT;
-using ::absl::cord_internal::EXTERNAL;
-using ::absl::cord_internal::FLAT;
-using ::absl::cord_internal::RING;
-using ::absl::cord_internal::SUBSTRING;
+using ::absl::cord_internal::kMinFlatLength;
 
 using ::absl::cord_internal::kInlinedVectorSize;
 using ::absl::cord_internal::kMaxBytesToCopy;
@@ -95,13 +94,13 @@
 
 static const int kMinLengthSize = ABSL_ARRAYSIZE(min_length);
 
-static inline bool cord_ring_enabled() {
-  return cord_internal::cord_ring_buffer_enabled.load(
+static inline bool btree_enabled() {
+  return cord_internal::cord_btree_enabled.load(
       std::memory_order_relaxed);
 }
 
 static inline bool IsRootBalanced(CordRep* node) {
-  if (node->tag != CONCAT) {
+  if (!node->IsConcat()) {
     return true;
   } else if (node->concat()->depth() <= 15) {
     return true;
@@ -138,7 +137,7 @@
 
 // Return the depth of a node
 static int Depth(const CordRep* rep) {
-  if (rep->tag == CONCAT) {
+  if (rep->IsConcat()) {
     return rep->concat()->depth();
   } else {
     return 0;
@@ -171,7 +170,7 @@
   }
 
   CordRepConcat* rep = new CordRepConcat();
-  rep->tag = CONCAT;
+  rep->tag = cord_internal::CONCAT;
   SetConcatChildren(rep, left, right);
 
   return rep;
@@ -206,36 +205,32 @@
 }
 
 static CordRepFlat* CreateFlat(const char* data, size_t length,
-                            size_t alloc_hint) {
+                               size_t alloc_hint) {
   CordRepFlat* flat = CordRepFlat::New(length + alloc_hint);
   flat->length = length;
   memcpy(flat->Data(), data, length);
   return flat;
 }
 
-// Creates a new flat or ringbuffer out of the specified array.
+// Creates a new flat or Btree out of the specified array.
 // The returned node has a refcount of 1.
-static CordRep* RingNewTree(const char* data, size_t length,
-                            size_t alloc_hint) {
+static CordRep* NewBtree(const char* data, size_t length, size_t alloc_hint) {
   if (length <= kMaxFlatLength) {
     return CreateFlat(data, length, alloc_hint);
   }
   CordRepFlat* flat = CreateFlat(data, kMaxFlatLength, 0);
   data += kMaxFlatLength;
   length -= kMaxFlatLength;
-  size_t extra = (length - 1) / kMaxFlatLength + 1;
-  auto* root = CordRepRing::Create(flat, extra);
-  return CordRepRing::Append(root, {data, length}, alloc_hint);
+  auto* root = CordRepBtree::Create(flat);
+  return CordRepBtree::Append(root, {data, length}, alloc_hint);
 }
 
 // Create a new tree out of the specified array.
 // The returned node has a refcount of 1.
-static CordRep* NewTree(const char* data,
-                        size_t length,
-                        size_t alloc_hint) {
+static CordRep* NewTree(const char* data, size_t length, size_t alloc_hint) {
   if (length == 0) return nullptr;
-  if (cord_ring_enabled()) {
-    return RingNewTree(data, length, alloc_hint);
+  if (btree_enabled()) {
+    return NewBtree(data, length, alloc_hint);
   }
   absl::FixedArray<CordRep*> reps((length - 1) / kMaxFlatLength + 1);
   size_t n = 0;
@@ -272,13 +267,42 @@
     CordRepSubstring* rep = new CordRepSubstring();
     assert((offset + length) <= child->length);
     rep->length = length;
-    rep->tag = SUBSTRING;
+    rep->tag = cord_internal::SUBSTRING;
     rep->start = offset;
     rep->child = child;
     return VerifyTree(rep);
   }
 }
 
+// Creates a CordRep from the provided string. If the string is large enough,
+// and not wasteful, we move the string into an external cord rep, preserving
+// the already allocated string contents.
+// Requires the provided string length to be larger than `kMaxInline`.
+static CordRep* CordRepFromString(std::string&& src) {
+  assert(src.length() > cord_internal::kMaxInline);
+  if (
+      // String is short: copy data to avoid external block overhead.
+      src.size() <= kMaxBytesToCopy ||
+      // String is wasteful: copy data to avoid pinning too much unused memory.
+      src.size() < src.capacity() / 2
+  ) {
+    return NewTree(src.data(), src.size(), 0);
+  }
+
+  struct StringReleaser {
+    void operator()(absl::string_view /* data */) {}
+    std::string data;
+  };
+  const absl::string_view original_data = src;
+  auto* rep =
+      static_cast<::absl::cord_internal::CordRepExternalImpl<StringReleaser>*>(
+          absl::cord_internal::NewExternalRep(original_data,
+                                              StringReleaser{std::move(src)}));
+  // Moving src may have invalidated its data pointer, so adjust it.
+  rep->base = rep->template get<0>().data.data();
+  return rep;
+}
+
 // --------------------------------------------------------------------
 // Cord::InlineRep functions
 
@@ -299,20 +323,6 @@
   return data_.as_chars();
 }
 
-inline CordRep* Cord::InlineRep::force_tree(size_t extra_hint) {
-  if (data_.is_tree()) {
-    return data_.as_tree();
-  }
-
-  size_t len = inline_size();
-  CordRepFlat* result = CordRepFlat::New(len + extra_hint);
-  result->length = len;
-  static_assert(kMinFlatLength >= sizeof(data_), "");
-  memcpy(result->Data(), data_.as_chars(), sizeof(data_));
-  set_tree(result);
-  return result;
-}
-
 inline void Cord::InlineRep::reduce_size(size_t n) {
   size_t tag = inline_size();
   assert(tag <= kMaxInline);
@@ -328,31 +338,78 @@
   reduce_size(n);
 }
 
-// Returns `rep` converted into a CordRepRing.
-// Directly returns `rep` if `rep` is already a CordRepRing.
-static CordRepRing* ForceRing(CordRep* rep, size_t extra) {
-  return (rep->tag == RING) ? rep->ring() : CordRepRing::Create(rep, extra);
+// Returns `rep` converted into a CordRepBtree.
+// Directly returns `rep` if `rep` is already a CordRepBtree.
+static CordRepBtree* ForceBtree(CordRep* rep) {
+  return rep->IsBtree() ? rep->btree() : CordRepBtree::Create(rep);
 }
 
-void Cord::InlineRep::AppendTree(CordRep* tree) {
-  if (tree == nullptr) return;
-  if (data_.is_empty()) {
-    set_tree(tree);
-  } else if (cord_ring_enabled()) {
-    set_tree(CordRepRing::Append(ForceRing(force_tree(0), 1), tree));
+void Cord::InlineRep::AppendTreeToInlined(CordRep* tree,
+                                          MethodIdentifier method) {
+  assert(!is_tree());
+  if (!data_.is_empty()) {
+    CordRepFlat* flat = MakeFlatWithExtraCapacity(0);
+    if (btree_enabled()) {
+      tree = CordRepBtree::Append(CordRepBtree::Create(flat), tree);
+    } else {
+      tree = Concat(flat, tree);
+    }
+  }
+  EmplaceTree(tree, method);
+}
+
+void Cord::InlineRep::AppendTreeToTree(CordRep* tree, MethodIdentifier method) {
+  assert(is_tree());
+  const CordzUpdateScope scope(data_.cordz_info(), method);
+  if (btree_enabled()) {
+    tree = CordRepBtree::Append(ForceBtree(data_.as_tree()), tree);
   } else {
-    set_tree(Concat(force_tree(0), tree));
+    tree = Concat(data_.as_tree(), tree);
+  }
+  SetTree(tree, scope);
+}
+
+void Cord::InlineRep::AppendTree(CordRep* tree, MethodIdentifier method) {
+  if (tree == nullptr) return;
+  if (data_.is_tree()) {
+    AppendTreeToTree(tree, method);
+  } else {
+    AppendTreeToInlined(tree, method);
   }
 }
 
-void Cord::InlineRep::PrependTree(CordRep* tree) {
-  assert(tree != nullptr);
-  if (data_.is_empty()) {
-    set_tree(tree);
-  } else if (cord_ring_enabled()) {
-    set_tree(CordRepRing::Prepend(ForceRing(force_tree(0), 1), tree));
+void Cord::InlineRep::PrependTreeToInlined(CordRep* tree,
+                                           MethodIdentifier method) {
+  assert(!is_tree());
+  if (!data_.is_empty()) {
+    CordRepFlat* flat = MakeFlatWithExtraCapacity(0);
+    if (btree_enabled()) {
+      tree = CordRepBtree::Prepend(CordRepBtree::Create(flat), tree);
+    } else {
+      tree = Concat(tree, flat);
+    }
+  }
+  EmplaceTree(tree, method);
+}
+
+void Cord::InlineRep::PrependTreeToTree(CordRep* tree,
+                                        MethodIdentifier method) {
+  assert(is_tree());
+  const CordzUpdateScope scope(data_.cordz_info(), method);
+  if (btree_enabled()) {
+    tree = CordRepBtree::Prepend(ForceBtree(data_.as_tree()), tree);
   } else {
-    set_tree(Concat(tree, force_tree(0)));
+    tree = Concat(tree, data_.as_tree());
+  }
+  SetTree(tree, scope);
+}
+
+void Cord::InlineRep::PrependTree(CordRep* tree, MethodIdentifier method) {
+  assert(tree != nullptr);
+  if (data_.is_tree()) {
+    PrependTreeToTree(tree, method);
+  } else {
+    PrependTreeToInlined(tree, method);
   }
 }
 
@@ -362,8 +419,8 @@
 // written to region and the actual size increase will be written to size.
 static inline bool PrepareAppendRegion(CordRep* root, char** region,
                                        size_t* size, size_t max_length) {
-  if (root->tag == RING && root->refcount.IsOne()) {
-    Span<char> span = root->ring()->GetAppendBuffer(max_length);
+  if (root->IsBtree() && root->refcount.IsMutable()) {
+    Span<char> span = root->btree()->GetAppendBuffer(max_length);
     if (!span.empty()) {
       *region = span.data();
       *size = span.size();
@@ -373,11 +430,11 @@
 
   // Search down the right-hand path for a non-full FLAT node.
   CordRep* dst = root;
-  while (dst->tag == CONCAT && dst->refcount.IsOne()) {
+  while (dst->IsConcat() && dst->refcount.IsMutable()) {
     dst = dst->concat()->right;
   }
 
-  if (dst->tag < FLAT || !dst->refcount.IsOne()) {
+  if (!dst->IsFlat() || !dst->refcount.IsMutable()) {
     *region = nullptr;
     *size = 0;
     return false;
@@ -404,148 +461,140 @@
   return true;
 }
 
+template <bool has_length>
 void Cord::InlineRep::GetAppendRegion(char** region, size_t* size,
-                                      size_t max_length) {
-  if (max_length == 0) {
-    *region = nullptr;
-    *size = 0;
-    return;
-  }
+                                      size_t length) {
+  auto constexpr method = CordzUpdateTracker::kGetAppendRegion;
 
-  // Try to fit in the inline buffer if possible.
-  if (!is_tree()) {
-    size_t inline_length = inline_size();
-    if (max_length <= kMaxInline - inline_length) {
-      *region = data_.as_chars() + inline_length;
-      *size = max_length;
-      set_inline_size(inline_length + max_length);
+  CordRep* root = tree();
+  size_t sz = root ? root->length : inline_size();
+  if (root == nullptr) {
+    size_t available = kMaxInline - sz;
+    if (available >= (has_length ? length : 1)) {
+      *region = data_.as_chars() + sz;
+      *size = has_length ? length : available;
+      set_inline_size(has_length ? sz + length : kMaxInline);
       return;
     }
   }
 
-  CordRep* root = force_tree(max_length);
-
-  if (PrepareAppendRegion(root, region, size, max_length)) {
+  size_t extra = has_length ? length : (std::max)(sz, kMinFlatLength);
+  CordRep* rep = root ? root : MakeFlatWithExtraCapacity(extra);
+  CordzUpdateScope scope(root ? data_.cordz_info() : nullptr, method);
+  if (PrepareAppendRegion(rep, region, size, length)) {
+    CommitTree(root, rep, scope, method);
     return;
   }
 
   // Allocate new node.
-  CordRepFlat* new_node =
-      CordRepFlat::New(std::max(static_cast<size_t>(root->length), max_length));
-  new_node->length = std::min(new_node->Capacity(), max_length);
+  CordRepFlat* new_node = CordRepFlat::New(extra);
+  new_node->length = std::min(new_node->Capacity(), length);
   *region = new_node->Data();
   *size = new_node->length;
 
-  if (cord_ring_enabled()) {
-    replace_tree(CordRepRing::Append(ForceRing(root, 1), new_node));
-    return;
+  if (btree_enabled()) {
+    rep = CordRepBtree::Append(ForceBtree(rep), new_node);
+  } else {
+    rep = Concat(rep, new_node);
   }
-  replace_tree(Concat(root, new_node));
+  CommitTree(root, rep, scope, method);
 }
 
-void Cord::InlineRep::GetAppendRegion(char** region, size_t* size) {
-  const size_t max_length = std::numeric_limits<size_t>::max();
-
-  // Try to fit in the inline buffer if possible.
-  if (!data_.is_tree()) {
-    size_t inline_length = inline_size();
-    if (inline_length < kMaxInline) {
-      *region = data_.as_chars() + inline_length;
-      *size = kMaxInline - inline_length;
-      set_inline_size(kMaxInline);
-      return;
-    }
+// Computes the memory side of the provided edge which must be a valid data edge
+// for a btrtee, i.e., a FLAT, EXTERNAL or SUBSTRING of a FLAT or EXTERNAL node.
+static bool RepMemoryUsageDataEdge(const CordRep* rep,
+                                   size_t* total_mem_usage) {
+  size_t maybe_sub_size = 0;
+  if (ABSL_PREDICT_FALSE(rep->IsSubstring())) {
+    maybe_sub_size = sizeof(cord_internal::CordRepSubstring);
+    rep = rep->substring()->child;
   }
-
-  CordRep* root = force_tree(max_length);
-
-  if (PrepareAppendRegion(root, region, size, max_length)) {
-    return;
+  if (rep->IsFlat()) {
+    *total_mem_usage += maybe_sub_size + rep->flat()->AllocatedSize();
+    return true;
   }
-
-  // Allocate new node.
-  CordRepFlat* new_node = CordRepFlat::New(root->length);
-  new_node->length = new_node->Capacity();
-  *region = new_node->Data();
-  *size = new_node->length;
-
-  if (cord_ring_enabled()) {
-    replace_tree(CordRepRing::Append(ForceRing(root, 1), new_node));
-    return;
+  if (rep->IsExternal()) {
+    // We don't know anything about the embedded / bound data, but we can safely
+    // assume it is 'at least' a word / pointer to data. In the future we may
+    // choose to use the 'data' byte as a tag to identify the types of some
+    // well-known externals, such as a std::string instance.
+    *total_mem_usage += maybe_sub_size +
+                        sizeof(cord_internal::CordRepExternalImpl<intptr_t>) +
+                        rep->length;
+    return true;
   }
-  replace_tree(Concat(root, new_node));
+  return false;
 }
 
 // If the rep is a leaf, this will increment the value at total_mem_usage and
 // will return true.
 static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) {
-  if (rep->tag >= FLAT) {
+  if (rep->IsFlat()) {
     *total_mem_usage += rep->flat()->AllocatedSize();
     return true;
   }
-  if (rep->tag == EXTERNAL) {
-    *total_mem_usage += sizeof(CordRepConcat) + rep->length;
+  if (rep->IsExternal()) {
+    // We don't know anything about the embedded / bound data, but we can safely
+    // assume it is 'at least' a word / pointer to data. In the future we may
+    // choose to use the 'data' byte as a tag to identify the types of some
+    // well-known externals, such as a std::string instance.
+    *total_mem_usage +=
+        sizeof(cord_internal::CordRepExternalImpl<intptr_t>) + rep->length;
     return true;
   }
   return false;
 }
 
 void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) {
-  ClearSlow();
-
-  data_ = src.data_;
-  if (is_tree()) {
-    data_.set_profiled(false);
-    CordRep::Ref(tree());
-    clear_cordz_info();
+  assert(&src != this);
+  assert(is_tree() || src.is_tree());
+  auto constexpr method = CordzUpdateTracker::kAssignCord;
+  if (ABSL_PREDICT_TRUE(!is_tree())) {
+    EmplaceTree(CordRep::Ref(src.as_tree()), src.data_, method);
+    return;
   }
+
+  CordRep* tree = as_tree();
+  if (CordRep* src_tree = src.tree()) {
+    // Leave any existing `cordz_info` in place, and let MaybeTrackCord()
+    // decide if this cord should be (or remains to be) sampled or not.
+    data_.set_tree(CordRep::Ref(src_tree));
+    CordzInfo::MaybeTrackCord(data_, src.data_, method);
+  } else {
+    CordzInfo::MaybeUntrackCord(data_.cordz_info());
+    data_ = src.data_;
+  }
+  CordRep::Unref(tree);
 }
 
-void Cord::InlineRep::ClearSlow() {
+void Cord::InlineRep::UnrefTree() {
   if (is_tree()) {
+    CordzInfo::MaybeUntrackCord(data_.cordz_info());
     CordRep::Unref(tree());
   }
-  ResetToEmpty();
 }
 
 // --------------------------------------------------------------------
 // Constructors and destructors
 
-Cord::Cord(absl::string_view src) {
+Cord::Cord(absl::string_view src, MethodIdentifier method)
+    : contents_(InlineData::kDefaultInit) {
   const size_t n = src.size();
   if (n <= InlineRep::kMaxInline) {
-    contents_.set_data(src.data(), n, false);
+    contents_.set_data(src.data(), n, true);
   } else {
-    contents_.set_tree(NewTree(src.data(), n, 0));
+    CordRep* rep = NewTree(src.data(), n, 0);
+    contents_.EmplaceTree(rep, method);
   }
 }
 
 template <typename T, Cord::EnableIfString<T>>
-Cord::Cord(T&& src) {
-  if (
-      // String is short: copy data to avoid external block overhead.
-      src.size() <= kMaxBytesToCopy ||
-      // String is wasteful: copy data to avoid pinning too much unused memory.
-      src.size() < src.capacity() / 2
-  ) {
-    if (src.size() <= InlineRep::kMaxInline) {
-      contents_.set_data(src.data(), src.size(), false);
-    } else {
-      contents_.set_tree(NewTree(src.data(), src.size(), 0));
-    }
+Cord::Cord(T&& src) : contents_(InlineData::kDefaultInit) {
+  if (src.size() <= InlineRep::kMaxInline) {
+    contents_.set_data(src.data(), src.size(), true);
   } else {
-    struct StringReleaser {
-      void operator()(absl::string_view /* data */) {}
-      std::string data;
-    };
-    const absl::string_view original_data = src;
-    auto* rep = static_cast<
-        ::absl::cord_internal::CordRepExternalImpl<StringReleaser>*>(
-        absl::cord_internal::NewExternalRep(
-            original_data, StringReleaser{std::forward<T>(src)}));
-    // Moving src may have invalidated its data pointer, so adjust it.
-    rep->base = rep->template get<0>().data.data();
-    contents_.set_tree(rep);
+    CordRep* rep = CordRepFromString(std::forward<T>(src));
+    contents_.EmplaceTree(rep, CordzUpdateTracker::kConstructorString);
   }
 }
 
@@ -554,9 +603,9 @@
 // The destruction code is separate so that the compiler can determine
 // that it does not need to call the destructor on a moved-from Cord.
 void Cord::DestroyCordSlow() {
-  if (CordRep* tree = contents_.tree()) {
-    CordRep::Unref(VerifyTree(tree));
-  }
+  assert(contents_.is_tree());
+  CordzInfo::MaybeUntrackCord(contents_.cordz_info());
+  CordRep::Unref(VerifyTree(contents_.as_tree()));
 }
 
 // --------------------------------------------------------------------
@@ -568,109 +617,116 @@
   }
 }
 
-Cord& Cord::operator=(absl::string_view src) {
+Cord& Cord::AssignLargeString(std::string&& src) {
+  auto constexpr method = CordzUpdateTracker::kAssignString;
+  assert(src.size() > kMaxBytesToCopy);
+  CordRep* rep = CordRepFromString(std::move(src));
+  if (CordRep* tree = contents_.tree()) {
+    CordzUpdateScope scope(contents_.cordz_info(), method);
+    contents_.SetTree(rep, scope);
+    CordRep::Unref(tree);
+  } else {
+    contents_.EmplaceTree(rep, method);
+  }
+  return *this;
+}
 
+Cord& Cord::operator=(absl::string_view src) {
+  auto constexpr method = CordzUpdateTracker::kAssignString;
   const char* data = src.data();
   size_t length = src.size();
   CordRep* tree = contents_.tree();
   if (length <= InlineRep::kMaxInline) {
-    // Embed into this->contents_
+    // Embed into this->contents_, which is somewhat subtle:
+    // - MaybeUntrackCord must be called before Unref(tree).
+    // - MaybeUntrackCord must be called before set_data() clobbers cordz_info.
+    // - set_data() must be called before Unref(tree) as it may reference tree.
+    if (tree != nullptr) CordzInfo::MaybeUntrackCord(contents_.cordz_info());
     contents_.set_data(data, length, true);
-    if (tree) CordRep::Unref(tree);
+    if (tree != nullptr) CordRep::Unref(tree);
     return *this;
   }
-  if (tree != nullptr && tree->tag >= FLAT &&
-      tree->flat()->Capacity() >= length &&
-      tree->refcount.IsOne()) {
-    // Copy in place if the existing FLAT node is reusable.
-    memmove(tree->flat()->Data(), data, length);
-    tree->length = length;
-    VerifyTree(tree);
-    return *this;
-  }
-  contents_.set_tree(NewTree(data, length, 0));
-  if (tree) CordRep::Unref(tree);
-  return *this;
-}
-
-template <typename T, Cord::EnableIfString<T>>
-Cord& Cord::operator=(T&& src) {
-  if (src.size() <= kMaxBytesToCopy) {
-    *this = absl::string_view(src);
+  if (tree != nullptr) {
+    CordzUpdateScope scope(contents_.cordz_info(), method);
+    if (tree->IsFlat() && tree->flat()->Capacity() >= length &&
+        tree->refcount.IsMutable()) {
+      // Copy in place if the existing FLAT node is reusable.
+      memmove(tree->flat()->Data(), data, length);
+      tree->length = length;
+      VerifyTree(tree);
+      return *this;
+    }
+    contents_.SetTree(NewTree(data, length, 0), scope);
+    CordRep::Unref(tree);
   } else {
-    *this = Cord(std::forward<T>(src));
+    contents_.EmplaceTree(NewTree(data, length, 0), method);
   }
   return *this;
 }
 
-template Cord& Cord::operator=(std::string&& src);
-
 // TODO(sanjay): Move to Cord::InlineRep section of file.  For now,
 // we keep it here to make diffs easier.
-void Cord::InlineRep::AppendArray(const char* src_data, size_t src_size) {
-  if (src_size == 0) return;  // memcpy(_, nullptr, 0) is undefined.
+void Cord::InlineRep::AppendArray(absl::string_view src,
+                                  MethodIdentifier method) {
+  if (src.empty()) return;  // memcpy(_, nullptr, 0) is undefined.
 
   size_t appended = 0;
-  CordRep* root = nullptr;
-  if (is_tree()) {
-    root = data_.as_tree();
+  CordRep* rep = tree();
+  const CordRep* const root = rep;
+  CordzUpdateScope scope(root ? cordz_info() : nullptr, method);
+  if (root != nullptr) {
     char* region;
-    if (PrepareAppendRegion(root, &region, &appended, src_size)) {
-      memcpy(region, src_data, appended);
+    if (PrepareAppendRegion(rep, &region, &appended, src.size())) {
+      memcpy(region, src.data(), appended);
     }
   } else {
     // Try to fit in the inline buffer if possible.
     size_t inline_length = inline_size();
-    if (src_size <= kMaxInline - inline_length) {
+    if (src.size() <= kMaxInline - inline_length) {
       // Append new data to embedded array
-      memcpy(data_.as_chars() + inline_length, src_data, src_size);
-      set_inline_size(inline_length + src_size);
+      memcpy(data_.as_chars() + inline_length, src.data(), src.size());
+      set_inline_size(inline_length + src.size());
       return;
     }
 
-    // It is possible that src_data == data_, but when we transition from an
-    // InlineRep to a tree we need to assign data_ = root via set_tree. To
-    // avoid corrupting the source data before we copy it, delay calling
-    // set_tree until after we've copied data.
-    // We are going from an inline size to beyond inline size. Make the new size
-    // either double the inlined size, or the added size + 10%.
-    const size_t size1 = inline_length * 2 + src_size;
-    const size_t size2 = inline_length + src_size / 10;
-    root = CordRepFlat::New(std::max<size_t>(size1, size2));
-    appended = std::min(
-        src_size, root->flat()->Capacity() - inline_length);
-    memcpy(root->flat()->Data(), data_.as_chars(), inline_length);
-    memcpy(root->flat()->Data() + inline_length, src_data, appended);
-    root->length = inline_length + appended;
-    set_tree(root);
+    // Allocate flat to be a perfect fit on first append exceeding inlined size.
+    // Subsequent growth will use amortized growth until we reach maximum flat
+    // size.
+    rep = CordRepFlat::New(inline_length + src.size());
+    appended = std::min(src.size(), rep->flat()->Capacity() - inline_length);
+    memcpy(rep->flat()->Data(), data_.as_chars(), inline_length);
+    memcpy(rep->flat()->Data() + inline_length, src.data(), appended);
+    rep->length = inline_length + appended;
   }
 
-  src_data += appended;
-  src_size -= appended;
-  if (src_size == 0) {
+  src.remove_prefix(appended);
+  if (src.empty()) {
+    CommitTree(root, rep, scope, method);
     return;
   }
 
-  if (cord_ring_enabled()) {
-    absl::string_view data(src_data, src_size);
-    root = ForceRing(root, (data.size() - 1) / kMaxFlatLength + 1);
-    replace_tree(CordRepRing::Append(root->ring(), data));
-    return;
+  if (btree_enabled()) {
+    // TODO(b/192061034): keep legacy 10% growth rate: consider other rates.
+    rep = ForceBtree(rep);
+    const size_t min_growth = std::max<size_t>(rep->length / 10, src.size());
+    rep = CordRepBtree::Append(rep->btree(), src, min_growth - src.size());
+  } else {
+    // Use new block(s) for any remaining bytes that were not handled above.
+    // Alloc extra memory only if the right child of the root of the new tree
+    // is going to be a FLAT node, which will permit further inplace appends.
+    size_t length = src.size();
+    if (src.size() < kMaxFlatLength) {
+      // The new length is either
+      // - old size + 10%
+      // - old_size + src.size()
+      // This will cause a reasonable conservative step-up in size that is
+      // still large enough to avoid excessive amounts of small fragments
+      // being added.
+      length = std::max<size_t>(rep->length / 10, src.size());
+    }
+    rep = Concat(rep, NewTree(src.data(), src.size(), length - src.size()));
   }
-
-  // Use new block(s) for any remaining bytes that were not handled above.
-  // Alloc extra memory only if the right child of the root of the new tree is
-  // going to be a FLAT node, which will permit further inplace appends.
-  size_t length = src_size;
-  if (src_size < kMaxFlatLength) {
-    // The new length is either
-    // - old size + 10%
-    // - old_size + src_size
-    // This will cause a reasonable conservative step-up in size that is still
-    // large enough to avoid excessive amounts of small fragments being added.
-    length = std::max<size_t>(root->length / 10, src_size);
-  }
-  set_tree(Concat(root, NewTree(src_data, src_size, length - src_size)));
+  CommitTree(root, rep, scope, method);
 }
 
 inline CordRep* Cord::TakeRep() const& {
@@ -685,10 +741,17 @@
 
 template <typename C>
 inline void Cord::AppendImpl(C&& src) {
+  auto constexpr method = CordzUpdateTracker::kAppendCord;
   if (empty()) {
-    // In case of an empty destination avoid allocating a new node, do not copy
-    // data.
-    *this = std::forward<C>(src);
+    // Since destination is empty, we can avoid allocating a node,
+    if (src.contents_.is_tree()) {
+      // by taking the tree directly
+      CordRep* rep = std::forward<C>(src).TakeRep();
+      contents_.EmplaceTree(rep, method);
+    } else {
+      // or copying over inline data
+      contents_.data_ = src.contents_.data_;
+    }
     return;
   }
 
@@ -698,12 +761,12 @@
     CordRep* src_tree = src.contents_.tree();
     if (src_tree == nullptr) {
       // src has embedded data.
-      contents_.AppendArray(src.contents_.data(), src_size);
+      contents_.AppendArray({src.contents_.data(), src_size}, method);
       return;
     }
-    if (src_tree->tag >= FLAT) {
+    if (src_tree->IsFlat()) {
       // src tree just has one flat node.
-      contents_.AppendArray(src_tree->flat()->Data(), src_size);
+      contents_.AppendArray({src_tree->flat()->Data(), src_size}, method);
       return;
     }
     if (&src == this) {
@@ -719,19 +782,25 @@
   }
 
   // Guaranteed to be a tree (kMaxBytesToCopy > kInlinedSize)
-  contents_.AppendTree(std::forward<C>(src).TakeRep());
+  CordRep* rep = std::forward<C>(src).TakeRep();
+  contents_.AppendTree(rep, CordzUpdateTracker::kAppendCord);
 }
 
-void Cord::Append(const Cord& src) { AppendImpl(src); }
+void Cord::Append(const Cord& src) {
+  AppendImpl(src);
+}
 
-void Cord::Append(Cord&& src) { AppendImpl(std::move(src)); }
+void Cord::Append(Cord&& src) {
+  AppendImpl(std::move(src));
+}
 
 template <typename T, Cord::EnableIfString<T>>
 void Cord::Append(T&& src) {
   if (src.size() <= kMaxBytesToCopy) {
     Append(absl::string_view(src));
   } else {
-    Append(Cord(std::forward<T>(src)));
+    CordRep* rep = CordRepFromString(std::forward<T>(src));
+    contents_.AppendTree(rep, CordzUpdateTracker::kAppendString);
   }
 }
 
@@ -741,7 +810,7 @@
   CordRep* src_tree = src.contents_.tree();
   if (src_tree != nullptr) {
     CordRep::Ref(src_tree);
-    contents_.PrependTree(src_tree);
+    contents_.PrependTree(src_tree, CordzUpdateTracker::kPrependCord);
     return;
   }
 
@@ -750,7 +819,7 @@
   return Prepend(src_contents);
 }
 
-void Cord::Prepend(absl::string_view src) {
+void Cord::PrependArray(absl::string_view src, MethodIdentifier method) {
   if (src.empty()) return;  // memcpy(_, nullptr, 0) is undefined.
   if (!contents_.is_tree()) {
     size_t cur_size = contents_.inline_size();
@@ -764,7 +833,8 @@
       return;
     }
   }
-  contents_.PrependTree(NewTree(src.data(), src.size(), 0));
+  CordRep* rep = NewTree(src.data(), src.size(), 0);
+  contents_.PrependTree(rep, method);
 }
 
 template <typename T, Cord::EnableIfString<T>>
@@ -772,7 +842,8 @@
   if (src.size() <= kMaxBytesToCopy) {
     Prepend(absl::string_view(src));
   } else {
-    Prepend(Cord(std::forward<T>(src)));
+    CordRep* rep = CordRepFromString(std::forward<T>(src));
+    contents_.PrependTree(rep, CordzUpdateTracker::kPrependString);
   }
 }
 
@@ -783,7 +854,7 @@
   if (n == 0) return CordRep::Ref(node);
   absl::InlinedVector<CordRep*, kInlinedVectorSize> rhs_stack;
 
-  while (node->tag == CONCAT) {
+  while (node->IsConcat()) {
     assert(n <= node->length);
     if (n < node->concat()->left->length) {
       // Push right to stack, descend left.
@@ -802,7 +873,7 @@
   } else {
     size_t start = n;
     size_t len = node->length - n;
-    if (node->tag == SUBSTRING) {
+    if (node->IsSubstring()) {
       // Consider in-place update of node, similar to in RemoveSuffixFrom().
       start += node->substring()->start;
       node = node->substring()->child;
@@ -823,9 +894,9 @@
   if (n >= node->length) return nullptr;
   if (n == 0) return CordRep::Ref(node);
   absl::InlinedVector<CordRep*, kInlinedVectorSize> lhs_stack;
-  bool inplace_ok = node->refcount.IsOne();
+  bool inplace_ok = node->refcount.IsMutable();
 
-  while (node->tag == CONCAT) {
+  while (node->IsConcat()) {
     assert(n <= node->length);
     if (n < node->concat()->right->length) {
       // Push left to stack, descend right.
@@ -836,13 +907,13 @@
       n -= node->concat()->right->length;
       node = node->concat()->left;
     }
-    inplace_ok = inplace_ok && node->refcount.IsOne();
+    inplace_ok = inplace_ok && node->refcount.IsMutable();
   }
   assert(n <= node->length);
 
   if (n == 0) {
     CordRep::Ref(node);
-  } else if (inplace_ok && node->tag != EXTERNAL) {
+  } else if (inplace_ok && !node->IsExternal()) {
     // Consider making a new buffer if the current node capacity is much
     // larger than the new length.
     CordRep::Ref(node);
@@ -850,7 +921,7 @@
   } else {
     size_t start = 0;
     size_t len = node->length - n;
-    if (node->tag == SUBSTRING) {
+    if (node->IsSubstring()) {
       start = node->substring()->start;
       node = node->substring()->child;
     }
@@ -870,12 +941,19 @@
   CordRep* tree = contents_.tree();
   if (tree == nullptr) {
     contents_.remove_prefix(n);
-  } else if (tree->tag == RING) {
-    contents_.replace_tree(CordRepRing::RemovePrefix(tree->ring(), n));
   } else {
-    CordRep* newrep = RemovePrefixFrom(tree, n);
-    CordRep::Unref(tree);
-    contents_.replace_tree(VerifyTree(newrep));
+    auto constexpr method = CordzUpdateTracker::kRemovePrefix;
+    CordzUpdateScope scope(contents_.cordz_info(), method);
+    if (tree->IsBtree()) {
+      CordRep* old = tree;
+      tree = tree->btree()->SubTree(n, tree->length - n);
+      CordRep::Unref(old);
+    } else {
+      CordRep* newrep = RemovePrefixFrom(tree, n);
+      CordRep::Unref(tree);
+      tree = VerifyTree(newrep);
+    }
+    contents_.SetTreeOrEmpty(tree, scope);
   }
 }
 
@@ -886,12 +964,17 @@
   CordRep* tree = contents_.tree();
   if (tree == nullptr) {
     contents_.reduce_size(n);
-  } else if (tree->tag == RING) {
-    contents_.replace_tree(CordRepRing::RemoveSuffix(tree->ring(), n));
   } else {
-    CordRep* newrep = RemoveSuffixFrom(tree, n);
-    CordRep::Unref(tree);
-    contents_.replace_tree(VerifyTree(newrep));
+    auto constexpr method = CordzUpdateTracker::kRemoveSuffix;
+    CordzUpdateScope scope(contents_.cordz_info(), method);
+    if (tree->IsBtree()) {
+      tree = CordRepBtree::RemoveSuffix(tree->btree(), n);
+    } else {
+      CordRep* newrep = RemoveSuffixFrom(tree, n);
+      CordRep::Unref(tree);
+      tree = VerifyTree(newrep);
+    }
+    contents_.SetTreeOrEmpty(tree, scope);
   }
 }
 
@@ -924,8 +1007,8 @@
       results.push_back(Concat(left, right));
     } else if (pos == 0 && n == node->length) {
       results.push_back(CordRep::Ref(node));
-    } else if (node->tag != CONCAT) {
-      if (node->tag == SUBSTRING) {
+    } else if (!node->IsConcat()) {
+      if (node->IsSubstring()) {
         pos += node->substring()->start;
         node = node->substring()->child;
       }
@@ -951,17 +1034,20 @@
   size_t length = size();
   if (pos > length) pos = length;
   if (new_size > length - pos) new_size = length - pos;
+  if (new_size == 0) return sub_cord;
+
   CordRep* tree = contents_.tree();
   if (tree == nullptr) {
     // sub_cord is newly constructed, no need to re-zero-out the tail of
     // contents_ memory.
     sub_cord.contents_.set_data(contents_.data() + pos, new_size, false);
-  } else if (new_size == 0) {
-    // We want to return empty subcord, so nothing to do.
-  } else if (new_size <= InlineRep::kMaxInline) {
+    return sub_cord;
+  }
+
+  if (new_size <= InlineRep::kMaxInline) {
+    char* dest = sub_cord.contents_.data_.as_chars();
     Cord::ChunkIterator it = chunk_begin();
     it.AdvanceBytes(pos);
-    char* dest = sub_cord.contents_.data_.as_chars();
     size_t remaining_size = new_size;
     while (remaining_size > it->size()) {
       cord_internal::SmallMemmove(dest, it->data(), it->size());
@@ -971,12 +1057,16 @@
     }
     cord_internal::SmallMemmove(dest, it->data(), remaining_size);
     sub_cord.contents_.set_inline_size(new_size);
-  } else if (tree->tag == RING) {
-    tree = CordRepRing::SubRing(CordRep::Ref(tree)->ring(), pos, new_size);
-    sub_cord.contents_.set_tree(tree);
-  } else {
-    sub_cord.contents_.set_tree(NewSubRange(tree, pos, new_size));
+    return sub_cord;
   }
+
+  if (tree->IsBtree()) {
+    tree = tree->btree()->SubTree(pos, new_size);
+  } else {
+    tree = NewSubRange(tree, pos, new_size);
+  }
+  sub_cord.contents_.EmplaceTree(tree, contents_.data_,
+                                 CordzUpdateTracker::kSubCord);
   return sub_cord;
 }
 
@@ -995,7 +1085,7 @@
       CordRep* node = pending.back();
       pending.pop_back();
       CheckNode(node);
-      if (ABSL_PREDICT_FALSE(node->tag != CONCAT)) {
+      if (ABSL_PREDICT_FALSE(!node->IsConcat())) {
         AddNode(node);
         continue;
       }
@@ -1089,7 +1179,7 @@
 
   static void CheckNode(CordRep* node) {
     ABSL_INTERNAL_CHECK(node->length != 0u, "");
-    if (node->tag == CONCAT) {
+    if (node->IsConcat()) {
       ABSL_INTERNAL_CHECK(node->concat()->left != nullptr, "");
       ABSL_INTERNAL_CHECK(node->concat()->right != nullptr, "");
       ABSL_INTERNAL_CHECK(node->length == (node->concat()->left->length +
@@ -1109,7 +1199,7 @@
 
 static CordRep* Rebalance(CordRep* node) {
   VerifyTree(node);
-  assert(node->tag == CONCAT);
+  assert(node->IsConcat());
 
   if (node->length == 0) {
     return nullptr;
@@ -1159,28 +1249,33 @@
 
 }  // namespace
 
-// Helper routine. Locates the first flat chunk of the Cord without
-// initializing the iterator.
+// Helper routine. Locates the first flat or external chunk of the Cord without
+// initializing the iterator, and returns a string_view referencing the data.
 inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const {
   if (!is_tree()) {
     return absl::string_view(data_.as_chars(), data_.inline_size());
   }
 
   CordRep* node = tree();
-  if (node->tag >= FLAT) {
+  if (node->IsFlat()) {
     return absl::string_view(node->flat()->Data(), node->length);
   }
 
-  if (node->tag == EXTERNAL) {
+  if (node->IsExternal()) {
     return absl::string_view(node->external()->base, node->length);
   }
 
-  if (node->tag == RING) {
-    return node->ring()->entry_data(node->ring()->head());
+  if (node->IsBtree()) {
+    CordRepBtree* tree = node->btree();
+    int height = tree->height();
+    while (--height >= 0) {
+      tree = tree->Edge(CordRepBtree::kFront)->btree();
+    }
+    return tree->Data(tree->begin());
   }
 
   // Walk down the left branches until we hit a non-CONCAT node.
-  while (node->tag == CONCAT) {
+  while (node->IsConcat()) {
     node = node->concat()->left;
   }
 
@@ -1189,16 +1284,16 @@
   size_t length = node->length;
   assert(length != 0);
 
-  if (node->tag == SUBSTRING) {
+  if (node->IsSubstring()) {
     offset = node->substring()->start;
     node = node->substring()->child;
   }
 
-  if (node->tag >= FLAT) {
+  if (node->IsFlat()) {
     return absl::string_view(node->flat()->Data() + offset, length);
   }
 
-  assert((node->tag == EXTERNAL) && "Expect FLAT or EXTERNAL node here");
+  assert(node->IsExternal() && "Expect FLAT or EXTERNAL node here");
 
   return absl::string_view(node->external()->base + offset, length);
 }
@@ -1392,7 +1487,7 @@
 
   // Walk down the left branches until we hit a non-CONCAT node. Save the
   // right children to the stack for subsequent traversal.
-  while (node->tag == CONCAT) {
+  while (node->IsConcat()) {
     stack_of_right_children.push_back(node->concat()->right);
     node = node->concat()->left;
   }
@@ -1400,15 +1495,15 @@
   // Get the child node if we encounter a SUBSTRING.
   size_t offset = 0;
   size_t length = node->length;
-  if (node->tag == SUBSTRING) {
+  if (node->IsSubstring()) {
     offset = node->substring()->start;
     node = node->substring()->child;
   }
 
-  assert(node->tag == EXTERNAL || node->tag >= FLAT);
+  assert(node->IsExternal() || node->IsFlat());
   assert(length != 0);
   const char* data =
-      node->tag == EXTERNAL ? node->external()->base : node->flat()->Data();
+      node->IsExternal() ? node->external()->base : node->flat()->Data();
   current_chunk_ = absl::string_view(data + offset, length);
   current_leaf_ = node;
   return *this;
@@ -1418,6 +1513,7 @@
   ABSL_HARDENING_ASSERT(bytes_remaining_ >= n &&
                         "Attempted to iterate past `end()`");
   Cord subcord;
+  auto constexpr method = CordzUpdateTracker::kCordReader;
 
   if (n <= InlineRep::kMaxInline) {
     // Range to read fits in inline data. Flatten it.
@@ -1437,21 +1533,21 @@
     return subcord;
   }
 
-  if (ring_reader_) {
+  if (btree_reader_) {
     size_t chunk_size = current_chunk_.size();
     if (n <= chunk_size && n <= kMaxBytesToCopy) {
-      subcord = Cord(current_chunk_.substr(0, n));
+      subcord = Cord(current_chunk_.substr(0, n), method);
+      if (n < chunk_size) {
+        current_chunk_.remove_prefix(n);
+      } else {
+        current_chunk_ = btree_reader_.Next();
+      }
     } else {
-      auto* ring = CordRep::Ref(ring_reader_.ring())->ring();
-      size_t offset = ring_reader_.length() - bytes_remaining_;
-      subcord.contents_.set_tree(CordRepRing::SubRing(ring, offset, n));
+      CordRep* rep;
+      current_chunk_ = btree_reader_.Read(n, chunk_size, rep);
+      subcord.contents_.EmplaceTree(rep, method);
     }
-    if (n < chunk_size) {
-      bytes_remaining_ -= n;
-      current_chunk_.remove_prefix(n);
-    } else {
-      AdvanceBytesRing(n);
-    }
+    bytes_remaining_ -= n;
     return subcord;
   }
 
@@ -1460,10 +1556,10 @@
     // Range to read is a proper subrange of the current chunk.
     assert(current_leaf_ != nullptr);
     CordRep* subnode = CordRep::Ref(current_leaf_);
-    const char* data = subnode->tag == EXTERNAL ? subnode->external()->base
-                                                : subnode->flat()->Data();
+    const char* data = subnode->IsExternal() ? subnode->external()->base
+                                             : subnode->flat()->Data();
     subnode = NewSubstring(subnode, current_chunk_.data() - data, n);
-    subcord.contents_.set_tree(VerifyTree(subnode));
+    subcord.contents_.EmplaceTree(VerifyTree(subnode), method);
     RemoveChunkPrefix(n);
     return subcord;
   }
@@ -1473,8 +1569,8 @@
   assert(current_leaf_ != nullptr);
   CordRep* subnode = CordRep::Ref(current_leaf_);
   if (current_chunk_.size() < subnode->length) {
-    const char* data = subnode->tag == EXTERNAL ? subnode->external()->base
-                                                : subnode->flat()->Data();
+    const char* data = subnode->IsExternal() ? subnode->external()->base
+                                             : subnode->flat()->Data();
     subnode = NewSubstring(subnode, current_chunk_.data() - data,
                            current_chunk_.size());
   }
@@ -1506,13 +1602,13 @@
   if (node == nullptr) {
     // We have reached the end of the Cord.
     assert(bytes_remaining_ == 0);
-    subcord.contents_.set_tree(VerifyTree(subnode));
+    subcord.contents_.EmplaceTree(VerifyTree(subnode), method);
     return subcord;
   }
 
   // Walk down the appropriate branches until we hit a non-CONCAT node. Save the
   // right children to the stack for subsequent traversal.
-  while (node->tag == CONCAT) {
+  while (node->IsConcat()) {
     if (node->concat()->left->length > n) {
       // Push right, descend left.
       stack_of_right_children.push_back(node->concat()->right);
@@ -1529,24 +1625,24 @@
   // Get the child node if we encounter a SUBSTRING.
   size_t offset = 0;
   size_t length = node->length;
-  if (node->tag == SUBSTRING) {
+  if (node->IsSubstring()) {
     offset = node->substring()->start;
     node = node->substring()->child;
   }
 
   // Range to read ends with a proper (possibly empty) subrange of the current
   // chunk.
-  assert(node->tag == EXTERNAL || node->tag >= FLAT);
+  assert(node->IsExternal() || node->IsFlat());
   assert(length > n);
   if (n > 0) {
     subnode = Concat(subnode, NewSubstring(CordRep::Ref(node), offset, n));
   }
   const char* data =
-      node->tag == EXTERNAL ? node->external()->base : node->flat()->Data();
+      node->IsExternal() ? node->external()->base : node->flat()->Data();
   current_chunk_ = absl::string_view(data + offset + n, length - n);
   current_leaf_ = node;
   bytes_remaining_ -= n;
-  subcord.contents_.set_tree(VerifyTree(subnode));
+  subcord.contents_.EmplaceTree(VerifyTree(subnode), method);
   return subcord;
 }
 
@@ -1585,7 +1681,7 @@
 
   // Walk down the appropriate branches until we hit a non-CONCAT node. Save the
   // right children to the stack for subsequent traversal.
-  while (node->tag == CONCAT) {
+  while (node->IsConcat()) {
     if (node->concat()->left->length > n) {
       // Push right, descend left.
       stack_of_right_children.push_back(node->concat()->right);
@@ -1601,15 +1697,15 @@
   // Get the child node if we encounter a SUBSTRING.
   size_t offset = 0;
   size_t length = node->length;
-  if (node->tag == SUBSTRING) {
+  if (node->IsSubstring()) {
     offset = node->substring()->start;
     node = node->substring()->child;
   }
 
-  assert(node->tag == EXTERNAL || node->tag >= FLAT);
+  assert(node->IsExternal() || node->IsFlat());
   assert(length > n);
   const char* data =
-      node->tag == EXTERNAL ? node->external()->base : node->flat()->Data();
+      node->IsExternal() ? node->external()->base : node->flat()->Data();
   current_chunk_ = absl::string_view(data + offset + n, length - n);
   current_leaf_ = node;
   bytes_remaining_ -= n;
@@ -1625,15 +1721,15 @@
   while (true) {
     assert(rep != nullptr);
     assert(offset < rep->length);
-    if (rep->tag >= FLAT) {
+    if (rep->IsFlat()) {
       // Get the "i"th character directly from the flat array.
       return rep->flat()->Data()[offset];
-    } else if (rep->tag == RING) {
-      return rep->ring()->GetCharacter(offset);
-    } else if (rep->tag == EXTERNAL) {
+    } else if (rep->IsBtree()) {
+      return rep->btree()->GetCharacter(offset);
+    } else if (rep->IsExternal()) {
       // Get the "i"th character from the external array.
       return rep->external()->base[offset];
-    } else if (rep->tag == CONCAT) {
+    } else if (rep->IsConcat()) {
       // Recursively branch to the side of the concatenation that the "i"th
       // character is on.
       size_t left_length = rep->concat()->left->length;
@@ -1645,7 +1741,7 @@
       }
     } else {
       // This must be a substring a node, so bypass it to get to the child.
-      assert(rep->tag == SUBSTRING);
+      assert(rep->IsSubstring());
       offset += rep->substring()->start;
       rep = rep->substring()->child;
     }
@@ -1653,6 +1749,7 @@
 }
 
 absl::string_view Cord::FlattenSlowPath() {
+  assert(contents_.is_tree());
   size_t total_size = size();
   CordRep* new_rep;
   char* new_buffer;
@@ -1673,31 +1770,35 @@
                                             s.size());
         });
   }
-  if (CordRep* tree = contents_.tree()) {
-    CordRep::Unref(tree);
-  }
-  contents_.set_tree(new_rep);
+  CordzUpdateScope scope(contents_.cordz_info(), CordzUpdateTracker::kFlatten);
+  CordRep::Unref(contents_.as_tree());
+  contents_.SetTree(new_rep, scope);
   return absl::string_view(new_buffer, total_size);
 }
 
 /* static */ bool Cord::GetFlatAux(CordRep* rep, absl::string_view* fragment) {
   assert(rep != nullptr);
-  if (rep->tag >= FLAT) {
+  if (rep->IsFlat()) {
     *fragment = absl::string_view(rep->flat()->Data(), rep->length);
     return true;
-  } else if (rep->tag == EXTERNAL) {
+  } else if (rep->IsExternal()) {
     *fragment = absl::string_view(rep->external()->base, rep->length);
     return true;
-  } else if (rep->tag == SUBSTRING) {
+  } else if (rep->IsBtree()) {
+    return rep->btree()->IsFlat(fragment);
+  } else if (rep->IsSubstring()) {
     CordRep* child = rep->substring()->child;
-    if (child->tag >= FLAT) {
+    if (child->IsFlat()) {
       *fragment = absl::string_view(
           child->flat()->Data() + rep->substring()->start, rep->length);
       return true;
-    } else if (child->tag == EXTERNAL) {
+    } else if (child->IsExternal()) {
       *fragment = absl::string_view(
           child->external()->base + rep->substring()->start, rep->length);
       return true;
+    } else if (child->IsBtree()) {
+      return child->btree()->IsFlat(rep->substring()->start, rep->length,
+                                    fragment);
     }
   }
   return false;
@@ -1706,7 +1807,7 @@
 /* static */ void Cord::ForEachChunkAux(
     absl::cord_internal::CordRep* rep,
     absl::FunctionRef<void(absl::string_view)> callback) {
-  if (rep->tag == RING) {
+  if (rep->IsBtree()) {
     ChunkIterator it(rep), end;
     while (it != end) {
       callback(*it);
@@ -1722,7 +1823,7 @@
   absl::cord_internal::CordRep* stack[stack_max];
   absl::cord_internal::CordRep* current_node = rep;
   while (true) {
-    if (current_node->tag == CONCAT) {
+    if (current_node->IsConcat()) {
       if (stack_pos == stack_max) {
         // There's no more room on our stack array to add another right branch,
         // and the idea is to avoid allocations, so call this function
@@ -1769,38 +1870,29 @@
     *os << "]";
     *os << " " << (IsRootBalanced(rep) ? 'b' : 'u');
     *os << " " << std::setw(indent) << "";
-    if (rep->tag == CONCAT) {
+    if (rep->IsConcat()) {
       *os << "CONCAT depth=" << Depth(rep) << "\n";
       indent += kIndentStep;
       indents.push_back(indent);
       stack.push_back(rep->concat()->right);
       rep = rep->concat()->left;
-    } else if (rep->tag == SUBSTRING) {
+    } else if (rep->IsSubstring()) {
       *os << "SUBSTRING @ " << rep->substring()->start << "\n";
       indent += kIndentStep;
       rep = rep->substring()->child;
     } else {  // Leaf or ring
-      if (rep->tag == EXTERNAL) {
+      if (rep->IsExternal()) {
         *os << "EXTERNAL [";
         if (include_data)
           *os << absl::CEscape(std::string(rep->external()->base, rep->length));
         *os << "]\n";
-      } else if (rep->tag >= FLAT) {
-        *os << "FLAT cap=" << rep->flat()->Capacity()
-            << " [";
+      } else if (rep->IsFlat()) {
+        *os << "FLAT cap=" << rep->flat()->Capacity() << " [";
         if (include_data)
           *os << absl::CEscape(std::string(rep->flat()->Data(), rep->length));
         *os << "]\n";
       } else {
-        assert(rep->tag == RING);
-        auto* ring = rep->ring();
-        *os << "RING, entries = " << ring->entries() << "\n";
-        CordRepRing::index_type head = ring->head();
-        do {
-          DumpNode(ring->entry_child(head), include_data, os,
-                   indent + kIndentStep);
-          head = ring->advance(head);;
-        } while (head != ring->tail());
+        CordRepBtree::Dump(rep, /*label=*/ "", include_data, *os);
       }
       if (stack.empty()) break;
       rep = stack.back();
@@ -1832,7 +1924,7 @@
       ABSL_INTERNAL_CHECK(node->length != 0, ReportError(root, node));
     }
 
-    if (node->tag == CONCAT) {
+    if (node->IsConcat()) {
       ABSL_INTERNAL_CHECK(node->concat()->left != nullptr,
                           ReportError(root, node));
       ABSL_INTERNAL_CHECK(node->concat()->right != nullptr,
@@ -1844,14 +1936,13 @@
         worklist.push_back(node->concat()->right);
         worklist.push_back(node->concat()->left);
       }
-    } else if (node->tag >= FLAT) {
-      ABSL_INTERNAL_CHECK(
-          node->length <= node->flat()->Capacity(),
-          ReportError(root, node));
-    } else if (node->tag == EXTERNAL) {
+    } else if (node->IsFlat()) {
+      ABSL_INTERNAL_CHECK(node->length <= node->flat()->Capacity(),
+                          ReportError(root, node));
+    } else if (node->IsExternal()) {
       ABSL_INTERNAL_CHECK(node->external()->base != nullptr,
                           ReportError(root, node));
-    } else if (node->tag == SUBSTRING) {
+    } else if (node->IsSubstring()) {
       ABSL_INTERNAL_CHECK(
           node->substring()->start < node->substring()->child->length,
           ReportError(root, node));
@@ -1880,7 +1971,7 @@
   while (true) {
     const CordRep* next_node = nullptr;
 
-    if (cur_node->tag == CONCAT) {
+    if (cur_node->IsConcat()) {
       total_mem_usage += sizeof(CordRepConcat);
       const CordRep* left = cur_node->concat()->left;
       if (!RepMemoryUsageLeaf(left, &total_mem_usage)) {
@@ -1894,18 +1985,21 @@
         }
         next_node = right;
       }
-    } else if (cur_node->tag == RING) {
-      total_mem_usage += CordRepRing::AllocSize(cur_node->ring()->capacity());
-      const CordRepRing* ring = cur_node->ring();
-      CordRepRing::index_type pos = ring->head(), tail = ring->tail();
-      do {
-        CordRep* node = ring->entry_child(pos);
-        assert(node->tag >= FLAT || node->tag == EXTERNAL);
-        RepMemoryUsageLeaf(node, &total_mem_usage);
-      } while ((pos = ring->advance(pos)) != tail);
+    } else if (cur_node->IsBtree()) {
+      total_mem_usage += sizeof(CordRepBtree);
+      const CordRepBtree* node = cur_node->btree();
+      if (node->height() == 0) {
+        for (const CordRep* edge : node->Edges()) {
+          RepMemoryUsageDataEdge(edge, &total_mem_usage);
+        }
+      } else {
+        for (const CordRep* edge : node->Edges()) {
+          tree_stack.push_back(edge);
+        }
+      }
     } else {
       // Since cur_node is not a leaf or a concat node it must be a substring.
-      assert(cur_node->tag == SUBSTRING);
+      assert(cur_node->IsSubstring());
       total_mem_usage += sizeof(CordRepSubstring);
       next_node = cur_node->substring()->child;
       if (RepMemoryUsageLeaf(next_node, &total_mem_usage)) {
diff --git a/absl/strings/cord.h b/absl/strings/cord.h
index fa9cb91..f0a1991 100644
--- a/absl/strings/cord.h
+++ b/absl/strings/cord.h
@@ -70,6 +70,7 @@
 #include <string>
 #include <type_traits>
 
+#include "absl/base/config.h"
 #include "absl/base/internal/endian.h"
 #include "absl/base/internal/per_thread_tls.h"
 #include "absl/base/macros.h"
@@ -78,8 +79,14 @@
 #include "absl/functional/function_ref.h"
 #include "absl/meta/type_traits.h"
 #include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_btree.h"
+#include "absl/strings/internal/cord_rep_btree_reader.h"
 #include "absl/strings/internal/cord_rep_ring.h"
-#include "absl/strings/internal/cord_rep_ring_reader.h"
+#include "absl/strings/internal/cordz_functions.h"
+#include "absl/strings/internal/cordz_info.h"
+#include "absl/strings/internal/cordz_statistics.h"
+#include "absl/strings/internal/cordz_update_scope.h"
+#include "absl/strings/internal/cordz_update_tracker.h"
 #include "absl/strings/internal/resize_uninitialized.h"
 #include "absl/strings/internal/string_constant.h"
 #include "absl/strings/string_view.h"
@@ -249,9 +256,7 @@
   // swap()
   //
   // Swaps the contents of two Cords.
-  friend void swap(Cord& x, Cord& y) noexcept {
-    x.swap(y);
-  }
+  friend void swap(Cord& x, Cord& y) noexcept { x.swap(y); }
 
   // Cord::size()
   //
@@ -364,8 +369,8 @@
 
    private:
     using CordRep = absl::cord_internal::CordRep;
-    using CordRepRing = absl::cord_internal::CordRepRing;
-    using CordRepRingReader = absl::cord_internal::CordRepRingReader;
+    using CordRepBtree = absl::cord_internal::CordRepBtree;
+    using CordRepBtreeReader = absl::cord_internal::CordRepBtreeReader;
 
     // Stack of right children of concat nodes that we have to visit.
     // Keep this at the end of the structure to avoid cache-thrashing.
@@ -391,9 +396,9 @@
     // Stack specific operator++
     ChunkIterator& AdvanceStack();
 
-    // Ring buffer specific operator++
-    ChunkIterator& AdvanceRing();
-    void AdvanceBytesRing(size_t n);
+    // Btree specific operator++
+    ChunkIterator& AdvanceBtree();
+    void AdvanceBytesBtree(size_t n);
 
     // Iterates `n` bytes, where `n` is expected to be greater than or equal to
     // `current_chunk_.size()`.
@@ -409,8 +414,8 @@
     // The number of bytes left in the `Cord` over which we are iterating.
     size_t bytes_remaining_ = 0;
 
-    // Cord reader for ring buffers. Empty if not traversing a ring buffer.
-    CordRepRingReader ring_reader_;
+    // Cord reader for cord btrees. Empty if not traversing a btree.
+    CordRepBtreeReader btree_reader_;
 
     // See 'Stack' alias definition.
     Stack stack_of_right_children_;
@@ -455,6 +460,16 @@
   // `Cord::chunk_begin()` and `Cord::chunk_end()`.
   class ChunkRange {
    public:
+    // Fulfill minimum c++ container requirements [container.requirements]
+    // Theses (partial) container type definitions allow ChunkRange to be used
+    // in various utilities expecting a subset of [container.requirements].
+    // For example, the below enables using `::testing::ElementsAre(...)`
+    using value_type = absl::string_view;
+    using reference = value_type&;
+    using const_reference = const value_type&;
+    using iterator = ChunkIterator;
+    using const_iterator = ChunkIterator;
+
     explicit ChunkRange(const Cord* cord) : cord_(cord) {}
 
     ChunkIterator begin() const;
@@ -586,6 +601,16 @@
   // `Cord::char_begin()` and `Cord::char_end()`.
   class CharRange {
    public:
+    // Fulfill minimum c++ container requirements [container.requirements]
+    // Theses (partial) container type definitions allow CharRange to be used
+    // in various utilities expecting a subset of [container.requirements].
+    // For example, the below enables using `::testing::ElementsAre(...)`
+    using value_type = char;
+    using reference = value_type&;
+    using const_reference = const value_type&;
+    using iterator = CharIterator;
+    using const_iterator = CharIterator;
+
     explicit CharRange(const Cord* cord) : cord_(cord) {}
 
     CharIterator begin() const;
@@ -664,10 +689,24 @@
   explicit constexpr Cord(strings_internal::StringConstant<T>);
 
  private:
+  using CordRep = absl::cord_internal::CordRep;
+  using CordRepFlat = absl::cord_internal::CordRepFlat;
+  using CordzInfo = cord_internal::CordzInfo;
+  using CordzUpdateScope = cord_internal::CordzUpdateScope;
+  using CordzUpdateTracker = cord_internal::CordzUpdateTracker;
+  using InlineData = cord_internal::InlineData;
+  using MethodIdentifier = CordzUpdateTracker::MethodIdentifier;
+
+  // Creates a cord instance with `method` representing the originating
+  // public API call causing the cord to be created.
+  explicit Cord(absl::string_view src, MethodIdentifier method);
+
   friend class CordTestPeer;
   friend bool operator==(const Cord& lhs, const Cord& rhs);
   friend bool operator==(const Cord& lhs, absl::string_view rhs);
 
+  friend const CordzInfo* GetCordzInfoForTesting(const Cord& cord);
+
   // Calls the provided function once for each cord chunk, in order.  Unlike
   // Chunks(), this API will not allocate memory.
   void ForEachChunk(absl::FunctionRef<void(absl::string_view)>) const;
@@ -687,6 +726,7 @@
     static_assert(kMaxInline >= sizeof(absl::cord_internal::CordRep*), "");
 
     constexpr InlineRep() : data_() {}
+    explicit InlineRep(InlineData::DefaultInitType init) : data_(init) {}
     InlineRep(const InlineRep& src);
     InlineRep(InlineRep&& src);
     InlineRep& operator=(const InlineRep& src);
@@ -700,27 +740,60 @@
     const char* data() const;  // Returns nullptr if holding pointer
     void set_data(const char* data, size_t n,
                   bool nullify_tail);  // Discards pointer, if any
-    char* set_data(size_t n);  // Write data to the result
+    char* set_data(size_t n);          // Write data to the result
     // Returns nullptr if holding bytes
     absl::cord_internal::CordRep* tree() const;
     absl::cord_internal::CordRep* as_tree() const;
-    // Discards old pointer, if any
-    void set_tree(absl::cord_internal::CordRep* rep);
-    // Replaces a tree with a new root. This is faster than set_tree, but it
-    // should only be used when it's clear that the old rep was a tree.
-    void replace_tree(absl::cord_internal::CordRep* rep);
     // Returns non-null iff was holding a pointer
     absl::cord_internal::CordRep* clear();
     // Converts to pointer if necessary.
-    absl::cord_internal::CordRep* force_tree(size_t extra_hint);
-    void reduce_size(size_t n);  // REQUIRES: holding data
+    void reduce_size(size_t n);    // REQUIRES: holding data
     void remove_prefix(size_t n);  // REQUIRES: holding data
-    void AppendArray(const char* src_data, size_t src_size);
+    void AppendArray(absl::string_view src, MethodIdentifier method);
     absl::string_view FindFlatStartPiece() const;
-    void AppendTree(absl::cord_internal::CordRep* tree);
-    void PrependTree(absl::cord_internal::CordRep* tree);
-    void GetAppendRegion(char** region, size_t* size, size_t max_length);
-    void GetAppendRegion(char** region, size_t* size);
+
+    // Creates a CordRepFlat instance from the current inlined data with `extra'
+    // bytes of desired additional capacity.
+    CordRepFlat* MakeFlatWithExtraCapacity(size_t extra);
+
+    // Sets the tree value for this instance. `rep` must not be null.
+    // Requires the current instance to hold a tree, and a lock to be held on
+    // any CordzInfo referenced by this instance. The latter is enforced through
+    // the CordzUpdateScope argument. If the current instance is sampled, then
+    // the CordzInfo instance is updated to reference the new `rep` value.
+    void SetTree(CordRep* rep, const CordzUpdateScope& scope);
+
+    // Identical to SetTree(), except that `rep` is allowed to be null, in
+    // which case the current instance is reset to an empty value.
+    void SetTreeOrEmpty(CordRep* rep, const CordzUpdateScope& scope);
+
+    // Sets the tree value for this instance, and randomly samples this cord.
+    // This function disregards existing contents in `data_`, and should be
+    // called when a Cord is 'promoted' from an 'uninitialized' or 'inlined'
+    // value to a non-inlined (tree / ring) value.
+    void EmplaceTree(CordRep* rep, MethodIdentifier method);
+
+    // Identical to EmplaceTree, except that it copies the parent stack from
+    // the provided `parent` data if the parent is sampled.
+    void EmplaceTree(CordRep* rep, const InlineData& parent,
+                     MethodIdentifier method);
+
+    // Commits the change of a newly created, or updated `rep` root value into
+    // this cord. `old_rep` indicates the old (inlined or tree) value of the
+    // cord, and determines if the commit invokes SetTree() or EmplaceTree().
+    void CommitTree(const CordRep* old_rep, CordRep* rep,
+                    const CordzUpdateScope& scope, MethodIdentifier method);
+
+    void AppendTreeToInlined(CordRep* tree, MethodIdentifier method);
+    void AppendTreeToTree(CordRep* tree, MethodIdentifier method);
+    void AppendTree(CordRep* tree, MethodIdentifier method);
+    void PrependTreeToInlined(CordRep* tree, MethodIdentifier method);
+    void PrependTreeToTree(CordRep* tree, MethodIdentifier method);
+    void PrependTree(CordRep* tree, MethodIdentifier method);
+
+    template <bool has_length>
+    void GetAppendRegion(char** region, size_t* size, size_t length);
+
     bool IsSame(const InlineRep& other) const {
       return memcmp(&data_, &other.data_, sizeof(data_)) == 0;
     }
@@ -776,8 +849,8 @@
     friend class Cord;
 
     void AssignSlow(const InlineRep& src);
-    // Unrefs the tree, stops profiling, and zeroes the contents
-    void ClearSlow();
+    // Unrefs the tree and stops profiling.
+    void UnrefTree();
 
     void ResetToEmpty() { data_ = {}; }
 
@@ -828,6 +901,14 @@
   template <typename C>
   void AppendImpl(C&& src);
 
+  // Prepends the provided data to this instance. `method` contains the public
+  // API method for this action which is tracked for Cordz sampling purposes.
+  void PrependArray(absl::string_view src, MethodIdentifier method);
+
+  // Assigns the value in 'src' to this instance, 'stealing' its contents.
+  // Requires src.length() > kMaxBytesToCopy.
+  Cord& AssignLargeString(std::string&& src);
+
   // Helper for AbslHashValue().
   template <typename H>
   H HashFragmented(H hash_state) const {
@@ -930,8 +1011,11 @@
 template <typename Releaser>
 Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser) {
   Cord cord;
-  cord.contents_.set_tree(::absl::cord_internal::NewExternalRep(
-      data, std::forward<Releaser>(releaser)));
+  if (auto* rep = ::absl::cord_internal::NewExternalRep(
+          data, std::forward<Releaser>(releaser))) {
+    cord.contents_.EmplaceTree(rep,
+                               Cord::MethodIdentifier::kMakeCordFromExternal);
+  }
   return cord;
 }
 
@@ -939,15 +1023,16 @@
     : data_(data) {}
 
 inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src)
-    : data_(src.data_) {
-  if (is_tree()) {
-    data_.clear_cordz_info();
-    absl::cord_internal::CordRep::Ref(as_tree());
+    : data_(InlineData::kDefaultInit) {
+  if (CordRep* tree = src.tree()) {
+    EmplaceTree(CordRep::Ref(tree), src.data_,
+                CordzUpdateTracker::kConstructorCord);
+  } else {
+    data_ = src.data_;
   }
 }
 
-inline Cord::InlineRep::InlineRep(Cord::InlineRep&& src) {
-  data_ = src.data_;
+inline Cord::InlineRep::InlineRep(Cord::InlineRep&& src) : data_(src.data_) {
   src.ResetToEmpty();
 }
 
@@ -966,7 +1051,7 @@
 inline Cord::InlineRep& Cord::InlineRep::operator=(
     Cord::InlineRep&& src) noexcept {
   if (is_tree()) {
-    ClearSlow();
+    UnrefTree();
   }
   data_ = src.data_;
   src.ResetToEmpty();
@@ -1003,31 +1088,62 @@
   return is_tree() ? as_tree()->length : inline_size();
 }
 
-inline void Cord::InlineRep::set_tree(absl::cord_internal::CordRep* rep) {
-  if (rep == nullptr) {
-    ResetToEmpty();
-  } else {
-    if (data_.is_tree()) {
-      // `data_` already holds a 'tree' value and an optional cordz_info value.
-      // Replace the tree value only, leaving the cordz_info value unchanged.
-      data_.set_tree(rep);
-    } else {
-      // `data_` contains inlined data: initialize data_ to tree value `rep`.
-      data_.make_tree(rep);
-    }
-  }
+inline cord_internal::CordRepFlat* Cord::InlineRep::MakeFlatWithExtraCapacity(
+    size_t extra) {
+  static_assert(cord_internal::kMinFlatLength >= sizeof(data_), "");
+  size_t len = data_.inline_size();
+  auto* result = CordRepFlat::New(len + extra);
+  result->length = len;
+  memcpy(result->Data(), data_.as_chars(), sizeof(data_));
+  return result;
 }
 
-inline void Cord::InlineRep::replace_tree(absl::cord_internal::CordRep* rep) {
-  ABSL_ASSERT(is_tree());
-  if (ABSL_PREDICT_FALSE(rep == nullptr)) {
-    set_tree(rep);
-    return;
-  }
+inline void Cord::InlineRep::EmplaceTree(CordRep* rep,
+                                         MethodIdentifier method) {
+  assert(rep);
+  data_.make_tree(rep);
+  CordzInfo::MaybeTrackCord(data_, method);
+}
+
+inline void Cord::InlineRep::EmplaceTree(CordRep* rep, const InlineData& parent,
+                                         MethodIdentifier method) {
+  data_.make_tree(rep);
+  CordzInfo::MaybeTrackCord(data_, parent, method);
+}
+
+inline void Cord::InlineRep::SetTree(CordRep* rep,
+                                     const CordzUpdateScope& scope) {
+  assert(rep);
+  assert(data_.is_tree());
   data_.set_tree(rep);
+  scope.SetCordRep(rep);
+}
+
+inline void Cord::InlineRep::SetTreeOrEmpty(CordRep* rep,
+                                            const CordzUpdateScope& scope) {
+  assert(data_.is_tree());
+  if (rep) {
+    data_.set_tree(rep);
+  } else {
+    data_ = {};
+  }
+  scope.SetCordRep(rep);
+}
+
+inline void Cord::InlineRep::CommitTree(const CordRep* old_rep, CordRep* rep,
+                                        const CordzUpdateScope& scope,
+                                        MethodIdentifier method) {
+  if (old_rep) {
+    SetTree(rep, scope);
+  } else {
+    EmplaceTree(rep, method);
+  }
 }
 
 inline absl::cord_internal::CordRep* Cord::InlineRep::clear() {
+  if (is_tree()) {
+    CordzInfo::MaybeUntrackCord(cordz_info());
+  }
   absl::cord_internal::CordRep* result = tree();
   ResetToEmpty();
   return result;
@@ -1042,6 +1158,9 @@
 
 constexpr inline Cord::Cord() noexcept {}
 
+inline Cord::Cord(absl::string_view src)
+    : Cord(src, CordzUpdateTracker::kConstructorString) {}
+
 template <typename T>
 constexpr Cord::Cord(strings_internal::StringConstant<T>)
     : contents_(strings_internal::StringConstant<T>::value.size() <=
@@ -1057,6 +1176,15 @@
   return *this;
 }
 
+template <typename T, Cord::EnableIfString<T>>
+Cord& Cord::operator=(T&& src) {
+  if (src.size() <= cord_internal::kMaxBytesToCopy) {
+    return operator=(absl::string_view(src));
+  } else {
+    return AssignLargeString(std::forward<T>(src));
+  }
+}
+
 inline Cord::Cord(const Cord& src) : contents_(src.contents_) {}
 
 inline Cord::Cord(Cord&& src) noexcept : contents_(std::move(src.contents_)) {}
@@ -1071,7 +1199,6 @@
 }
 
 extern template Cord::Cord(std::string&& src);
-extern template Cord& Cord::operator=(std::string&& src);
 
 inline size_t Cord::size() const {
   // Length is 1st field in str.rep_
@@ -1114,7 +1241,11 @@
 }
 
 inline void Cord::Append(absl::string_view src) {
-  contents_.AppendArray(src.data(), src.size());
+  contents_.AppendArray(src, CordzUpdateTracker::kAppendString);
+}
+
+inline void Cord::Prepend(absl::string_view src) {
+  PrependArray(src, CordzUpdateTracker::kPrependString);
 }
 
 extern template void Cord::Append(std::string&& src);
@@ -1143,8 +1274,8 @@
 }
 
 inline void Cord::ChunkIterator::InitTree(cord_internal::CordRep* tree) {
-  if (tree->tag == cord_internal::RING) {
-    current_chunk_ = ring_reader_.Reset(tree->ring());
+  if (tree->tag == cord_internal::BTREE) {
+    current_chunk_ = btree_reader_.Init(tree->btree());
     return;
   }
 
@@ -1167,20 +1298,20 @@
   }
 }
 
-inline Cord::ChunkIterator& Cord::ChunkIterator::AdvanceRing() {
-  current_chunk_ = ring_reader_.Next();
+inline Cord::ChunkIterator& Cord::ChunkIterator::AdvanceBtree() {
+  current_chunk_ = btree_reader_.Next();
   return *this;
 }
 
-inline void Cord::ChunkIterator::AdvanceBytesRing(size_t n) {
+inline void Cord::ChunkIterator::AdvanceBytesBtree(size_t n) {
   assert(n >= current_chunk_.size());
   bytes_remaining_ -= n;
   if (bytes_remaining_) {
     if (n == current_chunk_.size()) {
-      current_chunk_ = ring_reader_.Next();
+      current_chunk_ = btree_reader_.Next();
     } else {
-      size_t offset = ring_reader_.length() - bytes_remaining_;
-      current_chunk_ = ring_reader_.Seek(offset);
+      size_t offset = btree_reader_.length() - bytes_remaining_;
+      current_chunk_ = btree_reader_.Seek(offset);
     }
   } else {
     current_chunk_ = {};
@@ -1193,7 +1324,7 @@
   assert(bytes_remaining_ >= current_chunk_.size());
   bytes_remaining_ -= current_chunk_.size();
   if (bytes_remaining_ > 0) {
-    return ring_reader_ ? AdvanceRing() : AdvanceStack();
+    return btree_reader_ ? AdvanceBtree() : AdvanceStack();
   } else {
     current_chunk_ = {};
   }
@@ -1235,7 +1366,7 @@
   if (ABSL_PREDICT_TRUE(n < current_chunk_.size())) {
     RemoveChunkPrefix(n);
   } else if (n != 0) {
-    ring_reader_ ? AdvanceBytesRing(n) : AdvanceBytesSlowPath(n);
+    btree_reader_ ? AdvanceBytesBtree(n) : AdvanceBytesSlowPath(n);
   }
 }
 
@@ -1335,12 +1466,8 @@
 }
 
 inline bool operator!=(const Cord& x, const Cord& y) { return !(x == y); }
-inline bool operator<(const Cord& x, const Cord& y) {
-  return x.Compare(y) < 0;
-}
-inline bool operator>(const Cord& x, const Cord& y) {
-  return x.Compare(y) > 0;
-}
+inline bool operator<(const Cord& x, const Cord& y) { return x.Compare(y) < 0; }
+inline bool operator>(const Cord& x, const Cord& y) { return x.Compare(y) > 0; }
 inline bool operator<=(const Cord& x, const Cord& y) {
   return x.Compare(y) <= 0;
 }
diff --git a/absl/strings/cord_ring_reader_test.cc b/absl/strings/cord_ring_reader_test.cc
index 585616f..d9a9a76 100644
--- a/absl/strings/cord_ring_reader_test.cc
+++ b/absl/strings/cord_ring_reader_test.cc
@@ -78,6 +78,7 @@
   EXPECT_TRUE(static_cast<bool>(reader));
   EXPECT_THAT(reader.ring(), Eq(ring));
   EXPECT_THAT(reader.index(), Eq(ring->head()));
+  EXPECT_THAT(reader.node(), Eq(ring->entry_child(ring->head())));
   EXPECT_THAT(reader.length(), Eq(ring->length));
   EXPECT_THAT(reader.consumed(), Eq(flats[0].length()));
   EXPECT_THAT(reader.remaining(), Eq(ring->length - reader.consumed()));
@@ -99,11 +100,13 @@
   size_t consumed = reader.consumed();
   size_t remaining = reader.remaining();
   for (int i = 1; i < flats.size(); ++i) {
+    CordRepRing::index_type index = ring->advance(head, i);
     consumed += flats[i].length();
     remaining -= flats[i].length();
     absl::string_view next = reader.Next();
     ASSERT_THAT(next, Eq(flats[i]));
-    ASSERT_THAT(reader.index(), Eq(ring->advance(head, i)));
+    ASSERT_THAT(reader.index(), Eq(index));
+    ASSERT_THAT(reader.node(), Eq(ring->entry_child(index)));
     ASSERT_THAT(reader.consumed(), Eq(consumed));
     ASSERT_THAT(reader.remaining(), Eq(remaining));
   }
@@ -125,13 +128,15 @@
   size_t consumed = 0;
   size_t remaining = ring->length;;
   for (int i = 0; i < flats.size(); ++i) {
+    CordRepRing::index_type index = ring->advance(head, i);
     size_t offset = consumed;
     consumed += flats[i].length();
     remaining -= flats[i].length();
     for (int off = 0; off < flats[i].length(); ++off) {
       absl::string_view chunk = reader.Seek(offset + off);
       ASSERT_THAT(chunk, Eq(flats[i].substr(off)));
-      ASSERT_THAT(reader.index(), Eq(ring->advance(head, i)));
+      ASSERT_THAT(reader.index(), Eq(index));
+      ASSERT_THAT(reader.node(), Eq(ring->entry_child(index)));
       ASSERT_THAT(reader.consumed(), Eq(consumed));
       ASSERT_THAT(reader.remaining(), Eq(remaining));
     }
@@ -150,11 +155,13 @@
   size_t consumed = ring->length;
   size_t remaining = 0;
   for (int i = flats.size() - 1; i >= 0; --i) {
+    CordRepRing::index_type index = ring->advance(head, i);
     size_t offset = consumed - flats[i].length();
     for (int off = 0; off < flats[i].length(); ++off) {
       absl::string_view chunk = reader.Seek(offset + off);
       ASSERT_THAT(chunk, Eq(flats[i].substr(off)));
-      ASSERT_THAT(reader.index(), Eq(ring->advance(head, i)));
+      ASSERT_THAT(reader.index(), Eq(index));
+      ASSERT_THAT(reader.node(), Eq(ring->entry_child(index)));
       ASSERT_THAT(reader.consumed(), Eq(consumed));
       ASSERT_THAT(reader.remaining(), Eq(remaining));
     }
diff --git a/absl/strings/cord_ring_test.cc b/absl/strings/cord_ring_test.cc
index 7d75e10..f131859 100644
--- a/absl/strings/cord_ring_test.cc
+++ b/absl/strings/cord_ring_test.cc
@@ -31,9 +31,6 @@
 
 extern thread_local bool cord_ring;
 
-// TOOD(b/177688959): weird things happened with the original test
-#define ASAN_BUG_177688959_FIXED false
-
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace {
@@ -101,15 +98,22 @@
 // Matcher validating when mutable copies are required / performed.
 MATCHER_P2(EqIfPrivate, param, rep,
            absl::StrCat("Equal 0x", absl::Hex(rep), " if private")) {
-  return param.refcount_is_one ? arg == rep : arg != rep;
+  return param.refcount_is_one ? arg == rep : true;
 }
 
 // Matcher validating when mutable copies are required / performed.
 MATCHER_P2(EqIfPrivateAndCapacity, param, rep,
            absl::StrCat("Equal 0x", absl::Hex(rep),
                         " if private and capacity")) {
-  return (param.refcount_is_one && param.with_capacity) ? arg == rep
-                                                        : arg != rep;
+  return (param.refcount_is_one && param.with_capacity) ? arg == rep : true;
+}
+
+// Matcher validating a shared ring was re-allocated. Should only be used for
+// tests doing exactly one update as subsequent updates could return the
+// original (freed and re-used) pointer.
+MATCHER_P2(NeIfShared, param, rep,
+           absl::StrCat("Not equal 0x", absl::Hex(rep), " if shared")) {
+  return param.refcount_is_one ? true : arg != rep;
 }
 
 MATCHER_P2(EqIfInputPrivate, param, rep, "Equal if input is private") {
@@ -219,7 +223,7 @@
     std::string s;
     explicit Rep(size_t len) {
       this->tag = EXTERNAL;
-      this->base = this->storage;
+      this->base = reinterpret_cast<const char*>(this->storage);
       this->length = len;
       this->releaser_invoker = [](CordRepExternal* self) {
         delete static_cast<Rep*>(self);
@@ -271,7 +275,7 @@
 enum Composition { kMix, kAppend, kPrepend };
 
 Composition RandomComposition() {
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
+  RandomEngine rng(GTEST_FLAG_GET(random_seed));
   return (rng() & 1) ? kMix : ((rng() & 1) ? kAppend : kPrepend);
 }
 
@@ -340,19 +344,15 @@
 class CordRingTest : public testing::Test {
  public:
   ~CordRingTest() override {
-#if ASAN_BUG_177688959_FIXED
     for (CordRep* rep : unrefs_) {
       CordRep::Unref(rep);
     }
-#endif
   }
 
   template <typename CordRepType>
   CordRepType* NeedsUnref(CordRepType* rep) {
     assert(rep);
-#if ASAN_BUG_177688959_FIXED
     unrefs_.push_back(rep);
-#endif
     return rep;
   }
 
@@ -362,26 +362,16 @@
     return NeedsUnref(rep);
   }
 
-  void Unref(CordRep* rep) {
-#if !ASAN_BUG_177688959_FIXED
-    CordRep::Unref(rep);
-#endif
-  }
-
  private:
-#if ASAN_BUG_177688959_FIXED
   std::vector<CordRep*> unrefs_;
-#endif
 };
 
 class CordRingTestWithParam : public testing::TestWithParam<TestParam> {
  public:
   ~CordRingTestWithParam() override {
-#if ASAN_BUG_177688959_FIXED
     for (CordRep* rep : unrefs_) {
       CordRep::Unref(rep);
     }
-#endif
   }
 
   CordRepRing* CreateWithCapacity(CordRep* child, size_t extra_capacity) {
@@ -400,9 +390,7 @@
   template <typename CordRepType>
   CordRepType* NeedsUnref(CordRepType* rep) {
     assert(rep);
-#if ASAN_BUG_177688959_FIXED
     unrefs_.push_back(rep);
-#endif
     return rep;
   }
 
@@ -412,43 +400,23 @@
     return NeedsUnref(rep);
   }
 
-  void Unref(CordRep* rep) {
-#if !ASAN_BUG_177688959_FIXED
-    CordRep::Unref(rep);
-#endif
-  }
-
   template <typename CordRepType>
   CordRepType* RefIfShared(CordRepType* rep) {
     return Shared() ? Ref(rep) : rep;
   }
 
-  void UnrefIfShared(CordRep* rep) {
-    if (Shared()) Unref(rep);
-  }
-
   template <typename CordRepType>
   CordRepType* RefIfInputShared(CordRepType* rep) {
     return InputShared() ? Ref(rep) : rep;
   }
 
-  void UnrefIfInputShared(CordRep* rep) {
-    if (InputShared()) Unref(rep);
-  }
-
   template <typename CordRepType>
   CordRepType* RefIfInputSharedIndirect(CordRepType* rep) {
     return InputSharedIndirect() ? Ref(rep) : rep;
   }
 
-  void UnrefIfInputSharedIndirect(CordRep* rep) {
-    if (InputSharedIndirect()) Unref(rep);
-  }
-
  private:
-#if ASAN_BUG_177688959_FIXED
   std::vector<CordRep*> unrefs_;
-#endif
 };
 
 class CordRingCreateTest : public CordRingTestWithParam {
@@ -520,26 +488,26 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(WithParam, CordRingSubTest,
-                        testing::ValuesIn(CordRingSubTest::CreateTestParams()),
-                        TestParamToString);
+INSTANTIATE_TEST_SUITE_P(WithParam, CordRingSubTest,
+                         testing::ValuesIn(CordRingSubTest::CreateTestParams()),
+                         TestParamToString);
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     WithParam, CordRingCreateTest,
     testing::ValuesIn(CordRingCreateTest::CreateTestParams()),
     TestParamToString);
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     WithParam, CordRingCreateFromTreeTest,
     testing::ValuesIn(CordRingCreateFromTreeTest::CreateTestParams()),
     TestParamToString);
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     WithParam, CordRingBuildTest,
     testing::ValuesIn(CordRingBuildTest::CreateTestParams()),
     TestParamToString);
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     WithParam, CordRingBuildInputTest,
     testing::ValuesIn(CordRingBuildInputTest::CreateTestParams()),
     TestParamToString);
@@ -550,7 +518,6 @@
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result->length, Eq(str1.size()));
   EXPECT_THAT(ToFlats(result), ElementsAre(str1));
-  Unref(result);
 }
 
 TEST_P(CordRingCreateTest, CreateFromRing) {
@@ -558,9 +525,8 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Create(ring));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats));
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringRing) {
@@ -570,23 +536,20 @@
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfInputPrivate(GetParam(), ring));
   EXPECT_THAT(ToString(result), string_view(kFox).substr(2, 11));
-  UnrefIfInputSharedIndirect(ring);
-  UnrefIfInputShared(sub);
-  Unref(result);
 }
 
 TEST_F(CordRingTest, CreateWithIllegalExtraCapacity) {
-  CordRep* flat = NeedsUnref(MakeFlat("Hello world"));
 #if defined(ABSL_HAVE_EXCEPTIONS)
+  CordRep* flat = NeedsUnref(MakeFlat("Hello world"));
   try {
     CordRepRing::Create(flat, CordRepRing::kMaxCapacity);
     GTEST_FAIL() << "expected std::length_error exception";
   } catch (const std::length_error&) {
   }
 #elif defined(GTEST_HAS_DEATH_TEST)
+  CordRep* flat = NeedsUnref(MakeFlat("Hello world"));
   EXPECT_DEATH(CordRepRing::Create(flat, CordRepRing::kMaxCapacity), ".*");
 #endif
-  Unref(flat);
 }
 
 TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfFlat) {
@@ -597,9 +560,6 @@
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result->length, Eq(20));
   EXPECT_THAT(ToFlats(result), ElementsAre(str1.substr(4, 20)));
-  Unref(result);
-  UnrefIfInputShared(flat);
-  UnrefIfInputSharedIndirect(child);
 }
 
 TEST_P(CordRingCreateTest, CreateFromExternal) {
@@ -609,8 +569,6 @@
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result->length, Eq(str1.size()));
   EXPECT_THAT(ToFlats(result), ElementsAre(str1));
-  Unref(result);
-  UnrefIfInputShared(child);
 }
 
 TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfExternal) {
@@ -621,9 +579,6 @@
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result->length, Eq(24));
   EXPECT_THAT(ToFlats(result), ElementsAre(str1.substr(1, 24)));
-  Unref(result);
-  UnrefIfInputShared(external);
-  UnrefIfInputSharedIndirect(child);
 }
 
 TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfLargeExternal) {
@@ -637,9 +592,6 @@
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result->length, Eq(str.size()));
   EXPECT_THAT(ToRawFlats(result), ElementsAre(str));
-  Unref(result);
-  UnrefIfInputShared(external);
-  UnrefIfInputSharedIndirect(child);
 }
 
 TEST_P(CordRingBuildInputTest, CreateFromConcat) {
@@ -652,10 +604,6 @@
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result->length, Eq(26));
   EXPECT_THAT(ToString(result), Eq(kAlphabet));
-  UnrefIfInputSharedIndirect(flats[0]);
-  UnrefIfInputSharedIndirect(flats[3]);
-  UnrefIfInputShared(concat);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildInputTest, CreateFromSubstringConcat) {
@@ -671,10 +619,6 @@
       ASSERT_THAT(result, IsValidRingBuffer());
       ASSERT_THAT(result->length, Eq(len));
       ASSERT_THAT(ToString(result), string_view(kAlphabet).substr(off, len));
-      UnrefIfInputSharedIndirect(flats[0]);
-      UnrefIfInputSharedIndirect(flats[3]);
-      UnrefIfInputShared(child);
-      Unref(result);
     }
   }
 }
@@ -689,7 +633,6 @@
   EXPECT_THAT(result->capacity(), Le(2 * 120 + 1));
   EXPECT_THAT(result->entries(), Eq(1));
   EXPECT_THAT(result->begin_pos(), Eq(0));
-  Unref(result);
 }
 
 TEST_P(CordRingCreateTest, EntryForNewFlat) {
@@ -700,7 +643,6 @@
   EXPECT_THAT(result->entry_child(0), Eq(child));
   EXPECT_THAT(result->entry_end_pos(0), Eq(str1.length()));
   EXPECT_THAT(result->entry_data_offset(0), Eq(0));
-  Unref(result);
 }
 
 TEST_P(CordRingCreateTest, EntryForNewFlatSubstring) {
@@ -712,7 +654,6 @@
   EXPECT_THAT(result->entry_child(0), Eq(child));
   EXPECT_THAT(result->entry_end_pos(0), Eq(26));
   EXPECT_THAT(result->entry_data_offset(0), Eq(10));
-  Unref(result);
 }
 
 TEST_P(CordRingBuildTest, AppendFlat) {
@@ -722,10 +663,9 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, MakeFlat(str2)));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
   EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2));
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildTest, PrependFlat) {
@@ -735,10 +675,9 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, MakeFlat(str2)));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
   EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1));
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildTest, AppendString) {
@@ -748,10 +687,9 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
   EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2));
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildTest, AppendStringHavingExtra) {
@@ -762,8 +700,7 @@
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
   EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
-  UnrefIfShared(ring);
-  Unref(result);
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
 }
 
 TEST_P(CordRingBuildTest, AppendStringHavingPartialExtra) {
@@ -785,13 +722,12 @@
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   if (GetParam().refcount_is_one) {
     EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str1, str1a), str2a));
   } else {
     EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2));
   }
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildTest, AppendStringHavingExtraInSubstring) {
@@ -802,14 +738,13 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(result->length, Eq(4 + str2.size()));
   if (GetParam().refcount_is_one) {
     EXPECT_THAT(ToFlats(result), ElementsAre(StrCat("1234", str2)));
   } else {
     EXPECT_THAT(ToFlats(result), ElementsAre("1234", str2));
   }
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildTest, AppendStringHavingSharedExtra) {
@@ -837,10 +772,9 @@
     CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
     ASSERT_THAT(result, IsValidRingBuffer());
     EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+    EXPECT_THAT(result, NeIfShared(GetParam(), ring));
     EXPECT_THAT(result->length, Eq(4 + str2.size()));
     EXPECT_THAT(ToFlats(result), ElementsAre("1234", str2));
-    UnrefIfShared(ring);
-    Unref(result);
 
     CordRep::Unref(shared_type == 1 ? flat1 : flat);
   }
@@ -857,8 +791,6 @@
   EXPECT_THAT(result->length, Eq(str1.size() + str2.size() + str3.size()));
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre(str1, StrCat(str2, str3)));
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildTest, PrependString) {
@@ -875,8 +807,6 @@
   }
   EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
   EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1));
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildTest, PrependStringHavingExtra) {
@@ -887,14 +817,13 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(result->length, Eq(4 + str2.size()));
   if (GetParam().refcount_is_one) {
     EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str2, "1234")));
   } else {
     EXPECT_THAT(ToFlats(result), ElementsAre(str2, "1234"));
   }
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildTest, PrependStringHavingSharedExtra) {
@@ -920,9 +849,8 @@
     ASSERT_THAT(result, IsValidRingBuffer());
     EXPECT_THAT(result->length, Eq(str1a.size() + str2.size()));
     EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+    EXPECT_THAT(result, NeIfShared(GetParam(), ring));
     EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1a));
-    UnrefIfShared(ring);
-    Unref(result);
     CordRep::Unref(shared_type == 1 ? flat1 : flat);
   }
 }
@@ -938,8 +866,6 @@
   EXPECT_THAT(result->length, Eq(str1.size() + str2.size() + str3.size()));
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str3, str2), str1));
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildTest, AppendPrependStringMix) {
@@ -950,12 +876,10 @@
     result = CordRepRing::Prepend(result, flats[4 - i]);
     result = CordRepRing::Append(result, flats[4 + i]);
   }
-  UnrefIfShared(ring);
   NeedsUnref(result);
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
   EXPECT_THAT(ToString(result), kFox);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildTest, AppendPrependStringMixWithExtra) {
@@ -976,8 +900,6 @@
     EXPECT_THAT(ToFlats(result), ElementsAre("The quick brown fox ", "jumps ",
                                              "over the lazy dog"));
   }
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildTest, AppendPrependStringMixWithPrependedExtra) {
@@ -998,8 +920,6 @@
     EXPECT_THAT(ToFlats(result), ElementsAre("The quick brown fox ", "jumps ",
                                              "over the lazy dog"));
   }
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingSubTest, SubRing) {
@@ -1011,16 +931,14 @@
     CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
     CordRepRing* result = CordRepRing::SubRing(ring, offset, 0);
     EXPECT_THAT(result, nullptr);
-    UnrefIfShared(ring);
 
     for (size_t len = 1; len < all.size() - offset; ++len) {
       ring = RefIfShared(FromFlats(flats, composition));
       result = NeedsUnref(CordRepRing::SubRing(ring, offset, len));
       ASSERT_THAT(result, IsValidRingBuffer());
       ASSERT_THAT(result, EqIfPrivate(GetParam(), ring));
+      ASSERT_THAT(result, NeIfShared(GetParam(), ring));
       ASSERT_THAT(ToString(result), Eq(all.substr(offset, len)));
-      UnrefIfShared(ring);
-      Unref(result);
     }
   }
 }
@@ -1039,18 +957,16 @@
     CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
     CordRepRing* result = CordRepRing::SubRing(ring, offset, 0);
     EXPECT_THAT(result, nullptr);
-    UnrefIfShared(ring);
 
     for (size_t len = all.size() - 30; len < all.size() - offset; ++len) {
       ring = RefIfShared(FromFlats(flats, composition));
       result = NeedsUnref(CordRepRing::SubRing(ring, offset, len));
       ASSERT_THAT(result, IsValidRingBuffer());
       ASSERT_THAT(result, EqIfPrivate(GetParam(), ring));
+      ASSERT_THAT(result, NeIfShared(GetParam(), ring));
       auto str = ToString(result);
       ASSERT_THAT(str, SizeIs(len));
       ASSERT_THAT(str, Eq(all.substr(offset, len)));
-      UnrefIfShared(ring);
-      Unref(result);
     }
   }
 }
@@ -1063,16 +979,14 @@
   CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
   CordRepRing* result = CordRepRing::RemovePrefix(ring, all.size());
   EXPECT_THAT(result, nullptr);
-  UnrefIfShared(ring);
 
   for (size_t len = 1; len < all.size(); ++len) {
     ring = RefIfShared(FromFlats(flats, composition));
     result = NeedsUnref(CordRepRing::RemovePrefix(ring, len));
     ASSERT_THAT(result, IsValidRingBuffer());
     EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+    ASSERT_THAT(result, NeIfShared(GetParam(), ring));
     EXPECT_THAT(ToString(result), Eq(all.substr(len)));
-    UnrefIfShared(ring);
-    Unref(result);
   }
 }
 
@@ -1087,7 +1001,6 @@
       ElementsAre(
           not_a_string_view(external1->base, 1 << 20).remove_prefix(1 << 16),
           not_a_string_view(external2->base, 1 << 20)));
-  Unref(result);
 }
 
 TEST_P(CordRingSubTest, RemoveSuffix) {
@@ -1098,16 +1011,14 @@
   CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
   CordRepRing* result = CordRepRing::RemoveSuffix(ring, all.size());
   EXPECT_THAT(result, nullptr);
-  UnrefIfShared(ring);
 
   for (size_t len = 1; len < all.size(); ++len) {
     ring = RefIfShared(FromFlats(flats, composition));
     result = NeedsUnref(CordRepRing::RemoveSuffix(ring, len));
     ASSERT_THAT(result, IsValidRingBuffer());
-    EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
-    EXPECT_THAT(ToString(result), Eq(all.substr(0, all.size() - len)));
-    UnrefIfShared(ring);
-    Unref(result);
+    ASSERT_THAT(result, EqIfPrivate(GetParam(), ring));
+    ASSERT_THAT(result, NeIfShared(GetParam(), ring));
+    ASSERT_THAT(ToString(result), Eq(all.substr(0, all.size() - len)));
   }
 }
 
@@ -1120,9 +1031,8 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, child));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats));
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildInputTest, AppendRingWithFlatOffset) {
@@ -1135,11 +1045,9 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("Head", "brown ", "fox ", "jumps ",
                                            "over ", "the ", "lazy ", "dog"));
-  UnrefIfInputSharedIndirect(child);
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildInputTest, AppendRingWithBrokenOffset) {
@@ -1152,11 +1060,9 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result),
               ElementsAre("Head", "umps ", "over ", "the ", "lazy ", "dog"));
-  UnrefIfInputSharedIndirect(child);
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildInputTest, AppendRingWithFlatLength) {
@@ -1169,11 +1075,9 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ",
                                            "fox ", "jumps ", "over ", "the "));
-  UnrefIfInputSharedIndirect(child);
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildTest, AppendRingWithBrokenFlatLength) {
@@ -1186,11 +1090,9 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ",
                                            "fox ", "jumps ", "ov"));
-  UnrefIfInputSharedIndirect(child);
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildTest, AppendRingMiddlePiece) {
@@ -1203,11 +1105,9 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result),
               ElementsAre("Head", "ck ", "brown ", "fox ", "jum"));
-  UnrefIfInputSharedIndirect(child);
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildTest, AppendRingSinglePiece) {
@@ -1220,11 +1120,8 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("Head", "row"));
-  UnrefIfInputSharedIndirect(child);
-  UnrefIfInputShared(stripped);
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildInputTest, AppendRingSinglePieceWithPrefix) {
@@ -1241,11 +1138,8 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("Prepend", "Head", "row"));
-  UnrefIfInputSharedIndirect(child);
-  UnrefIfInputShared(stripped);
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildInputTest, PrependRing) {
@@ -1258,10 +1152,8 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, child));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats));
-  UnrefIfInputShared(child);
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildInputTest, PrependRingWithFlatOffset) {
@@ -1274,12 +1166,9 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("brown ", "fox ", "jumps ", "over ",
                                            "the ", "lazy ", "dog", "Tail"));
-  UnrefIfInputShared(child);
-  UnrefIfInputSharedIndirect(stripped);
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildInputTest, PrependRingWithBrokenOffset) {
@@ -1291,12 +1180,9 @@
   CordRep* stripped = RefIfInputSharedIndirect(RemovePrefix(21, child));
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result),
               ElementsAre("umps ", "over ", "the ", "lazy ", "dog", "Tail"));
-  UnrefIfInputShared(child);
-  UnrefIfInputSharedIndirect(stripped);
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildInputTest, PrependRingWithFlatLength) {
@@ -1309,12 +1195,9 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ",
                                            "jumps ", "over ", "the ", "Tail"));
-  UnrefIfShared(ring);
-  UnrefIfInputShared(child);
-  UnrefIfInputSharedIndirect(stripped);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildInputTest, PrependRingWithBrokenFlatLength) {
@@ -1327,12 +1210,9 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ",
                                            "jumps ", "ov", "Tail"));
-  UnrefIfInputShared(child);
-  UnrefIfInputSharedIndirect(stripped);
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildInputTest, PrependRingMiddlePiece) {
@@ -1346,12 +1226,9 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result),
               ElementsAre("ck ", "brown ", "fox ", "jum", "Tail"));
-  UnrefIfInputShared(child);
-  UnrefIfInputSharedIndirect(stripped);
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildInputTest, PrependRingSinglePiece) {
@@ -1364,11 +1241,8 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("row", "Tail"));
-  UnrefIfInputShared(child);
-  UnrefIfInputSharedIndirect(stripped);
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_P(CordRingBuildInputTest, PrependRingSinglePieceWithPrefix) {
@@ -1384,11 +1258,8 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("row", "Prepend", "Tail"));
-  UnrefIfInputShared(child);
-  UnrefIfInputSharedIndirect(stripped);
-  UnrefIfShared(ring);
-  Unref(result);
 }
 
 TEST_F(CordRingTest, Find) {
@@ -1406,7 +1277,6 @@
     ASSERT_THAT(found.offset, Lt(data.length()));
     ASSERT_THAT(data[found.offset], Eq(value[i]));
   }
-  Unref(ring);
 }
 
 TEST_F(CordRingTest, FindWithHint) {
@@ -1442,7 +1312,6 @@
     ++flat_pos;
     flat_offset += flat.length();
   }
-  Unref(ring);
 }
 
 TEST_F(CordRingTest, FindInLargeRing) {
@@ -1464,7 +1333,6 @@
     ASSERT_THAT(pos.offset, Lt(data.length()));
     ASSERT_THAT(data[pos.offset], Eq(value[i]));
   }
-  Unref(ring);
 }
 
 TEST_F(CordRingTest, FindTail) {
@@ -1483,7 +1351,6 @@
     ASSERT_THAT(pos.offset, Lt(data.length()));
     ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i]));
   }
-  Unref(ring);
 }
 
 TEST_F(CordRingTest, FindTailWithHint) {
@@ -1510,7 +1377,6 @@
     ASSERT_THAT(pos.offset, Lt(data.length()));
     ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i]));
   }
-  Unref(ring);
 }
 
 TEST_F(CordRingTest, FindTailInLargeRing) {
@@ -1532,7 +1398,6 @@
     ASSERT_THAT(pos.offset, Lt(data.length()));
     ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i]));
   }
-  Unref(ring);
 }
 
 TEST_F(CordRingTest, GetCharacter) {
@@ -1544,7 +1409,6 @@
   for (int i = 0; i < value.length(); ++i) {
     ASSERT_THAT(result->GetCharacter(i), Eq(value[i]));
   }
-  Unref(result);
 }
 
 TEST_F(CordRingTest, GetCharacterWithSubstring) {
@@ -1556,7 +1420,67 @@
   for (int i = 0; i < value.length(); ++i) {
     ASSERT_THAT(result->GetCharacter(i), Eq(value[i]));
   }
-  Unref(result);
+}
+
+TEST_F(CordRingTest, IsFlatSingleFlat) {
+  for (bool external : {false, true}) {
+    SCOPED_TRACE(external ? "With External" : "With Flat");
+    absl::string_view str = "Hello world";
+    CordRep* rep = external ? MakeExternal(str) : MakeFlat(str);
+    CordRepRing* ring = NeedsUnref(CordRepRing::Create(rep));
+
+    // The ring is a single non-fragmented flat:
+    absl::string_view fragment;
+    EXPECT_TRUE(ring->IsFlat(nullptr));
+    EXPECT_TRUE(ring->IsFlat(&fragment));
+    EXPECT_THAT(fragment, Eq("Hello world"));
+    fragment = "";
+    EXPECT_TRUE(ring->IsFlat(0, 11, nullptr));
+    EXPECT_TRUE(ring->IsFlat(0, 11, &fragment));
+    EXPECT_THAT(fragment, Eq("Hello world"));
+
+    // Arbitrary ranges must check true as well.
+    EXPECT_TRUE(ring->IsFlat(1, 4, &fragment));
+    EXPECT_THAT(fragment, Eq("ello"));
+    EXPECT_TRUE(ring->IsFlat(6, 5, &fragment));
+    EXPECT_THAT(fragment, Eq("world"));
+  }
+}
+
+TEST_F(CordRingTest, IsFlatMultiFlat) {
+  for (bool external : {false, true}) {
+    SCOPED_TRACE(external ? "With External" : "With Flat");
+    absl::string_view str1 = "Hello world";
+    absl::string_view str2 = "Halt and catch fire";
+    CordRep* rep1 = external ? MakeExternal(str1) : MakeFlat(str1);
+    CordRep* rep2 = external ? MakeExternal(str2) : MakeFlat(str2);
+    CordRepRing* ring = CordRepRing::Append(CordRepRing::Create(rep1), rep2);
+    NeedsUnref(ring);
+
+    // The ring is fragmented, IsFlat() on the entire cord must be false.
+    EXPECT_FALSE(ring->IsFlat(nullptr));
+    absl::string_view fragment = "Don't touch this";
+    EXPECT_FALSE(ring->IsFlat(&fragment));
+    EXPECT_THAT(fragment, Eq("Don't touch this"));
+
+    // Check for ranges exactly within both flats.
+    EXPECT_TRUE(ring->IsFlat(0, 11, &fragment));
+    EXPECT_THAT(fragment, Eq("Hello world"));
+    EXPECT_TRUE(ring->IsFlat(11, 19, &fragment));
+    EXPECT_THAT(fragment, Eq("Halt and catch fire"));
+
+    // Check for arbitrary partial range inside each flat.
+    EXPECT_TRUE(ring->IsFlat(1, 4, &fragment));
+    EXPECT_THAT(fragment, "ello");
+    EXPECT_TRUE(ring->IsFlat(26, 4, &fragment));
+    EXPECT_THAT(fragment, "fire");
+
+    // Check ranges spanning across both flats
+    fragment = "Don't touch this";
+    EXPECT_FALSE(ring->IsFlat(1, 18, &fragment));
+    EXPECT_FALSE(ring->IsFlat(10, 2, &fragment));
+    EXPECT_THAT(fragment, Eq("Don't touch this"));
+  }
 }
 
 TEST_F(CordRingTest, Dump) {
@@ -1564,7 +1488,6 @@
   auto flats = MakeSpan(kFoxFlats);
   CordRepRing* ring = NeedsUnref(FromFlats(flats, kPrepend));
   ss << *ring;
-  Unref(ring);
 }
 
 }  // namespace
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc
index f998242..cced9bb 100644
--- a/absl/strings/cord_test.cc
+++ b/absl/strings/cord_test.cc
@@ -34,7 +34,9 @@
 #include "absl/base/internal/raw_logging.h"
 #include "absl/base/macros.h"
 #include "absl/container/fixed_array.h"
+#include "absl/random/random.h"
 #include "absl/strings/cord_test_helpers.h"
+#include "absl/strings/cordz_test_helpers.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_format.h"
 #include "absl/strings/string_view.h"
@@ -187,12 +189,50 @@
   static cord_internal::CordzInfo* GetCordzInfo(const Cord& c) {
     return c.contents_.cordz_info();
   }
+
+  static Cord MakeSubstring(Cord src, size_t offset, size_t length) {
+    ABSL_RAW_CHECK(src.contents_.is_tree(), "Can not be inlined");
+    Cord cord;
+    auto* rep = new cord_internal::CordRepSubstring;
+    rep->tag = cord_internal::SUBSTRING;
+    rep->child = cord_internal::CordRep::Ref(src.contents_.tree());
+    rep->start = offset;
+    rep->length = length;
+    cord.contents_.EmplaceTree(rep,
+                               cord_internal::CordzUpdateTracker::kSubCord);
+    return cord;
+  }
 };
 
 ABSL_NAMESPACE_END
 }  // namespace absl
 
-TEST(Cord, AllFlatSizes) {
+// The CordTest fixture runs all tests with and without Cord Btree enabled.
+class CordTest : public testing::TestWithParam<bool> {
+ public:
+  CordTest() : was_btree_(absl::cord_internal::cord_btree_enabled.load()) {
+    absl::cord_internal::cord_btree_enabled.store(UseBtree());
+  }
+  ~CordTest() override {
+    absl::cord_internal::cord_btree_enabled.store(was_btree_);
+  }
+
+  // Returns true if test is running with btree enabled.
+  bool UseBtree() const { return GetParam(); }
+
+  // Returns human readable string representation of the test parameter.
+  static std::string ToString(testing::TestParamInfo<bool> param) {
+    return param.param ? "Btree" : "Concat";
+  }
+
+ private:
+  const bool was_btree_;
+};
+
+INSTANTIATE_TEST_SUITE_P(WithParam, CordTest, testing::Bool(),
+                         CordTest::ToString);
+
+TEST_P(CordTest, AllFlatSizes) {
   using absl::strings_internal::CordTestAccess;
 
   for (size_t s = 0; s < CordTestAccess::MaxFlatLength(); s++) {
@@ -210,7 +250,7 @@
 // We create a Cord at least 128GB in size using the fact that Cords can
 // internally reference-count; thus the Cord is enormous without actually
 // consuming very much memory.
-TEST(GigabyteCord, FromExternal) {
+TEST_P(CordTest, GigabyteCordFromExternal) {
   const size_t one_gig = 1024U * 1024U * 1024U;
   size_t max_size = 2 * one_gig;
   if (sizeof(max_size) > 4) max_size = 128 * one_gig;
@@ -227,7 +267,6 @@
   // caused crashes in production.  We grow exponentially so that the code will
   // execute in a reasonable amount of time.
   absl::Cord c;
-  ABSL_RAW_LOG(INFO, "Made a Cord with %zu bytes!", c.size());
   c.Append(from);
   while (c.size() < max_size) {
     c.Append(c);
@@ -260,7 +299,7 @@
 extern bool my_unique_true_boolean;
 bool my_unique_true_boolean = true;
 
-TEST(Cord, Assignment) {
+TEST_P(CordTest, Assignment) {
   absl::Cord x(absl::string_view("hi there"));
   absl::Cord y(x);
   ASSERT_EQ(std::string(x), "hi there");
@@ -314,7 +353,7 @@
   }
 }
 
-TEST(Cord, StartsEndsWith) {
+TEST_P(CordTest, StartsEndsWith) {
   absl::Cord x(absl::string_view("abcde"));
   absl::Cord empty("");
 
@@ -347,13 +386,13 @@
   ASSERT_TRUE(!empty.EndsWith("xyz"));
 }
 
-TEST(Cord, Subcord) {
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
+TEST_P(CordTest, Subcord) {
+  RandomEngine rng(GTEST_FLAG_GET(random_seed));
   const std::string s = RandomLowercaseString(&rng, 1024);
 
   absl::Cord a;
   AppendWithFragments(s, &rng, &a);
-  ASSERT_EQ(s.size(), a.size());
+  ASSERT_EQ(s, std::string(a));
 
   // Check subcords of a, from a variety of interesting points.
   std::set<size_t> positions;
@@ -408,7 +447,7 @@
   EXPECT_TRUE(sa.empty());
 }
 
-TEST(Cord, Swap) {
+TEST_P(CordTest, Swap) {
   absl::string_view a("Dexter");
   absl::string_view b("Mandark");
   absl::Cord x(a);
@@ -440,7 +479,7 @@
   }
 }
 
-TEST(Cord, CopyToString) {
+TEST_P(CordTest, CopyToString) {
   VerifyCopyToString(absl::Cord());
   VerifyCopyToString(absl::Cord("small cord"));
   VerifyCopyToString(
@@ -448,50 +487,80 @@
                                 "copying ", "to ", "a ", "string."}));
 }
 
-TEST(TryFlat, Empty) {
+TEST_P(CordTest, TryFlatEmpty) {
   absl::Cord c;
   EXPECT_EQ(c.TryFlat(), "");
 }
 
-TEST(TryFlat, Flat) {
+TEST_P(CordTest, TryFlatFlat) {
   absl::Cord c("hello");
   EXPECT_EQ(c.TryFlat(), "hello");
 }
 
-TEST(TryFlat, SubstrInlined) {
+TEST_P(CordTest, TryFlatSubstrInlined) {
   absl::Cord c("hello");
   c.RemovePrefix(1);
   EXPECT_EQ(c.TryFlat(), "ello");
 }
 
-TEST(TryFlat, SubstrFlat) {
+TEST_P(CordTest, TryFlatSubstrFlat) {
   absl::Cord c("longer than 15 bytes");
-  c.RemovePrefix(1);
-  EXPECT_EQ(c.TryFlat(), "onger than 15 bytes");
+  absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1);
+  EXPECT_EQ(sub.TryFlat(), "onger than 15 bytes");
 }
 
-TEST(TryFlat, Concat) {
+TEST_P(CordTest, TryFlatConcat) {
   absl::Cord c = absl::MakeFragmentedCord({"hel", "lo"});
   EXPECT_EQ(c.TryFlat(), absl::nullopt);
 }
 
-TEST(TryFlat, External) {
+TEST_P(CordTest, TryFlatExternal) {
   absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {});
   EXPECT_EQ(c.TryFlat(), "hell");
 }
 
-TEST(TryFlat, SubstrExternal) {
+TEST_P(CordTest, TryFlatSubstrExternal) {
   absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {});
-  c.RemovePrefix(1);
-  EXPECT_EQ(c.TryFlat(), "ell");
+  absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1);
+  EXPECT_EQ(sub.TryFlat(), "ell");
 }
 
-TEST(TryFlat, SubstrConcat) {
+TEST_P(CordTest, TryFlatSubstrConcat) {
   absl::Cord c = absl::MakeFragmentedCord({"hello", " world"});
+  absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1);
+  EXPECT_EQ(sub.TryFlat(), absl::nullopt);
   c.RemovePrefix(1);
   EXPECT_EQ(c.TryFlat(), absl::nullopt);
 }
 
+TEST_P(CordTest, TryFlatCommonlyAssumedInvariants) {
+  // The behavior tested below is not part of the API contract of Cord, but it's
+  // something we intend to be true in our current implementation.  This test
+  // exists to detect and prevent accidental breakage of the implementation.
+  absl::string_view fragments[] = {"A fragmented test",
+                                   " cord",
+                                   " to test subcords",
+                                   " of ",
+                                   "a",
+                                   " cord for",
+                                   " each chunk "
+                                   "returned by the ",
+                                   "iterator"};
+  absl::Cord c = absl::MakeFragmentedCord(fragments);
+  int fragment = 0;
+  int offset = 0;
+  absl::Cord::CharIterator itc = c.char_begin();
+  for (absl::string_view sv : c.Chunks()) {
+    absl::string_view expected = fragments[fragment];
+    absl::Cord subcord1 = c.Subcord(offset, sv.length());
+    absl::Cord subcord2 = absl::Cord::AdvanceAndRead(&itc, sv.size());
+    EXPECT_EQ(subcord1.TryFlat(), expected);
+    EXPECT_EQ(subcord2.TryFlat(), expected);
+    ++fragment;
+    offset += sv.length();
+  }
+}
+
 static bool IsFlat(const absl::Cord& c) {
   return c.chunk_begin() == c.chunk_end() || ++c.chunk_begin() == c.chunk_end();
 }
@@ -520,14 +589,14 @@
   EXPECT_TRUE(IsFlat(c));
 }
 
-TEST(Cord, Flatten) {
+TEST_P(CordTest, Flatten) {
   VerifyFlatten(absl::Cord());
   VerifyFlatten(absl::Cord("small cord"));
   VerifyFlatten(absl::Cord("larger than small buffer optimization"));
   VerifyFlatten(absl::MakeFragmentedCord({"small ", "fragmented ", "cord"}));
 
   // Test with a cord that is longer than the largest flat buffer
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
+  RandomEngine rng(GTEST_FLAG_GET(random_seed));
   VerifyFlatten(absl::Cord(RandomLowercaseString(&rng, 8192)));
 }
 
@@ -574,7 +643,7 @@
 };
 }  // namespace
 
-TEST(Cord, MultipleLengths) {
+TEST_P(CordTest, MultipleLengths) {
   TestData d;
   for (size_t i = 0; i < d.size(); i++) {
     std::string a = d.data(i);
@@ -650,7 +719,7 @@
 
 namespace {
 
-TEST(Cord, RemoveSuffixWithExternalOrSubstring) {
+TEST_P(CordTest, RemoveSuffixWithExternalOrSubstring) {
   absl::Cord cord = absl::MakeCordFromExternal(
       "foo bar baz", [](absl::string_view s) { DoNothing(s, nullptr); });
 
@@ -665,7 +734,7 @@
   EXPECT_EQ("foo", std::string(cord));
 }
 
-TEST(Cord, RemoveSuffixMakesZeroLengthNode) {
+TEST_P(CordTest, RemoveSuffixMakesZeroLengthNode) {
   absl::Cord c;
   c.Append(absl::Cord(std::string(100, 'x')));
   absl::Cord other_ref = c;  // Prevent inplace appends
@@ -692,7 +761,7 @@
 }
 
 // Establish that ZedBlock does what we think it does.
-TEST(CordSpliceTest, ZedBlock) {
+TEST_P(CordTest, CordSpliceTestZedBlock) {
   absl::Cord blob = CordWithZedBlock(10);
   EXPECT_EQ(10, blob.size());
   std::string s;
@@ -700,7 +769,7 @@
   EXPECT_EQ("zzzzzzzzzz", s);
 }
 
-TEST(CordSpliceTest, ZedBlock0) {
+TEST_P(CordTest, CordSpliceTestZedBlock0) {
   absl::Cord blob = CordWithZedBlock(0);
   EXPECT_EQ(0, blob.size());
   std::string s;
@@ -708,7 +777,7 @@
   EXPECT_EQ("", s);
 }
 
-TEST(CordSpliceTest, ZedBlockSuffix1) {
+TEST_P(CordTest, CordSpliceTestZedBlockSuffix1) {
   absl::Cord blob = CordWithZedBlock(10);
   EXPECT_EQ(10, blob.size());
   absl::Cord suffix(blob);
@@ -720,7 +789,7 @@
 }
 
 // Remove all of a prefix block
-TEST(CordSpliceTest, ZedBlockSuffix0) {
+TEST_P(CordTest, CordSpliceTestZedBlockSuffix0) {
   absl::Cord blob = CordWithZedBlock(10);
   EXPECT_EQ(10, blob.size());
   absl::Cord suffix(blob);
@@ -752,7 +821,7 @@
 }
 
 // Taking an empty suffix of a block breaks appending.
-TEST(CordSpliceTest, RemoveEntireBlock1) {
+TEST_P(CordTest, CordSpliceTestRemoveEntireBlock1) {
   absl::Cord zero = CordWithZedBlock(10);
   absl::Cord suffix(zero);
   suffix.RemovePrefix(10);
@@ -760,7 +829,7 @@
   result.Append(suffix);
 }
 
-TEST(CordSpliceTest, RemoveEntireBlock2) {
+TEST_P(CordTest, CordSpliceTestRemoveEntireBlock2) {
   absl::Cord zero = CordWithZedBlock(10);
   absl::Cord prefix(zero);
   prefix.RemoveSuffix(10);
@@ -770,7 +839,7 @@
   result.Append(suffix);
 }
 
-TEST(CordSpliceTest, RemoveEntireBlock3) {
+TEST_P(CordTest, CordSpliceTestRemoveEntireBlock3) {
   absl::Cord blob = CordWithZedBlock(10);
   absl::Cord block = BigCord(10, 'b');
   blob = SpliceCord(blob, 0, block);
@@ -801,7 +870,7 @@
       << "LHS=" << rhs_string << "; RHS=" << lhs_string;
 }
 
-TEST(Cord, Compare) {
+TEST_P(CordTest, Compare) {
   absl::Cord subcord("aaaaaBBBBBcccccDDDDD");
   subcord = subcord.Subcord(3, 10);
 
@@ -864,7 +933,7 @@
   }
 }
 
-TEST(Cord, CompareAfterAssign) {
+TEST_P(CordTest, CompareAfterAssign) {
   absl::Cord a("aaaaaa1111111");
   absl::Cord b("aaaaaa2222222");
   a = "cccccc";
@@ -893,8 +962,8 @@
   EXPECT_EQ(expected, sign(c.Compare(d))) << c << ", " << d;
 }
 
-TEST(Compare, ComparisonIsUnsigned) {
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
+TEST_P(CordTest, CompareComparisonIsUnsigned) {
+  RandomEngine rng(GTEST_FLAG_GET(random_seed));
   std::uniform_int_distribution<uint32_t> uniform_uint8(0, 255);
   char x = static_cast<char>(uniform_uint8(rng));
   TestCompare(
@@ -902,9 +971,9 @@
       absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100), x ^ 0x80)), &rng);
 }
 
-TEST(Compare, RandomComparisons) {
+TEST_P(CordTest, CompareRandomComparisons) {
   const int kIters = 5000;
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
+  RandomEngine rng(GTEST_FLAG_GET(random_seed));
 
   int n = GetUniformRandomUpTo(&rng, 5000);
   absl::Cord a[] = {MakeExternalCord(n),
@@ -960,43 +1029,43 @@
   EXPECT_FALSE(b <= a);
 }
 
-TEST(ComparisonOperators, Cord_Cord) {
+TEST_P(CordTest, ComparisonOperators_Cord_Cord) {
   CompareOperators<absl::Cord, absl::Cord>();
 }
 
-TEST(ComparisonOperators, Cord_StringPiece) {
+TEST_P(CordTest, ComparisonOperators_Cord_StringPiece) {
   CompareOperators<absl::Cord, absl::string_view>();
 }
 
-TEST(ComparisonOperators, StringPiece_Cord) {
+TEST_P(CordTest, ComparisonOperators_StringPiece_Cord) {
   CompareOperators<absl::string_view, absl::Cord>();
 }
 
-TEST(ComparisonOperators, Cord_string) {
+TEST_P(CordTest, ComparisonOperators_Cord_string) {
   CompareOperators<absl::Cord, std::string>();
 }
 
-TEST(ComparisonOperators, string_Cord) {
+TEST_P(CordTest, ComparisonOperators_string_Cord) {
   CompareOperators<std::string, absl::Cord>();
 }
 
-TEST(ComparisonOperators, stdstring_Cord) {
+TEST_P(CordTest, ComparisonOperators_stdstring_Cord) {
   CompareOperators<std::string, absl::Cord>();
 }
 
-TEST(ComparisonOperators, Cord_stdstring) {
+TEST_P(CordTest, ComparisonOperators_Cord_stdstring) {
   CompareOperators<absl::Cord, std::string>();
 }
 
-TEST(ComparisonOperators, charstar_Cord) {
+TEST_P(CordTest, ComparisonOperators_charstar_Cord) {
   CompareOperators<const char*, absl::Cord>();
 }
 
-TEST(ComparisonOperators, Cord_charstar) {
+TEST_P(CordTest, ComparisonOperators_Cord_charstar) {
   CompareOperators<absl::Cord, const char*>();
 }
 
-TEST(ConstructFromExternal, ReleaserInvoked) {
+TEST_P(CordTest, ConstructFromExternalReleaserInvoked) {
   // Empty external memory means the releaser should be called immediately.
   {
     bool invoked = false;
@@ -1038,8 +1107,8 @@
   }
 }
 
-TEST(ConstructFromExternal, CompareContents) {
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
+TEST_P(CordTest, ConstructFromExternalCompareContents) {
+  RandomEngine rng(GTEST_FLAG_GET(random_seed));
 
   for (int length = 1; length <= 2048; length *= 2) {
     std::string data = RandomLowercaseString(&rng, length);
@@ -1054,8 +1123,8 @@
   }
 }
 
-TEST(ConstructFromExternal, LargeReleaser) {
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
+TEST_P(CordTest, ConstructFromExternalLargeReleaser) {
+  RandomEngine rng(GTEST_FLAG_GET(random_seed));
   constexpr size_t kLength = 256;
   std::string data = RandomLowercaseString(&rng, kLength);
   std::array<char, kLength> data_array;
@@ -1069,7 +1138,7 @@
   EXPECT_TRUE(invoked);
 }
 
-TEST(ConstructFromExternal, FunctionPointerReleaser) {
+TEST_P(CordTest, ConstructFromExternalFunctionPointerReleaser) {
   static absl::string_view data("hello world");
   static bool invoked;
   auto* releaser =
@@ -1086,7 +1155,7 @@
   EXPECT_TRUE(invoked);
 }
 
-TEST(ConstructFromExternal, MoveOnlyReleaser) {
+TEST_P(CordTest, ConstructFromExternalMoveOnlyReleaser) {
   struct Releaser {
     explicit Releaser(bool* invoked) : invoked(invoked) {}
     Releaser(Releaser&& other) noexcept : invoked(other.invoked) {}
@@ -1100,20 +1169,20 @@
   EXPECT_TRUE(invoked);
 }
 
-TEST(ConstructFromExternal, NoArgLambda) {
+TEST_P(CordTest, ConstructFromExternalNoArgLambda) {
   bool invoked = false;
   (void)absl::MakeCordFromExternal("dummy", [&invoked]() { invoked = true; });
   EXPECT_TRUE(invoked);
 }
 
-TEST(ConstructFromExternal, StringViewArgLambda) {
+TEST_P(CordTest, ConstructFromExternalStringViewArgLambda) {
   bool invoked = false;
   (void)absl::MakeCordFromExternal(
       "dummy", [&invoked](absl::string_view) { invoked = true; });
   EXPECT_TRUE(invoked);
 }
 
-TEST(ConstructFromExternal, NonTrivialReleaserDestructor) {
+TEST_P(CordTest, ConstructFromExternalNonTrivialReleaserDestructor) {
   struct Releaser {
     explicit Releaser(bool* destroyed) : destroyed(destroyed) {}
     ~Releaser() { *destroyed = true; }
@@ -1128,7 +1197,7 @@
   EXPECT_TRUE(destroyed);
 }
 
-TEST(ConstructFromExternal, ReferenceQualifierOverloads) {
+TEST_P(CordTest, ConstructFromExternalReferenceQualifierOverloads) {
   struct Releaser {
     void operator()(absl::string_view) & { *lvalue_invoked = true; }
     void operator()(absl::string_view) && { *rvalue_invoked = true; }
@@ -1156,7 +1225,7 @@
   EXPECT_TRUE(rvalue_invoked);
 }
 
-TEST(ExternalMemory, BasicUsage) {
+TEST_P(CordTest, ExternalMemoryBasicUsage) {
   static const char* strings[] = {"", "hello", "there"};
   for (const char* str : strings) {
     absl::Cord dst("(prefix)");
@@ -1167,7 +1236,7 @@
   }
 }
 
-TEST(ExternalMemory, RemovePrefixSuffix) {
+TEST_P(CordTest, ExternalMemoryRemovePrefixSuffix) {
   // Exhaustively try all sub-strings.
   absl::Cord cord = MakeComposite();
   std::string s = std::string(cord);
@@ -1182,7 +1251,7 @@
   }
 }
 
-TEST(ExternalMemory, Get) {
+TEST_P(CordTest, ExternalMemoryGet) {
   absl::Cord cord("hello");
   AddExternalMemory(" world!", &cord);
   AddExternalMemory(" how are ", &cord);
@@ -1201,16 +1270,16 @@
 // Additionally we have some whiteboxed expectations based on our knowledge of
 // the layout and size of empty and inlined cords, and flat nodes.
 
-TEST(CordMemoryUsage, Empty) {
+TEST_P(CordTest, CordMemoryUsageEmpty) {
   EXPECT_EQ(sizeof(absl::Cord), absl::Cord().EstimatedMemoryUsage());
 }
 
-TEST(CordMemoryUsage, Embedded) {
+TEST_P(CordTest, CordMemoryUsageEmbedded) {
   absl::Cord a("hello");
   EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord));
 }
 
-TEST(CordMemoryUsage, EmbeddedAppend) {
+TEST_P(CordTest, CordMemoryUsageEmbeddedAppend) {
   absl::Cord a("a");
   absl::Cord b("bcd");
   EXPECT_EQ(b.EstimatedMemoryUsage(), sizeof(absl::Cord));
@@ -1218,7 +1287,7 @@
   EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord));
 }
 
-TEST(CordMemoryUsage, ExternalMemory) {
+TEST_P(CordTest, CordMemoryUsageExternalMemory) {
   static const int kLength = 1000;
   absl::Cord cord;
   AddExternalMemory(std::string(kLength, 'x'), &cord);
@@ -1226,14 +1295,14 @@
   EXPECT_LE(cord.EstimatedMemoryUsage(), kLength * 1.5);
 }
 
-TEST(CordMemoryUsage, Flat) {
+TEST_P(CordTest, CordMemoryUsageFlat) {
   static const int kLength = 125;
   absl::Cord a(std::string(kLength, 'a'));
   EXPECT_GT(a.EstimatedMemoryUsage(), kLength);
   EXPECT_LE(a.EstimatedMemoryUsage(), kLength * 1.5);
 }
 
-TEST(CordMemoryUsage, AppendFlat) {
+TEST_P(CordTest, CordMemoryUsageAppendFlat) {
   using absl::strings_internal::CordTestAccess;
   absl::Cord a(std::string(CordTestAccess::MaxFlatLength(), 'a'));
   size_t length = a.EstimatedMemoryUsage();
@@ -1243,9 +1312,32 @@
   EXPECT_LE(delta, CordTestAccess::MaxFlatLength() * 1.5);
 }
 
+TEST_P(CordTest, CordMemoryUsageAppendExternal) {
+  static const int kLength = 1000;
+  using absl::strings_internal::CordTestAccess;
+  absl::Cord a(std::string(CordTestAccess::MaxFlatLength(), 'a'));
+  size_t length = a.EstimatedMemoryUsage();
+  AddExternalMemory(std::string(kLength, 'b'), &a);
+  size_t delta = a.EstimatedMemoryUsage() - length;
+  EXPECT_GT(delta, kLength);
+  EXPECT_LE(delta, kLength * 1.5);
+}
+
+TEST_P(CordTest, CordMemoryUsageSubString) {
+  static const int kLength = 2000;
+  using absl::strings_internal::CordTestAccess;
+  absl::Cord a(std::string(kLength, 'a'));
+  size_t length = a.EstimatedMemoryUsage();
+  AddExternalMemory(std::string(kLength, 'b'), &a);
+  absl::Cord b = a.Subcord(0, kLength + kLength / 2);
+  size_t delta = b.EstimatedMemoryUsage() - length;
+  EXPECT_GT(delta, kLength);
+  EXPECT_LE(delta, kLength * 1.5);
+}
+
 // Regtest for a change that had to be rolled back because it expanded out
 // of the InlineRep too soon, which was observable through MemoryUsage().
-TEST(CordMemoryUsage, InlineRep) {
+TEST_P(CordTest, CordMemoryUsageInlineRep) {
   constexpr size_t kMaxInline = 15;  // Cord::InlineRep::N
   const std::string small_string(kMaxInline, 'x');
   absl::Cord c1(small_string);
@@ -1259,7 +1351,7 @@
 }  // namespace
 
 // Regtest for 7510292 (fix a bug introduced by 7465150)
-TEST(Cord, Concat_Append) {
+TEST_P(CordTest, Concat_Append) {
   // Create a rep of type CONCAT
   absl::Cord s1("foobarbarbarbarbar");
   s1.Append("abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg");
@@ -1274,7 +1366,80 @@
   EXPECT_EQ(s2.size(), size + 1);
 }
 
-TEST(MakeFragmentedCord, MakeFragmentedCordFromInitializerList) {
+TEST_P(CordTest, DiabolicalGrowth) {
+  // This test exercises a diabolical Append(<one char>) on a cord, making the
+  // cord shared before each Append call resulting in a terribly fragmented
+  // resulting cord.
+  // TODO(b/183983616): Apply some minimum compaction when copying a shared
+  // source cord into a mutable copy for updates in CordRepRing.
+  RandomEngine rng(GTEST_FLAG_GET(random_seed));
+  const std::string expected = RandomLowercaseString(&rng, 5000);
+  absl::Cord cord;
+  for (char c : expected) {
+    absl::Cord shared(cord);
+    cord.Append(absl::string_view(&c, 1));
+  }
+  std::string value;
+  absl::CopyCordToString(cord, &value);
+  EXPECT_EQ(value, expected);
+  ABSL_RAW_LOG(INFO, "Diabolical size allocated = %zu",
+               cord.EstimatedMemoryUsage());
+}
+
+// The following tests check support for >4GB cords in 64-bit binaries, and
+// 2GB-4GB cords in 32-bit binaries.  This function returns the large cord size
+// that's appropriate for the binary.
+
+// Construct a huge cord with the specified valid prefix.
+static absl::Cord MakeHuge(absl::string_view prefix) {
+  absl::Cord cord;
+  if (sizeof(size_t) > 4) {
+    // In 64-bit binaries, test 64-bit Cord support.
+    const size_t size =
+        static_cast<size_t>(std::numeric_limits<uint32_t>::max()) + 314;
+    cord.Append(absl::MakeCordFromExternal(
+        absl::string_view(prefix.data(), size),
+        [](absl::string_view s) { DoNothing(s, nullptr); }));
+  } else {
+    // Cords are limited to 32-bit lengths in 32-bit binaries.  The following
+    // tests check for use of "signed int" to represent Cord length/offset.
+    // However absl::string_view does not allow lengths >= (1u<<31), so we need
+    // to append in two parts;
+    const size_t s1 = (1u << 31) - 1;
+    // For shorter cord, `Append` copies the data rather than allocating a new
+    // node. The threshold is currently set to 511, so `s2` needs to be bigger
+    // to not trigger the copy.
+    const size_t s2 = 600;
+    cord.Append(absl::MakeCordFromExternal(
+        absl::string_view(prefix.data(), s1),
+        [](absl::string_view s) { DoNothing(s, nullptr); }));
+    cord.Append(absl::MakeCordFromExternal(
+        absl::string_view("", s2),
+        [](absl::string_view s) { DoNothing(s, nullptr); }));
+  }
+  return cord;
+}
+
+TEST_P(CordTest, HugeCord) {
+  absl::Cord cord = MakeHuge("huge cord");
+  EXPECT_LE(cord.size(), cord.EstimatedMemoryUsage());
+  EXPECT_GE(cord.size() + 100, cord.EstimatedMemoryUsage());
+}
+
+// Tests that Append() works ok when handed a self reference
+TEST_P(CordTest, AppendSelf) {
+  // We run the test until data is ~16K
+  // This guarantees it covers small, medium and large data.
+  std::string control_data = "Abc";
+  absl::Cord data(control_data);
+  while (control_data.length() < 0x4000) {
+    data.Append(data);
+    control_data.append(control_data);
+    ASSERT_EQ(control_data, data);
+  }
+}
+
+TEST_P(CordTest, MakeFragmentedCordFromInitializerList) {
   absl::Cord fragmented =
       absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"});
 
@@ -1294,7 +1459,7 @@
   ASSERT_TRUE(++chunk_it == fragmented.chunk_end());
 }
 
-TEST(MakeFragmentedCord, MakeFragmentedCordFromVector) {
+TEST_P(CordTest, MakeFragmentedCordFromVector) {
   std::vector<absl::string_view> chunks = {"A ", "fragmented ", "Cord"};
   absl::Cord fragmented = absl::MakeFragmentedCord(chunks);
 
@@ -1314,7 +1479,7 @@
   ASSERT_TRUE(++chunk_it == fragmented.chunk_end());
 }
 
-TEST(CordChunkIterator, Traits) {
+TEST_P(CordTest, CordChunkIteratorTraits) {
   static_assert(std::is_copy_constructible<absl::Cord::ChunkIterator>::value,
                 "");
   static_assert(std::is_copy_assignable<absl::Cord::ChunkIterator>::value, "");
@@ -1395,7 +1560,7 @@
   EXPECT_TRUE(post_iter == cord.chunk_end());  // NOLINT
 }
 
-TEST(CordChunkIterator, Operations) {
+TEST_P(CordTest, CordChunkIteratorOperations) {
   absl::Cord empty_cord;
   VerifyChunkIterator(empty_cord, 0);
 
@@ -1420,14 +1585,14 @@
     VerifyChunkIterator(reused_nodes_cord, expected_chunks);
   }
 
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
+  RandomEngine rng(GTEST_FLAG_GET(random_seed));
   absl::Cord flat_cord(RandomLowercaseString(&rng, 256));
   absl::Cord subcords;
   for (int i = 0; i < 128; ++i) subcords.Prepend(flat_cord.Subcord(i, 128));
   VerifyChunkIterator(subcords, 128);
 }
 
-TEST(CordCharIterator, Traits) {
+TEST_P(CordTest, CharIteratorTraits) {
   static_assert(std::is_copy_constructible<absl::Cord::CharIterator>::value,
                 "");
   static_assert(std::is_copy_assignable<absl::Cord::CharIterator>::value, "");
@@ -1536,7 +1701,7 @@
   }
 }
 
-TEST(CordCharIterator, Operations) {
+TEST_P(CordTest, CharIteratorOperations) {
   absl::Cord empty_cord;
   VerifyCharIterator(empty_cord);
 
@@ -1558,14 +1723,49 @@
     VerifyCharIterator(reused_nodes_cord);
   }
 
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
+  RandomEngine rng(GTEST_FLAG_GET(random_seed));
   absl::Cord flat_cord(RandomLowercaseString(&rng, 256));
   absl::Cord subcords;
   for (int i = 0; i < 4; ++i) subcords.Prepend(flat_cord.Subcord(16 * i, 128));
   VerifyCharIterator(subcords);
 }
 
-TEST(Cord, StreamingOutput) {
+TEST_P(CordTest, CharIteratorAdvanceAndRead) {
+  // Create a Cord holding 6 flats of 2500 bytes each, and then iterate over it
+  // reading 150, 1500, 2500 and 3000 bytes. This will result in all possible
+  // partial, full and straddled read combinations including reads below
+  // kMaxBytesToCopy. b/197776822 surfaced a bug for a specific partial, small
+  // read 'at end' on Cord which caused a failure on attempting to read past the
+  // end in CordRepBtreeReader which was not covered by any existing test.
+  constexpr int kBlocks = 6;
+  constexpr size_t kBlockSize = 2500;
+  constexpr size_t kChunkSize1 = 1500;
+  constexpr size_t kChunkSize2 = 2500;
+  constexpr size_t kChunkSize3 = 3000;
+  constexpr size_t kChunkSize4 = 150;
+  RandomEngine rng;
+  std::string data = RandomLowercaseString(&rng, kBlocks * kBlockSize);
+  absl::Cord cord;
+  for (int i = 0; i < kBlocks; ++i) {
+    const std::string block = data.substr(i * kBlockSize, kBlockSize);
+    cord.Append(absl::Cord(block));
+  }
+
+  for (size_t chunk_size :
+       {kChunkSize1, kChunkSize2, kChunkSize3, kChunkSize4}) {
+    absl::Cord::CharIterator it = cord.char_begin();
+    size_t offset = 0;
+    while (offset < data.length()) {
+      const size_t n = std::min<size_t>(data.length() - offset, chunk_size);
+      absl::Cord chunk = cord.AdvanceAndRead(&it, n);
+      ASSERT_EQ(chunk.size(), n);
+      ASSERT_EQ(chunk.Compare(data.substr(offset, n)), 0);
+      offset += n;
+    }
+  }
+}
+
+TEST_P(CordTest, StreamingOutput) {
   absl::Cord c =
       absl::MakeFragmentedCord({"A ", "small ", "fragmented ", "Cord", "."});
   std::stringstream output;
@@ -1573,7 +1773,7 @@
   EXPECT_EQ("A small fragmented Cord.", output.str());
 }
 
-TEST(Cord, ForEachChunk) {
+TEST_P(CordTest, ForEachChunk) {
   for (int num_elements : {1, 10, 200}) {
     SCOPED_TRACE(num_elements);
     std::vector<std::string> cord_chunks;
@@ -1591,7 +1791,7 @@
   }
 }
 
-TEST(Cord, SmallBufferAssignFromOwnData) {
+TEST_P(CordTest, SmallBufferAssignFromOwnData) {
   constexpr size_t kMaxInline = 15;
   std::string contents = "small buff cord";
   EXPECT_EQ(contents.size(), kMaxInline);
@@ -1606,7 +1806,7 @@
   }
 }
 
-TEST(Cord, Format) {
+TEST_P(CordTest, Format) {
   absl::Cord c;
   absl::Format(&c, "There were %04d little %s.", 3, "pigs");
   EXPECT_EQ(c, "There were 0003 little pigs.");
@@ -1614,7 +1814,7 @@
   EXPECT_EQ(c, "There were 0003 little pigs.And 1   bad wolf!");
 }
 
-TEST(CordDeathTest, Hardening) {
+TEST_P(CordTest, Hardening) {
   absl::Cord cord("hello");
   // These statement should abort the program in all builds modes.
   EXPECT_DEATH_IF_SUPPORTED(cord.RemovePrefix(6), "");
@@ -1634,6 +1834,48 @@
   EXPECT_DEATH_IF_SUPPORTED(++cord.chunk_end(), "");
 }
 
+// This test mimics a specific (and rare) application repeatedly splitting a
+// cord, inserting (overwriting) a string value, and composing a new cord from
+// the three pieces. This is hostile towards a Btree implementation: A split of
+// a node at any level is likely to have the right-most edge of the left split,
+// and the left-most edge of the right split shared. For example, splitting a
+// leaf node with 6 edges will result likely in a 1-6, 2-5, 3-4, etc. split,
+// sharing the 'split node'. When recomposing such nodes, we 'injected' an edge
+// in that node. As this happens with some probability on each level of the
+// tree, this will quickly grow the tree until it reaches maximum height.
+TEST_P(CordTest, BtreeHostileSplitInsertJoin) {
+  absl::BitGen bitgen;
+
+  // Start with about 1GB of data
+  std::string data(1 << 10, 'x');
+  absl::Cord buffer(data);
+  absl::Cord cord;
+  for (int i = 0; i < 1000000; ++i) {
+    cord.Append(buffer);
+  }
+
+  for (int j = 0; j < 1000; ++j) {
+    size_t offset = absl::Uniform(bitgen, 0u, cord.size());
+    size_t length = absl::Uniform(bitgen, 100u, data.size());
+    if (cord.size() == offset) {
+      cord.Append(absl::string_view(data.data(), length));
+    } else {
+      absl::Cord suffix;
+      if (offset + length < cord.size()) {
+        suffix = cord;
+        suffix.RemovePrefix(offset + length);
+      }
+      if (cord.size() > offset) {
+        cord.RemoveSuffix(cord.size() - offset);
+      }
+      cord.Append(absl::string_view(data.data(), length));
+      if (!suffix.empty()) {
+        cord.Append(suffix);
+      }
+    }
+  }
+}
+
 class AfterExitCordTester {
  public:
   bool Set(absl::Cord* cord, absl::string_view expected) {
@@ -1707,7 +1949,7 @@
 };
 
 
-TEST(Cord, ConstinitConstructor) {
+TEST_P(CordTest, ConstinitConstructor) {
   TestConstinitConstructor(
       absl::strings_internal::MakeStringConstant(ShortView{}));
   TestConstinitConstructor(
diff --git a/absl/strings/cord_test_helpers.h b/absl/strings/cord_test_helpers.h
index f1036e3..31a1dc8 100644
--- a/absl/strings/cord_test_helpers.h
+++ b/absl/strings/cord_test_helpers.h
@@ -17,11 +17,73 @@
 #ifndef ABSL_STRINGS_CORD_TEST_HELPERS_H_
 #define ABSL_STRINGS_CORD_TEST_HELPERS_H_
 
+#include <cstdint>
+#include <iostream>
+#include <string>
+
+#include "absl/base/config.h"
 #include "absl/strings/cord.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/string_view.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
+// Cord sizes relevant for testing
+enum class TestCordSize {
+  // An empty value
+  kEmpty = 0,
+
+  // An inlined string value
+  kInlined = cord_internal::kMaxInline / 2 + 1,
+
+  // 'Well known' SSO lengths (excluding terminating zero).
+  // libstdcxx has a maximum SSO of 15, libc++ has a maximum SSO of 22.
+  kStringSso1 = 15,
+  kStringSso2 = 22,
+
+  // A string value which is too large to fit in inlined data, but small enough
+  // such that Cord prefers copying the value if possible, i.e.: not stealing
+  // std::string inputs, or referencing existing CordReps on Append, etc.
+  kSmall = cord_internal::kMaxBytesToCopy / 2 + 1,
+
+  // A string value large enough that Cord prefers to reference or steal from
+  // existing inputs rather than copying contents of the input.
+  kMedium = cord_internal::kMaxFlatLength / 2 + 1,
+
+  // A string value large enough to cause it to be stored in mutliple flats.
+  kLarge = cord_internal::kMaxFlatLength * 4
+};
+
+// To string helper
+inline absl::string_view ToString(TestCordSize size) {
+  switch (size) {
+    case TestCordSize::kEmpty:
+      return "Empty";
+    case TestCordSize::kInlined:
+      return "Inlined";
+    case TestCordSize::kSmall:
+      return "Small";
+    case TestCordSize::kStringSso1:
+      return "StringSso1";
+    case TestCordSize::kStringSso2:
+      return "StringSso2";
+    case TestCordSize::kMedium:
+      return "Medium";
+    case TestCordSize::kLarge:
+      return "Large";
+  }
+  return "???";
+}
+
+// Returns the length matching the specified size
+inline size_t Length(TestCordSize size) { return static_cast<size_t>(size); }
+
+// Stream output helper
+inline std::ostream& operator<<(std::ostream& stream, TestCordSize size) {
+  return stream << ToString(size);
+}
+
 // Creates a multi-segment Cord from an iterable container of strings.  The
 // resulting Cord is guaranteed to have one segment for every string in the
 // container.  This allows code to be unit tested with multi-segment Cord
diff --git a/absl/strings/cordz_test.cc b/absl/strings/cordz_test.cc
new file mode 100644
index 0000000..2b7d30b
--- /dev/null
+++ b/absl/strings/cordz_test.cc
@@ -0,0 +1,466 @@
+// Copyright 2021 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 <cstdint>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/macros.h"
+#include "absl/strings/cord.h"
+#include "absl/strings/cord_test_helpers.h"
+#include "absl/strings/cordz_test_helpers.h"
+#include "absl/strings/internal/cordz_functions.h"
+#include "absl/strings/internal/cordz_info.h"
+#include "absl/strings/internal/cordz_sample_token.h"
+#include "absl/strings/internal/cordz_statistics.h"
+#include "absl/strings/internal/cordz_update_tracker.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+
+#ifdef ABSL_INTERNAL_CORDZ_ENABLED
+
+using testing::Eq;
+using testing::AnyOf;
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+using cord_internal::CordzInfo;
+using cord_internal::CordzSampleToken;
+using cord_internal::CordzStatistics;
+using cord_internal::CordzUpdateTracker;
+using Method = CordzUpdateTracker::MethodIdentifier;
+
+// Do not print cord contents, we only care about 'size' perhaps.
+// Note that this method must be inside the named namespace.
+inline void PrintTo(const Cord& cord, std::ostream* s) {
+  if (s) *s << "Cord[" << cord.size() << "]";
+}
+
+namespace {
+
+auto constexpr kMaxInline = cord_internal::kMaxInline;
+
+// Returns a string_view value of the specified length
+// We do this to avoid 'consuming' large strings in Cord by default.
+absl::string_view MakeString(size_t size) {
+  thread_local std::string str;
+  str = std::string(size, '.');
+  return str;
+}
+
+absl::string_view MakeString(TestCordSize size) {
+  return MakeString(Length(size));
+}
+
+// Returns a cord with a sampled method of kAppendString.
+absl::Cord MakeAppendStringCord(TestCordSize size) {
+  CordzSamplingIntervalHelper always(1);
+  absl::Cord cord;
+  cord.Append(MakeString(size));
+  return cord;
+}
+
+std::string TestParamToString(::testing::TestParamInfo<TestCordSize> size) {
+  return absl::StrCat("On", ToString(size.param), "Cord");
+}
+
+class CordzUpdateTest : public testing::TestWithParam<TestCordSize> {
+ public:
+  Cord& cord() { return cord_; }
+
+  Method InitialOr(Method method) const {
+    return (GetParam() > TestCordSize::kInlined) ? Method::kConstructorString
+                                                 : method;
+  }
+
+ private:
+  CordzSamplingIntervalHelper sample_every_{1};
+  Cord cord_{MakeString(GetParam())};
+};
+
+template <typename T>
+std::string ParamToString(::testing::TestParamInfo<T> param) {
+  return std::string(ToString(param.param));
+}
+
+INSTANTIATE_TEST_SUITE_P(WithParam, CordzUpdateTest,
+                         testing::Values(TestCordSize::kEmpty,
+                                         TestCordSize::kInlined,
+                                         TestCordSize::kLarge),
+                         TestParamToString);
+
+class CordzStringTest : public testing::TestWithParam<TestCordSize> {
+ private:
+  CordzSamplingIntervalHelper sample_every_{1};
+};
+
+INSTANTIATE_TEST_SUITE_P(WithParam, CordzStringTest,
+                         testing::Values(TestCordSize::kInlined,
+                                         TestCordSize::kStringSso1,
+                                         TestCordSize::kStringSso2,
+                                         TestCordSize::kSmall,
+                                         TestCordSize::kLarge),
+                         ParamToString<TestCordSize>);
+
+TEST(CordzTest, ConstructSmallArray) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord cord(MakeString(TestCordSize::kSmall));
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+}
+
+TEST(CordzTest, ConstructLargeArray) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord cord(MakeString(TestCordSize::kLarge));
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+}
+
+TEST_P(CordzStringTest, ConstructString) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord cord(std::string(Length(GetParam()), '.'));
+  if (Length(GetParam()) > kMaxInline) {
+    EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+  }
+}
+
+TEST(CordzTest, CopyConstructFromUnsampled) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord src = UnsampledCord(MakeString(TestCordSize::kLarge));
+  Cord cord(src);
+  EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr));
+}
+
+TEST(CordzTest, CopyConstructFromSampled) {
+  CordzSamplingIntervalHelper sample_never{99999};
+  Cord src = MakeAppendStringCord(TestCordSize::kLarge);
+  Cord cord(src);
+  ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorCord));
+  CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics();
+  EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString));
+  EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1));
+}
+
+TEST(CordzTest, MoveConstruct) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord src(MakeString(TestCordSize::kLarge));
+  Cord cord(std::move(src));
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+}
+
+TEST_P(CordzUpdateTest, AssignUnsampledCord) {
+  Cord src = UnsampledCord(MakeString(TestCordSize::kLarge));
+  const CordzInfo* info = GetCordzInfoForTesting(cord());
+  cord() = src;
+  EXPECT_THAT(GetCordzInfoForTesting(cord()), Eq(nullptr));
+  EXPECT_FALSE(CordzInfoIsListed(info));
+}
+
+TEST_P(CordzUpdateTest, AssignSampledCord) {
+  Cord src = MakeAppendStringCord(TestCordSize::kLarge);
+  cord() = src;
+  ASSERT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignCord));
+  CordzStatistics stats = GetCordzInfoForTesting(cord())->GetCordzStatistics();
+  EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString));
+  EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1));
+  EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0));
+}
+
+TEST(CordzUpdateTest, AssignSampledCordToInlined) {
+  CordzSamplingIntervalHelper sample_never{99999};
+  Cord cord;
+  Cord src = MakeAppendStringCord(TestCordSize::kLarge);
+  cord = src;
+  ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kAssignCord));
+  CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics();
+  EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString));
+  EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1));
+  EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0));
+}
+
+TEST(CordzUpdateTest, AssignSampledCordToUnsampledCord) {
+  CordzSamplingIntervalHelper sample_never{99999};
+  Cord cord = UnsampledCord(MakeString(TestCordSize::kLarge));
+  Cord src = MakeAppendStringCord(TestCordSize::kLarge);
+  cord = src;
+  ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kAssignCord));
+  CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics();
+  EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString));
+  EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1));
+  EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0));
+}
+
+TEST(CordzUpdateTest, AssignUnsampledCordToSampledCordWithoutSampling) {
+  CordzSamplingIntervalHelper sample_never{99999};
+  Cord cord = MakeAppendStringCord(TestCordSize::kLarge);
+  const CordzInfo* info = GetCordzInfoForTesting(cord);
+  Cord src = UnsampledCord(MakeString(TestCordSize::kLarge));
+  cord = src;
+  EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr));
+  EXPECT_FALSE(CordzInfoIsListed(info));
+}
+
+TEST(CordzUpdateTest, AssignUnsampledCordToSampledCordWithSampling) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord cord = MakeAppendStringCord(TestCordSize::kLarge);
+  const CordzInfo* info = GetCordzInfoForTesting(cord);
+  Cord src = UnsampledCord(MakeString(TestCordSize::kLarge));
+  cord = src;
+  EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr));
+  EXPECT_FALSE(CordzInfoIsListed(info));
+}
+
+TEST(CordzUpdateTest, AssignSampledCordToSampledCord) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord src = MakeAppendStringCord(TestCordSize::kLarge);
+  Cord cord(MakeString(TestCordSize::kLarge));
+  cord = src;
+  ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kAssignCord));
+  CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics();
+  EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString));
+  EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1));
+  EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0));
+}
+
+TEST(CordzUpdateTest, AssignUnsampledCordToSampledCord) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord src = MakeAppendStringCord(TestCordSize::kLarge);
+  Cord cord(MakeString(TestCordSize::kLarge));
+  cord = src;
+  ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kAssignCord));
+  CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics();
+  EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString));
+  EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1));
+  EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0));
+}
+
+TEST(CordzTest, AssignInlinedCordToSampledCord) {
+  CordzSampleToken token;
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord cord(MakeString(TestCordSize::kLarge));
+  const CordzInfo* info = GetCordzInfoForTesting(cord);
+  Cord src = UnsampledCord(MakeString(TestCordSize::kInlined));
+  cord = src;
+  EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr));
+  EXPECT_FALSE(CordzInfoIsListed(info));
+}
+
+TEST(CordzUpdateTest, MoveAssignCord) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord cord;
+  Cord src(MakeString(TestCordSize::kLarge));
+  cord = std::move(src);
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+}
+
+TEST_P(CordzUpdateTest, AssignLargeArray) {
+  cord() = MakeString(TestCordSize::kSmall);
+  EXPECT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignString));
+}
+
+TEST_P(CordzUpdateTest, AssignSmallArray) {
+  cord() = MakeString(TestCordSize::kSmall);
+  EXPECT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignString));
+}
+
+TEST_P(CordzUpdateTest, AssignInlinedArray) {
+  cord() = MakeString(TestCordSize::kInlined);
+  EXPECT_THAT(GetCordzInfoForTesting(cord()), Eq(nullptr));
+}
+
+TEST_P(CordzStringTest, AssignStringToInlined) {
+  Cord cord;
+  cord = std::string(Length(GetParam()), '.');
+  if (Length(GetParam()) > kMaxInline) {
+    EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAssignString));
+  }
+}
+
+TEST_P(CordzStringTest, AssignStringToCord) {
+  Cord cord(MakeString(TestCordSize::kLarge));
+  cord = std::string(Length(GetParam()), '.');
+  if (Length(GetParam()) > kMaxInline) {
+    EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+    EXPECT_THAT(cord, CordzMethodCountEq(Method::kAssignString, 1));
+  }
+}
+
+TEST_P(CordzUpdateTest, AssignInlinedString) {
+  cord() = std::string(Length(TestCordSize::kInlined), '.');
+  EXPECT_THAT(GetCordzInfoForTesting(cord()), Eq(nullptr));
+}
+
+TEST_P(CordzUpdateTest, AppendCord) {
+  Cord src = UnsampledCord(MakeString(TestCordSize::kLarge));
+  cord().Append(src);
+  EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendCord)));
+}
+
+TEST_P(CordzUpdateTest, MoveAppendCord) {
+  cord().Append(UnsampledCord(MakeString(TestCordSize::kLarge)));
+  EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendCord)));
+}
+
+TEST_P(CordzUpdateTest, AppendSmallArray) {
+  cord().Append(MakeString(TestCordSize::kSmall));
+  EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendString)));
+}
+
+TEST_P(CordzUpdateTest, AppendLargeArray) {
+  cord().Append(MakeString(TestCordSize::kLarge));
+  EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendString)));
+}
+
+TEST_P(CordzStringTest, AppendStringToEmpty) {
+  Cord cord;
+  cord.Append(std::string(Length(GetParam()), '.'));
+  if (Length(GetParam()) > kMaxInline) {
+    EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAppendString));
+  }
+}
+
+TEST_P(CordzStringTest, AppendStringToInlined) {
+  Cord cord(MakeString(TestCordSize::kInlined));
+  cord.Append(std::string(Length(GetParam()), '.'));
+  if (Length(TestCordSize::kInlined) + Length(GetParam()) > kMaxInline) {
+    EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAppendString));
+  }
+}
+
+TEST_P(CordzStringTest, AppendStringToCord) {
+  Cord cord(MakeString(TestCordSize::kLarge));
+  cord.Append(std::string(Length(GetParam()), '.'));
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+  EXPECT_THAT(cord, CordzMethodCountEq(Method::kAppendString, 1));
+}
+
+TEST(CordzTest, MakeCordFromExternal) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord cord = MakeCordFromExternal("Hello world", [](absl::string_view) {});
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kMakeCordFromExternal));
+}
+
+TEST(CordzTest, MakeCordFromEmptyExternal) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord cord = MakeCordFromExternal({}, [](absl::string_view) {});
+  EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr));
+}
+
+TEST_P(CordzUpdateTest, PrependCord) {
+  Cord src = UnsampledCord(MakeString(TestCordSize::kLarge));
+  cord().Prepend(src);
+  EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kPrependCord)));
+}
+
+TEST_P(CordzUpdateTest, PrependSmallArray) {
+  cord().Prepend(MakeString(TestCordSize::kSmall));
+  EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kPrependString)));
+}
+
+TEST_P(CordzUpdateTest, PrependLargeArray) {
+  cord().Prepend(MakeString(TestCordSize::kLarge));
+  EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kPrependString)));
+}
+
+TEST_P(CordzStringTest, PrependStringToEmpty) {
+  Cord cord;
+  cord.Prepend(std::string(Length(GetParam()), '.'));
+  if (Length(GetParam()) > kMaxInline) {
+    EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kPrependString));
+  }
+}
+
+TEST_P(CordzStringTest, PrependStringToInlined) {
+  Cord cord(MakeString(TestCordSize::kInlined));
+  cord.Prepend(std::string(Length(GetParam()), '.'));
+  if (Length(TestCordSize::kInlined) + Length(GetParam()) > kMaxInline) {
+    EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kPrependString));
+  }
+}
+
+TEST_P(CordzStringTest, PrependStringToCord) {
+  Cord cord(MakeString(TestCordSize::kLarge));
+  cord.Prepend(std::string(Length(GetParam()), '.'));
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+  EXPECT_THAT(cord, CordzMethodCountEq(Method::kPrependString, 1));
+}
+
+TEST(CordzTest, RemovePrefix) {
+  CordzSamplingIntervalHelper sample_every(1);
+  Cord cord(MakeString(TestCordSize::kLarge));
+
+  // Half the cord
+  cord.RemovePrefix(cord.size() / 2);
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+  EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemovePrefix, 1));
+
+  // TODO(mvels): RemovePrefix does not reset to inlined, except if empty?
+  cord.RemovePrefix(cord.size() - kMaxInline);
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+  EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemovePrefix, 2));
+
+  cord.RemovePrefix(cord.size());
+  EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr));
+}
+
+TEST(CordzTest, RemoveSuffix) {
+  CordzSamplingIntervalHelper sample_every(1);
+  Cord cord(MakeString(TestCordSize::kLarge));
+
+  // Half the cord
+  cord.RemoveSuffix(cord.size() / 2);
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+  EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemoveSuffix, 1));
+
+  // TODO(mvels): RemoveSuffix does not reset to inlined, except if empty?
+  cord.RemoveSuffix(cord.size() - kMaxInline);
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+  EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemoveSuffix, 2));
+
+  cord.RemoveSuffix(cord.size());
+  EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr));
+}
+
+TEST(CordzTest, SubCordFromUnsampledCord) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord src = UnsampledCord(MakeString(TestCordSize::kLarge));
+  Cord cord = src.Subcord(10, src.size() / 2);
+  EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr));
+}
+
+TEST(CordzTest, SubCordFromSampledCord) {
+  CordzSamplingIntervalHelper sample_never{99999};
+  Cord src = MakeAppendStringCord(TestCordSize::kLarge);
+  Cord cord = src.Subcord(10, src.size() / 2);
+  ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kSubCord));
+  CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics();
+  EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString));
+  EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1));
+}
+
+TEST(CordzTest, SmallSubCord) {
+  CordzSamplingIntervalHelper sample_never{99999};
+  Cord src = MakeAppendStringCord(TestCordSize::kLarge);
+  Cord cord = src.Subcord(10, kMaxInline + 1);
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kSubCord));
+}
+
+}  // namespace
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_INTERNAL_CORDZ_ENABLED
diff --git a/absl/strings/cordz_test_helpers.h b/absl/strings/cordz_test_helpers.h
new file mode 100644
index 0000000..e410eec
--- /dev/null
+++ b/absl/strings/cordz_test_helpers.h
@@ -0,0 +1,151 @@
+// Copyright 2021 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_CORDZ_TEST_HELPERS_H_
+#define ABSL_STRINGS_CORDZ_TEST_HELPERS_H_
+
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/base/macros.h"
+#include "absl/strings/cord.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cordz_info.h"
+#include "absl/strings/internal/cordz_sample_token.h"
+#include "absl/strings/internal/cordz_statistics.h"
+#include "absl/strings/internal/cordz_update_tracker.h"
+#include "absl/strings/str_cat.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// Returns the CordzInfo for the cord, or nullptr if the cord is not sampled.
+inline const cord_internal::CordzInfo* GetCordzInfoForTesting(
+    const Cord& cord) {
+  if (!cord.contents_.is_tree()) return nullptr;
+  return cord.contents_.cordz_info();
+}
+
+// Returns true if the provided cordz_info is in the list of sampled cords.
+inline bool CordzInfoIsListed(const cord_internal::CordzInfo* cordz_info,
+                              cord_internal::CordzSampleToken token = {}) {
+  for (const cord_internal::CordzInfo& info : token) {
+    if (cordz_info == &info) return true;
+  }
+  return false;
+}
+
+// Matcher on Cord that verifies all of:
+// - the cord is sampled
+// - the CordzInfo of the cord is listed / discoverable.
+// - the reported CordzStatistics match the cord's actual properties
+// - the cord has an (initial) UpdateTracker count of 1 for `method`
+MATCHER_P(HasValidCordzInfoOf, method, "CordzInfo matches cord") {
+  const cord_internal::CordzInfo* cord_info = GetCordzInfoForTesting(arg);
+  if (cord_info == nullptr) {
+    *result_listener << "cord is not sampled";
+    return false;
+  }
+  if (!CordzInfoIsListed(cord_info)) {
+    *result_listener << "cord is sampled, but not listed";
+    return false;
+  }
+  cord_internal::CordzStatistics stat = cord_info->GetCordzStatistics();
+  if (stat.size != arg.size()) {
+    *result_listener << "cordz size " << stat.size
+                     << " does not match cord size " << arg.size();
+    return false;
+  }
+  if (stat.update_tracker.Value(method) != 1) {
+    *result_listener << "Expected method count 1 for " << method << ", found "
+                     << stat.update_tracker.Value(method);
+    return false;
+  }
+  return true;
+}
+
+// Matcher on Cord that verifies that the cord is sampled and that the CordzInfo
+// update tracker has 'method' with a call count of 'n'
+MATCHER_P2(CordzMethodCountEq, method, n,
+           absl::StrCat("CordzInfo method count equals ", n)) {
+  const cord_internal::CordzInfo* cord_info = GetCordzInfoForTesting(arg);
+  if (cord_info == nullptr) {
+    *result_listener << "cord is not sampled";
+    return false;
+  }
+  cord_internal::CordzStatistics stat = cord_info->GetCordzStatistics();
+  if (stat.update_tracker.Value(method) != n) {
+    *result_listener << "Expected method count " << n << " for " << method
+                     << ", found " << stat.update_tracker.Value(method);
+    return false;
+  }
+  return true;
+}
+
+// Cordz will only update with a new rate once the previously scheduled event
+// has fired. When we disable Cordz, a long delay takes place where we won't
+// consider profiling new Cords. CordzSampleIntervalHelper will burn through
+// that interval and allow for testing that assumes that the average sampling
+// interval is a particular value.
+class CordzSamplingIntervalHelper {
+ public:
+  explicit CordzSamplingIntervalHelper(int32_t interval)
+      : orig_mean_interval_(absl::cord_internal::get_cordz_mean_interval()) {
+    absl::cord_internal::set_cordz_mean_interval(interval);
+    absl::cord_internal::cordz_set_next_sample_for_testing(interval);
+  }
+
+  ~CordzSamplingIntervalHelper() {
+    absl::cord_internal::set_cordz_mean_interval(orig_mean_interval_);
+    absl::cord_internal::cordz_set_next_sample_for_testing(orig_mean_interval_);
+  }
+
+ private:
+  int32_t orig_mean_interval_;
+};
+
+// Wrapper struct managing a small CordRep `rep`
+struct TestCordRep {
+  cord_internal::CordRepFlat* rep;
+
+  TestCordRep() {
+    rep = cord_internal::CordRepFlat::New(100);
+    rep->length = 100;
+    memset(rep->Data(), 1, 100);
+  }
+  ~TestCordRep() { cord_internal::CordRep::Unref(rep); }
+};
+
+// Wrapper struct managing a small CordRep `rep`, and
+// an InlineData `data` initialized with that CordRep.
+struct TestCordData {
+  TestCordRep rep;
+  cord_internal::InlineData data{rep.rep};
+};
+
+// Creates a Cord that is not sampled
+template <typename... Args>
+Cord UnsampledCord(Args... args) {
+  CordzSamplingIntervalHelper never(9999);
+  Cord cord(std::forward<Args>(args)...);
+  ABSL_ASSERT(GetCordzInfoForTesting(cord) == nullptr);
+  return cord;
+}
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_CORDZ_TEST_HELPERS_H_
diff --git a/absl/strings/internal/charconv_parse.cc b/absl/strings/internal/charconv_parse.cc
index 8b11868..d29acaf 100644
--- a/absl/strings/internal/charconv_parse.cc
+++ b/absl/strings/internal/charconv_parse.cc
@@ -52,7 +52,7 @@
 
 // The lowest valued 19-digit decimal mantissa we can read still contains
 // sufficient information to reconstruct a binary mantissa.
-static_assert(1000000000000000000u > (uint64_t(1) << (53 + 3)), "(b) above");
+static_assert(1000000000000000000u > (uint64_t{1} << (53 + 3)), "(b) above");
 
 // ParseFloat<16> will read the first 15 significant digits of the mantissa.
 //
diff --git a/absl/strings/internal/cord_internal.cc b/absl/strings/internal/cord_internal.cc
index 905ffd0..1767e6f 100644
--- a/absl/strings/internal/cord_internal.cc
+++ b/absl/strings/internal/cord_internal.cc
@@ -18,6 +18,7 @@
 #include <memory>
 
 #include "absl/container/inlined_vector.h"
+#include "absl/strings/internal/cord_rep_btree.h"
 #include "absl/strings/internal/cord_rep_flat.h"
 #include "absl/strings/internal/cord_rep_ring.h"
 
@@ -25,10 +26,12 @@
 ABSL_NAMESPACE_BEGIN
 namespace cord_internal {
 
+ABSL_CONST_INIT std::atomic<bool> cord_btree_enabled(kCordEnableBtreeDefault);
 ABSL_CONST_INIT std::atomic<bool> cord_ring_buffer_enabled(
     kCordEnableRingBufferDefault);
 ABSL_CONST_INIT std::atomic<bool> shallow_subcords_enabled(
     kCordShallowSubcordsDefault);
+ABSL_CONST_INIT std::atomic<bool> cord_btree_exhaustive_validation(false);
 
 void CordRep::Destroy(CordRep* rep) {
   assert(rep != nullptr);
@@ -49,6 +52,9 @@
         rep = left;
         continue;
       }
+    } else if (rep->tag == BTREE) {
+      CordRepBtree::Destroy(rep->btree());
+      rep = nullptr;
     } else if (rep->tag == RING) {
       CordRepRing::Destroy(rep->ring());
       rep = nullptr;
diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h
index a1ba67f..bfe5564 100644
--- a/absl/strings/internal/cord_internal.h
+++ b/absl/strings/internal/cord_internal.h
@@ -37,13 +37,25 @@
 
 // Default feature enable states for cord ring buffers
 enum CordFeatureDefaults {
+  kCordEnableBtreeDefault = true,
   kCordEnableRingBufferDefault = false,
   kCordShallowSubcordsDefault = false
 };
 
+extern std::atomic<bool> cord_btree_enabled;
 extern std::atomic<bool> cord_ring_buffer_enabled;
 extern std::atomic<bool> shallow_subcords_enabled;
 
+// `cord_btree_exhaustive_validation` can be set to force exhaustive validation
+// in debug assertions, and code that calls `IsValid()` explicitly. By default,
+// assertions should be relatively cheap and AssertValid() can easily lead to
+// O(n^2) complexity as recursive / full tree validation is O(n).
+extern std::atomic<bool> cord_btree_exhaustive_validation;
+
+inline void enable_cord_btree(bool enable) {
+  cord_btree_enabled.store(enable, std::memory_order_relaxed);
+}
+
 inline void enable_cord_ring_buffer(bool enable) {
   cord_ring_buffer_enabled.store(enable, std::memory_order_relaxed);
 }
@@ -68,12 +80,16 @@
   kMaxBytesToCopy = 511
 };
 
-// Wraps std::atomic for reference counting.
-class Refcount {
+// Compact class for tracking the reference count and state flags for CordRep
+// instances.  Data is stored in an atomic int32_t for compactness and speed.
+class RefcountAndFlags {
  public:
-  constexpr Refcount() : count_{kRefIncrement} {}
+  constexpr RefcountAndFlags() : count_{kRefIncrement} {}
   struct Immortal {};
-  explicit constexpr Refcount(Immortal) : count_(kImmortalTag) {}
+  explicit constexpr RefcountAndFlags(Immortal) : count_(kImmortalFlag) {}
+  struct WithCrc {};
+  explicit constexpr RefcountAndFlags(WithCrc)
+      : count_(kCrcFlag | kRefIncrement) {}
 
   // Increments the reference count. Imposes no memory ordering.
   inline void Increment() {
@@ -86,55 +102,82 @@
   // Returns false if there are no references outstanding; true otherwise.
   // Inserts barriers to ensure that state written before this method returns
   // false will be visible to a thread that just observed this method returning
-  // false.
+  // false.  Always returns false when the immortal bit is set.
   inline bool Decrement() {
-    int32_t refcount = count_.load(std::memory_order_acquire);
-    assert(refcount > 0 || refcount & kImmortalTag);
+    int32_t refcount = count_.load(std::memory_order_acquire) & kRefcountMask;
+    assert(refcount > 0 || refcount & kImmortalFlag);
     return refcount != kRefIncrement &&
-           count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) !=
-               kRefIncrement;
+           (count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) &
+            kRefcountMask) != kRefIncrement;
   }
 
   // Same as Decrement but expect that refcount is greater than 1.
   inline bool DecrementExpectHighRefcount() {
     int32_t refcount =
-        count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel);
-    assert(refcount > 0 || refcount & kImmortalTag);
+        count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) &
+        kRefcountMask;
+    assert(refcount > 0 || refcount & kImmortalFlag);
     return refcount != kRefIncrement;
   }
 
   // Returns the current reference count using acquire semantics.
   inline int32_t Get() const {
-    return count_.load(std::memory_order_acquire) >> kImmortalShift;
+    return count_.load(std::memory_order_acquire) >> kNumFlags;
   }
 
-  // Returns whether the atomic integer is 1.
-  // If the reference count is used in the conventional way, a
-  // reference count of 1 implies that the current thread owns the
-  // reference and no other thread shares it.
-  // This call performs the test for a reference count of one, and
-  // performs the memory barrier needed for the owning thread
-  // to act on the object, knowing that it has exclusive access to the
-  // object.
+  // Returns true if the referenced object carries a CRC value.
+  bool HasCrc() const {
+    return (count_.load(std::memory_order_relaxed) & kCrcFlag) != 0;
+  }
+
+  // Returns true iff the atomic integer is 1 and this node does not store
+  // a CRC.  When both these conditions are met, the current thread owns
+  // the reference and no other thread shares it, so its contents may be
+  // safely mutated.
+  //
+  // If the referenced item is shared, carries a CRC, or is immortal,
+  // it should not be modified in-place, and this function returns false.
+  //
+  // This call performs the memory barrier needed for the owning thread
+  // to act on the object, so that if it returns true, it may safely
+  // assume exclusive access to the object.
+  inline bool IsMutable() {
+    return (count_.load(std::memory_order_acquire)) == kRefIncrement;
+  }
+
+  // Returns whether the atomic integer is 1.  Similar to IsMutable(),
+  // but does not check for a stored CRC.  (An unshared node with a CRC is not
+  // mutable, because changing its data would invalidate the CRC.)
+  //
+  // When this returns true, there are no other references, and data sinks
+  // may safely adopt the children of the CordRep.
   inline bool IsOne() {
-    return count_.load(std::memory_order_acquire) == kRefIncrement;
+    return (count_.load(std::memory_order_acquire) & kRefcountMask) ==
+           kRefIncrement;
   }
 
   bool IsImmortal() const {
-    return (count_.load(std::memory_order_relaxed) & kImmortalTag) != 0;
+    return (count_.load(std::memory_order_relaxed) & kImmortalFlag) != 0;
   }
 
  private:
-  // We reserve the bottom bit to tag a reference count as immortal.
-  // By making it `1` we ensure that we never reach `0` when adding/subtracting
-  // `2`, thus it never looks as if it should be destroyed.
-  // These are used for the StringConstant constructor where we do not increase
-  // the refcount at construction time (due to constinit requirements) but we
-  // will still decrease it at destruction time to avoid branching on Unref.
+  // We reserve the bottom bits for flags.
+  // kImmortalBit indicates that this entity should never be collected; it is
+  // used for the StringConstant constructor to avoid collecting immutable
+  // constant cords.
+  // kReservedFlag is reserved for future use.
   enum {
-    kImmortalShift = 1,
-    kRefIncrement = 1 << kImmortalShift,
-    kImmortalTag = kRefIncrement - 1
+    kNumFlags = 2,
+
+    kImmortalFlag = 0x1,
+    kCrcFlag = 0x2,
+    kRefIncrement = (1 << kNumFlags),
+
+    // Bitmask to use when checking refcount by equality.  This masks out
+    // all flags except kImmortalFlag, which is part of the refcount for
+    // purposes of equality.  (A refcount of 0 or 1 does not count as 0 or 1
+    // if the immortal bit is set.)
+    kRefcountMask = ~kCrcFlag,
   };
 
   std::atomic<int32_t> count_;
@@ -150,37 +193,67 @@
 struct CordRepFlat;
 struct CordRepSubstring;
 class CordRepRing;
+class CordRepBtree;
 
 // Various representations that we allow
 enum CordRepKind {
   CONCAT = 0,
-  EXTERNAL = 1,
-  SUBSTRING = 2,
+  SUBSTRING = 1,
+  BTREE = 2,
   RING = 3,
+  EXTERNAL = 4,
 
   // We have different tags for different sized flat arrays,
-  // starting with FLAT, and limited to MAX_FLAT_TAG. The 224 value is based on
+  // starting with FLAT, and limited to MAX_FLAT_TAG. The 225 value is based on
   // the current 'size to tag' encoding of 8 / 32 bytes. If a new tag is needed
   // in the future, then 'FLAT' and 'MAX_FLAT_TAG' should be adjusted as well
   // as the Tag <---> Size logic so that FLAT stil represents the minimum flat
   // allocation size. (32 bytes as of now).
-  FLAT = 4,
-  MAX_FLAT_TAG = 224
+  FLAT = 5,
+  MAX_FLAT_TAG = 225
 };
 
+// There are various locations where we want to check if some rep is a 'plain'
+// data edge, i.e. an external or flat rep. By having FLAT == EXTERNAL + 1, we
+// can perform this check in a single branch as 'tag >= EXTERNAL'
+// Likewise, we have some locations where we check for 'ring or external/flat',
+// so likewise align RING to EXTERNAL.
+// Note that we can leave this optimization to the compiler. The compiler will
+// DTRT when it sees a condition like `tag == EXTERNAL || tag >= FLAT`.
+static_assert(RING == BTREE + 1, "BTREE and RING not consecutive");
+static_assert(EXTERNAL == RING + 1, "BTREE and EXTERNAL not consecutive");
+static_assert(FLAT == EXTERNAL + 1, "EXTERNAL and FLAT not consecutive");
+
 struct CordRep {
   CordRep() = default;
-  constexpr CordRep(Refcount::Immortal immortal, size_t l)
+  constexpr CordRep(RefcountAndFlags::Immortal immortal, size_t l)
       : length(l), refcount(immortal), tag(EXTERNAL), storage{} {}
 
   // The following three fields have to be less than 32 bytes since
   // that is the smallest supported flat node size.
   size_t length;
-  Refcount refcount;
+  RefcountAndFlags refcount;
   // If tag < FLAT, it represents CordRepKind and indicates the type of node.
   // Otherwise, the node type is CordRepFlat and the tag is the encoded size.
   uint8_t tag;
-  char storage[1];  // Starting point for flat array: MUST BE LAST FIELD
+
+  // `storage` provides two main purposes:
+  // - the starting point for FlatCordRep.Data() [flexible-array-member]
+  // - 3 bytes of additional storage for use by derived classes.
+  // The latter is used by CordrepConcat and CordRepBtree. CordRepConcat stores
+  // a 'depth' value in storage[0], and the (future) CordRepBtree class stores
+  // `height`, `begin` and `end` in the 3 entries. Otherwise we would need to
+  // allocate room for these in the derived class, as not all compilers reuse
+  // padding space from the base class (clang and gcc do, MSVC does not, etc)
+  uint8_t storage[3];
+
+  // Returns true if this instance's tag matches the requested type.
+  constexpr bool IsRing() const { return tag == RING; }
+  constexpr bool IsConcat() const { return tag == CONCAT; }
+  constexpr bool IsSubstring() const { return tag == SUBSTRING; }
+  constexpr bool IsExternal() const { return tag == EXTERNAL; }
+  constexpr bool IsFlat() const { return tag >= FLAT; }
+  constexpr bool IsBtree() const { return tag == BTREE; }
 
   inline CordRepRing* ring();
   inline const CordRepRing* ring() const;
@@ -192,6 +265,8 @@
   inline const CordRepExternal* external() const;
   inline CordRepFlat* flat();
   inline const CordRepFlat* flat() const;
+  inline CordRepBtree* btree();
+  inline const CordRepBtree* btree() const;
 
   // --------------------------------------------------------------------
   // Memory management
@@ -212,8 +287,8 @@
   CordRep* left;
   CordRep* right;
 
-  uint8_t depth() const { return static_cast<uint8_t>(storage[0]); }
-  void set_depth(uint8_t depth) { storage[0] = static_cast<char>(depth); }
+  uint8_t depth() const { return storage[0]; }
+  void set_depth(uint8_t depth) { storage[0] = depth; }
 };
 
 struct CordRepSubstring : public CordRep {
@@ -231,7 +306,7 @@
 struct CordRepExternal : public CordRep {
   CordRepExternal() = default;
   explicit constexpr CordRepExternal(absl::string_view str)
-      : CordRep(Refcount::Immortal{}, str.size()),
+      : CordRep(RefcountAndFlags::Immortal{}, str.size()),
         base(str.data()),
         releaser_invoker(nullptr) {}
 
@@ -240,7 +315,7 @@
   ExternalReleaserInvoker releaser_invoker;
 
   // Deletes (releases) the external rep.
-  // Requires rep != nullptr and rep->tag == EXTERNAL
+  // Requires rep != nullptr and rep->IsExternal()
   static void Delete(CordRep* rep);
 };
 
@@ -283,7 +358,7 @@
 };
 
 inline void CordRepExternal::Delete(CordRep* rep) {
-  assert(rep != nullptr && rep->tag == EXTERNAL);
+  assert(rep != nullptr && rep->IsExternal());
   auto* rep_external = static_cast<CordRepExternal*>(rep);
   assert(rep_external->releaser_invoker != nullptr);
   rep_external->releaser_invoker(rep_external);
@@ -329,18 +404,17 @@
 
 class InlineData {
  public:
+  // DefaultInitType forces the use of the default initialization constructor.
+  enum DefaultInitType { kDefaultInit };
+
   // kNullCordzInfo holds the big endian representation of intptr_t(1)
   // This is the 'null' / initial value of 'cordz_info'. The null value
   // is specifically big endian 1 as with 64-bit pointers, the last
   // byte of cordz_info overlaps with the last byte holding the tag.
   static constexpr cordz_info_t kNullCordzInfo = BigEndianByte(1);
 
-  // kFakeCordzInfo holds a 'fake', non-null cordz-info value we use to
-  // emulate the previous 'kProfiled' tag logic in 'set_profiled' until
-  // cord code is changed to store cordz_info values in InlineData.
-  static constexpr cordz_info_t kFakeCordzInfo = BigEndianByte(9);
-
   constexpr InlineData() : as_chars_{0} {}
+  explicit InlineData(DefaultInitType) {}
   explicit constexpr InlineData(CordRep* rep) : as_tree_(rep) {}
   explicit constexpr InlineData(absl::string_view chars)
       : as_chars_{
@@ -367,6 +441,16 @@
     return as_tree_.cordz_info != kNullCordzInfo;
   }
 
+  // Returns true if either of the provided instances hold a cordz_info value.
+  // This method is more efficient than the equivalent `data1.is_profiled() ||
+  // data2.is_profiled()`. Requires both arguments to hold a tree.
+  static bool is_either_profiled(const InlineData& data1,
+                                 const InlineData& data2) {
+    assert(data1.is_tree() && data2.is_tree());
+    return (data1.as_tree_.cordz_info | data2.as_tree_.cordz_info) !=
+           kNullCordzInfo;
+  }
+
   // Returns the cordz_info sampling instance for this instance, or nullptr
   // if the current instance is not sampled and does not have CordzInfo data.
   // Requires the current instance to hold a tree value.
@@ -454,13 +538,6 @@
     tag() = static_cast<char>(size << 1);
   }
 
-  // Sets or unsets the 'is_profiled' state of this instance.
-  // Requires the current instance to hold a tree value.
-  void set_profiled(bool profiled) {
-    assert(is_tree());
-    as_tree_.cordz_info = profiled ? kFakeCordzInfo : kNullCordzInfo;
-  }
-
  private:
   // See cordz_info_t for forced alignment and size of `cordz_info` details.
   struct AsTree {
@@ -483,7 +560,7 @@
   // store the size in the last char of `as_chars_` shifted left + 1.
   // Else we store it in a tree and store a pointer to that tree in
   // `as_tree_.rep` and store a tag in `tagged_size`.
-  union  {
+  union {
     char as_chars_[kMaxInline + 1];
     AsTree as_tree_;
   };
@@ -492,32 +569,32 @@
 static_assert(sizeof(InlineData) == kMaxInline + 1, "");
 
 inline CordRepConcat* CordRep::concat() {
-  assert(tag == CONCAT);
+  assert(IsConcat());
   return static_cast<CordRepConcat*>(this);
 }
 
 inline const CordRepConcat* CordRep::concat() const {
-  assert(tag == CONCAT);
+  assert(IsConcat());
   return static_cast<const CordRepConcat*>(this);
 }
 
 inline CordRepSubstring* CordRep::substring() {
-  assert(tag == SUBSTRING);
+  assert(IsSubstring());
   return static_cast<CordRepSubstring*>(this);
 }
 
 inline const CordRepSubstring* CordRep::substring() const {
-  assert(tag == SUBSTRING);
+  assert(IsSubstring());
   return static_cast<const CordRepSubstring*>(this);
 }
 
 inline CordRepExternal* CordRep::external() {
-  assert(tag == EXTERNAL);
+  assert(IsExternal());
   return static_cast<CordRepExternal*>(this);
 }
 
 inline const CordRepExternal* CordRep::external() const {
-  assert(tag == EXTERNAL);
+  assert(IsExternal());
   return static_cast<const CordRepExternal*>(this);
 }
 
diff --git a/absl/strings/internal/cord_internal_test.cc b/absl/strings/internal/cord_internal_test.cc
new file mode 100644
index 0000000..0758dfe
--- /dev/null
+++ b/absl/strings/internal/cord_internal_test.cc
@@ -0,0 +1,116 @@
+// Copyright 2021 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/cord_internal.h"
+
+#include "gmock/gmock.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+TEST(RefcountAndFlags, NormalRefcount) {
+  for (bool expect_high_refcount : {false, true}) {
+    SCOPED_TRACE(expect_high_refcount);
+    RefcountAndFlags refcount;
+    // count = 1
+
+    EXPECT_FALSE(refcount.HasCrc());
+    EXPECT_TRUE(refcount.IsMutable());
+    EXPECT_TRUE(refcount.IsOne());
+
+    refcount.Increment();
+    // count = 2
+
+    EXPECT_FALSE(refcount.HasCrc());
+    EXPECT_FALSE(refcount.IsMutable());
+    EXPECT_FALSE(refcount.IsOne());
+
+    // Decrementing should return true, since a reference is outstanding.
+    if (expect_high_refcount) {
+      EXPECT_TRUE(refcount.DecrementExpectHighRefcount());
+    } else {
+      EXPECT_TRUE(refcount.Decrement());
+    }
+    // count = 1
+
+    EXPECT_FALSE(refcount.HasCrc());
+    EXPECT_TRUE(refcount.IsMutable());
+    EXPECT_TRUE(refcount.IsOne());
+
+    // One more decremnt will return false, as no references remain.
+    if (expect_high_refcount) {
+      EXPECT_FALSE(refcount.DecrementExpectHighRefcount());
+    } else {
+      EXPECT_FALSE(refcount.Decrement());
+    }
+  }
+}
+
+TEST(RefcountAndFlags, CrcRefcount) {
+  for (bool expect_high_refcount : {false, true}) {
+    SCOPED_TRACE(expect_high_refcount);
+    RefcountAndFlags refcount(RefcountAndFlags::WithCrc{});
+    // count = 1
+
+    // A CRC-carrying node is never mutable, but can be unshared
+    EXPECT_TRUE(refcount.HasCrc());
+    EXPECT_FALSE(refcount.IsMutable());
+    EXPECT_TRUE(refcount.IsOne());
+
+    refcount.Increment();
+    // count = 2
+
+    EXPECT_TRUE(refcount.HasCrc());
+    EXPECT_FALSE(refcount.IsMutable());
+    EXPECT_FALSE(refcount.IsOne());
+
+    // Decrementing should return true, since a reference is outstanding.
+    if (expect_high_refcount) {
+      EXPECT_TRUE(refcount.DecrementExpectHighRefcount());
+    } else {
+      EXPECT_TRUE(refcount.Decrement());
+    }
+    // count = 1
+
+    EXPECT_TRUE(refcount.HasCrc());
+    EXPECT_FALSE(refcount.IsMutable());
+    EXPECT_TRUE(refcount.IsOne());
+
+    // One more decremnt will return false, as no references remain.
+    if (expect_high_refcount) {
+      EXPECT_FALSE(refcount.DecrementExpectHighRefcount());
+    } else {
+      EXPECT_FALSE(refcount.Decrement());
+    }
+  }
+}
+
+TEST(RefcountAndFlags, ImmortalRefcount) {
+  RefcountAndFlags immortal_refcount(RefcountAndFlags::Immortal{});
+
+  for (int i = 0; i < 100; ++i) {
+    // An immortal refcount is never unshared, and decrementing never causes
+    // a collection.
+    EXPECT_FALSE(immortal_refcount.HasCrc());
+    EXPECT_FALSE(immortal_refcount.IsMutable());
+    EXPECT_FALSE(immortal_refcount.IsOne());
+    EXPECT_TRUE(immortal_refcount.Decrement());
+    EXPECT_TRUE(immortal_refcount.DecrementExpectHighRefcount());
+  }
+}
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc
new file mode 100644
index 0000000..4404f33
--- /dev/null
+++ b/absl/strings/internal/cord_rep_btree.cc
@@ -0,0 +1,1128 @@
+// Copyright 2021 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/cord_rep_btree.h"
+
+#include <cassert>
+#include <cstdint>
+#include <iostream>
+#include <string>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_consume.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+constexpr size_t CordRepBtree::kMaxCapacity;  // NOLINT: needed for c++ < c++17
+
+namespace {
+
+using NodeStack = CordRepBtree * [CordRepBtree::kMaxDepth];
+using EdgeType = CordRepBtree::EdgeType;
+using OpResult = CordRepBtree::OpResult;
+using CopyResult = CordRepBtree::CopyResult;
+
+constexpr auto kFront = CordRepBtree::kFront;
+constexpr auto kBack = CordRepBtree::kBack;
+
+inline bool exhaustive_validation() {
+  return cord_btree_exhaustive_validation.load(std::memory_order_relaxed);
+}
+
+// Implementation of the various 'Dump' functions.
+// Prints the entire tree structure or 'rep'. External callers should
+// not specify 'depth' and leave it to its default (0) value.
+// Rep may be a CordRepBtree tree, or a SUBSTRING / EXTERNAL / FLAT node.
+void DumpAll(const CordRep* rep, bool include_contents, std::ostream& stream,
+             int depth = 0) {
+  // Allow for full height trees + substring -> flat / external nodes.
+  assert(depth <= CordRepBtree::kMaxDepth + 2);
+  std::string sharing = const_cast<CordRep*>(rep)->refcount.IsOne()
+                            ? std::string("Private")
+                            : absl::StrCat("Shared(", rep->refcount.Get(), ")");
+  std::string sptr = absl::StrCat("0x", absl::Hex(rep));
+
+  // Dumps the data contents of `rep` if `include_contents` is true.
+  // Always emits a new line character.
+  auto maybe_dump_data = [&stream, include_contents](const CordRep* r) {
+    if (include_contents) {
+      // Allow for up to 60 wide display of content data, which with some
+      // indentation and prefix / labels keeps us within roughly 80-100 wide.
+      constexpr size_t kMaxDataLength = 60;
+      stream << ", data = \""
+             << CordRepBtree::EdgeData(r).substr(0, kMaxDataLength)
+             << (r->length > kMaxDataLength ? "\"..." : "\"");
+    }
+    stream << '\n';
+  };
+
+  // For each level, we print the 'shared/private' state and the rep pointer,
+  // indented by two spaces per recursive depth.
+  stream << std::string(depth * 2, ' ') << sharing << " (" << sptr << ") ";
+
+  if (rep->IsBtree()) {
+    const CordRepBtree* node = rep->btree();
+    std::string label =
+        node->height() ? absl::StrCat("Node(", node->height(), ")") : "Leaf";
+    stream << label << ", len = " << node->length
+           << ", begin = " << node->begin() << ", end = " << node->end()
+           << "\n";
+    for (CordRep* edge : node->Edges()) {
+      DumpAll(edge, include_contents, stream, depth + 1);
+    }
+  } else if (rep->tag == SUBSTRING) {
+    const CordRepSubstring* substring = rep->substring();
+    stream << "Substring, len = " << rep->length
+           << ", start = " << substring->start;
+    maybe_dump_data(rep);
+    DumpAll(substring->child, include_contents, stream, depth + 1);
+  } else if (rep->tag >= FLAT) {
+    stream << "Flat, len = " << rep->length
+           << ", cap = " << rep->flat()->Capacity();
+    maybe_dump_data(rep);
+  } else if (rep->tag == EXTERNAL) {
+    stream << "Extn, len = " << rep->length;
+    maybe_dump_data(rep);
+  }
+}
+
+// TODO(b/192061034): add 'bytes to copy' logic to avoid large slop on substring
+// small data out of large reps, and general efficiency of 'always copy small
+// data'. Consider making this a cord rep internal library function.
+CordRepSubstring* CreateSubstring(CordRep* rep, size_t offset, size_t n) {
+  assert(n != 0);
+  assert(offset + n <= rep->length);
+  assert(offset != 0 || n != rep->length);
+
+  if (rep->tag == SUBSTRING) {
+    CordRepSubstring* substring = rep->substring();
+    offset += substring->start;
+    rep = CordRep::Ref(substring->child);
+    CordRep::Unref(substring);
+  }
+  CordRepSubstring* substring = new CordRepSubstring();
+  substring->length = n;
+  substring->tag = SUBSTRING;
+  substring->start = offset;
+  substring->child = rep;
+  return substring;
+}
+
+// TODO(b/192061034): consider making this a cord rep library function.
+inline CordRep* MakeSubstring(CordRep* rep, size_t offset, size_t n) {
+  if (n == rep->length) return rep;
+  if (n == 0) return CordRep::Unref(rep), nullptr;
+  return CreateSubstring(rep, offset, n);
+}
+
+// TODO(b/192061034): consider making this a cord rep library function.
+inline CordRep* MakeSubstring(CordRep* rep, size_t offset) {
+  if (offset == 0) return rep;
+  return CreateSubstring(rep, offset, rep->length - offset);
+}
+
+// Resizes `edge` to the provided `length`. Adopts a reference on `edge`.
+// This method directly returns `edge` if `length` equals `edge->length`.
+// If `is_mutable` is set to true, this function may return `edge` with
+// `edge->length` set to the new length depending on the type and size of
+// `edge`. Otherwise, this function returns a new CordRepSubstring value.
+// Requires `length > 0 && length <= edge->length`.
+CordRep* ResizeEdge(CordRep* edge, size_t length, bool is_mutable) {
+  assert(length > 0);
+  assert(length <= edge->length);
+  assert(CordRepBtree::IsDataEdge(edge));
+  if (length >= edge->length) return edge;
+
+  if (is_mutable && (edge->tag >= FLAT || edge->tag == SUBSTRING)) {
+    edge->length = length;
+    return edge;
+  }
+
+  return CreateSubstring(edge, 0, length);
+}
+
+template <EdgeType edge_type>
+inline absl::string_view Consume(absl::string_view s, size_t n) {
+  return edge_type == kBack ? s.substr(n) : s.substr(0, s.size() - n);
+}
+
+template <EdgeType edge_type>
+inline absl::string_view Consume(char* dst, absl::string_view s, size_t n) {
+  if (edge_type == kBack) {
+    memcpy(dst, s.data(), n);
+    return s.substr(n);
+  } else {
+    const size_t offset = s.size() - n;
+    memcpy(dst, s.data() + offset, n);
+    return s.substr(0, offset);
+  }
+}
+
+// Known issue / optimization weirdness: the store associated with the
+// decrement introduces traffic between cpus (even if the result of that
+// traffic does nothing), making this faster than a single call to
+// refcount.Decrement() checking the zero refcount condition.
+template <typename R, typename Fn>
+inline void FastUnref(R* r, Fn&& fn) {
+  if (r->refcount.IsOne()) {
+    fn(r);
+  } else if (!r->refcount.DecrementExpectHighRefcount()) {
+    fn(r);
+  }
+}
+
+// Deletes a leaf node data edge. Requires `rep` to be an EXTERNAL or FLAT
+// node, or a SUBSTRING of an EXTERNAL or FLAT node.
+void DeleteLeafEdge(CordRep* rep) {
+  for (;;) {
+    if (rep->tag >= FLAT) {
+      CordRepFlat::Delete(rep->flat());
+      return;
+    }
+    if (rep->tag == EXTERNAL) {
+      CordRepExternal::Delete(rep->external());
+      return;
+    }
+    assert(rep->tag == SUBSTRING);
+    CordRepSubstring* substring = rep->substring();
+    rep = substring->child;
+    assert(rep->tag == EXTERNAL || rep->tag >= FLAT);
+    delete substring;
+    if (rep->refcount.Decrement()) return;
+  }
+}
+
+// StackOperations contains the logic to build a left-most or right-most stack
+// (leg) down to the leaf level of a btree, and 'unwind' / 'Finalize' methods to
+// propagate node changes up the stack.
+template <EdgeType edge_type>
+struct StackOperations {
+  // Returns true if the node at 'depth' is mutable, i.e. has a refcount
+  // of one, carries no CRC, and all of its parent nodes have a refcount of one.
+  inline bool owned(int depth) const { return depth < share_depth; }
+
+  // Returns the node at 'depth'.
+  inline CordRepBtree* node(int depth) const { return stack[depth]; }
+
+  // Builds a `depth` levels deep stack starting at `tree` recording which nodes
+  // are private in the form of the 'share depth' where nodes are shared.
+  inline CordRepBtree* BuildStack(CordRepBtree* tree, int depth) {
+    assert(depth <= tree->height());
+    int current_depth = 0;
+    while (current_depth < depth && tree->refcount.IsMutable()) {
+      stack[current_depth++] = tree;
+      tree = tree->Edge(edge_type)->btree();
+    }
+    share_depth = current_depth + (tree->refcount.IsMutable() ? 1 : 0);
+    while (current_depth < depth) {
+      stack[current_depth++] = tree;
+      tree = tree->Edge(edge_type)->btree();
+    }
+    return tree;
+  }
+
+  // Builds a stack with the invariant that all nodes are private owned / not
+  // shared and carry no CRC data. This is used in iterative updates where a
+  // previous propagation guaranteed all nodes have this property.
+  inline void BuildOwnedStack(CordRepBtree* tree, int height) {
+    assert(height <= CordRepBtree::kMaxHeight);
+    int depth = 0;
+    while (depth < height) {
+      assert(tree->refcount.IsMutable());
+      stack[depth++] = tree;
+      tree = tree->Edge(edge_type)->btree();
+    }
+    assert(tree->refcount.IsMutable());
+    share_depth = depth + 1;
+  }
+
+  // Processes the final 'top level' result action for the tree.
+  // See the 'Action' enum for the various action implications.
+  static inline CordRepBtree* Finalize(CordRepBtree* tree, OpResult result) {
+    switch (result.action) {
+      case CordRepBtree::kPopped:
+        tree = edge_type == kBack ? CordRepBtree::New(tree, result.tree)
+                                  : CordRepBtree::New(result.tree, tree);
+        if (ABSL_PREDICT_FALSE(tree->height() > CordRepBtree::kMaxHeight)) {
+          tree = CordRepBtree::Rebuild(tree);
+          ABSL_RAW_CHECK(tree->height() <= CordRepBtree::kMaxHeight,
+                         "Max height exceeded");
+        }
+        return tree;
+      case CordRepBtree::kCopied:
+        CordRep::Unref(tree);
+        ABSL_FALLTHROUGH_INTENDED;
+      case CordRepBtree::kSelf:
+        return result.tree;
+    }
+    ABSL_INTERNAL_UNREACHABLE;
+    return result.tree;
+  }
+
+  // Propagate the action result in 'result' up into all nodes of the stack
+  // starting at depth 'depth'. 'length' contains the extra length of data that
+  // was added at the lowest level, and is updated into all nodes of the stack.
+  // See the 'Action' enum for the various action implications.
+  // If 'propagate' is true, then any copied node values are updated into the
+  // stack, which is used for iterative processing on the same stack.
+  template <bool propagate = false>
+  inline CordRepBtree* Unwind(CordRepBtree* tree, int depth, size_t length,
+                              OpResult result) {
+    // TODO(mvels): revisit the below code to check if 3 loops with 3
+    // (incremental) conditions is faster than 1 loop with a switch.
+    // Benchmarking and perf recordings indicate the loop with switch is
+    // fastest, likely because of indirect jumps on the tight case values and
+    // dense branches. But it's worth considering 3 loops, as the `action`
+    // transitions are mono directional. E.g.:
+    //   while (action == kPopped) {
+    //     ...
+    //   }
+    //   while (action == kCopied) {
+    //     ...
+    //   }
+    //   ...
+    // We also  found that an "if () do {}" loop here seems faster, possibly
+    // because it allows the branch predictor more granular heuristics on
+    // 'single leaf' (`depth` == 0) and 'single depth' (`depth` == 1) cases
+    // which appear to be the most common use cases.
+    if (depth != 0) {
+      do {
+        CordRepBtree* node = stack[--depth];
+        const bool owned = depth < share_depth;
+        switch (result.action) {
+          case CordRepBtree::kPopped:
+            assert(!propagate);
+            result = node->AddEdge<edge_type>(owned, result.tree, length);
+            break;
+          case CordRepBtree::kCopied:
+            result = node->SetEdge<edge_type>(owned, result.tree, length);
+            if (propagate) stack[depth] = result.tree;
+            break;
+          case CordRepBtree::kSelf:
+            node->length += length;
+            while (depth > 0) {
+              node = stack[--depth];
+              node->length += length;
+            }
+            return node;
+        }
+      } while (depth > 0);
+    }
+    return Finalize(tree, result);
+  }
+
+  // Invokes `Unwind` with `propagate=true` to update the stack node values.
+  inline CordRepBtree* Propagate(CordRepBtree* tree, int depth, size_t length,
+                                 OpResult result) {
+    return Unwind</*propagate=*/true>(tree, depth, length, result);
+  }
+
+  // `share_depth` contains the depth at which the nodes in the stack cannot
+  // be mutated. I.e., if the top most level is shared (i.e.:
+  // `!refcount.IsMutable()`), then `share_depth` is 0. If the 2nd node
+  // is shared (and implicitly all nodes below that) then `share_depth` is 1,
+  // etc. A `share_depth` greater than the depth of the stack indicates that
+  // none of the nodes in the stack are shared.
+  int share_depth;
+
+  NodeStack stack;
+};
+
+}  // namespace
+
+void CordRepBtree::Dump(const CordRep* rep, absl::string_view label,
+                        bool include_contents, std::ostream& stream) {
+  stream << "===================================\n";
+  if (!label.empty()) {
+    stream << label << '\n';
+    stream << "-----------------------------------\n";
+  }
+  if (rep) {
+    DumpAll(rep, include_contents, stream);
+  } else {
+    stream << "NULL\n";
+  }
+}
+
+void CordRepBtree::Dump(const CordRep* rep, absl::string_view label,
+                        std::ostream& stream) {
+  Dump(rep, label, false, stream);
+}
+
+void CordRepBtree::Dump(const CordRep* rep, std::ostream& stream) {
+  Dump(rep, absl::string_view(), false, stream);
+}
+
+void CordRepBtree::DestroyLeaf(CordRepBtree* tree, size_t begin, size_t end) {
+  for (CordRep* edge : tree->Edges(begin, end)) {
+    FastUnref(edge, DeleteLeafEdge);
+  }
+  Delete(tree);
+}
+
+void CordRepBtree::DestroyNonLeaf(CordRepBtree* tree, size_t begin,
+                                  size_t end) {
+  for (CordRep* edge : tree->Edges(begin, end)) {
+    FastUnref(edge->btree(), Destroy);
+  }
+  Delete(tree);
+}
+
+bool CordRepBtree::IsValid(const CordRepBtree* tree, bool shallow) {
+#define NODE_CHECK_VALID(x)                                           \
+  if (!(x)) {                                                         \
+    ABSL_RAW_LOG(ERROR, "CordRepBtree::CheckValid() FAILED: %s", #x); \
+    return false;                                                     \
+  }
+#define NODE_CHECK_EQ(x, y)                                                    \
+  if ((x) != (y)) {                                                            \
+    ABSL_RAW_LOG(ERROR,                                                        \
+                 "CordRepBtree::CheckValid() FAILED: %s != %s (%s vs %s)", #x, \
+                 #y, absl::StrCat(x).c_str(), absl::StrCat(y).c_str());        \
+    return false;                                                              \
+  }
+
+  NODE_CHECK_VALID(tree != nullptr);
+  NODE_CHECK_VALID(tree->IsBtree());
+  NODE_CHECK_VALID(tree->height() <= kMaxHeight);
+  NODE_CHECK_VALID(tree->begin() < tree->capacity());
+  NODE_CHECK_VALID(tree->end() <= tree->capacity());
+  NODE_CHECK_VALID(tree->begin() <= tree->end());
+  size_t child_length = 0;
+  for (CordRep* edge : tree->Edges()) {
+    NODE_CHECK_VALID(edge != nullptr);
+    if (tree->height() > 0) {
+      NODE_CHECK_VALID(edge->IsBtree());
+      NODE_CHECK_VALID(edge->btree()->height() == tree->height() - 1);
+    } else {
+      NODE_CHECK_VALID(IsDataEdge(edge));
+    }
+    child_length += edge->length;
+  }
+  NODE_CHECK_EQ(child_length, tree->length);
+  if ((!shallow || exhaustive_validation()) && tree->height() > 0) {
+    for (CordRep* edge : tree->Edges()) {
+      if (!IsValid(edge->btree(), shallow)) return false;
+    }
+  }
+  return true;
+
+#undef NODE_CHECK_VALID
+#undef NODE_CHECK_EQ
+}
+
+#ifndef NDEBUG
+
+CordRepBtree* CordRepBtree::AssertValid(CordRepBtree* tree, bool shallow) {
+  if (!IsValid(tree, shallow)) {
+    Dump(tree, "CordRepBtree validation failed:", false, std::cout);
+    ABSL_RAW_LOG(FATAL, "CordRepBtree::CheckValid() FAILED");
+  }
+  return tree;
+}
+
+const CordRepBtree* CordRepBtree::AssertValid(const CordRepBtree* tree,
+                                              bool shallow) {
+  if (!IsValid(tree, shallow)) {
+    Dump(tree, "CordRepBtree validation failed:", false, std::cout);
+    ABSL_RAW_LOG(FATAL, "CordRepBtree::CheckValid() FAILED");
+  }
+  return tree;
+}
+
+#endif  // NDEBUG
+
+template <EdgeType edge_type>
+inline OpResult CordRepBtree::AddEdge(bool owned, CordRep* edge, size_t delta) {
+  if (size() >= kMaxCapacity) return {New(edge), kPopped};
+  OpResult result = ToOpResult(owned);
+  result.tree->Add<edge_type>(edge);
+  result.tree->length += delta;
+  return result;
+}
+
+template <EdgeType edge_type>
+OpResult CordRepBtree::SetEdge(bool owned, CordRep* edge, size_t delta) {
+  OpResult result;
+  const size_t idx = index(edge_type);
+  if (owned) {
+    result = {this, kSelf};
+    CordRep::Unref(edges_[idx]);
+  } else {
+    // Create a copy containing all unchanged edges. Unchanged edges are the
+    // open interval [begin, back) or [begin + 1, end) depending on `edge_type`.
+    // We conveniently cover both case using a constexpr `shift` being 0 or 1
+    // as `end :== back + 1`.
+    result = {CopyRaw(), kCopied};
+    constexpr int shift = edge_type == kFront ? 1 : 0;
+    for (CordRep* r : Edges(begin() + shift, back() + shift)) {
+      CordRep::Ref(r);
+    }
+  }
+  result.tree->edges_[idx] = edge;
+  result.tree->length += delta;
+  return result;
+}
+
+template <EdgeType edge_type>
+CordRepBtree* CordRepBtree::AddCordRep(CordRepBtree* tree, CordRep* rep) {
+  const int depth = tree->height();
+  const size_t length = rep->length;
+  StackOperations<edge_type> ops;
+  CordRepBtree* leaf = ops.BuildStack(tree, depth);
+  const OpResult result =
+      leaf->AddEdge<edge_type>(ops.owned(depth), rep, length);
+  return ops.Unwind(tree, depth, length, result);
+}
+
+template <>
+CordRepBtree* CordRepBtree::NewLeaf<kBack>(absl::string_view data,
+                                           size_t extra) {
+  CordRepBtree* leaf = CordRepBtree::New(0);
+  size_t length = 0;
+  size_t end = 0;
+  const size_t cap = leaf->capacity();
+  while (!data.empty() && end != cap) {
+    auto* flat = CordRepFlat::New(data.length() + extra);
+    flat->length = (std::min)(data.length(), flat->Capacity());
+    length += flat->length;
+    leaf->edges_[end++] = flat;
+    data = Consume<kBack>(flat->Data(), data, flat->length);
+  }
+  leaf->length = length;
+  leaf->set_end(end);
+  return leaf;
+}
+
+template <>
+CordRepBtree* CordRepBtree::NewLeaf<kFront>(absl::string_view data,
+                                            size_t extra) {
+  CordRepBtree* leaf = CordRepBtree::New(0);
+  size_t length = 0;
+  size_t begin = leaf->capacity();
+  leaf->set_end(leaf->capacity());
+  while (!data.empty() && begin != 0) {
+    auto* flat = CordRepFlat::New(data.length() + extra);
+    flat->length = (std::min)(data.length(), flat->Capacity());
+    length += flat->length;
+    leaf->edges_[--begin] = flat;
+    data = Consume<kFront>(flat->Data(), data, flat->length);
+  }
+  leaf->length = length;
+  leaf->set_begin(begin);
+  return leaf;
+}
+
+template <>
+absl::string_view CordRepBtree::AddData<kBack>(absl::string_view data,
+                                               size_t extra) {
+  assert(!data.empty());
+  assert(size() < capacity());
+  AlignBegin();
+  const size_t cap = capacity();
+  do {
+    CordRepFlat* flat = CordRepFlat::New(data.length() + extra);
+    const size_t n = (std::min)(data.length(), flat->Capacity());
+    flat->length = n;
+    edges_[fetch_add_end(1)] = flat;
+    data = Consume<kBack>(flat->Data(), data, n);
+  } while (!data.empty() && end() != cap);
+  return data;
+}
+
+template <>
+absl::string_view CordRepBtree::AddData<kFront>(absl::string_view data,
+                                                size_t extra) {
+  assert(!data.empty());
+  assert(size() < capacity());
+  AlignEnd();
+  do {
+    CordRepFlat* flat = CordRepFlat::New(data.length() + extra);
+    const size_t n = (std::min)(data.length(), flat->Capacity());
+    flat->length = n;
+    edges_[sub_fetch_begin(1)] = flat;
+    data = Consume<kFront>(flat->Data(), data, n);
+  } while (!data.empty() && begin() != 0);
+  return data;
+}
+
+template <EdgeType edge_type>
+CordRepBtree* CordRepBtree::AddData(CordRepBtree* tree, absl::string_view data,
+                                    size_t extra) {
+  if (ABSL_PREDICT_FALSE(data.empty())) return tree;
+
+  const size_t original_data_size = data.size();
+  int depth = tree->height();
+  StackOperations<edge_type> ops;
+  CordRepBtree* leaf = ops.BuildStack(tree, depth);
+
+  // If there is capacity in the last edge, append as much data
+  // as possible into this last edge.
+  if (leaf->size() < leaf->capacity()) {
+    OpResult result = leaf->ToOpResult(ops.owned(depth));
+    data = result.tree->AddData<edge_type>(data, extra);
+    if (data.empty()) {
+      result.tree->length += original_data_size;
+      return ops.Unwind(tree, depth, original_data_size, result);
+    }
+
+    // We added some data into this leaf, but not all. Propagate the added
+    // length to the top most node, and rebuild the stack with any newly copied
+    // or updated nodes. From this point on, the path (leg) from the top most
+    // node to the right-most node towards the leaf node is privately owned.
+    size_t delta = original_data_size - data.size();
+    assert(delta > 0);
+    result.tree->length += delta;
+    tree = ops.Propagate(tree, depth, delta, result);
+    ops.share_depth = depth + 1;
+  }
+
+  // We were unable to append all data into the existing right-most leaf node.
+  // This means all remaining data must be put into (a) new leaf node(s) which
+  // we append to the tree. To make this efficient, we iteratively build full
+  // leaf nodes from `data` until the created leaf contains all remaining data.
+  // We utilize the `Unwind` method to merge the created leaf into the first
+  // level towards root that has capacity. On each iteration with remaining
+  // data, we rebuild the stack in the knowledge that right-most nodes are
+  // privately owned after the first `Unwind` completes.
+  for (;;) {
+    OpResult result = {CordRepBtree::NewLeaf<edge_type>(data, extra), kPopped};
+    if (result.tree->length == data.size()) {
+      return ops.Unwind(tree, depth, result.tree->length, result);
+    }
+    data = Consume<edge_type>(data, result.tree->length);
+    tree = ops.Unwind(tree, depth, result.tree->length, result);
+    depth = tree->height();
+    ops.BuildOwnedStack(tree, depth);
+  }
+}
+
+template <EdgeType edge_type>
+CordRepBtree* CordRepBtree::Merge(CordRepBtree* dst, CordRepBtree* src) {
+  assert(dst->height() >= src->height());
+
+  // Capture source length as we may consume / destroy `src`.
+  const size_t length = src->length;
+
+  // We attempt to merge `src` at its corresponding height in `dst`.
+  const int depth = dst->height() - src->height();
+  StackOperations<edge_type> ops;
+  CordRepBtree* merge_node = ops.BuildStack(dst, depth);
+
+  // If there is enough space in `merge_node` for all edges from `src`, add all
+  // edges to this node, making a fresh copy as needed if not privately owned.
+  // If `merge_node` does not have capacity for `src`, we rely on `Unwind` and
+  // `Finalize` to merge `src` into the first level towards `root` where there
+  // is capacity for another edge, or create a new top level node.
+  OpResult result;
+  if (merge_node->size() + src->size() <= kMaxCapacity) {
+    result = merge_node->ToOpResult(ops.owned(depth));
+    result.tree->Add<edge_type>(src->Edges());
+    result.tree->length += src->length;
+    if (src->refcount.IsOne()) {
+      Delete(src);
+    } else {
+      for (CordRep* edge : src->Edges()) CordRep::Ref(edge);
+      CordRepBtree::Unref(src);
+    }
+  } else {
+    result = {src, kPopped};
+  }
+
+  // Unless we merged at the top level (i.e.: src and dst are equal height),
+  // unwind the result towards the top level, and finalize the result.
+  if (depth) {
+    return ops.Unwind(dst, depth, length, result);
+  }
+  return ops.Finalize(dst, result);
+}
+
+CopyResult CordRepBtree::CopySuffix(size_t offset) {
+  assert(offset < this->length);
+
+  // As long as `offset` starts inside the last edge, we can 'drop' the current
+  // depth. For the most extreme example: if offset references the last data
+  // edge in the tree, there is only a single edge / path from the top of the
+  // tree to that last edge, so we can drop all the nodes except that edge.
+  // The fast path check for this is `back->length >= length - offset`.
+  int height = this->height();
+  CordRepBtree* node = this;
+  size_t len = node->length - offset;
+  CordRep* back = node->Edge(kBack);
+  while (back->length >= len) {
+    offset = back->length - len;
+    if (--height < 0) {
+      return {MakeSubstring(CordRep::Ref(back), offset), height};
+    }
+    node = back->btree();
+    back = node->Edge(kBack);
+  }
+  if (offset == 0) return {CordRep::Ref(node), height};
+
+  // Offset does not point into the last edge, so we span at least two edges.
+  // Find the index of offset with `IndexBeyond` which provides us the edge
+  // 'beyond' the offset if offset is not a clean starting point of an edge.
+  Position pos = node->IndexBeyond(offset);
+  CordRepBtree* sub = node->CopyToEndFrom(pos.index, len);
+  const CopyResult result = {sub, height};
+
+  // `pos.n` contains a non zero value if the offset is not an exact starting
+  // point of an edge. In this case, `pos.n` contains the 'trailing' amount of
+  // bytes of the edge preceding that in `pos.index`. We need to iteratively
+  // adjust the preceding edge with the 'broken' offset until we have a perfect
+  // start of the edge.
+  while (pos.n != 0) {
+    assert(pos.index >= 1);
+    const size_t begin = pos.index - 1;
+    sub->set_begin(begin);
+    CordRep* const edge = node->Edge(begin);
+
+    len = pos.n;
+    offset = edge->length - len;
+
+    if (--height < 0) {
+      sub->edges_[begin] = MakeSubstring(CordRep::Ref(edge), offset, len);
+      return result;
+    }
+
+    node = edge->btree();
+    pos = node->IndexBeyond(offset);
+
+    CordRepBtree* nsub = node->CopyToEndFrom(pos.index, len);
+    sub->edges_[begin] = nsub;
+    sub = nsub;
+  }
+  sub->set_begin(pos.index);
+  return result;
+}
+
+CopyResult CordRepBtree::CopyPrefix(size_t n, bool allow_folding) {
+  assert(n > 0);
+  assert(n <= this->length);
+
+  // As long as `n` does not exceed the length of the first edge, we can 'drop'
+  // the current depth. For the most extreme example: if we'd copy a 1 byte
+  // prefix from a tree, there is only a single edge / path from the top of the
+  // tree to the single data edge containing this byte, so we can drop all the
+  // nodes except the data node.
+  int height = this->height();
+  CordRepBtree* node = this;
+  CordRep* front = node->Edge(kFront);
+  if (allow_folding) {
+    while (front->length >= n) {
+      if (--height < 0) return {MakeSubstring(CordRep::Ref(front), 0, n), -1};
+      node = front->btree();
+      front = node->Edge(kFront);
+    }
+  }
+  if (node->length == n) return {CordRep::Ref(node), height};
+
+  // `n` spans at least two nodes, find the end point of the span.
+  Position pos = node->IndexOf(n);
+
+  // Create a partial copy of the node up to `pos.index`, with a defined length
+  // of `n`. Any 'partial last edge' is added further below as needed.
+  CordRepBtree* sub = node->CopyBeginTo(pos.index, n);
+  const CopyResult result = {sub, height};
+
+  // `pos.n` contains the 'offset inside the edge for IndexOf(n)'. As long as
+  // this is not zero, we don't have a 'clean cut', so we need to make a
+  // (partial) copy of that last edge, and repeat this until pos.n is zero.
+  while (pos.n != 0) {
+    size_t end = pos.index;
+    n = pos.n;
+
+    CordRep* edge = node->Edge(pos.index);
+    if (--height < 0) {
+      sub->edges_[end++] = MakeSubstring(CordRep::Ref(edge), 0, n);
+      sub->set_end(end);
+      AssertValid(result.edge->btree());
+      return result;
+    }
+
+    node = edge->btree();
+    pos = node->IndexOf(n);
+    CordRepBtree* nsub = node->CopyBeginTo(pos.index, n);
+    sub->edges_[end++] = nsub;
+    sub->set_end(end);
+    sub = nsub;
+  }
+  sub->set_end(pos.index);
+  AssertValid(result.edge->btree());
+  return result;
+}
+
+CordRep* CordRepBtree::ExtractFront(CordRepBtree* tree) {
+  CordRep* front = tree->Edge(tree->begin());
+  if (tree->refcount.IsMutable()) {
+    Unref(tree->Edges(tree->begin() + 1, tree->end()));
+    CordRepBtree::Delete(tree);
+  } else {
+    CordRep::Ref(front);
+    CordRep::Unref(tree);
+  }
+  return front;
+}
+
+CordRepBtree* CordRepBtree::ConsumeBeginTo(CordRepBtree* tree, size_t end,
+                                           size_t new_length) {
+  assert(end <= tree->end());
+  if (tree->refcount.IsMutable()) {
+    Unref(tree->Edges(end, tree->end()));
+    tree->set_end(end);
+    tree->length = new_length;
+  } else {
+    CordRepBtree* old = tree;
+    tree = tree->CopyBeginTo(end, new_length);
+    CordRep::Unref(old);
+  }
+  return tree;
+}
+
+CordRep* CordRepBtree::RemoveSuffix(CordRepBtree* tree, size_t n) {
+  // Check input and deal with trivial cases 'Remove all/none'
+  assert(tree != nullptr);
+  assert(n <= tree->length);
+  const size_t len = tree->length;
+  if (ABSL_PREDICT_FALSE(n == 0)) {
+    return tree;
+  }
+  if (ABSL_PREDICT_FALSE(n >= len)) {
+    CordRepBtree::Unref(tree);
+    return nullptr;
+  }
+
+  size_t length = len - n;
+  int height = tree->height();
+  bool is_mutable = tree->refcount.IsMutable();
+
+  // Extract all top nodes which are reduced to size = 1
+  Position pos = tree->IndexOfLength(length);
+  while (pos.index == tree->begin()) {
+    CordRep* edge = ExtractFront(tree);
+    is_mutable &= edge->refcount.IsMutable();
+    if (height-- == 0) return ResizeEdge(edge, length, is_mutable);
+    tree = edge->btree();
+    pos = tree->IndexOfLength(length);
+  }
+
+  // Repeat the following sequence traversing down the tree:
+  // - Crop the top node to the 'last remaining edge' adjusting length.
+  // - Set the length for down edges to the partial length in that last edge.
+  // - Repeat this until the last edge is 'included in full'
+  // - If we hit the data edge level, resize and return the last data edge
+  CordRepBtree* top = tree = ConsumeBeginTo(tree, pos.index + 1, length);
+  CordRep* edge = tree->Edge(pos.index);
+  length = pos.n;
+  while (length != edge->length) {
+    // ConsumeBeginTo guarantees `tree` is a clean, privately owned copy.
+    assert(tree->refcount.IsMutable());
+    const bool edge_is_mutable = edge->refcount.IsMutable();
+
+    if (height-- == 0) {
+      tree->edges_[pos.index] = ResizeEdge(edge, length, edge_is_mutable);
+      return AssertValid(top);
+    }
+
+    if (!edge_is_mutable) {
+      // We can't 'in place' remove any suffixes down this edge.
+      // Replace this edge with a prefix copy instead.
+      tree->edges_[pos.index] = edge->btree()->CopyPrefix(length, false).edge;
+      CordRep::Unref(edge);
+      return AssertValid(top);
+    }
+
+    // Move down one level, rinse repeat.
+    tree = edge->btree();
+    pos = tree->IndexOfLength(length);
+    tree = ConsumeBeginTo(edge->btree(), pos.index + 1, length);
+    edge = tree->Edge(pos.index);
+    length = pos.n;
+  }
+
+  return AssertValid(top);
+}
+
+CordRep* CordRepBtree::SubTree(size_t offset, size_t n) {
+  assert(n <= this->length);
+  assert(offset <= this->length - n);
+  if (ABSL_PREDICT_FALSE(n == 0)) return nullptr;
+
+  CordRepBtree* node = this;
+  int height = node->height();
+  Position front = node->IndexOf(offset);
+  CordRep* left = node->edges_[front.index];
+  while (front.n + n <= left->length) {
+    if (--height < 0) return MakeSubstring(CordRep::Ref(left), front.n, n);
+    node = left->btree();
+    front = node->IndexOf(front.n);
+    left = node->edges_[front.index];
+  }
+
+  const Position back = node->IndexBefore(front, n);
+  CordRep* const right = node->edges_[back.index];
+  assert(back.index > front.index);
+
+  // Get partial suffix and prefix entries.
+  CopyResult prefix;
+  CopyResult suffix;
+  if (height > 0) {
+    // Copy prefix and suffix of the boundary nodes.
+    prefix = left->btree()->CopySuffix(front.n);
+    suffix = right->btree()->CopyPrefix(back.n);
+
+    // If there is an edge between the prefix and suffix edges, then the tree
+    // must remain at its previous (full) height. If we have no edges between
+    // prefix and suffix edges, then the tree must be as high as either the
+    // suffix or prefix edges (which are collapsed to their minimum heights).
+    if (front.index + 1 == back.index) {
+      height = (std::max)(prefix.height, suffix.height) + 1;
+    }
+
+    // Raise prefix and suffixes to the new tree height.
+    for (int h = prefix.height + 1; h < height; ++h) {
+      prefix.edge = CordRepBtree::New(prefix.edge);
+    }
+    for (int h = suffix.height + 1; h < height; ++h) {
+      suffix.edge = CordRepBtree::New(suffix.edge);
+    }
+  } else {
+    // Leaf node, simply take substrings for prefix and suffix.
+    prefix = CopyResult{MakeSubstring(CordRep::Ref(left), front.n), -1};
+    suffix = CopyResult{MakeSubstring(CordRep::Ref(right), 0, back.n), -1};
+  }
+
+  // Compose resulting tree.
+  CordRepBtree* sub = CordRepBtree::New(height);
+  size_t end = 0;
+  sub->edges_[end++] = prefix.edge;
+  for (CordRep* r : node->Edges(front.index + 1, back.index)) {
+    sub->edges_[end++] = CordRep::Ref(r);
+  }
+  sub->edges_[end++] = suffix.edge;
+  sub->set_end(end);
+  sub->length = n;
+  return AssertValid(sub);
+}
+
+CordRepBtree* CordRepBtree::MergeTrees(CordRepBtree* left,
+                                       CordRepBtree* right) {
+  return left->height() >= right->height() ? Merge<kBack>(left, right)
+                                           : Merge<kFront>(right, left);
+}
+
+bool CordRepBtree::IsFlat(absl::string_view* fragment) const {
+  if (height() == 0 && size() == 1) {
+    if (fragment) *fragment = Data(begin());
+    return true;
+  }
+  return false;
+}
+
+bool CordRepBtree::IsFlat(size_t offset, const size_t n,
+                          absl::string_view* fragment) const {
+  assert(n <= this->length);
+  assert(offset <= this->length - n);
+  if (ABSL_PREDICT_FALSE(n == 0)) return false;
+  int height = this->height();
+  const CordRepBtree* node = this;
+  for (;;) {
+    const Position front = node->IndexOf(offset);
+    const CordRep* edge = node->Edge(front.index);
+    if (edge->length < front.n + n) return false;
+    if (--height < 0) {
+      if (fragment) *fragment = EdgeData(edge).substr(front.n, n);
+      return true;
+    }
+    offset = front.n;
+    node = node->Edge(front.index)->btree();
+  }
+}
+
+char CordRepBtree::GetCharacter(size_t offset) const {
+  assert(offset < length);
+  const CordRepBtree* node = this;
+  int height = node->height();
+  for (;;) {
+    Position front = node->IndexOf(offset);
+    if (--height < 0) return node->Data(front.index)[front.n];
+    offset = front.n;
+    node = node->Edge(front.index)->btree();
+  }
+}
+
+Span<char> CordRepBtree::GetAppendBufferSlow(size_t size) {
+  // The inlined version in `GetAppendBuffer()` deals with all heights <= 3.
+  assert(height() >= 4);
+  assert(refcount.IsMutable());
+
+  // Build a stack of nodes we may potentially need to update if we find a
+  // non-shared FLAT with capacity at the leaf level.
+  const int depth = height();
+  CordRepBtree* node = this;
+  CordRepBtree* stack[kMaxDepth];
+  for (int i = 0; i < depth; ++i) {
+    node = node->Edge(kBack)->btree();
+    if (!node->refcount.IsMutable()) return {};
+    stack[i] = node;
+  }
+
+  // Must be a privately owned, mutable flat.
+  CordRep* const edge = node->Edge(kBack);
+  if (!edge->refcount.IsMutable() || edge->tag < FLAT) return {};
+
+  // Must have capacity.
+  const size_t avail = edge->flat()->Capacity() - edge->length;
+  if (avail == 0) return {};
+
+  // Build span on remaining capacity.
+  size_t delta = (std::min)(size, avail);
+  Span<char> span = {edge->flat()->Data() + edge->length, delta};
+  edge->length += delta;
+  this->length += delta;
+  for (int i = 0; i < depth; ++i) {
+    stack[i]->length += delta;
+  }
+  return span;
+}
+
+CordRepBtree* CordRepBtree::CreateSlow(CordRep* rep) {
+  if (rep->IsBtree()) return rep->btree();
+
+  CordRepBtree* node = nullptr;
+  auto consume = [&node](CordRep* r, size_t offset, size_t length) {
+    r = MakeSubstring(r, offset, length);
+    if (node == nullptr) {
+      node = New(r);
+    } else {
+      node = CordRepBtree::AddCordRep<kBack>(node, r);
+    }
+  };
+  Consume(rep, consume);
+  return node;
+}
+
+CordRepBtree* CordRepBtree::AppendSlow(CordRepBtree* tree, CordRep* rep) {
+  if (ABSL_PREDICT_TRUE(rep->IsBtree())) {
+    return MergeTrees(tree, rep->btree());
+  }
+  auto consume = [&tree](CordRep* r, size_t offset, size_t length) {
+    r = MakeSubstring(r, offset, length);
+    tree = CordRepBtree::AddCordRep<kBack>(tree, r);
+  };
+  Consume(rep, consume);
+  return tree;
+}
+
+CordRepBtree* CordRepBtree::PrependSlow(CordRepBtree* tree, CordRep* rep) {
+  if (ABSL_PREDICT_TRUE(rep->IsBtree())) {
+    return MergeTrees(rep->btree(), tree);
+  }
+  auto consume = [&tree](CordRep* r, size_t offset, size_t length) {
+    r = MakeSubstring(r, offset, length);
+    tree = CordRepBtree::AddCordRep<kFront>(tree, r);
+  };
+  ReverseConsume(rep, consume);
+  return tree;
+}
+
+CordRepBtree* CordRepBtree::Append(CordRepBtree* tree, absl::string_view data,
+                                   size_t extra) {
+  return CordRepBtree::AddData<kBack>(tree, data, extra);
+}
+
+CordRepBtree* CordRepBtree::Prepend(CordRepBtree* tree, absl::string_view data,
+                                    size_t extra) {
+  return CordRepBtree::AddData<kFront>(tree, data, extra);
+}
+
+template CordRepBtree* CordRepBtree::AddCordRep<kFront>(CordRepBtree* tree,
+                                                        CordRep* rep);
+template CordRepBtree* CordRepBtree::AddCordRep<kBack>(CordRepBtree* tree,
+                                                       CordRep* rep);
+template CordRepBtree* CordRepBtree::AddData<kFront>(CordRepBtree* tree,
+                                                     absl::string_view data,
+                                                     size_t extra);
+template CordRepBtree* CordRepBtree::AddData<kBack>(CordRepBtree* tree,
+                                                    absl::string_view data,
+                                                    size_t extra);
+
+void CordRepBtree::Rebuild(CordRepBtree** stack, CordRepBtree* tree,
+                           bool consume) {
+  bool owned = consume && tree->refcount.IsOne();
+  if (tree->height() == 0) {
+    for (CordRep* edge : tree->Edges()) {
+      if (!owned) edge = CordRep::Ref(edge);
+      size_t height = 0;
+      size_t length = edge->length;
+      CordRepBtree* node = stack[0];
+      OpResult result = node->AddEdge<kBack>(true, edge, length);
+      while (result.action == CordRepBtree::kPopped) {
+        stack[height] = result.tree;
+        if (stack[++height] == nullptr) {
+          result.action = CordRepBtree::kSelf;
+          stack[height] = CordRepBtree::New(node, result.tree);
+        } else {
+          node = stack[height];
+          result = node->AddEdge<kBack>(true, result.tree, length);
+        }
+      }
+      while (stack[++height] != nullptr) {
+        stack[height]->length += length;
+      }
+    }
+  } else {
+    for (CordRep* rep : tree->Edges()) {
+      Rebuild(stack, rep->btree(), owned);
+    }
+  }
+  if (consume) {
+    if (owned) {
+      CordRepBtree::Delete(tree);
+    } else {
+      CordRepBtree::Unref(tree);
+    }
+  }
+}
+
+CordRepBtree* CordRepBtree::Rebuild(CordRepBtree* tree) {
+  // Set up initial stack with empty leaf node.
+  CordRepBtree* node = CordRepBtree::New();
+  CordRepBtree* stack[CordRepBtree::kMaxDepth + 1] = {node};
+
+  // Recursively build the tree, consuming the input tree.
+  Rebuild(stack, tree, /* consume reference */ true);
+
+  // Return top most node
+  for (CordRepBtree* parent : stack) {
+    if (parent == nullptr) return node;
+    node = parent;
+  }
+
+  // Unreachable
+  assert(false);
+  return nullptr;
+}
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h
new file mode 100644
index 0000000..bb38f0c
--- /dev/null
+++ b/absl/strings/internal/cord_rep_btree.h
@@ -0,0 +1,939 @@
+// Copyright 2021 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_CORD_REP_BTREE_H_
+#define ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_H_
+
+#include <cassert>
+#include <cstdint>
+#include <iosfwd>
+
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/optimization.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+class CordRepBtreeNavigator;
+
+// CordRepBtree is as the name implies a btree implementation of a Cordrep tree.
+// Data is stored at the leaf level only, non leaf nodes contain down pointers
+// only. Allowed types of data edges are FLAT, EXTERNAL and SUBSTRINGs of FLAT
+// or EXTERNAL nodes. The implementation allows for data to be added to either
+// end of the tree only, it does not provide any 'insert' logic. This has the
+// benefit that we can expect good fill ratios: all nodes except the outer
+// 'legs' will have 100% fill ratios for trees built using Append/Prepend
+// methods. Merged trees will typically have a fill ratio well above 50% as in a
+// similar fashion, one side of the merged tree will typically have a 100% fill
+// ratio, and the 'open' end will average 50%. All operations are O(log(n)) or
+// better, and the tree never needs balancing.
+//
+// All methods accepting a CordRep* or CordRepBtree* adopt a reference on that
+// input unless explicitly stated otherwise. All functions returning a CordRep*
+// or CordRepBtree* instance transfer a reference back to the caller.
+// Simplified, callers both 'donate' and 'consume' a reference count on each
+// call, simplifying the API. An example of building a tree:
+//
+//   CordRepBtree* tree = CordRepBtree::Create(MakeFlat("Hello"));
+//   tree = CordRepBtree::Append(tree, MakeFlat("world"));
+//
+// In the above example, all inputs are consumed, making each call affecting
+// `tree` reference count neutral. The returned `tree` value can be different
+// from the input if the input is shared with other threads, or if the tree
+// grows in height, but callers typically never have to concern themselves with
+// that and trust that all methods DTRT at all times.
+class CordRepBtree : public CordRep {
+ public:
+  // EdgeType identifies `front` and `back` enum values.
+  // Various implementations in CordRepBtree such as `Add` and `Edge` are
+  // generic and templated on operating on either of the boundary edges.
+  // For more information on the possible edges contained in a CordRepBtree
+  // instance see the documentation for `edges_`.
+  enum class EdgeType { kFront, kBack };
+
+  // Convenience constants into `EdgeType`
+  static constexpr EdgeType kFront = EdgeType::kFront;
+  static constexpr EdgeType kBack = EdgeType::kBack;
+
+  // Maximum number of edges: based on experiments and performance data, we can
+  // pick suitable values resulting in optimum cacheline aligned values. The
+  // preferred values are based on 64-bit systems where we aim to align this
+  // class onto 64 bytes, i.e.:  6 = 64 bytes, 14 = 128 bytes, etc.
+  // TODO(b/192061034): experiment with alternative sizes.
+  static constexpr size_t kMaxCapacity = 6;
+
+  // Reasonable maximum height of the btree. We can expect a fill ratio of at
+  // least 50%: trees are always expanded at the front or back. Concatenating
+  // trees will then typically fold at the top most node, where the lower nodes
+  // are at least at capacity on one side of joined inputs. At a lower fill
+  // rate of 4 edges per node, we have capacity for ~16 million leaf nodes.
+  // We will fail / abort if an application ever exceeds this height, which
+  // should be extremely rare (near impossible) and be an indication of an
+  // application error: we do not assume it reasonable for any application to
+  // operate correctly with such monster trees.
+  // Another compelling reason for the number `12` is that any contextual stack
+  // required for navigation or insertion requires 12 words and 12 bytes, which
+  // fits inside 2 cache lines with some room to spare, and is reasonable as a
+  // local stack variable compared to Cord's current near 400 bytes stack use.
+  // The maximum `height` value of a node is then `kMaxDepth - 1` as node height
+  // values start with a value of 0 for leaf nodes.
+  static constexpr int kMaxDepth = 12;
+  static constexpr int kMaxHeight = kMaxDepth - 1;
+
+  // `Action` defines the action for unwinding changes done at the btree's leaf
+  // level that need to be propagated up to the parent node(s). Each operation
+  // on a node has an effect / action defined as follows:
+  // - kSelf
+  //   The operation (add / update, etc) was performed directly on the node as
+  //   the node is private to the current thread (i.e.: not shared directly or
+  //   indirectly through a refcount > 1). Changes can be propagated directly to
+  //   all parent nodes as all parent nodes are also then private to the current
+  //   thread.
+  // - kCopied
+  //   The operation (add / update, etc) was performed on a copy of the original
+  //   node, as the node is (potentially) directly or indirectly shared with
+  //   other threads. Changes need to be propagated into the parent nodes where
+  //   the old down pointer must be unreffed and replaced with this new copy.
+  //   Such changes to parent nodes may themselves require a copy if the parent
+  //   node is also shared. A kCopied action can propagate all the way to the
+  //   top node where we then must unref the `tree` input provided by the
+  //   caller, and return the new copy.
+  // - kPopped
+  //   The operation (typically add) could not be satisfied due to insufficient
+  //   capacity in the targeted node, and a new 'leg' was created that needs to
+  //   be added into the parent node. For example, adding a FLAT inside a leaf
+  //   node that is at capacity will create a new leaf node containing that
+  //   FLAT, that needs to be 'popped' up the btree. Such 'pop' actions can
+  //   cascade up the tree if parent nodes are also at capacity. A 'Popped'
+  //   action propagating all the way to the top of the tree will result in
+  //   the tree becoming one level higher than the current tree through a final
+  //   `CordRepBtree::New(tree, popped)` call, resulting in a new top node
+  //   referencing the old tree and the new (fully popped upwards) 'leg'.
+  enum Action { kSelf, kCopied, kPopped };
+
+  // Result of an operation on a node. See the `Action` enum for details.
+  struct OpResult {
+    CordRepBtree* tree;
+    Action action;
+  };
+
+  // Return value of the CopyPrefix and CopySuffix methods which can
+  // return a node or data edge at any height inside the tree.
+  // A height of 0 defines the lowest (leaf) node, a height of -1 identifies
+  // `edge` as being a plain data node: EXTERNAL / FLAT or SUBSTRING thereof.
+  struct CopyResult {
+    CordRep* edge;
+    int height;
+  };
+
+  // Logical position inside a node:
+  // - index: index of the edge.
+  // - n: size or offset value depending on context.
+  struct Position {
+    size_t index;
+    size_t n;
+  };
+
+  // Creates a btree from the given input. Adopts a ref of `rep`.
+  // If the input `rep` is itself a btree, i.e., `IsBtree()`, then this
+  // function immediately returns `rep->btree()`. If the input is a valid data
+  // edge (see IsDataEdge()), then a new leaf node is returned containing `rep`
+  // as the sole data edge. Else, the input is assumed to be a (legacy) concat
+  // tree, and the input is consumed and transformed into a btree().
+  static CordRepBtree* Create(CordRep* rep);
+
+  // Destroys the provided tree. Should only be called by cord internal API's,
+  // typically after a ref_count.Decrement() on the last reference count.
+  static void Destroy(CordRepBtree* tree);
+
+  // Use CordRep::Unref() as we overload for absl::Span<CordRep* const>.
+  using CordRep::Unref;
+
+  // Unrefs all edges in `edges` which are assumed to be 'likely one'.
+  static void Unref(absl::Span<CordRep* const> edges);
+
+  // Appends / Prepends an existing CordRep instance to this tree.
+  // The below methods accept three types of input:
+  // 1) `rep` is a data node (See `IsDataNode` for valid data edges).
+  // `rep` is appended or prepended to this tree 'as is'.
+  // 2) `rep` is a BTREE.
+  // `rep` is merged into `tree` respecting the Append/Prepend order.
+  // 3) `rep` is some other (legacy) type.
+  // `rep` is converted in place and added to `tree`
+  // Requires `tree` and `rep` to be not null.
+  static CordRepBtree* Append(CordRepBtree* tree, CordRep* rep);
+  static CordRepBtree* Prepend(CordRepBtree* tree, CordRep* rep);
+
+  // Append/Prepend the data in `data` to this tree.
+  // The `extra` parameter defines how much extra capacity should be allocated
+  // for any additional FLAT being allocated. This is an optimization hint from
+  // the caller. For example, a caller may need to add 2 string_views of data
+  // "abc" and "defghi" which are not consecutive. The caller can in this case
+  // invoke `AddData(tree, "abc", 6)`, and any newly added flat is allocated
+  // where possible with at least 6 bytes of extra capacity beyond `length`.
+  // This helps avoiding data getting fragmented over multiple flats.
+  // There is no limit on the size of `data`. If `data` can not be stored inside
+  // a single flat, then the function will iteratively add flats until all data
+  // has been consumed and appended or prepended to the tree.
+  static CordRepBtree* Append(CordRepBtree* tree, string_view data,
+                              size_t extra = 0);
+  static CordRepBtree* Prepend(CordRepBtree* tree, string_view data,
+                               size_t extra = 0);
+
+  // Returns a new tree, containing `n` bytes of data from this instance
+  // starting at offset `offset`. Where possible, the returned tree shares
+  // (re-uses) data edges and nodes with this instance to minimize the
+  // combined memory footprint of both trees.
+  // Requires `offset + n <= length`. Returns `nullptr` if `n` is zero.
+  CordRep* SubTree(size_t offset, size_t n);
+
+  // Removes `n` trailing bytes from `tree`, and returns the resulting tree
+  // or data edge. Returns `tree` if n is zero, and nullptr if n == length.
+  // This function is logically identical to:
+  //   result = tree->SubTree(0, tree->length - n);
+  //   Unref(tree);
+  //   return result;
+  // However, the actual implementation will as much as possible perform 'in
+  // place' modifications on the tree on all nodes and edges that are mutable.
+  // For example, in a fully privately owned tree with the last edge being a
+  // flat of length 12, RemoveSuffix(1) will simply set the length of that data
+  // edge to 11, and reduce the length of all nodes on the edge path by 1.
+  static CordRep* RemoveSuffix(CordRepBtree* tree, size_t n);
+
+  // Returns the character at the given offset.
+  char GetCharacter(size_t offset) const;
+
+  // Returns true if this node holds a single data edge, and if so, sets
+  // `fragment` to reference the contained data. `fragment` is an optional
+  // output parameter and allowed to be null.
+  bool IsFlat(absl::string_view* fragment) const;
+
+  // Returns true if the data of `n` bytes starting at offset `offset`
+  // is contained in a single data edge, and if so, sets fragment to reference
+  // the contained data. `fragment` is an optional output parameter and allowed
+  // to be null.
+  bool IsFlat(size_t offset, size_t n, absl::string_view* fragment) const;
+
+  // Returns a span (mutable range of bytes) of up to `size` bytes into the
+  // last FLAT data edge inside this tree under the following conditions:
+  // - none of the nodes down into the FLAT node are shared.
+  // - the last data edge in this tree is a non-shared FLAT.
+  // - the referenced FLAT has additional capacity available.
+  // If all these conditions are met, a non-empty span is returned, and the
+  // length of the flat node and involved tree nodes have been increased by
+  // `span.length()`. The caller is responsible for immediately assigning values
+  // to all uninitialized data reference by the returned span.
+  // Requires `this->refcount.IsMutable()`: this function forces the
+  // caller to do this fast path check on the top level node, as this is the
+  // most commonly shared node of a cord tree.
+  Span<char> GetAppendBuffer(size_t size);
+
+  // Returns the `height` of the tree. The height of a tree is limited to
+  // kMaxHeight. `height` is implemented as an `int` as in some places we
+  // use negative (-1) values for 'data edges'.
+  int height() const { return static_cast<int>(storage[0]); }
+
+  // Properties: begin, back, end, front/back boundary indexes.
+  size_t begin() const { return static_cast<size_t>(storage[1]); }
+  size_t back() const { return static_cast<size_t>(storage[2]) - 1; }
+  size_t end() const { return static_cast<size_t>(storage[2]); }
+  size_t index(EdgeType edge) const {
+    return edge == kFront ? begin() : back();
+  }
+
+  // Properties: size and capacity.
+  // `capacity` contains the current capacity of this instance, where
+  // `kMaxCapacity` contains the maximum capacity of a btree node.
+  // For now, `capacity` and `kMaxCapacity` return the same value, but this may
+  // change in the future if we see benefit in dynamically sizing 'small' nodes
+  // to 'large' nodes for large data trees.
+  size_t size() const { return end() - begin(); }
+  size_t capacity() const { return kMaxCapacity; }
+
+  // Edge access
+  inline CordRep* Edge(size_t index) const;
+  inline CordRep* Edge(EdgeType edge_type) const;
+  inline absl::Span<CordRep* const> Edges() const;
+  inline absl::Span<CordRep* const> Edges(size_t begin, size_t end) const;
+
+  // Returns reference to the data edge at `index`.
+  // Requires this instance to be a leaf node, and `index` to be valid index.
+  inline absl::string_view Data(size_t index) const;
+
+  static const char* EdgeDataPtr(const CordRep* r);
+  static absl::string_view EdgeData(const CordRep* r);
+
+  // Returns true if the provided rep is a FLAT, EXTERNAL or a SUBSTRING node
+  // holding a FLAT or EXTERNAL child rep.
+  static bool IsDataEdge(const CordRep* rep);
+
+  // Diagnostics: returns true if `tree` is valid and internally consistent.
+  // If `shallow` is false, then the provided top level node and all child nodes
+  // below it are recursively checked. If `shallow` is true, only the provided
+  // node in `tree` and the cumulative length, type and height of the direct
+  // child nodes of `tree` are checked. The value of `shallow` is ignored if the
+  // internal `cord_btree_exhaustive_validation` diagnostics variable is true,
+  // in which case the performed validations works as if `shallow` were false.
+  // This function is intended for debugging and testing purposes only.
+  static bool IsValid(const CordRepBtree* tree, bool shallow = false);
+
+  // Diagnostics: asserts that the provided tree is valid.
+  // `AssertValid()` performs a shallow validation by default. `shallow` can be
+  // set to false in which case an exhaustive validation is performed. This
+  // function is implemented in terms of calling `IsValid()` and asserting the
+  // return value to be true. See `IsValid()` for more information.
+  // This function is intended for debugging and testing purposes only.
+  static CordRepBtree* AssertValid(CordRepBtree* tree, bool shallow = true);
+  static const CordRepBtree* AssertValid(const CordRepBtree* tree,
+                                         bool shallow = true);
+
+  // Diagnostics: dump the contents of this tree to `stream`.
+  // This function is intended for debugging and testing purposes only.
+  static void Dump(const CordRep* rep, std::ostream& stream);
+  static void Dump(const CordRep* rep, absl::string_view label,
+                   std::ostream& stream);
+  static void Dump(const CordRep* rep, absl::string_view label,
+                   bool include_contents, std::ostream& stream);
+
+  // Adds the edge `edge` to this node if possible. `owned` indicates if the
+  // current node is potentially shared or not with other threads. Returns:
+  // - {kSelf, <this>}
+  //   The edge was directly added to this node.
+  // - {kCopied, <node>}
+  //   The edge was added to a copy of this node.
+  // - {kPopped, New(edge, height())}
+  //   A new leg with the edge was created as this node has no extra capacity.
+  template <EdgeType edge_type>
+  inline OpResult AddEdge(bool owned, CordRep* edge, size_t delta);
+
+  // Replaces the front or back edge with the provided new edge. Returns:
+  // - {kSelf, <this>}
+  //   The edge was directly set in this node. The old edge is unreffed.
+  // - {kCopied, <node>}
+  //   A copy of this node was created with the new edge value.
+  // In both cases, the function adopts a reference on `edge`.
+  template <EdgeType edge_type>
+  OpResult SetEdge(bool owned, CordRep* edge, size_t delta);
+
+  // Creates a new empty node at the specified height.
+  static CordRepBtree* New(int height = 0);
+
+  // Creates a new node containing `rep`, with the height being computed
+  // automatically based on the type of `rep`.
+  static CordRepBtree* New(CordRep* rep);
+
+  // Creates a new node containing both `front` and `back` at height
+  // `front.height() + 1`. Requires `back.height() == front.height()`.
+  static CordRepBtree* New(CordRepBtree* front, CordRepBtree* back);
+
+  // Creates a fully balanced tree from the provided tree by rebuilding a new
+  // tree from all data edges in the input. This function is automatically
+  // invoked internally when the tree exceeds the maximum height.
+  static CordRepBtree* Rebuild(CordRepBtree* tree);
+
+ private:
+  CordRepBtree() = default;
+  ~CordRepBtree() = default;
+
+  // Initializes the main properties `tag`, `begin`, `end`, `height`.
+  inline void InitInstance(int height, size_t begin = 0, size_t end = 0);
+
+  // Direct property access begin / end
+  void set_begin(size_t begin) { storage[1] = static_cast<uint8_t>(begin); }
+  void set_end(size_t end) { storage[2] = static_cast<uint8_t>(end); }
+
+  // Decreases the value of `begin` by `n`, and returns the new value. Notice
+  // how this returns the new value unlike atomic::fetch_add which returns the
+  // old value. This is because this is used to prepend edges at 'begin - 1'.
+  size_t sub_fetch_begin(size_t n) {
+    storage[1] -= static_cast<uint8_t>(n);
+    return storage[1];
+  }
+
+  // Increases the value of `end` by `n`, and returns the previous value. This
+  // function is typically used to append edges at 'end'.
+  size_t fetch_add_end(size_t n) {
+    const uint8_t current = storage[2];
+    storage[2] = static_cast<uint8_t>(current + n);
+    return current;
+  }
+
+  // Returns the index of the last edge starting on, or before `offset`, with
+  // `n` containing the relative offset of `offset` inside that edge.
+  // Requires `offset` < length.
+  Position IndexOf(size_t offset) const;
+
+  // Returns the index of the last edge starting before `offset`, with `n`
+  // containing the relative offset of `offset` inside that edge.
+  // This function is useful to find the edges for some span of bytes ending at
+  // `offset` (i.e., `n` bytes). For example:
+  //
+  //   Position pos = IndexBefore(n)
+  //   edges = Edges(begin(), pos.index)     // All full edges (may be empty)
+  //   last = Sub(Edge(pos.index), 0, pos.n) // Last partial edge (may be empty)
+  //
+  // Requires 0 < `offset` <= length.
+  Position IndexBefore(size_t offset) const;
+
+  // Returns the index of the edge ending at (or on) length `length`, and the
+  // number of bytes inside that edge up to `length`. For example, if we have a
+  // Node with 2 edges, one of 10 and one of 20 long, then IndexOfLength(27)
+  // will return {1, 17}, and IndexOfLength(10) will return {0, 10}.
+  Position IndexOfLength(size_t n) const;
+
+  // Identical to the above function except starting from the position `front`.
+  // This function is equivalent to `IndexBefore(front.n + offset)`, with
+  // the difference that this function is optimized to start at `front.index`.
+  Position IndexBefore(Position front, size_t offset) const;
+
+  // Returns the index of the edge directly beyond the edge containing offset
+  // `offset`, with `n` containing the distance of that edge from `offset`.
+  // This function is useful for iteratively finding suffix nodes and remaining
+  // partial bytes in left-most suffix nodes as for example in CopySuffix.
+  // Requires `offset` < length.
+  Position IndexBeyond(size_t offset) const;
+
+  // Destruction
+  static void DestroyLeaf(CordRepBtree* tree, size_t begin, size_t end);
+  static void DestroyNonLeaf(CordRepBtree* tree, size_t begin, size_t end);
+  static void DestroyTree(CordRepBtree* tree, size_t begin, size_t end);
+  static void Delete(CordRepBtree* tree) { delete tree; }
+
+  // Creates a new leaf node containing as much data as possible from `data`.
+  // The data is added either forwards or reversed depending on `edge_type`.
+  // Callers must check the length of the returned node to determine if all data
+  // was copied or not.
+  // See the `Append/Prepend` function for the meaning and purpose of `extra`.
+  template <EdgeType edge_type>
+  static CordRepBtree* NewLeaf(absl::string_view data, size_t extra);
+
+  // Creates a raw copy of this Btree node, copying all properties, but
+  // without adding any references to existing edges.
+  CordRepBtree* CopyRaw() const;
+
+  // Creates a full copy of this Btree node, adding a reference on all edges.
+  CordRepBtree* Copy() const;
+
+  // Creates a partial copy of this Btree node, copying all edges up to `end`,
+  // adding a reference on each copied edge, and sets the length of the newly
+  // created copy to `new_length`.
+  CordRepBtree* CopyBeginTo(size_t end, size_t new_length) const;
+
+  // Returns a tree containing the edges [tree->begin(), end) and length
+  // of `new_length`. This method consumes a reference on the provided
+  // tree, and logically performs the following operation:
+  //   result = tree->CopyBeginTo(end, new_length);
+  //   CordRep::Unref(tree);
+  //   return result;
+  static CordRepBtree* ConsumeBeginTo(CordRepBtree* tree, size_t end,
+                                      size_t new_length);
+
+  // Creates a partial copy of this Btree node, copying all edges starting at
+  // `begin`, adding a reference on each copied edge, and sets the length of
+  // the newly created copy to `new_length`.
+  CordRepBtree* CopyToEndFrom(size_t begin, size_t new_length) const;
+
+  // Extracts and returns the front edge from the provided tree.
+  // This method consumes a reference on the provided tree, and logically
+  // performs the following operation:
+  //   edge = CordRep::Ref(tree->Edge(kFront));
+  //   CordRep::Unref(tree);
+  //   return edge;
+  static CordRep* ExtractFront(CordRepBtree* tree);
+
+  // Returns a tree containing the result of appending `right` to `left`.
+  static CordRepBtree* MergeTrees(CordRepBtree* left, CordRepBtree* right);
+
+  // Fallback functions for `Create()`, `Append()` and `Prepend()` which
+  // deal with legacy / non conforming input, i.e.: CONCAT trees.
+  static CordRepBtree* CreateSlow(CordRep* rep);
+  static CordRepBtree* AppendSlow(CordRepBtree*, CordRep* rep);
+  static CordRepBtree* PrependSlow(CordRepBtree*, CordRep* rep);
+
+  // Recursively rebuilds `tree` into `stack`. If 'consume` is set to true, the
+  // function will consume a reference on `tree`. `stack` is a null terminated
+  // array containing the new tree's state, with the current leaf node at
+  // stack[0], and parent nodes above that, or null for 'top of tree'.
+  static void Rebuild(CordRepBtree** stack, CordRepBtree* tree, bool consume);
+
+  // Aligns existing edges to start at index 0, to allow for a new edge to be
+  // added to the back of the current edges.
+  inline void AlignBegin();
+
+  // Aligns existing edges to end at `capacity`, to allow for a new edge to be
+  // added in front of the current edges.
+  inline void AlignEnd();
+
+  // Adds the provided edge to this node.
+  // Requires this node to have capacity for the edge. Realigns / moves
+  // existing edges as needed to prepend or append the new edge.
+  template <EdgeType edge_type>
+  inline void Add(CordRep* rep);
+
+  // Adds the provided edges to this node.
+  // Requires this node to have capacity for the edges. Realigns / moves
+  // existing edges as needed to prepend or append the new edges.
+  template <EdgeType edge_type>
+  inline void Add(absl::Span<CordRep* const>);
+
+  // Adds data from `data` to this node until either all data has been consumed,
+  // or there is no more capacity for additional flat nodes inside this node.
+  // Requires the current node to be a leaf node, data to be non empty, and the
+  // current node to have capacity for at least one more data edge.
+  // Returns any remaining data from `data` that was not added, which is
+  // depending on the edge type (front / back) either the remaining prefix of
+  // suffix of the input.
+  // See the `Append/Prepend` function for the meaning and purpose of `extra`.
+  template <EdgeType edge_type>
+  absl::string_view AddData(absl::string_view data, size_t extra);
+
+  // Replace the front or back edge with the provided value.
+  // Adopts a reference on `edge` and unrefs the old edge.
+  template <EdgeType edge_type>
+  inline void SetEdge(CordRep* edge);
+
+  // Returns a partial copy of the current tree containing the first `n` bytes
+  // of data. `CopyResult` contains both the resulting edge and its height. The
+  // resulting tree may be less high than the current tree, or even be a single
+  // matching data edge if `allow_folding` is set to true.
+  // For example, if `n == 1`, then the result will be the single data edge, and
+  // height will be set to -1 (one below the owning leaf node). If n == 0, this
+  // function returns null. Requires `n <= length`
+  CopyResult CopyPrefix(size_t n, bool allow_folding = true);
+
+  // Returns a partial copy of the current tree containing all data starting
+  // after `offset`. `CopyResult` contains both the resulting edge and its
+  // height. The resulting tree may be less high than the current tree, or even
+  // be a single matching data edge. For example, if `n == length - 1`, then the
+  // result will be a single data edge, and height will be set to -1 (one below
+  // the owning leaf node).
+  // Requires `offset < length`
+  CopyResult CopySuffix(size_t offset);
+
+  // Returns a OpResult value of {this, kSelf} or {Copy(), kCopied}
+  // depending on the value of `owned`.
+  inline OpResult ToOpResult(bool owned);
+
+  // Adds `rep` to the specified tree, returning the modified tree.
+  template <EdgeType edge_type>
+  static CordRepBtree* AddCordRep(CordRepBtree* tree, CordRep* rep);
+
+  // Adds `data` to the specified tree, returning the modified tree.
+  // See the `Append/Prepend` function for the meaning and purpose of `extra`.
+  template <EdgeType edge_type>
+  static CordRepBtree* AddData(CordRepBtree* tree, absl::string_view data,
+                               size_t extra = 0);
+
+  // Merges `src` into `dst` with `src` being added either before (kFront) or
+  // after (kBack) `dst`. Requires the height of `dst` to be greater than or
+  // equal to the height of `src`.
+  template <EdgeType edge_type>
+  static CordRepBtree* Merge(CordRepBtree* dst, CordRepBtree* src);
+
+  // Fallback version of GetAppendBuffer for large trees: GetAppendBuffer()
+  // implements an inlined version for trees of limited height (3 levels),
+  // GetAppendBufferSlow implements the logic for large trees.
+  Span<char> GetAppendBufferSlow(size_t size);
+
+  // `edges_` contains all edges starting from this instance.
+  // These are explicitly `child` edges only, a cord btree (or any cord tree in
+  // that respect) does not store `parent` pointers anywhere: multiple trees /
+  // parents can reference the same shared child edge. The type of these edges
+  // depends on the height of the node. `Leaf nodes` (height == 0) contain `data
+  // edges` (external or flat nodes, or sub-strings thereof). All other nodes
+  // (height > 0) contain pointers to BTREE nodes with a height of `height - 1`.
+  CordRep* edges_[kMaxCapacity];
+
+  friend class CordRepBtreeTestPeer;
+  friend class CordRepBtreeNavigator;
+};
+
+inline CordRepBtree* CordRep::btree() {
+  assert(IsBtree());
+  return static_cast<CordRepBtree*>(this);
+}
+
+inline const CordRepBtree* CordRep::btree() const {
+  assert(IsBtree());
+  return static_cast<const CordRepBtree*>(this);
+}
+
+inline void CordRepBtree::InitInstance(int height, size_t begin, size_t end) {
+  tag = BTREE;
+  storage[0] = static_cast<uint8_t>(height);
+  storage[1] = static_cast<uint8_t>(begin);
+  storage[2] = static_cast<uint8_t>(end);
+}
+
+inline CordRep* CordRepBtree::Edge(size_t index) const {
+  assert(index >= begin());
+  assert(index < end());
+  return edges_[index];
+}
+
+inline CordRep* CordRepBtree::Edge(EdgeType edge_type) const {
+  return edges_[edge_type == kFront ? begin() : back()];
+}
+
+inline absl::Span<CordRep* const> CordRepBtree::Edges() const {
+  return {edges_ + begin(), size()};
+}
+
+inline absl::Span<CordRep* const> CordRepBtree::Edges(size_t begin,
+                                                      size_t end) const {
+  assert(begin <= end);
+  assert(begin >= this->begin());
+  assert(end <= this->end());
+  return {edges_ + begin, static_cast<size_t>(end - begin)};
+}
+
+inline const char* CordRepBtree::EdgeDataPtr(const CordRep* r) {
+  assert(IsDataEdge(r));
+  size_t offset = 0;
+  if (r->tag == SUBSTRING) {
+    offset = r->substring()->start;
+    r = r->substring()->child;
+  }
+  return (r->tag >= FLAT ? r->flat()->Data() : r->external()->base) + offset;
+}
+
+inline absl::string_view CordRepBtree::EdgeData(const CordRep* r) {
+  return absl::string_view(EdgeDataPtr(r), r->length);
+}
+
+inline absl::string_view CordRepBtree::Data(size_t index) const {
+  assert(height() == 0);
+  return EdgeData(Edge(index));
+}
+
+inline bool CordRepBtree::IsDataEdge(const CordRep* rep) {
+  // The fast path is that `rep` is an EXTERNAL or FLAT node, making the below
+  // if a single, well predicted branch. We then repeat the FLAT or EXTERNAL
+  // check in the slow path the SUBSTRING check to optimize for the hot path.
+  if (rep->tag == EXTERNAL || rep->tag >= FLAT) return true;
+  if (rep->tag == SUBSTRING) rep = rep->substring()->child;
+  return rep->tag == EXTERNAL || rep->tag >= FLAT;
+}
+
+inline CordRepBtree* CordRepBtree::New(int height) {
+  CordRepBtree* tree = new CordRepBtree;
+  tree->length = 0;
+  tree->InitInstance(height);
+  return tree;
+}
+
+inline CordRepBtree* CordRepBtree::New(CordRep* rep) {
+  CordRepBtree* tree = new CordRepBtree;
+  int height = rep->IsBtree() ? rep->btree()->height() + 1 : 0;
+  tree->length = rep->length;
+  tree->InitInstance(height, /*begin=*/0, /*end=*/1);
+  tree->edges_[0] = rep;
+  return tree;
+}
+
+inline CordRepBtree* CordRepBtree::New(CordRepBtree* front,
+                                       CordRepBtree* back) {
+  assert(front->height() == back->height());
+  CordRepBtree* tree = new CordRepBtree;
+  tree->length = front->length + back->length;
+  tree->InitInstance(front->height() + 1, /*begin=*/0, /*end=*/2);
+  tree->edges_[0] = front;
+  tree->edges_[1] = back;
+  return tree;
+}
+
+inline void CordRepBtree::DestroyTree(CordRepBtree* tree, size_t begin,
+                                      size_t end) {
+  if (tree->height() == 0) {
+    DestroyLeaf(tree, begin, end);
+  } else {
+    DestroyNonLeaf(tree, begin, end);
+  }
+}
+
+inline void CordRepBtree::Destroy(CordRepBtree* tree) {
+  DestroyTree(tree, tree->begin(), tree->end());
+}
+
+inline void CordRepBtree::Unref(absl::Span<CordRep* const> edges) {
+  for (CordRep* edge : edges) {
+    if (ABSL_PREDICT_FALSE(!edge->refcount.Decrement())) {
+      CordRep::Destroy(edge);
+    }
+  }
+}
+
+inline CordRepBtree* CordRepBtree::CopyRaw() const {
+  auto* tree = static_cast<CordRepBtree*>(::operator new(sizeof(CordRepBtree)));
+  memcpy(static_cast<void*>(tree), this, sizeof(CordRepBtree));
+  new (&tree->refcount) RefcountAndFlags;
+  return tree;
+}
+
+inline CordRepBtree* CordRepBtree::Copy() const {
+  CordRepBtree* tree = CopyRaw();
+  for (CordRep* rep : Edges()) CordRep::Ref(rep);
+  return tree;
+}
+
+inline CordRepBtree* CordRepBtree::CopyToEndFrom(size_t begin,
+                                                 size_t new_length) const {
+  assert(begin >= this->begin());
+  assert(begin <= this->end());
+  CordRepBtree* tree = CopyRaw();
+  tree->length = new_length;
+  tree->set_begin(begin);
+  for (CordRep* edge : tree->Edges()) CordRep::Ref(edge);
+  return tree;
+}
+
+inline CordRepBtree* CordRepBtree::CopyBeginTo(size_t end,
+                                               size_t new_length) const {
+  assert(end <= capacity());
+  assert(end >= this->begin());
+  CordRepBtree* tree = CopyRaw();
+  tree->length = new_length;
+  tree->set_end(end);
+  for (CordRep* edge : tree->Edges()) CordRep::Ref(edge);
+  return tree;
+}
+
+inline void CordRepBtree::AlignBegin() {
+  // The below code itself does not need to be fast as typically we have
+  // mono-directional append/prepend calls, and `begin` / `end` are typically
+  // adjusted no more than once. But we want to avoid potential register clobber
+  // effects, making the compiler emit register save/store/spills, and minimize
+  // the size of code.
+  const size_t delta = begin();
+  if (ABSL_PREDICT_FALSE(delta != 0)) {
+    const size_t new_end = end() - delta;
+    set_begin(0);
+    set_end(new_end);
+    // TODO(mvels): we can write this using 2 loads / 2 stores depending on
+    // total size for the kMaxCapacity = 6 case. I.e., we can branch (switch) on
+    // size, and then do overlapping load/store of up to 4 pointers (inlined as
+    // XMM, YMM or ZMM load/store) and up to 2 pointers (XMM / YMM), which is a)
+    // compact and b) not clobbering any registers.
+    ABSL_INTERNAL_ASSUME(new_end <= kMaxCapacity);
+#ifdef __clang__
+#pragma unroll 1
+#endif
+    for (size_t i = 0; i < new_end; ++i) {
+      edges_[i] = edges_[i + delta];
+    }
+  }
+}
+
+inline void CordRepBtree::AlignEnd() {
+  // See comments in `AlignBegin` for motivation on the hand-rolled for loops.
+  const size_t delta = capacity() - end();
+  if (delta != 0) {
+    const size_t new_begin = begin() + delta;
+    const size_t new_end = end() + delta;
+    set_begin(new_begin);
+    set_end(new_end);
+    ABSL_INTERNAL_ASSUME(new_end <= kMaxCapacity);
+#ifdef __clang__
+#pragma unroll 1
+#endif
+    for (size_t i = new_end - 1; i >= new_begin; --i) {
+      edges_[i] = edges_[i - delta];
+    }
+  }
+}
+
+template <>
+inline void CordRepBtree::Add<CordRepBtree::kBack>(CordRep* rep) {
+  AlignBegin();
+  edges_[fetch_add_end(1)] = rep;
+}
+
+template <>
+inline void CordRepBtree::Add<CordRepBtree::kBack>(
+    absl::Span<CordRep* const> edges) {
+  AlignBegin();
+  size_t new_end = end();
+  for (CordRep* edge : edges) edges_[new_end++] = edge;
+  set_end(new_end);
+}
+
+template <>
+inline void CordRepBtree::Add<CordRepBtree::kFront>(CordRep* rep) {
+  AlignEnd();
+  edges_[sub_fetch_begin(1)] = rep;
+}
+
+template <>
+inline void CordRepBtree::Add<CordRepBtree::kFront>(
+    absl::Span<CordRep* const> edges) {
+  AlignEnd();
+  size_t new_begin = begin() - edges.size();
+  set_begin(new_begin);
+  for (CordRep* edge : edges) edges_[new_begin++] = edge;
+}
+
+template <CordRepBtree::EdgeType edge_type>
+inline void CordRepBtree::SetEdge(CordRep* edge) {
+  const int idx = edge_type == kFront ? begin() : back();
+  CordRep::Unref(edges_[idx]);
+  edges_[idx] = edge;
+}
+
+inline CordRepBtree::OpResult CordRepBtree::ToOpResult(bool owned) {
+  return owned ? OpResult{this, kSelf} : OpResult{Copy(), kCopied};
+}
+
+inline CordRepBtree::Position CordRepBtree::IndexOf(size_t offset) const {
+  assert(offset < length);
+  size_t index = begin();
+  while (offset >= edges_[index]->length) offset -= edges_[index++]->length;
+  return {index, offset};
+}
+
+inline CordRepBtree::Position CordRepBtree::IndexBefore(size_t offset) const {
+  assert(offset > 0);
+  assert(offset <= length);
+  size_t index = begin();
+  while (offset > edges_[index]->length) offset -= edges_[index++]->length;
+  return {index, offset};
+}
+
+inline CordRepBtree::Position CordRepBtree::IndexBefore(Position front,
+                                                        size_t offset) const {
+  size_t index = front.index;
+  offset = offset + front.n;
+  while (offset > edges_[index]->length) offset -= edges_[index++]->length;
+  return {index, offset};
+}
+
+inline CordRepBtree::Position CordRepBtree::IndexOfLength(size_t n) const {
+  assert(n <= length);
+  size_t index = back();
+  size_t strip = length - n;
+  while (strip >= edges_[index]->length) strip -= edges_[index--]->length;
+  return {index, edges_[index]->length - strip};
+}
+
+inline CordRepBtree::Position CordRepBtree::IndexBeyond(
+    const size_t offset) const {
+  // We need to find the edge which `starting offset` is beyond (>=)`offset`.
+  // For this we can't use the `offset -= length` logic of IndexOf. Instead, we
+  // track the offset of the `current edge` in `off`, which we increase as we
+  // iterate over the edges until we find the matching edge.
+  size_t off = 0;
+  size_t index = begin();
+  while (offset > off) off += edges_[index++]->length;
+  return {index, off - offset};
+}
+
+inline CordRepBtree* CordRepBtree::Create(CordRep* rep) {
+  if (IsDataEdge(rep)) return New(rep);
+  return CreateSlow(rep);
+}
+
+inline Span<char> CordRepBtree::GetAppendBuffer(size_t size) {
+  assert(refcount.IsMutable());
+  CordRepBtree* tree = this;
+  const int height = this->height();
+  CordRepBtree* n1 = tree;
+  CordRepBtree* n2 = tree;
+  CordRepBtree* n3 = tree;
+  switch (height) {
+    case 3:
+      tree = tree->Edge(kBack)->btree();
+      if (!tree->refcount.IsMutable()) return {};
+      n2 = tree;
+      ABSL_FALLTHROUGH_INTENDED;
+    case 2:
+      tree = tree->Edge(kBack)->btree();
+      if (!tree->refcount.IsMutable()) return {};
+      n1 = tree;
+      ABSL_FALLTHROUGH_INTENDED;
+    case 1:
+      tree = tree->Edge(kBack)->btree();
+      if (!tree->refcount.IsMutable()) return {};
+      ABSL_FALLTHROUGH_INTENDED;
+    case 0:
+      CordRep* edge = tree->Edge(kBack);
+      if (!edge->refcount.IsMutable()) return {};
+      if (edge->tag < FLAT) return {};
+      size_t avail = edge->flat()->Capacity() - edge->length;
+      if (avail == 0) return {};
+      size_t delta = (std::min)(size, avail);
+      Span<char> span = {edge->flat()->Data() + edge->length, delta};
+      edge->length += delta;
+      switch (height) {
+        case 3:
+          n3->length += delta;
+          ABSL_FALLTHROUGH_INTENDED;
+        case 2:
+          n2->length += delta;
+          ABSL_FALLTHROUGH_INTENDED;
+        case 1:
+          n1->length += delta;
+          ABSL_FALLTHROUGH_INTENDED;
+        case 0:
+          tree->length += delta;
+          return span;
+      }
+      break;
+  }
+  return GetAppendBufferSlow(size);
+}
+
+extern template CordRepBtree* CordRepBtree::AddCordRep<CordRepBtree::kBack>(
+    CordRepBtree* tree, CordRep* rep);
+
+extern template CordRepBtree* CordRepBtree::AddCordRep<CordRepBtree::kFront>(
+    CordRepBtree* tree, CordRep* rep);
+
+inline CordRepBtree* CordRepBtree::Append(CordRepBtree* tree, CordRep* rep) {
+  if (ABSL_PREDICT_TRUE(IsDataEdge(rep))) {
+    return CordRepBtree::AddCordRep<kBack>(tree, rep);
+  }
+  return AppendSlow(tree, rep);
+}
+
+inline CordRepBtree* CordRepBtree::Prepend(CordRepBtree* tree, CordRep* rep) {
+  if (ABSL_PREDICT_TRUE(IsDataEdge(rep))) {
+    return CordRepBtree::AddCordRep<kFront>(tree, rep);
+  }
+  return PrependSlow(tree, rep);
+}
+
+#ifdef NDEBUG
+
+inline CordRepBtree* CordRepBtree::AssertValid(CordRepBtree* tree,
+                                               bool /* shallow */) {
+  return tree;
+}
+
+inline const CordRepBtree* CordRepBtree::AssertValid(const CordRepBtree* tree,
+                                                     bool /* shallow */) {
+  return tree;
+}
+
+#endif
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_H_
diff --git a/absl/strings/internal/cord_rep_btree_navigator.cc b/absl/strings/internal/cord_rep_btree_navigator.cc
new file mode 100644
index 0000000..d1f9995
--- /dev/null
+++ b/absl/strings/internal/cord_rep_btree_navigator.cc
@@ -0,0 +1,185 @@
+// Copyright 2021 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/cord_rep_btree_navigator.h"
+
+#include <cassert>
+
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_btree.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+using ReadResult = CordRepBtreeNavigator::ReadResult;
+
+namespace {
+
+// Returns a `CordRepSubstring` from `rep` starting at `offset` of size `n`.
+// If `rep` is already a `CordRepSubstring` instance, an adjusted instance is
+// created based on the old offset and new offset.
+// Adopts a reference on `rep`. Rep must be a valid data edge. Returns
+// nullptr if `n == 0`, `rep` if `n == rep->length`.
+// Requires `offset < rep->length` and `offset + n <= rep->length`.
+// TODO(192061034): move to utility library in internal and optimize for small
+// substrings of larger reps.
+inline CordRep* Substring(CordRep* rep, size_t offset, size_t n) {
+  assert(n <= rep->length);
+  assert(offset < rep->length);
+  assert(offset <= rep->length - n);
+  assert(CordRepBtree::IsDataEdge(rep));
+
+  if (n == 0) return nullptr;
+  if (n == rep->length) return CordRep::Ref(rep);
+
+  if (rep->tag == SUBSTRING) {
+    offset += rep->substring()->start;
+    rep = rep->substring()->child;
+  }
+
+  CordRepSubstring* substring = new CordRepSubstring();
+  substring->length = n;
+  substring->tag = SUBSTRING;
+  substring->start = offset;
+  substring->child = CordRep::Ref(rep);
+  return substring;
+}
+
+inline CordRep* Substring(CordRep* rep, size_t offset) {
+  return Substring(rep, offset, rep->length - offset);
+}
+
+}  // namespace
+
+CordRepBtreeNavigator::Position CordRepBtreeNavigator::Skip(size_t n) {
+  int height = 0;
+  size_t index = index_[0];
+  CordRepBtree* node = node_[0];
+  CordRep* edge = node->Edge(index);
+
+  // Overall logic: Find an edge of at least the length we need to skip.
+  // We consume all edges which are smaller (i.e., must be 100% skipped).
+  // If we exhausted all edges on the current level, we move one level
+  // up the tree, and repeat until we either find the edge, or until we hit
+  // the top of the tree meaning the skip exceeds tree->length.
+  while (n >= edge->length) {
+    n -= edge->length;
+    while (++index == node->end()) {
+      if (++height > height_) return {nullptr, n};
+      node = node_[height];
+      index = index_[height];
+    }
+    edge = node->Edge(index);
+  }
+
+  // If we moved up the tree, descend down to the leaf level, consuming all
+  // edges that must be skipped.
+  while (height > 0) {
+    node = edge->btree();
+    index_[height] = index;
+    node_[--height] = node;
+    index = node->begin();
+    edge = node->Edge(index);
+    while (n >= edge->length) {
+      n -= edge->length;
+      ++index;
+      assert(index != node->end());
+      edge = node->Edge(index);
+    }
+  }
+  index_[0] = index;
+  return {edge, n};
+}
+
+ReadResult CordRepBtreeNavigator::Read(size_t edge_offset, size_t n) {
+  int height = 0;
+  size_t length = edge_offset + n;
+  size_t index = index_[0];
+  CordRepBtree* node = node_[0];
+  CordRep* edge = node->Edge(index);
+  assert(edge_offset < edge->length);
+
+  if (length < edge->length) {
+    return {Substring(edge, edge_offset, n), length};
+  }
+
+  // Similar to 'Skip', we consume all edges that are inside the 'length' of
+  // data that needs to be read. If we exhaust the current level, we move one
+  // level up the tree and repeat until we hit the final edge that must be
+  // (partially) read. We consume all edges into `subtree`.
+  CordRepBtree* subtree = CordRepBtree::New(Substring(edge, edge_offset));
+  size_t subtree_end = 1;
+  do {
+    length -= edge->length;
+    while (++index == node->end()) {
+      index_[height] = index;
+      if (++height > height_) {
+        subtree->set_end(subtree_end);
+        if (length == 0) return {subtree, 0};
+        CordRep::Unref(subtree);
+        return {nullptr, length};
+      }
+      if (length != 0) {
+        subtree->set_end(subtree_end);
+        subtree = CordRepBtree::New(subtree);
+        subtree_end = 1;
+      }
+      node = node_[height];
+      index = index_[height];
+    }
+    edge = node->Edge(index);
+    if (length >= edge->length) {
+      subtree->length += edge->length;
+      subtree->edges_[subtree_end++] = CordRep::Ref(edge);
+    }
+  } while (length >= edge->length);
+  CordRepBtree* tree = subtree;
+  subtree->length += length;
+
+  // If we moved up the tree, descend down to the leaf level, consuming all
+  // edges that must be read, adding 'down' nodes to `subtree`.
+  while (height > 0) {
+    node = edge->btree();
+    index_[height] = index;
+    node_[--height] = node;
+    index = node->begin();
+    edge = node->Edge(index);
+
+    if (length != 0) {
+      CordRepBtree* right = CordRepBtree::New(height);
+      right->length = length;
+      subtree->edges_[subtree_end++] = right;
+      subtree->set_end(subtree_end);
+      subtree = right;
+      subtree_end = 0;
+      while (length >= edge->length) {
+        subtree->edges_[subtree_end++] = CordRep::Ref(edge);
+        length -= edge->length;
+        edge = node->Edge(++index);
+      }
+    }
+  }
+  // Add any (partial) edge still remaining at the leaf level.
+  if (length != 0) {
+    subtree->edges_[subtree_end++] = Substring(edge, 0, length);
+  }
+  subtree->set_end(subtree_end);
+  index_[0] = index;
+  return {tree, length};
+}
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cord_rep_btree_navigator.h b/absl/strings/internal/cord_rep_btree_navigator.h
new file mode 100644
index 0000000..971b92e
--- /dev/null
+++ b/absl/strings/internal/cord_rep_btree_navigator.h
@@ -0,0 +1,265 @@
+// Copyright 2021 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_CORD_REP_BTREE_NAVIGATOR_H_
+#define ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_NAVIGATOR_H_
+
+#include <cassert>
+#include <iostream>
+
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_btree.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// CordRepBtreeNavigator is a bi-directional navigator allowing callers to
+// navigate all the (leaf) data edges in a CordRepBtree instance.
+//
+// A CordRepBtreeNavigator instance is by default empty. Callers initialize a
+// navigator instance by calling one of `InitFirst()`, `InitLast()` or
+// `InitOffset()`, which establishes a current position. Callers can then
+// navigate using the `Next`, `Previous`, `Skip` and `Seek` methods.
+//
+// The navigator instance does not take or adopt a reference on the provided
+// `tree` on any of the initialization calls. Callers are responsible for
+// guaranteeing the lifecycle of the provided tree. A navigator instance can
+// be reset to the empty state by calling `Reset`.
+//
+// A navigator only keeps positional state on the 'current data edge', it does
+// explicitly not keep any 'offset' state. The class does accept and return
+// offsets in the `Read()`, `Skip()` and 'Seek()` methods as these would
+// otherwise put a big burden on callers. Callers are expected to maintain
+// (returned) offset info if they require such granular state.
+class CordRepBtreeNavigator {
+ public:
+  // The logical position as returned by the Seek() and Skip() functions.
+  // Returns the current leaf edge for the desired seek or skip position and
+  // the offset of that position inside that edge.
+  struct Position {
+    CordRep* edge;
+    size_t offset;
+  };
+
+  // The read result as returned by the Read() function.
+  // `tree` contains the resulting tree which is identical to the result
+  // of calling CordRepBtree::SubTree(...) on the tree being navigated.
+  // `n` contains the number of bytes used from the last navigated to
+  // edge of the tree.
+  struct ReadResult {
+    CordRep* tree;
+    size_t n;
+  };
+
+  // Returns true if this instance is not empty.
+  explicit operator bool() const;
+
+  // Returns the tree for this instance or nullptr if empty.
+  CordRepBtree* btree() const;
+
+  // Returns the data edge of the current position.
+  // Requires this instance to not be empty.
+  CordRep* Current() const;
+
+  // Resets this navigator to `tree`, returning the first data edge in the tree.
+  CordRep* InitFirst(CordRepBtree* tree);
+
+  // Resets this navigator to `tree`, returning the last data edge in the tree.
+  CordRep* InitLast(CordRepBtree* tree);
+
+  // Resets this navigator to `tree` returning the data edge at position
+  // `offset` and the relative offset of `offset` into that data edge.
+  // Returns `Position.edge = nullptr` if the provided offset is greater
+  // than or equal to the length of the tree, in which case the state of
+  // the navigator instance remains unchanged.
+  Position InitOffset(CordRepBtree* tree, size_t offset);
+
+  // Navigates to the next data edge.
+  // Returns the next data edge or nullptr if there is no next data edge, in
+  // which case the current position remains unchanged.
+  CordRep* Next();
+
+  // Navigates to the previous data edge.
+  // Returns the previous data edge or nullptr if there is no previous data
+  // edge, in which case the current position remains unchanged.
+  CordRep* Previous();
+
+  // Navigates to the data edge at position `offset`. Returns the navigated to
+  // data edge in `Position.edge` and the relative offset of `offset` into that
+  // data edge in `Position.offset`. Returns `Position.edge = nullptr` if the
+  // provide offset is greater than or equal to the tree's length.
+  Position Seek(size_t offset);
+
+  // Reads `n` bytes of data starting at offset `edge_offset` of the current
+  // data edge, and returns the result in `ReadResult.tree`. `ReadResult.n`
+  // contains the 'bytes used` from the last / current data edge in the tree.
+  // This allows users that mix regular navigation (using string views) and
+  // 'read into cord' navigation to keep track of the current state, and which
+  // bytes have been consumed from a navigator.
+  // This function returns `ReadResult.tree = nullptr` if the requested length
+  // exceeds the length of the tree starting at the current data edge.
+  ReadResult Read(size_t edge_offset, size_t n);
+
+  // Skips `n` bytes forward from the current data edge, returning the navigated
+  // to data edge in `Position.edge` and `Position.offset` containing the offset
+  // inside that data edge. Note that the state of the navigator is left
+  // unchanged if `n` is smaller than the length of the current data edge.
+  Position Skip(size_t n);
+
+  // Resets this instance to the default / empty state.
+  void Reset();
+
+ private:
+  // Slow path for Next() if Next() reached the end of a leaf node. Backtracks
+  // up the stack until it finds a node that has a 'next' position available,
+  // and then does a 'front dive' towards the next leaf node.
+  CordRep* NextUp();
+
+  // Slow path for Previous() if Previous() reached the beginning of a leaf
+  // node. Backtracks up the stack until it finds a node that has a 'previous'
+  // position available, and then does a 'back dive' towards the previous leaf
+  // node.
+  CordRep* PreviousUp();
+
+  // Generic implementation of InitFirst() and InitLast().
+  template <CordRepBtree::EdgeType edge_type>
+  CordRep* Init(CordRepBtree* tree);
+
+  // `height_` contains the height of the current tree, or -1 if empty.
+  int height_ = -1;
+
+  // `index_` and `node_` contain the navigation state as the 'path' to the
+  // current data edge which is at `node_[0]->Edge(index_[0])`. The contents
+  // of these are undefined until the instance is initialized (`height_ >= 0`).
+  uint8_t index_[CordRepBtree::kMaxHeight];
+  CordRepBtree* node_[CordRepBtree::kMaxHeight];
+};
+
+// Returns true if this instance is not empty.
+inline CordRepBtreeNavigator::operator bool() const { return height_ >= 0; }
+
+inline CordRepBtree* CordRepBtreeNavigator::btree() const {
+  return height_ >= 0 ? node_[height_] : nullptr;
+}
+
+inline CordRep* CordRepBtreeNavigator::Current() const {
+  assert(height_ >= 0);
+  return node_[0]->Edge(index_[0]);
+}
+
+inline void CordRepBtreeNavigator::Reset() { height_ = -1; }
+
+inline CordRep* CordRepBtreeNavigator::InitFirst(CordRepBtree* tree) {
+  return Init<CordRepBtree::kFront>(tree);
+}
+
+inline CordRep* CordRepBtreeNavigator::InitLast(CordRepBtree* tree) {
+  return Init<CordRepBtree::kBack>(tree);
+}
+
+template <CordRepBtree::EdgeType edge_type>
+inline CordRep* CordRepBtreeNavigator::Init(CordRepBtree* tree) {
+  assert(tree != nullptr);
+  assert(tree->size() > 0);
+  int height = height_ = tree->height();
+  size_t index = tree->index(edge_type);
+  node_[height] = tree;
+  index_[height] = static_cast<uint8_t>(index);
+  while (--height >= 0) {
+    tree = tree->Edge(index)->btree();
+    node_[height] = tree;
+    index = tree->index(edge_type);
+    index_[height] = static_cast<uint8_t>(index);
+  }
+  return node_[0]->Edge(index);
+}
+
+inline CordRepBtreeNavigator::Position CordRepBtreeNavigator::Seek(
+    size_t offset) {
+  assert(btree() != nullptr);
+  int height = height_;
+  CordRepBtree* edge = node_[height];
+  if (ABSL_PREDICT_FALSE(offset >= edge->length)) return {nullptr, 0};
+  CordRepBtree::Position index = edge->IndexOf(offset);
+  index_[height] = static_cast<uint8_t>(index.index);
+  while (--height >= 0) {
+    edge = edge->Edge(index.index)->btree();
+    node_[height] = edge;
+    index = edge->IndexOf(index.n);
+    index_[height] = static_cast<uint8_t>(index.index);
+  }
+  return {edge->Edge(index.index), index.n};
+}
+
+inline CordRepBtreeNavigator::Position CordRepBtreeNavigator::InitOffset(
+    CordRepBtree* tree, size_t offset) {
+  assert(tree != nullptr);
+  if (ABSL_PREDICT_FALSE(offset >= tree->length)) return {nullptr, 0};
+  height_ = tree->height();
+  node_[height_] = tree;
+  return Seek(offset);
+}
+
+inline CordRep* CordRepBtreeNavigator::Next() {
+  CordRepBtree* edge = node_[0];
+  return index_[0] == edge->back() ? NextUp() : edge->Edge(++index_[0]);
+}
+
+inline CordRep* CordRepBtreeNavigator::Previous() {
+  CordRepBtree* edge = node_[0];
+  return index_[0] == edge->begin() ? PreviousUp() : edge->Edge(--index_[0]);
+}
+
+inline CordRep* CordRepBtreeNavigator::NextUp() {
+  assert(index_[0] == node_[0]->back());
+  CordRepBtree* edge;
+  size_t index;
+  int height = 0;
+  do {
+    if (++height > height_) return nullptr;
+    edge = node_[height];
+    index = index_[height] + 1;
+  } while (index == edge->end());
+  index_[height] = static_cast<uint8_t>(index);
+  do {
+    node_[--height] = edge = edge->Edge(index)->btree();
+    index_[height] = static_cast<uint8_t>(index = edge->begin());
+  } while (height > 0);
+  return edge->Edge(index);
+}
+
+inline CordRep* CordRepBtreeNavigator::PreviousUp() {
+  assert(index_[0] == node_[0]->begin());
+  CordRepBtree* edge;
+  size_t index;
+  int height = 0;
+  do {
+    if (++height > height_) return nullptr;
+    edge = node_[height];
+    index = index_[height];
+  } while (index == edge->begin());
+  index_[height] = static_cast<uint8_t>(--index);
+  do {
+    node_[--height] = edge = edge->Edge(index)->btree();
+    index_[height] = static_cast<uint8_t>(index = edge->back());
+  } while (height > 0);
+  return edge->Edge(index);
+}
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_NAVIGATOR_H_
diff --git a/absl/strings/internal/cord_rep_btree_navigator_test.cc b/absl/strings/internal/cord_rep_btree_navigator_test.cc
new file mode 100644
index 0000000..ce09b19
--- /dev/null
+++ b/absl/strings/internal/cord_rep_btree_navigator_test.cc
@@ -0,0 +1,325 @@
+// Copyright 2021 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/cord_rep_btree_navigator.h"
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_btree.h"
+#include "absl/strings/internal/cord_rep_test_util.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+namespace {
+
+using ::testing::Eq;
+using ::testing::Ne;
+
+using ::absl::cordrep_testing::CordRepBtreeFromFlats;
+using ::absl::cordrep_testing::CordToString;
+using ::absl::cordrep_testing::CreateFlatsFromString;
+using ::absl::cordrep_testing::CreateRandomString;
+using ::absl::cordrep_testing::MakeFlat;
+using ::absl::cordrep_testing::MakeSubstring;
+
+using ReadResult = CordRepBtreeNavigator::ReadResult;
+using Position = CordRepBtreeNavigator::Position;
+
+// CordRepBtreeNavigatorTest is a test fixture which automatically creates a
+// tree to test navigation logic on. The parameter `count' defines the number of
+// data edges in the test tree.
+class CordRepBtreeNavigatorTest : public testing::TestWithParam<int> {
+ public:
+  using Flats = std::vector<CordRep*>;
+  static constexpr size_t kCharsPerFlat = 3;
+
+  CordRepBtreeNavigatorTest() {
+    data_ = CreateRandomString(count() * kCharsPerFlat);
+    flats_ = CreateFlatsFromString(data_, kCharsPerFlat);
+
+    // Turn flat 0 or 1 into a substring to cover partial reads on substrings.
+    if (count() > 1) {
+      CordRep::Unref(flats_[1]);
+      flats_[1] = MakeSubstring(kCharsPerFlat, kCharsPerFlat, MakeFlat(data_));
+    } else {
+      CordRep::Unref(flats_[0]);
+      flats_[0] = MakeSubstring(0, kCharsPerFlat, MakeFlat(data_));
+    }
+
+    tree_ = CordRepBtreeFromFlats(flats_);
+  }
+
+  ~CordRepBtreeNavigatorTest() override { CordRep::Unref(tree_); }
+
+  int count() const { return GetParam(); }
+  CordRepBtree* tree() { return tree_; }
+  const std::string& data() const { return data_; }
+  const std::vector<CordRep*>& flats() const { return flats_; }
+
+  static std::string ToString(testing::TestParamInfo<int> param) {
+    return absl::StrCat(param.param, "_Flats");
+  }
+
+ private:
+  std::string data_;
+  Flats flats_;
+  CordRepBtree* tree_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    WithParam, CordRepBtreeNavigatorTest,
+    testing::Values(1, CordRepBtree::kMaxCapacity - 1,
+                    CordRepBtree::kMaxCapacity,
+                    CordRepBtree::kMaxCapacity* CordRepBtree::kMaxCapacity - 1,
+                    CordRepBtree::kMaxCapacity* CordRepBtree::kMaxCapacity,
+                    CordRepBtree::kMaxCapacity* CordRepBtree::kMaxCapacity + 1,
+                    CordRepBtree::kMaxCapacity* CordRepBtree::kMaxCapacity * 2 +
+                        17),
+    CordRepBtreeNavigatorTest::ToString);
+
+TEST(CordRepBtreeNavigatorTest, Uninitialized) {
+  CordRepBtreeNavigator nav;
+  EXPECT_FALSE(nav);
+  EXPECT_THAT(nav.btree(), Eq(nullptr));
+#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)
+  EXPECT_DEATH(nav.Current(), ".*");
+#endif
+}
+
+TEST_P(CordRepBtreeNavigatorTest, InitFirst) {
+  CordRepBtreeNavigator nav;
+  CordRep* edge = nav.InitFirst(tree());
+  EXPECT_TRUE(nav);
+  EXPECT_THAT(nav.btree(), Eq(tree()));
+  EXPECT_THAT(nav.Current(), Eq(flats().front()));
+  EXPECT_THAT(edge, Eq(flats().front()));
+}
+
+TEST_P(CordRepBtreeNavigatorTest, InitLast) {
+  CordRepBtreeNavigator nav;
+  CordRep* edge = nav.InitLast(tree());
+  EXPECT_TRUE(nav);
+  EXPECT_THAT(nav.btree(), Eq(tree()));
+  EXPECT_THAT(nav.Current(), Eq(flats().back()));
+  EXPECT_THAT(edge, Eq(flats().back()));
+}
+
+TEST_P(CordRepBtreeNavigatorTest, NextPrev) {
+  CordRepBtreeNavigator nav;
+  nav.InitFirst(tree());
+  const Flats& flats = this->flats();
+
+  EXPECT_THAT(nav.Previous(), Eq(nullptr));
+  EXPECT_THAT(nav.Current(), Eq(flats.front()));
+  for (int i = 1; i < flats.size(); ++i) {
+    ASSERT_THAT(nav.Next(), Eq(flats[i]));
+    EXPECT_THAT(nav.Current(), Eq(flats[i]));
+  }
+  EXPECT_THAT(nav.Next(), Eq(nullptr));
+  EXPECT_THAT(nav.Current(), Eq(flats.back()));
+  for (int i = static_cast<int>(flats.size()) - 2; i >= 0; --i) {
+    ASSERT_THAT(nav.Previous(), Eq(flats[i]));
+    EXPECT_THAT(nav.Current(), Eq(flats[i]));
+  }
+  EXPECT_THAT(nav.Previous(), Eq(nullptr));
+  EXPECT_THAT(nav.Current(), Eq(flats.front()));
+}
+
+TEST_P(CordRepBtreeNavigatorTest, PrevNext) {
+  CordRepBtreeNavigator nav;
+  nav.InitLast(tree());
+  const Flats& flats = this->flats();
+
+  EXPECT_THAT(nav.Next(), Eq(nullptr));
+  EXPECT_THAT(nav.Current(), Eq(flats.back()));
+  for (int i = static_cast<int>(flats.size()) - 2; i >= 0; --i) {
+    ASSERT_THAT(nav.Previous(), Eq(flats[i]));
+    EXPECT_THAT(nav.Current(), Eq(flats[i]));
+  }
+  EXPECT_THAT(nav.Previous(), Eq(nullptr));
+  EXPECT_THAT(nav.Current(), Eq(flats.front()));
+  for (int i = 1; i < flats.size(); ++i) {
+    ASSERT_THAT(nav.Next(), Eq(flats[i]));
+    EXPECT_THAT(nav.Current(), Eq(flats[i]));
+  }
+  EXPECT_THAT(nav.Next(), Eq(nullptr));
+  EXPECT_THAT(nav.Current(), Eq(flats.back()));
+}
+
+TEST(CordRepBtreeNavigatorTest, Reset) {
+  CordRepBtree* tree = CordRepBtree::Create(MakeFlat("abc"));
+  CordRepBtreeNavigator nav;
+  nav.InitFirst(tree);
+  nav.Reset();
+  EXPECT_FALSE(nav);
+  EXPECT_THAT(nav.btree(), Eq(nullptr));
+#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)
+  EXPECT_DEATH(nav.Current(), ".*");
+#endif
+  CordRep::Unref(tree);
+}
+
+TEST_P(CordRepBtreeNavigatorTest, Skip) {
+  int count = this->count();
+  const Flats& flats = this->flats();
+  CordRepBtreeNavigator nav;
+  nav.InitFirst(tree());
+
+  for (int char_offset = 0; char_offset < kCharsPerFlat; ++char_offset) {
+    Position pos = nav.Skip(char_offset);
+    EXPECT_THAT(pos.edge, Eq(nav.Current()));
+    EXPECT_THAT(pos.edge, Eq(flats[0]));
+    EXPECT_THAT(pos.offset, Eq(char_offset));
+  }
+
+  for (int index1 = 0; index1 < count; ++index1) {
+    for (int index2 = index1; index2 < count; ++index2) {
+      for (int char_offset = 0; char_offset < kCharsPerFlat; ++char_offset) {
+        CordRepBtreeNavigator nav;
+        nav.InitFirst(tree());
+
+        size_t length1 = index1 * kCharsPerFlat;
+        Position pos1 = nav.Skip(length1 + char_offset);
+        ASSERT_THAT(pos1.edge, Eq(flats[index1]));
+        ASSERT_THAT(pos1.edge, Eq(nav.Current()));
+        ASSERT_THAT(pos1.offset, Eq(char_offset));
+
+        size_t length2 = index2 * kCharsPerFlat;
+        Position pos2 = nav.Skip(length2 - length1 + char_offset);
+        ASSERT_THAT(pos2.edge, Eq(flats[index2]));
+        ASSERT_THAT(pos2.edge, Eq(nav.Current()));
+        ASSERT_THAT(pos2.offset, Eq(char_offset));
+      }
+    }
+  }
+}
+
+TEST_P(CordRepBtreeNavigatorTest, Seek) {
+  int count = this->count();
+  const Flats& flats = this->flats();
+  CordRepBtreeNavigator nav;
+  nav.InitFirst(tree());
+
+  for (int char_offset = 0; char_offset < kCharsPerFlat; ++char_offset) {
+    Position pos = nav.Seek(char_offset);
+    EXPECT_THAT(pos.edge, Eq(nav.Current()));
+    EXPECT_THAT(pos.edge, Eq(flats[0]));
+    EXPECT_THAT(pos.offset, Eq(char_offset));
+  }
+
+  for (int index = 0; index < count; ++index) {
+    for (int char_offset = 0; char_offset < kCharsPerFlat; ++char_offset) {
+      size_t offset = index * kCharsPerFlat + char_offset;
+      Position pos1 = nav.Seek(offset);
+      ASSERT_THAT(pos1.edge, Eq(flats[index]));
+      ASSERT_THAT(pos1.edge, Eq(nav.Current()));
+      ASSERT_THAT(pos1.offset, Eq(char_offset));
+    }
+  }
+}
+
+TEST(CordRepBtreeNavigatorTest, InitOffset) {
+  // Whitebox: InitOffset() is implemented in terms of Seek() which is
+  // exhaustively tested. Only test it initializes / forwards properly..
+  CordRepBtree* tree = CordRepBtree::Create(MakeFlat("abc"));
+  tree = CordRepBtree::Append(tree, MakeFlat("def"));
+  CordRepBtreeNavigator nav;
+  Position pos = nav.InitOffset(tree, 5);
+  EXPECT_TRUE(nav);
+  EXPECT_THAT(nav.btree(), Eq(tree));
+  EXPECT_THAT(pos.edge, Eq(tree->Edges()[1]));
+  EXPECT_THAT(pos.edge, Eq(nav.Current()));
+  EXPECT_THAT(pos.offset, Eq(2));
+  CordRep::Unref(tree);
+}
+
+TEST(CordRepBtreeNavigatorTest, InitOffsetAndSeekBeyondLength) {
+  CordRepBtree* tree1 = CordRepBtree::Create(MakeFlat("abc"));
+  CordRepBtree* tree2 = CordRepBtree::Create(MakeFlat("def"));
+
+  CordRepBtreeNavigator nav;
+  nav.InitFirst(tree1);
+  EXPECT_THAT(nav.Seek(3).edge, Eq(nullptr));
+  EXPECT_THAT(nav.Seek(100).edge, Eq(nullptr));
+  EXPECT_THAT(nav.btree(), Eq(tree1));
+  EXPECT_THAT(nav.Current(), Eq(tree1->Edges().front()));
+
+  EXPECT_THAT(nav.InitOffset(tree2, 3).edge, Eq(nullptr));
+  EXPECT_THAT(nav.InitOffset(tree2, 100).edge, Eq(nullptr));
+  EXPECT_THAT(nav.btree(), Eq(tree1));
+  EXPECT_THAT(nav.Current(), Eq(tree1->Edges().front()));
+
+  CordRep::Unref(tree1);
+  CordRep::Unref(tree2);
+}
+
+TEST_P(CordRepBtreeNavigatorTest, Read) {
+  const Flats& flats = this->flats();
+  const std::string& data = this->data();
+
+  for (size_t offset = 0; offset < data.size(); ++offset) {
+    for (size_t length = 1; length <= data.size() - offset; ++length) {
+      CordRepBtreeNavigator nav;
+      nav.InitFirst(tree());
+
+      // Skip towards edge holding offset
+      size_t edge_offset = nav.Skip(offset).offset;
+
+      // Read node
+      ReadResult result = nav.Read(edge_offset, length);
+      ASSERT_THAT(result.tree, Ne(nullptr));
+      EXPECT_THAT(result.tree->length, Eq(length));
+      if (result.tree->tag == BTREE) {
+        ASSERT_TRUE(CordRepBtree::IsValid(result.tree->btree()));
+      }
+
+      // Verify contents
+      std::string value = CordToString(result.tree);
+      EXPECT_THAT(value, Eq(data.substr(offset, length)));
+
+      // Verify 'partial last edge' reads.
+      size_t partial = (offset + length) % kCharsPerFlat;
+      ASSERT_THAT(result.n, Eq(partial));
+
+      // Verify ending position if not EOF
+      if (offset + length < data.size()) {
+        size_t index = (offset + length) / kCharsPerFlat;
+        EXPECT_THAT(nav.Current(), Eq(flats[index]));
+      }
+
+      CordRep::Unref(result.tree);
+    }
+  }
+}
+
+TEST_P(CordRepBtreeNavigatorTest, ReadBeyondLengthOfTree) {
+  CordRepBtreeNavigator nav;
+  nav.InitFirst(tree());
+  ReadResult result = nav.Read(2, tree()->length);
+  ASSERT_THAT(result.tree, Eq(nullptr));
+}
+
+}  // namespace
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cord_rep_btree_reader.cc b/absl/strings/internal/cord_rep_btree_reader.cc
new file mode 100644
index 0000000..5dc7696
--- /dev/null
+++ b/absl/strings/internal/cord_rep_btree_reader.cc
@@ -0,0 +1,68 @@
+// Copyright 2021 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/cord_rep_btree_reader.h"
+
+#include <cassert>
+
+#include "absl/base/config.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_btree.h"
+#include "absl/strings/internal/cord_rep_btree_navigator.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+absl::string_view CordRepBtreeReader::Read(size_t n, size_t chunk_size,
+                                           CordRep*& tree) {
+  assert(chunk_size <= navigator_.Current()->length);
+
+  // If chunk_size is non-zero, we need to start inside last returned edge.
+  // Else we start reading at the next data edge of the tree.
+  CordRep* edge = chunk_size ? navigator_.Current() : navigator_.Next();
+  const size_t offset = chunk_size ? edge->length - chunk_size : 0;
+
+  // Read the sub tree and verify we got what we wanted.
+  ReadResult result = navigator_.Read(offset, n);
+  tree = result.tree;
+
+  // If the data returned in `tree` was covered entirely by `chunk_size`, i.e.,
+  // read from the 'previous' edge, we did not consume any additional data, and
+  // can directly return the substring into the current data edge as the next
+  // chunk. We can easily establish from the above code that `navigator_.Next()`
+  // has not been called as that requires `chunk_size` to be zero.
+  if (n < chunk_size) return CordRepBtree::EdgeData(edge).substr(result.n);
+
+  // The amount of data taken from the last edge is `chunk_size` and `result.n`
+  // contains the offset into the current edge trailing the read data (which can
+  // be 0). As the call to `navigator_.Read()` could have consumed all remaining
+  // data, calling `navigator_.Current()` is not safe before checking if we
+  // already consumed all remaining data.
+  const size_t consumed_by_read = n - chunk_size - result.n;
+  if (consumed_by_read >= remaining_) {
+    remaining_ = 0;
+    return {};
+  }
+
+  // We did not read all data, return remaining data from current edge.
+  edge = navigator_.Current();
+  remaining_ -= consumed_by_read + edge->length;
+  return CordRepBtree::EdgeData(edge).substr(result.n);
+}
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cord_rep_btree_reader.h b/absl/strings/internal/cord_rep_btree_reader.h
new file mode 100644
index 0000000..7aa79db
--- /dev/null
+++ b/absl/strings/internal/cord_rep_btree_reader.h
@@ -0,0 +1,211 @@
+// Copyright 2021 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_CORD_REP_BTREE_READER_H_
+#define ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_READER_H_
+
+#include <cassert>
+
+#include "absl/base/config.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_btree.h"
+#include "absl/strings/internal/cord_rep_btree_navigator.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// CordRepBtreeReader implements logic to iterate over cord btrees.
+// References to the underlying data are returned as absl::string_view values.
+// The most typical use case is a forward only iteration over tree data.
+// The class also provides `Skip()`, `Seek()` and `Read()` methods similar to
+// CordRepBtreeNavigator that allow more advanced navigation.
+//
+// Example: iterate over all data inside a cord btree:
+//
+//   CordRepBtreeReader reader;
+//   for (string_view sv = reader.Init(tree); !sv.Empty(); sv = sv.Next()) {
+//     DoSomethingWithDataIn(sv);
+//   }
+//
+// All navigation methods always return the next 'chunk' of data. The class
+// assumes that all data is directly 'consumed' by the caller. For example:
+// invoking `Skip()` will skip the desired number of bytes, and directly
+// read and return the next chunk of data directly after the skipped bytes.
+//
+// Example: iterate over all data inside a btree skipping the first 100 bytes:
+//
+//   CordRepBtreeReader reader;
+//   absl::string_view sv = reader.Init(tree);
+//   if (sv.length() > 100) {
+//     sv.RemovePrefix(100);
+//   } else {
+//     sv = reader.Skip(100 - sv.length());
+//   }
+//   while (!sv.empty()) {
+//     DoSomethingWithDataIn(sv);
+//     absl::string_view sv = reader.Next();
+//   }
+//
+// It is important to notice that `remaining` is based on the end position of
+// the last data edge returned to the caller, not the cumulative data returned
+// to the caller which can be less in cases of skipping or seeking over data.
+//
+// For example, consider a cord btree with five data edges: "abc", "def", "ghi",
+// "jkl" and "mno":
+//
+//   absl::string_view sv;
+//   CordRepBtreeReader reader;
+//
+//   sv = reader.Init(tree); // sv = "abc", remaining = 12
+//   sv = reader.Skip(4);    // sv = "hi",  remaining = 6
+//   sv = reader.Skip(2);    // sv = "l",   remaining = 3
+//   sv = reader.Next();     // sv = "mno", remaining = 0
+//   sv = reader.Seek(1);    // sv = "bc", remaining = 12
+//
+class CordRepBtreeReader {
+ public:
+  using ReadResult = CordRepBtreeNavigator::ReadResult;
+  using Position = CordRepBtreeNavigator::Position;
+
+  // Returns true if this instance is not empty.
+  explicit operator bool() const { return navigator_.btree() != nullptr; }
+
+  // Returns the tree referenced by this instance or nullptr if empty.
+  CordRepBtree* btree() const { return navigator_.btree(); }
+
+  // Returns the current data edge inside the referenced btree.
+  // Requires that the current instance is not empty.
+  CordRep* node() const { return navigator_.Current(); }
+
+  // Returns the length of the referenced tree.
+  // Requires that the current instance is not empty.
+  size_t length() const;
+
+  // Returns the number of remaining bytes available for iteration, which is the
+  // number of bytes directly following the end of the last chunk returned.
+  // This value will be zero if we iterated over the last edge in the bound
+  // tree, in which case any call to Next() or Skip() will return an empty
+  // string_view reflecting the EOF state.
+  // Note that a call to `Seek()` resets `remaining` to a value based on the
+  // end position of the chunk returned by that call.
+  size_t remaining() const { return remaining_; }
+
+  // Resets this instance to an empty value.
+  void Reset() { navigator_.Reset(); }
+
+  // Initializes this instance with `tree`. `tree` must not be null.
+  // Returns a reference to the first data edge of the provided tree.
+  absl::string_view Init(CordRepBtree* tree);
+
+  // Navigates to and returns the next data edge of the referenced tree.
+  // Returns an empty string_view if an attempt is made to read beyond the end
+  // of the tree, i.e.: if `remaining()` is zero indicating an EOF condition.
+  // Requires that the current instance is not empty.
+  absl::string_view Next();
+
+  // Skips the provided amount of bytes and returns a reference to the data
+  // directly following the skipped bytes.
+  absl::string_view Skip(size_t skip);
+
+  // Reads `n` bytes into `tree`.
+  // If `chunk_size` is zero, starts reading at the next data edge. If
+  // `chunk_size` is non zero, the read starts at the last `chunk_size` bytes of
+  // the last returned data edge. Effectively, this means that the read starts
+  // at offset `consumed() - chunk_size`.
+  // Requires that `chunk_size` is less than or equal to the length of the
+  // last returned data edge. The purpose of `chunk_size` is to simplify code
+  // partially consuming a returned chunk and wanting to include the remaining
+  // bytes in the Read call. For example, the below code will read 1000 bytes of
+  // data into a cord tree if the first chunk starts with "big:":
+  //
+  //   CordRepBtreeReader reader;
+  //   absl::string_view sv = reader.Init(tree);
+  //   if (absl::StartsWith(sv, "big:")) {
+  //     CordRepBtree tree;
+  //     sv = reader.Read(1000, sv.size() - 4 /* "big:" */, &tree);
+  //   }
+  //
+  // This method will return an empty string view if all remaining data was
+  // read. If `n` exceeded the amount of remaining data this function will
+  // return an empty string view and `tree` will be set to nullptr.
+  // In both cases, `consumed` will be set to `length`.
+  absl::string_view Read(size_t n, size_t chunk_size, CordRep*& tree);
+
+  // Navigates to the chunk at offset `offset`.
+  // Returns a reference into the navigated to chunk, adjusted for the relative
+  // position of `offset` into that chunk. For example, calling `Seek(13)` on a
+  // cord tree containing 2 chunks of 10 and 20 bytes respectively will return
+  // a string view into the second chunk starting at offset 3 with a size of 17.
+  // Returns an empty string view if `offset` is equal to or greater than the
+  // length of the referenced tree.
+  absl::string_view Seek(size_t offset);
+
+ private:
+  size_t remaining_ = 0;
+  CordRepBtreeNavigator navigator_;
+};
+
+inline size_t CordRepBtreeReader::length() const {
+  assert(btree() != nullptr);
+  return btree()->length;
+}
+
+inline absl::string_view CordRepBtreeReader::Init(CordRepBtree* tree) {
+  assert(tree != nullptr);
+  const CordRep* edge = navigator_.InitFirst(tree);
+  remaining_ = tree->length - edge->length;
+  return CordRepBtree::EdgeData(edge);
+}
+
+inline absl::string_view CordRepBtreeReader::Next() {
+  if (remaining_ == 0) return {};
+  const CordRep* edge = navigator_.Next();
+  assert(edge != nullptr);
+  remaining_ -= edge->length;
+  return CordRepBtree::EdgeData(edge);
+}
+
+inline absl::string_view CordRepBtreeReader::Skip(size_t skip) {
+  // As we are always positioned on the last 'consumed' edge, we
+  // need to skip the current edge as well as `skip`.
+  const size_t edge_length = navigator_.Current()->length;
+  CordRepBtreeNavigator::Position pos = navigator_.Skip(skip + edge_length);
+  if (ABSL_PREDICT_FALSE(pos.edge == nullptr)) {
+    remaining_ = 0;
+    return {};
+  }
+  // The combined length of all edges skipped before `pos.edge` is `skip -
+  // pos.offset`, all of which are 'consumed', as well as the current edge.
+  remaining_ -= skip - pos.offset + pos.edge->length;
+  return CordRepBtree::EdgeData(pos.edge).substr(pos.offset);
+}
+
+inline absl::string_view CordRepBtreeReader::Seek(size_t offset) {
+  const CordRepBtreeNavigator::Position pos = navigator_.Seek(offset);
+  if (ABSL_PREDICT_FALSE(pos.edge == nullptr)) {
+    remaining_ = 0;
+    return {};
+  }
+  absl::string_view chunk = CordRepBtree::EdgeData(pos.edge).substr(pos.offset);
+  remaining_ = length() - offset - chunk.length();
+  return chunk;
+}
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_READER_H_
diff --git a/absl/strings/internal/cord_rep_btree_reader_test.cc b/absl/strings/internal/cord_rep_btree_reader_test.cc
new file mode 100644
index 0000000..9b27a81
--- /dev/null
+++ b/absl/strings/internal/cord_rep_btree_reader_test.cc
@@ -0,0 +1,293 @@
+// Copyright 2021 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/cord_rep_btree_reader.h"
+
+#include <iostream>
+#include <random>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/strings/cord.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_btree.h"
+#include "absl/strings/internal/cord_rep_test_util.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+namespace {
+
+using ::testing::Eq;
+using ::testing::IsEmpty;
+using ::testing::Ne;
+using ::testing::Not;
+
+using ::absl::cordrep_testing::CordRepBtreeFromFlats;
+using ::absl::cordrep_testing::MakeFlat;
+using ::absl::cordrep_testing::CordToString;
+using ::absl::cordrep_testing::CreateFlatsFromString;
+using ::absl::cordrep_testing::CreateRandomString;
+
+using ReadResult = CordRepBtreeReader::ReadResult;
+
+TEST(CordRepBtreeReaderTest, Next) {
+  constexpr size_t kChars = 3;
+  const size_t cap = CordRepBtree::kMaxCapacity;
+  int counts[] = {1, 2, cap, cap * cap, cap * cap + 1, cap * cap * 2 + 17};
+
+  for (int count : counts) {
+    std::string data = CreateRandomString(count * kChars);
+    std::vector<CordRep*> flats = CreateFlatsFromString(data, kChars);
+    CordRepBtree* node = CordRepBtreeFromFlats(flats);
+
+    CordRepBtreeReader reader;
+    size_t remaining = data.length();
+    absl::string_view chunk = reader.Init(node);
+    EXPECT_THAT(chunk, Eq(data.substr(0, chunk.length())));
+
+    remaining -= chunk.length();
+    EXPECT_THAT(reader.remaining(), Eq(remaining));
+
+    while (remaining > 0) {
+      const size_t offset = data.length() - remaining;
+      chunk = reader.Next();
+      EXPECT_THAT(chunk, Eq(data.substr(offset, chunk.length())));
+
+      remaining -= chunk.length();
+      EXPECT_THAT(reader.remaining(), Eq(remaining));
+    }
+
+    EXPECT_THAT(reader.remaining(), Eq(0));
+
+    // Verify trying to read beyond EOF returns empty string_view
+    EXPECT_THAT(reader.Next(), testing::IsEmpty());
+
+    CordRep::Unref(node);
+  }
+}
+
+TEST(CordRepBtreeReaderTest, Skip) {
+  constexpr size_t kChars = 3;
+  const size_t cap = CordRepBtree::kMaxCapacity;
+  int counts[] = {1, 2, cap, cap * cap, cap * cap + 1, cap * cap * 2 + 17};
+
+  for (int count : counts) {
+    std::string data = CreateRandomString(count * kChars);
+    std::vector<CordRep*> flats = CreateFlatsFromString(data, kChars);
+    CordRepBtree* node = CordRepBtreeFromFlats(flats);
+
+    for (size_t skip1 = 0; skip1 < data.length() - kChars; ++skip1) {
+      for (size_t skip2 = 0; skip2 < data.length() - kChars; ++skip2) {
+        CordRepBtreeReader reader;
+        size_t remaining = data.length();
+        absl::string_view chunk = reader.Init(node);
+        remaining -= chunk.length();
+
+        chunk = reader.Skip(skip1);
+        size_t offset = data.length() - remaining;
+        ASSERT_THAT(chunk, Eq(data.substr(offset + skip1, chunk.length())));
+        remaining -= chunk.length() + skip1;
+        ASSERT_THAT(reader.remaining(), Eq(remaining));
+
+        if (remaining == 0) continue;
+
+        size_t skip = std::min(remaining - 1, skip2);
+        chunk = reader.Skip(skip);
+        offset = data.length() - remaining;
+        ASSERT_THAT(chunk, Eq(data.substr(offset + skip, chunk.length())));
+      }
+    }
+
+    CordRep::Unref(node);
+  }
+}
+
+TEST(CordRepBtreeReaderTest, SkipBeyondLength) {
+  CordRepBtree* tree = CordRepBtree::Create(MakeFlat("abc"));
+  tree = CordRepBtree::Append(tree, MakeFlat("def"));
+  CordRepBtreeReader reader;
+  reader.Init(tree);
+  EXPECT_THAT(reader.Skip(100), IsEmpty());
+  EXPECT_THAT(reader.remaining(), Eq(0));
+  CordRep::Unref(tree);
+}
+
+TEST(CordRepBtreeReaderTest, Seek) {
+  constexpr size_t kChars = 3;
+  const size_t cap = CordRepBtree::kMaxCapacity;
+  int counts[] = {1, 2, cap, cap * cap, cap * cap + 1, cap * cap * 2 + 17};
+
+  for (int count : counts) {
+    std::string data = CreateRandomString(count * kChars);
+    std::vector<CordRep*> flats = CreateFlatsFromString(data, kChars);
+    CordRepBtree* node = CordRepBtreeFromFlats(flats);
+
+    for (size_t seek = 0; seek < data.length() - 1; ++seek) {
+      CordRepBtreeReader reader;
+      reader.Init(node);
+      absl::string_view chunk = reader.Seek(seek);
+      ASSERT_THAT(chunk, Not(IsEmpty()));
+      ASSERT_THAT(chunk, Eq(data.substr(seek, chunk.length())));
+      ASSERT_THAT(reader.remaining(),
+                  Eq(data.length() - seek - chunk.length()));
+    }
+
+    CordRep::Unref(node);
+  }
+}
+
+TEST(CordRepBtreeReaderTest, SeekBeyondLength) {
+  CordRepBtree* tree = CordRepBtree::Create(MakeFlat("abc"));
+  tree = CordRepBtree::Append(tree, MakeFlat("def"));
+  CordRepBtreeReader reader;
+  reader.Init(tree);
+  EXPECT_THAT(reader.Seek(6), IsEmpty());
+  EXPECT_THAT(reader.remaining(), Eq(0));
+  EXPECT_THAT(reader.Seek(100), IsEmpty());
+  EXPECT_THAT(reader.remaining(), Eq(0));
+  CordRep::Unref(tree);
+}
+
+TEST(CordRepBtreeReaderTest, Read) {
+  std::string data = "abcdefghijklmno";
+  std::vector<CordRep*> flats = CreateFlatsFromString(data, 5);
+  CordRepBtree* node = CordRepBtreeFromFlats(flats);
+
+  CordRep* tree;
+  CordRepBtreeReader reader;
+  absl::string_view chunk;
+
+  // Read zero bytes
+  chunk = reader.Init(node);
+  chunk = reader.Read(0, chunk.length(), tree);
+  EXPECT_THAT(tree, Eq(nullptr));
+  EXPECT_THAT(chunk, Eq("abcde"));
+  EXPECT_THAT(reader.remaining(), Eq(10));
+  EXPECT_THAT(reader.Next(), Eq("fghij"));
+
+  // Read in full
+  chunk = reader.Init(node);
+  chunk = reader.Read(15, chunk.length(), tree);
+  EXPECT_THAT(tree, Ne(nullptr));
+  EXPECT_THAT(CordToString(tree), Eq("abcdefghijklmno"));
+  EXPECT_THAT(chunk, Eq(""));
+  EXPECT_THAT(reader.remaining(), Eq(0));
+  CordRep::Unref(tree);
+
+  // Read < chunk bytes
+  chunk = reader.Init(node);
+  chunk = reader.Read(3, chunk.length(), tree);
+  ASSERT_THAT(tree, Ne(nullptr));
+  EXPECT_THAT(CordToString(tree), Eq("abc"));
+  EXPECT_THAT(chunk, Eq("de"));
+  EXPECT_THAT(reader.remaining(), Eq(10));
+  EXPECT_THAT(reader.Next(), Eq("fghij"));
+  CordRep::Unref(tree);
+
+  // Read < chunk bytes at offset
+  chunk = reader.Init(node);
+  chunk = reader.Read(2, chunk.length() - 2, tree);
+  ASSERT_THAT(tree, Ne(nullptr));
+  EXPECT_THAT(CordToString(tree), Eq("cd"));
+  EXPECT_THAT(chunk, Eq("e"));
+  EXPECT_THAT(reader.remaining(), Eq(10));
+  EXPECT_THAT(reader.Next(), Eq("fghij"));
+  CordRep::Unref(tree);
+
+  // Read from consumed chunk
+  chunk = reader.Init(node);
+  chunk = reader.Read(3, 0, tree);
+  ASSERT_THAT(tree, Ne(nullptr));
+  EXPECT_THAT(CordToString(tree), Eq("fgh"));
+  EXPECT_THAT(chunk, Eq("ij"));
+  EXPECT_THAT(reader.remaining(), Eq(5));
+  EXPECT_THAT(reader.Next(), Eq("klmno"));
+  CordRep::Unref(tree);
+
+  // Read across chunks
+  chunk = reader.Init(node);
+  chunk = reader.Read(12, chunk.length() - 2, tree);
+  ASSERT_THAT(tree, Ne(nullptr));
+  EXPECT_THAT(CordToString(tree), Eq("cdefghijklmn"));
+  EXPECT_THAT(chunk, Eq("o"));
+  EXPECT_THAT(reader.remaining(), Eq(0));
+  CordRep::Unref(tree);
+
+  // Read across chunks landing on exact edge boundary
+  chunk = reader.Init(node);
+  chunk = reader.Read(10 - 2, chunk.length() - 2, tree);
+  ASSERT_THAT(tree, Ne(nullptr));
+  EXPECT_THAT(CordToString(tree), Eq("cdefghij"));
+  EXPECT_THAT(chunk, Eq("klmno"));
+  EXPECT_THAT(reader.remaining(), Eq(0));
+  CordRep::Unref(tree);
+
+  CordRep::Unref(node);
+}
+
+TEST(CordRepBtreeReaderTest, ReadExhaustive) {
+  constexpr size_t kChars = 3;
+  const size_t cap = CordRepBtree::kMaxCapacity;
+  int counts[] = {1, 2, cap, cap * cap + 1, cap * cap * cap * 2 + 17};
+
+  for (int count : counts) {
+    std::string data = CreateRandomString(count * kChars);
+    std::vector<CordRep*> flats = CreateFlatsFromString(data, kChars);
+    CordRepBtree* node = CordRepBtreeFromFlats(flats);
+
+    for (size_t read_size : {kChars - 1, kChars, kChars + 7, cap * cap}) {
+      CordRepBtreeReader reader;
+      absl::string_view chunk = reader.Init(node);
+
+      // `consumed` tracks the end of last consumed chunk which is the start of
+      // the next chunk: we always read with `chunk_size = chunk.length()`.
+      size_t consumed = 0;
+      size_t remaining = data.length();
+      while (remaining > 0) {
+        CordRep* tree;
+        size_t n = (std::min)(remaining, read_size);
+        chunk = reader.Read(n, chunk.length(), tree);
+        EXPECT_THAT(tree, Ne(nullptr));
+        if (tree) {
+          EXPECT_THAT(CordToString(tree), Eq(data.substr(consumed, n)));
+          CordRep::Unref(tree);
+        }
+
+        consumed += n;
+        remaining -= n;
+        EXPECT_THAT(reader.remaining(), Eq(remaining - chunk.length()));
+
+        if (remaining > 0) {
+          ASSERT_FALSE(chunk.empty());
+          ASSERT_THAT(chunk, Eq(data.substr(consumed, chunk.length())));
+        } else {
+          ASSERT_TRUE(chunk.empty()) << chunk;
+        }
+      }
+    }
+
+    CordRep::Unref(node);
+  }
+}
+
+}  // namespace
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cord_rep_btree_test.cc b/absl/strings/internal/cord_rep_btree_test.cc
new file mode 100644
index 0000000..be9473d
--- /dev/null
+++ b/absl/strings/internal/cord_rep_btree_test.cc
@@ -0,0 +1,1489 @@
+// Copyright 2021 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/cord_rep_btree.h"
+
+#include <cmath>
+#include <deque>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/cleanup/cleanup.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_test_util.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+class CordRepBtreeTestPeer {
+ public:
+  static void SetEdge(CordRepBtree* node, size_t idx, CordRep* edge) {
+    node->edges_[idx] = edge;
+  }
+  static void AddEdge(CordRepBtree* node, CordRep* edge) {
+    node->edges_[node->fetch_add_end(1)] = edge;
+  }
+};
+
+namespace {
+
+using ::absl::cordrep_testing::AutoUnref;
+using ::absl::cordrep_testing::CordCollectRepsIf;
+using ::absl::cordrep_testing::CordToString;
+using ::absl::cordrep_testing::CordVisitReps;
+using ::absl::cordrep_testing::CreateFlatsFromString;
+using ::absl::cordrep_testing::CreateRandomString;
+using ::absl::cordrep_testing::MakeConcat;
+using ::absl::cordrep_testing::MakeExternal;
+using ::absl::cordrep_testing::MakeFlat;
+using ::absl::cordrep_testing::MakeSubstring;
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::AnyOf;
+using ::testing::Conditional;
+using ::testing::ElementsAre;
+using ::testing::ElementsAreArray;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::Le;
+using ::testing::Ne;
+using ::testing::Not;
+using ::testing::SizeIs;
+using ::testing::TypedEq;
+
+MATCHER_P(EqFlatHolding, data, "Equals flat holding data") {
+  if (arg->tag < FLAT) {
+    *result_listener << "Expected FLAT, got tag " << static_cast<int>(arg->tag);
+    return false;
+  }
+  std::string actual = CordToString(arg);
+  if (actual != data) {
+    *result_listener << "Expected flat holding \"" << data
+                     << "\", got flat holding \"" << actual << "\"";
+    return false;
+  }
+  return true;
+}
+
+MATCHER_P(IsNode, height, absl::StrCat("Is a valid node of height ", height)) {
+  if (arg == nullptr) {
+    *result_listener << "Expected NODE, got nullptr";
+    return false;
+  }
+  if (arg->tag != BTREE) {
+    *result_listener << "Expected NODE, got " << static_cast<int>(arg->tag);
+    return false;
+  }
+  if (!CordRepBtree::IsValid(arg->btree())) {
+    CordRepBtree::Dump(arg->btree(), "Expected valid NODE, got:", false,
+                       *result_listener->stream());
+    return false;
+  }
+  if (arg->btree()->height() != height) {
+    *result_listener << "Expected NODE of height " << height << ", got "
+                     << arg->btree()->height();
+    return false;
+  }
+  return true;
+}
+
+MATCHER_P2(IsSubstring, start, length,
+           absl::StrCat("Is a substring(start = ", start, ", length = ", length,
+                        ")")) {
+  if (arg == nullptr) {
+    *result_listener << "Expected substring, got nullptr";
+    return false;
+  }
+  if (arg->tag != SUBSTRING) {
+    *result_listener << "Expected SUBSTRING, got "
+                     << static_cast<int>(arg->tag);
+    return false;
+  }
+  const CordRepSubstring* const substr = arg->substring();
+  if (substr->start != start || substr->length != length) {
+    *result_listener << "Expected substring(" << start << ", " << length
+                     << "), got substring(" << substr->start << ", "
+                     << substr->length << ")";
+    return false;
+  }
+  return true;
+}
+
+// DataConsumer is a simple helper class used by tests to 'consume' string
+// fragments from the provided input in forward or backward direction.
+class DataConsumer {
+ public:
+  // Starts consumption of `data`. Caller must make sure `data` outlives this
+  // instance. Consumes data starting at the front if `forward` is true, else
+  // consumes data from the back.
+  DataConsumer(absl::string_view data, bool forward)
+      : data_(data), forward_(forward) {}
+
+  // Return the next `n` bytes from referenced data.
+  absl::string_view Next(size_t n) {
+    assert(n <= data_.size() - consumed_);
+    consumed_ += n;
+    return data_.substr(forward_ ? consumed_ - n : data_.size() - consumed_, n);
+  }
+
+  // Returns all data consumed so far.
+  absl::string_view Consumed() const {
+    return forward_ ? data_.substr(0, consumed_)
+                    : data_.substr(data_.size() - consumed_);
+  }
+
+ private:
+  absl::string_view data_;
+  size_t consumed_ = 0;
+  bool forward_;
+};
+
+// BtreeAdd returns either CordRepBtree::Append or CordRepBtree::Prepend.
+CordRepBtree* BtreeAdd(CordRepBtree* node, bool append,
+                       absl::string_view data) {
+  return append ? CordRepBtree::Append(node, data)
+                : CordRepBtree::Prepend(node, data);
+}
+
+// Recursively collects all leaf edges from `tree` and appends them to `edges`.
+void GetLeafEdges(const CordRepBtree* tree, std::vector<CordRep*>& edges) {
+  if (tree->height() == 0) {
+    for (CordRep* edge : tree->Edges()) {
+      edges.push_back(edge);
+    }
+  } else {
+    for (CordRep* edge : tree->Edges()) {
+      GetLeafEdges(edge->btree(), edges);
+    }
+  }
+}
+
+// Recursively collects and returns all leaf edges from `tree`.
+std::vector<CordRep*> GetLeafEdges(const CordRepBtree* tree) {
+  std::vector<CordRep*> edges;
+  GetLeafEdges(tree, edges);
+  return edges;
+}
+
+// Creates a flat containing the hexadecimal value of `i` zero padded
+// to at least 4 digits prefixed with "0x", e.g.: "0x04AC".
+CordRepFlat* MakeHexFlat(size_t i) {
+  return MakeFlat(absl::StrCat("0x", absl::Hex(i, absl::kZeroPad4)));
+}
+
+CordRepBtree* MakeLeaf(size_t size = CordRepBtree::kMaxCapacity) {
+  assert(size <= CordRepBtree::kMaxCapacity);
+  CordRepBtree* leaf = CordRepBtree::Create(MakeHexFlat(0));
+  for (size_t i = 1; i < size; ++i) {
+    leaf = CordRepBtree::Append(leaf, MakeHexFlat(i));
+  }
+  return leaf;
+}
+
+CordRepBtree* MakeTree(size_t size, bool append = true) {
+  CordRepBtree* tree = CordRepBtree::Create(MakeHexFlat(0));
+  for (size_t i = 1; i < size; ++i) {
+    tree = append ? CordRepBtree::Append(tree, MakeHexFlat(i))
+                  : CordRepBtree::Prepend(tree, MakeHexFlat(i));
+  }
+  return tree;
+}
+
+CordRepBtree* CreateTree(absl::Span<CordRep* const> reps) {
+  auto it = reps.begin();
+  CordRepBtree* tree = CordRepBtree::Create(*it);
+  while (++it != reps.end()) tree = CordRepBtree::Append(tree, *it);
+  return tree;
+}
+
+CordRepBtree* CreateTree(absl::string_view data, size_t chunk_size) {
+  return CreateTree(CreateFlatsFromString(data, chunk_size));
+}
+
+CordRepBtree* CreateTreeReverse(absl::string_view data, size_t chunk_size) {
+  std::vector<CordRep*> flats = CreateFlatsFromString(data, chunk_size);
+  auto rit = flats.rbegin();
+  CordRepBtree* tree = CordRepBtree::Create(*rit);
+  while (++rit != flats.rend()) tree = CordRepBtree::Prepend(tree, *rit);
+  return tree;
+}
+
+class CordRepBtreeTest : public testing::TestWithParam<bool> {
+ public:
+  bool shared() const { return GetParam(); }
+
+  static std::string ToString(testing::TestParamInfo<bool> param) {
+    return param.param ? "Shared" : "Private";
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(WithParam, CordRepBtreeTest, testing::Bool(),
+                         CordRepBtreeTest::ToString);
+
+class CordRepBtreeHeightTest : public testing::TestWithParam<int> {
+ public:
+  int height() const { return GetParam(); }
+
+  static std::string ToString(testing::TestParamInfo<int> param) {
+    return absl::StrCat(param.param);
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(WithHeights, CordRepBtreeHeightTest,
+                         testing::Range(0, CordRepBtree::kMaxHeight),
+                         CordRepBtreeHeightTest::ToString);
+
+using TwoBools = testing::tuple<bool, bool>;
+
+class CordRepBtreeDualTest : public testing::TestWithParam<TwoBools> {
+ public:
+  bool first_shared() const { return std::get<0>(GetParam()); }
+  bool second_shared() const { return std::get<1>(GetParam()); }
+
+  static std::string ToString(testing::TestParamInfo<TwoBools> param) {
+    if (std::get<0>(param.param)) {
+      return std::get<1>(param.param) ? "BothShared" : "FirstShared";
+    }
+    return std::get<1>(param.param) ? "SecondShared" : "Private";
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(WithParam, CordRepBtreeDualTest,
+                         testing::Combine(testing::Bool(), testing::Bool()),
+                         CordRepBtreeDualTest::ToString);
+
+TEST(CordRepBtreeTest, SizeIsMultipleOf64) {
+  // Only enforce for fully 64-bit platforms.
+  if (sizeof(size_t) == 8 && sizeof(void*) == 8) {
+    EXPECT_THAT(sizeof(CordRepBtree) % 64, Eq(0)) << "Should be multiple of 64";
+  }
+}
+
+TEST(CordRepBtreeTest, NewDestroyEmptyTree) {
+  auto* tree = CordRepBtree::New();
+  EXPECT_THAT(tree->size(), Eq(0));
+  EXPECT_THAT(tree->height(), Eq(0));
+  EXPECT_THAT(tree->Edges(), ElementsAre());
+  CordRepBtree::Destroy(tree);
+}
+
+TEST(CordRepBtreeTest, NewDestroyEmptyTreeAtHeight) {
+  auto* tree = CordRepBtree::New(3);
+  EXPECT_THAT(tree->size(), Eq(0));
+  EXPECT_THAT(tree->height(), Eq(3));
+  EXPECT_THAT(tree->Edges(), ElementsAre());
+  CordRepBtree::Destroy(tree);
+}
+
+TEST(CordRepBtreeTest, Btree) {
+  CordRep* rep = CordRepBtree::New();
+  EXPECT_THAT(rep->btree(), Eq(rep));
+  EXPECT_THAT(static_cast<const CordRep*>(rep)->btree(), Eq(rep));
+  CordRep::Unref(rep);
+#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)
+  rep = MakeFlat("Hello world");
+  EXPECT_DEATH(rep->btree(), ".*");
+  EXPECT_DEATH(static_cast<const CordRep*>(rep)->btree(), ".*");
+  CordRep::Unref(rep);
+#endif
+}
+
+TEST(CordRepBtreeTest, EdgeData) {
+  CordRepFlat* flat = MakeFlat("Hello world");
+  CordRepExternal* external = MakeExternal("Hello external");
+  CordRep* substr1 = MakeSubstring(1, 6, CordRep::Ref(flat));
+  CordRep* substr2 = MakeSubstring(1, 6, CordRep::Ref(external));
+  CordRep* concat = MakeConcat(CordRep::Ref(flat), CordRep::Ref(external));
+  CordRep* bad_substr = MakeSubstring(1, 2, CordRep::Ref(substr1));
+
+  EXPECT_TRUE(CordRepBtree::IsDataEdge(flat));
+  EXPECT_THAT(CordRepBtree::EdgeDataPtr(flat),
+              TypedEq<const void*>(flat->Data()));
+  EXPECT_THAT(CordRepBtree::EdgeData(flat), Eq("Hello world"));
+
+  EXPECT_TRUE(CordRepBtree::IsDataEdge(external));
+  EXPECT_THAT(CordRepBtree::EdgeDataPtr(external),
+              TypedEq<const void*>(external->base));
+  EXPECT_THAT(CordRepBtree::EdgeData(external), Eq("Hello external"));
+
+  EXPECT_TRUE(CordRepBtree::IsDataEdge(substr1));
+  EXPECT_THAT(CordRepBtree::EdgeDataPtr(substr1),
+              TypedEq<const void*>(flat->Data() + 1));
+  EXPECT_THAT(CordRepBtree::EdgeData(substr1), Eq("ello w"));
+
+  EXPECT_TRUE(CordRepBtree::IsDataEdge(substr2));
+  EXPECT_THAT(CordRepBtree::EdgeDataPtr(substr2),
+              TypedEq<const void*>(external->base + 1));
+  EXPECT_THAT(CordRepBtree::EdgeData(substr2), Eq("ello e"));
+
+  EXPECT_FALSE(CordRepBtree::IsDataEdge(concat));
+  EXPECT_FALSE(CordRepBtree::IsDataEdge(bad_substr));
+#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)
+  EXPECT_DEATH(CordRepBtree::EdgeData(concat), ".*");
+  EXPECT_DEATH(CordRepBtree::EdgeDataPtr(concat), ".*");
+  EXPECT_DEATH(CordRepBtree::EdgeData(bad_substr), ".*");
+  EXPECT_DEATH(CordRepBtree::EdgeDataPtr(bad_substr), ".*");
+#endif
+
+  CordRep::Unref(bad_substr);
+  CordRep::Unref(concat);
+  CordRep::Unref(substr2);
+  CordRep::Unref(substr1);
+  CordRep::Unref(external);
+  CordRep::Unref(flat);
+}
+
+TEST(CordRepBtreeTest, CreateUnrefLeaf) {
+  auto* flat = MakeFlat("a");
+  auto* leaf = CordRepBtree::Create(flat);
+  EXPECT_THAT(leaf->size(), Eq(1));
+  EXPECT_THAT(leaf->height(), Eq(0));
+  EXPECT_THAT(leaf->Edges(), ElementsAre(flat));
+  CordRepBtree::Unref(leaf);
+}
+
+TEST(CordRepBtreeTest, NewUnrefNode) {
+  auto* leaf = CordRepBtree::Create(MakeFlat("a"));
+  CordRepBtree* tree = CordRepBtree::New(leaf);
+  EXPECT_THAT(tree->size(), Eq(1));
+  EXPECT_THAT(tree->height(), Eq(1));
+  EXPECT_THAT(tree->Edges(), ElementsAre(leaf));
+  CordRepBtree::Unref(tree);
+}
+
+TEST_P(CordRepBtreeTest, AppendToLeafToCapacity) {
+  AutoUnref refs;
+  std::vector<CordRep*> flats;
+  flats.push_back(MakeHexFlat(0));
+  auto* leaf = CordRepBtree::Create(flats.back());
+
+  for (size_t i = 1; i < CordRepBtree::kMaxCapacity; ++i) {
+    refs.RefIf(shared(), leaf);
+    flats.push_back(MakeHexFlat(i));
+    auto* result = CordRepBtree::Append(leaf, flats.back());
+    EXPECT_THAT(result->height(), Eq(0));
+    EXPECT_THAT(result, Conditional(shared(), Ne(leaf), Eq(leaf)));
+    EXPECT_THAT(result->Edges(), ElementsAreArray(flats));
+    leaf = result;
+  }
+  CordRep::Unref(leaf);
+}
+
+TEST_P(CordRepBtreeTest, PrependToLeafToCapacity) {
+  AutoUnref refs;
+  std::deque<CordRep*> flats;
+  flats.push_front(MakeHexFlat(0));
+  auto* leaf = CordRepBtree::Create(flats.front());
+
+  for (size_t i = 1; i < CordRepBtree::kMaxCapacity; ++i) {
+    refs.RefIf(shared(), leaf);
+    flats.push_front(MakeHexFlat(i));
+    auto* result = CordRepBtree::Prepend(leaf, flats.front());
+    EXPECT_THAT(result->height(), Eq(0));
+    EXPECT_THAT(result, Conditional(shared(), Ne(leaf), Eq(leaf)));
+    EXPECT_THAT(result->Edges(), ElementsAreArray(flats));
+    leaf = result;
+  }
+  CordRep::Unref(leaf);
+}
+
+// This test specifically aims at code aligning data at either the front or the
+// back of the contained `edges[]` array, alternating Append and Prepend will
+// move `begin()` and `end()` values as needed for each added value.
+TEST_P(CordRepBtreeTest, AppendPrependToLeafToCapacity) {
+  AutoUnref refs;
+  std::deque<CordRep*> flats;
+  flats.push_front(MakeHexFlat(0));
+  auto* leaf = CordRepBtree::Create(flats.front());
+
+  for (size_t i = 1; i < CordRepBtree::kMaxCapacity; ++i) {
+    refs.RefIf(shared(), leaf);
+    CordRepBtree* result;
+    if (i % 2 != 0) {
+      flats.push_front(MakeHexFlat(i));
+      result = CordRepBtree::Prepend(leaf, flats.front());
+    } else {
+      flats.push_back(MakeHexFlat(i));
+      result = CordRepBtree::Append(leaf, flats.back());
+    }
+    EXPECT_THAT(result->height(), Eq(0));
+    EXPECT_THAT(result, Conditional(shared(), Ne(leaf), Eq(leaf)));
+    EXPECT_THAT(result->Edges(), ElementsAreArray(flats));
+    leaf = result;
+  }
+  CordRep::Unref(leaf);
+}
+
+TEST_P(CordRepBtreeTest, AppendToLeafBeyondCapacity) {
+  AutoUnref refs;
+  auto* leaf = MakeLeaf();
+  refs.RefIf(shared(), leaf);
+  CordRep* flat = MakeFlat("abc");
+  auto* result = CordRepBtree::Append(leaf, flat);
+  ASSERT_THAT(result, IsNode(1));
+  EXPECT_THAT(result, Ne(leaf));
+  absl::Span<CordRep* const> edges = result->Edges();
+  ASSERT_THAT(edges, ElementsAre(leaf, IsNode(0)));
+  EXPECT_THAT(edges[1]->btree()->Edges(), ElementsAre(flat));
+  CordRep::Unref(result);
+}
+
+TEST_P(CordRepBtreeTest, PrependToLeafBeyondCapacity) {
+  AutoUnref refs;
+  auto* leaf = MakeLeaf();
+  refs.RefIf(shared(), leaf);
+  CordRep* flat = MakeFlat("abc");
+  auto* result = CordRepBtree::Prepend(leaf, flat);
+  ASSERT_THAT(result, IsNode(1));
+  EXPECT_THAT(result, Ne(leaf));
+  absl::Span<CordRep* const> edges = result->Edges();
+  ASSERT_THAT(edges, ElementsAre(IsNode(0), leaf));
+  EXPECT_THAT(edges[0]->btree()->Edges(), ElementsAre(flat));
+  CordRep::Unref(result);
+}
+
+TEST_P(CordRepBtreeTest, AppendToTreeOneDeep) {
+  constexpr size_t max_cap = CordRepBtree::kMaxCapacity;
+  AutoUnref refs;
+  std::vector<CordRep*> flats;
+  flats.push_back(MakeHexFlat(0));
+  CordRepBtree* tree = CordRepBtree::Create(flats.back());
+  for (size_t i = 1; i <= max_cap; ++i) {
+    flats.push_back(MakeHexFlat(i));
+    tree = CordRepBtree::Append(tree, flats.back());
+  }
+  ASSERT_THAT(tree, IsNode(1));
+
+  for (size_t i = max_cap + 1; i < max_cap * max_cap; ++i) {
+    // Ref top level tree based on param.
+    // Ref leaf node once every 4 iterations, which should not have an
+    // observable effect other than that the leaf itself is copied.
+    refs.RefIf(shared(), tree);
+    refs.RefIf(i % 4 == 0, tree->Edges().back());
+
+    flats.push_back(MakeHexFlat(i));
+    CordRepBtree* result = CordRepBtree::Append(tree, flats.back());
+    ASSERT_THAT(result, IsNode(1));
+    ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree)));
+    std::vector<CordRep*> edges = GetLeafEdges(result);
+    ASSERT_THAT(edges, ElementsAreArray(flats));
+    tree = result;
+  }
+  CordRep::Unref(tree);
+}
+
+TEST_P(CordRepBtreeTest, AppendToTreeTwoDeep) {
+  constexpr size_t max_cap = CordRepBtree::kMaxCapacity;
+  AutoUnref refs;
+  std::vector<CordRep*> flats;
+  flats.push_back(MakeHexFlat(0));
+  CordRepBtree* tree = CordRepBtree::Create(flats.back());
+  for (size_t i = 1; i <= max_cap * max_cap; ++i) {
+    flats.push_back(MakeHexFlat(i));
+    tree = CordRepBtree::Append(tree, flats.back());
+  }
+  ASSERT_THAT(tree, IsNode(2));
+  for (size_t i = max_cap * max_cap + 1; i < max_cap * max_cap * max_cap; ++i) {
+    // Ref top level tree based on param.
+    // Ref child node once every 16 iterations, and leaf node every 4
+    // iterrations which  which should not have an observable effect other than
+    //  the node and/or the leaf below it being copied.
+    refs.RefIf(shared(), tree);
+    refs.RefIf(i % 16 == 0, tree->Edges().back());
+    refs.RefIf(i % 4 == 0, tree->Edges().back()->btree()->Edges().back());
+
+    flats.push_back(MakeHexFlat(i));
+    CordRepBtree* result = CordRepBtree::Append(tree, flats.back());
+    ASSERT_THAT(result, IsNode(2));
+    ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree)));
+    std::vector<CordRep*> edges = GetLeafEdges(result);
+    ASSERT_THAT(edges, ElementsAreArray(flats));
+    tree = result;
+  }
+  CordRep::Unref(tree);
+}
+
+TEST_P(CordRepBtreeTest, PrependToTreeOneDeep) {
+  constexpr size_t max_cap = CordRepBtree::kMaxCapacity;
+  AutoUnref refs;
+  std::deque<CordRep*> flats;
+  flats.push_back(MakeHexFlat(0));
+  CordRepBtree* tree = CordRepBtree::Create(flats.back());
+  for (size_t i = 1; i <= max_cap; ++i) {
+    flats.push_front(MakeHexFlat(i));
+    tree = CordRepBtree::Prepend(tree, flats.front());
+  }
+  ASSERT_THAT(tree, IsNode(1));
+
+  for (size_t i = max_cap + 1; i < max_cap * max_cap; ++i) {
+    // Ref top level tree based on param.
+    // Ref leaf node once every 4 iterations which should not have an observable
+    // effect other than than the leaf itself is copied.
+    refs.RefIf(shared(), tree);
+    refs.RefIf(i % 4 == 0, tree->Edges().back());
+
+    flats.push_front(MakeHexFlat(i));
+    CordRepBtree* result = CordRepBtree::Prepend(tree, flats.front());
+    ASSERT_THAT(result, IsNode(1));
+    ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree)));
+    std::vector<CordRep*> edges = GetLeafEdges(result);
+    ASSERT_THAT(edges, ElementsAreArray(flats));
+    tree = result;
+  }
+  CordRep::Unref(tree);
+}
+
+TEST_P(CordRepBtreeTest, PrependToTreeTwoDeep) {
+  constexpr size_t max_cap = CordRepBtree::kMaxCapacity;
+  AutoUnref refs;
+  std::deque<CordRep*> flats;
+  flats.push_back(MakeHexFlat(0));
+  CordRepBtree* tree = CordRepBtree::Create(flats.back());
+  for (size_t i = 1; i <= max_cap * max_cap; ++i) {
+    flats.push_front(MakeHexFlat(i));
+    tree = CordRepBtree::Prepend(tree, flats.front());
+  }
+  ASSERT_THAT(tree, IsNode(2));
+  for (size_t i = max_cap * max_cap + 1; i < max_cap * max_cap * max_cap; ++i) {
+    // Ref top level tree based on param.
+    // Ref child node once every 16 iterations, and leaf node every 4
+    // iterrations which  which should not have an observable effect other than
+    //  the node and/or the leaf below it being copied.
+    refs.RefIf(shared(), tree);
+    refs.RefIf(i % 16 == 0, tree->Edges().back());
+    refs.RefIf(i % 4 == 0, tree->Edges().back()->btree()->Edges().back());
+
+    flats.push_front(MakeHexFlat(i));
+    CordRepBtree* result = CordRepBtree::Prepend(tree, flats.front());
+    ASSERT_THAT(result, IsNode(2));
+    ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree)));
+    std::vector<CordRep*> edges = GetLeafEdges(result);
+    ASSERT_THAT(edges, ElementsAreArray(flats));
+    tree = result;
+  }
+  CordRep::Unref(tree);
+}
+
+TEST_P(CordRepBtreeDualTest, MergeLeafsNotExceedingCapacity) {
+  for (bool use_append : {false, true}) {
+    SCOPED_TRACE(use_append ? "Using Append" : "Using Prepend");
+
+    AutoUnref refs;
+    std::vector<CordRep*> flats;
+
+    // Build `left` side leaf appending all contained flats to `flats`
+    CordRepBtree* left = MakeLeaf(3);
+    GetLeafEdges(left, flats);
+    refs.RefIf(first_shared(), left);
+
+    // Build `right` side leaf appending all contained flats to `flats`
+    CordRepBtree* right = MakeLeaf(2);
+    GetLeafEdges(right, flats);
+    refs.RefIf(second_shared(), right);
+
+    CordRepBtree* tree = use_append ? CordRepBtree::Append(left, right)
+                                    : CordRepBtree::Prepend(right, left);
+    EXPECT_THAT(tree, IsNode(0));
+
+    // `tree` contains all flats originally belonging to `left` and `right`.
+    EXPECT_THAT(tree->Edges(), ElementsAreArray(flats));
+    CordRepBtree::Unref(tree);
+  }
+}
+
+TEST_P(CordRepBtreeDualTest, MergeLeafsExceedingCapacity) {
+  for (bool use_append : {false, true}) {
+    SCOPED_TRACE(use_append ? "Using Append" : "Using Prepend");
+
+    AutoUnref refs;
+
+    // Build `left` side tree appending all contained flats to `flats`
+    CordRepBtree* left = MakeLeaf(CordRepBtree::kMaxCapacity - 2);
+    refs.RefIf(first_shared(), left);
+
+    // Build `right` side tree appending all contained flats to `flats`
+    CordRepBtree* right = MakeLeaf(CordRepBtree::kMaxCapacity - 1);
+    refs.RefIf(second_shared(), right);
+
+    CordRepBtree* tree = use_append ? CordRepBtree::Append(left, right)
+                                    : CordRepBtree::Prepend(right, left);
+    EXPECT_THAT(tree, IsNode(1));
+    EXPECT_THAT(tree->Edges(), ElementsAre(left, right));
+    CordRepBtree::Unref(tree);
+  }
+}
+
+TEST_P(CordRepBtreeDualTest, MergeEqualHeightTrees) {
+  for (bool use_append : {false, true}) {
+    SCOPED_TRACE(use_append ? "Using Append" : "Using Prepend");
+
+    AutoUnref refs;
+    std::vector<CordRep*> flats;
+
+    // Build `left` side tree appending all contained flats to `flats`
+    CordRepBtree* left = MakeTree(CordRepBtree::kMaxCapacity * 3);
+    GetLeafEdges(left, flats);
+    refs.RefIf(first_shared(), left);
+
+    // Build `right` side tree appending all contained flats to `flats`
+    CordRepBtree* right = MakeTree(CordRepBtree::kMaxCapacity * 2);
+    GetLeafEdges(right, flats);
+    refs.RefIf(second_shared(), right);
+
+    CordRepBtree* tree = use_append ? CordRepBtree::Append(left, right)
+                                    : CordRepBtree::Prepend(right, left);
+    EXPECT_THAT(tree, IsNode(1));
+    EXPECT_THAT(tree->Edges(), SizeIs(5));
+
+    // `tree` contains all flats originally belonging to `left` and `right`.
+    EXPECT_THAT(GetLeafEdges(tree), ElementsAreArray(flats));
+    CordRepBtree::Unref(tree);
+  }
+}
+
+TEST_P(CordRepBtreeDualTest, MergeLeafWithTreeNotExceedingLeafCapacity) {
+  for (bool use_append : {false, true}) {
+    SCOPED_TRACE(use_append ? "Using Append" : "Using Prepend");
+
+    AutoUnref refs;
+    std::vector<CordRep*> flats;
+
+    // Build `left` side tree appending all added flats to `flats`
+    CordRepBtree* left = MakeTree(CordRepBtree::kMaxCapacity * 2 + 2);
+    GetLeafEdges(left, flats);
+    refs.RefIf(first_shared(), left);
+
+    // Build `right` side tree appending all added flats to `flats`
+    CordRepBtree* right = MakeTree(3);
+    GetLeafEdges(right, flats);
+    refs.RefIf(second_shared(), right);
+
+    CordRepBtree* tree = use_append ? CordRepBtree::Append(left, right)
+                                    : CordRepBtree::Prepend(right, left);
+    EXPECT_THAT(tree, IsNode(1));
+    EXPECT_THAT(tree->Edges(), SizeIs(3));
+
+    // `tree` contains all flats originally belonging to `left` and `right`.
+    EXPECT_THAT(GetLeafEdges(tree), ElementsAreArray(flats));
+    CordRepBtree::Unref(tree);
+  }
+}
+
+TEST_P(CordRepBtreeDualTest, MergeLeafWithTreeExceedingLeafCapacity) {
+  for (bool use_append : {false, true}) {
+    SCOPED_TRACE(use_append ? "Using Append" : "Using Prepend");
+
+    AutoUnref refs;
+    std::vector<CordRep*> flats;
+
+    // Build `left` side tree appending all added flats to `flats`
+    CordRepBtree* left = MakeTree(CordRepBtree::kMaxCapacity * 3 - 2);
+    GetLeafEdges(left, flats);
+    refs.RefIf(first_shared(), left);
+
+    // Build `right` side tree appending all added flats to `flats`
+    CordRepBtree* right = MakeTree(3);
+    GetLeafEdges(right, flats);
+    refs.RefIf(second_shared(), right);
+
+    CordRepBtree* tree = use_append ? CordRepBtree::Append(left, right)
+                                    : CordRepBtree::Prepend(right, left);
+    EXPECT_THAT(tree, IsNode(1));
+    EXPECT_THAT(tree->Edges(), SizeIs(4));
+
+    // `tree` contains all flats originally belonging to `left` and `right`.
+    EXPECT_THAT(GetLeafEdges(tree), ElementsAreArray(flats));
+    CordRepBtree::Unref(tree);
+  }
+}
+
+void RefEdgesAt(size_t depth, AutoUnref& refs, CordRepBtree* tree) {
+  absl::Span<CordRep* const> edges = tree->Edges();
+  if (depth == 0) {
+    refs.Ref(edges.front());
+    refs.Ref(edges.back());
+  } else {
+    assert(tree->height() > 0);
+    RefEdgesAt(depth - 1, refs, edges.front()->btree());
+    RefEdgesAt(depth - 1, refs, edges.back()->btree());
+  }
+}
+
+TEST(CordRepBtreeTest, MergeFuzzTest) {
+  constexpr size_t max_cap = CordRepBtree::kMaxCapacity;
+  std::minstd_rand rnd;
+  std::uniform_int_distribution<int> coin_flip(0, 1);
+  std::uniform_int_distribution<int> dice_throw(1, 6);
+
+  auto random_leaf_count = [&]() {
+    std::uniform_int_distribution<int> dist_height(0, 3);
+    std::uniform_int_distribution<int> dist_leaf(0, max_cap - 1);
+    const size_t height = dist_height(rnd);
+    return (height ? pow(max_cap, height) : 0) + dist_leaf(rnd);
+  };
+
+  for (int i = 0; i < 10000; ++i) {
+    AutoUnref refs;
+    std::vector<CordRep*> flats;
+
+    CordRepBtree* left = MakeTree(random_leaf_count(), coin_flip(rnd));
+    GetLeafEdges(left, flats);
+    if (dice_throw(rnd) == 1) {
+      std::uniform_int_distribution<int> dist(0, left->height());
+      RefEdgesAt(dist(rnd), refs, left);
+    }
+
+    CordRepBtree* right = MakeTree(random_leaf_count(), coin_flip(rnd));
+    GetLeafEdges(right, flats);
+    if (dice_throw(rnd) == 1) {
+      std::uniform_int_distribution<int> dist(0, right->height());
+      RefEdgesAt(dist(rnd), refs, right);
+    }
+
+    CordRepBtree* tree = CordRepBtree::Append(left, right);
+    EXPECT_THAT(GetLeafEdges(tree), ElementsAreArray(flats));
+    CordRepBtree::Unref(tree);
+  }
+}
+
+TEST_P(CordRepBtreeTest, RemoveSuffix) {
+  // Create tree of 1, 2 and 3 levels high
+  constexpr size_t max_cap = CordRepBtree::kMaxCapacity;
+  for (size_t cap : {max_cap - 1, max_cap * 2, max_cap * max_cap * 2}) {
+    const std::string data = CreateRandomString(cap * 512);
+
+    {
+      // Verify RemoveSuffix(<all>)
+      AutoUnref refs;
+      CordRepBtree* node = refs.RefIf(shared(), CreateTree(data, 512));
+      EXPECT_THAT(CordRepBtree::RemoveSuffix(node, data.length()), Eq(nullptr));
+
+      // Verify RemoveSuffix(<none>)
+      node = refs.RefIf(shared(), CreateTree(data, 512));
+      EXPECT_THAT(CordRepBtree::RemoveSuffix(node, 0), Eq(node));
+      CordRep::Unref(node);
+    }
+
+    for (int n = 1; n < data.length(); ++n) {
+      AutoUnref refs;
+      auto flats = CreateFlatsFromString(data, 512);
+      CordRepBtree* node = refs.RefIf(shared(), CreateTree(flats));
+      CordRep* rep = refs.Add(CordRepBtree::RemoveSuffix(node, n));
+      EXPECT_THAT(CordToString(rep), Eq(data.substr(0, data.length() - n)));
+
+      // Collect all flats
+      auto is_flat = [](CordRep* rep) { return rep->tag >= FLAT; };
+      std::vector<CordRep*> edges = CordCollectRepsIf(is_flat, rep);
+      ASSERT_THAT(edges.size(), Le(flats.size()));
+
+      // Isolate last edge
+      CordRep* last_edge = edges.back();
+      edges.pop_back();
+      const size_t last_length = rep->length - edges.size() * 512;
+
+      // All flats except the last edge must be kept or copied 'as is'
+      int index = 0;
+      for (CordRep* edge : edges) {
+        ASSERT_THAT(edge, Eq(flats[index++]));
+        ASSERT_THAT(edge->length, Eq(512));
+      }
+
+      // CordRepBtree may optimize small substrings to avoid waste, so only
+      // check for flat sharing / updates where the code should always do this.
+      if (last_length >= 500) {
+        EXPECT_THAT(last_edge, Eq(flats[index++]));
+        if (shared()) {
+          EXPECT_THAT(last_edge->length, Eq(512));
+        } else {
+          EXPECT_TRUE(last_edge->refcount.IsOne());
+          EXPECT_THAT(last_edge->length, Eq(last_length));
+        }
+      }
+    }
+  }
+}
+
+TEST(CordRepBtreeTest, SubTree) {
+  // Create tree of at least 2 levels high
+  constexpr size_t max_cap = CordRepBtree::kMaxCapacity;
+  const size_t n = max_cap * max_cap * 2;
+  const std::string data = CreateRandomString(n * 3);
+  std::vector<CordRep*> flats;
+  for (absl::string_view s = data; !s.empty(); s.remove_prefix(3)) {
+    flats.push_back(MakeFlat(s.substr(0, 3)));
+  }
+  CordRepBtree* node = CordRepBtree::Create(CordRep::Ref(flats[0]));
+  for (size_t i = 1; i < flats.size(); ++i) {
+    node = CordRepBtree::Append(node, CordRep::Ref(flats[i]));
+  }
+
+  for (int offset = 0; offset < data.length(); ++offset) {
+    for (int length = 1; length <= data.length() - offset; ++length) {
+      CordRep* rep = node->SubTree(offset, length);
+      EXPECT_THAT(CordToString(rep), Eq(data.substr(offset, length)));
+      CordRep::Unref(rep);
+    }
+  }
+  CordRepBtree::Unref(node);
+  for (CordRep* rep : flats) {
+    CordRep::Unref(rep);
+  }
+}
+
+TEST(CordRepBtreeTest, SubTreeOnExistingSubstring) {
+  // This test verifies that a SubTree call on a pre-existing (large) substring
+  // adjusts the existing substring if not shared, and else rewrites the
+  // existing substring.
+  AutoUnref refs;
+  std::string data = CreateRandomString(1000);
+  CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("abc"));
+  CordRep* flat = MakeFlat(data);
+  leaf = CordRepBtree::Append(leaf, flat);
+
+  // Setup tree containing substring.
+  CordRep* result = leaf->SubTree(0, 3 + 990);
+  ASSERT_THAT(result->tag, Eq(BTREE));
+  CordRep::Unref(leaf);
+  leaf = result->btree();
+  ASSERT_THAT(leaf->Edges(), ElementsAre(_, IsSubstring(0, 990)));
+  EXPECT_THAT(leaf->Edges()[1]->substring()->child, Eq(flat));
+
+  // Verify substring of substring.
+  result = leaf->SubTree(3 + 5, 970);
+  ASSERT_THAT(result, IsSubstring(5, 970));
+  EXPECT_THAT(result->substring()->child, Eq(flat));
+  CordRep::Unref(result);
+
+  CordRep::Unref(leaf);
+}
+
+TEST_P(CordRepBtreeTest, AddDataToLeaf) {
+  const size_t n = CordRepBtree::kMaxCapacity;
+  const std::string data = CreateRandomString(n * 3);
+
+  for (bool append : {true, false}) {
+    AutoUnref refs;
+    DataConsumer consumer(data, append);
+    SCOPED_TRACE(append ? "Append" : "Prepend");
+
+    CordRepBtree* leaf = CordRepBtree::Create(MakeFlat(consumer.Next(3)));
+    for (size_t i = 1; i < n; ++i) {
+      refs.RefIf(shared(), leaf);
+      CordRepBtree* result = BtreeAdd(leaf, append, consumer.Next(3));
+      EXPECT_THAT(result, Conditional(shared(), Ne(leaf), Eq(leaf)));
+      EXPECT_THAT(CordToString(result), Eq(consumer.Consumed()));
+      leaf = result;
+    }
+    CordRep::Unref(leaf);
+  }
+}
+
+TEST_P(CordRepBtreeTest, AppendDataToTree) {
+  AutoUnref refs;
+  size_t n = CordRepBtree::kMaxCapacity + CordRepBtree::kMaxCapacity / 2;
+  std::string data = CreateRandomString(n * 3);
+  CordRepBtree* tree = refs.RefIf(shared(), CreateTree(data, 3));
+  CordRepBtree* leaf0 = tree->Edges()[0]->btree();
+  CordRepBtree* leaf1 = tree->Edges()[1]->btree();
+  CordRepBtree* result = CordRepBtree::Append(tree, "123456789");
+  EXPECT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree)));
+  EXPECT_THAT(result->Edges(),
+              ElementsAre(leaf0, Conditional(shared(), Ne(leaf1), Eq(leaf1))));
+  EXPECT_THAT(CordToString(result), Eq(data + "123456789"));
+  CordRep::Unref(result);
+}
+
+TEST_P(CordRepBtreeTest, PrependDataToTree) {
+  AutoUnref refs;
+  size_t n = CordRepBtree::kMaxCapacity + CordRepBtree::kMaxCapacity / 2;
+  std::string data = CreateRandomString(n * 3);
+  CordRepBtree* tree = refs.RefIf(shared(), CreateTreeReverse(data, 3));
+  CordRepBtree* leaf0 = tree->Edges()[0]->btree();
+  CordRepBtree* leaf1 = tree->Edges()[1]->btree();
+  CordRepBtree* result = CordRepBtree::Prepend(tree, "123456789");
+  EXPECT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree)));
+  EXPECT_THAT(result->Edges(),
+              ElementsAre(Conditional(shared(), Ne(leaf0), Eq(leaf0)), leaf1));
+  EXPECT_THAT(CordToString(result), Eq("123456789" + data));
+  CordRep::Unref(result);
+}
+
+TEST_P(CordRepBtreeTest, AddDataToTreeThreeLevelsDeep) {
+  constexpr size_t max_cap = CordRepBtree::kMaxCapacity;
+  const size_t n = max_cap * max_cap * max_cap;
+  const std::string data = CreateRandomString(n * 3);
+
+  for (bool append : {true, false}) {
+    AutoUnref refs;
+    DataConsumer consumer(data, append);
+    SCOPED_TRACE(append ? "Append" : "Prepend");
+
+    // Fill leaf
+    CordRepBtree* tree = CordRepBtree::Create(MakeFlat(consumer.Next(3)));
+    for (size_t i = 1; i < max_cap; ++i) {
+      tree = BtreeAdd(tree, append, consumer.Next(3));
+    }
+    ASSERT_THAT(CordToString(tree), Eq(consumer.Consumed()));
+
+    // Fill to maximum at one deep
+    refs.RefIf(shared(), tree);
+    CordRepBtree* result = BtreeAdd(tree, append, consumer.Next(3));
+    ASSERT_THAT(result, IsNode(1));
+    ASSERT_THAT(result, Ne(tree));
+    ASSERT_THAT(CordToString(result), Eq(consumer.Consumed()));
+    tree = result;
+    for (size_t i = max_cap + 1; i < max_cap * max_cap; ++i) {
+      refs.RefIf(shared(), tree);
+      result = BtreeAdd(tree, append, consumer.Next(3));
+      ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree)));
+      ASSERT_THAT(CordToString(result), Eq(consumer.Consumed()));
+      tree = result;
+    }
+
+    // Fill to maximum at two deep
+    refs.RefIf(shared(), tree);
+    result = BtreeAdd(tree, append, consumer.Next(3));
+    ASSERT_THAT(result, IsNode(2));
+    ASSERT_THAT(result, Ne(tree));
+    ASSERT_THAT(CordToString(result), Eq(consumer.Consumed()));
+    tree = result;
+    for (size_t i = max_cap * max_cap + 1; i < max_cap * max_cap * max_cap;
+         ++i) {
+      refs.RefIf(shared(), tree);
+      result = BtreeAdd(tree, append, consumer.Next(3));
+      ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree)));
+      ASSERT_THAT(CordToString(result), Eq(consumer.Consumed()));
+      tree = result;
+    }
+
+    CordRep::Unref(tree);
+  }
+}
+
+TEST_P(CordRepBtreeTest, AddLargeDataToLeaf) {
+  const size_t max_cap = CordRepBtree::kMaxCapacity;
+  const size_t n = max_cap * max_cap * max_cap * 3 + 2;
+  const std::string data = CreateRandomString(n * kMaxFlatLength);
+
+  for (bool append : {true, false}) {
+    AutoUnref refs;
+    SCOPED_TRACE(append ? "Append" : "Prepend");
+
+    CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("abc"));
+    refs.RefIf(shared(), leaf);
+    CordRepBtree* result = BtreeAdd(leaf, append, data);
+    EXPECT_THAT(CordToString(result), Eq(append ? "abc" + data : data + "abc"));
+    CordRep::Unref(result);
+  }
+}
+
+TEST_P(CordRepBtreeDualTest, CreateFromConcat) {
+  AutoUnref refs;
+  CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"),
+                      MakeFlat("nopqrstuv"), MakeFlat("wxyz")};
+  auto* left = MakeConcat(flats[0], flats[1]);
+  auto* right = MakeConcat(flats[2], refs.RefIf(first_shared(), flats[3]));
+  auto* concat = refs.RefIf(second_shared(), MakeConcat(left, right));
+  CordRepBtree* result = CordRepBtree::Create(concat);
+  ASSERT_TRUE(CordRepBtree::IsValid(result));
+  EXPECT_THAT(result->length, Eq(26));
+  EXPECT_THAT(CordToString(result), Eq("abcdefghijklmnopqrstuvwxyz"));
+  CordRep::Unref(result);
+}
+
+TEST_P(CordRepBtreeDualTest, AppendConcat) {
+  AutoUnref refs;
+  CordRep* flats[] = {MakeFlat("defgh"), MakeFlat("ijklm"),
+                      MakeFlat("nopqrstuv"), MakeFlat("wxyz")};
+  auto* left = MakeConcat(flats[0], flats[1]);
+  auto* right = MakeConcat(flats[2], refs.RefIf(first_shared(), flats[3]));
+  auto* concat = refs.RefIf(second_shared(), MakeConcat(left, right));
+  CordRepBtree* result = CordRepBtree::Create(MakeFlat("abc"));
+  result = CordRepBtree::Append(result, concat);
+  ASSERT_TRUE(CordRepBtree::IsValid(result));
+  EXPECT_THAT(result->length, Eq(26));
+  EXPECT_THAT(CordToString(result), Eq("abcdefghijklmnopqrstuvwxyz"));
+  CordRep::Unref(result);
+}
+
+TEST_P(CordRepBtreeDualTest, PrependConcat) {
+  AutoUnref refs;
+  CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"),
+                      MakeFlat("nopqrstuv"), MakeFlat("wx")};
+  auto* left = MakeConcat(flats[0], flats[1]);
+  auto* right = MakeConcat(flats[2], refs.RefIf(first_shared(), flats[3]));
+  auto* concat = refs.RefIf(second_shared(), MakeConcat(left, right));
+  CordRepBtree* result = CordRepBtree::Create(MakeFlat("yz"));
+  result = CordRepBtree::Prepend(result, concat);
+  ASSERT_TRUE(CordRepBtree::IsValid(result));
+  EXPECT_THAT(result->length, Eq(26));
+  EXPECT_THAT(CordToString(result), Eq("abcdefghijklmnopqrstuvwxyz"));
+  CordRep::Unref(result);
+}
+
+TEST_P(CordRepBtreeTest, CreateFromTreeReturnsTree) {
+  AutoUnref refs;
+  CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("Hello world"));
+  refs.RefIf(shared(), leaf);
+  CordRepBtree* result = CordRepBtree::Create(leaf);
+  EXPECT_THAT(result, Eq(leaf));
+  CordRep::Unref(result);
+}
+
+TEST(CordRepBtreeTest, GetCharacter) {
+  size_t n = CordRepBtree::kMaxCapacity * CordRepBtree::kMaxCapacity + 2;
+  std::string data = CreateRandomString(n * 3);
+  CordRepBtree* tree = CreateTree(data, 3);
+  // Add a substring node for good measure.
+  tree = tree->Append(tree, MakeSubstring(4, 5, MakeFlat("abcdefghijklm")));
+  data += "efghi";
+  for (size_t i = 0; i < data.length(); ++i) {
+    ASSERT_THAT(tree->GetCharacter(i), Eq(data[i]));
+  }
+  CordRep::Unref(tree);
+}
+
+TEST_P(CordRepBtreeTest, IsFlatSingleFlat) {
+  CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("Hello world"));
+
+  absl::string_view fragment;
+  EXPECT_TRUE(leaf->IsFlat(nullptr));
+  EXPECT_TRUE(leaf->IsFlat(&fragment));
+  EXPECT_THAT(fragment, Eq("Hello world"));
+  fragment = "";
+  EXPECT_TRUE(leaf->IsFlat(0, 11, nullptr));
+  EXPECT_TRUE(leaf->IsFlat(0, 11, &fragment));
+  EXPECT_THAT(fragment, Eq("Hello world"));
+
+  // Arbitrary ranges must check true as well.
+  EXPECT_TRUE(leaf->IsFlat(1, 4, &fragment));
+  EXPECT_THAT(fragment, Eq("ello"));
+  EXPECT_TRUE(leaf->IsFlat(6, 5, &fragment));
+  EXPECT_THAT(fragment, Eq("world"));
+
+  CordRep::Unref(leaf);
+}
+
+TEST(CordRepBtreeTest, IsFlatMultiFlat) {
+  size_t n = CordRepBtree::kMaxCapacity * CordRepBtree::kMaxCapacity + 2;
+  std::string data = CreateRandomString(n * 3);
+  CordRepBtree* tree = CreateTree(data, 3);
+  // Add substring nodes for good measure.
+  tree = tree->Append(tree, MakeSubstring(4, 3, MakeFlat("abcdefghijklm")));
+  tree = tree->Append(tree, MakeSubstring(8, 3, MakeFlat("abcdefghijklm")));
+  data += "efgijk";
+
+  EXPECT_FALSE(tree->IsFlat(nullptr));
+  absl::string_view fragment = "Can't touch this";
+  EXPECT_FALSE(tree->IsFlat(&fragment));
+  EXPECT_THAT(fragment, Eq("Can't touch this"));
+
+  for (size_t offset = 0; offset < data.size(); offset += 3) {
+    EXPECT_TRUE(tree->IsFlat(offset, 3, nullptr));
+    EXPECT_TRUE(tree->IsFlat(offset, 3, &fragment));
+    EXPECT_THAT(fragment, Eq(data.substr(offset, 3)));
+
+    fragment = "Can't touch this";
+    if (offset > 0) {
+      EXPECT_FALSE(tree->IsFlat(offset - 1, 4, nullptr));
+      EXPECT_FALSE(tree->IsFlat(offset - 1, 4, &fragment));
+      EXPECT_THAT(fragment, Eq("Can't touch this"));
+    }
+    if (offset < data.size() - 4) {
+      EXPECT_FALSE(tree->IsFlat(offset, 4, nullptr));
+      EXPECT_FALSE(tree->IsFlat(offset, 4, &fragment));
+      EXPECT_THAT(fragment, Eq("Can't touch this"));
+    }
+  }
+
+  CordRep::Unref(tree);
+}
+
+#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)
+
+TEST_P(CordRepBtreeHeightTest, GetAppendBufferNotPrivate) {
+  CordRepBtree* tree = CordRepBtree::Create(MakeExternal("Foo"));
+  CordRepBtree::Ref(tree);
+  EXPECT_DEATH(tree->GetAppendBuffer(1), ".*");
+  CordRepBtree::Unref(tree);
+  CordRepBtree::Unref(tree);
+}
+
+#endif  // defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)
+
+TEST_P(CordRepBtreeHeightTest, GetAppendBufferNotFlat) {
+  CordRepBtree* tree = CordRepBtree::Create(MakeExternal("Foo"));
+  for (int i = 1; i <= height(); ++i) {
+    tree = CordRepBtree::New(tree);
+  }
+  EXPECT_THAT(tree->GetAppendBuffer(1), SizeIs(0));
+  CordRepBtree::Unref(tree);
+}
+
+TEST_P(CordRepBtreeHeightTest, GetAppendBufferFlatNotPrivate) {
+  CordRepFlat* flat = MakeFlat("abc");
+  CordRepBtree* tree = CordRepBtree::Create(CordRep::Ref(flat));
+  for (int i = 1; i <= height(); ++i) {
+    tree = CordRepBtree::New(tree);
+  }
+  EXPECT_THAT(tree->GetAppendBuffer(1), SizeIs(0));
+  CordRepBtree::Unref(tree);
+  CordRep::Unref(flat);
+}
+
+TEST_P(CordRepBtreeHeightTest, GetAppendBufferTreeNotPrivate) {
+  if (height() == 0) return;
+  AutoUnref refs;
+  CordRepFlat* flat = MakeFlat("abc");
+  CordRepBtree* tree = CordRepBtree::Create(CordRep::Ref(flat));
+  for (int i = 1; i <= height(); ++i) {
+    if (i == (height() + 1) / 2) refs.Ref(tree);
+    tree = CordRepBtree::New(tree);
+  }
+  EXPECT_THAT(tree->GetAppendBuffer(1), SizeIs(0));
+  CordRepBtree::Unref(tree);
+  CordRep::Unref(flat);
+}
+
+TEST_P(CordRepBtreeHeightTest, GetAppendBufferFlatNoCapacity) {
+  CordRepFlat* flat = MakeFlat("abc");
+  flat->length = flat->Capacity();
+  CordRepBtree* tree = CordRepBtree::Create(flat);
+  for (int i = 1; i <= height(); ++i) {
+    tree = CordRepBtree::New(tree);
+  }
+  EXPECT_THAT(tree->GetAppendBuffer(1), SizeIs(0));
+  CordRepBtree::Unref(tree);
+}
+
+TEST_P(CordRepBtreeHeightTest, GetAppendBufferFlatWithCapacity) {
+  CordRepFlat* flat = MakeFlat("abc");
+  CordRepBtree* tree = CordRepBtree::Create(flat);
+  for (int i = 1; i <= height(); ++i) {
+    tree = CordRepBtree::New(tree);
+  }
+  absl::Span<char> span = tree->GetAppendBuffer(2);
+  EXPECT_THAT(span, SizeIs(2));
+  EXPECT_THAT(span.data(), TypedEq<void*>(flat->Data() + 3));
+  EXPECT_THAT(tree->length, Eq(5));
+
+  size_t avail = flat->Capacity() - 5;
+  span = tree->GetAppendBuffer(avail + 100);
+  EXPECT_THAT(span, SizeIs(avail));
+  EXPECT_THAT(span.data(), TypedEq<void*>(flat->Data() + 5));
+  EXPECT_THAT(tree->length, Eq(5 + avail));
+
+  CordRepBtree::Unref(tree);
+}
+
+TEST(CordRepBtreeTest, Dump) {
+  // Handles nullptr
+  std::stringstream ss;
+  CordRepBtree::Dump(nullptr, ss);
+  CordRepBtree::Dump(nullptr, "Once upon a label", ss);
+  CordRepBtree::Dump(nullptr, "Once upon a label", false, ss);
+  CordRepBtree::Dump(nullptr, "Once upon a label", true, ss);
+
+  // Cover legal edges
+  CordRepFlat* flat = MakeFlat("Hello world");
+  CordRepExternal* external = MakeExternal("Hello external");
+  CordRep* substr_flat = MakeSubstring(1, 6, CordRep::Ref(flat));
+  CordRep* substr_external = MakeSubstring(2, 7, CordRep::Ref(external));
+
+  // Build tree
+  CordRepBtree* tree = CordRepBtree::Create(flat);
+  tree = CordRepBtree::Append(tree, external);
+  tree = CordRepBtree::Append(tree, substr_flat);
+  tree = CordRepBtree::Append(tree, substr_external);
+
+  // Repeat until we have a tree
+  while (tree->height() == 0) {
+    tree = CordRepBtree::Append(tree, CordRep::Ref(flat));
+    tree = CordRepBtree::Append(tree, CordRep::Ref(external));
+    tree = CordRepBtree::Append(tree, CordRep::Ref(substr_flat));
+    tree = CordRepBtree::Append(tree, CordRep::Ref(substr_external));
+  }
+
+  for (int api = 0; api <= 3; ++api) {
+    absl::string_view api_scope;
+    std::stringstream ss;
+    switch (api) {
+      case 0:
+        api_scope = "Bare";
+        CordRepBtree::Dump(tree, ss);
+        break;
+      case 1:
+        api_scope = "Label only";
+        CordRepBtree::Dump(tree, "Once upon a label", ss);
+        break;
+      case 2:
+        api_scope = "Label no content";
+        CordRepBtree::Dump(tree, "Once upon a label", false, ss);
+        break;
+      default:
+        api_scope = "Label and content";
+        CordRepBtree::Dump(tree, "Once upon a label", true, ss);
+        break;
+    }
+    SCOPED_TRACE(api_scope);
+    std::string str = ss.str();
+
+    // Contains Node(depth) / Leaf and private / shared indicators
+    EXPECT_THAT(str, AllOf(HasSubstr("Node(1)"), HasSubstr("Leaf"),
+                           HasSubstr("Private"), HasSubstr("Shared")));
+
+    // Contains length and start offset of all data edges
+    EXPECT_THAT(str, AllOf(HasSubstr("len = 11"), HasSubstr("len = 14"),
+                           HasSubstr("len = 6"), HasSubstr("len = 7"),
+                           HasSubstr("start = 1"), HasSubstr("start = 2")));
+
+    // Contains address of all data edges
+    EXPECT_THAT(
+        str, AllOf(HasSubstr(absl::StrCat("0x", absl::Hex(flat))),
+                   HasSubstr(absl::StrCat("0x", absl::Hex(external))),
+                   HasSubstr(absl::StrCat("0x", absl::Hex(substr_flat))),
+                   HasSubstr(absl::StrCat("0x", absl::Hex(substr_external)))));
+
+    if (api != 0) {
+      // Contains label
+      EXPECT_THAT(str, HasSubstr("Once upon a label"));
+    }
+
+    if (api != 3) {
+      // Does not contain contents
+      EXPECT_THAT(str, Not(AnyOf((HasSubstr("data = \"Hello world\""),
+                                  HasSubstr("data = \"Hello external\""),
+                                  HasSubstr("data = \"ello w\""),
+                                  HasSubstr("data = \"llo ext\"")))));
+    } else {
+      // Contains contents
+      EXPECT_THAT(str, AllOf((HasSubstr("data = \"Hello world\""),
+                              HasSubstr("data = \"Hello external\""),
+                              HasSubstr("data = \"ello w\""),
+                              HasSubstr("data = \"llo ext\""))));
+    }
+  }
+
+  CordRep::Unref(tree);
+}
+
+TEST(CordRepBtreeTest, IsValid) {
+  EXPECT_FALSE(CordRepBtree::IsValid(nullptr));
+
+  CordRepBtree* empty = CordRepBtree::New(0);
+  EXPECT_TRUE(CordRepBtree::IsValid(empty));
+  CordRep::Unref(empty);
+
+  for (bool as_tree : {false, true}) {
+    CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("abc"));
+    CordRepBtree* tree = as_tree ? CordRepBtree::New(leaf) : nullptr;
+    CordRepBtree* check = as_tree ? tree : leaf;
+
+    ASSERT_TRUE(CordRepBtree::IsValid(check));
+    leaf->length--;
+    EXPECT_FALSE(CordRepBtree::IsValid(check));
+    leaf->length++;
+
+    ASSERT_TRUE(CordRepBtree::IsValid(check));
+    leaf->tag--;
+    EXPECT_FALSE(CordRepBtree::IsValid(check));
+    leaf->tag++;
+
+    // Height
+    ASSERT_TRUE(CordRepBtree::IsValid(check));
+    leaf->storage[0] = static_cast<uint8_t>(CordRepBtree::kMaxHeight + 1);
+    EXPECT_FALSE(CordRepBtree::IsValid(check));
+    leaf->storage[0] = 1;
+    EXPECT_FALSE(CordRepBtree::IsValid(check));
+    leaf->storage[0] = 0;
+
+    // Begin
+    ASSERT_TRUE(CordRepBtree::IsValid(check));
+    const uint8_t begin = leaf->storage[1];
+    leaf->storage[1] = static_cast<uint8_t>(CordRepBtree::kMaxCapacity);
+    EXPECT_FALSE(CordRepBtree::IsValid(check));
+    leaf->storage[1] = 2;
+    EXPECT_FALSE(CordRepBtree::IsValid(check));
+    leaf->storage[1] = begin;
+
+    // End
+    ASSERT_TRUE(CordRepBtree::IsValid(check));
+    const uint8_t end = leaf->storage[2];
+    leaf->storage[2] = static_cast<uint8_t>(CordRepBtree::kMaxCapacity + 1);
+    EXPECT_FALSE(CordRepBtree::IsValid(check));
+    leaf->storage[2] = end;
+
+    // DataEdge tag and value
+    ASSERT_TRUE(CordRepBtree::IsValid(check));
+    CordRep* const edge = leaf->Edges()[0];
+    const uint8_t tag = edge->tag;
+    CordRepBtreeTestPeer::SetEdge(leaf, begin, nullptr);
+    EXPECT_FALSE(CordRepBtree::IsValid(check));
+    CordRepBtreeTestPeer::SetEdge(leaf, begin, edge);
+    edge->tag = BTREE;
+    EXPECT_FALSE(CordRepBtree::IsValid(check));
+    edge->tag = tag;
+
+    if (as_tree) {
+      ASSERT_TRUE(CordRepBtree::IsValid(check));
+      leaf->length--;
+      EXPECT_FALSE(CordRepBtree::IsValid(check));
+      leaf->length++;
+
+      // Height
+      ASSERT_TRUE(CordRepBtree::IsValid(check));
+      tree->storage[0] = static_cast<uint8_t>(2);
+      EXPECT_FALSE(CordRepBtree::IsValid(check));
+      tree->storage[0] = 1;
+
+      // Btree edge
+      ASSERT_TRUE(CordRepBtree::IsValid(check));
+      CordRep* const edge = tree->Edges()[0];
+      const uint8_t tag = edge->tag;
+      edge->tag = FLAT;
+      EXPECT_FALSE(CordRepBtree::IsValid(check));
+      edge->tag = tag;
+    }
+
+    ASSERT_TRUE(CordRepBtree::IsValid(check));
+    CordRep::Unref(check);
+  }
+}
+
+TEST(CordRepBtreeTest, AssertValid) {
+  CordRepBtree* tree = CordRepBtree::Create(MakeFlat("abc"));
+  const CordRepBtree* ctree = tree;
+  EXPECT_THAT(CordRepBtree::AssertValid(tree), Eq(tree));
+  EXPECT_THAT(CordRepBtree::AssertValid(ctree), Eq(ctree));
+
+#if defined(GTEST_HAS_DEATH_TEST)
+  CordRepBtree* nulltree = nullptr;
+  const CordRepBtree* cnulltree = nullptr;
+  EXPECT_DEBUG_DEATH(
+      EXPECT_THAT(CordRepBtree::AssertValid(nulltree), Eq(nulltree)), ".*");
+  EXPECT_DEBUG_DEATH(
+      EXPECT_THAT(CordRepBtree::AssertValid(cnulltree), Eq(cnulltree)), ".*");
+
+  tree->length--;
+  EXPECT_DEBUG_DEATH(EXPECT_THAT(CordRepBtree::AssertValid(tree), Eq(tree)),
+                     ".*");
+  EXPECT_DEBUG_DEATH(EXPECT_THAT(CordRepBtree::AssertValid(ctree), Eq(ctree)),
+                     ".*");
+  tree->length++;
+#endif
+  CordRep::Unref(tree);
+}
+
+TEST(CordRepBtreeTest, CheckAssertValidShallowVsDeep) {
+  // Restore exhaustive validation on any exit.
+  const bool exhaustive_validation = cord_btree_exhaustive_validation.load();
+  auto cleanup = absl::MakeCleanup([exhaustive_validation] {
+    cord_btree_exhaustive_validation.store(exhaustive_validation);
+  });
+
+  // Create a tree of at least 2 levels, and mess with the original flat, which
+  // should go undetected in shallow mode as the flat is too far away, but
+  // should be detected in forced non-shallow mode.
+  CordRep* flat = MakeFlat("abc");
+  CordRepBtree* tree = CordRepBtree::Create(flat);
+  constexpr size_t max_cap = CordRepBtree::kMaxCapacity;
+  const size_t n = max_cap * max_cap * 2;
+  for (size_t i = 0; i < n; ++i) {
+    tree = CordRepBtree::Append(tree, MakeFlat("Hello world"));
+  }
+  flat->length = 100;
+
+  cord_btree_exhaustive_validation.store(false);
+  EXPECT_FALSE(CordRepBtree::IsValid(tree));
+  EXPECT_TRUE(CordRepBtree::IsValid(tree, true));
+  EXPECT_FALSE(CordRepBtree::IsValid(tree, false));
+  CordRepBtree::AssertValid(tree);
+  CordRepBtree::AssertValid(tree, true);
+#if defined(GTEST_HAS_DEATH_TEST)
+  EXPECT_DEBUG_DEATH(CordRepBtree::AssertValid(tree, false), ".*");
+#endif
+
+  cord_btree_exhaustive_validation.store(true);
+  EXPECT_FALSE(CordRepBtree::IsValid(tree));
+  EXPECT_FALSE(CordRepBtree::IsValid(tree, true));
+  EXPECT_FALSE(CordRepBtree::IsValid(tree, false));
+#if defined(GTEST_HAS_DEATH_TEST)
+  EXPECT_DEBUG_DEATH(CordRepBtree::AssertValid(tree), ".*");
+  EXPECT_DEBUG_DEATH(CordRepBtree::AssertValid(tree, true), ".*");
+#endif
+
+  flat->length = 3;
+  CordRep::Unref(tree);
+}
+
+TEST_P(CordRepBtreeTest, Rebuild) {
+  for (size_t size : {3, 8, 100, 10000, 1000000}) {
+    SCOPED_TRACE(absl::StrCat("Rebuild @", size));
+
+    std::vector<CordRepFlat*> flats;
+    for (int i = 0; i < size; ++i) {
+      flats.push_back(CordRepFlat::New(2));
+      flats.back()->Data()[0] = 'x';
+      flats.back()->length = 1;
+    }
+
+    // Build the tree into 'right', and each so many 'split_limit' edges,
+    // combine 'left' + 'right' into a new 'left', and start a new 'right'.
+    // This guarantees we get a reasonable amount of chaos in the tree.
+    size_t split_count = 0;
+    size_t split_limit = 3;
+    auto it = flats.begin();
+    CordRepBtree* left = nullptr;
+    CordRepBtree* right = CordRepBtree::New(*it);
+    while (++it != flats.end()) {
+      if (++split_count >= split_limit) {
+        split_limit += split_limit / 16;
+        left = left ? CordRepBtree::Append(left, right) : right;
+        right = CordRepBtree::New(*it);
+      } else {
+        right = CordRepBtree::Append(right, *it);
+      }
+    }
+
+    // Finalize tree
+    left = left ? CordRepBtree::Append(left, right) : right;
+
+    // Rebuild
+    AutoUnref ref;
+    left = ref.Add(CordRepBtree::Rebuild(ref.RefIf(shared(), left)));
+    ASSERT_TRUE(CordRepBtree::IsValid(left));
+
+    // Verify we have the exact same edges in the exact same order.
+    bool ok = true;
+    it = flats.begin();
+    CordVisitReps(left, [&](CordRep* edge) {
+      if (edge->tag < FLAT) return;
+      ok = ok && (it != flats.end() && *it++ == edge);
+    });
+    EXPECT_TRUE(ok && it == flats.end()) << "Rebuild edges mismatch";
+  }
+}
+
+}  // namespace
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cord_rep_consume.cc b/absl/strings/internal/cord_rep_consume.cc
new file mode 100644
index 0000000..8151454
--- /dev/null
+++ b/absl/strings/internal/cord_rep_consume.cc
@@ -0,0 +1,129 @@
+// Copyright 2021 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/cord_rep_consume.h"
+
+#include <array>
+#include <utility>
+
+#include "absl/container/inlined_vector.h"
+#include "absl/functional/function_ref.h"
+#include "absl/strings/internal/cord_internal.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+namespace {
+
+// Unrefs the provided `substring`, and returns `substring->child`
+// Adds or assumes a reference on `substring->child`
+CordRep* ClipSubstring(CordRepSubstring* substring) {
+  CordRep* child = substring->child;
+  if (substring->refcount.IsOne()) {
+    delete substring;
+  } else {
+    CordRep::Ref(child);
+    CordRep::Unref(substring);
+  }
+  return child;
+}
+
+// Unrefs the provided `concat`, and returns `{concat->left, concat->right}`
+// Adds or assumes a reference on `concat->left` and `concat->right`.
+// Returns an array of 2 elements containing the left and right nodes.
+std::array<CordRep*, 2> ClipConcat(CordRepConcat* concat) {
+  std::array<CordRep*, 2> result{concat->left, concat->right};
+  if (concat->refcount.IsOne()) {
+    delete concat;
+  } else {
+    CordRep::Ref(result[0]);
+    CordRep::Ref(result[1]);
+    CordRep::Unref(concat);
+  }
+  return result;
+}
+
+void Consume(bool forward, CordRep* rep, ConsumeFn consume_fn) {
+  size_t offset = 0;
+  size_t length = rep->length;
+  struct Entry {
+    CordRep* rep;
+    size_t offset;
+    size_t length;
+  };
+  absl::InlinedVector<Entry, 40> stack;
+
+  for (;;) {
+    if (rep->tag == CONCAT) {
+      std::array<CordRep*, 2> res = ClipConcat(rep->concat());
+      CordRep* left = res[0];
+      CordRep* right = res[1];
+
+      if (left->length <= offset) {
+        // Don't need left node
+        offset -= left->length;
+        CordRep::Unref(left);
+        rep = right;
+        continue;
+      }
+
+      size_t length_left = left->length - offset;
+      if (length_left >= length) {
+        // Don't need right node
+        CordRep::Unref(right);
+        rep = left;
+        continue;
+      }
+
+      // Need both nodes
+      size_t length_right = length - length_left;
+      if (forward) {
+        stack.push_back({right, 0, length_right});
+        rep = left;
+        length = length_left;
+      } else {
+        stack.push_back({left, offset, length_left});
+        rep = right;
+        offset = 0;
+        length = length_right;
+      }
+    } else if (rep->tag == SUBSTRING) {
+      offset += rep->substring()->start;
+      rep = ClipSubstring(rep->substring());
+    } else {
+      consume_fn(rep, offset, length);
+      if (stack.empty()) return;
+
+      rep = stack.back().rep;
+      offset = stack.back().offset;
+      length = stack.back().length;
+      stack.pop_back();
+    }
+  }
+}
+
+}  // namespace
+
+void Consume(CordRep* rep, ConsumeFn consume_fn) {
+  return Consume(true, rep, std::move(consume_fn));
+}
+
+void ReverseConsume(CordRep* rep, ConsumeFn consume_fn) {
+  return Consume(false, rep, std::move(consume_fn));
+}
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cord_rep_consume.h b/absl/strings/internal/cord_rep_consume.h
new file mode 100644
index 0000000..d46fca2
--- /dev/null
+++ b/absl/strings/internal/cord_rep_consume.h
@@ -0,0 +1,50 @@
+// Copyright 2021 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_CORD_REP_CONSUME_H_
+#define ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_
+
+#include <functional>
+
+#include "absl/functional/function_ref.h"
+#include "absl/strings/internal/cord_internal.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// Functor for the Consume() and ReverseConsume() functions:
+//   void ConsumeFunc(CordRep* rep, size_t offset, size_t length);
+// See the Consume() and ReverseConsume() function comments for documentation.
+using ConsumeFn = FunctionRef<void(CordRep*, size_t, size_t)>;
+
+// Consume() and ReverseConsume() consume CONCAT based trees and invoke the
+// provided functor with the contained nodes in the proper forward or reverse
+// order, which is used to convert CONCAT trees into other tree or cord data.
+// All CONCAT and SUBSTRING nodes are processed internally. The 'offset`
+// parameter of the functor is non-zero for any nodes below SUBSTRING nodes.
+// It's up to the caller to form these back into SUBSTRING nodes or otherwise
+// store offset / prefix information. These functions are intended to be used
+// only for migration / transitional code where due to factors such as ODR
+// violations, we can not 100% guarantee that all code respects 'new format'
+// settings and flags, so we need to be able to parse old data on the fly until
+// all old code is deprecated / no longer the default format.
+void Consume(CordRep* rep, ConsumeFn consume_fn);
+void ReverseConsume(CordRep* rep, ConsumeFn consume_fn);
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_
diff --git a/absl/strings/internal/cord_rep_consume_test.cc b/absl/strings/internal/cord_rep_consume_test.cc
new file mode 100644
index 0000000..e507824
--- /dev/null
+++ b/absl/strings/internal/cord_rep_consume_test.cc
@@ -0,0 +1,173 @@
+// Copyright 2021 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/cord_rep_consume.h"
+
+#include <functional>
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+namespace {
+
+using testing::InSequence;
+using testing::MockFunction;
+
+// Returns the depth of a node
+int Depth(const CordRep* rep) {
+  return (rep->tag == CONCAT) ? rep->concat()->depth() : 0;
+}
+
+// Creates a concatenation of the specified nodes.
+CordRepConcat* CreateConcat(CordRep* left, CordRep* right) {
+  auto* concat = new CordRepConcat();
+  concat->tag = CONCAT;
+  concat->left = left;
+  concat->right = right;
+  concat->length = left->length + right->length;
+  concat->set_depth(1 + (std::max)(Depth(left), Depth(right)));
+  return concat;
+}
+
+// Creates a flat with the length set to `length`
+CordRepFlat* CreateFlatWithLength(size_t length) {
+  auto* flat = CordRepFlat::New(length);
+  flat->length = length;
+  return flat;
+}
+
+// Creates a substring node on the specified child.
+CordRepSubstring* CreateSubstring(CordRep* child, size_t start, size_t length) {
+  auto* rep = new CordRepSubstring();
+  rep->length = length;
+  rep->tag = SUBSTRING;
+  rep->start = start;
+  rep->child = child;
+  return rep;
+}
+
+// Flats we use in the tests
+CordRep* flat[6];
+
+// Creates a test tree
+CordRep* CreateTestTree() {
+  flat[0] = CreateFlatWithLength(1);
+  flat[1] = CreateFlatWithLength(7);
+  CordRepConcat* left = CreateConcat(flat[0], CreateSubstring(flat[1], 2, 4));
+
+  flat[2] = CreateFlatWithLength(9);
+  flat[3] = CreateFlatWithLength(13);
+  CordRepConcat* right1 = CreateConcat(flat[2], flat[3]);
+
+  flat[4] = CreateFlatWithLength(15);
+  flat[5] = CreateFlatWithLength(19);
+  CordRepConcat* right2 = CreateConcat(flat[4], flat[5]);
+
+  CordRepConcat* right = CreateConcat(right1, CreateSubstring(right2, 5, 17));
+  return CreateConcat(left, right);
+}
+
+TEST(CordRepConsumeTest, Consume) {
+  InSequence in_sequence;
+  CordRep* tree = CreateTestTree();
+  MockFunction<void(CordRep*, size_t, size_t)> consume;
+  EXPECT_CALL(consume, Call(flat[0], 0, 1));
+  EXPECT_CALL(consume, Call(flat[1], 2, 4));
+  EXPECT_CALL(consume, Call(flat[2], 0, 9));
+  EXPECT_CALL(consume, Call(flat[3], 0, 13));
+  EXPECT_CALL(consume, Call(flat[4], 5, 10));
+  EXPECT_CALL(consume, Call(flat[5], 0, 7));
+  Consume(tree, consume.AsStdFunction());
+  for (CordRep* rep : flat) {
+    EXPECT_TRUE(rep->refcount.IsOne());
+    CordRep::Unref(rep);
+  }
+}
+
+TEST(CordRepConsumeTest, ConsumeShared) {
+  InSequence in_sequence;
+  CordRep* tree = CreateTestTree();
+  MockFunction<void(CordRep*, size_t, size_t)> consume;
+  EXPECT_CALL(consume, Call(flat[0], 0, 1));
+  EXPECT_CALL(consume, Call(flat[1], 2, 4));
+  EXPECT_CALL(consume, Call(flat[2], 0, 9));
+  EXPECT_CALL(consume, Call(flat[3], 0, 13));
+  EXPECT_CALL(consume, Call(flat[4], 5, 10));
+  EXPECT_CALL(consume, Call(flat[5], 0, 7));
+  Consume(CordRep::Ref(tree), consume.AsStdFunction());
+  for (CordRep* rep : flat) {
+    EXPECT_FALSE(rep->refcount.IsOne());
+    CordRep::Unref(rep);
+  }
+  CordRep::Unref(tree);
+}
+
+TEST(CordRepConsumeTest, Reverse) {
+  InSequence in_sequence;
+  CordRep* tree = CreateTestTree();
+  MockFunction<void(CordRep*, size_t, size_t)> consume;
+  EXPECT_CALL(consume, Call(flat[5], 0, 7));
+  EXPECT_CALL(consume, Call(flat[4], 5, 10));
+  EXPECT_CALL(consume, Call(flat[3], 0, 13));
+  EXPECT_CALL(consume, Call(flat[2], 0, 9));
+  EXPECT_CALL(consume, Call(flat[1], 2, 4));
+  EXPECT_CALL(consume, Call(flat[0], 0, 1));
+  ReverseConsume(tree, consume.AsStdFunction());
+  for (CordRep* rep : flat) {
+    EXPECT_TRUE(rep->refcount.IsOne());
+    CordRep::Unref(rep);
+  }
+}
+
+TEST(CordRepConsumeTest, ReverseShared) {
+  InSequence in_sequence;
+  CordRep* tree = CreateTestTree();
+  MockFunction<void(CordRep*, size_t, size_t)> consume;
+  EXPECT_CALL(consume, Call(flat[5], 0, 7));
+  EXPECT_CALL(consume, Call(flat[4], 5, 10));
+  EXPECT_CALL(consume, Call(flat[3], 0, 13));
+  EXPECT_CALL(consume, Call(flat[2], 0, 9));
+  EXPECT_CALL(consume, Call(flat[1], 2, 4));
+  EXPECT_CALL(consume, Call(flat[0], 0, 1));
+  ReverseConsume(CordRep::Ref(tree), consume.AsStdFunction());
+  for (CordRep* rep : flat) {
+    EXPECT_FALSE(rep->refcount.IsOne());
+    CordRep::Unref(rep);
+  }
+  CordRep::Unref(tree);
+}
+
+TEST(CordRepConsumeTest, UnreachableFlat) {
+  InSequence in_sequence;
+  CordRepFlat* flat1 = CreateFlatWithLength(10);
+  CordRepFlat* flat2 = CreateFlatWithLength(20);
+  CordRepConcat* concat = CreateConcat(flat1, flat2);
+  CordRepSubstring* tree = CreateSubstring(concat, 15, 10);
+  MockFunction<void(CordRep*, size_t, size_t)> consume;
+  EXPECT_CALL(consume, Call(flat2, 5, 10));
+  Consume(tree, consume.AsStdFunction());
+  EXPECT_TRUE(flat2->refcount.IsOne());
+  CordRep::Unref(flat2);
+}
+
+}  // namespace
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cord_rep_flat.h b/absl/strings/internal/cord_rep_flat.h
index a98aa9d..4d0f988 100644
--- a/absl/strings/internal/cord_rep_flat.h
+++ b/absl/strings/internal/cord_rep_flat.h
@@ -44,11 +44,11 @@
 static constexpr size_t kMinFlatLength = kMinFlatSize - kFlatOverhead;
 
 constexpr uint8_t AllocatedSizeToTagUnchecked(size_t size) {
-  return static_cast<uint8_t>((size <= 1024) ? size / 8
-                                             : 128 + size / 32 - 1024 / 32);
+  return static_cast<uint8_t>((size <= 1024) ? size / 8 + 1
+                                             : 129 + size / 32 - 1024 / 32);
 }
 
-static_assert(kMinFlatSize / 8 >= FLAT, "");
+static_assert(kMinFlatSize / 8 + 1 >= FLAT, "");
 static_assert(AllocatedSizeToTagUnchecked(kMaxFlatSize) <= MAX_FLAT_TAG, "");
 
 // Helper functions for rounded div, and rounding to exact sizes.
@@ -73,7 +73,7 @@
 
 // Converts the provided tag to the corresponding allocated size
 constexpr size_t TagToAllocatedSize(uint8_t tag) {
-  return (tag <= 128) ? (tag * 8) : (1024 + (tag - 128) * 32);
+  return (tag <= 129) ? ((tag - 1) * 8) : (1024 + (tag - 129) * 32);
 }
 
 // Converts the provided tag to the corresponding available data length
@@ -82,7 +82,7 @@
 }
 
 // Enforce that kMaxFlatSize maps to a well-known exact tag value.
-static_assert(TagToAllocatedSize(224) == kMaxFlatSize, "Bad tag logic");
+static_assert(TagToAllocatedSize(225) == kMaxFlatSize, "Bad tag logic");
 
 struct CordRepFlat : public CordRep {
   // Creates a new flat node.
@@ -118,8 +118,8 @@
   }
 
   // Returns a pointer to the data inside this flat rep.
-  char* Data() { return storage; }
-  const char* Data() const { return storage; }
+  char* Data() { return reinterpret_cast<char*>(storage); }
+  const char* Data() const { return reinterpret_cast<const char*>(storage); }
 
   // Returns the maximum capacity (payload size) of this instance.
   size_t Capacity() const { return TagToLength(tag); }
diff --git a/absl/strings/internal/cord_rep_ring.cc b/absl/strings/internal/cord_rep_ring.cc
index 4d31d1d..07c77eb 100644
--- a/absl/strings/internal/cord_rep_ring.cc
+++ b/absl/strings/internal/cord_rep_ring.cc
@@ -26,21 +26,13 @@
 #include "absl/base/macros.h"
 #include "absl/container/inlined_vector.h"
 #include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_consume.h"
 #include "absl/strings/internal/cord_rep_flat.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace cord_internal {
 
-// See https://bugs.llvm.org/show_bug.cgi?id=48477
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wshadow"
-#if __has_warning("-Wshadow-field")
-#pragma clang diagnostic ignored "-Wshadow-field"
-#endif
-#endif
-
 namespace {
 
 using index_type = CordRepRing::index_type;
@@ -48,7 +40,7 @@
 enum class Direction { kForward, kReversed };
 
 inline bool IsFlatOrExternal(CordRep* rep) {
-  return rep->tag >= FLAT || rep->tag == EXTERNAL;
+  return rep->IsFlat() || rep->IsExternal();
 }
 
 // Verifies that n + extra <= kMaxCapacity: throws std::length_error otherwise.
@@ -58,14 +50,6 @@
   }
 }
 
-// Removes a reference from `rep` only.
-// Asserts that the refcount after decrement is not zero.
-inline bool UnrefNeverOne(CordRep* rep) {
-  bool result = rep->refcount.Decrement();
-  assert(result);
-  return result;
-}
-
 // Creates a flat from the provided string data, allocating up to `extra`
 // capacity in the returned flat depending on kMaxFlatLength limitations.
 // Requires `len` to be less or equal to `kMaxFlatLength`
@@ -77,40 +61,6 @@
   return rep;
 }
 
-// Unrefs the provided `substring`, and returns `substring->child`
-// Adds or assumes a reference on `substring->child`
-CordRep* ClipSubstring(CordRepSubstring* substring) {
-  CordRep* child = substring->child;
-  if (substring->refcount.IsOne()) {
-    delete substring;
-  } else {
-    CordRep::Ref(child);
-    if (ABSL_PREDICT_FALSE(!substring->refcount.Decrement())) {
-      UnrefNeverOne(child);
-      delete substring;
-    }
-  }
-  return child;
-}
-
-// Unrefs the provided `concat`, and returns `{concat->left, concat->right}`
-// Adds or assumes a reference on `concat->left` and `concat->right`.
-std::pair<CordRep*, CordRep*> ClipConcat(CordRepConcat* concat) {
-  auto result = std::make_pair(concat->left, concat->right);
-  if (concat->refcount.IsOne()) {
-    delete concat;
-  } else {
-    CordRep::Ref(result.first);
-    CordRep::Ref(result.second);
-    if (ABSL_PREDICT_FALSE(!concat->refcount.Decrement())) {
-      UnrefNeverOne(result.first);
-      UnrefNeverOne(result.second);
-      delete concat;
-    }
-  }
-  return result;
-}
-
 // Unrefs the entries in `[head, tail)`.
 // Requires all entries to be a FLAT or EXTERNAL node.
 void UnrefEntries(const CordRepRing* rep, index_type head, index_type tail) {
@@ -126,79 +76,6 @@
   });
 }
 
-template <typename F>
-void Consume(Direction direction, CordRep* rep, F&& fn) {
-  size_t offset = 0;
-  size_t length = rep->length;
-  struct Entry {
-    CordRep* rep;
-    size_t offset;
-    size_t length;
-  };
-  absl::InlinedVector<Entry, 40> stack;
-
-  for (;;) {
-    if (rep->tag >= FLAT || rep->tag == EXTERNAL || rep->tag == RING) {
-      fn(rep, offset, length);
-      if (stack.empty()) return;
-
-      rep = stack.back().rep;
-      offset = stack.back().offset;
-      length = stack.back().length;
-      stack.pop_back();
-    } else if (rep->tag == SUBSTRING) {
-      offset += rep->substring()->start;
-      rep = ClipSubstring(rep->substring());
-    } else if (rep->tag == CONCAT) {
-      auto res = ClipConcat(rep->concat());
-      CordRep* left = res.first;
-      CordRep* right = res.second;
-
-      if (left->length <= offset) {
-        // Don't need left node
-        offset -= left->length;
-        CordRep::Unref(left);
-        rep = right;
-        continue;
-      }
-
-      size_t length_left = left->length - offset;
-      if (length_left >= length) {
-        // Don't need right node
-        CordRep::Unref(right);
-        rep = left;
-        continue;
-      }
-
-      // Need both nodes
-      size_t length_right = length - length_left;
-      if (direction == Direction::kReversed) {
-        stack.push_back({left, offset, length_left});
-        rep = right;
-        offset = 0;
-        length = length_right;
-      } else {
-        stack.push_back({right, 0, length_right});
-        rep = left;
-        length = length_left;
-      }
-    } else {
-      assert("Valid tag" == nullptr);
-      return;
-    }
-  }
-}
-
-template <typename F>
-void Consume(CordRep* rep, F&& fn) {
-  return Consume(Direction::kForward, rep, std::forward<F>(fn));
-}
-
-template <typename F>
-void RConsume(CordRep* rep, F&& fn) {
-  return Consume(Direction::kReversed, rep, std::forward<F>(fn));
-}
-
 }  // namespace
 
 std::ostream& operator<<(std::ostream& s, const CordRepRing& rep) {
@@ -301,7 +178,7 @@
     if (offset >= child->length || entry_length > child->length - offset) {
       output << "entry[" << head << "] has offset " << offset
              << " and entry length " << entry_length
-             << " which are outside of the childs length of " << child->length;
+             << " which are outside of the child's length of " << child->length;
       return false;
     }
 
@@ -352,7 +229,7 @@
 }
 
 void CordRepRing::Delete(CordRepRing* rep) {
-  assert(rep != nullptr && rep->tag == RING);
+  assert(rep != nullptr && rep->IsRing());
 #if defined(__cpp_sized_deallocation)
   size_t size = AllocSize(rep->capacity_);
   rep->~CordRepRing();
@@ -400,10 +277,11 @@
   // Get current number of entries, and check for max capacity.
   size_t entries = rep->entries();
 
-  size_t min_extra = (std::max)(extra, rep->capacity() * 2 - entries);
-  if (!rep->refcount.IsOne()) {
-    return Copy(rep, rep->head(), rep->tail(), min_extra);
+  if (!rep->refcount.IsMutable()) {
+    return Copy(rep, rep->head(), rep->tail(), extra);
   } else if (entries + extra > rep->capacity()) {
+    const size_t min_grow = rep->capacity() + rep->capacity() / 2;
+    const size_t min_extra = (std::max)(extra, min_grow - entries);
     CordRepRing* newrep = CordRepRing::New(entries, min_extra);
     newrep->Fill<false>(rep, rep->head(), rep->tail());
     CordRepRing::Delete(rep);
@@ -414,10 +292,10 @@
 }
 
 Span<char> CordRepRing::GetAppendBuffer(size_t size) {
-  assert(refcount.IsOne());
+  assert(refcount.IsMutable());
   index_type back = retreat(tail_);
   CordRep* child = entry_child(back);
-  if (child->tag >= FLAT && child->refcount.IsOne()) {
+  if (child->tag >= FLAT && child->refcount.IsMutable()) {
     size_t capacity = child->flat()->Capacity();
     pos_type end_pos = entry_end_pos(back);
     size_t data_offset = entry_data_offset(back);
@@ -434,10 +312,10 @@
 }
 
 Span<char> CordRepRing::GetPrependBuffer(size_t size) {
-  assert(refcount.IsOne());
+  assert(refcount.IsMutable());
   CordRep* child = entry_child(head_);
   size_t data_offset = entry_data_offset(head_);
-  if (data_offset && child->refcount.IsOne() && child->tag >= FLAT) {
+  if (data_offset && child->refcount.IsMutable() && child->tag >= FLAT) {
     size_t n = (std::min)(data_offset, size);
     this->length += n;
     begin_pos_ -= n;
@@ -449,12 +327,12 @@
 }
 
 CordRepRing* CordRepRing::CreateFromLeaf(CordRep* child, size_t offset,
-                                         size_t length, size_t extra) {
+                                         size_t len, size_t extra) {
   CordRepRing* rep = CordRepRing::New(1, extra);
   rep->head_ = 0;
   rep->tail_ = rep->advance(0);
-  rep->length = length;
-  rep->entry_end_pos()[0] = length;
+  rep->length = len;
+  rep->entry_end_pos()[0] = len;
   rep->entry_child()[0] = child;
   rep->entry_data_offset()[0] = static_cast<offset_type>(offset);
   return Validate(rep);
@@ -462,16 +340,16 @@
 
 CordRepRing* CordRepRing::CreateSlow(CordRep* child, size_t extra) {
   CordRepRing* rep = nullptr;
-  Consume(child, [&](CordRep* child, size_t offset, size_t length) {
-    if (IsFlatOrExternal(child)) {
-      rep = rep ? AppendLeaf(rep, child, offset, length)
-                : CreateFromLeaf(child, offset, length, extra);
+  Consume(child, [&](CordRep* child_arg, size_t offset, size_t len) {
+    if (IsFlatOrExternal(child_arg)) {
+      rep = rep ? AppendLeaf(rep, child_arg, offset, len)
+                : CreateFromLeaf(child_arg, offset, len, extra);
     } else if (rep) {
-      rep = AddRing<AddMode::kAppend>(rep, child->ring(), offset, length);
-    } else if (offset == 0 && child->length == length) {
-      rep = Mutable(child->ring(), extra);
+      rep = AddRing<AddMode::kAppend>(rep, child_arg->ring(), offset, len);
+    } else if (offset == 0 && child_arg->length == len) {
+      rep = Mutable(child_arg->ring(), extra);
     } else {
-      rep = SubRing(child->ring(), offset, length, extra);
+      rep = SubRing(child_arg->ring(), offset, len, extra);
     }
   });
   return Validate(rep, nullptr, __LINE__);
@@ -482,7 +360,7 @@
   if (IsFlatOrExternal(child)) {
     return CreateFromLeaf(child, 0, length, extra);
   }
-  if (child->tag == RING) {
+  if (child->IsRing()) {
     return Mutable(child->ring(), extra);
   }
   return CreateSlow(child, extra);
@@ -490,18 +368,18 @@
 
 template <CordRepRing::AddMode mode>
 CordRepRing* CordRepRing::AddRing(CordRepRing* rep, CordRepRing* ring,
-                                  size_t offset, size_t length) {
+                                  size_t offset, size_t len) {
   assert(offset < ring->length);
   constexpr bool append = mode == AddMode::kAppend;
   Position head = ring->Find(offset);
-  Position tail = ring->FindTail(head.index, offset + length);
+  Position tail = ring->FindTail(head.index, offset + len);
   const index_type entries = ring->entries(head.index, tail.index);
 
   rep = Mutable(rep, entries);
 
   // The delta for making ring[head].end_pos into 'len - offset'
   const pos_type delta_length =
-      (append ? rep->begin_pos_ + rep->length : rep->begin_pos_ - length) -
+      (append ? rep->begin_pos_ + rep->length : rep->begin_pos_ - len) -
       ring->entry_begin_pos(head.index) - head.offset;
 
   // Start filling at `tail`, or `entries` before `head`
@@ -542,36 +420,36 @@
   }
 
   // Commit changes
-  rep->length += length;
+  rep->length += len;
   if (append) {
     rep->tail_ = filler.pos();
   } else {
     rep->head_ = filler.head();
-    rep->begin_pos_ -= length;
+    rep->begin_pos_ -= len;
   }
 
   return Validate(rep);
 }
 
 CordRepRing* CordRepRing::AppendSlow(CordRepRing* rep, CordRep* child) {
-  Consume(child, [&rep](CordRep* child, size_t offset, size_t length) {
-    if (child->tag == RING) {
-      rep = AddRing<AddMode::kAppend>(rep, child->ring(), offset, length);
+  Consume(child, [&rep](CordRep* child_arg, size_t offset, size_t len) {
+    if (child_arg->IsRing()) {
+      rep = AddRing<AddMode::kAppend>(rep, child_arg->ring(), offset, len);
     } else {
-      rep = AppendLeaf(rep, child, offset, length);
+      rep = AppendLeaf(rep, child_arg, offset, len);
     }
   });
   return rep;
 }
 
 CordRepRing* CordRepRing::AppendLeaf(CordRepRing* rep, CordRep* child,
-                                     size_t offset, size_t length) {
+                                     size_t offset, size_t len) {
   rep = Mutable(rep, 1);
   index_type back = rep->tail_;
   const pos_type begin_pos = rep->begin_pos_ + rep->length;
   rep->tail_ = rep->advance(rep->tail_);
-  rep->length += length;
-  rep->entry_end_pos()[back] = begin_pos + length;
+  rep->length += len;
+  rep->entry_end_pos()[back] = begin_pos + len;
   rep->entry_child()[back] = child;
   rep->entry_data_offset()[back] = static_cast<offset_type>(offset);
   return Validate(rep, nullptr, __LINE__);
@@ -582,31 +460,31 @@
   if (IsFlatOrExternal(child)) {
     return AppendLeaf(rep, child, 0, length);
   }
-  if (child->tag == RING) {
+  if (child->IsRing()) {
     return AddRing<AddMode::kAppend>(rep, child->ring(), 0, length);
   }
   return AppendSlow(rep, child);
 }
 
 CordRepRing* CordRepRing::PrependSlow(CordRepRing* rep, CordRep* child) {
-  RConsume(child, [&](CordRep* child, size_t offset, size_t length) {
-    if (IsFlatOrExternal(child)) {
-      rep = PrependLeaf(rep, child, offset, length);
+  ReverseConsume(child, [&](CordRep* child_arg, size_t offset, size_t len) {
+    if (IsFlatOrExternal(child_arg)) {
+      rep = PrependLeaf(rep, child_arg, offset, len);
     } else {
-      rep = AddRing<AddMode::kPrepend>(rep, child->ring(), offset, length);
+      rep = AddRing<AddMode::kPrepend>(rep, child_arg->ring(), offset, len);
     }
   });
   return Validate(rep);
 }
 
 CordRepRing* CordRepRing::PrependLeaf(CordRepRing* rep, CordRep* child,
-                                      size_t offset, size_t length) {
+                                      size_t offset, size_t len) {
   rep = Mutable(rep, 1);
   index_type head = rep->retreat(rep->head_);
   pos_type end_pos = rep->begin_pos_;
   rep->head_ = head;
-  rep->length += length;
-  rep->begin_pos_ -= length;
+  rep->length += len;
+  rep->begin_pos_ -= len;
   rep->entry_end_pos()[head] = end_pos;
   rep->entry_child()[head] = child;
   rep->entry_data_offset()[head] = static_cast<offset_type>(offset);
@@ -618,7 +496,7 @@
   if (IsFlatOrExternal(child)) {
     return PrependLeaf(rep, child, 0, length);
   }
-  if (child->tag == RING) {
+  if (child->IsRing()) {
     return AddRing<AddMode::kPrepend>(rep, child->ring(), 0, length);
   }
   return PrependSlow(rep, child);
@@ -626,7 +504,7 @@
 
 CordRepRing* CordRepRing::Append(CordRepRing* rep, absl::string_view data,
                                  size_t extra) {
-  if (rep->refcount.IsOne()) {
+  if (rep->refcount.IsMutable()) {
     Span<char> avail = rep->GetAppendBuffer(data.length());
     if (!avail.empty()) {
       memcpy(avail.data(), data.data(), avail.length());
@@ -660,7 +538,7 @@
 
 CordRepRing* CordRepRing::Prepend(CordRepRing* rep, absl::string_view data,
                                   size_t extra) {
-  if (rep->refcount.IsOne()) {
+  if (rep->refcount.IsMutable()) {
     Span<char> avail = rep->GetPrependBuffer(data.length());
     if (!avail.empty()) {
       const char* tail = data.data() + data.length() - avail.length();
@@ -786,21 +664,21 @@
 }
 
 CordRepRing* CordRepRing::SubRing(CordRepRing* rep, size_t offset,
-                                  size_t length, size_t extra) {
+                                  size_t len, size_t extra) {
   assert(offset <= rep->length);
-  assert(offset <= rep->length - length);
+  assert(offset <= rep->length - len);
 
-  if (length == 0) {
+  if (len == 0) {
     CordRep::Unref(rep);
     return nullptr;
   }
 
   // Find position of first byte
   Position head = rep->Find(offset);
-  Position tail = rep->FindTail(head.index, offset + length);
+  Position tail = rep->FindTail(head.index, offset + len);
   const size_t new_entries = rep->entries(head.index, tail.index);
 
-  if (rep->refcount.IsOne() && extra <= (rep->capacity() - new_entries)) {
+  if (rep->refcount.IsMutable() && extra <= (rep->capacity() - new_entries)) {
     // We adopt a privately owned rep and no extra entries needed.
     if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index);
     if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_);
@@ -814,7 +692,7 @@
   }
 
   // Adjust begin_pos and length
-  rep->length = length;
+  rep->length = len;
   rep->begin_pos_ += offset;
 
   // Adjust head and tail blocks
@@ -837,7 +715,7 @@
   }
 
   Position head = rep->Find(len);
-  if (rep->refcount.IsOne()) {
+  if (rep->refcount.IsMutable()) {
     if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index);
     rep->head_ = head.index;
   } else {
@@ -867,7 +745,7 @@
   }
 
   Position tail = rep->FindTail(rep->length - len);
-  if (rep->refcount.IsOne()) {
+  if (rep->refcount.IsMutable()) {
     // We adopt a privately owned rep, scrub.
     if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_);
     rep->tail_ = tail.index;
@@ -888,10 +766,6 @@
   return Validate(rep);
 }
 
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
 }  // namespace cord_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/strings/internal/cord_rep_ring.h b/absl/strings/internal/cord_rep_ring.h
index c74d335..2000e21 100644
--- a/absl/strings/internal/cord_rep_ring.h
+++ b/absl/strings/internal/cord_rep_ring.h
@@ -30,15 +30,6 @@
 ABSL_NAMESPACE_BEGIN
 namespace cord_internal {
 
-// See https://bugs.llvm.org/show_bug.cgi?id=48477
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wshadow"
-#if __has_warning("-Wshadow-field")
-#pragma clang diagnostic ignored "-Wshadow-field"
-#endif
-#endif
-
 // All operations modifying a ring buffer are implemented as static methods
 // requiring a CordRepRing instance with a reference adopted by the method.
 //
@@ -210,23 +201,23 @@
   // referencing up to `size` capacity directly before the existing data.
   Span<char> GetPrependBuffer(size_t size);
 
-  // Returns a cord ring buffer containing `length` bytes of data starting at
+  // Returns a cord ring buffer containing `len` bytes of data starting at
   // `offset`. If the input is not shared, this function will remove all head
   // and tail child nodes outside of the requested range, and adjust the new
   // head and tail nodes as required. If the input is shared, this function
   // returns a new instance sharing some or all of the nodes from the input.
-  static CordRepRing* SubRing(CordRepRing* r, size_t offset, size_t length,
+  static CordRepRing* SubRing(CordRepRing* r, size_t offset, size_t len,
                               size_t extra = 0);
 
-  // Returns a cord ring buffer with the first `length` bytes removed.
+  // Returns a cord ring buffer with the first `len` bytes removed.
   // If the input is not shared, this function will remove all head child nodes
   // fully inside the first `length` bytes, and adjust the new head as required.
   // If the input is shared, this function returns a new instance sharing some
   // or all of the nodes from the input.
-  static CordRepRing* RemoveSuffix(CordRepRing* r, size_t length,
+  static CordRepRing* RemoveSuffix(CordRepRing* r, size_t len,
                                    size_t extra = 0);
 
-  // Returns a cord ring buffer with the last `length` bytes removed.
+  // Returns a cord ring buffer with the last `len` bytes removed.
   // If the input is not shared, this function will remove all head child nodes
   // fully inside the first `length` bytes, and adjust the new head as required.
   // If the input is shared, this function returns a new instance sharing some
@@ -237,6 +228,18 @@
   // Returns the character at `offset`. Requires that `offset < length`.
   char GetCharacter(size_t offset) const;
 
+  // Returns true if this instance manages a single contiguous buffer, in which
+  // case the (optional) output parameter `fragment` is set. Otherwise, the
+  // function returns false, and `fragment` is left unchanged.
+  bool IsFlat(absl::string_view* fragment) const;
+
+  // Returns true if the data starting at `offset` with length `len` is
+  // managed by this instance inside a single contiguous buffer, in which case
+  // the (optional) output parameter `fragment` is set to the contiguous memory
+  // starting at offset `offset` with length `length`. Otherwise, the function
+  // returns false, and `fragment` is left unchanged.
+  bool IsFlat(size_t offset, size_t len, absl::string_view* fragment) const;
+
   // Testing only: set capacity to requested capacity.
   void SetCapacityForTesting(size_t capacity);
 
@@ -380,8 +383,8 @@
 
   // Destroys the provided ring buffer, decrementing the reference count of all
   // contained child CordReps. The provided 1\`rep` should have a ref count of
-  // one (pre decrement destroy call observing `refcount.IsOne()`) or zero (post
-  // decrement destroy call observing `!refcount.Decrement()`).
+  // one (pre decrement destroy call observing `refcount.IsOne()`) or zero
+  // (post decrement destroy call observing `!refcount.Decrement()`).
   static void Destroy(CordRepRing* rep);
 
   // Returns a mutable reference to the logical end position array.
@@ -461,10 +464,10 @@
                                      size_t length, size_t extra);
 
   // Appends or prepends (depending on AddMode) the ring buffer in `ring' to
-  // `rep` starting at `offset` with length `length`.
+  // `rep` starting at `offset` with length `len`.
   template <AddMode mode>
   static CordRepRing* AddRing(CordRepRing* rep, CordRepRing* ring,
-                              size_t offset, size_t length);
+                              size_t offset, size_t len);
 
   // Increases the data offset for entry `index` by `n`.
   void AddDataOffset(index_type index, size_t n);
@@ -567,20 +570,35 @@
 
 // Now that CordRepRing is defined, we can define CordRep's helper casts:
 inline CordRepRing* CordRep::ring() {
-  assert(tag == RING);
+  assert(IsRing());
   return static_cast<CordRepRing*>(this);
 }
 
 inline const CordRepRing* CordRep::ring() const {
-  assert(tag == RING);
+  assert(IsRing());
   return static_cast<const CordRepRing*>(this);
 }
 
-std::ostream& operator<<(std::ostream& s, const CordRepRing& rep);
+inline bool CordRepRing::IsFlat(absl::string_view* fragment) const {
+  if (entries() == 1) {
+    if (fragment) *fragment = entry_data(head());
+    return true;
+  }
+  return false;
+}
 
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
+inline bool CordRepRing::IsFlat(size_t offset, size_t len,
+                                absl::string_view* fragment) const {
+  const Position pos = Find(offset);
+  const absl::string_view data = entry_data(pos.index);
+  if (data.length() >= len && data.length() - len >= pos.offset) {
+    if (fragment) *fragment = data.substr(pos.offset, len);
+    return true;
+  }
+  return false;
+}
+
+std::ostream& operator<<(std::ostream& s, const CordRepRing& rep);
 
 }  // namespace cord_internal
 ABSL_NAMESPACE_END
diff --git a/absl/strings/internal/cord_rep_ring_reader.h b/absl/strings/internal/cord_rep_ring_reader.h
index 396c0e2..7ceeaa0 100644
--- a/absl/strings/internal/cord_rep_ring_reader.h
+++ b/absl/strings/internal/cord_rep_ring_reader.h
@@ -40,6 +40,10 @@
   // The returned value is undefined if this instance is empty.
   CordRepRing::index_type index() const { return index_; }
 
+  // Returns the current node inside the ring buffer for this instance.
+  // The returned value is undefined if this instance is empty.
+  CordRep* node() const { return ring_->entry_child(index_); }
+
   // Returns the length of the referenced ring buffer.
   // Requires the current instance to be non empty.
   size_t length() const {
diff --git a/absl/strings/internal/cord_rep_test_util.h b/absl/strings/internal/cord_rep_test_util.h
new file mode 100644
index 0000000..ad828af
--- /dev/null
+++ b/absl/strings/internal/cord_rep_test_util.h
@@ -0,0 +1,220 @@
+// Copyright 2021 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_CORD_REP_TEST_UTIL_H_
+#define ABSL_STRINGS_INTERNAL_CORD_REP_TEST_UTIL_H_
+
+#include <cassert>
+#include <memory>
+#include <random>
+#include <string>
+#include <vector>
+
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_btree.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cordrep_testing {
+
+inline cord_internal::CordRepSubstring* MakeSubstring(
+    size_t start, size_t len, cord_internal::CordRep* rep) {
+  auto* sub = new cord_internal::CordRepSubstring;
+  sub->tag = cord_internal::SUBSTRING;
+  sub->start = start;
+  sub->length = len <= 0 ? rep->length - start + len : len;
+  sub->child = rep;
+  return sub;
+}
+
+inline cord_internal::CordRepConcat* MakeConcat(cord_internal::CordRep* left,
+                                                cord_internal::CordRep* right,
+                                                int depth = 0) {
+  auto* concat = new cord_internal::CordRepConcat;
+  concat->tag = cord_internal::CONCAT;
+  concat->length = left->length + right->length;
+  concat->left = left;
+  concat->right = right;
+  concat->set_depth(depth);
+  return concat;
+}
+
+inline cord_internal::CordRepFlat* MakeFlat(absl::string_view value) {
+  assert(value.length() <= cord_internal::kMaxFlatLength);
+  auto* flat = cord_internal::CordRepFlat::New(value.length());
+  flat->length = value.length();
+  memcpy(flat->Data(), value.data(), value.length());
+  return flat;
+}
+
+// Creates an external node for testing
+inline cord_internal::CordRepExternal* MakeExternal(absl::string_view s) {
+  struct Rep : public cord_internal::CordRepExternal {
+    std::string s;
+    explicit Rep(absl::string_view sv) : s(sv) {
+      this->tag = cord_internal::EXTERNAL;
+      this->base = s.data();
+      this->length = s.length();
+      this->releaser_invoker = [](cord_internal::CordRepExternal* self) {
+        delete static_cast<Rep*>(self);
+      };
+    }
+  };
+  return new Rep(s);
+}
+
+inline std::string CreateRandomString(size_t n) {
+  absl::string_view data =
+      "abcdefghijklmnopqrstuvwxyz"
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+      "0123456789~!@#$%^&*()_+=-<>?:\"{}[]|";
+  std::minstd_rand rnd;
+  std::uniform_int_distribution<size_t> dist(0, data.size() - 1);
+  std::string s(n, ' ');
+  for (size_t i = 0; i < n; ++i) {
+    s[i] = data[dist(rnd)];
+  }
+  return s;
+}
+
+// Creates an array of flats from the provided string, chopping
+// the provided string up into flats of size `chunk_size` characters
+// resulting in roughly `data.size() / chunk_size` total flats.
+inline std::vector<cord_internal::CordRep*> CreateFlatsFromString(
+    absl::string_view data, size_t chunk_size) {
+  assert(chunk_size > 0);
+  std::vector<cord_internal::CordRep*> flats;
+  for (absl::string_view s = data; !s.empty(); s.remove_prefix(chunk_size)) {
+    flats.push_back(MakeFlat(s.substr(0, chunk_size)));
+  }
+  return flats;
+}
+
+inline cord_internal::CordRepBtree* CordRepBtreeFromFlats(
+    absl::Span<cord_internal::CordRep* const> flats) {
+  assert(!flats.empty());
+  auto* node = cord_internal::CordRepBtree::Create(flats[0]);
+  for (size_t i = 1; i < flats.size(); ++i) {
+    node = cord_internal::CordRepBtree::Append(node, flats[i]);
+  }
+  return node;
+}
+
+template <typename Fn>
+inline void CordVisitReps(cord_internal::CordRep* rep, Fn&& fn) {
+  fn(rep);
+  while (rep->tag == cord_internal::SUBSTRING) {
+    rep = rep->substring()->child;
+    fn(rep);
+  }
+  if (rep->tag == cord_internal::BTREE) {
+    for (cord_internal::CordRep* edge : rep->btree()->Edges()) {
+      CordVisitReps(edge, fn);
+    }
+  } else if (rep->tag == cord_internal::CONCAT) {
+    CordVisitReps(rep->concat()->left, fn);
+    CordVisitReps(rep->concat()->right, fn);
+  }
+}
+
+template <typename Predicate>
+inline std::vector<cord_internal::CordRep*> CordCollectRepsIf(
+    Predicate&& predicate, cord_internal::CordRep* rep) {
+  std::vector<cord_internal::CordRep*> reps;
+  CordVisitReps(rep, [&reps, &predicate](cord_internal::CordRep* rep) {
+    if (predicate(rep)) reps.push_back(rep);
+  });
+  return reps;
+}
+
+inline std::vector<cord_internal::CordRep*> CordCollectReps(
+    cord_internal::CordRep* rep) {
+  std::vector<cord_internal::CordRep*> reps;
+  auto fn = [&reps](cord_internal::CordRep* rep) { reps.push_back(rep); };
+  CordVisitReps(rep, fn);
+  return reps;
+}
+
+inline void CordToString(cord_internal::CordRep* rep, std::string& s) {
+  size_t offset = 0;
+  size_t length = rep->length;
+  while (rep->tag == cord_internal::SUBSTRING) {
+    offset += rep->substring()->start;
+    rep = rep->substring()->child;
+  }
+  if (rep->tag == cord_internal::BTREE) {
+    for (cord_internal::CordRep* edge : rep->btree()->Edges()) {
+      CordToString(edge, s);
+    }
+  } else if (rep->tag >= cord_internal::FLAT) {
+    s.append(rep->flat()->Data() + offset, length);
+  } else if (rep->tag == cord_internal::EXTERNAL) {
+    s.append(rep->external()->base + offset, length);
+  } else {
+    ABSL_RAW_LOG(FATAL, "Unsupported tag %d", rep->tag);
+  }
+}
+
+inline std::string CordToString(cord_internal::CordRep* rep) {
+  std::string s;
+  s.reserve(rep->length);
+  CordToString(rep, s);
+  return s;
+}
+
+// RAII Helper class to automatically unref reps on destruction.
+class AutoUnref {
+ public:
+  ~AutoUnref() {
+    for (CordRep* rep : unrefs_) CordRep::Unref(rep);
+  }
+
+  // Adds `rep` to the list of reps to be unreffed at destruction.
+  template <typename CordRepType>
+  CordRepType* Add(CordRepType* rep) {
+    unrefs_.push_back(rep);
+    return rep;
+  }
+
+  // Increments the reference count of `rep` by one, and adds it to
+  // the list of reps to be unreffed at destruction.
+  template <typename CordRepType>
+  CordRepType* Ref(CordRepType* rep) {
+    unrefs_.push_back(CordRep::Ref(rep));
+    return rep;
+  }
+
+  // Increments the reference count of `rep` by one if `condition` is true,
+  // and adds it to the list of reps to be unreffed at destruction.
+  template <typename CordRepType>
+  CordRepType* RefIf(bool condition, CordRepType* rep) {
+    if (condition) unrefs_.push_back(CordRep::Ref(rep));
+    return rep;
+  }
+
+ private:
+  using CordRep = absl::cord_internal::CordRep;
+
+  std::vector<CordRep*> unrefs_;
+};
+
+}  // namespace cordrep_testing
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_CORD_REP_TEST_UTIL_H_
diff --git a/absl/strings/internal/cordz_functions.cc b/absl/strings/internal/cordz_functions.cc
new file mode 100644
index 0000000..20d314f
--- /dev/null
+++ b/absl/strings/internal/cordz_functions.cc
@@ -0,0 +1,96 @@
+// Copyright 2019 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/cordz_functions.h"
+
+#include <atomic>
+#include <cmath>
+#include <limits>
+#include <random>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/profiling/internal/exponential_biased.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+namespace {
+
+// The average interval until the next sample. A value of 0 disables profiling
+// while a value of 1 will profile all Cords.
+std::atomic<int> g_cordz_mean_interval(50000);
+
+}  // namespace
+
+#ifdef ABSL_INTERNAL_CORDZ_ENABLED
+
+// Special negative 'not initialized' per thread value for cordz_next_sample.
+static constexpr int64_t kInitCordzNextSample = -1;
+
+ABSL_CONST_INIT thread_local int64_t cordz_next_sample = kInitCordzNextSample;
+
+// kIntervalIfDisabled is the number of profile-eligible events need to occur
+// before the code will confirm that cordz is still disabled.
+constexpr int64_t kIntervalIfDisabled = 1 << 16;
+
+ABSL_ATTRIBUTE_NOINLINE bool cordz_should_profile_slow() {
+
+  thread_local absl::profiling_internal::ExponentialBiased
+      exponential_biased_generator;
+  int32_t mean_interval = get_cordz_mean_interval();
+
+  // Check if we disabled profiling. If so, set the next sample to a "large"
+  // number to minimize the overhead of the should_profile codepath.
+  if (mean_interval <= 0) {
+    cordz_next_sample = kIntervalIfDisabled;
+    return false;
+  }
+
+  // Check if we're always sampling.
+  if (mean_interval == 1) {
+    cordz_next_sample = 1;
+    return true;
+  }
+
+  if (cordz_next_sample <= 0) {
+    // If first check on current thread, check cordz_should_profile()
+    // again using the created (initial) stride in cordz_next_sample.
+    const bool initialized = cordz_next_sample != kInitCordzNextSample;
+    cordz_next_sample = exponential_biased_generator.GetStride(mean_interval);
+    return initialized || cordz_should_profile();
+  }
+
+  --cordz_next_sample;
+  return false;
+}
+
+void cordz_set_next_sample_for_testing(int64_t next_sample) {
+  cordz_next_sample = next_sample;
+}
+
+#endif  // ABSL_INTERNAL_CORDZ_ENABLED
+
+int32_t get_cordz_mean_interval() {
+  return g_cordz_mean_interval.load(std::memory_order_acquire);
+}
+
+void set_cordz_mean_interval(int32_t mean_interval) {
+  g_cordz_mean_interval.store(mean_interval, std::memory_order_release);
+}
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cordz_functions.h b/absl/strings/internal/cordz_functions.h
new file mode 100644
index 0000000..c9ba145
--- /dev/null
+++ b/absl/strings/internal/cordz_functions.h
@@ -0,0 +1,85 @@
+// Copyright 2019 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_CORDZ_FUNCTIONS_H_
+#define ABSL_STRINGS_CORDZ_FUNCTIONS_H_
+
+#include <stdint.h>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/optimization.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// Returns the current sample rate. This represents the average interval
+// between samples.
+int32_t get_cordz_mean_interval();
+
+// Sets the sample rate with the average interval between samples.
+void set_cordz_mean_interval(int32_t mean_interval);
+
+// Enable cordz unless any of the following applies:
+// - no thread local support
+// - MSVC build
+// - Android build
+// - Apple build
+// - DLL build
+// Hashtablez is turned off completely in opensource builds.
+// MSVC's static atomics are dynamically initialized in debug mode, which breaks
+// sampling.
+#if defined(ABSL_HAVE_THREAD_LOCAL) && !defined(_MSC_VER)  && \
+    !defined(ABSL_BUILD_DLL) && !defined(ABSL_CONSUME_DLL) && \
+    !defined(__ANDROID__) && !defined(__APPLE__)
+#define ABSL_INTERNAL_CORDZ_ENABLED 1
+#endif
+
+#ifdef ABSL_INTERNAL_CORDZ_ENABLED
+
+// cordz_next_sample is the number of events until the next sample event. If
+// the value is 1 or less, the code will check on the next event if cordz is
+// enabled, and if so, will sample the Cord. cordz is only enabled when we can
+// use thread locals.
+ABSL_CONST_INIT extern thread_local int64_t cordz_next_sample;
+
+// Determines if the next sample should be profiled. If it is, the value pointed
+// at by next_sample will be set with the interval until the next sample.
+bool cordz_should_profile_slow();
+
+// Returns true if the next cord should be sampled.
+inline bool cordz_should_profile() {
+  if (ABSL_PREDICT_TRUE(cordz_next_sample > 1)) {
+    cordz_next_sample--;
+    return false;
+  }
+  return cordz_should_profile_slow();
+}
+
+// Sets the interval until the next sample (for testing only)
+void cordz_set_next_sample_for_testing(int64_t next_sample);
+
+#else  // ABSL_INTERNAL_CORDZ_ENABLED
+
+inline bool cordz_should_profile() { return false; }
+inline void cordz_set_next_sample_for_testing(int64_t) {}
+
+#endif  // ABSL_INTERNAL_CORDZ_ENABLED
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_CORDZ_FUNCTIONS_H_
diff --git a/absl/strings/internal/cordz_functions_test.cc b/absl/strings/internal/cordz_functions_test.cc
new file mode 100644
index 0000000..350623c
--- /dev/null
+++ b/absl/strings/internal/cordz_functions_test.cc
@@ -0,0 +1,149 @@
+// Copyright 2019 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/cordz_functions.h"
+
+#include <thread>  // NOLINT we need real clean new threads
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+namespace {
+
+using ::testing::Eq;
+using ::testing::Ge;
+using ::testing::Le;
+
+TEST(CordzFunctionsTest, SampleRate) {
+  int32_t orig_sample_rate = get_cordz_mean_interval();
+  int32_t expected_sample_rate = 123;
+  set_cordz_mean_interval(expected_sample_rate);
+  EXPECT_THAT(get_cordz_mean_interval(), Eq(expected_sample_rate));
+  set_cordz_mean_interval(orig_sample_rate);
+}
+
+// Cordz is disabled when we don't have thread_local. All calls to
+// should_profile will return false when cordz is diabled, so we might want to
+// avoid those tests.
+#ifdef ABSL_INTERNAL_CORDZ_ENABLED
+
+TEST(CordzFunctionsTest, ShouldProfileDisable) {
+  int32_t orig_sample_rate = get_cordz_mean_interval();
+
+  set_cordz_mean_interval(0);
+  cordz_set_next_sample_for_testing(0);
+  EXPECT_FALSE(cordz_should_profile());
+  // 1 << 16 is from kIntervalIfDisabled in cordz_functions.cc.
+  EXPECT_THAT(cordz_next_sample, Eq(1 << 16));
+
+  set_cordz_mean_interval(orig_sample_rate);
+}
+
+TEST(CordzFunctionsTest, ShouldProfileAlways) {
+  int32_t orig_sample_rate = get_cordz_mean_interval();
+
+  set_cordz_mean_interval(1);
+  cordz_set_next_sample_for_testing(1);
+  EXPECT_TRUE(cordz_should_profile());
+  EXPECT_THAT(cordz_next_sample, Le(1));
+
+  set_cordz_mean_interval(orig_sample_rate);
+}
+
+TEST(CordzFunctionsTest, DoesNotAlwaysSampleFirstCord) {
+  // Set large enough interval such that the chance of 'tons' of threads
+  // randomly sampling the first call is infinitely small.
+  set_cordz_mean_interval(10000);
+  int tries = 0;
+  bool sampled = false;
+  do {
+    ++tries;
+    ASSERT_THAT(tries, Le(1000));
+    std::thread thread([&sampled] {
+      sampled = cordz_should_profile();
+    });
+    thread.join();
+  } while (sampled);
+}
+
+TEST(CordzFunctionsTest, ShouldProfileRate) {
+  static constexpr int kDesiredMeanInterval = 1000;
+  static constexpr int kSamples = 10000;
+  int32_t orig_sample_rate = get_cordz_mean_interval();
+
+  set_cordz_mean_interval(kDesiredMeanInterval);
+
+  int64_t sum_of_intervals = 0;
+  for (int i = 0; i < kSamples; i++) {
+    // Setting next_sample to 0 will force cordz_should_profile to generate a
+    // new value for next_sample each iteration.
+    cordz_set_next_sample_for_testing(0);
+    cordz_should_profile();
+    sum_of_intervals += cordz_next_sample;
+  }
+
+  // The sum of independent exponential variables is an Erlang distribution,
+  // which is a gamma distribution where the shape parameter is equal to the
+  // number of summands. The distribution used for cordz_should_profile is
+  // actually floor(Exponential(1/mean)) which introduces bias. However, we can
+  // apply the squint-really-hard correction factor. That is, when mean is
+  // large, then if we squint really hard the shape of the distribution between
+  // N and N+1 looks like a uniform distribution. On average, each value for
+  // next_sample will be about 0.5 lower than we would expect from an
+  // exponential distribution. This squint-really-hard correction approach won't
+  // work when mean is smaller than about 10 but works fine when mean is 1000.
+  //
+  // We can use R to calculate a confidence interval. This
+  // shows how to generate a confidence interval with a false positive rate of
+  // one in a billion.
+  //
+  // $ R -q
+  // > mean = 1000
+  // > kSamples = 10000
+  // > errorRate = 1e-9
+  // > correction = -kSamples / 2
+  // > low = qgamma(errorRate/2, kSamples, 1/mean) + correction
+  // > high = qgamma(1 - errorRate/2, kSamples, 1/mean) + correction
+  // > low
+  // [1] 9396115
+  // > high
+  // [1] 10618100
+  EXPECT_THAT(sum_of_intervals, Ge(9396115));
+  EXPECT_THAT(sum_of_intervals, Le(10618100));
+
+  set_cordz_mean_interval(orig_sample_rate);
+}
+
+#else  // ABSL_INTERNAL_CORDZ_ENABLED
+
+TEST(CordzFunctionsTest, ShouldProfileDisabled) {
+  int32_t orig_sample_rate = get_cordz_mean_interval();
+
+  set_cordz_mean_interval(1);
+  cordz_set_next_sample_for_testing(0);
+  EXPECT_FALSE(cordz_should_profile());
+
+  set_cordz_mean_interval(orig_sample_rate);
+}
+
+#endif  // ABSL_INTERNAL_CORDZ_ENABLED
+
+}  // namespace
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cordz_handle.cc b/absl/strings/internal/cordz_handle.cc
new file mode 100644
index 0000000..a73fefe
--- /dev/null
+++ b/absl/strings/internal/cordz_handle.cc
@@ -0,0 +1,139 @@
+// Copyright 2019 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/cordz_handle.h"
+
+#include <atomic>
+
+#include "absl/base/internal/raw_logging.h"  // For ABSL_RAW_CHECK
+#include "absl/base/internal/spinlock.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+using ::absl::base_internal::SpinLockHolder;
+
+ABSL_CONST_INIT CordzHandle::Queue CordzHandle::global_queue_(absl::kConstInit);
+
+CordzHandle::CordzHandle(bool is_snapshot) : is_snapshot_(is_snapshot) {
+  if (is_snapshot) {
+    SpinLockHolder lock(&queue_->mutex);
+    CordzHandle* dq_tail = queue_->dq_tail.load(std::memory_order_acquire);
+    if (dq_tail != nullptr) {
+      dq_prev_ = dq_tail;
+      dq_tail->dq_next_ = this;
+    }
+    queue_->dq_tail.store(this, std::memory_order_release);
+  }
+}
+
+CordzHandle::~CordzHandle() {
+  ODRCheck();
+  if (is_snapshot_) {
+    std::vector<CordzHandle*> to_delete;
+    {
+      SpinLockHolder lock(&queue_->mutex);
+      CordzHandle* next = dq_next_;
+      if (dq_prev_ == nullptr) {
+        // We were head of the queue, delete every CordzHandle until we reach
+        // either the end of the list, or a snapshot handle.
+        while (next && !next->is_snapshot_) {
+          to_delete.push_back(next);
+          next = next->dq_next_;
+        }
+      } else {
+        // Another CordzHandle existed before this one, don't delete anything.
+        dq_prev_->dq_next_ = next;
+      }
+      if (next) {
+        next->dq_prev_ = dq_prev_;
+      } else {
+        queue_->dq_tail.store(dq_prev_, std::memory_order_release);
+      }
+    }
+    for (CordzHandle* handle : to_delete) {
+      delete handle;
+    }
+  }
+}
+
+bool CordzHandle::SafeToDelete() const {
+  return is_snapshot_ || queue_->IsEmpty();
+}
+
+void CordzHandle::Delete(CordzHandle* handle) {
+  assert(handle);
+  if (handle) {
+    handle->ODRCheck();
+    Queue* const queue = handle->queue_;
+    if (!handle->SafeToDelete()) {
+      SpinLockHolder lock(&queue->mutex);
+      CordzHandle* dq_tail = queue->dq_tail.load(std::memory_order_acquire);
+      if (dq_tail != nullptr) {
+        handle->dq_prev_ = dq_tail;
+        dq_tail->dq_next_ = handle;
+        queue->dq_tail.store(handle, std::memory_order_release);
+        return;
+      }
+    }
+    delete handle;
+  }
+}
+
+std::vector<const CordzHandle*> CordzHandle::DiagnosticsGetDeleteQueue() {
+  std::vector<const CordzHandle*> handles;
+  SpinLockHolder lock(&global_queue_.mutex);
+  CordzHandle* dq_tail = global_queue_.dq_tail.load(std::memory_order_acquire);
+  for (const CordzHandle* p = dq_tail; p; p = p->dq_prev_) {
+    handles.push_back(p);
+  }
+  return handles;
+}
+
+bool CordzHandle::DiagnosticsHandleIsSafeToInspect(
+    const CordzHandle* handle) const {
+  ODRCheck();
+  if (!is_snapshot_) return false;
+  if (handle == nullptr) return true;
+  if (handle->is_snapshot_) return false;
+  bool snapshot_found = false;
+  SpinLockHolder lock(&queue_->mutex);
+  for (const CordzHandle* p = queue_->dq_tail; p; p = p->dq_prev_) {
+    if (p == handle) return !snapshot_found;
+    if (p == this) snapshot_found = true;
+  }
+  ABSL_ASSERT(snapshot_found);  // Assert that 'this' is in delete queue.
+  return true;
+}
+
+std::vector<const CordzHandle*>
+CordzHandle::DiagnosticsGetSafeToInspectDeletedHandles() {
+  ODRCheck();
+  std::vector<const CordzHandle*> handles;
+  if (!is_snapshot()) {
+    return handles;
+  }
+
+  SpinLockHolder lock(&queue_->mutex);
+  for (const CordzHandle* p = dq_next_; p != nullptr; p = p->dq_next_) {
+    if (!p->is_snapshot()) {
+      handles.push_back(p);
+    }
+  }
+  return handles;
+}
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cordz_handle.h b/absl/strings/internal/cordz_handle.h
new file mode 100644
index 0000000..5df53c7
--- /dev/null
+++ b/absl/strings/internal/cordz_handle.h
@@ -0,0 +1,131 @@
+// Copyright 2019 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_CORDZ_HANDLE_H_
+#define ABSL_STRINGS_CORDZ_HANDLE_H_
+
+#include <atomic>
+#include <vector>
+
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/internal/spinlock.h"
+#include "absl/synchronization/mutex.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// This base class allows multiple types of object (CordzInfo and
+// CordzSampleToken) to exist simultaneously on the delete queue (pointed to by
+// global_dq_tail and traversed using dq_prev_ and dq_next_). The
+// delete queue guarantees that once a profiler creates a CordzSampleToken and
+// has gained visibility into a CordzInfo object, that CordzInfo object will not
+// be deleted prematurely. This allows the profiler to inspect all CordzInfo
+// objects that are alive without needing to hold a global lock.
+class CordzHandle {
+ public:
+  CordzHandle() : CordzHandle(false) {}
+
+  bool is_snapshot() const { return is_snapshot_; }
+
+  // Returns true if this instance is safe to be deleted because it is either a
+  // snapshot, which is always safe to delete, or not included in the global
+  // delete queue and thus not included in any snapshot.
+  // Callers are responsible for making sure this instance can not be newly
+  // discovered by other threads. For example, CordzInfo instances first de-list
+  // themselves from the global CordzInfo list before determining if they are
+  // safe to be deleted directly.
+  // If SafeToDelete returns false, callers MUST use the Delete() method to
+  // safely queue CordzHandle instances for deletion.
+  bool SafeToDelete() const;
+
+  // Deletes the provided instance, or puts it on the delete queue to be deleted
+  // once there are no more sample tokens (snapshot) instances potentially
+  // referencing the instance. `handle` should not be null.
+  static void Delete(CordzHandle* handle);
+
+  // Returns the current entries in the delete queue in LIFO order.
+  static std::vector<const CordzHandle*> DiagnosticsGetDeleteQueue();
+
+  // Returns true if the provided handle is nullptr or guarded by this handle.
+  // Since the CordzSnapshot token is itself a CordzHandle, this method will
+  // allow tests to check if that token is keeping an arbitrary CordzHandle
+  // alive.
+  bool DiagnosticsHandleIsSafeToInspect(const CordzHandle* handle) const;
+
+  // Returns the current entries in the delete queue, in LIFO order, that are
+  // protected by this. CordzHandle objects are only placed on the delete queue
+  // after CordzHandle::Delete is called with them as an argument. Only
+  // CordzHandle objects that are not also CordzSnapshot objects will be
+  // included in the return vector. For each of the handles in the return
+  // vector, the earliest that their memory can be freed is when this
+  // CordzSnapshot object is deleted.
+  std::vector<const CordzHandle*> DiagnosticsGetSafeToInspectDeletedHandles();
+
+ protected:
+  explicit CordzHandle(bool is_snapshot);
+  virtual ~CordzHandle();
+
+ private:
+  // Global queue data. CordzHandle stores a pointer to the global queue
+  // instance to harden against ODR violations.
+  struct Queue {
+    constexpr explicit Queue(absl::ConstInitType)
+        : mutex(absl::kConstInit,
+                absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL) {}
+
+    absl::base_internal::SpinLock mutex;
+    std::atomic<CordzHandle*> dq_tail ABSL_GUARDED_BY(mutex){nullptr};
+
+    // Returns true if this delete queue is empty. This method does not acquire
+    // the lock, but does a 'load acquire' observation on the delete queue tail.
+    // It is used inside Delete() to check for the presence of a delete queue
+    // without holding the lock. The assumption is that the caller is in the
+    // state of 'being deleted', and can not be newly discovered by a concurrent
+    // 'being constructed' snapshot instance. Practically, this means that any
+    // such discovery (`find`, 'first' or 'next', etc) must have proper 'happens
+    // before / after' semantics and atomic fences.
+    bool IsEmpty() const ABSL_NO_THREAD_SAFETY_ANALYSIS {
+      return dq_tail.load(std::memory_order_acquire) == nullptr;
+    }
+  };
+
+  void ODRCheck() const {
+#ifndef NDEBUG
+    ABSL_RAW_CHECK(queue_ == &global_queue_, "ODR violation in Cord");
+#endif
+  }
+
+  ABSL_CONST_INIT static Queue global_queue_;
+  Queue* const queue_ = &global_queue_;
+  const bool is_snapshot_;
+
+  // dq_prev_ and dq_next_ require the global queue mutex to be held.
+  // Unfortunately we can't use thread annotations such that the thread safety
+  // analysis understands that queue_ and global_queue_ are one and the same.
+  CordzHandle* dq_prev_  = nullptr;
+  CordzHandle* dq_next_ = nullptr;
+};
+
+class CordzSnapshot : public CordzHandle {
+ public:
+  CordzSnapshot() : CordzHandle(true) {}
+};
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_CORDZ_HANDLE_H_
diff --git a/absl/strings/internal/cordz_handle_test.cc b/absl/strings/internal/cordz_handle_test.cc
new file mode 100644
index 0000000..fd68e06
--- /dev/null
+++ b/absl/strings/internal/cordz_handle_test.cc
@@ -0,0 +1,265 @@
+// Copyright 2019 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/cordz_handle.h"
+
+#include <random>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/memory/memory.h"
+#include "absl/synchronization/internal/thread_pool.h"
+#include "absl/synchronization/notification.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::Gt;
+using ::testing::IsEmpty;
+using ::testing::SizeIs;
+
+// Local less verbose helper
+std::vector<const CordzHandle*> DeleteQueue() {
+  return CordzHandle::DiagnosticsGetDeleteQueue();
+}
+
+struct CordzHandleDeleteTracker : public CordzHandle {
+  bool* deleted;
+  explicit CordzHandleDeleteTracker(bool* deleted) : deleted(deleted) {}
+  ~CordzHandleDeleteTracker() override { *deleted = true; }
+};
+
+TEST(CordzHandleTest, DeleteQueueIsEmpty) {
+  EXPECT_THAT(DeleteQueue(), SizeIs(0));
+}
+
+TEST(CordzHandleTest, CordzHandleCreateDelete) {
+  bool deleted = false;
+  auto* handle = new CordzHandleDeleteTracker(&deleted);
+  EXPECT_FALSE(handle->is_snapshot());
+  EXPECT_TRUE(handle->SafeToDelete());
+  EXPECT_THAT(DeleteQueue(), SizeIs(0));
+
+  CordzHandle::Delete(handle);
+  EXPECT_THAT(DeleteQueue(), SizeIs(0));
+  EXPECT_TRUE(deleted);
+}
+
+TEST(CordzHandleTest, CordzSnapshotCreateDelete) {
+  auto* snapshot = new CordzSnapshot();
+  EXPECT_TRUE(snapshot->is_snapshot());
+  EXPECT_TRUE(snapshot->SafeToDelete());
+  EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot));
+  delete snapshot;
+  EXPECT_THAT(DeleteQueue(), SizeIs(0));
+}
+
+TEST(CordzHandleTest, CordzHandleCreateDeleteWithSnapshot) {
+  bool deleted = false;
+  auto* snapshot = new CordzSnapshot();
+  auto* handle = new CordzHandleDeleteTracker(&deleted);
+  EXPECT_FALSE(handle->SafeToDelete());
+
+  CordzHandle::Delete(handle);
+  EXPECT_THAT(DeleteQueue(), ElementsAre(handle, snapshot));
+  EXPECT_FALSE(deleted);
+  EXPECT_FALSE(handle->SafeToDelete());
+
+  delete snapshot;
+  EXPECT_THAT(DeleteQueue(), SizeIs(0));
+  EXPECT_TRUE(deleted);
+}
+
+TEST(CordzHandleTest, MultiSnapshot) {
+  bool deleted[3] = {false, false, false};
+
+  CordzSnapshot* snapshot[3];
+  CordzHandleDeleteTracker* handle[3];
+  for (int i = 0; i < 3; ++i) {
+    snapshot[i] = new CordzSnapshot();
+    handle[i] = new CordzHandleDeleteTracker(&deleted[i]);
+    CordzHandle::Delete(handle[i]);
+  }
+
+  EXPECT_THAT(DeleteQueue(), ElementsAre(handle[2], snapshot[2], handle[1],
+                                         snapshot[1], handle[0], snapshot[0]));
+  EXPECT_THAT(deleted, ElementsAre(false, false, false));
+
+  delete snapshot[1];
+  EXPECT_THAT(DeleteQueue(), ElementsAre(handle[2], snapshot[2], handle[1],
+                                         handle[0], snapshot[0]));
+  EXPECT_THAT(deleted, ElementsAre(false, false, false));
+
+  delete snapshot[0];
+  EXPECT_THAT(DeleteQueue(), ElementsAre(handle[2], snapshot[2]));
+  EXPECT_THAT(deleted, ElementsAre(true, true, false));
+
+  delete snapshot[2];
+  EXPECT_THAT(DeleteQueue(), SizeIs(0));
+  EXPECT_THAT(deleted, ElementsAre(true, true, deleted));
+}
+
+TEST(CordzHandleTest, DiagnosticsHandleIsSafeToInspect) {
+  CordzSnapshot snapshot1;
+  EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(nullptr));
+
+  auto* handle1 = new CordzHandle();
+  EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1));
+
+  CordzHandle::Delete(handle1);
+  EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1));
+
+  CordzSnapshot snapshot2;
+  auto* handle2 = new CordzHandle();
+  EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1));
+  EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle2));
+  EXPECT_FALSE(snapshot2.DiagnosticsHandleIsSafeToInspect(handle1));
+  EXPECT_TRUE(snapshot2.DiagnosticsHandleIsSafeToInspect(handle2));
+
+  CordzHandle::Delete(handle2);
+  EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1));
+}
+
+TEST(CordzHandleTest, DiagnosticsGetSafeToInspectDeletedHandles) {
+  EXPECT_THAT(DeleteQueue(), IsEmpty());
+
+  auto* handle = new CordzHandle();
+  auto* snapshot1 = new CordzSnapshot();
+
+  // snapshot1 should be able to see handle.
+  EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot1));
+  EXPECT_TRUE(snapshot1->DiagnosticsHandleIsSafeToInspect(handle));
+  EXPECT_THAT(snapshot1->DiagnosticsGetSafeToInspectDeletedHandles(),
+              IsEmpty());
+
+  // This handle will be safe to inspect as long as snapshot1 is alive. However,
+  // since only snapshot1 can prove that it's alive, it will be hidden from
+  // snapshot2.
+  CordzHandle::Delete(handle);
+
+  // This snapshot shouldn't be able to see handle because handle was already
+  // sent to Delete.
+  auto* snapshot2 = new CordzSnapshot();
+
+  // DeleteQueue elements are LIFO order.
+  EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot2, handle, snapshot1));
+
+  EXPECT_TRUE(snapshot1->DiagnosticsHandleIsSafeToInspect(handle));
+  EXPECT_FALSE(snapshot2->DiagnosticsHandleIsSafeToInspect(handle));
+
+  EXPECT_THAT(snapshot1->DiagnosticsGetSafeToInspectDeletedHandles(),
+              ElementsAre(handle));
+  EXPECT_THAT(snapshot2->DiagnosticsGetSafeToInspectDeletedHandles(),
+              IsEmpty());
+
+  CordzHandle::Delete(snapshot1);
+  EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot2));
+
+  CordzHandle::Delete(snapshot2);
+  EXPECT_THAT(DeleteQueue(), IsEmpty());
+}
+
+// Create and delete CordzHandle and CordzSnapshot objects in multiple threads
+// so that tsan has some time to chew on it and look for memory problems.
+TEST(CordzHandleTest, MultiThreaded) {
+  Notification stop;
+  static constexpr int kNumThreads = 4;
+  // Keep the number of handles relatively small so that the test will naturally
+  // transition to an empty delete queue during the test. If there are, say, 100
+  // handles, that will virtually never happen. With 10 handles and around 50k
+  // iterations in each of 4 threads, the delete queue appears to become empty
+  // around 200 times.
+  static constexpr int kNumHandles = 10;
+
+  // Each thread is going to pick a random index and atomically swap its
+  // CordzHandle with one in handles. This way, each thread can avoid
+  // manipulating a CordzHandle that might be operated upon in another thread.
+  std::vector<std::atomic<CordzHandle*>> handles(kNumHandles);
+
+  // global bool which is set when any thread did get some 'safe to inspect'
+  // handles. On some platforms and OSS tests, we might risk that some pool
+  // threads are starved, stalled, or just got a few unlikely random 'handle'
+  // coin tosses, so we satisfy this test with simply observing 'some' thread
+  // did something meaningful, which should minimize the potential for flakes.
+  std::atomic<bool> found_safe_to_inspect(false);
+
+  {
+    absl::synchronization_internal::ThreadPool pool(kNumThreads);
+    for (int i = 0; i < kNumThreads; ++i) {
+      pool.Schedule([&stop, &handles, &found_safe_to_inspect]() {
+        std::minstd_rand gen;
+        std::uniform_int_distribution<int> dist_type(0, 2);
+        std::uniform_int_distribution<int> dist_handle(0, kNumHandles - 1);
+
+        while (!stop.HasBeenNotified()) {
+          CordzHandle* handle;
+          switch (dist_type(gen)) {
+            case 0:
+              handle = new CordzHandle();
+              break;
+            case 1:
+              handle = new CordzSnapshot();
+              break;
+            default:
+              handle = nullptr;
+              break;
+          }
+          CordzHandle* old_handle = handles[dist_handle(gen)].exchange(handle);
+          if (old_handle != nullptr) {
+            std::vector<const CordzHandle*> safe_to_inspect =
+                old_handle->DiagnosticsGetSafeToInspectDeletedHandles();
+            for (const CordzHandle* handle : safe_to_inspect) {
+              // We're in a tight loop, so don't generate too many error
+              // messages.
+              ASSERT_FALSE(handle->is_snapshot());
+            }
+            if (!safe_to_inspect.empty()) {
+              found_safe_to_inspect.store(true);
+            }
+            CordzHandle::Delete(old_handle);
+          }
+        }
+
+        // Have each thread attempt to clean up everything. Some thread will be
+        // the last to reach this cleanup code, and it will be guaranteed to
+        // clean up everything because nothing remains to create new handles.
+        for (auto& h : handles) {
+          if (CordzHandle* handle = h.exchange(nullptr)) {
+            CordzHandle::Delete(handle);
+          }
+        }
+      });
+    }
+
+    // The threads will hammer away.  Give it a little bit of time for tsan to
+    // spot errors.
+    absl::SleepFor(absl::Seconds(3));
+    stop.Notify();
+  }
+
+  // Confirm that the test did *something*. This check will be satisfied as
+  // long as any thread has deleted a CordzSnapshot object and a non-snapshot
+  // CordzHandle was deleted after the CordzSnapshot was created.
+  // See also comments on `found_safe_to_inspect`
+  EXPECT_TRUE(found_safe_to_inspect.load());
+}
+
+}  // namespace
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc
new file mode 100644
index 0000000..5c18bbc
--- /dev/null
+++ b/absl/strings/internal/cordz_info.cc
@@ -0,0 +1,445 @@
+// Copyright 2019 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/cordz_info.h"
+
+#include "absl/base/config.h"
+#include "absl/base/internal/spinlock.h"
+#include "absl/container/inlined_vector.h"
+#include "absl/debugging/stacktrace.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_btree.h"
+#include "absl/strings/internal/cord_rep_ring.h"
+#include "absl/strings/internal/cordz_handle.h"
+#include "absl/strings/internal/cordz_statistics.h"
+#include "absl/strings/internal/cordz_update_tracker.h"
+#include "absl/synchronization/mutex.h"
+#include "absl/types/span.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+using ::absl::base_internal::SpinLockHolder;
+
+constexpr int CordzInfo::kMaxStackDepth;
+
+ABSL_CONST_INIT CordzInfo::List CordzInfo::global_list_{absl::kConstInit};
+
+namespace {
+
+// CordRepAnalyzer performs the analysis of a cord.
+//
+// It computes absolute node counts and total memory usage, and an 'estimated
+// fair share memory usage` statistic.
+// Conceptually, it divides the 'memory usage' at each location in the 'cord
+// graph' by the cumulative reference count of that location. The cumulative
+// reference count is the factored total of all edges leading into that node.
+//
+// The top level node is treated specially: we assume the current thread
+// (typically called from the CordzHandler) to hold a reference purely to
+// perform a safe analysis, and not being part of the application. So we
+// substract 1 from the reference count of the top node to compute the
+// 'application fair share' excluding the reference of the current thread.
+//
+// An example of fair sharing, and why we multiply reference counts:
+// Assume we have 2 CordReps, both being a Substring referencing a Flat:
+//   CordSubstring A (refcount = 5) --> child Flat C (refcount = 2)
+//   CordSubstring B (refcount = 9) --> child Flat C (refcount = 2)
+//
+// Flat C has 2 incoming edges from the 2 substrings (refcount = 2) and is not
+// referenced directly anywhere else. Translated into a 'fair share', we then
+// attribute 50% of the memory (memory / refcount = 2) to each incoming edge.
+// Rep A has a refcount of 5, so we attribute each incoming edge 1 / 5th of the
+// memory cost below it, i.e.: the fair share of Rep A of the memory used by C
+// is then 'memory C / (refcount C * refcount A) + (memory A / refcount A)'.
+// It is also easy to see how all incoming edges add up to 100%.
+class CordRepAnalyzer {
+ public:
+  // Creates an analyzer instance binding to `statistics`.
+  explicit CordRepAnalyzer(CordzStatistics& statistics)
+      : statistics_(statistics) {}
+
+  // Analyzes the memory statistics and node counts for the provided `rep`, and
+  // adds the results to `statistics`. Note that node counts and memory sizes
+  // are not initialized, computed values are added to any existing values.
+  void AnalyzeCordRep(const CordRep* rep) {
+    // Process all linear nodes.
+    // As per the class comments, use refcout - 1 on the top level node, as the
+    // top level node is assumed to be referenced only for analysis purposes.
+    size_t refcount = rep->refcount.Get();
+    RepRef repref{rep, (refcount > 1) ? refcount - 1 : 1};
+
+    // Process all top level linear nodes (substrings and flats).
+    repref = CountLinearReps(repref, memory_usage_);
+
+    if (repref.rep != nullptr) {
+      if (repref.rep->tag == RING) {
+        AnalyzeRing(repref);
+      } else if (repref.rep->tag == BTREE) {
+        AnalyzeBtree(repref);
+      } else if (repref.rep->tag == CONCAT) {
+        AnalyzeConcat(repref);
+      } else {
+        // We should have either a concat, btree, or ring node if not null.
+        assert(false);
+      }
+    }
+
+    // Adds values to output
+    statistics_.estimated_memory_usage += memory_usage_.total;
+    statistics_.estimated_fair_share_memory_usage +=
+        static_cast<size_t>(memory_usage_.fair_share);
+  }
+
+ private:
+  // RepRef identifies a CordRep* inside the Cord tree with its cumulative
+  // refcount including itself. For example, a tree consisting of a substring
+  // with a refcount of 3 and a child flat with a refcount of 4 will have RepRef
+  // refcounts of 3 and 12 respectively.
+  struct RepRef {
+    const CordRep* rep;
+    size_t refcount;
+
+    // Returns a 'child' RepRef which contains the cumulative reference count of
+    // this instance multiplied by the child's reference count.
+    RepRef Child(const CordRep* child) const {
+      return RepRef{child, refcount * child->refcount.Get()};
+    }
+  };
+
+  // Memory usage values
+  struct MemoryUsage {
+    size_t total = 0;
+    double fair_share = 0.0;
+
+    // Adds 'size` memory usage to this class, with a cumulative (recursive)
+    // reference count of `refcount`
+    void Add(size_t size, size_t refcount) {
+      total += size;
+      fair_share += static_cast<double>(size) / refcount;
+    }
+  };
+
+  // Returns `rr` if `rr.rep` is not null and a CONCAT type.
+  // Asserts that `rr.rep` is a concat node or null.
+  static RepRef AssertConcat(RepRef repref) {
+    const CordRep* rep = repref.rep;
+    assert(rep == nullptr || rep->tag == CONCAT);
+    return (rep != nullptr && rep->tag == CONCAT) ? repref : RepRef{nullptr, 0};
+  }
+
+  // Counts a flat of the provide allocated size
+  void CountFlat(size_t size) {
+    statistics_.node_count++;
+    statistics_.node_counts.flat++;
+    if (size <= 64) {
+      statistics_.node_counts.flat_64++;
+    } else if (size <= 128) {
+      statistics_.node_counts.flat_128++;
+    } else if (size <= 256) {
+      statistics_.node_counts.flat_256++;
+    } else if (size <= 512) {
+      statistics_.node_counts.flat_512++;
+    } else if (size <= 1024) {
+      statistics_.node_counts.flat_1k++;
+    }
+  }
+
+  // Processes 'linear' reps (substring, flat, external) not requiring iteration
+  // or recursion. Returns RefRep{null} if all reps were processed, else returns
+  // the top-most non-linear concat or ring cordrep.
+  // Node counts are updated into `statistics_`, memory usage is update into
+  // `memory_usage`, which typically references `memory_usage_` except for ring
+  // buffers where we count children unrounded.
+  RepRef CountLinearReps(RepRef rep, MemoryUsage& memory_usage) {
+    // Consume all substrings
+    while (rep.rep->tag == SUBSTRING) {
+      statistics_.node_count++;
+      statistics_.node_counts.substring++;
+      memory_usage.Add(sizeof(CordRepSubstring), rep.refcount);
+      rep = rep.Child(rep.rep->substring()->child);
+    }
+
+    // Consume possible FLAT
+    if (rep.rep->tag >= FLAT) {
+      size_t size = rep.rep->flat()->AllocatedSize();
+      CountFlat(size);
+      memory_usage.Add(size, rep.refcount);
+      return RepRef{nullptr, 0};
+    }
+
+    // Consume possible external
+    if (rep.rep->tag == EXTERNAL) {
+      statistics_.node_count++;
+      statistics_.node_counts.external++;
+      size_t size = rep.rep->length + sizeof(CordRepExternalImpl<intptr_t>);
+      memory_usage.Add(size, rep.refcount);
+      return RepRef{nullptr, 0};
+    }
+
+    return rep;
+  }
+
+  // Analyzes the provided concat node in a flattened recursive way.
+  void AnalyzeConcat(RepRef rep) {
+    absl::InlinedVector<RepRef, 47> pending;
+
+    while (rep.rep != nullptr) {
+      const CordRepConcat* concat = rep.rep->concat();
+      RepRef left = rep.Child(concat->left);
+      RepRef right = rep.Child(concat->right);
+
+      statistics_.node_count++;
+      statistics_.node_counts.concat++;
+      memory_usage_.Add(sizeof(CordRepConcat), rep.refcount);
+
+      right = AssertConcat(CountLinearReps(right, memory_usage_));
+      rep = AssertConcat(CountLinearReps(left, memory_usage_));
+      if (rep.rep != nullptr) {
+        if (right.rep != nullptr) {
+          pending.push_back(right);
+        }
+      } else if (right.rep != nullptr) {
+        rep = right;
+      } else if (!pending.empty()) {
+        rep = pending.back();
+        pending.pop_back();
+      }
+    }
+  }
+
+  // Analyzes the provided ring.
+  void AnalyzeRing(RepRef rep) {
+    statistics_.node_count++;
+    statistics_.node_counts.ring++;
+    const CordRepRing* ring = rep.rep->ring();
+    memory_usage_.Add(CordRepRing::AllocSize(ring->capacity()), rep.refcount);
+    ring->ForEach([&](CordRepRing::index_type pos) {
+      CountLinearReps(rep.Child(ring->entry_child(pos)), memory_usage_);
+    });
+  }
+
+  // Analyzes the provided btree.
+  void AnalyzeBtree(RepRef rep) {
+    statistics_.node_count++;
+    statistics_.node_counts.btree++;
+    memory_usage_.Add(sizeof(CordRepBtree), rep.refcount);
+    const CordRepBtree* tree = rep.rep->btree();
+    if (tree->height() > 0) {
+      for (CordRep* edge : tree->Edges()) {
+        AnalyzeBtree(rep.Child(edge));
+      }
+    } else {
+      for (CordRep* edge : tree->Edges()) {
+        CountLinearReps(rep.Child(edge), memory_usage_);
+      }
+    }
+  }
+
+  CordzStatistics& statistics_;
+  MemoryUsage memory_usage_;
+};
+
+}  // namespace
+
+CordzInfo* CordzInfo::Head(const CordzSnapshot& snapshot) {
+  ABSL_ASSERT(snapshot.is_snapshot());
+
+  // We can do an 'unsafe' load of 'head', as we are guaranteed that the
+  // instance it points to is kept alive by the provided CordzSnapshot, so we
+  // can simply return the current value using an acquire load.
+  // We do enforce in DEBUG builds that the 'head' value is present in the
+  // delete queue: ODR violations may lead to 'snapshot' and 'global_list_'
+  // being in different libraries / modules.
+  CordzInfo* head = global_list_.head.load(std::memory_order_acquire);
+  ABSL_ASSERT(snapshot.DiagnosticsHandleIsSafeToInspect(head));
+  return head;
+}
+
+CordzInfo* CordzInfo::Next(const CordzSnapshot& snapshot) const {
+  ABSL_ASSERT(snapshot.is_snapshot());
+
+  // Similar to the 'Head()' function, we do not need a mutex here.
+  CordzInfo* next = ci_next_.load(std::memory_order_acquire);
+  ABSL_ASSERT(snapshot.DiagnosticsHandleIsSafeToInspect(this));
+  ABSL_ASSERT(snapshot.DiagnosticsHandleIsSafeToInspect(next));
+  return next;
+}
+
+void CordzInfo::TrackCord(InlineData& cord, MethodIdentifier method) {
+  assert(cord.is_tree());
+  assert(!cord.is_profiled());
+  CordzInfo* cordz_info = new CordzInfo(cord.as_tree(), nullptr, method);
+  cord.set_cordz_info(cordz_info);
+  cordz_info->Track();
+}
+
+void CordzInfo::TrackCord(InlineData& cord, const InlineData& src,
+                          MethodIdentifier method) {
+  assert(cord.is_tree());
+  assert(src.is_tree());
+
+  // Unsample current as we the current cord is being replaced with 'src',
+  // so any method history is no longer relevant.
+  CordzInfo* cordz_info = cord.cordz_info();
+  if (cordz_info != nullptr) cordz_info->Untrack();
+
+  // Start new cord sample
+  cordz_info = new CordzInfo(cord.as_tree(), src.cordz_info(), method);
+  cord.set_cordz_info(cordz_info);
+  cordz_info->Track();
+}
+
+void CordzInfo::MaybeTrackCordImpl(InlineData& cord, const InlineData& src,
+                                   MethodIdentifier method) {
+  if (src.is_profiled()) {
+    TrackCord(cord, src, method);
+  } else if (cord.is_profiled()) {
+    cord.cordz_info()->Untrack();
+    cord.clear_cordz_info();
+  }
+}
+
+CordzInfo::MethodIdentifier CordzInfo::GetParentMethod(const CordzInfo* src) {
+  if (src == nullptr) return MethodIdentifier::kUnknown;
+  return src->parent_method_ != MethodIdentifier::kUnknown ? src->parent_method_
+                                                           : src->method_;
+}
+
+int CordzInfo::FillParentStack(const CordzInfo* src, void** stack) {
+  assert(stack);
+  if (src == nullptr) return 0;
+  if (src->parent_stack_depth_) {
+    memcpy(stack, src->parent_stack_, src->parent_stack_depth_ * sizeof(void*));
+    return src->parent_stack_depth_;
+  }
+  memcpy(stack, src->stack_, src->stack_depth_ * sizeof(void*));
+  return src->stack_depth_;
+}
+
+CordzInfo::CordzInfo(CordRep* rep, const CordzInfo* src,
+                     MethodIdentifier method)
+    : rep_(rep),
+      stack_depth_(absl::GetStackTrace(stack_, /*max_depth=*/kMaxStackDepth,
+                                       /*skip_count=*/1)),
+      parent_stack_depth_(FillParentStack(src, parent_stack_)),
+      method_(method),
+      parent_method_(GetParentMethod(src)),
+      create_time_(absl::Now()) {
+  update_tracker_.LossyAdd(method);
+  if (src) {
+    // Copy parent counters.
+    update_tracker_.LossyAdd(src->update_tracker_);
+  }
+}
+
+CordzInfo::~CordzInfo() {
+  // `rep_` is potentially kept alive if CordzInfo is included
+  // in a collection snapshot (which should be rare).
+  if (ABSL_PREDICT_FALSE(rep_)) {
+    CordRep::Unref(rep_);
+  }
+}
+
+void CordzInfo::Track() {
+  SpinLockHolder l(&list_->mutex);
+
+  CordzInfo* const head = list_->head.load(std::memory_order_acquire);
+  if (head != nullptr) {
+    head->ci_prev_.store(this, std::memory_order_release);
+  }
+  ci_next_.store(head, std::memory_order_release);
+  list_->head.store(this, std::memory_order_release);
+}
+
+void CordzInfo::Untrack() {
+  ODRCheck();
+  {
+    SpinLockHolder l(&list_->mutex);
+
+    CordzInfo* const head = list_->head.load(std::memory_order_acquire);
+    CordzInfo* const next = ci_next_.load(std::memory_order_acquire);
+    CordzInfo* const prev = ci_prev_.load(std::memory_order_acquire);
+
+    if (next) {
+      ABSL_ASSERT(next->ci_prev_.load(std::memory_order_acquire) == this);
+      next->ci_prev_.store(prev, std::memory_order_release);
+    }
+    if (prev) {
+      ABSL_ASSERT(head != this);
+      ABSL_ASSERT(prev->ci_next_.load(std::memory_order_acquire) == this);
+      prev->ci_next_.store(next, std::memory_order_release);
+    } else {
+      ABSL_ASSERT(head == this);
+      list_->head.store(next, std::memory_order_release);
+    }
+  }
+
+  // We can no longer be discovered: perform a fast path check if we are not
+  // listed on any delete queue, so we can directly delete this instance.
+  if (SafeToDelete()) {
+    UnsafeSetCordRep(nullptr);
+    delete this;
+    return;
+  }
+
+  // We are likely part of a snapshot, extend the life of the CordRep
+  {
+    absl::MutexLock lock(&mutex_);
+    if (rep_) CordRep::Ref(rep_);
+  }
+  CordzHandle::Delete(this);
+}
+
+void CordzInfo::Lock(MethodIdentifier method)
+    ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex_) {
+  mutex_.Lock();
+  update_tracker_.LossyAdd(method);
+  assert(rep_);
+}
+
+void CordzInfo::Unlock() ABSL_UNLOCK_FUNCTION(mutex_) {
+  bool tracked = rep_ != nullptr;
+  mutex_.Unlock();
+  if (!tracked) {
+    Untrack();
+  }
+}
+
+absl::Span<void* const> CordzInfo::GetStack() const {
+  return absl::MakeConstSpan(stack_, stack_depth_);
+}
+
+absl::Span<void* const> CordzInfo::GetParentStack() const {
+  return absl::MakeConstSpan(parent_stack_, parent_stack_depth_);
+}
+
+CordzStatistics CordzInfo::GetCordzStatistics() const {
+  CordzStatistics stats;
+  stats.method = method_;
+  stats.parent_method = parent_method_;
+  stats.update_tracker = update_tracker_;
+  if (CordRep* rep = RefCordRep()) {
+    stats.size = rep->length;
+    CordRepAnalyzer analyzer(stats);
+    analyzer.AnalyzeCordRep(rep);
+    CordRep::Unref(rep);
+  }
+  return stats;
+}
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cordz_info.h b/absl/strings/internal/cordz_info.h
new file mode 100644
index 0000000..026d5b9
--- /dev/null
+++ b/absl/strings/internal/cordz_info.h
@@ -0,0 +1,298 @@
+// Copyright 2019 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_CORDZ_INFO_H_
+#define ABSL_STRINGS_CORDZ_INFO_H_
+
+#include <atomic>
+#include <cstdint>
+#include <functional>
+
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/internal/spinlock.h"
+#include "absl/base/thread_annotations.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cordz_functions.h"
+#include "absl/strings/internal/cordz_handle.h"
+#include "absl/strings/internal/cordz_statistics.h"
+#include "absl/strings/internal/cordz_update_tracker.h"
+#include "absl/synchronization/mutex.h"
+#include "absl/types/span.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// CordzInfo tracks a profiled Cord. Each of these objects can be in two places.
+// If a Cord is alive, the CordzInfo will be in the global_cordz_infos map, and
+// can also be retrieved via the linked list starting with
+// global_cordz_infos_head and continued via the cordz_info_next() method. When
+// a Cord has reached the end of its lifespan, the CordzInfo object will be
+// migrated out of the global_cordz_infos list and the global_cordz_infos_map,
+// and will either be deleted or appended to the global_delete_queue. If it is
+// placed on the global_delete_queue, the CordzInfo object will be cleaned in
+// the destructor of a CordzSampleToken object.
+class ABSL_LOCKABLE CordzInfo : public CordzHandle {
+ public:
+  using MethodIdentifier = CordzUpdateTracker::MethodIdentifier;
+
+  // TrackCord creates a CordzInfo instance which tracks important metrics of
+  // a sampled cord, and stores the created CordzInfo instance into `cord'. All
+  // CordzInfo instances are placed in a global list which is used to discover
+  // and snapshot all actively tracked cords. Callers are responsible for
+  // calling UntrackCord() before the tracked Cord instance is deleted, or to
+  // stop tracking the sampled Cord. Callers are also responsible for guarding
+  // changes to the 'tree' value of a Cord (InlineData.tree) through the Lock()
+  // and Unlock() calls. Any change resulting in a new tree value for the cord
+  // requires a call to SetCordRep() before the old tree has been unreffed
+  // and/or deleted. `method` identifies the Cord public API method initiating
+  // the cord to be sampled.
+  // Requires `cord` to hold a tree, and `cord.cordz_info()` to be null.
+  static void TrackCord(InlineData& cord, MethodIdentifier method);
+
+  // Identical to TrackCord(), except that this function fills the
+  // `parent_stack` and `parent_method` properties of the returned CordzInfo
+  // instance from the provided `src` instance if `src` is sampled.
+  // This function should be used for sampling 'copy constructed' and 'copy
+  // assigned' cords. This function allows 'cord` to be already sampled, in
+  // which case the CordzInfo will be newly created from `src`.
+  static void TrackCord(InlineData& cord, const InlineData& src,
+                        MethodIdentifier method);
+
+  // Maybe sample the cord identified by 'cord' for method 'method'.
+  // Uses `cordz_should_profile` to randomly pick cords to be sampled, and if
+  // so, invokes `TrackCord` to start sampling `cord`.
+  static void MaybeTrackCord(InlineData& cord, MethodIdentifier method);
+
+  // Maybe sample the cord identified by 'cord' for method 'method'.
+  // `src` identifies a 'parent' cord which is assigned to `cord`, typically the
+  // input cord for a copy constructor, or an assign method such as `operator=`
+  // `cord` will be sampled if (and only if) `src` is sampled.
+  // If `cord` is currently being sampled and `src` is not being sampled, then
+  // this function will stop sampling the cord and reset the cord's cordz_info.
+  //
+  // Previously this function defined that `cord` will be sampled if either
+  // `src` is sampled, or if `cord` is randomly picked for sampling. However,
+  // this can cause issues, as there may be paths where some cord is assigned an
+  // indirect copy of it's own value. As such a 'string of copies' would then
+  // remain sampled (`src.is_profiled`), then assigning such a cord back to
+  // 'itself' creates a cycle where the cord will converge to 'always sampled`.
+  //
+  // For example:
+  //
+  //   Cord x;
+  //   for (...) {
+  //     // Copy ctor --> y.is_profiled := x.is_profiled | random(...)
+  //     Cord y = x;
+  //     ...
+  //     // Assign x = y --> x.is_profiled = y.is_profiled | random(...)
+  //     //              ==> x.is_profiled |= random(...)
+  //     //              ==> x converges to 'always profiled'
+  //     x = y;
+  //   }
+  static void MaybeTrackCord(InlineData& cord, const InlineData& src,
+                             MethodIdentifier method);
+
+  // Stops tracking changes for a sampled cord, and deletes the provided info.
+  // This function must be called before the sampled cord instance is deleted,
+  // and before the root cordrep of the sampled cord is unreffed.
+  // This function may extend the lifetime of the cordrep in cases where the
+  // CordInfo instance is being held by a concurrent collection thread.
+  void Untrack();
+
+  // Invokes UntrackCord() on `info` if `info` is not null.
+  static void MaybeUntrackCord(CordzInfo* info);
+
+  CordzInfo() = delete;
+  CordzInfo(const CordzInfo&) = delete;
+  CordzInfo& operator=(const CordzInfo&) = delete;
+
+  // Retrieves the oldest existing CordzInfo.
+  static CordzInfo* Head(const CordzSnapshot& snapshot)
+      ABSL_NO_THREAD_SAFETY_ANALYSIS;
+
+  // Retrieves the next oldest existing CordzInfo older than 'this' instance.
+  CordzInfo* Next(const CordzSnapshot& snapshot) const
+      ABSL_NO_THREAD_SAFETY_ANALYSIS;
+
+  // Locks this instance for the update identified by `method`.
+  // Increases the count for `method` in `update_tracker`.
+  void Lock(MethodIdentifier method) ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex_);
+
+  // Unlocks this instance. If the contained `rep` has been set to null
+  // indicating the Cord has been cleared or is otherwise no longer sampled,
+  // then this method will delete this CordzInfo instance.
+  void Unlock() ABSL_UNLOCK_FUNCTION(mutex_);
+
+  // Asserts that this CordzInfo instance is locked.
+  void AssertHeld() ABSL_ASSERT_EXCLUSIVE_LOCK(mutex_);
+
+  // Updates the `rep` property of this instance. This methods is invoked by
+  // Cord logic each time the root node of a sampled Cord changes, and before
+  // the old root reference count is deleted. This guarantees that collection
+  // code can always safely take a reference on the tracked cord.
+  // Requires a lock to be held through the `Lock()` method.
+  // TODO(b/117940323): annotate with ABSL_EXCLUSIVE_LOCKS_REQUIRED once all
+  // Cord code is in a state where this can be proven true by the compiler.
+  void SetCordRep(CordRep* rep);
+
+  // Returns the current `rep` property of this instance with a reference
+  // added, or null if this instance represents a cord that has since been
+  // deleted or untracked.
+  CordRep* RefCordRep() const ABSL_LOCKS_EXCLUDED(mutex_);
+
+  // Returns the current value of `rep_` for testing purposes only.
+  CordRep* GetCordRepForTesting() const ABSL_NO_THREAD_SAFETY_ANALYSIS {
+    return rep_;
+  }
+
+  // Sets the current value of `rep_` for testing purposes only.
+  void SetCordRepForTesting(CordRep* rep) ABSL_NO_THREAD_SAFETY_ANALYSIS {
+    rep_ = rep;
+  }
+
+  // Returns the stack trace for where the cord was first sampled. Cords are
+  // potentially sampled when they promote from an inlined cord to a tree or
+  // ring representation, which is not necessarily the location where the cord
+  // was first created. Some cords are created as inlined cords, and only as
+  // data is added do they become a non-inlined cord. However, typically the
+  // location represents reasonably well where the cord is 'created'.
+  absl::Span<void* const> GetStack() const;
+
+  // Returns the stack trace for a sampled cord's 'parent stack trace'. This
+  // value may be set if the cord is sampled (promoted) after being created
+  // from, or being assigned the value of an existing (sampled) cord.
+  absl::Span<void* const> GetParentStack() const;
+
+  // Retrieves the CordzStatistics associated with this Cord. The statistics
+  // are only updated when a Cord goes through a mutation, such as an Append
+  // or RemovePrefix.
+  CordzStatistics GetCordzStatistics() const;
+
+ private:
+  using SpinLock = absl::base_internal::SpinLock;
+  using SpinLockHolder = ::absl::base_internal::SpinLockHolder;
+
+  // Global cordz info list. CordzInfo stores a pointer to the global list
+  // instance to harden against ODR violations.
+  struct List {
+    constexpr explicit List(absl::ConstInitType)
+        : mutex(absl::kConstInit,
+                absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL) {}
+
+    SpinLock mutex;
+    std::atomic<CordzInfo*> head ABSL_GUARDED_BY(mutex){nullptr};
+  };
+
+  static constexpr int kMaxStackDepth = 64;
+
+  explicit CordzInfo(CordRep* rep, const CordzInfo* src,
+                     MethodIdentifier method);
+  ~CordzInfo() override;
+
+  // Sets `rep_` without holding a lock.
+  void UnsafeSetCordRep(CordRep* rep) ABSL_NO_THREAD_SAFETY_ANALYSIS;
+
+  void Track();
+
+  // Returns the parent method from `src`, which is either `parent_method_` or
+  // `method_` depending on `parent_method_` being kUnknown.
+  // Returns kUnknown if `src` is null.
+  static MethodIdentifier GetParentMethod(const CordzInfo* src);
+
+  // Fills the provided stack from `src`, copying either `parent_stack_` or
+  // `stack_` depending on `parent_stack_` being empty, returning the size of
+  // the parent stack.
+  // Returns 0 if `src` is null.
+  static int FillParentStack(const CordzInfo* src, void** stack);
+
+  void ODRCheck() const {
+#ifndef NDEBUG
+    ABSL_RAW_CHECK(list_ == &global_list_, "ODR violation in Cord");
+#endif
+  }
+
+  // Non-inlined implementation of `MaybeTrackCord`, which is executed if
+  // either `src` is sampled or `cord` is sampled, and either untracks or
+  // tracks `cord` as documented per `MaybeTrackCord`.
+  static void MaybeTrackCordImpl(InlineData& cord, const InlineData& src,
+                                 MethodIdentifier method);
+
+  ABSL_CONST_INIT static List global_list_;
+  List* const list_ = &global_list_;
+
+  // ci_prev_ and ci_next_ require the global list mutex to be held.
+  // Unfortunately we can't use thread annotations such that the thread safety
+  // analysis understands that list_ and global_list_ are one and the same.
+  std::atomic<CordzInfo*> ci_prev_{nullptr};
+  std::atomic<CordzInfo*> ci_next_{nullptr};
+
+  mutable absl::Mutex mutex_;
+  CordRep* rep_ ABSL_GUARDED_BY(mutex_);
+
+  void* stack_[kMaxStackDepth];
+  void* parent_stack_[kMaxStackDepth];
+  const int stack_depth_;
+  const int parent_stack_depth_;
+  const MethodIdentifier method_;
+  const MethodIdentifier parent_method_;
+  CordzUpdateTracker update_tracker_;
+  const absl::Time create_time_;
+};
+
+inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord(
+    InlineData& cord, MethodIdentifier method) {
+  if (ABSL_PREDICT_FALSE(cordz_should_profile())) {
+    TrackCord(cord, method);
+  }
+}
+
+inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord(
+    InlineData& cord, const InlineData& src, MethodIdentifier method) {
+  if (ABSL_PREDICT_FALSE(InlineData::is_either_profiled(cord, src))) {
+    MaybeTrackCordImpl(cord, src, method);
+  }
+}
+
+inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeUntrackCord(
+    CordzInfo* info) {
+  if (ABSL_PREDICT_FALSE(info)) {
+    info->Untrack();
+  }
+}
+
+inline void CordzInfo::AssertHeld() ABSL_ASSERT_EXCLUSIVE_LOCK(mutex_) {
+#ifndef NDEBUG
+  mutex_.AssertHeld();
+#endif
+}
+
+inline void CordzInfo::SetCordRep(CordRep* rep) {
+  AssertHeld();
+  rep_ = rep;
+}
+
+inline void CordzInfo::UnsafeSetCordRep(CordRep* rep) { rep_ = rep; }
+
+inline CordRep* CordzInfo::RefCordRep() const ABSL_LOCKS_EXCLUDED(mutex_) {
+  MutexLock lock(&mutex_);
+  return rep_ ? CordRep::Ref(rep_) : nullptr;
+}
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_CORDZ_INFO_H_
diff --git a/absl/strings/internal/cordz_info_statistics_test.cc b/absl/strings/internal/cordz_info_statistics_test.cc
new file mode 100644
index 0000000..7430d28
--- /dev/null
+++ b/absl/strings/internal/cordz_info_statistics_test.cc
@@ -0,0 +1,625 @@
+// Copyright 2021 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 <iostream>
+#include <random>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/strings/cord.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_btree.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+#include "absl/strings/internal/cord_rep_ring.h"
+#include "absl/strings/internal/cordz_info.h"
+#include "absl/strings/internal/cordz_sample_token.h"
+#include "absl/strings/internal/cordz_statistics.h"
+#include "absl/strings/internal/cordz_update_scope.h"
+#include "absl/strings/internal/cordz_update_tracker.h"
+#include "absl/synchronization/internal/thread_pool.h"
+#include "absl/synchronization/notification.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// Do not print statistics contents, the matcher prints them as needed.
+inline void PrintTo(const CordzStatistics& stats, std::ostream* s) {
+  if (s) *s << "CordzStatistics{...}";
+}
+
+namespace {
+
+using ::testing::Ge;
+
+// Creates a flat of the specified allocated size
+CordRepFlat* Flat(size_t size) {
+  // Round up to a tag size, as we are going to poke an exact tag size back into
+  // the allocated flat. 'size returning allocators' could grant us more than we
+  // wanted, but we are ok to poke the 'requested' size in the tag, even in the
+  // presence of sized deletes, so we need to make sure the size rounds
+  // perfectly to a tag value.
+  assert(size >= kMinFlatSize);
+  size = RoundUpForTag(size);
+  CordRepFlat* flat = CordRepFlat::New(size - kFlatOverhead);
+  flat->tag = AllocatedSizeToTag(size);
+  flat->length = size - kFlatOverhead;
+  return flat;
+}
+
+// Creates an external of the specified length
+CordRepExternal* External(int length = 512) {
+  return static_cast<CordRepExternal*>(
+      NewExternalRep(absl::string_view("", length), [](absl::string_view) {}));
+}
+
+// Creates a substring on the provided rep of length - 1
+CordRepSubstring* Substring(CordRep* rep) {
+  auto* substring = new CordRepSubstring;
+  substring->length = rep->length - 1;
+  substring->tag = SUBSTRING;
+  substring->child = rep;
+  return substring;
+}
+
+// Creates a concat on the provided reps
+CordRepConcat* Concat(CordRep* left, CordRep* right) {
+  auto* concat = new CordRepConcat;
+  concat->length = left->length + right->length;
+  concat->tag = CONCAT;
+  concat->left = left;
+  concat->right = right;
+  return concat;
+}
+
+// Reference count helper
+struct RefHelper {
+  std::vector<CordRep*> refs;
+
+  ~RefHelper() {
+    for (CordRep* rep : refs) {
+      CordRep::Unref(rep);
+    }
+  }
+
+  // Invokes CordRep::Unref() on `rep` when this instance is destroyed.
+  template <typename T>
+  T* NeedsUnref(T* rep) {
+    refs.push_back(rep);
+    return rep;
+  }
+
+  // Adds `n` reference counts to `rep` which will be unreffed when this
+  // instance is destroyed.
+  template <typename T>
+  T* Ref(T* rep, size_t n = 1) {
+    while (n--) {
+      NeedsUnref(CordRep::Ref(rep));
+    }
+    return rep;
+  }
+};
+
+// Sizeof helper. Returns the allocated size of `p`, excluding any child
+// elements for substring, concat and ring cord reps.
+template <typename T>
+size_t SizeOf(const T* rep) {
+  return sizeof(T);
+}
+
+template <>
+size_t SizeOf(const CordRepFlat* rep) {
+  return rep->AllocatedSize();
+}
+
+template <>
+size_t SizeOf(const CordRepExternal* rep) {
+  // See cord.cc
+  return sizeof(CordRepExternalImpl<intptr_t>) + rep->length;
+}
+
+template <>
+size_t SizeOf(const CordRepRing* rep) {
+  return CordRepRing::AllocSize(rep->capacity());
+}
+
+// Computes fair share memory used in a naive 'we dare to recurse' way.
+double FairShareImpl(CordRep* rep, size_t ref) {
+  double self = 0.0, children = 0.0;
+  ref *= rep->refcount.Get();
+  if (rep->tag >= FLAT) {
+    self = SizeOf(rep->flat());
+  } else if (rep->tag == EXTERNAL) {
+    self = SizeOf(rep->external());
+  } else if (rep->tag == SUBSTRING) {
+    self = SizeOf(rep->substring());
+    children = FairShareImpl(rep->substring()->child, ref);
+  } else if (rep->tag == BTREE) {
+    self = SizeOf(rep->btree());
+    for (CordRep*edge : rep->btree()->Edges()) {
+      children += FairShareImpl(edge, ref);
+    }
+  } else if (rep->tag == RING) {
+    self = SizeOf(rep->ring());
+    rep->ring()->ForEach([&](CordRepRing::index_type i) {
+      self += FairShareImpl(rep->ring()->entry_child(i), 1);
+    });
+  } else if (rep->tag == CONCAT) {
+    self = SizeOf(rep->concat());
+    children = FairShareImpl(rep->concat()->left, ref) +
+               FairShareImpl(rep->concat()->right, ref);
+  } else {
+    assert(false);
+  }
+  return self / ref + children;
+}
+
+// Returns the fair share memory size from `ShareFhareImpl()` as a size_t.
+size_t FairShare(CordRep* rep, size_t ref = 1) {
+  return static_cast<size_t>(FairShareImpl(rep, ref));
+}
+
+// Samples the cord and returns CordzInfo::GetStatistics()
+CordzStatistics SampleCord(CordRep* rep) {
+  InlineData cord(rep);
+  CordzInfo::TrackCord(cord, CordzUpdateTracker::kUnknown);
+  CordzStatistics stats = cord.cordz_info()->GetCordzStatistics();
+  cord.cordz_info()->Untrack();
+  return stats;
+}
+
+MATCHER_P(EqStatistics, stats, "Statistics equal expected values") {
+  bool ok = true;
+
+#define STATS_MATCHER_EXPECT_EQ(member)                              \
+  if (stats.member != arg.member) {                                  \
+    *result_listener << "\n    stats." << #member                    \
+                     << ": actual = " << arg.member << ", expected " \
+                     << stats.member;                                \
+    ok = false;                                                      \
+  }
+
+  STATS_MATCHER_EXPECT_EQ(size);
+  STATS_MATCHER_EXPECT_EQ(node_count);
+  STATS_MATCHER_EXPECT_EQ(node_counts.flat);
+  STATS_MATCHER_EXPECT_EQ(node_counts.flat_64);
+  STATS_MATCHER_EXPECT_EQ(node_counts.flat_128);
+  STATS_MATCHER_EXPECT_EQ(node_counts.flat_256);
+  STATS_MATCHER_EXPECT_EQ(node_counts.flat_512);
+  STATS_MATCHER_EXPECT_EQ(node_counts.flat_1k);
+  STATS_MATCHER_EXPECT_EQ(node_counts.external);
+  STATS_MATCHER_EXPECT_EQ(node_counts.concat);
+  STATS_MATCHER_EXPECT_EQ(node_counts.substring);
+  STATS_MATCHER_EXPECT_EQ(node_counts.ring);
+  STATS_MATCHER_EXPECT_EQ(node_counts.btree);
+  STATS_MATCHER_EXPECT_EQ(estimated_memory_usage);
+  STATS_MATCHER_EXPECT_EQ(estimated_fair_share_memory_usage);
+
+#undef STATS_MATCHER_EXPECT_EQ
+
+  return ok;
+}
+
+TEST(CordzInfoStatisticsTest, Flat) {
+  RefHelper ref;
+  auto* flat = ref.NeedsUnref(Flat(512));
+
+  CordzStatistics expected;
+  expected.size = flat->length;
+  expected.estimated_memory_usage = SizeOf(flat);
+  expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage;
+  expected.node_count = 1;
+  expected.node_counts.flat = 1;
+  expected.node_counts.flat_512 = 1;
+
+  EXPECT_THAT(SampleCord(flat), EqStatistics(expected));
+}
+
+TEST(CordzInfoStatisticsTest, SharedFlat) {
+  RefHelper ref;
+  auto* flat = ref.Ref(ref.NeedsUnref(Flat(64)));
+
+  CordzStatistics expected;
+  expected.size = flat->length;
+  expected.estimated_memory_usage = SizeOf(flat);
+  expected.estimated_fair_share_memory_usage = SizeOf(flat) / 2;
+  expected.node_count = 1;
+  expected.node_counts.flat = 1;
+  expected.node_counts.flat_64 = 1;
+
+  EXPECT_THAT(SampleCord(flat), EqStatistics(expected));
+}
+
+TEST(CordzInfoStatisticsTest, External) {
+  RefHelper ref;
+  auto* external = ref.NeedsUnref(External());
+
+  CordzStatistics expected;
+  expected.size = external->length;
+  expected.estimated_memory_usage = SizeOf(external);
+  expected.estimated_fair_share_memory_usage = SizeOf(external);
+  expected.node_count = 1;
+  expected.node_counts.external = 1;
+
+  EXPECT_THAT(SampleCord(external), EqStatistics(expected));
+}
+
+TEST(CordzInfoStatisticsTest, SharedExternal) {
+  RefHelper ref;
+  auto* external = ref.Ref(ref.NeedsUnref(External()));
+
+  CordzStatistics expected;
+  expected.size = external->length;
+  expected.estimated_memory_usage = SizeOf(external);
+  expected.estimated_fair_share_memory_usage = SizeOf(external) / 2;
+  expected.node_count = 1;
+  expected.node_counts.external = 1;
+
+  EXPECT_THAT(SampleCord(external), EqStatistics(expected));
+}
+
+TEST(CordzInfoStatisticsTest, Substring) {
+  RefHelper ref;
+  auto* flat = Flat(1024);
+  auto* substring = ref.NeedsUnref(Substring(flat));
+
+  CordzStatistics expected;
+  expected.size = substring->length;
+  expected.estimated_memory_usage = SizeOf(substring) + SizeOf(flat);
+  expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage;
+  expected.node_count = 2;
+  expected.node_counts.flat = 1;
+  expected.node_counts.flat_1k = 1;
+  expected.node_counts.substring = 1;
+
+  EXPECT_THAT(SampleCord(substring), EqStatistics(expected));
+}
+
+TEST(CordzInfoStatisticsTest, SharedSubstring) {
+  RefHelper ref;
+  auto* flat = ref.Ref(Flat(511), 2);
+  auto* substring = ref.Ref(ref.NeedsUnref(Substring(flat)));
+
+  CordzStatistics expected;
+  expected.size = substring->length;
+  expected.estimated_memory_usage = SizeOf(flat) + SizeOf(substring);
+  expected.estimated_fair_share_memory_usage =
+      SizeOf(substring) / 2 + SizeOf(flat) / 6;
+  expected.node_count = 2;
+  expected.node_counts.flat = 1;
+  expected.node_counts.flat_512 = 1;
+  expected.node_counts.substring = 1;
+
+  EXPECT_THAT(SampleCord(substring), EqStatistics(expected));
+}
+
+TEST(CordzInfoStatisticsTest, Concat) {
+  RefHelper ref;
+  auto* flat1 = Flat(300);
+  auto* flat2 = Flat(2000);
+  auto* concat = ref.NeedsUnref(Concat(flat1, flat2));
+
+  CordzStatistics expected;
+  expected.size = concat->length;
+  expected.estimated_memory_usage =
+      SizeOf(concat) + SizeOf(flat1) + SizeOf(flat2);
+  expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage;
+  expected.node_count = 3;
+  expected.node_counts.flat = 2;
+  expected.node_counts.flat_512 = 1;
+  expected.node_counts.concat = 1;
+
+  EXPECT_THAT(SampleCord(concat), EqStatistics(expected));
+}
+
+TEST(CordzInfoStatisticsTest, DeepConcat) {
+  RefHelper ref;
+  auto* flat1 = Flat(300);
+  auto* flat2 = Flat(2000);
+  auto* flat3 = Flat(400);
+  auto* external = External(3000);
+  auto* substring = Substring(external);
+  auto* concat1 = Concat(flat1, flat2);
+  auto* concat2 = Concat(flat3, substring);
+  auto* concat = ref.NeedsUnref(Concat(concat1, concat2));
+
+  CordzStatistics expected;
+  expected.size = concat->length;
+  expected.estimated_memory_usage = SizeOf(concat) * 3 + SizeOf(flat1) +
+                                    SizeOf(flat2) + SizeOf(flat3) +
+                                    SizeOf(external) + SizeOf(substring);
+  expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage;
+
+  expected.node_count = 8;
+  expected.node_counts.flat = 3;
+  expected.node_counts.flat_512 = 2;
+  expected.node_counts.external = 1;
+  expected.node_counts.concat = 3;
+  expected.node_counts.substring = 1;
+
+  EXPECT_THAT(SampleCord(concat), EqStatistics(expected));
+}
+
+TEST(CordzInfoStatisticsTest, DeepSharedConcat) {
+  RefHelper ref;
+  auto* flat1 = Flat(40);
+  auto* flat2 = ref.Ref(Flat(2000), 4);
+  auto* flat3 = Flat(70);
+  auto* external = ref.Ref(External(3000));
+  auto* substring = ref.Ref(Substring(external), 3);
+  auto* concat1 = Concat(flat1, flat2);
+  auto* concat2 = Concat(flat3, substring);
+  auto* concat = ref.Ref(ref.NeedsUnref(Concat(concat1, concat2)));
+
+  CordzStatistics expected;
+  expected.size = concat->length;
+  expected.estimated_memory_usage = SizeOf(concat) * 3 + SizeOf(flat1) +
+                                    SizeOf(flat2) + SizeOf(flat3) +
+                                    SizeOf(external) + SizeOf(substring);
+  expected.estimated_fair_share_memory_usage = FairShare(concat);
+  expected.node_count = 8;
+  expected.node_counts.flat = 3;
+  expected.node_counts.flat_64 = 1;
+  expected.node_counts.flat_128 = 1;
+  expected.node_counts.external = 1;
+  expected.node_counts.concat = 3;
+  expected.node_counts.substring = 1;
+
+  EXPECT_THAT(SampleCord(concat), EqStatistics(expected));
+}
+
+TEST(CordzInfoStatisticsTest, Ring) {
+  RefHelper ref;
+  auto* flat1 = Flat(240);
+  auto* flat2 = Flat(2000);
+  auto* flat3 = Flat(70);
+  auto* external = External(3000);
+  CordRepRing* ring = CordRepRing::Create(flat1);
+  ring = CordRepRing::Append(ring, flat2);
+  ring = CordRepRing::Append(ring, flat3);
+  ring = ref.NeedsUnref(CordRepRing::Append(ring, external));
+
+  CordzStatistics expected;
+  expected.size = ring->length;
+  expected.estimated_memory_usage = SizeOf(ring) + SizeOf(flat1) +
+                                    SizeOf(flat2) + SizeOf(flat3) +
+                                    SizeOf(external);
+  expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage;
+  expected.node_count = 5;
+  expected.node_counts.flat = 3;
+  expected.node_counts.flat_128 = 1;
+  expected.node_counts.flat_256 = 1;
+  expected.node_counts.external = 1;
+  expected.node_counts.ring = 1;
+
+  EXPECT_THAT(SampleCord(ring), EqStatistics(expected));
+}
+
+TEST(CordzInfoStatisticsTest, SharedSubstringRing) {
+  RefHelper ref;
+  auto* flat1 = ref.Ref(Flat(240));
+  auto* flat2 = Flat(200);
+  auto* flat3 = Flat(70);
+  auto* external = ref.Ref(External(3000), 5);
+  CordRepRing* ring = CordRepRing::Create(flat1);
+  ring = CordRepRing::Append(ring, flat2);
+  ring = CordRepRing::Append(ring, flat3);
+  ring = ref.Ref(CordRepRing::Append(ring, external), 4);
+  auto* substring = ref.Ref(ref.NeedsUnref(Substring(ring)));
+
+
+  CordzStatistics expected;
+  expected.size = substring->length;
+  expected.estimated_memory_usage = SizeOf(ring) + SizeOf(flat1) +
+                                    SizeOf(flat2) + SizeOf(flat3) +
+                                    SizeOf(external) + SizeOf(substring);
+  expected.estimated_fair_share_memory_usage = FairShare(substring);
+  expected.node_count = 6;
+  expected.node_counts.flat = 3;
+  expected.node_counts.flat_128 = 1;
+  expected.node_counts.flat_256 = 2;
+  expected.node_counts.external = 1;
+  expected.node_counts.ring = 1;
+  expected.node_counts.substring = 1;
+
+  EXPECT_THAT(SampleCord(substring), EqStatistics(expected));
+}
+
+TEST(CordzInfoStatisticsTest, BtreeLeaf) {
+  ASSERT_THAT(CordRepBtree::kMaxCapacity, Ge(3));
+  RefHelper ref;
+  auto* flat1 = Flat(2000);
+  auto* flat2 = Flat(200);
+  auto* substr = Substring(flat2);
+  auto* external = External(3000);
+
+  CordRepBtree* tree = CordRepBtree::Create(flat1);
+  tree = CordRepBtree::Append(tree, substr);
+  tree = CordRepBtree::Append(tree, external);
+  size_t flat3_count = CordRepBtree::kMaxCapacity - 3;
+  size_t flat3_size = 0;
+  for (size_t i = 0; i < flat3_count; ++i) {
+    auto* flat3 = Flat(70);
+    flat3_size += SizeOf(flat3);
+    tree = CordRepBtree::Append(tree, flat3);
+  }
+  ref.NeedsUnref(tree);
+
+  CordzStatistics expected;
+  expected.size = tree->length;
+  expected.estimated_memory_usage = SizeOf(tree) + SizeOf(flat1) +
+                                    SizeOf(flat2) + SizeOf(substr) +
+                                    flat3_size + SizeOf(external);
+  expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage;
+  expected.node_count = 1 + 3 + 1 + flat3_count;
+  expected.node_counts.flat = 2 + flat3_count;
+  expected.node_counts.flat_128 = flat3_count;
+  expected.node_counts.flat_256 = 1;
+  expected.node_counts.external = 1;
+  expected.node_counts.substring = 1;
+  expected.node_counts.btree = 1;
+
+  EXPECT_THAT(SampleCord(tree), EqStatistics(expected));
+}
+
+TEST(CordzInfoStatisticsTest, BtreeNodeShared) {
+  RefHelper ref;
+  static constexpr int leaf_count = 3;
+  const size_t flat3_count = CordRepBtree::kMaxCapacity - 3;
+  ASSERT_THAT(flat3_count, Ge(0));
+
+  CordRepBtree* tree = nullptr;
+  size_t mem_size = 0;
+  for (int i = 0; i < leaf_count; ++i) {
+    auto* flat1 = ref.Ref(Flat(2000), 9);
+    mem_size += SizeOf(flat1);
+    if (i == 0) {
+      tree = CordRepBtree::Create(flat1);
+    } else {
+      tree = CordRepBtree::Append(tree, flat1);
+    }
+
+    auto* flat2 = Flat(200);
+    auto* substr = Substring(flat2);
+    mem_size += SizeOf(flat2) + SizeOf(substr);
+    tree = CordRepBtree::Append(tree, substr);
+
+    auto* external = External(30);
+    mem_size += SizeOf(external);
+    tree = CordRepBtree::Append(tree, external);
+
+    for (size_t i = 0; i < flat3_count; ++i) {
+      auto* flat3 = Flat(70);
+      mem_size += SizeOf(flat3);
+      tree = CordRepBtree::Append(tree, flat3);
+    }
+
+    if (i == 0) {
+      mem_size += SizeOf(tree);
+    } else {
+      mem_size += SizeOf(tree->Edges().back()->btree());
+    }
+  }
+  ref.NeedsUnref(tree);
+
+  // Ref count: 2 for top (add 1), 5 for leaf 0 (add 4).
+  ref.Ref(tree, 1);
+  ref.Ref(tree->Edges().front(), 4);
+
+  CordzStatistics expected;
+  expected.size = tree->length;
+  expected.estimated_memory_usage = SizeOf(tree) + mem_size;
+  expected.estimated_fair_share_memory_usage = FairShare(tree);
+
+  expected.node_count = 1 + leaf_count * (1 + 3 + 1 + flat3_count);
+  expected.node_counts.flat = leaf_count * (2 + flat3_count);
+  expected.node_counts.flat_128 = leaf_count * flat3_count;
+  expected.node_counts.flat_256 = leaf_count;
+  expected.node_counts.external = leaf_count;
+  expected.node_counts.substring = leaf_count;
+  expected.node_counts.btree = 1 + leaf_count;
+
+  EXPECT_THAT(SampleCord(tree), EqStatistics(expected));
+}
+
+TEST(CordzInfoStatisticsTest, ThreadSafety) {
+  Notification stop;
+  static constexpr int kNumThreads = 8;
+  int64_t sampled_node_count = 0;
+
+  {
+    absl::synchronization_internal::ThreadPool pool(kNumThreads);
+
+    // Run analyzer thread emulating a CordzHandler collection.
+    pool.Schedule([&]() {
+      while (!stop.HasBeenNotified()) {
+        // Run every 10us (about 100K total collections).
+        absl::SleepFor(absl::Microseconds(10));
+        CordzSampleToken token;
+        for (const CordzInfo& cord_info : token) {
+          CordzStatistics stats = cord_info.GetCordzStatistics();
+          sampled_node_count += stats.node_count;
+        }
+      }
+    });
+
+    // Run 'application threads'
+    for (int i = 0; i < kNumThreads; ++i) {
+      pool.Schedule([&]() {
+        // Track 0 - 2 cordz infos at a time, providing permutations of 0, 1
+        // and 2 CordzHandle and CordzInfo queues being active, with plenty of
+        // 'empty to non empty' transitions.
+        InlineData cords[2];
+        std::minstd_rand gen;
+        std::uniform_int_distribution<int> coin_toss(0, 1);
+
+        while (!stop.HasBeenNotified()) {
+          for (InlineData& cord : cords) {
+            // 50/50 flip the state of the cord
+            if (coin_toss(gen) != 0) {
+              if (cord.is_tree()) {
+                // 50/50 simulate delete (untrack) or 'edit to empty'
+                if (coin_toss(gen) != 0) {
+                  CordzInfo::MaybeUntrackCord(cord.cordz_info());
+                } else {
+                  CordzUpdateScope scope(cord.cordz_info(),
+                                         CordzUpdateTracker::kUnknown);
+                  scope.SetCordRep(nullptr);
+                }
+                CordRep::Unref(cord.as_tree());
+                cord.set_inline_size(0);
+              } else {
+                // Coin toss to 25% ring, 25% btree, and 50% flat.
+                CordRep* rep = Flat(256);
+                if (coin_toss(gen) != 0) {
+                  if (coin_toss(gen) != 0) {
+                    rep = CordRepRing::Create(rep);
+                  } else {
+                    rep = CordRepBtree::Create(rep);
+                  }
+                }
+                cord.make_tree(rep);
+
+                // 50/50 sample
+                if (coin_toss(gen) != 0) {
+                  CordzInfo::TrackCord(cord, CordzUpdateTracker::kUnknown);
+                }
+              }
+            }
+          }
+        }
+        for (InlineData& cord : cords) {
+          if (cord.is_tree()) {
+            CordzInfo::MaybeUntrackCord(cord.cordz_info());
+            CordRep::Unref(cord.as_tree());
+          }
+        }
+      });
+    }
+
+    // Run for 1 second to give memory and thread safety analyzers plenty of
+    // time to detect any mishaps or undefined behaviors.
+    absl::SleepFor(absl::Seconds(1));
+    stop.Notify();
+  }
+
+  std::cout << "Sampled " << sampled_node_count << " nodes\n";
+}
+
+}  // namespace
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cordz_info_test.cc b/absl/strings/internal/cordz_info_test.cc
new file mode 100644
index 0000000..b98343a
--- /dev/null
+++ b/absl/strings/internal/cordz_info_test.cc
@@ -0,0 +1,341 @@
+// Copyright 2019 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/cordz_info.h"
+
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/debugging/stacktrace.h"
+#include "absl/debugging/symbolize.h"
+#include "absl/strings/cordz_test_helpers.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+#include "absl/strings/internal/cordz_handle.h"
+#include "absl/strings/internal/cordz_statistics.h"
+#include "absl/strings/internal/cordz_update_tracker.h"
+#include "absl/strings/str_cat.h"
+#include "absl/types/span.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::Ne;
+using ::testing::SizeIs;
+
+// Used test values
+auto constexpr kUnknownMethod = CordzUpdateTracker::kUnknown;
+auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString;
+auto constexpr kChildMethod = CordzUpdateTracker::kConstructorCord;
+auto constexpr kUpdateMethod = CordzUpdateTracker::kAppendString;
+
+// Local less verbose helper
+std::vector<const CordzHandle*> DeleteQueue() {
+  return CordzHandle::DiagnosticsGetDeleteQueue();
+}
+
+std::string FormatStack(absl::Span<void* const> raw_stack) {
+  static constexpr size_t buf_size = 1 << 14;
+  std::unique_ptr<char[]> buf(new char[buf_size]);
+  std::string output;
+  for (void* stackp : raw_stack) {
+    if (absl::Symbolize(stackp, buf.get(), buf_size)) {
+      absl::StrAppend(&output, "    ", buf.get(), "\n");
+    }
+  }
+  return output;
+}
+
+TEST(CordzInfoTest, TrackCord) {
+  TestCordData data;
+  CordzInfo::TrackCord(data.data, kTrackCordMethod);
+  CordzInfo* info = data.data.cordz_info();
+  ASSERT_THAT(info, Ne(nullptr));
+  EXPECT_FALSE(info->is_snapshot());
+  EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(info));
+  EXPECT_THAT(info->GetCordRepForTesting(), Eq(data.rep.rep));
+  info->Untrack();
+}
+
+TEST(CordzInfoTest, MaybeTrackChildCordWithoutSampling) {
+  CordzSamplingIntervalHelper sample_none(99999);
+  TestCordData parent, child;
+  CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod);
+  EXPECT_THAT(child.data.cordz_info(), Eq(nullptr));
+}
+
+TEST(CordzInfoTest, MaybeTrackChildCordWithSampling) {
+  CordzSamplingIntervalHelper sample_all(1);
+  TestCordData parent, child;
+  CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod);
+  EXPECT_THAT(child.data.cordz_info(), Eq(nullptr));
+}
+
+TEST(CordzInfoTest, MaybeTrackChildCordWithoutSamplingParentSampled) {
+  CordzSamplingIntervalHelper sample_none(99999);
+  TestCordData parent, child;
+  CordzInfo::TrackCord(parent.data, kTrackCordMethod);
+  CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod);
+  CordzInfo* parent_info = parent.data.cordz_info();
+  CordzInfo* child_info = child.data.cordz_info();
+  ASSERT_THAT(child_info, Ne(nullptr));
+  EXPECT_THAT(child_info->GetCordRepForTesting(), Eq(child.rep.rep));
+  EXPECT_THAT(child_info->GetParentStack(), parent_info->GetStack());
+  parent_info->Untrack();
+  child_info->Untrack();
+}
+
+TEST(CordzInfoTest, MaybeTrackChildCordWithoutSamplingChildSampled) {
+  CordzSamplingIntervalHelper sample_none(99999);
+  TestCordData parent, child;
+  CordzInfo::TrackCord(child.data, kTrackCordMethod);
+  CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod);
+  EXPECT_THAT(child.data.cordz_info(), Eq(nullptr));
+}
+
+TEST(CordzInfoTest, MaybeTrackChildCordWithSamplingChildSampled) {
+  CordzSamplingIntervalHelper sample_all(1);
+  TestCordData parent, child;
+  CordzInfo::TrackCord(child.data, kTrackCordMethod);
+  CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod);
+  EXPECT_THAT(child.data.cordz_info(), Eq(nullptr));
+}
+
+TEST(CordzInfoTest, UntrackCord) {
+  TestCordData data;
+  CordzInfo::TrackCord(data.data, kTrackCordMethod);
+  CordzInfo* info = data.data.cordz_info();
+
+  info->Untrack();
+  EXPECT_THAT(DeleteQueue(), SizeIs(0));
+}
+
+TEST(CordzInfoTest, UntrackCordWithSnapshot) {
+  TestCordData data;
+  CordzInfo::TrackCord(data.data, kTrackCordMethod);
+  CordzInfo* info = data.data.cordz_info();
+
+  CordzSnapshot snapshot;
+  info->Untrack();
+  EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(nullptr));
+  EXPECT_THAT(info->GetCordRepForTesting(), Eq(data.rep.rep));
+  EXPECT_THAT(DeleteQueue(), ElementsAre(info, &snapshot));
+}
+
+TEST(CordzInfoTest, SetCordRep) {
+  TestCordData data;
+  CordzInfo::TrackCord(data.data, kTrackCordMethod);
+  CordzInfo* info = data.data.cordz_info();
+
+  TestCordRep rep;
+  info->Lock(CordzUpdateTracker::kAppendCord);
+  info->SetCordRep(rep.rep);
+  info->Unlock();
+  EXPECT_THAT(info->GetCordRepForTesting(), Eq(rep.rep));
+
+  info->Untrack();
+}
+
+TEST(CordzInfoTest, SetCordRepNullUntracksCordOnUnlock) {
+  TestCordData data;
+  CordzInfo::TrackCord(data.data, kTrackCordMethod);
+  CordzInfo* info = data.data.cordz_info();
+
+  info->Lock(CordzUpdateTracker::kAppendString);
+  info->SetCordRep(nullptr);
+  EXPECT_THAT(info->GetCordRepForTesting(), Eq(nullptr));
+  EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(info));
+
+  info->Unlock();
+  EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(nullptr));
+}
+
+TEST(CordzInfoTest, RefCordRep) {
+  TestCordData data;
+  CordzInfo::TrackCord(data.data, kTrackCordMethod);
+  CordzInfo* info = data.data.cordz_info();
+
+  size_t refcount = data.rep.rep->refcount.Get();
+  EXPECT_THAT(info->RefCordRep(), Eq(data.rep.rep));
+  EXPECT_THAT(data.rep.rep->refcount.Get(), Eq(refcount + 1));
+  CordRep::Unref(data.rep.rep);
+  info->Untrack();
+}
+
+#if GTEST_HAS_DEATH_TEST
+
+TEST(CordzInfoTest, SetCordRepRequiresMutex) {
+  TestCordData data;
+  CordzInfo::TrackCord(data.data, kTrackCordMethod);
+  CordzInfo* info = data.data.cordz_info();
+  TestCordRep rep;
+  EXPECT_DEBUG_DEATH(info->SetCordRep(rep.rep), ".*");
+  info->Untrack();
+}
+
+#endif  // GTEST_HAS_DEATH_TEST
+
+TEST(CordzInfoTest, TrackUntrackHeadFirstV2) {
+  CordzSnapshot snapshot;
+  EXPECT_THAT(CordzInfo::Head(snapshot), Eq(nullptr));
+
+  TestCordData data;
+  CordzInfo::TrackCord(data.data, kTrackCordMethod);
+  CordzInfo* info1 = data.data.cordz_info();
+  ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1));
+  EXPECT_THAT(info1->Next(snapshot), Eq(nullptr));
+
+  TestCordData data2;
+  CordzInfo::TrackCord(data2.data, kTrackCordMethod);
+  CordzInfo* info2 = data2.data.cordz_info();
+  ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2));
+  EXPECT_THAT(info2->Next(snapshot), Eq(info1));
+  EXPECT_THAT(info1->Next(snapshot), Eq(nullptr));
+
+  info2->Untrack();
+  ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1));
+  EXPECT_THAT(info1->Next(snapshot), Eq(nullptr));
+
+  info1->Untrack();
+  ASSERT_THAT(CordzInfo::Head(snapshot), Eq(nullptr));
+}
+
+TEST(CordzInfoTest, TrackUntrackTailFirstV2) {
+  CordzSnapshot snapshot;
+  EXPECT_THAT(CordzInfo::Head(snapshot), Eq(nullptr));
+
+  TestCordData data;
+  CordzInfo::TrackCord(data.data, kTrackCordMethod);
+  CordzInfo* info1 = data.data.cordz_info();
+  ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1));
+  EXPECT_THAT(info1->Next(snapshot), Eq(nullptr));
+
+  TestCordData data2;
+  CordzInfo::TrackCord(data2.data, kTrackCordMethod);
+  CordzInfo* info2 = data2.data.cordz_info();
+  ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2));
+  EXPECT_THAT(info2->Next(snapshot), Eq(info1));
+  EXPECT_THAT(info1->Next(snapshot), Eq(nullptr));
+
+  info1->Untrack();
+  ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2));
+  EXPECT_THAT(info2->Next(snapshot), Eq(nullptr));
+
+  info2->Untrack();
+  ASSERT_THAT(CordzInfo::Head(snapshot), Eq(nullptr));
+}
+
+TEST(CordzInfoTest, StackV2) {
+  TestCordData data;
+  // kMaxStackDepth is intentionally less than 64 (which is the max depth that
+  // Cordz will record) because if the actual stack depth is over 64
+  // (which it is on Apple platforms) then the expected_stack will end up
+  // catching a few frames at the end that the actual_stack didn't get and
+  // it will no longer be subset. At the time of this writing 58 is the max
+  // that will allow this test to pass (with a minimum os version of iOS 9), so
+  // rounded down to 50 to hopefully not run into this in the future if Apple
+  // makes small modifications to its testing stack. 50 is sufficient to prove
+  // that we got a decent stack.
+  static constexpr int kMaxStackDepth = 50;
+  CordzInfo::TrackCord(data.data, kTrackCordMethod);
+  CordzInfo* info = data.data.cordz_info();
+  std::vector<void*> local_stack;
+  local_stack.resize(kMaxStackDepth);
+  // In some environments we don't get stack traces. For example in Android
+  // absl::GetStackTrace will return 0 indicating it didn't find any stack. The
+  // resultant formatted stack will be "", but that still equals the stack
+  // recorded in CordzInfo, which is also empty. The skip_count is 1 so that the
+  // line number of the current stack isn't included in the HasSubstr check.
+  local_stack.resize(absl::GetStackTrace(local_stack.data(), kMaxStackDepth,
+                                         /*skip_count=*/1));
+
+  std::string got_stack = FormatStack(info->GetStack());
+  std::string expected_stack = FormatStack(local_stack);
+  // If TrackCord is inlined, got_stack should match expected_stack. If it isn't
+  // inlined, got_stack should include an additional frame not present in
+  // expected_stack. Either way, expected_stack should be a substring of
+  // got_stack.
+  EXPECT_THAT(got_stack, HasSubstr(expected_stack));
+
+  info->Untrack();
+}
+
+// Local helper functions to get different stacks for child and parent.
+CordzInfo* TrackChildCord(InlineData& data, const InlineData& parent) {
+  CordzInfo::TrackCord(data, parent, kChildMethod);
+  return data.cordz_info();
+}
+CordzInfo* TrackParentCord(InlineData& data) {
+  CordzInfo::TrackCord(data, kTrackCordMethod);
+  return data.cordz_info();
+}
+
+TEST(CordzInfoTest, GetStatistics) {
+  TestCordData data;
+  CordzInfo* info = TrackParentCord(data.data);
+
+  CordzStatistics statistics = info->GetCordzStatistics();
+  EXPECT_THAT(statistics.size, Eq(data.rep.rep->length));
+  EXPECT_THAT(statistics.method, Eq(kTrackCordMethod));
+  EXPECT_THAT(statistics.parent_method, Eq(kUnknownMethod));
+  EXPECT_THAT(statistics.update_tracker.Value(kTrackCordMethod), Eq(1));
+
+  info->Untrack();
+}
+
+TEST(CordzInfoTest, LockCountsMethod) {
+  TestCordData data;
+  CordzInfo* info = TrackParentCord(data.data);
+
+  info->Lock(kUpdateMethod);
+  info->Unlock();
+  info->Lock(kUpdateMethod);
+  info->Unlock();
+
+  CordzStatistics statistics = info->GetCordzStatistics();
+  EXPECT_THAT(statistics.update_tracker.Value(kUpdateMethod), Eq(2));
+
+  info->Untrack();
+}
+
+TEST(CordzInfoTest, FromParent) {
+  TestCordData parent;
+  TestCordData child;
+  CordzInfo* info_parent = TrackParentCord(parent.data);
+  CordzInfo* info_child = TrackChildCord(child.data, parent.data);
+
+  std::string stack = FormatStack(info_parent->GetStack());
+  std::string parent_stack = FormatStack(info_child->GetParentStack());
+  EXPECT_THAT(stack, Eq(parent_stack));
+
+  CordzStatistics statistics = info_child->GetCordzStatistics();
+  EXPECT_THAT(statistics.size, Eq(child.rep.rep->length));
+  EXPECT_THAT(statistics.method, Eq(kChildMethod));
+  EXPECT_THAT(statistics.parent_method, Eq(kTrackCordMethod));
+  EXPECT_THAT(statistics.update_tracker.Value(kChildMethod), Eq(1));
+
+  info_parent->Untrack();
+  info_child->Untrack();
+}
+
+}  // namespace
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cordz_sample_token.cc b/absl/strings/internal/cordz_sample_token.cc
new file mode 100644
index 0000000..ba1270d
--- /dev/null
+++ b/absl/strings/internal/cordz_sample_token.cc
@@ -0,0 +1,64 @@
+// Copyright 2019 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/cordz_sample_token.h"
+
+#include "absl/base/config.h"
+#include "absl/strings/internal/cordz_handle.h"
+#include "absl/strings/internal/cordz_info.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+CordzSampleToken::Iterator& CordzSampleToken::Iterator::operator++() {
+  if (current_) {
+    current_ = current_->Next(*token_);
+  }
+  return *this;
+}
+
+CordzSampleToken::Iterator CordzSampleToken::Iterator::operator++(int) {
+  Iterator it(*this);
+  operator++();
+  return it;
+}
+
+bool operator==(const CordzSampleToken::Iterator& lhs,
+                const CordzSampleToken::Iterator& rhs) {
+  return lhs.current_ == rhs.current_ &&
+         (lhs.current_ == nullptr || lhs.token_ == rhs.token_);
+}
+
+bool operator!=(const CordzSampleToken::Iterator& lhs,
+                const CordzSampleToken::Iterator& rhs) {
+  return !(lhs == rhs);
+}
+
+CordzSampleToken::Iterator::reference CordzSampleToken::Iterator::operator*()
+    const {
+  return *current_;
+}
+
+CordzSampleToken::Iterator::pointer CordzSampleToken::Iterator::operator->()
+    const {
+  return current_;
+}
+
+CordzSampleToken::Iterator::Iterator(const CordzSampleToken* token)
+    : token_(token), current_(CordzInfo::Head(*token)) {}
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cordz_sample_token.h b/absl/strings/internal/cordz_sample_token.h
new file mode 100644
index 0000000..28a1d70
--- /dev/null
+++ b/absl/strings/internal/cordz_sample_token.h
@@ -0,0 +1,97 @@
+// Copyright 2019 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/base/config.h"
+#include "absl/strings/internal/cordz_handle.h"
+#include "absl/strings/internal/cordz_info.h"
+
+#ifndef ABSL_STRINGS_CORDZ_SAMPLE_TOKEN_H_
+#define ABSL_STRINGS_CORDZ_SAMPLE_TOKEN_H_
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// The existence of a CordzSampleToken guarantees that a reader can traverse the
+// global_cordz_infos_head linked-list without needing to hold a mutex. When a
+// CordzSampleToken exists, all CordzInfo objects that would be destroyed are
+// instead appended to a deletion queue. When the CordzSampleToken is destroyed,
+// it will also clean up any of these CordzInfo objects.
+//
+// E.g., ST are CordzSampleToken objects and CH are CordzHandle objects.
+//   ST1 <- CH1 <- CH2 <- ST2 <- CH3 <- global_delete_queue_tail
+//
+// This list tracks that CH1 and CH2 were created after ST1, so the thread
+// holding ST1 might have a referece to CH1, CH2, ST2, and CH3. However, ST2 was
+// created later, so the thread holding the ST2 token cannot have a reference to
+// ST1, CH1, or CH2. If ST1 is cleaned up first, that thread will delete ST1,
+// CH1, and CH2. If instead ST2 is cleaned up first, that thread will only
+// delete ST2.
+//
+// If ST1 is cleaned up first, the new list will be:
+//   ST2 <- CH3 <- global_delete_queue_tail
+//
+// If ST2 is cleaned up first, the new list will be:
+//   ST1 <- CH1 <- CH2 <- CH3 <- global_delete_queue_tail
+//
+// All new CordzHandle objects are appended to the list, so if a new thread
+// comes along before either ST1 or ST2 are cleaned up, the new list will be:
+//   ST1 <- CH1 <- CH2 <- ST2 <- CH3 <- ST3 <- global_delete_queue_tail
+//
+// A thread must hold the global_delete_queue_mu mutex whenever it's altering
+// this list.
+//
+// It is safe for thread that holds a CordzSampleToken to read
+// global_cordz_infos at any time since the objects it is able to retrieve will
+// not be deleted while the CordzSampleToken exists.
+class CordzSampleToken : public CordzSnapshot {
+ public:
+  class Iterator {
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = const CordzInfo&;
+    using difference_type = ptrdiff_t;
+    using pointer = const CordzInfo*;
+    using reference = value_type;
+
+    Iterator() = default;
+
+    Iterator& operator++();
+    Iterator operator++(int);
+    friend bool operator==(const Iterator& lhs, const Iterator& rhs);
+    friend bool operator!=(const Iterator& lhs, const Iterator& rhs);
+    reference operator*() const;
+    pointer operator->() const;
+
+   private:
+    friend class CordzSampleToken;
+    explicit Iterator(const CordzSampleToken* token);
+
+    const CordzSampleToken* token_ = nullptr;
+    pointer current_ = nullptr;
+  };
+
+  CordzSampleToken() = default;
+  CordzSampleToken(const CordzSampleToken&) = delete;
+  CordzSampleToken& operator=(const CordzSampleToken&) = delete;
+
+  Iterator begin() { return Iterator(this); }
+  Iterator end() { return Iterator(); }
+};
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_CORDZ_SAMPLE_TOKEN_H_
diff --git a/absl/strings/internal/cordz_sample_token_test.cc b/absl/strings/internal/cordz_sample_token_test.cc
new file mode 100644
index 0000000..9f54301
--- /dev/null
+++ b/absl/strings/internal/cordz_sample_token_test.cc
@@ -0,0 +1,208 @@
+// Copyright 2019 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/cordz_sample_token.h"
+
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/memory/memory.h"
+#include "absl/random/random.h"
+#include "absl/strings/cordz_test_helpers.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+#include "absl/strings/internal/cordz_handle.h"
+#include "absl/strings/internal/cordz_info.h"
+#include "absl/synchronization/internal/thread_pool.h"
+#include "absl/synchronization/notification.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::Ne;
+
+// Used test values
+auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString;
+
+TEST(CordzSampleTokenTest, IteratorTraits) {
+  static_assert(std::is_copy_constructible<CordzSampleToken::Iterator>::value,
+                "");
+  static_assert(std::is_copy_assignable<CordzSampleToken::Iterator>::value, "");
+  static_assert(std::is_move_constructible<CordzSampleToken::Iterator>::value,
+                "");
+  static_assert(std::is_move_assignable<CordzSampleToken::Iterator>::value, "");
+  static_assert(
+      std::is_same<
+          std::iterator_traits<CordzSampleToken::Iterator>::iterator_category,
+          std::input_iterator_tag>::value,
+      "");
+  static_assert(
+      std::is_same<std::iterator_traits<CordzSampleToken::Iterator>::value_type,
+                   const CordzInfo&>::value,
+      "");
+  static_assert(
+      std::is_same<
+          std::iterator_traits<CordzSampleToken::Iterator>::difference_type,
+          ptrdiff_t>::value,
+      "");
+  static_assert(
+      std::is_same<std::iterator_traits<CordzSampleToken::Iterator>::pointer,
+                   const CordzInfo*>::value,
+      "");
+  static_assert(
+      std::is_same<std::iterator_traits<CordzSampleToken::Iterator>::reference,
+                   const CordzInfo&>::value,
+      "");
+}
+
+TEST(CordzSampleTokenTest, IteratorEmpty) {
+  CordzSampleToken token;
+  EXPECT_THAT(token.begin(), Eq(token.end()));
+}
+
+TEST(CordzSampleTokenTest, Iterator) {
+  TestCordData cord1, cord2, cord3;
+  CordzInfo::TrackCord(cord1.data, kTrackCordMethod);
+  CordzInfo* info1 = cord1.data.cordz_info();
+  CordzInfo::TrackCord(cord2.data, kTrackCordMethod);
+  CordzInfo* info2 = cord2.data.cordz_info();
+  CordzInfo::TrackCord(cord3.data, kTrackCordMethod);
+  CordzInfo* info3 = cord3.data.cordz_info();
+
+  CordzSampleToken token;
+  std::vector<const CordzInfo*> found;
+  for (const CordzInfo& cord_info : token) {
+    found.push_back(&cord_info);
+  }
+
+  EXPECT_THAT(found, ElementsAre(info3, info2, info1));
+
+  info1->Untrack();
+  info2->Untrack();
+  info3->Untrack();
+}
+
+TEST(CordzSampleTokenTest, IteratorEquality) {
+  TestCordData cord1;
+  TestCordData cord2;
+  TestCordData cord3;
+  CordzInfo::TrackCord(cord1.data, kTrackCordMethod);
+  CordzInfo* info1 = cord1.data.cordz_info();
+
+  CordzSampleToken token1;
+  // lhs starts with the CordzInfo corresponding to cord1 at the head.
+  CordzSampleToken::Iterator lhs = token1.begin();
+
+  CordzInfo::TrackCord(cord2.data, kTrackCordMethod);
+  CordzInfo* info2 = cord2.data.cordz_info();
+
+  CordzSampleToken token2;
+  // rhs starts with the CordzInfo corresponding to cord2 at the head.
+  CordzSampleToken::Iterator rhs = token2.begin();
+
+  CordzInfo::TrackCord(cord3.data, kTrackCordMethod);
+  CordzInfo* info3 = cord3.data.cordz_info();
+
+  // lhs is on cord1 while rhs is on cord2.
+  EXPECT_THAT(lhs, Ne(rhs));
+
+  rhs++;
+  // lhs and rhs are both on cord1, but they didn't come from the same
+  // CordzSampleToken.
+  EXPECT_THAT(lhs, Ne(rhs));
+
+  lhs++;
+  rhs++;
+  // Both lhs and rhs are done, so they are on nullptr.
+  EXPECT_THAT(lhs, Eq(rhs));
+
+  info1->Untrack();
+  info2->Untrack();
+  info3->Untrack();
+}
+
+TEST(CordzSampleTokenTest, MultiThreaded) {
+  Notification stop;
+  static constexpr int kNumThreads = 4;
+  static constexpr int kNumCords = 3;
+  static constexpr int kNumTokens = 3;
+  absl::synchronization_internal::ThreadPool pool(kNumThreads);
+
+  for (int i = 0; i < kNumThreads; ++i) {
+    pool.Schedule([&stop]() {
+      absl::BitGen gen;
+      TestCordData cords[kNumCords];
+      std::unique_ptr<CordzSampleToken> tokens[kNumTokens];
+
+      while (!stop.HasBeenNotified()) {
+        // Randomly perform one of five actions:
+        //   1) Untrack
+        //   2) Track
+        //   3) Iterate over Cords visible to a token.
+        //   4) Unsample
+        //   5) Sample
+        int index = absl::Uniform(gen, 0, kNumCords);
+        if (absl::Bernoulli(gen, 0.5)) {
+          TestCordData& cord = cords[index];
+          // Track/untrack.
+          if (cord.data.is_profiled()) {
+            // 1) Untrack
+            cord.data.cordz_info()->Untrack();
+            cord.data.clear_cordz_info();;
+          } else {
+            // 2) Track
+            CordzInfo::TrackCord(cord.data, kTrackCordMethod);
+          }
+        } else {
+          std::unique_ptr<CordzSampleToken>& token = tokens[index];
+          if (token) {
+            if (absl::Bernoulli(gen, 0.5)) {
+              // 3) Iterate over Cords visible to a token.
+              for (const CordzInfo& info : *token) {
+                // This is trivial work to allow us to compile the loop.
+                EXPECT_THAT(info.Next(*token), Ne(&info));
+              }
+            } else {
+              // 4) Unsample
+              token = nullptr;
+            }
+          } else {
+            // 5) Sample
+            token = absl::make_unique<CordzSampleToken>();
+          }
+        }
+      }
+      for (TestCordData& cord : cords) {
+        CordzInfo::MaybeUntrackCord(cord.data.cordz_info());
+      }
+    });
+  }
+  // The threads will hammer away.  Give it a little bit of time for tsan to
+  // spot errors.
+  absl::SleepFor(absl::Seconds(3));
+  stop.Notify();
+}
+
+}  // namespace
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/cordz_statistics.h b/absl/strings/internal/cordz_statistics.h
new file mode 100644
index 0000000..da4c7db
--- /dev/null
+++ b/absl/strings/internal/cordz_statistics.h
@@ -0,0 +1,87 @@
+// Copyright 2019 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_CORDZ_STATISTICS_H_
+#define ABSL_STRINGS_INTERNAL_CORDZ_STATISTICS_H_
+
+#include <cstdint>
+
+#include "absl/base/config.h"
+#include "absl/strings/internal/cordz_update_tracker.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// CordzStatistics captures some meta information about a Cord's shape.
+struct CordzStatistics {
+  using MethodIdentifier = CordzUpdateTracker::MethodIdentifier;
+
+  // Node counts information
+  struct NodeCounts {
+    size_t flat = 0;       // #flats
+    size_t flat_64 = 0;    // #flats up to 64 bytes
+    size_t flat_128 = 0;   // #flats up to 128 bytes
+    size_t flat_256 = 0;   // #flats up to 256 bytes
+    size_t flat_512 = 0;   // #flats up to 512 bytes
+    size_t flat_1k = 0;    // #flats up to 1K bytes
+    size_t external = 0;   // #external reps
+    size_t substring = 0;  // #substring reps
+    size_t concat = 0;     // #concat reps
+    size_t ring = 0;       // #ring buffer reps
+    size_t btree = 0;      // #btree reps
+  };
+
+  // The size of the cord in bytes. This matches the result of Cord::size().
+  int64_t size = 0;
+
+  // The estimated memory used by the sampled cord. This value matches the
+  // value as reported by Cord::EstimatedMemoryUsage().
+  // A value of 0 implies the property has not been recorded.
+  int64_t estimated_memory_usage = 0;
+
+  // The effective memory used by the sampled cord, inversely weighted by the
+  // effective indegree of each allocated node. This is a representation of the
+  // fair share of memory usage that should be attributed to the sampled cord.
+  // This value is more useful for cases where one or more nodes are referenced
+  // by multiple Cord instances, and for cases where a Cord includes the same
+  // node multiple times (either directly or indirectly).
+  // A value of 0 implies the property has not been recorded.
+  int64_t estimated_fair_share_memory_usage = 0;
+
+  // The total number of nodes referenced by this cord.
+  // For ring buffer Cords, this includes the 'ring buffer' node.
+  // For btree Cords, this includes all 'CordRepBtree' tree nodes as well as all
+  // the substring, flat and external nodes referenced by the tree.
+  // A value of 0 implies the property has not been recorded.
+  int64_t node_count = 0;
+
+  // Detailed node counts per type
+  NodeCounts node_counts;
+
+  // The cord method responsible for sampling the cord.
+  MethodIdentifier method = MethodIdentifier::kUnknown;
+
+  // The cord method responsible for sampling the parent cord if applicable.
+  MethodIdentifier parent_method = MethodIdentifier::kUnknown;
+
+  // Update tracker tracking invocation count per cord method.
+  CordzUpdateTracker update_tracker;
+};
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_CORDZ_STATISTICS_H_
diff --git a/absl/strings/internal/cordz_update_scope.h b/absl/strings/internal/cordz_update_scope.h
new file mode 100644
index 0000000..57ba75d
--- /dev/null
+++ b/absl/strings/internal/cordz_update_scope.h
@@ -0,0 +1,71 @@
+// Copyright 2021 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_CORDZ_UPDATE_SCOPE_H_
+#define ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_SCOPE_H_
+
+#include "absl/base/config.h"
+#include "absl/base/optimization.h"
+#include "absl/base/thread_annotations.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cordz_info.h"
+#include "absl/strings/internal/cordz_update_tracker.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// CordzUpdateScope scopes an update to the provided CordzInfo.
+// The class invokes `info->Lock(method)` and `info->Unlock()` to guard
+// cordrep updates. This class does nothing if `info` is null.
+// See also the 'Lock`, `Unlock` and `SetCordRep` methods in `CordzInfo`.
+class ABSL_SCOPED_LOCKABLE CordzUpdateScope {
+ public:
+  CordzUpdateScope(CordzInfo* info, CordzUpdateTracker::MethodIdentifier method)
+      ABSL_EXCLUSIVE_LOCK_FUNCTION(info)
+      : info_(info) {
+    if (ABSL_PREDICT_FALSE(info_)) {
+      info->Lock(method);
+    }
+  }
+
+  // CordzUpdateScope can not be copied or assigned to.
+  CordzUpdateScope(CordzUpdateScope&& rhs) = delete;
+  CordzUpdateScope(const CordzUpdateScope&) = delete;
+  CordzUpdateScope& operator=(CordzUpdateScope&& rhs) = delete;
+  CordzUpdateScope& operator=(const CordzUpdateScope&) = delete;
+
+  ~CordzUpdateScope() ABSL_UNLOCK_FUNCTION() {
+    if (ABSL_PREDICT_FALSE(info_)) {
+      info_->Unlock();
+    }
+  }
+
+  void SetCordRep(CordRep* rep) const {
+    if (ABSL_PREDICT_FALSE(info_)) {
+      info_->SetCordRep(rep);
+    }
+  }
+
+  CordzInfo* info() const { return info_; }
+
+ private:
+  CordzInfo* info_;
+};
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_SCOPE_H_
diff --git a/absl/strings/internal/cordz_update_scope_test.cc b/absl/strings/internal/cordz_update_scope_test.cc
new file mode 100644
index 0000000..3d08c62
--- /dev/null
+++ b/absl/strings/internal/cordz_update_scope_test.cc
@@ -0,0 +1,49 @@
+// Copyright 2021 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/cordz_update_scope.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/strings/cordz_test_helpers.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+#include "absl/strings/internal/cordz_info.h"
+#include "absl/strings/internal/cordz_update_tracker.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+namespace {
+
+// Used test values
+auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString;
+
+TEST(CordzUpdateScopeTest, ScopeNullptr) {
+  CordzUpdateScope scope(nullptr, kTrackCordMethod);
+}
+
+TEST(CordzUpdateScopeTest, ScopeSampledCord) {
+  TestCordData cord;
+  CordzInfo::TrackCord(cord.data, kTrackCordMethod);
+  CordzUpdateScope scope(cord.data.cordz_info(), kTrackCordMethod);
+  cord.data.cordz_info()->SetCordRep(nullptr);
+}
+
+}  // namespace
+ABSL_NAMESPACE_END
+}  // namespace cord_internal
+
+}  // namespace absl
diff --git a/absl/strings/internal/cordz_update_tracker.h b/absl/strings/internal/cordz_update_tracker.h
new file mode 100644
index 0000000..1f76448
--- /dev/null
+++ b/absl/strings/internal/cordz_update_tracker.h
@@ -0,0 +1,121 @@
+// Copyright 2021 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_CORDZ_UPDATE_TRACKER_H_
+#define ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_TRACKER_H_
+
+#include <atomic>
+#include <cstdint>
+
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// CordzUpdateTracker tracks counters for Cord update methods.
+//
+// The purpose of CordzUpdateTracker is to track the number of calls to methods
+// updating Cord data for sampled cords. The class internally uses 'lossy'
+// atomic operations: Cord is thread-compatible, so there is no need to
+// synchronize updates. However, Cordz collection threads may call 'Value()' at
+// any point, so the class needs to provide thread safe access.
+//
+// This class is thread-safe. But as per above comments, all non-const methods
+// should be used single-threaded only: updates are thread-safe but lossy.
+class CordzUpdateTracker {
+ public:
+  // Tracked update methods.
+  enum MethodIdentifier {
+    kUnknown,
+    kAppendBuffer,
+    kAppendCord,
+    kAppendExternalMemory,
+    kAppendString,
+    kAssignCord,
+    kAssignString,
+    kClear,
+    kConstructorCord,
+    kConstructorString,
+    kCordReader,
+    kFlatten,
+    kGetAppendRegion,
+    kMakeCordFromExternal,
+    kMoveAppendCord,
+    kMoveAssignCord,
+    kMovePrependCord,
+    kPrependBuffer,
+    kPrependCord,
+    kPrependString,
+    kRemovePrefix,
+    kRemoveSuffix,
+    kSubCord,
+
+    // kNumMethods defines the number of entries: must be the last entry.
+    kNumMethods,
+  };
+
+  // Constructs a new instance. All counters are zero-initialized.
+  constexpr CordzUpdateTracker() noexcept : values_{} {}
+
+  // Copy constructs a new instance.
+  CordzUpdateTracker(const CordzUpdateTracker& rhs) noexcept { *this = rhs; }
+
+  // Assigns the provided value to this instance.
+  CordzUpdateTracker& operator=(const CordzUpdateTracker& rhs) noexcept {
+    for (int i = 0; i < kNumMethods; ++i) {
+      values_[i].store(rhs.values_[i].load(std::memory_order_relaxed),
+                       std::memory_order_relaxed);
+    }
+    return *this;
+  }
+
+  // Returns the value for the specified method.
+  int64_t Value(MethodIdentifier method) const {
+    return values_[method].load(std::memory_order_relaxed);
+  }
+
+  // Increases the value for the specified method by `n`
+  void LossyAdd(MethodIdentifier method, int64_t n = 1) {
+    auto& value = values_[method];
+    value.store(value.load(std::memory_order_relaxed) + n,
+                std::memory_order_relaxed);
+  }
+
+  // Adds all the values from `src` to this instance
+  void LossyAdd(const CordzUpdateTracker& src) {
+    for (int i = 0; i < kNumMethods; ++i) {
+      MethodIdentifier method = static_cast<MethodIdentifier>(i);
+      if (int64_t value = src.Value(method)) {
+        LossyAdd(method, value);
+      }
+    }
+  }
+
+ private:
+  // Until C++20 std::atomic is not constexpr default-constructible, so we need
+  // a wrapper for this class to be constexpr constructible.
+  class Counter : public std::atomic<int64_t> {
+   public:
+    constexpr Counter() noexcept : std::atomic<int64_t>(0) {}
+  };
+
+  Counter values_[kNumMethods];
+};
+
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_TRACKER_H_
diff --git a/absl/strings/internal/cordz_update_tracker_test.cc b/absl/strings/internal/cordz_update_tracker_test.cc
new file mode 100644
index 0000000..2348a17
--- /dev/null
+++ b/absl/strings/internal/cordz_update_tracker_test.cc
@@ -0,0 +1,145 @@
+// Copyright 2021 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/cordz_update_tracker.h"
+
+#include <array>
+#include <thread>  // NOLINT
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/synchronization/notification.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+namespace {
+
+using ::testing::AnyOf;
+using ::testing::Eq;
+
+using Method = CordzUpdateTracker::MethodIdentifier;
+using Methods = std::array<Method, Method::kNumMethods>;
+
+// Returns an array of all methods defined in `MethodIdentifier`
+Methods AllMethods() {
+  return Methods{Method::kUnknown,
+                 Method::kAppendBuffer,
+                 Method::kAppendCord,
+                 Method::kAppendExternalMemory,
+                 Method::kAppendString,
+                 Method::kAssignCord,
+                 Method::kAssignString,
+                 Method::kClear,
+                 Method::kConstructorCord,
+                 Method::kConstructorString,
+                 Method::kCordReader,
+                 Method::kFlatten,
+                 Method::kGetAppendRegion,
+                 Method::kMakeCordFromExternal,
+                 Method::kMoveAppendCord,
+                 Method::kMoveAssignCord,
+                 Method::kMovePrependCord,
+                 Method::kPrependBuffer,
+                 Method::kPrependCord,
+                 Method::kPrependString,
+                 Method::kRemovePrefix,
+                 Method::kRemoveSuffix,
+                 Method::kSubCord};
+}
+
+TEST(CordzUpdateTracker, IsConstExprAndInitializesToZero) {
+  constexpr CordzUpdateTracker tracker;
+  for (Method method : AllMethods()) {
+    ASSERT_THAT(tracker.Value(method), Eq(0));
+  }
+}
+
+TEST(CordzUpdateTracker, LossyAdd) {
+  int64_t n = 1;
+  CordzUpdateTracker tracker;
+  for (Method method : AllMethods()) {
+    tracker.LossyAdd(method, n);
+    EXPECT_THAT(tracker.Value(method), Eq(n));
+    n += 2;
+  }
+}
+
+TEST(CordzUpdateTracker, CopyConstructor) {
+  int64_t n = 1;
+  CordzUpdateTracker src;
+  for (Method method : AllMethods()) {
+    src.LossyAdd(method, n);
+    n += 2;
+  }
+
+  n = 1;
+  CordzUpdateTracker tracker(src);
+  for (Method method : AllMethods()) {
+    EXPECT_THAT(tracker.Value(method), Eq(n));
+    n += 2;
+  }
+}
+
+TEST(CordzUpdateTracker, OperatorAssign) {
+  int64_t n = 1;
+  CordzUpdateTracker src;
+  CordzUpdateTracker tracker;
+  for (Method method : AllMethods()) {
+    src.LossyAdd(method, n);
+    n += 2;
+  }
+
+  n = 1;
+  tracker = src;
+  for (Method method : AllMethods()) {
+    EXPECT_THAT(tracker.Value(method), Eq(n));
+    n += 2;
+  }
+}
+
+TEST(CordzUpdateTracker, ThreadSanitizedValueCheck) {
+  absl::Notification done;
+  CordzUpdateTracker tracker;
+
+  std::thread reader([&done, &tracker] {
+    while (!done.HasBeenNotified()) {
+      int n = 1;
+      for (Method method : AllMethods()) {
+        EXPECT_THAT(tracker.Value(method), AnyOf(Eq(n), Eq(0)));
+        n += 2;
+      }
+    }
+    int n = 1;
+    for (Method method : AllMethods()) {
+      EXPECT_THAT(tracker.Value(method), Eq(n));
+      n += 2;
+    }
+  });
+
+  int64_t n = 1;
+  for (Method method : AllMethods()) {
+    tracker.LossyAdd(method, n);
+    n += 2;
+  }
+  done.Notify();
+  reader.join();
+}
+
+}  // namespace
+}  // namespace cord_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/resize_uninitialized.h b/absl/strings/internal/resize_uninitialized.h
index e42628e..49859dc 100644
--- a/absl/strings/internal/resize_uninitialized.h
+++ b/absl/strings/internal/resize_uninitialized.h
@@ -17,6 +17,7 @@
 #ifndef ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_
 #define ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_
 
+#include <algorithm>
 #include <string>
 #include <type_traits>
 #include <utility>
@@ -28,8 +29,9 @@
 ABSL_NAMESPACE_BEGIN
 namespace strings_internal {
 
-// Is a subclass of true_type or false_type, depending on whether or not
-// T has a __resize_default_init member.
+// In this type trait, we look for a __resize_default_init member function, and
+// we use it if available, otherwise, we use resize. We provide HasMember to
+// indicate whether __resize_default_init is present.
 template <typename string_type, typename = void>
 struct ResizeUninitializedTraits {
   using HasMember = std::false_type;
@@ -66,6 +68,50 @@
   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>
+struct AppendUninitializedTraits {
+  static void Append(string_type* s, size_t n) {
+    s->append(n, typename string_type::value_type());
+  }
+};
+
+template <typename string_type>
+struct AppendUninitializedTraits<
+    string_type, absl::void_t<decltype(std::declval<string_type&>()
+                                           .__append_default_init(237))> > {
+  static void Append(string_type* s, size_t n) {
+    s->__append_default_init(n);
+  }
+};
+
+// Like STLStringResizeUninitialized(str, new_size), except guaranteed to use
+// 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 STLStringResizeUninitializedAmortized(string_type* s, size_t new_size) {
+  const size_t size = s->size();
+  if (new_size > size) {
+    AppendUninitializedTraits<string_type>::Append(s, new_size - size);
+  } else {
+    s->erase(new_size);
+  }
+}
+
 }  // namespace strings_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/strings/internal/resize_uninitialized_test.cc b/absl/strings/internal/resize_uninitialized_test.cc
index 0f8b3c2..ad1b9c5 100644
--- a/absl/strings/internal/resize_uninitialized_test.cc
+++ b/absl/strings/internal/resize_uninitialized_test.cc
@@ -19,64 +19,115 @@
 namespace {
 
 int resize_call_count = 0;
+int append_call_count = 0;
 
 // A mock string class whose only purpose is to track how many times its
-// resize() method has been called.
+// resize()/append() methods have been called.
 struct resizable_string {
+  using value_type = char;
   size_t size() const { return 0; }
+  size_t capacity() const { return 0; }
   char& operator[](size_t) {
     static char c = '\0';
     return c;
   }
   void resize(size_t) { resize_call_count += 1; }
+  void append(size_t, value_type) { append_call_count += 1; }
+  void reserve(size_t) {}
+  resizable_string& erase(size_t = 0, size_t = 0) { return *this; }
 };
 
 int resize_default_init_call_count = 0;
+int append_default_init_call_count = 0;
 
 // A mock string class whose only purpose is to track how many times its
-// resize() and __resize_default_init() methods have been called.
-struct resize_default_init_string {
+// resize()/__resize_default_init()/append()/__append_default_init() methods
+// have been called.
+struct default_init_string {
   size_t size() const { return 0; }
+  size_t capacity() const { return 0; }
   char& operator[](size_t) {
     static char c = '\0';
     return c;
   }
   void resize(size_t) { resize_call_count += 1; }
   void __resize_default_init(size_t) { resize_default_init_call_count += 1; }
+  void __append_default_init(size_t) { append_default_init_call_count += 1; }
+  void reserve(size_t) {}
+  default_init_string& erase(size_t = 0, size_t = 0) { return *this; }
 };
 
 TEST(ResizeUninit, WithAndWithout) {
   resize_call_count = 0;
+  append_call_count = 0;
   resize_default_init_call_count = 0;
+  append_default_init_call_count = 0;
   {
     resizable_string rs;
 
     EXPECT_EQ(resize_call_count, 0);
+    EXPECT_EQ(append_call_count, 0);
     EXPECT_EQ(resize_default_init_call_count, 0);
+    EXPECT_EQ(append_default_init_call_count, 0);
     EXPECT_FALSE(
         absl::strings_internal::STLStringSupportsNontrashingResize(&rs));
     EXPECT_EQ(resize_call_count, 0);
+    EXPECT_EQ(append_call_count, 0);
     EXPECT_EQ(resize_default_init_call_count, 0);
+    EXPECT_EQ(append_default_init_call_count, 0);
     absl::strings_internal::STLStringResizeUninitialized(&rs, 237);
     EXPECT_EQ(resize_call_count, 1);
+    EXPECT_EQ(append_call_count, 0);
     EXPECT_EQ(resize_default_init_call_count, 0);
+    EXPECT_EQ(append_default_init_call_count, 0);
+    absl::strings_internal::STLStringResizeUninitializedAmortized(&rs, 1000);
+    EXPECT_EQ(resize_call_count, 1);
+    EXPECT_EQ(append_call_count, 1);
+    EXPECT_EQ(resize_default_init_call_count, 0);
+    EXPECT_EQ(append_default_init_call_count, 0);
   }
 
   resize_call_count = 0;
+  append_call_count = 0;
   resize_default_init_call_count = 0;
+  append_default_init_call_count = 0;
   {
-    resize_default_init_string rus;
+    default_init_string rus;
 
     EXPECT_EQ(resize_call_count, 0);
+    EXPECT_EQ(append_call_count, 0);
     EXPECT_EQ(resize_default_init_call_count, 0);
+    EXPECT_EQ(append_default_init_call_count, 0);
     EXPECT_TRUE(
         absl::strings_internal::STLStringSupportsNontrashingResize(&rus));
     EXPECT_EQ(resize_call_count, 0);
+    EXPECT_EQ(append_call_count, 0);
     EXPECT_EQ(resize_default_init_call_count, 0);
+    EXPECT_EQ(append_default_init_call_count, 0);
     absl::strings_internal::STLStringResizeUninitialized(&rus, 237);
     EXPECT_EQ(resize_call_count, 0);
+    EXPECT_EQ(append_call_count, 0);
     EXPECT_EQ(resize_default_init_call_count, 1);
+    EXPECT_EQ(append_default_init_call_count, 0);
+    absl::strings_internal::STLStringResizeUninitializedAmortized(&rus, 1000);
+    EXPECT_EQ(resize_call_count, 0);
+    EXPECT_EQ(append_call_count, 0);
+    EXPECT_EQ(resize_default_init_call_count, 1);
+    EXPECT_EQ(append_default_init_call_count, 1);
   }
 }
 
+TEST(ResizeUninit, Amortized) {
+  std::string str;
+  size_t prev_cap = str.capacity();
+  int cap_increase_count = 0;
+  for (int i = 0; i < 1000; ++i) {
+    absl::strings_internal::STLStringResizeUninitializedAmortized(&str, i);
+    size_t new_cap = str.capacity();
+    if (new_cap > prev_cap) ++cap_increase_count;
+    prev_cap = new_cap;
+  }
+  EXPECT_LT(cap_increase_count, 50);
+}
+
 }  // namespace
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
index 7040c86..3c91be7 100644
--- a/absl/strings/internal/str_format/arg.h
+++ b/absl/strings/internal/str_format/arg.h
@@ -122,6 +122,14 @@
 StringConvertResult FormatConvertImpl(string_view v,
                                       FormatConversionSpecImpl conv,
                                       FormatSinkImpl* sink);
+#if defined(ABSL_HAVE_STD_STRING_VIEW) && !defined(ABSL_USES_STD_STRING_VIEW)
+inline StringConvertResult FormatConvertImpl(std::string_view v,
+                                             FormatConversionSpecImpl conv,
+                                             FormatSinkImpl* sink) {
+  return FormatConvertImpl(absl::string_view(v.data(), v.size()), conv, sink);
+}
+#endif  // ABSL_HAVE_STD_STRING_VIEW && !ABSL_USES_STD_STRING_VIEW
+
 ArgConvertResult<FormatConversionCharSetUnion(
     FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)>
 FormatConvertImpl(const char* v, const FormatConversionSpecImpl conv,
diff --git a/absl/strings/internal/str_format/bind.cc b/absl/strings/internal/str_format/bind.cc
index 4e68b90..c988ba8 100644
--- a/absl/strings/internal/str_format/bind.cc
+++ b/absl/strings/internal/str_format/bind.cc
@@ -58,7 +58,7 @@
   if (static_cast<size_t>(arg_position - 1) >= pack_.size()) return false;
   arg = &pack_[arg_position - 1];  // 1-based
 
-  if (!unbound->flags.basic) {
+  if (unbound->flags != Flags::kBasic) {
     int width = unbound->width.value();
     bool force_left = false;
     if (unbound->width.is_from_arg()) {
@@ -84,9 +84,8 @@
     FormatConversionSpecImplFriend::SetPrecision(precision, bound);
 
     if (force_left) {
-      Flags flags = unbound->flags;
-      flags.left = true;
-      FormatConversionSpecImplFriend::SetFlags(flags, bound);
+      FormatConversionSpecImplFriend::SetFlags(unbound->flags | Flags::kLeft,
+                                               bound);
     } else {
       FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound);
     }
diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h
index 267cc0e..b26cff6 100644
--- a/absl/strings/internal/str_format/bind.h
+++ b/absl/strings/internal/str_format/bind.h
@@ -100,7 +100,7 @@
   // We use the 'unavailable' attribute to give a better compiler error than
   // just 'method is deleted'.
   // To avoid checking the format twice, we just check that the format is
-  // constexpr. If is it valid, then the overload below will kick in.
+  // constexpr. If it is valid, then the overload below will kick in.
   // We add the template here to make this overload have lower priority.
   template <typename = void>
   FormatSpecTemplate(const char* s)  // NOLINT
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc
index 926283c..91e0360 100644
--- a/absl/strings/internal/str_format/convert_test.cc
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -229,6 +229,9 @@
   TestStringConvert(static_cast<const char*>("hello"));
   TestStringConvert(std::string("hello"));
   TestStringConvert(string_view("hello"));
+#if defined(ABSL_HAVE_STD_STRING_VIEW)
+  TestStringConvert(std::string_view("hello"));
+#endif  // ABSL_HAVE_STD_STRING_VIEW
 }
 
 TEST_F(FormatConvertTest, NullString) {
diff --git a/absl/strings/internal/str_format/extension.cc b/absl/strings/internal/str_format/extension.cc
index bb0d96c..484f6eb 100644
--- a/absl/strings/internal/str_format/extension.cc
+++ b/absl/strings/internal/str_format/extension.cc
@@ -23,13 +23,13 @@
 ABSL_NAMESPACE_BEGIN
 namespace str_format_internal {
 
-std::string Flags::ToString() const {
+std::string FlagsToString(Flags v) {
   std::string s;
-  s.append(left     ? "-" : "");
-  s.append(show_pos ? "+" : "");
-  s.append(sign_col ? " " : "");
-  s.append(alt      ? "#" : "");
-  s.append(zero     ? "0" : "");
+  s.append(FlagsContains(v, Flags::kLeft) ? "-" : "");
+  s.append(FlagsContains(v, Flags::kShowPos) ? "+" : "");
+  s.append(FlagsContains(v, Flags::kSignCol) ? " " : "");
+  s.append(FlagsContains(v, Flags::kAlt) ? "#" : "");
+  s.append(FlagsContains(v, Flags::kZero) ? "0" : "");
   return s;
 }
 
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h
index a9b9e13..55cbb56 100644
--- a/absl/strings/internal/str_format/extension.h
+++ b/absl/strings/internal/str_format/extension.h
@@ -128,19 +128,33 @@
   char buf_[1024];
 };
 
-struct Flags {
-  bool basic : 1;     // fastest conversion: no flags, width, or precision
-  bool left : 1;      // "-"
-  bool show_pos : 1;  // "+"
-  bool sign_col : 1;  // " "
-  bool alt : 1;       // "#"
-  bool zero : 1;      // "0"
-  std::string ToString() const;
-  friend std::ostream& operator<<(std::ostream& os, const Flags& v) {
-    return os << v.ToString();
-  }
+enum class Flags : uint8_t {
+  kBasic = 0,
+  kLeft = 1 << 0,
+  kShowPos = 1 << 1,
+  kSignCol = 1 << 2,
+  kAlt = 1 << 3,
+  kZero = 1 << 4,
+  // This is not a real flag. It just exists to turn off kBasic when no other
+  // flags are set. This is for when width/precision are specified.
+  kNonBasic = 1 << 5,
 };
 
+constexpr Flags operator|(Flags a, Flags b) {
+  return static_cast<Flags>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b));
+}
+
+constexpr bool FlagsContains(Flags haystack, Flags needle) {
+  return (static_cast<uint8_t>(haystack) & static_cast<uint8_t>(needle)) ==
+         static_cast<uint8_t>(needle);
+}
+
+std::string FlagsToString(Flags v);
+
+inline std::ostream& operator<<(std::ostream& os, Flags v) {
+  return os << FlagsToString(v);
+}
+
 // clang-format off
 #define ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \
   /* text */ \
@@ -257,12 +271,16 @@
 class FormatConversionSpecImpl {
  public:
   // Width and precison are not specified, no flags are set.
-  bool is_basic() const { return flags_.basic; }
-  bool has_left_flag() const { return flags_.left; }
-  bool has_show_pos_flag() const { return flags_.show_pos; }
-  bool has_sign_col_flag() const { return flags_.sign_col; }
-  bool has_alt_flag() const { return flags_.alt; }
-  bool has_zero_flag() const { return flags_.zero; }
+  bool is_basic() const { return flags_ == Flags::kBasic; }
+  bool has_left_flag() const { return FlagsContains(flags_, Flags::kLeft); }
+  bool has_show_pos_flag() const {
+    return FlagsContains(flags_, Flags::kShowPos);
+  }
+  bool has_sign_col_flag() const {
+    return FlagsContains(flags_, Flags::kSignCol);
+  }
+  bool has_alt_flag() const { return FlagsContains(flags_, Flags::kAlt); }
+  bool has_zero_flag() const { return FlagsContains(flags_, Flags::kZero); }
 
   FormatConversionChar conversion_char() const {
     // Keep this field first in the struct . It generates better code when
@@ -306,7 +324,7 @@
     conv->precision_ = p;
   }
   static std::string FlagsToString(const FormatConversionSpecImpl& spec) {
-    return spec.flags_.ToString();
+    return str_format_internal::FlagsToString(spec.flags_);
   }
 };
 
diff --git a/absl/strings/internal/str_format/parser.cc b/absl/strings/internal/str_format/parser.cc
index f308d02..2c9c07d 100644
--- a/absl/strings/internal/str_format/parser.cc
+++ b/absl/strings/internal/str_format/parser.cc
@@ -34,60 +34,67 @@
 using CC = FormatConversionCharInternal;
 using LM = LengthMod;
 
+// Abbreviations to fit in the table below.
+constexpr auto f_sign = Flags::kSignCol;
+constexpr auto f_alt = Flags::kAlt;
+constexpr auto f_pos = Flags::kShowPos;
+constexpr auto f_left = Flags::kLeft;
+constexpr auto f_zero = Flags::kZero;
+
 ABSL_CONST_INIT const ConvTag kTags[256] = {
-    {},    {},    {},    {},    {},    {},    {},    {},     // 00-07
-    {},    {},    {},    {},    {},    {},    {},    {},     // 08-0f
-    {},    {},    {},    {},    {},    {},    {},    {},     // 10-17
-    {},    {},    {},    {},    {},    {},    {},    {},     // 18-1f
-    {},    {},    {},    {},    {},    {},    {},    {},     // 20-27
-    {},    {},    {},    {},    {},    {},    {},    {},     // 28-2f
-    {},    {},    {},    {},    {},    {},    {},    {},     // 30-37
-    {},    {},    {},    {},    {},    {},    {},    {},     // 38-3f
-    {},    CC::A, {},    {},    {},    CC::E, CC::F, CC::G,  // @ABCDEFG
-    {},    {},    {},    {},    LM::L, {},    {},    {},     // HIJKLMNO
-    {},    {},    {},    {},    {},    {},    {},    {},     // PQRSTUVW
-    CC::X, {},    {},    {},    {},    {},    {},    {},     // XYZ[\]^_
-    {},    CC::a, {},    CC::c, CC::d, CC::e, CC::f, CC::g,  // `abcdefg
-    LM::h, CC::i, LM::j, {},    LM::l, {},    CC::n, CC::o,  // hijklmno
-    CC::p, LM::q, {},    CC::s, LM::t, CC::u, {},    {},     // pqrstuvw
-    CC::x, {},    LM::z, {},    {},    {},    {},    {},     // xyz{|}!
-    {},    {},    {},    {},    {},    {},    {},    {},     // 80-87
-    {},    {},    {},    {},    {},    {},    {},    {},     // 88-8f
-    {},    {},    {},    {},    {},    {},    {},    {},     // 90-97
-    {},    {},    {},    {},    {},    {},    {},    {},     // 98-9f
-    {},    {},    {},    {},    {},    {},    {},    {},     // a0-a7
-    {},    {},    {},    {},    {},    {},    {},    {},     // a8-af
-    {},    {},    {},    {},    {},    {},    {},    {},     // b0-b7
-    {},    {},    {},    {},    {},    {},    {},    {},     // b8-bf
-    {},    {},    {},    {},    {},    {},    {},    {},     // c0-c7
-    {},    {},    {},    {},    {},    {},    {},    {},     // c8-cf
-    {},    {},    {},    {},    {},    {},    {},    {},     // d0-d7
-    {},    {},    {},    {},    {},    {},    {},    {},     // d8-df
-    {},    {},    {},    {},    {},    {},    {},    {},     // e0-e7
-    {},    {},    {},    {},    {},    {},    {},    {},     // e8-ef
-    {},    {},    {},    {},    {},    {},    {},    {},     // f0-f7
-    {},    {},    {},    {},    {},    {},    {},    {},     // f8-ff
+    {},     {},    {},    {},    {},    {},     {},    {},     // 00-07
+    {},     {},    {},    {},    {},    {},     {},    {},     // 08-0f
+    {},     {},    {},    {},    {},    {},     {},    {},     // 10-17
+    {},     {},    {},    {},    {},    {},     {},    {},     // 18-1f
+    f_sign, {},    {},    f_alt, {},    {},     {},    {},     //  !"#$%&'
+    {},     {},    {},    f_pos, {},    f_left, {},    {},     // ()*+,-./
+    f_zero, {},    {},    {},    {},    {},     {},    {},     // 01234567
+    {},     {},    {},    {},    {},    {},     {},    {},     // 89:;<=>?
+    {},     CC::A, {},    {},    {},    CC::E,  CC::F, CC::G,  // @ABCDEFG
+    {},     {},    {},    {},    LM::L, {},     {},    {},     // HIJKLMNO
+    {},     {},    {},    {},    {},    {},     {},    {},     // PQRSTUVW
+    CC::X,  {},    {},    {},    {},    {},     {},    {},     // XYZ[\]^_
+    {},     CC::a, {},    CC::c, CC::d, CC::e,  CC::f, CC::g,  // `abcdefg
+    LM::h,  CC::i, LM::j, {},    LM::l, {},     CC::n, CC::o,  // hijklmno
+    CC::p,  LM::q, {},    CC::s, LM::t, CC::u,  {},    {},     // pqrstuvw
+    CC::x,  {},    LM::z, {},    {},    {},     {},    {},     // xyz{|}!
+    {},     {},    {},    {},    {},    {},     {},    {},     // 80-87
+    {},     {},    {},    {},    {},    {},     {},    {},     // 88-8f
+    {},     {},    {},    {},    {},    {},     {},    {},     // 90-97
+    {},     {},    {},    {},    {},    {},     {},    {},     // 98-9f
+    {},     {},    {},    {},    {},    {},     {},    {},     // a0-a7
+    {},     {},    {},    {},    {},    {},     {},    {},     // a8-af
+    {},     {},    {},    {},    {},    {},     {},    {},     // b0-b7
+    {},     {},    {},    {},    {},    {},     {},    {},     // b8-bf
+    {},     {},    {},    {},    {},    {},     {},    {},     // c0-c7
+    {},     {},    {},    {},    {},    {},     {},    {},     // c8-cf
+    {},     {},    {},    {},    {},    {},     {},    {},     // d0-d7
+    {},     {},    {},    {},    {},    {},     {},    {},     // d8-df
+    {},     {},    {},    {},    {},    {},     {},    {},     // e0-e7
+    {},     {},    {},    {},    {},    {},     {},    {},     // e8-ef
+    {},     {},    {},    {},    {},    {},     {},    {},     // f0-f7
+    {},     {},    {},    {},    {},    {},     {},    {},     // f8-ff
 };
 
 namespace {
 
 bool CheckFastPathSetting(const UnboundConversion& conv) {
-  bool should_be_basic = !conv.flags.left &&      //
-                         !conv.flags.show_pos &&  //
-                         !conv.flags.sign_col &&  //
-                         !conv.flags.alt &&       //
-                         !conv.flags.zero &&      //
-                         (conv.width.value() == -1) &&
-                         (conv.precision.value() == -1);
-  if (should_be_basic != conv.flags.basic) {
+  bool width_precision_needed =
+      conv.width.value() >= 0 || conv.precision.value() >= 0;
+  if (width_precision_needed && conv.flags == Flags::kBasic) {
     fprintf(stderr,
             "basic=%d left=%d show_pos=%d sign_col=%d alt=%d zero=%d "
             "width=%d precision=%d\n",
-            conv.flags.basic, conv.flags.left, conv.flags.show_pos,
-            conv.flags.sign_col, conv.flags.alt, conv.flags.zero,
-            conv.width.value(), conv.precision.value());
+            conv.flags == Flags::kBasic ? 1 : 0,
+            FlagsContains(conv.flags, Flags::kLeft) ? 1 : 0,
+            FlagsContains(conv.flags, Flags::kShowPos) ? 1 : 0,
+            FlagsContains(conv.flags, Flags::kSignCol) ? 1 : 0,
+            FlagsContains(conv.flags, Flags::kAlt) ? 1 : 0,
+            FlagsContains(conv.flags, Flags::kZero) ? 1 : 0, conv.width.value(),
+            conv.precision.value());
+    return false;
   }
-  return should_be_basic == conv.flags.basic;
+  return true;
 }
 
 template <bool is_positional>
@@ -131,40 +138,21 @@
   ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
 
   // We should start with the basic flag on.
-  assert(conv->flags.basic);
+  assert(conv->flags == Flags::kBasic);
 
   // Any non alpha character makes this conversion not basic.
   // This includes flags (-+ #0), width (1-9, *) or precision (.).
   // All conversion characters and length modifiers are alpha characters.
   if (c < 'A') {
-    conv->flags.basic = false;
-
-    for (; c <= '0';) {
-      // FIXME: We might be able to speed this up reusing the lookup table from
-      // above. It might require changing Flags to be a plain integer where we
-      // can |= a value.
-      switch (c) {
-        case '-':
-          conv->flags.left = true;
-          break;
-        case '+':
-          conv->flags.show_pos = true;
-          break;
-        case ' ':
-          conv->flags.sign_col = true;
-          break;
-        case '#':
-          conv->flags.alt = true;
-          break;
-        case '0':
-          conv->flags.zero = true;
-          break;
-        default:
-          goto flags_done;
+    while (c <= '0') {
+      auto tag = GetTagForChar(c);
+      if (tag.is_flags()) {
+        conv->flags = conv->flags | tag.as_flags();
+        ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+      } else {
+        break;
       }
-      ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
     }
-flags_done:
 
     if (c <= '9') {
       if (c >= '0') {
@@ -173,12 +161,12 @@
           if (ABSL_PREDICT_FALSE(*next_arg != 0)) return nullptr;
           // Positional conversion.
           *next_arg = -1;
-          conv->flags = Flags();
-          conv->flags.basic = true;
           return ConsumeConversion<true>(original_pos, end, conv, next_arg);
         }
+        conv->flags = conv->flags | Flags::kNonBasic;
         conv->width.set_value(maybe_width);
       } else if (c == '*') {
+        conv->flags = conv->flags | Flags::kNonBasic;
         ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
         if (is_positional) {
           if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr;
@@ -192,6 +180,7 @@
     }
 
     if (c == '.') {
+      conv->flags = conv->flags | Flags::kNonBasic;
       ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
       if (std::isdigit(c)) {
         conv->precision.set_value(parse_digits());
diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h
index 6504dd3..ad8646e 100644
--- a/absl/strings/internal/str_format/parser.h
+++ b/absl/strings/internal/str_format/parser.h
@@ -41,10 +41,7 @@
 
 // The analyzed properties of a single specified conversion.
 struct UnboundConversion {
-  UnboundConversion()
-      : flags() /* This is required to zero all the fields of flags. */ {
-    flags.basic = true;
-  }
+  UnboundConversion() {}
 
   class InputValue {
    public:
@@ -79,7 +76,7 @@
   InputValue width;
   InputValue precision;
 
-  Flags flags;
+  Flags flags = Flags::kBasic;
   LengthMod length_mod = LengthMod::none;
   FormatConversionChar conv = FormatConversionCharInternal::kNone;
 };
@@ -93,32 +90,43 @@
                                      UnboundConversion* conv, int* next_arg);
 
 // Helper tag class for the table below.
-// It allows fast `char -> ConversionChar/LengthMod` checking and
+// It allows fast `char -> ConversionChar/LengthMod/Flags` checking and
 // conversions.
 class ConvTag {
  public:
   constexpr ConvTag(FormatConversionChar conversion_char)  // NOLINT
-      : tag_(static_cast<int8_t>(conversion_char)) {}
-  // We invert the length modifiers to make them negative so that we can easily
-  // test for them.
+      : tag_(static_cast<uint8_t>(conversion_char)) {}
   constexpr ConvTag(LengthMod length_mod)  // NOLINT
-      : tag_(~static_cast<std::int8_t>(length_mod)) {}
-  // Everything else is -128, which is negative to make is_conv() simpler.
-  constexpr ConvTag() : tag_(-128) {}
+      : tag_(0x80 | static_cast<uint8_t>(length_mod)) {}
+  constexpr ConvTag(Flags flags)  // NOLINT
+      : tag_(0xc0 | static_cast<uint8_t>(flags)) {}
+  constexpr ConvTag() : tag_(0xFF) {}
 
-  bool is_conv() const { return tag_ >= 0; }
-  bool is_length() const { return tag_ < 0 && tag_ != -128; }
+  bool is_conv() const { return (tag_ & 0x80) == 0; }
+  bool is_length() const { return (tag_ & 0xC0) == 0x80; }
+  bool is_flags() const { return (tag_ & 0xE0) == 0xC0; }
+
   FormatConversionChar as_conv() const {
     assert(is_conv());
+    assert(!is_length());
+    assert(!is_flags());
     return static_cast<FormatConversionChar>(tag_);
   }
   LengthMod as_length() const {
+    assert(!is_conv());
     assert(is_length());
-    return static_cast<LengthMod>(~tag_);
+    assert(!is_flags());
+    return static_cast<LengthMod>(tag_ & 0x3F);
+  }
+  Flags as_flags() const {
+    assert(!is_conv());
+    assert(!is_length());
+    assert(is_flags());
+    return static_cast<Flags>(tag_ & 0x1F);
   }
 
  private:
-  std::int8_t tag_;
+  uint8_t tag_;
 };
 
 extern const ConvTag kTags[256];
diff --git a/absl/strings/internal/str_format/parser_test.cc b/absl/strings/internal/str_format/parser_test.cc
index a5fa1c7..fe0d296 100644
--- a/absl/strings/internal/str_format/parser_test.cc
+++ b/absl/strings/internal/str_format/parser_test.cc
@@ -270,15 +270,22 @@
       for (int k = 0; k < kNumFlags; ++k)
         if ((i >> k) & 1) fmt += kAllFlags[k];
       // flag order shouldn't matter
-      if (rev == 1) { std::reverse(fmt.begin(), fmt.end()); }
+      if (rev == 1) {
+        std::reverse(fmt.begin(), fmt.end());
+      }
       fmt += 'd';
       SCOPED_TRACE(fmt);
       EXPECT_TRUE(Run(fmt.c_str()));
-      EXPECT_EQ(fmt.find('-') == std::string::npos, !o.flags.left);
-      EXPECT_EQ(fmt.find('+') == std::string::npos, !o.flags.show_pos);
-      EXPECT_EQ(fmt.find(' ') == std::string::npos, !o.flags.sign_col);
-      EXPECT_EQ(fmt.find('#') == std::string::npos, !o.flags.alt);
-      EXPECT_EQ(fmt.find('0') == std::string::npos, !o.flags.zero);
+      EXPECT_EQ(fmt.find('-') == std::string::npos,
+                !FlagsContains(o.flags, Flags::kLeft));
+      EXPECT_EQ(fmt.find('+') == std::string::npos,
+                !FlagsContains(o.flags, Flags::kShowPos));
+      EXPECT_EQ(fmt.find(' ') == std::string::npos,
+                !FlagsContains(o.flags, Flags::kSignCol));
+      EXPECT_EQ(fmt.find('#') == std::string::npos,
+                !FlagsContains(o.flags, Flags::kAlt));
+      EXPECT_EQ(fmt.find('0') == std::string::npos,
+                !FlagsContains(o.flags, Flags::kZero));
     }
   }
 }
@@ -288,14 +295,14 @@
   for (const char* fmt : {"d", "llx", "G", "1$X"}) {
     SCOPED_TRACE(fmt);
     EXPECT_TRUE(Run(fmt));
-    EXPECT_TRUE(o.flags.basic);
+    EXPECT_EQ(o.flags, Flags::kBasic);
   }
 
   // Flag is off
   for (const char* fmt : {"3d", ".llx", "-G", "1$#X"}) {
     SCOPED_TRACE(fmt);
     EXPECT_TRUE(Run(fmt));
-    EXPECT_FALSE(o.flags.basic);
+    EXPECT_NE(o.flags, Flags::kBasic);
   }
 }
 
diff --git a/absl/strings/internal/str_split_internal.h b/absl/strings/internal/str_split_internal.h
index a2f41c1..e766421 100644
--- a/absl/strings/internal/str_split_internal.h
+++ b/absl/strings/internal/str_split_internal.h
@@ -32,7 +32,7 @@
 #include <array>
 #include <initializer_list>
 #include <iterator>
-#include <map>
+#include <tuple>
 #include <type_traits>
 #include <utility>
 #include <vector>
@@ -64,7 +64,7 @@
   ConvertibleToStringView(const std::string& s)  // NOLINT(runtime/explicit)
       : value_(s) {}
 
-  // Matches rvalue strings and moves their data to a member.
+  // Disable conversion from rvalue strings.
   ConvertibleToStringView(std::string&& s) = delete;
   ConvertibleToStringView(const std::string&& s) = delete;
 
@@ -182,6 +182,13 @@
 struct HasConstIterator<T, absl::void_t<typename T::const_iterator>>
     : std::true_type {};
 
+// HasEmplace<T>::value is true iff there exists a method T::emplace().
+template <typename T, typename = void>
+struct HasEmplace : std::false_type {};
+template <typename T>
+struct HasEmplace<T, absl::void_t<decltype(std::declval<T>().emplace())>>
+    : std::true_type {};
+
 // IsInitializerList<T>::value is true iff T is an std::initializer_list. More
 // details below in Splitter<> where this is used.
 std::false_type IsInitializerListDispatch(...);  // default: No
@@ -372,50 +379,43 @@
   // value.
   template <typename Container, typename First, typename Second>
   struct ConvertToContainer<Container, std::pair<const First, Second>, true> {
+    using iterator = typename Container::iterator;
+
     Container operator()(const Splitter& splitter) const {
       Container m;
-      typename Container::iterator it;
+      iterator it;
       bool insert = true;
-      for (const auto& sp : splitter) {
+      for (const absl::string_view sv : splitter) {
         if (insert) {
-          it = Inserter<Container>::Insert(&m, First(sp), Second());
+          it = InsertOrEmplace(&m, sv);
         } else {
-          it->second = Second(sp);
+          it->second = Second(sv);
         }
         insert = !insert;
       }
       return m;
     }
 
-    // Inserts the key and value into the given map, returning an iterator to
-    // the inserted item. Specialized for std::map and std::multimap to use
-    // emplace() and adapt emplace()'s return value.
-    template <typename Map>
-    struct Inserter {
-      using M = Map;
-      template <typename... Args>
-      static typename M::iterator Insert(M* m, Args&&... args) {
-        return m->insert(std::make_pair(std::forward<Args>(args)...)).first;
-      }
-    };
+    // Inserts the key and an empty value into the map, returning an iterator to
+    // the inserted item. We use emplace() if available, otherwise insert().
+    template <typename M>
+    static absl::enable_if_t<HasEmplace<M>::value, iterator> InsertOrEmplace(
+        M* m, absl::string_view key) {
+      // Use piecewise_construct to support old versions of gcc in which pair
+      // constructor can't otherwise construct string from string_view.
+      return ToIter(m->emplace(std::piecewise_construct, std::make_tuple(key),
+                               std::tuple<>()));
+    }
+    template <typename M>
+    static absl::enable_if_t<!HasEmplace<M>::value, iterator> InsertOrEmplace(
+        M* m, absl::string_view key) {
+      return ToIter(m->insert(std::make_pair(First(key), Second(""))));
+    }
 
-    template <typename... Ts>
-    struct Inserter<std::map<Ts...>> {
-      using M = std::map<Ts...>;
-      template <typename... Args>
-      static typename M::iterator Insert(M* m, Args&&... args) {
-        return m->emplace(std::make_pair(std::forward<Args>(args)...)).first;
-      }
-    };
-
-    template <typename... Ts>
-    struct Inserter<std::multimap<Ts...>> {
-      using M = std::multimap<Ts...>;
-      template <typename... Args>
-      static typename M::iterator Insert(M* m, Args&&... args) {
-        return m->emplace(std::make_pair(std::forward<Args>(args)...));
-      }
-    };
+    static iterator ToIter(std::pair<iterator, bool> pair) {
+      return pair.first;
+    }
+    static iterator ToIter(iterator iter) { return iter; }
   };
 
   StringType text_;
diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc
index 966d94b..cbd84c9 100644
--- a/absl/strings/numbers.cc
+++ b/absl/strings/numbers.cc
@@ -505,7 +505,7 @@
     *out++ = '-';
     d = -d;
   }
-  if (std::isinf(d)) {
+  if (d > std::numeric_limits<double>::max()) {
     strcpy(out, "inf");  // NOLINT(runtime/printf)
     return out + 3 - buffer;
   }
diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h
index 1780bb4..4ae07c2 100644
--- a/absl/strings/numbers.h
+++ b/absl/strings/numbers.h
@@ -96,6 +96,25 @@
 // unspecified state.
 ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* out);
 
+// SimpleHexAtoi()
+//
+// Converts a hexadecimal string (optionally followed or preceded by ASCII
+// whitespace) to an integer, returning `true` if successful. Only valid base-16
+// hexadecimal integers whose value falls within the range of the integer type
+// (optionally preceded by a `+` or `-`) can be converted. A valid hexadecimal
+// value may include both upper and lowercase character symbols, and may
+// optionally include a leading "0x" (or "0X") number prefix, which is ignored
+// by this function. If any errors are encountered, this function returns
+// `false`, leaving `out` in an unspecified state.
+template <typename int_type>
+ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, int_type* out);
+
+// Overloads of SimpleHexAtoi() for 128 bit integers.
+ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str,
+                                               absl::int128* out);
+ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str,
+                                               absl::uint128* out);
+
 ABSL_NAMESPACE_END
 }  // namespace absl
 
@@ -260,6 +279,21 @@
   return numbers_internal::safe_strtou128_base(str, out, 10);
 }
 
+template <typename int_type>
+ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, int_type* out) {
+  return numbers_internal::safe_strtoi_base(str, out, 16);
+}
+
+ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str,
+                                               absl::int128* out) {
+  return numbers_internal::safe_strto128_base(str, out, 16);
+}
+
+ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str,
+                                               absl::uint128* out) {
+  return numbers_internal::safe_strtou128_base(str, out, 16);
+}
+
 ABSL_NAMESPACE_END
 }  // namespace absl
 
diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc
index f310310..498c210 100644
--- a/absl/strings/numbers_test.cc
+++ b/absl/strings/numbers_test.cc
@@ -47,6 +47,7 @@
 namespace {
 
 using absl::SimpleAtoi;
+using absl::SimpleHexAtoi;
 using absl::numbers_internal::kSixDigitsToBufferSize;
 using absl::numbers_internal::safe_strto32_base;
 using absl::numbers_internal::safe_strto64_base;
@@ -468,6 +469,148 @@
   VerifySimpleAtoiGood<E_biguint>(E_biguint_max32, E_biguint_max32);
 }
 
+template <typename int_type, typename in_val_type>
+void VerifySimpleHexAtoiGood(in_val_type in_value, int_type exp_value) {
+  std::string s;
+  // uint128 can be streamed but not StrCat'd
+  absl::strings_internal::OStringStream strm(&s);
+  if (in_value >= 0) {
+    strm << std::hex << in_value;
+  } else {
+    // Inefficient for small integers, but works with all integral types.
+    strm << "-" << std::hex << -absl::uint128(in_value);
+  }
+  int_type x = static_cast<int_type>(~exp_value);
+  EXPECT_TRUE(SimpleHexAtoi(s, &x))
+      << "in_value=" << std::hex << in_value << " s=" << s << " x=" << x;
+  EXPECT_EQ(exp_value, x);
+  x = static_cast<int_type>(~exp_value);
+  EXPECT_TRUE(SimpleHexAtoi(
+      s.c_str(), &x));  // NOLINT: readability-redundant-string-conversions
+  EXPECT_EQ(exp_value, x);
+}
+
+template <typename int_type, typename in_val_type>
+void VerifySimpleHexAtoiBad(in_val_type in_value) {
+  std::string s;
+  // uint128 can be streamed but not StrCat'd
+  absl::strings_internal::OStringStream strm(&s);
+  if (in_value >= 0) {
+    strm << std::hex << in_value;
+  } else {
+    // Inefficient for small integers, but works with all integral types.
+    strm << "-" << std::hex << -absl::uint128(in_value);
+  }
+  int_type x;
+  EXPECT_FALSE(SimpleHexAtoi(s, &x));
+  EXPECT_FALSE(SimpleHexAtoi(
+      s.c_str(), &x));  // NOLINT: readability-redundant-string-conversions
+}
+
+TEST(NumbersTest, HexAtoi) {
+  // SimpleHexAtoi(absl::string_view, int32_t)
+  VerifySimpleHexAtoiGood<int32_t>(0, 0);
+  VerifySimpleHexAtoiGood<int32_t>(0x42, 0x42);
+  VerifySimpleHexAtoiGood<int32_t>(-0x42, -0x42);
+
+  VerifySimpleHexAtoiGood<int32_t>(std::numeric_limits<int32_t>::min(),
+                                   std::numeric_limits<int32_t>::min());
+  VerifySimpleHexAtoiGood<int32_t>(std::numeric_limits<int32_t>::max(),
+                                   std::numeric_limits<int32_t>::max());
+
+  // SimpleHexAtoi(absl::string_view, uint32_t)
+  VerifySimpleHexAtoiGood<uint32_t>(0, 0);
+  VerifySimpleHexAtoiGood<uint32_t>(0x42, 0x42);
+  VerifySimpleHexAtoiBad<uint32_t>(-0x42);
+
+  VerifySimpleHexAtoiBad<uint32_t>(std::numeric_limits<int32_t>::min());
+  VerifySimpleHexAtoiGood<uint32_t>(std::numeric_limits<int32_t>::max(),
+                                    std::numeric_limits<int32_t>::max());
+  VerifySimpleHexAtoiGood<uint32_t>(std::numeric_limits<uint32_t>::max(),
+                                    std::numeric_limits<uint32_t>::max());
+  VerifySimpleHexAtoiBad<uint32_t>(std::numeric_limits<int64_t>::min());
+  VerifySimpleHexAtoiBad<uint32_t>(std::numeric_limits<int64_t>::max());
+  VerifySimpleHexAtoiBad<uint32_t>(std::numeric_limits<uint64_t>::max());
+
+  // SimpleHexAtoi(absl::string_view, int64_t)
+  VerifySimpleHexAtoiGood<int64_t>(0, 0);
+  VerifySimpleHexAtoiGood<int64_t>(0x42, 0x42);
+  VerifySimpleHexAtoiGood<int64_t>(-0x42, -0x42);
+
+  VerifySimpleHexAtoiGood<int64_t>(std::numeric_limits<int32_t>::min(),
+                                   std::numeric_limits<int32_t>::min());
+  VerifySimpleHexAtoiGood<int64_t>(std::numeric_limits<int32_t>::max(),
+                                   std::numeric_limits<int32_t>::max());
+  VerifySimpleHexAtoiGood<int64_t>(std::numeric_limits<uint32_t>::max(),
+                                   std::numeric_limits<uint32_t>::max());
+  VerifySimpleHexAtoiGood<int64_t>(std::numeric_limits<int64_t>::min(),
+                                   std::numeric_limits<int64_t>::min());
+  VerifySimpleHexAtoiGood<int64_t>(std::numeric_limits<int64_t>::max(),
+                                   std::numeric_limits<int64_t>::max());
+  VerifySimpleHexAtoiBad<int64_t>(std::numeric_limits<uint64_t>::max());
+
+  // SimpleHexAtoi(absl::string_view, uint64_t)
+  VerifySimpleHexAtoiGood<uint64_t>(0, 0);
+  VerifySimpleHexAtoiGood<uint64_t>(0x42, 0x42);
+  VerifySimpleHexAtoiBad<uint64_t>(-0x42);
+
+  VerifySimpleHexAtoiBad<uint64_t>(std::numeric_limits<int32_t>::min());
+  VerifySimpleHexAtoiGood<uint64_t>(std::numeric_limits<int32_t>::max(),
+                                    std::numeric_limits<int32_t>::max());
+  VerifySimpleHexAtoiGood<uint64_t>(std::numeric_limits<uint32_t>::max(),
+                                    std::numeric_limits<uint32_t>::max());
+  VerifySimpleHexAtoiBad<uint64_t>(std::numeric_limits<int64_t>::min());
+  VerifySimpleHexAtoiGood<uint64_t>(std::numeric_limits<int64_t>::max(),
+                                    std::numeric_limits<int64_t>::max());
+  VerifySimpleHexAtoiGood<uint64_t>(std::numeric_limits<uint64_t>::max(),
+                                    std::numeric_limits<uint64_t>::max());
+
+  // SimpleHexAtoi(absl::string_view, absl::uint128)
+  VerifySimpleHexAtoiGood<absl::uint128>(0, 0);
+  VerifySimpleHexAtoiGood<absl::uint128>(0x42, 0x42);
+  VerifySimpleHexAtoiBad<absl::uint128>(-0x42);
+
+  VerifySimpleHexAtoiBad<absl::uint128>(std::numeric_limits<int32_t>::min());
+  VerifySimpleHexAtoiGood<absl::uint128>(std::numeric_limits<int32_t>::max(),
+                                         std::numeric_limits<int32_t>::max());
+  VerifySimpleHexAtoiGood<absl::uint128>(std::numeric_limits<uint32_t>::max(),
+                                         std::numeric_limits<uint32_t>::max());
+  VerifySimpleHexAtoiBad<absl::uint128>(std::numeric_limits<int64_t>::min());
+  VerifySimpleHexAtoiGood<absl::uint128>(std::numeric_limits<int64_t>::max(),
+                                         std::numeric_limits<int64_t>::max());
+  VerifySimpleHexAtoiGood<absl::uint128>(std::numeric_limits<uint64_t>::max(),
+                                         std::numeric_limits<uint64_t>::max());
+  VerifySimpleHexAtoiGood<absl::uint128>(
+      std::numeric_limits<absl::uint128>::max(),
+      std::numeric_limits<absl::uint128>::max());
+
+  // Some other types
+  VerifySimpleHexAtoiGood<int>(-0x42, -0x42);
+  VerifySimpleHexAtoiGood<int32_t>(-0x42, -0x42);
+  VerifySimpleHexAtoiGood<uint32_t>(0x42, 0x42);
+  VerifySimpleHexAtoiGood<unsigned int>(0x42, 0x42);
+  VerifySimpleHexAtoiGood<int64_t>(-0x42, -0x42);
+  VerifySimpleHexAtoiGood<long>(-0x42, -0x42);  // NOLINT: runtime-int
+  VerifySimpleHexAtoiGood<uint64_t>(0x42, 0x42);
+  VerifySimpleHexAtoiGood<size_t>(0x42, 0x42);
+  VerifySimpleHexAtoiGood<std::string::size_type>(0x42, 0x42);
+
+  // Number prefix
+  int32_t value;
+  EXPECT_TRUE(safe_strto32_base("0x34234324", &value, 16));
+  EXPECT_EQ(0x34234324, value);
+
+  EXPECT_TRUE(safe_strto32_base("0X34234324", &value, 16));
+  EXPECT_EQ(0x34234324, value);
+
+  // ASCII whitespace
+  EXPECT_TRUE(safe_strto32_base(" \t\n 34234324", &value, 16));
+  EXPECT_EQ(0x34234324, value);
+
+  EXPECT_TRUE(safe_strto32_base("34234324 \t\n ", &value, 16));
+  EXPECT_EQ(0x34234324, value);
+}
+
 TEST(stringtest, safe_strto32_base) {
   int32_t value;
   EXPECT_TRUE(safe_strto32_base("0x34234324", &value, 16));
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc
index dd5d25b..f4a7749 100644
--- a/absl/strings/str_cat.cc
+++ b/absl/strings/str_cat.cc
@@ -174,7 +174,7 @@
     ASSERT_NO_OVERLAP(*dest, piece);
     total_size += piece.size();
   }
-  strings_internal::STLStringResizeUninitialized(dest, total_size);
+  strings_internal::STLStringResizeUninitializedAmortized(dest, total_size);
 
   char* const begin = &(*dest)[0];
   char* out = begin + old_size;
@@ -199,7 +199,7 @@
   ASSERT_NO_OVERLAP(*dest, a);
   ASSERT_NO_OVERLAP(*dest, b);
   std::string::size_type old_size = dest->size();
-  strings_internal::STLStringResizeUninitialized(
+  strings_internal::STLStringResizeUninitializedAmortized(
       dest, old_size + a.size() + b.size());
   char* const begin = &(*dest)[0];
   char* out = begin + old_size;
@@ -214,7 +214,7 @@
   ASSERT_NO_OVERLAP(*dest, b);
   ASSERT_NO_OVERLAP(*dest, c);
   std::string::size_type old_size = dest->size();
-  strings_internal::STLStringResizeUninitialized(
+  strings_internal::STLStringResizeUninitializedAmortized(
       dest, old_size + a.size() + b.size() + c.size());
   char* const begin = &(*dest)[0];
   char* out = begin + old_size;
@@ -231,7 +231,7 @@
   ASSERT_NO_OVERLAP(*dest, c);
   ASSERT_NO_OVERLAP(*dest, d);
   std::string::size_type old_size = dest->size();
-  strings_internal::STLStringResizeUninitialized(
+  strings_internal::STLStringResizeUninitializedAmortized(
       dest, old_size + a.size() + b.size() + c.size() + d.size());
   char* const begin = &(*dest)[0];
   char* out = begin + old_size;
diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h
index 0146510..4b05c70 100644
--- a/absl/strings/str_format.h
+++ b/absl/strings/str_format.h
@@ -536,8 +536,7 @@
 // The arguments are provided in an `absl::Span<const absl::FormatArg>`.
 // Each `absl::FormatArg` object binds to a single argument and keeps a
 // reference to it. The values used to create the `FormatArg` objects must
-// outlive this function call. (See `str_format_arg.h` for information on
-// the `FormatArg` class.)_
+// outlive this function call.
 //
 // Example:
 //
diff --git a/absl/strings/str_split_test.cc b/absl/strings/str_split_test.cc
index 7f7c097..1b4427b 100644
--- a/absl/strings/str_split_test.cc
+++ b/absl/strings/str_split_test.cc
@@ -29,6 +29,8 @@
 #include "gtest/gtest.h"
 #include "absl/base/dynamic_annotations.h"
 #include "absl/base/macros.h"
+#include "absl/container/btree_map.h"
+#include "absl/container/btree_set.h"
 #include "absl/container/flat_hash_map.h"
 #include "absl/container/node_hash_map.h"
 #include "absl/strings/numbers.h"
@@ -405,6 +407,10 @@
   TestConversionOperator<std::set<std::string>>(splitter);
   TestConversionOperator<std::multiset<absl::string_view>>(splitter);
   TestConversionOperator<std::multiset<std::string>>(splitter);
+  TestConversionOperator<absl::btree_set<absl::string_view>>(splitter);
+  TestConversionOperator<absl::btree_set<std::string>>(splitter);
+  TestConversionOperator<absl::btree_multiset<absl::string_view>>(splitter);
+  TestConversionOperator<absl::btree_multiset<std::string>>(splitter);
   TestConversionOperator<std::unordered_set<std::string>>(splitter);
 
   // Tests conversion to map-like objects.
@@ -421,6 +427,22 @@
   TestMapConversionOperator<std::multimap<std::string, absl::string_view>>(
       splitter);
   TestMapConversionOperator<std::multimap<std::string, std::string>>(splitter);
+  TestMapConversionOperator<
+      absl::btree_map<absl::string_view, absl::string_view>>(splitter);
+  TestMapConversionOperator<absl::btree_map<absl::string_view, std::string>>(
+      splitter);
+  TestMapConversionOperator<absl::btree_map<std::string, absl::string_view>>(
+      splitter);
+  TestMapConversionOperator<absl::btree_map<std::string, std::string>>(
+      splitter);
+  TestMapConversionOperator<
+      absl::btree_multimap<absl::string_view, absl::string_view>>(splitter);
+  TestMapConversionOperator<
+      absl::btree_multimap<absl::string_view, std::string>>(splitter);
+  TestMapConversionOperator<
+      absl::btree_multimap<std::string, absl::string_view>>(splitter);
+  TestMapConversionOperator<absl::btree_multimap<std::string, std::string>>(
+      splitter);
   TestMapConversionOperator<std::unordered_map<std::string, std::string>>(
       splitter);
   TestMapConversionOperator<
@@ -921,8 +943,14 @@
 }
 
 TEST(Split, WorksWithLargeStrings) {
+#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+    defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER)
+  constexpr size_t kSize = (uint32_t{1} << 26) + 1;  // 64M + 1 byte
+#else
+  constexpr size_t kSize = (uint32_t{1} << 31) + 1;  // 2G + 1 byte
+#endif
   if (sizeof(size_t) > 4) {
-    std::string s((uint32_t{1} << 31) + 1, 'x');  // 2G + 1 byte
+    std::string s(kSize, 'x');
     s.back() = '-';
     std::vector<absl::string_view> v = absl::StrSplit(s, '-');
     EXPECT_EQ(2, v.size());
diff --git a/absl/strings/string_view.cc b/absl/strings/string_view.cc
index c5f5de9..d596e08 100644
--- a/absl/strings/string_view.cc
+++ b/absl/strings/string_view.cc
@@ -78,8 +78,8 @@
   return o;
 }
 
-string_view::size_type string_view::find(string_view s, size_type pos) const
-    noexcept {
+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;
@@ -98,8 +98,8 @@
   return result != nullptr ? result - ptr_ : npos;
 }
 
-string_view::size_type string_view::rfind(string_view s, size_type pos) const
-    noexcept {
+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_;
@@ -108,8 +108,8 @@
 }
 
 // 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 {
+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) {
@@ -121,9 +121,8 @@
   return npos;
 }
 
-string_view::size_type string_view::find_first_of(string_view s,
-                                                  size_type pos) const
-    noexcept {
+string_view::size_type string_view::find_first_of(
+    string_view s, size_type pos) const noexcept {
   if (empty() || s.empty()) {
     return npos;
   }
@@ -138,9 +137,8 @@
   return npos;
 }
 
-string_view::size_type string_view::find_first_not_of(string_view s,
-                                                      size_type pos) const
-    noexcept {
+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);
@@ -153,9 +151,8 @@
   return npos;
 }
 
-string_view::size_type string_view::find_first_not_of(char c,
-                                                      size_type pos) const
-    noexcept {
+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) {
@@ -180,9 +177,8 @@
   return npos;
 }
 
-string_view::size_type string_view::find_last_not_of(string_view s,
-                                                     size_type pos) const
-    noexcept {
+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;
@@ -198,9 +194,8 @@
   return npos;
 }
 
-string_view::size_type string_view::find_last_not_of(char c,
-                                                     size_type pos) const
-    noexcept {
+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) {
diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h
index 5260b5b..a4c9a65 100644
--- a/absl/strings/string_view.h
+++ b/absl/strings/string_view.h
@@ -36,6 +36,7 @@
 #include <limits>
 #include <string>
 
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/internal/throw_delegate.h"
 #include "absl/base/macros.h"
@@ -61,6 +62,12 @@
 #define ABSL_INTERNAL_STRING_VIEW_MEMCMP memcmp
 #endif  // ABSL_HAVE_BUILTIN(__builtin_memcmp)
 
+#if defined(__cplusplus) && __cplusplus >= 201402L
+#define ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR constexpr
+#else
+#define ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR
+#endif
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
@@ -180,18 +187,20 @@
 
   template <typename Allocator>
   string_view(  // NOLINT(runtime/explicit)
-      const std::basic_string<char, std::char_traits<char>, Allocator>&
-          str) noexcept
+      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.
-      : string_view(str.data(), str.size()) {}
+      // 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(const char* str)  // NOLINT(runtime/explicit)
-      : ptr_(str),
-        length_(str ? CheckLengthInternal(StrlenInternal(str)) : 0) {}
+      : ptr_(str), length_(str ? StrlenInternal(str) : 0) {}
 
   // Implicit constructor of a `string_view` from a `const char*` and length.
   constexpr string_view(const char* data, size_type len)
@@ -264,9 +273,7 @@
   // string_view::size()
   //
   // Returns the number of characters in the `string_view`.
-  constexpr size_type size() const noexcept {
-    return length_;
-  }
+  constexpr size_type size() const noexcept { return length_; }
 
   // string_view::length()
   //
@@ -333,7 +340,7 @@
   //
   // Removes the first `n` characters from the `string_view`. Note that the
   // underlying string is not changed, only the view.
-  void remove_prefix(size_type n) {
+  ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR void remove_prefix(size_type n) {
     ABSL_HARDENING_ASSERT(n <= length_);
     ptr_ += n;
     length_ -= n;
@@ -343,7 +350,7 @@
   //
   // Removes the last `n` characters from the `string_view`. Note that the
   // underlying string is not changed, only the view.
-  void remove_suffix(size_type n) {
+  ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR void remove_suffix(size_type n) {
     ABSL_HARDENING_ASSERT(n <= length_);
     length_ -= n;
   }
@@ -351,7 +358,7 @@
   // string_view::swap()
   //
   // Swaps this `string_view` with another `string_view`.
-  void swap(string_view& s) noexcept {
+  ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR void swap(string_view& s) noexcept {
     auto t = *this;
     *this = s;
     s = t;
@@ -388,7 +395,7 @@
   // `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, size_type n = npos) const {
+  constexpr string_view substr(size_type pos = 0, size_type n = npos) const {
     return ABSL_PREDICT_FALSE(pos > length_)
                ? (base_internal::ThrowStdOutOfRange(
                       "absl::string_view::substr"),
@@ -398,12 +405,10 @@
 
   // string_view::compare()
   //
-  // Performs a lexicographical comparison between the `string_view` and
-  // another `absl::string_view`, returning -1 if `this` is less than, 0 if
-  // `this` is equal to, and 1 if `this` is greater than the passed string
-  // view. Note that in the case of data equality, a further comparison is made
-  // on the respective sizes of the two `string_view`s to determine which is
-  // smaller, equal, or greater.
+  // 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_,
                        Min(length_, x.length_) == 0
@@ -414,31 +419,31 @@
 
   // Overload of `string_view::compare()` for comparing a substring of the
   // 'string_view` and another `absl::string_view`.
-  int compare(size_type pos1, size_type count1, string_view v) const {
+  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`.
-  int compare(size_type pos1, size_type count1, string_view v, size_type pos2,
-              size_type count2) const {
+  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`.
-  int compare(const char* s) const { return compare(string_view(s)); }
+  // a different C-style string `s`.
+  constexpr int compare(const char* 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`.
-  int compare(size_type pos1, size_type count1, const char* s) const {
+  constexpr int compare(size_type pos1, size_type count1, const char* 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`.
-  int compare(size_type pos1, size_type count1, const char* s,
-              size_type count2) const {
+  constexpr int compare(size_type pos1, size_type count1, const char* s,
+                        size_type count2) const {
     return substr(pos1, count1).compare(string_view(s, count2));
   }
 
@@ -455,48 +460,92 @@
   // 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* 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* 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;
+  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* 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* 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;
+  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 {
+  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* 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* 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;
+  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 {
+  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* 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* 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
@@ -508,20 +557,51 @@
   // 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* 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* 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;
+                             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;
+  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* 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* s, size_type pos = npos) const {
+    return find_last_not_of(string_view(s), pos);
+  }
 
  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* data, size_type len, SkipCheckLengthTag) noexcept
+      : ptr_(data), length_(len) {}
+
   static constexpr size_type kMaxSize =
       (std::numeric_limits<difference_type>::max)();
 
@@ -597,6 +677,7 @@
 ABSL_NAMESPACE_END
 }  // namespace absl
 
+#undef ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR
 #undef ABSL_INTERNAL_STRING_VIEW_MEMCMP
 
 #endif  // ABSL_USES_STD_STRING_VIEW
diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc
index 643af8f..2c13dd1 100644
--- a/absl/strings/string_view_test.cc
+++ b/absl/strings/string_view_test.cc
@@ -449,6 +449,24 @@
   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), 1);
+  EXPECT_EQ(a.find(c.data(), 9, 0), 9);
+  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), 23);
+  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), 0);
   EXPECT_EQ(a.rfind(b, 1), 0);
   EXPECT_EQ(a.rfind(c), 23);
@@ -490,6 +508,14 @@
   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), 1);
+  EXPECT_EQ(a.rfind(c.data(), 22, 0), 22);
+  EXPECT_EQ(a.rfind(c.data(), 1, 0), 1);
+  EXPECT_EQ(a.rfind(c.data(), 0, 0), 0);
+  EXPECT_EQ(b.rfind(c.data(), 0, 0), 0);
+  EXPECT_EQ(d.rfind(b.data(), 4, 0), 0);
+  EXPECT_EQ(e.rfind(b.data(), 7, 0), 0);
 }
 
 // Continued from STL2
@@ -678,6 +704,7 @@
   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);
@@ -1087,7 +1114,24 @@
   EXPECT_EQ(sp_npos, -1);
 }
 
-TEST(StringViewTest, ConstexprSubstr) {
+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);
diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc
index 1f3c740..8980b19 100644
--- a/absl/strings/substitute.cc
+++ b/absl/strings/substitute.cc
@@ -75,7 +75,8 @@
 
   // Build the string.
   size_t original_size = output->size();
-  strings_internal::STLStringResizeUninitialized(output, original_size + size);
+  strings_internal::STLStringResizeUninitializedAmortized(output,
+                                                          original_size + size);
   char* target = &(*output)[original_size];
   for (size_t i = 0; i < format.size(); i++) {
     if (format[i] == '$') {
diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h
index c6da4dc..151c56f 100644
--- a/absl/strings/substitute.h
+++ b/absl/strings/substitute.h
@@ -361,43 +361,49 @@
 // This body of functions catches cases where the number of placeholders
 // doesn't match the number of data arguments.
 void SubstituteAndAppend(std::string* output, const char* format)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0,
-                     "There were no substitution arguments "
-                     "but this format string has a $[0-9] in it");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 0,
+        "There were no substitution arguments "
+        "but this format string either has a $[0-9] in it or contains "
+        "an unescaped $ character (use $$ instead)");
 
 void SubstituteAndAppend(std::string* output, const char* format,
                          const substitute_internal::Arg& a0)
     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1,
                      "There was 1 substitution argument given, but "
-                     "this format string is either missing its $0, or "
-                     "contains one of $1-$9");
+                     "this format string is missing its $0, contains "
+                     "one of $1-$9, or contains an unescaped $ character (use "
+                     "$$ instead)");
 
 void SubstituteAndAppend(std::string* output, const char* format,
                          const substitute_internal::Arg& a0,
                          const substitute_internal::Arg& a1)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 3,
-                     "There were 2 substitution arguments given, but "
-                     "this format string is either missing its $0/$1, or "
-                     "contains one of $2-$9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 3,
+        "There were 2 substitution arguments given, but this format string is "
+        "missing its $0/$1, contains one of $2-$9, or contains an "
+        "unescaped $ character (use $$ instead)");
 
 void SubstituteAndAppend(std::string* output, const char* format,
                          const substitute_internal::Arg& a0,
                          const substitute_internal::Arg& a1,
                          const substitute_internal::Arg& a2)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 7,
-                     "There were 3 substitution arguments given, but "
-                     "this format string is either missing its $0/$1/$2, or "
-                     "contains one of $3-$9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 7,
+        "There were 3 substitution arguments given, but "
+        "this format string is missing its $0/$1/$2, contains one of "
+        "$3-$9, or contains an unescaped $ character (use $$ instead)");
 
 void SubstituteAndAppend(std::string* output, const char* format,
                          const substitute_internal::Arg& a0,
                          const substitute_internal::Arg& a1,
                          const substitute_internal::Arg& a2,
                          const substitute_internal::Arg& a3)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 15,
-                     "There were 4 substitution arguments given, but "
-                     "this format string is either missing its $0-$3, or "
-                     "contains one of $4-$9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 15,
+        "There were 4 substitution arguments given, but "
+        "this format string is missing its $0-$3, contains one of "
+        "$4-$9, or contains an unescaped $ character (use $$ instead)");
 
 void SubstituteAndAppend(std::string* output, const char* format,
                          const substitute_internal::Arg& a0,
@@ -405,10 +411,11 @@
                          const substitute_internal::Arg& a2,
                          const substitute_internal::Arg& a3,
                          const substitute_internal::Arg& a4)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 31,
-                     "There were 5 substitution arguments given, but "
-                     "this format string is either missing its $0-$4, or "
-                     "contains one of $5-$9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 31,
+        "There were 5 substitution arguments given, but "
+        "this format string is missing its $0-$4, contains one of "
+        "$5-$9, or contains an unescaped $ character (use $$ instead)");
 
 void SubstituteAndAppend(std::string* output, const char* format,
                          const substitute_internal::Arg& a0,
@@ -417,20 +424,22 @@
                          const substitute_internal::Arg& a3,
                          const substitute_internal::Arg& a4,
                          const substitute_internal::Arg& a5)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 63,
-                     "There were 6 substitution arguments given, but "
-                     "this format string is either missing its $0-$5, or "
-                     "contains one of $6-$9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 63,
+        "There were 6 substitution arguments given, but "
+        "this format string is missing its $0-$5, contains one of "
+        "$6-$9, or contains an unescaped $ character (use $$ instead)");
 
 void SubstituteAndAppend(
     std::string* output, const char* format, const substitute_internal::Arg& a0,
     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 127,
-                     "There were 7 substitution arguments given, but "
-                     "this format string is either missing its $0-$6, or "
-                     "contains one of $7-$9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 127,
+        "There were 7 substitution arguments given, but "
+        "this format string is missing its $0-$6, contains one of "
+        "$7-$9, or contains an unescaped $ character (use $$ instead)");
 
 void SubstituteAndAppend(
     std::string* output, const char* format, const substitute_internal::Arg& a0,
@@ -438,10 +447,11 @@
     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
     const substitute_internal::Arg& a7)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 255,
-                     "There were 8 substitution arguments given, but "
-                     "this format string is either missing its $0-$7, or "
-                     "contains one of $8-$9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 255,
+        "There were 8 substitution arguments given, but "
+        "this format string is missing its $0-$7, contains one of "
+        "$8-$9, or contains an unescaped $ character (use $$ instead)");
 
 void SubstituteAndAppend(
     std::string* output, const char* format, const substitute_internal::Arg& a0,
@@ -452,7 +462,8 @@
     ABSL_BAD_CALL_IF(
         substitute_internal::PlaceholderBitmask(format) != 511,
         "There were 9 substitution arguments given, but "
-        "this format string is either missing its $0-$8, or contains a $9");
+        "this format string is missing its $0-$8, contains a $9, or "
+        "contains an unescaped $ character (use $$ instead)");
 
 void SubstituteAndAppend(
     std::string* output, const char* format, const substitute_internal::Arg& a0,
@@ -461,9 +472,11 @@
     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
     const substitute_internal::Arg& a9)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1023,
-                     "There were 10 substitution arguments given, but this "
-                     "format string doesn't contain all of $0 through $9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 1023,
+        "There were 10 substitution arguments given, but this "
+        "format string either doesn't contain all of $0 through $9 or "
+        "contains an unescaped $ character (use $$ instead)");
 #endif  // ABSL_BAD_CALL_IF
 
 // Substitute()
@@ -589,47 +602,53 @@
 std::string Substitute(const char* format)
     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0,
                      "There were no substitution arguments "
-                     "but this format string has a $[0-9] in it");
+                     "but this format string either has a $[0-9] in it or "
+                     "contains an unescaped $ character (use $$ instead)");
 
 std::string Substitute(const char* format, const substitute_internal::Arg& a0)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1,
-                     "There was 1 substitution argument given, but "
-                     "this format string is either missing its $0, or "
-                     "contains one of $1-$9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 1,
+        "There was 1 substitution argument given, but "
+        "this format string is missing its $0, contains one of $1-$9, "
+        "or contains an unescaped $ character (use $$ instead)");
 
 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
                        const substitute_internal::Arg& a1)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 3,
-                     "There were 2 substitution arguments given, but "
-                     "this format string is either missing its $0/$1, or "
-                     "contains one of $2-$9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 3,
+        "There were 2 substitution arguments given, but "
+        "this format string is missing its $0/$1, contains one of "
+        "$2-$9, or contains an unescaped $ character (use $$ instead)");
 
 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
                        const substitute_internal::Arg& a1,
                        const substitute_internal::Arg& a2)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 7,
-                     "There were 3 substitution arguments given, but "
-                     "this format string is either missing its $0/$1/$2, or "
-                     "contains one of $3-$9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 7,
+        "There were 3 substitution arguments given, but "
+        "this format string is missing its $0/$1/$2, contains one of "
+        "$3-$9, or contains an unescaped $ character (use $$ instead)");
 
 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
                        const substitute_internal::Arg& a1,
                        const substitute_internal::Arg& a2,
                        const substitute_internal::Arg& a3)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 15,
-                     "There were 4 substitution arguments given, but "
-                     "this format string is either missing its $0-$3, or "
-                     "contains one of $4-$9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 15,
+        "There were 4 substitution arguments given, but "
+        "this format string is missing its $0-$3, contains one of "
+        "$4-$9, or contains an unescaped $ character (use $$ instead)");
 
 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
                        const substitute_internal::Arg& a1,
                        const substitute_internal::Arg& a2,
                        const substitute_internal::Arg& a3,
                        const substitute_internal::Arg& a4)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 31,
-                     "There were 5 substitution arguments given, but "
-                     "this format string is either missing its $0-$4, or "
-                     "contains one of $5-$9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 31,
+        "There were 5 substitution arguments given, but "
+        "this format string is missing its $0-$4, contains one of "
+        "$5-$9, or contains an unescaped $ character (use $$ instead)");
 
 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
                        const substitute_internal::Arg& a1,
@@ -637,10 +656,11 @@
                        const substitute_internal::Arg& a3,
                        const substitute_internal::Arg& a4,
                        const substitute_internal::Arg& a5)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 63,
-                     "There were 6 substitution arguments given, but "
-                     "this format string is either missing its $0-$5, or "
-                     "contains one of $6-$9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 63,
+        "There were 6 substitution arguments given, but "
+        "this format string is missing its $0-$5, contains one of "
+        "$6-$9, or contains an unescaped $ character (use $$ instead)");
 
 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
                        const substitute_internal::Arg& a1,
@@ -649,10 +669,11 @@
                        const substitute_internal::Arg& a4,
                        const substitute_internal::Arg& a5,
                        const substitute_internal::Arg& a6)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 127,
-                     "There were 7 substitution arguments given, but "
-                     "this format string is either missing its $0-$6, or "
-                     "contains one of $7-$9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 127,
+        "There were 7 substitution arguments given, but "
+        "this format string is missing its $0-$6, contains one of "
+        "$7-$9, or contains an unescaped $ character (use $$ instead)");
 
 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
                        const substitute_internal::Arg& a1,
@@ -662,10 +683,11 @@
                        const substitute_internal::Arg& a5,
                        const substitute_internal::Arg& a6,
                        const substitute_internal::Arg& a7)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 255,
-                     "There were 8 substitution arguments given, but "
-                     "this format string is either missing its $0-$7, or "
-                     "contains one of $8-$9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 255,
+        "There were 8 substitution arguments given, but "
+        "this format string is missing its $0-$7, contains one of "
+        "$8-$9, or contains an unescaped $ character (use $$ instead)");
 
 std::string Substitute(
     const char* format, const substitute_internal::Arg& a0,
@@ -676,7 +698,8 @@
     ABSL_BAD_CALL_IF(
         substitute_internal::PlaceholderBitmask(format) != 511,
         "There were 9 substitution arguments given, but "
-        "this format string is either missing its $0-$8, or contains a $9");
+        "this format string is missing its $0-$8, contains a $9, or "
+        "contains an unescaped $ character (use $$ instead)");
 
 std::string Substitute(
     const char* format, const substitute_internal::Arg& a0,
@@ -685,9 +708,11 @@
     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
     const substitute_internal::Arg& a9)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1023,
-                     "There were 10 substitution arguments given, but this "
-                     "format string doesn't contain all of $0 through $9");
+    ABSL_BAD_CALL_IF(
+        substitute_internal::PlaceholderBitmask(format) != 1023,
+        "There were 10 substitution arguments given, but this "
+        "format string either doesn't contain all of $0 through $9 or "
+        "contains an unescaped $ character (use $$ instead)");
 #endif  // ABSL_BAD_CALL_IF
 
 ABSL_NAMESPACE_END
diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel
index 5ce1695..d719547 100644
--- a/absl/synchronization/BUILD.bazel
+++ b/absl/synchronization/BUILD.bazel
@@ -14,7 +14,6 @@
 # limitations under the License.
 #
 
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
@@ -136,6 +135,21 @@
     ],
 )
 
+cc_binary(
+    name = "blocking_counter_benchmark",
+    testonly = 1,
+    srcs = ["blocking_counter_benchmark.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    tags = ["benchmark"],
+    visibility = ["//visibility:private"],
+    deps = [
+        ":synchronization",
+        ":thread_pool",
+        "@com_github_google_benchmark//:benchmark_main",
+    ],
+)
+
 cc_test(
     name = "graphcycles_test",
     size = "medium",
diff --git a/absl/synchronization/CMakeLists.txt b/absl/synchronization/CMakeLists.txt
index e633d0b..605efe2 100644
--- a/absl/synchronization/CMakeLists.txt
+++ b/absl/synchronization/CMakeLists.txt
@@ -95,7 +95,7 @@
   DEPS
     absl::synchronization
     absl::time
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -108,7 +108,7 @@
   DEPS
     absl::synchronization
     absl::time
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -122,7 +122,7 @@
     absl::graphcycles_internal
     absl::core_headers
     absl::raw_logging_internal
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -154,7 +154,7 @@
     absl::memory
     absl::raw_logging_internal
     absl::time
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -167,7 +167,7 @@
   DEPS
     absl::synchronization
     absl::time
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -183,7 +183,7 @@
     absl::config
     absl::strings
     absl::time
-    gmock
+    GTest::gmock
   TESTONLY
 )
 
@@ -199,7 +199,7 @@
     absl::synchronization
     absl::strings
     absl::time
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
diff --git a/absl/synchronization/blocking_counter.cc b/absl/synchronization/blocking_counter.cc
index 3cea7ae..d2f82da 100644
--- a/absl/synchronization/blocking_counter.cc
+++ b/absl/synchronization/blocking_counter.cc
@@ -14,41 +14,51 @@
 
 #include "absl/synchronization/blocking_counter.h"
 
+#include <atomic>
+
 #include "absl/base/internal/raw_logging.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
-// Return whether int *arg is zero.
-static bool IsZero(void *arg) {
-  return 0 == *reinterpret_cast<int *>(arg);
+namespace {
+
+// Return whether int *arg is true.
+bool IsDone(void *arg) { return *reinterpret_cast<bool *>(arg); }
+
+}  // namespace
+
+BlockingCounter::BlockingCounter(int initial_count)
+    : count_(initial_count),
+      num_waiting_(0),
+      done_{initial_count == 0 ? true : false} {
+  ABSL_RAW_CHECK(initial_count >= 0, "BlockingCounter initial_count negative");
 }
 
 bool BlockingCounter::DecrementCount() {
-  MutexLock l(&lock_);
-  count_--;
-  if (count_ < 0) {
-    ABSL_RAW_LOG(
-        FATAL,
-        "BlockingCounter::DecrementCount() called too many times.  count=%d",
-        count_);
+  int count = count_.fetch_sub(1, std::memory_order_acq_rel) - 1;
+  ABSL_RAW_CHECK(count >= 0,
+                 "BlockingCounter::DecrementCount() called too many times");
+  if (count == 0) {
+    MutexLock l(&lock_);
+    done_ = true;
+    return true;
   }
-  return count_ == 0;
+  return false;
 }
 
 void BlockingCounter::Wait() {
   MutexLock l(&this->lock_);
-  ABSL_RAW_CHECK(count_ >= 0, "BlockingCounter underflow");
 
   // only one thread may call Wait(). To support more than one thread,
   // implement a counter num_to_exit, like in the Barrier class.
   ABSL_RAW_CHECK(num_waiting_ == 0, "multiple threads called Wait()");
   num_waiting_++;
 
-  this->lock_.Await(Condition(IsZero, &this->count_));
+  this->lock_.Await(Condition(IsDone, &this->done_));
 
-  // At this point, We know that all threads executing DecrementCount have
-  // released the lock, and so will not touch this object again.
+  // At this point, we know that all threads executing DecrementCount
+  // will not touch this object again.
   // Therefore, the thread calling this method is free to delete the object
   // after we return from this method.
 }
diff --git a/absl/synchronization/blocking_counter.h b/absl/synchronization/blocking_counter.h
index 1f53f9f..1908fdb 100644
--- a/absl/synchronization/blocking_counter.h
+++ b/absl/synchronization/blocking_counter.h
@@ -20,6 +20,8 @@
 #ifndef ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_
 #define ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_
 
+#include <atomic>
+
 #include "absl/base/thread_annotations.h"
 #include "absl/synchronization/mutex.h"
 
@@ -60,8 +62,7 @@
 //
 class BlockingCounter {
  public:
-  explicit BlockingCounter(int initial_count)
-      : count_(initial_count), num_waiting_(0) {}
+  explicit BlockingCounter(int initial_count);
 
   BlockingCounter(const BlockingCounter&) = delete;
   BlockingCounter& operator=(const BlockingCounter&) = delete;
@@ -89,8 +90,9 @@
 
  private:
   Mutex lock_;
-  int count_ ABSL_GUARDED_BY(lock_);
+  std::atomic<int> count_;
   int num_waiting_ ABSL_GUARDED_BY(lock_);
+  bool done_ ABSL_GUARDED_BY(lock_);
 };
 
 ABSL_NAMESPACE_END
diff --git a/absl/synchronization/blocking_counter_benchmark.cc b/absl/synchronization/blocking_counter_benchmark.cc
new file mode 100644
index 0000000..b504d1a
--- /dev/null
+++ b/absl/synchronization/blocking_counter_benchmark.cc
@@ -0,0 +1,83 @@
+// Copyright 2021 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 <limits>
+
+#include "absl/synchronization/blocking_counter.h"
+#include "absl/synchronization/internal/thread_pool.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+void BM_BlockingCounter_SingleThread(benchmark::State& state) {
+  for (auto _ : state) {
+    int iterations = state.range(0);
+    absl::BlockingCounter counter{iterations};
+    for (int i = 0; i < iterations; ++i) {
+      counter.DecrementCount();
+    }
+    counter.Wait();
+  }
+}
+BENCHMARK(BM_BlockingCounter_SingleThread)
+    ->ArgName("iterations")
+    ->Arg(2)
+    ->Arg(4)
+    ->Arg(16)
+    ->Arg(64)
+    ->Arg(256);
+
+void BM_BlockingCounter_DecrementCount(benchmark::State& state) {
+  static absl::BlockingCounter* counter =
+      new absl::BlockingCounter{std::numeric_limits<int>::max()};
+  for (auto _ : state) {
+    counter->DecrementCount();
+  }
+}
+BENCHMARK(BM_BlockingCounter_DecrementCount)
+    ->Threads(2)
+    ->Threads(4)
+    ->Threads(6)
+    ->Threads(8)
+    ->Threads(10)
+    ->Threads(12)
+    ->Threads(16)
+    ->Threads(32)
+    ->Threads(64)
+    ->Threads(128);
+
+void BM_BlockingCounter_Wait(benchmark::State& state) {
+  int num_threads = state.range(0);
+  absl::synchronization_internal::ThreadPool pool(num_threads);
+  for (auto _ : state) {
+    absl::BlockingCounter counter{num_threads};
+    pool.Schedule([num_threads, &counter, &pool]() {
+      for (int i = 0; i < num_threads; ++i) {
+        pool.Schedule([&counter]() { counter.DecrementCount(); });
+      }
+    });
+    counter.Wait();
+  }
+}
+BENCHMARK(BM_BlockingCounter_Wait)
+    ->ArgName("threads")
+    ->Arg(2)
+    ->Arg(4)
+    ->Arg(8)
+    ->Arg(16)
+    ->Arg(32)
+    ->Arg(64)
+    ->Arg(128);
+
+}  // namespace
diff --git a/absl/synchronization/blocking_counter_test.cc b/absl/synchronization/blocking_counter_test.cc
index 2926224..06885f5 100644
--- a/absl/synchronization/blocking_counter_test.cc
+++ b/absl/synchronization/blocking_counter_test.cc
@@ -63,6 +63,18 @@
   }
 }
 
+TEST(BlockingCounterTest, WaitZeroInitialCount) {
+  BlockingCounter counter(0);
+  counter.Wait();
+}
+
+#if GTEST_HAS_DEATH_TEST
+TEST(BlockingCounterTest, WaitNegativeInitialCount) {
+  EXPECT_DEATH(BlockingCounter counter(-1),
+               "BlockingCounter initial_count negative");
+}
+#endif
+
 }  // namespace
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/synchronization/internal/per_thread_sem_test.cc b/absl/synchronization/internal/per_thread_sem_test.cc
index 8cf59e6..db1184e 100644
--- a/absl/synchronization/internal/per_thread_sem_test.cc
+++ b/absl/synchronization/internal/per_thread_sem_test.cc
@@ -159,7 +159,7 @@
   const absl::Duration elapsed = absl::Now() - start;
   // Allow for a slight early return, to account for quality of implementation
   // issues on various platforms.
-  const absl::Duration slop = absl::Microseconds(200);
+  const absl::Duration slop = absl::Milliseconds(1);
   EXPECT_LE(delay - slop, elapsed)
       << "Wait returned " << delay - elapsed
       << " early (with " << slop << " slop), start time was " << start;
diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc
index 2123be6..28ef311 100644
--- a/absl/synchronization/internal/waiter.cc
+++ b/absl/synchronization/internal/waiter.cc
@@ -79,6 +79,7 @@
   // Note that, since the thread ticker is just reset, we don't need to check
   // whether the thread is idle on the very first pass of the loop.
   bool first_pass = true;
+
   while (true) {
     int32_t x = futex_.load(std::memory_order_relaxed);
     while (x != 0) {
@@ -90,7 +91,6 @@
       return true;  // Consumed a wakeup, we are done.
     }
 
-
     if (!first_pass) MaybeBecomeIdle();
     const int err = Futex::WaitUntil(&futex_, 0, t);
     if (err != 0) {
diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h
index f49e0c8..38338f2 100644
--- a/absl/synchronization/mutex.h
+++ b/absl/synchronization/mutex.h
@@ -778,9 +778,9 @@
 //
 // Usage to wake T is:
 //       mu.Lock();
-//      // process data, possibly establishing C
-//      if (C) { cv->Signal(); }
-//      mu.Unlock();
+//       // process data, possibly establishing C
+//       if (C) { cv->Signal(); }
+//       mu.Unlock();
 //
 // If C may be useful to more than one waiter, use `SignalAll()` instead of
 // `Signal()`.
diff --git a/absl/synchronization/mutex_benchmark.cc b/absl/synchronization/mutex_benchmark.cc
index e35aed8..b5d2fbc 100644
--- a/absl/synchronization/mutex_benchmark.cc
+++ b/absl/synchronization/mutex_benchmark.cc
@@ -97,7 +97,7 @@
   // Mutex queueing behavior is modified.
   const bool multiple_priorities = state.range(0);
   ScopedThreadMutexPriority priority_setter(
-      (multiple_priorities && state.thread_index != 0) ? 1 : 0);
+      (multiple_priorities && state.thread_index() != 0) ? 1 : 0);
 
   struct Shared {
     absl::Mutex mu;
@@ -176,7 +176,7 @@
 
 template <typename MutexType>
 void BM_Contended(benchmark::State& state) {
-  int priority = state.thread_index % state.range(1);
+  int priority = state.thread_index() % state.range(1);
   ScopedThreadMutexPriority priority_setter(priority);
 
   struct Shared {
@@ -196,7 +196,7 @@
     // To achieve this amount of local work is multiplied by number of threads
     // to keep ratio between local work and critical section approximately
     // equal regardless of number of threads.
-    DelayNs(100 * state.threads, &local);
+    DelayNs(100 * state.threads(), &local);
     RaiiLocker<MutexType> locker(&shared->mu);
     DelayNs(state.range(0), &shared->data);
   }
diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc
index f8fbf94..4f40317 100644
--- a/absl/synchronization/mutex_test.cc
+++ b/absl/synchronization/mutex_test.cc
@@ -26,6 +26,7 @@
 #include <random>
 #include <string>
 #include <thread>  // NOLINT(build/c++11)
+#include <type_traits>
 #include <vector>
 
 #include "gtest/gtest.h"
@@ -870,33 +871,6 @@
   }
 }
 
-// --------------------------------------------------------
-// Test for bug with pattern of readers using a condvar.  The bug was that if a
-// reader went to sleep on a condition variable while one or more other readers
-// held the lock, but there were no waiters, the reader count (held in the
-// mutex word) would be lost.  (This is because Enqueue() had at one time
-// always placed the thread on the Mutex queue.  Later (CL 4075610), to
-// tolerate re-entry into Mutex from a Condition predicate, Enqueue() was
-// changed so that it could also place a thread on a condition-variable.  This
-// introduced the case where Enqueue() returned with an empty queue, and this
-// case was handled incorrectly in one place.)
-
-static void ReaderForReaderOnCondVar(absl::Mutex *mu, absl::CondVar *cv,
-                                     int *running) {
-  std::random_device dev;
-  std::mt19937 gen(dev());
-  std::uniform_int_distribution<int> random_millis(0, 15);
-  mu->ReaderLock();
-  while (*running == 3) {
-    absl::SleepFor(absl::Milliseconds(random_millis(gen)));
-    cv->WaitWithTimeout(mu, absl::Milliseconds(random_millis(gen)));
-  }
-  mu->ReaderUnlock();
-  mu->Lock();
-  (*running)--;
-  mu->Unlock();
-}
-
 struct True {
   template <class... Args>
   bool operator()(Args...) const {
@@ -945,6 +919,33 @@
   }
 }
 
+// --------------------------------------------------------
+// Test for bug with pattern of readers using a condvar.  The bug was that if a
+// reader went to sleep on a condition variable while one or more other readers
+// held the lock, but there were no waiters, the reader count (held in the
+// mutex word) would be lost.  (This is because Enqueue() had at one time
+// always placed the thread on the Mutex queue.  Later (CL 4075610), to
+// tolerate re-entry into Mutex from a Condition predicate, Enqueue() was
+// changed so that it could also place a thread on a condition-variable.  This
+// introduced the case where Enqueue() returned with an empty queue, and this
+// case was handled incorrectly in one place.)
+
+static void ReaderForReaderOnCondVar(absl::Mutex *mu, absl::CondVar *cv,
+                                     int *running) {
+  std::random_device dev;
+  std::mt19937 gen(dev());
+  std::uniform_int_distribution<int> random_millis(0, 15);
+  mu->ReaderLock();
+  while (*running == 3) {
+    absl::SleepFor(absl::Milliseconds(random_millis(gen)));
+    cv->WaitWithTimeout(mu, absl::Milliseconds(random_millis(gen)));
+  }
+  mu->ReaderUnlock();
+  mu->Lock();
+  (*running)--;
+  mu->Unlock();
+}
+
 static bool IntIsZero(int *x) { return *x == 0; }
 
 // Test for reader waiting condition variable when there are other readers
diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel
index 3e25ca2..e8c4966 100644
--- a/absl/time/BUILD.bazel
+++ b/absl/time/BUILD.bazel
@@ -14,7 +14,6 @@
 # limitations under the License.
 #
 
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
diff --git a/absl/time/CMakeLists.txt b/absl/time/CMakeLists.txt
index 00bdd49..f6ff8bd 100644
--- a/absl/time/CMakeLists.txt
+++ b/absl/time/CMakeLists.txt
@@ -102,7 +102,7 @@
     absl::config
     absl::raw_logging_internal
     absl::time_zone
-    gmock
+    GTest::gmock
   TESTONLY
 )
 
@@ -124,5 +124,5 @@
     absl::config
     absl::core_headers
     absl::time_zone
-    gmock_main
+    GTest::gmock_main
 )
diff --git a/absl/time/civil_time.cc b/absl/time/civil_time.cc
index bdfe9ce..6a231ed 100644
--- a/absl/time/civil_time.cc
+++ b/absl/time/civil_time.cc
@@ -38,9 +38,7 @@
   const CivilSecond ncs(NormalizeYear(cs.year()), cs.month(), cs.day(),
                         cs.hour(), cs.minute(), cs.second());
   const TimeZone utc = UTCTimeZone();
-  // TODO(absl-team): Avoid conversion of fmt string.
-  return StrCat(cs.year(),
-                FormatTime(std::string(fmt), FromCivil(ncs, utc), utc));
+  return StrCat(cs.year(), FormatTime(fmt, FromCivil(ncs, utc), utc));
 }
 
 template <typename CivilT>
diff --git a/absl/time/clock_test.cc b/absl/time/clock_test.cc
index 4bcfc6b..bc77dbc 100644
--- a/absl/time/clock_test.cc
+++ b/absl/time/clock_test.cc
@@ -18,6 +18,10 @@
 #if defined(ABSL_HAVE_ALARM)
 #include <signal.h>
 #include <unistd.h>
+#ifdef _AIX
+// sig_t is not defined in AIX.
+typedef void (*sig_t)(int);
+#endif
 #elif defined(__linux__) || defined(__APPLE__)
 #error all known Linux and Apple targets have alarm
 #endif
diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc
index fb28fa9..b7209e1 100644
--- a/absl/time/duration_test.cc
+++ b/absl/time/duration_test.cc
@@ -17,6 +17,7 @@
 #endif
 
 #include <chrono>  // NOLINT(build/c++11)
+#include <cfloat>
 #include <cmath>
 #include <cstdint>
 #include <ctime>
@@ -1320,7 +1321,7 @@
 
   EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(0));
   // TODO(bww): Is the next one OK?
-  EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(0.124999999e-9));
+  EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(std::nextafter(0.125e-9, 0)));
   EXPECT_EQ(absl::Nanoseconds(1) / 4, absl::Seconds(0.125e-9));
   EXPECT_EQ(absl::Nanoseconds(1) / 4, absl::Seconds(0.250e-9));
   EXPECT_EQ(absl::Nanoseconds(1) / 2, absl::Seconds(0.375e-9));
@@ -1330,7 +1331,7 @@
   EXPECT_EQ(absl::Nanoseconds(1), absl::Seconds(0.875e-9));
   EXPECT_EQ(absl::Nanoseconds(1), absl::Seconds(1.000e-9));
 
-  EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(-0.124999999e-9));
+  EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(std::nextafter(-0.125e-9, 0)));
   EXPECT_EQ(-absl::Nanoseconds(1) / 4, absl::Seconds(-0.125e-9));
   EXPECT_EQ(-absl::Nanoseconds(1) / 4, absl::Seconds(-0.250e-9));
   EXPECT_EQ(-absl::Nanoseconds(1) / 2, absl::Seconds(-0.375e-9));
@@ -1390,6 +1391,14 @@
 // Seconds(point) returns a duration near point * Seconds(1.0). (They may
 // not be exactly equal due to fused multiply/add contraction.)
 TEST(Duration, ToDoubleSecondsCheckEdgeCases) {
+#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0
+  // We're using an x87-compatible FPU, and intermediate operations can be
+  // performed with 80-bit floats. This means the edge cases are different than
+  // what we expect here, so just skip this test.
+  GTEST_SKIP()
+      << "Skipping the test because we detected x87 floating-point semantics";
+#endif
+
   constexpr uint32_t kTicksPerSecond = absl::time_internal::kTicksPerSecond;
   constexpr auto duration_tick = absl::time_internal::MakeDuration(0, 1u);
   int misses = 0;
diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel
index 45a9529..bdc2e30 100644
--- a/absl/time/internal/cctz/BUILD.bazel
+++ b/absl/time/internal/cctz/BUILD.bazel
@@ -12,8 +12,6 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-
 package(features = ["-parse_headers"])
 
 licenses(["notice"])
@@ -26,14 +24,14 @@
 config_setting(
     name = "osx",
     constraint_values = [
-        "@bazel_tools//platforms:osx",
+        "@platforms//os:osx",
     ],
 )
 
 config_setting(
     name = "ios",
     constraint_values = [
-        "@bazel_tools//platforms:ios",
+        "@platforms//os:ios",
     ],
 )
 
diff --git a/absl/time/internal/cctz/include/cctz/time_zone.h b/absl/time/internal/cctz/include/cctz/time_zone.h
index 5562a37..6e382dc 100644
--- a/absl/time/internal/cctz/include/cctz/time_zone.h
+++ b/absl/time/internal/cctz/include/cctz/time_zone.h
@@ -22,6 +22,7 @@
 
 #include <chrono>
 #include <cstdint>
+#include <limits>
 #include <string>
 #include <utility>
 
@@ -41,20 +42,9 @@
 
 namespace detail {
 template <typename D>
-inline std::pair<time_point<seconds>, D> split_seconds(
-    const time_point<D>& tp) {
-  auto sec = std::chrono::time_point_cast<seconds>(tp);
-  auto sub = tp - sec;
-  if (sub.count() < 0) {
-    sec -= seconds(1);
-    sub += seconds(1);
-  }
-  return {sec, std::chrono::duration_cast<D>(sub)};
-}
-inline std::pair<time_point<seconds>, seconds> split_seconds(
-    const time_point<seconds>& tp) {
-  return {tp, seconds::zero()};
-}
+std::pair<time_point<seconds>, D> split_seconds(const time_point<D>& tp);
+std::pair<time_point<seconds>, seconds> split_seconds(
+    const time_point<seconds>& tp);
 }  // namespace detail
 
 // cctz::time_zone is an opaque, small, value-type class representing a
@@ -279,6 +269,20 @@
                    const femtoseconds&, const time_zone&);
 bool parse(const std::string&, const std::string&, const time_zone&,
            time_point<seconds>*, femtoseconds*, std::string* err = nullptr);
+template <typename Rep, std::intmax_t Denom>
+bool join_seconds(
+    const time_point<seconds>& sec, const femtoseconds& fs,
+    time_point<std::chrono::duration<Rep, std::ratio<1, Denom>>>* tpp);
+template <typename Rep, std::intmax_t Num>
+bool join_seconds(
+    const time_point<seconds>& sec, const femtoseconds& fs,
+    time_point<std::chrono::duration<Rep, std::ratio<Num, 1>>>* tpp);
+template <typename Rep>
+bool join_seconds(
+    const time_point<seconds>& sec, const femtoseconds& fs,
+    time_point<std::chrono::duration<Rep, std::ratio<1, 1>>>* tpp);
+bool join_seconds(const time_point<seconds>& sec, const femtoseconds&,
+                  time_point<seconds>* tpp);
 }  // namespace detail
 
 // Formats the given time_point in the given cctz::time_zone according to
@@ -369,15 +373,84 @@
                   const time_zone& tz, time_point<D>* tpp) {
   time_point<seconds> sec;
   detail::femtoseconds fs;
-  const bool b = detail::parse(fmt, input, tz, &sec, &fs);
-  if (b) {
-    // TODO: Return false if unrepresentable as a time_point<D>.
-    *tpp = std::chrono::time_point_cast<D>(sec);
-    *tpp += std::chrono::duration_cast<D>(fs);
-  }
-  return b;
+  return detail::parse(fmt, input, tz, &sec, &fs) &&
+         detail::join_seconds(sec, fs, tpp);
 }
 
+namespace detail {
+
+// Split a time_point<D> into a time_point<seconds> and a D subseconds.
+// Undefined behavior if time_point<seconds> is not of sufficient range.
+// Note that this means it is UB to call cctz::time_zone::lookup(tp) or
+// cctz::format(fmt, tp, tz) with a time_point that is outside the range
+// of a 64-bit std::time_t.
+template <typename D>
+std::pair<time_point<seconds>, D> split_seconds(const time_point<D>& tp) {
+  auto sec = std::chrono::time_point_cast<seconds>(tp);
+  auto sub = tp - sec;
+  if (sub.count() < 0) {
+    sec -= seconds(1);
+    sub += seconds(1);
+  }
+  return {sec, std::chrono::duration_cast<D>(sub)};
+}
+
+inline std::pair<time_point<seconds>, seconds> split_seconds(
+    const time_point<seconds>& tp) {
+  return {tp, seconds::zero()};
+}
+
+// Join a time_point<seconds> and femto subseconds into a time_point<D>.
+// Floors to the resolution of time_point<D>. Returns false if time_point<D>
+// is not of sufficient range.
+template <typename Rep, std::intmax_t Denom>
+bool join_seconds(
+    const time_point<seconds>& sec, const femtoseconds& fs,
+    time_point<std::chrono::duration<Rep, std::ratio<1, Denom>>>* tpp) {
+  using D = std::chrono::duration<Rep, std::ratio<1, Denom>>;
+  // TODO(#199): Return false if result unrepresentable as a time_point<D>.
+  *tpp = std::chrono::time_point_cast<D>(sec);
+  *tpp += std::chrono::duration_cast<D>(fs);
+  return true;
+}
+
+template <typename Rep, std::intmax_t Num>
+bool join_seconds(
+    const time_point<seconds>& sec, const femtoseconds&,
+    time_point<std::chrono::duration<Rep, std::ratio<Num, 1>>>* tpp) {
+  using D = std::chrono::duration<Rep, std::ratio<Num, 1>>;
+  auto count = sec.time_since_epoch().count();
+  if (count >= 0 || count % Num == 0) {
+    count /= Num;
+  } else {
+    count /= Num;
+    count -= 1;
+  }
+  if (count > (std::numeric_limits<Rep>::max)()) return false;
+  if (count < (std::numeric_limits<Rep>::min)()) return false;
+  *tpp = time_point<D>() + D{static_cast<Rep>(count)};
+  return true;
+}
+
+template <typename Rep>
+bool join_seconds(
+    const time_point<seconds>& sec, const femtoseconds&,
+    time_point<std::chrono::duration<Rep, std::ratio<1, 1>>>* tpp) {
+  using D = std::chrono::duration<Rep, std::ratio<1, 1>>;
+  auto count = sec.time_since_epoch().count();
+  if (count > (std::numeric_limits<Rep>::max)()) return false;
+  if (count < (std::numeric_limits<Rep>::min)()) return false;
+  *tpp = time_point<D>() + D{static_cast<Rep>(count)};
+  return true;
+}
+
+inline bool join_seconds(const time_point<seconds>& sec, const femtoseconds&,
+                         time_point<seconds>* tpp) {
+  *tpp = sec;
+  return true;
+}
+
+}  // namespace detail
 }  // namespace cctz
 }  // namespace time_internal
 ABSL_NAMESPACE_END
diff --git a/absl/time/internal/cctz/src/cctz_benchmark.cc b/absl/time/internal/cctz/src/cctz_benchmark.cc
index 4e39188..6770ad6 100644
--- a/absl/time/internal/cctz/src/cctz_benchmark.cc
+++ b/absl/time/internal/cctz/src/cctz_benchmark.cc
@@ -648,6 +648,7 @@
                                       "Pacific/Guam",
                                       "Pacific/Honolulu",
                                       "Pacific/Johnston",
+                                      "Pacific/Kanton",
                                       "Pacific/Kiritimati",
                                       "Pacific/Kosrae",
                                       "Pacific/Kwajalein",
diff --git a/absl/time/internal/cctz/src/time_zone_fixed.cc b/absl/time/internal/cctz/src/time_zone_fixed.cc
index 303c024..f2b3294 100644
--- a/absl/time/internal/cctz/src/time_zone_fixed.cc
+++ b/absl/time/internal/cctz/src/time_zone_fixed.cc
@@ -53,7 +53,7 @@
 }  // namespace
 
 bool FixedOffsetFromName(const std::string& name, seconds* offset) {
-  if (name.compare(0, std::string::npos, "UTC", 3) == 0) {
+  if (name == "UTC" || name == "UTC0") {
     *offset = seconds::zero();
     return true;
   }
diff --git a/absl/time/internal/cctz/src/time_zone_format_test.cc b/absl/time/internal/cctz/src/time_zone_format_test.cc
index a11f93e..6487fa9 100644
--- a/absl/time/internal/cctz/src/time_zone_format_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_format_test.cc
@@ -13,6 +13,7 @@
 //   limitations under the License.
 
 #include <chrono>
+#include <cstdint>
 #include <iomanip>
 #include <sstream>
 #include <string>
@@ -1135,7 +1136,7 @@
   // All %E<prec>S cases are treated the same as %E*S on input.
   auto precisions = {"*", "0", "1",  "2",  "3",  "4",  "5",  "6", "7",
                      "8", "9", "10", "11", "12", "13", "14", "15"};
-  for (const std::string& prec : precisions) {
+  for (const std::string prec : precisions) {
     const std::string fmt = "%E" + prec + "S";
     SCOPED_TRACE(fmt);
     time_point<chrono::nanoseconds> tp = unix_epoch;
@@ -1217,7 +1218,7 @@
   // All %E<prec>f cases are treated the same as %E*f on input.
   auto precisions = {"*", "0", "1",  "2",  "3",  "4",  "5",  "6", "7",
                      "8", "9", "10", "11", "12", "13", "14", "15"};
-  for (const std::string& prec : precisions) {
+  for (const std::string prec : precisions) {
     const std::string fmt = "%E" + prec + "f";
     SCOPED_TRACE(fmt);
     time_point<chrono::nanoseconds> tp = unix_epoch - chrono::seconds(1);
@@ -1504,7 +1505,7 @@
       parse(RFC3339_sec, "292277026596-12-04T14:30:07-01:00", utc, &tp));
   EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::max());
   EXPECT_FALSE(
-      parse(RFC3339_sec, "292277026596-12-04T15:30:07-01:00", utc, &tp));
+      parse(RFC3339_sec, "292277026596-12-04T14:30:08-01:00", utc, &tp));
 
   // tests the lower limit using +00:00 offset
   EXPECT_TRUE(
@@ -1525,10 +1526,82 @@
       parse(RFC3339_sec, "9223372036854775807-12-31T23:59:59-00:01", utc, &tp));
   EXPECT_FALSE(parse(RFC3339_sec, "-9223372036854775808-01-01T00:00:00+00:01",
                      utc, &tp));
+}
 
-  // TODO: Add tests that parsing times with fractional seconds overflow
-  // appropriately. This can't be done until cctz::parse() properly detects
-  // overflow when combining the chrono seconds and femto.
+TEST(Parse, TimePointOverflow) {
+  const time_zone utc = utc_time_zone();
+
+  using D = chrono::duration<std::int64_t, std::nano>;
+  time_point<D> tp;
+
+  EXPECT_TRUE(
+      parse(RFC3339_full, "2262-04-11T23:47:16.8547758079+00:00", utc, &tp));
+  EXPECT_EQ(tp, time_point<D>::max());
+  EXPECT_EQ("2262-04-11T23:47:16.854775807+00:00",
+            format(RFC3339_full, tp, utc));
+#if 0
+  // TODO(#199): Will fail until cctz::parse() properly detects overflow.
+  EXPECT_FALSE(
+      parse(RFC3339_full, "2262-04-11T23:47:16.8547758080+00:00", utc, &tp));
+  EXPECT_TRUE(
+      parse(RFC3339_full, "1677-09-21T00:12:43.1452241920+00:00", utc, &tp));
+  EXPECT_EQ(tp, time_point<D>::min());
+  EXPECT_EQ("1677-09-21T00:12:43.145224192+00:00",
+            format(RFC3339_full, tp, utc));
+  EXPECT_FALSE(
+      parse(RFC3339_full, "1677-09-21T00:12:43.1452241919+00:00", utc, &tp));
+#endif
+
+  using DS = chrono::duration<std::int8_t, chrono::seconds::period>;
+  time_point<DS> stp;
+
+  EXPECT_TRUE(parse(RFC3339_full, "1970-01-01T00:02:07.9+00:00", utc, &stp));
+  EXPECT_EQ(stp, time_point<DS>::max());
+  EXPECT_EQ("1970-01-01T00:02:07+00:00", format(RFC3339_full, stp, utc));
+  EXPECT_FALSE(parse(RFC3339_full, "1970-01-01T00:02:08+00:00", utc, &stp));
+
+  EXPECT_TRUE(parse(RFC3339_full, "1969-12-31T23:57:52+00:00", utc, &stp));
+  EXPECT_EQ(stp, time_point<DS>::min());
+  EXPECT_EQ("1969-12-31T23:57:52+00:00", format(RFC3339_full, stp, utc));
+  EXPECT_FALSE(parse(RFC3339_full, "1969-12-31T23:57:51.9+00:00", utc, &stp));
+
+  using DM = chrono::duration<std::int8_t, chrono::minutes::period>;
+  time_point<DM> mtp;
+
+  EXPECT_TRUE(parse(RFC3339_full, "1970-01-01T02:07:59+00:00", utc, &mtp));
+  EXPECT_EQ(mtp, time_point<DM>::max());
+  EXPECT_EQ("1970-01-01T02:07:00+00:00", format(RFC3339_full, mtp, utc));
+  EXPECT_FALSE(parse(RFC3339_full, "1970-01-01T02:08:00+00:00", utc, &mtp));
+
+  EXPECT_TRUE(parse(RFC3339_full, "1969-12-31T21:52:00+00:00", utc, &mtp));
+  EXPECT_EQ(mtp, time_point<DM>::min());
+  EXPECT_EQ("1969-12-31T21:52:00+00:00", format(RFC3339_full, mtp, utc));
+  EXPECT_FALSE(parse(RFC3339_full, "1969-12-31T21:51:59+00:00", utc, &mtp));
+}
+
+TEST(Parse, TimePointOverflowFloor) {
+  const time_zone utc = utc_time_zone();
+
+  using D = chrono::duration<std::int64_t, std::micro>;
+  time_point<D> tp;
+
+  EXPECT_TRUE(
+      parse(RFC3339_full, "294247-01-10T04:00:54.7758079+00:00", utc, &tp));
+  EXPECT_EQ(tp, time_point<D>::max());
+  EXPECT_EQ("294247-01-10T04:00:54.775807+00:00",
+            format(RFC3339_full, tp, utc));
+#if 0
+  // TODO(#199): Will fail until cctz::parse() properly detects overflow.
+  EXPECT_FALSE(
+      parse(RFC3339_full, "294247-01-10T04:00:54.7758080+00:00", utc, &tp));
+  EXPECT_TRUE(
+      parse(RFC3339_full, "-290308-12-21T19:59:05.2241920+00:00", utc, &tp));
+  EXPECT_EQ(tp, time_point<D>::min());
+  EXPECT_EQ("-290308-12-21T19:59:05.224192+00:00",
+            format(RFC3339_full, tp, utc));
+  EXPECT_FALSE(
+      parse(RFC3339_full, "-290308-12-21T19:59:05.2241919+00:00", utc, &tp));
+#endif
 }
 
 //
diff --git a/absl/time/internal/cctz/src/time_zone_if.h b/absl/time/internal/cctz/src/time_zone_if.h
index 32c0891..7d3e42d 100644
--- a/absl/time/internal/cctz/src/time_zone_if.h
+++ b/absl/time/internal/cctz/src/time_zone_if.h
@@ -56,7 +56,8 @@
 
 // Convert between time_point<seconds> and a count of seconds since the
 // Unix epoch.  We assume that the std::chrono::system_clock and the
-// Unix clock are second aligned, but not that they share an epoch.
+// Unix clock are second aligned, and that the results are representable.
+// (That is, that they share an epoch, which is required since C++20.)
 inline std::int_fast64_t ToUnixSeconds(const time_point<seconds>& tp) {
   return (tp - std::chrono::time_point_cast<seconds>(
                    std::chrono::system_clock::from_time_t(0)))
diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc
index 8039353..4f175d9 100644
--- a/absl/time/internal/cctz/src/time_zone_info.cc
+++ b/absl/time/internal/cctz/src/time_zone_info.cc
@@ -39,10 +39,12 @@
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
+#include <fstream>
 #include <functional>
 #include <memory>
 #include <sstream>
 #include <string>
+#include <utility>
 
 #include "absl/base/config.h"
 #include "absl/time/internal/cctz/include/cctz/civil_time.h"
@@ -576,14 +578,17 @@
 
 namespace {
 
+using FilePtr = std::unique_ptr<FILE, int (*)(FILE*)>;
+
 // fopen(3) adaptor.
-inline FILE* FOpen(const char* path, const char* mode) {
+inline FilePtr FOpen(const char* path, const char* mode) {
 #if defined(_MSC_VER)
   FILE* fp;
   if (fopen_s(&fp, path, mode) != 0) fp = nullptr;
-  return fp;
+  return FilePtr(fp, fclose);
 #else
-  return fopen(path, mode);  // TODO: Enable the close-on-exec flag.
+  // TODO: Enable the close-on-exec flag.
+  return FilePtr(fopen(path, mode), fclose);
 #endif
 }
 
@@ -611,11 +616,11 @@
 
  protected:
   explicit FileZoneInfoSource(
-      FILE* fp, std::size_t len = std::numeric_limits<std::size_t>::max())
-      : fp_(fp, fclose), len_(len) {}
+      FilePtr fp, std::size_t len = std::numeric_limits<std::size_t>::max())
+      : fp_(std::move(fp)), len_(len) {}
 
  private:
-  std::unique_ptr<FILE, int (*)(FILE*)> fp_;
+  FilePtr fp_;
   std::size_t len_;
 };
 
@@ -644,17 +649,9 @@
   path.append(name, pos, std::string::npos);
 
   // Open the zoneinfo file.
-  FILE* fp = FOpen(path.c_str(), "rb");
+  auto fp = FOpen(path.c_str(), "rb");
   if (fp == nullptr) return nullptr;
-  std::size_t length = 0;
-  if (fseek(fp, 0, SEEK_END) == 0) {
-    long offset = ftell(fp);
-    if (offset >= 0) {
-      length = static_cast<std::size_t>(offset);
-    }
-    rewind(fp);
-  }
-  return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(fp, length));
+  return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(std::move(fp)));
 }
 
 class AndroidZoneInfoSource : public FileZoneInfoSource {
@@ -663,8 +660,9 @@
   std::string Version() const override { return version_; }
 
  private:
-  explicit AndroidZoneInfoSource(FILE* fp, std::size_t len, const char* vers)
-      : FileZoneInfoSource(fp, len), version_(vers) {}
+  explicit AndroidZoneInfoSource(FilePtr fp, std::size_t len,
+                                 std::string version)
+      : FileZoneInfoSource(std::move(fp), len), version_(std::move(version)) {}
   std::string version_;
 };
 
@@ -676,8 +674,8 @@
   // See Android's libc/tzcode/bionic.cpp for additional information.
   for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata",
                              "/system/usr/share/zoneinfo/tzdata"}) {
-    std::unique_ptr<FILE, int (*)(FILE*)> fp(FOpen(tzdata, "rb"), fclose);
-    if (fp.get() == nullptr) continue;
+    auto fp = FOpen(tzdata, "rb");
+    if (fp == nullptr) continue;
 
     char hbuf[24];  // covers header.zonetab_offset too
     if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue;
@@ -703,7 +701,7 @@
       if (strcmp(name.c_str() + pos, ebuf) == 0) {
         if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break;
         return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource(
-            fp.release(), static_cast<std::size_t>(length), vers));
+            std::move(fp), static_cast<std::size_t>(length), vers));
       }
     }
   }
@@ -711,6 +709,69 @@
   return nullptr;
 }
 
+// A zoneinfo source for use inside Fuchsia components. This attempts to
+// read zoneinfo files from one of several known paths in a component's
+// incoming namespace. [Config data][1] is preferred, but package-specific
+// resources are also supported.
+//
+// Fuchsia's implementation supports `FileZoneInfoSource::Version()`.
+//
+// [1]:
+// https://fuchsia.dev/fuchsia-src/development/components/data#using_config_data_in_your_component
+class FuchsiaZoneInfoSource : public FileZoneInfoSource {
+ public:
+  static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
+  std::string Version() const override { return version_; }
+
+ private:
+  explicit FuchsiaZoneInfoSource(FilePtr fp, std::string version)
+      : FileZoneInfoSource(std::move(fp)), version_(std::move(version)) {}
+  std::string version_;
+};
+
+std::unique_ptr<ZoneInfoSource> FuchsiaZoneInfoSource::Open(
+    const std::string& name) {
+  // Use of the "file:" prefix is intended for testing purposes only.
+  const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
+
+  // Prefixes where a Fuchsia component might find zoneinfo files,
+  // in descending order of preference.
+  const auto kTzdataPrefixes = {
+      "/config/data/tzdata/",
+      "/pkg/data/tzdata/",
+      "/data/tzdata/",
+  };
+  const auto kEmptyPrefix = {""};
+  const bool name_absolute = (pos != name.size() && name[pos] == '/');
+  const auto prefixes = name_absolute ? kEmptyPrefix : kTzdataPrefixes;
+
+  // Fuchsia builds place zoneinfo files at "<prefix><format><name>".
+  for (const std::string prefix : prefixes) {
+    std::string path = prefix;
+    if (!prefix.empty()) path += "zoneinfo/tzif2/";  // format
+    path.append(name, pos, std::string::npos);
+
+    auto fp = FOpen(path.c_str(), "rb");
+    if (fp == nullptr) continue;
+
+    std::string version;
+    if (!prefix.empty()) {
+      // Fuchsia builds place the version in "<prefix>revision.txt".
+      std::ifstream version_stream(prefix + "revision.txt");
+      if (version_stream.is_open()) {
+        // revision.txt should contain no newlines, but to be
+        // defensive we read just the first line.
+        std::getline(version_stream, version);
+      }
+    }
+
+    return std::unique_ptr<ZoneInfoSource>(
+        new FuchsiaZoneInfoSource(std::move(fp), std::move(version)));
+  }
+
+  return nullptr;
+}
+
 }  // namespace
 
 bool TimeZoneInfo::Load(const std::string& name) {
@@ -728,6 +789,7 @@
       name, [](const std::string& n) -> std::unique_ptr<ZoneInfoSource> {
         if (auto z = FileZoneInfoSource::Open(n)) return z;
         if (auto z = AndroidZoneInfoSource::Open(n)) return z;
+        if (auto z = FuchsiaZoneInfoSource::Open(n)) return z;
         return nullptr;
       });
   return zip != nullptr && Load(zip.get());
diff --git a/absl/time/internal/cctz/src/time_zone_lookup.cc b/absl/time/internal/cctz/src/time_zone_lookup.cc
index efdea64..898d04c 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup.cc
@@ -28,6 +28,13 @@
 #include <vector>
 #endif
 
+#if defined(__Fuchsia__)
+#include <fuchsia/intl/cpp/fidl.h>
+#include <lib/async-loop/cpp/loop.h>
+#include <lib/sys/cpp/component_context.h>
+#include <zircon/types.h>
+#endif
+
 #include <cstdlib>
 #include <cstring>
 #include <string>
@@ -140,6 +147,48 @@
   }
   CFRelease(tz_default);
 #endif
+#if defined(__Fuchsia__)
+  std::string primary_tz;
+  [&]() {
+    // Note: We can't use the synchronous FIDL API here because it doesn't
+    // allow timeouts; if the FIDL call failed, local_time_zone() would never
+    // return.
+
+    const zx::duration kTimeout = zx::msec(500);
+
+    // Don't attach to the thread because otherwise the thread's dispatcher
+    // would be set to null when the loop is destroyed, causing any other FIDL
+    // code running on the same thread to crash.
+    async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
+    std::unique_ptr<sys::ComponentContext> context =
+        sys::ComponentContext::Create();
+
+    fuchsia::intl::PropertyProviderHandle handle;
+    zx_status_t status = context->svc()->Connect(handle.NewRequest());
+    if (status != ZX_OK) {
+      return;
+    }
+
+    fuchsia::intl::PropertyProviderPtr intl_provider;
+    status = intl_provider.Bind(std::move(handle), loop.dispatcher());
+    if (status != ZX_OK) {
+      return;
+    }
+
+    intl_provider->GetProfile(
+        [&loop, &primary_tz](fuchsia::intl::Profile profile) {
+          if (!profile.time_zones().empty()) {
+            primary_tz = profile.time_zones()[0].id;
+          }
+          loop.Quit();
+        });
+    loop.Run(zx::deadline_after(kTimeout));
+  }();
+
+  if (!primary_tz.empty()) {
+    zone = primary_tz.c_str();
+  }
+#endif
 
   // Allow ${TZ} to override to default zone.
   char* tz_env = nullptr;
diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
index 9a1a8d6..0226ab7 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -579,6 +579,7 @@
                                       "Pacific/Guam",
                                       "Pacific/Honolulu",
                                       "Pacific/Johnston",
+                                      "Pacific/Kanton",
                                       "Pacific/Kiritimati",
                                       "Pacific/Kosrae",
                                       "Pacific/Kwajalein",
@@ -717,6 +718,18 @@
 }
 #endif
 
+TEST(TimeZone, UTC) {
+  const time_zone utc = utc_time_zone();
+
+  time_zone loaded_utc;
+  EXPECT_TRUE(load_time_zone("UTC", &loaded_utc));
+  EXPECT_EQ(loaded_utc, utc);
+
+  time_zone loaded_utc0;
+  EXPECT_TRUE(load_time_zone("UTC0", &loaded_utc0));
+  EXPECT_EQ(loaded_utc0, utc);
+}
+
 TEST(TimeZone, NamedTimeZones) {
   const time_zone utc = utc_time_zone();
   EXPECT_EQ("UTC", utc.name());
@@ -1014,8 +1027,12 @@
 #endif
     const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900;
     tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut);
+#if defined(__Fuchsia__)
+    // Fuchsia's gmtime_r() fails on extreme negative values (fxbug.dev/78527).
+#else
     EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, cut));
 #endif
+#endif
   }
 }
 
diff --git a/absl/time/internal/cctz/src/tzfile.h b/absl/time/internal/cctz/src/tzfile.h
index 269fa36..31e8598 100644
--- a/absl/time/internal/cctz/src/tzfile.h
+++ b/absl/time/internal/cctz/src/tzfile.h
@@ -43,7 +43,7 @@
 
 struct tzhead {
   char tzh_magic[4];      /* TZ_MAGIC */
-  char tzh_version[1];    /* '\0' or '2' or '3' as of 2013 */
+  char tzh_version[1];    /* '\0' or '2'-'4' as of 2021 */
   char tzh_reserved[15];  /* reserved; must be zero */
   char tzh_ttisutcnt[4];  /* coded number of trans. time flags */
   char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
diff --git a/absl/time/internal/cctz/src/zone_info_source.cc b/absl/time/internal/cctz/src/zone_info_source.cc
index 7209533..5ab5a59 100644
--- a/absl/time/internal/cctz/src/zone_info_source.cc
+++ b/absl/time/internal/cctz/src/zone_info_source.cc
@@ -65,7 +65,7 @@
 extern ZoneInfoSourceFactory zone_info_source_factory;
 extern ZoneInfoSourceFactory default_factory;
 ZoneInfoSourceFactory default_factory = DefaultFactory;
-#if defined(_M_IX86)
+#if defined(_M_IX86) || defined(_M_ARM)
 #pragma comment(                                                                                                         \
     linker,                                                                                                              \
     "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS                    \
@@ -83,8 +83,7 @@
     "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                                   \
     "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
     "@@ZA")
-#elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM) || \
-    defined(_M_ARM64)
+#elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM64)
 #pragma comment(                                                                                                          \
     linker,                                                                                                               \
     "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS                     \
diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version
index 1d59095..8ee898b 100644
--- a/absl/time/internal/cctz/testdata/version
+++ b/absl/time/internal/cctz/testdata/version
@@ -1 +1 @@
-2021a
+2021e
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
index c39ae38..8906e88 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla b/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla
index f4fe590..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua b/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua
index f4fe590..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba b/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba
index d6ddf7d..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan b/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan
index c828715..9154643 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados b/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados
index 9d3afa6..720c986 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon b/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon
index 7096b69..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour b/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour
index c828715..9154643 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Creston b/absl/time/internal/cctz/testdata/zoneinfo/America/Creston
index 9d69a0a..c2bd2f9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Creston
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Creston
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao b/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao
index d6ddf7d..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica b/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica
index f4fe590..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada b/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada
index f4fe590..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe b/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe
index f4fe590..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana b/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana
index ebd85d0..bcc6688 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk b/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk
index d6ddf7d..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes b/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes
index d6ddf7d..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot b/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot
index f4fe590..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat b/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat
index f4fe590..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau b/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau
index 2ef2aa8..fe6be8e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain b/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain
index f4fe590..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy
index f4fe590..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts
index f4fe590..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia
index f4fe590..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas
index f4fe590..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent
index f4fe590..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola b/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola
index f4fe590..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin b/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin
index f4fe590..47b4dc3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville
index c0cfc85..5d8fc3a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa
index 97d80d7..01c47cc 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman
index 1bd09fe..d97d308 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
index 58e9fdf..ccc57c9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
index aeda06b..906d8d5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores
index b7f75a9..e6e2616 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira
index 7c3a49c..cf965c3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon
index 6484166..f0c70b6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia
index 244af26..a6b835a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury
index b22ab14..2b6a060 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
index e3934e4..8b2dd52 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kanton b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kanton
new file mode 100644
index 0000000..2b6a060
--- /dev/null
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kanton
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue
index 7b35793..be874e2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga
index 143a188..7220bda 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu
index 54aeb0f..f28c840 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Portugal b/absl/time/internal/cctz/testdata/zoneinfo/Portugal
index 6484166..f0c70b6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Portugal
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Portugal
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
index 396e4d3..c614be8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
@@ -40,11 +40,9 @@
 AM	+4011+04430	Asia/Yerevan
 AQ	-6617+11031	Antarctica/Casey	Casey
 AQ	-6835+07758	Antarctica/Davis	Davis
-AQ	-6640+14001	Antarctica/DumontDUrville	Dumont-d'Urville
 AQ	-6736+06253	Antarctica/Mawson	Mawson
 AQ	-6448-06406	Antarctica/Palmer	Palmer
 AQ	-6734-06808	Antarctica/Rothera	Rothera
-AQ	-690022+0393524	Antarctica/Syowa	Syowa
 AQ	-720041+0023206	Antarctica/Troll	Troll
 AQ	-7824+10654	Antarctica/Vostok	Vostok
 AR	-3436-05827	America/Argentina/Buenos_Aires	Buenos Aires (BA, CF)
@@ -97,7 +95,6 @@
 BR	-0308-06001	America/Manaus	Amazonas (east)
 BR	-0640-06952	America/Eirunepe	Amazonas (west)
 BR	-0958-06748	America/Rio_Branco	Acre
-BS	+2505-07721	America/Nassau
 BT	+2728+08939	Asia/Thimphu
 BY	+5354+02734	Europe/Minsk
 BZ	+1730-08812	America/Belize
@@ -106,13 +103,11 @@
 CA	+4612-05957	America/Glace_Bay	Atlantic - NS (Cape Breton)
 CA	+4606-06447	America/Moncton	Atlantic - New Brunswick
 CA	+5320-06025	America/Goose_Bay	Atlantic - Labrador (most areas)
-CA	+5125-05707	America/Blanc-Sablon	AST - QC (Lower North Shore)
-CA	+4339-07923	America/Toronto	Eastern - ON, QC (most areas)
+CA,BS	+4339-07923	America/Toronto	Eastern - ON, QC (most areas), Bahamas
 CA	+4901-08816	America/Nipigon	Eastern - ON, QC (no DST 1967-73)
 CA	+4823-08915	America/Thunder_Bay	Eastern - ON (Thunder Bay)
 CA	+6344-06828	America/Iqaluit	Eastern - NU (most east areas)
 CA	+6608-06544	America/Pangnirtung	Eastern - NU (Pangnirtung)
-CA	+484531-0913718	America/Atikokan	EST - ON (Atikokan); NU (Coral H)
 CA	+4953-09709	America/Winnipeg	Central - ON (west); Manitoba
 CA	+4843-09434	America/Rainy_River	Central - ON (Rainy R, Ft Frances)
 CA	+744144-0944945	America/Resolute	Central - NU (Resolute)
@@ -123,7 +118,6 @@
 CA	+690650-1050310	America/Cambridge_Bay	Mountain - NU (west)
 CA	+6227-11421	America/Yellowknife	Mountain - NT (central)
 CA	+682059-1334300	America/Inuvik	Mountain - NT (west)
-CA	+4906-11631	America/Creston	MST - BC (Creston)
 CA	+5946-12014	America/Dawson_Creek	MST - BC (Dawson Cr, Ft St John)
 CA	+5848-12242	America/Fort_Nelson	MST - BC (Ft Nelson)
 CA	+6043-13503	America/Whitehorse	MST - Yukon (east)
@@ -131,7 +125,7 @@
 CA	+4916-12307	America/Vancouver	Pacific - BC (most areas)
 CC	-1210+09655	Indian/Cocos
 CH,DE,LI	+4723+00832	Europe/Zurich	Swiss time
-CI,BF,GM,GN,ML,MR,SH,SL,SN,TG	+0519-00402	Africa/Abidjan
+CI,BF,GH,GM,GN,ML,MR,SH,SL,SN,TG	+0519-00402	Africa/Abidjan
 CK	-2114-15946	Pacific/Rarotonga
 CL	-3327-07040	America/Santiago	Chile (most areas)
 CL	-5309-07055	America/Punta_Arenas	Region of Magallanes
@@ -142,7 +136,6 @@
 CR	+0956-08405	America/Costa_Rica
 CU	+2308-08222	America/Havana
 CV	+1455-02331	Atlantic/Cape_Verde
-CW,AW,BQ,SX	+1211-06900	America/Curacao
 CX	-1025+10543	Indian/Christmas
 CY	+3510+03322	Asia/Nicosia	Cyprus (most areas)
 CY	+3507+03357	Asia/Famagusta	Northern Cyprus
@@ -170,7 +163,6 @@
 GB,GG,IM,JE	+513030-0000731	Europe/London
 GE	+4143+04449	Asia/Tbilisi
 GF	+0456-05220	America/Cayenne
-GH	+0533-00013	Africa/Accra
 GI	+3608-00521	Europe/Gibraltar
 GL	+6411-05144	America/Nuuk	Greenland (most areas)
 GL	+7646-01840	America/Danmarkshavn	National Park (east coast)
@@ -204,7 +196,7 @@
 KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT	-0117+03649	Africa/Nairobi
 KG	+4254+07436	Asia/Bishkek
 KI	+0125+17300	Pacific/Tarawa	Gilbert Islands
-KI	-0308-17105	Pacific/Enderbury	Phoenix Islands
+KI	-0247-17143	Pacific/Kanton	Phoenix Islands
 KI	+0152-15720	Pacific/Kiritimati	Line Islands
 KP	+3901+12545	Asia/Pyongyang
 KR	+3733+12658	Asia/Seoul
@@ -262,19 +254,19 @@
 NU	-1901-16955	Pacific/Niue
 NZ,AQ	-3652+17446	Pacific/Auckland	New Zealand time
 NZ	-4357-17633	Pacific/Chatham	Chatham Islands
-PA,KY	+0858-07932	America/Panama
+PA,CA,KY	+0858-07932	America/Panama	EST - Panama, Cayman, ON (Atikokan), NU (Coral H)
 PE	-1203-07703	America/Lima
 PF	-1732-14934	Pacific/Tahiti	Society Islands
 PF	-0900-13930	Pacific/Marquesas	Marquesas Islands
 PF	-2308-13457	Pacific/Gambier	Gambier Islands
-PG	-0930+14710	Pacific/Port_Moresby	Papua New Guinea (most areas)
+PG,AQ	-0930+14710	Pacific/Port_Moresby	Papua New Guinea (most areas), Dumont d'Urville
 PG	-0613+15534	Pacific/Bougainville	Bougainville
 PH	+1435+12100	Asia/Manila
 PK	+2452+06703	Asia/Karachi
 PL	+5215+02100	Europe/Warsaw
 PM	+4703-05620	America/Miquelon
 PN	-2504-13005	Pacific/Pitcairn
-PR	+182806-0660622	America/Puerto_Rico
+PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI	+182806-0660622	America/Puerto_Rico	AST
 PS	+3130+03428	Asia/Gaza	Gaza Strip
 PS	+313200+0350542	Asia/Hebron	West Bank
 PT	+3843-00908	Europe/Lisbon	Portugal (mainland)
@@ -314,12 +306,12 @@
 RU	+6728+15343	Asia/Srednekolymsk	MSK+08 - Sakha (E); North Kuril Is
 RU	+5301+15839	Asia/Kamchatka	MSK+09 - Kamchatka
 RU	+6445+17729	Asia/Anadyr	MSK+09 - Bering Sea
-SA,KW,YE	+2438+04643	Asia/Riyadh
+SA,AQ,KW,YE	+2438+04643	Asia/Riyadh	Arabia, Syowa
 SB	-0932+16012	Pacific/Guadalcanal
 SC	-0440+05528	Indian/Mahe
 SD	+1536+03232	Africa/Khartoum
 SE	+5920+01803	Europe/Stockholm
-SG	+0117+10351	Asia/Singapore
+SG,MY	+0117+10351	Asia/Singapore	Singapore, peninsular Malaysia
 SR	+0550-05510	America/Paramaribo
 SS	+0451+03137	Africa/Juba
 ST	+0020+00644	Africa/Sao_Tome
@@ -334,9 +326,8 @@
 TL	-0833+12535	Asia/Dili
 TM	+3757+05823	Asia/Ashgabat
 TN	+3648+01011	Africa/Tunis
-TO	-2110-17510	Pacific/Tongatapu
+TO	-210800-1751200	Pacific/Tongatapu
 TR	+4101+02858	Europe/Istanbul
-TT,AG,AI,BL,DM,GD,GP,KN,LC,MF,MS,VC,VG,VI	+1039-06131	America/Port_of_Spain
 TV	-0831+17913	Pacific/Funafuti
 TW	+2503+12130	Asia/Taipei
 UA	+5026+03031	Europe/Kiev	Ukraine (most areas)
@@ -362,7 +353,7 @@
 US	+471551-1014640	America/North_Dakota/Beulah	Central - ND (Mercer)
 US	+394421-1045903	America/Denver	Mountain (most areas)
 US	+433649-1161209	America/Boise	Mountain - ID (south); OR (east)
-US	+332654-1120424	America/Phoenix	MST - Arizona (except Navajo)
+US,CA	+332654-1120424	America/Phoenix	MST - Arizona (except Navajo), Creston BC
 US	+340308-1181434	America/Los_Angeles	Pacific
 US	+611305-1495401	America/Anchorage	Alaska (most areas)
 US	+581807-1342511	America/Juneau	Alaska - Juneau area
diff --git a/absl/time/time.h b/absl/time/time.h
index 2df6858..5abd815 100644
--- a/absl/time/time.h
+++ b/absl/time/time.h
@@ -182,18 +182,29 @@
 
   // Overloads that forward to either the int64_t or double overloads above.
   // Integer operands must be representable as int64_t.
-  template <typename T>
+  template <typename T, time_internal::EnableIfIntegral<T> = 0>
   Duration& operator*=(T r) {
     int64_t x = r;
     return *this *= x;
   }
-  template <typename T>
+
+  template <typename T, time_internal::EnableIfIntegral<T> = 0>
   Duration& operator/=(T r) {
     int64_t x = r;
     return *this /= x;
   }
-  Duration& operator*=(float r) { return *this *= static_cast<double>(r); }
-  Duration& operator/=(float r) { return *this /= static_cast<double>(r); }
+
+  template <typename T, time_internal::EnableIfFloat<T> = 0>
+  Duration& operator*=(T r) {
+    double x = r;
+    return *this *= x;
+  }
+
+  template <typename T, time_internal::EnableIfFloat<T> = 0>
+  Duration& operator/=(T r) {
+    double x = r;
+    return *this /= x;
+  }
 
   template <typename H>
   friend H AbslHashValue(H h, Duration d) {
@@ -392,12 +403,30 @@
 //
 //   absl::Duration a = absl::Seconds(60);
 //   absl::Duration b = absl::Minutes(1);  // b == a
-constexpr Duration Nanoseconds(int64_t n);
-constexpr Duration Microseconds(int64_t n);
-constexpr Duration Milliseconds(int64_t n);
-constexpr Duration Seconds(int64_t n);
-constexpr Duration Minutes(int64_t n);
-constexpr Duration Hours(int64_t n);
+template <typename T, time_internal::EnableIfIntegral<T> = 0>
+constexpr Duration Nanoseconds(T n) {
+  return time_internal::FromInt64(n, std::nano{});
+}
+template <typename T, time_internal::EnableIfIntegral<T> = 0>
+constexpr Duration Microseconds(T n) {
+  return time_internal::FromInt64(n, std::micro{});
+}
+template <typename T, time_internal::EnableIfIntegral<T> = 0>
+constexpr Duration Milliseconds(T n) {
+  return time_internal::FromInt64(n, std::milli{});
+}
+template <typename T, time_internal::EnableIfIntegral<T> = 0>
+constexpr Duration Seconds(T n) {
+  return time_internal::FromInt64(n, std::ratio<1>{});
+}
+template <typename T, time_internal::EnableIfIntegral<T> = 0>
+constexpr Duration Minutes(T n) {
+  return time_internal::FromInt64(n, std::ratio<60>{});
+}
+template <typename T, time_internal::EnableIfIntegral<T> = 0>
+constexpr Duration Hours(T n) {
+  return time_internal::FromInt64(n, std::ratio<3600>{});
+}
 
 // Factory overloads for constructing `Duration` values from a floating-point
 // number of the unit indicated by the factory function's name. These functions
@@ -451,8 +480,9 @@
 // ToInt64Hours()
 //
 // Helper functions that convert a Duration to an integral count of the
-// indicated unit. These functions are shorthand for the `IDivDuration()`
-// function above; see its documentation for details about overflow, etc.
+// indicated unit. These return the same results as the `IDivDuration()`
+// function, though they usually do so more efficiently; see the
+// documentation of `IDivDuration()` for details about overflow, etc.
 //
 // Example:
 //
@@ -547,10 +577,20 @@
 // `ZeroDuration()`. Parses "inf" and "-inf" as +/- `InfiniteDuration()`.
 bool ParseDuration(absl::string_view dur_string, Duration* d);
 
-// Support for flag values of type Duration. Duration flags must be specified
-// in a format that is valid input for absl::ParseDuration().
+// AbslParseFlag()
+//
+// Parses a command-line flag string representation `text` into a a Duration
+// value. Duration flags must be specified in a format that is valid input for
+// `absl::ParseDuration()`.
 bool AbslParseFlag(absl::string_view text, Duration* dst, std::string* error);
+
+
+// AbslUnparseFlag()
+//
+// Unparses a Duration value into a command-line string representation using
+// the format specified by `absl::ParseDuration()`.
 std::string AbslUnparseFlag(Duration d);
+
 ABSL_DEPRECATED("Use AbslParseFlag() instead.")
 bool ParseFlag(const std::string& text, Duration* dst, std::string* error);
 ABSL_DEPRECATED("Use AbslUnparseFlag() instead.")
@@ -813,8 +853,12 @@
 //   // tp == std::chrono::system_clock::from_time_t(123);
 std::chrono::system_clock::time_point ToChronoTime(Time);
 
-// Support for flag values of type Time. Time flags must be specified in a
-// format that matches absl::RFC3339_full. For example:
+// AbslParseFlag()
+//
+// Parses the command-line flag string representation `text` into a Time value.
+// Time flags must be specified in a format that matches absl::RFC3339_full.
+//
+// For example:
 //
 //   --start_time=2016-01-02T03:04:05.678+08:00
 //
@@ -824,7 +868,13 @@
 // seconds/milliseconds/etc from the Unix epoch, use an absl::Duration flag
 // and add that duration to absl::UnixEpoch() to get an absl::Time.
 bool AbslParseFlag(absl::string_view text, Time* t, std::string* error);
+
+// AbslUnparseFlag()
+//
+// Unparses a Time value into a command-line string representation using
+// the format specified by `absl::ParseTime()`.
 std::string AbslUnparseFlag(Time t);
+
 ABSL_DEPRECATED("Use AbslParseFlag() instead.")
 bool ParseFlag(const std::string& text, Time* t, std::string* error);
 ABSL_DEPRECATED("Use AbslUnparseFlag() instead.")
@@ -1352,7 +1402,7 @@
 inline Duration MakePosDoubleDuration(double n) {
   const int64_t int_secs = static_cast<int64_t>(n);
   const uint32_t ticks = static_cast<uint32_t>(
-      (n - static_cast<double>(int_secs)) * kTicksPerSecond + 0.5);
+      std::round((n - static_cast<double>(int_secs)) * kTicksPerSecond));
   return ticks < kTicksPerSecond
              ? MakeDuration(int_secs, ticks)
              : MakeDuration(int_secs + 1, ticks - kTicksPerSecond);
@@ -1476,25 +1526,6 @@
 
 }  // namespace time_internal
 
-constexpr Duration Nanoseconds(int64_t n) {
-  return time_internal::FromInt64(n, std::nano{});
-}
-constexpr Duration Microseconds(int64_t n) {
-  return time_internal::FromInt64(n, std::micro{});
-}
-constexpr Duration Milliseconds(int64_t n) {
-  return time_internal::FromInt64(n, std::milli{});
-}
-constexpr Duration Seconds(int64_t n) {
-  return time_internal::FromInt64(n, std::ratio<1>{});
-}
-constexpr Duration Minutes(int64_t n) {
-  return time_internal::FromInt64(n, std::ratio<60>{});
-}
-constexpr Duration Hours(int64_t n) {
-  return time_internal::FromInt64(n, std::ratio<3600>{});
-}
-
 constexpr bool operator<(Duration lhs, Duration rhs) {
   return time_internal::GetRepHi(lhs) != time_internal::GetRepHi(rhs)
              ? time_internal::GetRepHi(lhs) < time_internal::GetRepHi(rhs)
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel
index 83be936..38ed228 100644
--- a/absl/types/BUILD.bazel
+++ b/absl/types/BUILD.bazel
@@ -13,7 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt
index c356b21..d7e8614 100644
--- a/absl/types/CMakeLists.txt
+++ b/absl/types/CMakeLists.txt
@@ -69,7 +69,7 @@
     absl::exception_testing
     absl::raw_logging_internal
     absl::test_instance_tracker
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -85,7 +85,7 @@
     absl::exception_testing
     absl::raw_logging_internal
     absl::test_instance_tracker
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -99,7 +99,7 @@
     absl::any
     absl::config
     absl::exception_safety_testing
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -136,7 +136,7 @@
     absl::inlined_vector
     absl::hash_testing
     absl::strings
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -156,7 +156,7 @@
     absl::inlined_vector
     absl::hash_testing
     absl::strings
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -222,7 +222,7 @@
     absl::raw_logging_internal
     absl::strings
     absl::type_traits
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -236,7 +236,7 @@
     absl::optional
     absl::config
     absl::exception_safety_testing
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -258,7 +258,7 @@
     absl::type_traits
     absl::strings
     absl::utility
-    gmock_main
+    GTest::gmock_main
   TESTONLY
 )
 
@@ -275,7 +275,7 @@
   DEPS
     absl::conformance_testing
     absl::type_traits
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -288,7 +288,7 @@
   DEPS
     absl::conformance_testing
     absl::type_traits
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -324,7 +324,7 @@
     absl::memory
     absl::type_traits
     absl::strings
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_library(
@@ -350,7 +350,7 @@
   DEPS
     absl::base
     absl::compare
-    gmock_main
+    GTest::gmock_main
 )
 
 absl_cc_test(
@@ -365,5 +365,5 @@
     absl::config
     absl::exception_safety_testing
     absl::memory
-    gmock_main
+    GTest::gmock_main
 )
diff --git a/absl/types/bad_optional_access.h b/absl/types/bad_optional_access.h
index a500286..049e72a 100644
--- a/absl/types/bad_optional_access.h
+++ b/absl/types/bad_optional_access.h
@@ -67,7 +67,7 @@
 namespace optional_internal {
 
 // throw delegator
-[[noreturn]] void throw_bad_optional_access();
+[[noreturn]] ABSL_DLL void throw_bad_optional_access();
 
 }  // namespace optional_internal
 ABSL_NAMESPACE_END
diff --git a/absl/types/bad_variant_access.h b/absl/types/bad_variant_access.h
index 095969f..8ab215e 100644
--- a/absl/types/bad_variant_access.h
+++ b/absl/types/bad_variant_access.h
@@ -70,8 +70,8 @@
 
 namespace variant_internal {
 
-[[noreturn]] void ThrowBadVariantAccess();
-[[noreturn]] void Rethrow();
+[[noreturn]] ABSL_DLL void ThrowBadVariantAccess();
+[[noreturn]] ABSL_DLL void Rethrow();
 
 }  // namespace variant_internal
 ABSL_NAMESPACE_END
diff --git a/absl/types/span.h b/absl/types/span.h
index 95fe792..6272bb7 100644
--- a/absl/types/span.h
+++ b/absl/types/span.h
@@ -40,7 +40,6 @@
 //    * `absl::Span` has compiler-provided move and copy constructors and
 //      assignment. This is due to them being specified as `constexpr`, but that
 //      implies const in C++11.
-//    * `absl::Span` has no `element_type` typedef
 //    * A read-only `absl::Span<const T>` can be implicitly constructed from an
 //      initializer list.
 //    * `absl::Span` has no `bytes()`, `size_bytes()`, `as_bytes()`, or
@@ -170,6 +169,7 @@
       typename std::enable_if<!std::is_const<T>::value, U>::type;
 
  public:
+  using element_type = T;
   using value_type = absl::remove_cv_t<T>;
   using pointer = T*;
   using const_pointer = const T*;
@@ -243,8 +243,8 @@
   //
   template <typename LazyT = T,
             typename = EnableIfConstView<LazyT>>
-  Span(
-      std::initializer_list<value_type> v) noexcept  // NOLINT(runtime/explicit)
+  Span(std::initializer_list<value_type> v
+           ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept  // NOLINT(runtime/explicit)
       : Span(v.begin(), v.size()) {}
 
   // Accessors
diff --git a/absl/types/span_test.cc b/absl/types/span_test.cc
index 2584339..13264aa 100644
--- a/absl/types/span_test.cc
+++ b/absl/types/span_test.cc
@@ -661,6 +661,8 @@
   CheckType<absl::Span<int>::const_reverse_iterator>(slice.crend());
   testing::StaticAssertTypeEq<int, absl::Span<int>::value_type>();
   testing::StaticAssertTypeEq<int, absl::Span<const int>::value_type>();
+  testing::StaticAssertTypeEq<int, absl::Span<int>::element_type>();
+  testing::StaticAssertTypeEq<const int, absl::Span<const int>::element_type>();
   testing::StaticAssertTypeEq<int*, absl::Span<int>::pointer>();
   testing::StaticAssertTypeEq<const int*, absl::Span<const int>::pointer>();
   testing::StaticAssertTypeEq<int&, absl::Span<int>::reference>();
diff --git a/absl/utility/BUILD.bazel b/absl/utility/BUILD.bazel
index 02b2c40..ca4bc0a 100644
--- a/absl/utility/BUILD.bazel
+++ b/absl/utility/BUILD.bazel
@@ -14,7 +14,6 @@
 # limitations under the License.
 #
 
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
 load(
     "//absl:copts/configure_copts.bzl",
     "ABSL_DEFAULT_COPTS",
diff --git a/absl/utility/CMakeLists.txt b/absl/utility/CMakeLists.txt
index e1edd19..865b758 100644
--- a/absl/utility/CMakeLists.txt
+++ b/absl/utility/CMakeLists.txt
@@ -40,5 +40,5 @@
     absl::core_headers
     absl::memory
     absl::strings
-    gmock_main
+    GTest::gmock_main
 )
diff --git a/ci/cmake_common.sh b/ci/cmake_common.sh
index aec8a11..51f3106 100644
--- a/ci/cmake_common.sh
+++ b/ci/cmake_common.sh
@@ -14,7 +14,7 @@
 
 # The commit of GoogleTest to be used in the CMake tests in this directory.
 # Keep this in sync with the commit in the WORKSPACE file.
-readonly ABSL_GOOGLETEST_COMMIT="8567b09290fe402cf01923e2131c5635b8ed851b"
+readonly ABSL_GOOGLETEST_COMMIT="8d51ffdfab10b3fba636ae69bc03da4b54f8c235"
 
 # Avoid depending on GitHub by looking for a cached copy of the commit first.
 if [[ -r "${KOKORO_GFILE_DIR:-}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip" ]]; then
diff --git a/ci/cmake_install_test.sh b/ci/cmake_install_test.sh
index ffc6b51..97ed847 100755
--- a/ci/cmake_install_test.sh
+++ b/ci/cmake_install_test.sh
@@ -36,8 +36,11 @@
     --tmpfs=/abseil-cpp:exec \
     --workdir=/abseil-cpp \
     --cap-add=SYS_PTRACE \
+    -e "ABSL_GOOGLETEST_COMMIT=${ABSL_GOOGLETEST_COMMIT}" \
+    -e "ABSL_GOOGLETEST_DOWNLOAD_URL=${ABSL_GOOGLETEST_DOWNLOAD_URL}" \
     -e "LINK_TYPE=${link_type}" \
     --rm \
+    ${DOCKER_EXTRA_ARGS:-} \
     ${DOCKER_CONTAINER} \
     /bin/bash -c "cp -r /abseil-cpp-ro/* . && CMake/install_test_project/test.sh"
 done
diff --git a/ci/linux_clang-latest_libcxx_asan_bazel.sh b/ci/linux_clang-latest_libcxx_asan_bazel.sh
index ffbb832..5245933 100755
--- a/ci/linux_clang-latest_libcxx_asan_bazel.sh
+++ b/ci/linux_clang-latest_libcxx_asan_bazel.sh
@@ -83,6 +83,7 @@
           --copt="-fsanitize=undefined" \
           --copt="-fno-sanitize-blacklist" \
           --copt=-Werror \
+          --distdir="/bazel-distdir" \
           --keep_going \
           --linkopt="-fsanitize=address" \
           --linkopt="-fsanitize-link-c++-runtime" \
diff --git a/ci/linux_clang-latest_libcxx_bazel.sh b/ci/linux_clang-latest_libcxx_bazel.sh
index f6a2221..e0fe653 100755
--- a/ci/linux_clang-latest_libcxx_bazel.sh
+++ b/ci/linux_clang-latest_libcxx_bazel.sh
@@ -85,6 +85,7 @@
             --copt=\"${exceptions_mode}\" \
             --copt=-Werror \
             --define=\"absl=1\" \
+            --distdir=\"/bazel-distdir\" \
             --keep_going \
             --show_timestamps \
             --test_env=\"GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1\" \
diff --git a/ci/linux_clang-latest_libcxx_tsan_bazel.sh b/ci/linux_clang-latest_libcxx_tsan_bazel.sh
index e70e821..555f6b1 100755
--- a/ci/linux_clang-latest_libcxx_tsan_bazel.sh
+++ b/ci/linux_clang-latest_libcxx_tsan_bazel.sh
@@ -81,6 +81,7 @@
           --copt="-fsanitize=thread" \
           --copt="-fno-sanitize-blacklist" \
           --copt=-Werror \
+          --distdir="/bazel-distdir" \
           --keep_going \
           --linkopt="-fsanitize=thread" \
           --show_timestamps \
diff --git a/ci/linux_clang-latest_libstdcxx_bazel.sh b/ci/linux_clang-latest_libstdcxx_bazel.sh
index 0986ff4..36fdf82 100755
--- a/ci/linux_clang-latest_libstdcxx_bazel.sh
+++ b/ci/linux_clang-latest_libstdcxx_bazel.sh
@@ -25,7 +25,7 @@
 fi
 
 if [[ -z ${STD:-} ]]; then
-  STD="c++11 c++14 c++17 c++20"
+  STD="c++11 c++14 c++17"
 fi
 
 if [[ -z ${COMPILATION_MODE:-} ]]; then
@@ -78,6 +78,7 @@
           --copt="${exceptions_mode}" \
           --copt=-Werror \
           --define="absl=1" \
+          --distdir="/bazel-distdir" \
           --keep_going \
           --linkopt="--gcc-toolchain=/usr/local" \
           --show_timestamps \
diff --git a/ci/linux_docker_containers.sh b/ci/linux_docker_containers.sh
index 1c29d9a..32865b8 100644
--- a/ci/linux_docker_containers.sh
+++ b/ci/linux_docker_containers.sh
@@ -16,6 +16,6 @@
 # Test scripts should source this file to get the identifiers.
 
 readonly LINUX_ALPINE_CONTAINER="gcr.io/google.com/absl-177019/alpine:20201026"
-readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20201008"
-readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20201008"
-readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20201015"
+readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20210617"
+readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20210617"
+readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20210617"
diff --git a/ci/linux_gcc-floor_libstdcxx_bazel.sh b/ci/linux_gcc-floor_libstdcxx_bazel.sh
index 224aef8..54ab68a 100755
--- a/ci/linux_gcc-floor_libstdcxx_bazel.sh
+++ b/ci/linux_gcc-floor_libstdcxx_bazel.sh
@@ -77,6 +77,7 @@
           --copt="${exceptions_mode}" \
           --copt=-Werror \
           --define="absl=1" \
+          --distdir="/bazel-distdir" \
           --keep_going \
           --show_timestamps \
           --test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" \
diff --git a/ci/linux_gcc-latest_libstdcxx_bazel.sh b/ci/linux_gcc-latest_libstdcxx_bazel.sh
index 37d89d9..0555ece 100755
--- a/ci/linux_gcc-latest_libstdcxx_bazel.sh
+++ b/ci/linux_gcc-latest_libstdcxx_bazel.sh
@@ -83,6 +83,7 @@
             --copt=\"${exceptions_mode}\" \
             --copt=-Werror \
             --define=\"absl=1\" \
+            --distdir=\"/bazel-distdir\" \
             --keep_going \
             --show_timestamps \
             --test_env=\"GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1\" \
diff --git a/ci/macos_xcode_bazel.sh b/ci/macos_xcode_bazel.sh
index 738adf9..9e14e66 100755
--- a/ci/macos_xcode_bazel.sh
+++ b/ci/macos_xcode_bazel.sh
@@ -24,7 +24,7 @@
 fi
 
 # If we are running on Kokoro, check for a versioned Bazel binary.
-KOKORO_GFILE_BAZEL_BIN="bazel-2.0.0-darwin-x86_64"
+KOKORO_GFILE_BAZEL_BIN="bazel-3.7.0-darwin-x86_64"
 if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f ${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN} ]]; then
   BAZEL_BIN="${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN}"
   chmod +x ${BAZEL_BIN}
diff --git a/create_lts.py b/create_lts.py
index a98c76b..5617080 100755
--- a/create_lts.py
+++ b/create_lts.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright 2021 The Abseil Authors.
 #
@@ -96,6 +96,13 @@
 
   # Replacement directives go here.
   ReplaceStringsInFile(
+      'absl/base/config.h', {
+          '#undef ABSL_LTS_RELEASE_VERSION':
+              '#define ABSL_LTS_RELEASE_VERSION {}'.format(datestamp),
+          '#undef ABSL_LTS_RELEASE_PATCH_LEVEL':
+              '#define ABSL_LTS_RELEASE_PATCH_LEVEL 0'
+      })
+  ReplaceStringsInFile(
       'absl/base/options.h', {
           '#define ABSL_OPTION_USE_INLINE_NAMESPACE 0':
               '#define ABSL_OPTION_USE_INLINE_NAMESPACE 1',
@@ -108,11 +115,16 @@
           'project(absl LANGUAGES CXX)':
               'project(absl LANGUAGES CXX VERSION {})'.format(datestamp)
       })
-  # Set the SOVERSION to YYYYMMDD.0.0 - The first 0 means we only have
-  # ABI compatible changes, and the second 0 means we can increment it
-  # to mark changes as ABI-compatible, for patch releases.
-  ReplaceStringsInFile('CMake/AbseilHelpers.cmake',
-                       {'SOVERSION 0': 'SOVERSION "{}.0.0"'.format(datestamp)})
+  # Set the SOVERSION to YYMM.0.0 - The first 0 means we only have ABI
+  # compatible changes, and the second 0 means we can increment it to
+  # mark changes as ABI-compatible, for patch releases.  Note that we
+  # only use the last two digits of the year and the month because the
+  # MacOS linker requires the first part of the SOVERSION to fit into
+  # 16 bits.
+  # https://www.sicpers.info/2013/03/how-to-version-a-mach-o-library/
+  ReplaceStringsInFile(
+      'CMake/AbseilHelpers.cmake',
+      {'SOVERSION 0': 'SOVERSION "{}.0.0"'.format(datestamp[2:6])})
   StripContentBetweenTags('CMakeLists.txt', '# absl:lts-remove-begin',
                           '# absl:lts-remove-end')