Merge pull request #1290 from jun-sheaf:patch-1

PiperOrigin-RevId: 532199109
Change-Id: I5a2a96b9c8b52feb0107d912b0011911fc1f0f4c
diff --git a/.github/ISSUE_TEMPLATE/00-bug_report.md b/.github/ISSUE_TEMPLATE/00-bug_report.md
deleted file mode 100644
index 1edf3de..0000000
--- a/.github/ISSUE_TEMPLATE/00-bug_report.md
+++ /dev/null
@@ -1,41 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve
-title: ''
-labels: 'bug'
-assignees: ''
----
-
-**Describe the bug**
-
-Include a clear and concise description of what the problem is, including what
-you expected to happen, and what actually happened.
-
-**Steps to reproduce the bug**
-
-It's important that we are able to reproduce the problem that you are
-experiencing. Please provide all code and relevant steps to reproduce the
-problem, including your `BUILD`/`CMakeLists.txt` file and build commands. Links
-to a GitHub branch or [godbolt.org](https://godbolt.org/) that demonstrate the
-problem are also helpful.
-
-**What version of Abseil are you using?**
-
-**What operating system and version are you using**
-
-If you are using a Linux distribution please include the name and version of the
-distribution as well.
-
-**What compiler and version are you using?**
-
-Please include the output of `gcc -v` or `clang -v`, or the equivalent for your
-compiler.
-
-**What build system are you using?**
-
-Please include the output of `bazel --version` or `cmake --version`, or the
-equivalent for your build system.
-
-**Additional context**
-
-Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/00-bug_report.yml b/.github/ISSUE_TEMPLATE/00-bug_report.yml
new file mode 100644
index 0000000..32cebe1
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/00-bug_report.yml
@@ -0,0 +1,53 @@
+name: Bug Report
+description: Let us know that something does not work as expected.
+title: "[Bug]: Please title this bug report"
+body:
+  - type: textarea
+    id: what-happened
+    attributes:
+      label: Describe the issue
+      description: What happened, and what did you expect to happen?
+    validations:
+      required: true
+  - type: textarea
+    id: steps
+    attributes:
+      label: Steps to reproduce the problem
+      description: It is important that we are able to reproduce the problem that you are experiencing. Please provide all code and relevant steps to reproduce the problem, including your `BUILD`/`CMakeLists.txt` file and build commands. Links to a GitHub branch or [godbolt.org](https://godbolt.org/) that demonstrate the problem are also helpful.
+    validations:
+      required: true
+  - type: textarea
+    id: version
+    attributes:
+      label: What version of Abseil are you using?
+      description: Please include the output of `git rev-parse HEAD` or the name of the LTS release that you are using.
+    validations:
+      required: true
+  - type: textarea
+    id: os
+    attributes:
+      label: What operating system and version are you using?
+      description: If you are using a Linux distribution please include the name and version of the distribution as well.
+    validations:
+      required: true
+  - type: textarea
+    id: compiler
+    attributes:
+      label: What compiler and version are you using?
+      description: Please include the output of `gcc -v` or `clang -v`, or the equivalent for your compiler.
+    validations:
+      required: true
+  - type: textarea
+    id: buildsystem
+    attributes:
+      label: What build system are you using?
+      description: Please include the output of `bazel --version` or `cmake --version`, or the equivalent for your build system.
+    validations:
+      required: true
+  - type: textarea
+    id: additional
+    attributes:
+      label: Additional context
+      description: Add any other context about the problem here.
+    validations:
+      required: false
diff --git a/.github/ISSUE_TEMPLATE/90-question.md b/.github/ISSUE_TEMPLATE/90-question.md
deleted file mode 100644
index 84cf349..0000000
--- a/.github/ISSUE_TEMPLATE/90-question.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-name: Question
-about: Have a question? Ask us anything! :-)
-title: ''
-labels: 'question'
-assignees: ''
----
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 0086358..c690fd9 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1 +1,5 @@
-blank_issues_enabled: true
+blank_issues_enabled: false
+contact_links:
+    - name: Question
+      url: https://github.com/abseil/abseil-cpp/discussions
+      about: Have a question? Ask us anything! :-)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..ff55e35
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,8 @@
+Thank you for your contribution to Abseil!
+
+Before submitting this PR, please be sure to read our [contributing
+guidelines](https://github.com/abseil/abseil-cpp/blob/master/CONTRIBUTING.md).
+
+If you are a Googler, please also note that it is required that you send us a
+Piper CL instead of using the GitHub pull-request process. The code propagation
+process will deliver the change to GitHub.
diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake
index d8ddcb3..8e6e21d 100644
--- a/CMake/AbseilDll.cmake
+++ b/CMake/AbseilDll.cmake
@@ -28,6 +28,7 @@
   "base/internal/low_level_scheduling.h"
   "base/internal/per_thread_tls.h"
   "base/internal/prefetch.h"
+  "base/prefetch.h"
   "base/internal/pretty_function.h"
   "base/internal/raw_logging.cc"
   "base/internal/raw_logging.h"
@@ -91,6 +92,26 @@
   "container/internal/tracked.h"
   "container/node_hash_map.h"
   "container/node_hash_set.h"
+  "crc/crc32c.cc"
+  "crc/crc32c.h"
+  "crc/internal/cpu_detect.cc"
+  "crc/internal/cpu_detect.h"
+  "crc/internal/crc32c.h"
+  "crc/internal/crc32c_inline.h"
+  "crc/internal/crc32_x86_arm_combined_simd.h"
+  "crc/internal/crc.cc"
+  "crc/internal/crc.h"
+  "crc/internal/crc_cord_state.cc"
+  "crc/internal/crc_cord_state.h"
+  "crc/internal/crc_internal.h"
+  "crc/internal/crc_x86_arm_combined.cc"
+  "crc/internal/crc_memcpy_fallback.cc"
+  "crc/internal/crc_memcpy.h"
+  "crc/internal/crc_memcpy_x86_64.cc"
+  "crc/internal/crc_non_temporal_memcpy.cc"
+  "crc/internal/crc_x86_arm_combined.cc"
+  "crc/internal/non_temporal_arm_intrinsics.h"
+  "crc/internal/non_temporal_memcpy.h"
   "debugging/failure_signal_handler.cc"
   "debugging/failure_signal_handler.h"
   "debugging/leak_check.h"
@@ -126,6 +147,47 @@
   "hash/internal/spy_hash_state.h"
   "hash/internal/low_level_hash.h"
   "hash/internal/low_level_hash.cc"
+  "log/absl_check.h"
+  "log/absl_log.h"
+  "log/check.h"
+  "log/die_if_null.cc"
+  "log/die_if_null.h"
+  "log/globals.cc"
+  "log/globals.h"
+  "log/internal/append_truncated.h"
+  "log/internal/check_impl.h"
+  "log/internal/check_op.cc"
+  "log/internal/check_op.h"
+  "log/internal/conditions.cc"
+  "log/internal/conditions.h"
+  "log/internal/config.h"
+  "log/internal/globals.cc"
+  "log/internal/globals.h"
+  "log/internal/log_format.cc"
+  "log/internal/log_format.h"
+  "log/internal/log_impl.h"
+  "log/internal/log_message.cc"
+  "log/internal/log_message.h"
+  "log/internal/log_sink_set.cc"
+  "log/internal/log_sink_set.h"
+  "log/internal/nullguard.cc"
+  "log/internal/nullguard.h"
+  "log/internal/nullstream.h"
+  "log/internal/proto.h"
+  "log/internal/proto.cc"
+  "log/internal/strip.h"
+  "log/internal/structured.h"
+  "log/internal/voidify.h"
+  "log/initialize.cc"
+  "log/initialize.h"
+  "log/log.h"
+  "log/log_entry.cc"
+  "log/log_entry.h"
+  "log/log_sink.cc"
+  "log/log_sink.h"
+  "log/log_sink_registry.h"
+  "log/log_streamer.h"
+  "log/structured.h"
   "memory/memory.h"
   "meta/type_traits.h"
   "numeric/bits.h"
@@ -152,7 +214,6 @@
   "random/internal/fast_uniform_bits.h"
   "random/internal/generate_real.h"
   "random/internal/iostream_state_saver.h"
-  "random/internal/mock_helpers.h"
   "random/internal/nonsecure_base.h"
   "random/internal/pcg_engine.h"
   "random/internal/platform.h"
@@ -238,8 +299,13 @@
   "strings/internal/cordz_statistics.h"
   "strings/internal/cordz_update_scope.h"
   "strings/internal/cordz_update_tracker.h"
+  "strings/internal/damerau_levenshtein_distance.h"
+  "strings/internal/damerau_levenshtein_distance.cc"
   "strings/internal/stl_type_traits.h"
   "strings/internal/string_constant.h"
+  "strings/internal/stringify_sink.h"
+  "strings/internal/stringify_sink.cc"
+  "strings/internal/has_absl_stringify.h"
   "strings/match.cc"
   "strings/match.h"
   "strings/numbers.cc"
@@ -272,6 +338,7 @@
   "strings/internal/str_format/bind.cc"
   "strings/internal/str_format/bind.h"
   "strings/internal/str_format/checker.h"
+  "strings/internal/str_format/constexpr_parser.h"
   "strings/internal/str_format/extension.cc"
   "strings/internal/str_format/extension.h"
   "strings/internal/str_format/float_conversion.cc"
@@ -295,14 +362,26 @@
   "synchronization/internal/create_thread_identity.cc"
   "synchronization/internal/create_thread_identity.h"
   "synchronization/internal/futex.h"
+  "synchronization/internal/futex_waiter.h"
+  "synchronization/internal/futex_waiter.cc"
   "synchronization/internal/graphcycles.cc"
   "synchronization/internal/graphcycles.h"
   "synchronization/internal/kernel_timeout.h"
+  "synchronization/internal/kernel_timeout.cc"
   "synchronization/internal/per_thread_sem.cc"
   "synchronization/internal/per_thread_sem.h"
+  "synchronization/internal/pthread_waiter.h"
+  "synchronization/internal/pthread_waiter.cc"
+  "synchronization/internal/sem_waiter.h"
+  "synchronization/internal/sem_waiter.cc"
+  "synchronization/internal/stdcpp_waiter.h"
+  "synchronization/internal/stdcpp_waiter.cc"
   "synchronization/internal/thread_pool.h"
-  "synchronization/internal/waiter.cc"
   "synchronization/internal/waiter.h"
+  "synchronization/internal/waiter_base.h"
+  "synchronization/internal/waiter_base.cc"
+  "synchronization/internal/win32_waiter.h"
+  "synchronization/internal/win32_waiter.cc"
   "time/civil_time.cc"
   "time/civil_time.h"
   "time/clock.cc"
@@ -351,11 +430,14 @@
   "types/span.h"
   "types/internal/span.h"
   "types/variant.h"
+  "utility/internal/if_constexpr.h"
   "utility/utility.h"
   "debugging/leak_check.cc"
 )
 
 set(ABSL_INTERNAL_DLL_TARGETS
+  "absl_check"
+  "absl_log"
   "algorithm"
   "algorithm_container"
   "any"
@@ -370,6 +452,7 @@
   "bind_front"
   "bits"
   "btree"
+  "check"
   "city"
   "civil_time"
   "compare"
@@ -379,11 +462,21 @@
   "container_common"
   "container_memory"
   "cord"
+  "cord_internal"
+  "cordz_functions"
+  "cordz_handle"
+  "cordz_info"
+  "cordz_sample_token"
   "core_headers"
   "counting_allocator"
+  "crc_cord_state"
+  "crc_cpu_detect"
+  "crc_internal"
+  "crc32c"
   "debugging"
   "debugging_internal"
   "demangle_internal"
+  "die_if_null"
   "dynamic_annotations"
   "endian"
   "examine_stack"
@@ -406,13 +499,40 @@
   "kernel_timeout_internal"
   "layout"
   "leak_check"
+  "log_internal_check_impl"
+  "log_internal_check_op"
+  "log_internal_conditions"
+  "log_internal_config"
+  "log_internal_format"
+  "log_internal_globals"
+  "log_internal_log_impl"
+  "log_internal_proto"
+  "log_internal_message"
+  "log_internal_log_sink_set"
+  "log_internal_nullguard"
+  "log_internal_nullstream"
+  "log_internal_strip"
+  "log_internal_voidify"
+  "log_internal_append_truncated"
+  "log_globals"
+  "log_initialize"
+  "log"
+  "log_entry"
+  "log_sink"
+  "log_sink_registry"
+  "log_streamer"
+  "log_internal_structured"
   "log_severity"
+  "log_structured"
+  "low_level_hash"
   "malloc_internal"
   "memory"
   "meta"
   "node_hash_map"
   "node_hash_set"
   "node_slot_policy"
+  "non_temporal_arm_intrinsics"
+  "non_temporal_memcpy"
   "numeric"
   "optional"
   "periodic_sampler"
@@ -456,8 +576,10 @@
   "stack_consumption"
   "stacktrace"
   "status"
+  "statusor"
   "str_format"
   "str_format_internal"
+  "strerror"
   "strings"
   "strings_internal"
   "symbolize"
@@ -472,6 +594,53 @@
   "variant"
 )
 
+set(ABSL_INTERNAL_TEST_DLL_FILES
+  "hash/hash_testing.h"
+  "log/scoped_mock_log.cc"
+  "log/scoped_mock_log.h"
+  "random/internal/chi_square.cc"
+  "random/internal/chi_square.h"
+  "random/internal/distribution_test_util.cc"
+  "random/internal/distribution_test_util.h"
+  "random/internal/mock_helpers.h"
+  "random/internal/mock_overload_set.h"
+  "random/mocking_bit_gen.h"
+  "random/mock_distributions.h"
+  "strings/cordz_test_helpers.h"
+  "strings/cord_test_helpers.h"
+)
+
+set(ABSL_INTERNAL_TEST_DLL_TARGETS
+  "cord_test_helpers"
+  "cordz_test_helpers"
+  "hash_testing"
+  "random_mocking_bit_gen"
+  "random_internal_distribution_test_util"
+  "random_internal_mock_overload_set"
+  "scoped_mock_log"
+)
+
+include(CheckCXXSourceCompiles)
+
+check_cxx_source_compiles(
+  [==[
+#ifdef _MSC_VER
+#  if _MSVC_LANG < 201700L
+#    error "The compiler defaults or is configured for C++ < 17"
+#  endif
+#elif __cplusplus < 201700L
+#  error "The compiler defaults or is configured for C++ < 17"
+#endif
+int main() { return 0; }
+]==]
+  ABSL_INTERNAL_AT_LEAST_CXX17)
+
+if(ABSL_INTERNAL_AT_LEAST_CXX17)
+  set(ABSL_INTERNAL_CXX_STD_FEATURE cxx_std_17)
+else()
+  set(ABSL_INTERNAL_CXX_STD_FEATURE cxx_std_14)
+endif()
+
 function(absl_internal_dll_contains)
   cmake_parse_arguments(ABSL_INTERNAL_DLL
     ""
@@ -494,6 +663,28 @@
   endif()
 endfunction()
 
+function(absl_internal_test_dll_contains)
+  cmake_parse_arguments(ABSL_INTERNAL_TEST_DLL
+    ""
+    "OUTPUT;TARGET"
+    ""
+    ${ARGN}
+  )
+
+  STRING(REGEX REPLACE "^absl::" "" _target ${ABSL_INTERNAL_TEST_DLL_TARGET})
+
+  list(FIND
+    ABSL_INTERNAL_TEST_DLL_TARGETS
+    "${_target}"
+    _index)
+
+  if (${_index} GREATER -1)
+    set(${ABSL_INTERNAL_TEST_DLL_OUTPUT} 1 PARENT_SCOPE)
+  else()
+    set(${ABSL_INTERNAL_TEST_DLL_OUTPUT} 0 PARENT_SCOPE)
+  endif()
+endfunction()
+
 function(absl_internal_dll_targets)
   cmake_parse_arguments(ABSL_INTERNAL_DLL
   ""
@@ -504,9 +695,12 @@
 
   set(_deps "")
   foreach(dep IN LISTS ABSL_INTERNAL_DLL_DEPS)
-    absl_internal_dll_contains(TARGET ${dep} OUTPUT _contains)
-    if (_contains)
+    absl_internal_dll_contains(TARGET ${dep} OUTPUT _dll_contains)
+    absl_internal_test_dll_contains(TARGET ${dep} OUTPUT _test_dll_contains)
+    if (_dll_contains)
       list(APPEND _deps abseil_dll)
+    elseif (_test_dll_contains)
+      list(APPEND _deps abseil_test_dll)
     else()
       list(APPEND _deps ${dep})
     endif()
@@ -518,40 +712,105 @@
 endfunction()
 
 function(absl_make_dll)
+  cmake_parse_arguments(ABSL_INTERNAL_MAKE_DLL
+  ""
+  "TEST"
+  ""
+  ${ARGN}
+  )
+
+  if (ABSL_INTERNAL_MAKE_DLL_TEST)
+    set(_dll "abseil_test_dll")
+    set(_dll_files ${ABSL_INTERNAL_TEST_DLL_FILES})
+    set(_dll_libs "abseil_dll" "GTest::gtest" "GTest::gmock")
+    set(_dll_compile_definitions "GTEST_LINKED_AS_SHARED_LIBRARY=1")
+    set(_dll_includes ${absl_gtest_src_dir}/googletest/include ${absl_gtest_src_dir}/googlemock/include)
+    set(_dll_consume "ABSL_CONSUME_TEST_DLL")
+    set(_dll_build "ABSL_BUILD_TEST_DLL")
+  else()
+    set(_dll "abseil_dll")
+    set(_dll_files ${ABSL_INTERNAL_DLL_FILES})
+    set(_dll_libs "")
+    set(_dll_compile_definitions "")
+    set(_dll_includes "")
+    set(_dll_consume "ABSL_CONSUME_DLL")
+    set(_dll_build "ABSL_BUILD_DLL")
+  endif()
+
   add_library(
-    abseil_dll
+    ${_dll}
     SHARED
-      "${ABSL_INTERNAL_DLL_FILES}"
+      ${_dll_files}
   )
   target_link_libraries(
-    abseil_dll
+    ${_dll}
     PRIVATE
+      ${_dll_libs}
       ${ABSL_DEFAULT_LINKOPTS}
   )
-  set_property(TARGET abseil_dll PROPERTY LINKER_LANGUAGE "CXX")
+  set_property(TARGET ${_dll} PROPERTY LINKER_LANGUAGE "CXX")
   target_include_directories(
-    abseil_dll
+    ${_dll}
     PUBLIC
       "$<BUILD_INTERFACE:${ABSL_COMMON_INCLUDE_DIRS}>"
       $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+    PRIVATE
+      ${_dll_includes}
   )
 
   target_compile_options(
-    abseil_dll
+    ${_dll}
     PRIVATE
       ${ABSL_DEFAULT_COPTS}
   )
 
+  foreach(cflag ${ABSL_CC_LIB_COPTS})
+    if(${cflag} MATCHES "^(-Wno|/wd)")
+      # These flags are needed to suppress warnings that might fire in our headers.
+      set(PC_CFLAGS "${PC_CFLAGS} ${cflag}")
+    elseif(${cflag} MATCHES "^(-W|/w[1234eo])")
+      # Don't impose our warnings on others.
+    else()
+      set(PC_CFLAGS "${PC_CFLAGS} ${cflag}")
+    endif()
+  endforeach()
+  string(REPLACE ";" " " PC_LINKOPTS "${ABSL_CC_LIB_LINKOPTS}")
+
+  FILE(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/lib/pkgconfig/${_dll}.pc" CONTENT "\
+prefix=${CMAKE_INSTALL_PREFIX}\n\
+exec_prefix=\${prefix}\n\
+libdir=${CMAKE_INSTALL_FULL_LIBDIR}\n\
+includedir=${CMAKE_INSTALL_FULL_INCLUDEDIR}\n\
+\n\
+Name: ${_dll}\n\
+Description: Abseil DLL library\n\
+URL: https://abseil.io/\n\
+Version: ${absl_VERSION}\n\
+Libs: -L\${libdir} ${PC_LINKOPTS} $<$<NOT:$<BOOL:${ABSL_CC_LIB_IS_INTERFACE}>>:-l${_dll}>\n\
+Cflags: -I\${includedir}${PC_CFLAGS}\n")
+  INSTALL(FILES "${CMAKE_BINARY_DIR}/lib/pkgconfig/${_dll}.pc"
+    DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+
   target_compile_definitions(
-    abseil_dll
+    ${_dll}
+    PUBLIC
+      ${_dll_compile_definitions}
     PRIVATE
-      ABSL_BUILD_DLL
+      ${_dll_build}
       NOMINMAX
     INTERFACE
       ${ABSL_CC_LIB_DEFINES}
-      ABSL_CONSUME_DLL
+      ${_dll_consume}
   )
-  install(TARGETS abseil_dll EXPORT ${PROJECT_NAME}Targets
+
+  if(ABSL_PROPAGATE_CXX_STD)
+    # Abseil libraries require C++14 as the current minimum standard. When
+    # compiled with C++17 (either because it is the compiler's default or
+    # explicitly requested), then Abseil requires C++17.
+    target_compile_features(${_dll} PUBLIC ${ABSL_INTERNAL_CXX_STD_FEATURE})
+  endif()
+
+  install(TARGETS ${_dll} EXPORT ${PROJECT_NAME}Targets
         RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
         LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
         ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake
index e1196fd..de63531 100644
--- a/CMake/AbseilHelpers.cmake
+++ b/CMake/AbseilHelpers.cmake
@@ -32,35 +32,6 @@
   set(ABSL_INTERNAL_INCLUDE_WARNING_GUARD "")
 endif()
 
-function(_target_compile_features_if_available TARGET TYPE FEATURE)
-  if(FEATURE IN_LIST CMAKE_CXX_COMPILE_FEATURES)
-    target_compile_features(${TARGET} ${TYPE} ${FEATURE})
-  else()
-    message(WARNING "Feature ${FEATURE} is unknown for the CXX compiler")
-  endif()
-endfunction()
-
-include(CheckCXXSourceCompiles)
-
-check_cxx_source_compiles(
-  [==[
-#ifdef _MSC_VER
-#  if _MSVC_LANG < 201700L
-#    error "The compiler defaults or is configured for C++ < 17"
-#  endif
-#elif __cplusplus < 201700L
-#  error "The compiler defaults or is configured for C++ < 17"
-#endif
-int main() { return 0; }
-]==]
-  ABSL_INTERNAL_AT_LEAST_CXX17)
-
-if(ABSL_INTERNAL_AT_LEAST_CXX17)
-  set(ABSL_INTERNAL_CXX_STD_FEATURE cxx_std_17)
-else()
-  set(ABSL_INTERNAL_CXX_STD_FEATURE cxx_std_14)
-endif()
-
 # absl_cc_library()
 #
 # CMake function to imitate Bazel's cc_library rule.
@@ -161,10 +132,12 @@
   if (${ABSL_BUILD_DLL})
     if(ABSL_ENABLE_INSTALL)
       absl_internal_dll_contains(TARGET ${_NAME} OUTPUT _in_dll)
+      absl_internal_test_dll_contains(TARGET ${_NAME} OUTPUT _in_test_dll)
     else()
       absl_internal_dll_contains(TARGET ${ABSL_CC_LIB_NAME} OUTPUT _in_dll)
+      absl_internal_test_dll_contains(TARGET ${ABSL_CC_LIB_NAME} OUTPUT _in_test_dll)
     endif()
-    if (${_in_dll})
+    if (${_in_dll} OR ${_in_test_dll})
       # This target should be replaced by the DLL
       set(_build_type "dll")
       set(ABSL_CC_LIB_IS_INTERFACE 1)
@@ -179,38 +152,55 @@
   endif()
 
   # Generate a pkg-config file for every library:
-  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}")
-      else()
-        set(PC_VERSION "head")
-      endif()
-      foreach(dep ${ABSL_CC_LIB_DEPS})
-        if(${dep} MATCHES "^absl::(.*)")
-	  # Join deps with commas.
+  if(ABSL_ENABLE_INSTALL)
+    if(absl_VERSION)
+      set(PC_VERSION "${absl_VERSION}")
+    else()
+      set(PC_VERSION "head")
+    endif()
+    if(NOT _build_type STREQUAL "dll")
+      set(LNK_LIB "${LNK_LIB} -labsl_${_NAME}")
+    endif()
+    foreach(dep ${ABSL_CC_LIB_DEPS})
+      if(${dep} MATCHES "^absl::(.*)")
+        # for DLL builds many libs are not created, but add
+        # the pkgconfigs nevertheless, pointing to the dll.
+        if(_build_type STREQUAL "dll")
+          # hide this MATCHES in an if-clause so it doesn't overwrite
+          # the CMAKE_MATCH_1 from (${dep} MATCHES "^absl::(.*)")
+          if(NOT PC_DEPS MATCHES "abseil_dll")
+            # Join deps with commas.
+            if(PC_DEPS)
+              set(PC_DEPS "${PC_DEPS},")
+            endif()
+            # don't duplicate dll-dep if it exists already
+            set(PC_DEPS "${PC_DEPS} abseil_dll = ${PC_VERSION}")
+            set(LNK_LIB "${LNK_LIB} -labseil_dll")
+          endif()
+        else()
+          # Join deps with commas.
           if(PC_DEPS)
             set(PC_DEPS "${PC_DEPS},")
           endif()
           set(PC_DEPS "${PC_DEPS} absl_${CMAKE_MATCH_1} = ${PC_VERSION}")
         endif()
-      endforeach()
-      foreach(cflag ${ABSL_CC_LIB_COPTS})
-        if(${cflag} MATCHES "^(-Wno|/wd)")
-          # These flags are needed to suppress warnings that might fire in our headers.
-          set(PC_CFLAGS "${PC_CFLAGS} ${cflag}")
-        elseif(${cflag} MATCHES "^(-W|/w[1234eo])")
-          # Don't impose our warnings on others.
-        elseif(${cflag} MATCHES "^-m")
-          # Don't impose CPU instruction requirements on others, as
-          # the code performs feature detection on runtime.
-        else()
-          set(PC_CFLAGS "${PC_CFLAGS} ${cflag}")
-        endif()
-      endforeach()
-      string(REPLACE ";" " " PC_LINKOPTS "${ABSL_CC_LIB_LINKOPTS}")
-      FILE(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" CONTENT "\
+      endif()
+    endforeach()
+    foreach(cflag ${ABSL_CC_LIB_COPTS})
+      if(${cflag} MATCHES "^(-Wno|/wd)")
+        # These flags are needed to suppress warnings that might fire in our headers.
+        set(PC_CFLAGS "${PC_CFLAGS} ${cflag}")
+      elseif(${cflag} MATCHES "^(-W|/w[1234eo])")
+        # Don't impose our warnings on others.
+      elseif(${cflag} MATCHES "^-m")
+        # Don't impose CPU instruction requirements on others, as
+        # the code performs feature detection on runtime.
+      else()
+        set(PC_CFLAGS "${PC_CFLAGS} ${cflag}")
+      endif()
+    endforeach()
+    string(REPLACE ";" " " PC_LINKOPTS "${ABSL_CC_LIB_LINKOPTS}")
+    FILE(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" CONTENT "\
 prefix=${CMAKE_INSTALL_PREFIX}\n\
 exec_prefix=\${prefix}\n\
 libdir=${CMAKE_INSTALL_FULL_LIBDIR}\n\
@@ -221,11 +211,10 @@
 URL: https://abseil.io/\n\
 Version: ${PC_VERSION}\n\
 Requires:${PC_DEPS}\n\
-Libs: -L\${libdir} ${PC_LINKOPTS} $<$<NOT:$<BOOL:${ABSL_CC_LIB_IS_INTERFACE}>>:-labsl_${_NAME}>\n\
+Libs: -L\${libdir} ${PC_LINKOPTS} $<$<NOT:$<BOOL:${ABSL_CC_LIB_IS_INTERFACE}>>:${LNK_LIB}>\n\
 Cflags: -I\${includedir}${PC_CFLAGS}\n")
-      INSTALL(FILES "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc"
-              DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
-    endif()
+    INSTALL(FILES "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc"
+            DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
   endif()
 
   if(NOT ABSL_CC_LIB_IS_INTERFACE)
@@ -297,21 +286,10 @@
     endif()
 
     if(ABSL_PROPAGATE_CXX_STD)
-      # Abseil libraries require C++14 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_if_available(${_NAME} PUBLIC ${ABSL_INTERNAL_CXX_STD_FEATURE})
-    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)
+      # Abseil libraries require C++14 as the current minimum standard. When
+      # compiled with C++17 (either because it is the compiler's default or
+      # explicitly requested), then Abseil requires C++17.
+      target_compile_features(${_NAME} PUBLIC ${ABSL_INTERNAL_CXX_STD_FEATURE})
     endif()
 
     # When being installed, we lose the absl_ prefix.  We want to put it back
@@ -348,16 +326,11 @@
       # Abseil libraries require C++14 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_if_available(${_NAME} INTERFACE ${ABSL_INTERNAL_CXX_STD_FEATURE})
-
-      # (INTERFACE libraries can't have the CXX_STANDARD property set, so there
-      # is no legacy behavior else case).
+      target_compile_features(${_NAME} INTERFACE ${ABSL_INTERNAL_CXX_STD_FEATURE})
     endif()
   endif()
 
-  # TODO currently we don't install googletest alongside abseil sources, so
-  # installed abseil can't be tested.
-  if(NOT ABSL_CC_LIB_TESTONLY AND ABSL_ENABLE_INSTALL)
+  if(ABSL_ENABLE_INSTALL)
     install(TARGETS ${_NAME} EXPORT ${PROJECT_NAME}Targets
           RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
           LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
@@ -423,7 +396,7 @@
   target_sources(${_NAME} PRIVATE ${ABSL_CC_TEST_SRCS})
   target_include_directories(${_NAME}
     PUBLIC ${ABSL_COMMON_INCLUDE_DIRS}
-    PRIVATE ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS}
+    PRIVATE ${absl_gtest_src_dir}/googletest/include ${absl_gtest_src_dir}/googlemock/include
   )
 
   if (${ABSL_BUILD_DLL})
@@ -431,6 +404,7 @@
       PUBLIC
         ${ABSL_CC_TEST_DEFINES}
         ABSL_CONSUME_DLL
+        ABSL_CONSUME_TEST_DLL
         GTEST_LINKED_AS_SHARED_LIBRARY=1
     )
 
@@ -439,6 +413,10 @@
       DEPS ${ABSL_CC_TEST_DEPS}
       OUTPUT ABSL_CC_TEST_DEPS
     )
+    absl_internal_dll_targets(
+      DEPS ${ABSL_CC_TEST_LINKOPTS}
+      OUTPUT ABSL_CC_TEST_LINKOPTS
+    )
   else()
     target_compile_definitions(${_NAME}
       PUBLIC
@@ -460,27 +438,8 @@
     # Abseil libraries require C++14 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_if_available(${_NAME} PUBLIC ${ABSL_INTERNAL_CXX_STD_FEATURE})
-  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)
+    target_compile_features(${_NAME} PUBLIC ${ABSL_INTERNAL_CXX_STD_FEATURE})
   endif()
 
   add_test(NAME ${_NAME} COMMAND ${_NAME})
 endfunction()
-
-
-function(check_target my_target)
-  if(NOT TARGET ${my_target})
-    message(FATAL_ERROR " ABSL: compiling absl requires a ${my_target} CMake target in your project,
-                   see CMake/README.md for more details")
-  endif(NOT TARGET ${my_target})
-endfunction()
diff --git a/CMake/README.md b/CMake/README.md
index 19fb327..c7ddee6 100644
--- a/CMake/README.md
+++ b/CMake/README.md
@@ -170,7 +170,7 @@
 cmake --build /temporary/build/abseil-cpp --target install
 ```
 
-# CMake Option Synposis
+# CMake Option Synopsis
 
 ## Enable Standard CMake Installation
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3b67d8f..ae79981 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -68,7 +68,7 @@
 option(ABSL_PROPAGATE_CXX_STD
   "Use CMake C++ standard meta features (e.g. cxx_std_14) 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))
+if(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()
 
@@ -140,7 +140,6 @@
   )
 
 if((BUILD_TESTING AND ABSL_BUILD_TESTING) OR ABSL_BUILD_TEST_HELPERS)
-  ## check targets
   if (ABSL_USE_EXTERNAL_GOOGLETEST)
     if (ABSL_FIND_GOOGLETEST)
       find_package(GTest REQUIRED)
@@ -172,11 +171,6 @@
     endif()
     include(CMake/Googletest/DownloadGTest.cmake)
   endif()
-
-  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/CONTRIBUTING.md b/CONTRIBUTING.md
index 9dadae9..a87254c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -75,9 +75,9 @@
 
 ## Guidelines for Pull Requests
 
-*   If you are a Googler, it is preferable to first create an internal CL and
-    have it reviewed and submitted. The code propagation process will deliver
-    the change to GitHub.
+*   If you are a Googler, it is required that you send us a Piper CL instead of
+    using the GitHub pull-request process. The code propagation process will
+    deliver the change to GitHub.
 
 *   Create **small PRs** that are narrowly focused on **addressing a single
     concern**. We often receive PRs that are trying to fix several things at a
diff --git a/README.md b/README.md
index 36e9b7c..f834fcd 100644
--- a/README.md
+++ b/README.md
@@ -80,6 +80,8 @@
 * [`container`](absl/container/)
   <br /> The `container` library contains additional STL-style containers,
   including Abseil's unordered "Swiss table" containers.
+* [`crc`](absl/crc/) The `crc` library contains code for
+  computing error-detecting cyclic redundancy checks on data.
 * [`debugging`](absl/debugging/)
   <br /> The `debugging` library contains code useful for enabling leak
   checks, and stacktrace and symbolization utilities.
@@ -89,9 +91,6 @@
 * [`hash`](absl/hash/)
   <br /> The `hash` library contains the hashing framework and default hash
   functor implementations for hashable types in Abseil.
-* [`iterator`](absl/iterator/)
-  <br /> The `iterator` library contains utilities for augmenting ranges in
-  range-based for loops.
 * [`log`](absl/log/)
   <br /> The `log` library contains `LOG` and `CHECK` macros and facilities
   for writing logged messages out to disk, `stderr`, or user-extensible
diff --git a/UPGRADES.md b/UPGRADES.md
index 35599d0..3cac141 100644
--- a/UPGRADES.md
+++ b/UPGRADES.md
@@ -1,6 +1,6 @@
 # C++ Upgrade Tools
 
-Abseil may occassionally release API-breaking changes. As noted in our
+Abseil may occasionally release API-breaking changes. As noted in our
 [Compatibility Guidelines][compatibility-guide], we will aim to provide a tool
 to do the work of effecting such API-breaking changes, when absolutely
 necessary.
diff --git a/WORKSPACE b/WORKSPACE
index 072fa93..ce8e859 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -20,11 +20,11 @@
 
 # GoogleTest/GoogleMock framework. Used by most unit-tests.
 http_archive(
-    name = "com_google_googletest",  # 2022-06-16T20:18:32Z
-    sha256 = "a1d3123179024258f9c399d45da3e0b09c4aaf8d2c041466ce5b4793a8929f23",
-    strip_prefix = "googletest-86add13493e5c881d7e4ba77fb91c1f57752b3a4",
+    name = "com_google_googletest",  # 2023-02-28T13:15:29Z
+    sha256 = "82ad62a4e26c199de52a707778334e80f6b195dd298d48d520d8507d2bcb88c4",
+    strip_prefix = "googletest-2d4f208765af7fa376b878860a7677ecc0bc390a",
     # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh.
-    urls = ["https://github.com/google/googletest/archive/86add13493e5c881d7e4ba77fb91c1f57752b3a4.zip"],
+    urls = ["https://github.com/google/googletest/archive/2d4f208765af7fa376b878860a7677ecc0bc390a.zip"],
 )
 
 # RE2 (the regular expression library used by GoogleTest)
@@ -39,23 +39,24 @@
 
 # Google benchmark.
 http_archive(
-    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"],
+    name = "com_github_google_benchmark",  # 2023-01-10T16:48:17Z
+    sha256 = "ede6830512f21490eeea1f238f083702eb178890820c14451c1c3d69fd375b19",
+    strip_prefix = "benchmark-a3235d7b69c84e8c9ff8722a22b8ac5e1bc716a6",
+    urls = ["https://github.com/google/benchmark/archive/a3235d7b69c84e8c9ff8722a22b8ac5e1bc716a6.zip"],
 )
 
 # Bazel Skylib.
 http_archive(
-    name = "bazel_skylib",
-    urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz"],
-    sha256 = "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728",
+    name = "bazel_skylib",  # 2022-11-16T18:29:32Z
+    sha256 = "a22290c26d29d3ecca286466f7f295ac6cbe32c0a9da3a91176a90e0725e3649",
+    strip_prefix = "bazel-skylib-5bfcb1a684550626ce138fe0fe8f5f702b3764c3",
+    urls = ["https://github.com/bazelbuild/bazel-skylib/archive/5bfcb1a684550626ce138fe0fe8f5f702b3764c3.zip"],
 )
 
 # Bazel platform rules.
 http_archive(
-    name = "platforms",
-    sha256 = "a879ea428c6d56ab0ec18224f976515948822451473a80d06c2e50af0bbe5121",
-    strip_prefix = "platforms-da5541f26b7de1dc8e04c075c99df5351742a4a2",
-    urls = ["https://github.com/bazelbuild/platforms/archive/da5541f26b7de1dc8e04c075c99df5351742a4a2.zip"],  # 2022-05-27
+    name = "platforms",  # 2022-11-09T19:18:22Z
+    sha256 = "b4a3b45dc4202e2b3e34e3bc49d2b5b37295fc23ea58d88fb9e01f3642ad9b55",
+    strip_prefix = "platforms-3fbc687756043fb58a407c2ea8c944bc2fe1d922",
+    urls = ["https://github.com/bazelbuild/platforms/archive/3fbc687756043fb58a407c2ea8c944bc2fe1d922.zip"],
 )
diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel
index 29963cc..b2300ba 100644
--- a/absl/BUILD.bazel
+++ b/absl/BUILD.bazel
@@ -36,6 +36,22 @@
 )
 
 config_setting(
+    name = "mingw_unspecified_compiler",
+    flag_values = {
+        "@bazel_tools//tools/cpp:compiler": "mingw",
+    },
+    visibility = [":__subpackages__"],
+)
+
+config_setting(
+    name = "mingw-gcc_compiler",
+    flag_values = {
+        "@bazel_tools//tools/cpp:compiler": "mingw-gcc",
+    },
+    visibility = [":__subpackages__"],
+)
+
+config_setting(
     name = "msvc_compiler",
     flag_values = {
         "@bazel_tools//tools/cpp:compiler": "msvc-cl",
@@ -123,3 +139,12 @@
     },
     visibility = [":__subpackages__"],
 )
+
+selects.config_setting_group(
+    name = "mingw_compiler",
+    match_any = [
+        ":mingw_unspecified_compiler",
+        ":mingw-gcc_compiler",
+    ],
+    visibility = [":__subpackages__"],
+)
diff --git a/absl/CMakeLists.txt b/absl/CMakeLists.txt
index 925be19..3a7c12f 100644
--- a/absl/CMakeLists.txt
+++ b/absl/CMakeLists.txt
@@ -18,6 +18,7 @@
 add_subdirectory(algorithm)
 add_subdirectory(cleanup)
 add_subdirectory(container)
+add_subdirectory(crc)
 add_subdirectory(debugging)
 add_subdirectory(flags)
 add_subdirectory(functional)
@@ -37,4 +38,7 @@
 
 if (${ABSL_BUILD_DLL})
   absl_make_dll()
+  if (${ABSL_BUILD_TEST_HELPERS})
+    absl_make_dll(TEST ON)
+  endif()
 endif()
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h
index 26b1952..679e026 100644
--- a/absl/algorithm/container.h
+++ b/absl/algorithm/container.h
@@ -77,9 +77,8 @@
     decltype(std::make_pair(ContainerIter<C1>(), ContainerIter<C2>()));
 
 template <typename C>
-using ContainerDifferenceType =
-    decltype(std::distance(std::declval<ContainerIter<C>>(),
-                           std::declval<ContainerIter<C>>()));
+using ContainerDifferenceType = decltype(std::distance(
+    std::declval<ContainerIter<C>>(), std::declval<ContainerIter<C>>()));
 
 template <typename C>
 using ContainerPointerType =
@@ -97,10 +96,14 @@
 // These are meant for internal use only.
 
 template <typename C>
-ContainerIter<C> c_begin(C& c) { return begin(c); }
+ContainerIter<C> c_begin(C& c) {
+  return begin(c);
+}
 
 template <typename C>
-ContainerIter<C> c_end(C& c) { return end(c); }
+ContainerIter<C> c_end(C& c) {
+  return end(c);
+}
 
 template <typename T>
 struct IsUnorderedContainer : std::false_type {};
@@ -343,8 +346,8 @@
 // return the first element where two ordered containers differ. Applies `==` to
 // the first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)).
 template <typename C1, typename C2>
-container_algorithm_internal::ContainerIterPairType<C1, C2>
-c_mismatch(C1& c1, C2& c2) {
+container_algorithm_internal::ContainerIterPairType<C1, C2> c_mismatch(C1& c1,
+                                                                       C2& c2) {
   auto first1 = container_algorithm_internal::c_begin(c1);
   auto last1 = container_algorithm_internal::c_end(c1);
   auto first2 = container_algorithm_internal::c_begin(c2);
@@ -365,8 +368,8 @@
 // the function's test condition. Applies `pred`to the first N elements of `c1`
 // and `c2`, where N = min(size(c1), size(c2)).
 template <typename C1, typename C2, typename BinaryPredicate>
-container_algorithm_internal::ContainerIterPairType<C1, C2>
-c_mismatch(C1& c1, C2& c2, BinaryPredicate pred) {
+container_algorithm_internal::ContainerIterPairType<C1, C2> c_mismatch(
+    C1& c1, C2& c2, BinaryPredicate pred) {
   auto first1 = container_algorithm_internal::c_begin(c1);
   auto last1 = container_algorithm_internal::c_end(c1);
   auto first2 = container_algorithm_internal::c_begin(c2);
@@ -655,11 +658,10 @@
 // some condition, and return the results within an iterator.
 template <typename C, typename OutputIterator, typename Pred, typename T>
 OutputIterator c_replace_copy_if(const C& c, OutputIterator result, Pred&& pred,
-                                 T&& new_value) {
+                                 const T& new_value) {
   return std::replace_copy_if(container_algorithm_internal::c_begin(c),
                               container_algorithm_internal::c_end(c), result,
-                              std::forward<Pred>(pred),
-                              std::forward<T>(new_value));
+                              std::forward<Pred>(pred), new_value);
 }
 
 // c_fill()
@@ -667,9 +669,9 @@
 // Container-based version of the <algorithm> `std::fill()` function to fill a
 // container with some value.
 template <typename C, typename T>
-void c_fill(C& c, T&& value) {
+void c_fill(C& c, const T& value) {
   std::fill(container_algorithm_internal::c_begin(c),
-            container_algorithm_internal::c_end(c), std::forward<T>(value));
+            container_algorithm_internal::c_end(c), value);
 }
 
 // c_fill_n()
@@ -677,9 +679,8 @@
 // Container-based version of the <algorithm> `std::fill_n()` function to fill
 // the first N elements in a container with some value.
 template <typename C, typename Size, typename T>
-void c_fill_n(C& c, Size n, T&& value) {
-  std::fill_n(container_algorithm_internal::c_begin(c), n,
-              std::forward<T>(value));
+void c_fill_n(C& c, Size n, const T& value) {
+  std::fill_n(container_algorithm_internal::c_begin(c), n, value);
 }
 
 // c_generate()
@@ -716,10 +717,11 @@
 // copy a container's elements while removing any elements matching the given
 // `value`.
 template <typename C, typename OutputIterator, typename T>
-OutputIterator c_remove_copy(const C& c, OutputIterator result, T&& value) {
+OutputIterator c_remove_copy(const C& c, OutputIterator result,
+                             const T& value) {
   return std::remove_copy(container_algorithm_internal::c_begin(c),
                           container_algorithm_internal::c_end(c), result,
-                          std::forward<T>(value));
+                          value);
 }
 
 // c_remove_copy_if()
@@ -1064,20 +1066,19 @@
 // which does not compare less than `value`.
 template <typename Sequence, typename T>
 container_algorithm_internal::ContainerIter<Sequence> c_lower_bound(
-    Sequence& sequence, T&& value) {
+    Sequence& sequence, const T& value) {
   return std::lower_bound(container_algorithm_internal::c_begin(sequence),
-                          container_algorithm_internal::c_end(sequence),
-                          std::forward<T>(value));
+                          container_algorithm_internal::c_end(sequence), value);
 }
 
 // Overload of c_lower_bound() for performing a `comp` comparison other than
 // the default `operator<`.
 template <typename Sequence, typename T, typename LessThan>
 container_algorithm_internal::ContainerIter<Sequence> c_lower_bound(
-    Sequence& sequence, T&& value, LessThan&& comp) {
+    Sequence& sequence, const 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<LessThan>(comp));
+                          container_algorithm_internal::c_end(sequence), value,
+                          std::forward<LessThan>(comp));
 }
 
 // c_upper_bound()
@@ -1087,20 +1088,19 @@
 // which is greater than `value`.
 template <typename Sequence, typename T>
 container_algorithm_internal::ContainerIter<Sequence> c_upper_bound(
-    Sequence& sequence, T&& value) {
+    Sequence& sequence, const T& value) {
   return std::upper_bound(container_algorithm_internal::c_begin(sequence),
-                          container_algorithm_internal::c_end(sequence),
-                          std::forward<T>(value));
+                          container_algorithm_internal::c_end(sequence), value);
 }
 
 // Overload of c_upper_bound() for performing a `comp` comparison other than
 // the default `operator<`.
 template <typename Sequence, typename T, typename LessThan>
 container_algorithm_internal::ContainerIter<Sequence> c_upper_bound(
-    Sequence& sequence, T&& value, LessThan&& comp) {
+    Sequence& sequence, const 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<LessThan>(comp));
+                          container_algorithm_internal::c_end(sequence), value,
+                          std::forward<LessThan>(comp));
 }
 
 // c_equal_range()
@@ -1110,20 +1110,19 @@
 // sorted container which compare equal to `value`.
 template <typename Sequence, typename T>
 container_algorithm_internal::ContainerIterPairType<Sequence, Sequence>
-c_equal_range(Sequence& sequence, T&& value) {
+c_equal_range(Sequence& sequence, const T& value) {
   return std::equal_range(container_algorithm_internal::c_begin(sequence),
-                          container_algorithm_internal::c_end(sequence),
-                          std::forward<T>(value));
+                          container_algorithm_internal::c_end(sequence), value);
 }
 
 // Overload of c_equal_range() for performing a `comp` comparison other than
 // the default `operator<`.
 template <typename Sequence, typename T, typename LessThan>
 container_algorithm_internal::ContainerIterPairType<Sequence, Sequence>
-c_equal_range(Sequence& sequence, T&& value, LessThan&& comp) {
+c_equal_range(Sequence& sequence, const 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<LessThan>(comp));
+                          container_algorithm_internal::c_end(sequence), value,
+                          std::forward<LessThan>(comp));
 }
 
 // c_binary_search()
@@ -1132,20 +1131,20 @@
 // to test if any element in the sorted container contains a value equivalent to
 // 'value'.
 template <typename Sequence, typename T>
-bool c_binary_search(Sequence&& sequence, T&& value) {
+bool c_binary_search(const Sequence& sequence, const T& value) {
   return std::binary_search(container_algorithm_internal::c_begin(sequence),
                             container_algorithm_internal::c_end(sequence),
-                            std::forward<T>(value));
+                            value);
 }
 
 // Overload of c_binary_search() for performing a `comp` comparison other than
 // the default `operator<`.
 template <typename Sequence, typename T, typename LessThan>
-bool c_binary_search(Sequence&& sequence, T&& value, LessThan&& comp) {
+bool c_binary_search(const Sequence& sequence, const 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<LessThan>(comp));
+                            value, std::forward<LessThan>(comp));
 }
 
 //------------------------------------------------------------------------------
@@ -1560,8 +1559,8 @@
 // smallest and largest values, respectively, using `operator<` to make the
 // comparisons.
 template <typename C>
-container_algorithm_internal::ContainerIterPairType<C, C>
-c_minmax_element(C& c) {
+container_algorithm_internal::ContainerIterPairType<C, C> c_minmax_element(
+    C& c) {
   return std::minmax_element(container_algorithm_internal::c_begin(c),
                              container_algorithm_internal::c_end(c));
 }
@@ -1569,8 +1568,8 @@
 // Overload of c_minmax_element() for performing `comp` comparisons other than
 // `operator<`.
 template <typename C, typename LessThan>
-container_algorithm_internal::ContainerIterPairType<C, C>
-c_minmax_element(C& c, LessThan&& comp) {
+container_algorithm_internal::ContainerIterPairType<C, C> 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<LessThan>(comp));
@@ -1588,7 +1587,8 @@
 // that capital letters ("A-Z") have ASCII values less than lowercase letters
 // ("a-z").
 template <typename Sequence1, typename Sequence2>
-bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2) {
+bool c_lexicographical_compare(const Sequence1& sequence1,
+                               const Sequence2& sequence2) {
   return std::lexicographical_compare(
       container_algorithm_internal::c_begin(sequence1),
       container_algorithm_internal::c_end(sequence1),
@@ -1599,8 +1599,8 @@
 // Overload of c_lexicographical_compare() for performing a lexicographical
 // comparison using a `comp` operator instead of `operator<`.
 template <typename Sequence1, typename Sequence2, typename LessThan>
-bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2,
-                               LessThan&& comp) {
+bool c_lexicographical_compare(const Sequence1& sequence1,
+                               const Sequence2& sequence2, LessThan&& comp) {
   return std::lexicographical_compare(
       container_algorithm_internal::c_begin(sequence1),
       container_algorithm_internal::c_end(sequence1),
@@ -1655,18 +1655,18 @@
 
 // c_iota()
 //
-// Container-based version of the <algorithm> `std::iota()` function
+// Container-based version of the <numeric> `std::iota()` function
 // to compute successive values of `value`, as if incremented with `++value`
 // after each element is written. and write them to the container.
 template <typename Sequence, typename T>
-void c_iota(Sequence& sequence, T&& value) {
+void c_iota(Sequence& sequence, const T& value) {
   std::iota(container_algorithm_internal::c_begin(sequence),
-            container_algorithm_internal::c_end(sequence),
-            std::forward<T>(value));
+            container_algorithm_internal::c_end(sequence), value);
 }
+
 // c_accumulate()
 //
-// Container-based version of the <algorithm> `std::accumulate()` function
+// Container-based version of the <numeric> `std::accumulate()` function
 // to accumulate the element values of a container to `init` and return that
 // accumulation by value.
 //
@@ -1693,7 +1693,7 @@
 
 // c_inner_product()
 //
-// Container-based version of the <algorithm> `std::inner_product()` function
+// Container-based version of the <numeric> `std::inner_product()` function
 // to compute the cumulative inner product of container element pairs.
 //
 // Note: Due to a language technicality this function has return type
@@ -1724,7 +1724,7 @@
 
 // c_adjacent_difference()
 //
-// Container-based version of the <algorithm> `std::adjacent_difference()`
+// Container-based version of the <numeric> `std::adjacent_difference()`
 // function to compute the difference between each element and the one preceding
 // it and write it to an iterator.
 template <typename InputSequence, typename OutputIt>
@@ -1747,7 +1747,7 @@
 
 // c_partial_sum()
 //
-// Container-based version of the <algorithm> `std::partial_sum()` function
+// Container-based version of the <numeric> `std::partial_sum()` function
 // to compute the partial sum of the elements in a sequence and write them
 // to an iterator. The partial sum is the sum of all element values so far in
 // the sequence.
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index ded26d6..28cbf28 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -246,6 +246,10 @@
         "//absl:clang-cl_compiler": [
             "-DEFAULTLIB:advapi32.lib",
         ],
+        "//absl:mingw_compiler": [
+            "-DEFAULTLIB:advapi32.lib",
+            "-ladvapi32",
+        ],
         "//absl:wasm": [],
         "//conditions:default": ["-pthread"],
     }) + ABSL_DEFAULT_LINKOPTS,
@@ -732,21 +736,25 @@
 
 cc_library(
     name = "prefetch",
-    hdrs = ["internal/prefetch.h"],
+    hdrs = [
+        "internal/prefetch.h",
+        "prefetch.h",
+    ],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
     deps = [
         ":config",
+        ":core_headers",  # TODO(b/265984188): remove
     ],
 )
 
 cc_test(
     name = "prefetch_test",
     size = "small",
-    srcs = ["internal/prefetch_test.cc"],
+    srcs = [
+        "internal/prefetch_test.cc",
+        "prefetch_test.cc",
+    ],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index 5c46ba3..71b9379 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -201,7 +201,7 @@
   LINKOPTS
     ${ABSL_DEFAULT_LINKOPTS}
     $<$<BOOL:${LIBRT}>:-lrt>
-    $<$<BOOL:${MINGW}>:"advapi32">
+    $<$<BOOL:${MINGW}>:-ladvapi32>
   DEPS
     absl::atomic_hook
     absl::base_internal
@@ -645,11 +645,11 @@
     GTest::gtest_main
 )
 
-# Internal-only target, do not depend on directly.
 absl_cc_library(
   NAME
     prefetch
   HDRS
+    "prefetch.h"
     "internal/prefetch.h"
   COPTS
     ${ABSL_DEFAULT_COPTS}
@@ -657,12 +657,14 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::config
+    absl::core_headers  # TODO(b/265984188): remove
 )
 
 absl_cc_test(
   NAME
     prefetch_test
   SRCS
+    "prefetch_test.cc"
     "internal/prefetch_test.cc"
   COPTS
     ${ABSL_TEST_COPTS}
diff --git a/absl/base/attributes.h b/absl/base/attributes.h
index e11a064..3e5aafb 100644
--- a/absl/base/attributes.h
+++ b/absl/base/attributes.h
@@ -211,11 +211,20 @@
 // out of bounds or does other scary things with memory.
 // NOTE: GCC supports AddressSanitizer(asan) since 4.8.
 // https://gcc.gnu.org/gcc-4.8/changes.html
-#if ABSL_HAVE_ATTRIBUTE(no_sanitize_address)
+#if defined(ABSL_HAVE_ADDRESS_SANITIZER) && \
+    ABSL_HAVE_ATTRIBUTE(no_sanitize_address)
 #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
-#elif defined(_MSC_VER) && _MSC_VER >= 1928
+#elif defined(ABSL_HAVE_ADDRESS_SANITIZER) && defined(_MSC_VER) && \
+    _MSC_VER >= 1928
 // https://docs.microsoft.com/en-us/cpp/cpp/no-sanitize-address
 #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __declspec(no_sanitize_address)
+#elif defined(ABSL_HAVE_HWADDRESS_SANITIZER) && ABSL_HAVE_ATTRIBUTE(no_sanitize)
+// HWAddressSanitizer is a sanitizer similar to AddressSanitizer, which uses CPU
+// features to detect similar bugs with less CPU and memory overhead.
+// NOTE: GCC supports HWAddressSanitizer(hwasan) since 11.
+// https://gcc.gnu.org/gcc-11/changes.html
+#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS \
+  __attribute__((no_sanitize("hwaddress")))
 #else
 #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS
 #endif
@@ -322,8 +331,8 @@
 // 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
+// __attribute__((section(#name))) on AIX is achieved 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)
@@ -716,26 +725,9 @@
 #define ABSL_CONST_INIT
 #endif
 
-// ABSL_ATTRIBUTE_PURE_FUNCTION
-//
-// ABSL_ATTRIBUTE_PURE_FUNCTION is used to annotate declarations of "pure"
-// functions. A function is pure if its return value is only a function of its
-// arguments. The pure attribute prohibits a function from modifying the state
-// of the program that is observable by means other than inspecting the
-// function's return value. Declaring such functions with the pure attribute
-// allows the compiler to avoid emitting some calls in repeated invocations of
-// the function with the same argument values.
-//
-// Example:
-//
-//  ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Milliseconds(Duration d);
-#if ABSL_HAVE_CPP_ATTRIBUTE(gnu::pure)
-#define ABSL_ATTRIBUTE_PURE_FUNCTION [[gnu::pure]]
-#elif ABSL_HAVE_ATTRIBUTE(pure)
-#define ABSL_ATTRIBUTE_PURE_FUNCTION __attribute__((pure))
-#else
+// These annotations are not available yet due to fear of breaking code.
 #define ABSL_ATTRIBUTE_PURE_FUNCTION
-#endif
+#define ABSL_ATTRIBUTE_CONST_FUNCTION
 
 // 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
@@ -796,4 +788,26 @@
 #define ABSL_ATTRIBUTE_TRIVIAL_ABI
 #endif
 
+// ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS
+//
+// Indicates a data member can be optimized to occupy no space (if it is empty)
+// and/or its tail padding can be used for other members.
+//
+// For code that is assured to only build with C++20 or later, prefer using
+// the standard attribute `[[no_unique_address]]` directly instead of this
+// macro.
+//
+// https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch/#c20-no_unique_address
+// Current versions of MSVC have disabled `[[no_unique_address]]` since it
+// breaks ABI compatibility, but offers `[[msvc::no_unique_address]]` for
+// situations when it can be assured that it is desired. Since Abseil does not
+// claim ABI compatibility in mixed builds, we can offer it unconditionally.
+#if defined(_MSC_VER) && _MSC_VER >= 1929
+#define ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
+#elif ABSL_HAVE_CPP_ATTRIBUTE(no_unique_address)
+#define ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS [[no_unique_address]]
+#else
+#define ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS
+#endif
+
 #endif  // ABSL_BASE_ATTRIBUTES_H_
diff --git a/absl/base/call_once.h b/absl/base/call_once.h
index 96109f5..08436ba 100644
--- a/absl/base/call_once.h
+++ b/absl/base/call_once.h
@@ -123,7 +123,7 @@
 
  private:
   base_internal::SchedulingMode mode_;
-  bool guard_result_;
+  bool guard_result_ = false;
 };
 
 // Bit patterns for call_once state machine values.  Internal implementation
diff --git a/absl/base/casts.h b/absl/base/casts.h
index b99adb0..d195888 100644
--- a/absl/base/casts.h
+++ b/absl/base/casts.h
@@ -149,16 +149,16 @@
 
 #else  // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
 
-template <typename Dest, typename Source,
-          typename std::enable_if<
-              sizeof(Dest) == sizeof(Source) &&
-                  type_traits_internal::is_trivially_copyable<Source>::value &&
-                  type_traits_internal::is_trivially_copyable<Dest>::value
+template <
+    typename Dest, typename Source,
+    typename std::enable_if<sizeof(Dest) == sizeof(Source) &&
+                                std::is_trivially_copyable<Source>::value &&
+                                std::is_trivially_copyable<Dest>::value
 #if !ABSL_HAVE_BUILTIN(__builtin_bit_cast)
-                  && std::is_default_constructible<Dest>::value
+                                && std::is_default_constructible<Dest>::value
 #endif  // !ABSL_HAVE_BUILTIN(__builtin_bit_cast)
-              ,
-              int>::type = 0>
+                            ,
+                            int>::type = 0>
 #if ABSL_HAVE_BUILTIN(__builtin_bit_cast)
 inline constexpr Dest bit_cast(const Source& source) {
   return __builtin_bit_cast(Dest, source);
diff --git a/absl/base/config.h b/absl/base/config.h
index 1058ce7..7d06e11 100644
--- a/absl/base/config.h
+++ b/absl/base/config.h
@@ -237,15 +237,8 @@
 // ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
 //
 // Checks whether `std::is_trivially_destructible<T>` is supported.
-//
-// Notes: All supported compilers using libc++ support this feature, as does
-// 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(_MSC_VER) || \
-    (defined(__clang__) && __clang_major__ >= 15) ||   \
-    (!defined(__clang__) && defined(__GLIBCXX__) &&    \
-     ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(4, 8))
 #define ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1
 #endif
 
@@ -253,36 +246,26 @@
 //
 // Checks whether `std::is_trivially_default_constructible<T>` and
 // `std::is_trivially_copy_constructible<T>` are supported.
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+#error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set
+#else
+#define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1
+#endif
 
 // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
 //
 // Checks whether `std::is_trivially_copy_assignable<T>` is supported.
-
-// 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__) && __clang_major__ >= 15) ||                         \
-    (!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__) && !defined(__clang__))
-#define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
+#error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot be directly set
+#else
 #define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1
 #endif
 
 // ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE
 //
 // Checks whether `std::is_trivially_copyable<T>` is supported.
-//
-// Notes: Clang 15+ with libc++ supports these features, GCC hasn't been tested.
-#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE)
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE
 #error ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE cannot be directly set
-#elif defined(__clang__) && (__clang_major__ >= 15)
 #define ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE 1
 #endif
 
@@ -578,73 +561,44 @@
 
 // ABSL_HAVE_STD_ANY
 //
-// Checks whether C++17 std::any is available by checking whether <any> exists.
+// Checks whether C++17 std::any is available.
 #ifdef ABSL_HAVE_STD_ANY
 #error "ABSL_HAVE_STD_ANY cannot be directly set."
-#endif
-
-#ifdef __has_include
-#if __has_include(<any>) && defined(__cplusplus) && __cplusplus >= 201703L && \
+#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
+    ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L && \
     !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
 #define ABSL_HAVE_STD_ANY 1
 #endif
-#endif
 
 // ABSL_HAVE_STD_OPTIONAL
 //
 // Checks whether C++17 std::optional is available.
 #ifdef ABSL_HAVE_STD_OPTIONAL
 #error "ABSL_HAVE_STD_OPTIONAL cannot be directly set."
-#endif
-
-#ifdef __has_include
-#if __has_include(<optional>) && defined(__cplusplus) && \
-    __cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
+#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) &&  \
+    ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L && \
+    !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
 #define ABSL_HAVE_STD_OPTIONAL 1
 #endif
-#endif
 
 // ABSL_HAVE_STD_VARIANT
 //
 // Checks whether C++17 std::variant is available.
 #ifdef ABSL_HAVE_STD_VARIANT
 #error "ABSL_HAVE_STD_VARIANT cannot be directly set."
-#endif
-
-#ifdef __has_include
-#if __has_include(<variant>) && defined(__cplusplus) && \
-    __cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
+#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
+    ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L && \
+    !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
 #define ABSL_HAVE_STD_VARIANT 1
 #endif
-#endif
 
 // ABSL_HAVE_STD_STRING_VIEW
 //
 // Checks whether C++17 std::string_view is available.
 #ifdef ABSL_HAVE_STD_STRING_VIEW
 #error "ABSL_HAVE_STD_STRING_VIEW cannot be directly set."
-#endif
-
-#ifdef __has_include
-#if __has_include(<string_view>) && defined(__cplusplus) && \
-    __cplusplus >= 201703L
-#define ABSL_HAVE_STD_STRING_VIEW 1
-#endif
-#endif
-
-// For MSVC, `__has_include` is supported in VS 2017 15.3, which is later than
-// the support for <optional>, <any>, <string_view>, <variant>. So we use
-// _MSC_VER to check whether we have VS 2017 RTM (when <optional>, <any>,
-// <string_view>, <variant> is implemented) or higher. Also, `__cplusplus` is
-// not correctly set by MSVC, so we use `_MSVC_LANG` to check the language
-// version.
-// TODO(zhangxy): fix tests before enabling aliasing for `std::any`.
-#if defined(_MSC_VER) && _MSC_VER >= 1910 &&         \
-    ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || \
-     (defined(__cplusplus) && __cplusplus > 201402))
-// #define ABSL_HAVE_STD_ANY 1
-#define ABSL_HAVE_STD_OPTIONAL 1
-#define ABSL_HAVE_STD_VARIANT 1
+#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
+    ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
 #define ABSL_HAVE_STD_STRING_VIEW 1
 #endif
 
@@ -759,6 +713,18 @@
 #define ABSL_DLL
 #endif  // defined(_MSC_VER)
 
+#if defined(_MSC_VER)
+#if defined(ABSL_BUILD_TEST_DLL)
+#define ABSL_TEST_DLL __declspec(dllexport)
+#elif defined(ABSL_CONSUME_TEST_DLL)
+#define ABSL_TEST_DLL __declspec(dllimport)
+#else
+#define ABSL_TEST_DLL
+#endif
+#else
+#define ABSL_TEST_DLL
+#endif  // defined(_MSC_VER)
+
 // ABSL_HAVE_MEMORY_SANITIZER
 //
 // MemorySanitizer (MSan) is a detector of uninitialized reads. It consists of
@@ -804,6 +770,20 @@
 #define ABSL_HAVE_HWADDRESS_SANITIZER 1
 #endif
 
+// ABSL_HAVE_DATAFLOW_SANITIZER
+//
+// Dataflow Sanitizer (or DFSAN) is a generalised dynamic data flow analysis.
+#ifdef ABSL_HAVE_DATAFLOW_SANITIZER
+#error "ABSL_HAVE_DATAFLOW_SANITIZER cannot be directly set."
+#elif defined(DATAFLOW_SANITIZER)
+// GCC provides no method for detecting the presence of the standalone
+// DataFlowSanitizer (-fsanitize=dataflow), so GCC users of -fsanitize=dataflow
+// should also use -DDATAFLOW_SANITIZER.
+#define ABSL_HAVE_DATAFLOW_SANITIZER 1
+#elif ABSL_HAVE_FEATURE(dataflow_sanitizer)
+#define ABSL_HAVE_DATAFLOW_SANITIZER 1
+#endif
+
 // ABSL_HAVE_LEAK_SANITIZER
 //
 // LeakSanitizer (or lsan) is a detector of memory leaks.
@@ -818,7 +798,7 @@
 #ifdef ABSL_HAVE_LEAK_SANITIZER
 #error "ABSL_HAVE_LEAK_SANITIZER cannot be directly set."
 #elif defined(LEAK_SANITIZER)
-// GCC provides no method for detecting the presense of the standalone
+// GCC provides no method for detecting the presence of the standalone
 // LeakSanitizer (-fsanitize=leak), so GCC users of -fsanitize=leak should also
 // use -DLEAK_SANITIZER.
 #define ABSL_HAVE_LEAK_SANITIZER 1
@@ -866,7 +846,9 @@
 // RTTI support.
 #ifdef ABSL_INTERNAL_HAS_RTTI
 #error ABSL_INTERNAL_HAS_RTTI cannot be directly set
-#elif !defined(__GNUC__) || defined(__GXX_RTTI)
+#elif (defined(__GNUC__) && defined(__GXX_RTTI)) || \
+    (defined(_MSC_VER) && defined(_CPPRTTI)) ||     \
+    (!defined(__GNUC__) && !defined(_MSC_VER))
 #define ABSL_INTERNAL_HAS_RTTI 1
 #endif  // !defined(__GNUC__) || defined(__GXX_RTTI)
 
@@ -877,7 +859,8 @@
 #error ABSL_INTERNAL_HAVE_SSE cannot be directly set
 #elif defined(__SSE__)
 #define ABSL_INTERNAL_HAVE_SSE 1
-#elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 1)
+#elif (defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 1)) && \
+    !defined(_M_ARM64EC)
 // MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 1
 // indicates that at least SSE was targeted with the /arch:SSE option.
 // All x86-64 processors support SSE, so support can be assumed.
@@ -892,7 +875,8 @@
 #error ABSL_INTERNAL_HAVE_SSE2 cannot be directly set
 #elif defined(__SSE2__)
 #define ABSL_INTERNAL_HAVE_SSE2 1
-#elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
+#elif (defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2)) && \
+    !defined(_M_ARM64EC)
 // MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 2
 // indicates that at least SSE2 was targeted with the /arch:SSE2 option.
 // All x86-64 processors support SSE2, so support can be assumed.
@@ -928,4 +912,15 @@
 #define ABSL_INTERNAL_HAVE_ARM_NEON 1
 #endif
 
+// ABSL_HAVE_CONSTANT_EVALUATED is used for compile-time detection of
+// constant evaluation support through `absl::is_constant_evaluated`.
+#ifdef ABSL_HAVE_CONSTANT_EVALUATED
+#error ABSL_HAVE_CONSTANT_EVALUATED cannot be directly set
+#endif
+#ifdef __cpp_lib_is_constant_evaluated
+#define ABSL_HAVE_CONSTANT_EVALUATED 1
+#elif ABSL_HAVE_BUILTIN(__builtin_is_constant_evaluated)
+#define ABSL_HAVE_CONSTANT_EVALUATED 1
+#endif
+
 #endif  // ABSL_BASE_CONFIG_H_
diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h
index 77a5aec..c106154 100644
--- a/absl/base/internal/exception_safety_testing.h
+++ b/absl/base/internal/exception_safety_testing.h
@@ -946,7 +946,7 @@
  *   `std::unique_ptr<T> operator()() const` where T is the type being tested.
  *   It is used for reliably creating identical T instances to test on.
  *
- * - Operation: The operation object (passsed in via tester.WithOperation(...)
+ * - Operation: The operation object (passed in via tester.WithOperation(...)
  *   or tester.Test(...)) must be invocable with the signature
  *   `void operator()(T*) const` where T is the type being tested. It is used
  *   for performing steps on a T instance that may throw and that need to be
diff --git a/absl/base/internal/low_level_alloc.cc b/absl/base/internal/low_level_alloc.cc
index 662167b..f8c4336 100644
--- a/absl/base/internal/low_level_alloc.cc
+++ b/absl/base/internal/low_level_alloc.cc
@@ -42,6 +42,10 @@
 #include <windows.h>
 #endif
 
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif
+
 #include <string.h>
 #include <algorithm>
 #include <atomic>
@@ -550,7 +554,7 @@
       size_t new_pages_size = RoundUp(req_rnd, arena->pagesize * 16);
       void *new_pages;
 #ifdef _WIN32
-      new_pages = VirtualAlloc(0, new_pages_size,
+      new_pages = VirtualAlloc(nullptr, new_pages_size,
                                MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
       ABSL_RAW_CHECK(new_pages != nullptr, "VirtualAlloc failed");
 #else
@@ -570,6 +574,18 @@
         ABSL_RAW_LOG(FATAL, "mmap error: %d", errno);
       }
 
+#ifdef __linux__
+#if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME)
+      // Attempt to name the allocated address range in /proc/$PID/smaps on
+      // Linux.
+      //
+      // This invocation of prctl() may fail if the Linux kernel was not
+      // configured with the CONFIG_ANON_VMA_NAME option.  This is OK since
+      // the naming of arenas is primarily a debugging aid.
+      prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, new_pages, new_pages_size,
+            "absl");
+#endif
+#endif  // __linux__
 #endif  // _WIN32
       arena->mu.Lock();
       s = reinterpret_cast<AllocList *>(new_pages);
diff --git a/absl/base/internal/prefetch.h b/absl/base/internal/prefetch.h
index 0641928..aecfd87 100644
--- a/absl/base/internal/prefetch.h
+++ b/absl/base/internal/prefetch.h
@@ -12,10 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// TODO(b/265984188): remove all uses and delete this header.
+
 #ifndef ABSL_BASE_INTERNAL_PREFETCH_H_
 #define ABSL_BASE_INTERNAL_PREFETCH_H_
 
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
+#include "absl/base/prefetch.h"
 
 #ifdef __SSE__
 #include <xmmintrin.h>
@@ -72,10 +76,21 @@
 ABSL_NAMESPACE_BEGIN
 namespace base_internal {
 
-void PrefetchT0(const void* addr);
+ABSL_DEPRECATED("Use absl::PrefetchToLocalCache() instead")
+inline void PrefetchT0(const void* address) {
+  absl::PrefetchToLocalCache(address);
+}
+
+ABSL_DEPRECATED("Use absl::PrefetchToLocalCache() instead")
+inline void PrefetchNta(const void* address) {
+  absl::PrefetchToLocalCacheNta(address);
+}
+
+ABSL_DEPRECATED("Use __builtin_prefetch() for advanced prefetch logic instead")
 void PrefetchT1(const void* addr);
+
+ABSL_DEPRECATED("Use __builtin_prefetch() for advanced prefetch logic instead")
 void PrefetchT2(const void* addr);
-void PrefetchNta(const void* addr);
 
 // Implementation details follow.
 
@@ -90,10 +105,6 @@
 // safe for all currently supported platforms. However, prefetch for
 // store may have problems depending on the target platform.
 //
-inline void PrefetchT0(const void* addr) {
-  // Note: this uses prefetcht0 on Intel.
-  __builtin_prefetch(addr, 0, 3);
-}
 inline void PrefetchT1(const void* addr) {
   // Note: this uses prefetcht1 on Intel.
   __builtin_prefetch(addr, 0, 2);
@@ -102,33 +113,21 @@
   // Note: this uses prefetcht2 on Intel.
   __builtin_prefetch(addr, 0, 1);
 }
-inline void PrefetchNta(const void* addr) {
-  // Note: this uses prefetchtnta on Intel.
-  __builtin_prefetch(addr, 0, 0);
-}
 
 #elif defined(ABSL_INTERNAL_HAVE_SSE)
 
 #define ABSL_INTERNAL_HAVE_PREFETCH 1
 
-inline void PrefetchT0(const void* addr) {
-  _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_T0);
-}
 inline void PrefetchT1(const void* addr) {
   _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_T1);
 }
 inline void PrefetchT2(const void* addr) {
   _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_T2);
 }
-inline void PrefetchNta(const void* addr) {
-  _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_NTA);
-}
 
 #else
-inline void PrefetchT0(const void*) {}
 inline void PrefetchT1(const void*) {}
 inline void PrefetchT2(const void*) {}
-inline void PrefetchNta(const void*) {}
 #endif
 
 }  // namespace base_internal
diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h
index db2ef38..e876525 100644
--- a/absl/base/internal/raw_logging.h
+++ b/absl/base/internal/raw_logging.h
@@ -48,6 +48,7 @@
     ::absl::raw_log_internal::RawLog(ABSL_RAW_LOG_INTERNAL_##severity,         \
                                      absl_raw_log_internal_basename, __LINE__, \
                                      __VA_ARGS__);                             \
+    ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_##severity;                        \
   } while (0)
 
 // Similar to CHECK(condition) << message, but for low-level modules:
@@ -77,8 +78,7 @@
     ::absl::raw_log_internal::internal_log_function(                      \
         ABSL_RAW_LOG_INTERNAL_##severity, absl_raw_log_internal_filename, \
         __LINE__, message);                                               \
-    if (ABSL_RAW_LOG_INTERNAL_##severity == ::absl::LogSeverity::kFatal)  \
-      ABSL_INTERNAL_UNREACHABLE;                                          \
+    ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_##severity;                   \
   } while (0)
 
 #define ABSL_INTERNAL_CHECK(condition, message)                    \
@@ -90,6 +90,20 @@
     }                                                              \
   } while (0)
 
+#ifndef NDEBUG
+
+#define ABSL_RAW_DLOG(severity, ...) ABSL_RAW_LOG(severity, __VA_ARGS__)
+#define ABSL_RAW_DCHECK(condition, message) ABSL_RAW_CHECK(condition, message)
+
+#else  // NDEBUG
+
+#define ABSL_RAW_DLOG(severity, ...)                   \
+  while (false) ABSL_RAW_LOG(severity, __VA_ARGS__)
+#define ABSL_RAW_DCHECK(condition, message) \
+  while (false) ABSL_RAW_CHECK(condition, message)
+
+#endif  // NDEBUG
+
 #define ABSL_RAW_LOG_INTERNAL_INFO ::absl::LogSeverity::kInfo
 #define ABSL_RAW_LOG_INTERNAL_WARNING ::absl::LogSeverity::kWarning
 #define ABSL_RAW_LOG_INTERNAL_ERROR ::absl::LogSeverity::kError
@@ -97,6 +111,12 @@
 #define ABSL_RAW_LOG_INTERNAL_LEVEL(severity) \
   ::absl::NormalizeLogSeverity(severity)
 
+#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_INFO
+#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_WARNING
+#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_ERROR
+#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_FATAL ABSL_UNREACHABLE()
+#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_LEVEL(severity)
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace raw_log_internal {
diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc
index da499d3..8429fb9 100644
--- a/absl/base/internal/sysinfo.cc
+++ b/absl/base/internal/sysinfo.cc
@@ -159,7 +159,7 @@
     DWORD type = 0;
     DWORD data = 0;
     DWORD data_size = sizeof(data);
-    auto result = RegQueryValueExA(key, "~MHz", 0, &type,
+    auto result = RegQueryValueExA(key, "~MHz", nullptr, &type,
                                    reinterpret_cast<LPBYTE>(&data), &data_size);
     RegCloseKey(key);
     if (result == ERROR_SUCCESS && type == REG_DWORD &&
diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h
index 659694b..496ec21 100644
--- a/absl/base/internal/thread_identity.h
+++ b/absl/base/internal/thread_identity.h
@@ -134,6 +134,10 @@
 
 // The instances of this class are allocated in NewThreadIdentity() with an
 // alignment of PerThreadSynch::kAlignment.
+//
+// NOTE: The layout of fields in this structure is critical, please do not
+//       add, remove, or modify the field placements without fully auditing the
+//       layout.
 struct ThreadIdentity {
   // Must be the first member.  The Mutex implementation requires that
   // the PerThreadSynch object associated with each thread is
@@ -143,7 +147,7 @@
 
   // Private: Reserved for absl::synchronization_internal::Waiter.
   struct WaiterState {
-    alignas(void*) char data[128];
+    alignas(void*) char data[256];
   } waiter_state;
 
   // Used by PerThreadSem::{Get,Set}ThreadBlockedCounter().
@@ -166,7 +170,10 @@
 //
 // Does not malloc(*), and is async-signal safe.
 // [*] Technically pthread_setspecific() does malloc on first use; however this
-// is handled internally within tcmalloc's initialization already.
+// is handled internally within tcmalloc's initialization already. Note that
+// darwin does *not* use tcmalloc, so this can catch you if using MallocHooks
+// on Apple platforms. Whatever function is calling your MallocHooks will need
+// to watch for recursion on Apple platforms.
 //
 // New ThreadIdentity objects can be constructed and associated with a thread
 // by calling GetOrCreateCurrentThreadIdentity() in per-thread-sem.h.
diff --git a/absl/base/internal/thread_identity_test.cc b/absl/base/internal/thread_identity_test.cc
index 46a6f74..5f17553 100644
--- a/absl/base/internal/thread_identity_test.cc
+++ b/absl/base/internal/thread_identity_test.cc
@@ -95,7 +95,7 @@
 }
 
 TEST(ThreadIdentityTest, ReusedThreadIdentityMutexTest) {
-  // This test repeatly creates and joins a series of threads, each of
+  // This test repeatedly creates and joins a series of threads, each of
   // which acquires and releases shared Mutex locks. This verifies
   // Mutex operations work correctly under a reused
   // ThreadIdentity. Note that the most likely failure mode of this
diff --git a/absl/base/internal/throw_delegate.cc b/absl/base/internal/throw_delegate.cc
index c260ff1..337e870 100644
--- a/absl/base/internal/throw_delegate.cc
+++ b/absl/base/internal/throw_delegate.cc
@@ -26,22 +26,13 @@
 ABSL_NAMESPACE_BEGIN
 namespace base_internal {
 
-// NOTE: The various STL exception throwing functions are placed within the
-// #ifdef blocks so the symbols aren't exposed on platforms that don't support
-// them, such as the Android NDK. For example, ANGLE fails to link when building
-// within AOSP without them, since the STL functions don't exist.
-namespace {
-#ifdef ABSL_HAVE_EXCEPTIONS
-template <typename T>
-[[noreturn]] void Throw(const T& error) {
-  throw error;
-}
-#endif
-}  // namespace
+// NOTE: The exception types, like `std::logic_error`, do not exist on all
+// platforms. (For example, the Android NDK does not have them.)
+// Therefore, their use must be guarded by `#ifdef` or equivalent.
 
 void ThrowStdLogicError(const std::string& what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::logic_error(what_arg));
+  throw std::logic_error(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
   std::abort();
@@ -49,7 +40,7 @@
 }
 void ThrowStdLogicError(const char* what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::logic_error(what_arg));
+  throw std::logic_error(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg);
   std::abort();
@@ -57,7 +48,7 @@
 }
 void ThrowStdInvalidArgument(const std::string& what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::invalid_argument(what_arg));
+  throw std::invalid_argument(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
   std::abort();
@@ -65,7 +56,7 @@
 }
 void ThrowStdInvalidArgument(const char* what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::invalid_argument(what_arg));
+  throw std::invalid_argument(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg);
   std::abort();
@@ -74,7 +65,7 @@
 
 void ThrowStdDomainError(const std::string& what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::domain_error(what_arg));
+  throw std::domain_error(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
   std::abort();
@@ -82,7 +73,7 @@
 }
 void ThrowStdDomainError(const char* what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::domain_error(what_arg));
+  throw std::domain_error(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg);
   std::abort();
@@ -91,7 +82,7 @@
 
 void ThrowStdLengthError(const std::string& what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::length_error(what_arg));
+  throw std::length_error(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
   std::abort();
@@ -99,7 +90,7 @@
 }
 void ThrowStdLengthError(const char* what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::length_error(what_arg));
+  throw std::length_error(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg);
   std::abort();
@@ -108,7 +99,7 @@
 
 void ThrowStdOutOfRange(const std::string& what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::out_of_range(what_arg));
+  throw std::out_of_range(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
   std::abort();
@@ -116,7 +107,7 @@
 }
 void ThrowStdOutOfRange(const char* what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::out_of_range(what_arg));
+  throw std::out_of_range(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg);
   std::abort();
@@ -125,7 +116,7 @@
 
 void ThrowStdRuntimeError(const std::string& what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::runtime_error(what_arg));
+  throw std::runtime_error(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
   std::abort();
@@ -133,7 +124,7 @@
 }
 void ThrowStdRuntimeError(const char* what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::runtime_error(what_arg));
+  throw std::runtime_error(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg);
   std::abort();
@@ -142,7 +133,7 @@
 
 void ThrowStdRangeError(const std::string& what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::range_error(what_arg));
+  throw std::range_error(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
   std::abort();
@@ -150,7 +141,7 @@
 }
 void ThrowStdRangeError(const char* what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::range_error(what_arg));
+  throw std::range_error(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg);
   std::abort();
@@ -159,7 +150,7 @@
 
 void ThrowStdOverflowError(const std::string& what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::overflow_error(what_arg));
+  throw std::overflow_error(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
   std::abort();
@@ -167,7 +158,7 @@
 }
 void ThrowStdOverflowError(const char* what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::overflow_error(what_arg));
+  throw std::overflow_error(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg);
   std::abort();
@@ -176,7 +167,7 @@
 
 void ThrowStdUnderflowError(const std::string& what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::underflow_error(what_arg));
+  throw std::underflow_error(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
   std::abort();
@@ -184,7 +175,7 @@
 }
 void ThrowStdUnderflowError(const char* what_arg) {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::underflow_error(what_arg));
+  throw std::underflow_error(what_arg);
 #else
   ABSL_RAW_LOG(FATAL, "%s", what_arg);
   std::abort();
@@ -193,7 +184,7 @@
 
 void ThrowStdBadFunctionCall() {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::bad_function_call());
+  throw std::bad_function_call();
 #else
   std::abort();
 #endif
@@ -201,7 +192,7 @@
 
 void ThrowStdBadAlloc() {
 #ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::bad_alloc());
+  throw std::bad_alloc();
 #else
   std::abort();
 #endif
diff --git a/absl/base/macros.h b/absl/base/macros.h
index 3e085a9..f33cd19 100644
--- a/absl/base/macros.h
+++ b/absl/base/macros.h
@@ -103,17 +103,11 @@
 // aborts the program in release mode (when NDEBUG is defined). The
 // implementation should abort the program as quickly as possible and ideally it
 // should not be possible to ignore the abort request.
-#if (ABSL_HAVE_BUILTIN(__builtin_trap) &&         \
-     ABSL_HAVE_BUILTIN(__builtin_unreachable)) || \
-    (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_INTERNAL_HARDENING_ABORT() \
-  do {                                  \
-    __builtin_trap();                   \
-    __builtin_unreachable();            \
+#define ABSL_INTERNAL_HARDENING_ABORT()   \
+  do {                                    \
+    ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL(); \
+    ABSL_INTERNAL_UNREACHABLE_IMPL();     \
   } while (false)
-#else
-#define ABSL_INTERNAL_HARDENING_ABORT() abort()
-#endif
 
 // ABSL_HARDENING_ASSERT()
 //
@@ -144,15 +138,4 @@
 #define ABSL_INTERNAL_RETHROW do {} while (false)
 #endif  // ABSL_HAVE_EXCEPTIONS
 
-// `ABSL_INTERNAL_UNREACHABLE` is an unreachable statement.  A program which
-// reaches one has undefined behavior, and the compiler may optimize
-// accordingly.
-#if defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable)
-#define ABSL_INTERNAL_UNREACHABLE __builtin_unreachable()
-#elif defined(_MSC_VER)
-#define ABSL_INTERNAL_UNREACHABLE __assume(0)
-#else
-#define ABSL_INTERNAL_UNREACHABLE
-#endif
-
 #endif  // ABSL_BASE_MACROS_H_
diff --git a/absl/base/optimization.h b/absl/base/optimization.h
index db5cc09..ad0121a 100644
--- a/absl/base/optimization.h
+++ b/absl/base/optimization.h
@@ -91,6 +91,7 @@
 #define ABSL_CACHELINE_SIZE 64
 #endif
 #endif
+#endif
 
 #ifndef ABSL_CACHELINE_SIZE
 // A reasonable default guess.  Note that overestimates tend to waste more
@@ -141,12 +142,11 @@
 //    the generated machine code.
 // 3) Prefer applying this attribute to individual variables. Avoid
 //    applying it to types. This tends to localize the effect.
+#if defined(__clang__) || defined(__GNUC__)
 #define ABSL_CACHELINE_ALIGNED __attribute__((aligned(ABSL_CACHELINE_SIZE)))
 #elif defined(_MSC_VER)
-#define ABSL_CACHELINE_SIZE 64
 #define ABSL_CACHELINE_ALIGNED __declspec(align(ABSL_CACHELINE_SIZE))
 #else
-#define ABSL_CACHELINE_SIZE 64
 #define ABSL_CACHELINE_ALIGNED
 #endif
 
@@ -181,6 +181,53 @@
 #define ABSL_PREDICT_TRUE(x) (x)
 #endif
 
+// `ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL()` aborts the program in the fastest
+// possible way, with no attempt at logging. One use is to implement hardening
+// aborts with ABSL_OPTION_HARDENED.  Since this is an internal symbol, it
+// should not be used directly outside of Abseil.
+#if ABSL_HAVE_BUILTIN(__builtin_trap) || \
+    (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL() __builtin_trap()
+#else
+#define ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL() abort()
+#endif
+
+// `ABSL_INTERNAL_UNREACHABLE_IMPL()` is the platform specific directive to
+// indicate that a statement is unreachable, and to allow the compiler to
+// optimize accordingly. Clients should use `ABSL_UNREACHABLE()`, which is
+// defined below.
+#if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L
+#define ABSL_INTERNAL_UNREACHABLE_IMPL() std::unreachable()
+#elif defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable)
+#define ABSL_INTERNAL_UNREACHABLE_IMPL() __builtin_unreachable()
+#elif ABSL_HAVE_BUILTIN(__builtin_assume)
+#define ABSL_INTERNAL_UNREACHABLE_IMPL() __builtin_assume(false)
+#elif defined(_MSC_VER)
+#define ABSL_INTERNAL_UNREACHABLE_IMPL() __assume(false)
+#else
+#define ABSL_INTERNAL_UNREACHABLE_IMPL()
+#endif
+
+// `ABSL_UNREACHABLE()` is an unreachable statement.  A program which reaches
+// one has undefined behavior, and the compiler may optimize accordingly.
+#if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG)
+// Abort in hardened mode to avoid dangerous undefined behavior.
+#define ABSL_UNREACHABLE()                \
+  do {                                    \
+    ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL(); \
+    ABSL_INTERNAL_UNREACHABLE_IMPL();     \
+  } while (false)
+#else
+// The assert only fires in debug mode to aid in debugging.
+// When NDEBUG is defined, reaching ABSL_UNREACHABLE() is undefined behavior.
+#define ABSL_UNREACHABLE()                       \
+  do {                                           \
+    /* NOLINTNEXTLINE: misc-static-assert */     \
+    assert(false && "ABSL_UNREACHABLE reached"); \
+    ABSL_INTERNAL_UNREACHABLE_IMPL();            \
+  } while (false)
+#endif
+
 // ABSL_ASSUME(cond)
 //
 // Informs the compiler that a condition is always true and that it can assume
@@ -209,18 +256,23 @@
 #define ABSL_ASSUME(cond) assert(cond)
 #elif ABSL_HAVE_BUILTIN(__builtin_assume)
 #define ABSL_ASSUME(cond) __builtin_assume(cond)
+#elif defined(_MSC_VER)
+#define ABSL_ASSUME(cond) __assume(cond)
+#elif defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L
+#define ABSL_ASSUME(cond)            \
+  do {                               \
+    if (!(cond)) std::unreachable(); \
+  } while (false)
 #elif defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable)
 #define ABSL_ASSUME(cond)                 \
   do {                                    \
     if (!(cond)) __builtin_unreachable(); \
-  } while (0)
-#elif defined(_MSC_VER)
-#define ABSL_ASSUME(cond) __assume(cond)
+  } while (false)
 #else
 #define ABSL_ASSUME(cond)               \
   do {                                  \
     static_cast<void>(false && (cond)); \
-  } while (0)
+  } while (false)
 #endif
 
 // ABSL_INTERNAL_UNIQUE_SMALL_NAME(cond)
diff --git a/absl/base/policy_checks.h b/absl/base/policy_checks.h
index d13073c..372e848 100644
--- a/absl/base/policy_checks.h
+++ b/absl/base/policy_checks.h
@@ -44,17 +44,17 @@
 // Toolchain Check
 // -----------------------------------------------------------------------------
 
-// We support MSVC++ 14.0 update 2 and later.
+// We support Visual Studio 2019 (MSVC++ 16.0) and later.
 // This minimum will go up.
-#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023918 && !defined(__clang__)
-#error "This package requires Visual Studio 2015 Update 2 or higher."
+#if defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__)
+#error "This package requires Visual Studio 2019 (MSVC++ 16.0) or higher."
 #endif
 
-// We support gcc 5 and later.
+// We support GCC 7 and later.
 // This minimum will go up.
 #if defined(__GNUC__) && !defined(__clang__)
-#if __GNUC__ < 5
-#error "This package requires gcc 5 or higher."
+#if __GNUC__ < 7
+#error "This package requires GCC 7 or higher."
 #endif
 #endif
 
diff --git a/absl/base/prefetch.h b/absl/base/prefetch.h
new file mode 100644
index 0000000..de7a180
--- /dev/null
+++ b/absl/base/prefetch.h
@@ -0,0 +1,198 @@
+// Copyright 2023 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: prefetch.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines prefetch functions to prefetch memory contents
+// into the first level cache (L1) for the current CPU. The prefetch logic
+// offered in this header is limited to prefetching first level cachelines
+// only, and is aimed at relatively 'simple' prefetching logic.
+//
+#ifndef ABSL_BASE_PREFETCH_H_
+#define ABSL_BASE_PREFETCH_H_
+
+#include "absl/base/config.h"
+
+#if defined(ABSL_INTERNAL_HAVE_SSE)
+#include <xmmintrin.h>
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1900 && \
+    (defined(_M_X64) || defined(_M_IX86))
+#include <intrin.h>
+#pragma intrinsic(_mm_prefetch)
+#endif
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// Moves data into the L1 cache before it is read, or "prefetches" it.
+//
+// The value of `addr` is the address of the memory to prefetch. If
+// the target and compiler support it, data prefetch instructions are
+// generated. If the prefetch is done some time before the memory is
+// read, it may be in the cache by the time the read occurs.
+//
+// This method prefetches data with the highest degree of temporal locality;
+// data is prefetched where possible into all levels of the cache.
+//
+// Incorrect or gratuitous use of this function can degrade performance.
+// Use this function only when representative benchmarks show an improvement.
+//
+// Example:
+//
+//  // Computes incremental checksum for `data`.
+//  int ComputeChecksum(int sum, absl::string_view data);
+//
+//  // Computes cumulative checksum for all values in `data`
+//  int ComputeChecksum(absl::Span<const std::string> data) {
+//    int sum = 0;
+//    auto it = data.begin();
+//    auto pit = data.begin();
+//    auto end = data.end();
+//    for (int dist = 8; dist > 0 && pit != data.end(); --dist, ++pit) {
+//      absl::PrefetchToLocalCache(pit->data());
+//    }
+//    for (; pit != end; ++pit, ++it) {
+//      sum = ComputeChecksum(sum, *it);
+//      absl::PrefetchToLocalCache(pit->data());
+//    }
+//    for (; it != end; ++it) {
+//      sum = ComputeChecksum(sum, *it);
+//    }
+//    return sum;
+//  }
+//
+void PrefetchToLocalCache(const void* addr);
+
+// Moves data into the L1 cache before it is read, or "prefetches" it.
+//
+// This function is identical to `PrefetchToLocalCache()` except that it has
+// non-temporal locality: the fetched data should not be left in any of the
+// cache tiers. This is useful for cases where the data is used only once /
+// short term, for example, invoking a destructor on an object.
+//
+// Incorrect or gratuitous use of this function can degrade performance.
+// Use this function only when representative benchmarks show an improvement.
+//
+// Example:
+//
+//  template <typename Iterator>
+//  void DestroyPointers(Iterator begin, Iterator end) {
+//    size_t distance = std::min(8U, bars.size());
+//
+//    int dist = 8;
+//    auto prefetch_it = begin;
+//    while (prefetch_it != end && --dist;) {
+//      absl::PrefetchToLocalCacheNta(*prefetch_it++);
+//    }
+//    while (prefetch_it != end) {
+//      delete *begin++;
+//      absl::PrefetchToLocalCacheNta(*prefetch_it++);
+//    }
+//    while (begin != end) {
+//      delete *begin++;
+//    }
+//  }
+//
+void PrefetchToLocalCacheNta(const void* addr);
+
+// Moves data into the L1 cache with the intent to modify it.
+//
+// This function is similar to `PrefetchToLocalCache()` except that it
+// prefetches cachelines with an 'intent to modify' This typically includes
+// invalidating cache entries for this address in all other cache tiers, and an
+// exclusive access intent.
+//
+// Incorrect or gratuitous use of this function can degrade performance. As this
+// function can invalidate cached cachelines on other caches and computer cores,
+// incorrect usage of this function can have an even greater negative impact
+// than incorrect regular prefetches.
+// Use this function only when representative benchmarks show an improvement.
+//
+// Example:
+//
+//  void* Arena::Allocate(size_t size) {
+//    void* ptr = AllocateBlock(size);
+//    absl::PrefetchToLocalCacheForWrite(p);
+//    return ptr;
+//  }
+//
+void PrefetchToLocalCacheForWrite(const void* addr);
+
+#if ABSL_HAVE_BUILTIN(__builtin_prefetch) || defined(__GNUC__)
+
+#define ABSL_HAVE_PREFETCH 1
+
+// See __builtin_prefetch:
+// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html.
+//
+inline void PrefetchToLocalCache(const void* addr) {
+  __builtin_prefetch(addr, 0, 3);
+}
+
+inline void PrefetchToLocalCacheNta(const void* addr) {
+  __builtin_prefetch(addr, 0, 0);
+}
+
+inline void PrefetchToLocalCacheForWrite(const void* addr) {
+  // [x86] gcc/clang don't generate PREFETCHW for __builtin_prefetch(.., 1)
+  // unless -march=broadwell or newer; this is not generally the default, so we
+  // manually emit prefetchw. PREFETCHW is recognized as a no-op on older Intel
+  // processors and has been present on AMD processors since the K6-2.
+#if defined(__x86_64__)
+  asm("prefetchw (%0)" : : "r"(addr));
+#else
+  __builtin_prefetch(addr, 1, 3);
+#endif
+}
+
+#elif defined(ABSL_INTERNAL_HAVE_SSE)
+
+#define ABSL_HAVE_PREFETCH 1
+
+inline void PrefetchToLocalCache(const void* addr) {
+  _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_T0);
+}
+
+inline void PrefetchToLocalCacheNta(const void* addr) {
+  _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_NTA);
+}
+
+inline void PrefetchToLocalCacheForWrite(const void* addr) {
+#if defined(_MM_HINT_ET0)
+  _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_ET0);
+#elif !defined(_MSC_VER) && defined(__x86_64__)
+  // _MM_HINT_ET0 is not universally supported. As we commented further
+  // up, PREFETCHW is recognized as a no-op on older Intel processors
+  // and has been present on AMD processors since the K6-2. We have this
+  // disabled for MSVC compilers as this miscompiles on older MSVC compilers.
+  asm("prefetchw (%0)" : : "r"(addr));
+#endif
+}
+
+#else
+
+inline void PrefetchToLocalCache(const void* addr) {}
+inline void PrefetchToLocalCacheNta(const void* addr) {}
+inline void PrefetchToLocalCacheForWrite(const void* addr) {}
+
+#endif
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_BASE_PREFETCH_H_
diff --git a/absl/base/prefetch_test.cc b/absl/base/prefetch_test.cc
new file mode 100644
index 0000000..ee21989
--- /dev/null
+++ b/absl/base/prefetch_test.cc
@@ -0,0 +1,64 @@
+// Copyright 2023 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/prefetch.h"
+
+#include <memory>
+
+#include "gtest/gtest.h"
+
+namespace {
+
+// Below tests exercise the functions only to guarantee they compile and execute
+// correctly. We make no attempt at verifying any prefetch instructions being
+// generated and executed: we assume the various implementation in terms of
+// __builtin_prefetch() or x86 intrinsics to be correct and well tested.
+
+TEST(PrefetchTest, PrefetchToLocalCache_StackA) {
+  char buf[100] = {};
+  absl::PrefetchToLocalCache(buf);
+  absl::PrefetchToLocalCacheNta(buf);
+  absl::PrefetchToLocalCacheForWrite(buf);
+}
+
+TEST(PrefetchTest, PrefetchToLocalCache_Heap) {
+  auto memory = std::make_unique<char[]>(200 << 10);
+  memset(memory.get(), 0, 200 << 10);
+  absl::PrefetchToLocalCache(memory.get());
+  absl::PrefetchToLocalCacheNta(memory.get());
+  absl::PrefetchToLocalCacheForWrite(memory.get());
+  absl::PrefetchToLocalCache(memory.get() + (50 << 10));
+  absl::PrefetchToLocalCacheNta(memory.get() + (50 << 10));
+  absl::PrefetchToLocalCacheForWrite(memory.get() + (50 << 10));
+  absl::PrefetchToLocalCache(memory.get() + (100 << 10));
+  absl::PrefetchToLocalCacheNta(memory.get() + (100 << 10));
+  absl::PrefetchToLocalCacheForWrite(memory.get() + (100 << 10));
+  absl::PrefetchToLocalCache(memory.get() + (150 << 10));
+  absl::PrefetchToLocalCacheNta(memory.get() + (150 << 10));
+  absl::PrefetchToLocalCacheForWrite(memory.get() + (150 << 10));
+}
+
+TEST(PrefetchTest, PrefetchToLocalCache_Nullptr) {
+  absl::PrefetchToLocalCache(nullptr);
+  absl::PrefetchToLocalCacheNta(nullptr);
+  absl::PrefetchToLocalCacheForWrite(nullptr);
+}
+
+TEST(PrefetchTest, PrefetchToLocalCache_InvalidPtr) {
+  absl::PrefetchToLocalCache(reinterpret_cast<const void*>(0x785326532L));
+  absl::PrefetchToLocalCacheNta(reinterpret_cast<const void*>(0x785326532L));
+  absl::PrefetchToLocalCacheForWrite(reinterpret_cast<const void*>(0x78532L));
+}
+
+}  // namespace
diff --git a/absl/base/throw_delegate_test.cc b/absl/base/throw_delegate_test.cc
index 5ba4ce5..e74362b 100644
--- a/absl/base/throw_delegate_test.cc
+++ b/absl/base/throw_delegate_test.cc
@@ -78,29 +78,97 @@
 #endif
 }
 
-TEST(ThrowHelper, Test) {
-  // Not using EXPECT_THROW because we want to check the .what() message too.
+TEST(ThrowDelegate, ThrowStdLogicErrorChar) {
   ExpectThrowChar<std::logic_error>(ThrowStdLogicError);
+}
+
+TEST(ThrowDelegate, ThrowStdInvalidArgumentChar) {
   ExpectThrowChar<std::invalid_argument>(ThrowStdInvalidArgument);
+}
+
+TEST(ThrowDelegate, ThrowStdDomainErrorChar) {
   ExpectThrowChar<std::domain_error>(ThrowStdDomainError);
+}
+
+TEST(ThrowDelegate, ThrowStdLengthErrorChar) {
   ExpectThrowChar<std::length_error>(ThrowStdLengthError);
+}
+
+TEST(ThrowDelegate, ThrowStdOutOfRangeChar) {
   ExpectThrowChar<std::out_of_range>(ThrowStdOutOfRange);
+}
+
+TEST(ThrowDelegate, ThrowStdRuntimeErrorChar) {
   ExpectThrowChar<std::runtime_error>(ThrowStdRuntimeError);
+}
+
+TEST(ThrowDelegate, ThrowStdRangeErrorChar) {
   ExpectThrowChar<std::range_error>(ThrowStdRangeError);
+}
+
+TEST(ThrowDelegate, ThrowStdOverflowErrorChar) {
   ExpectThrowChar<std::overflow_error>(ThrowStdOverflowError);
+}
+
+TEST(ThrowDelegate, ThrowStdUnderflowErrorChar) {
   ExpectThrowChar<std::underflow_error>(ThrowStdUnderflowError);
+}
 
+TEST(ThrowDelegate, ThrowStdLogicErrorString) {
   ExpectThrowString<std::logic_error>(ThrowStdLogicError);
-  ExpectThrowString<std::invalid_argument>(ThrowStdInvalidArgument);
-  ExpectThrowString<std::domain_error>(ThrowStdDomainError);
-  ExpectThrowString<std::length_error>(ThrowStdLengthError);
-  ExpectThrowString<std::out_of_range>(ThrowStdOutOfRange);
-  ExpectThrowString<std::runtime_error>(ThrowStdRuntimeError);
-  ExpectThrowString<std::range_error>(ThrowStdRangeError);
-  ExpectThrowString<std::overflow_error>(ThrowStdOverflowError);
-  ExpectThrowString<std::underflow_error>(ThrowStdUnderflowError);
+}
 
-  ExpectThrowNoWhat<std::bad_function_call>(ThrowStdBadFunctionCall);
+TEST(ThrowDelegate, ThrowStdInvalidArgumentString) {
+  ExpectThrowString<std::invalid_argument>(ThrowStdInvalidArgument);
+}
+
+TEST(ThrowDelegate, ThrowStdDomainErrorString) {
+  ExpectThrowString<std::domain_error>(ThrowStdDomainError);
+}
+
+TEST(ThrowDelegate, ThrowStdLengthErrorString) {
+  ExpectThrowString<std::length_error>(ThrowStdLengthError);
+}
+
+TEST(ThrowDelegate, ThrowStdOutOfRangeString) {
+  ExpectThrowString<std::out_of_range>(ThrowStdOutOfRange);
+}
+
+TEST(ThrowDelegate, ThrowStdRuntimeErrorString) {
+  ExpectThrowString<std::runtime_error>(ThrowStdRuntimeError);
+}
+
+TEST(ThrowDelegate, ThrowStdRangeErrorString) {
+  ExpectThrowString<std::range_error>(ThrowStdRangeError);
+}
+
+TEST(ThrowDelegate, ThrowStdOverflowErrorString) {
+  ExpectThrowString<std::overflow_error>(ThrowStdOverflowError);
+}
+
+TEST(ThrowDelegate, ThrowStdUnderflowErrorString) {
+  ExpectThrowString<std::underflow_error>(ThrowStdUnderflowError);
+}
+
+TEST(ThrowDelegate, ThrowStdBadFunctionCallNoWhat) {
+#ifdef ABSL_HAVE_EXCEPTIONS
+  try {
+    ThrowStdBadFunctionCall();
+    FAIL() << "Didn't throw";
+  } catch (const std::bad_function_call&) {
+  }
+#ifdef _LIBCPP_VERSION
+  catch (const std::exception&) {
+    // https://reviews.llvm.org/D92397 causes issues with the vtable for
+    // std::bad_function_call when using libc++ as a shared library.
+  }
+#endif
+#else
+  EXPECT_DEATH_IF_SUPPORTED(ThrowStdBadFunctionCall(), "");
+#endif
+}
+
+TEST(ThrowDelegate, ThrowStdBadAllocNoWhat) {
   ExpectThrowNoWhat<std::bad_alloc>(ThrowStdBadAlloc);
 }
 
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index 6b4cedd..902f6ed 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -134,6 +134,7 @@
         "//absl/base:core_headers",
         "//absl/base:throw_delegate",
         "//absl/memory",
+        "//absl/meta:type_traits",
     ],
 )
 
@@ -392,6 +393,9 @@
     hdrs = ["internal/hash_function_defaults.h"],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = [
+        "//visibility:private",
+    ],
     deps = [
         "//absl/base:config",
         "//absl/hash",
@@ -530,11 +534,13 @@
         "//absl/base",
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/base:raw_logging_internal",
         "//absl/debugging:stacktrace",
         "//absl/memory",
         "//absl/profiling:exponential_biased",
         "//absl/profiling:sample_recorder",
         "//absl/synchronization",
+        "//absl/time",
         "//absl/utility",
     ],
 )
@@ -616,8 +622,11 @@
         ":hashtablez_sampler",
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/base:dynamic_annotations",
         "//absl/base:endian",
         "//absl/base:prefetch",
+        "//absl/base:raw_logging_internal",
+        "//absl/hash",
         "//absl/memory",
         "//absl/meta:type_traits",
         "//absl/numeric:bits",
@@ -983,13 +992,16 @@
         ":btree_test_common",
         ":counting_allocator",
         ":test_instance_tracker",
+        "//absl/algorithm:container",
         "//absl/base:core_headers",
         "//absl/base:raw_logging_internal",
         "//absl/flags:flag",
         "//absl/hash:hash_testing",
         "//absl/memory",
+        "//absl/random",
         "//absl/strings",
         "//absl/types:compare",
+        "//absl/types:optional",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -1010,10 +1022,12 @@
         ":flat_hash_map",
         ":flat_hash_set",
         ":hashtable_debug",
+        "//absl/algorithm:container",
         "//absl/base:raw_logging_internal",
         "//absl/hash",
         "//absl/log",
         "//absl/memory",
+        "//absl/random",
         "//absl/strings:cord",
         "//absl/strings:str_format",
         "//absl/time",
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt
index 6c2931b..3c48270 100644
--- a/absl/container/CMakeLists.txt
+++ b/absl/container/CMakeLists.txt
@@ -72,6 +72,7 @@
   LINKOPTS
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
+    absl::algorithm_container
     absl::btree
     absl::btree_test_common
     absl::compare
@@ -79,6 +80,8 @@
     absl::counting_allocator
     absl::flags
     absl::hash_testing
+    absl::optional
+    absl::random_random
     absl::raw_logging_internal
     absl::strings
     absl::test_instance_tracker
@@ -194,6 +197,7 @@
     absl::inlined_vector_internal
     absl::throw_delegate
     absl::memory
+    absl::type_traits
   PUBLIC
 )
 
@@ -589,8 +593,10 @@
     absl::base
     absl::config
     absl::exponential_biased
+    absl::raw_logging_internal
     absl::sample_recorder
     absl::synchronization
+    absl::time
 )
 
 absl_cc_test(
@@ -701,15 +707,18 @@
     absl::container_common
     absl::container_memory
     absl::core_headers
+    absl::dynamic_annotations
     absl::endian
+    absl::hash
     absl::hash_policy_traits
     absl::hashtable_debug_hooks
+    absl::hashtablez_sampler
     absl::memory
     absl::meta
     absl::optional
     absl::prefetch
+    absl::raw_logging_internal
     absl::utility
-    absl::hashtablez_sampler
   PUBLIC
 )
 
@@ -721,18 +730,18 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
+    absl::base
+    absl::config
     absl::container_memory
+    absl::core_headers
     absl::flat_hash_map
     absl::flat_hash_set
     absl::hash_function_defaults
     absl::hash_policy_testing
     absl::hashtable_debug
-    absl::raw_hash_set
-    absl::base
-    absl::config
     absl::log
-    absl::core_headers
     absl::prefetch
+    absl::raw_hash_set
     absl::raw_logging_internal
     absl::strings
     GTest::gmock_main
diff --git a/absl/container/btree_benchmark.cc b/absl/container/btree_benchmark.cc
index cc9a106..0d26fd4 100644
--- a/absl/container/btree_benchmark.cc
+++ b/absl/container/btree_benchmark.cc
@@ -27,6 +27,7 @@
 #include <vector>
 
 #include "benchmark/benchmark.h"
+#include "absl/algorithm/container.h"
 #include "absl/base/internal/raw_logging.h"
 #include "absl/container/btree_map.h"
 #include "absl/container/btree_set.h"
@@ -37,6 +38,7 @@
 #include "absl/hash/hash.h"
 #include "absl/log/log.h"
 #include "absl/memory/memory.h"
+#include "absl/random/random.h"
 #include "absl/strings/cord.h"
 #include "absl/strings/str_format.h"
 #include "absl/time/time.h"
@@ -733,6 +735,29 @@
 
 BIG_TYPE_PTR_BENCHMARKS(32);
 
+void BM_BtreeSet_IteratorSubtraction(benchmark::State& state) {
+  absl::InsecureBitGen bitgen;
+  std::vector<int> vec;
+  // Randomize the set's insertion order so the nodes aren't all full.
+  vec.reserve(state.range(0));
+  for (int i = 0; i < state.range(0); ++i) vec.push_back(i);
+  absl::c_shuffle(vec, bitgen);
+
+  absl::btree_set<int> set;
+  for (int i : vec) set.insert(i);
+
+  size_t distance = absl::Uniform(bitgen, 0u, set.size());
+  while (state.KeepRunningBatch(distance)) {
+    size_t end = absl::Uniform(bitgen, distance, set.size());
+    size_t begin = end - distance;
+    benchmark::DoNotOptimize(set.find(static_cast<int>(end)) -
+                             set.find(static_cast<int>(begin)));
+    distance = absl::Uniform(bitgen, 0u, set.size());
+  }
+}
+
+BENCHMARK(BM_BtreeSet_IteratorSubtraction)->Range(1 << 10, 1 << 20);
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h
index 286817f..cd3ee2b 100644
--- a/absl/container/btree_map.h
+++ b/absl/container/btree_map.h
@@ -42,10 +42,13 @@
 // Importantly, insertions and deletions may invalidate outstanding iterators,
 // pointers, and references to elements. Such invalidations are typically only
 // an issue if insertion and deletion operations are interleaved with the use of
-// more than one iterator, pointer, or reference simultaneously. For this
-// reason, `insert()` and `erase()` return a valid iterator at the current
-// position. Another important difference is that key-types must be
-// copy-constructible.
+// more than one iterator, pointer, or reference simultaneously.  For this
+// reason, `insert()`, `erase()`, and `extract_and_get_next()` return a valid
+// iterator at the current position. Another important difference is that
+// key-types must be copy-constructible.
+//
+// Another API difference is that btree iterators can be subtracted, and this
+// is faster than using std::distance.
 
 #ifndef ABSL_CONTAINER_BTREE_MAP_H_
 #define ABSL_CONTAINER_BTREE_MAP_H_
@@ -322,7 +325,8 @@
   // btree_map::extract()
   //
   // Extracts the indicated element, erasing it in the process, and returns it
-  // as a C++17-compatible node handle. Overloads are listed below.
+  // as a C++17-compatible node handle. Any references, pointers, or iterators
+  // are invalidated. Overloads are listed below.
   //
   // node_type extract(const_iterator position):
   //
@@ -347,6 +351,21 @@
   // It does NOT refer to the data layout of the underlying btree.
   using Base::extract;
 
+  // btree_map::extract_and_get_next()
+  //
+  // Extracts the indicated element, erasing it in the process, and returns it
+  // as a C++17-compatible node handle along with an iterator to the next
+  // element.
+  //
+  // extract_and_get_next_return_type extract_and_get_next(
+  //     const_iterator position):
+  //
+  //   Extracts the element at the indicated position, returns a struct
+  //   containing a member named `node`: a node handle owning that extracted
+  //   data and a member named `next`: an iterator pointing to the next element
+  //   in the btree.
+  using Base::extract_and_get_next;
+
   // btree_map::merge()
   //
   // Extracts elements from a given `source` btree_map into this
@@ -698,6 +717,21 @@
   // It does NOT refer to the data layout of the underlying btree.
   using Base::extract;
 
+  // btree_multimap::extract_and_get_next()
+  //
+  // Extracts the indicated element, erasing it in the process, and returns it
+  // as a C++17-compatible node handle along with an iterator to the next
+  // element.
+  //
+  // extract_and_get_next_return_type extract_and_get_next(
+  //     const_iterator position):
+  //
+  //   Extracts the element at the indicated position, returns a struct
+  //   containing a member named `node`: a node handle owning that extracted
+  //   data and a member named `next`: an iterator pointing to the next element
+  //   in the btree.
+  using Base::extract_and_get_next;
+
   // btree_multimap::merge()
   //
   // Extracts all elements from a given `source` btree_multimap into this
diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h
index e823a2a..51dc42b 100644
--- a/absl/container/btree_set.h
+++ b/absl/container/btree_set.h
@@ -43,8 +43,11 @@
 // pointers, and references to elements. Such invalidations are typically only
 // an issue if insertion and deletion operations are interleaved with the use of
 // more than one iterator, pointer, or reference simultaneously. For this
-// reason, `insert()` and `erase()` return a valid iterator at the current
-// position.
+// reason, `insert()`, `erase()`, and `extract_and_get_next()` return a valid
+// iterator at the current position.
+//
+// Another API difference is that btree iterators can be subtracted, and this
+// is faster than using std::distance.
 
 #ifndef ABSL_CONTAINER_BTREE_SET_H_
 #define ABSL_CONTAINER_BTREE_SET_H_
@@ -269,7 +272,8 @@
   // btree_set::extract()
   //
   // Extracts the indicated element, erasing it in the process, and returns it
-  // as a C++17-compatible node handle. Overloads are listed below.
+  // as a C++17-compatible node handle. Any references, pointers, or iterators
+  // are invalidated. Overloads are listed below.
   //
   // node_type extract(const_iterator position):
   //
@@ -289,6 +293,21 @@
   // It does NOT refer to the data layout of the underlying btree.
   using Base::extract;
 
+  // btree_set::extract_and_get_next()
+  //
+  // Extracts the indicated element, erasing it in the process, and returns it
+  // as a C++17-compatible node handle along with an iterator to the next
+  // element.
+  //
+  // extract_and_get_next_return_type extract_and_get_next(
+  //     const_iterator position):
+  //
+  //   Extracts the element at the indicated position, returns a struct
+  //   containing a member named `node`: a node handle owning that extracted
+  //   data and a member named `next`: an iterator pointing to the next element
+  //   in the btree.
+  using Base::extract_and_get_next;
+
   // btree_set::merge()
   //
   // Extracts elements from a given `source` btree_set into this
@@ -611,6 +630,21 @@
   // It does NOT refer to the data layout of the underlying btree.
   using Base::extract;
 
+  // btree_multiset::extract_and_get_next()
+  //
+  // Extracts the indicated element, erasing it in the process, and returns it
+  // as a C++17-compatible node handle along with an iterator to the next
+  // element.
+  //
+  // extract_and_get_next_return_type extract_and_get_next(
+  //     const_iterator position):
+  //
+  //   Extracts the element at the indicated position, returns a struct
+  //   containing a member named `node`: a node handle owning that extracted
+  //   data and a member named `next`: an iterator pointing to the next element
+  //   in the btree.
+  using Base::extract_and_get_next;
+
   // btree_multiset::merge()
   //
   // Extracts all elements from a given `source` btree_multiset into this
diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc
index 9386a6b..72f446b 100644
--- a/absl/container/btree_test.cc
+++ b/absl/container/btree_test.cc
@@ -18,6 +18,7 @@
 #include <array>
 #include <cstdint>
 #include <functional>
+#include <iostream>
 #include <iterator>
 #include <limits>
 #include <map>
@@ -31,6 +32,7 @@
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/algorithm/container.h"
 #include "absl/base/internal/raw_logging.h"
 #include "absl/base/macros.h"
 #include "absl/container/btree_map.h"
@@ -40,10 +42,12 @@
 #include "absl/flags/flag.h"
 #include "absl/hash/hash_testing.h"
 #include "absl/memory/memory.h"
+#include "absl/random/random.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_split.h"
 #include "absl/strings/string_view.h"
 #include "absl/types/compare.h"
+#include "absl/types/optional.h"
 
 ABSL_FLAG(int, test_values, 10000, "The number of values to use for tests");
 
@@ -72,6 +76,16 @@
   CheckPairEquals(x.first, y.first);
   CheckPairEquals(x.second, y.second);
 }
+
+bool IsAssertEnabled() {
+  // Use an assert with side-effects to figure out if they are actually enabled.
+  bool assert_enabled = false;
+  assert([&]() {  // NOLINT
+    assert_enabled = true;
+    return true;
+  }());
+  return assert_enabled;
+}
 }  // namespace
 
 // The base class for a sorted associative container checker. TreeType is the
@@ -1219,8 +1233,10 @@
   }
 
   template <typename Btree>
-  constexpr static bool UsesGenerations() {
-    return Btree::params_type::kEnableGenerations;
+  constexpr static bool FieldTypeEqualsSlotType() {
+    return std::is_same<
+        typename btree_node<typename Btree::params_type>::field_type,
+        typename btree_node<typename Btree::params_type>::slot_type>::value;
   }
 };
 
@@ -1449,7 +1465,7 @@
   using Base = typename SizedBtreeSet::btree_set_container;
 
  public:
-  SizedBtreeSet() {}
+  SizedBtreeSet() = default;
   using Base::Base;
 };
 
@@ -1467,9 +1483,17 @@
   tracker->ResetCopiesMovesSwaps();
 }
 
+#ifdef ABSL_HAVE_ADDRESS_SANITIZER
+constexpr bool kAsan = true;
+#else
+constexpr bool kAsan = false;
+#endif
+
 // Note: when the values in this test change, it is expected to have an impact
 // on performance.
 TEST(Btree, MovesComparisonsCopiesSwapsTracking) {
+  if (kAsan) GTEST_SKIP() << "We do extra operations in ASan mode.";
+
   InstanceTracker tracker;
   // Note: this is minimum number of values per node.
   SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/4> set4;
@@ -1487,10 +1511,9 @@
   EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>(), 61);
   EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set100)>(), 100);
   if (sizeof(void *) == 8) {
-    EXPECT_EQ(
-        BtreeNodePeer::GetNumSlotsPerNode<absl::btree_set<int32_t>>(),
-        // When we have generations, there is one fewer slot.
-        BtreeNodePeer::UsesGenerations<absl::btree_set<int32_t>>() ? 60 : 61);
+    EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<absl::btree_set<int32_t>>(),
+              // When we have generations, there is one fewer slot.
+              BtreeGenerationsEnabled() ? 60 : 61);
   }
 
   // Test key insertion/deletion in random order.
@@ -1521,6 +1544,8 @@
 // Note: when the values in this test change, it is expected to have an impact
 // on performance.
 TEST(Btree, MovesComparisonsCopiesSwapsTrackingThreeWayCompare) {
+  if (kAsan) GTEST_SKIP() << "We do extra operations in ASan mode.";
+
   InstanceTracker tracker;
   // Note: this is minimum number of values per node.
   SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/4,
@@ -1544,10 +1569,9 @@
   EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>(), 61);
   EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set100)>(), 100);
   if (sizeof(void *) == 8) {
-    EXPECT_EQ(
-        BtreeNodePeer::GetNumSlotsPerNode<absl::btree_set<int32_t>>(),
-        // When we have generations, there is one fewer slot.
-        BtreeNodePeer::UsesGenerations<absl::btree_set<int32_t>>() ? 60 : 61);
+    EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<absl::btree_set<int32_t>>(),
+              // When we have generations, there is one fewer slot.
+              BtreeGenerationsEnabled() ? 60 : 61);
   }
 
   // Test key insertion/deletion in random order.
@@ -1649,10 +1673,9 @@
   auto iter = s.emplace(value_to_insert);
   ASSERT_NE(iter, s.end());
   EXPECT_EQ(*iter, value_to_insert);
-  auto iter2 = s.emplace(value_to_insert);
-  EXPECT_NE(iter2, iter);
-  ASSERT_NE(iter2, s.end());
-  EXPECT_EQ(*iter2, value_to_insert);
+  iter = s.emplace(value_to_insert);
+  ASSERT_NE(iter, s.end());
+  EXPECT_EQ(*iter, value_to_insert);
   auto result = s.equal_range(value_to_insert);
   EXPECT_EQ(std::distance(result.first, result.second), 2);
 }
@@ -1663,44 +1686,45 @@
   auto iter = s.emplace(value_to_insert);
   ASSERT_NE(iter, s.end());
   EXPECT_EQ(*iter, value_to_insert);
-  auto emplace_iter = s.emplace_hint(iter, value_to_insert);
-  EXPECT_NE(emplace_iter, iter);
-  ASSERT_NE(emplace_iter, s.end());
-  EXPECT_EQ(*emplace_iter, value_to_insert);
+  iter = s.emplace_hint(iter, value_to_insert);
+  // The new element should be before the previously inserted one.
+  EXPECT_EQ(iter, s.lower_bound(value_to_insert));
+  ASSERT_NE(iter, s.end());
+  EXPECT_EQ(*iter, value_to_insert);
 }
 
 TEST(Btree, BtreeMultimapEmplace) {
   const int key_to_insert = 123456;
   const char value0[] = "a";
-  absl::btree_multimap<int, std::string> s;
-  auto iter = s.emplace(key_to_insert, value0);
-  ASSERT_NE(iter, s.end());
+  absl::btree_multimap<int, std::string> m;
+  auto iter = m.emplace(key_to_insert, value0);
+  ASSERT_NE(iter, m.end());
   EXPECT_EQ(iter->first, key_to_insert);
   EXPECT_EQ(iter->second, value0);
   const char value1[] = "b";
-  auto iter2 = s.emplace(key_to_insert, value1);
-  EXPECT_NE(iter2, iter);
-  ASSERT_NE(iter2, s.end());
-  EXPECT_EQ(iter2->first, key_to_insert);
-  EXPECT_EQ(iter2->second, value1);
-  auto result = s.equal_range(key_to_insert);
+  iter = m.emplace(key_to_insert, value1);
+  ASSERT_NE(iter, m.end());
+  EXPECT_EQ(iter->first, key_to_insert);
+  EXPECT_EQ(iter->second, value1);
+  auto result = m.equal_range(key_to_insert);
   EXPECT_EQ(std::distance(result.first, result.second), 2);
 }
 
 TEST(Btree, BtreeMultimapEmplaceHint) {
   const int key_to_insert = 123456;
   const char value0[] = "a";
-  absl::btree_multimap<int, std::string> s;
-  auto iter = s.emplace(key_to_insert, value0);
-  ASSERT_NE(iter, s.end());
+  absl::btree_multimap<int, std::string> m;
+  auto iter = m.emplace(key_to_insert, value0);
+  ASSERT_NE(iter, m.end());
   EXPECT_EQ(iter->first, key_to_insert);
   EXPECT_EQ(iter->second, value0);
   const char value1[] = "b";
-  auto emplace_iter = s.emplace_hint(iter, key_to_insert, value1);
-  EXPECT_NE(emplace_iter, iter);
-  ASSERT_NE(emplace_iter, s.end());
-  EXPECT_EQ(emplace_iter->first, key_to_insert);
-  EXPECT_EQ(emplace_iter->second, value1);
+  iter = m.emplace_hint(iter, key_to_insert, value1);
+  // The new element should be before the previously inserted one.
+  EXPECT_EQ(iter, m.lower_bound(key_to_insert));
+  ASSERT_NE(iter, m.end());
+  EXPECT_EQ(iter->first, key_to_insert);
+  EXPECT_EQ(iter->second, value1);
 }
 
 TEST(Btree, ConstIteratorAccessors) {
@@ -2111,6 +2135,79 @@
   }
 }
 
+TEST(Btree, ExtractAndGetNextSet) {
+  absl::btree_set<int> src = {1, 2, 3, 4, 5};
+  auto it = src.find(3);
+  auto extracted_and_next = src.extract_and_get_next(it);
+  EXPECT_THAT(src, ElementsAre(1, 2, 4, 5));
+  EXPECT_EQ(extracted_and_next.node.value(), 3);
+  EXPECT_EQ(*extracted_and_next.next, 4);
+}
+
+TEST(Btree, ExtractAndGetNextMultiSet) {
+  absl::btree_multiset<int> src = {1, 2, 3, 4, 5};
+  auto it = src.find(3);
+  auto extracted_and_next = src.extract_and_get_next(it);
+  EXPECT_THAT(src, ElementsAre(1, 2, 4, 5));
+  EXPECT_EQ(extracted_and_next.node.value(), 3);
+  EXPECT_EQ(*extracted_and_next.next, 4);
+}
+
+TEST(Btree, ExtractAndGetNextMap) {
+  absl::btree_map<int, int> src = {{1, 2}, {3, 4}, {5, 6}};
+  auto it = src.find(3);
+  auto extracted_and_next = src.extract_and_get_next(it);
+  EXPECT_THAT(src, ElementsAre(Pair(1, 2), Pair(5, 6)));
+  EXPECT_EQ(extracted_and_next.node.key(), 3);
+  EXPECT_EQ(extracted_and_next.node.mapped(), 4);
+  EXPECT_THAT(*extracted_and_next.next, Pair(5, 6));
+}
+
+TEST(Btree, ExtractAndGetNextMultiMap) {
+  absl::btree_multimap<int, int> src = {{1, 2}, {3, 4}, {5, 6}};
+  auto it = src.find(3);
+  auto extracted_and_next = src.extract_and_get_next(it);
+  EXPECT_THAT(src, ElementsAre(Pair(1, 2), Pair(5, 6)));
+  EXPECT_EQ(extracted_and_next.node.key(), 3);
+  EXPECT_EQ(extracted_and_next.node.mapped(), 4);
+  EXPECT_THAT(*extracted_and_next.next, Pair(5, 6));
+}
+
+TEST(Btree, ExtractAndGetNextEndIter) {
+  absl::btree_set<int> src = {1, 2, 3, 4, 5};
+  auto it = src.find(5);
+  auto extracted_and_next = src.extract_and_get_next(it);
+  EXPECT_THAT(src, ElementsAre(1, 2, 3, 4));
+  EXPECT_EQ(extracted_and_next.node.value(), 5);
+  EXPECT_EQ(extracted_and_next.next, src.end());
+}
+
+TEST(Btree, ExtractDoesntCauseExtraMoves) {
+#ifdef _MSC_VER
+  GTEST_SKIP() << "This test fails on MSVC.";
+#endif
+
+  using Set = absl::btree_set<MovableOnlyInstance>;
+  std::array<std::function<void(Set &)>, 3> extracters = {
+      [](Set &s) { auto node = s.extract(s.begin()); },
+      [](Set &s) { auto ret = s.extract_and_get_next(s.begin()); },
+      [](Set &s) { auto node = s.extract(MovableOnlyInstance(0)); }};
+
+  InstanceTracker tracker;
+  for (int i = 0; i < 3; ++i) {
+    Set s;
+    s.insert(MovableOnlyInstance(0));
+    tracker.ResetCopiesMovesSwaps();
+
+    extracters[i](s);
+    // We expect to see exactly 1 move: from the original slot into the
+    // extracted node.
+    EXPECT_EQ(tracker.copies(), 0) << i;
+    EXPECT_EQ(tracker.moves(), 1) << i;
+    EXPECT_EQ(tracker.swaps(), 0) << i;
+  }
+}
+
 // For multisets, insert with hint also affects correctness because we need to
 // insert immediately before the hint if possible.
 struct InsertMultiHintData {
@@ -3003,8 +3100,9 @@
   absl::btree_set<MultiKey, MultiKeyComp> set = {{}, MultiKeyComp{}};
 }
 
-#ifndef NDEBUG
 TEST(Btree, InvalidComparatorsCaught) {
+  if (!IsAssertEnabled()) GTEST_SKIP() << "Assertions not enabled.";
+
   {
     struct ZeroAlwaysLessCmp {
       bool operator()(int lhs, int rhs) const {
@@ -3051,28 +3149,104 @@
     absl::btree_set<int, ThreeWaySumGreaterZeroCmp> set;
     EXPECT_DEATH(set.insert({0, 1, 2}), "lhs_comp_rhs < 0 -> rhs_comp_lhs > 0");
   }
+  // Verify that we detect cases of comparators that violate transitivity.
+  // When the comparators below check for the presence of an optional field,
+  // they violate transitivity because instances that have the optional field
+  // compare differently with each other from how they compare with instances
+  // that don't have the optional field.
+  struct ClockTime {
+    absl::optional<int> hour;
+    int minute;
+  };
+  // `comp(a,b) && comp(b,c) && !comp(a,c)` violates transitivity.
+  ClockTime a = {absl::nullopt, 1};
+  ClockTime b = {2, 5};
+  ClockTime c = {6, 0};
+  {
+    struct NonTransitiveTimeCmp {
+      bool operator()(ClockTime lhs, ClockTime rhs) const {
+        if (lhs.hour.has_value() && rhs.hour.has_value() &&
+            *lhs.hour != *rhs.hour) {
+          return *lhs.hour < *rhs.hour;
+        }
+        return lhs.minute < rhs.minute;
+      }
+    };
+    NonTransitiveTimeCmp cmp;
+    ASSERT_TRUE(cmp(a, b) && cmp(b, c) && !cmp(a, c));
+    absl::btree_set<ClockTime, NonTransitiveTimeCmp> set;
+    EXPECT_DEATH(set.insert({a, b, c}), "is_ordered_correctly");
+    absl::btree_multiset<ClockTime, NonTransitiveTimeCmp> mset;
+    EXPECT_DEATH(mset.insert({a, a, b, b, c, c}), "is_ordered_correctly");
+  }
+  {
+    struct ThreeWayNonTransitiveTimeCmp {
+      absl::weak_ordering operator()(ClockTime lhs, ClockTime rhs) const {
+        if (lhs.hour.has_value() && rhs.hour.has_value() &&
+            *lhs.hour != *rhs.hour) {
+          return *lhs.hour < *rhs.hour ? absl::weak_ordering::less
+                                       : absl::weak_ordering::greater;
+        }
+        return lhs.minute < rhs.minute    ? absl::weak_ordering::less
+               : lhs.minute == rhs.minute ? absl::weak_ordering::equivalent
+                                          : absl::weak_ordering::greater;
+      }
+    };
+    ThreeWayNonTransitiveTimeCmp cmp;
+    ASSERT_TRUE(cmp(a, b) < 0 && cmp(b, c) < 0 && cmp(a, c) > 0);
+    absl::btree_set<ClockTime, ThreeWayNonTransitiveTimeCmp> set;
+    EXPECT_DEATH(set.insert({a, b, c}), "is_ordered_correctly");
+    absl::btree_multiset<ClockTime, ThreeWayNonTransitiveTimeCmp> mset;
+    EXPECT_DEATH(mset.insert({a, a, b, b, c, c}), "is_ordered_correctly");
+  }
 }
-#endif
+
+TEST(Btree, MutatedKeysCaught) {
+  if (!IsAssertEnabled()) GTEST_SKIP() << "Assertions not enabled.";
+
+  struct IntPtrCmp {
+    bool operator()(int *lhs, int *rhs) const { return *lhs < *rhs; }
+  };
+  {
+    absl::btree_set<int *, IntPtrCmp> set;
+    int arr[] = {0, 1, 2};
+    set.insert({&arr[0], &arr[1], &arr[2]});
+    arr[0] = 100;
+    EXPECT_DEATH(set.insert(&arr[0]), "is_ordered_correctly");
+  }
+  {
+    absl::btree_multiset<int *, IntPtrCmp> set;
+    int arr[] = {0, 1, 2};
+    set.insert({&arr[0], &arr[0], &arr[1], &arr[1], &arr[2], &arr[2]});
+    arr[0] = 100;
+    EXPECT_DEATH(set.insert(&arr[0]), "is_ordered_correctly");
+  }
+}
 
 #ifndef _MSC_VER
 // This test crashes on MSVC.
 TEST(Btree, InvalidIteratorUse) {
-  if (!BtreeNodePeer::UsesGenerations<absl::btree_set<int>>())
+  if (!BtreeGenerationsEnabled())
     GTEST_SKIP() << "Generation validation for iterators is disabled.";
 
+  // Invalid memory use can trigger heap-use-after-free in ASan or invalidated
+  // iterator assertions.
+  constexpr const char *kInvalidMemoryDeathMessage =
+      "heap-use-after-free|invalidated iterator";
+
   {
     absl::btree_set<int> set;
     for (int i = 0; i < 10; ++i) set.insert(i);
     auto it = set.begin();
     set.erase(it++);
-    EXPECT_DEATH(set.erase(it++), "invalidated iterator");
+    EXPECT_DEATH(set.erase(it++), kInvalidMemoryDeathMessage);
   }
   {
     absl::btree_set<int> set;
     for (int i = 0; i < 10; ++i) set.insert(i);
     auto it = set.insert(20).first;
     set.insert(30);
-    EXPECT_DEATH(*it, "invalidated iterator");
+    EXPECT_DEATH(*it, kInvalidMemoryDeathMessage);
   }
   {
     absl::btree_set<int> set;
@@ -3080,7 +3254,15 @@
     auto it = set.find(5000);
     ASSERT_NE(it, set.end());
     set.erase(1);
-    EXPECT_DEATH(*it, "invalidated iterator");
+    EXPECT_DEATH(*it, kInvalidMemoryDeathMessage);
+  }
+  {
+    absl::btree_set<int> set;
+    for (int i = 0; i < 10; ++i) set.insert(i);
+    auto it = set.insert(20).first;
+    set.insert(30);
+    EXPECT_DEATH(void(it == set.begin()), kInvalidMemoryDeathMessage);
+    EXPECT_DEATH(void(set.begin() == it), kInvalidMemoryDeathMessage);
   }
 }
 #endif
@@ -3320,6 +3502,108 @@
   set.insert(0);
 }
 
+TEST(Btree, IteratorSubtraction) {
+  absl::BitGen bitgen;
+  std::vector<int> vec;
+  // Randomize the set's insertion order so the nodes aren't all full.
+  for (int i = 0; i < 1000000; ++i) vec.push_back(i);
+  absl::c_shuffle(vec, bitgen);
+
+  absl::btree_set<int> set;
+  for (int i : vec) set.insert(i);
+
+  for (int i = 0; i < 1000; ++i) {
+    size_t begin = absl::Uniform(bitgen, 0u, set.size());
+    size_t end = absl::Uniform(bitgen, begin, set.size());
+    ASSERT_EQ(end - begin, set.find(end) - set.find(begin))
+        << begin << " " << end;
+  }
+}
+
+TEST(Btree, DereferencingEndIterator) {
+  if (!IsAssertEnabled()) GTEST_SKIP() << "Assertions not enabled.";
+
+  absl::btree_set<int> set;
+  for (int i = 0; i < 1000; ++i) set.insert(i);
+  EXPECT_DEATH(*set.end(), R"regex(Dereferencing end\(\) iterator)regex");
+}
+
+TEST(Btree, InvalidIteratorComparison) {
+  if (!IsAssertEnabled()) GTEST_SKIP() << "Assertions not enabled.";
+
+  absl::btree_set<int> set1, set2;
+  for (int i = 0; i < 1000; ++i) {
+    set1.insert(i);
+    set2.insert(i);
+  }
+
+  constexpr const char *kValueInitDeathMessage =
+      "Comparing default-constructed iterator with .*non-default-constructed "
+      "iterator";
+  typename absl::btree_set<int>::iterator iter1, iter2;
+  EXPECT_EQ(iter1, iter2);
+  EXPECT_DEATH(void(set1.begin() == iter1), kValueInitDeathMessage);
+  EXPECT_DEATH(void(iter1 == set1.begin()), kValueInitDeathMessage);
+
+  constexpr const char *kDifferentContainerDeathMessage =
+      "Comparing iterators from different containers";
+  iter1 = set1.begin();
+  iter2 = set2.begin();
+  EXPECT_DEATH(void(iter1 == iter2), kDifferentContainerDeathMessage);
+  EXPECT_DEATH(void(iter2 == iter1), kDifferentContainerDeathMessage);
+}
+
+TEST(Btree, InvalidPointerUse) {
+  if (!kAsan)
+    GTEST_SKIP() << "We only detect invalid pointer use in ASan mode.";
+
+  absl::btree_set<int> set;
+  set.insert(0);
+  const int *ptr = &*set.begin();
+  set.insert(1);
+  EXPECT_DEATH(std::cout << *ptr, "heap-use-after-free");
+  size_t slots_per_node = BtreeNodePeer::GetNumSlotsPerNode<decltype(set)>();
+  for (int i = 2; i < slots_per_node - 1; ++i) set.insert(i);
+  ptr = &*set.begin();
+  set.insert(static_cast<int>(slots_per_node));
+  EXPECT_DEATH(std::cout << *ptr, "heap-use-after-free");
+}
+
+template<typename Set>
+void TestBasicFunctionality(Set set) {
+  using value_type = typename Set::value_type;
+  for (int i = 0; i < 100; ++i) { set.insert(value_type(i)); }
+  for (int i = 50; i < 100; ++i) { set.erase(value_type(i)); }
+  auto it = set.begin();
+  for (int i = 0; i < 50; ++i, ++it) {
+    ASSERT_EQ(set.find(value_type(i)), it) << i;
+  }
+}
+
+template<size_t align>
+struct alignas(align) OveralignedKey {
+  explicit OveralignedKey(int i) : key(i) {}
+  bool operator<(const OveralignedKey &other) const { return key < other.key; }
+  int key = 0;
+};
+
+TEST(Btree, OveralignedKey) {
+  // Test basic functionality with both even and odd numbers of slots per node.
+  // The goal here is to detect cases where alignment may be incorrect.
+  TestBasicFunctionality(
+      SizedBtreeSet<OveralignedKey<16>, /*TargetValuesPerNode=*/8>());
+  TestBasicFunctionality(
+      SizedBtreeSet<OveralignedKey<16>, /*TargetValuesPerNode=*/9>());
+}
+
+TEST(Btree, FieldTypeEqualsSlotType) {
+  // This breaks if we try to do layout_type::Pointer<slot_type> because
+  // slot_type is the same as field_type.
+  using set_type = absl::btree_set<uint8_t>;
+  static_assert(BtreeNodePeer::FieldTypeEqualsSlotType<set_type>(), "");
+  TestBasicFunctionality(set_type());
+}
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h
index 5543243..e99137a 100644
--- a/absl/container/fixed_array.h
+++ b/absl/container/fixed_array.h
@@ -62,11 +62,10 @@
 // A `FixedArray` provides a run-time fixed-size array, allocating a small array
 // inline for efficiency.
 //
-// Most users should not specify an `inline_elements` argument and let
-// `FixedArray` automatically determine the number of elements
-// to store inline based on `sizeof(T)`. If `inline_elements` is specified, the
-// `FixedArray` implementation will use inline storage for arrays with a
-// length <= `inline_elements`.
+// Most users should not specify the `N` template parameter and let `FixedArray`
+// automatically determine the number of elements to store inline based on
+// `sizeof(T)`. If `N` is specified, the `FixedArray` implementation will use
+// inline storage for arrays with a length <= `N`.
 //
 // Note that a `FixedArray` constructed with a `size_type` argument will
 // default-initialize its values by leaving trivially constructible types
@@ -201,18 +200,22 @@
   //
   // Returns a const T* pointer to elements of the `FixedArray`. This pointer
   // can be used to access (but not modify) the contained elements.
-  const_pointer data() const { return AsValueType(storage_.begin()); }
+  const_pointer data() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return AsValueType(storage_.begin());
+  }
 
   // Overload of FixedArray::data() to return a T* pointer to elements of the
   // fixed array. This pointer can be used to access and modify the contained
   // elements.
-  pointer data() { return AsValueType(storage_.begin()); }
+  pointer data() ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return AsValueType(storage_.begin());
+  }
 
   // FixedArray::operator[]
   //
   // Returns a reference the ith element of the fixed array.
   // REQUIRES: 0 <= i < size()
-  reference operator[](size_type i) {
+  reference operator[](size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(i < size());
     return data()[i];
   }
@@ -220,7 +223,7 @@
   // Overload of FixedArray::operator()[] to return a const reference to the
   // ith element of the fixed array.
   // REQUIRES: 0 <= i < size()
-  const_reference operator[](size_type i) const {
+  const_reference operator[](size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(i < size());
     return data()[i];
   }
@@ -229,7 +232,7 @@
   //
   // Bounds-checked access.  Returns a reference to the ith element of the fixed
   // array, or throws std::out_of_range
-  reference at(size_type i) {
+  reference at(size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     if (ABSL_PREDICT_FALSE(i >= size())) {
       base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check");
     }
@@ -238,7 +241,7 @@
 
   // Overload of FixedArray::at() to return a const reference to the ith element
   // of the fixed array.
-  const_reference at(size_type i) const {
+  const_reference at(size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     if (ABSL_PREDICT_FALSE(i >= size())) {
       base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check");
     }
@@ -248,14 +251,14 @@
   // FixedArray::front()
   //
   // Returns a reference to the first element of the fixed array.
-  reference front() {
+  reference front() ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(!empty());
     return data()[0];
   }
 
   // Overload of FixedArray::front() to return a reference to the first element
   // of a fixed array of const values.
-  const_reference front() const {
+  const_reference front() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(!empty());
     return data()[0];
   }
@@ -263,14 +266,14 @@
   // FixedArray::back()
   //
   // Returns a reference to the last element of the fixed array.
-  reference back() {
+  reference back() ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(!empty());
     return data()[size() - 1];
   }
 
   // Overload of FixedArray::back() to return a reference to the last element
   // of a fixed array of const values.
-  const_reference back() const {
+  const_reference back() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(!empty());
     return data()[size() - 1];
   }
@@ -278,62 +281,74 @@
   // FixedArray::begin()
   //
   // Returns an iterator to the beginning of the fixed array.
-  iterator begin() { return data(); }
+  iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return data(); }
 
   // Overload of FixedArray::begin() to return a const iterator to the
   // beginning of the fixed array.
-  const_iterator begin() const { return data(); }
+  const_iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return data(); }
 
   // FixedArray::cbegin()
   //
   // Returns a const iterator to the beginning of the fixed array.
-  const_iterator cbegin() const { return begin(); }
+  const_iterator cbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return begin();
+  }
 
   // FixedArray::end()
   //
   // Returns an iterator to the end of the fixed array.
-  iterator end() { return data() + size(); }
+  iterator end() ABSL_ATTRIBUTE_LIFETIME_BOUND { return data() + size(); }
 
   // Overload of FixedArray::end() to return a const iterator to the end of the
   // fixed array.
-  const_iterator end() const { return data() + size(); }
+  const_iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return data() + size();
+  }
 
   // FixedArray::cend()
   //
   // Returns a const iterator to the end of the fixed array.
-  const_iterator cend() const { return end(); }
+  const_iterator cend() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return end(); }
 
   // FixedArray::rbegin()
   //
   // Returns a reverse iterator from the end of the fixed array.
-  reverse_iterator rbegin() { return reverse_iterator(end()); }
+  reverse_iterator rbegin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return reverse_iterator(end());
+  }
 
   // Overload of FixedArray::rbegin() to return a const reverse iterator from
   // the end of the fixed array.
-  const_reverse_iterator rbegin() const {
+  const_reverse_iterator rbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return const_reverse_iterator(end());
   }
 
   // FixedArray::crbegin()
   //
   // Returns a const reverse iterator from the end of the fixed array.
-  const_reverse_iterator crbegin() const { return rbegin(); }
+  const_reverse_iterator crbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return rbegin();
+  }
 
   // FixedArray::rend()
   //
   // Returns a reverse iterator from the beginning of the fixed array.
-  reverse_iterator rend() { return reverse_iterator(begin()); }
+  reverse_iterator rend() ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return reverse_iterator(begin());
+  }
 
   // Overload of FixedArray::rend() for returning a const reverse iterator
   // from the beginning of the fixed array.
-  const_reverse_iterator rend() const {
+  const_reverse_iterator rend() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return const_reverse_iterator(begin());
   }
 
   // FixedArray::crend()
   //
   // Returns a reverse iterator from the beginning of the fixed array.
-  const_reverse_iterator crend() const { return rend(); }
+  const_reverse_iterator crend() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return rend();
+  }
 
   // FixedArray::fill()
   //
@@ -343,7 +358,7 @@
   // Relational operators. Equality operators are elementwise using
   // `operator==`, while order operators order FixedArrays lexicographically.
   friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) {
-    return absl::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+    return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
   }
 
   friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) {
diff --git a/absl/container/flat_hash_map_test.cc b/absl/container/flat_hash_map_test.cc
index 263951f..03171f6 100644
--- a/absl/container/flat_hash_map_test.cc
+++ b/absl/container/flat_hash_map_test.cc
@@ -311,6 +311,14 @@
   }
 }
 
+TEST(FlatHashMap, RecursiveTypeCompiles) {
+  struct RecursiveType {
+    flat_hash_map<int, RecursiveType> m;
+  };
+  RecursiveType t;
+  t.m[0] = RecursiveType{};
+}
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h
index f5376f9..17bbf1a 100644
--- a/absl/container/flat_hash_set.h
+++ b/absl/container/flat_hash_set.h
@@ -343,7 +343,7 @@
   // for the past-the-end iterator, which is invalidated.
   //
   // `swap()` requires that the flat hash set's hashing and key equivalence
-  // functions be Swappable, and are exchaged using unqualified calls to
+  // functions be Swappable, and are exchanged using unqualified calls to
   // non-member `swap()`. If the set's allocator has
   // `std::allocator_traits<allocator_type>::propagate_on_container_swap::value`
   // set to `true`, the allocators are also exchanged using an unqualified call
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h
index 60f1246..04e2c38 100644
--- a/absl/container/inlined_vector.h
+++ b/absl/container/inlined_vector.h
@@ -52,6 +52,7 @@
 #include "absl/base/port.h"
 #include "absl/container/internal/inlined_vector.h"
 #include "absl/memory/memory.h"
+#include "absl/meta/type_traits.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -76,7 +77,7 @@
   template <typename TheA>
   using MoveIterator = inlined_vector_internal::MoveIterator<TheA>;
   template <typename TheA>
-  using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk<TheA>;
+  using IsMoveAssignOk = inlined_vector_internal::IsMoveAssignOk<TheA>;
 
   template <typename TheA, typename Iterator>
   using IteratorValueAdapter =
@@ -94,6 +95,12 @@
   using DisableIfAtLeastForwardIterator = absl::enable_if_t<
       !inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
 
+  using MemcpyPolicy = typename Storage::MemcpyPolicy;
+  using ElementwiseAssignPolicy = typename Storage::ElementwiseAssignPolicy;
+  using ElementwiseConstructPolicy =
+      typename Storage::ElementwiseConstructPolicy;
+  using MoveAssignmentPolicy = typename Storage::MoveAssignmentPolicy;
+
  public:
   using allocator_type = A;
   using value_type = inlined_vector_internal::ValueType<A>;
@@ -173,14 +180,23 @@
   // provided `allocator`.
   InlinedVector(const InlinedVector& other, const allocator_type& allocator)
       : storage_(allocator) {
+    // Fast path: if the other vector is empty, there's nothing for us to do.
     if (other.empty()) {
-      // Empty; nothing to do.
-    } else if (IsMemcpyOk<A>::value && !other.storage_.GetIsAllocated()) {
-      // Memcpy-able and do not need allocation.
-      storage_.MemcpyFrom(other.storage_);
-    } else {
-      storage_.InitFrom(other.storage_);
+      return;
     }
+
+    // Fast path: if the value type is trivially copy constructible, we know the
+    // allocator doesn't do anything fancy, and there is nothing on the heap
+    // then we know it is legal for us to simply memcpy the other vector's
+    // inlined bytes to form our copy of its elements.
+    if (absl::is_trivially_copy_constructible<value_type>::value &&
+        std::is_same<A, std::allocator<value_type>>::value &&
+        !other.storage_.GetIsAllocated()) {
+      storage_.MemcpyFrom(other.storage_);
+      return;
+    }
+
+    storage_.InitFrom(other.storage_);
   }
 
   // Creates an inlined vector by moving in the contents of `other` without
@@ -201,26 +217,38 @@
       absl::allocator_is_nothrow<allocator_type>::value ||
       std::is_nothrow_move_constructible<value_type>::value)
       : storage_(other.storage_.GetAllocator()) {
-    if (IsMemcpyOk<A>::value) {
+    // Fast path: if the value type can be trivially relocated (i.e. moved from
+    // and destroyed), and we know the allocator doesn't do anything fancy, then
+    // it's safe for us to simply adopt the contents of the storage for `other`
+    // and remove its own reference to them. It's as if we had individually
+    // move-constructed each value and then destroyed the original.
+    if (absl::is_trivially_relocatable<value_type>::value &&
+        std::is_same<A, std::allocator<value_type>>::value) {
       storage_.MemcpyFrom(other.storage_);
-
       other.storage_.SetInlinedSize(0);
-    } else if (other.storage_.GetIsAllocated()) {
+      return;
+    }
+
+    // Fast path: if the other vector is on the heap, we can simply take over
+    // its allocation.
+    if (other.storage_.GetIsAllocated()) {
       storage_.SetAllocation({other.storage_.GetAllocatedData(),
                               other.storage_.GetAllocatedCapacity()});
       storage_.SetAllocatedSize(other.storage_.GetSize());
 
       other.storage_.SetInlinedSize(0);
-    } else {
-      IteratorValueAdapter<A, MoveIterator<A>> other_values(
-          MoveIterator<A>(other.storage_.GetInlinedData()));
-
-      inlined_vector_internal::ConstructElements<A>(
-          storage_.GetAllocator(), storage_.GetInlinedData(), other_values,
-          other.storage_.GetSize());
-
-      storage_.SetInlinedSize(other.storage_.GetSize());
+      return;
     }
+
+    // Otherwise we must move each element individually.
+    IteratorValueAdapter<A, MoveIterator<A>> other_values(
+        MoveIterator<A>(other.storage_.GetInlinedData()));
+
+    inlined_vector_internal::ConstructElements<A>(
+        storage_.GetAllocator(), storage_.GetInlinedData(), other_values,
+        other.storage_.GetSize());
+
+    storage_.SetInlinedSize(other.storage_.GetSize());
   }
 
   // Creates an inlined vector by moving in the contents of `other` with a copy
@@ -235,22 +263,34 @@
       const allocator_type&
           allocator) noexcept(absl::allocator_is_nothrow<allocator_type>::value)
       : storage_(allocator) {
-    if (IsMemcpyOk<A>::value) {
+    // Fast path: if the value type can be trivially relocated (i.e. moved from
+    // and destroyed), and we know the allocator doesn't do anything fancy, then
+    // it's safe for us to simply adopt the contents of the storage for `other`
+    // and remove its own reference to them. It's as if we had individually
+    // move-constructed each value and then destroyed the original.
+    if (absl::is_trivially_relocatable<value_type>::value &&
+        std::is_same<A, std::allocator<value_type>>::value) {
       storage_.MemcpyFrom(other.storage_);
-
       other.storage_.SetInlinedSize(0);
-    } else if ((storage_.GetAllocator() == other.storage_.GetAllocator()) &&
-               other.storage_.GetIsAllocated()) {
+      return;
+    }
+
+    // Fast path: if the other vector is on the heap and shared the same
+    // allocator, we can simply take over its allocation.
+    if ((storage_.GetAllocator() == other.storage_.GetAllocator()) &&
+        other.storage_.GetIsAllocated()) {
       storage_.SetAllocation({other.storage_.GetAllocatedData(),
                               other.storage_.GetAllocatedCapacity()});
       storage_.SetAllocatedSize(other.storage_.GetSize());
 
       other.storage_.SetInlinedSize(0);
-    } else {
-      storage_.Initialize(IteratorValueAdapter<A, MoveIterator<A>>(
-                              MoveIterator<A>(other.data())),
-                          other.size());
+      return;
     }
+
+    // Otherwise we must move each element individually.
+    storage_.Initialize(
+        IteratorValueAdapter<A, MoveIterator<A>>(MoveIterator<A>(other.data())),
+        other.size());
   }
 
   ~InlinedVector() {}
@@ -301,7 +341,7 @@
   // can be used to access and modify the contained elements.
   //
   // NOTE: only elements within [`data()`, `data() + size()`) are valid.
-  pointer data() noexcept {
+  pointer data() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return storage_.GetIsAllocated() ? storage_.GetAllocatedData()
                                      : storage_.GetInlinedData();
   }
@@ -311,7 +351,7 @@
   // modify the contained elements.
   //
   // NOTE: only elements within [`data()`, `data() + size()`) are valid.
-  const_pointer data() const noexcept {
+  const_pointer data() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return storage_.GetIsAllocated() ? storage_.GetAllocatedData()
                                      : storage_.GetInlinedData();
   }
@@ -319,14 +359,14 @@
   // `InlinedVector::operator[](...)`
   //
   // Returns a `reference` to the `i`th element of the inlined vector.
-  reference operator[](size_type i) {
+  reference operator[](size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(i < size());
     return data()[i];
   }
 
   // Overload of `InlinedVector::operator[](...)` that returns a
   // `const_reference` to the `i`th element of the inlined vector.
-  const_reference operator[](size_type i) const {
+  const_reference operator[](size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(i < size());
     return data()[i];
   }
@@ -337,7 +377,7 @@
   //
   // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`,
   // in both debug and non-debug builds, `std::out_of_range` will be thrown.
-  reference at(size_type i) {
+  reference at(size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     if (ABSL_PREDICT_FALSE(i >= size())) {
       base_internal::ThrowStdOutOfRange(
           "`InlinedVector::at(size_type)` failed bounds check");
@@ -350,7 +390,7 @@
   //
   // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`,
   // in both debug and non-debug builds, `std::out_of_range` will be thrown.
-  const_reference at(size_type i) const {
+  const_reference at(size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     if (ABSL_PREDICT_FALSE(i >= size())) {
       base_internal::ThrowStdOutOfRange(
           "`InlinedVector::at(size_type) const` failed bounds check");
@@ -361,14 +401,14 @@
   // `InlinedVector::front()`
   //
   // Returns a `reference` to the first element of the inlined vector.
-  reference front() {
+  reference front() ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(!empty());
     return data()[0];
   }
 
   // Overload of `InlinedVector::front()` that returns a `const_reference` to
   // the first element of the inlined vector.
-  const_reference front() const {
+  const_reference front() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(!empty());
     return data()[0];
   }
@@ -376,14 +416,14 @@
   // `InlinedVector::back()`
   //
   // Returns a `reference` to the last element of the inlined vector.
-  reference back() {
+  reference back() ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(!empty());
     return data()[size() - 1];
   }
 
   // Overload of `InlinedVector::back()` that returns a `const_reference` to the
   // last element of the inlined vector.
-  const_reference back() const {
+  const_reference back() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(!empty());
     return data()[size() - 1];
   }
@@ -391,63 +431,82 @@
   // `InlinedVector::begin()`
   //
   // Returns an `iterator` to the beginning of the inlined vector.
-  iterator begin() noexcept { return data(); }
+  iterator begin() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { return data(); }
 
   // Overload of `InlinedVector::begin()` that returns a `const_iterator` to
   // the beginning of the inlined vector.
-  const_iterator begin() const noexcept { return data(); }
+  const_iterator begin() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return data();
+  }
 
   // `InlinedVector::end()`
   //
   // Returns an `iterator` to the end of the inlined vector.
-  iterator end() noexcept { return data() + size(); }
+  iterator end() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return data() + size();
+  }
 
   // Overload of `InlinedVector::end()` that returns a `const_iterator` to the
   // end of the inlined vector.
-  const_iterator end() const noexcept { return data() + size(); }
+  const_iterator end() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return data() + size();
+  }
 
   // `InlinedVector::cbegin()`
   //
   // Returns a `const_iterator` to the beginning of the inlined vector.
-  const_iterator cbegin() const noexcept { return begin(); }
+  const_iterator cbegin() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return begin();
+  }
 
   // `InlinedVector::cend()`
   //
   // Returns a `const_iterator` to the end of the inlined vector.
-  const_iterator cend() const noexcept { return end(); }
+  const_iterator cend() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return end();
+  }
 
   // `InlinedVector::rbegin()`
   //
   // Returns a `reverse_iterator` from the end of the inlined vector.
-  reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
+  reverse_iterator rbegin() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return reverse_iterator(end());
+  }
 
   // Overload of `InlinedVector::rbegin()` that returns a
   // `const_reverse_iterator` from the end of the inlined vector.
-  const_reverse_iterator rbegin() const noexcept {
+  const_reverse_iterator rbegin() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return const_reverse_iterator(end());
   }
 
   // `InlinedVector::rend()`
   //
   // Returns a `reverse_iterator` from the beginning of the inlined vector.
-  reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
+  reverse_iterator rend() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return reverse_iterator(begin());
+  }
 
   // Overload of `InlinedVector::rend()` that returns a `const_reverse_iterator`
   // from the beginning of the inlined vector.
-  const_reverse_iterator rend() const noexcept {
+  const_reverse_iterator rend() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return const_reverse_iterator(begin());
   }
 
   // `InlinedVector::crbegin()`
   //
   // Returns a `const_reverse_iterator` from the end of the inlined vector.
-  const_reverse_iterator crbegin() const noexcept { return rbegin(); }
+  const_reverse_iterator crbegin() const noexcept
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return rbegin();
+  }
 
   // `InlinedVector::crend()`
   //
   // Returns a `const_reverse_iterator` from the beginning of the inlined
   // vector.
-  const_reverse_iterator crend() const noexcept { return rend(); }
+  const_reverse_iterator crend() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return rend();
+  }
 
   // `InlinedVector::get_allocator()`
   //
@@ -486,18 +545,7 @@
   // unspecified state.
   InlinedVector& operator=(InlinedVector&& other) {
     if (ABSL_PREDICT_TRUE(this != std::addressof(other))) {
-      if (IsMemcpyOk<A>::value || other.storage_.GetIsAllocated()) {
-        inlined_vector_internal::DestroyAdapter<A>::DestroyElements(
-            storage_.GetAllocator(), data(), size());
-        storage_.DeallocateIfAllocated();
-        storage_.MemcpyFrom(other.storage_);
-
-        other.storage_.SetInlinedSize(0);
-      } else {
-        storage_.Assign(IteratorValueAdapter<A, MoveIterator<A>>(
-                            MoveIterator<A>(other.storage_.GetInlinedData())),
-                        other.size());
-      }
+      MoveAssignment(MoveAssignmentPolicy{}, std::move(other));
     }
 
     return *this;
@@ -568,20 +616,23 @@
   //
   // Inserts a copy of `v` at `pos`, returning an `iterator` to the newly
   // inserted element.
-  iterator insert(const_iterator pos, const_reference v) {
+  iterator insert(const_iterator pos,
+                  const_reference v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return emplace(pos, v);
   }
 
   // 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, value_type&& v) {
+  iterator insert(const_iterator pos,
+                  value_type&& v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return emplace(pos, std::move(v));
   }
 
   // Overload of `InlinedVector::insert(...)` that inserts `n` contiguous copies
   // of `v` starting at `pos`, returning an `iterator` pointing to the first of
   // the newly inserted elements.
-  iterator insert(const_iterator pos, size_type n, const_reference v) {
+  iterator insert(const_iterator pos, size_type n,
+                  const_reference v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(pos >= begin());
     ABSL_HARDENING_ASSERT(pos <= end());
 
@@ -609,7 +660,8 @@
   // Overload of `InlinedVector::insert(...)` that inserts copies of the
   // elements of `list` starting at `pos`, returning an `iterator` pointing to
   // the first of the newly inserted elements.
-  iterator insert(const_iterator pos, std::initializer_list<value_type> list) {
+  iterator insert(const_iterator pos, std::initializer_list<value_type> list)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert(pos, list.begin(), list.end());
   }
 
@@ -621,7 +673,7 @@
   template <typename ForwardIterator,
             EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
   iterator insert(const_iterator pos, ForwardIterator first,
-                  ForwardIterator last) {
+                  ForwardIterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(pos >= begin());
     ABSL_HARDENING_ASSERT(pos <= end());
 
@@ -641,7 +693,8 @@
   // NOTE: this overload is for iterators that are "input" category.
   template <typename InputIterator,
             DisableIfAtLeastForwardIterator<InputIterator> = 0>
-  iterator insert(const_iterator pos, InputIterator first, InputIterator last) {
+  iterator insert(const_iterator pos, InputIterator first,
+                  InputIterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(pos >= begin());
     ABSL_HARDENING_ASSERT(pos <= end());
 
@@ -658,15 +711,28 @@
   // Constructs and inserts an element using `args...` in the inlined vector at
   // `pos`, returning an `iterator` pointing to the newly emplaced element.
   template <typename... Args>
-  iterator emplace(const_iterator pos, Args&&... args) {
+  iterator emplace(const_iterator pos,
+                   Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(pos >= begin());
     ABSL_HARDENING_ASSERT(pos <= end());
 
     value_type dealias(std::forward<Args>(args)...);
+    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2
+    // It appears that GCC thinks that since `pos` is a const pointer and may
+    // point to uninitialized memory at this point, a warning should be
+    // issued. But `pos` is actually only used to compute an array index to
+    // write to.
+#if !defined(__clang__) && defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
     return storage_.Insert(pos,
                            IteratorValueAdapter<A, MoveIterator<A>>(
                                MoveIterator<A>(std::addressof(dealias))),
                            1);
+#if !defined(__clang__) && defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
   }
 
   // `InlinedVector::emplace_back(...)`
@@ -674,7 +740,7 @@
   // Constructs and inserts an element using `args...` in the inlined vector at
   // `end()`, returning a `reference` to the newly emplaced element.
   template <typename... Args>
-  reference emplace_back(Args&&... args) {
+  reference emplace_back(Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return storage_.EmplaceBack(std::forward<Args>(args)...);
   }
 
@@ -704,8 +770,8 @@
   // Erases the element at `pos`, returning an `iterator` pointing to where the
   // erased element was located.
   //
-  // NOTE: may return `end()`, which is not dereferencable.
-  iterator erase(const_iterator pos) {
+  // NOTE: may return `end()`, which is not dereferenceable.
+  iterator erase(const_iterator pos) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(pos >= begin());
     ABSL_HARDENING_ASSERT(pos < end());
 
@@ -716,8 +782,9 @@
   // range [`from`, `to`), returning an `iterator` pointing to where the first
   // erased element was located.
   //
-  // NOTE: may return `end()`, which is not dereferencable.
-  iterator erase(const_iterator from, const_iterator to) {
+  // NOTE: may return `end()`, which is not dereferenceable.
+  iterator erase(const_iterator from,
+                 const_iterator to) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(from >= begin());
     ABSL_HARDENING_ASSERT(from <= to);
     ABSL_HARDENING_ASSERT(to <= end());
@@ -773,6 +840,73 @@
   template <typename H, typename TheT, size_t TheN, typename TheA>
   friend H AbslHashValue(H h, const absl::InlinedVector<TheT, TheN, TheA>& a);
 
+  void MoveAssignment(MemcpyPolicy, InlinedVector&& other) {
+    // Assumption check: we shouldn't be told to use memcpy to implement move
+    // assignment unless we have trivially destructible elements and an
+    // allocator that does nothing fancy.
+    static_assert(absl::is_trivially_destructible<value_type>::value, "");
+    static_assert(std::is_same<A, std::allocator<value_type>>::value, "");
+
+    // Throw away our existing heap allocation, if any. There is no need to
+    // destroy the existing elements one by one because we know they are
+    // trivially destructible.
+    storage_.DeallocateIfAllocated();
+
+    // Adopt the other vector's inline elements or heap allocation.
+    storage_.MemcpyFrom(other.storage_);
+    other.storage_.SetInlinedSize(0);
+  }
+
+  // Destroy our existing elements, if any, and adopt the heap-allocated
+  // elements of the other vector.
+  //
+  // REQUIRES: other.storage_.GetIsAllocated()
+  void DestroyExistingAndAdopt(InlinedVector&& other) {
+    ABSL_HARDENING_ASSERT(other.storage_.GetIsAllocated());
+
+    inlined_vector_internal::DestroyAdapter<A>::DestroyElements(
+        storage_.GetAllocator(), data(), size());
+    storage_.DeallocateIfAllocated();
+
+    storage_.MemcpyFrom(other.storage_);
+    other.storage_.SetInlinedSize(0);
+  }
+
+  void MoveAssignment(ElementwiseAssignPolicy, InlinedVector&& other) {
+    // Fast path: if the other vector is on the heap then we don't worry about
+    // actually move-assigning each element. Instead we only throw away our own
+    // existing elements and adopt the heap allocation of the other vector.
+    if (other.storage_.GetIsAllocated()) {
+      DestroyExistingAndAdopt(std::move(other));
+      return;
+    }
+
+    storage_.Assign(IteratorValueAdapter<A, MoveIterator<A>>(
+                        MoveIterator<A>(other.storage_.GetInlinedData())),
+                    other.size());
+  }
+
+  void MoveAssignment(ElementwiseConstructPolicy, InlinedVector&& other) {
+    // Fast path: if the other vector is on the heap then we don't worry about
+    // actually move-assigning each element. Instead we only throw away our own
+    // existing elements and adopt the heap allocation of the other vector.
+    if (other.storage_.GetIsAllocated()) {
+      DestroyExistingAndAdopt(std::move(other));
+      return;
+    }
+
+    inlined_vector_internal::DestroyAdapter<A>::DestroyElements(
+        storage_.GetAllocator(), data(), size());
+    storage_.DeallocateIfAllocated();
+
+    IteratorValueAdapter<A, MoveIterator<A>> other_values(
+        MoveIterator<A>(other.storage_.GetInlinedData()));
+    inlined_vector_internal::ConstructElements<A>(
+        storage_.GetAllocator(), storage_.GetInlinedData(), other_values,
+        other.storage_.GetSize());
+    storage_.SetInlinedSize(other.storage_.GetSize());
+  }
+
   Storage storage_;
 };
 
@@ -797,7 +931,7 @@
                 const absl::InlinedVector<T, N, A>& b) {
   auto a_data = a.data();
   auto b_data = b.data();
-  return absl::equal(a_data, a_data + a.size(), b_data, b_data + b.size());
+  return std::equal(a_data, a_data + a.size(), b_data, b_data + b.size());
 }
 
 // `operator!=(...)`
diff --git a/absl/container/inlined_vector_benchmark.cc b/absl/container/inlined_vector_benchmark.cc
index 56a6bfd..5a04277 100644
--- a/absl/container/inlined_vector_benchmark.cc
+++ b/absl/container/inlined_vector_benchmark.cc
@@ -66,7 +66,7 @@
 BENCHMARK(BM_StdVectorFill)->Range(1, 256);
 
 // The purpose of the next two benchmarks is to verify that
-// absl::InlinedVector is efficient when moving is more efficent than
+// absl::InlinedVector is efficient when moving is more efficient than
 // copying. To do so, we use strings that are larger than the short
 // string optimization.
 bool StringRepresentedInline(std::string s) {
diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc
index b872eb4..808f97c 100644
--- a/absl/container/inlined_vector_test.cc
+++ b/absl/container/inlined_vector_test.cc
@@ -15,13 +15,16 @@
 #include "absl/container/inlined_vector.h"
 
 #include <algorithm>
+#include <cstddef>
 #include <forward_list>
+#include <iterator>
 #include <list>
 #include <memory>
 #include <scoped_allocator>
 #include <sstream>
 #include <stdexcept>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "gmock/gmock.h"
@@ -49,14 +52,13 @@
 using testing::ElementsAreArray;
 using testing::Eq;
 using testing::Gt;
+using testing::Pointee;
+using testing::Pointwise;
 using testing::PrintToString;
+using testing::SizeIs;
 
 using IntVec = absl::InlinedVector<int, 8>;
 
-MATCHER_P(SizeIs, n, "") {
-  return testing::ExplainMatchResult(n, arg.size(), result_listener);
-}
-
 MATCHER_P(CapacityIs, n, "") {
   return testing::ExplainMatchResult(n, arg.capacity(), result_listener);
 }
@@ -259,6 +261,49 @@
 #endif
 }
 
+// Move construction of a container of unique pointers should work fine, with no
+// leaks, despite the fact that unique pointers are trivially relocatable but
+// not trivially destructible.
+TEST(UniquePtr, MoveConstruct) {
+  for (size_t size = 0; size < 16; ++size) {
+    SCOPED_TRACE(size);
+
+    absl::InlinedVector<std::unique_ptr<size_t>, 2> a;
+    for (size_t i = 0; i < size; ++i) {
+      a.push_back(std::make_unique<size_t>(i));
+    }
+
+    absl::InlinedVector<std::unique_ptr<size_t>, 2> b(std::move(a));
+
+    ASSERT_THAT(b, SizeIs(size));
+    for (size_t i = 0; i < size; ++i) {
+      ASSERT_THAT(b[i], Pointee(i));
+    }
+  }
+}
+
+// Move assignment of a container of unique pointers should work fine, with no
+// leaks, despite the fact that unique pointers are trivially relocatable but
+// not trivially destructible.
+TEST(UniquePtr, MoveAssign) {
+  for (size_t size = 0; size < 16; ++size) {
+    SCOPED_TRACE(size);
+
+    absl::InlinedVector<std::unique_ptr<size_t>, 2> a;
+    for (size_t i = 0; i < size; ++i) {
+      a.push_back(std::make_unique<size_t>(i));
+    }
+
+    absl::InlinedVector<std::unique_ptr<size_t>, 2> b;
+    b = std::move(a);
+
+    ASSERT_THAT(b, SizeIs(size));
+    for (size_t i = 0; i < size; ++i) {
+      ASSERT_THAT(b[i], Pointee(i));
+    }
+  }
+}
+
 // At the end of this test loop, the elements between [erase_begin, erase_end)
 // should have reference counts == 0, and all others elements should have
 // reference counts == 1.
@@ -1205,6 +1250,8 @@
 }
 
 TEST(CountElemAssign, SimpleTypeWithInlineBacking) {
+  const size_t inlined_capacity = absl::InlinedVector<int, 2>().capacity();
+
   for (size_t original_size = 0; original_size <= 5; ++original_size) {
     SCOPED_TRACE(original_size);
     // Original contents are [12345, 12345, ...]
@@ -1214,9 +1261,9 @@
                                   original_contents.end());
     v.assign(2, 123);
     EXPECT_THAT(v, AllOf(SizeIs(2u), ElementsAre(123, 123)));
-    if (original_size <= 2) {
+    if (original_size <= inlined_capacity) {
       // If the original had inline backing, it should stay inline.
-      EXPECT_EQ(2u, v.capacity());
+      EXPECT_EQ(v.capacity(), inlined_capacity);
     }
   }
 }
@@ -1357,6 +1404,8 @@
 }
 
 TEST(RangedAssign, SimpleType) {
+  const size_t inlined_capacity = absl::InlinedVector<int, 3>().capacity();
+
   // Test for all combinations of original sizes (empty and non-empty inline,
   // and out of line) and target sizes.
   for (size_t original_size = 0; original_size <= 5; ++original_size) {
@@ -1364,13 +1413,13 @@
     // Original contents are [12345, 12345, ...]
     std::vector<int> original_contents(original_size, 12345);
 
-    for (int target_size = 0; target_size <= 5; ++target_size) {
+    for (size_t target_size = 0; target_size <= 5; ++target_size) {
       SCOPED_TRACE(target_size);
 
       // New contents are [3, 4, ...]
       std::vector<int> new_contents;
-      for (int i = 0; i < target_size; ++i) {
-        new_contents.push_back(i + 3);
+      for (size_t i = 0; i < target_size; ++i) {
+        new_contents.push_back(static_cast<int>(i + 3));
       }
 
       absl::InlinedVector<int, 3> v(original_contents.begin(),
@@ -1379,9 +1428,10 @@
 
       EXPECT_EQ(new_contents.size(), v.size());
       EXPECT_LE(new_contents.size(), v.capacity());
-      if (target_size <= 3 && original_size <= 3) {
+      if (target_size <= inlined_capacity &&
+          original_size <= inlined_capacity) {
         // Storage should stay inline when target size is small.
-        EXPECT_EQ(3u, v.capacity());
+        EXPECT_EQ(v.capacity(), inlined_capacity);
       }
       EXPECT_THAT(v, ElementsAreArray(new_contents));
     }
@@ -1467,9 +1517,12 @@
 }
 
 TEST(InitializerListConstructor, ComplexTypeWithInlineBacking) {
-  EXPECT_THAT((absl::InlinedVector<CopyableMovableInstance, 1>{
-                  CopyableMovableInstance(0)}),
-              AllOf(SizeIs(1u), CapacityIs(1u), ElementsAre(ValueIs(0))));
+  const size_t inlined_capacity =
+      absl::InlinedVector<CopyableMovableInstance, 1>().capacity();
+  EXPECT_THAT(
+      (absl::InlinedVector<CopyableMovableInstance, 1>{
+          CopyableMovableInstance(0)}),
+      AllOf(SizeIs(1u), CapacityIs(inlined_capacity), ElementsAre(ValueIs(0))));
 }
 
 TEST(InitializerListConstructor, ComplexTypeWithReallocationRequired) {
@@ -1824,4 +1877,226 @@
   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases));
 }
 
+class MoveConstructibleOnlyInstance
+    : public absl::test_internal::BaseCountedInstance {
+ public:
+  explicit MoveConstructibleOnlyInstance(int x) : BaseCountedInstance(x) {}
+  MoveConstructibleOnlyInstance(MoveConstructibleOnlyInstance&& other) =
+      default;
+  MoveConstructibleOnlyInstance& operator=(
+      MoveConstructibleOnlyInstance&& other) = delete;
+};
+
+MATCHER(HasValue, "") {
+  return ::testing::get<0>(arg).value() == ::testing::get<1>(arg);
+}
+
+TEST(NonAssignableMoveAssignmentTest, AllocatedToInline) {
+  using X = MoveConstructibleOnlyInstance;
+  InstanceTracker tracker;
+  absl::InlinedVector<X, 2> inlined;
+  inlined.emplace_back(1);
+  absl::InlinedVector<X, 2> allocated;
+  allocated.emplace_back(1);
+  allocated.emplace_back(2);
+  allocated.emplace_back(3);
+  tracker.ResetCopiesMovesSwaps();
+
+  inlined = std::move(allocated);
+  // passed ownership of the allocated storage
+  EXPECT_EQ(tracker.moves(), 0);
+  EXPECT_EQ(tracker.live_instances(), 3);
+
+  EXPECT_THAT(inlined, Pointwise(HasValue(), {1, 2, 3}));
+}
+
+TEST(NonAssignableMoveAssignmentTest, InlineToAllocated) {
+  using X = MoveConstructibleOnlyInstance;
+  InstanceTracker tracker;
+  absl::InlinedVector<X, 2> inlined;
+  inlined.emplace_back(1);
+  absl::InlinedVector<X, 2> allocated;
+  allocated.emplace_back(1);
+  allocated.emplace_back(2);
+  allocated.emplace_back(3);
+  tracker.ResetCopiesMovesSwaps();
+
+  allocated = std::move(inlined);
+  // Moved elements
+  EXPECT_EQ(tracker.moves(), 1);
+  EXPECT_EQ(tracker.live_instances(), 1);
+
+  EXPECT_THAT(allocated, Pointwise(HasValue(), {1}));
+}
+
+TEST(NonAssignableMoveAssignmentTest, InlineToInline) {
+  using X = MoveConstructibleOnlyInstance;
+  InstanceTracker tracker;
+  absl::InlinedVector<X, 2> inlined_a;
+  inlined_a.emplace_back(1);
+  absl::InlinedVector<X, 2> inlined_b;
+  inlined_b.emplace_back(1);
+  tracker.ResetCopiesMovesSwaps();
+
+  inlined_a = std::move(inlined_b);
+  // Moved elements
+  EXPECT_EQ(tracker.moves(), 1);
+  EXPECT_EQ(tracker.live_instances(), 1);
+
+  EXPECT_THAT(inlined_a, Pointwise(HasValue(), {1}));
+}
+
+TEST(NonAssignableMoveAssignmentTest, AllocatedToAllocated) {
+  using X = MoveConstructibleOnlyInstance;
+  InstanceTracker tracker;
+  absl::InlinedVector<X, 2> allocated_a;
+  allocated_a.emplace_back(1);
+  allocated_a.emplace_back(2);
+  allocated_a.emplace_back(3);
+  absl::InlinedVector<X, 2> allocated_b;
+  allocated_b.emplace_back(4);
+  allocated_b.emplace_back(5);
+  allocated_b.emplace_back(6);
+  allocated_b.emplace_back(7);
+  tracker.ResetCopiesMovesSwaps();
+
+  allocated_a = std::move(allocated_b);
+  // passed ownership of the allocated storage
+  EXPECT_EQ(tracker.moves(), 0);
+  EXPECT_EQ(tracker.live_instances(), 4);
+
+  EXPECT_THAT(allocated_a, Pointwise(HasValue(), {4, 5, 6, 7}));
+}
+
+TEST(NonAssignableMoveAssignmentTest, AssignThis) {
+  using X = MoveConstructibleOnlyInstance;
+  InstanceTracker tracker;
+  absl::InlinedVector<X, 2> v;
+  v.emplace_back(1);
+  v.emplace_back(2);
+  v.emplace_back(3);
+
+  tracker.ResetCopiesMovesSwaps();
+
+  // Obfuscated in order to pass -Wself-move.
+  v = std::move(*std::addressof(v));
+  // nothing happens
+  EXPECT_EQ(tracker.moves(), 0);
+  EXPECT_EQ(tracker.live_instances(), 3);
+
+  EXPECT_THAT(v, Pointwise(HasValue(), {1, 2, 3}));
+}
+
+class NonSwappableInstance : public absl::test_internal::BaseCountedInstance {
+ public:
+  explicit NonSwappableInstance(int x) : BaseCountedInstance(x) {}
+  NonSwappableInstance(const NonSwappableInstance& other) = default;
+  NonSwappableInstance& operator=(const NonSwappableInstance& other) = default;
+  NonSwappableInstance(NonSwappableInstance&& other) = default;
+  NonSwappableInstance& operator=(NonSwappableInstance&& other) = default;
+};
+
+void swap(NonSwappableInstance&, NonSwappableInstance&) = delete;
+
+TEST(NonSwappableSwapTest, InlineAndAllocatedTransferStorageAndMove) {
+  using X = NonSwappableInstance;
+  InstanceTracker tracker;
+  absl::InlinedVector<X, 2> inlined;
+  inlined.emplace_back(1);
+  absl::InlinedVector<X, 2> allocated;
+  allocated.emplace_back(1);
+  allocated.emplace_back(2);
+  allocated.emplace_back(3);
+  tracker.ResetCopiesMovesSwaps();
+
+  inlined.swap(allocated);
+  EXPECT_EQ(tracker.moves(), 1);
+  EXPECT_EQ(tracker.live_instances(), 4);
+
+  EXPECT_THAT(inlined, Pointwise(HasValue(), {1, 2, 3}));
+}
+
+TEST(NonSwappableSwapTest, InlineAndInlineMoveIndividualElements) {
+  using X = NonSwappableInstance;
+  InstanceTracker tracker;
+  absl::InlinedVector<X, 2> inlined_a;
+  inlined_a.emplace_back(1);
+  absl::InlinedVector<X, 2> inlined_b;
+  inlined_b.emplace_back(2);
+  tracker.ResetCopiesMovesSwaps();
+
+  inlined_a.swap(inlined_b);
+  EXPECT_EQ(tracker.moves(), 3);
+  EXPECT_EQ(tracker.live_instances(), 2);
+
+  EXPECT_THAT(inlined_a, Pointwise(HasValue(), {2}));
+  EXPECT_THAT(inlined_b, Pointwise(HasValue(), {1}));
+}
+
+TEST(NonSwappableSwapTest, AllocatedAndAllocatedOnlyTransferStorage) {
+  using X = NonSwappableInstance;
+  InstanceTracker tracker;
+  absl::InlinedVector<X, 2> allocated_a;
+  allocated_a.emplace_back(1);
+  allocated_a.emplace_back(2);
+  allocated_a.emplace_back(3);
+  absl::InlinedVector<X, 2> allocated_b;
+  allocated_b.emplace_back(4);
+  allocated_b.emplace_back(5);
+  allocated_b.emplace_back(6);
+  allocated_b.emplace_back(7);
+  tracker.ResetCopiesMovesSwaps();
+
+  allocated_a.swap(allocated_b);
+  EXPECT_EQ(tracker.moves(), 0);
+  EXPECT_EQ(tracker.live_instances(), 7);
+
+  EXPECT_THAT(allocated_a, Pointwise(HasValue(), {4, 5, 6, 7}));
+  EXPECT_THAT(allocated_b, Pointwise(HasValue(), {1, 2, 3}));
+}
+
+TEST(NonSwappableSwapTest, SwapThis) {
+  using X = NonSwappableInstance;
+  InstanceTracker tracker;
+  absl::InlinedVector<X, 2> v;
+  v.emplace_back(1);
+  v.emplace_back(2);
+  v.emplace_back(3);
+
+  tracker.ResetCopiesMovesSwaps();
+
+  v.swap(v);
+  EXPECT_EQ(tracker.moves(), 0);
+  EXPECT_EQ(tracker.live_instances(), 3);
+
+  EXPECT_THAT(v, Pointwise(HasValue(), {1, 2, 3}));
+}
+
+template <size_t N>
+using CharVec = absl::InlinedVector<char, N>;
+
+// Warning: This struct "simulates" the type `InlinedVector::Storage::Allocated`
+// to make reasonable expectations for inlined storage capacity optimization. If
+// implementation changes `Allocated`, then `MySpan` and tests that use it need
+// to be updated accordingly.
+template <typename T>
+struct MySpan {
+  T* data;
+  size_t size;
+};
+
+TEST(StorageTest, InlinedCapacityAutoIncrease) {
+  // The requested capacity is auto increased to `sizeof(MySpan<char>)`.
+  EXPECT_GT(CharVec<1>().capacity(), 1);
+  EXPECT_EQ(CharVec<1>().capacity(), sizeof(MySpan<char>));
+  EXPECT_EQ(CharVec<1>().capacity(), CharVec<2>().capacity());
+  EXPECT_EQ(sizeof(CharVec<1>), sizeof(CharVec<2>));
+
+  // The requested capacity is auto increased to
+  // `sizeof(MySpan<int>) / sizeof(int)`.
+  EXPECT_GT((absl::InlinedVector<int, 1>().capacity()), 1);
+  EXPECT_EQ((absl::InlinedVector<int, 1>().capacity()),
+            sizeof(MySpan<int>) / sizeof(int));
+}
+
 }  // anonymous namespace
diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h
index ecf31be..569faa0 100644
--- a/absl/container/internal/btree.h
+++ b/absl/container/internal/btree.h
@@ -86,6 +86,12 @@
 #define ABSL_BTREE_ENABLE_GENERATIONS
 #endif
 
+#ifdef ABSL_BTREE_ENABLE_GENERATIONS
+constexpr bool BtreeGenerationsEnabled() { return true; }
+#else
+constexpr bool BtreeGenerationsEnabled() { return false; }
+#endif
+
 template <typename Compare, typename T, typename U>
 using compare_result_t = absl::result_of_t<const Compare(const T &, const U &)>;
 
@@ -378,12 +384,6 @@
       std::is_same<key_compare, StringBtreeDefaultGreater>::value;
   static constexpr bool kIsKeyCompareTransparent =
       IsTransparent<original_key_compare>::value || kIsKeyCompareStringAdapted;
-  static constexpr bool kEnableGenerations =
-#ifdef ABSL_BTREE_ENABLE_GENERATIONS
-      true;
-#else
-      false;
-#endif
 
   // A type which indicates if we have a key-compare-to functor or a plain old
   // key-compare functor.
@@ -589,7 +589,7 @@
   constexpr static size_type SizeWithNSlots(size_type n) {
     return layout_type(
                /*parent*/ 1,
-               /*generation*/ params_type::kEnableGenerations ? 1 : 0,
+               /*generation*/ BtreeGenerationsEnabled() ? 1 : 0,
                /*position, start, finish, max_count*/ 4,
                /*slots*/ n,
                /*children*/ 0)
@@ -629,23 +629,22 @@
   // has this value.
   constexpr static field_type kInternalNodeMaxCount = 0;
 
+  constexpr static layout_type Layout(const size_type slot_count,
+                                      const size_type child_count) {
+    return layout_type(
+        /*parent*/ 1,
+        /*generation*/ BtreeGenerationsEnabled() ? 1 : 0,
+        /*position, start, finish, max_count*/ 4,
+        /*slots*/ slot_count,
+        /*children*/ child_count);
+  }
   // Leaves can have less than kNodeSlots values.
   constexpr static layout_type LeafLayout(
       const size_type slot_count = kNodeSlots) {
-    return layout_type(
-        /*parent*/ 1,
-        /*generation*/ params_type::kEnableGenerations ? 1 : 0,
-        /*position, start, finish, max_count*/ 4,
-        /*slots*/ slot_count,
-        /*children*/ 0);
+    return Layout(slot_count, 0);
   }
   constexpr static layout_type InternalLayout() {
-    return layout_type(
-        /*parent*/ 1,
-        /*generation*/ params_type::kEnableGenerations ? 1 : 0,
-        /*position, start, finish, max_count*/ 4,
-        /*slots*/ kNodeSlots,
-        /*children*/ kNodeSlots + 1);
+    return Layout(kNodeSlots, kNodeSlots + 1);
   }
   constexpr static size_type LeafSize(const size_type slot_count = kNodeSlots) {
     return LeafLayout(slot_count).AllocSize();
@@ -729,7 +728,7 @@
 
   // Gets the root node's generation integer, which is the one used by the tree.
   uint32_t *get_root_generation() const {
-    assert(params_type::kEnableGenerations);
+    assert(BtreeGenerationsEnabled());
     const btree_node *curr = this;
     for (; !curr->is_root(); curr = curr->parent()) continue;
     return const_cast<uint32_t *>(&curr->GetField<1>()[0]);
@@ -737,16 +736,16 @@
 
   // Returns the generation for iterator validation.
   uint32_t generation() const {
-    return params_type::kEnableGenerations ? *get_root_generation() : 0;
+    return BtreeGenerationsEnabled() ? *get_root_generation() : 0;
   }
   // Updates generation. Should only be called on a root node or during node
   // initialization.
   void set_generation(uint32_t generation) {
-    if (params_type::kEnableGenerations) GetField<1>()[0] = generation;
+    if (BtreeGenerationsEnabled()) GetField<1>()[0] = generation;
   }
   // Updates the generation. We do this whenever the node is mutated.
   void next_generation() {
-    if (params_type::kEnableGenerations) ++*get_root_generation();
+    if (BtreeGenerationsEnabled()) ++*get_root_generation();
   }
 
   // Getters for the key/value at position i in the node.
@@ -763,9 +762,12 @@
   void clear_child(field_type i) {
     absl::container_internal::SanitizerPoisonObject(&mutable_child(i));
   }
-  void set_child(field_type i, btree_node *c) {
+  void set_child_noupdate_position(field_type i, btree_node *c) {
     absl::container_internal::SanitizerUnpoisonObject(&mutable_child(i));
     mutable_child(i) = c;
+  }
+  void set_child(field_type i, btree_node *c) {
+    set_child_noupdate_position(i, c);
     c->set_position(i);
   }
   void init_child(field_type i, btree_node *c) {
@@ -892,6 +894,38 @@
     }
   }
 
+  // Returns whether key i is ordered correctly with respect to the other keys
+  // in the node. The motivation here is to detect comparators that violate
+  // transitivity. Note: we only do comparisons of keys on this node rather than
+  // the whole tree so that this is constant time.
+  template <typename Compare>
+  bool is_ordered_correctly(field_type i, const Compare &comp) const {
+    if (std::is_base_of<BtreeTestOnlyCheckedCompareOptOutBase,
+                        Compare>::value ||
+        params_type::kIsKeyCompareStringAdapted) {
+      return true;
+    }
+
+    const auto compare = [&](field_type a, field_type b) {
+      const absl::weak_ordering cmp =
+          compare_internal::do_three_way_comparison(comp, key(a), key(b));
+      return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+    };
+    int cmp = -1;
+    constexpr bool kCanHaveEquivKeys =
+        params_type::template can_have_multiple_equivalent_keys<key_type>();
+    for (field_type j = start(); j < finish(); ++j) {
+      if (j == i) {
+        if (cmp > 0) return false;
+        continue;
+      }
+      int new_cmp = compare(j, i);
+      if (new_cmp < cmp || (!kCanHaveEquivKeys && new_cmp == 0)) return false;
+      cmp = new_cmp;
+    }
+    return true;
+  }
+
   // Emplaces a value at position i, shifting all existing values and
   // children at positions >= i to the right by 1.
   template <typename... Args>
@@ -916,18 +950,19 @@
   void merge(btree_node *src, allocator_type *alloc);
 
   // Node allocation/deletion routines.
-  void init_leaf(field_type max_count, btree_node *parent) {
+  void init_leaf(field_type position, field_type max_count,
+                 btree_node *parent) {
     set_generation(0);
     set_parent(parent);
-    set_position(0);
+    set_position(position);
     set_start(0);
     set_finish(0);
     set_max_count(max_count);
     absl::container_internal::SanitizerPoisonMemoryRegion(
         start_slot(), max_count * sizeof(slot_type));
   }
-  void init_internal(btree_node *parent) {
-    init_leaf(kNodeSlots, parent);
+  void init_internal(field_type position, btree_node *parent) {
+    init_leaf(position, kNodeSlots, parent);
     // Set `max_count` to a sentinel value to indicate that this node is
     // internal.
     set_max_count(kInternalNodeMaxCount);
@@ -1017,8 +1052,61 @@
   friend struct btree_access;
 };
 
+template <typename Node>
+bool AreNodesFromSameContainer(const Node *node_a, const Node *node_b) {
+  // If either node is null, then give up on checking whether they're from the
+  // same container. (If exactly one is null, then we'll trigger the
+  // default-constructed assert in Equals.)
+  if (node_a == nullptr || node_b == nullptr) return true;
+  while (!node_a->is_root()) node_a = node_a->parent();
+  while (!node_b->is_root()) node_b = node_b->parent();
+  return node_a == node_b;
+}
+
+class btree_iterator_generation_info_enabled {
+ public:
+  explicit btree_iterator_generation_info_enabled(uint32_t g)
+      : generation_(g) {}
+
+  // Updates the generation. For use internally right before we return an
+  // iterator to the user.
+  template <typename Node>
+  void update_generation(const Node *node) {
+    if (node != nullptr) generation_ = node->generation();
+  }
+  uint32_t generation() const { return generation_; }
+
+  template <typename Node>
+  void assert_valid_generation(const Node *node) const {
+    if (node != nullptr && node->generation() != generation_) {
+      ABSL_INTERNAL_LOG(
+          FATAL,
+          "Attempting to use an invalidated iterator. The corresponding b-tree "
+          "container has been mutated since this iterator was constructed.");
+    }
+  }
+
+ private:
+  // Used to check that the iterator hasn't been invalidated.
+  uint32_t generation_;
+};
+
+class btree_iterator_generation_info_disabled {
+ public:
+  explicit btree_iterator_generation_info_disabled(uint32_t) {}
+  static void update_generation(const void *) {}
+  static uint32_t generation() { return 0; }
+  static void assert_valid_generation(const void *) {}
+};
+
+#ifdef ABSL_BTREE_ENABLE_GENERATIONS
+using btree_iterator_generation_info = btree_iterator_generation_info_enabled;
+#else
+using btree_iterator_generation_info = btree_iterator_generation_info_disabled;
+#endif
+
 template <typename Node, typename Reference, typename Pointer>
-class btree_iterator {
+class btree_iterator : private btree_iterator_generation_info {
   using field_type = typename Node::field_type;
   using key_type = typename Node::key_type;
   using size_type = typename Node::size_type;
@@ -1049,13 +1137,11 @@
 
   btree_iterator() : btree_iterator(nullptr, -1) {}
   explicit btree_iterator(Node *n) : btree_iterator(n, n->start()) {}
-  btree_iterator(Node *n, int p) : node_(n), position_(p) {
-#ifdef ABSL_BTREE_ENABLE_GENERATIONS
-    // Use `~uint32_t{}` as a sentinel value for iterator generations so it
-    // doesn't match the initial value for the actual generation.
-    generation_ = n != nullptr ? n->generation() : ~uint32_t{};
-#endif
-  }
+  btree_iterator(Node *n, int p)
+      : btree_iterator_generation_info(n != nullptr ? n->generation()
+                                                    : ~uint32_t{}),
+        node_(n),
+        position_(p) {}
 
   // NOTE: this SFINAE allows for implicit conversions from iterator to
   // const_iterator, but it specifically avoids hiding the copy constructor so
@@ -1066,31 +1152,42 @@
                     std::is_same<btree_iterator, const_iterator>::value,
                 int> = 0>
   btree_iterator(const btree_iterator<N, R, P> other)  // NOLINT
-      : node_(other.node_), position_(other.position_) {
-#ifdef ABSL_BTREE_ENABLE_GENERATIONS
-    generation_ = other.generation_;
-#endif
-  }
+      : btree_iterator_generation_info(other),
+        node_(other.node_),
+        position_(other.position_) {}
 
   bool operator==(const iterator &other) const {
-    return node_ == other.node_ && position_ == other.position_;
+    return Equals(other);
   }
   bool operator==(const const_iterator &other) const {
-    return node_ == other.node_ && position_ == other.position_;
+    return Equals(other);
   }
   bool operator!=(const iterator &other) const {
-    return node_ != other.node_ || position_ != other.position_;
+    return !Equals(other);
   }
   bool operator!=(const const_iterator &other) const {
-    return node_ != other.node_ || position_ != other.position_;
+    return !Equals(other);
+  }
+
+  // Returns n such that n calls to ++other yields *this.
+  // Precondition: n exists.
+  difference_type operator-(const_iterator other) const {
+    if (node_ == other.node_) {
+      if (node_->is_leaf()) return position_ - other.position_;
+      if (position_ == other.position_) return 0;
+    }
+    return distance_slow(other);
   }
 
   // Accessors for the key/value the iterator is pointing at.
   reference operator*() const {
     ABSL_HARDENING_ASSERT(node_ != nullptr);
-    ABSL_HARDENING_ASSERT(node_->start() <= position_);
-    ABSL_HARDENING_ASSERT(node_->finish() > position_);
-    assert_valid_generation();
+    assert_valid_generation(node_);
+    ABSL_HARDENING_ASSERT(position_ >= node_->start());
+    if (position_ >= node_->finish()) {
+      ABSL_HARDENING_ASSERT(!IsEndIterator() && "Dereferencing end() iterator");
+      ABSL_HARDENING_ASSERT(position_ < node_->finish());
+    }
     return node_->value(static_cast<field_type>(position_));
   }
   pointer operator->() const { return &operator*(); }
@@ -1141,16 +1238,43 @@
                     std::is_same<btree_iterator, iterator>::value,
                 int> = 0>
   explicit btree_iterator(const btree_iterator<N, R, P> other)
-      : node_(const_cast<node_type *>(other.node_)),
-        position_(other.position_) {
-#ifdef ABSL_BTREE_ENABLE_GENERATIONS
-    generation_ = other.generation_;
-#endif
+      : btree_iterator_generation_info(other.generation()),
+        node_(const_cast<node_type *>(other.node_)),
+        position_(other.position_) {}
+
+  bool Equals(const const_iterator other) const {
+    ABSL_HARDENING_ASSERT(((node_ == nullptr && other.node_ == nullptr) ||
+                           (node_ != nullptr && other.node_ != nullptr)) &&
+                          "Comparing default-constructed iterator with "
+                          "non-default-constructed iterator.");
+    // Note: we use assert instead of ABSL_HARDENING_ASSERT here because this
+    // changes the complexity of Equals from O(1) to O(log(N) + log(M)) where
+    // N/M are sizes of the containers containing node_/other.node_.
+    assert(AreNodesFromSameContainer(node_, other.node_) &&
+           "Comparing iterators from different containers.");
+    assert_valid_generation(node_);
+    other.assert_valid_generation(other.node_);
+    return node_ == other.node_ && position_ == other.position_;
   }
 
+  bool IsEndIterator() const {
+    if (position_ != node_->finish()) return false;
+    node_type *node = node_;
+    while (!node->is_root()) {
+      if (node->position() != node->parent()->finish()) return false;
+      node = node->parent();
+    }
+    return true;
+  }
+
+  // Returns n such that n calls to ++other yields *this.
+  // Precondition: n exists && (this->node_ != other.node_ ||
+  // !this->node_->is_leaf() || this->position_ != other.position_).
+  difference_type distance_slow(const_iterator other) const;
+
   // Increment/decrement the iterator.
   void increment() {
-    assert_valid_generation();
+    assert_valid_generation(node_);
     if (node_->is_leaf() && ++position_ < node_->finish()) {
       return;
     }
@@ -1159,7 +1283,7 @@
   void increment_slow();
 
   void decrement() {
-    assert_valid_generation();
+    assert_valid_generation(node_);
     if (node_->is_leaf() && --position_ >= node_->start()) {
       return;
     }
@@ -1167,14 +1291,6 @@
   }
   void decrement_slow();
 
-  // Updates the generation. For use internally right before we return an
-  // iterator to the user.
-  void update_generation() {
-#ifdef ABSL_BTREE_ENABLE_GENERATIONS
-    if (node_ != nullptr) generation_ = node_->generation();
-#endif
-  }
-
   const key_type &key() const {
     return node_->key(static_cast<size_type>(position_));
   }
@@ -1182,15 +1298,8 @@
     return node_->slot(static_cast<size_type>(position_));
   }
 
-  void assert_valid_generation() const {
-#ifdef ABSL_BTREE_ENABLE_GENERATIONS
-    if (node_ != nullptr && node_->generation() != generation_) {
-      ABSL_INTERNAL_LOG(
-          FATAL,
-          "Attempting to use an invalidated iterator. The corresponding b-tree "
-          "container has been mutated since this iterator was constructed.");
-    }
-#endif
+  void update_generation() {
+    btree_iterator_generation_info::update_generation(node_);
   }
 
   // The node in the tree the iterator is pointing at.
@@ -1199,10 +1308,6 @@
   // NOTE: this is an int rather than a field_type because iterators can point
   // to invalid positions (such as -1) in certain circumstances.
   int position_;
-#ifdef ABSL_BTREE_ENABLE_GENERATIONS
-  // Used to check that the iterator hasn't been invalidated.
-  uint32_t generation_;
-#endif
 };
 
 template <typename Params>
@@ -1437,7 +1542,8 @@
 
   // Insert a range of values into the btree.
   template <typename InputIterator>
-  void insert_iterator_multi(InputIterator b, InputIterator e);
+  void insert_iterator_multi(InputIterator b,
+                             InputIterator e);
 
   // Erase the specified iterator from the btree. The iterator must be valid
   // (i.e. not equal to end()).  Return an iterator pointing to the node after
@@ -1593,19 +1699,19 @@
   }
 
   // Node creation/deletion routines.
-  node_type *new_internal_node(node_type *parent) {
+  node_type *new_internal_node(field_type position, node_type *parent) {
     node_type *n = allocate(node_type::InternalSize());
-    n->init_internal(parent);
+    n->init_internal(position, parent);
     return n;
   }
-  node_type *new_leaf_node(node_type *parent) {
+  node_type *new_leaf_node(field_type position, node_type *parent) {
     node_type *n = allocate(node_type::LeafSize());
-    n->init_leaf(kNodeSlots, parent);
+    n->init_leaf(position, kNodeSlots, parent);
     return n;
   }
   node_type *new_leaf_root_node(field_type max_count) {
     node_type *n = allocate(node_type::LeafSize(max_count));
-    n->init_leaf(max_count, /*parent=*/n);
+    n->init_leaf(/*position=*/0, max_count, /*parent=*/n);
     return n;
   }
 
@@ -1844,6 +1950,8 @@
                           allocator_type *alloc) {
   assert(dest->count() == 0);
   assert(max_count() == kNodeSlots);
+  assert(position() + 1 == dest->position());
+  assert(parent() == dest->parent());
 
   // We bias the split based on the position being inserted. If we're
   // inserting at the beginning of the left node then bias the split to put
@@ -1866,7 +1974,7 @@
   --mutable_finish();
   parent()->emplace_value(position(), alloc, finish_slot());
   value_destroy(finish(), alloc);
-  parent()->init_child(position() + 1, dest);
+  parent()->set_child_noupdate_position(position() + 1, dest);
 
   if (is_internal()) {
     for (field_type i = dest->start(), j = finish() + 1; i <= dest->finish();
@@ -1975,6 +2083,64 @@
 
 ////
 // btree_iterator methods
+
+// Note: the implementation here is based on btree_node::clear_and_delete.
+template <typename N, typename R, typename P>
+auto btree_iterator<N, R, P>::distance_slow(const_iterator other) const
+    -> difference_type {
+  const_iterator begin = other;
+  const_iterator end = *this;
+  assert(begin.node_ != end.node_ || !begin.node_->is_leaf() ||
+         begin.position_ != end.position_);
+
+  const node_type *node = begin.node_;
+  // We need to compensate for double counting if begin.node_ is a leaf node.
+  difference_type count = node->is_leaf() ? -begin.position_ : 0;
+
+  // First navigate to the leftmost leaf node past begin.
+  if (node->is_internal()) {
+    ++count;
+    node = node->child(begin.position_ + 1);
+  }
+  while (node->is_internal()) node = node->start_child();
+
+  // Use `size_type` because `pos` needs to be able to hold `kNodeSlots+1`,
+  // which isn't guaranteed to be a valid `field_type`.
+  size_type pos = node->position();
+  const node_type *parent = node->parent();
+  for (;;) {
+    // In each iteration of the next loop, we count one leaf node and go right.
+    assert(pos <= parent->finish());
+    do {
+      node = parent->child(static_cast<field_type>(pos));
+      if (node->is_internal()) {
+        // Navigate to the leftmost leaf under node.
+        while (node->is_internal()) node = node->start_child();
+        pos = node->position();
+        parent = node->parent();
+      }
+      if (node == end.node_) return count + end.position_;
+      if (parent == end.node_ && pos == static_cast<size_type>(end.position_))
+        return count + node->count();
+      // +1 is for the next internal node value.
+      count += node->count() + 1;
+      ++pos;
+    } while (pos <= parent->finish());
+
+    // Once we've counted all children of parent, go up/right.
+    assert(pos > parent->finish());
+    do {
+      node = parent;
+      pos = node->position();
+      parent = node->parent();
+      // -1 because we counted the value at end and shouldn't.
+      if (parent == end.node_ && pos == static_cast<size_type>(end.position_))
+        return count - 1;
+      ++pos;
+    } while (pos > parent->finish());
+  }
+}
+
 template <typename N, typename R, typename P>
 void btree_iterator<N, R, P>::increment_slow() {
   if (node_->is_leaf()) {
@@ -2052,7 +2218,7 @@
                 "Key comparison must be nothrow copy constructible");
   static_assert(std::is_nothrow_copy_constructible<allocator_type>::value,
                 "Allocator must be nothrow copy constructible");
-  static_assert(type_traits_internal::is_trivially_copyable<iterator>::value,
+  static_assert(std::is_trivially_copyable<iterator>::value,
                 "iterator not trivially copyable.");
 
   // Note: We assert that kTargetValues, which is computed from
@@ -2371,7 +2537,7 @@
 template <typename P>
 auto btree<P>::erase_range(iterator begin, iterator end)
     -> std::pair<size_type, iterator> {
-  size_type count = static_cast<size_type>(std::distance(begin, end));
+  size_type count = static_cast<size_type>(end - begin);
   assert(count >= 0);
 
   if (count == 0) {
@@ -2525,16 +2691,17 @@
     // value.
     assert(parent->max_count() == kNodeSlots);
     if (parent->count() == kNodeSlots) {
-      iterator parent_iter(node->parent(), node->position());
+      iterator parent_iter(parent, node->position());
       rebalance_or_split(&parent_iter);
+      parent = node->parent();
     }
   } else {
     // Rebalancing not possible because this is the root node.
     // Create a new root node and set the current root node as the child of the
     // new root.
-    parent = new_internal_node(parent);
+    parent = new_internal_node(/*position=*/0, parent);
     parent->set_generation(root()->generation());
-    parent->init_child(parent->start(), root());
+    parent->init_child(parent->start(), node);
     mutable_root() = parent;
     // If the former root was a leaf node, then it's now the rightmost node.
     assert(parent->start_child()->is_internal() ||
@@ -2544,11 +2711,11 @@
   // Split the node.
   node_type *split_node;
   if (node->is_leaf()) {
-    split_node = new_leaf_node(parent);
+    split_node = new_leaf_node(node->position() + 1, parent);
     node->split(insert_position, split_node, mutable_allocator());
     if (rightmost() == node) mutable_rightmost() = split_node;
   } else {
-    split_node = new_internal_node(parent);
+    split_node = new_internal_node(node->position() + 1, parent);
     node->split(insert_position, split_node, mutable_allocator());
   }
 
@@ -2664,30 +2831,67 @@
   }
   const field_type max_count = iter.node_->max_count();
   allocator_type *alloc = mutable_allocator();
+
+  const auto transfer_and_delete = [&](node_type *old_node,
+                                       node_type *new_node) {
+    new_node->transfer_n(old_node->count(), new_node->start(),
+                         old_node->start(), old_node, alloc);
+    new_node->set_finish(old_node->finish());
+    old_node->set_finish(old_node->start());
+    new_node->set_generation(old_node->generation());
+    node_type::clear_and_delete(old_node, alloc);
+  };
+  const auto replace_leaf_root_node = [&](field_type new_node_size) {
+    assert(iter.node_ == root());
+    node_type *old_root = iter.node_;
+    node_type *new_root = iter.node_ = new_leaf_root_node(new_node_size);
+    transfer_and_delete(old_root, new_root);
+    mutable_root() = mutable_rightmost() = new_root;
+  };
+
+  bool replaced_node = false;
   if (iter.node_->count() == max_count) {
     // Make room in the leaf for the new item.
     if (max_count < kNodeSlots) {
       // Insertion into the root where the root is smaller than the full node
       // size. Simply grow the size of the root node.
-      assert(iter.node_ == root());
-      iter.node_ = new_leaf_root_node(static_cast<field_type>(
+      replace_leaf_root_node(static_cast<field_type>(
           (std::min)(static_cast<int>(kNodeSlots), 2 * max_count)));
-      // Transfer the values from the old root to the new root.
-      node_type *old_root = root();
-      node_type *new_root = iter.node_;
-      new_root->transfer_n(old_root->count(), new_root->start(),
-                           old_root->start(), old_root, alloc);
-      new_root->set_finish(old_root->finish());
-      old_root->set_finish(old_root->start());
-      new_root->set_generation(old_root->generation());
-      node_type::clear_and_delete(old_root, alloc);
-      mutable_root() = mutable_rightmost() = new_root;
+      replaced_node = true;
     } else {
       rebalance_or_split(&iter);
     }
   }
+  (void)replaced_node;
+#ifdef ABSL_HAVE_ADDRESS_SANITIZER
+  if (!replaced_node) {
+    assert(iter.node_->is_leaf());
+    if (iter.node_->is_root()) {
+      replace_leaf_root_node(max_count);
+    } else {
+      node_type *old_node = iter.node_;
+      const bool was_rightmost = rightmost() == old_node;
+      const bool was_leftmost = leftmost() == old_node;
+      node_type *parent = old_node->parent();
+      const field_type position = old_node->position();
+      node_type *new_node = iter.node_ = new_leaf_node(position, parent);
+      parent->set_child_noupdate_position(position, new_node);
+      transfer_and_delete(old_node, new_node);
+      if (was_rightmost) mutable_rightmost() = new_node;
+      // The leftmost node is stored as the parent of the root node.
+      if (was_leftmost) root()->set_parent(new_node);
+    }
+  }
+#endif
   iter.node_->emplace_value(static_cast<field_type>(iter.position_), alloc,
                             std::forward<Args>(args)...);
+  assert(
+      iter.node_->is_ordered_correctly(static_cast<field_type>(iter.position_),
+                                       original_key_compare(key_comp())) &&
+      "If this assert fails, then either (1) the comparator may violate "
+      "transitivity, i.e. comp(a,b) && comp(b,c) -> comp(a,c) (see "
+      "https://en.cppreference.com/w/cpp/named_req/Compare), or (2) a "
+      "key may have been mutated after it was inserted into the tree.");
   ++size_;
   iter.update_generation();
   return iter;
diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h
index fc2f740..a68ce44 100644
--- a/absl/container/internal/btree_container.h
+++ b/absl/container/internal/btree_container.h
@@ -65,6 +65,11 @@
   using const_reverse_iterator = typename Tree::const_reverse_iterator;
   using node_type = typename Tree::node_handle_type;
 
+  struct extract_and_get_next_return_type {
+    node_type node;
+    iterator next;
+  };
+
   // Constructors/assignments.
   btree_container() : tree_(key_compare(), allocator_type()) {}
   explicit btree_container(const key_compare &comp,
@@ -90,31 +95,50 @@
       std::is_nothrow_move_assignable<Tree>::value) = default;
 
   // Iterator routines.
-  iterator begin() { return tree_.begin(); }
-  const_iterator begin() const { return tree_.begin(); }
-  const_iterator cbegin() const { return tree_.begin(); }
-  iterator end() { return tree_.end(); }
-  const_iterator end() const { return tree_.end(); }
-  const_iterator cend() const { return tree_.end(); }
-  reverse_iterator rbegin() { return tree_.rbegin(); }
-  const_reverse_iterator rbegin() const { return tree_.rbegin(); }
-  const_reverse_iterator crbegin() const { return tree_.rbegin(); }
-  reverse_iterator rend() { return tree_.rend(); }
-  const_reverse_iterator rend() const { return tree_.rend(); }
-  const_reverse_iterator crend() const { return tree_.rend(); }
+  iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return tree_.begin(); }
+  const_iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return tree_.begin();
+  }
+  const_iterator cbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return tree_.begin();
+  }
+  iterator end() ABSL_ATTRIBUTE_LIFETIME_BOUND { return tree_.end(); }
+  const_iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return tree_.end();
+  }
+  const_iterator cend() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return tree_.end();
+  }
+  reverse_iterator rbegin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return tree_.rbegin();
+  }
+  const_reverse_iterator rbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return tree_.rbegin();
+  }
+  const_reverse_iterator crbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return tree_.rbegin();
+  }
+  reverse_iterator rend() ABSL_ATTRIBUTE_LIFETIME_BOUND { return tree_.rend(); }
+  const_reverse_iterator rend() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return tree_.rend();
+  }
+  const_reverse_iterator crend() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return tree_.rend();
+  }
 
   // Lookup routines.
   template <typename K = key_type>
   size_type count(const key_arg<K> &key) const {
     auto equal_range = this->equal_range(key);
-    return std::distance(equal_range.first, equal_range.second);
+    return equal_range.second - equal_range.first;
   }
   template <typename K = key_type>
-  iterator find(const key_arg<K> &key) {
+  iterator find(const key_arg<K> &key) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return tree_.find(key);
   }
   template <typename K = key_type>
-  const_iterator find(const key_arg<K> &key) const {
+  const_iterator find(const key_arg<K> &key) const
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return tree_.find(key);
   }
   template <typename K = key_type>
@@ -122,28 +146,31 @@
     return find(key) != end();
   }
   template <typename K = key_type>
-  iterator lower_bound(const key_arg<K> &key) {
+  iterator lower_bound(const key_arg<K> &key) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return tree_.lower_bound(key);
   }
   template <typename K = key_type>
-  const_iterator lower_bound(const key_arg<K> &key) const {
+  const_iterator lower_bound(const key_arg<K> &key) const
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return tree_.lower_bound(key);
   }
   template <typename K = key_type>
-  iterator upper_bound(const key_arg<K> &key) {
+  iterator upper_bound(const key_arg<K> &key) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return tree_.upper_bound(key);
   }
   template <typename K = key_type>
-  const_iterator upper_bound(const key_arg<K> &key) const {
+  const_iterator upper_bound(const key_arg<K> &key) const
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return tree_.upper_bound(key);
   }
   template <typename K = key_type>
-  std::pair<iterator, iterator> equal_range(const key_arg<K> &key) {
+  std::pair<iterator, iterator> equal_range(const key_arg<K> &key)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return tree_.equal_range(key);
   }
   template <typename K = key_type>
   std::pair<const_iterator, const_iterator> equal_range(
-      const key_arg<K> &key) const {
+      const key_arg<K> &key) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return tree_.equal_range(key);
   }
 
@@ -153,9 +180,14 @@
   // Erase the specified iterator from the btree. The iterator must be valid
   // (i.e. not equal to end()).  Return an iterator pointing to the node after
   // the one that was erased (or end() if none exists).
-  iterator erase(const_iterator iter) { return tree_.erase(iterator(iter)); }
-  iterator erase(iterator iter) { return tree_.erase(iter); }
-  iterator erase(const_iterator first, const_iterator last) {
+  iterator erase(const_iterator iter) ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return tree_.erase(iterator(iter));
+  }
+  iterator erase(iterator iter) ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return tree_.erase(iter);
+  }
+  iterator erase(const_iterator first,
+                 const_iterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return tree_.erase_range(iterator(first), iterator(last)).second;
   }
   template <typename K = key_type>
@@ -165,6 +197,15 @@
   }
 
   // Extract routines.
+  extract_and_get_next_return_type extract_and_get_next(const_iterator position)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    // Use Construct instead of Transfer because the rebalancing code will
+    // destroy the slot later.
+    // Note: we rely on erase() taking place after Construct().
+    return {CommonAccess::Construct<node_type>(get_allocator(),
+                                               iterator(position).slot()),
+            erase(position)};
+  }
   node_type extract(iterator position) {
     // Use Construct instead of Transfer because the rebalancing code will
     // destroy the slot later.
@@ -284,32 +325,38 @@
       : btree_set_container(init.begin(), init.end(), alloc) {}
 
   // Insertion routines.
-  std::pair<iterator, bool> insert(const value_type &v) {
+  std::pair<iterator, bool> insert(const value_type &v)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return this->tree_.insert_unique(params_type::key(v), v);
   }
-  std::pair<iterator, bool> insert(value_type &&v) {
+  std::pair<iterator, bool> insert(value_type &&v)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return this->tree_.insert_unique(params_type::key(v), std::move(v));
   }
   template <typename... Args>
-  std::pair<iterator, bool> emplace(Args &&... args) {
+  std::pair<iterator, bool> emplace(Args &&...args)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     // Use a node handle to manage a temp slot.
     auto node = CommonAccess::Construct<node_type>(this->get_allocator(),
                                                    std::forward<Args>(args)...);
     auto *slot = CommonAccess::GetSlot(node);
     return this->tree_.insert_unique(params_type::key(slot), slot);
   }
-  iterator insert(const_iterator hint, const value_type &v) {
+  iterator insert(const_iterator hint,
+                  const value_type &v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return this->tree_
         .insert_hint_unique(iterator(hint), params_type::key(v), v)
         .first;
   }
-  iterator insert(const_iterator hint, value_type &&v) {
+  iterator insert(const_iterator hint,
+                  value_type &&v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return this->tree_
         .insert_hint_unique(iterator(hint), params_type::key(v), std::move(v))
         .first;
   }
   template <typename... Args>
-  iterator emplace_hint(const_iterator hint, Args &&... args) {
+  iterator emplace_hint(const_iterator hint,
+                        Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     // Use a node handle to manage a temp slot.
     auto node = CommonAccess::Construct<node_type>(this->get_allocator(),
                                                    std::forward<Args>(args)...);
@@ -325,7 +372,7 @@
   void insert(std::initializer_list<init_type> init) {
     this->tree_.insert_iterator_unique(init.begin(), init.end(), 0);
   }
-  insert_return_type insert(node_type &&node) {
+  insert_return_type insert(node_type &&node) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     if (!node) return {this->end(), false, node_type()};
     std::pair<iterator, bool> res =
         this->tree_.insert_unique(params_type::key(CommonAccess::GetSlot(node)),
@@ -337,7 +384,8 @@
       return {res.first, false, std::move(node)};
     }
   }
-  iterator insert(const_iterator hint, node_type &&node) {
+  iterator insert(const_iterator hint,
+                  node_type &&node) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     if (!node) return this->end();
     std::pair<iterator, bool> res = this->tree_.insert_hint_unique(
         iterator(hint), params_type::key(CommonAccess::GetSlot(node)),
@@ -420,37 +468,43 @@
   // Note: the nullptr template arguments and extra `const M&` overloads allow
   // for supporting bitfield arguments.
   template <typename K = key_type, class M>
-  std::pair<iterator, bool> insert_or_assign(const key_arg<K> &k,
-                                             const M &obj) {
+  std::pair<iterator, bool> insert_or_assign(const key_arg<K> &k, const M &obj)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign_impl(k, obj);
   }
   template <typename K = key_type, class M, K * = nullptr>
-  std::pair<iterator, bool> insert_or_assign(key_arg<K> &&k, const M &obj) {
+  std::pair<iterator, bool> insert_or_assign(key_arg<K> &&k, const M &obj)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign_impl(std::forward<K>(k), obj);
   }
   template <typename K = key_type, class M, M * = nullptr>
-  std::pair<iterator, bool> insert_or_assign(const key_arg<K> &k, M &&obj) {
+  std::pair<iterator, bool> insert_or_assign(const key_arg<K> &k, M &&obj)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign_impl(k, std::forward<M>(obj));
   }
   template <typename K = key_type, class M, K * = nullptr, M * = nullptr>
-  std::pair<iterator, bool> insert_or_assign(key_arg<K> &&k, M &&obj) {
+  std::pair<iterator, bool> insert_or_assign(key_arg<K> &&k, M &&obj)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign_impl(std::forward<K>(k), std::forward<M>(obj));
   }
   template <typename K = key_type, class M>
   iterator insert_or_assign(const_iterator hint, const key_arg<K> &k,
-                            const M &obj) {
+                            const M &obj) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign_hint_impl(hint, k, obj);
   }
   template <typename K = key_type, class M, K * = nullptr>
-  iterator insert_or_assign(const_iterator hint, key_arg<K> &&k, const M &obj) {
+  iterator insert_or_assign(const_iterator hint, key_arg<K> &&k,
+                            const M &obj) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign_hint_impl(hint, std::forward<K>(k), obj);
   }
   template <typename K = key_type, class M, M * = nullptr>
-  iterator insert_or_assign(const_iterator hint, const key_arg<K> &k, M &&obj) {
+  iterator insert_or_assign(const_iterator hint, const key_arg<K> &k,
+                            M &&obj) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign_hint_impl(hint, k, std::forward<M>(obj));
   }
   template <typename K = key_type, class M, K * = nullptr, M * = nullptr>
-  iterator insert_or_assign(const_iterator hint, key_arg<K> &&k, M &&obj) {
+  iterator insert_or_assign(const_iterator hint, key_arg<K> &&k,
+                            M &&obj) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign_hint_impl(hint, std::forward<K>(k),
                                       std::forward<M>(obj));
   }
@@ -458,44 +512,48 @@
   template <typename K = key_type, typename... Args,
             typename absl::enable_if_t<
                 !std::is_convertible<K, const_iterator>::value, int> = 0>
-  std::pair<iterator, bool> try_emplace(const key_arg<K> &k, Args &&... args) {
+  std::pair<iterator, bool> try_emplace(const key_arg<K> &k, Args &&...args)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return try_emplace_impl(k, std::forward<Args>(args)...);
   }
   template <typename K = key_type, typename... Args,
             typename absl::enable_if_t<
                 !std::is_convertible<K, const_iterator>::value, int> = 0>
-  std::pair<iterator, bool> try_emplace(key_arg<K> &&k, Args &&... args) {
+  std::pair<iterator, bool> try_emplace(key_arg<K> &&k, Args &&...args)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return try_emplace_impl(std::forward<K>(k), std::forward<Args>(args)...);
   }
   template <typename K = key_type, typename... Args>
   iterator try_emplace(const_iterator hint, const key_arg<K> &k,
-                       Args &&... args) {
+                       Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return try_emplace_hint_impl(hint, k, std::forward<Args>(args)...);
   }
   template <typename K = key_type, typename... Args>
-  iterator try_emplace(const_iterator hint, key_arg<K> &&k, Args &&... args) {
+  iterator try_emplace(const_iterator hint, key_arg<K> &&k,
+                       Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return try_emplace_hint_impl(hint, std::forward<K>(k),
                                  std::forward<Args>(args)...);
   }
 
   template <typename K = key_type>
-  mapped_type &operator[](const key_arg<K> &k) {
+  mapped_type &operator[](const key_arg<K> &k) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return try_emplace(k).first->second;
   }
   template <typename K = key_type>
-  mapped_type &operator[](key_arg<K> &&k) {
+  mapped_type &operator[](key_arg<K> &&k) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return try_emplace(std::forward<K>(k)).first->second;
   }
 
   template <typename K = key_type>
-  mapped_type &at(const key_arg<K> &key) {
+  mapped_type &at(const key_arg<K> &key) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     auto it = this->find(key);
     if (it == this->end())
       base_internal::ThrowStdOutOfRange("absl::btree_map::at");
     return it->second;
   }
   template <typename K = key_type>
-  const mapped_type &at(const key_arg<K> &key) const {
+  const mapped_type &at(const key_arg<K> &key) const
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     auto it = this->find(key);
     if (it == this->end())
       base_internal::ThrowStdOutOfRange("absl::btree_map::at");
@@ -586,14 +644,18 @@
       : btree_multiset_container(init.begin(), init.end(), alloc) {}
 
   // Insertion routines.
-  iterator insert(const value_type &v) { return this->tree_.insert_multi(v); }
-  iterator insert(value_type &&v) {
+  iterator insert(const value_type &v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return this->tree_.insert_multi(v);
+  }
+  iterator insert(value_type &&v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return this->tree_.insert_multi(std::move(v));
   }
-  iterator insert(const_iterator hint, const value_type &v) {
+  iterator insert(const_iterator hint,
+                  const value_type &v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return this->tree_.insert_hint_multi(iterator(hint), v);
   }
-  iterator insert(const_iterator hint, value_type &&v) {
+  iterator insert(const_iterator hint,
+                  value_type &&v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return this->tree_.insert_hint_multi(iterator(hint), std::move(v));
   }
   template <typename InputIterator>
@@ -604,21 +666,22 @@
     this->tree_.insert_iterator_multi(init.begin(), init.end());
   }
   template <typename... Args>
-  iterator emplace(Args &&... args) {
+  iterator emplace(Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     // Use a node handle to manage a temp slot.
     auto node = CommonAccess::Construct<node_type>(this->get_allocator(),
                                                    std::forward<Args>(args)...);
     return this->tree_.insert_multi(CommonAccess::GetSlot(node));
   }
   template <typename... Args>
-  iterator emplace_hint(const_iterator hint, Args &&... args) {
+  iterator emplace_hint(const_iterator hint,
+                        Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     // Use a node handle to manage a temp slot.
     auto node = CommonAccess::Construct<node_type>(this->get_allocator(),
                                                    std::forward<Args>(args)...);
     return this->tree_.insert_hint_multi(iterator(hint),
                                          CommonAccess::GetSlot(node));
   }
-  iterator insert(node_type &&node) {
+  iterator insert(node_type &&node) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     if (!node) return this->end();
     iterator res =
         this->tree_.insert_multi(params_type::key(CommonAccess::GetSlot(node)),
@@ -626,7 +689,8 @@
     CommonAccess::Destroy(&node);
     return res;
   }
-  iterator insert(const_iterator hint, node_type &&node) {
+  iterator insert(const_iterator hint,
+                  node_type &&node) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     if (!node) return this->end();
     iterator res = this->tree_.insert_hint_multi(
         iterator(hint),
diff --git a/absl/container/internal/common_policy_traits.h b/absl/container/internal/common_policy_traits.h
index c99e68f..3558a54 100644
--- a/absl/container/internal/common_policy_traits.h
+++ b/absl/container/internal/common_policy_traits.h
@@ -63,7 +63,7 @@
   //                UNINITIALIZED
   template <class Alloc>
   static void transfer(Alloc* alloc, slot_type* new_slot, slot_type* old_slot) {
-    transfer_impl(alloc, new_slot, old_slot, 0);
+    transfer_impl(alloc, new_slot, old_slot, Rank0{});
   }
 
   // PRECONDITION: `slot` is INITIALIZED
@@ -80,28 +80,46 @@
     return P::element(slot);
   }
 
+  static constexpr bool transfer_uses_memcpy() {
+    return std::is_same<decltype(transfer_impl<std::allocator<char>>(
+                            nullptr, nullptr, nullptr, Rank0{})),
+                        std::true_type>::value;
+  }
+
  private:
+  // To rank the overloads below for overload resolution. Rank0 is preferred.
+  struct Rank2 {};
+  struct Rank1 : Rank2 {};
+  struct Rank0 : Rank1 {};
+
   // Use auto -> decltype as an enabler.
   template <class Alloc, class P = Policy>
   static auto transfer_impl(Alloc* alloc, slot_type* new_slot,
-                            slot_type* old_slot, int)
+                            slot_type* old_slot, Rank0)
       -> decltype((void)P::transfer(alloc, new_slot, old_slot)) {
     P::transfer(alloc, new_slot, old_slot);
   }
-  template <class Alloc>
-  static void transfer_impl(Alloc* alloc, slot_type* new_slot,
-                            slot_type* old_slot, char) {
 #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
-    if (absl::is_trivially_relocatable<value_type>()) {
-      // TODO(b/247130232): remove cast after fixing class-memaccess warning.
-      std::memcpy(static_cast<void*>(
-                      std::launder(const_cast<std::remove_const_t<value_type>*>(
-                          &element(new_slot)))),
-                  &element(old_slot), sizeof(value_type));
-      return;
-    }
+  // This overload returns true_type for the trait below.
+  // The conditional_t is to make the enabler type dependent.
+  template <class Alloc,
+            typename = std::enable_if_t<absl::is_trivially_relocatable<
+                std::conditional_t<false, Alloc, value_type>>::value>>
+  static std::true_type transfer_impl(Alloc*, slot_type* new_slot,
+                                      slot_type* old_slot, Rank1) {
+    // TODO(b/247130232): remove casts after fixing warnings.
+    // TODO(b/251814870): remove casts after fixing warnings.
+    std::memcpy(
+        static_cast<void*>(std::launder(
+            const_cast<std::remove_const_t<value_type>*>(&element(new_slot)))),
+        static_cast<const void*>(&element(old_slot)), sizeof(value_type));
+    return {};
+  }
 #endif
 
+  template <class Alloc>
+  static void transfer_impl(Alloc* alloc, slot_type* new_slot,
+                            slot_type* old_slot, Rank2) {
     construct(alloc, new_slot, std::move(element(old_slot)));
     destroy(alloc, old_slot);
   }
diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h
index c29c533..f59ca4e 100644
--- a/absl/container/internal/container_memory.h
+++ b/absl/container/internal/container_memory.h
@@ -165,7 +165,7 @@
       std::forward<F>(f));
 }
 
-// Given arguments of an std::pair's consructor, PairArgs() returns a pair of
+// Given arguments of an std::pair's constructor, PairArgs() returns a pair of
 // tuples with references to the passed arguments. The tuples contain
 // constructor arguments for the first and the second elements of the pair.
 //
@@ -428,9 +428,10 @@
     emplace(new_slot);
 #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
     if (absl::is_trivially_relocatable<value_type>()) {
-      // TODO(b/247130232): remove cast after fixing class-memaccess warning.
+      // TODO(b/247130232,b/251814870): remove casts after fixing warnings.
       std::memcpy(static_cast<void*>(std::launder(&new_slot->value)),
-                  &old_slot->value, sizeof(value_type));
+                  static_cast<const void*>(&old_slot->value),
+                  sizeof(value_type));
       return;
     }
 #endif
diff --git a/absl/container/internal/hash_function_defaults.h b/absl/container/internal/hash_function_defaults.h
index 250e662..a3613b4 100644
--- a/absl/container/internal/hash_function_defaults.h
+++ b/absl/container/internal/hash_function_defaults.h
@@ -56,6 +56,10 @@
 #include "absl/strings/cord.h"
 #include "absl/strings/string_view.h"
 
+#ifdef ABSL_HAVE_STD_STRING_VIEW
+#include <string_view>
+#endif
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
@@ -107,6 +111,48 @@
 template <>
 struct HashEq<absl::Cord> : StringHashEq {};
 
+#ifdef ABSL_HAVE_STD_STRING_VIEW
+
+template <typename TChar>
+struct BasicStringHash {
+  using is_transparent = void;
+
+  size_t operator()(std::basic_string_view<TChar> v) const {
+    return absl::Hash<std::basic_string_view<TChar>>{}(v);
+  }
+};
+
+template <typename TChar>
+struct BasicStringEq {
+  using is_transparent = void;
+  bool operator()(std::basic_string_view<TChar> lhs,
+                  std::basic_string_view<TChar> rhs) const {
+    return lhs == rhs;
+  }
+};
+
+// Supports heterogeneous lookup for w/u16/u32 string + string_view + char*.
+template <typename TChar>
+struct BasicStringHashEq {
+  using Hash = BasicStringHash<TChar>;
+  using Eq = BasicStringEq<TChar>;
+};
+
+template <>
+struct HashEq<std::wstring> : BasicStringHashEq<wchar_t> {};
+template <>
+struct HashEq<std::wstring_view> : BasicStringHashEq<wchar_t> {};
+template <>
+struct HashEq<std::u16string> : BasicStringHashEq<char16_t> {};
+template <>
+struct HashEq<std::u16string_view> : BasicStringHashEq<char16_t> {};
+template <>
+struct HashEq<std::u32string> : BasicStringHashEq<char32_t> {};
+template <>
+struct HashEq<std::u32string_view> : BasicStringHashEq<char32_t> {};
+
+#endif  // ABSL_HAVE_STD_STRING_VIEW
+
 // Supports heterogeneous lookup for pointers and smart pointers.
 template <class T>
 struct HashEq<T*> {
diff --git a/absl/container/internal/hash_function_defaults_test.cc b/absl/container/internal/hash_function_defaults_test.cc
index 9f0a4c7..c31af3b 100644
--- a/absl/container/internal/hash_function_defaults_test.cc
+++ b/absl/container/internal/hash_function_defaults_test.cc
@@ -24,6 +24,10 @@
 #include "absl/strings/cord_test_helpers.h"
 #include "absl/strings/string_view.h"
 
+#ifdef ABSL_HAVE_STD_STRING_VIEW
+#include <string_view>
+#endif
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
@@ -109,6 +113,168 @@
   EXPECT_NE(h, hash(std::string("b")));
 }
 
+TEST(BasicStringViewTest, WStringEqWorks) {
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+  GTEST_SKIP();
+#else
+  hash_default_eq<std::wstring> eq;
+  EXPECT_TRUE(eq(L"a", L"a"));
+  EXPECT_TRUE(eq(L"a", std::wstring_view(L"a")));
+  EXPECT_TRUE(eq(L"a", std::wstring(L"a")));
+  EXPECT_FALSE(eq(L"a", L"b"));
+  EXPECT_FALSE(eq(L"a", std::wstring_view(L"b")));
+  EXPECT_FALSE(eq(L"a", std::wstring(L"b")));
+#endif
+}
+
+TEST(BasicStringViewTest, WStringViewEqWorks) {
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+  GTEST_SKIP();
+#else
+  hash_default_eq<std::wstring_view> eq;
+  EXPECT_TRUE(eq(L"a", L"a"));
+  EXPECT_TRUE(eq(L"a", std::wstring_view(L"a")));
+  EXPECT_TRUE(eq(L"a", std::wstring(L"a")));
+  EXPECT_FALSE(eq(L"a", L"b"));
+  EXPECT_FALSE(eq(L"a", std::wstring_view(L"b")));
+  EXPECT_FALSE(eq(L"a", std::wstring(L"b")));
+#endif
+}
+
+TEST(BasicStringViewTest, U16StringEqWorks) {
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+  GTEST_SKIP();
+#else
+  hash_default_eq<std::u16string> eq;
+  EXPECT_TRUE(eq(u"a", u"a"));
+  EXPECT_TRUE(eq(u"a", std::u16string_view(u"a")));
+  EXPECT_TRUE(eq(u"a", std::u16string(u"a")));
+  EXPECT_FALSE(eq(u"a", u"b"));
+  EXPECT_FALSE(eq(u"a", std::u16string_view(u"b")));
+  EXPECT_FALSE(eq(u"a", std::u16string(u"b")));
+#endif
+}
+
+TEST(BasicStringViewTest, U16StringViewEqWorks) {
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+  GTEST_SKIP();
+#else
+  hash_default_eq<std::u16string_view> eq;
+  EXPECT_TRUE(eq(u"a", u"a"));
+  EXPECT_TRUE(eq(u"a", std::u16string_view(u"a")));
+  EXPECT_TRUE(eq(u"a", std::u16string(u"a")));
+  EXPECT_FALSE(eq(u"a", u"b"));
+  EXPECT_FALSE(eq(u"a", std::u16string_view(u"b")));
+  EXPECT_FALSE(eq(u"a", std::u16string(u"b")));
+#endif
+}
+
+TEST(BasicStringViewTest, U32StringEqWorks) {
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+  GTEST_SKIP();
+#else
+  hash_default_eq<std::u32string> eq;
+  EXPECT_TRUE(eq(U"a", U"a"));
+  EXPECT_TRUE(eq(U"a", std::u32string_view(U"a")));
+  EXPECT_TRUE(eq(U"a", std::u32string(U"a")));
+  EXPECT_FALSE(eq(U"a", U"b"));
+  EXPECT_FALSE(eq(U"a", std::u32string_view(U"b")));
+  EXPECT_FALSE(eq(U"a", std::u32string(U"b")));
+#endif
+}
+
+TEST(BasicStringViewTest, U32StringViewEqWorks) {
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+  GTEST_SKIP();
+#else
+  hash_default_eq<std::u32string_view> eq;
+  EXPECT_TRUE(eq(U"a", U"a"));
+  EXPECT_TRUE(eq(U"a", std::u32string_view(U"a")));
+  EXPECT_TRUE(eq(U"a", std::u32string(U"a")));
+  EXPECT_FALSE(eq(U"a", U"b"));
+  EXPECT_FALSE(eq(U"a", std::u32string_view(U"b")));
+  EXPECT_FALSE(eq(U"a", std::u32string(U"b")));
+#endif
+}
+
+TEST(BasicStringViewTest, WStringHashWorks) {
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+  GTEST_SKIP();
+#else
+  hash_default_hash<std::wstring> hash;
+  auto h = hash(L"a");
+  EXPECT_EQ(h, hash(std::wstring_view(L"a")));
+  EXPECT_EQ(h, hash(std::wstring(L"a")));
+  EXPECT_NE(h, hash(std::wstring_view(L"b")));
+  EXPECT_NE(h, hash(std::wstring(L"b")));
+#endif
+}
+
+TEST(BasicStringViewTest, WStringViewHashWorks) {
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+  GTEST_SKIP();
+#else
+  hash_default_hash<std::wstring_view> hash;
+  auto h = hash(L"a");
+  EXPECT_EQ(h, hash(std::wstring_view(L"a")));
+  EXPECT_EQ(h, hash(std::wstring(L"a")));
+  EXPECT_NE(h, hash(std::wstring_view(L"b")));
+  EXPECT_NE(h, hash(std::wstring(L"b")));
+#endif
+}
+
+TEST(BasicStringViewTest, U16StringHashWorks) {
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+  GTEST_SKIP();
+#else
+  hash_default_hash<std::u16string> hash;
+  auto h = hash(u"a");
+  EXPECT_EQ(h, hash(std::u16string_view(u"a")));
+  EXPECT_EQ(h, hash(std::u16string(u"a")));
+  EXPECT_NE(h, hash(std::u16string_view(u"b")));
+  EXPECT_NE(h, hash(std::u16string(u"b")));
+#endif
+}
+
+TEST(BasicStringViewTest, U16StringViewHashWorks) {
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+  GTEST_SKIP();
+#else
+  hash_default_hash<std::u16string_view> hash;
+  auto h = hash(u"a");
+  EXPECT_EQ(h, hash(std::u16string_view(u"a")));
+  EXPECT_EQ(h, hash(std::u16string(u"a")));
+  EXPECT_NE(h, hash(std::u16string_view(u"b")));
+  EXPECT_NE(h, hash(std::u16string(u"b")));
+#endif
+}
+
+TEST(BasicStringViewTest, U32StringHashWorks) {
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+  GTEST_SKIP();
+#else
+  hash_default_hash<std::u32string> hash;
+  auto h = hash(U"a");
+  EXPECT_EQ(h, hash(std::u32string_view(U"a")));
+  EXPECT_EQ(h, hash(std::u32string(U"a")));
+  EXPECT_NE(h, hash(std::u32string_view(U"b")));
+  EXPECT_NE(h, hash(std::u32string(U"b")));
+#endif
+}
+
+TEST(BasicStringViewTest, U32StringViewHashWorks) {
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+  GTEST_SKIP();
+#else
+  hash_default_hash<std::u32string_view> hash;
+  auto h = hash(U"a");
+  EXPECT_EQ(h, hash(std::u32string_view(U"a")));
+  EXPECT_EQ(h, hash(std::u32string(U"a")));
+  EXPECT_NE(h, hash(std::u32string_view(U"b")));
+  EXPECT_NE(h, hash(std::u32string(U"b")));
+#endif
+}
+
 struct NoDeleter {
   template <class T>
   void operator()(const T* ptr) const {}
diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc
index 5b8cf34..79a0973 100644
--- a/absl/container/internal/hashtablez_sampler.cc
+++ b/absl/container/internal/hashtablez_sampler.cc
@@ -14,6 +14,7 @@
 
 #include "absl/container/internal/hashtablez_sampler.h"
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <cmath>
@@ -22,11 +23,13 @@
 
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.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"
+#include "absl/time/clock.h"
 #include "absl/utility/utility.h"
 
 namespace absl {
@@ -158,6 +161,43 @@
   GlobalHashtablezSampler().Unregister(info);
 }
 
+void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) {
+#ifdef ABSL_INTERNAL_HAVE_SSE2
+  total_probe_length /= 16;
+#else
+  total_probe_length /= 8;
+#endif
+  info->total_probe_length.store(total_probe_length, std::memory_order_relaxed);
+  info->num_erases.store(0, std::memory_order_relaxed);
+  // There is only one concurrent writer, so `load` then `store` is sufficient
+  // instead of using `fetch_add`.
+  info->num_rehashes.store(
+      1 + info->num_rehashes.load(std::memory_order_relaxed),
+      std::memory_order_relaxed);
+}
+
+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);
+}
+
+void RecordClearedReservationSlow(HashtablezInfo* info) {
+  info->max_reserve.store(0, std::memory_order_relaxed);
+}
+
+void RecordStorageChangedSlow(HashtablezInfo* info, size_t size,
+                              size_t capacity) {
+  info->size.store(size, std::memory_order_relaxed);
+  info->capacity.store(capacity, std::memory_order_relaxed);
+  if (size == 0) {
+    // This is a clear, reset the total/num_erases too.
+    info->total_probe_length.store(0, std::memory_order_relaxed);
+    info->num_erases.store(0, std::memory_order_relaxed);
+  }
+}
+
 void RecordInsertSlow(HashtablezInfo* info, size_t hash,
                       size_t distance_from_desired) {
   // SwissTables probe in groups of 16, so scale this to count items probes and
@@ -180,6 +220,14 @@
   info->size.fetch_add(1, std::memory_order_relaxed);
 }
 
+void RecordEraseSlow(HashtablezInfo* info) {
+  info->size.fetch_sub(1, std::memory_order_relaxed);
+  // There is only one concurrent writer, so `load` then `store` is sufficient
+  // instead of using `fetch_add`.
+  info->num_erases.store(1 + info->num_erases.load(std::memory_order_relaxed),
+                         std::memory_order_relaxed);
+}
+
 void SetHashtablezConfigListener(HashtablezConfigListener l) {
   g_hashtablez_config_listener.store(l, std::memory_order_release);
 }
diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h
index a89518b..d8fd8f3 100644
--- a/absl/container/internal/hashtablez_sampler.h
+++ b/absl/container/internal/hashtablez_sampler.h
@@ -95,55 +95,19 @@
   size_t inline_element_size;  // How big is the slot?
 };
 
-inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) {
-#ifdef ABSL_INTERNAL_HAVE_SSE2
-  total_probe_length /= 16;
-#else
-  total_probe_length /= 8;
-#endif
-  info->total_probe_length.store(total_probe_length, std::memory_order_relaxed);
-  info->num_erases.store(0, std::memory_order_relaxed);
-  // There is only one concurrent writer, so `load` then `store` is sufficient
-  // instead of using `fetch_add`.
-  info->num_rehashes.store(
-      1 + info->num_rehashes.load(std::memory_order_relaxed),
-      std::memory_order_relaxed);
-}
+void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length);
 
-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);
-}
+void RecordReservationSlow(HashtablezInfo* info, size_t target_capacity);
 
-inline void RecordClearedReservationSlow(HashtablezInfo* info) {
-  info->max_reserve.store(0, std::memory_order_relaxed);
-}
+void RecordClearedReservationSlow(HashtablezInfo* info);
 
-inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size,
-                                     size_t capacity) {
-  info->size.store(size, std::memory_order_relaxed);
-  info->capacity.store(capacity, std::memory_order_relaxed);
-  if (size == 0) {
-    // This is a clear, reset the total/num_erases too.
-    info->total_probe_length.store(0, std::memory_order_relaxed);
-    info->num_erases.store(0, std::memory_order_relaxed);
-  }
-}
+void RecordStorageChangedSlow(HashtablezInfo* info, size_t size,
+                              size_t capacity);
 
 void RecordInsertSlow(HashtablezInfo* info, size_t hash,
                       size_t distance_from_desired);
 
-inline void RecordEraseSlow(HashtablezInfo* info) {
-  info->size.fetch_sub(1, std::memory_order_relaxed);
-  // There is only one concurrent writer, so `load` then `store` is sufficient
-  // instead of using `fetch_add`.
-  info->num_erases.store(
-      1 + info->num_erases.load(std::memory_order_relaxed),
-      std::memory_order_relaxed);
-}
+void RecordEraseSlow(HashtablezInfo* info);
 
 struct SamplingState {
   int64_t next_sample;
@@ -165,7 +129,10 @@
  public:
   explicit HashtablezInfoHandle() : info_(nullptr) {}
   explicit HashtablezInfoHandle(HashtablezInfo* info) : info_(info) {}
-  ~HashtablezInfoHandle() {
+
+  // We do not have a destructor. Caller is responsible for calling Unregister
+  // before destroying the handle.
+  void Unregister() {
     if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
     UnsampleSlow(info_);
   }
@@ -230,6 +197,7 @@
   explicit HashtablezInfoHandle() = default;
   explicit HashtablezInfoHandle(std::nullptr_t) {}
 
+  inline void Unregister() {}
   inline void RecordStorageChanged(size_t /*size*/, size_t /*capacity*/) {}
   inline void RecordRehash(size_t /*total_probe_length*/) {}
   inline void RecordReservation(size_t /*target_capacity*/) {}
diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h
index a56b757..f886dfa 100644
--- a/absl/container/internal/inlined_vector.h
+++ b/absl/container/internal/inlined_vector.h
@@ -77,11 +77,9 @@
     std::forward_iterator_tag>;
 
 template <typename A>
-using IsMemcpyOk =
-    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>>>;
+using IsMoveAssignOk = std::is_move_assignable<ValueType<A>>;
+template <typename A>
+using IsSwapOk = absl::type_traits_internal::IsSwappable<ValueType<A>>;
 
 template <typename T>
 struct TypeIdentity {
@@ -120,8 +118,8 @@
 
 template <typename A>
 struct Allocation {
-  Pointer<A> data;
-  SizeType<A> capacity;
+  Pointer<A> data = nullptr;
+  SizeType<A> capacity = 0;
 };
 
 template <typename A,
@@ -297,6 +295,45 @@
 template <typename T, size_t N, typename A>
 class Storage {
  public:
+  struct MemcpyPolicy {};
+  struct ElementwiseAssignPolicy {};
+  struct ElementwiseSwapPolicy {};
+  struct ElementwiseConstructPolicy {};
+
+  using MoveAssignmentPolicy = absl::conditional_t<
+      // Fast path: if the value type can be trivially move assigned and
+      // destroyed, and we know the allocator doesn't do anything fancy, then
+      // it's safe for us to simply adopt the contents of the storage for
+      // `other` and remove its own reference to them. It's as if we had
+      // individually move-assigned each value and then destroyed the original.
+      absl::conjunction<absl::is_trivially_move_assignable<ValueType<A>>,
+                        absl::is_trivially_destructible<ValueType<A>>,
+                        std::is_same<A, std::allocator<ValueType<A>>>>::value,
+      MemcpyPolicy,
+      // Otherwise we use move assignment if possible. If not, we simulate
+      // move assignment using move construction.
+      //
+      // Note that this is in contrast to e.g. std::vector and std::optional,
+      // which are themselves not move-assignable when their contained type is
+      // not.
+      absl::conditional_t<IsMoveAssignOk<A>::value, ElementwiseAssignPolicy,
+                          ElementwiseConstructPolicy>>;
+
+  // The policy to be used specifically when swapping inlined elements.
+  using SwapInlinedElementsPolicy = absl::conditional_t<
+      // Fast path: if the value type can be trivially move constructed/assigned
+      // and destroyed, and we know the allocator doesn't do anything fancy,
+      // then it's safe for us to simply swap the bytes in the inline storage.
+      // It's as if we had move-constructed a temporary vector, move-assigned
+      // one to the other, then move-assigned the first from the temporary.
+      absl::conjunction<absl::is_trivially_move_constructible<ValueType<A>>,
+                        absl::is_trivially_move_assignable<ValueType<A>>,
+                        absl::is_trivially_destructible<ValueType<A>>,
+                        std::is_same<A, std::allocator<ValueType<A>>>>::value,
+      MemcpyPolicy,
+      absl::conditional_t<IsSwapOk<A>::value, ElementwiseSwapPolicy,
+                          ElementwiseConstructPolicy>>;
+
   static SizeType<A> NextCapacity(SizeType<A> current_capacity) {
     return current_capacity * 2;
   }
@@ -316,14 +353,21 @@
       : metadata_(allocator, /* size and is_allocated */ 0u) {}
 
   ~Storage() {
+    // Fast path: if we are empty and not allocated, there's nothing to do.
     if (GetSizeAndIsAllocated() == 0) {
-      // Empty and not allocated; nothing to do.
-    } else if (IsMemcpyOk<A>::value) {
-      // No destructors need to be run; just deallocate if necessary.
-      DeallocateIfAllocated();
-    } else {
-      DestroyContents();
+      return;
     }
+
+    // Fast path: if no destructors need to be run and we know the allocator
+    // doesn't do anything fancy, then all we need to do is deallocate (and
+    // maybe not even that).
+    if (absl::is_trivially_destructible<ValueType<A>>::value &&
+        std::is_same<A, std::allocator<ValueType<A>>>::value) {
+      DeallocateIfAllocated();
+      return;
+    }
+
+    DestroyContents();
   }
 
   // ---------------------------------------------------------------------------
@@ -360,7 +404,9 @@
     return data_.allocated.allocated_capacity;
   }
 
-  SizeType<A> GetInlinedCapacity() const { return static_cast<SizeType<A>>(N); }
+  SizeType<A> GetInlinedCapacity() const {
+    return static_cast<SizeType<A>>(kOptimalInlinedSize);
+  }
 
   StorageView<A> MakeStorageView() {
     return GetIsAllocated() ? StorageView<A>{GetAllocatedData(), GetSize(),
@@ -440,8 +486,32 @@
   }
 
   void MemcpyFrom(const Storage& other_storage) {
-    ABSL_HARDENING_ASSERT(IsMemcpyOk<A>::value ||
-                          other_storage.GetIsAllocated());
+    // Assumption check: it doesn't make sense to memcpy inlined elements unless
+    // we know the allocator doesn't do anything fancy, and one of the following
+    // holds:
+    //
+    //  *  The elements are trivially relocatable.
+    //
+    //  *  It's possible to trivially assign the elements and then destroy the
+    //     source.
+    //
+    //  *  It's possible to trivially copy construct/assign the elements.
+    //
+    {
+      using V = ValueType<A>;
+      ABSL_HARDENING_ASSERT(
+          other_storage.GetIsAllocated() ||
+          (std::is_same<A, std::allocator<V>>::value &&
+           (
+               // First case above
+               absl::is_trivially_relocatable<V>::value ||
+               // Second case above
+               (absl::is_trivially_move_assignable<V>::value &&
+                absl::is_trivially_destructible<V>::value) ||
+               // Third case above
+               (absl::is_trivially_copy_constructible<V>::value ||
+                absl::is_trivially_copy_assignable<V>::value))));
+    }
 
     GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated();
     data_ = other_storage.data_;
@@ -464,8 +534,15 @@
     SizeType<A> allocated_capacity;
   };
 
+  // `kOptimalInlinedSize` is an automatically adjusted inlined capacity of the
+  // `InlinedVector`. Sometimes, it is possible to increase the capacity (from
+  // the user requested `N`) without increasing the size of the `InlinedVector`.
+  static constexpr size_t kOptimalInlinedSize =
+      (std::max)(N, sizeof(Allocated) / sizeof(ValueType<A>));
+
   struct Inlined {
-    alignas(ValueType<A>) char inlined_data[sizeof(ValueType<A>[N])];
+    alignas(ValueType<A>) char inlined_data[sizeof(
+        ValueType<A>[kOptimalInlinedSize])];
   };
 
   union Data {
@@ -473,6 +550,13 @@
     Inlined inlined;
   };
 
+  void SwapN(ElementwiseSwapPolicy, Storage* other, SizeType<A> n);
+  void SwapN(ElementwiseConstructPolicy, Storage* other, SizeType<A> n);
+
+  void SwapInlinedElements(MemcpyPolicy, Storage* other);
+  template <typename NotMemcpyPolicy>
+  void SwapInlinedElements(NotMemcpyPolicy, Storage* other);
+
   template <typename... Args>
   ABSL_ATTRIBUTE_NOINLINE Reference<A> EmplaceBackSlow(Args&&... args);
 
@@ -507,13 +591,19 @@
     dst = allocation.data;
     src = other.GetAllocatedData();
   }
-  if (IsMemcpyOk<A>::value) {
+
+  // Fast path: if the value type is trivially copy constructible and we know
+  // the allocator doesn't do anything fancy, then we know it is legal for us to
+  // simply memcpy the other vector's elements.
+  if (absl::is_trivially_copy_constructible<ValueType<A>>::value &&
+      std::is_same<A, std::allocator<ValueType<A>>>::value) {
     std::memcpy(reinterpret_cast<char*>(dst),
                 reinterpret_cast<const char*>(src), n * sizeof(ValueType<A>));
   } else {
     auto values = IteratorValueAdapter<A, ConstPointer<A>>(src);
     ConstructElements<A>(GetAllocator(), dst, values, n);
   }
+
   GetSizeAndIsAllocated() = other.GetSizeAndIsAllocated();
 }
 
@@ -886,26 +976,7 @@
   if (GetIsAllocated() && other_storage_ptr->GetIsAllocated()) {
     swap(data_.allocated, other_storage_ptr->data_.allocated);
   } else if (!GetIsAllocated() && !other_storage_ptr->GetIsAllocated()) {
-    Storage* small_ptr = this;
-    Storage* large_ptr = other_storage_ptr;
-    if (small_ptr->GetSize() > large_ptr->GetSize()) swap(small_ptr, large_ptr);
-
-    for (SizeType<A> i = 0; i < small_ptr->GetSize(); ++i) {
-      swap(small_ptr->GetInlinedData()[i], large_ptr->GetInlinedData()[i]);
-    }
-
-    IteratorValueAdapter<A, MoveIterator<A>> move_values(
-        MoveIterator<A>(large_ptr->GetInlinedData() + small_ptr->GetSize()));
-
-    ConstructElements<A>(large_ptr->GetAllocator(),
-                         small_ptr->GetInlinedData() + small_ptr->GetSize(),
-                         move_values,
-                         large_ptr->GetSize() - small_ptr->GetSize());
-
-    DestroyAdapter<A>::DestroyElements(
-        large_ptr->GetAllocator(),
-        large_ptr->GetInlinedData() + small_ptr->GetSize(),
-        large_ptr->GetSize() - small_ptr->GetSize());
+    SwapInlinedElements(SwapInlinedElementsPolicy{}, other_storage_ptr);
   } else {
     Storage* allocated_ptr = this;
     Storage* inlined_ptr = other_storage_ptr;
@@ -941,6 +1012,68 @@
   swap(GetAllocator(), other_storage_ptr->GetAllocator());
 }
 
+template <typename T, size_t N, typename A>
+void Storage<T, N, A>::SwapN(ElementwiseSwapPolicy, Storage* other,
+                             SizeType<A> n) {
+  std::swap_ranges(GetInlinedData(), GetInlinedData() + n,
+                   other->GetInlinedData());
+}
+
+template <typename T, size_t N, typename A>
+void Storage<T, N, A>::SwapN(ElementwiseConstructPolicy, Storage* other,
+                             SizeType<A> n) {
+  Pointer<A> a = GetInlinedData();
+  Pointer<A> b = other->GetInlinedData();
+  // see note on allocators in `SwapInlinedElements`.
+  A& allocator_a = GetAllocator();
+  A& allocator_b = other->GetAllocator();
+  for (SizeType<A> i = 0; i < n; ++i, ++a, ++b) {
+    ValueType<A> tmp(std::move(*a));
+
+    AllocatorTraits<A>::destroy(allocator_a, a);
+    AllocatorTraits<A>::construct(allocator_b, a, std::move(*b));
+
+    AllocatorTraits<A>::destroy(allocator_b, b);
+    AllocatorTraits<A>::construct(allocator_a, b, std::move(tmp));
+  }
+}
+
+template <typename T, size_t N, typename A>
+void Storage<T, N, A>::SwapInlinedElements(MemcpyPolicy, Storage* other) {
+  Data tmp = data_;
+  data_ = other->data_;
+  other->data_ = tmp;
+}
+
+template <typename T, size_t N, typename A>
+template <typename NotMemcpyPolicy>
+void Storage<T, N, A>::SwapInlinedElements(NotMemcpyPolicy policy,
+                                           Storage* other) {
+  // Note: `destroy` needs to use pre-swap allocator while `construct` -
+  // post-swap allocator. Allocators will be swapped later on outside of
+  // `SwapInlinedElements`.
+  Storage* small_ptr = this;
+  Storage* large_ptr = other;
+  if (small_ptr->GetSize() > large_ptr->GetSize()) {
+    std::swap(small_ptr, large_ptr);
+  }
+
+  auto small_size = small_ptr->GetSize();
+  auto diff = large_ptr->GetSize() - small_size;
+  SwapN(policy, other, small_size);
+
+  IteratorValueAdapter<A, MoveIterator<A>> move_values(
+      MoveIterator<A>(large_ptr->GetInlinedData() + small_size));
+
+  ConstructElements<A>(large_ptr->GetAllocator(),
+                       small_ptr->GetInlinedData() + small_size, move_values,
+                       diff);
+
+  DestroyAdapter<A>::DestroyElements(large_ptr->GetAllocator(),
+                                     large_ptr->GetInlinedData() + small_size,
+                                     diff);
+}
+
 // End ignore "array-bounds"
 #if !defined(__clang__) && defined(__GNUC__)
 #pragma GCC diagnostic pop
diff --git a/absl/container/internal/layout_benchmark.cc b/absl/container/internal/layout_benchmark.cc
index d8636e8..3af35e3 100644
--- a/absl/container/internal/layout_benchmark.cc
+++ b/absl/container/internal/layout_benchmark.cc
@@ -85,7 +85,7 @@
   size_t m = 5;
   size_t k = 7;
   ABSL_RAW_CHECK(L::Partial(n, m, k).template Offset<3>() == Offset,
-                 "Inavlid offset");
+                 "Invalid offset");
   for (auto _ : state) {
     DoNotOptimize(n);
     DoNotOptimize(m);
diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h
index c7df2ef..2d5a871 100644
--- a/absl/container/internal/raw_hash_map.h
+++ b/absl/container/internal/raw_hash_map.h
@@ -71,43 +71,51 @@
   //   m.insert_or_assign(n, n);
   template <class K = key_type, class V = mapped_type, K* = nullptr,
             V* = nullptr>
-  std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, V&& v) {
+  std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, V&& v)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign_impl(std::forward<K>(k), std::forward<V>(v));
   }
 
   template <class K = key_type, class V = mapped_type, K* = nullptr>
-  std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, const V& v) {
+  std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, const V& v)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign_impl(std::forward<K>(k), v);
   }
 
   template <class K = key_type, class V = mapped_type, V* = nullptr>
-  std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, V&& v) {
+  std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, V&& v)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign_impl(k, std::forward<V>(v));
   }
 
   template <class K = key_type, class V = mapped_type>
-  std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, const V& v) {
+  std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, const V& v)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign_impl(k, v);
   }
 
   template <class K = key_type, class V = mapped_type, K* = nullptr,
             V* = nullptr>
-  iterator insert_or_assign(const_iterator, key_arg<K>&& k, V&& v) {
+  iterator insert_or_assign(const_iterator, key_arg<K>&& k,
+                            V&& v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign(std::forward<K>(k), std::forward<V>(v)).first;
   }
 
   template <class K = key_type, class V = mapped_type, K* = nullptr>
-  iterator insert_or_assign(const_iterator, key_arg<K>&& k, const V& v) {
+  iterator insert_or_assign(const_iterator, key_arg<K>&& k,
+                            const V& v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign(std::forward<K>(k), v).first;
   }
 
   template <class K = key_type, class V = mapped_type, V* = nullptr>
-  iterator insert_or_assign(const_iterator, const key_arg<K>& k, V&& v) {
+  iterator insert_or_assign(const_iterator, const key_arg<K>& k,
+                            V&& v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign(k, std::forward<V>(v)).first;
   }
 
   template <class K = key_type, class V = mapped_type>
-  iterator insert_or_assign(const_iterator, const key_arg<K>& k, const V& v) {
+  iterator insert_or_assign(const_iterator, const key_arg<K>& k,
+                            const V& v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert_or_assign(k, v).first;
   }
 
@@ -118,29 +126,33 @@
             typename std::enable_if<
                 !std::is_convertible<K, const_iterator>::value, int>::type = 0,
             K* = nullptr>
-  std::pair<iterator, bool> try_emplace(key_arg<K>&& k, Args&&... args) {
+  std::pair<iterator, bool> try_emplace(key_arg<K>&& k, Args&&... args)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return try_emplace_impl(std::forward<K>(k), std::forward<Args>(args)...);
   }
 
   template <class K = key_type, class... Args,
             typename std::enable_if<
                 !std::is_convertible<K, const_iterator>::value, int>::type = 0>
-  std::pair<iterator, bool> try_emplace(const key_arg<K>& k, Args&&... args) {
+  std::pair<iterator, bool> try_emplace(const key_arg<K>& k, Args&&... args)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return try_emplace_impl(k, std::forward<Args>(args)...);
   }
 
   template <class K = key_type, class... Args, K* = nullptr>
-  iterator try_emplace(const_iterator, key_arg<K>&& k, Args&&... args) {
+  iterator try_emplace(const_iterator, key_arg<K>&& k,
+                       Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return try_emplace(std::forward<K>(k), std::forward<Args>(args)...).first;
   }
 
   template <class K = key_type, class... Args>
-  iterator try_emplace(const_iterator, const key_arg<K>& k, Args&&... args) {
+  iterator try_emplace(const_iterator, const key_arg<K>& k,
+                       Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return try_emplace(k, std::forward<Args>(args)...).first;
   }
 
   template <class K = key_type, class P = Policy>
-  MappedReference<P> at(const key_arg<K>& key) {
+  MappedReference<P> at(const key_arg<K>& key) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     auto it = this->find(key);
     if (it == this->end()) {
       base_internal::ThrowStdOutOfRange(
@@ -150,7 +162,8 @@
   }
 
   template <class K = key_type, class P = Policy>
-  MappedConstReference<P> at(const key_arg<K>& key) const {
+  MappedConstReference<P> at(const key_arg<K>& key) const
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     auto it = this->find(key);
     if (it == this->end()) {
       base_internal::ThrowStdOutOfRange(
@@ -160,18 +173,21 @@
   }
 
   template <class K = key_type, class P = Policy, K* = nullptr>
-  MappedReference<P> operator[](key_arg<K>&& key) {
+  MappedReference<P> operator[](key_arg<K>&& key)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return Policy::value(&*try_emplace(std::forward<K>(key)).first);
   }
 
   template <class K = key_type, class P = Policy>
-  MappedReference<P> operator[](const key_arg<K>& key) {
+  MappedReference<P> operator[](const key_arg<K>& key)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return Policy::value(&*try_emplace(key).first);
   }
 
  private:
   template <class K, class V>
-  std::pair<iterator, bool> insert_or_assign_impl(K&& k, V&& v) {
+  std::pair<iterator, bool> insert_or_assign_impl(K&& k, V&& v)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     auto res = this->find_or_prepare_insert(k);
     if (res.second)
       this->emplace_at(res.first, std::forward<K>(k), std::forward<V>(v));
@@ -181,7 +197,8 @@
   }
 
   template <class K = key_type, class... Args>
-  std::pair<iterator, bool> try_emplace_impl(K&& k, Args&&... args) {
+  std::pair<iterator, bool> try_emplace_impl(K&& k, Args&&... args)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     auto res = this->find_or_prepare_insert(k);
     if (res.second)
       this->emplace_at(res.first, std::piecewise_construct,
diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc
index c63a2e0..b91d5a4 100644
--- a/absl/container/internal/raw_hash_set.cc
+++ b/absl/container/internal/raw_hash_set.cc
@@ -16,8 +16,11 @@
 
 #include <atomic>
 #include <cstddef>
+#include <cstring>
 
 #include "absl/base/config.h"
+#include "absl/base/dynamic_annotations.h"
+#include "absl/hash/hash.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -35,10 +38,17 @@
 constexpr size_t Group::kWidth;
 #endif
 
+namespace {
+
 // Returns "random" seed.
 inline size_t RandomSeed() {
 #ifdef ABSL_HAVE_THREAD_LOCAL
   static thread_local size_t counter = 0;
+  // On Linux kernels >= 5.4 the MSAN runtime has a false-positive when
+  // accessing thread local storage data from loaded libraries
+  // (https://github.com/google/sanitizers/issues/1265), for this reason counter
+  // needs to be annotated as initialized.
+  ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(&counter, sizeof(size_t));
   size_t value = ++counter;
 #else   // ABSL_HAVE_THREAD_LOCAL
   static std::atomic<size_t> counter(0);
@@ -47,6 +57,32 @@
   return value ^ static_cast<size_t>(reinterpret_cast<uintptr_t>(&counter));
 }
 
+}  // namespace
+
+GenerationType* EmptyGeneration() {
+  if (SwisstableGenerationsEnabled()) {
+    constexpr size_t kNumEmptyGenerations = 1024;
+    static constexpr GenerationType kEmptyGenerations[kNumEmptyGenerations]{};
+    return const_cast<GenerationType*>(
+        &kEmptyGenerations[RandomSeed() % kNumEmptyGenerations]);
+  }
+  return nullptr;
+}
+
+bool CommonFieldsGenerationInfoEnabled::
+    should_rehash_for_bug_detection_on_insert(const ctrl_t* ctrl,
+                                              size_t capacity) const {
+  if (reserved_growth_ == kReservedGrowthJustRanOut) return true;
+  if (reserved_growth_ > 0) return false;
+  // Note: we can't use the abseil-random library because abseil-random
+  // depends on swisstable. We want to return true with probability
+  // `min(1, RehashProbabilityConstant() / capacity())`. In order to do this,
+  // we probe based on a random hash and see if the offset is less than
+  // RehashProbabilityConstant().
+  return probe(ctrl, capacity, absl::HashOf(RandomSeed())).offset() <
+         RehashProbabilityConstant();
+}
+
 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
@@ -63,8 +99,155 @@
   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);
+// Extern template instantiation for inline function.
+template FindInfo find_first_non_full(const CommonFields&, size_t);
+
+FindInfo find_first_non_full_outofline(const CommonFields& common,
+                                       size_t hash) {
+  return find_first_non_full(common, hash);
+}
+
+// Return address of the ith slot in slots where each slot occupies slot_size.
+static inline void* SlotAddress(void* slot_array, size_t slot,
+                                size_t slot_size) {
+  return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot_array) +
+                                 (slot * slot_size));
+}
+
+// Return the address of the slot just after slot assuming each slot
+// has the specified size.
+static inline void* NextSlot(void* slot, size_t slot_size) {
+  return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot) + slot_size);
+}
+
+// Return the address of the slot just before slot assuming each slot
+// has the specified size.
+static inline void* PrevSlot(void* slot, size_t slot_size) {
+  return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot) - slot_size);
+}
+
+void DropDeletesWithoutResize(CommonFields& common,
+                              const PolicyFunctions& policy, void* tmp_space) {
+  void* set = &common;
+  void* slot_array = common.slots_;
+  const size_t capacity = common.capacity_;
+  assert(IsValidCapacity(capacity));
+  assert(!is_small(capacity));
+  // Algorithm:
+  // - mark all DELETED slots as EMPTY
+  // - mark all FULL slots as DELETED
+  // - for each slot marked as DELETED
+  //     hash = Hash(element)
+  //     target = find_first_non_full(hash)
+  //     if target is in the same group
+  //       mark slot as FULL
+  //     else if target is EMPTY
+  //       transfer element to target
+  //       mark slot as EMPTY
+  //       mark target as FULL
+  //     else if target is DELETED
+  //       swap current element with target element
+  //       mark target as FULL
+  //       repeat procedure for current slot with moved from element (target)
+  ctrl_t* ctrl = common.control_;
+  ConvertDeletedToEmptyAndFullToDeleted(ctrl, capacity);
+  auto hasher = policy.hash_slot;
+  auto transfer = policy.transfer;
+  const size_t slot_size = policy.slot_size;
+
+  size_t total_probe_length = 0;
+  void* slot_ptr = SlotAddress(slot_array, 0, slot_size);
+  for (size_t i = 0; i != capacity;
+       ++i, slot_ptr = NextSlot(slot_ptr, slot_size)) {
+    assert(slot_ptr == SlotAddress(slot_array, i, slot_size));
+    if (!IsDeleted(ctrl[i])) continue;
+    const size_t hash = (*hasher)(set, slot_ptr);
+    const FindInfo target = find_first_non_full(common, hash);
+    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 size_t probe_offset = probe(common, hash).offset();
+    const auto probe_index = [probe_offset, capacity](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))) {
+      SetCtrl(common, i, H2(hash), slot_size);
+      continue;
+    }
+
+    void* new_slot_ptr = SlotAddress(slot_array, new_i, slot_size);
+    if (IsEmpty(ctrl[new_i])) {
+      // Transfer element to the empty spot.
+      // SetCtrl poisons/unpoisons the slots so we have to call it at the
+      // right time.
+      SetCtrl(common, new_i, H2(hash), slot_size);
+      (*transfer)(set, new_slot_ptr, slot_ptr);
+      SetCtrl(common, i, ctrl_t::kEmpty, slot_size);
+    } else {
+      assert(IsDeleted(ctrl[new_i]));
+      SetCtrl(common, new_i, H2(hash), slot_size);
+      // Until we are done rehashing, DELETED marks previously FULL slots.
+
+      // Swap i and new_i elements.
+      (*transfer)(set, tmp_space, new_slot_ptr);
+      (*transfer)(set, new_slot_ptr, slot_ptr);
+      (*transfer)(set, slot_ptr, tmp_space);
+
+      // repeat the processing of the ith slot
+      --i;
+      slot_ptr = PrevSlot(slot_ptr, slot_size);
+    }
+  }
+  ResetGrowthLeft(common);
+  common.infoz().RecordRehash(total_probe_length);
+}
+
+void EraseMetaOnly(CommonFields& c, ctrl_t* it, size_t slot_size) {
+  assert(IsFull(*it) && "erasing a dangling iterator");
+  --c.size_;
+  const auto index = static_cast<size_t>(it - c.control_);
+  const size_t index_before = (index - Group::kWidth) & c.capacity_;
+  const auto empty_after = Group(it).MaskEmpty();
+  const auto empty_before = Group(c.control_ + index_before).MaskEmpty();
+
+  // We count how many consecutive non empties we have to the right and to the
+  // left of `it`. If the sum is >= kWidth then there is at least one probe
+  // window that might have seen a full group.
+  bool was_never_full = empty_before && empty_after &&
+                        static_cast<size_t>(empty_after.TrailingZeros()) +
+                                empty_before.LeadingZeros() <
+                            Group::kWidth;
+
+  SetCtrl(c, index, was_never_full ? ctrl_t::kEmpty : ctrl_t::kDeleted,
+          slot_size);
+  c.growth_left() += (was_never_full ? 1 : 0);
+  c.infoz().RecordErase();
+}
+
+void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy,
+                       bool reuse) {
+  c.size_ = 0;
+  if (reuse) {
+    ResetCtrl(c, policy.slot_size);
+    c.infoz().RecordStorageChanged(0, c.capacity_);
+  } else {
+    void* set = &c;
+    (*policy.dealloc)(set, policy, c.control_, c.slots_, c.capacity_);
+    c.control_ = EmptyGroup();
+    c.set_generation_ptr(EmptyGeneration());
+    c.slots_ = nullptr;
+    c.capacity_ = 0;
+    c.growth_left() = 0;
+    c.infoz().RecordClearedReservation();
+    assert(c.size_ == 0);
+    c.infoz().RecordStorageChanged(0, 0);
+  }
+}
 
 }  // 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 93de222..df7ff79 100644
--- a/absl/container/internal/raw_hash_set.h
+++ b/absl/container/internal/raw_hash_set.h
@@ -115,7 +115,7 @@
 // starting with that index and extract potential candidates: occupied slots
 // with a control byte equal to `H2(hash(x))`. If we find an empty slot in the
 // group, we stop and return an error. Each candidate slot `y` is compared with
-// `x`; if `x == y`, we are done and return `&y`; otherwise we contine to the
+// `x`; if `x == y`, we are done and return `&y`; otherwise we continue to the
 // next probe index. Tombstones effectively behave like full slots that never
 // match the value we're looking for.
 //
@@ -179,15 +179,17 @@
 #include <iterator>
 #include <limits>
 #include <memory>
+#include <string>
 #include <tuple>
 #include <type_traits>
 #include <utility>
 
 #include "absl/base/config.h"
 #include "absl/base/internal/endian.h"
-#include "absl/base/internal/prefetch.h"
+#include "absl/base/internal/raw_logging.h"
 #include "absl/base/optimization.h"
 #include "absl/base/port.h"
+#include "absl/base/prefetch.h"
 #include "absl/container/internal/common.h"
 #include "absl/container/internal/compressed_tuple.h"
 #include "absl/container/internal/container_memory.h"
@@ -219,6 +221,37 @@
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
 
+#ifdef ABSL_SWISSTABLE_ENABLE_GENERATIONS
+#error ABSL_SWISSTABLE_ENABLE_GENERATIONS cannot be directly set
+#elif defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+    defined(ABSL_HAVE_MEMORY_SANITIZER)
+// When compiled in sanitizer mode, we add generation integers to the backing
+// array and iterators. In the backing array, we store the generation between
+// the control bytes and the slots. When iterators are dereferenced, we assert
+// that the container has not been mutated in a way that could cause iterator
+// invalidation since the iterator was initialized.
+#define ABSL_SWISSTABLE_ENABLE_GENERATIONS
+#endif
+
+// We use uint8_t so we don't need to worry about padding.
+using GenerationType = uint8_t;
+
+// A sentinel value for empty generations. Using 0 makes it easy to constexpr
+// initialize an array of this value.
+constexpr GenerationType SentinelEmptyGeneration() { return 0; }
+
+constexpr GenerationType NextGeneration(GenerationType generation) {
+  return ++generation == SentinelEmptyGeneration() ? ++generation : generation;
+}
+
+#ifdef ABSL_SWISSTABLE_ENABLE_GENERATIONS
+constexpr bool SwisstableGenerationsEnabled() { return true; }
+constexpr size_t NumGenerationBytes() { return sizeof(GenerationType); }
+#else
+constexpr bool SwisstableGenerationsEnabled() { return false; }
+constexpr size_t NumGenerationBytes() { return 0; }
+#endif
+
 template <typename AllocType>
 void SwapAlloc(AllocType& lhs, AllocType& rhs,
                std::true_type /* propagate_on_container_swap */) {
@@ -460,6 +493,15 @@
   return const_cast<ctrl_t*>(kEmptyGroup);
 }
 
+// Returns a pointer to a generation to use for an empty hashtable.
+GenerationType* EmptyGeneration();
+
+// Returns whether `generation` is a generation for an empty hashtable that
+// could be returned by EmptyGeneration().
+inline bool IsEmptyGeneration(const GenerationType* generation) {
+  return *generation == SentinelEmptyGeneration();
+}
+
 // Mixes a randomly generated per-process seed with `hash` and `ctrl` to
 // randomize insertion order within groups.
 bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl);
@@ -629,22 +671,25 @@
   }
 
   uint32_t CountLeadingEmptyOrDeleted() const {
-    uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(ctrl), 0);
-    // ctrl | ~(ctrl >> 7) will have the lowest bit set to zero for kEmpty and
-    // kDeleted. We lower all other bits and count number of trailing zeros.
+    uint64_t mask =
+        vget_lane_u64(vreinterpret_u64_u8(vcle_s8(
+                          vdup_n_s8(static_cast<int8_t>(ctrl_t::kSentinel)),
+                          vreinterpret_s8_u8(ctrl))),
+                      0);
+    // Similar to MaskEmptyorDeleted() but we invert the logic to invert the
+    // produced bitfield. We then count number of trailing zeros.
     // Clang and GCC optimize countr_zero to rbit+clz without any check for 0,
     // so we should be fine.
-    constexpr uint64_t bits = 0x0101010101010101ULL;
-    return static_cast<uint32_t>(countr_zero((mask | ~(mask >> 7)) & bits) >>
-                                 3);
+    return static_cast<uint32_t>(countr_zero(mask)) >> 3;
   }
 
   void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const {
     uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(ctrl), 0);
     constexpr uint64_t msbs = 0x8080808080808080ULL;
-    constexpr uint64_t lsbs = 0x0101010101010101ULL;
-    auto x = mask & msbs;
-    auto res = (~x + (x >> 7)) & ~lsbs;
+    constexpr uint64_t slsbs = 0x0202020202020202ULL;
+    constexpr uint64_t midbs = 0x7e7e7e7e7e7e7e7eULL;
+    auto x = slsbs & (mask >> 6);
+    auto res = (x + midbs) | msbs;
     little_endian::Store64(dst, res);
   }
 
@@ -717,6 +762,205 @@
 using Group = GroupPortableImpl;
 #endif
 
+// When there is an insertion with no reserved growth, we rehash with
+// probability `min(1, RehashProbabilityConstant() / capacity())`. Using a
+// constant divided by capacity ensures that inserting N elements is still O(N)
+// in the average case. Using the constant 16 means that we expect to rehash ~8
+// times more often than when generations are disabled. We are adding expected
+// rehash_probability * #insertions/capacity_growth = 16/capacity * ((7/8 -
+// 7/16) * capacity)/capacity_growth = ~7 extra rehashes per capacity growth.
+inline size_t RehashProbabilityConstant() { return 16; }
+
+class CommonFieldsGenerationInfoEnabled {
+  // A sentinel value for reserved_growth_ indicating that we just ran out of
+  // reserved growth on the last insertion. When reserve is called and then
+  // insertions take place, reserved_growth_'s state machine is N, ..., 1,
+  // kReservedGrowthJustRanOut, 0.
+  static constexpr size_t kReservedGrowthJustRanOut =
+      (std::numeric_limits<size_t>::max)();
+
+ public:
+  CommonFieldsGenerationInfoEnabled() = default;
+  CommonFieldsGenerationInfoEnabled(CommonFieldsGenerationInfoEnabled&& that)
+      : reserved_growth_(that.reserved_growth_), generation_(that.generation_) {
+    that.reserved_growth_ = 0;
+    that.generation_ = EmptyGeneration();
+  }
+  CommonFieldsGenerationInfoEnabled& operator=(
+      CommonFieldsGenerationInfoEnabled&&) = default;
+
+  // Whether we should rehash on insert in order to detect bugs of using invalid
+  // references. We rehash on the first insertion after reserved_growth_ reaches
+  // 0 after a call to reserve. We also do a rehash with low probability
+  // whenever reserved_growth_ is zero.
+  bool should_rehash_for_bug_detection_on_insert(const ctrl_t* ctrl,
+                                                 size_t capacity) const;
+  void maybe_increment_generation_on_insert() {
+    if (reserved_growth_ == kReservedGrowthJustRanOut) reserved_growth_ = 0;
+
+    if (reserved_growth_ > 0) {
+      if (--reserved_growth_ == 0) reserved_growth_ = kReservedGrowthJustRanOut;
+    } else {
+      *generation_ = NextGeneration(*generation_);
+    }
+  }
+  void reset_reserved_growth(size_t reservation, size_t size) {
+    reserved_growth_ = reservation - size;
+  }
+  size_t reserved_growth() const { return reserved_growth_; }
+  void set_reserved_growth(size_t r) { reserved_growth_ = r; }
+  GenerationType generation() const { return *generation_; }
+  void set_generation(GenerationType g) { *generation_ = g; }
+  GenerationType* generation_ptr() const { return generation_; }
+  void set_generation_ptr(GenerationType* g) { generation_ = g; }
+
+ private:
+  // The number of insertions remaining that are guaranteed to not rehash due to
+  // a prior call to reserve. Note: we store reserved growth rather than
+  // reservation size because calls to erase() decrease size_ but don't decrease
+  // reserved growth.
+  size_t reserved_growth_ = 0;
+  // Pointer to the generation counter, which is used to validate iterators and
+  // is stored in the backing array between the control bytes and the slots.
+  // Note that we can't store the generation inside the container itself and
+  // keep a pointer to the container in the iterators because iterators must
+  // remain valid when the container is moved.
+  // Note: we could derive this pointer from the control pointer, but it makes
+  // the code more complicated, and there's a benefit in having the sizes of
+  // raw_hash_set in sanitizer mode and non-sanitizer mode a bit more different,
+  // which is that tests are less likely to rely on the size remaining the same.
+  GenerationType* generation_ = EmptyGeneration();
+};
+
+class CommonFieldsGenerationInfoDisabled {
+ public:
+  CommonFieldsGenerationInfoDisabled() = default;
+  CommonFieldsGenerationInfoDisabled(CommonFieldsGenerationInfoDisabled&&) =
+      default;
+  CommonFieldsGenerationInfoDisabled& operator=(
+      CommonFieldsGenerationInfoDisabled&&) = default;
+
+  bool should_rehash_for_bug_detection_on_insert(const ctrl_t*, size_t) const {
+    return false;
+  }
+  void maybe_increment_generation_on_insert() {}
+  void reset_reserved_growth(size_t, size_t) {}
+  size_t reserved_growth() const { return 0; }
+  void set_reserved_growth(size_t) {}
+  GenerationType generation() const { return 0; }
+  void set_generation(GenerationType) {}
+  GenerationType* generation_ptr() const { return nullptr; }
+  void set_generation_ptr(GenerationType*) {}
+};
+
+class HashSetIteratorGenerationInfoEnabled {
+ public:
+  HashSetIteratorGenerationInfoEnabled() = default;
+  explicit HashSetIteratorGenerationInfoEnabled(
+      const GenerationType* generation_ptr)
+      : generation_ptr_(generation_ptr), generation_(*generation_ptr) {}
+
+  GenerationType generation() const { return generation_; }
+  void reset_generation() { generation_ = *generation_ptr_; }
+  const GenerationType* generation_ptr() const { return generation_ptr_; }
+  void set_generation_ptr(const GenerationType* ptr) { generation_ptr_ = ptr; }
+
+ private:
+  const GenerationType* generation_ptr_ = EmptyGeneration();
+  GenerationType generation_ = *generation_ptr_;
+};
+
+class HashSetIteratorGenerationInfoDisabled {
+ public:
+  HashSetIteratorGenerationInfoDisabled() = default;
+  explicit HashSetIteratorGenerationInfoDisabled(const GenerationType*) {}
+
+  GenerationType generation() const { return 0; }
+  void reset_generation() {}
+  const GenerationType* generation_ptr() const { return nullptr; }
+  void set_generation_ptr(const GenerationType*) {}
+};
+
+#ifdef ABSL_SWISSTABLE_ENABLE_GENERATIONS
+using CommonFieldsGenerationInfo = CommonFieldsGenerationInfoEnabled;
+using HashSetIteratorGenerationInfo = HashSetIteratorGenerationInfoEnabled;
+#else
+using CommonFieldsGenerationInfo = CommonFieldsGenerationInfoDisabled;
+using HashSetIteratorGenerationInfo = HashSetIteratorGenerationInfoDisabled;
+#endif
+
+// CommonFields hold the fields in raw_hash_set that do not depend
+// on template parameters. This allows us to conveniently pass all
+// of this state to helper functions as a single argument.
+class CommonFields : public CommonFieldsGenerationInfo {
+ public:
+  CommonFields() = default;
+
+  // Not copyable
+  CommonFields(const CommonFields&) = delete;
+  CommonFields& operator=(const CommonFields&) = delete;
+
+  // Movable
+  CommonFields(CommonFields&& that)
+      : CommonFieldsGenerationInfo(
+            std::move(static_cast<CommonFieldsGenerationInfo&&>(that))),
+        // Explicitly copying fields into "this" and then resetting "that"
+        // fields generates less code then calling absl::exchange per field.
+        control_(that.control_),
+        slots_(that.slots_),
+        size_(that.size_),
+        capacity_(that.capacity_),
+        compressed_tuple_(that.growth_left(), std::move(that.infoz())) {
+    that.control_ = EmptyGroup();
+    that.slots_ = nullptr;
+    that.size_ = 0;
+    that.capacity_ = 0;
+    that.growth_left() = 0;
+  }
+  CommonFields& operator=(CommonFields&&) = default;
+
+  // The number of slots we can still fill without needing to rehash.
+  size_t& growth_left() { return compressed_tuple_.template get<0>(); }
+
+  HashtablezInfoHandle& infoz() { return compressed_tuple_.template get<1>(); }
+  const HashtablezInfoHandle& infoz() const {
+    return compressed_tuple_.template get<1>();
+  }
+
+  bool should_rehash_for_bug_detection_on_insert() const {
+    return CommonFieldsGenerationInfo::
+        should_rehash_for_bug_detection_on_insert(control_, capacity_);
+  }
+  void reset_reserved_growth(size_t reservation) {
+    CommonFieldsGenerationInfo::reset_reserved_growth(reservation, size_);
+  }
+
+  // TODO(b/259599413): Investigate removing some of these fields:
+  // - control/slots can be derived from each other
+  // - size can be moved into the slot array
+
+  // The control bytes (and, also, a pointer to the base of the backing array).
+  //
+  // This contains `capacity + 1 + NumClonedBytes()` entries, even
+  // when the table is empty (hence EmptyGroup).
+  ctrl_t* control_ = EmptyGroup();
+
+  // The beginning of the slots, located at `SlotOffset()` bytes after
+  // `control`. May be null for empty tables.
+  void* slots_ = nullptr;
+
+  // The number of filled slots.
+  size_t size_ = 0;
+
+  // The total number of available slots.
+  size_t capacity_ = 0;
+
+  // Bundle together growth_left and HashtablezInfoHandle to ensure EBO for
+  // HashtablezInfoHandle when sampling is turned off.
+  absl::container_internal::CompressedTuple<size_t, HashtablezInfoHandle>
+      compressed_tuple_{0u, HashtablezInfoHandle{}};
+};
+
 // Returns he number of "cloned control bytes".
 //
 // This is the number of control bytes that are present both at the beginning
@@ -732,6 +976,12 @@
 // A valid capacity is a non-zero integer `2^m - 1`.
 inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; }
 
+// Returns the next valid capacity after `n`.
+inline size_t NextCapacity(size_t n) {
+  assert(IsValidCapacity(n) || n == 0);
+  return n * 2 + 1;
+}
+
 // Applies the following mapping to every byte in the control array:
 //   * kDeleted -> kEmpty
 //   * kEmpty -> kEmpty
@@ -797,15 +1047,148 @@
   return 0;
 }
 
-#define ABSL_INTERNAL_ASSERT_IS_FULL(ctrl, msg) \
-  ABSL_HARDENING_ASSERT((ctrl != nullptr && IsFull(*ctrl)) && msg)
+constexpr bool SwisstableDebugEnabled() {
+#if defined(ABSL_SWISSTABLE_ENABLE_GENERATIONS) || \
+    ABSL_OPTION_HARDENED == 1 || !defined(NDEBUG)
+  return true;
+#else
+  return false;
+#endif
+}
 
-inline void AssertIsValid(ctrl_t* ctrl) {
-  ABSL_HARDENING_ASSERT(
-      (ctrl == nullptr || IsFull(*ctrl)) &&
-      "Invalid operation on iterator. The element might have "
-      "been erased, the table might have rehashed, or this may "
-      "be an end() iterator.");
+inline void AssertIsFull(const ctrl_t* ctrl, GenerationType generation,
+                         const GenerationType* generation_ptr,
+                         const char* operation) {
+  if (!SwisstableDebugEnabled()) return;
+  if (ctrl == nullptr) {
+    ABSL_INTERNAL_LOG(FATAL,
+                      std::string(operation) + " called on end() iterator.");
+  }
+  if (ctrl == EmptyGroup()) {
+    ABSL_INTERNAL_LOG(FATAL, std::string(operation) +
+                                 " called on default-constructed iterator.");
+  }
+  if (SwisstableGenerationsEnabled()) {
+    if (generation != *generation_ptr) {
+      ABSL_INTERNAL_LOG(FATAL,
+                        std::string(operation) +
+                            " called on invalid iterator. The table could have "
+                            "rehashed since this iterator was initialized.");
+    }
+    if (!IsFull(*ctrl)) {
+      ABSL_INTERNAL_LOG(
+          FATAL,
+          std::string(operation) +
+              " called on invalid iterator. The element was likely erased.");
+    }
+  } else {
+    if (!IsFull(*ctrl)) {
+      ABSL_INTERNAL_LOG(
+          FATAL,
+          std::string(operation) +
+              " called on invalid iterator. The element might have been erased "
+              "or the table might have rehashed. Consider running with "
+              "--config=asan to diagnose rehashing issues.");
+    }
+  }
+}
+
+// Note that for comparisons, null/end iterators are valid.
+inline void AssertIsValidForComparison(const ctrl_t* ctrl,
+                                       GenerationType generation,
+                                       const GenerationType* generation_ptr) {
+  if (!SwisstableDebugEnabled()) return;
+  const bool ctrl_is_valid_for_comparison =
+      ctrl == nullptr || ctrl == EmptyGroup() || IsFull(*ctrl);
+  if (SwisstableGenerationsEnabled()) {
+    if (generation != *generation_ptr) {
+      ABSL_INTERNAL_LOG(FATAL,
+                        "Invalid iterator comparison. The table could have "
+                        "rehashed since this iterator was initialized.");
+    }
+    if (!ctrl_is_valid_for_comparison) {
+      ABSL_INTERNAL_LOG(
+          FATAL, "Invalid iterator comparison. The element was likely erased.");
+    }
+  } else {
+    ABSL_HARDENING_ASSERT(
+        ctrl_is_valid_for_comparison &&
+        "Invalid iterator comparison. The element might have been erased or "
+        "the table might have rehashed. Consider running with --config=asan to "
+        "diagnose rehashing issues.");
+  }
+}
+
+// If the two iterators come from the same container, then their pointers will
+// interleave such that ctrl_a <= ctrl_b < slot_a <= slot_b or vice/versa.
+// Note: we take slots by reference so that it's not UB if they're uninitialized
+// as long as we don't read them (when ctrl is null).
+inline bool AreItersFromSameContainer(const ctrl_t* ctrl_a,
+                                      const ctrl_t* ctrl_b,
+                                      const void* const& slot_a,
+                                      const void* const& slot_b) {
+  // If either control byte is null, then we can't tell.
+  if (ctrl_a == nullptr || ctrl_b == nullptr) return true;
+  const void* low_slot = slot_a;
+  const void* hi_slot = slot_b;
+  if (ctrl_a > ctrl_b) {
+    std::swap(ctrl_a, ctrl_b);
+    std::swap(low_slot, hi_slot);
+  }
+  return ctrl_b < low_slot && low_slot <= hi_slot;
+}
+
+// Asserts that two iterators come from the same container.
+// Note: we take slots by reference so that it's not UB if they're uninitialized
+// as long as we don't read them (when ctrl is null).
+inline void AssertSameContainer(const ctrl_t* ctrl_a, const ctrl_t* ctrl_b,
+                                const void* const& slot_a,
+                                const void* const& slot_b,
+                                const GenerationType* generation_ptr_a,
+                                const GenerationType* generation_ptr_b) {
+  if (!SwisstableDebugEnabled()) return;
+  const bool a_is_default = ctrl_a == EmptyGroup();
+  const bool b_is_default = ctrl_b == EmptyGroup();
+  if (a_is_default != b_is_default) {
+    ABSL_INTERNAL_LOG(
+        FATAL,
+        "Invalid iterator comparison. Comparing default-constructed iterator "
+        "with non-default-constructed iterator.");
+  }
+  if (a_is_default && b_is_default) return;
+
+  if (SwisstableGenerationsEnabled()) {
+    if (generation_ptr_a == generation_ptr_b) return;
+    const bool a_is_empty = IsEmptyGeneration(generation_ptr_a);
+    const bool b_is_empty = IsEmptyGeneration(generation_ptr_b);
+    if (a_is_empty != b_is_empty) {
+      ABSL_INTERNAL_LOG(FATAL,
+                        "Invalid iterator comparison. Comparing iterator from "
+                        "a non-empty hashtable with an iterator from an empty "
+                        "hashtable.");
+    }
+    if (a_is_empty && b_is_empty) {
+      ABSL_INTERNAL_LOG(FATAL,
+                        "Invalid iterator comparison. Comparing iterators from "
+                        "different empty hashtables.");
+    }
+    const bool a_is_end = ctrl_a == nullptr;
+    const bool b_is_end = ctrl_b == nullptr;
+    if (a_is_end || b_is_end) {
+      ABSL_INTERNAL_LOG(FATAL,
+                        "Invalid iterator comparison. Comparing iterator with "
+                        "an end() iterator from a different hashtable.");
+    }
+    ABSL_INTERNAL_LOG(FATAL,
+                      "Invalid iterator comparison. Comparing non-end() "
+                      "iterators from different hashtables.");
+  } else {
+    ABSL_HARDENING_ASSERT(
+        AreItersFromSameContainer(ctrl_a, ctrl_b, slot_a, slot_b) &&
+        "Invalid iterator comparison. The iterators may be from different "
+        "containers or the container might have rehashed. Consider running "
+        "with --config=asan to diagnose rehashing issues.");
+  }
 }
 
 struct FindInfo {
@@ -827,11 +1210,14 @@
 // `ShouldInsertBackwards()` for small tables.
 inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; }
 
-// Begins a probing operation on `ctrl`, using `hash`.
-inline probe_seq<Group::kWidth> probe(const ctrl_t* ctrl, size_t hash,
-                                      size_t capacity) {
+// Begins a probing operation on `common.control`, using `hash`.
+inline probe_seq<Group::kWidth> probe(const ctrl_t* ctrl, const size_t capacity,
+                                      size_t hash) {
   return probe_seq<Group::kWidth>(H1(hash, ctrl), capacity);
 }
+inline probe_seq<Group::kWidth> probe(const CommonFields& common, size_t hash) {
+  return probe(common.control_, common.capacity_, hash);
+}
 
 // Probes an array of control bits using a probe sequence derived from `hash`,
 // and returns the offset corresponding to the first deleted or empty slot.
@@ -841,9 +1227,9 @@
 // NOTE: this function must work with tables having both empty and deleted
 // slots in the same group. Such tables appear during `erase()`.
 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);
+inline FindInfo find_first_non_full(const CommonFields& common, size_t hash) {
+  auto seq = probe(common, hash);
+  const ctrl_t* ctrl = common.control_;
   while (true) {
     Group g{ctrl + seq.offset()};
     auto mask = g.MaskEmptyOrDeleted();
@@ -853,55 +1239,75 @@
       // In debug build we will randomly insert in either the front or back of
       // the group.
       // TODO(kfm,sbenza): revisit after we do unconditional mixing
-      if (!is_small(capacity) && ShouldInsertBackwards(hash, ctrl)) {
+      if (!is_small(common.capacity_) && ShouldInsertBackwards(hash, ctrl)) {
         return {seq.offset(mask.HighestBitSet()), seq.index()};
       }
 #endif
       return {seq.offset(mask.LowestBitSet()), seq.index()};
     }
     seq.next();
-    assert(seq.index() <= capacity && "full table!");
+    assert(seq.index() <= common.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);
+extern template FindInfo find_first_non_full(const CommonFields&, size_t);
+
+// Non-inlined version of find_first_non_full for use in less
+// performance critical routines.
+FindInfo find_first_non_full_outofline(const CommonFields&, size_t);
+
+inline void ResetGrowthLeft(CommonFields& common) {
+  common.growth_left() = CapacityToGrowth(common.capacity_) - common.size_;
+}
 
 // Sets `ctrl` to `{kEmpty, kSentinel, ..., kEmpty}`, marking the entire
 // array as marked as empty.
-inline void ResetCtrl(size_t capacity, ctrl_t* ctrl, const void* slot,
-                      size_t slot_size) {
+inline void ResetCtrl(CommonFields& common, size_t slot_size) {
+  const size_t capacity = common.capacity_;
+  ctrl_t* ctrl = common.control_;
   std::memset(ctrl, static_cast<int8_t>(ctrl_t::kEmpty),
               capacity + 1 + NumClonedBytes());
   ctrl[capacity] = ctrl_t::kSentinel;
-  SanitizerPoisonMemoryRegion(slot, slot_size * capacity);
+  SanitizerPoisonMemoryRegion(common.slots_, slot_size * capacity);
+  ResetGrowthLeft(common);
 }
 
 // Sets `ctrl[i]` to `h`.
 //
 // Unlike setting it directly, this function will perform bounds checks and
 // mirror the value to the cloned tail if necessary.
-inline void SetCtrl(size_t i, ctrl_t h, size_t capacity, ctrl_t* ctrl,
-                    const void* slot, size_t slot_size) {
+inline void SetCtrl(const CommonFields& common, size_t i, ctrl_t h,
+                    size_t slot_size) {
+  const size_t capacity = common.capacity_;
   assert(i < capacity);
 
-  auto* slot_i = static_cast<const char*>(slot) + i * slot_size;
+  auto* slot_i = static_cast<const char*>(common.slots_) + i * slot_size;
   if (IsFull(h)) {
     SanitizerUnpoisonMemoryRegion(slot_i, slot_size);
   } else {
     SanitizerPoisonMemoryRegion(slot_i, slot_size);
   }
 
+  ctrl_t* ctrl = common.control_;
   ctrl[i] = h;
   ctrl[((i - NumClonedBytes()) & capacity) + (NumClonedBytes() & capacity)] = h;
 }
 
 // Overload for setting to an occupied `h2_t` rather than a special `ctrl_t`.
-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);
+inline void SetCtrl(const CommonFields& common, size_t i, h2_t h,
+                    size_t slot_size) {
+  SetCtrl(common, i, static_cast<ctrl_t>(h), slot_size);
+}
+
+// Given the capacity of a table, computes the offset (from the start of the
+// backing allocation) of the generation counter (if it exists).
+inline size_t GenerationOffset(size_t capacity) {
+  assert(IsValidCapacity(capacity));
+  const size_t num_control_bytes = capacity + 1 + NumClonedBytes();
+  return num_control_bytes;
 }
 
 // Given the capacity of a table, computes the offset (from the start of the
@@ -909,7 +1315,8 @@
 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);
+  return (num_control_bytes + NumGenerationBytes() + slot_align - 1) &
+         (~slot_align + 1);
 }
 
 // Given the capacity of a table, computes the total size of the backing
@@ -918,6 +1325,91 @@
   return SlotOffset(capacity, slot_align) + capacity * slot_size;
 }
 
+template <typename Alloc, size_t SizeOfSlot, size_t AlignOfSlot>
+ABSL_ATTRIBUTE_NOINLINE void InitializeSlots(CommonFields& c, Alloc alloc) {
+  assert(c.capacity_);
+  // Folks with custom allocators often make unwarranted assumptions about the
+  // behavior of their classes vis-a-vis trivial destructability and what
+  // calls they will or won't make.  Avoid sampling for people with custom
+  // allocators to get us out of this mess.  This is not a hard guarantee but
+  // a workaround while we plan the exact guarantee we want to provide.
+  const size_t sample_size =
+      (std::is_same<Alloc, std::allocator<char>>::value && c.slots_ == nullptr)
+          ? SizeOfSlot
+          : 0;
+
+  const size_t cap = c.capacity_;
+  char* mem = static_cast<char*>(
+      Allocate<AlignOfSlot>(&alloc, AllocSize(cap, SizeOfSlot, AlignOfSlot)));
+  const GenerationType old_generation = c.generation();
+  c.set_generation_ptr(
+      reinterpret_cast<GenerationType*>(mem + GenerationOffset(cap)));
+  c.set_generation(NextGeneration(old_generation));
+  c.control_ = reinterpret_cast<ctrl_t*>(mem);
+  c.slots_ = mem + SlotOffset(cap, AlignOfSlot);
+  ResetCtrl(c, SizeOfSlot);
+  if (sample_size) {
+    c.infoz() = Sample(sample_size);
+  }
+  c.infoz().RecordStorageChanged(c.size_, cap);
+}
+
+// PolicyFunctions bundles together some information for a particular
+// raw_hash_set<T, ...> instantiation. This information is passed to
+// type-erased functions that want to do small amounts of type-specific
+// work.
+struct PolicyFunctions {
+  size_t slot_size;
+
+  // Return the hash of the pointed-to slot.
+  size_t (*hash_slot)(void* set, void* slot);
+
+  // Transfer the contents of src_slot to dst_slot.
+  void (*transfer)(void* set, void* dst_slot, void* src_slot);
+
+  // Deallocate the specified backing store which is sized for n slots.
+  void (*dealloc)(void* set, const PolicyFunctions& policy, ctrl_t* ctrl,
+                  void* slot_array, size_t n);
+};
+
+// ClearBackingArray clears the backing array, either modifying it in place,
+// or creating a new one based on the value of "reuse".
+// REQUIRES: c.capacity > 0
+void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy,
+                       bool reuse);
+
+// Type-erased version of raw_hash_set::erase_meta_only.
+void EraseMetaOnly(CommonFields& c, ctrl_t* it, size_t slot_size);
+
+// Function to place in PolicyFunctions::dealloc for raw_hash_sets
+// that are using std::allocator. This allows us to share the same
+// function body for raw_hash_set instantiations that have the
+// same slot alignment.
+template <size_t AlignOfSlot>
+ABSL_ATTRIBUTE_NOINLINE void DeallocateStandard(void*,
+                                                const PolicyFunctions& policy,
+                                                ctrl_t* ctrl, void* slot_array,
+                                                size_t n) {
+  // Unpoison before returning the memory to the allocator.
+  SanitizerUnpoisonMemoryRegion(slot_array, policy.slot_size * n);
+
+  std::allocator<char> alloc;
+  Deallocate<AlignOfSlot>(&alloc, ctrl,
+                          AllocSize(n, policy.slot_size, AlignOfSlot));
+}
+
+// For trivially relocatable types we use memcpy directly. This allows us to
+// share the same function body for raw_hash_set instantiations that have the
+// same slot size as long as they are relocatable.
+template <size_t SizeOfSlot>
+ABSL_ATTRIBUTE_NOINLINE void TransferRelocatable(void*, void* dst, void* src) {
+  memcpy(dst, src, SizeOfSlot);
+}
+
+// Type-erased version of raw_hash_set::drop_deletes_without_resize.
+void DropDeletesWithoutResize(CommonFields& common,
+                              const PolicyFunctions& policy, void* tmp_space);
+
 // A SwissTable.
 //
 // Policy: a policy defines how to perform different operations on
@@ -1018,7 +1510,7 @@
   static_assert(std::is_same<const_pointer, const value_type*>::value,
                 "Allocators with custom pointer types are not supported");
 
-  class iterator {
+  class iterator : private HashSetIteratorGenerationInfo {
     friend class raw_hash_set;
 
    public:
@@ -1034,22 +1526,19 @@
 
     // PRECONDITION: not an end() iterator.
     reference operator*() const {
-      ABSL_INTERNAL_ASSERT_IS_FULL(ctrl_,
-                                   "operator*() called on invalid iterator.");
+      AssertIsFull(ctrl_, generation(), generation_ptr(), "operator*()");
       return PolicyTraits::element(slot_);
     }
 
     // PRECONDITION: not an end() iterator.
     pointer operator->() const {
-      ABSL_INTERNAL_ASSERT_IS_FULL(ctrl_,
-                                   "operator-> called on invalid iterator.");
+      AssertIsFull(ctrl_, generation(), generation_ptr(), "operator->");
       return &operator*();
     }
 
     // PRECONDITION: not an end() iterator.
     iterator& operator++() {
-      ABSL_INTERNAL_ASSERT_IS_FULL(ctrl_,
-                                   "operator++ called on invalid iterator.");
+      AssertIsFull(ctrl_, generation(), generation_ptr(), "operator++");
       ++ctrl_;
       ++slot_;
       skip_empty_or_deleted();
@@ -1063,8 +1552,10 @@
     }
 
     friend bool operator==(const iterator& a, const iterator& b) {
-      AssertIsValid(a.ctrl_);
-      AssertIsValid(b.ctrl_);
+      AssertIsValidForComparison(a.ctrl_, a.generation(), a.generation_ptr());
+      AssertIsValidForComparison(b.ctrl_, b.generation(), b.generation_ptr());
+      AssertSameContainer(a.ctrl_, b.ctrl_, a.slot_, b.slot_,
+                          a.generation_ptr(), b.generation_ptr());
       return a.ctrl_ == b.ctrl_;
     }
     friend bool operator!=(const iterator& a, const iterator& b) {
@@ -1072,16 +1563,23 @@
     }
 
    private:
-    iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) {
+    iterator(ctrl_t* ctrl, slot_type* slot,
+             const GenerationType* generation_ptr)
+        : HashSetIteratorGenerationInfo(generation_ptr),
+          ctrl_(ctrl),
+          slot_(slot) {
       // This assumption helps the compiler know that any non-end iterator is
       // not equal to any end iterator.
       ABSL_ASSUME(ctrl != nullptr);
     }
+    // For end() iterators.
+    explicit iterator(const GenerationType* generation_ptr)
+        : HashSetIteratorGenerationInfo(generation_ptr), ctrl_(nullptr) {}
 
     // Fixes up `ctrl_` to point to a full by advancing it and `slot_` until
     // they reach one.
     //
-    // If a sentinel is reached, we null both of them out instead.
+    // If a sentinel is reached, we null `ctrl_` out instead.
     void skip_empty_or_deleted() {
       while (IsEmptyOrDeleted(*ctrl_)) {
         uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted();
@@ -1091,7 +1589,9 @@
       if (ABSL_PREDICT_FALSE(*ctrl_ == ctrl_t::kSentinel)) ctrl_ = nullptr;
     }
 
-    ctrl_t* ctrl_ = nullptr;
+    // We use EmptyGroup() for default-constructed iterators so that they can
+    // be distinguished from end iterators, which have nullptr ctrl_.
+    ctrl_t* ctrl_ = EmptyGroup();
     // To avoid uninitialized member warnings, put slot_ in an anonymous union.
     // The member is not initialized on singleton and end iterators.
     union {
@@ -1109,9 +1609,9 @@
     using pointer = typename raw_hash_set::const_pointer;
     using difference_type = typename raw_hash_set::difference_type;
 
-    const_iterator() {}
+    const_iterator() = default;
     // Implicit construction from iterator.
-    const_iterator(iterator i) : inner_(std::move(i)) {}
+    const_iterator(iterator i) : inner_(std::move(i)) {}  // NOLINT
 
     reference operator*() const { return *inner_; }
     pointer operator->() const { return inner_.operator->(); }
@@ -1130,8 +1630,10 @@
     }
 
    private:
-    const_iterator(const ctrl_t* ctrl, const slot_type* slot)
-        : inner_(const_cast<ctrl_t*>(ctrl), const_cast<slot_type*>(slot)) {}
+    const_iterator(const ctrl_t* ctrl, const slot_type* slot,
+                   const GenerationType* gen)
+        : inner_(const_cast<ctrl_t*>(ctrl), const_cast<slot_type*>(slot), gen) {
+    }
 
     iterator inner_;
   };
@@ -1139,19 +1641,20 @@
   using node_type = node_handle<Policy, hash_policy_traits<Policy>, Alloc>;
   using insert_return_type = InsertReturnType<iterator, node_type>;
 
+  // Note: can't use `= default` due to non-default noexcept (causes
+  // problems for some compilers). NOLINTNEXTLINE
   raw_hash_set() noexcept(
       std::is_nothrow_default_constructible<hasher>::value&&
           std::is_nothrow_default_constructible<key_equal>::value&&
               std::is_nothrow_default_constructible<allocator_type>::value) {}
 
-  explicit raw_hash_set(size_t bucket_count,
-                        const hasher& hash = hasher(),
-                        const key_equal& eq = key_equal(),
-                        const allocator_type& alloc = allocator_type())
-      : ctrl_(EmptyGroup()),
-        settings_(0u, HashtablezInfoHandle(), hash, eq, alloc) {
+  ABSL_ATTRIBUTE_NOINLINE explicit raw_hash_set(
+      size_t bucket_count, const hasher& hash = hasher(),
+      const key_equal& eq = key_equal(),
+      const allocator_type& alloc = allocator_type())
+      : settings_(CommonFields{}, hash, eq, alloc) {
     if (bucket_count) {
-      capacity_ = NormalizeCapacity(bucket_count);
+      common().capacity_ = NormalizeCapacity(bucket_count);
       initialize_slots();
     }
   }
@@ -1258,47 +1761,30 @@
     // than a full `insert`.
     for (const auto& v : that) {
       const size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, v);
-      auto target = find_first_non_full(ctrl_, hash, capacity_);
-      SetCtrl(target.offset, H2(hash), capacity_, ctrl_, slots_,
-              sizeof(slot_type));
+      auto target = find_first_non_full_outofline(common(), hash);
+      SetCtrl(common(), target.offset, H2(hash), sizeof(slot_type));
       emplace_at(target.offset, v);
+      common().maybe_increment_generation_on_insert();
       infoz().RecordInsert(hash, target.probe_length);
     }
-    size_ = that.size();
+    common().size_ = that.size();
     growth_left() -= that.size();
   }
 
-  raw_hash_set(raw_hash_set&& that) noexcept(
+  ABSL_ATTRIBUTE_NOINLINE raw_hash_set(raw_hash_set&& that) noexcept(
       std::is_nothrow_copy_constructible<hasher>::value&&
           std::is_nothrow_copy_constructible<key_equal>::value&&
               std::is_nothrow_copy_constructible<allocator_type>::value)
-      : ctrl_(absl::exchange(that.ctrl_, EmptyGroup())),
-        slots_(absl::exchange(that.slots_, nullptr)),
-        size_(absl::exchange(that.size_, size_t{0})),
-        capacity_(absl::exchange(that.capacity_, size_t{0})),
-        // Hash, equality and allocator are copied instead of moved because
-        // `that` must be left valid. If Hash is std::function<Key>, moving it
-        // would create a nullptr functor that cannot be called.
-        settings_(absl::exchange(that.growth_left(), size_t{0}),
-                  absl::exchange(that.infoz(), HashtablezInfoHandle()),
-                  that.hash_ref(),
-                  that.eq_ref(),
-                  that.alloc_ref()) {}
+      :  // Hash, equality and allocator are copied instead of moved because
+         // `that` must be left valid. If Hash is std::function<Key>, moving it
+         // would create a nullptr functor that cannot be called.
+        settings_(absl::exchange(that.common(), CommonFields{}),
+                  that.hash_ref(), that.eq_ref(), that.alloc_ref()) {}
 
   raw_hash_set(raw_hash_set&& that, const allocator_type& a)
-      : ctrl_(EmptyGroup()),
-        slots_(nullptr),
-        size_(0),
-        capacity_(0),
-        settings_(0, HashtablezInfoHandle(), that.hash_ref(), that.eq_ref(),
-                  a) {
+      : settings_(CommonFields{}, that.hash_ref(), that.eq_ref(), a) {
     if (a == that.alloc_ref()) {
-      std::swap(ctrl_, that.ctrl_);
-      std::swap(slots_, that.slots_);
-      std::swap(size_, that.size_);
-      std::swap(capacity_, that.capacity_);
-      std::swap(growth_left(), that.growth_left());
-      std::swap(infoz(), that.infoz());
+      std::swap(common(), that.common());
     } else {
       reserve(that.size());
       // Note: this will copy elements of dense_set and unordered_set instead of
@@ -1322,30 +1808,49 @@
               std::is_nothrow_move_assignable<key_equal>::value) {
     // TODO(sbenza): We should only use the operations from the noexcept clause
     // to make sure we actually adhere to that contract.
+    // NOLINTNEXTLINE: not returning *this for performance.
     return move_assign(
         std::move(that),
         typename AllocTraits::propagate_on_container_move_assignment());
   }
 
-  ~raw_hash_set() { destroy_slots(); }
+  ~raw_hash_set() {
+    const size_t cap = capacity();
+    if (!cap) return;
+    destroy_slots();
 
-  iterator begin() {
+    // Unpoison before returning the memory to the allocator.
+    SanitizerUnpoisonMemoryRegion(slot_array(), sizeof(slot_type) * cap);
+    Deallocate<alignof(slot_type)>(
+        &alloc_ref(), control(),
+        AllocSize(cap, sizeof(slot_type), alignof(slot_type)));
+
+    infoz().Unregister();
+  }
+
+  iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
     auto it = iterator_at(0);
     it.skip_empty_or_deleted();
     return it;
   }
-  iterator end() { return {}; }
+  iterator end() ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return iterator(common().generation_ptr());
+  }
 
-  const_iterator begin() const {
+  const_iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return const_cast<raw_hash_set*>(this)->begin();
   }
-  const_iterator end() const { return {}; }
-  const_iterator cbegin() const { return begin(); }
-  const_iterator cend() const { return end(); }
+  const_iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return iterator(common().generation_ptr());
+  }
+  const_iterator cbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return begin();
+  }
+  const_iterator cend() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return end(); }
 
   bool empty() const { return !size(); }
-  size_t size() const { return size_; }
-  size_t capacity() const { return capacity_; }
+  size_t size() const { return common().size_; }
+  size_t capacity() const { return common().capacity_; }
   size_t max_size() const { return (std::numeric_limits<size_t>::max)(); }
 
   ABSL_ATTRIBUTE_REINITIALIZES void clear() {
@@ -1356,22 +1861,26 @@
     // compared to destruction of the elements of the container. So we pick the
     // largest bucket_count() threshold for which iteration is still fast and
     // past that we simply deallocate the array.
-    if (capacity_ > 127) {
+    const size_t cap = capacity();
+    if (cap == 0) {
+      // Already guaranteed to be empty; so nothing to do.
+    } else {
       destroy_slots();
-
-      infoz().RecordClearedReservation();
-    } else if (capacity_) {
-      for (size_t i = 0; i != capacity_; ++i) {
-        if (IsFull(ctrl_[i])) {
-          PolicyTraits::destroy(&alloc_ref(), slots_ + i);
-        }
-      }
-      size_ = 0;
-      ResetCtrl(capacity_, ctrl_, slots_, sizeof(slot_type));
-      reset_growth_left();
+      ClearBackingArray(common(), GetPolicyFunctions(),
+                        /*reuse=*/cap < 128);
     }
-    assert(empty());
-    infoz().RecordStorageChanged(0, capacity_);
+    common().set_reserved_growth(0);
+  }
+
+  inline void destroy_slots() {
+    const size_t cap = capacity();
+    const ctrl_t* ctrl = control();
+    slot_type* slot = slot_array();
+    for (size_t i = 0; i != cap; ++i) {
+      if (IsFull(ctrl[i])) {
+        PolicyTraits::destroy(&alloc_ref(), slot + i);
+      }
+    }
   }
 
   // This overload kicks in when the argument is an rvalue of insertable and
@@ -1384,7 +1893,7 @@
   template <class T, RequiresInsertable<T> = 0, class T2 = T,
             typename std::enable_if<IsDecomposable<T2>::value, int>::type = 0,
             T* = nullptr>
-  std::pair<iterator, bool> insert(T&& value) {
+  std::pair<iterator, bool> insert(T&& value) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return emplace(std::forward<T>(value));
   }
 
@@ -1399,13 +1908,11 @@
   //   const char* p = "hello";
   //   s.insert(p);
   //
-  // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace
-  // RequiresInsertable<T> with RequiresInsertable<const T&>.
-  // We are hitting this bug: https://godbolt.org/g/1Vht4f.
   template <
-      class T, RequiresInsertable<T> = 0,
+      class T, RequiresInsertable<const T&> = 0,
       typename std::enable_if<IsDecomposable<const T&>::value, int>::type = 0>
-  std::pair<iterator, bool> insert(const T& value) {
+  std::pair<iterator, bool> insert(const T& value)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return emplace(value);
   }
 
@@ -1414,7 +1921,8 @@
   //
   //   flat_hash_map<std::string, int> s;
   //   s.insert({"abc", 42});
-  std::pair<iterator, bool> insert(init_type&& value) {
+  std::pair<iterator, bool> insert(init_type&& value)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return emplace(std::move(value));
   }
 
@@ -1423,21 +1931,20 @@
   template <class T, RequiresInsertable<T> = 0, class T2 = T,
             typename std::enable_if<IsDecomposable<T2>::value, int>::type = 0,
             T* = nullptr>
-  iterator insert(const_iterator, T&& value) {
+  iterator insert(const_iterator, T&& value) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert(std::forward<T>(value)).first;
   }
 
-  // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace
-  // RequiresInsertable<T> with RequiresInsertable<const T&>.
-  // We are hitting this bug: https://godbolt.org/g/1Vht4f.
   template <
-      class T, RequiresInsertable<T> = 0,
+      class T, RequiresInsertable<const T&> = 0,
       typename std::enable_if<IsDecomposable<const T&>::value, int>::type = 0>
-  iterator insert(const_iterator, const T& value) {
+  iterator insert(const_iterator,
+                  const T& value) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert(value).first;
   }
 
-  iterator insert(const_iterator, init_type&& value) {
+  iterator insert(const_iterator,
+                  init_type&& value) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return insert(std::move(value)).first;
   }
 
@@ -1455,7 +1962,7 @@
     insert(ilist.begin(), ilist.end());
   }
 
-  insert_return_type insert(node_type&& node) {
+  insert_return_type insert(node_type&& node) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     if (!node) return {end(), false, node_type()};
     const auto& elem = PolicyTraits::element(CommonAccess::GetSlot(node));
     auto res = PolicyTraits::apply(
@@ -1469,7 +1976,8 @@
     }
   }
 
-  iterator insert(const_iterator, node_type&& node) {
+  iterator insert(const_iterator,
+                  node_type&& node) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     auto res = insert(std::move(node));
     node = std::move(res.node);
     return res.position;
@@ -1486,7 +1994,8 @@
   //   m.emplace("abc", "xyz");
   template <class... Args, typename std::enable_if<
                                IsDecomposable<Args...>::value, int>::type = 0>
-  std::pair<iterator, bool> emplace(Args&&... args) {
+  std::pair<iterator, bool> emplace(Args&&... args)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return PolicyTraits::apply(EmplaceDecomposable{*this},
                                std::forward<Args>(args)...);
   }
@@ -1496,7 +2005,8 @@
   // destroys.
   template <class... Args, typename std::enable_if<
                                !IsDecomposable<Args...>::value, int>::type = 0>
-  std::pair<iterator, bool> emplace(Args&&... args) {
+  std::pair<iterator, bool> emplace(Args&&... args)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     alignas(slot_type) unsigned char raw[sizeof(slot_type)];
     slot_type* slot = reinterpret_cast<slot_type*>(&raw);
 
@@ -1506,7 +2016,8 @@
   }
 
   template <class... Args>
-  iterator emplace_hint(const_iterator, Args&&... args) {
+  iterator emplace_hint(const_iterator,
+                        Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return emplace(std::forward<Args>(args)...).first;
   }
 
@@ -1556,10 +2067,11 @@
   };
 
   template <class K = key_type, class F>
-  iterator lazy_emplace(const key_arg<K>& key, F&& f) {
+  iterator lazy_emplace(const key_arg<K>& key,
+                        F&& f) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     auto res = find_or_prepare_insert(key);
     if (res.second) {
-      slot_type* slot = slots_ + res.first;
+      slot_type* slot = slot_array() + res.first;
       std::forward<F>(f)(constructor(&alloc_ref(), &slot));
       assert(!slot);
     }
@@ -1601,13 +2113,13 @@
   // This overload is necessary because otherwise erase<K>(const K&) would be
   // a better match if non-const iterator is passed as an argument.
   void erase(iterator it) {
-    ABSL_INTERNAL_ASSERT_IS_FULL(it.ctrl_,
-                                 "erase() called on invalid iterator.");
+    AssertIsFull(it.ctrl_, it.generation(), it.generation_ptr(), "erase()");
     PolicyTraits::destroy(&alloc_ref(), it.slot_);
     erase_meta_only(it);
   }
 
-  iterator erase(const_iterator first, const_iterator last) {
+  iterator erase(const_iterator first,
+                 const_iterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     while (first != last) {
       erase(first++);
     }
@@ -1636,8 +2148,8 @@
   }
 
   node_type extract(const_iterator position) {
-    ABSL_INTERNAL_ASSERT_IS_FULL(position.inner_.ctrl_,
-                                 "extract() called on invalid iterator.");
+    AssertIsFull(position.inner_.ctrl_, position.inner_.generation(),
+                 position.inner_.generation_ptr(), "extract()");
     auto node =
         CommonAccess::Transfer<node_type>(alloc_ref(), position.inner_.slot_);
     erase_meta_only(position);
@@ -1657,24 +2169,18 @@
       IsNoThrowSwappable<allocator_type>(
           typename AllocTraits::propagate_on_container_swap{})) {
     using std::swap;
-    swap(ctrl_, that.ctrl_);
-    swap(slots_, that.slots_);
-    swap(size_, that.size_);
-    swap(capacity_, that.capacity_);
-    swap(growth_left(), that.growth_left());
+    swap(common(), that.common());
     swap(hash_ref(), that.hash_ref());
     swap(eq_ref(), that.eq_ref());
-    swap(infoz(), that.infoz());
     SwapAlloc(alloc_ref(), that.alloc_ref(),
               typename AllocTraits::propagate_on_container_swap{});
   }
 
   void rehash(size_t n) {
-    if (n == 0 && capacity_ == 0) return;
-    if (n == 0 && size_ == 0) {
-      destroy_slots();
-      infoz().RecordStorageChanged(0, 0);
-      infoz().RecordClearedReservation();
+    if (n == 0 && capacity() == 0) return;
+    if (n == 0 && size() == 0) {
+      ClearBackingArray(common(), GetPolicyFunctions(),
+                        /*reuse=*/false);
       return;
     }
 
@@ -1682,7 +2188,7 @@
     // 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_) {
+    if (n == 0 || m > capacity()) {
       resize(m);
 
       // This is after resize, to ensure that we have completed the allocation
@@ -1700,6 +2206,7 @@
       // and have potentially sampled the hashtable.
       infoz().RecordReservation(n);
     }
+    common().reset_reserved_growth(n);
   }
 
   // Extension API: support for heterogeneous keys.
@@ -1725,12 +2232,12 @@
   void prefetch(const key_arg<K>& key) const {
     (void)key;
     // Avoid probing if we won't be able to prefetch the addresses received.
-#ifdef ABSL_INTERNAL_HAVE_PREFETCH
+#ifdef ABSL_HAVE_PREFETCH
     prefetch_heap_block();
-    auto seq = probe(ctrl_, hash_ref()(key), capacity_);
-    base_internal::PrefetchT0(ctrl_ + seq.offset());
-    base_internal::PrefetchT0(slots_ + seq.offset());
-#endif  // ABSL_INTERNAL_HAVE_PREFETCH
+    auto seq = probe(common(), hash_ref()(key));
+    PrefetchToLocalCache(control() + seq.offset());
+    PrefetchToLocalCache(slot_array() + seq.offset());
+#endif  // ABSL_HAVE_PREFETCH
   }
 
   // The API of find() has two extensions.
@@ -1741,33 +2248,38 @@
   // 2. The type of the key argument doesn't have to be key_type. This is so
   // called heterogeneous key support.
   template <class K = key_type>
-  iterator find(const key_arg<K>& key, size_t hash) {
-    auto seq = probe(ctrl_, hash, capacity_);
+  iterator find(const key_arg<K>& key,
+                size_t hash) ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    auto seq = probe(common(), hash);
+    slot_type* slot_ptr = slot_array();
+    const ctrl_t* ctrl = control();
     while (true) {
-      Group g{ctrl_ + seq.offset()};
+      Group g{ctrl + seq.offset()};
       for (uint32_t i : g.Match(H2(hash))) {
         if (ABSL_PREDICT_TRUE(PolicyTraits::apply(
                 EqualElement<K>{key, eq_ref()},
-                PolicyTraits::element(slots_ + seq.offset(i)))))
+                PolicyTraits::element(slot_ptr + seq.offset(i)))))
           return iterator_at(seq.offset(i));
       }
       if (ABSL_PREDICT_TRUE(g.MaskEmpty())) 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) {
+  iterator find(const key_arg<K>& key) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     prefetch_heap_block();
     return find(key, hash_ref()(key));
   }
 
   template <class K = key_type>
-  const_iterator find(const key_arg<K>& key, size_t hash) const {
+  const_iterator find(const key_arg<K>& key,
+                      size_t hash) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return const_cast<raw_hash_set*>(this)->find(key, hash);
   }
   template <class K = key_type>
-  const_iterator find(const key_arg<K>& key) const {
+  const_iterator find(const key_arg<K>& key) const
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     prefetch_heap_block();
     return find(key, hash_ref()(key));
   }
@@ -1778,22 +2290,23 @@
   }
 
   template <class K = key_type>
-  std::pair<iterator, iterator> equal_range(const key_arg<K>& key) {
+  std::pair<iterator, iterator> equal_range(const key_arg<K>& key)
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     auto it = find(key);
     if (it != end()) return {it, std::next(it)};
     return {it, it};
   }
   template <class K = key_type>
   std::pair<const_iterator, const_iterator> equal_range(
-      const key_arg<K>& key) const {
+      const key_arg<K>& key) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     auto it = find(key);
     if (it != end()) return {it, std::next(it)};
     return {it, it};
   }
 
-  size_t bucket_count() const { return capacity_; }
+  size_t bucket_count() const { return capacity(); }
   float load_factor() const {
-    return capacity_ ? static_cast<double>(size()) / capacity_ : 0.0;
+    return capacity() ? static_cast<double>(size()) / capacity() : 0.0;
   }
   float max_load_factor() const { return 1.0f; }
   void max_load_factor(float) {
@@ -1880,7 +2393,8 @@
     std::pair<iterator, bool> operator()(const K& key, Args&&...) && {
       auto res = s.find_or_prepare_insert(key);
       if (res.second) {
-        PolicyTraits::transfer(&s.alloc_ref(), s.slots_ + res.first, &slot);
+        PolicyTraits::transfer(&s.alloc_ref(), s.slot_array() + res.first,
+                               &slot);
       } else if (do_destroy) {
         PolicyTraits::destroy(&s.alloc_ref(), &slot);
       }
@@ -1896,102 +2410,43 @@
   // This merely updates the pertinent control byte. This can be used in
   // conjunction with Policy::transfer to move the object to another place.
   void erase_meta_only(const_iterator it) {
-    assert(IsFull(*it.inner_.ctrl_) && "erasing a dangling iterator");
-    --size_;
-    const size_t index = static_cast<size_t>(it.inner_.ctrl_ - ctrl_);
-    const size_t index_before = (index - Group::kWidth) & capacity_;
-    const auto empty_after = Group(it.inner_.ctrl_).MaskEmpty();
-    const auto empty_before = Group(ctrl_ + index_before).MaskEmpty();
-
-    // We count how many consecutive non empties we have to the right and to the
-    // left of `it`. If the sum is >= kWidth then there is at least one probe
-    // window that might have seen a full group.
-    bool was_never_full =
-        empty_before && empty_after &&
-        static_cast<size_t>(empty_after.TrailingZeros() +
-                            empty_before.LeadingZeros()) < Group::kWidth;
-
-    SetCtrl(index, was_never_full ? ctrl_t::kEmpty : ctrl_t::kDeleted,
-            capacity_, ctrl_, slots_, sizeof(slot_type));
-    growth_left() += was_never_full;
-    infoz().RecordErase();
+    EraseMetaOnly(common(), it.inner_.ctrl_, sizeof(slot_type));
   }
 
   // Allocates a backing array for `self` and initializes its control bytes.
-  // This reads `capacity_` and updates all other fields based on the result of
+  // This reads `capacity` and updates all other fields based on the result of
   // the allocation.
   //
-  // This does not free the currently held array; `capacity_` must be nonzero.
-  void initialize_slots() {
-    assert(capacity_);
-    // Folks with custom allocators often make unwarranted assumptions about the
-    // behavior of their classes vis-a-vis trivial destructability and what
-    // calls they will or wont make.  Avoid sampling for people with custom
-    // allocators to get us out of this mess.  This is not a hard guarantee but
-    // a workaround while we plan the exact guarantee we want to provide.
-    //
+  // This does not free the currently held array; `capacity` must be nonzero.
+  inline void initialize_slots() {
     // People are often sloppy with the exact type of their allocator (sometimes
     // it has an extra const or is missing the pair, but rebinds made it work
-    // anyway).  To avoid the ambiguity, we work off SlotAlloc which we have
-    // bound more carefully.
-    if (std::is_same<SlotAlloc, std::allocator<slot_type>>::value &&
-        slots_ == nullptr) {
-      infoz() = Sample(sizeof(slot_type));
-    }
-
-    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_);
+    // anyway).
+    using CharAlloc =
+        typename absl::allocator_traits<Alloc>::template rebind_alloc<char>;
+    InitializeSlots<CharAlloc, sizeof(slot_type), alignof(slot_type)>(
+        common(), CharAlloc(alloc_ref()));
   }
 
-  // Destroys all slots in the backing array, frees the backing array, and
-  // clears all top-level book-keeping data.
-  //
-  // This essentially implements `map = raw_hash_set();`.
-  void destroy_slots() {
-    if (!capacity_) return;
-    for (size_t i = 0; i != capacity_; ++i) {
-      if (IsFull(ctrl_[i])) {
-        PolicyTraits::destroy(&alloc_ref(), slots_ + i);
-      }
-    }
-
-    // Unpoison before returning the memory to the allocator.
-    SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_);
-    Deallocate<alignof(slot_type)>(
-        &alloc_ref(), ctrl_,
-        AllocSize(capacity_, sizeof(slot_type), alignof(slot_type)));
-    ctrl_ = EmptyGroup();
-    slots_ = nullptr;
-    size_ = 0;
-    capacity_ = 0;
-    growth_left() = 0;
-  }
-
-  void resize(size_t new_capacity) {
+  ABSL_ATTRIBUTE_NOINLINE void resize(size_t new_capacity) {
     assert(IsValidCapacity(new_capacity));
-    auto* old_ctrl = ctrl_;
-    auto* old_slots = slots_;
-    const size_t old_capacity = capacity_;
-    capacity_ = new_capacity;
+    auto* old_ctrl = control();
+    auto* old_slots = slot_array();
+    const size_t old_capacity = common().capacity_;
+    common().capacity_ = new_capacity;
     initialize_slots();
 
+    auto* new_slots = slot_array();
     size_t total_probe_length = 0;
     for (size_t i = 0; i != old_capacity; ++i) {
       if (IsFull(old_ctrl[i])) {
         size_t hash = PolicyTraits::apply(HashElement{hash_ref()},
                                           PolicyTraits::element(old_slots + i));
-        auto target = find_first_non_full(ctrl_, hash, capacity_);
+        auto target = find_first_non_full(common(), hash);
         size_t new_i = target.offset;
         total_probe_length += target.probe_length;
-        SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type));
-        PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i);
+        SetCtrl(common(), new_i, H2(hash), sizeof(slot_type));
+        PolicyTraits::transfer(&alloc_ref(), new_slots + new_i, old_slots + i);
       }
     }
     if (old_capacity) {
@@ -2007,70 +2462,10 @@
   // Prunes control bytes to remove as many tombstones as possible.
   //
   // See the comment on `rehash_and_grow_if_necessary()`.
-  void drop_deletes_without_resize() ABSL_ATTRIBUTE_NOINLINE {
-    assert(IsValidCapacity(capacity_));
-    assert(!is_small(capacity_));
-    // Algorithm:
-    // - mark all DELETED slots as EMPTY
-    // - mark all FULL slots as DELETED
-    // - for each slot marked as DELETED
-    //     hash = Hash(element)
-    //     target = find_first_non_full(hash)
-    //     if target is in the same group
-    //       mark slot as FULL
-    //     else if target is EMPTY
-    //       transfer element to target
-    //       mark slot as EMPTY
-    //       mark target as FULL
-    //     else if target is DELETED
-    //       swap current element with target element
-    //       mark target as FULL
-    //       repeat procedure for current slot with moved from element (target)
-    ConvertDeletedToEmptyAndFullToDeleted(ctrl_, capacity_);
-    alignas(slot_type) unsigned char raw[sizeof(slot_type)];
-    size_t total_probe_length = 0;
-    slot_type* slot = reinterpret_cast<slot_type*>(&raw);
-    for (size_t i = 0; i != capacity_; ++i) {
-      if (!IsDeleted(ctrl_[i])) continue;
-      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 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))) {
-        SetCtrl(i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type));
-        continue;
-      }
-      if (IsEmpty(ctrl_[new_i])) {
-        // Transfer element to the empty spot.
-        // SetCtrl poisons/unpoisons the slots so we have to call it at the
-        // right time.
-        SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type));
-        PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slots_ + i);
-        SetCtrl(i, ctrl_t::kEmpty, capacity_, ctrl_, slots_, sizeof(slot_type));
-      } else {
-        assert(IsDeleted(ctrl_[new_i]));
-        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);
-        PolicyTraits::transfer(&alloc_ref(), slots_ + i, slots_ + new_i);
-        PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slot);
-        --i;  // repeat
-      }
-    }
-    reset_growth_left();
-    infoz().RecordRehash(total_probe_length);
+  inline void drop_deletes_without_resize() {
+    // Stack-allocate space for swapping elements.
+    alignas(slot_type) unsigned char tmp[sizeof(slot_type)];
+    DropDeletesWithoutResize(common(), GetPolicyFunctions(), tmp);
   }
 
   // Called whenever the table *might* need to conditionally grow.
@@ -2079,14 +2474,13 @@
   // growth is unnecessary, because vacating tombstones is beneficial for
   // performance in the long-run.
   void rehash_and_grow_if_necessary() {
-    if (capacity_ == 0) {
-      resize(1);
-    } else if (capacity_ > Group::kWidth &&
-               // Do these calcuations in 64-bit to avoid overflow.
-               size() * uint64_t{32} <= capacity_ * uint64_t{25}) {
+    const size_t cap = capacity();
+    if (cap > Group::kWidth &&
+        // Do these calculations in 64-bit to avoid overflow.
+        size() * uint64_t{32} <= cap * uint64_t{25}) {
       // Squash DELETED without growing if there is enough capacity.
       //
-      // Rehash in place if the current size is <= 25/32 of 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
@@ -2104,8 +2498,8 @@
       //
       // 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_).
+      // 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.
@@ -2128,23 +2522,24 @@
       drop_deletes_without_resize();
     } else {
       // Otherwise grow the container.
-      resize(capacity_ * 2 + 1);
+      resize(NextCapacity(cap));
     }
   }
 
   bool has_element(const value_type& elem) const {
     size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, elem);
-    auto seq = probe(ctrl_, hash, capacity_);
+    auto seq = probe(common(), hash);
+    const ctrl_t* ctrl = control();
     while (true) {
-      Group g{ctrl_ + seq.offset()};
+      Group g{ctrl + seq.offset()};
       for (uint32_t i : g.Match(H2(hash))) {
-        if (ABSL_PREDICT_TRUE(PolicyTraits::element(slots_ + seq.offset(i)) ==
-                              elem))
+        if (ABSL_PREDICT_TRUE(
+                PolicyTraits::element(slot_array() + seq.offset(i)) == elem))
           return true;
       }
       if (ABSL_PREDICT_TRUE(g.MaskEmpty())) return false;
       seq.next();
-      assert(seq.index() <= capacity_ && "full table!");
+      assert(seq.index() <= capacity() && "full table!");
     }
     return false;
   }
@@ -2169,18 +2564,19 @@
   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_);
+    auto seq = probe(common(), hash);
+    const ctrl_t* ctrl = control();
     while (true) {
-      Group g{ctrl_ + seq.offset()};
+      Group g{ctrl + seq.offset()};
       for (uint32_t i : g.Match(H2(hash))) {
         if (ABSL_PREDICT_TRUE(PolicyTraits::apply(
                 EqualElement<K>{key, eq_ref()},
-                PolicyTraits::element(slots_ + seq.offset(i)))))
+                PolicyTraits::element(slot_array() + seq.offset(i)))))
           return {seq.offset(i), false};
       }
       if (ABSL_PREDICT_TRUE(g.MaskEmpty())) break;
       seq.next();
-      assert(seq.index() <= capacity_ && "full table!");
+      assert(seq.index() <= capacity() && "full table!");
     }
     return {prepare_insert(hash), true};
   }
@@ -2190,16 +2586,24 @@
   //
   // REQUIRES: At least one non-full slot available.
   size_t prepare_insert(size_t hash) ABSL_ATTRIBUTE_NOINLINE {
-    auto target = find_first_non_full(ctrl_, hash, capacity_);
-    if (ABSL_PREDICT_FALSE(growth_left() == 0 &&
-                           !IsDeleted(ctrl_[target.offset]))) {
-      rehash_and_grow_if_necessary();
-      target = find_first_non_full(ctrl_, hash, capacity_);
+    const bool rehash_for_bug_detection =
+        common().should_rehash_for_bug_detection_on_insert();
+    if (rehash_for_bug_detection) {
+      // Move to a different heap allocation in order to detect bugs.
+      const size_t cap = capacity();
+      resize(growth_left() > 0 ? cap : NextCapacity(cap));
     }
-    ++size_;
-    growth_left() -= IsEmpty(ctrl_[target.offset]);
-    SetCtrl(target.offset, H2(hash), capacity_, ctrl_, slots_,
-            sizeof(slot_type));
+    auto target = find_first_non_full(common(), hash);
+    if (!rehash_for_bug_detection &&
+        ABSL_PREDICT_FALSE(growth_left() == 0 &&
+                           !IsDeleted(control()[target.offset]))) {
+      rehash_and_grow_if_necessary();
+      target = find_first_non_full(common(), hash);
+    }
+    ++common().size_;
+    growth_left() -= IsEmpty(control()[target.offset]);
+    SetCtrl(common(), target.offset, H2(hash), sizeof(slot_type));
+    common().maybe_increment_generation_on_insert();
     infoz().RecordInsert(hash, target.probe_length);
     return target.offset;
   }
@@ -2214,7 +2618,7 @@
   // POSTCONDITION: *m.iterator_at(i) == value_type(forward<Args>(args)...).
   template <class... Args>
   void emplace_at(size_t i, Args&&... args) {
-    PolicyTraits::construct(&alloc_ref(), slots_ + i,
+    PolicyTraits::construct(&alloc_ref(), slot_array() + i,
                             std::forward<Args>(args)...);
 
     assert(PolicyTraits::apply(FindElement{*this}, *iterator_at(i)) ==
@@ -2222,16 +2626,16 @@
            "constructed value does not match the lookup key");
   }
 
-  iterator iterator_at(size_t i) { return {ctrl_ + i, slots_ + i}; }
-  const_iterator iterator_at(size_t i) const { return {ctrl_ + i, slots_ + i}; }
+  iterator iterator_at(size_t i) ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return {control() + i, slot_array() + i, common().generation_ptr()};
+  }
+  const_iterator iterator_at(size_t i) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return {control() + i, slot_array() + i, common().generation_ptr()};
+  }
 
  private:
   friend struct RawHashSetTestOnlyAccess;
 
-  void reset_growth_left() {
-    growth_left() = CapacityToGrowth(capacity()) - size_;
-  }
-
   // The number of slots we can still fill without needing to rehash.
   //
   // This is stored separately due to tombstones: we do not include tombstones
@@ -2242,49 +2646,80 @@
   // side-effect.
   //
   // See `CapacityToGrowth()`.
-  size_t& growth_left() { return settings_.template get<0>(); }
+  size_t& growth_left() { return common().growth_left(); }
 
-  // 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.
+  // Prefetch the heap-allocated memory region to resolve potential TLB and
+  // cache misses. This is intended to overlap with execution of calculating the
+  // hash for a key.
   void prefetch_heap_block() const {
-    base_internal::PrefetchT2(ctrl_);
+#if ABSL_HAVE_BUILTIN(__builtin_prefetch) || defined(__GNUC__)
+    __builtin_prefetch(control(), 0, 1);
+#endif
   }
 
-  HashtablezInfoHandle& infoz() { return settings_.template get<1>(); }
+  CommonFields& common() { return settings_.template get<0>(); }
+  const CommonFields& common() const { return settings_.template get<0>(); }
 
-  hasher& hash_ref() { return settings_.template get<2>(); }
-  const hasher& hash_ref() const { return settings_.template get<2>(); }
-  key_equal& eq_ref() { return settings_.template get<3>(); }
-  const key_equal& eq_ref() const { return settings_.template get<3>(); }
-  allocator_type& alloc_ref() { return settings_.template get<4>(); }
+  ctrl_t* control() const { return common().control_; }
+  slot_type* slot_array() const {
+    return static_cast<slot_type*>(common().slots_);
+  }
+  HashtablezInfoHandle& infoz() { return common().infoz(); }
+
+  hasher& hash_ref() { return settings_.template get<1>(); }
+  const hasher& hash_ref() const { return settings_.template get<1>(); }
+  key_equal& eq_ref() { return settings_.template get<2>(); }
+  const key_equal& eq_ref() const { return settings_.template get<2>(); }
+  allocator_type& alloc_ref() { return settings_.template get<3>(); }
   const allocator_type& alloc_ref() const {
-    return settings_.template get<4>();
+    return settings_.template get<3>();
   }
 
-  // TODO(alkis): Investigate removing some of these fields:
-  // - ctrl/slots can be derived from each other
-  // - size can be moved into the slot array
+  // Make type-specific functions for this type's PolicyFunctions struct.
+  static size_t hash_slot_fn(void* set, void* slot) {
+    auto* h = static_cast<raw_hash_set*>(set);
+    return PolicyTraits::apply(
+        HashElement{h->hash_ref()},
+        PolicyTraits::element(static_cast<slot_type*>(slot)));
+  }
+  static void transfer_slot_fn(void* set, void* dst, void* src) {
+    auto* h = static_cast<raw_hash_set*>(set);
+    PolicyTraits::transfer(&h->alloc_ref(), static_cast<slot_type*>(dst),
+                           static_cast<slot_type*>(src));
+  }
+  // Note: dealloc_fn will only be used if we have a non-standard allocator.
+  static void dealloc_fn(void* set, const PolicyFunctions&, ctrl_t* ctrl,
+                         void* slot_mem, size_t n) {
+    auto* h = static_cast<raw_hash_set*>(set);
 
-  // The control bytes (and, also, a pointer to the base of the backing array).
-  //
-  // This contains `capacity_ + 1 + NumClonedBytes()` entries, even
-  // when the table is empty (hence EmptyGroup).
-  ctrl_t* ctrl_ = EmptyGroup();
-  // The beginning of the slots, located at `SlotOffset()` bytes after
-  // `ctrl_`. May be null for empty tables.
-  slot_type* slots_ = nullptr;
+    // Unpoison before returning the memory to the allocator.
+    SanitizerUnpoisonMemoryRegion(slot_mem, sizeof(slot_type) * n);
 
-  // The number of filled slots.
-  size_t size_ = 0;
+    Deallocate<alignof(slot_type)>(
+        &h->alloc_ref(), ctrl,
+        AllocSize(n, sizeof(slot_type), alignof(slot_type)));
+  }
 
-  // The total number of available slots.
-  size_t capacity_ = 0;
-  absl::container_internal::CompressedTuple<size_t /* growth_left */,
-                                            HashtablezInfoHandle, hasher,
-                                            key_equal, allocator_type>
-      settings_{0u, HashtablezInfoHandle{}, hasher{}, key_equal{},
-                allocator_type{}};
+  static const PolicyFunctions& GetPolicyFunctions() {
+    static constexpr PolicyFunctions value = {
+        sizeof(slot_type),
+        &raw_hash_set::hash_slot_fn,
+        PolicyTraits::transfer_uses_memcpy()
+            ? TransferRelocatable<sizeof(slot_type)>
+            : &raw_hash_set::transfer_slot_fn,
+        (std::is_same<SlotAlloc, std::allocator<slot_type>>::value
+             ? &DeallocateStandard<alignof(slot_type)>
+             : &raw_hash_set::dealloc_fn),
+    };
+    return value;
+  }
+
+  // Bundle together CommonFields plus other objects which might be empty.
+  // CompressedTuple will ensure that sizeof is not affected by any of the empty
+  // fields that occur after CommonFields.
+  absl::container_internal::CompressedTuple<CommonFields, hasher, key_equal,
+                                            allocator_type>
+      settings_{CommonFields{}, hasher{}, key_equal{}, allocator_type{}};
 };
 
 // Erases all elements that satisfy the predicate `pred` from the container `c`.
@@ -2312,14 +2747,15 @@
                              const typename Set::key_type& key) {
     size_t num_probes = 0;
     size_t hash = set.hash_ref()(key);
-    auto seq = probe(set.ctrl_, hash, set.capacity_);
+    auto seq = probe(set.common(), hash);
+    const ctrl_t* ctrl = set.control();
     while (true) {
-      container_internal::Group g{set.ctrl_ + seq.offset()};
+      container_internal::Group g{ctrl + seq.offset()};
       for (uint32_t i : g.Match(container_internal::H2(hash))) {
         if (Traits::apply(
                 typename Set::template EqualElement<typename Set::key_type>{
                     key, set.eq_ref()},
-                Traits::element(set.slots_ + seq.offset(i))))
+                Traits::element(set.slot_array() + seq.offset(i))))
           return num_probes;
         ++num_probes;
       }
@@ -2330,7 +2766,7 @@
   }
 
   static size_t AllocatedByteSize(const Set& c) {
-    size_t capacity = c.capacity_;
+    size_t capacity = c.capacity();
     if (capacity == 0) return 0;
     size_t m = AllocSize(capacity, sizeof(Slot), alignof(Slot));
 
@@ -2338,9 +2774,10 @@
     if (per_slot != ~size_t{}) {
       m += per_slot * c.size();
     } else {
+      const ctrl_t* ctrl = c.control();
       for (size_t i = 0; i != capacity; ++i) {
-        if (container_internal::IsFull(c.ctrl_[i])) {
-          m += Traits::space_used(c.slots_ + i);
+        if (container_internal::IsFull(ctrl[i])) {
+          m += Traits::space_used(c.slot_array() + i);
         }
       }
     }
@@ -2365,6 +2802,6 @@
 ABSL_NAMESPACE_END
 }  // namespace absl
 
-#undef ABSL_INTERNAL_ASSERT_IS_FULL
+#undef ABSL_SWISSTABLE_ENABLE_GENERATIONS
 
 #endif  // ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
diff --git a/absl/container/internal/raw_hash_set_benchmark.cc b/absl/container/internal/raw_hash_set_benchmark.cc
index e17ba9b..f77f2a7 100644
--- a/absl/container/internal/raw_hash_set_benchmark.cc
+++ b/absl/container/internal/raw_hash_set_benchmark.cc
@@ -16,6 +16,7 @@
 #include <cmath>
 #include <numeric>
 #include <random>
+#include <tuple>
 #include <utility>
 #include <vector>
 
@@ -477,6 +478,24 @@
 }
 BENCHMARK(BM_DropDeletes);
 
+void BM_Resize(benchmark::State& state) {
+  // For now just measure a small cheap hash table since we
+  // are mostly interested in the overhead of type-erasure
+  // in resize().
+  constexpr int kElements = 64;
+  const int kCapacity = kElements * 2;
+
+  IntTable table;
+  for (int i = 0; i < kElements; i++) {
+    table.insert(i);
+  }
+  for (auto unused : state) {
+    table.rehash(0);
+    table.rehash(kCapacity);
+  }
+}
+BENCHMARK(BM_Resize);
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
@@ -494,6 +513,12 @@
   return table->find(key) != table->end();
 }
 
+// This is useful because the find isn't inlined but the iterator comparison is.
+bool CodegenAbslRawHashSetStringFindNeEnd(
+    absl::container_internal::StringTable* table, const std::string& key) {
+  return table->find(key) != table->end();
+}
+
 auto CodegenAbslRawHashSetInt64Insert(absl::container_internal::IntTable* table,
                                       int64_t key)
     -> decltype(table->insert(key)) {
@@ -513,6 +538,7 @@
 int odr =
     (::benchmark::DoNotOptimize(std::make_tuple(
          &CodegenAbslRawHashSetInt64Find, &CodegenAbslRawHashSetInt64FindNeEnd,
+         &CodegenAbslRawHashSetStringFindNeEnd,
          &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 eec9da4..c46a593 100644
--- a/absl/container/internal/raw_hash_set_test.cc
+++ b/absl/container/internal/raw_hash_set_test.cc
@@ -20,6 +20,7 @@
 #include <cstdint>
 #include <deque>
 #include <functional>
+#include <iostream>
 #include <iterator>
 #include <list>
 #include <map>
@@ -39,8 +40,8 @@
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/internal/cycleclock.h"
-#include "absl/base/internal/prefetch.h"
 #include "absl/base/internal/raw_logging.h"
+#include "absl/base/prefetch.h"
 #include "absl/container/flat_hash_map.h"
 #include "absl/container/flat_hash_set.h"
 #include "absl/container/internal/container_memory.h"
@@ -56,8 +57,8 @@
 
 struct RawHashSetTestOnlyAccess {
   template <typename C>
-  static auto GetSlots(const C& c) -> decltype(c.slots_) {
-    return c.slots_;
+  static auto GetSlots(const C& c) -> decltype(c.slot_array()) {
+    return c.slot_array();
   }
 };
 
@@ -399,7 +400,7 @@
 struct StringTable
     : raw_hash_set<StringPolicy, StringHash, StringEq, std::allocator<int>> {
   using Base = typename StringTable::raw_hash_set;
-  StringTable() {}
+  StringTable() = default;
   using Base::Base;
 };
 
@@ -419,7 +420,7 @@
 
 template <typename T>
 struct CustomAlloc : std::allocator<T> {
-  CustomAlloc() {}
+  CustomAlloc() = default;
 
   template <typename U>
   explicit CustomAlloc(const CustomAlloc<U>& /*other*/) {}
@@ -446,7 +447,7 @@
 struct BadTable : raw_hash_set<IntPolicy, BadFastHash, std::equal_to<int>,
                                std::allocator<int>> {
   using Base = typename BadTable::raw_hash_set;
-  BadTable() {}
+  BadTable() = default;
   using Base::Base;
 };
 
@@ -455,12 +456,12 @@
   static_assert(std::is_empty<std::allocator<int>>::value, "");
 
   struct MockTable {
+    void* infoz;
     void* ctrl;
     void* slots;
     size_t size;
     size_t capacity;
     size_t growth_left;
-    void* infoz;
   };
   struct MockTableInfozDisabled {
     void* ctrl;
@@ -476,27 +477,37 @@
     size_t dummy;
   };
 
-  if (std::is_empty<HashtablezInfoHandle>::value) {
-    EXPECT_EQ(sizeof(MockTableInfozDisabled),
-              sizeof(raw_hash_set<StringPolicy, StatelessHash,
-                                  std::equal_to<absl::string_view>,
-                                  std::allocator<int>>));
+  struct GenerationData {
+    size_t reserved_growth;
+    GenerationType* generation;
+  };
 
-    EXPECT_EQ(sizeof(MockTableInfozDisabled) + sizeof(StatefulHash),
-              sizeof(raw_hash_set<StringPolicy, StatefulHash,
-                                  std::equal_to<absl::string_view>,
-                                  std::allocator<int>>));
-  } else {
-    EXPECT_EQ(sizeof(MockTable),
-              sizeof(raw_hash_set<StringPolicy, StatelessHash,
-                                  std::equal_to<absl::string_view>,
-                                  std::allocator<int>>));
+// Ignore unreachable-code warning. Compiler thinks one branch of each ternary
+// conditional is unreachable.
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+  constexpr size_t mock_size = std::is_empty<HashtablezInfoHandle>()
+                                   ? sizeof(MockTableInfozDisabled)
+                                   : sizeof(MockTable);
+  constexpr size_t generation_size =
+      SwisstableGenerationsEnabled() ? sizeof(GenerationData) : 0;
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
 
-    EXPECT_EQ(sizeof(MockTable) + sizeof(StatefulHash),
-              sizeof(raw_hash_set<StringPolicy, StatefulHash,
-                                  std::equal_to<absl::string_view>,
-                                  std::allocator<int>>));
-  }
+  EXPECT_EQ(
+      mock_size + generation_size,
+      sizeof(
+          raw_hash_set<StringPolicy, StatelessHash,
+                       std::equal_to<absl::string_view>, std::allocator<int>>));
+
+  EXPECT_EQ(
+      mock_size + sizeof(StatefulHash) + generation_size,
+      sizeof(
+          raw_hash_set<StringPolicy, StatefulHash,
+                       std::equal_to<absl::string_view>, std::allocator<int>>));
 }
 
 TEST(Table, Empty) {
@@ -854,6 +865,10 @@
 }
 
 TEST(Table, Decompose) {
+  if (SwisstableGenerationsEnabled()) {
+    GTEST_SKIP() << "Generations being enabled causes extra rehashes.";
+  }
+
   TestDecompose<DecomposeHash, DecomposeEq>(false);
 
   struct TransparentHashIntOverload {
@@ -892,6 +907,10 @@
 
 // Test that rehash with no resize happen in case of many deleted slots.
 TEST(Table, RehashWithNoResize) {
+  if (SwisstableGenerationsEnabled()) {
+    GTEST_SKIP() << "Generations being enabled causes extra rehashes.";
+  }
+
   Modulo1000HashTable t;
   // Adding the same length (and the same hash) strings
   // to have at least kMinFullGroups groups
@@ -985,6 +1004,10 @@
 }
 
 TEST(Table, ClearBug) {
+  if (SwisstableGenerationsEnabled()) {
+    GTEST_SKIP() << "Generations being enabled causes extra rehashes.";
+  }
+
   IntTable t;
   constexpr size_t capacity = container_internal::Group::kWidth - 1;
   constexpr size_t max_size = capacity / 2 + 1;
@@ -1003,7 +1026,7 @@
   // We are checking that original and second are close enough to each other
   // that they are probably still in the same group.  This is not strictly
   // guaranteed.
-  EXPECT_LT(std::abs(original - second),
+  EXPECT_LT(static_cast<size_t>(std::abs(original - second)),
             capacity * sizeof(IntTable::value_type));
 }
 
@@ -1080,19 +1103,6 @@
   // Ratios total_probe_length/size for every tested table.
   std::vector<double> single_table_ratios;
 
-  friend ProbeStats operator+(const ProbeStats& a, const ProbeStats& b) {
-    ProbeStats res = a;
-    res.all_probes_histogram.resize(std::max(res.all_probes_histogram.size(),
-                                             b.all_probes_histogram.size()));
-    std::transform(b.all_probes_histogram.begin(), b.all_probes_histogram.end(),
-                   res.all_probes_histogram.begin(),
-                   res.all_probes_histogram.begin(), std::plus<size_t>());
-    res.single_table_ratios.insert(res.single_table_ratios.end(),
-                                   b.single_table_ratios.begin(),
-                                   b.single_table_ratios.end());
-    return res;
-  }
-
   // Average ratio total_probe_length/size over tables.
   double AvgRatio() const {
     return std::accumulate(single_table_ratios.begin(),
@@ -1555,7 +1565,7 @@
 struct ExplicitAllocIntTable
     : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
                    std::equal_to<int64_t>, Alloc<int64_t>> {
-  ExplicitAllocIntTable() {}
+  ExplicitAllocIntTable() = default;
 };
 
 TEST(Table, AllocWithExplicitCtor) {
@@ -1822,7 +1832,6 @@
   EXPECT_TRUE((VerifyResultOf<CallCount, TransparentTable>()));
 }
 
-// TODO(alkis): Expand iterator tests.
 TEST(Iterator, IsDefaultConstructible) {
   StringTable::iterator i;
   EXPECT_TRUE(i == StringTable::iterator());
@@ -1943,7 +1952,7 @@
   EXPECT_FALSE(res.inserted);
   EXPECT_THAT(*res.position, Pair(k0, ""));
   EXPECT_TRUE(res.node);
-  EXPECT_FALSE(node);
+  EXPECT_FALSE(node);  // NOLINT(bugprone-use-after-move)
 }
 
 TEST(Nodes, HintInsert) {
@@ -1953,7 +1962,7 @@
   auto it = t.insert(t.begin(), std::move(node));
   EXPECT_THAT(t, UnorderedElementsAre(1, 2, 3));
   EXPECT_EQ(*it, 1);
-  EXPECT_FALSE(node);
+  EXPECT_FALSE(node);  // NOLINT(bugprone-use-after-move)
 
   node = t.extract(2);
   EXPECT_THAT(t, UnorderedElementsAre(1, 3));
@@ -1963,7 +1972,7 @@
   it = t.insert(t.begin(), std::move(node));
   EXPECT_EQ(*it, 2);
   // The node was not emptied by the insert call.
-  EXPECT_TRUE(node);
+  EXPECT_TRUE(node);  // NOLINT(bugprone-use-after-move)
 }
 
 IntTable MakeSimpleTable(size_t size) {
@@ -2036,20 +2045,95 @@
   EXPECT_NE(old_ptr, addr(0));
 }
 
-// Confirm that we assert if we try to erase() end().
-TEST(TableDeathTest, EraseOfEndAsserts) {
+bool IsAssertEnabled() {
   // Use an assert with side-effects to figure out if they are actually enabled.
   bool assert_enabled = false;
   assert([&]() {  // NOLINT
     assert_enabled = true;
     return true;
   }());
-  if (!assert_enabled) return;
+  return assert_enabled;
+}
+
+TEST(TableDeathTest, InvalidIteratorAsserts) {
+  if (!IsAssertEnabled() && !SwisstableGenerationsEnabled())
+    GTEST_SKIP() << "Assertions not enabled.";
 
   IntTable t;
   // Extra simple "regexp" as regexp support is highly varied across platforms.
-  constexpr char kDeathMsg[] = "erase.. called on invalid iterator";
-  EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()), kDeathMsg);
+  EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()),
+                            "erase.* called on end.. iterator.");
+  typename IntTable::iterator iter;
+  EXPECT_DEATH_IF_SUPPORTED(
+      ++iter, "operator.* called on default-constructed iterator.");
+  t.insert(0);
+  iter = t.begin();
+  t.erase(iter);
+  const char* const kErasedDeathMessage =
+      SwisstableGenerationsEnabled()
+          ? "operator.* called on invalid iterator.*was likely erased"
+          : "operator.* called on invalid iterator.*might have been "
+            "erased.*config=asan";
+  EXPECT_DEATH_IF_SUPPORTED(++iter, kErasedDeathMessage);
+}
+
+// Invalid iterator use can trigger heap-use-after-free in asan,
+// use-of-uninitialized-value in msan, or invalidated iterator assertions.
+constexpr const char* kInvalidIteratorDeathMessage =
+    "heap-use-after-free|use-of-uninitialized-value|invalidated "
+    "iterator|Invalid iterator";
+
+// MSVC doesn't support | in regex.
+#if defined(_MSC_VER)
+constexpr bool kMsvc = true;
+#else
+constexpr bool kMsvc = false;
+#endif
+
+TEST(TableDeathTest, IteratorInvalidAssertsEqualityOperator) {
+  if (!IsAssertEnabled() && !SwisstableGenerationsEnabled())
+    GTEST_SKIP() << "Assertions not enabled.";
+
+  IntTable t;
+  t.insert(1);
+  t.insert(2);
+  t.insert(3);
+  auto iter1 = t.begin();
+  auto iter2 = std::next(iter1);
+  ASSERT_NE(iter1, t.end());
+  ASSERT_NE(iter2, t.end());
+  t.erase(iter1);
+  // Extra simple "regexp" as regexp support is highly varied across platforms.
+  const char* const kErasedDeathMessage =
+      SwisstableGenerationsEnabled()
+          ? "Invalid iterator comparison.*was likely erased"
+          : "Invalid iterator comparison.*might have been erased.*config=asan";
+  EXPECT_DEATH_IF_SUPPORTED(void(iter1 == iter2), kErasedDeathMessage);
+  EXPECT_DEATH_IF_SUPPORTED(void(iter2 != iter1), kErasedDeathMessage);
+  t.erase(iter2);
+  EXPECT_DEATH_IF_SUPPORTED(void(iter1 == iter2), kErasedDeathMessage);
+
+  IntTable t1, t2;
+  t1.insert(0);
+  t2.insert(0);
+  iter1 = t1.begin();
+  iter2 = t2.begin();
+  const char* const kContainerDiffDeathMessage =
+      SwisstableGenerationsEnabled()
+          ? "Invalid iterator comparison.*iterators from different hashtables"
+          : "Invalid iterator comparison.*may be from different "
+            ".*containers.*config=asan";
+  EXPECT_DEATH_IF_SUPPORTED(void(iter1 == iter2), kContainerDiffDeathMessage);
+  EXPECT_DEATH_IF_SUPPORTED(void(iter2 == iter1), kContainerDiffDeathMessage);
+
+  for (int i = 0; i < 10; ++i) t1.insert(i);
+  // There should have been a rehash in t1.
+  if (kMsvc) return;  // MSVC doesn't support | in regex.
+  const char* const kRehashedDeathMessage =
+      SwisstableGenerationsEnabled()
+          ? kInvalidIteratorDeathMessage
+          : "Invalid iterator comparison.*might have rehashed.*config=asan";
+  EXPECT_DEATH_IF_SUPPORTED(void(iter1 == t1.begin()), kRehashedDeathMessage);
 }
 
 #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
@@ -2194,6 +2278,129 @@
   }
 }
 
+TEST(Iterator, InvalidUseCrashesWithSanitizers) {
+  if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
+  if (kMsvc) GTEST_SKIP() << "MSVC doesn't support | in regexp.";
+
+  IntTable t;
+  // Start with 1 element so that `it` is never an end iterator.
+  t.insert(-1);
+  for (int i = 0; i < 10; ++i) {
+    auto it = t.begin();
+    t.insert(i);
+    EXPECT_DEATH_IF_SUPPORTED(*it, kInvalidIteratorDeathMessage);
+    EXPECT_DEATH_IF_SUPPORTED(void(it == t.begin()),
+                              kInvalidIteratorDeathMessage);
+  }
+}
+
+TEST(Iterator, InvalidUseWithReserveCrashesWithSanitizers) {
+  if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
+  if (kMsvc) GTEST_SKIP() << "MSVC doesn't support | in regexp.";
+
+  IntTable t;
+  t.reserve(10);
+  t.insert(0);
+  auto it = t.begin();
+  // Reserved growth can't rehash.
+  for (int i = 1; i < 10; ++i) {
+    t.insert(i);
+    EXPECT_EQ(*it, 0);
+  }
+  // ptr will become invalidated on rehash.
+  const int64_t* ptr = &*it;
+  (void)ptr;
+
+  // erase decreases size but does not decrease reserved growth so the next
+  // insertion still invalidates iterators.
+  t.erase(0);
+  // The first insert after reserved growth is 0 is guaranteed to rehash when
+  // generations are enabled.
+  t.insert(10);
+  EXPECT_DEATH_IF_SUPPORTED(*it, kInvalidIteratorDeathMessage);
+  EXPECT_DEATH_IF_SUPPORTED(void(it == t.begin()),
+                            kInvalidIteratorDeathMessage);
+#ifdef ABSL_HAVE_ADDRESS_SANITIZER
+  EXPECT_DEATH_IF_SUPPORTED(std::cout << *ptr, "heap-use-after-free");
+#endif
+}
+
+TEST(Table, ReservedGrowthUpdatesWhenTableDoesntGrow) {
+  IntTable t;
+  for (int i = 0; i < 8; ++i) t.insert(i);
+  // Want to insert twice without invalidating iterators so reserve.
+  const size_t cap = t.capacity();
+  t.reserve(t.size() + 2);
+  // We want to be testing the case in which the reserve doesn't grow the table.
+  ASSERT_EQ(cap, t.capacity());
+  auto it = t.find(0);
+  t.insert(100);
+  t.insert(200);
+  // `it` shouldn't have been invalidated.
+  EXPECT_EQ(*it, 0);
+}
+
+TEST(Table, GenerationInfoResetsOnClear) {
+  if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
+  if (kMsvc) GTEST_SKIP() << "MSVC doesn't support | in regexp.";
+
+  IntTable t;
+  for (int i = 0; i < 1000; ++i) t.insert(i);
+  t.reserve(t.size() + 100);
+
+  t.clear();
+
+  t.insert(0);
+  auto it = t.begin();
+  t.insert(1);
+  EXPECT_DEATH_IF_SUPPORTED(*it, kInvalidIteratorDeathMessage);
+}
+
+TEST(Table, InvalidReferenceUseCrashesWithSanitizers) {
+  if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
+#ifdef ABSL_HAVE_MEMORY_SANITIZER
+  GTEST_SKIP() << "MSan fails to detect some of these rehashes.";
+#endif
+
+  IntTable t;
+  t.insert(0);
+  // Rehashing is guaranteed on every insertion while capacity is less than
+  // RehashProbabilityConstant().
+  int64_t i = 0;
+  while (t.capacity() <= RehashProbabilityConstant()) {
+    // ptr will become invalidated on rehash.
+    const int64_t* ptr = &*t.begin();
+    t.insert(++i);
+    EXPECT_DEATH_IF_SUPPORTED(std::cout << *ptr, "heap-use-after-free") << i;
+  }
+}
+
+TEST(Iterator, InvalidComparisonDifferentTables) {
+  if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
+
+  IntTable t1, t2;
+  IntTable::iterator default_constructed_iter;
+  // We randomly use one of N empty generations for generations from empty
+  // hashtables. In general, we won't always detect when iterators from
+  // different empty hashtables are compared, but in this test case, we
+  // should deterministically detect the error due to our randomness yielding
+  // consecutive random generations.
+  EXPECT_DEATH_IF_SUPPORTED(void(t1.end() == t2.end()),
+                            "Invalid iterator comparison.*empty hashtables");
+  EXPECT_DEATH_IF_SUPPORTED(void(t1.end() == default_constructed_iter),
+                            "Invalid iterator comparison.*default-constructed");
+  t1.insert(0);
+  EXPECT_DEATH_IF_SUPPORTED(void(t1.begin() == t2.end()),
+                            "Invalid iterator comparison.*empty hashtable");
+  EXPECT_DEATH_IF_SUPPORTED(void(t1.begin() == default_constructed_iter),
+                            "Invalid iterator comparison.*default-constructed");
+  t2.insert(0);
+  EXPECT_DEATH_IF_SUPPORTED(void(t1.begin() == t2.end()),
+                            "Invalid iterator comparison.*end.. iterator");
+  EXPECT_DEATH_IF_SUPPORTED(void(t1.begin() == t2.begin()),
+                            "Invalid iterator comparison.*non-end");
+}
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h
index 6868e63..dc8d7d9 100644
--- a/absl/container/node_hash_map.h
+++ b/absl/container/node_hash_map.h
@@ -404,7 +404,7 @@
   // for the past-the-end iterator, which is invalidated.
   //
   // `swap()` requires that the node hash map's hashing and key equivalence
-  // functions be Swappable, and are exchaged using unqualified calls to
+  // functions be Swappable, and are exchanged using unqualified calls to
   // non-member `swap()`. If the map's allocator has
   // `std::allocator_traits<allocator_type>::propagate_on_container_swap::value`
   // set to `true`, the allocators are also exchanged using an unqualified call
diff --git a/absl/container/node_hash_map_test.cc b/absl/container/node_hash_map_test.cc
index e941a83..9bcf470 100644
--- a/absl/container/node_hash_map_test.cc
+++ b/absl/container/node_hash_map_test.cc
@@ -272,6 +272,14 @@
 }
 #endif
 
+TEST(NodeHashMap, RecursiveTypeCompiles) {
+  struct RecursiveType {
+    node_hash_map<int, RecursiveType> m;
+  };
+  RecursiveType t;
+  t.m[0] = RecursiveType{};
+}
+
 }  // namespace
 }  // namespace container_internal
 ABSL_NAMESPACE_END
diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h
index f2cc70c..905a93d 100644
--- a/absl/container/node_hash_set.h
+++ b/absl/container/node_hash_set.h
@@ -334,7 +334,7 @@
   // for the past-the-end iterator, which is invalidated.
   //
   // `swap()` requires that the node hash set's hashing and key equivalence
-  // functions be Swappable, and are exchaged using unqualified calls to
+  // functions be Swappable, and are exchanged using unqualified calls to
   // non-member `swap()`. If the set's allocator has
   // `std::allocator_traits<allocator_type>::propagate_on_container_swap::value`
   // set to `true`, the allocators are also exchanged using an unqualified call
diff --git a/absl/copts/AbseilConfigureCopts.cmake b/absl/copts/AbseilConfigureCopts.cmake
index 73435e9..8209b26 100644
--- a/absl/copts/AbseilConfigureCopts.cmake
+++ b/absl/copts/AbseilConfigureCopts.cmake
@@ -67,26 +67,25 @@
     message(WARNING "Value of CMAKE_SIZEOF_VOID_P (${CMAKE_SIZEOF_VOID_P}) is not supported.")
   endif()
 else()
-  message(WARNING "Value of CMAKE_SYSTEM_PROCESSOR (${CMAKE_SYSTEM_PROCESSOR}) is unknown and cannot be used to set ABSL_RANDOM_RANDEN_COPTS")
   set(ABSL_RANDOM_RANDEN_COPTS "")
 endif()
 
 
 if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
   set(ABSL_DEFAULT_COPTS "${ABSL_GCC_FLAGS}")
-  set(ABSL_TEST_COPTS "${ABSL_GCC_FLAGS};${ABSL_GCC_TEST_FLAGS}")
+  set(ABSL_TEST_COPTS "${ABSL_GCC_TEST_FLAGS}")
 elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")  # MATCHES so we get both Clang and AppleClang
   if(MSVC)
     # clang-cl is half MSVC, half LLVM
     set(ABSL_DEFAULT_COPTS "${ABSL_CLANG_CL_FLAGS}")
-    set(ABSL_TEST_COPTS "${ABSL_CLANG_CL_FLAGS};${ABSL_CLANG_CL_TEST_FLAGS}")
+    set(ABSL_TEST_COPTS "${ABSL_CLANG_CL_TEST_FLAGS}")
   else()
     set(ABSL_DEFAULT_COPTS "${ABSL_LLVM_FLAGS}")
-    set(ABSL_TEST_COPTS "${ABSL_LLVM_FLAGS};${ABSL_LLVM_TEST_FLAGS}")
+    set(ABSL_TEST_COPTS "${ABSL_LLVM_TEST_FLAGS}")
   endif()
 elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
   set(ABSL_DEFAULT_COPTS "${ABSL_MSVC_FLAGS}")
-  set(ABSL_TEST_COPTS "${ABSL_MSVC_FLAGS};${ABSL_MSVC_TEST_FLAGS}")
+  set(ABSL_TEST_COPTS "${ABSL_MSVC_TEST_FLAGS}")
   set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}")
 else()
   message(WARNING "Unknown compiler: ${CMAKE_CXX_COMPILER}.  Building with no default flags")
diff --git a/absl/copts/GENERATED_AbseilCopts.cmake b/absl/copts/GENERATED_AbseilCopts.cmake
index a4ab1aa..430916f 100644
--- a/absl/copts/GENERATED_AbseilCopts.cmake
+++ b/absl/copts/GENERATED_AbseilCopts.cmake
@@ -13,22 +13,27 @@
 )
 
 list(APPEND ABSL_CLANG_CL_TEST_FLAGS
-    "-Wno-c99-extensions"
+    "/W3"
+    "/DNOMINMAX"
+    "/DWIN32_LEAN_AND_MEAN"
+    "/D_CRT_SECURE_NO_WARNINGS"
+    "/D_SCL_SECURE_NO_WARNINGS"
+    "/D_ENABLE_EXTENDED_ALIGNED_STORAGE"
     "-Wno-deprecated-declarations"
-    "-Wno-missing-noreturn"
+    "-Wno-implicit-int-conversion"
     "-Wno-missing-prototypes"
     "-Wno-missing-variable-declarations"
-    "-Wno-null-conversion"
     "-Wno-shadow"
-    "-Wno-shift-sign-overflow"
+    "-Wno-shorten-64-to-32"
     "-Wno-sign-compare"
+    "-Wno-sign-conversion"
+    "-Wno-unreachable-code-loop-increment"
     "-Wno-unused-function"
     "-Wno-unused-member-function"
     "-Wno-unused-parameter"
     "-Wno-unused-private-field"
     "-Wno-unused-template"
     "-Wno-used-but-marked-unused"
-    "-Wno-zero-as-null-pointer-constant"
     "-Wno-gnu-zero-variadic-macro-arguments"
 )
 
@@ -51,9 +56,23 @@
 )
 
 list(APPEND ABSL_GCC_TEST_FLAGS
-    "-Wno-conversion-null"
+    "-Wall"
+    "-Wextra"
+    "-Wcast-qual"
+    "-Wconversion-null"
+    "-Wformat-security"
+    "-Woverlength-strings"
+    "-Wpointer-arith"
+    "-Wundef"
+    "-Wunused-local-typedefs"
+    "-Wunused-result"
+    "-Wvarargs"
+    "-Wvla"
+    "-Wwrite-strings"
+    "-DNOMINMAX"
     "-Wno-deprecated-declarations"
     "-Wno-missing-declarations"
+    "-Wno-self-move"
     "-Wno-sign-compare"
     "-Wno-unused-function"
     "-Wno-unused-parameter"
@@ -78,8 +97,11 @@
     "-Wpointer-arith"
     "-Wself-assign"
     "-Wshadow-all"
+    "-Wshorten-64-to-32"
+    "-Wsign-conversion"
     "-Wstring-conversion"
     "-Wtautological-overlap-compare"
+    "-Wtautological-unsigned-zero-compare"
     "-Wundef"
     "-Wuninitialized"
     "-Wunreachable-code"
@@ -91,40 +113,64 @@
     "-Wno-float-conversion"
     "-Wno-implicit-float-conversion"
     "-Wno-implicit-int-float-conversion"
-    "-Wno-implicit-int-conversion"
-    "-Wno-shorten-64-to-32"
-    "-Wno-sign-conversion"
     "-Wno-unknown-warning-option"
     "-DNOMINMAX"
 )
 
 list(APPEND ABSL_LLVM_TEST_FLAGS
-    "-Wno-c99-extensions"
+    "-Wall"
+    "-Wextra"
+    "-Wcast-qual"
+    "-Wconversion"
+    "-Wfloat-overflow-conversion"
+    "-Wfloat-zero-conversion"
+    "-Wfor-loop-analysis"
+    "-Wformat-security"
+    "-Wgnu-redeclared-enum"
+    "-Winfinite-recursion"
+    "-Winvalid-constexpr"
+    "-Wliteral-conversion"
+    "-Wmissing-declarations"
+    "-Woverlength-strings"
+    "-Wpointer-arith"
+    "-Wself-assign"
+    "-Wshadow-all"
+    "-Wstring-conversion"
+    "-Wtautological-overlap-compare"
+    "-Wtautological-unsigned-zero-compare"
+    "-Wundef"
+    "-Wuninitialized"
+    "-Wunreachable-code"
+    "-Wunused-comparison"
+    "-Wunused-local-typedefs"
+    "-Wunused-result"
+    "-Wvla"
+    "-Wwrite-strings"
+    "-Wno-float-conversion"
+    "-Wno-implicit-float-conversion"
+    "-Wno-implicit-int-float-conversion"
+    "-Wno-unknown-warning-option"
+    "-DNOMINMAX"
     "-Wno-deprecated-declarations"
-    "-Wno-missing-noreturn"
+    "-Wno-implicit-int-conversion"
     "-Wno-missing-prototypes"
     "-Wno-missing-variable-declarations"
-    "-Wno-null-conversion"
     "-Wno-shadow"
-    "-Wno-shift-sign-overflow"
+    "-Wno-shorten-64-to-32"
     "-Wno-sign-compare"
+    "-Wno-sign-conversion"
+    "-Wno-unreachable-code-loop-increment"
     "-Wno-unused-function"
     "-Wno-unused-member-function"
     "-Wno-unused-parameter"
     "-Wno-unused-private-field"
     "-Wno-unused-template"
     "-Wno-used-but-marked-unused"
-    "-Wno-zero-as-null-pointer-constant"
     "-Wno-gnu-zero-variadic-macro-arguments"
 )
 
 list(APPEND ABSL_MSVC_FLAGS
     "/W3"
-    "/DNOMINMAX"
-    "/DWIN32_LEAN_AND_MEAN"
-    "/D_CRT_SECURE_NO_WARNINGS"
-    "/D_SCL_SECURE_NO_WARNINGS"
-    "/D_ENABLE_EXTENDED_ALIGNED_STORAGE"
     "/bigobj"
     "/wd4005"
     "/wd4068"
@@ -133,6 +179,11 @@
     "/wd4267"
     "/wd4503"
     "/wd4800"
+    "/DNOMINMAX"
+    "/DWIN32_LEAN_AND_MEAN"
+    "/D_CRT_SECURE_NO_WARNINGS"
+    "/D_SCL_SECURE_NO_WARNINGS"
+    "/D_ENABLE_EXTENDED_ALIGNED_STORAGE"
 )
 
 list(APPEND ABSL_MSVC_LINKOPTS
@@ -140,6 +191,20 @@
 )
 
 list(APPEND ABSL_MSVC_TEST_FLAGS
+    "/W3"
+    "/bigobj"
+    "/wd4005"
+    "/wd4068"
+    "/wd4180"
+    "/wd4244"
+    "/wd4267"
+    "/wd4503"
+    "/wd4800"
+    "/DNOMINMAX"
+    "/DWIN32_LEAN_AND_MEAN"
+    "/D_CRT_SECURE_NO_WARNINGS"
+    "/D_SCL_SECURE_NO_WARNINGS"
+    "/D_ENABLE_EXTENDED_ALIGNED_STORAGE"
     "/wd4018"
     "/wd4101"
     "/wd4503"
diff --git a/absl/copts/GENERATED_copts.bzl b/absl/copts/GENERATED_copts.bzl
index a6efc98..011d8a9 100644
--- a/absl/copts/GENERATED_copts.bzl
+++ b/absl/copts/GENERATED_copts.bzl
@@ -14,22 +14,27 @@
 ]
 
 ABSL_CLANG_CL_TEST_FLAGS = [
-    "-Wno-c99-extensions",
+    "/W3",
+    "/DNOMINMAX",
+    "/DWIN32_LEAN_AND_MEAN",
+    "/D_CRT_SECURE_NO_WARNINGS",
+    "/D_SCL_SECURE_NO_WARNINGS",
+    "/D_ENABLE_EXTENDED_ALIGNED_STORAGE",
     "-Wno-deprecated-declarations",
-    "-Wno-missing-noreturn",
+    "-Wno-implicit-int-conversion",
     "-Wno-missing-prototypes",
     "-Wno-missing-variable-declarations",
-    "-Wno-null-conversion",
     "-Wno-shadow",
-    "-Wno-shift-sign-overflow",
+    "-Wno-shorten-64-to-32",
     "-Wno-sign-compare",
+    "-Wno-sign-conversion",
+    "-Wno-unreachable-code-loop-increment",
     "-Wno-unused-function",
     "-Wno-unused-member-function",
     "-Wno-unused-parameter",
     "-Wno-unused-private-field",
     "-Wno-unused-template",
     "-Wno-used-but-marked-unused",
-    "-Wno-zero-as-null-pointer-constant",
     "-Wno-gnu-zero-variadic-macro-arguments",
 ]
 
@@ -52,9 +57,23 @@
 ]
 
 ABSL_GCC_TEST_FLAGS = [
-    "-Wno-conversion-null",
+    "-Wall",
+    "-Wextra",
+    "-Wcast-qual",
+    "-Wconversion-null",
+    "-Wformat-security",
+    "-Woverlength-strings",
+    "-Wpointer-arith",
+    "-Wundef",
+    "-Wunused-local-typedefs",
+    "-Wunused-result",
+    "-Wvarargs",
+    "-Wvla",
+    "-Wwrite-strings",
+    "-DNOMINMAX",
     "-Wno-deprecated-declarations",
     "-Wno-missing-declarations",
+    "-Wno-self-move",
     "-Wno-sign-compare",
     "-Wno-unused-function",
     "-Wno-unused-parameter",
@@ -79,8 +98,11 @@
     "-Wpointer-arith",
     "-Wself-assign",
     "-Wshadow-all",
+    "-Wshorten-64-to-32",
+    "-Wsign-conversion",
     "-Wstring-conversion",
     "-Wtautological-overlap-compare",
+    "-Wtautological-unsigned-zero-compare",
     "-Wundef",
     "-Wuninitialized",
     "-Wunreachable-code",
@@ -92,40 +114,64 @@
     "-Wno-float-conversion",
     "-Wno-implicit-float-conversion",
     "-Wno-implicit-int-float-conversion",
-    "-Wno-implicit-int-conversion",
-    "-Wno-shorten-64-to-32",
-    "-Wno-sign-conversion",
     "-Wno-unknown-warning-option",
     "-DNOMINMAX",
 ]
 
 ABSL_LLVM_TEST_FLAGS = [
-    "-Wno-c99-extensions",
+    "-Wall",
+    "-Wextra",
+    "-Wcast-qual",
+    "-Wconversion",
+    "-Wfloat-overflow-conversion",
+    "-Wfloat-zero-conversion",
+    "-Wfor-loop-analysis",
+    "-Wformat-security",
+    "-Wgnu-redeclared-enum",
+    "-Winfinite-recursion",
+    "-Winvalid-constexpr",
+    "-Wliteral-conversion",
+    "-Wmissing-declarations",
+    "-Woverlength-strings",
+    "-Wpointer-arith",
+    "-Wself-assign",
+    "-Wshadow-all",
+    "-Wstring-conversion",
+    "-Wtautological-overlap-compare",
+    "-Wtautological-unsigned-zero-compare",
+    "-Wundef",
+    "-Wuninitialized",
+    "-Wunreachable-code",
+    "-Wunused-comparison",
+    "-Wunused-local-typedefs",
+    "-Wunused-result",
+    "-Wvla",
+    "-Wwrite-strings",
+    "-Wno-float-conversion",
+    "-Wno-implicit-float-conversion",
+    "-Wno-implicit-int-float-conversion",
+    "-Wno-unknown-warning-option",
+    "-DNOMINMAX",
     "-Wno-deprecated-declarations",
-    "-Wno-missing-noreturn",
+    "-Wno-implicit-int-conversion",
     "-Wno-missing-prototypes",
     "-Wno-missing-variable-declarations",
-    "-Wno-null-conversion",
     "-Wno-shadow",
-    "-Wno-shift-sign-overflow",
+    "-Wno-shorten-64-to-32",
     "-Wno-sign-compare",
+    "-Wno-sign-conversion",
+    "-Wno-unreachable-code-loop-increment",
     "-Wno-unused-function",
     "-Wno-unused-member-function",
     "-Wno-unused-parameter",
     "-Wno-unused-private-field",
     "-Wno-unused-template",
     "-Wno-used-but-marked-unused",
-    "-Wno-zero-as-null-pointer-constant",
     "-Wno-gnu-zero-variadic-macro-arguments",
 ]
 
 ABSL_MSVC_FLAGS = [
     "/W3",
-    "/DNOMINMAX",
-    "/DWIN32_LEAN_AND_MEAN",
-    "/D_CRT_SECURE_NO_WARNINGS",
-    "/D_SCL_SECURE_NO_WARNINGS",
-    "/D_ENABLE_EXTENDED_ALIGNED_STORAGE",
     "/bigobj",
     "/wd4005",
     "/wd4068",
@@ -134,6 +180,11 @@
     "/wd4267",
     "/wd4503",
     "/wd4800",
+    "/DNOMINMAX",
+    "/DWIN32_LEAN_AND_MEAN",
+    "/D_CRT_SECURE_NO_WARNINGS",
+    "/D_SCL_SECURE_NO_WARNINGS",
+    "/D_ENABLE_EXTENDED_ALIGNED_STORAGE",
 ]
 
 ABSL_MSVC_LINKOPTS = [
@@ -141,6 +192,20 @@
 ]
 
 ABSL_MSVC_TEST_FLAGS = [
+    "/W3",
+    "/bigobj",
+    "/wd4005",
+    "/wd4068",
+    "/wd4180",
+    "/wd4244",
+    "/wd4267",
+    "/wd4503",
+    "/wd4800",
+    "/DNOMINMAX",
+    "/DWIN32_LEAN_AND_MEAN",
+    "/D_CRT_SECURE_NO_WARNINGS",
+    "/D_SCL_SECURE_NO_WARNINGS",
+    "/D_ENABLE_EXTENDED_ALIGNED_STORAGE",
     "/wd4018",
     "/wd4101",
     "/wd4503",
diff --git a/absl/copts/configure_copts.bzl b/absl/copts/configure_copts.bzl
index c5e57b3..ca5f26d 100644
--- a/absl/copts/configure_copts.bzl
+++ b/absl/copts/configure_copts.bzl
@@ -29,7 +29,7 @@
     "//conditions:default": ABSL_GCC_FLAGS,
 })
 
-ABSL_TEST_COPTS = ABSL_DEFAULT_COPTS + select({
+ABSL_TEST_COPTS = select({
     "//absl:msvc_compiler": ABSL_MSVC_TEST_FLAGS,
     "//absl:clang-cl_compiler": ABSL_CLANG_CL_TEST_FLAGS,
     "//absl:clang_compiler": ABSL_LLVM_TEST_FLAGS,
diff --git a/absl/copts/copts.py b/absl/copts/copts.py
index 0d6c1ec..e6e1194 100644
--- a/absl/copts/copts.py
+++ b/absl/copts/copts.py
@@ -11,32 +11,120 @@
 AbseilConfigureCopts.cmake.
 """
 
-# /Wall with msvc includes unhelpful warnings such as C4711, C4710, ...
-MSVC_BIG_WARNING_FLAGS = [
-    "/W3",
+ABSL_GCC_FLAGS = [
+    "-Wall",
+    "-Wextra",
+    "-Wcast-qual",
+    "-Wconversion-null",
+    "-Wformat-security",
+    "-Wmissing-declarations",
+    "-Woverlength-strings",
+    "-Wpointer-arith",
+    "-Wundef",
+    "-Wunused-local-typedefs",
+    "-Wunused-result",
+    "-Wvarargs",
+    "-Wvla",  # variable-length array
+    "-Wwrite-strings",
+    # Don't define min and max macros (Build on Windows using gcc)
+    "-DNOMINMAX",
 ]
 
-LLVM_TEST_DISABLE_WARNINGS_FLAGS = [
-    "-Wno-c99-extensions",
+ABSL_GCC_TEST_ADDITIONAL_FLAGS = [
     "-Wno-deprecated-declarations",
-    "-Wno-missing-noreturn",
+    "-Wno-missing-declarations",
+    "-Wno-self-move",
+    "-Wno-sign-compare",
+    "-Wno-unused-function",
+    "-Wno-unused-parameter",
+    "-Wno-unused-private-field",
+]
+
+ABSL_LLVM_FLAGS = [
+    "-Wall",
+    "-Wextra",
+    "-Wcast-qual",
+    "-Wconversion",
+    "-Wfloat-overflow-conversion",
+    "-Wfloat-zero-conversion",
+    "-Wfor-loop-analysis",
+    "-Wformat-security",
+    "-Wgnu-redeclared-enum",
+    "-Winfinite-recursion",
+    "-Winvalid-constexpr",
+    "-Wliteral-conversion",
+    "-Wmissing-declarations",
+    "-Woverlength-strings",
+    "-Wpointer-arith",
+    "-Wself-assign",
+    "-Wshadow-all",
+    "-Wshorten-64-to-32",
+    "-Wsign-conversion",
+    "-Wstring-conversion",
+    "-Wtautological-overlap-compare",
+    "-Wtautological-unsigned-zero-compare",
+    "-Wundef",
+    "-Wuninitialized",
+    "-Wunreachable-code",
+    "-Wunused-comparison",
+    "-Wunused-local-typedefs",
+    "-Wunused-result",
+    "-Wvla",
+    "-Wwrite-strings",
+    # Warnings that are enabled by group warning flags like -Wall that we
+    # explicitly disable.
+    "-Wno-float-conversion",
+    "-Wno-implicit-float-conversion",
+    "-Wno-implicit-int-float-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",
+]
+
+ABSL_LLVM_TEST_ADDITIONAL_FLAGS = [
+    "-Wno-deprecated-declarations",
+    "-Wno-implicit-int-conversion",
     "-Wno-missing-prototypes",
     "-Wno-missing-variable-declarations",
-    "-Wno-null-conversion",
     "-Wno-shadow",
-    "-Wno-shift-sign-overflow",
+    "-Wno-shorten-64-to-32",
     "-Wno-sign-compare",
+    "-Wno-sign-conversion",
+    "-Wno-unreachable-code-loop-increment",
     "-Wno-unused-function",
     "-Wno-unused-member-function",
     "-Wno-unused-parameter",
     "-Wno-unused-private-field",
     "-Wno-unused-template",
     "-Wno-used-but-marked-unused",
-    "-Wno-zero-as-null-pointer-constant",
     # gtest depends on this GNU extension being offered.
     "-Wno-gnu-zero-variadic-macro-arguments",
 ]
 
+# /Wall with msvc includes unhelpful warnings such as C4711, C4710, ...
+MSVC_BIG_WARNING_FLAGS = [
+    "/W3",
+]
+
+MSVC_WARNING_FLAGS = [
+    # Increase the number of sections available in object files
+    "/bigobj",
+    "/wd4005",  # macro-redefinition
+    "/wd4068",  # unknown pragma
+    # qualifier applied to function type has no meaning; ignored
+    "/wd4180",
+    # conversion from 'type1' to 'type2', possible loss of data
+    "/wd4244",
+    # conversion from 'size_t' to 'type', possible loss of data
+    "/wd4267",
+    # The decorated name was longer than the compiler limit
+    "/wd4503",
+    # forcing value to bool 'true' or 'false' (performance warning)
+    "/wd4800",
+]
+
 MSVC_DEFINES = [
     "/DNOMINMAX",  # Don't define min and max macros (windows.h)
     # Don't bloat namespace with incompatible winsock versions.
@@ -48,106 +136,43 @@
     "/D_ENABLE_EXTENDED_ALIGNED_STORAGE",
 ]
 
+
+def GccStyleFilterAndCombine(default_flags, test_flags):
+  """Merges default_flags and test_flags for GCC and LLVM.
+
+  Args:
+    default_flags: A list of default compiler flags
+    test_flags: A list of flags that are only used in tests
+
+  Returns:
+    A combined list of default_flags and test_flags, but with all flags of the
+    form '-Wwarning' removed if test_flags contains a flag of the form
+    '-Wno-warning'
+  """
+  remove = set(["-W" + f[5:] for f in test_flags if f[:5] == "-Wno-"])
+  return [f for f in default_flags if f not in remove] + test_flags
+
 COPT_VARS = {
-    "ABSL_GCC_FLAGS": [
-        "-Wall",
-        "-Wextra",
-        "-Wcast-qual",
-        "-Wconversion-null",
-        "-Wformat-security",
-        "-Wmissing-declarations",
-        "-Woverlength-strings",
-        "-Wpointer-arith",
-        "-Wundef",
-        "-Wunused-local-typedefs",
-        "-Wunused-result",
-        "-Wvarargs",
-        "-Wvla",  # variable-length array
-        "-Wwrite-strings",
-        # Don't define min and max macros (Build on Windows using gcc)
-        "-DNOMINMAX",
-    ],
-    "ABSL_GCC_TEST_FLAGS": [
-        "-Wno-conversion-null",
-        "-Wno-deprecated-declarations",
-        "-Wno-missing-declarations",
-        "-Wno-sign-compare",
-        "-Wno-unused-function",
-        "-Wno-unused-parameter",
-        "-Wno-unused-private-field",
-    ],
-    "ABSL_LLVM_FLAGS": [
-        "-Wall",
-        "-Wextra",
-        "-Wcast-qual",
-        "-Wconversion",
-        "-Wfloat-overflow-conversion",
-        "-Wfloat-zero-conversion",
-        "-Wfor-loop-analysis",
-        "-Wformat-security",
-        "-Wgnu-redeclared-enum",
-        "-Winfinite-recursion",
-        "-Winvalid-constexpr",
-        "-Wliteral-conversion",
-        "-Wmissing-declarations",
-        "-Woverlength-strings",
-        "-Wpointer-arith",
-        "-Wself-assign",
-        "-Wshadow-all",
-        "-Wstring-conversion",
-        "-Wtautological-overlap-compare",
-        "-Wundef",
-        "-Wuninitialized",
-        "-Wunreachable-code",
-        "-Wunused-comparison",
-        "-Wunused-local-typedefs",
-        "-Wunused-result",
-        "-Wvla",
-        "-Wwrite-strings",
-        # Warnings that are enabled by group warning flags like -Wall that we
-        # explicitly disable.
-        "-Wno-float-conversion",
-        "-Wno-implicit-float-conversion",
-        "-Wno-implicit-int-float-conversion",
-        "-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",
-    ],
-    "ABSL_LLVM_TEST_FLAGS":
-        LLVM_TEST_DISABLE_WARNINGS_FLAGS,
+    "ABSL_GCC_FLAGS": ABSL_GCC_FLAGS,
+    "ABSL_GCC_TEST_FLAGS": GccStyleFilterAndCombine(
+        ABSL_GCC_FLAGS, ABSL_GCC_TEST_ADDITIONAL_FLAGS),
+    "ABSL_LLVM_FLAGS": ABSL_LLVM_FLAGS,
+    "ABSL_LLVM_TEST_FLAGS": GccStyleFilterAndCombine(
+        ABSL_LLVM_FLAGS, ABSL_LLVM_TEST_ADDITIONAL_FLAGS),
     "ABSL_CLANG_CL_FLAGS":
-        (MSVC_BIG_WARNING_FLAGS + MSVC_DEFINES),
+        MSVC_BIG_WARNING_FLAGS + MSVC_DEFINES,
     "ABSL_CLANG_CL_TEST_FLAGS":
-        LLVM_TEST_DISABLE_WARNINGS_FLAGS,
+        MSVC_BIG_WARNING_FLAGS + MSVC_DEFINES + ABSL_LLVM_TEST_ADDITIONAL_FLAGS,
     "ABSL_MSVC_FLAGS":
-        MSVC_BIG_WARNING_FLAGS + MSVC_DEFINES + [
-            # Increase the number of sections available in object files
-            "/bigobj",
-            "/wd4005",  # macro-redefinition
-            "/wd4068",  # unknown pragma
-            # qualifier applied to function type has no meaning; ignored
-            "/wd4180",
-            # conversion from 'type1' to 'type2', possible loss of data
-            "/wd4244",
-            # conversion from 'size_t' to 'type', possible loss of data
-            "/wd4267",
-            # The decorated name was longer than the compiler limit
-            "/wd4503",
-            # forcing value to bool 'true' or 'false' (performance warning)
-            "/wd4800",
+        MSVC_BIG_WARNING_FLAGS + MSVC_WARNING_FLAGS + MSVC_DEFINES,
+    "ABSL_MSVC_TEST_FLAGS":
+        MSVC_BIG_WARNING_FLAGS + MSVC_WARNING_FLAGS + MSVC_DEFINES + [
+            "/wd4018",  # signed/unsigned mismatch
+            "/wd4101",  # unreferenced local variable
+            "/wd4503",  # decorated name length exceeded, name was truncated
+            "/wd4996",  # use of deprecated symbol
+            "/DNOMINMAX",  # disable the min() and max() macros from <windows.h>
         ],
-    "ABSL_MSVC_TEST_FLAGS": [
-        "/wd4018",  # signed/unsigned mismatch
-        "/wd4101",  # unreferenced local variable
-        "/wd4503",  # decorated name length exceeded, name was truncated
-        "/wd4996",  # use of deprecated symbol
-        "/DNOMINMAX",  # disable the min() and max() macros from <windows.h>
-    ],
     "ABSL_MSVC_LINKOPTS": [
         # Object file doesn't export any previously undefined symbols
         "-ignore:4221",
diff --git a/absl/crc/BUILD.bazel b/absl/crc/BUILD.bazel
new file mode 100644
index 0000000..1c58f46
--- /dev/null
+++ b/absl/crc/BUILD.bazel
@@ -0,0 +1,210 @@
+# Copyright 2022 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 = "cpu_detect",
+    srcs = [
+        "internal/cpu_detect.cc",
+    ],
+    hdrs = ["internal/cpu_detect.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        "//absl/base",
+        "//absl/base:config",
+    ],
+)
+
+cc_library(
+    name = "crc_internal",
+    srcs = [
+        "internal/crc.cc",
+        "internal/crc_internal.h",
+        "internal/crc_x86_arm_combined.cc",
+    ],
+    hdrs = [
+        "internal/crc.h",
+        "internal/crc32_x86_arm_combined_simd.h",
+    ],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":cpu_detect",
+        "//absl/base",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/base:dynamic_annotations",
+        "//absl/base:endian",
+        "//absl/base:prefetch",
+        "//absl/base:raw_logging_internal",
+        "//absl/memory",
+        "//absl/numeric:bits",
+    ],
+)
+
+cc_library(
+    name = "crc32c",
+    srcs = [
+        "crc32c.cc",
+        "internal/crc32c_inline.h",
+        "internal/crc_memcpy_fallback.cc",
+        "internal/crc_memcpy_x86_64.cc",
+        "internal/crc_non_temporal_memcpy.cc",
+    ],
+    hdrs = [
+        "crc32c.h",
+        "internal/crc32c.h",
+        "internal/crc_memcpy.h",
+    ],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":cpu_detect",
+        ":crc_internal",
+        ":non_temporal_memcpy",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/base:dynamic_annotations",
+        "//absl/base:endian",
+        "//absl/base:prefetch",
+        "//absl/strings",
+    ],
+)
+
+cc_test(
+    name = "crc32c_test",
+    srcs = ["crc32c_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":crc32c",
+        "//absl/strings",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "non_temporal_arm_intrinsics",
+    hdrs = ["internal/non_temporal_arm_intrinsics.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        "//absl/base:config",
+    ],
+)
+
+cc_library(
+    name = "non_temporal_memcpy",
+    hdrs = ["internal/non_temporal_memcpy.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":non_temporal_arm_intrinsics",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+    ],
+)
+
+cc_test(
+    name = "crc_memcpy_test",
+    size = "large",
+    srcs = ["internal/crc_memcpy_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    shard_count = 3,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":crc32c",
+        "//absl/memory",
+        "//absl/random",
+        "//absl/random:distributions",
+        "//absl/strings",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "non_temporal_memcpy_test",
+    srcs = ["internal/non_temporal_memcpy_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":non_temporal_memcpy",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "crc_cord_state",
+    srcs = ["internal/crc_cord_state.cc"],
+    hdrs = ["internal/crc_cord_state.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = ["//absl/strings:__pkg__"],
+    deps = [
+        ":crc32c",
+        "//absl/base:config",
+        "//absl/numeric:bits",
+        "//absl/strings",
+    ],
+)
+
+cc_test(
+    name = "crc_cord_state_test",
+    srcs = ["internal/crc_cord_state_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":crc32c",
+        ":crc_cord_state",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_binary(
+    name = "crc32c_benchmark",
+    testonly = 1,
+    srcs = ["crc32c_benchmark.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    tags = [
+        "benchmark",
+    ],
+    visibility = ["//visibility:private"],
+    deps = [
+        ":crc32c",
+        "//absl/memory",
+        "//absl/strings",
+        "@com_github_google_benchmark//:benchmark_main",
+    ],
+)
diff --git a/absl/crc/CMakeLists.txt b/absl/crc/CMakeLists.txt
new file mode 100644
index 0000000..72ea209
--- /dev/null
+++ b/absl/crc/CMakeLists.txt
@@ -0,0 +1,176 @@
+# Copyright 2022 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.
+
+# Internal-only target, do not depend on directly.
+absl_cc_library(
+  NAME
+    crc_cpu_detect
+  HDRS
+    "internal/cpu_detect.h"
+  SRCS
+    "internal/cpu_detect.cc"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::base
+    absl::config
+)
+
+# Internal-only target, do not depend on directly.
+absl_cc_library(
+  NAME
+    crc_internal
+  HDRS
+    "internal/crc.h"
+    "internal/crc32_x86_arm_combined_simd.h"
+  SRCS
+    "internal/crc.cc"
+    "internal/crc_internal.h"
+    "internal/crc_x86_arm_combined.cc"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::crc_cpu_detect
+    absl::base
+    absl::config
+    absl::core_headers
+    absl::dynamic_annotations
+    absl::endian
+    absl::prefetch
+    absl::raw_logging_internal
+    absl::memory
+    absl::bits
+)
+
+absl_cc_library(
+  NAME
+    crc32c
+  HDRS
+    "crc32c.h"
+    "internal/crc32c.h"
+    "internal/crc_memcpy.h"
+  SRCS
+    "crc32c.cc"
+    "internal/crc32c_inline.h"
+    "internal/crc_memcpy_fallback.cc"
+    "internal/crc_memcpy_x86_64.cc"
+    "internal/crc_non_temporal_memcpy.cc"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::crc_cpu_detect
+    absl::crc_internal
+    absl::non_temporal_memcpy
+    absl::config
+    absl::core_headers
+    absl::dynamic_annotations
+    absl::endian
+    absl::prefetch
+    absl::strings
+)
+
+absl_cc_test(
+  NAME
+    crc32c_test
+  SRCS
+    "crc32c_test.cc"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::crc32c
+    absl::strings
+    GTest::gtest_main
+)
+
+# Internal-only target, do not depend on directly.
+absl_cc_library(
+  NAME
+    non_temporal_arm_intrinsics
+  HDRS
+    "internal/non_temporal_arm_intrinsics.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::config
+)
+
+# Internal-only target, do not depend on directly.
+absl_cc_library(
+  NAME
+    non_temporal_memcpy
+  HDRS
+    "internal/non_temporal_memcpy.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::non_temporal_arm_intrinsics
+    absl::config
+    absl::core_headers
+)
+
+absl_cc_test(
+  NAME
+    crc_memcpy_test
+  SRCS
+    "internal/crc_memcpy_test.cc"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::crc32c
+    absl::memory
+    absl::random_random
+    absl::random_distributions
+    absl::strings
+    GTest::gtest_main
+)
+
+absl_cc_test(
+  NAME
+    non_temporal_memcpy_test
+  SRCS
+    "internal/non_temporal_memcpy_test.cc"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::non_temporal_memcpy
+    GTest::gtest_main
+)
+
+absl_cc_library(
+  NAME
+    crc_cord_state
+  HDRS
+    "internal/crc_cord_state.h"
+  SRCS
+    "internal/crc_cord_state.cc"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::crc32c
+    absl::config
+    absl::strings
+)
+
+absl_cc_test(
+  NAME
+    crc_cord_state_test
+  SRCS
+    "internal/crc_cord_state_test.cc"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::crc_cord_state
+    absl::crc32c
+    GTest::gtest_main
+)
diff --git a/absl/crc/crc32c.cc b/absl/crc/crc32c.cc
new file mode 100644
index 0000000..468c1b3
--- /dev/null
+++ b/absl/crc/crc32c.cc
@@ -0,0 +1,99 @@
+// Copyright 2022 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/crc/crc32c.h"
+
+#include <cstdint>
+
+#include "absl/crc/internal/crc.h"
+#include "absl/crc/internal/crc32c.h"
+#include "absl/crc/internal/crc_memcpy.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+namespace {
+
+const crc_internal::CRC* CrcEngine() {
+  static const crc_internal::CRC* engine = crc_internal::CRC::Crc32c();
+  return engine;
+}
+
+constexpr uint32_t kCRC32Xor = 0xffffffffU;
+
+}  // namespace
+
+namespace crc_internal {
+
+crc32c_t UnextendCrc32cByZeroes(crc32c_t initial_crc, size_t length) {
+  uint32_t crc = static_cast<uint32_t>(initial_crc) ^ kCRC32Xor;
+  CrcEngine()->UnextendByZeroes(&crc, length);
+  return static_cast<crc32c_t>(crc ^ kCRC32Xor);
+}
+
+// Called by `absl::ExtendCrc32c()` on strings with size > 64 or when hardware
+// CRC32C support is missing.
+crc32c_t ExtendCrc32cInternal(crc32c_t initial_crc,
+                              absl::string_view buf_to_add) {
+  uint32_t crc = static_cast<uint32_t>(initial_crc) ^ kCRC32Xor;
+  CrcEngine()->Extend(&crc, buf_to_add.data(), buf_to_add.size());
+  return static_cast<crc32c_t>(crc ^ kCRC32Xor);
+}
+
+}  // namespace crc_internal
+
+crc32c_t ComputeCrc32c(absl::string_view buf) {
+  return ExtendCrc32c(crc32c_t{0}, buf);
+}
+
+crc32c_t ExtendCrc32cByZeroes(crc32c_t initial_crc, size_t length) {
+  uint32_t crc = static_cast<uint32_t>(initial_crc) ^ kCRC32Xor;
+  CrcEngine()->ExtendByZeroes(&crc, length);
+  return static_cast<crc32c_t>(crc ^ kCRC32Xor);
+}
+
+crc32c_t ConcatCrc32c(crc32c_t lhs_crc, crc32c_t rhs_crc, size_t rhs_len) {
+  uint32_t result = static_cast<uint32_t>(lhs_crc);
+  CrcEngine()->ExtendByZeroes(&result, rhs_len);
+  return crc32c_t{result ^ static_cast<uint32_t>(rhs_crc)};
+}
+
+crc32c_t RemoveCrc32cPrefix(crc32c_t crc_a, crc32c_t crc_ab, size_t length_b) {
+  return ConcatCrc32c(crc_a, crc_ab, length_b);
+}
+
+crc32c_t MemcpyCrc32c(void* dest, const void* src, size_t count,
+                      crc32c_t initial_crc) {
+  return static_cast<crc32c_t>(
+      crc_internal::Crc32CAndCopy(dest, src, count, initial_crc, false));
+}
+
+// Remove a Suffix of given size from a buffer
+//
+// Given a CRC32C of an existing buffer, `full_string_crc`; the CRC32C of a
+// suffix of that buffer to remove, `suffix_crc`; and suffix buffer's length,
+// `suffix_len` return the CRC32C of the buffer with suffix removed
+//
+// This operation has a runtime cost of O(log(`suffix_len`))
+crc32c_t RemoveCrc32cSuffix(crc32c_t full_string_crc, crc32c_t suffix_crc,
+                            size_t suffix_len) {
+  uint32_t result = static_cast<uint32_t>(full_string_crc) ^
+                    static_cast<uint32_t>(suffix_crc);
+  CrcEngine()->UnextendByZeroes(&result, suffix_len);
+  return crc32c_t{result};
+}
+
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/crc/crc32c.h b/absl/crc/crc32c.h
new file mode 100644
index 0000000..ba09e52
--- /dev/null
+++ b/absl/crc/crc32c.h
@@ -0,0 +1,183 @@
+// Copyright 2022 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: crc32c.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines the API for computing CRC32C values as checksums
+// for arbitrary sequences of bytes provided as a string buffer.
+//
+// The API includes the basic functions for computing such CRC32C values and
+// some utility functions for performing more efficient mathematical
+// computations using an existing checksum.
+#ifndef ABSL_CRC_CRC32C_H_
+#define ABSL_CRC_CRC32C_H_
+
+#include <cstdint>
+#include <ostream>
+
+#include "absl/crc/internal/crc32c_inline.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+//-----------------------------------------------------------------------------
+// crc32c_t
+//-----------------------------------------------------------------------------
+
+// `crc32c_t` defines a strongly-typed integer for holding a CRC32C value.
+//
+// Some operators are intentionally omitted. Only equality operators are defined
+// so that `crc32c_t` can be directly compared. Methods for putting `crc32c_t`
+// directly into a set are omitted because this is bug-prone due to checksum
+// collisions. Use an explicit conversion to the `uint32_t` space for operations
+// that treat `crc32c_t` as an integer.
+class crc32c_t final {
+ public:
+  crc32c_t() = default;
+  constexpr explicit crc32c_t(uint32_t crc) : crc_(crc) {}
+
+  crc32c_t(const crc32c_t&) = default;
+  crc32c_t& operator=(const crc32c_t&) = default;
+
+  explicit operator uint32_t() const { return crc_; }
+
+  friend bool operator==(crc32c_t lhs, crc32c_t rhs) {
+    return static_cast<uint32_t>(lhs) == static_cast<uint32_t>(rhs);
+  }
+
+  friend bool operator!=(crc32c_t lhs, crc32c_t rhs) { return !(lhs == rhs); }
+
+ private:
+  uint32_t crc_;
+};
+
+namespace crc_internal {
+// Non-inline code path for `absl::ExtendCrc32c()`. Do not call directly.
+// Call `absl::ExtendCrc32c()` (defined below) instead.
+crc32c_t ExtendCrc32cInternal(crc32c_t initial_crc,
+                              absl::string_view buf_to_add);
+}  // namespace crc_internal
+
+// -----------------------------------------------------------------------------
+// CRC32C Computation Functions
+// -----------------------------------------------------------------------------
+
+// ComputeCrc32c()
+//
+// Returns the CRC32C value of the provided string.
+crc32c_t ComputeCrc32c(absl::string_view buf);
+
+// ExtendCrc32c()
+//
+// Computes a CRC32C value from an `initial_crc` CRC32C value including the
+// `buf_to_add` bytes of an additional buffer. Using this function is more
+// efficient than computing a CRC32C value for the combined buffer from
+// scratch.
+//
+// Note: `ExtendCrc32c` with an initial_crc of 0 is equivalent to
+// `ComputeCrc32c`.
+//
+// This operation has a runtime cost of O(`buf_to_add.size()`)
+inline crc32c_t ExtendCrc32c(crc32c_t initial_crc,
+                             absl::string_view buf_to_add) {
+  // Approximately 75% of calls have size <= 64.
+  if (buf_to_add.size() <= 64) {
+    uint32_t crc = static_cast<uint32_t>(initial_crc);
+    if (crc_internal::ExtendCrc32cInline(&crc, buf_to_add.data(),
+                                         buf_to_add.size())) {
+      return crc32c_t{crc};
+    }
+  }
+  return crc_internal::ExtendCrc32cInternal(initial_crc, buf_to_add);
+}
+
+// ExtendCrc32cByZeroes()
+//
+// Computes a CRC32C value for a buffer with an `initial_crc` CRC32C value,
+// where `length` bytes with a value of 0 are appended to the buffer. Using this
+// function is more efficient than computing a CRC32C value for the combined
+// buffer from scratch.
+//
+// This operation has a runtime cost of O(log(`length`))
+crc32c_t ExtendCrc32cByZeroes(crc32c_t initial_crc, size_t length);
+
+// MemcpyCrc32c()
+//
+// Copies `src` to `dest` using `memcpy()` semantics, returning the CRC32C
+// value of the copied buffer.
+//
+// Using `MemcpyCrc32c()` is potentially faster than performing the `memcpy()`
+// and `ComputeCrc32c()` operations separately.
+crc32c_t MemcpyCrc32c(void* dest, const void* src, size_t count,
+                      crc32c_t initial_crc = crc32c_t{0});
+
+// -----------------------------------------------------------------------------
+// CRC32C Arithmetic Functions
+// -----------------------------------------------------------------------------
+
+// The following functions perform arithmetic on CRC32C values, which are
+// generally more efficient than recalculating any given result's CRC32C value.
+
+// ConcatCrc32c()
+//
+// Calculates the CRC32C value of two buffers with known CRC32C values
+// concatenated together.
+//
+// Given a buffer with CRC32C value `crc1` and a buffer with
+// CRC32C value `crc2` and length, `crc2_length`, returns the CRC32C value of
+// the concatenation of these two buffers.
+//
+// This operation has a runtime cost of O(log(`crc2_length`)).
+crc32c_t ConcatCrc32c(crc32c_t crc1, crc32c_t crc2, size_t crc2_length);
+
+// RemoveCrc32cPrefix()
+//
+// Calculates the CRC32C value of an existing buffer with a series of bytes
+// (the prefix) removed from the beginning of that buffer.
+//
+// Given the CRC32C value of an existing buffer, `full_string_crc`; The CRC32C
+// value of a prefix of that buffer, `prefix_crc`; and the length of the buffer
+// with the prefix removed, `remaining_string_length` , return the CRC32C
+// value of the buffer with the prefix removed.
+//
+// This operation has a runtime cost of O(log(`remaining_string_length`)).
+crc32c_t RemoveCrc32cPrefix(crc32c_t prefix_crc, crc32c_t full_string_crc,
+                            size_t remaining_string_length);
+// RemoveCrc32cSuffix()
+//
+// Calculates the CRC32C value of an existing buffer with a series of bytes
+// (the suffix) removed from the end of that buffer.
+//
+// Given a CRC32C value of an existing buffer `full_string_crc`, the CRC32C
+// value of the suffix to remove `suffix_crc`, and the length of that suffix
+// `suffix_len`, returns the CRC32C value of the buffer with suffix removed.
+//
+// This operation has a runtime cost of O(log(`suffix_len`))
+crc32c_t RemoveCrc32cSuffix(crc32c_t full_string_crc, crc32c_t suffix_crc,
+                            size_t suffix_length);
+
+// operator<<
+//
+// Streams the CRC32C value `crc` to the stream `os`.
+inline std::ostream& operator<<(std::ostream& os, crc32c_t crc) {
+  return os << static_cast<uint32_t>(crc);
+}
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_CRC_CRC32C_H_
diff --git a/absl/crc/crc32c_benchmark.cc b/absl/crc/crc32c_benchmark.cc
new file mode 100644
index 0000000..3b46ef3
--- /dev/null
+++ b/absl/crc/crc32c_benchmark.cc
@@ -0,0 +1,183 @@
+// Copyright 2022 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 <string>
+
+#include "absl/crc/crc32c.h"
+#include "absl/crc/internal/crc32c.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/string_view.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+std::string TestString(size_t len) {
+  std::string result;
+  result.reserve(len);
+  for (size_t i = 0; i < len; ++i) {
+    result.push_back(static_cast<char>(i % 256));
+  }
+  return result;
+}
+
+void BM_Calculate(benchmark::State& state) {
+  int len = state.range(0);
+  std::string data = TestString(len);
+  for (auto s : state) {
+    benchmark::DoNotOptimize(data);
+    absl::crc32c_t crc = absl::ComputeCrc32c(data);
+    benchmark::DoNotOptimize(crc);
+  }
+}
+BENCHMARK(BM_Calculate)->Arg(0)->Arg(1)->Arg(100)->Arg(10000)->Arg(500000);
+
+void BM_Extend(benchmark::State& state) {
+  int len = state.range(0);
+  std::string extension = TestString(len);
+  absl::crc32c_t base = absl::crc32c_t{0xC99465AA};  // CRC32C of "Hello World"
+  for (auto s : state) {
+    benchmark::DoNotOptimize(base);
+    benchmark::DoNotOptimize(extension);
+    absl::crc32c_t crc = absl::ExtendCrc32c(base, extension);
+    benchmark::DoNotOptimize(crc);
+  }
+}
+BENCHMARK(BM_Extend)->Arg(0)->Arg(1)->Arg(100)->Arg(10000)->Arg(500000)->Arg(
+    100 * 1000 * 1000);
+
+// Make working set >> CPU cache size to benchmark prefetches better
+void BM_ExtendCacheMiss(benchmark::State& state) {
+  int len = state.range(0);
+  constexpr int total = 300 * 1000 * 1000;
+  std::string extension = TestString(total);
+  absl::crc32c_t base = absl::crc32c_t{0xC99465AA};  // CRC32C of "Hello World"
+  for (auto s : state) {
+    for (int i = 0; i < total; i += len * 2) {
+      benchmark::DoNotOptimize(base);
+      benchmark::DoNotOptimize(extension);
+      absl::crc32c_t crc =
+          absl::ExtendCrc32c(base, absl::string_view(&extension[i], len));
+      benchmark::DoNotOptimize(crc);
+    }
+  }
+  state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) * total / 2);
+}
+BENCHMARK(BM_ExtendCacheMiss)->Arg(10)->Arg(100)->Arg(1000)->Arg(100000);
+
+void BM_ExtendByZeroes(benchmark::State& state) {
+  absl::crc32c_t base = absl::crc32c_t{0xC99465AA};  // CRC32C of "Hello World"
+  int num_zeroes = state.range(0);
+  for (auto s : state) {
+    benchmark::DoNotOptimize(base);
+    absl::crc32c_t crc = absl::ExtendCrc32cByZeroes(base, num_zeroes);
+    benchmark::DoNotOptimize(crc);
+  }
+}
+BENCHMARK(BM_ExtendByZeroes)
+    ->RangeMultiplier(10)
+    ->Range(1, 1000000)
+    ->RangeMultiplier(32)
+    ->Range(1, 1 << 20);
+
+void BM_UnextendByZeroes(benchmark::State& state) {
+  absl::crc32c_t base = absl::crc32c_t{0xdeadbeef};
+  int num_zeroes = state.range(0);
+  for (auto s : state) {
+    benchmark::DoNotOptimize(base);
+    absl::crc32c_t crc =
+        absl::crc_internal::UnextendCrc32cByZeroes(base, num_zeroes);
+    benchmark::DoNotOptimize(crc);
+  }
+}
+BENCHMARK(BM_UnextendByZeroes)
+    ->RangeMultiplier(10)
+    ->Range(1, 1000000)
+    ->RangeMultiplier(32)
+    ->Range(1, 1 << 20);
+
+void BM_Concat(benchmark::State& state) {
+  int string_b_len = state.range(0);
+  std::string string_b = TestString(string_b_len);
+
+  // CRC32C of "Hello World"
+  absl::crc32c_t crc_a = absl::crc32c_t{0xC99465AA};
+  absl::crc32c_t crc_b = absl::ComputeCrc32c(string_b);
+
+  for (auto s : state) {
+    benchmark::DoNotOptimize(crc_a);
+    benchmark::DoNotOptimize(crc_b);
+    benchmark::DoNotOptimize(string_b_len);
+    absl::crc32c_t crc_ab = absl::ConcatCrc32c(crc_a, crc_b, string_b_len);
+    benchmark::DoNotOptimize(crc_ab);
+  }
+}
+BENCHMARK(BM_Concat)
+    ->RangeMultiplier(10)
+    ->Range(1, 1000000)
+    ->RangeMultiplier(32)
+    ->Range(1, 1 << 20);
+
+void BM_Memcpy(benchmark::State& state) {
+  int string_len = state.range(0);
+
+  std::string source = TestString(string_len);
+  auto dest = absl::make_unique<char[]>(string_len);
+
+  for (auto s : state) {
+    benchmark::DoNotOptimize(source);
+    absl::crc32c_t crc =
+        absl::MemcpyCrc32c(dest.get(), source.data(), source.size());
+    benchmark::DoNotOptimize(crc);
+    benchmark::DoNotOptimize(dest);
+    benchmark::DoNotOptimize(dest.get());
+    benchmark::DoNotOptimize(dest[0]);
+  }
+
+  state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) *
+                          state.range(0));
+}
+BENCHMARK(BM_Memcpy)->Arg(0)->Arg(1)->Arg(100)->Arg(10000)->Arg(500000);
+
+void BM_RemoveSuffix(benchmark::State& state) {
+  int full_string_len = state.range(0);
+  int suffix_len = state.range(1);
+
+  std::string full_string = TestString(full_string_len);
+  std::string suffix = full_string.substr(
+    full_string_len - suffix_len, full_string_len);
+
+  absl::crc32c_t full_string_crc = absl::ComputeCrc32c(full_string);
+  absl::crc32c_t suffix_crc = absl::ComputeCrc32c(suffix);
+
+  for (auto s : state) {
+    benchmark::DoNotOptimize(full_string_crc);
+    benchmark::DoNotOptimize(suffix_crc);
+    benchmark::DoNotOptimize(suffix_len);
+    absl::crc32c_t crc = absl::RemoveCrc32cSuffix(full_string_crc, suffix_crc,
+      suffix_len);
+    benchmark::DoNotOptimize(crc);
+  }
+}
+BENCHMARK(BM_RemoveSuffix)
+    ->ArgPair(1, 1)
+    ->ArgPair(100, 10)
+    ->ArgPair(100, 100)
+    ->ArgPair(10000, 1)
+    ->ArgPair(10000, 100)
+    ->ArgPair(10000, 10000)
+    ->ArgPair(500000, 1)
+    ->ArgPair(500000, 100)
+    ->ArgPair(500000, 10000)
+    ->ArgPair(500000, 500000);
+}  // namespace
diff --git a/absl/crc/crc32c_test.cc b/absl/crc/crc32c_test.cc
new file mode 100644
index 0000000..72d422a
--- /dev/null
+++ b/absl/crc/crc32c_test.cc
@@ -0,0 +1,194 @@
+// Copyright 2022 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/crc/crc32c.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <string>
+
+#include "gtest/gtest.h"
+#include "absl/crc/internal/crc32c.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+
+namespace {
+
+TEST(CRC32C, RFC3720) {
+  // Test the results of the vectors from
+  // https://www.rfc-editor.org/rfc/rfc3720#appendix-B.4
+  char data[32];
+
+  // 32 bytes of ones.
+  memset(data, 0, sizeof(data));
+  EXPECT_EQ(absl::ComputeCrc32c(absl::string_view(data, sizeof(data))),
+            absl::crc32c_t{0x8a9136aa});
+
+  // 32 bytes of ones.
+  memset(data, 0xff, sizeof(data));
+  EXPECT_EQ(absl::ComputeCrc32c(absl::string_view(data, sizeof(data))),
+            absl::crc32c_t{0x62a8ab43});
+
+  // 32 incrementing bytes.
+  for (int i = 0; i < 32; ++i) data[i] = static_cast<char>(i);
+  EXPECT_EQ(absl::ComputeCrc32c(absl::string_view(data, sizeof(data))),
+            absl::crc32c_t{0x46dd794e});
+
+  // 32 decrementing bytes.
+  for (int i = 0; i < 32; ++i) data[i] = static_cast<char>(31 - i);
+  EXPECT_EQ(absl::ComputeCrc32c(absl::string_view(data, sizeof(data))),
+            absl::crc32c_t{0x113fdb5c});
+
+  // An iSCSI - SCSI Read (10) Command PDU.
+  constexpr uint8_t cmd[48] = {
+      0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+      0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  };
+  EXPECT_EQ(absl::ComputeCrc32c(absl::string_view(
+                reinterpret_cast<const char*>(cmd), sizeof(cmd))),
+            absl::crc32c_t{0xd9963a56});
+}
+
+std::string TestString(size_t len) {
+  std::string result;
+  result.reserve(len);
+  for (size_t i = 0; i < len; ++i) {
+    result.push_back(static_cast<char>(i % 256));
+  }
+  return result;
+}
+
+TEST(CRC32C, Compute) {
+  EXPECT_EQ(absl::ComputeCrc32c(""), absl::crc32c_t{0});
+  EXPECT_EQ(absl::ComputeCrc32c("hello world"), absl::crc32c_t{0xc99465aa});
+}
+
+TEST(CRC32C, Extend) {
+  uint32_t base = 0xC99465AA;  // CRC32C of "Hello World"
+  std::string extension = "Extension String";
+
+  EXPECT_EQ(
+      absl::ExtendCrc32c(absl::crc32c_t{base}, extension),
+      absl::crc32c_t{0xD2F65090});  // CRC32C of "Hello WorldExtension String"
+}
+
+TEST(CRC32C, ExtendByZeroes) {
+  std::string base = "hello world";
+  absl::crc32c_t base_crc = absl::crc32c_t{0xc99465aa};
+
+  constexpr size_t kExtendByValues[] = {100, 10000, 100000};
+  for (const size_t extend_by : kExtendByValues) {
+    SCOPED_TRACE(extend_by);
+    absl::crc32c_t crc2 = absl::ExtendCrc32cByZeroes(base_crc, extend_by);
+    EXPECT_EQ(crc2, absl::ComputeCrc32c(base + std::string(extend_by, '\0')));
+  }
+}
+
+TEST(CRC32C, UnextendByZeroes) {
+  constexpr size_t kExtendByValues[] = {2, 200, 20000, 200000, 20000000};
+  constexpr size_t kUnextendByValues[] = {0, 100, 10000, 100000, 10000000};
+
+  for (auto seed_crc : {absl::crc32c_t{0}, absl::crc32c_t{0xc99465aa}}) {
+    SCOPED_TRACE(seed_crc);
+    for (const size_t size_1 : kExtendByValues) {
+      for (const size_t size_2 : kUnextendByValues) {
+        size_t extend_size = std::max(size_1, size_2);
+        size_t unextend_size = std::min(size_1, size_2);
+        SCOPED_TRACE(extend_size);
+        SCOPED_TRACE(unextend_size);
+
+        // Extending by A zeroes an unextending by B<A zeros should be identical
+        // to extending by A-B zeroes.
+        absl::crc32c_t crc1 = seed_crc;
+        crc1 = absl::ExtendCrc32cByZeroes(crc1, extend_size);
+        crc1 = absl::crc_internal::UnextendCrc32cByZeroes(crc1, unextend_size);
+
+        absl::crc32c_t crc2 = seed_crc;
+        crc2 = absl::ExtendCrc32cByZeroes(crc2, extend_size - unextend_size);
+
+        EXPECT_EQ(crc1, crc2);
+      }
+    }
+  }
+
+  constexpr size_t kSizes[] = {0, 1, 100, 10000};
+  for (const size_t size : kSizes) {
+    SCOPED_TRACE(size);
+    std::string string_before = TestString(size);
+    std::string string_after = string_before + std::string(size, '\0');
+
+    absl::crc32c_t crc_before = absl::ComputeCrc32c(string_before);
+    absl::crc32c_t crc_after = absl::ComputeCrc32c(string_after);
+
+    EXPECT_EQ(crc_before,
+              absl::crc_internal::UnextendCrc32cByZeroes(crc_after, size));
+  }
+}
+
+TEST(CRC32C, Concat) {
+  std::string hello = "Hello, ";
+  std::string world = "world!";
+  std::string hello_world = absl::StrCat(hello, world);
+
+  absl::crc32c_t crc_a = absl::ComputeCrc32c(hello);
+  absl::crc32c_t crc_b = absl::ComputeCrc32c(world);
+  absl::crc32c_t crc_ab = absl::ComputeCrc32c(hello_world);
+
+  EXPECT_EQ(absl::ConcatCrc32c(crc_a, crc_b, world.size()), crc_ab);
+}
+
+TEST(CRC32C, Memcpy) {
+  constexpr size_t kBytesSize[] = {0, 1, 20, 500, 100000};
+  for (size_t bytes : kBytesSize) {
+    SCOPED_TRACE(bytes);
+    std::string sample_string = TestString(bytes);
+    std::string target_buffer = std::string(bytes, '\0');
+
+    absl::crc32c_t memcpy_crc =
+        absl::MemcpyCrc32c(&(target_buffer[0]), sample_string.data(), bytes);
+    absl::crc32c_t compute_crc = absl::ComputeCrc32c(sample_string);
+
+    EXPECT_EQ(memcpy_crc, compute_crc);
+    EXPECT_EQ(sample_string, target_buffer);
+  }
+}
+
+TEST(CRC32C, RemovePrefix) {
+  std::string hello = "Hello, ";
+  std::string world = "world!";
+  std::string hello_world = absl::StrCat(hello, world);
+
+  absl::crc32c_t crc_a = absl::ComputeCrc32c(hello);
+  absl::crc32c_t crc_b = absl::ComputeCrc32c(world);
+  absl::crc32c_t crc_ab = absl::ComputeCrc32c(hello_world);
+
+  EXPECT_EQ(absl::RemoveCrc32cPrefix(crc_a, crc_ab, world.size()), crc_b);
+}
+
+TEST(CRC32C, RemoveSuffix) {
+  std::string hello = "Hello, ";
+  std::string world = "world!";
+  std::string hello_world = absl::StrCat(hello, world);
+
+  absl::crc32c_t crc_a = absl::ComputeCrc32c(hello);
+  absl::crc32c_t crc_b = absl::ComputeCrc32c(world);
+  absl::crc32c_t crc_ab = absl::ComputeCrc32c(hello_world);
+
+  EXPECT_EQ(absl::RemoveCrc32cSuffix(crc_ab, crc_b, world.size()), crc_a);
+}
+}  // namespace
diff --git a/absl/crc/internal/cpu_detect.cc b/absl/crc/internal/cpu_detect.cc
new file mode 100644
index 0000000..d61b701
--- /dev/null
+++ b/absl/crc/internal/cpu_detect.cc
@@ -0,0 +1,256 @@
+// Copyright 2022 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/crc/internal/cpu_detect.h"
+
+#include <cstdint>
+#include <string>
+
+#include "absl/base/config.h"
+
+#if defined(__aarch64__) && defined(__linux__)
+#include <asm/hwcap.h>
+#include <sys/auxv.h>
+#endif
+
+#if defined(_WIN32) || defined(_WIN64)
+#include <intrin.h>
+#endif
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+#if defined(__x86_64__) || defined(_M_X64)
+
+namespace {
+
+#if !defined(_WIN32) && !defined(_WIN64)
+// MSVC defines this function for us.
+// https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
+static void __cpuid(int cpu_info[4], int info_type) {
+  __asm__ volatile("cpuid \n\t"
+                   : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]),
+                     "=d"(cpu_info[3])
+                   : "a"(info_type), "c"(0));
+}
+#endif  // !defined(_WIN32) && !defined(_WIN64)
+
+enum class Vendor {
+  kUnknown,
+  kIntel,
+  kAmd,
+};
+
+Vendor GetVendor() {
+  // Get the vendor string (issue CPUID with eax = 0).
+  int cpu_info[4];
+  __cpuid(cpu_info, 0);
+
+  std::string vendor;
+  vendor.append(reinterpret_cast<char*>(&cpu_info[1]), 4);
+  vendor.append(reinterpret_cast<char*>(&cpu_info[3]), 4);
+  vendor.append(reinterpret_cast<char*>(&cpu_info[2]), 4);
+  if (vendor == "GenuineIntel") {
+    return Vendor::kIntel;
+  } else if (vendor == "AuthenticAMD") {
+    return Vendor::kAmd;
+  } else {
+    return Vendor::kUnknown;
+  }
+}
+
+CpuType GetIntelCpuType() {
+  // To get general information and extended features we send eax = 1 and
+  // ecx = 0 to cpuid.  The response is returned in eax, ebx, ecx and edx.
+  // (See Intel 64 and IA-32 Architectures Software Developer's Manual
+  // Volume 2A: Instruction Set Reference, A-M CPUID).
+  // https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-2a-manual.html
+  // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
+  int cpu_info[4];
+  __cpuid(cpu_info, 1);
+
+  // Response in eax bits as follows:
+  // 0-3 (stepping id)
+  // 4-7 (model number),
+  // 8-11 (family code),
+  // 12-13 (processor type),
+  // 16-19 (extended model)
+  // 20-27 (extended family)
+
+  int family = (cpu_info[0] >> 8) & 0x0f;
+  int model_num = (cpu_info[0] >> 4) & 0x0f;
+  int ext_family = (cpu_info[0] >> 20) & 0xff;
+  int ext_model_num = (cpu_info[0] >> 16) & 0x0f;
+
+  int brand_id = cpu_info[1] & 0xff;
+
+  // Process the extended family and model info if necessary
+  if (family == 0x0f) {
+    family += ext_family;
+  }
+
+  if (family == 0x0f || family == 0x6) {
+    model_num += (ext_model_num << 4);
+  }
+
+  switch (brand_id) {
+    case 0:  // no brand ID, so parse CPU family/model
+      switch (family) {
+        case 6:  // Most PentiumIII processors are in this category
+          switch (model_num) {
+            case 0x2c:  // Westmere: Gulftown
+              return CpuType::kIntelWestmere;
+            case 0x2d:  // Sandybridge
+              return CpuType::kIntelSandybridge;
+            case 0x3e:  // Ivybridge
+              return CpuType::kIntelIvybridge;
+            case 0x3c:  // Haswell (client)
+            case 0x3f:  // Haswell
+              return CpuType::kIntelHaswell;
+            case 0x4f:  // Broadwell
+            case 0x56:  // BroadwellDE
+              return CpuType::kIntelBroadwell;
+            case 0x55:                 // Skylake Xeon
+              if ((cpu_info[0] & 0x0f) < 5) {  // stepping < 5 is skylake
+                return CpuType::kIntelSkylakeXeon;
+              } else {  // stepping >= 5 is cascadelake
+                return CpuType::kIntelCascadelakeXeon;
+              }
+            case 0x5e:  // Skylake (client)
+              return CpuType::kIntelSkylake;
+            default:
+              return CpuType::kUnknown;
+          }
+        default:
+          return CpuType::kUnknown;
+      }
+    default:
+      return CpuType::kUnknown;
+  }
+}
+
+CpuType GetAmdCpuType() {
+  // To get general information and extended features we send eax = 1 and
+  // ecx = 0 to cpuid.  The response is returned in eax, ebx, ecx and edx.
+  // (See Intel 64 and IA-32 Architectures Software Developer's Manual
+  // Volume 2A: Instruction Set Reference, A-M CPUID).
+  // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
+  int cpu_info[4];
+  __cpuid(cpu_info, 1);
+
+  // Response in eax bits as follows:
+  // 0-3 (stepping id)
+  // 4-7 (model number),
+  // 8-11 (family code),
+  // 12-13 (processor type),
+  // 16-19 (extended model)
+  // 20-27 (extended family)
+
+  int family = (cpu_info[0] >> 8) & 0x0f;
+  int model_num = (cpu_info[0] >> 4) & 0x0f;
+  int ext_family = (cpu_info[0] >> 20) & 0xff;
+  int ext_model_num = (cpu_info[0] >> 16) & 0x0f;
+
+  if (family == 0x0f) {
+    family += ext_family;
+    model_num += (ext_model_num << 4);
+  }
+
+  switch (family) {
+    case 0x17:
+      switch (model_num) {
+        case 0x0:  // Stepping Ax
+        case 0x1:  // Stepping Bx
+          return CpuType::kAmdNaples;
+        case 0x30:  // Stepping Ax
+        case 0x31:  // Stepping Bx
+          return CpuType::kAmdRome;
+        default:
+          return CpuType::kUnknown;
+      }
+      break;
+    case 0x19:
+      switch (model_num) {
+        case 0x1:  // Stepping B0
+          return CpuType::kAmdMilan;
+        default:
+          return CpuType::kUnknown;
+      }
+      break;
+    default:
+      return CpuType::kUnknown;
+  }
+}
+
+}  // namespace
+
+CpuType GetCpuType() {
+  switch (GetVendor()) {
+    case Vendor::kIntel:
+      return GetIntelCpuType();
+    case Vendor::kAmd:
+      return GetAmdCpuType();
+    default:
+      return CpuType::kUnknown;
+  }
+}
+
+bool SupportsArmCRC32PMULL() { return false; }
+
+#elif defined(__aarch64__) && defined(__linux__)
+
+#ifndef HWCAP_CPUID
+#define HWCAP_CPUID (1 << 11)
+#endif
+
+#define ABSL_INTERNAL_AARCH64_ID_REG_READ(id, val) \
+  asm("mrs %0, " #id : "=r"(val))
+
+CpuType GetCpuType() {
+  // MIDR_EL1 is not visible to EL0, however the access will be emulated by
+  // linux if AT_HWCAP has HWCAP_CPUID set.
+  //
+  // This method will be unreliable on heterogeneous computing systems (ex:
+  // big.LITTLE) since the value of MIDR_EL1 will change based on the calling
+  // thread.
+  uint64_t hwcaps = getauxval(AT_HWCAP);
+  if (hwcaps & HWCAP_CPUID) {
+    uint64_t midr = 0;
+    ABSL_INTERNAL_AARCH64_ID_REG_READ(MIDR_EL1, midr);
+    uint32_t implementer = (midr >> 24) & 0xff;
+    uint32_t part_number = (midr >> 4) & 0xfff;
+    if (implementer == 0x41 && part_number == 0xd0c) {
+      return CpuType::kArmNeoverseN1;
+    }
+  }
+  return CpuType::kUnknown;
+}
+
+bool SupportsArmCRC32PMULL() {
+  uint64_t hwcaps = getauxval(AT_HWCAP);
+  return (hwcaps & HWCAP_CRC32) && (hwcaps & HWCAP_PMULL);
+}
+
+#else
+
+CpuType GetCpuType() { return CpuType::kUnknown; }
+
+bool SupportsArmCRC32PMULL() { return false; }
+
+#endif
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/crc/internal/cpu_detect.h b/absl/crc/internal/cpu_detect.h
new file mode 100644
index 0000000..6054f69
--- /dev/null
+++ b/absl/crc/internal/cpu_detect.h
@@ -0,0 +1,57 @@
+// Copyright 2022 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_CRC_INTERNAL_CPU_DETECT_H_
+#define ABSL_CRC_INTERNAL_CPU_DETECT_H_
+
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+// Enumeration of architectures that we have special-case tuning parameters for.
+// This set may change over time.
+enum class CpuType {
+  kUnknown,
+  kIntelHaswell,
+  kAmdRome,
+  kAmdNaples,
+  kAmdMilan,
+  kIntelCascadelakeXeon,
+  kIntelSkylakeXeon,
+  kIntelBroadwell,
+  kIntelSkylake,
+  kIntelIvybridge,
+  kIntelSandybridge,
+  kIntelWestmere,
+  kArmNeoverseN1,
+};
+
+// Returns the type of host CPU this code is running on.  Returns kUnknown if
+// the host CPU is of unknown type, or if detection otherwise fails.
+CpuType GetCpuType();
+
+// Returns whether the host CPU supports the CPU features needed for our
+// accelerated implementations. The CpuTypes enumerated above apart from
+// kUnknown support the required features. On unknown CPUs, we can use
+// this to see if it's safe to use hardware acceleration, though without any
+// tuning.
+bool SupportsArmCRC32PMULL();
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_CRC_INTERNAL_CPU_DETECT_H_
diff --git a/absl/crc/internal/crc.cc b/absl/crc/internal/crc.cc
new file mode 100644
index 0000000..ba4c6d1
--- /dev/null
+++ b/absl/crc/internal/crc.cc
@@ -0,0 +1,468 @@
+// Copyright 2022 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.
+
+// Implementation of CRCs (aka Rabin Fingerprints).
+// Treats the input as a polynomial with coefficients in Z(2),
+// and finds the remainder when divided by an irreducible polynomial
+// of the appropriate length.
+// It handles all CRC sizes from 8 to 128 bits.
+// It's somewhat complicated by having separate implementations optimized for
+// CRC's <=32 bits, <= 64 bits, and <= 128 bits.
+// The input string is prefixed with a "1" bit, and has "degree" "0" bits
+// appended to it before the remainder is found.   This ensures that
+// short strings are scrambled somewhat and that strings consisting
+// of all nulls have a non-zero CRC.
+//
+// Uses the "interleaved word-by-word" method from
+// "Everything we know about CRC but afraid to forget" by Andrew Kadatch
+// and Bob Jenkins,
+// http://crcutil.googlecode.com/files/crc-doc.1.0.pdf
+//
+// The idea is to compute kStride CRCs simultaneously, allowing the
+// processor to more effectively use multiple execution units. Each of
+// the CRCs is calculated on one word of data followed by kStride - 1
+// words of zeroes; the CRC starting points are staggered by one word.
+// Assuming a stride of 4 with data words "ABCDABCDABCD", the first
+// CRC is over A000A000A, the second over 0B000B000B, and so on.
+// The CRC of the whole data is then calculated by properly aligning the
+// CRCs by appending zeroes until the data lengths agree then XORing
+// the CRCs.
+
+#include "absl/crc/internal/crc.h"
+
+#include <cstdint>
+
+#include "absl/base/internal/endian.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/prefetch.h"
+#include "absl/crc/internal/crc_internal.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+namespace {
+
+// Constants
+#if defined(__i386__) || defined(__x86_64__)
+constexpr bool kNeedAlignedLoads = false;
+#else
+constexpr bool kNeedAlignedLoads = true;
+#endif
+
+// We express the number of zeroes as a number in base ZEROES_BASE. By
+// pre-computing the zero extensions for all possible components of such an
+// expression (numbers in a form a*ZEROES_BASE**b), we can calculate the
+// resulting extension by multiplying the extensions for individual components
+// using log_{ZEROES_BASE}(num_zeroes) polynomial multiplications. The tables of
+// zero extensions contain (ZEROES_BASE - 1) * (log_{ZEROES_BASE}(64)) entries.
+constexpr int ZEROES_BASE_LG = 4;                   // log_2(ZEROES_BASE)
+constexpr int ZEROES_BASE = (1 << ZEROES_BASE_LG);  // must be a power of 2
+
+constexpr uint32_t kCrc32cPoly = 0x82f63b78;
+
+uint32_t ReverseBits(uint32_t bits) {
+  bits = (bits & 0xaaaaaaaau) >> 1 | (bits & 0x55555555u) << 1;
+  bits = (bits & 0xccccccccu) >> 2 | (bits & 0x33333333u) << 2;
+  bits = (bits & 0xf0f0f0f0u) >> 4 | (bits & 0x0f0f0f0fu) << 4;
+  return absl::gbswap_32(bits);
+}
+
+// Polynomial long multiplication mod the polynomial of degree 32.
+void PolyMultiply(uint32_t* val, uint32_t m, uint32_t poly) {
+  uint32_t l = *val;
+  uint32_t result = 0;
+  auto onebit = uint32_t{0x80000000u};
+  for (uint32_t one = onebit; one != 0; one >>= 1) {
+    if ((l & one) != 0) {
+      result ^= m;
+    }
+    if (m & 1) {
+      m = (m >> 1) ^ poly;
+    } else {
+      m >>= 1;
+    }
+  }
+  *val = result;
+}
+}  // namespace
+
+void CRCImpl::FillWordTable(uint32_t poly, uint32_t last, int word_size,
+                            Uint32By256* t) {
+  for (int j = 0; j != word_size; j++) {  // for each byte of extension....
+    t[j][0] = 0;                          // a zero has no effect
+    for (int i = 128; i != 0; i >>= 1) {  // fill in entries for powers of 2
+      if (j == 0 && i == 128) {
+        t[j][i] = last;  // top bit in last byte is given
+      } else {
+        // each successive power of two is derived from the previous
+        // one, either in this table, or the last table
+        uint32_t pred;
+        if (i == 128) {
+          pred = t[j - 1][1];
+        } else {
+          pred = t[j][i << 1];
+        }
+        // Advance the CRC by one bit (multiply by X, and take remainder
+        // through one step of polynomial long division)
+        if (pred & 1) {
+          t[j][i] = (pred >> 1) ^ poly;
+        } else {
+          t[j][i] = pred >> 1;
+        }
+      }
+    }
+    // CRCs have the property that CRC(a xor b) == CRC(a) xor CRC(b)
+    // so we can make all the tables for non-powers of two by
+    // xoring previously created entries.
+    for (int i = 2; i != 256; i <<= 1) {
+      for (int k = i + 1; k != (i << 1); k++) {
+        t[j][k] = t[j][i] ^ t[j][k - i];
+      }
+    }
+  }
+}
+
+int CRCImpl::FillZeroesTable(uint32_t poly, Uint32By256* t) {
+  uint32_t inc = 1;
+  inc <<= 31;
+
+  // Extend by one zero bit. We know degree > 1 so (inc & 1) == 0.
+  inc >>= 1;
+
+  // Now extend by 2, 4, and 8 bits, so now `inc` is extended by one zero byte.
+  for (int i = 0; i < 3; ++i) {
+    PolyMultiply(&inc, inc, poly);
+  }
+
+  int j = 0;
+  for (uint64_t inc_len = 1; inc_len != 0; inc_len <<= ZEROES_BASE_LG) {
+    // Every entry in the table adds an additional inc_len zeroes.
+    uint32_t v = inc;
+    for (int a = 1; a != ZEROES_BASE; a++) {
+      t[0][j] = v;
+      PolyMultiply(&v, inc, poly);
+      j++;
+    }
+    inc = v;
+  }
+  ABSL_RAW_CHECK(j <= 256, "");
+  return j;
+}
+
+// Internal version of the "constructor".
+CRCImpl* CRCImpl::NewInternal() {
+  // Find an accelearated implementation first.
+  CRCImpl* result = TryNewCRC32AcceleratedX86ARMCombined();
+
+  // Fall back to generic implementions if no acceleration is available.
+  if (result == nullptr) {
+    result = new CRC32();
+  }
+
+  result->InitTables();
+
+  return result;
+}
+
+// The CRC of the empty string is always the CRC polynomial itself.
+void CRCImpl::Empty(uint32_t* crc) const { *crc = kCrc32cPoly; }
+
+//  The 32-bit implementation
+
+void CRC32::InitTables() {
+  // Compute the table for extending a CRC by one byte.
+  Uint32By256* t = new Uint32By256[4];
+  FillWordTable(kCrc32cPoly, kCrc32cPoly, 1, t);
+  for (int i = 0; i != 256; i++) {
+    this->table0_[i] = t[0][i];
+  }
+
+  // Construct a table for updating the CRC by 4 bytes data followed by
+  // 12 bytes of zeroes.
+  //
+  // Note: the data word size could be larger than the CRC size; it might
+  // be slightly faster to use a 64-bit data word, but doing so doubles the
+  // table size.
+  uint32_t last = kCrc32cPoly;
+  const size_t size = 12;
+  for (size_t i = 0; i < size; ++i) {
+    last = (last >> 8) ^ this->table0_[last & 0xff];
+  }
+  FillWordTable(kCrc32cPoly, last, 4, t);
+  for (size_t b = 0; b < 4; ++b) {
+    for (int i = 0; i < 256; ++i) {
+      this->table_[b][i] = t[b][i];
+    }
+  }
+
+  int j = FillZeroesTable(kCrc32cPoly, t);
+  ABSL_RAW_CHECK(j <= static_cast<int>(ABSL_ARRAYSIZE(this->zeroes_)), "");
+  for (int i = 0; i < j; i++) {
+    this->zeroes_[i] = t[0][i];
+  }
+
+  delete[] t;
+
+  // Build up tables for _reversing_ the operation of doing CRC operations on
+  // zero bytes.
+
+  // In C++, extending `crc` by a single zero bit is done by the following:
+  // (A)  bool low_bit_set = (crc & 1);
+  //      crc >>= 1;
+  //      if (low_bit_set) crc ^= kCrc32cPoly;
+  //
+  // In particular note that the high bit of `crc` after this operation will be
+  // set if and only if the low bit of `crc` was set before it.  This means that
+  // no information is lost, and the operation can be reversed, as follows:
+  // (B)  bool high_bit_set = (crc & 0x80000000u);
+  //      if (high_bit_set) crc ^= kCrc32cPoly;
+  //      crc <<= 1;
+  //      if (high_bit_set) crc ^= 1;
+  //
+  // Or, equivalently:
+  // (C)  bool high_bit_set = (crc & 0x80000000u);
+  //      crc <<= 1;
+  //      if (high_bit_set) crc ^= ((kCrc32cPoly << 1) ^ 1);
+  //
+  // The last observation is, if we store our checksums in variable `rcrc`,
+  // with order of the bits reversed, the inverse operation becomes:
+  // (D)  bool low_bit_set = (rcrc & 1);
+  //      rcrc >>= 1;
+  //      if (low_bit_set) rcrc ^= ReverseBits((kCrc32cPoly << 1) ^ 1)
+  //
+  // This is the same algorithm (A) that we started with, only with a different
+  // polynomial bit pattern.  This means that by building up our tables with
+  // this alternate polynomial, we can apply the CRC algorithms to a
+  // bit-reversed CRC checksum to perform inverse zero-extension.
+
+  const uint32_t kCrc32cUnextendPoly =
+      ReverseBits(static_cast<uint32_t>((kCrc32cPoly << 1) ^ 1));
+  FillWordTable(kCrc32cUnextendPoly, kCrc32cUnextendPoly, 1, &reverse_table0_);
+
+  j = FillZeroesTable(kCrc32cUnextendPoly, &reverse_zeroes_);
+  ABSL_RAW_CHECK(j <= static_cast<int>(ABSL_ARRAYSIZE(this->reverse_zeroes_)),
+                 "");
+}
+
+void CRC32::Extend(uint32_t* crc, const void* bytes, size_t length) const {
+  const uint8_t* p = static_cast<const uint8_t*>(bytes);
+  const uint8_t* e = p + length;
+  uint32_t l = *crc;
+
+  auto step_one_byte = [this, &p, &l]() {
+    int c = (l & 0xff) ^ *p++;
+    l = this->table0_[c] ^ (l >> 8);
+  };
+
+  if (kNeedAlignedLoads) {
+    // point x at first 4-byte aligned byte in string. this might be past the
+    // end of the string.
+    const uint8_t* x = RoundUp<4>(p);
+    if (x <= e) {
+      // Process bytes until finished or p is 4-byte aligned
+      while (p != x) {
+        step_one_byte();
+      }
+    }
+  }
+
+  const size_t kSwathSize = 16;
+  if (static_cast<size_t>(e - p) >= kSwathSize) {
+    // Load one swath of data into the operating buffers.
+    uint32_t buf0 = absl::little_endian::Load32(p) ^ l;
+    uint32_t buf1 = absl::little_endian::Load32(p + 4);
+    uint32_t buf2 = absl::little_endian::Load32(p + 8);
+    uint32_t buf3 = absl::little_endian::Load32(p + 12);
+    p += kSwathSize;
+
+    // Increment a CRC value by a "swath"; this combines the four bytes
+    // starting at `ptr` and twelve zero bytes, so that four CRCs can be
+    // built incrementally and combined at the end.
+    const auto step_swath = [this](uint32_t crc_in, const std::uint8_t* ptr) {
+      return absl::little_endian::Load32(ptr) ^
+             this->table_[3][crc_in & 0xff] ^
+             this->table_[2][(crc_in >> 8) & 0xff] ^
+             this->table_[1][(crc_in >> 16) & 0xff] ^
+             this->table_[0][crc_in >> 24];
+    };
+
+    // Run one CRC calculation step over all swaths in one 16-byte stride
+    const auto step_stride = [&]() {
+      buf0 = step_swath(buf0, p);
+      buf1 = step_swath(buf1, p + 4);
+      buf2 = step_swath(buf2, p + 8);
+      buf3 = step_swath(buf3, p + 12);
+      p += 16;
+    };
+
+    // Process kStride interleaved swaths through the data in parallel.
+    while ((e - p) > kPrefetchHorizon) {
+      PrefetchToLocalCacheNta(
+          reinterpret_cast<const void*>(p + kPrefetchHorizon));
+      // Process 64 bytes at a time
+      step_stride();
+      step_stride();
+      step_stride();
+      step_stride();
+    }
+    while (static_cast<size_t>(e - p) >= kSwathSize) {
+      step_stride();
+    }
+
+    // Now advance one word at a time as far as possible. This isn't worth
+    // doing if we have word-advance tables.
+    while (static_cast<size_t>(e - p) >= 4) {
+      buf0 = step_swath(buf0, p);
+      uint32_t tmp = buf0;
+      buf0 = buf1;
+      buf1 = buf2;
+      buf2 = buf3;
+      buf3 = tmp;
+      p += 4;
+    }
+
+    // Combine the results from the different swaths. This is just a CRC
+    // on the data values in the bufX words.
+    auto combine_one_word = [this](uint32_t crc_in, uint32_t w) {
+      w ^= crc_in;
+      for (size_t i = 0; i < 4; ++i) {
+        w = (w >> 8) ^ this->table0_[w & 0xff];
+      }
+      return w;
+    };
+
+    l = combine_one_word(0, buf0);
+    l = combine_one_word(l, buf1);
+    l = combine_one_word(l, buf2);
+    l = combine_one_word(l, buf3);
+  }
+
+  // Process the last few bytes
+  while (p != e) {
+    step_one_byte();
+  }
+
+  *crc = l;
+}
+
+void CRC32::ExtendByZeroesImpl(uint32_t* crc, size_t length,
+                               const uint32_t zeroes_table[256],
+                               const uint32_t poly_table[256]) {
+  if (length != 0) {
+    uint32_t l = *crc;
+    // For each ZEROES_BASE_LG bits in length
+    // (after the low-order bits have been removed)
+    // we lookup the appropriate polynomial in the zeroes_ array
+    // and do a polynomial long multiplication (mod the CRC polynomial)
+    // to extend the CRC by the appropriate number of bits.
+    for (int i = 0; length != 0;
+         i += ZEROES_BASE - 1, length >>= ZEROES_BASE_LG) {
+      int c = length & (ZEROES_BASE - 1);  // pick next ZEROES_BASE_LG bits
+      if (c != 0) {                        // if they are not zero,
+                                           // multiply by entry in table
+        // Build a table to aid in multiplying 2 bits at a time.
+        // It takes too long to build tables for more bits.
+        uint64_t m = zeroes_table[c + i - 1];
+        m <<= 1;
+        uint64_t m2 = m << 1;
+        uint64_t mtab[4] = {0, m, m2, m2 ^ m};
+
+        // Do the multiply one byte at a time.
+        uint64_t result = 0;
+        for (int x = 0; x < 32; x += 8) {
+          // The carry-less multiply.
+          result ^= mtab[l & 3] ^ (mtab[(l >> 2) & 3] << 2) ^
+                    (mtab[(l >> 4) & 3] << 4) ^ (mtab[(l >> 6) & 3] << 6);
+          l >>= 8;
+
+          // Reduce modulo the polynomial
+          result = (result >> 8) ^ poly_table[result & 0xff];
+        }
+        l = static_cast<uint32_t>(result);
+      }
+    }
+    *crc = l;
+  }
+}
+
+void CRC32::ExtendByZeroes(uint32_t* crc, size_t length) const {
+  return CRC32::ExtendByZeroesImpl(crc, length, zeroes_, table0_);
+}
+
+void CRC32::UnextendByZeroes(uint32_t* crc, size_t length) const {
+  // See the comment in CRC32::InitTables() for an explanation of the algorithm
+  // below.
+  *crc = ReverseBits(*crc);
+  ExtendByZeroesImpl(crc, length, reverse_zeroes_, reverse_table0_);
+  *crc = ReverseBits(*crc);
+}
+
+void CRC32::Scramble(uint32_t* crc) const {
+  // Rotate by near half the word size plus 1.  See the scramble comment in
+  // crc_internal.h for an explanation.
+  constexpr int scramble_rotate = (32 / 2) + 1;
+  *crc = RotateRight<uint32_t>(static_cast<unsigned int>(*crc + kScrambleLo),
+                               32, scramble_rotate) &
+         MaskOfLength<uint32_t>(32);
+}
+
+void CRC32::Unscramble(uint32_t* crc) const {
+  constexpr int scramble_rotate = (32 / 2) + 1;
+  uint64_t rotated = RotateRight<uint32_t>(static_cast<unsigned int>(*crc), 32,
+                                           32 - scramble_rotate);
+  *crc = (rotated - kScrambleLo) & MaskOfLength<uint32_t>(32);
+}
+
+// Constructor and destructor for base class CRC.
+CRC::~CRC() {}
+CRC::CRC() {}
+
+// The "constructor" for a CRC32C with a standard polynomial.
+CRC* CRC::Crc32c() {
+  static CRC* singleton = CRCImpl::NewInternal();
+  return singleton;
+}
+
+// This Concat implementation works for arbitrary polynomials.
+void CRC::Concat(uint32_t* px, uint32_t y, size_t ylen) {
+  // https://en.wikipedia.org/wiki/Mathematics_of_cyclic_redundancy_checks
+  // The CRC of a message M is the remainder of polynomial division modulo G,
+  // where the coefficient arithmetic is performed modulo 2 (so +/- are XOR):
+  //   R(x) = M(x) x**n (mod G)
+  // (n is the degree of G)
+  // In practice, we use an initial value A and a bitmask B to get
+  //   R = (A ^ B)x**|M| ^ Mx**n ^ B (mod G)
+  // If M is the concatenation of two strings S and T, and Z is the string of
+  // len(T) 0s, then the remainder CRC(ST) can be expressed as:
+  //   R = (A ^ B)x**|ST| ^ STx**n ^ B
+  //     = (A ^ B)x**|SZ| ^ SZx**n ^ B ^ Tx**n
+  //     = CRC(SZ) ^ Tx**n
+  // CRC(Z) = (A ^ B)x**|T| ^ B
+  // CRC(T) = (A ^ B)x**|T| ^ Tx**n ^ B
+  // So R = CRC(SZ) ^ CRC(Z) ^ CRC(T)
+  //
+  // And further, since CRC(SZ) = Extend(CRC(S), Z),
+  //  CRC(SZ) ^ CRC(Z) = Extend(CRC(S) ^ CRC(''), Z).
+  uint32_t z;
+  uint32_t t;
+  Empty(&z);
+  t = *px ^ z;
+  ExtendByZeroes(&t, ylen);
+  *px = t ^ y;
+}
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/crc/internal/crc.h b/absl/crc/internal/crc.h
new file mode 100644
index 0000000..e683c25
--- /dev/null
+++ b/absl/crc/internal/crc.h
@@ -0,0 +1,91 @@
+// Copyright 2022 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_CRC_INTERNAL_CRC_H_
+#define ABSL_CRC_INTERNAL_CRC_H_
+
+#include <cstdint>
+
+#include "absl/base/config.h"
+
+// This class implements CRCs (aka Rabin Fingerprints).
+// Treats the input as a polynomial with coefficients in Z(2),
+// and finds the remainder when divided by an primitive polynomial
+// of the appropriate length.
+
+// A polynomial is represented by the bit pattern formed by its coefficients,
+// but with the highest order bit not stored.
+// The highest degree coefficient is stored in the lowest numbered bit
+// in the lowest addressed byte.   Thus, in what follows, the highest degree
+// coefficient that is stored is in the low order bit of "lo" or "*lo".
+
+// Hardware acceleration is used when available.
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+class CRC {
+ public:
+  virtual ~CRC();
+
+  // Place the CRC of the empty string in "*crc"
+  virtual void Empty(uint32_t* crc) const = 0;
+
+  // If "*crc" is the CRC of bytestring A, place the CRC of
+  // the bytestring formed from the concatenation of A and the "length"
+  // bytes at "bytes" into "*crc".
+  virtual void Extend(uint32_t* crc, const void* bytes,
+                      size_t length) const = 0;
+
+  // Equivalent to Extend(crc, bytes, length) where "bytes"
+  // points to an array of "length" zero bytes.
+  virtual void ExtendByZeroes(uint32_t* crc, size_t length) const = 0;
+
+  // Inverse operation of ExtendByZeroes.  If `crc` is the CRC value of a string
+  // ending in `length` zero bytes, this returns a CRC value of that string
+  // with those zero bytes removed.
+  virtual void UnextendByZeroes(uint32_t* crc, size_t length) const = 0;
+
+  // If *px is the CRC (as defined by *crc) of some string X,
+  // and y is the CRC of some string Y that is ylen bytes long, set
+  // *px to the CRC of the concatenation of X followed by Y.
+  virtual void Concat(uint32_t* px, uint32_t y, size_t ylen);
+
+  // Apply a non-linear transformation to "*crc" so that
+  // it is safe to CRC the result with the same polynomial without
+  // any reduction of error-detection ability in the outer CRC.
+  // Unscramble() performs the inverse transformation.
+  // It is strongly recommended that CRCs be scrambled before storage or
+  // transmission, and unscrambled at the other end before further manipulation.
+  virtual void Scramble(uint32_t* crc) const = 0;
+  virtual void Unscramble(uint32_t* crc) const = 0;
+
+  // Crc32c() returns the singleton implementation of CRC for the CRC32C
+  // polynomial.  Returns a handle that MUST NOT be destroyed with delete.
+  static CRC* Crc32c();
+
+ protected:
+  CRC();  // Clients may not call constructor; use Crc32c() instead.
+
+ private:
+  CRC(const CRC&) = delete;
+  CRC& operator=(const CRC&) = delete;
+};
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_CRC_INTERNAL_CRC_H_
diff --git a/absl/crc/internal/crc32_x86_arm_combined_simd.h b/absl/crc/internal/crc32_x86_arm_combined_simd.h
new file mode 100644
index 0000000..39e53dd
--- /dev/null
+++ b/absl/crc/internal/crc32_x86_arm_combined_simd.h
@@ -0,0 +1,269 @@
+// Copyright 2022 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_CRC_INTERNAL_CRC32_X86_ARM_COMBINED_SIMD_H_
+#define ABSL_CRC_INTERNAL_CRC32_X86_ARM_COMBINED_SIMD_H_
+
+#include <cstdint>
+
+#include "absl/base/config.h"
+
+// -------------------------------------------------------------------------
+// Many x86 and ARM machines have CRC acceleration hardware.
+// We can do a faster version of Extend() on such machines.
+// We define a translation layer for both x86 and ARM for the ease of use and
+// most performance gains.
+
+// This implementation requires 64-bit CRC instructions (part of SSE 4.2) and
+// PCLMULQDQ instructions. 32-bit builds with SSE 4.2 do exist, so the
+// __x86_64__ condition is necessary.
+#if defined(__x86_64__) && defined(__SSE4_2__) && defined(__PCLMUL__)
+
+#include <x86intrin.h>
+#define ABSL_CRC_INTERNAL_HAVE_X86_SIMD
+
+#elif defined(_MSC_VER) && !defined(__clang__) && defined(__AVX__)
+
+// MSVC AVX (/arch:AVX) implies SSE 4.2 and PCLMULQDQ.
+#include <intrin.h>
+#define ABSL_CRC_INTERNAL_HAVE_X86_SIMD
+
+#elif defined(__aarch64__) && defined(__LITTLE_ENDIAN__) && \
+    defined(__ARM_FEATURE_CRC32) && defined(ABSL_INTERNAL_HAVE_ARM_NEON) &&  \
+    defined(__ARM_FEATURE_CRYPTO)
+
+#include <arm_acle.h>
+#include <arm_neon.h>
+#define ABSL_CRC_INTERNAL_HAVE_ARM_SIMD
+
+#endif
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+#if defined(ABSL_CRC_INTERNAL_HAVE_ARM_SIMD) || \
+    defined(ABSL_CRC_INTERNAL_HAVE_X86_SIMD)
+
+#if defined(ABSL_CRC_INTERNAL_HAVE_ARM_SIMD)
+using V128 = uint64x2_t;
+#else
+using V128 = __m128i;
+#endif
+
+// Starting with the initial value in |crc|, accumulates a CRC32 value for
+// unsigned integers of different sizes.
+uint32_t CRC32_u8(uint32_t crc, uint8_t v);
+
+uint32_t CRC32_u16(uint32_t crc, uint16_t v);
+
+uint32_t CRC32_u32(uint32_t crc, uint32_t v);
+
+uint32_t CRC32_u64(uint32_t crc, uint64_t v);
+
+// Loads 128 bits of integer data. |src| must be 16-byte aligned.
+V128 V128_Load(const V128* src);
+
+// Load 128 bits of integer data. |src| does not need to be aligned.
+V128 V128_LoadU(const V128* src);
+
+// Polynomially multiplies the high 64 bits of |l| and |r|.
+V128 V128_PMulHi(const V128 l, const V128 r);
+
+// Polynomially multiplies the low 64 bits of |l| and |r|.
+V128 V128_PMulLow(const V128 l, const V128 r);
+
+// Polynomially multiplies the low 64 bits of |r| and high 64 bits of |l|.
+V128 V128_PMul01(const V128 l, const V128 r);
+
+// Polynomially multiplies the low 64 bits of |l| and high 64 bits of |r|.
+V128 V128_PMul10(const V128 l, const V128 r);
+
+// Produces a XOR operation of |l| and |r|.
+V128 V128_Xor(const V128 l, const V128 r);
+
+// Produces an AND operation of |l| and |r|.
+V128 V128_And(const V128 l, const V128 r);
+
+// Sets two 64 bit integers to one 128 bit vector. The order is reverse.
+// dst[63:0] := |r|
+// dst[127:64] := |l|
+V128 V128_From2x64(const uint64_t l, const uint64_t r);
+
+// Shift |l| right by |imm| bytes while shifting in zeros.
+template <int imm>
+V128 V128_ShiftRight(const V128 l);
+
+// Extracts a 32-bit integer from |l|, selected with |imm|.
+template <int imm>
+int V128_Extract32(const V128 l);
+
+// Extracts the low 64 bits from V128.
+int64_t V128_Low64(const V128 l);
+
+// Left-shifts packed 64-bit integers in l by r.
+V128 V128_ShiftLeft64(const V128 l, const V128 r);
+
+#endif
+
+#if defined(ABSL_CRC_INTERNAL_HAVE_X86_SIMD)
+
+inline uint32_t CRC32_u8(uint32_t crc, uint8_t v) {
+  return _mm_crc32_u8(crc, v);
+}
+
+inline uint32_t CRC32_u16(uint32_t crc, uint16_t v) {
+  return _mm_crc32_u16(crc, v);
+}
+
+inline uint32_t CRC32_u32(uint32_t crc, uint32_t v) {
+  return _mm_crc32_u32(crc, v);
+}
+
+inline uint32_t CRC32_u64(uint32_t crc, uint64_t v) {
+  return static_cast<uint32_t>(_mm_crc32_u64(crc, v));
+}
+
+inline V128 V128_Load(const V128* src) { return _mm_load_si128(src); }
+
+inline V128 V128_LoadU(const V128* src) { return _mm_loadu_si128(src); }
+
+inline V128 V128_PMulHi(const V128 l, const V128 r) {
+  return _mm_clmulepi64_si128(l, r, 0x11);
+}
+
+inline V128 V128_PMulLow(const V128 l, const V128 r) {
+  return _mm_clmulepi64_si128(l, r, 0x00);
+}
+
+inline V128 V128_PMul01(const V128 l, const V128 r) {
+  return _mm_clmulepi64_si128(l, r, 0x01);
+}
+
+inline V128 V128_PMul10(const V128 l, const V128 r) {
+  return _mm_clmulepi64_si128(l, r, 0x10);
+}
+
+inline V128 V128_Xor(const V128 l, const V128 r) { return _mm_xor_si128(l, r); }
+
+inline V128 V128_And(const V128 l, const V128 r) { return _mm_and_si128(l, r); }
+
+inline V128 V128_From2x64(const uint64_t l, const uint64_t r) {
+  return _mm_set_epi64x(static_cast<int64_t>(l), static_cast<int64_t>(r));
+}
+
+template <int imm>
+inline V128 V128_ShiftRight(const V128 l) {
+  return _mm_srli_si128(l, imm);
+}
+
+template <int imm>
+inline int V128_Extract32(const V128 l) {
+  return _mm_extract_epi32(l, imm);
+}
+
+inline int64_t V128_Low64(const V128 l) { return _mm_cvtsi128_si64(l); }
+
+inline V128 V128_ShiftLeft64(const V128 l, const V128 r) {
+  return _mm_sll_epi64(l, r);
+}
+
+#elif defined(ABSL_CRC_INTERNAL_HAVE_ARM_SIMD)
+
+inline uint32_t CRC32_u8(uint32_t crc, uint8_t v) { return __crc32cb(crc, v); }
+
+inline uint32_t CRC32_u16(uint32_t crc, uint16_t v) {
+  return __crc32ch(crc, v);
+}
+
+inline uint32_t CRC32_u32(uint32_t crc, uint32_t v) {
+  return __crc32cw(crc, v);
+}
+
+inline uint32_t CRC32_u64(uint32_t crc, uint64_t v) {
+  return __crc32cd(crc, v);
+}
+
+inline V128 V128_Load(const V128* src) {
+  return vld1q_u64(reinterpret_cast<const uint64_t*>(src));
+}
+
+inline V128 V128_LoadU(const V128* src) {
+  return vld1q_u64(reinterpret_cast<const uint64_t*>(src));
+}
+
+// Using inline assembly as clang does not generate the pmull2 instruction and
+// performance drops by 15-20%.
+// TODO(b/193678732): Investigate why the compiler decides not to generate
+// such instructions and why it becomes so much worse.
+inline V128 V128_PMulHi(const V128 l, const V128 r) {
+  uint64x2_t res;
+  __asm__ __volatile__("pmull2 %0.1q, %1.2d, %2.2d \n\t"
+                       : "=w"(res)
+                       : "w"(l), "w"(r));
+  return res;
+}
+
+inline V128 V128_PMulLow(const V128 l, const V128 r) {
+  return reinterpret_cast<V128>(vmull_p64(
+      reinterpret_cast<poly64_t>(vget_low_p64(vreinterpretq_p64_u64(l))),
+      reinterpret_cast<poly64_t>(vget_low_p64(vreinterpretq_p64_u64(r)))));
+}
+
+inline V128 V128_PMul01(const V128 l, const V128 r) {
+  return reinterpret_cast<V128>(vmull_p64(
+      reinterpret_cast<poly64_t>(vget_high_p64(vreinterpretq_p64_u64(l))),
+      reinterpret_cast<poly64_t>(vget_low_p64(vreinterpretq_p64_u64(r)))));
+}
+
+inline V128 V128_PMul10(const V128 l, const V128 r) {
+  return reinterpret_cast<V128>(vmull_p64(
+      reinterpret_cast<poly64_t>(vget_low_p64(vreinterpretq_p64_u64(l))),
+      reinterpret_cast<poly64_t>(vget_high_p64(vreinterpretq_p64_u64(r)))));
+}
+
+inline V128 V128_Xor(const V128 l, const V128 r) { return veorq_u64(l, r); }
+
+inline V128 V128_And(const V128 l, const V128 r) { return vandq_u64(l, r); }
+
+inline V128 V128_From2x64(const uint64_t l, const uint64_t r) {
+  return vcombine_u64(vcreate_u64(r), vcreate_u64(l));
+}
+
+template <int imm>
+inline V128 V128_ShiftRight(const V128 l) {
+  return vreinterpretq_u64_s8(
+      vextq_s8(vreinterpretq_s8_u64(l), vdupq_n_s8(0), imm));
+}
+
+template <int imm>
+inline int V128_Extract32(const V128 l) {
+  return vgetq_lane_s32(vreinterpretq_s32_u64(l), imm);
+}
+
+inline int64_t V128_Low64(const V128 l) {
+  return vgetq_lane_s64(vreinterpretq_s64_u64(l), 0);
+}
+
+inline V128 V128_ShiftLeft64(const V128 l, const V128 r) {
+  return vshlq_u64(l, vreinterpretq_s64_u64(r));
+}
+
+#endif
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_CRC_INTERNAL_CRC32_X86_ARM_COMBINED_SIMD_H_
diff --git a/absl/crc/internal/crc32c.h b/absl/crc/internal/crc32c.h
new file mode 100644
index 0000000..34027c5
--- /dev/null
+++ b/absl/crc/internal/crc32c.h
@@ -0,0 +1,39 @@
+// Copyright 2022 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_CRC_INTERNAL_CRC32C_H_
+#define ABSL_CRC_INTERNAL_CRC32C_H_
+
+#include "absl/base/config.h"
+#include "absl/crc/crc32c.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+// Modifies a CRC32 value by removing `length` bytes with a value of 0 from
+// the end of the string.
+//
+// This is the inverse operation of ExtendCrc32cByZeroes().
+//
+// This operation has a runtime cost of O(log(`length`))
+//
+// Internal implementation detail, exposed for testing only.
+crc32c_t UnextendCrc32cByZeroes(crc32c_t initial_crc, size_t length);
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_CRC_INTERNAL_CRC32C_H_
diff --git a/absl/crc/internal/crc32c_inline.h b/absl/crc/internal/crc32c_inline.h
new file mode 100644
index 0000000..6236c10
--- /dev/null
+++ b/absl/crc/internal/crc32c_inline.h
@@ -0,0 +1,72 @@
+// Copyright 2022 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_CRC_INTERNAL_CRC32C_INLINE_H_
+#define ABSL_CRC_INTERNAL_CRC32C_INLINE_H_
+
+#include <cstdint>
+
+#include "absl/base/config.h"
+#include "absl/base/internal/endian.h"
+#include "absl/crc/internal/crc32_x86_arm_combined_simd.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+// CRC32C implementation optimized for small inputs.
+// Either computes crc and return true, or if there is
+// no hardware support does nothing and returns false.
+inline bool ExtendCrc32cInline(uint32_t* crc, const char* p, size_t n) {
+#if defined(ABSL_CRC_INTERNAL_HAVE_ARM_SIMD) || \
+    defined(ABSL_CRC_INTERNAL_HAVE_X86_SIMD)
+  constexpr uint32_t kCrc32Xor = 0xffffffffU;
+  *crc ^= kCrc32Xor;
+  if (n & 1) {
+    *crc = CRC32_u8(*crc, static_cast<uint8_t>(*p));
+    n--;
+    p++;
+  }
+  if (n & 2) {
+    *crc = CRC32_u16(*crc, absl::little_endian::Load16(p));
+    n -= 2;
+    p += 2;
+  }
+  if (n & 4) {
+    *crc = CRC32_u32(*crc, absl::little_endian::Load32(p));
+    n -= 4;
+    p += 4;
+  }
+  while (n) {
+    *crc = CRC32_u64(*crc, absl::little_endian::Load64(p));
+    n -= 8;
+    p += 8;
+  }
+  *crc ^= kCrc32Xor;
+  return true;
+#else
+  // No hardware support, signal the need to fallback.
+  static_cast<void>(crc);
+  static_cast<void>(p);
+  static_cast<void>(n);
+  return false;
+#endif  // defined(ABSL_CRC_INTERNAL_HAVE_ARM_SIMD) ||
+        // defined(ABSL_CRC_INTERNAL_HAVE_X86_SIMD)
+}
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_CRC_INTERNAL_CRC32C_INLINE_H_
diff --git a/absl/crc/internal/crc_cord_state.cc b/absl/crc/internal/crc_cord_state.cc
new file mode 100644
index 0000000..28d04dc
--- /dev/null
+++ b/absl/crc/internal/crc_cord_state.cc
@@ -0,0 +1,130 @@
+// Copyright 2022 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/crc/internal/crc_cord_state.h"
+
+#include <cassert>
+
+#include "absl/base/config.h"
+#include "absl/numeric/bits.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+CrcCordState::RefcountedRep* CrcCordState::RefSharedEmptyRep() {
+  static CrcCordState::RefcountedRep* empty = new CrcCordState::RefcountedRep;
+
+  assert(empty->count.load(std::memory_order_relaxed) >= 1);
+  assert(empty->rep.removed_prefix.length == 0);
+  assert(empty->rep.prefix_crc.empty());
+
+  Ref(empty);
+  return empty;
+}
+
+CrcCordState::CrcCordState() : refcounted_rep_(new RefcountedRep) {}
+
+CrcCordState::CrcCordState(const CrcCordState& other)
+    : refcounted_rep_(other.refcounted_rep_) {
+  Ref(refcounted_rep_);
+}
+
+CrcCordState::CrcCordState(CrcCordState&& other)
+    : refcounted_rep_(other.refcounted_rep_) {
+  // Make `other` valid for use after move.
+  other.refcounted_rep_ = RefSharedEmptyRep();
+}
+
+CrcCordState& CrcCordState::operator=(const CrcCordState& other) {
+  if (this != &other) {
+    Unref(refcounted_rep_);
+    refcounted_rep_ = other.refcounted_rep_;
+    Ref(refcounted_rep_);
+  }
+  return *this;
+}
+
+CrcCordState& CrcCordState::operator=(CrcCordState&& other) {
+  if (this != &other) {
+    Unref(refcounted_rep_);
+    refcounted_rep_ = other.refcounted_rep_;
+    // Make `other` valid for use after move.
+    other.refcounted_rep_ = RefSharedEmptyRep();
+  }
+  return *this;
+}
+
+CrcCordState::~CrcCordState() {
+  Unref(refcounted_rep_);
+}
+
+crc32c_t CrcCordState::Checksum() const {
+  if (rep().prefix_crc.empty()) {
+    return absl::crc32c_t{0};
+  }
+  if (IsNormalized()) {
+    return rep().prefix_crc.back().crc;
+  }
+  return absl::RemoveCrc32cPrefix(
+      rep().removed_prefix.crc, rep().prefix_crc.back().crc,
+      rep().prefix_crc.back().length - rep().removed_prefix.length);
+}
+
+CrcCordState::PrefixCrc CrcCordState::NormalizedPrefixCrcAtNthChunk(
+    size_t n) const {
+  assert(n < NumChunks());
+  if (IsNormalized()) {
+    return rep().prefix_crc[n];
+  }
+  size_t length = rep().prefix_crc[n].length - rep().removed_prefix.length;
+  return PrefixCrc(length,
+                   absl::RemoveCrc32cPrefix(rep().removed_prefix.crc,
+                                            rep().prefix_crc[n].crc, length));
+}
+
+void CrcCordState::Normalize() {
+  if (IsNormalized() || rep().prefix_crc.empty()) {
+    return;
+  }
+
+  Rep* r = mutable_rep();
+  for (auto& prefix_crc : r->prefix_crc) {
+    size_t remaining = prefix_crc.length - r->removed_prefix.length;
+    prefix_crc.crc = absl::RemoveCrc32cPrefix(r->removed_prefix.crc,
+                                              prefix_crc.crc, remaining);
+    prefix_crc.length = remaining;
+  }
+  r->removed_prefix = PrefixCrc();
+}
+
+void CrcCordState::Poison() {
+  Rep* rep = mutable_rep();
+  if (NumChunks() > 0) {
+    for (auto& prefix_crc : rep->prefix_crc) {
+      // This is basically CRC32::Scramble().
+      uint32_t crc = static_cast<uint32_t>(prefix_crc.crc);
+      crc += 0x2e76e41b;
+      crc = absl::rotr(crc, 17);
+      prefix_crc.crc = crc32c_t{crc};
+    }
+  } else {
+    // Add a fake corrupt chunk.
+    rep->prefix_crc.emplace_back(0, crc32c_t{1});
+  }
+}
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/crc/internal/crc_cord_state.h b/absl/crc/internal/crc_cord_state.h
new file mode 100644
index 0000000..fbbb8c0
--- /dev/null
+++ b/absl/crc/internal/crc_cord_state.h
@@ -0,0 +1,159 @@
+// Copyright 2022 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_CRC_INTERNAL_CRC_CORD_STATE_H_
+#define ABSL_CRC_INTERNAL_CRC_CORD_STATE_H_
+
+#include <atomic>
+#include <cstddef>
+#include <deque>
+
+#include "absl/base/config.h"
+#include "absl/crc/crc32c.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+// CrcCordState is a copy-on-write class that holds the chunked CRC32C data
+// that allows CrcCord to perform efficient substring operations. CrcCordState
+// is used as a member variable in CrcCord. When a CrcCord is converted to a
+// Cord, the CrcCordState is shallow-copied into the root node of the Cord. If
+// the converted Cord is modified outside of CrcCord, the CrcCordState is
+// discarded from the Cord. If the Cord is converted back to a CrcCord, and the
+// Cord is still carrying the CrcCordState in its root node, the CrcCord can
+// re-use the CrcCordState, making the construction of the CrcCord cheap.
+//
+// CrcCordState does not try to encapsulate the CRC32C state (CrcCord requires
+// knowledge of how CrcCordState represents the CRC32C state). It does
+// encapsulate the copy-on-write nature of the state.
+class CrcCordState {
+ public:
+  // Constructors.
+  CrcCordState();
+  CrcCordState(const CrcCordState&);
+  CrcCordState(CrcCordState&&);
+
+  // Destructor. Atomically unreferences the data.
+  ~CrcCordState();
+
+  // Copy and move operators.
+  CrcCordState& operator=(const CrcCordState&);
+  CrcCordState& operator=(CrcCordState&&);
+
+  // A (length, crc) pair.
+  struct PrefixCrc {
+    PrefixCrc() = default;
+    PrefixCrc(size_t length_arg, absl::crc32c_t crc_arg)
+        : length(length_arg), crc(crc_arg) {}
+
+    size_t length = 0;
+
+    // TODO(absl-team): Memory stomping often zeros out memory. If this struct
+    // gets overwritten, we could end up with {0, 0}, which is the correct CRC
+    // for a string of length 0. Consider storing a scrambled value and
+    // unscrambling it before verifying it.
+    absl::crc32c_t crc = absl::crc32c_t{0};
+  };
+
+  // The representation of the chunked CRC32C data.
+  struct Rep {
+    // `removed_prefix` is the crc and length of any prefix that has been
+    // removed from the Cord (for example, by calling
+    // `CrcCord::RemovePrefix()`). To get the checksum of any prefix of the
+    // cord, this value must be subtracted from `prefix_crc`. See `Checksum()`
+    // for an example.
+    //
+    // CrcCordState is said to be "normalized" if removed_prefix.length == 0.
+    PrefixCrc removed_prefix;
+
+    // A deque of (length, crc) pairs, representing length and crc of a prefix
+    // of the Cord, before removed_prefix has been subtracted. The lengths of
+    // the prefixes are stored in increasing order. If the Cord is not empty,
+    // the last value in deque is the contains the CRC32C of the entire Cord
+    // when removed_prefix is subtracted from it.
+    std::deque<PrefixCrc> prefix_crc;
+  };
+
+  // Returns a reference to the representation of the chunked CRC32C data.
+  const Rep& rep() const { return refcounted_rep_->rep; }
+
+  // Returns a mutable reference to the representation of the chunked CRC32C
+  // data. Calling this function will copy the data if another instance also
+  // holds a reference to the data, so it is important to call rep() instead if
+  // the data may not be mutated.
+  Rep* mutable_rep() {
+    if (refcounted_rep_->count.load(std::memory_order_acquire) != 1) {
+      RefcountedRep* copy = new RefcountedRep;
+      copy->rep = refcounted_rep_->rep;
+      Unref(refcounted_rep_);
+      refcounted_rep_ = copy;
+    }
+    return &refcounted_rep_->rep;
+  }
+
+  // Returns the CRC32C of the entire Cord.
+  absl::crc32c_t Checksum() const;
+
+  // Returns true if the chunked CRC32C cached is normalized.
+  bool IsNormalized() const { return rep().removed_prefix.length == 0; }
+
+  // Normalizes the chunked CRC32C checksum cache by subtracting any removed
+  // prefix from the chunks.
+  void Normalize();
+
+  // Returns the number of cached chunks.
+  size_t NumChunks() const { return rep().prefix_crc.size(); }
+
+  // Helper that returns the (length, crc) of the `n`-th cached chunked.
+  PrefixCrc NormalizedPrefixCrcAtNthChunk(size_t n) const;
+
+  // Poisons all chunks to so that Checksum() will likely be incorrect with high
+  // probability.
+  void Poison();
+
+ private:
+  struct RefcountedRep {
+    std::atomic<int32_t> count{1};
+    Rep rep;
+  };
+
+  // Adds a reference to the shared global empty `RefcountedRep`, and returns a
+  // pointer to the `RefcountedRep`. This is an optimization to avoid unneeded
+  // allocations when the allocation is unlikely to ever be used. The returned
+  // pointer can be `Unref()`ed when it is no longer needed.  Since the returned
+  // instance will always have a reference counter greater than 1, attempts to
+  // modify it (by calling `mutable_rep()`) will create a new unshared copy.
+  static RefcountedRep* RefSharedEmptyRep();
+
+  static void Ref(RefcountedRep* r) {
+    assert(r != nullptr);
+    r->count.fetch_add(1, std::memory_order_relaxed);
+  }
+
+  static void Unref(RefcountedRep* r) {
+    assert(r != nullptr);
+    if (r->count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
+      delete r;
+    }
+  }
+
+  RefcountedRep* refcounted_rep_;
+};
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_CRC_INTERNAL_CRC_CORD_STATE_H_
diff --git a/absl/crc/internal/crc_cord_state_test.cc b/absl/crc/internal/crc_cord_state_test.cc
new file mode 100644
index 0000000..e2c8e3c
--- /dev/null
+++ b/absl/crc/internal/crc_cord_state_test.cc
@@ -0,0 +1,124 @@
+// Copyright 2022 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/crc/internal/crc_cord_state.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <string>
+#include <utility>
+
+#include "gtest/gtest.h"
+#include "absl/crc/crc32c.h"
+
+namespace {
+
+TEST(CrcCordState, Default) {
+  absl::crc_internal::CrcCordState state;
+  EXPECT_TRUE(state.IsNormalized());
+  EXPECT_EQ(state.Checksum(), absl::crc32c_t{0});
+  state.Normalize();
+  EXPECT_EQ(state.Checksum(), absl::crc32c_t{0});
+}
+
+TEST(CrcCordState, Normalize) {
+  absl::crc_internal::CrcCordState state;
+  auto* rep = state.mutable_rep();
+  rep->prefix_crc.push_back(
+      absl::crc_internal::CrcCordState::PrefixCrc(1000, absl::crc32c_t{1000}));
+  rep->prefix_crc.push_back(
+      absl::crc_internal::CrcCordState::PrefixCrc(2000, absl::crc32c_t{2000}));
+  rep->removed_prefix =
+      absl::crc_internal::CrcCordState::PrefixCrc(500, absl::crc32c_t{500});
+
+  // The removed_prefix means state is not normalized.
+  EXPECT_FALSE(state.IsNormalized());
+
+  absl::crc32c_t crc = state.Checksum();
+  state.Normalize();
+  EXPECT_TRUE(state.IsNormalized());
+
+  // The checksum should not change as a result of calling Normalize().
+  EXPECT_EQ(state.Checksum(), crc);
+  EXPECT_EQ(rep->removed_prefix.length, 0);
+}
+
+TEST(CrcCordState, Copy) {
+  absl::crc_internal::CrcCordState state;
+  auto* rep = state.mutable_rep();
+  rep->prefix_crc.push_back(
+      absl::crc_internal::CrcCordState::PrefixCrc(1000, absl::crc32c_t{1000}));
+
+  absl::crc_internal::CrcCordState copy = state;
+
+  EXPECT_EQ(state.Checksum(), absl::crc32c_t{1000});
+  EXPECT_EQ(copy.Checksum(), absl::crc32c_t{1000});
+}
+
+TEST(CrcCordState, UnsharedSelfCopy) {
+  absl::crc_internal::CrcCordState state;
+  auto* rep = state.mutable_rep();
+  rep->prefix_crc.push_back(
+      absl::crc_internal::CrcCordState::PrefixCrc(1000, absl::crc32c_t{1000}));
+
+  const absl::crc_internal::CrcCordState& ref = state;
+  state = ref;
+
+  EXPECT_EQ(state.Checksum(), absl::crc32c_t{1000});
+}
+
+TEST(CrcCordState, Move) {
+  absl::crc_internal::CrcCordState state;
+  auto* rep = state.mutable_rep();
+  rep->prefix_crc.push_back(
+      absl::crc_internal::CrcCordState::PrefixCrc(1000, absl::crc32c_t{1000}));
+
+  absl::crc_internal::CrcCordState moved = std::move(state);
+  EXPECT_EQ(moved.Checksum(), absl::crc32c_t{1000});
+}
+
+TEST(CrcCordState, UnsharedSelfMove) {
+  absl::crc_internal::CrcCordState state;
+  auto* rep = state.mutable_rep();
+  rep->prefix_crc.push_back(
+      absl::crc_internal::CrcCordState::PrefixCrc(1000, absl::crc32c_t{1000}));
+
+  absl::crc_internal::CrcCordState& ref = state;
+  state = std::move(ref);
+
+  EXPECT_EQ(state.Checksum(), absl::crc32c_t{1000});
+}
+
+TEST(CrcCordState, PoisonDefault) {
+  absl::crc_internal::CrcCordState state;
+  state.Poison();
+  EXPECT_NE(state.Checksum(), absl::crc32c_t{0});
+}
+
+TEST(CrcCordState, PoisonData) {
+  absl::crc_internal::CrcCordState state;
+  auto* rep = state.mutable_rep();
+  rep->prefix_crc.push_back(
+      absl::crc_internal::CrcCordState::PrefixCrc(1000, absl::crc32c_t{1000}));
+  rep->prefix_crc.push_back(
+      absl::crc_internal::CrcCordState::PrefixCrc(2000, absl::crc32c_t{2000}));
+  rep->removed_prefix =
+      absl::crc_internal::CrcCordState::PrefixCrc(500, absl::crc32c_t{500});
+
+  absl::crc32c_t crc = state.Checksum();
+  state.Poison();
+  EXPECT_NE(state.Checksum(), crc);
+}
+
+}  // namespace
diff --git a/absl/crc/internal/crc_internal.h b/absl/crc/internal/crc_internal.h
new file mode 100644
index 0000000..7d77bdf
--- /dev/null
+++ b/absl/crc/internal/crc_internal.h
@@ -0,0 +1,179 @@
+// Copyright 2022 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_CRC_INTERNAL_CRC_INTERNAL_H_
+#define ABSL_CRC_INTERNAL_CRC_INTERNAL_H_
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include "absl/base/internal/raw_logging.h"
+#include "absl/crc/internal/crc.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+namespace crc_internal {
+
+// Prefetch constants used in some Extend() implementations
+constexpr int kPrefetchHorizon = ABSL_CACHELINE_SIZE * 4;  // Prefetch this far
+// Shorter prefetch distance for smaller buffers
+constexpr int kPrefetchHorizonMedium = ABSL_CACHELINE_SIZE * 1;
+static_assert(kPrefetchHorizon >= 64, "CRCPrefetchHorizon less than loop len");
+
+// We require the Scramble() function:
+//  - to be reversible (Unscramble() must exist)
+//  - to be non-linear in the polynomial's Galois field (so the CRC of a
+//    scrambled CRC is not linearly affected by the scrambled CRC, even if
+//    using the same polynomial)
+//  - not to be its own inverse.  Preferably, if X=Scramble^N(X) and N!=0, then
+//    N is large.
+//  - to be fast.
+//  - not to change once defined.
+// We introduce non-linearity in two ways:
+//     Addition of a constant.
+//         - The carries introduce non-linearity; we use bits of an irrational
+//           (phi) to make it unlikely that we introduce no carries.
+//     Rotate by a constant number of bits.
+//         - We use floor(degree/2)+1, which does not divide the degree, and
+//           splits the bits nearly evenly, which makes it less likely the
+//           halves will be the same or one will be all zeroes.
+// We do both things to improve the chances of non-linearity in the face of
+// bit patterns with low numbers of bits set, while still being fast.
+// Below is the constant that we add.  The bits are the first 128 bits of the
+// fractional part of phi, with a 1 ored into the bottom bit to maximize the
+// cycle length of repeated adds.
+constexpr uint64_t kScrambleHi = (static_cast<uint64_t>(0x4f1bbcdcU) << 32) |
+                                 static_cast<uint64_t>(0xbfa53e0aU);
+constexpr uint64_t kScrambleLo = (static_cast<uint64_t>(0xf9ce6030U) << 32) |
+                                 static_cast<uint64_t>(0x2e76e41bU);
+
+class CRCImpl : public CRC {  // Implementation of the abstract class CRC
+ public:
+  using Uint32By256 = uint32_t[256];
+
+  CRCImpl() = default;
+  ~CRCImpl() override = default;
+
+  // The internal version of CRC::New().
+  static CRCImpl* NewInternal();
+
+  void Empty(uint32_t* crc) const override;
+
+  // Fill in a table for updating a CRC by one word of 'word_size' bytes
+  // [last_lo, last_hi] contains the answer if the last bit in the word
+  // is set.
+  static void FillWordTable(uint32_t poly, uint32_t last, int word_size,
+                            Uint32By256* t);
+
+  // Build the table for extending by zeroes, returning the number of entries.
+  // For a in {1, 2, ..., ZEROES_BASE-1}, b in {0, 1, 2, 3, ...},
+  // entry j=a-1+(ZEROES_BASE-1)*b
+  // contains a polynomial Pi such that multiplying
+  // a CRC by Pi mod P, where P is the CRC polynomial, is equivalent to
+  // appending a*2**(ZEROES_BASE_LG*b) zero bytes to the original string.
+  static int FillZeroesTable(uint32_t poly, Uint32By256* t);
+
+  virtual void InitTables() = 0;
+
+ private:
+  CRCImpl(const CRCImpl&) = delete;
+  CRCImpl& operator=(const CRCImpl&) = delete;
+};
+
+// This is the 32-bit implementation.  It handles all sizes from 8 to 32.
+class CRC32 : public CRCImpl {
+ public:
+  CRC32() = default;
+  ~CRC32() override = default;
+
+  void Extend(uint32_t* crc, const void* bytes, size_t length) const override;
+  void ExtendByZeroes(uint32_t* crc, size_t length) const override;
+  void Scramble(uint32_t* crc) const override;
+  void Unscramble(uint32_t* crc) const override;
+  void UnextendByZeroes(uint32_t* crc, size_t length) const override;
+
+  void InitTables() override;
+
+ private:
+  // Common implementation guts for ExtendByZeroes and UnextendByZeroes().
+  //
+  // zeroes_table is a table as returned by FillZeroesTable(), containing
+  // polynomials representing CRCs of strings-of-zeros of various lengths,
+  // and which can be combined by polynomial multiplication.  poly_table is
+  // a table of CRC byte extension values.  These tables are determined by
+  // the generator polynomial.
+  //
+  // These will be set to reverse_zeroes_ and reverse_table0_ for Unextend, and
+  // CRC32::zeroes_ and CRC32::table0_ for Extend.
+  static void ExtendByZeroesImpl(uint32_t* crc, size_t length,
+                                 const uint32_t zeroes_table[256],
+                                 const uint32_t poly_table[256]);
+
+  uint32_t table0_[256];  // table of byte extensions
+  uint32_t zeroes_[256];  // table of zero extensions
+
+  // table of 4-byte extensions shifted by 12 bytes of zeroes
+  uint32_t table_[4][256];
+
+  // Reverse lookup tables, using the alternate polynomial used by
+  // UnextendByZeroes().
+  uint32_t reverse_table0_[256];  // table of reverse byte extensions
+  uint32_t reverse_zeroes_[256];  // table of reverse zero extensions
+
+  CRC32(const CRC32&) = delete;
+  CRC32& operator=(const CRC32&) = delete;
+};
+
+// Helpers
+
+// Return a bit mask containing len 1-bits.
+// Requires 0 < len <= sizeof(T)
+template <typename T>
+T MaskOfLength(int len) {
+  // shift 2 by len-1 rather than 1 by len because shifts of wordsize
+  // are undefined.
+  return (T(2) << (len - 1)) - 1;
+}
+
+// Rotate low-order "width" bits of "in" right by "r" bits,
+// setting other bits in word to arbitrary values.
+template <typename T>
+T RotateRight(T in, int width, int r) {
+  return (in << (width - r)) | ((in >> r) & MaskOfLength<T>(width - r));
+}
+
+// RoundUp<N>(p) returns the lowest address >= p aligned to an N-byte
+// boundary.  Requires that N is a power of 2.
+template <int alignment>
+const uint8_t* RoundUp(const uint8_t* p) {
+  static_assert((alignment & (alignment - 1)) == 0, "alignment is not 2^n");
+  constexpr uintptr_t mask = alignment - 1;
+  const uintptr_t as_uintptr = reinterpret_cast<uintptr_t>(p);
+  return reinterpret_cast<const uint8_t*>((as_uintptr + mask) & ~mask);
+}
+
+// Return a newly created CRC32AcceleratedX86ARMCombined if we can use Intel's
+// or ARM's CRC acceleration for a given polynomial.  Return nullptr otherwise.
+CRCImpl* TryNewCRC32AcceleratedX86ARMCombined();
+
+// Return all possible hardware accelerated implementations. For testing only.
+std::vector<std::unique_ptr<CRCImpl>> NewCRC32AcceleratedX86ARMCombinedAll();
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_CRC_INTERNAL_CRC_INTERNAL_H_
diff --git a/absl/crc/internal/crc_memcpy.h b/absl/crc/internal/crc_memcpy.h
new file mode 100644
index 0000000..4909d43
--- /dev/null
+++ b/absl/crc/internal/crc_memcpy.h
@@ -0,0 +1,119 @@
+// Copyright 2022 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_CRC_INTERNAL_CRC_MEMCPY_H_
+#define ABSL_CRC_INTERNAL_CRC_MEMCPY_H_
+
+#include <cstddef>
+#include <memory>
+
+#include "absl/base/config.h"
+#include "absl/crc/crc32c.h"
+
+// Defined if the class AcceleratedCrcMemcpyEngine exists.
+#if defined(__x86_64__) && defined(__SSE4_2__)
+#define ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE 1
+#elif defined(_MSC_VER) && defined(__AVX__)
+#define ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE 1
+#endif
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+class CrcMemcpyEngine {
+ public:
+  virtual ~CrcMemcpyEngine() = default;
+
+  virtual crc32c_t Compute(void* __restrict dst, const void* __restrict src,
+                           std::size_t length, crc32c_t initial_crc) const = 0;
+
+ protected:
+  CrcMemcpyEngine() = default;
+};
+
+class CrcMemcpy {
+ public:
+  static crc32c_t CrcAndCopy(void* __restrict dst, const void* __restrict src,
+                             std::size_t length,
+                             crc32c_t initial_crc = crc32c_t{0},
+                             bool non_temporal = false) {
+    static const ArchSpecificEngines engines = GetArchSpecificEngines();
+    auto* engine = non_temporal ? engines.non_temporal : engines.temporal;
+    return engine->Compute(dst, src, length, initial_crc);
+  }
+
+  // For testing only: get an architecture-specific engine for tests.
+  static std::unique_ptr<CrcMemcpyEngine> GetTestEngine(int vector,
+                                                        int integer);
+
+ private:
+  struct ArchSpecificEngines {
+    CrcMemcpyEngine* temporal;
+    CrcMemcpyEngine* non_temporal;
+  };
+
+  static ArchSpecificEngines GetArchSpecificEngines();
+};
+
+// Fallback CRC-memcpy engine.
+class FallbackCrcMemcpyEngine : public CrcMemcpyEngine {
+ public:
+  FallbackCrcMemcpyEngine() = default;
+  FallbackCrcMemcpyEngine(const FallbackCrcMemcpyEngine&) = delete;
+  FallbackCrcMemcpyEngine operator=(const FallbackCrcMemcpyEngine&) = delete;
+
+  crc32c_t Compute(void* __restrict dst, const void* __restrict src,
+                   std::size_t length, crc32c_t initial_crc) const override;
+};
+
+// CRC Non-Temporal-Memcpy engine.
+class CrcNonTemporalMemcpyEngine : public CrcMemcpyEngine {
+ public:
+  CrcNonTemporalMemcpyEngine() = default;
+  CrcNonTemporalMemcpyEngine(const CrcNonTemporalMemcpyEngine&) = delete;
+  CrcNonTemporalMemcpyEngine operator=(const CrcNonTemporalMemcpyEngine&) =
+      delete;
+
+  crc32c_t Compute(void* __restrict dst, const void* __restrict src,
+                   std::size_t length, crc32c_t initial_crc) const override;
+};
+
+// CRC Non-Temporal-Memcpy AVX engine.
+class CrcNonTemporalMemcpyAVXEngine : public CrcMemcpyEngine {
+ public:
+  CrcNonTemporalMemcpyAVXEngine() = default;
+  CrcNonTemporalMemcpyAVXEngine(const CrcNonTemporalMemcpyAVXEngine&) = delete;
+  CrcNonTemporalMemcpyAVXEngine operator=(
+      const CrcNonTemporalMemcpyAVXEngine&) = delete;
+
+  crc32c_t Compute(void* __restrict dst, const void* __restrict src,
+                   std::size_t length, crc32c_t initial_crc) const override;
+};
+
+// Copy source to destination and return the CRC32C of the data copied.  If an
+// accelerated version is available, use the accelerated version, otherwise use
+// the generic fallback version.
+inline crc32c_t Crc32CAndCopy(void* __restrict dst, const void* __restrict src,
+                              std::size_t length,
+                              crc32c_t initial_crc = crc32c_t{0},
+                              bool non_temporal = false) {
+  return CrcMemcpy::CrcAndCopy(dst, src, length, initial_crc, non_temporal);
+}
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_CRC_INTERNAL_CRC_MEMCPY_H_
diff --git a/absl/crc/internal/crc_memcpy_fallback.cc b/absl/crc/internal/crc_memcpy_fallback.cc
new file mode 100644
index 0000000..15b4b05
--- /dev/null
+++ b/absl/crc/internal/crc_memcpy_fallback.cc
@@ -0,0 +1,75 @@
+// Copyright 2022 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 <memory>
+
+#include "absl/base/config.h"
+#include "absl/crc/crc32c.h"
+#include "absl/crc/internal/crc_memcpy.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+absl::crc32c_t FallbackCrcMemcpyEngine::Compute(void* __restrict dst,
+                                                const void* __restrict src,
+                                                std::size_t length,
+                                                crc32c_t initial_crc) const {
+  constexpr size_t kBlockSize = 8192;
+  absl::crc32c_t crc = initial_crc;
+
+  const char* src_bytes = reinterpret_cast<const char*>(src);
+  char* dst_bytes = reinterpret_cast<char*>(dst);
+
+  // Copy + CRC loop - run 8k chunks until we are out of full chunks.  CRC
+  // then copy was found to be slightly more efficient in our test cases.
+  std::size_t offset = 0;
+  for (; offset + kBlockSize < length; offset += kBlockSize) {
+    crc = absl::ExtendCrc32c(crc,
+                             absl::string_view(src_bytes + offset, kBlockSize));
+    memcpy(dst_bytes + offset, src_bytes + offset, kBlockSize);
+  }
+
+  // Save some work if length is 0.
+  if (offset < length) {
+    std::size_t final_copy_size = length - offset;
+    crc = absl::ExtendCrc32c(
+        crc, absl::string_view(src_bytes + offset, final_copy_size));
+    memcpy(dst_bytes + offset, src_bytes + offset, final_copy_size);
+  }
+
+  return crc;
+}
+
+// Compile the following only if we don't have
+#ifndef ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE
+
+CrcMemcpy::ArchSpecificEngines CrcMemcpy::GetArchSpecificEngines() {
+  CrcMemcpy::ArchSpecificEngines engines;
+  engines.temporal = new FallbackCrcMemcpyEngine();
+  engines.non_temporal = new FallbackCrcMemcpyEngine();
+  return engines;
+}
+
+std::unique_ptr<CrcMemcpyEngine> CrcMemcpy::GetTestEngine(int /*vector*/,
+                                                          int /*integer*/) {
+  return std::make_unique<FallbackCrcMemcpyEngine>();
+}
+
+#endif  // ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/crc/internal/crc_memcpy_test.cc b/absl/crc/internal/crc_memcpy_test.cc
new file mode 100644
index 0000000..bbdcd20
--- /dev/null
+++ b/absl/crc/internal/crc_memcpy_test.cc
@@ -0,0 +1,169 @@
+// Copyright 2022 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/crc/internal/crc_memcpy.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <limits>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "gtest/gtest.h"
+#include "absl/crc/crc32c.h"
+#include "absl/memory/memory.h"
+#include "absl/random/distributions.h"
+#include "absl/random/random.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+
+namespace {
+
+enum CrcEngine {
+  X86 = 0,
+  NONTEMPORAL = 1,
+  FALLBACK = 2,
+};
+
+// Correctness tests:
+// - Every source/destination byte alignment 0-15, every size 0-511 bytes
+// - Arbitrarily aligned source, large size
+template <size_t max_size>
+class CrcMemcpyTest : public testing::Test {
+ protected:
+  CrcMemcpyTest() {
+    source_ = std::make_unique<char[]>(kSize);
+    destination_ = std::make_unique<char[]>(kSize);
+  }
+  static constexpr size_t kAlignment = 16;
+  static constexpr size_t kMaxCopySize = max_size;
+  static constexpr size_t kSize = kAlignment + kMaxCopySize;
+  std::unique_ptr<char[]> source_;
+  std::unique_ptr<char[]> destination_;
+
+  absl::BitGen gen_;
+};
+
+// Small test is slightly larger 4096 bytes to allow coverage of the "large"
+// copy function.  The minimum size to exercise all code paths in that function
+// would be around 256 consecutive tests (getting every possible tail value
+// and 0-2 small copy loops after the main block), so testing from 4096-4500
+// will cover all of those code paths multiple times.
+typedef CrcMemcpyTest<4500> CrcSmallTest;
+typedef CrcMemcpyTest<(1 << 24)> CrcLargeTest;
+// Parametrize the small test so that it can be done with all configurations.
+template <typename ParamsT>
+class x86ParamTestTemplate : public CrcSmallTest,
+                             public ::testing::WithParamInterface<ParamsT> {
+ protected:
+  x86ParamTestTemplate() {
+    if (GetParam().crc_engine_selector == FALLBACK) {
+      engine_ = std::make_unique<absl::crc_internal::FallbackCrcMemcpyEngine>();
+    } else if (GetParam().crc_engine_selector == NONTEMPORAL) {
+      engine_ =
+          std::make_unique<absl::crc_internal::CrcNonTemporalMemcpyEngine>();
+    } else {
+      engine_ = absl::crc_internal::CrcMemcpy::GetTestEngine(
+          GetParam().vector_lanes, GetParam().integer_lanes);
+    }
+  }
+
+  // Convenience method.
+  ParamsT GetParam() const {
+    return ::testing::WithParamInterface<ParamsT>::GetParam();
+  }
+
+  std::unique_ptr<absl::crc_internal::CrcMemcpyEngine> engine_;
+};
+struct TestParams {
+  CrcEngine crc_engine_selector = X86;
+  int vector_lanes = 0;
+  int integer_lanes = 0;
+};
+using x86ParamTest = x86ParamTestTemplate<TestParams>;
+// SmallCorrectness is designed to exercise every possible set of code paths
+// in the memcpy code, not including the loop.
+TEST_P(x86ParamTest, SmallCorrectnessCheckSourceAlignment) {
+  constexpr size_t kTestSizes[] = {0, 100, 255, 512, 1024, 4000, kMaxCopySize};
+
+  for (size_t source_alignment = 0; source_alignment < kAlignment;
+       source_alignment++) {
+    for (auto size : kTestSizes) {
+      char* base_data = static_cast<char*>(source_.get()) + source_alignment;
+      for (size_t i = 0; i < size; i++) {
+        *(base_data + i) =
+            static_cast<char>(absl::Uniform<unsigned char>(gen_));
+      }
+      absl::crc32c_t initial_crc =
+          absl::crc32c_t{absl::Uniform<uint32_t>(gen_)};
+      absl::crc32c_t experiment_crc =
+          engine_->Compute(destination_.get(), source_.get() + source_alignment,
+                           size, initial_crc);
+      // Check the memory region to make sure it is the same
+      int mem_comparison =
+          memcmp(destination_.get(), source_.get() + source_alignment, size);
+      SCOPED_TRACE(absl::StrCat("Error in memcpy of size: ", size,
+                                " with source alignment: ", source_alignment));
+      ASSERT_EQ(mem_comparison, 0);
+      absl::crc32c_t baseline_crc = absl::ExtendCrc32c(
+          initial_crc,
+          absl::string_view(
+              static_cast<char*>(source_.get()) + source_alignment, size));
+      ASSERT_EQ(baseline_crc, experiment_crc);
+    }
+  }
+}
+
+TEST_P(x86ParamTest, SmallCorrectnessCheckDestAlignment) {
+  constexpr size_t kTestSizes[] = {0, 100, 255, 512, 1024, 4000, kMaxCopySize};
+
+  for (size_t dest_alignment = 0; dest_alignment < kAlignment;
+       dest_alignment++) {
+    for (auto size : kTestSizes) {
+      char* base_data = static_cast<char*>(source_.get());
+      for (size_t i = 0; i < size; i++) {
+        *(base_data + i) =
+            static_cast<char>(absl::Uniform<unsigned char>(gen_));
+      }
+      absl::crc32c_t initial_crc =
+          absl::crc32c_t{absl::Uniform<uint32_t>(gen_)};
+      absl::crc32c_t experiment_crc =
+          engine_->Compute(destination_.get() + dest_alignment, source_.get(),
+                           size, initial_crc);
+      // Check the memory region to make sure it is the same
+      int mem_comparison =
+          memcmp(destination_.get() + dest_alignment, source_.get(), size);
+      SCOPED_TRACE(absl::StrCat("Error in memcpy of size: ", size,
+                                " with dest alignment: ", dest_alignment));
+      ASSERT_EQ(mem_comparison, 0);
+      absl::crc32c_t baseline_crc = absl::ExtendCrc32c(
+          initial_crc,
+          absl::string_view(static_cast<char*>(source_.get()), size));
+      ASSERT_EQ(baseline_crc, experiment_crc);
+    }
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(x86ParamTest, x86ParamTest,
+                         ::testing::Values(
+                             // Tests for configurations that may occur in prod.
+                             TestParams{X86, 3, 0}, TestParams{X86, 1, 2},
+                             // Fallback test.
+                             TestParams{FALLBACK, 0, 0},
+                             // Non Temporal
+                             TestParams{NONTEMPORAL, 0, 0}));
+
+}  // namespace
diff --git a/absl/crc/internal/crc_memcpy_x86_64.cc b/absl/crc/internal/crc_memcpy_x86_64.cc
new file mode 100644
index 0000000..d42b08d
--- /dev/null
+++ b/absl/crc/internal/crc_memcpy_x86_64.cc
@@ -0,0 +1,432 @@
+// Copyright 2022 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.
+
+// Simultaneous memcopy and CRC-32C for x86-64.  Uses integer registers because
+// XMM registers do not support the CRC instruction (yet).  While copying,
+// compute the running CRC of the data being copied.
+//
+// It is assumed that any CPU running this code has SSE4.2 instructions
+// available (for CRC32C).  This file will do nothing if that is not true.
+//
+// The CRC instruction has a 3-byte latency, and we are stressing the ALU ports
+// here (unlike a traditional memcopy, which has almost no ALU use), so we will
+// need to copy in such a way that the CRC unit is used efficiently. We have two
+// regimes in this code:
+//  1. For operations of size < kCrcSmallSize, do the CRC then the memcpy
+//  2. For operations of size > kCrcSmallSize:
+//      a) compute an initial CRC + copy on a small amount of data to align the
+//         destination pointer on a 16-byte boundary.
+//      b) Split the data into 3 main regions and a tail (smaller than 48 bytes)
+//      c) Do the copy and CRC of the 3 main regions, interleaving (start with
+//         full cache line copies for each region, then move to single 16 byte
+//         pieces per region).
+//      d) Combine the CRCs with CRC32C::Concat.
+//      e) Copy the tail and extend the CRC with the CRC of the tail.
+// This method is not ideal for op sizes between ~1k and ~8k because CRC::Concat
+// takes a significant amount of time.  A medium-sized approach could be added
+// using 3 CRCs over fixed-size blocks where the zero-extensions required for
+// CRC32C::Concat can be precomputed.
+
+#ifdef __SSE4_2__
+#include <immintrin.h>
+#endif
+
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif
+
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <type_traits>
+
+#include "absl/base/dynamic_annotations.h"
+#include "absl/base/optimization.h"
+#include "absl/base/prefetch.h"
+#include "absl/crc/crc32c.h"
+#include "absl/crc/internal/cpu_detect.h"
+#include "absl/crc/internal/crc_memcpy.h"
+#include "absl/strings/string_view.h"
+
+#ifdef ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+namespace {
+
+inline crc32c_t ShortCrcCopy(char* dst, const char* src, std::size_t length,
+                             crc32c_t crc) {
+  // Small copy: just go 1 byte at a time: being nice to the branch predictor
+  // is more important here than anything else
+  uint32_t crc_uint32 = static_cast<uint32_t>(crc);
+  for (std::size_t i = 0; i < length; i++) {
+    uint8_t data = *reinterpret_cast<const uint8_t*>(src);
+    crc_uint32 = _mm_crc32_u8(crc_uint32, data);
+    *reinterpret_cast<uint8_t*>(dst) = data;
+    ++src;
+    ++dst;
+  }
+  return crc32c_t{crc_uint32};
+}
+
+constexpr size_t kIntLoadsPerVec = sizeof(__m128i) / sizeof(uint64_t);
+
+// Common function for copying the tails of multiple large regions.
+template <size_t vec_regions, size_t int_regions>
+inline void LargeTailCopy(crc32c_t* crcs, char** dst, const char** src,
+                          size_t region_size, size_t copy_rounds) {
+  std::array<__m128i, vec_regions> data;
+  std::array<uint64_t, kIntLoadsPerVec * int_regions> int_data;
+
+  while (copy_rounds > 0) {
+    for (size_t i = 0; i < vec_regions; i++) {
+      size_t region = i;
+
+      auto* vsrc =
+          reinterpret_cast<const __m128i*>(*src + region_size * region);
+      auto* vdst = reinterpret_cast<__m128i*>(*dst + region_size * region);
+
+      // Load the blocks, unaligned
+      data[i] = _mm_loadu_si128(vsrc);
+
+      // Store the blocks, aligned
+      _mm_store_si128(vdst, data[i]);
+
+      // Compute the running CRC
+      crcs[region] = crc32c_t{static_cast<uint32_t>(
+          _mm_crc32_u64(static_cast<uint32_t>(crcs[region]),
+                        static_cast<uint64_t>(_mm_extract_epi64(data[i], 0))))};
+      crcs[region] = crc32c_t{static_cast<uint32_t>(
+          _mm_crc32_u64(static_cast<uint32_t>(crcs[region]),
+                        static_cast<uint64_t>(_mm_extract_epi64(data[i], 1))))};
+    }
+
+    for (size_t i = 0; i < int_regions; i++) {
+      size_t region = vec_regions + i;
+
+      auto* usrc =
+          reinterpret_cast<const uint64_t*>(*src + region_size * region);
+      auto* udst = reinterpret_cast<uint64_t*>(*dst + region_size * region);
+
+      for (size_t j = 0; j < kIntLoadsPerVec; j++) {
+        size_t data_index = i * kIntLoadsPerVec + j;
+
+        int_data[data_index] = *(usrc + j);
+        crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64(
+            static_cast<uint32_t>(crcs[region]), int_data[data_index]))};
+
+        *(udst + j) = int_data[data_index];
+      }
+    }
+
+    // Increment pointers
+    *src += sizeof(__m128i);
+    *dst += sizeof(__m128i);
+    --copy_rounds;
+  }
+}
+
+}  // namespace
+
+template <size_t vec_regions, size_t int_regions>
+class AcceleratedCrcMemcpyEngine : public CrcMemcpyEngine {
+ public:
+  AcceleratedCrcMemcpyEngine() = default;
+  AcceleratedCrcMemcpyEngine(const AcceleratedCrcMemcpyEngine&) = delete;
+  AcceleratedCrcMemcpyEngine operator=(const AcceleratedCrcMemcpyEngine&) =
+      delete;
+
+  crc32c_t Compute(void* __restrict dst, const void* __restrict src,
+                   std::size_t length, crc32c_t initial_crc) const override;
+};
+
+template <size_t vec_regions, size_t int_regions>
+crc32c_t AcceleratedCrcMemcpyEngine<vec_regions, int_regions>::Compute(
+    void* __restrict dst, const void* __restrict src, std::size_t length,
+    crc32c_t initial_crc) const {
+  constexpr std::size_t kRegions = vec_regions + int_regions;
+  constexpr uint32_t kCrcDataXor = uint32_t{0xffffffff};
+  constexpr std::size_t kBlockSize = sizeof(__m128i);
+  constexpr std::size_t kCopyRoundSize = kRegions * kBlockSize;
+
+  // Number of blocks per cacheline.
+  constexpr std::size_t kBlocksPerCacheLine = ABSL_CACHELINE_SIZE / kBlockSize;
+
+  char* dst_bytes = static_cast<char*>(dst);
+  const char* src_bytes = static_cast<const char*>(src);
+
+  // Make sure that one prefetch per big block is enough to cover the whole
+  // dataset, and we don't prefetch too much.
+  static_assert(ABSL_CACHELINE_SIZE % kBlockSize == 0,
+                "Cache lines are not divided evenly into blocks, may have "
+                "unintended behavior!");
+
+  // Experimentally-determined boundary between a small and large copy.
+  // Below this number, spin-up and concatenation of CRCs takes enough time that
+  // it kills the throughput gains of using 3 regions and wide vectors.
+  constexpr size_t kCrcSmallSize = 256;
+
+  // Experimentally-determined prefetch distance.  Main loop copies will
+  // prefeth data 2 cache lines ahead.
+  constexpr std::size_t kPrefetchAhead = 2 * ABSL_CACHELINE_SIZE;
+
+  // Small-size CRC-memcpy : just do CRC + memcpy
+  if (length < kCrcSmallSize) {
+    crc32c_t crc =
+        ExtendCrc32c(initial_crc, absl::string_view(src_bytes, length));
+    memcpy(dst, src, length);
+    return crc;
+  }
+
+  // Start work on the CRC: undo the XOR from the previous calculation or set up
+  // the initial value of the CRC.
+  // initial_crc ^= kCrcDataXor;
+  initial_crc = crc32c_t{static_cast<uint32_t>(initial_crc) ^ kCrcDataXor};
+
+  // Do an initial alignment copy, so we can use aligned store instructions to
+  // the destination pointer.  We align the destination pointer because the
+  // penalty for an unaligned load is small compared to the penalty of an
+  // unaligned store on modern CPUs.
+  std::size_t bytes_from_last_aligned =
+      reinterpret_cast<uintptr_t>(dst) & (kBlockSize - 1);
+  if (bytes_from_last_aligned != 0) {
+    std::size_t bytes_for_alignment = kBlockSize - bytes_from_last_aligned;
+
+    // Do the short-sized copy and CRC.
+    initial_crc =
+        ShortCrcCopy(dst_bytes, src_bytes, bytes_for_alignment, initial_crc);
+    src_bytes += bytes_for_alignment;
+    dst_bytes += bytes_for_alignment;
+    length -= bytes_for_alignment;
+  }
+
+  // We are going to do the copy and CRC in kRegions regions to make sure that
+  // we can saturate the CRC unit.  The CRCs will be combined at the end of the
+  // run.  Copying will use the SSE registers, and we will extract words from
+  // the SSE registers to add to the CRC.  Initially, we run the loop one full
+  // cache line per region at a time, in order to insert prefetches.
+
+  // Initialize CRCs for kRegions regions.
+  crc32c_t crcs[kRegions];
+  crcs[0] = initial_crc;
+  for (size_t i = 1; i < kRegions; i++) {
+    crcs[i] = crc32c_t{kCrcDataXor};
+  }
+
+  // Find the number of rounds to copy and the region size.  Also compute the
+  // tail size here.
+  size_t copy_rounds = length / kCopyRoundSize;
+
+  // Find the size of each region and the size of the tail.
+  const std::size_t region_size = copy_rounds * kBlockSize;
+  const std::size_t tail_size = length - (kRegions * region_size);
+
+  // Holding registers for data in each region.
+  std::array<__m128i, vec_regions> vec_data;
+  std::array<uint64_t, int_regions * kIntLoadsPerVec> int_data;
+
+  // Main loop.
+  while (copy_rounds > kBlocksPerCacheLine) {
+    // Prefetch kPrefetchAhead bytes ahead of each pointer.
+    for (size_t i = 0; i < kRegions; i++) {
+      absl::PrefetchToLocalCache(src_bytes + kPrefetchAhead + region_size * i);
+      absl::PrefetchToLocalCache(dst_bytes + kPrefetchAhead + region_size * i);
+    }
+
+    // Load and store data, computing CRC on the way.
+    for (size_t i = 0; i < kBlocksPerCacheLine; i++) {
+      // Copy and CRC the data for the CRC regions.
+      for (size_t j = 0; j < vec_regions; j++) {
+        // Cycle which regions get vector load/store and integer load/store, to
+        // engage prefetching logic around vector load/stores and save issue
+        // slots by using the integer registers.
+        size_t region = (j + i) % kRegions;
+
+        auto* vsrc =
+            reinterpret_cast<const __m128i*>(src_bytes + region_size * region);
+        auto* vdst =
+            reinterpret_cast<__m128i*>(dst_bytes + region_size * region);
+
+        // Load and CRC data.
+        vec_data[j] = _mm_loadu_si128(vsrc + i);
+        crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64(
+            static_cast<uint32_t>(crcs[region]),
+            static_cast<uint64_t>(_mm_extract_epi64(vec_data[j], 0))))};
+        crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64(
+            static_cast<uint32_t>(crcs[region]),
+            static_cast<uint64_t>(_mm_extract_epi64(vec_data[j], 1))))};
+
+        // Store the data.
+        _mm_store_si128(vdst + i, vec_data[j]);
+      }
+
+      // Preload the partial CRCs for the CLMUL subregions.
+      for (size_t j = 0; j < int_regions; j++) {
+        // Cycle which regions get vector load/store and integer load/store, to
+        // engage prefetching logic around vector load/stores and save issue
+        // slots by using the integer registers.
+        size_t region = (j + vec_regions + i) % kRegions;
+
+        auto* usrc =
+            reinterpret_cast<const uint64_t*>(src_bytes + region_size * region);
+        auto* udst =
+            reinterpret_cast<uint64_t*>(dst_bytes + region_size * region);
+
+        for (size_t k = 0; k < kIntLoadsPerVec; k++) {
+          size_t data_index = j * kIntLoadsPerVec + k;
+
+          // Load and CRC the data.
+          int_data[data_index] = *(usrc + i * kIntLoadsPerVec + k);
+          crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64(
+              static_cast<uint32_t>(crcs[region]), int_data[data_index]))};
+
+          // Store the data.
+          *(udst + i * kIntLoadsPerVec + k) = int_data[data_index];
+        }
+      }
+    }
+
+    // Increment pointers
+    src_bytes += kBlockSize * kBlocksPerCacheLine;
+    dst_bytes += kBlockSize * kBlocksPerCacheLine;
+    copy_rounds -= kBlocksPerCacheLine;
+  }
+
+  // Copy and CRC the tails of each region.
+  LargeTailCopy<vec_regions, int_regions>(crcs, &dst_bytes, &src_bytes,
+                                          region_size, copy_rounds);
+
+  // Move the source and destination pointers to the end of the region
+  src_bytes += region_size * (kRegions - 1);
+  dst_bytes += region_size * (kRegions - 1);
+
+  // Finalize the first CRCs: XOR the internal CRCs by the XOR mask to undo the
+  // XOR done before doing block copy + CRCs.
+  for (size_t i = 0; i + 1 < kRegions; i++) {
+    crcs[i] = crc32c_t{static_cast<uint32_t>(crcs[i]) ^ kCrcDataXor};
+  }
+
+  // Build a CRC of the first kRegions - 1 regions.
+  crc32c_t full_crc = crcs[0];
+  for (size_t i = 1; i + 1 < kRegions; i++) {
+    full_crc = ConcatCrc32c(full_crc, crcs[i], region_size);
+  }
+
+  // Copy and CRC the tail through the XMM registers.
+  std::size_t tail_blocks = tail_size / kBlockSize;
+  LargeTailCopy<0, 1>(&crcs[kRegions - 1], &dst_bytes, &src_bytes, 0,
+                      tail_blocks);
+
+  // Final tail copy for under 16 bytes.
+  crcs[kRegions - 1] =
+      ShortCrcCopy(dst_bytes, src_bytes, tail_size - tail_blocks * kBlockSize,
+                   crcs[kRegions - 1]);
+
+  // Finalize and concatenate the final CRC, then return.
+  crcs[kRegions - 1] =
+      crc32c_t{static_cast<uint32_t>(crcs[kRegions - 1]) ^ kCrcDataXor};
+  return ConcatCrc32c(full_crc, crcs[kRegions - 1], region_size + tail_size);
+}
+
+CrcMemcpy::ArchSpecificEngines CrcMemcpy::GetArchSpecificEngines() {
+#ifdef UNDEFINED_BEHAVIOR_SANITIZER
+  // UBSAN does not play nicely with unaligned loads (which we use a lot).
+  // Get the underlying architecture.
+  CpuType cpu_type = GetCpuType();
+  switch (cpu_type) {
+    case CpuType::kUnknown:
+    case CpuType::kAmdRome:
+    case CpuType::kAmdNaples:
+    case CpuType::kIntelCascadelakeXeon:
+    case CpuType::kIntelSkylakeXeon:
+    case CpuType::kIntelSkylake:
+    case CpuType::kIntelBroadwell:
+    case CpuType::kIntelHaswell:
+    case CpuType::kIntelIvybridge:
+      return {
+          /*.temporal=*/new FallbackCrcMemcpyEngine(),
+          /*.non_temporal=*/new CrcNonTemporalMemcpyAVXEngine(),
+      };
+    // INTEL_SANDYBRIDGE performs better with SSE than AVX.
+    case CpuType::kIntelSandybridge:
+      return {
+          /*.temporal=*/new FallbackCrcMemcpyEngine(),
+          /*.non_temporal=*/new CrcNonTemporalMemcpyEngine(),
+      };
+    default:
+      return {/*.temporal=*/new FallbackCrcMemcpyEngine(),
+              /*.non_temporal=*/new FallbackCrcMemcpyEngine()};
+  }
+#else
+  // Get the underlying architecture.
+  CpuType cpu_type = GetCpuType();
+  switch (cpu_type) {
+    // On Zen 2, PEXTRQ uses 2 micro-ops, including one on the vector store port
+    // which data movement from the vector registers to the integer registers
+    // (where CRC32C happens) to crowd the same units as vector stores.  As a
+    // result, using that path exclusively causes bottlenecking on this port.
+    // We can avoid this bottleneck by using the integer side of the CPU for
+    // most operations rather than the vector side.  We keep a vector region to
+    // engage some of the prefetching logic in the cache hierarchy which seems
+    // to give vector instructions special treatment.  These prefetch units see
+    // strided access to each region, and do the right thing.
+    case CpuType::kAmdRome:
+    case CpuType::kAmdNaples:
+      return {
+          /*.temporal=*/new AcceleratedCrcMemcpyEngine<1, 2>(),
+          /*.non_temporal=*/new CrcNonTemporalMemcpyAVXEngine(),
+      };
+    // PCLMULQDQ is slow and we don't have wide enough issue width to take
+    // advantage of it.  For an unknown architecture, don't risk using CLMULs.
+    case CpuType::kIntelCascadelakeXeon:
+    case CpuType::kIntelSkylakeXeon:
+    case CpuType::kIntelSkylake:
+    case CpuType::kIntelBroadwell:
+    case CpuType::kIntelHaswell:
+    case CpuType::kIntelIvybridge:
+      return {
+          /*.temporal=*/new AcceleratedCrcMemcpyEngine<3, 0>(),
+          /*.non_temporal=*/new CrcNonTemporalMemcpyAVXEngine(),
+      };
+    // INTEL_SANDYBRIDGE performs better with SSE than AVX.
+    case CpuType::kIntelSandybridge:
+      return {
+          /*.temporal=*/new AcceleratedCrcMemcpyEngine<3, 0>(),
+          /*.non_temporal=*/new CrcNonTemporalMemcpyEngine(),
+      };
+    default:
+      return {/*.temporal=*/new FallbackCrcMemcpyEngine(),
+              /*.non_temporal=*/new FallbackCrcMemcpyEngine()};
+  }
+#endif  // UNDEFINED_BEHAVIOR_SANITIZER
+}
+
+// For testing, allow the user to specify which engine they want.
+std::unique_ptr<CrcMemcpyEngine> CrcMemcpy::GetTestEngine(int vector,
+                                                          int integer) {
+  if (vector == 3 && integer == 0) {
+    return std::make_unique<AcceleratedCrcMemcpyEngine<3, 0>>();
+  } else if (vector == 1 && integer == 2) {
+    return std::make_unique<AcceleratedCrcMemcpyEngine<1, 2>>();
+  }
+  return nullptr;
+}
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE
diff --git a/absl/crc/internal/crc_non_temporal_memcpy.cc b/absl/crc/internal/crc_non_temporal_memcpy.cc
new file mode 100644
index 0000000..adc867f
--- /dev/null
+++ b/absl/crc/internal/crc_non_temporal_memcpy.cc
@@ -0,0 +1,93 @@
+// Copyright 2022 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 "absl/base/config.h"
+#include "absl/crc/crc32c.h"
+#include "absl/crc/internal/crc_memcpy.h"
+#include "absl/crc/internal/non_temporal_memcpy.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+crc32c_t CrcNonTemporalMemcpyEngine::Compute(void* __restrict dst,
+                                             const void* __restrict src,
+                                             std::size_t length,
+                                             crc32c_t initial_crc) const {
+  constexpr size_t kBlockSize = 8192;
+  crc32c_t crc = initial_crc;
+
+  const char* src_bytes = reinterpret_cast<const char*>(src);
+  char* dst_bytes = reinterpret_cast<char*>(dst);
+
+  // Copy + CRC loop - run 8k chunks until we are out of full chunks.
+  std::size_t offset = 0;
+  for (; offset + kBlockSize < length; offset += kBlockSize) {
+    crc = absl::ExtendCrc32c(crc,
+                             absl::string_view(src_bytes + offset, kBlockSize));
+    non_temporal_store_memcpy(dst_bytes + offset, src_bytes + offset,
+                              kBlockSize);
+  }
+
+  // Save some work if length is 0.
+  if (offset < length) {
+    std::size_t final_copy_size = length - offset;
+    crc = ExtendCrc32c(crc,
+                       absl::string_view(src_bytes + offset, final_copy_size));
+
+    non_temporal_store_memcpy(dst_bytes + offset, src_bytes + offset,
+                              final_copy_size);
+  }
+
+  return crc;
+}
+
+crc32c_t CrcNonTemporalMemcpyAVXEngine::Compute(void* __restrict dst,
+                                                const void* __restrict src,
+                                                std::size_t length,
+                                                crc32c_t initial_crc) const {
+  constexpr size_t kBlockSize = 8192;
+  crc32c_t crc = initial_crc;
+
+  const char* src_bytes = reinterpret_cast<const char*>(src);
+  char* dst_bytes = reinterpret_cast<char*>(dst);
+
+  // Copy + CRC loop - run 8k chunks until we are out of full chunks.
+  std::size_t offset = 0;
+  for (; offset + kBlockSize < length; offset += kBlockSize) {
+    crc = ExtendCrc32c(crc, absl::string_view(src_bytes + offset, kBlockSize));
+
+    non_temporal_store_memcpy_avx(dst_bytes + offset, src_bytes + offset,
+                                  kBlockSize);
+  }
+
+  // Save some work if length is 0.
+  if (offset < length) {
+    std::size_t final_copy_size = length - offset;
+    crc = ExtendCrc32c(crc,
+                       absl::string_view(src_bytes + offset, final_copy_size));
+
+    non_temporal_store_memcpy_avx(dst_bytes + offset, src_bytes + offset,
+                                  final_copy_size);
+  }
+
+  return crc;
+}
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/crc/internal/crc_x86_arm_combined.cc b/absl/crc/internal/crc_x86_arm_combined.cc
new file mode 100644
index 0000000..ef521d2
--- /dev/null
+++ b/absl/crc/internal/crc_x86_arm_combined.cc
@@ -0,0 +1,725 @@
+// Copyright 2022 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.
+
+// Hardware accelerated CRC32 computation on Intel and ARM architecture.
+
+#include <cstddef>
+#include <cstdint>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/dynamic_annotations.h"
+#include "absl/base/internal/endian.h"
+#include "absl/base/prefetch.h"
+#include "absl/crc/internal/cpu_detect.h"
+#include "absl/crc/internal/crc.h"
+#include "absl/crc/internal/crc32_x86_arm_combined_simd.h"
+#include "absl/crc/internal/crc_internal.h"
+#include "absl/memory/memory.h"
+#include "absl/numeric/bits.h"
+
+#if defined(ABSL_CRC_INTERNAL_HAVE_ARM_SIMD) || \
+    defined(ABSL_CRC_INTERNAL_HAVE_X86_SIMD)
+#define ABSL_INTERNAL_CAN_USE_SIMD_CRC32C
+#endif
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+#if defined(ABSL_INTERNAL_CAN_USE_SIMD_CRC32C)
+
+// Implementation details not exported outside of file
+namespace {
+
+// Some machines have CRC acceleration hardware.
+// We can do a faster version of Extend() on such machines.
+class CRC32AcceleratedX86ARMCombined : public CRC32 {
+ public:
+  CRC32AcceleratedX86ARMCombined() {}
+  ~CRC32AcceleratedX86ARMCombined() override {}
+  void ExtendByZeroes(uint32_t* crc, size_t length) const override;
+  uint32_t ComputeZeroConstant(size_t length) const;
+
+ private:
+  CRC32AcceleratedX86ARMCombined(const CRC32AcceleratedX86ARMCombined&) =
+      delete;
+  CRC32AcceleratedX86ARMCombined& operator=(
+      const CRC32AcceleratedX86ARMCombined&) = delete;
+};
+
+// Constants for switching between algorithms.
+// Chosen by comparing speed at different powers of 2.
+constexpr size_t kSmallCutoff = 256;
+constexpr size_t kMediumCutoff = 2048;
+
+#define ABSL_INTERNAL_STEP1(crc)                      \
+  do {                                                \
+    crc = CRC32_u8(static_cast<uint32_t>(crc), *p++); \
+  } while (0)
+#define ABSL_INTERNAL_STEP2(crc)                                               \
+  do {                                                                         \
+    crc =                                                                      \
+        CRC32_u16(static_cast<uint32_t>(crc), absl::little_endian::Load16(p)); \
+    p += 2;                                                                    \
+  } while (0)
+#define ABSL_INTERNAL_STEP4(crc)                                               \
+  do {                                                                         \
+    crc =                                                                      \
+        CRC32_u32(static_cast<uint32_t>(crc), absl::little_endian::Load32(p)); \
+    p += 4;                                                                    \
+  } while (0)
+#define ABSL_INTERNAL_STEP8(crc, data)                  \
+  do {                                                  \
+    crc = CRC32_u64(static_cast<uint32_t>(crc),         \
+                    absl::little_endian::Load64(data)); \
+    data += 8;                                          \
+  } while (0)
+#define ABSL_INTERNAL_STEP8BY2(crc0, crc1, p0, p1) \
+  do {                                             \
+    ABSL_INTERNAL_STEP8(crc0, p0);                 \
+    ABSL_INTERNAL_STEP8(crc1, p1);                 \
+  } while (0)
+#define ABSL_INTERNAL_STEP8BY3(crc0, crc1, crc2, p0, p1, p2) \
+  do {                                                       \
+    ABSL_INTERNAL_STEP8(crc0, p0);                           \
+    ABSL_INTERNAL_STEP8(crc1, p1);                           \
+    ABSL_INTERNAL_STEP8(crc2, p2);                           \
+  } while (0)
+
+namespace {
+
+uint32_t multiply(uint32_t a, uint32_t b) {
+  V128 shifts = V128_From2x64(0, 1);
+  V128 power = V128_From2x64(0, a);
+  V128 crc = V128_From2x64(0, b);
+  V128 res = V128_PMulLow(power, crc);
+
+  // Combine crc values
+  res = V128_ShiftLeft64(res, shifts);
+  return static_cast<uint32_t>(V128_Extract32<1>(res)) ^
+         CRC32_u32(0, static_cast<uint32_t>(V128_Low64(res)));
+}
+
+// Powers of crc32c polynomial, for faster ExtendByZeros.
+// Verified against folly:
+// folly/hash/detail/Crc32CombineDetail.cpp
+constexpr uint32_t kCRC32CPowers[] = {
+    0x82f63b78, 0x6ea2d55c, 0x18b8ea18, 0x510ac59a, 0xb82be955, 0xb8fdb1e7,
+    0x88e56f72, 0x74c360a4, 0xe4172b16, 0x0d65762a, 0x35d73a62, 0x28461564,
+    0xbf455269, 0xe2ea32dc, 0xfe7740e6, 0xf946610b, 0x3c204f8f, 0x538586e3,
+    0x59726915, 0x734d5309, 0xbc1ac763, 0x7d0722cc, 0xd289cabe, 0xe94ca9bc,
+    0x05b74f3f, 0xa51e1f42, 0x40000000, 0x20000000, 0x08000000, 0x00800000,
+    0x00008000, 0x82f63b78, 0x6ea2d55c, 0x18b8ea18, 0x510ac59a, 0xb82be955,
+    0xb8fdb1e7, 0x88e56f72, 0x74c360a4, 0xe4172b16, 0x0d65762a, 0x35d73a62,
+    0x28461564, 0xbf455269, 0xe2ea32dc, 0xfe7740e6, 0xf946610b, 0x3c204f8f,
+    0x538586e3, 0x59726915, 0x734d5309, 0xbc1ac763, 0x7d0722cc, 0xd289cabe,
+    0xe94ca9bc, 0x05b74f3f, 0xa51e1f42, 0x40000000, 0x20000000, 0x08000000,
+    0x00800000, 0x00008000,
+};
+
+}  // namespace
+
+// Compute a magic constant, so that multiplying by it is the same as
+// extending crc by length zeros.
+uint32_t CRC32AcceleratedX86ARMCombined::ComputeZeroConstant(
+    size_t length) const {
+  // Lowest 2 bits are handled separately in ExtendByZeroes
+  length >>= 2;
+
+  int index = absl::countr_zero(length);
+  uint32_t prev = kCRC32CPowers[index];
+  length &= length - 1;
+
+  while (length) {
+    // For each bit of length, extend by 2**n zeros.
+    index = absl::countr_zero(length);
+    prev = multiply(prev, kCRC32CPowers[index]);
+    length &= length - 1;
+  }
+  return prev;
+}
+
+void CRC32AcceleratedX86ARMCombined::ExtendByZeroes(uint32_t* crc,
+                                                    size_t length) const {
+  uint32_t val = *crc;
+  // Don't bother with multiplication for small length.
+  switch (length & 3) {
+    case 0:
+      break;
+    case 1:
+      val = CRC32_u8(val, 0);
+      break;
+    case 2:
+      val = CRC32_u16(val, 0);
+      break;
+    case 3:
+      val = CRC32_u8(val, 0);
+      val = CRC32_u16(val, 0);
+      break;
+  }
+  if (length > 3) {
+    val = multiply(val, ComputeZeroConstant(length));
+  }
+  *crc = val;
+}
+
+// Taken from Intel paper "Fast CRC Computation for iSCSI Polynomial Using CRC32
+// Instruction"
+// https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/crc-iscsi-polynomial-crc32-instruction-paper.pdf
+// We only need every 4th value, because we unroll loop by 4.
+constexpr uint64_t kClmulConstants[] = {
+    0x09e4addf8, 0x0ba4fc28e, 0x00d3b6092, 0x09e4addf8, 0x0ab7aff2a,
+    0x102f9b8a2, 0x0b9e02b86, 0x00d3b6092, 0x1bf2e8b8a, 0x18266e456,
+    0x0d270f1a2, 0x0ab7aff2a, 0x11eef4f8e, 0x083348832, 0x0dd7e3b0c,
+    0x0b9e02b86, 0x0271d9844, 0x1b331e26a, 0x06b749fb2, 0x1bf2e8b8a,
+    0x0e6fc4e6a, 0x0ce7f39f4, 0x0d7a4825c, 0x0d270f1a2, 0x026f6a60a,
+    0x12ed0daac, 0x068bce87a, 0x11eef4f8e, 0x1329d9f7e, 0x0b3e32c28,
+    0x0170076fa, 0x0dd7e3b0c, 0x1fae1cc66, 0x010746f3c, 0x086d8e4d2,
+    0x0271d9844, 0x0b3af077a, 0x093a5f730, 0x1d88abd4a, 0x06b749fb2,
+    0x0c9c8b782, 0x0cec3662e, 0x1ddffc5d4, 0x0e6fc4e6a, 0x168763fa6,
+    0x0b0cd4768, 0x19b1afbc4, 0x0d7a4825c, 0x123888b7a, 0x00167d312,
+    0x133d7a042, 0x026f6a60a, 0x000bcf5f6, 0x19d34af3a, 0x1af900c24,
+    0x068bce87a, 0x06d390dec, 0x16cba8aca, 0x1f16a3418, 0x1329d9f7e,
+    0x19fb2a8b0, 0x02178513a, 0x1a0f717c4, 0x0170076fa,
+};
+
+enum class CutoffStrategy {
+  // Use 3 CRC streams to fold into 1.
+  Fold3,
+  // Unroll CRC instructions for 64 bytes.
+  Unroll64CRC,
+};
+
+// Base class for CRC32AcceleratedX86ARMCombinedMultipleStreams containing the
+// methods and data that don't need the template arguments.
+class CRC32AcceleratedX86ARMCombinedMultipleStreamsBase
+    : public CRC32AcceleratedX86ARMCombined {
+ protected:
+  // Update partialCRC with crc of 64 byte block. Calling FinalizePclmulStream
+  // would produce a single crc checksum, but it is expensive. PCLMULQDQ has a
+  // high latency, so we run 4 128-bit partial checksums that can be reduced to
+  // a single value by FinalizePclmulStream later. Computing crc for arbitrary
+  // polynomialas with PCLMULQDQ is described in Intel paper "Fast CRC
+  // Computation for Generic Polynomials Using PCLMULQDQ Instruction"
+  // https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf
+  // We are applying it to CRC32C polynomial.
+  ABSL_ATTRIBUTE_ALWAYS_INLINE void Process64BytesPclmul(
+      const uint8_t* p, V128* partialCRC) const {
+    V128 loopMultiplicands = V128_Load(reinterpret_cast<const V128*>(k1k2));
+
+    V128 partialCRC1 = partialCRC[0];
+    V128 partialCRC2 = partialCRC[1];
+    V128 partialCRC3 = partialCRC[2];
+    V128 partialCRC4 = partialCRC[3];
+
+    V128 tmp1 = V128_PMulHi(partialCRC1, loopMultiplicands);
+    V128 tmp2 = V128_PMulHi(partialCRC2, loopMultiplicands);
+    V128 tmp3 = V128_PMulHi(partialCRC3, loopMultiplicands);
+    V128 tmp4 = V128_PMulHi(partialCRC4, loopMultiplicands);
+    V128 data1 = V128_LoadU(reinterpret_cast<const V128*>(p + 16 * 0));
+    V128 data2 = V128_LoadU(reinterpret_cast<const V128*>(p + 16 * 1));
+    V128 data3 = V128_LoadU(reinterpret_cast<const V128*>(p + 16 * 2));
+    V128 data4 = V128_LoadU(reinterpret_cast<const V128*>(p + 16 * 3));
+    partialCRC1 = V128_PMulLow(partialCRC1, loopMultiplicands);
+    partialCRC2 = V128_PMulLow(partialCRC2, loopMultiplicands);
+    partialCRC3 = V128_PMulLow(partialCRC3, loopMultiplicands);
+    partialCRC4 = V128_PMulLow(partialCRC4, loopMultiplicands);
+    partialCRC1 = V128_Xor(tmp1, partialCRC1);
+    partialCRC2 = V128_Xor(tmp2, partialCRC2);
+    partialCRC3 = V128_Xor(tmp3, partialCRC3);
+    partialCRC4 = V128_Xor(tmp4, partialCRC4);
+    partialCRC1 = V128_Xor(partialCRC1, data1);
+    partialCRC2 = V128_Xor(partialCRC2, data2);
+    partialCRC3 = V128_Xor(partialCRC3, data3);
+    partialCRC4 = V128_Xor(partialCRC4, data4);
+    partialCRC[0] = partialCRC1;
+    partialCRC[1] = partialCRC2;
+    partialCRC[2] = partialCRC3;
+    partialCRC[3] = partialCRC4;
+  }
+
+  // Reduce partialCRC produced by Process64BytesPclmul into a single value,
+  // that represents crc checksum of all the processed bytes.
+  ABSL_ATTRIBUTE_ALWAYS_INLINE uint64_t
+  FinalizePclmulStream(V128* partialCRC) const {
+    V128 partialCRC1 = partialCRC[0];
+    V128 partialCRC2 = partialCRC[1];
+    V128 partialCRC3 = partialCRC[2];
+    V128 partialCRC4 = partialCRC[3];
+
+    // Combine 4 vectors of partial crc into a single vector.
+    V128 reductionMultiplicands =
+        V128_Load(reinterpret_cast<const V128*>(k5k6));
+
+    V128 low = V128_PMulLow(reductionMultiplicands, partialCRC1);
+    V128 high = V128_PMulHi(reductionMultiplicands, partialCRC1);
+
+    partialCRC1 = V128_Xor(low, high);
+    partialCRC1 = V128_Xor(partialCRC1, partialCRC2);
+
+    low = V128_PMulLow(reductionMultiplicands, partialCRC3);
+    high = V128_PMulHi(reductionMultiplicands, partialCRC3);
+
+    partialCRC3 = V128_Xor(low, high);
+    partialCRC3 = V128_Xor(partialCRC3, partialCRC4);
+
+    reductionMultiplicands = V128_Load(reinterpret_cast<const V128*>(k3k4));
+
+    low = V128_PMulLow(reductionMultiplicands, partialCRC1);
+    high = V128_PMulHi(reductionMultiplicands, partialCRC1);
+    V128 fullCRC = V128_Xor(low, high);
+    fullCRC = V128_Xor(fullCRC, partialCRC3);
+
+    // Reduce fullCRC into scalar value.
+    reductionMultiplicands = V128_Load(reinterpret_cast<const V128*>(k5k6));
+
+    V128 mask = V128_Load(reinterpret_cast<const V128*>(kMask));
+
+    V128 tmp = V128_PMul01(reductionMultiplicands, fullCRC);
+    fullCRC = V128_ShiftRight<8>(fullCRC);
+    fullCRC = V128_Xor(fullCRC, tmp);
+
+    reductionMultiplicands = V128_Load(reinterpret_cast<const V128*>(k7k0));
+
+    tmp = V128_ShiftRight<4>(fullCRC);
+    fullCRC = V128_And(fullCRC, mask);
+    fullCRC = V128_PMulLow(reductionMultiplicands, fullCRC);
+    fullCRC = V128_Xor(tmp, fullCRC);
+
+    reductionMultiplicands = V128_Load(reinterpret_cast<const V128*>(kPoly));
+
+    tmp = V128_And(fullCRC, mask);
+    tmp = V128_PMul01(reductionMultiplicands, tmp);
+    tmp = V128_And(tmp, mask);
+    tmp = V128_PMulLow(reductionMultiplicands, tmp);
+
+    fullCRC = V128_Xor(tmp, fullCRC);
+
+    return static_cast<uint64_t>(V128_Extract32<1>(fullCRC));
+  }
+
+  // Update crc with 64 bytes of data from p.
+  ABSL_ATTRIBUTE_ALWAYS_INLINE uint64_t Process64BytesCRC(const uint8_t* p,
+                                                          uint64_t crc) const {
+    for (int i = 0; i < 8; i++) {
+      crc =
+          CRC32_u64(static_cast<uint32_t>(crc), absl::little_endian::Load64(p));
+      p += 8;
+    }
+    return crc;
+  }
+
+  // Generated by crc32c_x86_test --crc32c_generate_constants=true
+  // and verified against constants in linux kernel for S390:
+  // https://github.com/torvalds/linux/blob/master/arch/s390/crypto/crc32le-vx.S
+  alignas(16) static constexpr uint64_t k1k2[2] = {0x0740eef02, 0x09e4addf8};
+  alignas(16) static constexpr uint64_t k3k4[2] = {0x1384aa63a, 0x0ba4fc28e};
+  alignas(16) static constexpr uint64_t k5k6[2] = {0x0f20c0dfe, 0x14cd00bd6};
+  alignas(16) static constexpr uint64_t k7k0[2] = {0x0dd45aab8, 0x000000000};
+  alignas(16) static constexpr uint64_t kPoly[2] = {0x105ec76f0, 0x0dea713f1};
+  alignas(16) static constexpr uint32_t kMask[4] = {~0u, 0u, ~0u, 0u};
+
+  // Medium runs of bytes are broken into groups of kGroupsSmall blocks of same
+  // size. Each group is CRCed in parallel then combined at the end of the
+  // block.
+  static constexpr size_t kGroupsSmall = 3;
+  // For large runs we use up to kMaxStreams blocks computed with CRC
+  // instruction, and up to kMaxStreams blocks computed with PCLMULQDQ, which
+  // are combined in the end.
+  static constexpr size_t kMaxStreams = 3;
+};
+
+#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
+alignas(16) constexpr uint64_t
+    CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::k1k2[2];
+alignas(16) constexpr uint64_t
+    CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::k3k4[2];
+alignas(16) constexpr uint64_t
+    CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::k5k6[2];
+alignas(16) constexpr uint64_t
+    CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::k7k0[2];
+alignas(16) constexpr uint64_t
+    CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::kPoly[2];
+alignas(16) constexpr uint32_t
+    CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::kMask[4];
+constexpr size_t
+    CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::kGroupsSmall;
+constexpr size_t CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::kMaxStreams;
+#endif  // ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
+
+template <size_t num_crc_streams, size_t num_pclmul_streams,
+          CutoffStrategy strategy>
+class CRC32AcceleratedX86ARMCombinedMultipleStreams
+    : public CRC32AcceleratedX86ARMCombinedMultipleStreamsBase {
+  ABSL_ATTRIBUTE_HOT
+  void Extend(uint32_t* crc, const void* bytes, size_t length) const override {
+    static_assert(num_crc_streams >= 1 && num_crc_streams <= kMaxStreams,
+                  "Invalid number of crc streams");
+    static_assert(num_pclmul_streams >= 0 && num_pclmul_streams <= kMaxStreams,
+                  "Invalid number of pclmul streams");
+    const uint8_t* p = static_cast<const uint8_t*>(bytes);
+    const uint8_t* e = p + length;
+    uint32_t l = *crc;
+    uint64_t l64;
+
+    // We have dedicated instruction for 1,2,4 and 8 bytes.
+    if (length & 8) {
+      ABSL_INTERNAL_STEP8(l, p);
+      length &= ~size_t{8};
+    }
+    if (length & 4) {
+      ABSL_INTERNAL_STEP4(l);
+      length &= ~size_t{4};
+    }
+    if (length & 2) {
+      ABSL_INTERNAL_STEP2(l);
+      length &= ~size_t{2};
+    }
+    if (length & 1) {
+      ABSL_INTERNAL_STEP1(l);
+      length &= ~size_t{1};
+    }
+    if (length == 0) {
+      *crc = l;
+      return;
+    }
+    // length is now multiple of 16.
+
+    // For small blocks just run simple loop, because cost of combining multiple
+    // streams is significant.
+    if (strategy != CutoffStrategy::Unroll64CRC) {
+      if (length < kSmallCutoff) {
+        while (length >= 16) {
+          ABSL_INTERNAL_STEP8(l, p);
+          ABSL_INTERNAL_STEP8(l, p);
+          length -= 16;
+        }
+        *crc = l;
+        return;
+      }
+    }
+
+    // For medium blocks we run 3 crc streams and combine them as described in
+    // Intel paper above. Running 4th stream doesn't help, because crc
+    // instruction has latency 3 and throughput 1.
+    if (length < kMediumCutoff) {
+      l64 = l;
+      if (strategy == CutoffStrategy::Fold3) {
+        uint64_t l641 = 0;
+        uint64_t l642 = 0;
+        const size_t blockSize = 32;
+        size_t bs = static_cast<size_t>(e - p) / kGroupsSmall / blockSize;
+        const uint8_t* p1 = p + bs * blockSize;
+        const uint8_t* p2 = p1 + bs * blockSize;
+
+        for (size_t i = 0; i + 1 < bs; ++i) {
+          ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2);
+          ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2);
+          ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2);
+          ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2);
+          PrefetchToLocalCache(
+              reinterpret_cast<const char*>(p + kPrefetchHorizonMedium));
+          PrefetchToLocalCache(
+              reinterpret_cast<const char*>(p1 + kPrefetchHorizonMedium));
+          PrefetchToLocalCache(
+              reinterpret_cast<const char*>(p2 + kPrefetchHorizonMedium));
+        }
+        // Don't run crc on last 8 bytes.
+        ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2);
+        ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2);
+        ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2);
+        ABSL_INTERNAL_STEP8BY2(l64, l641, p, p1);
+
+        V128 magic = *(reinterpret_cast<const V128*>(kClmulConstants) + bs - 1);
+
+        V128 tmp = V128_From2x64(0, l64);
+
+        V128 res1 = V128_PMulLow(tmp, magic);
+
+        tmp = V128_From2x64(0, l641);
+
+        V128 res2 = V128_PMul10(tmp, magic);
+        V128 x = V128_Xor(res1, res2);
+        l64 = static_cast<uint64_t>(V128_Low64(x)) ^
+              absl::little_endian::Load64(p2);
+        l64 = CRC32_u64(static_cast<uint32_t>(l642), l64);
+
+        p = p2 + 8;
+      } else if (strategy == CutoffStrategy::Unroll64CRC) {
+        while ((e - p) >= 64) {
+          l64 = Process64BytesCRC(p, l64);
+          p += 64;
+        }
+      }
+    } else {
+      // There is a lot of data, we can ignore combine costs and run all
+      // requested streams (num_crc_streams + num_pclmul_streams),
+      // using prefetch. CRC and PCLMULQDQ use different cpu execution units,
+      // so on some cpus it makes sense to execute both of them for different
+      // streams.
+
+      // Point x at first 8-byte aligned byte in string.
+      const uint8_t* x = RoundUp<8>(p);
+      // Process bytes until p is 8-byte aligned, if that isn't past the end.
+      while (p != x) {
+        ABSL_INTERNAL_STEP1(l);
+      }
+
+      size_t bs = static_cast<size_t>(e - p) /
+                  (num_crc_streams + num_pclmul_streams) / 64;
+      const uint8_t* crc_streams[kMaxStreams];
+      const uint8_t* pclmul_streams[kMaxStreams];
+      // We are guaranteed to have at least one crc stream.
+      crc_streams[0] = p;
+      for (size_t i = 1; i < num_crc_streams; i++) {
+        crc_streams[i] = crc_streams[i - 1] + bs * 64;
+      }
+      pclmul_streams[0] = crc_streams[num_crc_streams - 1] + bs * 64;
+      for (size_t i = 1; i < num_pclmul_streams; i++) {
+        pclmul_streams[i] = pclmul_streams[i - 1] + bs * 64;
+      }
+
+      // Per stream crc sums.
+      uint64_t l64_crc[kMaxStreams] = {l};
+      uint64_t l64_pclmul[kMaxStreams] = {0};
+
+      // Peel first iteration, because PCLMULQDQ stream, needs setup.
+      for (size_t i = 0; i < num_crc_streams; i++) {
+        l64_crc[i] = Process64BytesCRC(crc_streams[i], l64_crc[i]);
+        crc_streams[i] += 16 * 4;
+      }
+
+      V128 partialCRC[kMaxStreams][4];
+      for (size_t i = 0; i < num_pclmul_streams; i++) {
+        partialCRC[i][0] = V128_LoadU(
+            reinterpret_cast<const V128*>(pclmul_streams[i] + 16 * 0));
+        partialCRC[i][1] = V128_LoadU(
+            reinterpret_cast<const V128*>(pclmul_streams[i] + 16 * 1));
+        partialCRC[i][2] = V128_LoadU(
+            reinterpret_cast<const V128*>(pclmul_streams[i] + 16 * 2));
+        partialCRC[i][3] = V128_LoadU(
+            reinterpret_cast<const V128*>(pclmul_streams[i] + 16 * 3));
+        pclmul_streams[i] += 16 * 4;
+      }
+
+      for (size_t i = 1; i < bs; i++) {
+        // Prefetch data for next iterations.
+        for (size_t j = 0; j < num_crc_streams; j++) {
+          PrefetchToLocalCache(
+              reinterpret_cast<const char*>(crc_streams[j] + kPrefetchHorizon));
+        }
+        for (size_t j = 0; j < num_pclmul_streams; j++) {
+          PrefetchToLocalCache(reinterpret_cast<const char*>(pclmul_streams[j] +
+                                                             kPrefetchHorizon));
+        }
+
+        // We process each stream in 64 byte blocks. This can be written as
+        // for (int i = 0; i < num_pclmul_streams; i++) {
+        //   Process64BytesPclmul(pclmul_streams[i], partialCRC[i]);
+        //   pclmul_streams[i] += 16 * 4;
+        // }
+        // for (int i = 0; i < num_crc_streams; i++) {
+        //   l64_crc[i] = Process64BytesCRC(crc_streams[i], l64_crc[i]);
+        //   crc_streams[i] += 16*4;
+        // }
+        // But unrolling and interleaving PCLMULQDQ and CRC blocks manually
+        // gives ~2% performance boost.
+        l64_crc[0] = Process64BytesCRC(crc_streams[0], l64_crc[0]);
+        crc_streams[0] += 16 * 4;
+        if (num_pclmul_streams > 0) {
+          Process64BytesPclmul(pclmul_streams[0], partialCRC[0]);
+          pclmul_streams[0] += 16 * 4;
+        }
+        if (num_crc_streams > 1) {
+          l64_crc[1] = Process64BytesCRC(crc_streams[1], l64_crc[1]);
+          crc_streams[1] += 16 * 4;
+        }
+        if (num_pclmul_streams > 1) {
+          Process64BytesPclmul(pclmul_streams[1], partialCRC[1]);
+          pclmul_streams[1] += 16 * 4;
+        }
+        if (num_crc_streams > 2) {
+          l64_crc[2] = Process64BytesCRC(crc_streams[2], l64_crc[2]);
+          crc_streams[2] += 16 * 4;
+        }
+        if (num_pclmul_streams > 2) {
+          Process64BytesPclmul(pclmul_streams[2], partialCRC[2]);
+          pclmul_streams[2] += 16 * 4;
+        }
+      }
+
+      // PCLMULQDQ based streams require special final step;
+      // CRC based don't.
+      for (size_t i = 0; i < num_pclmul_streams; i++) {
+        l64_pclmul[i] = FinalizePclmulStream(partialCRC[i]);
+      }
+
+      // Combine all streams into single result.
+      uint32_t magic = ComputeZeroConstant(bs * 64);
+      l64 = l64_crc[0];
+      for (size_t i = 1; i < num_crc_streams; i++) {
+        l64 = multiply(static_cast<uint32_t>(l64), magic);
+        l64 ^= l64_crc[i];
+      }
+      for (size_t i = 0; i < num_pclmul_streams; i++) {
+        l64 = multiply(static_cast<uint32_t>(l64), magic);
+        l64 ^= l64_pclmul[i];
+      }
+
+      // Update p.
+      if (num_pclmul_streams > 0) {
+        p = pclmul_streams[num_pclmul_streams - 1];
+      } else {
+        p = crc_streams[num_crc_streams - 1];
+      }
+    }
+    l = static_cast<uint32_t>(l64);
+
+    while ((e - p) >= 16) {
+      ABSL_INTERNAL_STEP8(l, p);
+      ABSL_INTERNAL_STEP8(l, p);
+    }
+    // Process the last few bytes
+    while (p != e) {
+      ABSL_INTERNAL_STEP1(l);
+    }
+
+#undef ABSL_INTERNAL_STEP8BY3
+#undef ABSL_INTERNAL_STEP8BY2
+#undef ABSL_INTERNAL_STEP8
+#undef ABSL_INTERNAL_STEP4
+#undef ABSL_INTERNAL_STEP2
+#undef ABSL_INTERNAL_STEP1
+
+    *crc = l;
+  }
+};
+
+}  // namespace
+
+// Intel processors with SSE4.2 have an instruction for one particular
+// 32-bit CRC polynomial:  crc32c
+CRCImpl* TryNewCRC32AcceleratedX86ARMCombined() {
+  CpuType type = GetCpuType();
+  switch (type) {
+    case CpuType::kIntelHaswell:
+    case CpuType::kAmdRome:
+    case CpuType::kAmdNaples:
+    case CpuType::kAmdMilan:
+      return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
+          3, 1, CutoffStrategy::Fold3>();
+    // PCLMULQDQ is fast, use combined PCLMULQDQ + CRC implementation.
+    case CpuType::kIntelCascadelakeXeon:
+    case CpuType::kIntelSkylakeXeon:
+    case CpuType::kIntelBroadwell:
+    case CpuType::kIntelSkylake:
+      return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
+          3, 2, CutoffStrategy::Fold3>();
+    // PCLMULQDQ is slow, don't use it.
+    case CpuType::kIntelIvybridge:
+    case CpuType::kIntelSandybridge:
+    case CpuType::kIntelWestmere:
+      return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
+          3, 0, CutoffStrategy::Fold3>();
+    case CpuType::kArmNeoverseN1:
+      return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
+          1, 1, CutoffStrategy::Unroll64CRC>();
+#if defined(__aarch64__)
+    default:
+      // Not all ARM processors support the needed instructions, so check here
+      // before trying to use an accelerated implementation.
+      if (SupportsArmCRC32PMULL()) {
+        return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
+            1, 1, CutoffStrategy::Unroll64CRC>();
+      } else {
+        return nullptr;
+      }
+#else
+    default:
+      // Something else, play it safe and assume slow PCLMULQDQ.
+      return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
+          3, 0, CutoffStrategy::Fold3>();
+#endif
+  }
+}
+
+std::vector<std::unique_ptr<CRCImpl>> NewCRC32AcceleratedX86ARMCombinedAll() {
+  auto ret = std::vector<std::unique_ptr<CRCImpl>>();
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    1, 0, CutoffStrategy::Fold3>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    1, 1, CutoffStrategy::Fold3>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    1, 2, CutoffStrategy::Fold3>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    1, 3, CutoffStrategy::Fold3>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    2, 0, CutoffStrategy::Fold3>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    2, 1, CutoffStrategy::Fold3>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    2, 2, CutoffStrategy::Fold3>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    2, 3, CutoffStrategy::Fold3>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    3, 0, CutoffStrategy::Fold3>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    3, 1, CutoffStrategy::Fold3>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    3, 2, CutoffStrategy::Fold3>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    3, 3, CutoffStrategy::Fold3>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    1, 0, CutoffStrategy::Unroll64CRC>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    1, 1, CutoffStrategy::Unroll64CRC>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    1, 2, CutoffStrategy::Unroll64CRC>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    1, 3, CutoffStrategy::Unroll64CRC>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    2, 0, CutoffStrategy::Unroll64CRC>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    2, 1, CutoffStrategy::Unroll64CRC>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    2, 2, CutoffStrategy::Unroll64CRC>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    2, 3, CutoffStrategy::Unroll64CRC>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    3, 0, CutoffStrategy::Unroll64CRC>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    3, 1, CutoffStrategy::Unroll64CRC>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    3, 2, CutoffStrategy::Unroll64CRC>>());
+  ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
+                    3, 3, CutoffStrategy::Unroll64CRC>>());
+
+  return ret;
+}
+
+#else  // !ABSL_INTERNAL_CAN_USE_SIMD_CRC32C
+
+std::vector<std::unique_ptr<CRCImpl>> NewCRC32AcceleratedX86ARMCombinedAll() {
+  return std::vector<std::unique_ptr<CRCImpl>>();
+}
+
+// no hardware acceleration available
+CRCImpl* TryNewCRC32AcceleratedX86ARMCombined() { return nullptr; }
+
+#endif
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/crc/internal/non_temporal_arm_intrinsics.h b/absl/crc/internal/non_temporal_arm_intrinsics.h
new file mode 100644
index 0000000..9e5ccfc
--- /dev/null
+++ b/absl/crc/internal/non_temporal_arm_intrinsics.h
@@ -0,0 +1,79 @@
+// Copyright 2022 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_CRC_INTERNAL_NON_TEMPORAL_ARM_INTRINSICS_H_
+#define ABSL_CRC_INTERNAL_NON_TEMPORAL_ARM_INTRINSICS_H_
+
+#include "absl/base/config.h"
+
+#ifdef __aarch64__
+#include <arm_neon.h>
+
+typedef int64x2_t __m128i; /* 128-bit vector containing integers */
+#define vreinterpretq_m128i_s32(x) vreinterpretq_s64_s32(x)
+#define vreinterpretq_s64_m128i(x) (x)
+
+// Guarantees that every preceding store is globally visible before any
+// subsequent store.
+// https://msdn.microsoft.com/en-us/library/5h2w73d1%28v=vs.90%29.aspx
+static inline __attribute__((always_inline)) void _mm_sfence(void) {
+  __sync_synchronize();
+}
+
+// Load 128-bits of integer data from unaligned memory into dst. This intrinsic
+// may perform better than _mm_loadu_si128 when the data crosses a cache line
+// boundary.
+//
+//   dst[127:0] := MEM[mem_addr+127:mem_addr]
+//
+// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_lddqu_si128
+#define _mm_lddqu_si128 _mm_loadu_si128
+
+// Loads 128-bit value. :
+// https://msdn.microsoft.com/zh-cn/library/f4k12ae8(v=vs.90).aspx
+static inline __attribute__((always_inline)) __m128i _mm_loadu_si128(
+    const __m128i *p) {
+  return vreinterpretq_m128i_s32(vld1q_s32((const int32_t *)p));
+}
+
+// Stores the data in a to the address p without polluting the caches.  If the
+// cache line containing address p is already in the cache, the cache will be
+// updated.
+// https://msdn.microsoft.com/en-us/library/ba08y07y%28v=vs.90%29.aspx
+static inline __attribute__((always_inline)) void _mm_stream_si128(__m128i *p,
+                                                                   __m128i a) {
+#if ABSL_HAVE_BUILTIN(__builtin_nontemporal_store)
+  __builtin_nontemporal_store(a, p);
+#else
+  vst1q_s64((int64_t *)p, vreinterpretq_s64_m128i(a));
+#endif
+}
+
+// Sets the 16 signed 8-bit integer values.
+// https://msdn.microsoft.com/en-us/library/x0cx8zd3(v=vs.90).aspx
+static inline __attribute__((always_inline)) __m128i _mm_set_epi8(
+    signed char b15, signed char b14, signed char b13, signed char b12,
+    signed char b11, signed char b10, signed char b9, signed char b8,
+    signed char b7, signed char b6, signed char b5, signed char b4,
+    signed char b3, signed char b2, signed char b1, signed char b0) {
+  int8_t __attribute__((aligned(16)))
+  data[16] = {(int8_t)b0,  (int8_t)b1,  (int8_t)b2,  (int8_t)b3,
+              (int8_t)b4,  (int8_t)b5,  (int8_t)b6,  (int8_t)b7,
+              (int8_t)b8,  (int8_t)b9,  (int8_t)b10, (int8_t)b11,
+              (int8_t)b12, (int8_t)b13, (int8_t)b14, (int8_t)b15};
+  return (__m128i)vld1q_s8(data);
+}
+#endif  // __aarch64__
+
+#endif  // ABSL_CRC_INTERNAL_NON_TEMPORAL_ARM_INTRINSICS_H_
diff --git a/absl/crc/internal/non_temporal_memcpy.h b/absl/crc/internal/non_temporal_memcpy.h
new file mode 100644
index 0000000..b3d94ba
--- /dev/null
+++ b/absl/crc/internal/non_temporal_memcpy.h
@@ -0,0 +1,180 @@
+// Copyright 2022 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_CRC_INTERNAL_NON_TEMPORAL_MEMCPY_H_
+#define ABSL_CRC_INTERNAL_NON_TEMPORAL_MEMCPY_H_
+
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif
+
+#ifdef __SSE__
+#include <xmmintrin.h>
+#endif
+
+#ifdef __SSE2__
+#include <emmintrin.h>
+#endif
+
+#ifdef __SSE3__
+#include <pmmintrin.h>
+#endif
+
+#ifdef __AVX__
+#include <immintrin.h>
+#endif
+
+#ifdef __aarch64__
+#include "absl/crc/internal/non_temporal_arm_intrinsics.h"
+#endif
+
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <cstring>
+
+#include "absl/base/config.h"
+#include "absl/base/optimization.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+// This non-temporal memcpy does regular load and non-temporal store memory
+// copy. It is compatible to both 16-byte aligned and unaligned addresses. If
+// data at the destination is not immediately accessed, using non-temporal
+// memcpy can save 1 DRAM load of the destination cacheline.
+constexpr size_t kCacheLineSize = ABSL_CACHELINE_SIZE;
+
+// If the objects overlap, the behavior is undefined.
+inline void *non_temporal_store_memcpy(void *__restrict dst,
+                                       const void *__restrict src, size_t len) {
+#if defined(__SSE3__) || defined(__aarch64__) || \
+    (defined(_MSC_VER) && defined(__AVX__))
+  // This implementation requires SSE3.
+  // MSVC cannot target SSE3 directly, but when MSVC targets AVX,
+  // SSE3 support is implied.
+  uint8_t *d = reinterpret_cast<uint8_t *>(dst);
+  const uint8_t *s = reinterpret_cast<const uint8_t *>(src);
+
+  // memcpy() the misaligned header. At the end of this if block, <d> is
+  // aligned to a 64-byte cacheline boundary or <len> == 0.
+  if (reinterpret_cast<uintptr_t>(d) & (kCacheLineSize - 1)) {
+    uintptr_t bytes_before_alignment_boundary =
+        kCacheLineSize -
+        (reinterpret_cast<uintptr_t>(d) & (kCacheLineSize - 1));
+    size_t header_len = (std::min)(bytes_before_alignment_boundary, len);
+    assert(bytes_before_alignment_boundary < kCacheLineSize);
+    memcpy(d, s, header_len);
+    d += header_len;
+    s += header_len;
+    len -= header_len;
+  }
+
+  if (len >= kCacheLineSize) {
+    _mm_sfence();
+    __m128i *dst_cacheline = reinterpret_cast<__m128i *>(d);
+    const __m128i *src_cacheline = reinterpret_cast<const __m128i *>(s);
+    constexpr int kOpsPerCacheLine = kCacheLineSize / sizeof(__m128i);
+    size_t loops = len / kCacheLineSize;
+
+    while (len >= kCacheLineSize) {
+      __m128i temp1, temp2, temp3, temp4;
+      temp1 = _mm_lddqu_si128(src_cacheline + 0);
+      temp2 = _mm_lddqu_si128(src_cacheline + 1);
+      temp3 = _mm_lddqu_si128(src_cacheline + 2);
+      temp4 = _mm_lddqu_si128(src_cacheline + 3);
+      _mm_stream_si128(dst_cacheline + 0, temp1);
+      _mm_stream_si128(dst_cacheline + 1, temp2);
+      _mm_stream_si128(dst_cacheline + 2, temp3);
+      _mm_stream_si128(dst_cacheline + 3, temp4);
+      src_cacheline += kOpsPerCacheLine;
+      dst_cacheline += kOpsPerCacheLine;
+      len -= kCacheLineSize;
+    }
+    d += loops * kCacheLineSize;
+    s += loops * kCacheLineSize;
+    _mm_sfence();
+  }
+
+  // memcpy the tail.
+  if (len) {
+    memcpy(d, s, len);
+  }
+  return dst;
+#else
+  // Fallback to regular memcpy.
+  return memcpy(dst, src, len);
+#endif  // __SSE3__ || __aarch64__ || (_MSC_VER && __AVX__)
+}
+
+inline void *non_temporal_store_memcpy_avx(void *__restrict dst,
+                                           const void *__restrict src,
+                                           size_t len) {
+#ifdef __AVX__
+  uint8_t *d = reinterpret_cast<uint8_t *>(dst);
+  const uint8_t *s = reinterpret_cast<const uint8_t *>(src);
+
+  // memcpy() the misaligned header. At the end of this if block, <d> is
+  // aligned to a 64-byte cacheline boundary or <len> == 0.
+  if (reinterpret_cast<uintptr_t>(d) & (kCacheLineSize - 1)) {
+    uintptr_t bytes_before_alignment_boundary =
+        kCacheLineSize -
+        (reinterpret_cast<uintptr_t>(d) & (kCacheLineSize - 1));
+    size_t header_len = (std::min)(bytes_before_alignment_boundary, len);
+    assert(bytes_before_alignment_boundary < kCacheLineSize);
+    memcpy(d, s, header_len);
+    d += header_len;
+    s += header_len;
+    len -= header_len;
+  }
+
+  if (len >= kCacheLineSize) {
+    _mm_sfence();
+    __m256i *dst_cacheline = reinterpret_cast<__m256i *>(d);
+    const __m256i *src_cacheline = reinterpret_cast<const __m256i *>(s);
+    constexpr int kOpsPerCacheLine = kCacheLineSize / sizeof(__m256i);
+    size_t loops = len / kCacheLineSize;
+
+    while (len >= kCacheLineSize) {
+      __m256i temp1, temp2;
+      temp1 = _mm256_lddqu_si256(src_cacheline + 0);
+      temp2 = _mm256_lddqu_si256(src_cacheline + 1);
+      _mm256_stream_si256(dst_cacheline + 0, temp1);
+      _mm256_stream_si256(dst_cacheline + 1, temp2);
+      src_cacheline += kOpsPerCacheLine;
+      dst_cacheline += kOpsPerCacheLine;
+      len -= kCacheLineSize;
+    }
+    d += loops * kCacheLineSize;
+    s += loops * kCacheLineSize;
+    _mm_sfence();
+  }
+
+  // memcpy the tail.
+  if (len) {
+    memcpy(d, s, len);
+  }
+  return dst;
+#else
+  // Fallback to regular memcpy when AVX is not available.
+  return memcpy(dst, src, len);
+#endif  // __AVX__
+}
+
+}  // namespace crc_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_CRC_INTERNAL_NON_TEMPORAL_MEMCPY_H_
diff --git a/absl/crc/internal/non_temporal_memcpy_test.cc b/absl/crc/internal/non_temporal_memcpy_test.cc
new file mode 100644
index 0000000..eb07a55
--- /dev/null
+++ b/absl/crc/internal/non_temporal_memcpy_test.cc
@@ -0,0 +1,88 @@
+// Copyright 2022 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/crc/internal/non_temporal_memcpy.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <iostream>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+namespace {
+
+struct TestParam {
+  size_t copy_size;
+  uint32_t src_offset;
+  uint32_t dst_offset;
+};
+
+class NonTemporalMemcpyTest : public testing::TestWithParam<TestParam> {
+ protected:
+  void SetUp() override {
+    // Make buf_size multiple of 16 bytes.
+    size_t buf_size = ((std::max(GetParam().src_offset, GetParam().dst_offset) +
+                        GetParam().copy_size) +
+                       15) /
+                      16 * 16;
+    a_.resize(buf_size);
+    b_.resize(buf_size);
+    for (size_t i = 0; i < buf_size; i++) {
+      a_[i] = static_cast<uint8_t>(i % 256);
+      b_[i] = ~a_[i];
+    }
+  }
+
+  std::vector<uint8_t> a_, b_;
+};
+
+TEST_P(NonTemporalMemcpyTest, SSEEquality) {
+  uint8_t *src = a_.data() + GetParam().src_offset;
+  uint8_t *dst = b_.data() + GetParam().dst_offset;
+  absl::crc_internal::non_temporal_store_memcpy(dst, src, GetParam().copy_size);
+  for (size_t i = 0; i < GetParam().copy_size; i++) {
+    EXPECT_EQ(src[i], dst[i]);
+  }
+}
+
+TEST_P(NonTemporalMemcpyTest, AVXEquality) {
+  uint8_t* src = a_.data() + GetParam().src_offset;
+  uint8_t* dst = b_.data() + GetParam().dst_offset;
+
+  absl::crc_internal::non_temporal_store_memcpy_avx(dst, src,
+                                                    GetParam().copy_size);
+  for (size_t i = 0; i < GetParam().copy_size; i++) {
+    EXPECT_EQ(src[i], dst[i]);
+  }
+}
+
+// 63B is smaller than one cacheline operation thus the non-temporal routine
+// will not be called.
+// 4352B is sufficient for testing 4092B data copy with room for offsets.
+constexpr TestParam params[] = {
+    {63, 0, 0},       {58, 5, 5},    {61, 2, 0},    {61, 0, 2},
+    {58, 5, 2},       {4096, 0, 0},  {4096, 0, 1},  {4096, 0, 2},
+    {4096, 0, 3},     {4096, 0, 4},  {4096, 0, 5},  {4096, 0, 6},
+    {4096, 0, 7},     {4096, 0, 8},  {4096, 0, 9},  {4096, 0, 10},
+    {4096, 0, 11},    {4096, 0, 12}, {4096, 0, 13}, {4096, 0, 14},
+    {4096, 0, 15},    {4096, 7, 7},  {4096, 3, 0},  {4096, 1, 0},
+    {4096, 9, 3},     {4096, 9, 11}, {8192, 0, 0},  {8192, 5, 2},
+    {1024768, 7, 11}, {1, 0, 0},     {1, 0, 1},     {1, 1, 0},
+    {1, 1, 1}};
+
+INSTANTIATE_TEST_SUITE_P(ParameterizedNonTemporalMemcpyTest,
+                         NonTemporalMemcpyTest, testing::ValuesIn(params));
+
+}  // namespace
diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel
index a40285c..e89dbae 100644
--- a/absl/debugging/BUILD.bazel
+++ b/absl/debugging/BUILD.bazel
@@ -49,10 +49,23 @@
         ":debugging_internal",
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/base:dynamic_annotations",
         "//absl/base:raw_logging_internal",
     ],
 )
 
+cc_test(
+    name = "stacktrace_test",
+    srcs = ["stacktrace_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":stacktrace",
+        "//absl/base:core_headers",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
 cc_library(
     name = "symbolize",
     srcs = [
@@ -71,6 +84,10 @@
     linkopts = ABSL_DEFAULT_LINKOPTS + select({
         "//absl:msvc_compiler": ["-DEFAULTLIB:dbghelp.lib"],
         "//absl:clang-cl_compiler": ["-DEFAULTLIB:dbghelp.lib"],
+        "//absl:mingw_compiler": [
+            "-DEFAULTLIB:dbghelp.lib",
+            "-ldbghelp",
+        ],
         "//conditions:default": [],
     }),
     deps = [
diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt
index 051e701..ef6b496 100644
--- a/absl/debugging/CMakeLists.txt
+++ b/absl/debugging/CMakeLists.txt
@@ -41,10 +41,24 @@
     absl::debugging_internal
     absl::config
     absl::core_headers
+    absl::dynamic_annotations
     absl::raw_logging_internal
   PUBLIC
 )
 
+absl_cc_test(
+  NAME
+    stacktrace_test
+  SRCS
+    "stacktrace_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::stacktrace
+    absl::core_headers
+    GTest::gmock_main
+)
+
 absl_cc_library(
   NAME
     symbolize
@@ -62,7 +76,7 @@
     ${ABSL_DEFAULT_COPTS}
   LINKOPTS
     ${ABSL_DEFAULT_LINKOPTS}
-    $<$<BOOL:${MINGW}>:"dbghelp">
+    $<$<BOOL:${MINGW}>:-ldbghelp>
   DEPS
     absl::debugging_internal
     absl::demangle_internal
diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc
index 5e8f0b0..9f399d0 100644
--- a/absl/debugging/failure_signal_handler.cc
+++ b/absl/debugging/failure_signal_handler.cc
@@ -33,6 +33,10 @@
 #include <sys/mman.h>
 #endif
 
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif
+
 #include <algorithm>
 #include <atomic>
 #include <cerrno>
@@ -138,7 +142,8 @@
   const size_t page_mask = static_cast<size_t>(sysconf(_SC_PAGESIZE)) - 1;
 #endif
   size_t stack_size =
-      (std::max<size_t>(SIGSTKSZ, 65536) + page_mask) & ~page_mask;
+      (std::max(static_cast<size_t>(SIGSTKSZ), size_t{65536}) + page_mask) &
+      ~page_mask;
 #if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
     defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER)
   // Account for sanitizer instrumentation requiring additional stack space.
@@ -171,6 +176,20 @@
   if (sigaltstack(&sigstk, nullptr) != 0) {
     ABSL_RAW_LOG(FATAL, "sigaltstack() failed with errno=%d", errno);
   }
+
+#ifdef __linux__
+#if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME)
+  // Make a best-effort attempt to name the allocated region in
+  // /proc/$PID/smaps.
+  //
+  // The call to prctl() may fail if the kernel was not configured with the
+  // CONFIG_ANON_VMA_NAME kernel option.  This is OK since the call is
+  // primarily a debugging aid.
+  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, sigstk.ss_sp, sigstk.ss_size,
+        "absl-signalstack");
+#endif
+#endif  // __linux__
+
   return true;
 }
 
diff --git a/absl/debugging/failure_signal_handler.h b/absl/debugging/failure_signal_handler.h
index 500115c..5e03478 100644
--- a/absl/debugging/failure_signal_handler.h
+++ b/absl/debugging/failure_signal_handler.h
@@ -62,7 +62,7 @@
   // If true, try to run signal handlers on an alternate stack (if supported on
   // the given platform). An alternate stack is useful for program crashes due
   // to a stack overflow; by running on a alternate stack, the signal handler
-  // may run even when normal stack space has been exausted. The downside of
+  // may run even when normal stack space has been exhausted. The downside of
   // using an alternate stack is that extra memory for the alternate stack needs
   // to be pre-allocated.
   bool use_alternate_stack = true;
diff --git a/absl/debugging/internal/demangle_test.cc b/absl/debugging/internal/demangle_test.cc
index 8463a2b..2fa4143 100644
--- a/absl/debugging/internal/demangle_test.cc
+++ b/absl/debugging/internal/demangle_test.cc
@@ -38,7 +38,7 @@
   }
 }
 
-// Test corner cases of bounary conditions.
+// Test corner cases of boundary conditions.
 TEST(Demangle, CornerCases) {
   char tmp[10];
   EXPECT_TRUE(Demangle("_Z6foobarv", tmp, sizeof(tmp)));
diff --git a/absl/debugging/internal/stack_consumption.cc b/absl/debugging/internal/stack_consumption.cc
index 5134864..6d97499 100644
--- a/absl/debugging/internal/stack_consumption.cc
+++ b/absl/debugging/internal/stack_consumption.cc
@@ -162,7 +162,7 @@
     // versions of musl have a bug that rejects ss_size==0. Work around this by
     // setting ss_size to MINSIGSTKSZ, which should be ignored by the kernel
     // when SS_DISABLE is set.
-    old_sigstk.ss_size = MINSIGSTKSZ;
+    old_sigstk.ss_size = static_cast<size_t>(MINSIGSTKSZ);
   }
   ABSL_RAW_CHECK(sigaltstack(&old_sigstk, nullptr) == 0,
                  "sigaltstack() failed");
diff --git a/absl/debugging/internal/stacktrace_aarch64-inl.inc b/absl/debugging/internal/stacktrace_aarch64-inl.inc
index 891942c..b66beba 100644
--- a/absl/debugging/internal/stacktrace_aarch64-inl.inc
+++ b/absl/debugging/internal/stacktrace_aarch64-inl.inc
@@ -19,7 +19,7 @@
 #include "absl/debugging/internal/vdso_support.h"  // a no-op on non-elf or non-glibc systems
 #include "absl/debugging/stacktrace.h"
 
-static const uintptr_t kUnknownFrameSize = 0;
+static const size_t kUnknownFrameSize = 0;
 
 #if defined(__linux__)
 // Returns the address of the VDSO __kernel_rt_sigreturn function, if present.
@@ -65,11 +65,12 @@
 // 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) {
+static inline size_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;
+  return low < high ? static_cast<size_t>(high_char_ptr - low_char_ptr)
+                    : kUnknownFrameSize;
 }
 
 // Given a pointer to a stack frame, locate and return the calling
@@ -117,8 +118,8 @@
   // 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 =
+    const size_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
+    const size_t frame_size =
         ComputeStackFrameSize(old_frame_pointer, new_frame_pointer);
     if (frame_size == kUnknownFrameSize || frame_size > max_size)
       return nullptr;
@@ -137,41 +138,45 @@
 #else
 # error reading stack point not yet supported on this platform.
 #endif
-
   skip_count++;    // Skip the frame for this function.
   int n = 0;
 
   // The frame pointer points to low address of a frame.  The first 64-bit
   // word of a frame points to the next frame up the call chain, which normally
   // is just after the high address of the current frame.  The second word of
-  // a frame contains return adress of to the caller.   To find a pc value
+  // a frame contains return address of to the caller.   To find a pc value
   // associated with the current frame, we need to go down a level in the call
   // chain.  So we remember return the 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;
+  // The nth frame size is the difference between the nth frame pointer and the
+  // the frame pointer below it in the call chain. There is no frame below the
+  // leaf frame, but this function is the leaf anyway, and we skip it.
+  void** prev_frame_pointer = nullptr;
 
-  while (frame_pointer && n < max_depth) {
-    // The absl::GetStackFrames routine is 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);
-
+   while (frame_pointer && n < max_depth) {
     if (skip_count > 0) {
       skip_count--;
     } else {
       result[n] = prev_return_address;
       if (IS_STACK_FRAMES) {
-        sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer);
+        sizes[n] = static_cast<int>(
+            ComputeStackFrameSize(prev_frame_pointer, frame_pointer));
       }
       n++;
     }
     prev_return_address = frame_pointer[1];
-    frame_pointer = next_frame_pointer;
+    prev_frame_pointer = frame_pointer;
+    // The absl::GetStackFrames routine is 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).
+    frame_pointer =
+        NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp);
   }
+
   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.
diff --git a/absl/debugging/internal/stacktrace_powerpc-inl.inc b/absl/debugging/internal/stacktrace_powerpc-inl.inc
index 085cef6..a49ed2f 100644
--- a/absl/debugging/internal/stacktrace_powerpc-inl.inc
+++ b/absl/debugging/internal/stacktrace_powerpc-inl.inc
@@ -57,7 +57,7 @@
   // This check is in case the compiler doesn't define _CALL_SYSV.
   return *(sp+1);
 #else
-#error Need to specify the PPC ABI for your archiecture.
+#error Need to specify the PPC ABI for your architecture.
 #endif
 }
 
diff --git a/absl/debugging/internal/stacktrace_x86-inl.inc b/absl/debugging/internal/stacktrace_x86-inl.inc
index 9fbfcf7..1975ba7 100644
--- a/absl/debugging/internal/stacktrace_x86-inl.inc
+++ b/absl/debugging/internal/stacktrace_x86-inl.inc
@@ -29,6 +29,7 @@
 #include <cstdint>
 #include <limits>
 
+#include "absl/base/attributes.h"
 #include "absl/base/macros.h"
 #include "absl/base/port.h"
 #include "absl/debugging/internal/address_is_readable.h"
@@ -39,7 +40,7 @@
 
 #if defined(__linux__) && defined(__i386__)
 // Count "push %reg" instructions in VDSO __kernel_vsyscall(),
-// preceeding "syscall" or "sysenter".
+// preceding "syscall" or "sysenter".
 // If __kernel_vsyscall uses frame pointer, answer 0.
 //
 // kMaxBytes tells how many instruction bytes of __kernel_vsyscall
@@ -111,6 +112,10 @@
 
 // Assume stack frames larger than 100,000 bytes are bogus.
 static const int kMaxFrameBytes = 100000;
+// Stack end to use when we don't know the actual stack end
+// (effectively just the end of address space).
+constexpr uintptr_t kUnknownStackEnd =
+    std::numeric_limits<size_t>::max() - sizeof(void *);
 
 // Returns the stack frame pointer from signal context, 0 if unknown.
 // vuc is a ucontext_t *.  We use void* to avoid the use
@@ -257,8 +262,26 @@
     // With the stack growing downwards, older stack frame must be
     // 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 we get a very large frame size, it may be an indication that we
+    // guessed frame pointers incorrectly and now risk a paging fault
+    // dereferencing a wrong frame pointer. Or maybe not because large frames
+    // are possible as well. The main stack is assumed to be readable,
+    // so we assume the large frame is legit if we know the real stack bounds
+    // and are within the stack.
+    if (new_fp_u - old_fp_u > kMaxFrameBytes) {
+      if (stack_high < kUnknownStackEnd &&
+          static_cast<size_t>(getpagesize()) < stack_low) {
+        // Stack bounds are known.
+        if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) {
+          // new_fp_u is not within the known stack.
+          return nullptr;
+        }
+      } else {
+        // Stack bounds are unknown, prefer truncated stack to possible crash.
+        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)) {
@@ -311,7 +334,7 @@
 
   // Assume that the first page is not stack.
   size_t stack_low = static_cast<size_t>(getpagesize());
-  size_t stack_high = std::numeric_limits<size_t>::max() - sizeof(void *);
+  size_t stack_high = kUnknownStackEnd;
 
   while (fp && n < max_depth) {
     if (*(fp + 1) == reinterpret_cast<void *>(0)) {
diff --git a/absl/debugging/internal/symbolize.h b/absl/debugging/internal/symbolize.h
index 27d5e65..5593fde 100644
--- a/absl/debugging/internal/symbolize.h
+++ b/absl/debugging/internal/symbolize.h
@@ -115,7 +115,7 @@
 
 // Remove all installed decorators.  Returns true if successful, false if
 // symbolization is currently in progress.
-bool RemoveAllSymbolDecorators(void);
+bool RemoveAllSymbolDecorators();
 
 // Registers an address range to a file mapping.
 //
diff --git a/absl/debugging/leak_check.cc b/absl/debugging/leak_check.cc
index 195e82b..fdb8798 100644
--- a/absl/debugging/leak_check.cc
+++ b/absl/debugging/leak_check.cc
@@ -65,8 +65,8 @@
 void DoIgnoreLeak(const void*) { }
 void RegisterLivePointers(const void*, size_t) { }
 void UnRegisterLivePointers(const void*, size_t) { }
-LeakCheckDisabler::LeakCheckDisabler() { }
-LeakCheckDisabler::~LeakCheckDisabler() { }
+LeakCheckDisabler::LeakCheckDisabler() = default;
+LeakCheckDisabler::~LeakCheckDisabler() = default;
 ABSL_NAMESPACE_END
 }  // namespace absl
 
diff --git a/absl/debugging/leak_check.h b/absl/debugging/leak_check.h
index eff162f..6bd7940 100644
--- a/absl/debugging/leak_check.h
+++ b/absl/debugging/leak_check.h
@@ -37,7 +37,7 @@
 // not also use AddressSanitizer). To use the mode, simply pass
 // `-fsanitize=leak` to both the compiler and linker. Since GCC does not
 // currently provide a way of detecting this mode at compile-time, GCC users
-// must also pass -DLEAK_SANIITIZER to the compiler. An example Bazel command
+// must also pass -DLEAK_SANITIZER to the compiler. An example Bazel command
 // could be
 //
 //   $ bazel test --copt=-DLEAK_SANITIZER --copt=-fsanitize=leak
diff --git a/absl/debugging/stacktrace_test.cc b/absl/debugging/stacktrace_test.cc
new file mode 100644
index 0000000..78ce7ad
--- /dev/null
+++ b/absl/debugging/stacktrace_test.cc
@@ -0,0 +1,47 @@
+// Copyright 2023 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/debugging/stacktrace.h"
+
+#include "gtest/gtest.h"
+#include "absl/base/macros.h"
+#include "absl/base/optimization.h"
+
+namespace {
+
+// This test is currently only known to pass on linux/x86_64.
+#if defined(__linux__) && defined(__x86_64__)
+ABSL_ATTRIBUTE_NOINLINE void Unwind(void* p) {
+  ABSL_ATTRIBUTE_UNUSED static void* volatile sink = p;
+  constexpr int kSize = 16;
+  void* stack[kSize];
+  int frames[kSize];
+  absl::GetStackTrace(stack, kSize, 0);
+  absl::GetStackFrames(stack, frames, kSize, 0);
+}
+
+ABSL_ATTRIBUTE_NOINLINE void HugeFrame() {
+  char buffer[1 << 20];
+  Unwind(buffer);
+  ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
+}
+
+TEST(StackTrace, HugeFrame) {
+  // Ensure that the unwinder is not confused by very large stack frames.
+  HugeFrame();
+  ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
+}
+#endif
+
+}  // namespace
diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc
index ffb4eec..0fee89f 100644
--- a/absl/debugging/symbolize_elf.inc
+++ b/absl/debugging/symbolize_elf.inc
@@ -532,6 +532,11 @@
     return false;
   }
 
+  // Technically it can be larger, but in practice this never happens.
+  if (elf_header.e_shentsize != sizeof(ElfW(Shdr))) {
+    return false;
+  }
+
   ElfW(Shdr) shstrtab;
   off_t shstrtab_offset = static_cast<off_t>(elf_header.e_shoff) +
                           elf_header.e_shentsize * elf_header.e_shstrndx;
@@ -584,6 +589,11 @@
     return false;
   }
 
+  // Technically it can be larger, but in practice this never happens.
+  if (elf_header.e_shentsize != sizeof(ElfW(Shdr))) {
+    return false;
+  }
+
   ElfW(Shdr) shstrtab;
   off_t shstrtab_offset = static_cast<off_t>(elf_header.e_shoff) +
                           elf_header.e_shentsize * elf_header.e_shstrndx;
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel
index 3d6d59e..570d280 100644
--- a/absl/flags/BUILD.bazel
+++ b/absl/flags/BUILD.bazel
@@ -284,6 +284,7 @@
         ":usage_internal",
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/base:raw_logging_internal",
         "//absl/strings",
         "//absl/synchronization",
     ],
@@ -309,6 +310,7 @@
         ":reflection",
         ":usage",
         ":usage_internal",
+        "//absl/algorithm:container",
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/strings",
diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt
index 3e9d5ad..b5f6dfc 100644
--- a/absl/flags/CMakeLists.txt
+++ b/absl/flags/CMakeLists.txt
@@ -262,6 +262,7 @@
     absl::config
     absl::core_headers
     absl::flags_usage_internal
+    absl::raw_logging_internal
     absl::strings
     absl::synchronization
 )
@@ -279,6 +280,7 @@
   LINKOPTS
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
+    absl::algorithm_container
     absl::config
     absl::core_headers
     absl::flags_config
@@ -295,7 +297,7 @@
 )
 
 ############################################################################
-# Unit tests in alpahabetical order.
+# Unit tests in alphabetical order.
 
 absl_cc_test(
   NAME
diff --git a/absl/flags/commandlineflag.h b/absl/flags/commandlineflag.h
index f2fa089..c30aa60 100644
--- a/absl/flags/commandlineflag.h
+++ b/absl/flags/commandlineflag.h
@@ -186,7 +186,7 @@
   // command line.
   virtual bool IsSpecifiedOnCommandLine() const = 0;
 
-  // Validates supplied value usign validator or parseflag routine
+  // Validates supplied value using validator or parseflag routine
   virtual bool ValidateInputValue(absl::string_view value) const = 0;
 
   // Checks that flags default value can be converted to string and back to the
diff --git a/absl/flags/flag_benchmark.cc b/absl/flags/flag_benchmark.cc
index fc572d9..758a6a5 100644
--- a/absl/flags/flag_benchmark.cc
+++ b/absl/flags/flag_benchmark.cc
@@ -241,10 +241,11 @@
 
 }  // namespace
 
+#ifdef __llvm__
+// To view disassembly use: gdb ${BINARY}  -batch -ex "disassemble /s $FUNC"
 #define InvokeGetFlag(T)                                             \
   T AbslInvokeGetFlag##T() { return absl::GetFlag(SINGLE_FLAG(T)); } \
   int odr##T = (benchmark::DoNotOptimize(AbslInvokeGetFlag##T), 1);
 
 BENCHMARKED_TYPES(InvokeGetFlag)
-
-// To veiw disassembly use: gdb ${BINARY}  -batch -ex "disassemble /s $FUNC"
+#endif  // __llvm__
diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc
index 4482955..3c114d1 100644
--- a/absl/flags/internal/commandlineflag.cc
+++ b/absl/flags/internal/commandlineflag.cc
@@ -19,7 +19,7 @@
 ABSL_NAMESPACE_BEGIN
 namespace flags_internal {
 
-FlagStateInterface::~FlagStateInterface() {}
+FlagStateInterface::~FlagStateInterface() = default;
 
 }  // namespace flags_internal
 ABSL_NAMESPACE_END
diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc
index cc656f9..65d0e58 100644
--- a/absl/flags/internal/flag.cc
+++ b/absl/flags/internal/flag.cc
@@ -197,7 +197,7 @@
   FlagFastTypeId lhs_type_id = flags_internal::FastTypeId(op_);
 
   // `rhs_type_id` is the fast type id corresponding to the declaration
-  // visibile at the call site. `lhs_type_id` is the fast type id
+  // visible at the call site. `lhs_type_id` is the fast type id
   // corresponding to the type specified in flag definition. They must match
   //  for this operation to be well-defined.
   if (ABSL_PREDICT_TRUE(lhs_type_id == rhs_type_id)) return;
@@ -238,7 +238,7 @@
   switch (ValueStorageKind()) {
     case FlagValueStorageKind::kValueAndInitBit:
     case FlagValueStorageKind::kOneWordAtomic: {
-      // Load the current value to avoid setting 'init' bit manualy.
+      // Load the current value to avoid setting 'init' bit manually.
       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);
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index 6154638..b41f9a6 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -121,7 +121,7 @@
   flags_internal::CopyConstruct(op, obj, res);
   return res;
 }
-// Returns true if parsing of input text is successfull.
+// Returns true if parsing of input text is successful.
 inline bool Parse(FlagOpFn op, absl::string_view text, void* dst,
                   std::string* error) {
   return op(FlagOp::kParse, &text, dst, error) != nullptr;
@@ -139,12 +139,12 @@
   return static_cast<size_t>(reinterpret_cast<intptr_t>(
       op(FlagOp::kSizeof, nullptr, nullptr, nullptr)));
 }
-// Returns fast type id coresponding to the value type.
+// Returns fast type id corresponding to the value type.
 inline FlagFastTypeId FastTypeId(FlagOpFn op) {
   return reinterpret_cast<FlagFastTypeId>(
       op(FlagOp::kFastTypeId, nullptr, nullptr, nullptr));
 }
-// Returns fast type id coresponding to the value type.
+// Returns fast type id corresponding to the value type.
 inline const std::type_info* RuntimeTypeId(FlagOpFn op) {
   return reinterpret_cast<const std::type_info*>(
       op(FlagOp::kRuntimeTypeId, nullptr, nullptr, nullptr));
@@ -223,12 +223,12 @@
 // first overload if possible. If help message is evaluatable on constexpr
 // context We'll be able to make FixedCharArray out of it and we'll choose first
 // overload. In this case the help message expression is immediately evaluated
-// and is used to construct the absl::Flag. No additionl code is generated by
+// and is used to construct the absl::Flag. No additional code is generated by
 // ABSL_FLAG Otherwise SFINAE kicks in and first overload is dropped from the
 // consideration, in which case the second overload will be used. The second
 // overload does not attempt to evaluate the help message expression
-// immediately and instead delays the evaluation by returing the function
-// pointer (&T::NonConst) genering the help message when necessary. This is
+// immediately and instead delays the evaluation by returning the function
+// pointer (&T::NonConst) generating the help message when necessary. This is
 // evaluatable in constexpr context, but the cost is an extra function being
 // generated in the ABSL_FLAG code.
 template <typename Gen, size_t N>
@@ -308,19 +308,20 @@
 }
 
 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)>;
+using FlagUseValueAndInitBitStorage =
+    std::integral_constant<bool, std::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)>;
+using FlagUseOneWordStorage =
+    std::integral_constant<bool, std::is_trivially_copyable<T>::value &&
+                                     (sizeof(T) <= 8)>;
 
 template <class T>
-using FlagUseSequenceLockStorage = std::integral_constant<
-    bool, absl::type_traits_internal::is_trivially_copyable<T>::value &&
-              (sizeof(T) > 8)>;
+using FlagUseSequenceLockStorage =
+    std::integral_constant<bool, std::is_trivially_copyable<T>::value &&
+                                     (sizeof(T) > 8)>;
 
 enum class FlagValueStorageKind : uint8_t {
   kValueAndInitBit = 0,
diff --git a/absl/flags/internal/flag_msvc.inc b/absl/flags/internal/flag_msvc.inc
index c31bd27..614d09f 100644
--- a/absl/flags/internal/flag_msvc.inc
+++ b/absl/flags/internal/flag_msvc.inc
@@ -29,7 +29,7 @@
 // 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:
+// This solution is based on a recommendation here:
 // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454
 
 namespace flags_internal {
diff --git a/absl/flags/internal/parse.h b/absl/flags/internal/parse.h
index de706c8..10c531b 100644
--- a/absl/flags/internal/parse.h
+++ b/absl/flags/internal/parse.h
@@ -16,11 +16,14 @@
 #ifndef ABSL_FLAGS_INTERNAL_PARSE_H_
 #define ABSL_FLAGS_INTERNAL_PARSE_H_
 
+#include <iostream>
+#include <ostream>
 #include <string>
 #include <vector>
 
 #include "absl/base/config.h"
 #include "absl/flags/declare.h"
+#include "absl/flags/internal/usage.h"
 #include "absl/strings/string_view.h"
 
 ABSL_DECLARE_FLAG(std::vector<std::string>, flagfile);
@@ -32,7 +35,6 @@
 ABSL_NAMESPACE_BEGIN
 namespace flags_internal {
 
-enum class ArgvListAction { kRemoveParsedArgs, kKeepParsedArgs };
 enum class UsageFlagsAction { kHandleUsage, kIgnoreUsage };
 enum class OnUndefinedFlag {
   kIgnoreUndefined,
@@ -40,10 +42,15 @@
   kAbortIfUndefined
 };
 
-std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
-                                        ArgvListAction arg_list_act,
-                                        UsageFlagsAction usage_flag_act,
-                                        OnUndefinedFlag on_undef_flag);
+// This is not a public interface. This interface exists to expose the ability
+// to change help output stream in case of parsing errors. This is used by
+// internal unit tests to validate expected outputs.
+// When this was written, `EXPECT_EXIT` only supported matchers on stderr,
+// but not on stdout.
+std::vector<char*> ParseCommandLineImpl(
+    int argc, char* argv[], UsageFlagsAction usage_flag_action,
+    OnUndefinedFlag undef_flag_action,
+    std::ostream& error_help_output = std::cout);
 
 // --------------------------------------------------------------------
 // Inspect original command line
@@ -52,6 +59,10 @@
 // command line or specified in flag file present on the original command line.
 bool WasPresentOnCommandLine(absl::string_view flag_name);
 
+// Return existing flags similar to the parameter, in order to help in case of
+// misspellings.
+std::vector<std::string> GetMisspellingHints(absl::string_view flag);
+
 }  // namespace flags_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc
index a3b13ed..6a56fce 100644
--- a/absl/flags/internal/usage.cc
+++ b/absl/flags/internal/usage.cc
@@ -17,7 +17,10 @@
 
 #include <stdint.h>
 
+#include <algorithm>
+#include <cstdlib>
 #include <functional>
+#include <iterator>
 #include <map>
 #include <ostream>
 #include <string>
@@ -33,6 +36,7 @@
 #include "absl/flags/internal/program_name.h"
 #include "absl/flags/internal/registry.h"
 #include "absl/flags/usage_config.h"
+#include "absl/strings/match.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_split.h"
 #include "absl/strings/string_view.h"
@@ -127,7 +131,7 @@
       for (auto line : absl::StrSplit(str, absl::ByAnyChar("\n\r"))) {
         if (!tokens.empty()) {
           // Keep line separators in the input string.
-          tokens.push_back("\n");
+          tokens.emplace_back("\n");
         }
         for (auto token :
              absl::StrSplit(line, absl::ByAnyChar(" \t"), absl::SkipEmpty())) {
@@ -343,7 +347,7 @@
 void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format,
                absl::string_view program_usage_message) {
   flags_internal::FlagKindFilter filter_cb = [&](absl::string_view filename) {
-    return filter.empty() || filename.find(filter) != absl::string_view::npos;
+    return filter.empty() || absl::StrContains(filename, filter);
   };
   flags_internal::FlagsHelpImpl(out, filter_cb, format, program_usage_message);
 }
@@ -351,8 +355,8 @@
 // --------------------------------------------------------------------
 // Checks all the 'usage' command line flags to see if any have been set.
 // If so, handles them appropriately.
-int HandleUsageFlags(std::ostream& out,
-                     absl::string_view program_usage_message) {
+HelpMode HandleUsageFlags(std::ostream& out,
+                          absl::string_view program_usage_message) {
   switch (GetFlagsHelpMode()) {
     case HelpMode::kNone:
       break;
@@ -360,25 +364,24 @@
       flags_internal::FlagsHelpImpl(
           out, flags_internal::GetUsageConfig().contains_help_flags,
           GetFlagsHelpFormat(), program_usage_message);
-      return 1;
+      break;
 
     case HelpMode::kShort:
       flags_internal::FlagsHelpImpl(
           out, flags_internal::GetUsageConfig().contains_helpshort_flags,
           GetFlagsHelpFormat(), program_usage_message);
-      return 1;
+      break;
 
     case HelpMode::kFull:
       flags_internal::FlagsHelp(out, "", GetFlagsHelpFormat(),
                                 program_usage_message);
-      return 1;
+      break;
 
     case HelpMode::kPackage:
       flags_internal::FlagsHelpImpl(
           out, flags_internal::GetUsageConfig().contains_helppackage_flags,
           GetFlagsHelpFormat(), program_usage_message);
-
-      return 1;
+      break;
 
     case HelpMode::kMatch: {
       std::string substr = GetFlagsHelpMatchSubstr();
@@ -397,20 +400,19 @@
         flags_internal::FlagsHelpImpl(
             out, filter_cb, HelpFormat::kHumanReadable, program_usage_message);
       }
-
-      return 1;
+      break;
     }
     case HelpMode::kVersion:
       if (flags_internal::GetUsageConfig().version_string)
         out << flags_internal::GetUsageConfig().version_string();
       // Unlike help, we may be asking for version in a script, so return 0
-      return 0;
+      break;
 
     case HelpMode::kOnlyCheckArgs:
-      return 0;
+      break;
   }
 
-  return -1;
+  return GetFlagsHelpMode();
 }
 
 // --------------------------------------------------------------------
@@ -465,7 +467,7 @@
 // function.
 bool DeduceUsageFlags(absl::string_view name, absl::string_view value) {
   if (absl::ConsumePrefix(&name, "help")) {
-    if (name == "") {
+    if (name.empty()) {
       if (value.empty()) {
         SetFlagsHelpMode(HelpMode::kImportant);
       } else {
@@ -518,6 +520,22 @@
   return false;
 }
 
+// --------------------------------------------------------------------
+
+void MaybeExit(HelpMode mode) {
+  switch (mode) {
+    case flags_internal::HelpMode::kNone:
+      return;
+    case flags_internal::HelpMode::kOnlyCheckArgs:
+    case flags_internal::HelpMode::kVersion:
+      std::exit(0);
+    default:  // For all the other modes we exit with 1
+      std::exit(1);
+  }
+}
+
+// --------------------------------------------------------------------
+
 }  // namespace flags_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/flags/internal/usage.h b/absl/flags/internal/usage.h
index c0bcac5..a96cbf3 100644
--- a/absl/flags/internal/usage.h
+++ b/absl/flags/internal/usage.h
@@ -17,11 +17,11 @@
 #define ABSL_FLAGS_INTERNAL_USAGE_H_
 
 #include <iosfwd>
+#include <ostream>
 #include <string>
 
 #include "absl/base/config.h"
 #include "absl/flags/commandlineflag.h"
-#include "absl/flags/declare.h"
 #include "absl/strings/string_view.h"
 
 // --------------------------------------------------------------------
@@ -36,6 +36,18 @@
   kHumanReadable,
 };
 
+// The kind of usage help requested.
+enum class HelpMode {
+  kNone,
+  kImportant,
+  kShort,
+  kFull,
+  kPackage,
+  kMatch,
+  kVersion,
+  kOnlyCheckArgs
+};
+
 // Streams the help message describing `flag` to `out`.
 // The default value for `flag` is included in the output.
 void FlagHelp(std::ostream& out, const CommandLineFlag& flag,
@@ -57,29 +69,19 @@
 
 // If any of the 'usage' related command line flags (listed on the bottom of
 // this file) has been set this routine produces corresponding help message in
-// the specified output stream and returns:
-//  0 - if "version" or "only_check_flags" flags were set and handled.
-//  1 - if some other 'usage' related flag was set and handled.
-// -1 - if no usage flags were set on a commmand line.
-// Non negative return values are expected to be used as an exit code for a
-// binary.
-int HandleUsageFlags(std::ostream& out,
-                     absl::string_view program_usage_message);
+// the specified output stream and returns HelpMode that was handled. Otherwise
+// it returns HelpMode::kNone.
+HelpMode HandleUsageFlags(std::ostream& out,
+                          absl::string_view program_usage_message);
+
+// --------------------------------------------------------------------
+// Encapsulates the logic of exiting the binary depending on handled help mode.
+
+void MaybeExit(HelpMode mode);
 
 // --------------------------------------------------------------------
 // Globals representing usage reporting flags
 
-enum class HelpMode {
-  kNone,
-  kImportant,
-  kShort,
-  kFull,
-  kPackage,
-  kMatch,
-  kVersion,
-  kOnlyCheckArgs
-};
-
 // Returns substring to filter help output (--help=substr argument)
 std::string GetFlagsHelpMatchSubstr();
 // Returns the requested help mode.
diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc
index 209a7be..c3ab4a4 100644
--- a/absl/flags/internal/usage_test.cc
+++ b/absl/flags/internal/usage_test.cc
@@ -24,7 +24,6 @@
 #include "gtest/gtest.h"
 #include "absl/flags/flag.h"
 #include "absl/flags/internal/parse.h"
-#include "absl/flags/internal/path_util.h"
 #include "absl/flags/internal/program_name.h"
 #include "absl/flags/reflection.h"
 #include "absl/flags/usage.h"
@@ -256,7 +255,8 @@
 
 TEST_F(UsageReportingTest, TestNoUsageFlags) {
   std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), -1);
+  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+            flags::HelpMode::kNone);
 }
 
 // --------------------------------------------------------------------
@@ -265,7 +265,8 @@
   flags::SetFlagsHelpMode(flags::HelpMode::kShort);
 
   std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
+  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+            flags::HelpMode::kShort);
   EXPECT_EQ(test_buf.str(),
             R"(usage_test: Custom usage message
 
@@ -298,7 +299,8 @@
   flags::SetFlagsHelpMode(flags::HelpMode::kImportant);
 
   std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
+  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+            flags::HelpMode::kImportant);
   EXPECT_EQ(test_buf.str(),
             R"(usage_test: Custom usage message
 
@@ -332,7 +334,8 @@
   flags::SetFlagsHelpMatchSubstr("usage_reporting_test_flag_06");
 
   std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
+  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+            flags::HelpMode::kMatch);
   EXPECT_EQ(test_buf.str(),
             R"(usage_test: Custom usage message
 
@@ -356,7 +359,8 @@
   flags::SetFlagsHelpMatchSubstr("test_flag");
 
   std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
+  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+            flags::HelpMode::kMatch);
   EXPECT_EQ(test_buf.str(),
             R"(usage_test: Custom usage message
 
@@ -389,7 +393,8 @@
   flags::SetFlagsHelpMode(flags::HelpMode::kPackage);
 
   std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
+  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+            flags::HelpMode::kPackage);
   EXPECT_EQ(test_buf.str(),
             R"(usage_test: Custom usage message
 
@@ -422,7 +427,8 @@
   flags::SetFlagsHelpMode(flags::HelpMode::kVersion);
 
   std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0);
+  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+            flags::HelpMode::kVersion);
 #ifndef NDEBUG
   EXPECT_EQ(test_buf.str(), "usage_test\nDebug build (NDEBUG not #defined)\n");
 #else
@@ -436,7 +442,8 @@
   flags::SetFlagsHelpMode(flags::HelpMode::kOnlyCheckArgs);
 
   std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0);
+  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage),
+            flags::HelpMode::kOnlyCheckArgs);
   EXPECT_EQ(test_buf.str(), "");
 }
 
@@ -447,7 +454,8 @@
   flags::SetFlagsHelpMatchSubstr("/bla-bla.");
 
   std::stringstream test_buf_01;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf_01, kTestUsageMessage), 1);
+  EXPECT_EQ(flags::HandleUsageFlags(test_buf_01, kTestUsageMessage),
+            flags::HelpMode::kMatch);
   EXPECT_EQ(test_buf_01.str(),
             R"(usage_test: Custom usage message
 
@@ -461,7 +469,8 @@
   flags::SetFlagsHelpMatchSubstr("/usage_test.");
 
   std::stringstream test_buf_02;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf_02, kTestUsageMessage), 1);
+  EXPECT_EQ(flags::HandleUsageFlags(test_buf_02, kTestUsageMessage),
+            flags::HelpMode::kMatch);
   EXPECT_EQ(test_buf_02.str(),
             R"(usage_test: Custom usage message
 
diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc
index 2851c0f..f41ddfc 100644
--- a/absl/flags/parse.cc
+++ b/absl/flags/parse.cc
@@ -18,9 +18,11 @@
 #include <stdlib.h>
 
 #include <algorithm>
+#include <cstdint>
+#include <cstdlib>
 #include <fstream>
 #include <iostream>
-#include <iterator>
+#include <ostream>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -30,6 +32,7 @@
 #include <windows.h>
 #endif
 
+#include "absl/algorithm/container.h"
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/const_init.h"
@@ -47,7 +50,9 @@
 #include "absl/flags/usage.h"
 #include "absl/flags/usage_config.h"
 #include "absl/strings/ascii.h"
+#include "absl/strings/internal/damerau_levenshtein_distance.h"
 #include "absl/strings/str_cat.h"
+#include "absl/strings/str_join.h"
 #include "absl/strings/string_view.h"
 #include "absl/strings/strip.h"
 #include "absl/synchronization/mutex.h"
@@ -72,6 +77,11 @@
 ABSL_CONST_INIT std::vector<const CommandLineFlag*>* specified_flags
     ABSL_GUARDED_BY(specified_flags_guard) = nullptr;
 
+// Suggesting at most kMaxHints flags in case of misspellings.
+ABSL_CONST_INIT const size_t kMaxHints = 100;
+// Suggesting only flags which have a smaller distance than kMaxDistance.
+ABSL_CONST_INIT const size_t kMaxDistance = 3;
+
 struct SpecifiedFlagsCompare {
   bool operator()(const CommandLineFlag* a, const CommandLineFlag* b) const {
     return a->Name() < b->Name();
@@ -181,7 +191,7 @@
 
   // This argument represents fake argv[0], which should be present in all arg
   // lists.
-  args_.push_back("");
+  args_.emplace_back("");
 
   std::string line;
   bool success = true;
@@ -203,7 +213,7 @@
         break;
       }
 
-      args_.push_back(std::string(stripped));
+      args_.emplace_back(stripped);
       continue;
     }
 
@@ -269,7 +279,7 @@
     return std::make_tuple("", "", false);
   }
 
-  auto equal_sign_pos = arg.find("=");
+  auto equal_sign_pos = arg.find('=');
 
   absl::string_view flag_name = arg.substr(0, equal_sign_pos);
 
@@ -358,7 +368,7 @@
 
   // This argument represents fake argv[0], which should be present in all arg
   // lists.
-  args.push_back("");
+  args.emplace_back("");
 
   for (const auto& flag_name : flag_names) {
     // Avoid infinite recursion.
@@ -407,7 +417,7 @@
   // programmatically before invoking ParseCommandLine. Note that we do not
   // actually process arguments specified in the flagfile, but instead
   // create a secondary arguments list to be processed along with the rest
-  // of the comamnd line arguments. Since we always the process most recently
+  // of the command line arguments. Since we always the process most recently
   // created list of arguments first, this will result in flagfile argument
   // being processed before any other argument in the command line. If
   // FLAGS_flagfile contains more than one file name we create multiple new
@@ -590,6 +600,34 @@
   return false;
 }
 
+// --------------------------------------------------------------------
+
+void ReportUnrecognizedFlags(
+    const std::vector<UnrecognizedFlag>& unrecognized_flags,
+    bool report_as_fatal_error) {
+  for (const auto& unrecognized : unrecognized_flags) {
+    // Verify if flag_name has the "no" already removed
+    std::vector<std::string> misspelling_hints;
+    if (unrecognized.source == UnrecognizedFlag::kFromArgv) {
+      misspelling_hints =
+          flags_internal::GetMisspellingHints(unrecognized.flag_name);
+    }
+
+    if (misspelling_hints.empty()) {
+      flags_internal::ReportUsageError(
+          absl::StrCat("Unknown command line flag '", unrecognized.flag_name,
+                       "'"),
+          report_as_fatal_error);
+    } else {
+      flags_internal::ReportUsageError(
+          absl::StrCat("Unknown command line flag '", unrecognized.flag_name,
+                       "'. Did you mean: ",
+                       absl::StrJoin(misspelling_hints, ", "), " ?"),
+          report_as_fatal_error);
+    }
+  }
+}
+
 }  // namespace
 
 // --------------------------------------------------------------------
@@ -605,60 +643,144 @@
 
 // --------------------------------------------------------------------
 
+struct BestHints {
+  explicit BestHints(uint8_t _max) : best_distance(_max + 1) {}
+  bool AddHint(absl::string_view hint, uint8_t distance) {
+    if (hints.size() >= kMaxHints) return false;
+    if (distance == best_distance) {
+      hints.emplace_back(hint);
+    }
+    if (distance < best_distance) {
+      best_distance = distance;
+      hints = std::vector<std::string>{std::string(hint)};
+    }
+    return true;
+  }
+
+  uint8_t best_distance;
+  std::vector<std::string> hints;
+};
+
+// Return the list of flags with the smallest Damerau-Levenshtein distance to
+// the given flag.
+std::vector<std::string> GetMisspellingHints(const absl::string_view flag) {
+  const size_t maxCutoff = std::min(flag.size() / 2 + 1, kMaxDistance);
+  auto undefok = absl::GetFlag(FLAGS_undefok);
+  BestHints best_hints(static_cast<uint8_t>(maxCutoff));
+  flags_internal::ForEachFlag([&](const CommandLineFlag& f) {
+    if (best_hints.hints.size() >= kMaxHints) return;
+    uint8_t distance = strings_internal::CappedDamerauLevenshteinDistance(
+        flag, f.Name(), best_hints.best_distance);
+    best_hints.AddHint(f.Name(), distance);
+    // For boolean flags, also calculate distance to the negated form.
+    if (f.IsOfType<bool>()) {
+      const std::string negated_flag = absl::StrCat("no", f.Name());
+      distance = strings_internal::CappedDamerauLevenshteinDistance(
+          flag, negated_flag, best_hints.best_distance);
+      best_hints.AddHint(negated_flag, distance);
+    }
+  });
+  // Finally calculate distance to flags in "undefok".
+  absl::c_for_each(undefok, [&](const absl::string_view f) {
+    if (best_hints.hints.size() >= kMaxHints) return;
+    uint8_t distance = strings_internal::CappedDamerauLevenshteinDistance(
+        flag, f, best_hints.best_distance);
+    best_hints.AddHint(absl::StrCat(f, " (undefok)"), distance);
+  });
+  return best_hints.hints;
+}
+
+// --------------------------------------------------------------------
+
 std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
-                                        ArgvListAction arg_list_act,
-                                        UsageFlagsAction usage_flag_act,
-                                        OnUndefinedFlag on_undef_flag) {
+                                        UsageFlagsAction usage_flag_action,
+                                        OnUndefinedFlag undef_flag_action,
+                                        std::ostream& error_help_output) {
+  std::vector<char*> positional_args;
+  std::vector<UnrecognizedFlag> unrecognized_flags;
+
+  auto help_mode = flags_internal::ParseAbseilFlagsOnlyImpl(
+      argc, argv, positional_args, unrecognized_flags, usage_flag_action);
+
+  if (undef_flag_action != OnUndefinedFlag::kIgnoreUndefined) {
+    flags_internal::ReportUnrecognizedFlags(
+        unrecognized_flags,
+        (undef_flag_action == OnUndefinedFlag::kAbortIfUndefined));
+
+    if (undef_flag_action == OnUndefinedFlag::kAbortIfUndefined) {
+      if (!unrecognized_flags.empty()) {
+        flags_internal::HandleUsageFlags(error_help_output,
+        ProgramUsageMessage()); std::exit(1);
+      }
+    }
+  }
+
+  flags_internal::MaybeExit(help_mode);
+
+  return positional_args;
+}
+
+// --------------------------------------------------------------------
+
+// This function handles all Abseil Flags and built-in usage flags and, if any
+// help mode was handled, it returns that help mode. The caller of this function
+// can decide to exit based on the returned help mode.
+// The caller may decide to handle unrecognized positional arguments and
+// unrecognized flags first before exiting.
+//
+// Returns:
+// * HelpMode::kFull if parsing errors were detected in recognized arguments
+// * The HelpMode that was handled in case when `usage_flag_action` is
+//   UsageFlagsAction::kHandleUsage and a usage flag was specified on the
+//   commandline
+// * Otherwise it returns HelpMode::kNone
+HelpMode ParseAbseilFlagsOnlyImpl(
+    int argc, char* argv[], std::vector<char*>& positional_args,
+    std::vector<UnrecognizedFlag>& unrecognized_flags,
+    UsageFlagsAction usage_flag_action) {
   ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]");
 
-  // Once parsing has started we will not have more flag registrations.
-  // If we did, they would be missing during parsing, which is a problem on
-  // itself.
+  using flags_internal::ArgsList;
+  using flags_internal::specified_flags;
+
+  std::vector<std::string> flagfile_value;
+  std::vector<ArgsList> input_args;
+
+  // Once parsing has started we will not allow more flag registrations.
   flags_internal::FinalizeRegistry();
 
   // This routine does not return anything since we abort on failure.
-  CheckDefaultValuesParsingRoundtrip();
+  flags_internal::CheckDefaultValuesParsingRoundtrip();
 
-  std::vector<std::string> flagfile_value;
-
-  std::vector<ArgsList> input_args;
   input_args.push_back(ArgsList(argc, argv));
 
-  std::vector<char*> output_args;
-  std::vector<char*> positional_args;
-  output_args.reserve(static_cast<size_t>(argc));
-
-  // This is the list of undefined flags. The element of the list is the pair
-  // consisting of boolean indicating if flag came from command line (vs from
-  // some flag file we've read) and flag name.
-  // TODO(rogeeff): Eliminate the first element in the pair after cleanup.
-  std::vector<std::pair<bool, std::string>> undefined_flag_names;
-
   // Set program invocation name if it is not set before.
-  if (ProgramInvocationName() == "UNKNOWN") {
+  if (flags_internal::ProgramInvocationName() == "UNKNOWN") {
     flags_internal::SetProgramInvocationName(argv[0]);
   }
-  output_args.push_back(argv[0]);
+  positional_args.push_back(argv[0]);
 
-  absl::MutexLock l(&specified_flags_guard);
+  absl::MutexLock l(&flags_internal::specified_flags_guard);
   if (specified_flags == nullptr) {
     specified_flags = new std::vector<const CommandLineFlag*>;
   } else {
     specified_flags->clear();
   }
 
-  // Iterate through the list of the input arguments. First level are arguments
-  // originated from argc/argv. Following levels are arguments originated from
-  // recursive parsing of flagfile(s).
+  // Iterate through the list of the input arguments. First level are
+  // arguments originated from argc/argv. Following levels are arguments
+  // originated from recursive parsing of flagfile(s).
   bool success = true;
   while (!input_args.empty()) {
-    // 10. First we process the built-in generator flags.
-    success &= HandleGeneratorFlags(input_args, flagfile_value);
+    // First we process the built-in generator flags.
+    success &= flags_internal::HandleGeneratorFlags(input_args, flagfile_value);
 
-    // 30. Select top-most (most recent) arguments list. If it is empty drop it
+    // Select top-most (most recent) arguments list. If it is empty drop it
     // and re-try.
     ArgsList& curr_list = input_args.back();
 
+    // Every ArgsList starts with real or fake program name, so we can always
+    // start by skipping it.
     curr_list.PopFront();
 
     if (curr_list.Size() == 0) {
@@ -666,13 +788,13 @@
       continue;
     }
 
-    // 40. Pick up the front remaining argument in the current list. If current
-    // stack of argument lists contains only one element - we are processing an
-    // argument from the original argv.
+    // Handle the next argument in the current list. If the stack of argument
+    // lists contains only one element - we are processing an argument from
+    // the original argv.
     absl::string_view arg(curr_list.Front());
     bool arg_from_argv = input_args.size() == 1;
 
-    // 50. If argument does not start with - or is just "-" - this is
+    // If argument does not start with '-' or is just "-" - this is
     // positional argument.
     if (!absl::ConsumePrefix(&arg, "-") || arg.empty()) {
       ABSL_INTERNAL_CHECK(arg_from_argv,
@@ -682,12 +804,8 @@
       continue;
     }
 
-    if (arg_from_argv && (arg_list_act == ArgvListAction::kKeepParsedArgs)) {
-      output_args.push_back(argv[curr_list.FrontIndex()]);
-    }
-
-    // 60. Split the current argument on '=' to figure out the argument
-    // name and value. If flag name is empty it means we've got "--". value
+    // Split the current argument on '=' to deduce the argument flag name and
+    // value. If flag name is empty it means we've got an "--" argument. Value
     // can be empty either if there were no '=' in argument string at all or
     // an argument looked like "--foo=". In a latter case is_empty_value is
     // true.
@@ -695,10 +813,11 @@
     absl::string_view value;
     bool is_empty_value = false;
 
-    std::tie(flag_name, value, is_empty_value) = SplitNameAndValue(arg);
+    std::tie(flag_name, value, is_empty_value) =
+        flags_internal::SplitNameAndValue(arg);
 
-    // 70. "--" alone means what it does for GNU: stop flags parsing. We do
-    // not support positional arguments in flagfiles, so we just drop them.
+    // Standalone "--" argument indicates that the rest of the arguments are
+    // positional. We do not support positional arguments in flagfiles.
     if (flag_name.empty()) {
       ABSL_INTERNAL_CHECK(arg_from_argv,
                           "Flagfile cannot contain positional argument");
@@ -707,43 +826,36 @@
       break;
     }
 
-    // 80. Locate the flag based on flag name. Handle both --foo and --nofoo
+    // Locate the flag based on flag name. Handle both --foo and --nofoo.
     CommandLineFlag* flag = nullptr;
     bool is_negative = false;
-    std::tie(flag, is_negative) = LocateFlag(flag_name);
+    std::tie(flag, is_negative) = flags_internal::LocateFlag(flag_name);
 
     if (flag == nullptr) {
       // Usage flags are not modeled as Abseil flags. Locate them separately.
       if (flags_internal::DeduceUsageFlags(flag_name, value)) {
         continue;
       }
-
-      if (on_undef_flag != OnUndefinedFlag::kIgnoreUndefined) {
-        undefined_flag_names.emplace_back(arg_from_argv,
-                                          std::string(flag_name));
-      }
+      unrecognized_flags.emplace_back(arg_from_argv
+                                          ? UnrecognizedFlag::kFromArgv
+                                          : UnrecognizedFlag::kFromFlagfile,
+                                      flag_name);
       continue;
     }
 
-    // 90. Deduce flag's value (from this or next argument)
-    auto curr_index = curr_list.FrontIndex();
+    // Deduce flag's value (from this or next argument).
     bool value_success = true;
-    std::tie(value_success, value) =
-        DeduceFlagValue(*flag, value, is_negative, is_empty_value, &curr_list);
+    std::tie(value_success, value) = flags_internal::DeduceFlagValue(
+        *flag, value, is_negative, is_empty_value, &curr_list);
     success &= value_success;
 
-    // If above call consumed an argument, it was a standalone value
-    if (arg_from_argv && (arg_list_act == ArgvListAction::kKeepParsedArgs) &&
-        (curr_index != curr_list.FrontIndex())) {
-      output_args.push_back(argv[curr_list.FrontIndex()]);
-    }
-
-    // 100. Set the located flag to a new new value, unless it is retired.
-    // Setting retired flag fails, but we ignoring it here while also reporting
-    // access to retired flag.
+    // Set the located flag to a new value, unless it is retired. Setting
+    // retired flag fails, but we ignoring it here while also reporting access
+    // to retired flag.
     std::string error;
     if (!flags_internal::PrivateHandleAccessor::ParseFrom(
-            *flag, value, SET_FLAGS_VALUE, kCommandLine, error)) {
+            *flag, value, flags_internal::SET_FLAGS_VALUE,
+            flags_internal::kCommandLine, error)) {
       if (flag->IsRetired()) continue;
 
       flags_internal::ReportUsageError(error, true);
@@ -753,69 +865,73 @@
     }
   }
 
-  for (const auto& flag_name : undefined_flag_names) {
-    if (CanIgnoreUndefinedFlag(flag_name.second)) continue;
-
-    flags_internal::ReportUsageError(
-        absl::StrCat("Unknown command line flag '", flag_name.second, "'"),
-        true);
-
-    success = false;
-  }
-
-#if ABSL_FLAGS_STRIP_NAMES
-  if (!success) {
-    flags_internal::ReportUsageError(
-        "NOTE: command line flags are disabled in this build", true);
-  }
-#endif
-
-  if (!success) {
-    flags_internal::HandleUsageFlags(std::cout,
-                                     ProgramUsageMessage());
-    std::exit(1);
-  }
-
-  if (usage_flag_act == UsageFlagsAction::kHandleUsage) {
-    int exit_code = flags_internal::HandleUsageFlags(
-        std::cout, ProgramUsageMessage());
-
-    if (exit_code != -1) {
-      std::exit(exit_code);
-    }
-  }
-
-  ResetGeneratorFlags(flagfile_value);
-
-  // Reinstate positional args which were intermixed with flags in the arguments
-  // list.
-  for (auto arg : positional_args) {
-    output_args.push_back(arg);
-  }
+  flags_internal::ResetGeneratorFlags(flagfile_value);
 
   // All the remaining arguments are positional.
   if (!input_args.empty()) {
     for (size_t arg_index = input_args.back().FrontIndex();
          arg_index < static_cast<size_t>(argc); ++arg_index) {
-      output_args.push_back(argv[arg_index]);
+      positional_args.push_back(argv[arg_index]);
     }
   }
 
   // Trim and sort the vector.
   specified_flags->shrink_to_fit();
   std::sort(specified_flags->begin(), specified_flags->end(),
-            SpecifiedFlagsCompare{});
-  return output_args;
+            flags_internal::SpecifiedFlagsCompare{});
+
+  // Filter out unrecognized flags, which are ok to ignore.
+  std::vector<UnrecognizedFlag> filtered;
+  filtered.reserve(unrecognized_flags.size());
+  for (const auto& unrecognized : unrecognized_flags) {
+    if (flags_internal::CanIgnoreUndefinedFlag(unrecognized.flag_name))
+      continue;
+    filtered.push_back(unrecognized);
+  }
+
+  std::swap(unrecognized_flags, filtered);
+
+  if (!success) {
+#if ABSL_FLAGS_STRIP_NAMES
+    flags_internal::ReportUsageError(
+        "NOTE: command line flags are disabled in this build", true);
+#else
+    flags_internal::HandleUsageFlags(std::cerr, ProgramUsageMessage());
+#endif
+    return HelpMode::kFull;  // We just need to make sure the exit with
+                             // code 1.
+  }
+
+  return usage_flag_action == UsageFlagsAction::kHandleUsage
+             ? flags_internal::HandleUsageFlags(std::cout,
+                                                ProgramUsageMessage())
+             : HelpMode::kNone;
 }
 
 }  // namespace flags_internal
 
+void ParseAbseilFlagsOnly(int argc, char* argv[],
+                          std::vector<char*>& positional_args,
+                          std::vector<UnrecognizedFlag>& unrecognized_flags) {
+  auto help_mode = flags_internal::ParseAbseilFlagsOnlyImpl(
+      argc, argv, positional_args, unrecognized_flags,
+      flags_internal::UsageFlagsAction::kHandleUsage);
+
+  flags_internal::MaybeExit(help_mode);
+}
+
+// --------------------------------------------------------------------
+
+void ReportUnrecognizedFlags(
+    const std::vector<UnrecognizedFlag>& unrecognized_flags) {
+  flags_internal::ReportUnrecognizedFlags(unrecognized_flags, true);
+}
+
 // --------------------------------------------------------------------
 
 std::vector<char*> ParseCommandLine(int argc, char* argv[]) {
   return flags_internal::ParseCommandLineImpl(
-      argc, argv, flags_internal::ArgvListAction::kRemoveParsedArgs,
-      flags_internal::UsageFlagsAction::kHandleUsage,
+      argc, argv, flags_internal::UsageFlagsAction::kHandleUsage,
       flags_internal::OnUndefinedFlag::kAbortIfUndefined);
 }
 
diff --git a/absl/flags/parse.h b/absl/flags/parse.h
index 929de2c..f2a5cb1 100644
--- a/absl/flags/parse.h
+++ b/absl/flags/parse.h
@@ -23,6 +23,7 @@
 #ifndef ABSL_FLAGS_PARSE_H_
 #define ABSL_FLAGS_PARSE_H_
 
+#include <string>
 #include <vector>
 
 #include "absl/base/config.h"
@@ -31,27 +32,96 @@
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
+// This type represent information about an unrecognized flag in the command
+// line.
+struct UnrecognizedFlag {
+  enum Source { kFromArgv, kFromFlagfile };
+
+  explicit UnrecognizedFlag(Source s, absl::string_view f)
+      : source(s), flag_name(f) {}
+  // This field indicates where we found this flag: on the original command line
+  // or read in some flag file.
+  Source source;
+  // Name of the flag we did not recognize in --flag_name=value or --flag_name.
+  std::string flag_name;
+};
+
+inline bool operator==(const UnrecognizedFlag& lhs,
+                       const UnrecognizedFlag& rhs) {
+  return lhs.source == rhs.source && lhs.flag_name == rhs.flag_name;
+}
+
+namespace flags_internal {
+
+HelpMode ParseAbseilFlagsOnlyImpl(
+    int argc, char* argv[], std::vector<char*>& positional_args,
+    std::vector<UnrecognizedFlag>& unrecognized_flags,
+    UsageFlagsAction usage_flag_action);
+
+}  // namespace flags_internal
+
+// ParseAbseilFlagsOnly()
+//
+// Parses a list of command-line arguments, passed in the `argc` and `argv[]`
+// parameters, into a set of Abseil Flag values, returning any unparsed
+// arguments in `positional_args` and `unrecognized_flags` output parameters.
+//
+// This function classifies all the arguments (including content of the
+// flagfiles, if any) into one of the following groups:
+//
+//   * arguments specified as "--flag=value" or "--flag value" that match
+//     registered or built-in Abseil Flags. These are "Abseil Flag arguments."
+//   * arguments specified as "--flag" that are unrecognized as Abseil Flags
+//   * arguments that are not specified as "--flag" are positional arguments
+//   * arguments that follow the flag-terminating delimiter (`--`) are also
+//     treated as positional arguments regardless of their syntax.
+//
+// All of the deduced Abseil Flag arguments are then parsed into their
+// corresponding flag values. If any syntax errors are found in these arguments,
+// the binary exits with code 1.
+//
+// This function also handles Abseil Flags built-in usage flags (e.g. --help)
+// if any were present on the command line.
+//
+// All the remaining positional arguments including original program name
+// (argv[0]) are are returned in the `positional_args` output parameter.
+//
+// All unrecognized flags that are not otherwise ignored are returned in the
+// `unrecognized_flags` output parameter. Note that the special `undefok`
+// flag allows you to specify flags which can be safely ignored; `undefok`
+// specifies these flags as a comma-separated list. Any unrecognized flags
+// that appear within `undefok` will therefore be ignored and not included in
+// the `unrecognized_flag` output parameter.
+//
+void ParseAbseilFlagsOnly(int argc, char* argv[],
+                          std::vector<char*>& positional_args,
+                          std::vector<UnrecognizedFlag>& unrecognized_flags);
+
+// ReportUnrecognizedFlags()
+//
+// Reports an error to `stderr` for all non-ignored unrecognized flags in
+// the provided `unrecognized_flags` list.
+void ReportUnrecognizedFlags(
+    const std::vector<UnrecognizedFlag>& unrecognized_flags);
+
 // ParseCommandLine()
 //
-// Parses the set of command-line arguments passed in the `argc` (argument
-// count) and `argv[]` (argument vector) parameters from `main()`, assigning
-// values to any defined Abseil flags. (Any arguments passed after the
-// flag-terminating delimiter (`--`) are treated as positional arguments and
-// ignored.)
+// First parses Abseil Flags only from the command line according to the
+// description in `ParseAbseilFlagsOnly`. In addition this function handles
+// unrecognized and usage flags.
 //
-// Any command-line flags (and arguments to those flags) are parsed into Abseil
-// Flag values, if those flags are defined. Any undefined flags will either
-// return an error, or be ignored if that flag is designated using `undefok` to
-// indicate "undefined is OK."
+// If any unrecognized flags are located they are reported using
+// `ReportUnrecognizedFlags`.
 //
-// Any command-line positional arguments not part of any command-line flag (or
-// arguments to a flag) are returned in a vector, with the program invocation
-// name at position 0 of that vector. (Note that this includes positional
-// arguments after the flag-terminating delimiter `--`.)
+// If any errors detected during command line parsing, this routine reports a
+// usage message and aborts the program.
 //
-// After all flags and flag arguments are parsed, this function looks for any
-// built-in usage flags (e.g. `--help`), and if any were specified, it reports
-// help messages and then exits the program.
+// If any built-in usage flags were specified on the command line (e.g.
+// `--help`), this function reports help messages and then gracefully exits the
+// program.
+//
+// This function returns all the remaining positional arguments collected by
+// `ParseAbseilFlagsOnly`.
 std::vector<char*> ParseCommandLine(int argc, char* argv[]);
 
 ABSL_NAMESPACE_END
diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc
index 8dc91db..cd32efc 100644
--- a/absl/flags/parse_test.cc
+++ b/absl/flags/parse_test.cc
@@ -18,6 +18,7 @@
 #include <stdlib.h>
 
 #include <fstream>
+#include <iostream>
 #include <string>
 #include <vector>
 
@@ -25,7 +26,6 @@
 #include "gtest/gtest.h"
 #include "absl/base/internal/raw_logging.h"
 #include "absl/base/internal/scoped_set_env.h"
-#include "absl/flags/declare.h"
 #include "absl/flags/flag.h"
 #include "absl/flags/internal/parse.h"
 #include "absl/flags/internal/usage.h"
@@ -39,6 +39,36 @@
 #include <windows.h>
 #endif
 
+// Define 125 similar flags to test kMaxHints for flag suggestions.
+#define FLAG_MULT(x) F3(x)
+#define TEST_FLAG_HEADER FLAG_HEADER_
+
+#define F(name) ABSL_FLAG(int, name, 0, "");
+
+#define F1(name) \
+  F(name##1);    \
+  F(name##2);    \
+  F(name##3);    \
+  F(name##4);    \
+  F(name##5);
+/**/
+#define F2(name) \
+  F1(name##1);   \
+  F1(name##2);   \
+  F1(name##3);   \
+  F1(name##4);   \
+  F1(name##5);
+/**/
+#define F3(name) \
+  F2(name##1);   \
+  F2(name##2);   \
+  F2(name##3);   \
+  F2(name##4);   \
+  F2(name##5);
+/**/
+
+FLAG_MULT(TEST_FLAG_HEADER)
+
 namespace {
 
 using absl::base_internal::ScopedSetEnv;
@@ -168,7 +198,7 @@
 // Builds flagfile flag in the flagfile_flag buffer and returns it. This
 // function also creates a temporary flagfile based on FlagfileData input.
 // We create a flagfile in a temporary directory with the name specified in
-// FlagfileData and populate it with lines specifed in FlagfileData. If $0 is
+// FlagfileData and populate it with lines specified in FlagfileData. If $0 is
 // referenced in any of the lines in FlagfileData they are replaced with
 // temporary directory location. This way we can test inclusion of one flagfile
 // from another flagfile.
@@ -206,7 +236,9 @@
 namespace {
 
 namespace flags = absl::flags_internal;
+using testing::AllOf;
 using testing::ElementsAreArray;
+using testing::HasSubstr;
 
 class ParseTest : public testing::Test {
  public:
@@ -219,6 +251,38 @@
 // --------------------------------------------------------------------
 
 template <int N>
+flags::HelpMode InvokeParseAbslOnlyImpl(const char* (&in_argv)[N]) {
+  std::vector<char*> positional_args;
+  std::vector<absl::UnrecognizedFlag> unrecognized_flags;
+
+  return flags::ParseAbseilFlagsOnlyImpl(N, const_cast<char**>(in_argv),
+                                         positional_args, unrecognized_flags,
+                                         flags::UsageFlagsAction::kHandleUsage);
+}
+
+// --------------------------------------------------------------------
+
+template <int N>
+void InvokeParseAbslOnly(const char* (&in_argv)[N]) {
+  std::vector<char*> positional_args;
+  std::vector<absl::UnrecognizedFlag> unrecognized_flags;
+
+  absl::ParseAbseilFlagsOnly(2, const_cast<char**>(in_argv), positional_args,
+                             unrecognized_flags);
+}
+
+// --------------------------------------------------------------------
+
+template <int N>
+std::vector<char*> InvokeParseCommandLineImpl(const char* (&in_argv)[N]) {
+  return flags::ParseCommandLineImpl(
+      N, const_cast<char**>(in_argv), flags::UsageFlagsAction::kHandleUsage,
+      flags::OnUndefinedFlag::kAbortIfUndefined, std::cerr);
+}
+
+// --------------------------------------------------------------------
+
+template <int N>
 std::vector<char*> InvokeParse(const char* (&in_argv)[N]) {
   return absl::ParseCommandLine(N, const_cast<char**>(in_argv));
 }
@@ -565,6 +629,49 @@
 
 // --------------------------------------------------------------------
 
+TEST_F(ParseDeathTest, TestFlagSuggestions) {
+  const char* in_args1[] = {
+      "testbin",
+      "--legacy_boo",
+  };
+  EXPECT_DEATH_IF_SUPPORTED(
+      InvokeParse(in_args1),
+      "Unknown command line flag 'legacy_boo'. Did you mean: legacy_bool ?");
+
+  const char* in_args2[] = {"testbin", "--foo", "--undefok=foo1"};
+  EXPECT_DEATH_IF_SUPPORTED(
+      InvokeParse(in_args2),
+      "Unknown command line flag 'foo'. Did you mean: foo1 \\(undefok\\)?");
+
+  const char* in_args3[] = {
+      "testbin",
+      "--nolegacy_ino",
+  };
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args3),
+                            "Unknown command line flag 'nolegacy_ino'. Did "
+                            "you mean: nolegacy_bool, legacy_int ?");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, GetHints) {
+  EXPECT_THAT(absl::flags_internal::GetMisspellingHints("legacy_boo"),
+              testing::ContainerEq(std::vector<std::string>{"legacy_bool"}));
+  EXPECT_THAT(absl::flags_internal::GetMisspellingHints("nolegacy_itn"),
+              testing::ContainerEq(std::vector<std::string>{"legacy_int"}));
+  EXPECT_THAT(absl::flags_internal::GetMisspellingHints("nolegacy_int1"),
+              testing::ContainerEq(std::vector<std::string>{"legacy_int"}));
+  EXPECT_THAT(absl::flags_internal::GetMisspellingHints("nolegacy_int"),
+              testing::ContainerEq(std::vector<std::string>{"legacy_int"}));
+  EXPECT_THAT(absl::flags_internal::GetMisspellingHints("nolegacy_ino"),
+              testing::ContainerEq(
+                  std::vector<std::string>{"nolegacy_bool", "legacy_int"}));
+  EXPECT_THAT(
+      absl::flags_internal::GetMisspellingHints("FLAG_HEADER_000").size(), 100);
+}
+
+// --------------------------------------------------------------------
+
 TEST_F(ParseTest, TestLegacyFlags) {
   const char* in_args1[] = {
       "testbin",
@@ -780,88 +887,13 @@
 
 // --------------------------------------------------------------------
 
-TEST_F(ParseTest, TestKeepParsedArgs) {
-  const char* in_args1[] = {
-      "testbin",        "arg1", "--bool_flag",
-      "--int_flag=211", "arg2", "--double_flag=1.1",
-      "--string_flag",  "asd",  "--",
-      "arg3",           "arg4",
-  };
-
-  auto out_args1 = InvokeParse(in_args1);
-
-  EXPECT_THAT(
-      out_args1,
-      ElementsAreArray({absl::string_view("testbin"), absl::string_view("arg1"),
-                        absl::string_view("arg2"), absl::string_view("arg3"),
-                        absl::string_view("arg4")}));
-
-  auto out_args2 = flags::ParseCommandLineImpl(
-      11, const_cast<char**>(in_args1), flags::ArgvListAction::kKeepParsedArgs,
-      flags::UsageFlagsAction::kHandleUsage,
-      flags::OnUndefinedFlag::kAbortIfUndefined);
-
-  EXPECT_THAT(
-      out_args2,
-      ElementsAreArray({absl::string_view("testbin"),
-                        absl::string_view("--bool_flag"),
-                        absl::string_view("--int_flag=211"),
-                        absl::string_view("--double_flag=1.1"),
-                        absl::string_view("--string_flag"),
-                        absl::string_view("asd"), absl::string_view("--"),
-                        absl::string_view("arg1"), absl::string_view("arg2"),
-                        absl::string_view("arg3"), absl::string_view("arg4")}));
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestIgnoreUndefinedFlags) {
-  const char* in_args1[] = {
-      "testbin",
-      "arg1",
-      "--undef_flag=aa",
-      "--int_flag=21",
-  };
-
-  auto out_args1 = flags::ParseCommandLineImpl(
-      4, const_cast<char**>(in_args1), flags::ArgvListAction::kRemoveParsedArgs,
-      flags::UsageFlagsAction::kHandleUsage,
-      flags::OnUndefinedFlag::kIgnoreUndefined);
-
-  EXPECT_THAT(out_args1, ElementsAreArray({absl::string_view("testbin"),
-                                           absl::string_view("arg1")}));
-
-  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 21);
-
-  const char* in_args2[] = {
-      "testbin",
-      "arg1",
-      "--undef_flag=aa",
-      "--string_flag=AA",
-  };
-
-  auto out_args2 = flags::ParseCommandLineImpl(
-      4, const_cast<char**>(in_args2), flags::ArgvListAction::kKeepParsedArgs,
-      flags::UsageFlagsAction::kHandleUsage,
-      flags::OnUndefinedFlag::kIgnoreUndefined);
-
-  EXPECT_THAT(
-      out_args2,
-      ElementsAreArray(
-          {absl::string_view("testbin"), absl::string_view("--undef_flag=aa"),
-           absl::string_view("--string_flag=AA"), absl::string_view("arg1")}));
-
-  EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "AA");
-}
-
-// --------------------------------------------------------------------
-
 TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) {
   const char* in_args1[] = {
       "testbin",
       "--help",
   };
 
+  EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args1), flags::HelpMode::kImportant);
   EXPECT_EXIT(InvokeParse(in_args1), testing::ExitedWithCode(1), "");
 
   const char* in_args2[] = {
@@ -870,39 +902,51 @@
       "--int_flag=3",
   };
 
-  auto out_args2 = flags::ParseCommandLineImpl(
-      3, const_cast<char**>(in_args2), flags::ArgvListAction::kRemoveParsedArgs,
-      flags::UsageFlagsAction::kIgnoreUsage,
-      flags::OnUndefinedFlag::kAbortIfUndefined);
-
-  EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
+  EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args2), flags::HelpMode::kImportant);
   EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3);
+
+  const char* in_args3[] = {"testbin", "--help", "some_positional_arg"};
+
+  EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args3), flags::HelpMode::kImportant);
 }
 
 // --------------------------------------------------------------------
 
-TEST_F(ParseDeathTest, TestSubstringHelpFlagHandling) {
+TEST_F(ParseTest, TestSubstringHelpFlagHandling) {
   const char* in_args1[] = {
       "testbin",
       "--help=abcd",
   };
 
-  auto out_args1 = flags::ParseCommandLineImpl(
-      2, const_cast<char**>(in_args1), flags::ArgvListAction::kRemoveParsedArgs,
-      flags::UsageFlagsAction::kIgnoreUsage,
-      flags::OnUndefinedFlag::kAbortIfUndefined);
-
-  EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kMatch);
+  EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args1), flags::HelpMode::kMatch);
   EXPECT_EQ(flags::GetFlagsHelpMatchSubstr(), "abcd");
+}
 
-  const char* in_args2[] = {"testbin", "--help", "some_positional_arg"};
+// --------------------------------------------------------------------
 
-  auto out_args2 = flags::ParseCommandLineImpl(
-      3, const_cast<char**>(in_args2), flags::ArgvListAction::kRemoveParsedArgs,
-      flags::UsageFlagsAction::kIgnoreUsage,
-      flags::OnUndefinedFlag::kAbortIfUndefined);
+TEST_F(ParseDeathTest, TestVersionHandling) {
+  const char* in_args1[] = {
+      "testbin",
+      "--version",
+  };
 
-  EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
+  EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args1), flags::HelpMode::kVersion);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestCheckArgsHandling) {
+  const char* in_args1[] = {"testbin", "--only_check_args", "--int_flag=211"};
+
+  EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args1), flags::HelpMode::kOnlyCheckArgs);
+  EXPECT_EXIT(InvokeParseAbslOnly(in_args1), testing::ExitedWithCode(0), "");
+  EXPECT_EXIT(InvokeParse(in_args1), testing::ExitedWithCode(0), "");
+
+  const char* in_args2[] = {"testbin", "--only_check_args", "--unknown_flag=a"};
+
+  EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args2), flags::HelpMode::kOnlyCheckArgs);
+  EXPECT_EXIT(InvokeParseAbslOnly(in_args2), testing::ExitedWithCode(0), "");
+  EXPECT_EXIT(InvokeParse(in_args2), testing::ExitedWithCode(1), "");
 }
 
 // --------------------------------------------------------------------
@@ -927,4 +971,118 @@
 
 // --------------------------------------------------------------------
 
+TEST_F(ParseTest, ParseAbseilFlagsOnlySuccess) {
+  const char* in_args[] = {
+      "testbin",
+      "arg1",
+      "--bool_flag",
+      "--int_flag=211",
+      "arg2",
+      "--double_flag=1.1",
+      "--undef_flag1",
+      "--undef_flag2=123",
+      "--string_flag",
+      "asd",
+      "--",
+      "--some_flag",
+      "arg4",
+  };
+
+  std::vector<char*> positional_args;
+  std::vector<absl::UnrecognizedFlag> unrecognized_flags;
+
+  absl::ParseAbseilFlagsOnly(13, const_cast<char**>(in_args), positional_args,
+                             unrecognized_flags);
+  EXPECT_THAT(positional_args,
+              ElementsAreArray(
+                  {absl::string_view("testbin"), absl::string_view("arg1"),
+                   absl::string_view("arg2"), absl::string_view("--some_flag"),
+                   absl::string_view("arg4")}));
+  EXPECT_THAT(unrecognized_flags,
+              ElementsAreArray(
+                  {absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
+                                          "undef_flag1"),
+                   absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
+                                          "undef_flag2")}));
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseDeathTest, ParseAbseilFlagsOnlyFailure) {
+  const char* in_args[] = {
+      "testbin",
+      "--int_flag=21.1",
+  };
+
+  EXPECT_DEATH_IF_SUPPORTED(
+      InvokeParseAbslOnly(in_args),
+      "Illegal value '21.1' specified for flag 'int_flag'");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, UndefOkFlagsAreIgnored) {
+  const char* in_args[] = {
+      "testbin",           "--undef_flag1",
+      "--undef_flag2=123", "--undefok=undef_flag2",
+      "--undef_flag3",     "value",
+  };
+
+  std::vector<char*> positional_args;
+  std::vector<absl::UnrecognizedFlag> unrecognized_flags;
+
+  absl::ParseAbseilFlagsOnly(6, const_cast<char**>(in_args), positional_args,
+                             unrecognized_flags);
+  EXPECT_THAT(positional_args, ElementsAreArray({absl::string_view("testbin"),
+                                                 absl::string_view("value")}));
+  EXPECT_THAT(unrecognized_flags,
+              ElementsAreArray(
+                  {absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
+                                          "undef_flag1"),
+                   absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv,
+                                          "undef_flag3")}));
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, AllUndefOkFlagsAreIgnored) {
+  const char* in_args[] = {
+      "testbin",
+      "--undef_flag1",
+      "--undef_flag2=123",
+      "--undefok=undef_flag2,undef_flag1,undef_flag3",
+      "--undef_flag3",
+      "value",
+      "--",
+      "--undef_flag4",
+  };
+
+  std::vector<char*> positional_args;
+  std::vector<absl::UnrecognizedFlag> unrecognized_flags;
+
+  absl::ParseAbseilFlagsOnly(8, const_cast<char**>(in_args), positional_args,
+                             unrecognized_flags);
+  EXPECT_THAT(positional_args,
+              ElementsAreArray({absl::string_view("testbin"),
+                                absl::string_view("value"),
+                                absl::string_view("--undef_flag4")}));
+  EXPECT_THAT(unrecognized_flags, testing::IsEmpty());
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseDeathTest, ExitOnUnrecognizedFlagPrintsHelp) {
+  const char* in_args[] = {
+      "testbin",
+      "--undef_flag1",
+      "--help=int_flag",
+  };
+
+  EXPECT_EXIT(InvokeParseCommandLineImpl(in_args), testing::ExitedWithCode(1),
+              AllOf(HasSubstr("Unknown command line flag 'undef_flag1'"),
+                    HasSubstr("Try --helpfull to get a list of all flags")));
+}
+
+// --------------------------------------------------------------------
+
 }  // namespace
diff --git a/absl/flags/usage.cc b/absl/flags/usage.cc
index 452f667..267a503 100644
--- a/absl/flags/usage.cc
+++ b/absl/flags/usage.cc
@@ -21,6 +21,7 @@
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/const_init.h"
+#include "absl/base/internal/raw_logging.h"
 #include "absl/base/thread_annotations.h"
 #include "absl/flags/internal/usage.h"
 #include "absl/strings/string_view.h"
diff --git a/absl/functional/BUILD.bazel b/absl/functional/BUILD.bazel
index c4fbce9..4ceac53 100644
--- a/absl/functional/BUILD.bazel
+++ b/absl/functional/BUILD.bazel
@@ -92,6 +92,7 @@
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
+        ":any_invocable",
         "//absl/base:base_internal",
         "//absl/base:core_headers",
         "//absl/meta:type_traits",
@@ -104,6 +105,7 @@
     srcs = ["function_ref_test.cc"],
     copts = ABSL_TEST_COPTS,
     deps = [
+        ":any_invocable",
         ":function_ref",
         "//absl/container:test_instance_tracker",
         "//absl/memory",
diff --git a/absl/functional/CMakeLists.txt b/absl/functional/CMakeLists.txt
index c0f6eaa..628b2ff 100644
--- a/absl/functional/CMakeLists.txt
+++ b/absl/functional/CMakeLists.txt
@@ -90,6 +90,7 @@
   DEPS
     absl::base_internal
     absl::core_headers
+    absl::any_invocable
     absl::meta
   PUBLIC
 )
diff --git a/absl/functional/any_invocable.h b/absl/functional/any_invocable.h
index 040418d..68d8825 100644
--- a/absl/functional/any_invocable.h
+++ b/absl/functional/any_invocable.h
@@ -148,6 +148,9 @@
 //     // rvalue-reference qualified.
 //     std::move(continuation)(result_of_foo);
 //   }
+//
+// Attempting to call `absl::AnyInvocable` multiple times in such a case
+// results in undefined behavior.
 template <class Sig>
 class AnyInvocable : private internal_any_invocable::Impl<Sig> {
  private:
@@ -263,9 +266,17 @@
   // Exchanges the targets of `*this` and `other`.
   void swap(AnyInvocable& other) noexcept { std::swap(*this, other); }
 
-  // abl::AnyInvocable::operator bool()
+  // absl::AnyInvocable::operator bool()
   //
   // Returns `true` if `*this` is not empty.
+  //
+  // WARNING: An `AnyInvocable` that wraps an empty `std::function` is not
+  // itself empty. This behavior is consistent with the standard equivalent
+  // `std::move_only_function`.
+  //
+  // In other words:
+  //   std::function<void()> f;  // empty
+  //   absl::AnyInvocable<void()> a = std::move(f);  // not empty
   explicit operator bool() const noexcept { return this->HasValue(); }
 
   // Invokes the target object of `*this`. `*this` must not be empty.
diff --git a/absl/functional/any_invocable_test.cc b/absl/functional/any_invocable_test.cc
index dabaae9..a740faa 100644
--- a/absl/functional/any_invocable_test.cc
+++ b/absl/functional/any_invocable_test.cc
@@ -16,6 +16,7 @@
 
 #include <cstddef>
 #include <initializer_list>
+#include <memory>
 #include <numeric>
 #include <type_traits>
 
@@ -1156,9 +1157,6 @@
 
   EXPECT_TRUE(static_cast<bool>(fun));
   EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value);
-
-  EXPECT_TRUE(static_cast<bool>(fun));
-  EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value);
 }
 
 TYPED_TEST_P(AnyInvTestMovable, ConversionConstructionVoidCovariance) {
@@ -1179,9 +1177,6 @@
 
   EXPECT_TRUE(static_cast<bool>(fun));
   EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value);
-
-  EXPECT_TRUE(static_cast<bool>(fun));
-  EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value);
 }
 
 TYPED_TEST_P(AnyInvTestMovable, ConversionAssignUserDefinedTypeNonemptyLhs) {
@@ -1193,9 +1188,6 @@
 
   EXPECT_TRUE(static_cast<bool>(fun));
   EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value);
-
-  EXPECT_TRUE(static_cast<bool>(fun));
-  EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value);
 }
 
 TYPED_TEST_P(AnyInvTestMovable, ConversionAssignVoidCovariance) {
@@ -1414,6 +1406,41 @@
       std::is_assignable<AnyInvType&, std::reference_wrapper<AddType>>::value));
 }
 
+TYPED_TEST_P(AnyInvTestRvalue, NonConstCrashesOnSecondCall) {
+  using AnyInvType = typename TypeParam::AnyInvType;
+  using AddType = typename TypeParam::AddType;
+
+  AnyInvType fun(absl::in_place_type<AddType>, 5);
+
+  EXPECT_TRUE(static_cast<bool>(fun));
+  std::move(fun)(7, 8, 9);
+
+  // Ensure we're still valid
+  EXPECT_TRUE(static_cast<bool>(fun));  // NOLINT(bugprone-use-after-move)
+
+#if !defined(NDEBUG)
+  EXPECT_DEATH_IF_SUPPORTED(std::move(fun)(7, 8, 9), "");
+#endif
+}
+
+// Ensure that any qualifiers (in particular &&-qualifiers) do not affect
+// when the destructor is actually run.
+TYPED_TEST_P(AnyInvTestRvalue, QualifierIndependentObjectLifetime) {
+  using AnyInvType = typename TypeParam::AnyInvType;
+
+  auto refs = std::make_shared<std::nullptr_t>();
+  {
+    AnyInvType fun([refs](auto&&...) noexcept { return 0; });
+    EXPECT_GT(refs.use_count(), 1);
+
+    std::move(fun)(7, 8, 9);
+
+    // Ensure destructor hasn't run even if rref-qualified
+    EXPECT_GT(refs.use_count(), 1);
+  }
+  EXPECT_EQ(refs.use_count(), 1);
+}
+
 // NOTE: This test suite originally attempted to enumerate all possible
 // combinations of type properties but the build-time started getting too large.
 // Instead, it is now assumed that certain parameters are orthogonal and so
@@ -1670,7 +1697,9 @@
 REGISTER_TYPED_TEST_SUITE_P(AnyInvTestRvalue,
                             ConversionConstructionReferenceWrapper,
                             NonMoveableResultType,
-                            ConversionAssignReferenceWrapper);
+                            ConversionAssignReferenceWrapper,
+                            NonConstCrashesOnSecondCall,
+                            QualifierIndependentObjectLifetime);
 
 INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestRvalue,
                                TestParameterListRvalueQualifiersCallMayThrow);
diff --git a/absl/functional/bind_front.h b/absl/functional/bind_front.h
index f9075bd..a956eb0 100644
--- a/absl/functional/bind_front.h
+++ b/absl/functional/bind_front.h
@@ -46,7 +46,7 @@
 //
 // Like `std::bind()`, `absl::bind_front()` is implicitly convertible to
 // `std::function`.  In particular, it may be used as a simpler replacement for
-// `std::bind()` in most cases, as it does not require  placeholders to be
+// `std::bind()` in most cases, as it does not require placeholders to be
 // specified. More importantly, it provides more reliable correctness guarantees
 // than `std::bind()`; while `std::bind()` will silently ignore passing more
 // parameters than expected, for example, `absl::bind_front()` will report such
diff --git a/absl/functional/function_ref.h b/absl/functional/function_ref.h
index f977960..2b9139d 100644
--- a/absl/functional/function_ref.h
+++ b/absl/functional/function_ref.h
@@ -66,11 +66,11 @@
 
 // FunctionRef
 //
-// An `absl::FunctionRef` is a lightweight wrapper to any invokable object with
+// An `absl::FunctionRef` is a lightweight wrapper to any invocable object with
 // a compatible signature. Generally, an `absl::FunctionRef` should only be used
 // as an argument type and should be preferred as an argument over a const
 // reference to a `std::function`. `absl::FunctionRef` itself does not allocate,
-// although the wrapped invokable may.
+// although the wrapped invocable may.
 //
 // Example:
 //
@@ -98,7 +98,7 @@
                               std::is_convertible<FR, R>::value>::type;
 
  public:
-  // Constructs a FunctionRef from any invokable type.
+  // Constructs a FunctionRef from any invocable type.
   template <typename F, typename = EnableIfCompatible<const F&>>
   // NOLINTNEXTLINE(runtime/explicit)
   FunctionRef(const F& f ABSL_ATTRIBUTE_LIFETIME_BOUND)
diff --git a/absl/functional/function_ref_test.cc b/absl/functional/function_ref_test.cc
index 412027c..c61117e 100644
--- a/absl/functional/function_ref_test.cc
+++ b/absl/functional/function_ref_test.cc
@@ -20,6 +20,7 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/container/internal/test_instance_tracker.h"
+#include "absl/functional/any_invocable.h"
 #include "absl/memory/memory.h"
 
 namespace absl {
@@ -157,6 +158,25 @@
   EXPECT_DEBUG_DEATH({ FunctionRef<int(const S& s)> ref(mem_ptr); }, "");
 }
 
+TEST(FunctionRef, NullStdFunctionAssertPasses) {
+  std::function<void()> function = []() {};
+  FunctionRef<void()> ref(function);
+}
+
+TEST(FunctionRef, NullStdFunctionAssertFails) {
+  std::function<void()> function = nullptr;
+  EXPECT_DEBUG_DEATH({ FunctionRef<void()> ref(function); }, "");
+}
+
+TEST(FunctionRef, NullAnyInvocableAssertPasses) {
+  AnyInvocable<void() const> invocable = []() {};
+  FunctionRef<void()> ref(invocable);
+}
+TEST(FunctionRef, NullAnyInvocableAssertFails) {
+  AnyInvocable<void() const> invocable = nullptr;
+  EXPECT_DEBUG_DEATH({ FunctionRef<void()> ref(invocable); }, "");
+}
+
 #endif  // GTEST_HAS_DEATH_TEST
 
 TEST(FunctionRef, CopiesAndMovesPerPassByValue) {
@@ -237,7 +257,7 @@
       "Reference types should be preserved");
 
   // Make sure the address of an object received by reference is the same as the
-  // addess of the object passed by the caller.
+  // address of the object passed by the caller.
   {
     LargeTrivial obj;
     auto test = [&obj](LargeTrivial& input) { ASSERT_EQ(&input, &obj); };
@@ -253,6 +273,16 @@
   }
 }
 
+TEST(FunctionRef, ReferenceToIncompleteType) {
+  struct IncompleteType;
+  auto test = [](IncompleteType&) {};
+  absl::FunctionRef<void(IncompleteType&)> ref(test);
+
+  struct IncompleteType {};
+  IncompleteType obj;
+  ref(obj);
+}
+
 }  // namespace
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/functional/internal/any_invocable.h b/absl/functional/internal/any_invocable.h
index 35b389d..d41b7e5 100644
--- a/absl/functional/internal/any_invocable.h
+++ b/absl/functional/internal/any_invocable.h
@@ -56,6 +56,7 @@
 #include <cassert>
 #include <cstddef>
 #include <cstring>
+#include <exception>
 #include <functional>
 #include <initializer_list>
 #include <memory>
@@ -66,6 +67,7 @@
 #include "absl/base/config.h"
 #include "absl/base/internal/invoke.h"
 #include "absl/base/macros.h"
+#include "absl/base/optimization.h"
 #include "absl/meta/type_traits.h"
 #include "absl/utility/utility.h"
 
@@ -281,7 +283,7 @@
       from_object.~T();  // Must not throw. // NOLINT
       return;
   }
-  ABSL_INTERNAL_UNREACHABLE;
+  ABSL_UNREACHABLE();
 }
 
 // The invoker that is used when a target function is in local storage
@@ -319,7 +321,7 @@
 #endif  // __cpp_sized_deallocation
       return;
   }
-  ABSL_INTERNAL_UNREACHABLE;
+  ABSL_UNREACHABLE();
 }
 
 // The manager that is used when a target function is in remote storage and the
@@ -341,7 +343,7 @@
       ::delete static_cast<T*>(from->remote.target);  // Must not throw.
       return;
   }
-  ABSL_INTERNAL_UNREACHABLE;
+  ABSL_UNREACHABLE();
 }
 
 // The invoker that is used when a target function is in remote storage
@@ -486,7 +488,7 @@
     // object.
     Clear();
 
-    // Perform the actual move/destory operation on the target function.
+    // Perform the actual move/destroy operation on the target function.
     other.manager_(FunctionToCall::relocate_from_to, &other.state_, &state_);
     manager_ = other.manager_;
     invoker_ = other.invoker_;
@@ -809,11 +811,34 @@
         : Core(absl::in_place_type<absl::decay_t<T> inv_quals>,                \
                std::forward<Args>(args)...) {}                                 \
                                                                                \
+    /*Raises a fatal error when the AnyInvocable is invoked after a move*/     \
+    static ReturnType InvokedAfterMove(                                        \
+      TypeErasedState*,                                                        \
+      ForwardedParameterType<P>...) noexcept(noex) {                           \
+      ABSL_HARDENING_ASSERT(false && "AnyInvocable use-after-move");           \
+      std::terminate();                                                        \
+    }                                                                          \
+                                                                               \
+    InvokerType<noex, ReturnType, P...>* ExtractInvoker() cv {                 \
+      using QualifiedTestType = int cv ref;                                    \
+      auto* invoker = this->invoker_;                                          \
+      if (!std::is_const<QualifiedTestType>::value &&                          \
+          std::is_rvalue_reference<QualifiedTestType>::value) {                \
+        ABSL_ASSERT([this]() {                                                 \
+          /* We checked that this isn't const above, so const_cast is safe */  \
+          const_cast<Impl*>(this)->invoker_ = InvokedAfterMove;                \
+          return this->HasValue();                                             \
+        }());                                                                  \
+      }                                                                        \
+      return invoker;                                                          \
+    }                                                                          \
+                                                                               \
     /*The actual invocation operation with the proper signature*/              \
     ReturnType operator()(P... args) cv ref noexcept(noex) {                   \
       assert(this->invoker_ != nullptr);                                       \
-      return this->invoker_(const_cast<TypeErasedState*>(&this->state_),       \
-                            static_cast<ForwardedParameterType<P>>(args)...);  \
+      return this->ExtractInvoker()(                                           \
+          const_cast<TypeErasedState*>(&this->state_),                         \
+          static_cast<ForwardedParameterType<P>>(args)...);                    \
     }                                                                          \
   }
 
diff --git a/absl/functional/internal/function_ref.h b/absl/functional/internal/function_ref.h
index b5bb8b4..1cd34a3 100644
--- a/absl/functional/internal/function_ref.h
+++ b/absl/functional/internal/function_ref.h
@@ -20,6 +20,7 @@
 #include <type_traits>
 
 #include "absl/base/internal/invoke.h"
+#include "absl/functional/any_invocable.h"
 #include "absl/meta/type_traits.h"
 
 namespace absl {
@@ -40,18 +41,21 @@
 // Chooses the best type for passing T as an argument.
 // Attempt to be close to SystemV AMD64 ABI. Objects with trivial copy ctor are
 // passed by value.
-template <typename T>
-constexpr bool PassByValue() {
-  return !std::is_lvalue_reference<T>::value &&
-         absl::is_trivially_copy_constructible<T>::value &&
-         absl::is_trivially_copy_assignable<
-             typename std::remove_cv<T>::type>::value &&
-         std::is_trivially_destructible<T>::value &&
-         sizeof(T) <= 2 * sizeof(void*);
-}
+template <typename T,
+          bool IsLValueReference = std::is_lvalue_reference<T>::value>
+struct PassByValue : std::false_type {};
 
 template <typename T>
-struct ForwardT : std::conditional<PassByValue<T>(), T, T&&> {};
+struct PassByValue<T, /*IsLValueReference=*/false>
+    : std::integral_constant<bool,
+                             absl::is_trivially_copy_constructible<T>::value &&
+                                 absl::is_trivially_copy_assignable<
+                                     typename std::remove_cv<T>::type>::value &&
+                                 std::is_trivially_destructible<T>::value &&
+                                 sizeof(T) <= 2 * sizeof(void*)> {};
+
+template <typename T>
+struct ForwardT : std::conditional<PassByValue<T>::value, T, T&&> {};
 
 // An Invoker takes a pointer to the type-erased invokable object, followed by
 // the arguments that the invokable object expects.
@@ -87,6 +91,12 @@
   (void)f;
 }
 
+template <typename Sig>
+void AssertNonNull(const AnyInvocable<Sig>& f) {
+  assert(f != nullptr);
+  (void)f;
+}
+
 template <typename F>
 void AssertNonNull(const F&) {}
 
diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel
index bcc316f..a0db919 100644
--- a/absl/hash/BUILD.bazel
+++ b/absl/hash/BUILD.bazel
@@ -43,6 +43,7 @@
         "//absl/container:fixed_array",
         "//absl/functional:function_ref",
         "//absl/meta:type_traits",
+        "//absl/numeric:bits",
         "//absl/numeric:int128",
         "//absl/strings",
         "//absl/types:optional",
@@ -157,7 +158,6 @@
     deps = [
         "//absl/base:config",
         "//absl/base:endian",
-        "//absl/numeric:bits",
         "//absl/numeric:int128",
     ],
 )
diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt
index 1569125..f99f35b 100644
--- a/absl/hash/CMakeLists.txt
+++ b/absl/hash/CMakeLists.txt
@@ -25,6 +25,7 @@
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
+    absl::bits
     absl::city
     absl::config
     absl::core_headers
@@ -140,7 +141,6 @@
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
-    absl::bits
     absl::config
     absl::endian
     absl::int128
diff --git a/absl/hash/hash.h b/absl/hash/hash.h
index 74e2d7c..470cca4 100644
--- a/absl/hash/hash.h
+++ b/absl/hash/hash.h
@@ -42,7 +42,7 @@
 //
 // `absl::Hash` may also produce different values from different dynamically
 // loaded libraries. For this reason, `absl::Hash` values must never cross
-// boundries in dynamically loaded libraries (including when used in types like
+// boundaries in dynamically loaded libraries (including when used in types like
 // hash containers.)
 //
 // `absl::Hash` is intended to strongly mix input bits with a target of passing
@@ -110,9 +110,12 @@
 //   * std::unique_ptr and std::shared_ptr
 //   * All string-like types including:
 //     * absl::Cord
-//     * std::string
-//     * std::string_view (as well as any instance of std::basic_string that
-//       uses char and std::char_traits)
+//     * std::string (as well as any instance of std::basic_string that
+//       uses one of {char, wchar_t, char16_t, char32_t} and its associated
+//       std::char_traits)
+//     * std::string_view (as well as any instance of std::basic_string_view
+//       that uses one of {char, wchar_t, char16_t, char32_t} and its associated
+//       std::char_traits)
 //  * All the standard sequence containers (provided the elements are hashable)
 //  * All the standard associative containers (provided the elements are
 //    hashable)
diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc
index 744a2e5..6727daf 100644
--- a/absl/hash/hash_test.cc
+++ b/absl/hash/hash_test.cc
@@ -17,6 +17,7 @@
 #include <algorithm>
 #include <array>
 #include <bitset>
+#include <cstdint>
 #include <cstring>
 #include <deque>
 #include <forward_list>
@@ -52,6 +53,10 @@
 #include "absl/numeric/int128.h"
 #include "absl/strings/cord_test_helpers.h"
 
+#ifdef ABSL_HAVE_STD_STRING_VIEW
+#include <string_view>
+#endif
+
 namespace {
 
 // Utility wrapper of T for the purposes of testing the `AbslHash` type erasure
@@ -487,6 +492,47 @@
       std::u32string(U"Iñtërnâtiônàlizætiøn"))));
 }
 
+TEST(HashValueTest, WStringView) {
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+  GTEST_SKIP();
+#else
+  EXPECT_TRUE((is_hashable<std::wstring_view>::value));
+
+  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
+      std::wstring_view(), std::wstring_view(L"ABC"), std::wstring_view(L"ABC"),
+      std::wstring_view(L"Some other different string_view"),
+      std::wstring_view(L"Iñtërnâtiônàlizætiøn"))));
+#endif
+}
+
+TEST(HashValueTest, U16StringView) {
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+  GTEST_SKIP();
+#else
+  EXPECT_TRUE((is_hashable<std::u16string_view>::value));
+
+  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
+      std::make_tuple(std::u16string_view(), std::u16string_view(u"ABC"),
+                      std::u16string_view(u"ABC"),
+                      std::u16string_view(u"Some other different string_view"),
+                      std::u16string_view(u"Iñtërnâtiônàlizætiøn"))));
+#endif
+}
+
+TEST(HashValueTest, U32StringView) {
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+  GTEST_SKIP();
+#else
+  EXPECT_TRUE((is_hashable<std::u32string_view>::value));
+
+  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
+      std::make_tuple(std::u32string_view(), std::u32string_view(U"ABC"),
+                      std::u32string_view(U"ABC"),
+                      std::u32string_view(U"Some other different string_view"),
+                      std::u32string_view(U"Iñtërnâtiônàlizætiøn"))));
+#endif
+}
+
 TEST(HashValueTest, StdArray) {
   EXPECT_TRUE((is_hashable<std::array<int, 3>>::value));
 
@@ -904,8 +950,8 @@
   EXPECT_FALSE(absl::is_copy_assignable<absl::Hash<X>>::value);
   EXPECT_FALSE(absl::is_move_assignable<absl::Hash<X>>::value);
   EXPECT_FALSE(IsHashCallable<X>::value);
-#if !defined(__GNUC__) || __GNUC__ < 9
-  // This doesn't compile on GCC 9.
+#if !defined(__GNUC__) || defined(__clang__)
+  // TODO(b/144368551): As of GCC 8.4 this does not compile.
   EXPECT_FALSE(IsAggregateInitializable<absl::Hash<X>>::value);
 #endif
 }
@@ -1241,14 +1287,24 @@
 
 TEST(HashOf, MatchesHashForSingleArgument) {
   std::string s = "forty two";
-  int i = 42;
   double d = 42.0;
   std::tuple<int, int> t{4, 2};
+  int i = 42;
+  int neg_i = -42;
+  int16_t i16 = 42;
+  int16_t neg_i16 = -42;
+  int8_t i8 = 42;
+  int8_t neg_i8 = -42;
 
   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)));
+  EXPECT_EQ(absl::HashOf(i), absl::Hash<int>{}(i));
+  EXPECT_EQ(absl::HashOf(neg_i), absl::Hash<int>{}(neg_i));
+  EXPECT_EQ(absl::HashOf(i16), absl::Hash<int16_t>{}(i16));
+  EXPECT_EQ(absl::HashOf(neg_i16), absl::Hash<int16_t>{}(neg_i16));
+  EXPECT_EQ(absl::HashOf(i8), absl::Hash<int8_t>{}(i8));
+  EXPECT_EQ(absl::HashOf(neg_i8), absl::Hash<int8_t>{}(neg_i8));
 }
 
 TEST(HashOf, MatchesHashOfTupleForMultipleArguments) {
diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h
index dbdc205..ef3f366 100644
--- a/absl/hash/internal/hash.h
+++ b/absl/hash/internal/hash.h
@@ -49,12 +49,17 @@
 #include "absl/hash/internal/city.h"
 #include "absl/hash/internal/low_level_hash.h"
 #include "absl/meta/type_traits.h"
+#include "absl/numeric/bits.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"
 
+#ifdef ABSL_HAVE_STD_STRING_VIEW
+#include <string_view>
+#endif
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
@@ -423,7 +428,7 @@
 
 // AbslHashValue() for hashing pointers-to-member
 template <typename H, typename T, typename C>
-H AbslHashValue(H hash_state, T C::* ptr) {
+H AbslHashValue(H hash_state, T C::*ptr) {
   auto salient_ptm_size = [](std::size_t n) -> std::size_t {
 #if defined(_MSC_VER)
     // Pointers-to-member-function on MSVC consist of one pointer plus 0, 1, 2,
@@ -441,8 +446,8 @@
       return n == 24 ? 20 : n == 16 ? 12 : n;
     }
 #else
-    // On other platforms, we assume that pointers-to-members do not have
-    // padding.
+  // On other platforms, we assume that pointers-to-members do not have
+  // padding.
 #ifdef __cpp_lib_has_unique_object_representations
     static_assert(std::has_unique_object_representations<T C::*>::value);
 #endif  // __cpp_lib_has_unique_object_representations
@@ -515,14 +520,15 @@
 // the same character sequence. These types are:
 //
 //  - `absl::Cord`
-//  - `std::string` (and std::basic_string<char, std::char_traits<char>, A> for
-//      any allocator A)
-//  - `absl::string_view` and `std::string_view`
+//  - `std::string` (and std::basic_string<T, std::char_traits<T>, A> for
+//      any allocator A and any T in {char, wchar_t, char16_t, char32_t})
+//  - `absl::string_view`, `std::string_view`, `std::wstring_view`,
+//    `std::u16string_view`, and `std::u32_string_view`.
 //
-// For simplicity, we currently support only `char` strings. This support may
-// be broadened, if necessary, but with some caution - this overload would
-// misbehave in cases where the traits' `eq()` member isn't equivalent to `==`
-// on the underlying character type.
+// For simplicity, we currently support only strings built on `char`, `wchar_t`,
+// `char16_t`, or `char32_t`. This support may be broadened, if necessary, but
+// with some caution - this overload would misbehave in cases where the traits'
+// `eq()` member isn't equivalent to `==` on the underlying character type.
 template <typename H>
 H AbslHashValue(H hash_state, absl::string_view str) {
   return H::combine(
@@ -543,6 +549,21 @@
       str.size());
 }
 
+#ifdef ABSL_HAVE_STD_STRING_VIEW
+
+// Support std::wstring_view, std::u16string_view and std::u32string_view.
+template <typename Char, typename H,
+          typename = absl::enable_if_t<std::is_same<Char, wchar_t>::value ||
+                                       std::is_same<Char, char16_t>::value ||
+                                       std::is_same<Char, char32_t>::value>>
+H AbslHashValue(H hash_state, std::basic_string_view<Char> str) {
+  return H::combine(
+      H::combine_contiguous(std::move(hash_state), str.data(), str.size()),
+      str.size());
+}
+
+#endif  // ABSL_HAVE_STD_STRING_VIEW
+
 // -----------------------------------------------------------------------------
 // AbslHashValue for Sequence Containers
 // -----------------------------------------------------------------------------
@@ -934,8 +955,8 @@
 #endif  // ABSL_HAVE_INTRINSIC_INT128
 
   static constexpr uint64_t kMul =
-      sizeof(size_t) == 4 ? uint64_t{0xcc9e2d51}
-                          : uint64_t{0x9ddfea08eb382d69};
+  sizeof(size_t) == 4 ? uint64_t{0xcc9e2d51}
+                      : uint64_t{0x9ddfea08eb382d69};
 
   template <typename T>
   using IntegralFastPath =
@@ -968,7 +989,8 @@
   // The result should be the same as running the whole algorithm, but faster.
   template <typename T, absl::enable_if_t<IntegralFastPath<T>::value, int> = 0>
   static size_t hash(T value) {
-    return static_cast<size_t>(Mix(Seed(), static_cast<uint64_t>(value)));
+    return static_cast<size_t>(
+        Mix(Seed(), static_cast<std::make_unsigned_t<T>>(value)));
   }
 
   // Overload of MixingHashState::hash()
@@ -1052,7 +1074,7 @@
     uint64_t most_significant = low_mem;
     uint64_t least_significant = high_mem;
 #endif
-    return {least_significant, most_significant >> (128 - len * 8)};
+    return {least_significant, most_significant};
   }
 
   // Reads 4 to 8 bytes from p. Zero pads to fill uint64_t.
@@ -1072,6 +1094,7 @@
 
   // Reads 1 to 3 bytes from p. Zero pads to fill uint32_t.
   static uint32_t Read1To3(const unsigned char* p, size_t len) {
+    // The trick used by this implementation is to avoid branches if possible.
     unsigned char mem0 = p[0];
     unsigned char mem1 = p[len / 2];
     unsigned char mem2 = p[len - 1];
@@ -1081,7 +1104,7 @@
     unsigned char significant0 = mem0;
 #else
     unsigned char significant2 = mem0;
-    unsigned char significant1 = mem1;
+    unsigned char significant1 = len == 2 ? mem0 : mem1;
     unsigned char significant0 = mem2;
 #endif
     return static_cast<uint32_t>(significant0 |                     //
@@ -1134,7 +1157,8 @@
   // probably per-build and not per-process.
   ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Seed() {
 #if (!defined(__clang__) || __clang_major__ > 11) && \
-    !defined(__apple_build_version__)
+    (!defined(__apple_build_version__) ||            \
+     __apple_build_version__ >= 19558921)  // Xcode 12
     return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(&kSeed));
 #else
     // Workaround the absence of
@@ -1183,9 +1207,22 @@
     }
     v = Hash64(first, len);
   } else if (len > 8) {
+    // This hash function was constructed by the ML-driven algorithm discovery
+    // using reinforcement learning. We fed the agent lots of inputs from
+    // microbenchmarks, SMHasher, low hamming distance from generated inputs and
+    // picked up the one that was good on micro and macrobenchmarks.
     auto p = Read9To16(first, len);
-    state = Mix(state, p.first);
-    v = p.second;
+    uint64_t lo = p.first;
+    uint64_t hi = p.second;
+    // Rotation by 53 was found to be most often useful when discovering these
+    // hashing algorithms with ML techniques.
+    lo = absl::rotr(lo, 53);
+    state += kMul;
+    lo += state;
+    state ^= hi;
+    uint128 m = state;
+    m *= lo;
+    return static_cast<uint64_t>(m ^ (m >> 64));
   } else if (len >= 4) {
     v = Read4To8(first, len);
   } else if (len > 0) {
diff --git a/absl/hash/internal/low_level_hash.cc b/absl/hash/internal/low_level_hash.cc
index e05e788..c917457 100644
--- a/absl/hash/internal/low_level_hash.cc
+++ b/absl/hash/internal/low_level_hash.cc
@@ -15,7 +15,6 @@
 #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 {
@@ -23,20 +22,9 @@
 namespace hash_internal {
 
 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 LowLevelHash(const void* data, size_t len, uint64_t seed,
diff --git a/absl/hash/internal/low_level_hash_test.cc b/absl/hash/internal/low_level_hash_test.cc
index ae930b3..589a3d8 100644
--- a/absl/hash/internal/low_level_hash_test.cc
+++ b/absl/hash/internal/low_level_hash_test.cc
@@ -452,54 +452,6 @@
       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,
diff --git a/absl/log/BUILD.bazel b/absl/log/BUILD.bazel
index dadc885..4813111 100644
--- a/absl/log/BUILD.bazel
+++ b/absl/log/BUILD.bazel
@@ -26,13 +26,34 @@
 licenses(["notice"])
 
 # Public targets
+
+cc_library(
+    name = "absl_check",
+    hdrs = ["absl_check.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        "//absl/log/internal:check_impl",
+    ],
+)
+
+cc_library(
+    name = "absl_log",
+    hdrs = ["absl_log.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        "//absl/log/internal:log_impl",
+    ],
+)
+
 cc_library(
     name = "check",
     hdrs = ["check.h"],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
-        "//absl/base:core_headers",
+        "//absl/log/internal:check_impl",
         "//absl/log/internal:check_op",
         "//absl/log/internal:conditions",
         "//absl/log/internal:log_message",
@@ -88,6 +109,7 @@
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:log_severity",
+        "//absl/base:raw_logging_internal",
         "//absl/hash",
         "//absl/strings",
     ],
@@ -114,9 +136,7 @@
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
-        "//absl/log/internal:conditions",
-        "//absl/log/internal:log_message",
-        "//absl/log/internal:strip",
+        "//absl/log/internal:log_impl",
     ],
 )
 
@@ -167,7 +187,7 @@
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
-        ":log",
+        ":absl_log",
         "//absl/base:config",
         "//absl/base:log_severity",
         "//absl/strings",
@@ -196,23 +216,47 @@
     ],
 )
 
+cc_library(
+    name = "structured",
+    hdrs = ["structured.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        "//absl/base:config",
+        "//absl/log/internal:structured",
+        "//absl/strings",
+    ],
+)
+
 # Test targets
+
 cc_test(
-    name = "basic_log_test",
+    name = "absl_check_test",
     size = "small",
-    srcs = ["basic_log_test.cc"],
+    srcs = ["absl_check_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    tags = [
+        "no_test:os:ios",
+        "no_test_ios",
+        "no_test_wasm",
+    ],
+    deps = [
+        ":absl_check",
+        ":check_test_impl",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "absl_log_basic_test",
+    size = "small",
+    srcs = ["absl_log_basic_test.cc"],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
-        ":globals",
-        ":log",
-        ":log_entry",
-        ":scoped_mock_log",
-        "//absl/base",
-        "//absl/base:log_severity",
-        "//absl/log/internal:test_actions",
-        "//absl/log/internal:test_helpers",
-        "//absl/log/internal:test_matchers",
+        ":absl_log",
+        ":log_basic_test_impl",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -230,10 +274,29 @@
     ],
     deps = [
         ":check",
+        ":check_test_impl",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "check_test_impl",
+    testonly = True,
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    tags = [
+        "no_test:os:ios",
+        "no_test_ios",
+        "no_test_wasm",
+    ],
+    textual_hdrs = ["check_test_impl.h"],
+    visibility = ["//visibility:private"],
+    deps = [
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/log/internal:test_helpers",
-        "@com_google_googletest//:gtest_main",
+        "//absl/status",
+        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -293,6 +356,39 @@
 )
 
 cc_test(
+    name = "log_basic_test",
+    size = "small",
+    srcs = ["log_basic_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":log",
+        ":log_basic_test_impl",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "log_basic_test_impl",
+    testonly = True,
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    textual_hdrs = ["log_basic_test_impl.h"],
+    visibility = ["//visibility:private"],
+    deps = [
+        "//absl/base",
+        "//absl/base:log_severity",
+        "//absl/log:globals",
+        "//absl/log:log_entry",
+        "//absl/log:scoped_mock_log",
+        "//absl/log/internal:test_actions",
+        "//absl/log/internal:test_helpers",
+        "//absl/log/internal:test_matchers",
+        "@com_google_googletest//:gtest",
+    ],
+)
+
+cc_test(
     name = "log_entry_test",
     size = "small",
     srcs = ["log_entry_test.cc"],
@@ -303,6 +399,7 @@
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:log_severity",
+        "//absl/log/internal:append_truncated",
         "//absl/log/internal:format",
         "//absl/log/internal:test_helpers",
         "//absl/strings",
@@ -319,11 +416,13 @@
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
+        ":check",
         ":log",
         ":scoped_mock_log",
-        "//absl/log/internal:config",
         "//absl/log/internal:test_matchers",
         "//absl/strings",
+        "//absl/strings:str_format",
+        "//absl/types:optional",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -448,6 +547,7 @@
     deps = [
         ":check",
         ":log",
+        "//absl/base:log_severity",
         "//absl/base:strerror",
         "//absl/flags:program_name",
         "//absl/log/internal:test_helpers",
@@ -457,6 +557,23 @@
     ],
 )
 
+cc_test(
+    name = "structured_test",
+    size = "small",
+    srcs = ["structured_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":log",
+        ":scoped_mock_log",
+        ":structured",
+        "//absl/base:core_headers",
+        "//absl/log/internal:test_helpers",
+        "//absl/log/internal:test_matchers",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
 cc_binary(
     name = "log_benchmark",
     testonly = 1,
diff --git a/absl/log/CMakeLists.txt b/absl/log/CMakeLists.txt
index 09e4ca0..4cba008 100644
--- a/absl/log/CMakeLists.txt
+++ b/absl/log/CMakeLists.txt
@@ -17,6 +17,24 @@
 # Internal targets
 absl_cc_library(
   NAME
+    log_internal_check_impl
+  SRCS
+  HDRS
+    "internal/check_impl.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::core_headers
+    absl::log_internal_check_op
+    absl::log_internal_conditions
+    absl::log_internal_message
+    absl::log_internal_strip
+)
+
+absl_cc_library(
+  NAME
     log_internal_check_op
   SRCS
     "internal/check_op.cc"
@@ -96,6 +114,7 @@
   DEPS
     absl::config
     absl::core_headers
+    absl::log_internal_append_truncated
     absl::log_internal_config
     absl::log_internal_globals
     absl::log_severity
@@ -127,6 +146,41 @@
 
 absl_cc_library(
   NAME
+    log_internal_log_impl
+  SRCS
+  HDRS
+    "internal/log_impl.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::log_internal_conditions
+    absl::log_internal_message
+    absl::log_internal_strip
+)
+
+absl_cc_library(
+  NAME
+    log_internal_proto
+  SRCS
+    "internal/proto.cc"
+  HDRS
+    "internal/proto.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::base
+    absl::config
+    absl::core_headers
+    absl::strings
+    absl::span
+)
+
+absl_cc_library(
+  NAME
     log_internal_message
   SRCS
     "internal/log_message.cc"
@@ -143,9 +197,10 @@
     absl::errno_saver
     absl::inlined_vector
     absl::examine_stack
-    absl::log_internal_config
+    absl::log_internal_append_truncated
     absl::log_internal_format
     absl::log_internal_globals
+    absl::log_internal_proto
     absl::log_internal_log_sink_set
     absl::log_internal_nullguard
     absl::log_globals
@@ -157,7 +212,6 @@
     absl::raw_logging_internal
     absl::strings
     absl::strerror
-    absl::str_format
     absl::time
     absl::span
 )
@@ -195,6 +249,7 @@
   NAME
     log_internal_nullguard
   SRCS
+    "internal/nullguard.cc"
   HDRS
     "internal/nullguard.h"
   COPTS
@@ -203,6 +258,7 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::config
+    absl::core_headers
 )
 
 absl_cc_library(
@@ -251,8 +307,8 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::config
+    absl::core_headers
     absl::log_entry
-    absl::log_internal_config
     absl::log_severity
     absl::strings
     absl::time
@@ -293,8 +349,8 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::config
+    absl::core_headers
     absl::log_entry
-    absl::log_internal_config
     absl::log_internal_test_helpers
     absl::log_severity
     absl::strings
@@ -318,9 +374,55 @@
     absl::config
 )
 
+absl_cc_library(
+  NAME
+    log_internal_append_truncated
+  SRCS
+  HDRS
+    "internal/append_truncated.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::config
+    absl::strings
+    absl::span
+)
+
 # Public targets
 absl_cc_library(
   NAME
+    absl_check
+  SRCS
+  HDRS
+    "absl_check.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::log_internal_check_impl
+  PUBLIC
+)
+
+absl_cc_library(
+  NAME
+    absl_log
+  SRCS
+  HDRS
+    "absl_log.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::log_internal_log_impl
+  PUBLIC
+)
+
+absl_cc_library(
+  NAME
     check
   SRCS
   HDRS
@@ -330,6 +432,7 @@
   LINKOPTS
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
+    absl::log_internal_check_impl
     absl::core_headers
     absl::log_internal_check_op
     absl::log_internal_conditions
@@ -398,6 +501,7 @@
     absl::core_headers
     absl::hash
     absl::log_severity
+    absl::raw_logging_internal
     absl::strings
 )
 
@@ -431,9 +535,7 @@
   LINKOPTS
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
-    absl::log_internal_conditions
-    absl::log_internal_message
-    absl::log_internal_strip
+    absl::log_internal_log_impl
   PUBLIC
 )
 
@@ -505,7 +607,7 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::config
-    absl::log
+    absl::absl_log
     absl::log_severity
     absl::optional
     absl::strings
@@ -539,12 +641,125 @@
   TESTONLY
 )
 
+absl_cc_library(
+  NAME
+    log_internal_structured
+  HDRS
+    "internal/structured.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::config
+    absl::log_internal_message
+    absl::strings
+)
+
+absl_cc_library(
+  NAME
+    log_structured
+  HDRS
+    "structured.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::config
+    absl::log_internal_structured
+    absl::strings
+  PUBLIC
+)
+
 # Test targets
+
 absl_cc_test(
   NAME
-    basic_log_test
+    absl_check_test
   SRCS
-    "basic_log_test.cc"
+    "absl_check_test.cc"
+    "check_test_impl.h"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::absl_check
+    absl::config
+    absl::core_headers
+    absl::log_internal_test_helpers
+    absl::status
+    GTest::gmock
+    GTest::gtest_main
+)
+
+absl_cc_test(
+  NAME
+    absl_log_basic_test
+  SRCS
+    "log_basic_test.cc"
+    "log_basic_test_impl.h"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::base
+    absl::absl_log
+    absl::log_entry
+    absl::log_globals
+    absl::log_severity
+    absl::log_internal_test_actions
+    absl::log_internal_test_helpers
+    absl::log_internal_test_matchers
+    absl::scoped_mock_log
+    GTest::gmock
+    GTest::gtest_main
+)
+
+absl_cc_test(
+  NAME
+    check_test
+  SRCS
+    "check_test.cc"
+    "check_test_impl.h"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::check
+    absl::config
+    absl::core_headers
+    absl::log_internal_test_helpers
+    absl::status
+    GTest::gmock
+    GTest::gtest_main
+)
+
+absl_cc_test(
+  NAME
+    die_if_null_test
+  SRCS
+    "die_if_null_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::core_headers
+    absl::die_if_null
+    absl::log_internal_test_helpers
+    GTest::gtest_main
+)
+
+absl_cc_test(
+  NAME
+    log_basic_test
+  SRCS
+    "log_basic_test.cc"
+    "log_basic_test_impl.h"
   COPTS
     ${ABSL_TEST_COPTS}
   LINKOPTS
@@ -565,40 +780,31 @@
 
 absl_cc_test(
   NAME
-    check_test
+    log_entry_test
   SRCS
-    "check_test.cc"
+    "log_entry_test.cc"
   COPTS
     ${ABSL_TEST_COPTS}
   LINKOPTS
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
-    absl::check
     absl::config
     absl::core_headers
+    absl::log_entry
+    absl::log_internal_append_truncated
+    absl::log_internal_format
+    absl::log_internal_globals
     absl::log_internal_test_helpers
+    absl::log_severity
+    absl::span
+    absl::strings
+    absl::time
     GTest::gmock
     GTest::gtest_main
 )
 
 absl_cc_test(
   NAME
-    die_if_null_test
-  SRCS
-    "die_if_null_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::core_headers
-    absl::die_if_null
-    absl::log_internal_test_helpers
-    GTest::gtest_main
-)
-
-absl_cc_test(
-  NAME
     log_flags_test
   SRCS
     "flags_test.cc"
@@ -625,30 +831,6 @@
 
 absl_cc_test(
   NAME
-    log_entry_test
-  SRCS
-    "log_entry_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-    absl::log_entry
-    absl::log_internal_format
-    absl::log_internal_globals
-    absl::log_internal_test_helpers
-    absl::log_severity
-    absl::span
-    absl::strings
-    absl::time
-    GTest::gmock
-    GTest::gtest_main
-)
-
-absl_cc_test(
-  NAME
     log_globals_test
   SRCS
     "globals_test.cc"
@@ -677,10 +859,12 @@
   LINKOPTS
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
+    absl::check
     absl::log
-    absl::log_internal_config
     absl::log_internal_test_matchers
+    absl::optional
     absl::scoped_mock_log
+    absl::str_format
     absl::strings
     GTest::gmock
     GTest::gtest_main
@@ -831,9 +1015,30 @@
     absl::flags_program_name
     absl::log
     absl::log_internal_test_helpers
+    absl::log_severity
     absl::strerror
     absl::strings
     absl::str_format
     GTest::gmock
     GTest::gtest_main
 )
+
+absl_cc_test(
+  NAME
+    log_structured_test
+  SRCS
+    "structured_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  LINKOPTS
+    ${ABSL_DEFAULT_LINKOPTS}
+  DEPS
+    absl::core_headers
+    absl::log
+    absl::log_internal_test_helpers
+    absl::log_internal_test_matchers
+    absl::log_structured
+    absl::scoped_mock_log
+    GTest::gmock
+    GTest::gtest_main
+)
diff --git a/absl/log/absl_check.h b/absl/log/absl_check.h
new file mode 100644
index 0000000..14a2307
--- /dev/null
+++ b/absl/log/absl_check.h
@@ -0,0 +1,105 @@
+// Copyright 2022 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: log/absl_check.h
+// -----------------------------------------------------------------------------
+//
+// This header declares a family of `ABSL_CHECK` macros as alternative spellings
+// for `CHECK` macros in `check.h`.
+//
+// Except for those whose names begin with `ABSL_DCHECK`, these macros are not
+// controlled by `NDEBUG` (cf. `assert`), so the check will be executed
+// regardless of compilation mode. `ABSL_CHECK` and friends are thus useful for
+// confirming invariants in situations where continuing to run would be worse
+// than terminating, e.g., due to risk of data corruption or security
+// compromise.  It is also more robust and portable to deliberately terminate
+// at a particular place with a useful message and backtrace than to assume some
+// ultimately unspecified and unreliable crashing behavior (such as a
+// "segmentation fault").
+//
+// For full documentation of each macro, see comments in `check.h`, which has an
+// identical set of macros without the ABSL_* prefix.
+
+#ifndef ABSL_LOG_ABSL_CHECK_H_
+#define ABSL_LOG_ABSL_CHECK_H_
+
+#include "absl/log/internal/check_impl.h"
+
+#define ABSL_CHECK(condition) ABSL_CHECK_IMPL((condition), #condition)
+#define ABSL_QCHECK(condition) ABSL_QCHECK_IMPL((condition), #condition)
+#define ABSL_PCHECK(condition) ABSL_PCHECK_IMPL((condition), #condition)
+#define ABSL_DCHECK(condition) ABSL_DCHECK_IMPL((condition), #condition)
+
+#define ABSL_CHECK_EQ(val1, val2) \
+  ABSL_CHECK_EQ_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_CHECK_NE(val1, val2) \
+  ABSL_CHECK_NE_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_CHECK_LE(val1, val2) \
+  ABSL_CHECK_LE_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_CHECK_LT(val1, val2) \
+  ABSL_CHECK_LT_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_CHECK_GE(val1, val2) \
+  ABSL_CHECK_GE_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_CHECK_GT(val1, val2) \
+  ABSL_CHECK_GT_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_QCHECK_EQ(val1, val2) \
+  ABSL_QCHECK_EQ_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_QCHECK_NE(val1, val2) \
+  ABSL_QCHECK_NE_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_QCHECK_LE(val1, val2) \
+  ABSL_QCHECK_LE_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_QCHECK_LT(val1, val2) \
+  ABSL_QCHECK_LT_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_QCHECK_GE(val1, val2) \
+  ABSL_QCHECK_GE_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_QCHECK_GT(val1, val2) \
+  ABSL_QCHECK_GT_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_DCHECK_EQ(val1, val2) \
+  ABSL_DCHECK_EQ_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_DCHECK_NE(val1, val2) \
+  ABSL_DCHECK_NE_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_DCHECK_LE(val1, val2) \
+  ABSL_DCHECK_LE_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_DCHECK_LT(val1, val2) \
+  ABSL_DCHECK_LT_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_DCHECK_GE(val1, val2) \
+  ABSL_DCHECK_GE_IMPL((val1), #val1, (val2), #val2)
+#define ABSL_DCHECK_GT(val1, val2) \
+  ABSL_DCHECK_GT_IMPL((val1), #val1, (val2), #val2)
+
+#define ABSL_CHECK_OK(status) ABSL_CHECK_OK_IMPL((status), #status)
+#define ABSL_QCHECK_OK(status) ABSL_QCHECK_OK_IMPL((status), #status)
+#define ABSL_DCHECK_OK(status) ABSL_DCHECK_OK_IMPL((status), #status)
+
+#define ABSL_CHECK_STREQ(s1, s2) ABSL_CHECK_STREQ_IMPL((s1), #s1, (s2), #s2)
+#define ABSL_CHECK_STRNE(s1, s2) ABSL_CHECK_STRNE_IMPL((s1), #s1, (s2), #s2)
+#define ABSL_CHECK_STRCASEEQ(s1, s2) \
+  ABSL_CHECK_STRCASEEQ_IMPL((s1), #s1, (s2), #s2)
+#define ABSL_CHECK_STRCASENE(s1, s2) \
+  ABSL_CHECK_STRCASENE_IMPL((s1), #s1, (s2), #s2)
+#define ABSL_QCHECK_STREQ(s1, s2) ABSL_QCHECK_STREQ_IMPL((s1), #s1, (s2), #s2)
+#define ABSL_QCHECK_STRNE(s1, s2) ABSL_QCHECK_STRNE_IMPL((s1), #s1, (s2), #s2)
+#define ABSL_QCHECK_STRCASEEQ(s1, s2) \
+  ABSL_QCHECK_STRCASEEQ_IMPL((s1), #s1, (s2), #s2)
+#define ABSL_QCHECK_STRCASENE(s1, s2) \
+  ABSL_QCHECK_STRCASENE_IMPL((s1), #s1, (s2), #s2)
+#define ABSL_DCHECK_STREQ(s1, s2) ABSL_DCHECK_STREQ_IMPL((s1), #s1, (s2), #s2)
+#define ABSL_DCHECK_STRNE(s1, s2) ABSL_DCHECK_STRNE_IMPL((s1), #s1, (s2), #s2)
+#define ABSL_DCHECK_STRCASEEQ(s1, s2) \
+  ABSL_DCHECK_STRCASEEQ_IMPL((s1), #s1, (s2), #s2)
+#define ABSL_DCHECK_STRCASENE(s1, s2) \
+  ABSL_DCHECK_STRCASENE_IMPL((s1), #s1, (s2), #s2)
+
+#endif  // ABSL_LOG_ABSL_CHECK_H_
diff --git a/absl/log/absl_check_test.cc b/absl/log/absl_check_test.cc
new file mode 100644
index 0000000..8ddacdb
--- /dev/null
+++ b/absl/log/absl_check_test.cc
@@ -0,0 +1,58 @@
+//
+// Copyright 2022 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/log/absl_check.h"
+
+#define ABSL_TEST_CHECK ABSL_CHECK
+#define ABSL_TEST_CHECK_OK ABSL_CHECK_OK
+#define ABSL_TEST_CHECK_EQ ABSL_CHECK_EQ
+#define ABSL_TEST_CHECK_NE ABSL_CHECK_NE
+#define ABSL_TEST_CHECK_GE ABSL_CHECK_GE
+#define ABSL_TEST_CHECK_LE ABSL_CHECK_LE
+#define ABSL_TEST_CHECK_GT ABSL_CHECK_GT
+#define ABSL_TEST_CHECK_LT ABSL_CHECK_LT
+#define ABSL_TEST_CHECK_STREQ ABSL_CHECK_STREQ
+#define ABSL_TEST_CHECK_STRNE ABSL_CHECK_STRNE
+#define ABSL_TEST_CHECK_STRCASEEQ ABSL_CHECK_STRCASEEQ
+#define ABSL_TEST_CHECK_STRCASENE ABSL_CHECK_STRCASENE
+
+#define ABSL_TEST_DCHECK ABSL_DCHECK
+#define ABSL_TEST_DCHECK_OK ABSL_DCHECK_OK
+#define ABSL_TEST_DCHECK_EQ ABSL_DCHECK_EQ
+#define ABSL_TEST_DCHECK_NE ABSL_DCHECK_NE
+#define ABSL_TEST_DCHECK_GE ABSL_DCHECK_GE
+#define ABSL_TEST_DCHECK_LE ABSL_DCHECK_LE
+#define ABSL_TEST_DCHECK_GT ABSL_DCHECK_GT
+#define ABSL_TEST_DCHECK_LT ABSL_DCHECK_LT
+#define ABSL_TEST_DCHECK_STREQ ABSL_DCHECK_STREQ
+#define ABSL_TEST_DCHECK_STRNE ABSL_DCHECK_STRNE
+#define ABSL_TEST_DCHECK_STRCASEEQ ABSL_DCHECK_STRCASEEQ
+#define ABSL_TEST_DCHECK_STRCASENE ABSL_DCHECK_STRCASENE
+
+#define ABSL_TEST_QCHECK ABSL_QCHECK
+#define ABSL_TEST_QCHECK_OK ABSL_QCHECK_OK
+#define ABSL_TEST_QCHECK_EQ ABSL_QCHECK_EQ
+#define ABSL_TEST_QCHECK_NE ABSL_QCHECK_NE
+#define ABSL_TEST_QCHECK_GE ABSL_QCHECK_GE
+#define ABSL_TEST_QCHECK_LE ABSL_QCHECK_LE
+#define ABSL_TEST_QCHECK_GT ABSL_QCHECK_GT
+#define ABSL_TEST_QCHECK_LT ABSL_QCHECK_LT
+#define ABSL_TEST_QCHECK_STREQ ABSL_QCHECK_STREQ
+#define ABSL_TEST_QCHECK_STRNE ABSL_QCHECK_STRNE
+#define ABSL_TEST_QCHECK_STRCASEEQ ABSL_QCHECK_STRCASEEQ
+#define ABSL_TEST_QCHECK_STRCASENE ABSL_QCHECK_STRCASENE
+
+#include "gtest/gtest.h"
+#include "absl/log/check_test_impl.h"
diff --git a/absl/log/absl_log.h b/absl/log/absl_log.h
new file mode 100644
index 0000000..1c6cf26
--- /dev/null
+++ b/absl/log/absl_log.h
@@ -0,0 +1,94 @@
+// Copyright 2022 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: log/absl_log.h
+// -----------------------------------------------------------------------------
+//
+// This header declares a family of `ABSL_LOG` macros as alternative spellings
+// for macros in `log.h`.
+//
+// Basic invocation looks like this:
+//
+//   ABSL_LOG(INFO) << "Found " << num_cookies << " cookies";
+//
+// Most `ABSL_LOG` macros take a severity level argument. The severity levels
+// are `INFO`, `WARNING`, `ERROR`, and `FATAL`.
+//
+// For full documentation, see comments in `log.h`, which includes full
+// reference documentation on use of the equivalent `LOG` macro and has an
+// identical set of macros without the ABSL_* prefix.
+
+#ifndef ABSL_LOG_ABSL_LOG_H_
+#define ABSL_LOG_ABSL_LOG_H_
+
+#include "absl/log/internal/log_impl.h"
+
+#define ABSL_LOG(severity) ABSL_LOG_IMPL(_##severity)
+#define ABSL_PLOG(severity) ABSL_PLOG_IMPL(_##severity)
+#define ABSL_DLOG(severity) ABSL_DLOG_IMPL(_##severity)
+
+#define ABSL_LOG_IF(severity, condition) \
+  ABSL_LOG_IF_IMPL(_##severity, condition)
+#define ABSL_PLOG_IF(severity, condition) \
+  ABSL_PLOG_IF_IMPL(_##severity, condition)
+#define ABSL_DLOG_IF(severity, condition) \
+  ABSL_DLOG_IF_IMPL(_##severity, condition)
+
+#define ABSL_LOG_EVERY_N(severity, n) ABSL_LOG_EVERY_N_IMPL(_##severity, n)
+#define ABSL_LOG_FIRST_N(severity, n) ABSL_LOG_FIRST_N_IMPL(_##severity, n)
+#define ABSL_LOG_EVERY_POW_2(severity) ABSL_LOG_EVERY_POW_2_IMPL(_##severity)
+#define ABSL_LOG_EVERY_N_SEC(severity, n_seconds) \
+  ABSL_LOG_EVERY_N_SEC_IMPL(_##severity, n_seconds)
+
+#define ABSL_PLOG_EVERY_N(severity, n) ABSL_PLOG_EVERY_N_IMPL(_##severity, n)
+#define ABSL_PLOG_FIRST_N(severity, n) ABSL_PLOG_FIRST_N_IMPL(_##severity, n)
+#define ABSL_PLOG_EVERY_POW_2(severity) ABSL_PLOG_EVERY_POW_2_IMPL(_##severity)
+#define ABSL_PLOG_EVERY_N_SEC(severity, n_seconds) \
+  ABSL_PLOG_EVERY_N_SEC_IMPL(_##severity, n_seconds)
+
+#define ABSL_DLOG_EVERY_N(severity, n) ABSL_DLOG_EVERY_N_IMPL(_##severity, n)
+#define ABSL_DLOG_FIRST_N(severity, n) ABSL_DLOG_FIRST_N_IMPL(_##severity, n)
+#define ABSL_DLOG_EVERY_POW_2(severity) ABSL_DLOG_EVERY_POW_2_IMPL(_##severity)
+#define ABSL_DLOG_EVERY_N_SEC(severity, n_seconds) \
+  ABSL_DLOG_EVERY_N_SEC_IMPL(_##severity, n_seconds)
+
+#define ABSL_LOG_IF_EVERY_N(severity, condition, n) \
+  ABSL_LOG_IF_EVERY_N_IMPL(_##severity, condition, n)
+#define ABSL_LOG_IF_FIRST_N(severity, condition, n) \
+  ABSL_LOG_IF_FIRST_N_IMPL(_##severity, condition, n)
+#define ABSL_LOG_IF_EVERY_POW_2(severity, condition) \
+  ABSL_LOG_IF_EVERY_POW_2_IMPL(_##severity, condition)
+#define ABSL_LOG_IF_EVERY_N_SEC(severity, condition, n_seconds) \
+  ABSL_LOG_IF_EVERY_N_SEC_IMPL(_##severity, condition, n_seconds)
+
+#define ABSL_PLOG_IF_EVERY_N(severity, condition, n) \
+  ABSL_PLOG_IF_EVERY_N_IMPL(_##severity, condition, n)
+#define ABSL_PLOG_IF_FIRST_N(severity, condition, n) \
+  ABSL_PLOG_IF_FIRST_N_IMPL(_##severity, condition, n)
+#define ABSL_PLOG_IF_EVERY_POW_2(severity, condition) \
+  ABSL_PLOG_IF_EVERY_POW_2_IMPL(_##severity, condition)
+#define ABSL_PLOG_IF_EVERY_N_SEC(severity, condition, n_seconds) \
+  ABSL_PLOG_IF_EVERY_N_SEC_IMPL(_##severity, condition, n_seconds)
+
+#define ABSL_DLOG_IF_EVERY_N(severity, condition, n) \
+  ABSL_DLOG_IF_EVERY_N_IMPL(_##severity, condition, n)
+#define ABSL_DLOG_IF_FIRST_N(severity, condition, n) \
+  ABSL_DLOG_IF_FIRST_N_IMPL(_##severity, condition, n)
+#define ABSL_DLOG_IF_EVERY_POW_2(severity, condition) \
+  ABSL_DLOG_IF_EVERY_POW_2_IMPL(_##severity, condition)
+#define ABSL_DLOG_IF_EVERY_N_SEC(severity, condition, n_seconds) \
+  ABSL_DLOG_IF_EVERY_N_SEC_IMPL(_##severity, condition, n_seconds)
+
+#endif  // ABSL_LOG_ABSL_LOG_H_
diff --git a/absl/log/absl_log_basic_test.cc b/absl/log/absl_log_basic_test.cc
new file mode 100644
index 0000000..bc8a787
--- /dev/null
+++ b/absl/log/absl_log_basic_test.cc
@@ -0,0 +1,21 @@
+//
+// Copyright 2022 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/log/absl_log.h"
+
+#define ABSL_TEST_LOG ABSL_LOG
+
+#include "gtest/gtest.h"
+#include "absl/log/log_basic_test_impl.h"
diff --git a/absl/log/check.h b/absl/log/check.h
index c7303b8..33145a5 100644
--- a/absl/log/check.h
+++ b/absl/log/check.h
@@ -34,7 +34,7 @@
 #ifndef ABSL_LOG_CHECK_H_
 #define ABSL_LOG_CHECK_H_
 
-#include "absl/base/optimization.h"
+#include "absl/log/internal/check_impl.h"
 #include "absl/log/internal/check_op.h"     // IWYU pragma: export
 #include "absl/log/internal/conditions.h"   // IWYU pragma: export
 #include "absl/log/internal/log_message.h"  // IWYU pragma: export
@@ -54,10 +54,7 @@
 // Might produce a message like:
 //
 //   Check failed: !cheese.empty() Out of Cheese
-#define CHECK(condition)                                              \
-  ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS,                        \
-                                    ABSL_PREDICT_FALSE(!(condition))) \
-  ABSL_LOG_INTERNAL_CHECK(#condition).InternalStream()
+#define CHECK(condition) ABSL_CHECK_IMPL((condition), #condition)
 
 // QCHECK()
 //
@@ -65,10 +62,7 @@
 // not run registered error handlers (as `QFATAL`).  It is useful when the
 // problem is definitely unrelated to program flow, e.g. when validating user
 // input.
-#define QCHECK(condition)                                              \
-  ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS,                        \
-                                     ABSL_PREDICT_FALSE(!(condition))) \
-  ABSL_LOG_INTERNAL_QCHECK(#condition).InternalStream()
+#define QCHECK(condition) ABSL_QCHECK_IMPL((condition), #condition)
 
 // PCHECK()
 //
@@ -83,7 +77,7 @@
 // Might produce a message like:
 //
 //   Check failed: fd != -1 posix is difficult: No such file or directory [2]
-#define PCHECK(condition) CHECK(condition).WithPerror()
+#define PCHECK(condition) ABSL_PCHECK_IMPL((condition), #condition)
 
 // DCHECK()
 //
@@ -91,11 +85,7 @@
 // `DLOG`).  Unlike with `CHECK` (but as with `assert`), it is not safe to rely
 // on evaluation of `condition`: when `NDEBUG` is enabled, DCHECK does not
 // evaluate the condition.
-#ifndef NDEBUG
-#define DCHECK(condition) CHECK(condition)
-#else
-#define DCHECK(condition) CHECK(true || (condition))
-#endif
+#define DCHECK(condition) ABSL_DCHECK_IMPL((condition), #condition)
 
 // `CHECK_EQ` and friends are syntactic sugar for `CHECK(x == y)` that
 // automatically output the expression being tested and the evaluated values on
@@ -123,43 +113,24 @@
 //
 // WARNING: Passing `NULL` as an argument to `CHECK_EQ` and similar macros does
 // not compile.  Use `nullptr` instead.
-#define CHECK_EQ(val1, val2) \
-  ABSL_LOG_INTERNAL_CHECK_OP(Check_EQ, ==, val1, val2)
-#define CHECK_NE(val1, val2) \
-  ABSL_LOG_INTERNAL_CHECK_OP(Check_NE, !=, val1, val2)
-#define CHECK_LE(val1, val2) \
-  ABSL_LOG_INTERNAL_CHECK_OP(Check_LE, <=, val1, val2)
-#define CHECK_LT(val1, val2) ABSL_LOG_INTERNAL_CHECK_OP(Check_LT, <, val1, val2)
-#define CHECK_GE(val1, val2) \
-  ABSL_LOG_INTERNAL_CHECK_OP(Check_GE, >=, val1, val2)
-#define CHECK_GT(val1, val2) ABSL_LOG_INTERNAL_CHECK_OP(Check_GT, >, val1, val2)
-#define QCHECK_EQ(val1, val2) \
-  ABSL_LOG_INTERNAL_QCHECK_OP(Check_EQ, ==, val1, val2)
-#define QCHECK_NE(val1, val2) \
-  ABSL_LOG_INTERNAL_QCHECK_OP(Check_NE, !=, val1, val2)
-#define QCHECK_LE(val1, val2) \
-  ABSL_LOG_INTERNAL_QCHECK_OP(Check_LE, <=, val1, val2)
-#define QCHECK_LT(val1, val2) \
-  ABSL_LOG_INTERNAL_QCHECK_OP(Check_LT, <, val1, val2)
-#define QCHECK_GE(val1, val2) \
-  ABSL_LOG_INTERNAL_QCHECK_OP(Check_GE, >=, val1, val2)
-#define QCHECK_GT(val1, val2) \
-  ABSL_LOG_INTERNAL_QCHECK_OP(Check_GT, >, val1, val2)
-#ifndef NDEBUG
-#define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2)
-#define DCHECK_NE(val1, val2) CHECK_NE(val1, val2)
-#define DCHECK_LE(val1, val2) CHECK_LE(val1, val2)
-#define DCHECK_LT(val1, val2) CHECK_LT(val1, val2)
-#define DCHECK_GE(val1, val2) CHECK_GE(val1, val2)
-#define DCHECK_GT(val1, val2) CHECK_GT(val1, val2)
-#else  // ndef NDEBUG
-#define DCHECK_EQ(val1, val2) ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2)
-#define DCHECK_NE(val1, val2) ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2)
-#define DCHECK_LE(val1, val2) ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2)
-#define DCHECK_LT(val1, val2) ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2)
-#define DCHECK_GE(val1, val2) ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2)
-#define DCHECK_GT(val1, val2) ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2)
-#endif  // def NDEBUG
+#define CHECK_EQ(val1, val2) ABSL_CHECK_EQ_IMPL((val1), #val1, (val2), #val2)
+#define CHECK_NE(val1, val2) ABSL_CHECK_NE_IMPL((val1), #val1, (val2), #val2)
+#define CHECK_LE(val1, val2) ABSL_CHECK_LE_IMPL((val1), #val1, (val2), #val2)
+#define CHECK_LT(val1, val2) ABSL_CHECK_LT_IMPL((val1), #val1, (val2), #val2)
+#define CHECK_GE(val1, val2) ABSL_CHECK_GE_IMPL((val1), #val1, (val2), #val2)
+#define CHECK_GT(val1, val2) ABSL_CHECK_GT_IMPL((val1), #val1, (val2), #val2)
+#define QCHECK_EQ(val1, val2) ABSL_QCHECK_EQ_IMPL((val1), #val1, (val2), #val2)
+#define QCHECK_NE(val1, val2) ABSL_QCHECK_NE_IMPL((val1), #val1, (val2), #val2)
+#define QCHECK_LE(val1, val2) ABSL_QCHECK_LE_IMPL((val1), #val1, (val2), #val2)
+#define QCHECK_LT(val1, val2) ABSL_QCHECK_LT_IMPL((val1), #val1, (val2), #val2)
+#define QCHECK_GE(val1, val2) ABSL_QCHECK_GE_IMPL((val1), #val1, (val2), #val2)
+#define QCHECK_GT(val1, val2) ABSL_QCHECK_GT_IMPL((val1), #val1, (val2), #val2)
+#define DCHECK_EQ(val1, val2) ABSL_DCHECK_EQ_IMPL((val1), #val1, (val2), #val2)
+#define DCHECK_NE(val1, val2) ABSL_DCHECK_NE_IMPL((val1), #val1, (val2), #val2)
+#define DCHECK_LE(val1, val2) ABSL_DCHECK_LE_IMPL((val1), #val1, (val2), #val2)
+#define DCHECK_LT(val1, val2) ABSL_DCHECK_LT_IMPL((val1), #val1, (val2), #val2)
+#define DCHECK_GE(val1, val2) ABSL_DCHECK_GE_IMPL((val1), #val1, (val2), #val2)
+#define DCHECK_GT(val1, val2) ABSL_DCHECK_GT_IMPL((val1), #val1, (val2), #val2)
 
 // `CHECK_OK` and friends validate that the provided `absl::Status` or
 // `absl::StatusOr<T>` is OK.  If it isn't, they print a failure message that
@@ -175,13 +146,9 @@
 // Might produce a message like:
 //
 //   Check failed: FunctionReturnsStatus(x, y, z) is OK (ABORTED: timeout) oops!
-#define CHECK_OK(status) ABSL_LOG_INTERNAL_CHECK_OK(status)
-#define QCHECK_OK(status) ABSL_LOG_INTERNAL_QCHECK_OK(status)
-#ifndef NDEBUG
-#define DCHECK_OK(status) ABSL_LOG_INTERNAL_CHECK_OK(status)
-#else
-#define DCHECK_OK(status) ABSL_LOG_INTERNAL_DCHECK_NOP(status, nullptr)
-#endif
+#define CHECK_OK(status) ABSL_CHECK_OK_IMPL((status), #status)
+#define QCHECK_OK(status) ABSL_QCHECK_OK_IMPL((status), #status)
+#define DCHECK_OK(status) ABSL_DCHECK_OK_IMPL((status), #status)
 
 // `CHECK_STREQ` and friends provide `CHECK_EQ` functionality for C strings,
 // i.e., nul-terminated char arrays.  The `CASE` versions are case-insensitive.
@@ -196,32 +163,21 @@
 // Example:
 //
 //   CHECK_STREQ(Foo().c_str(), Bar().c_str());
-#define CHECK_STREQ(s1, s2) \
-  ABSL_LOG_INTERNAL_CHECK_STROP(strcmp, ==, true, s1, s2)
-#define CHECK_STRNE(s1, s2) \
-  ABSL_LOG_INTERNAL_CHECK_STROP(strcmp, !=, false, s1, s2)
-#define CHECK_STRCASEEQ(s1, s2) \
-  ABSL_LOG_INTERNAL_CHECK_STROP(strcasecmp, ==, true, s1, s2)
-#define CHECK_STRCASENE(s1, s2) \
-  ABSL_LOG_INTERNAL_CHECK_STROP(strcasecmp, !=, false, s1, s2)
-#define QCHECK_STREQ(s1, s2) \
-  ABSL_LOG_INTERNAL_QCHECK_STROP(strcmp, ==, true, s1, s2)
-#define QCHECK_STRNE(s1, s2) \
-  ABSL_LOG_INTERNAL_QCHECK_STROP(strcmp, !=, false, s1, s2)
+#define CHECK_STREQ(s1, s2) ABSL_CHECK_STREQ_IMPL((s1), #s1, (s2), #s2)
+#define CHECK_STRNE(s1, s2) ABSL_CHECK_STRNE_IMPL((s1), #s1, (s2), #s2)
+#define CHECK_STRCASEEQ(s1, s2) ABSL_CHECK_STRCASEEQ_IMPL((s1), #s1, (s2), #s2)
+#define CHECK_STRCASENE(s1, s2) ABSL_CHECK_STRCASENE_IMPL((s1), #s1, (s2), #s2)
+#define QCHECK_STREQ(s1, s2) ABSL_QCHECK_STREQ_IMPL((s1), #s1, (s2), #s2)
+#define QCHECK_STRNE(s1, s2) ABSL_QCHECK_STRNE_IMPL((s1), #s1, (s2), #s2)
 #define QCHECK_STRCASEEQ(s1, s2) \
-  ABSL_LOG_INTERNAL_QCHECK_STROP(strcasecmp, ==, true, s1, s2)
+  ABSL_QCHECK_STRCASEEQ_IMPL((s1), #s1, (s2), #s2)
 #define QCHECK_STRCASENE(s1, s2) \
-  ABSL_LOG_INTERNAL_QCHECK_STROP(strcasecmp, !=, false, s1, s2)
-#ifndef NDEBUG
-#define DCHECK_STREQ(s1, s2) CHECK_STREQ(s1, s2)
-#define DCHECK_STRCASEEQ(s1, s2) CHECK_STRCASEEQ(s1, s2)
-#define DCHECK_STRNE(s1, s2) CHECK_STRNE(s1, s2)
-#define DCHECK_STRCASENE(s1, s2) CHECK_STRCASENE(s1, s2)
-#else  // ndef NDEBUG
-#define DCHECK_STREQ(s1, s2) ABSL_LOG_INTERNAL_DCHECK_NOP(s1, s2)
-#define DCHECK_STRCASEEQ(s1, s2) ABSL_LOG_INTERNAL_DCHECK_NOP(s1, s2)
-#define DCHECK_STRNE(s1, s2) ABSL_LOG_INTERNAL_DCHECK_NOP(s1, s2)
-#define DCHECK_STRCASENE(s1, s2) ABSL_LOG_INTERNAL_DCHECK_NOP(s1, s2)
-#endif  // def NDEBUG
+  ABSL_QCHECK_STRCASENE_IMPL((s1), #s1, (s2), #s2)
+#define DCHECK_STREQ(s1, s2) ABSL_DCHECK_STREQ_IMPL((s1), #s1, (s2), #s2)
+#define DCHECK_STRNE(s1, s2) ABSL_DCHECK_STRNE_IMPL((s1), #s1, (s2), #s2)
+#define DCHECK_STRCASEEQ(s1, s2) \
+  ABSL_DCHECK_STRCASEEQ_IMPL((s1), #s1, (s2), #s2)
+#define DCHECK_STRCASENE(s1, s2) \
+  ABSL_DCHECK_STRCASENE_IMPL((s1), #s1, (s2), #s2)
 
 #endif  // ABSL_LOG_CHECK_H_
diff --git a/absl/log/check_test.cc b/absl/log/check_test.cc
index 4ce9d87..f44a686 100644
--- a/absl/log/check_test.cc
+++ b/absl/log/check_test.cc
@@ -15,419 +15,44 @@
 
 #include "absl/log/check.h"
 
-#include <ostream>
-#include <string>
+#define ABSL_TEST_CHECK CHECK
+#define ABSL_TEST_CHECK_OK CHECK_OK
+#define ABSL_TEST_CHECK_EQ CHECK_EQ
+#define ABSL_TEST_CHECK_NE CHECK_NE
+#define ABSL_TEST_CHECK_GE CHECK_GE
+#define ABSL_TEST_CHECK_LE CHECK_LE
+#define ABSL_TEST_CHECK_GT CHECK_GT
+#define ABSL_TEST_CHECK_LT CHECK_LT
+#define ABSL_TEST_CHECK_STREQ CHECK_STREQ
+#define ABSL_TEST_CHECK_STRNE CHECK_STRNE
+#define ABSL_TEST_CHECK_STRCASEEQ CHECK_STRCASEEQ
+#define ABSL_TEST_CHECK_STRCASENE CHECK_STRCASENE
 
-#include "gmock/gmock.h"
+#define ABSL_TEST_DCHECK DCHECK
+#define ABSL_TEST_DCHECK_OK DCHECK_OK
+#define ABSL_TEST_DCHECK_EQ DCHECK_EQ
+#define ABSL_TEST_DCHECK_NE DCHECK_NE
+#define ABSL_TEST_DCHECK_GE DCHECK_GE
+#define ABSL_TEST_DCHECK_LE DCHECK_LE
+#define ABSL_TEST_DCHECK_GT DCHECK_GT
+#define ABSL_TEST_DCHECK_LT DCHECK_LT
+#define ABSL_TEST_DCHECK_STREQ DCHECK_STREQ
+#define ABSL_TEST_DCHECK_STRNE DCHECK_STRNE
+#define ABSL_TEST_DCHECK_STRCASEEQ DCHECK_STRCASEEQ
+#define ABSL_TEST_DCHECK_STRCASENE DCHECK_STRCASENE
+
+#define ABSL_TEST_QCHECK QCHECK
+#define ABSL_TEST_QCHECK_OK QCHECK_OK
+#define ABSL_TEST_QCHECK_EQ QCHECK_EQ
+#define ABSL_TEST_QCHECK_NE QCHECK_NE
+#define ABSL_TEST_QCHECK_GE QCHECK_GE
+#define ABSL_TEST_QCHECK_LE QCHECK_LE
+#define ABSL_TEST_QCHECK_GT QCHECK_GT
+#define ABSL_TEST_QCHECK_LT QCHECK_LT
+#define ABSL_TEST_QCHECK_STREQ QCHECK_STREQ
+#define ABSL_TEST_QCHECK_STRNE QCHECK_STRNE
+#define ABSL_TEST_QCHECK_STRCASEEQ QCHECK_STRCASEEQ
+#define ABSL_TEST_QCHECK_STRCASENE QCHECK_STRCASENE
+
 #include "gtest/gtest.h"
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/log/internal/test_helpers.h"
-
-namespace {
-using ::testing::AllOf;
-using ::testing::HasSubstr;
-using ::testing::Not;
-
-auto* test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment(
-    new absl::log_internal::LogTestEnvironment);
-
-#if GTEST_HAS_DEATH_TEST
-
-TEST(CHECKDeathTest, TestBasicValues) {
-  CHECK(true);
-
-  EXPECT_DEATH(CHECK(false), "Check failed: false");
-
-  int i = 2;
-  CHECK(i != 3);  // NOLINT
-}
-
-#endif  // GTEST_HAS_DEATH_TEST
-
-TEST(CHECKTest, TestLogicExpressions) {
-  int i = 5;
-  CHECK(i > 0 && i < 10);
-  CHECK(i < 0 || i > 3);
-}
-
-#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
-ABSL_CONST_INIT const auto global_var_check = [](int i) {
-  CHECK(i > 0);  // NOLINT
-  return i + 1;
-}(3);
-
-ABSL_CONST_INIT const auto global_var = [](int i) {
-  CHECK_GE(i, 0);  // NOLINT
-  return i + 1;
-}(global_var_check);
-#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG
-
-TEST(CHECKTest, TestPlacementsInCompoundStatements) {
-  // check placement inside if/else clauses
-  if (true) CHECK(true);
-
-  if (false)
-    ;  // NOLINT
-  else
-    CHECK(true);
-
-  switch (0)
-  case 0:
-    CHECK(true);  // NOLINT
-
-#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
-  constexpr auto var = [](int i) {
-    CHECK(i > 0);  // NOLINT
-    return i + 1;
-  }(global_var);
-  (void)var;
-#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG
-}
-
-TEST(CHECKTest, TestBoolConvertible) {
-  struct Tester {
-  } tester;
-  CHECK([&]() { return &tester; }());
-}
-
-#if GTEST_HAS_DEATH_TEST
-
-TEST(CHECKDeathTest, TestChecksWithSideeffects) {
-  int var = 0;
-  CHECK([&var]() {
-    ++var;
-    return true;
-  }());
-  EXPECT_EQ(var, 1);
-
-  EXPECT_DEATH(CHECK([&var]() {
-                 ++var;
-                 return false;
-               }()) << var,
-               "Check failed: .* 2");
-}
-
-#endif  // GTEST_HAS_DEATH_TEST
-
-#if GTEST_HAS_DEATH_TEST
-
-TEST(CHECKDeachTest, TestOrderOfInvocationsBetweenCheckAndMessage) {
-  int counter = 0;
-
-  auto GetStr = [&counter]() -> std::string {
-    return counter++ == 0 ? "" : "non-empty";
-  };
-
-  EXPECT_DEATH(CHECK(!GetStr().empty()) << GetStr(), HasSubstr("non-empty"));
-}
-
-TEST(CHECKTest, TestSecondaryFailure) {
-  auto FailingRoutine = []() {
-    CHECK(false) << "Secondary";
-    return false;
-  };
-  EXPECT_DEATH(CHECK(FailingRoutine()) << "Primary",
-               AllOf(HasSubstr("Secondary"), Not(HasSubstr("Primary"))));
-}
-
-TEST(CHECKTest, TestSecondaryFailureInMessage) {
-  auto MessageGen = []() {
-    CHECK(false) << "Secondary";
-    return "Primary";
-  };
-  EXPECT_DEATH(CHECK(false) << MessageGen(),
-               AllOf(HasSubstr("Secondary"), Not(HasSubstr("Primary"))));
-}
-
-#endif  // GTEST_HAS_DEATH_TEST
-
-TEST(CHECKTest, TestBinaryChecksWithPrimitives) {
-  CHECK_EQ(1, 1);
-  CHECK_NE(1, 2);
-  CHECK_GE(1, 1);
-  CHECK_GE(2, 1);
-  CHECK_LE(1, 1);
-  CHECK_LE(1, 2);
-  CHECK_GT(2, 1);
-  CHECK_LT(1, 2);
-}
-
-// For testing using CHECK*() on anonymous enums.
-enum { CASE_A, CASE_B };
-
-TEST(CHECKTest, TestBinaryChecksWithEnumValues) {
-  // Tests using CHECK*() on anonymous enums.
-  CHECK_EQ(CASE_A, CASE_A);
-  CHECK_NE(CASE_A, CASE_B);
-  CHECK_GE(CASE_A, CASE_A);
-  CHECK_GE(CASE_B, CASE_A);
-  CHECK_LE(CASE_A, CASE_A);
-  CHECK_LE(CASE_A, CASE_B);
-  CHECK_GT(CASE_B, CASE_A);
-  CHECK_LT(CASE_A, CASE_B);
-}
-
-TEST(CHECKTest, TestBinaryChecksWithNullptr) {
-  const void* p_null = nullptr;
-  const void* p_not_null = &p_null;
-  CHECK_EQ(p_null, nullptr);
-  CHECK_EQ(nullptr, p_null);
-  CHECK_NE(p_not_null, nullptr);
-  CHECK_NE(nullptr, p_not_null);
-}
-
-#if GTEST_HAS_DEATH_TEST
-
-// Test logging of various char-typed values by failing CHECK*().
-TEST(CHECKDeathTest, TestComparingCharsValues) {
-  {
-    char a = ';';
-    char b = 'b';
-    EXPECT_DEATH(CHECK_EQ(a, b), "Check failed: a == b \\(';' vs. 'b'\\)");
-    b = 1;
-    EXPECT_DEATH(CHECK_EQ(a, b),
-                 "Check failed: a == b \\(';' vs. char value 1\\)");
-  }
-  {
-    signed char a = ';';
-    signed char b = 'b';
-    EXPECT_DEATH(CHECK_EQ(a, b), "Check failed: a == b \\(';' vs. 'b'\\)");
-    b = -128;
-    EXPECT_DEATH(CHECK_EQ(a, b),
-                 "Check failed: a == b \\(';' vs. signed char value -128\\)");
-  }
-  {
-    unsigned char a = ';';
-    unsigned char b = 'b';
-    EXPECT_DEATH(CHECK_EQ(a, b), "Check failed: a == b \\(';' vs. 'b'\\)");
-    b = 128;
-    EXPECT_DEATH(CHECK_EQ(a, b),
-                 "Check failed: a == b \\(';' vs. unsigned char value 128\\)");
-  }
-}
-
-TEST(CHECKDeathTest, TestNullValuesAreReportedCleanly) {
-  const char* a = nullptr;
-  const char* b = nullptr;
-  EXPECT_DEATH(CHECK_NE(a, b),
-               "Check failed: a != b \\(\\(null\\) vs. \\(null\\)\\)");
-
-  a = "xx";
-  EXPECT_DEATH(CHECK_EQ(a, b), "Check failed: a == b \\(xx vs. \\(null\\)\\)");
-  EXPECT_DEATH(CHECK_EQ(b, a), "Check failed: b == a \\(\\(null\\) vs. xx\\)");
-
-  std::nullptr_t n{};
-  EXPECT_DEATH(CHECK_NE(n, nullptr),
-               "Check failed: n != nullptr \\(\\(null\\) vs. \\(null\\)\\)");
-}
-
-#endif  // GTEST_HAS_DEATH_TEST
-
-TEST(CHECKTest, TestSTREQ) {
-  CHECK_STREQ("this", "this");
-  CHECK_STREQ(nullptr, nullptr);
-  CHECK_STRCASEEQ("this", "tHiS");
-  CHECK_STRCASEEQ(nullptr, nullptr);
-  CHECK_STRNE("this", "tHiS");
-  CHECK_STRNE("this", nullptr);
-  CHECK_STRCASENE("this", "that");
-  CHECK_STRCASENE(nullptr, "that");
-  CHECK_STREQ((std::string("a") + "b").c_str(), "ab");
-  CHECK_STREQ(std::string("test").c_str(),
-              (std::string("te") + std::string("st")).c_str());
-}
-
-TEST(CHECKTest, TestComparisonPlacementsInCompoundStatements) {
-  // check placement inside if/else clauses
-  if (true) CHECK_EQ(1, 1);
-  if (true) CHECK_STREQ("c", "c");
-
-  if (false)
-    ;  // NOLINT
-  else
-    CHECK_LE(0, 1);
-
-  if (false)
-    ;  // NOLINT
-  else
-    CHECK_STRNE("a", "b");
-
-  switch (0)
-  case 0:
-    CHECK_NE(1, 0);
-
-  switch (0)
-  case 0:
-    CHECK_STRCASEEQ("A", "a");
-
-#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
-  constexpr auto var = [](int i) {
-    CHECK_GT(i, 0);
-    return i + 1;
-  }(global_var);
-  (void)var;
-
-  // CHECK_STR... checks are not supported in constexpr routines.
-  // constexpr auto var2 = [](int i) {
-  //  CHECK_STRNE("c", "d");
-  //  return i + 1;
-  // }(global_var);
-
-#if defined(__GNUC__)
-  int var3 = (({ CHECK_LE(1, 2); }), global_var < 10) ? 1 : 0;
-  (void)var3;
-
-  int var4 = (({ CHECK_STREQ("a", "a"); }), global_var < 10) ? 1 : 0;
-  (void)var4;
-#endif  // __GNUC__
-#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG
-}
-
-TEST(CHECKTest, TestDCHECK) {
-#ifdef NDEBUG
-  DCHECK(1 == 2) << " DCHECK's shouldn't be compiled in normal mode";
-#endif
-  DCHECK(1 == 1);  // NOLINT(readability/check)
-  DCHECK_EQ(1, 1);
-  DCHECK_NE(1, 2);
-  DCHECK_GE(1, 1);
-  DCHECK_GE(2, 1);
-  DCHECK_LE(1, 1);
-  DCHECK_LE(1, 2);
-  DCHECK_GT(2, 1);
-  DCHECK_LT(1, 2);
-
-  // Test DCHECK on std::nullptr_t
-  const void* p_null = nullptr;
-  const void* p_not_null = &p_null;
-  DCHECK_EQ(p_null, nullptr);
-  DCHECK_EQ(nullptr, p_null);
-  DCHECK_NE(p_not_null, nullptr);
-  DCHECK_NE(nullptr, p_not_null);
-}
-
-TEST(CHECKTest, TestQCHECK) {
-  // The tests that QCHECK does the same as CHECK
-  QCHECK(1 == 1);  // NOLINT(readability/check)
-  QCHECK_EQ(1, 1);
-  QCHECK_NE(1, 2);
-  QCHECK_GE(1, 1);
-  QCHECK_GE(2, 1);
-  QCHECK_LE(1, 1);
-  QCHECK_LE(1, 2);
-  QCHECK_GT(2, 1);
-  QCHECK_LT(1, 2);
-
-  // Tests using QCHECK*() on anonymous enums.
-  QCHECK_EQ(CASE_A, CASE_A);
-  QCHECK_NE(CASE_A, CASE_B);
-  QCHECK_GE(CASE_A, CASE_A);
-  QCHECK_GE(CASE_B, CASE_A);
-  QCHECK_LE(CASE_A, CASE_A);
-  QCHECK_LE(CASE_A, CASE_B);
-  QCHECK_GT(CASE_B, CASE_A);
-  QCHECK_LT(CASE_A, CASE_B);
-}
-
-TEST(CHECKTest, TestQCHECKPlacementsInCompoundStatements) {
-  // check placement inside if/else clauses
-  if (true) QCHECK(true);
-
-  if (false)
-    ;  // NOLINT
-  else
-    QCHECK(true);
-
-  if (false)
-    ;  // NOLINT
-  else
-    QCHECK(true);
-
-  switch (0)
-  case 0:
-    QCHECK(true);
-
-#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
-  constexpr auto var = [](int i) {
-    QCHECK(i > 0);  // NOLINT
-    return i + 1;
-  }(global_var);
-  (void)var;
-
-#if defined(__GNUC__)
-  int var2 = (({ CHECK_LE(1, 2); }), global_var < 10) ? 1 : 0;
-  (void)var2;
-#endif  // __GNUC__
-#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG
-}
-
-class ComparableType {
- public:
-  explicit ComparableType(int v) : v_(v) {}
-
-  void MethodWithCheck(int i) {
-    CHECK_EQ(*this, i);
-    CHECK_EQ(i, *this);
-  }
-
-  int Get() const { return v_; }
-
- private:
-  friend bool operator==(const ComparableType& lhs, const ComparableType& rhs) {
-    return lhs.v_ == rhs.v_;
-  }
-  friend bool operator!=(const ComparableType& lhs, const ComparableType& rhs) {
-    return lhs.v_ != rhs.v_;
-  }
-  friend bool operator<(const ComparableType& lhs, const ComparableType& rhs) {
-    return lhs.v_ < rhs.v_;
-  }
-  friend bool operator<=(const ComparableType& lhs, const ComparableType& rhs) {
-    return lhs.v_ <= rhs.v_;
-  }
-  friend bool operator>(const ComparableType& lhs, const ComparableType& rhs) {
-    return lhs.v_ > rhs.v_;
-  }
-  friend bool operator>=(const ComparableType& lhs, const ComparableType& rhs) {
-    return lhs.v_ >= rhs.v_;
-  }
-  friend bool operator==(const ComparableType& lhs, int rhs) {
-    return lhs.v_ == rhs;
-  }
-  friend bool operator==(int lhs, const ComparableType& rhs) {
-    return lhs == rhs.v_;
-  }
-
-  friend std::ostream& operator<<(std::ostream& out, const ComparableType& v) {
-    return out << "ComparableType{" << v.Get() << "}";
-  }
-
-  int v_;
-};
-
-TEST(CHECKTest, TestUserDefinedCompOp) {
-  CHECK_EQ(ComparableType{0}, ComparableType{0});
-  CHECK_NE(ComparableType{1}, ComparableType{2});
-  CHECK_LT(ComparableType{1}, ComparableType{2});
-  CHECK_LE(ComparableType{1}, ComparableType{2});
-  CHECK_GT(ComparableType{2}, ComparableType{1});
-  CHECK_GE(ComparableType{2}, ComparableType{2});
-}
-
-TEST(CHECKTest, TestCheckInMethod) {
-  ComparableType v{1};
-  v.MethodWithCheck(1);
-}
-
-TEST(CHECKDeathTest, TestUserDefinedStreaming) {
-  ComparableType v1{1};
-  ComparableType v2{2};
-
-  EXPECT_DEATH(
-      CHECK_EQ(v1, v2),
-      HasSubstr(
-          "Check failed: v1 == v2 (ComparableType{1} vs. ComparableType{2})"));
-}
-
-}  // namespace
+#include "absl/log/check_test_impl.h"
diff --git a/absl/log/check_test_impl.h b/absl/log/check_test_impl.h
new file mode 100644
index 0000000..d5c0aee
--- /dev/null
+++ b/absl/log/check_test_impl.h
@@ -0,0 +1,528 @@
+//
+// Copyright 2022 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_LOG_CHECK_TEST_IMPL_H_
+#define ABSL_LOG_CHECK_TEST_IMPL_H_
+
+// Verify that both sets of macros behave identically by parameterizing the
+// entire test file.
+#ifndef ABSL_TEST_CHECK
+#error ABSL_TEST_CHECK must be defined for these tests to work.
+#endif
+
+#include <ostream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/log/internal/test_helpers.h"
+#include "absl/status/status.h"
+
+// NOLINTBEGIN(misc-definitions-in-headers)
+
+namespace absl_log_internal {
+
+using ::testing::AllOf;
+using ::testing::HasSubstr;
+using ::testing::Not;
+
+auto* test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment(
+    new absl::log_internal::LogTestEnvironment);
+
+#if GTEST_HAS_DEATH_TEST
+
+TEST(CHECKDeathTest, TestBasicValues) {
+  ABSL_TEST_CHECK(true);
+
+  EXPECT_DEATH(ABSL_TEST_CHECK(false), "Check failed: false");
+
+  int i = 2;
+  ABSL_TEST_CHECK(i != 3);  // NOLINT
+}
+
+#endif  // GTEST_HAS_DEATH_TEST
+
+TEST(CHECKTest, TestLogicExpressions) {
+  int i = 5;
+  ABSL_TEST_CHECK(i > 0 && i < 10);
+  ABSL_TEST_CHECK(i < 0 || i > 3);
+}
+
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+ABSL_CONST_INIT const auto global_var_check = [](int i) {
+  ABSL_TEST_CHECK(i > 0);  // NOLINT
+  return i + 1;
+}(3);
+
+ABSL_CONST_INIT const auto global_var = [](int i) {
+  ABSL_TEST_CHECK_GE(i, 0);  // NOLINT
+  return i + 1;
+}(global_var_check);
+#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG
+
+TEST(CHECKTest, TestPlacementsInCompoundStatements) {
+  // check placement inside if/else clauses
+  if (true) ABSL_TEST_CHECK(true);
+
+  if (false)
+    ;  // NOLINT
+  else
+    ABSL_TEST_CHECK(true);
+
+  switch (0)
+  case 0:
+    ABSL_TEST_CHECK(true);  // NOLINT
+
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+  constexpr auto var = [](int i) {
+    ABSL_TEST_CHECK(i > 0);  // NOLINT
+    return i + 1;
+  }(global_var);
+  (void)var;
+#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG
+}
+
+TEST(CHECKTest, TestBoolConvertible) {
+  struct Tester {
+  } tester;
+  ABSL_TEST_CHECK([&]() { return &tester; }());
+}
+
+#if GTEST_HAS_DEATH_TEST
+
+TEST(CHECKDeathTest, TestChecksWithSideEffects) {
+  int var = 0;
+  ABSL_TEST_CHECK([&var]() {
+    ++var;
+    return true;
+  }());
+  EXPECT_EQ(var, 1);
+
+  EXPECT_DEATH(ABSL_TEST_CHECK([&var]() {
+                 ++var;
+                 return false;
+               }()) << var,
+               "Check failed: .* 2");
+}
+
+#endif  // GTEST_HAS_DEATH_TEST
+
+template <int a, int b>
+constexpr int sum() {
+  return a + b;
+}
+#define MACRO_ONE 1
+#define TEMPLATE_SUM(a, b) sum<a, b>()
+#define CONCAT(a, b) a b
+#define IDENTITY(x) x
+
+TEST(CHECKTest, TestPassingMacroExpansion) {
+  ABSL_TEST_CHECK(IDENTITY(true));
+  ABSL_TEST_CHECK_EQ(TEMPLATE_SUM(MACRO_ONE, 2), 3);
+  ABSL_TEST_CHECK_STREQ(CONCAT("x", "y"), "xy");
+}
+
+#if GTEST_HAS_DEATH_TEST
+
+TEST(CHECKTest, TestMacroExpansionInMessage) {
+  auto MessageGen = []() { ABSL_TEST_CHECK(IDENTITY(false)); };
+  EXPECT_DEATH(MessageGen(), HasSubstr("IDENTITY(false)"));
+}
+
+TEST(CHECKTest, TestNestedMacroExpansionInMessage) {
+  EXPECT_DEATH(ABSL_TEST_CHECK(IDENTITY(false)), HasSubstr("IDENTITY(false)"));
+}
+
+TEST(CHECKTest, TestMacroExpansionCompare) {
+  EXPECT_DEATH(ABSL_TEST_CHECK_EQ(IDENTITY(false), IDENTITY(true)),
+               HasSubstr("IDENTITY(false) == IDENTITY(true)"));
+  EXPECT_DEATH(ABSL_TEST_CHECK_GT(IDENTITY(1), IDENTITY(2)),
+               HasSubstr("IDENTITY(1) > IDENTITY(2)"));
+}
+
+TEST(CHECKTest, TestMacroExpansionStrCompare) {
+  EXPECT_DEATH(ABSL_TEST_CHECK_STREQ(IDENTITY("x"), IDENTITY("y")),
+               HasSubstr("IDENTITY(\"x\") == IDENTITY(\"y\")"));
+  EXPECT_DEATH(ABSL_TEST_CHECK_STRCASENE(IDENTITY("a"), IDENTITY("A")),
+               HasSubstr("IDENTITY(\"a\") != IDENTITY(\"A\")"));
+}
+
+TEST(CHECKTest, TestMacroExpansionStatus) {
+  EXPECT_DEATH(
+      ABSL_TEST_CHECK_OK(IDENTITY(absl::FailedPreconditionError("message"))),
+      HasSubstr("IDENTITY(absl::FailedPreconditionError(\"message\"))"));
+}
+
+TEST(CHECKTest, TestMacroExpansionComma) {
+  EXPECT_DEATH(ABSL_TEST_CHECK(TEMPLATE_SUM(MACRO_ONE, 2) == 4),
+               HasSubstr("TEMPLATE_SUM(MACRO_ONE, 2) == 4"));
+}
+
+TEST(CHECKTest, TestMacroExpansionCommaCompare) {
+  EXPECT_DEATH(
+      ABSL_TEST_CHECK_EQ(TEMPLATE_SUM(2, MACRO_ONE), TEMPLATE_SUM(3, 2)),
+      HasSubstr("TEMPLATE_SUM(2, MACRO_ONE) == TEMPLATE_SUM(3, 2)"));
+  EXPECT_DEATH(
+      ABSL_TEST_CHECK_GT(TEMPLATE_SUM(2, MACRO_ONE), TEMPLATE_SUM(3, 2)),
+      HasSubstr("TEMPLATE_SUM(2, MACRO_ONE) > TEMPLATE_SUM(3, 2)"));
+}
+
+TEST(CHECKTest, TestMacroExpansionCommaStrCompare) {
+  EXPECT_DEATH(ABSL_TEST_CHECK_STREQ(CONCAT("x", "y"), "z"),
+               HasSubstr("CONCAT(\"x\", \"y\") == \"z\""));
+  EXPECT_DEATH(ABSL_TEST_CHECK_STRNE(CONCAT("x", "y"), "xy"),
+               HasSubstr("CONCAT(\"x\", \"y\") != \"xy\""));
+}
+
+#endif  // GTEST_HAS_DEATH_TEST
+
+#undef TEMPLATE_SUM
+#undef CONCAT
+#undef MACRO
+#undef ONE
+
+#if GTEST_HAS_DEATH_TEST
+
+TEST(CHECKDeachTest, TestOrderOfInvocationsBetweenCheckAndMessage) {
+  int counter = 0;
+
+  auto GetStr = [&counter]() -> std::string {
+    return counter++ == 0 ? "" : "non-empty";
+  };
+
+  EXPECT_DEATH(ABSL_TEST_CHECK(!GetStr().empty()) << GetStr(),
+               HasSubstr("non-empty"));
+}
+
+TEST(CHECKTest, TestSecondaryFailure) {
+  auto FailingRoutine = []() {
+    ABSL_TEST_CHECK(false) << "Secondary";
+    return false;
+  };
+  EXPECT_DEATH(ABSL_TEST_CHECK(FailingRoutine()) << "Primary",
+               AllOf(HasSubstr("Secondary"), Not(HasSubstr("Primary"))));
+}
+
+TEST(CHECKTest, TestSecondaryFailureInMessage) {
+  auto MessageGen = []() {
+    ABSL_TEST_CHECK(false) << "Secondary";
+    return "Primary";
+  };
+  EXPECT_DEATH(ABSL_TEST_CHECK(false) << MessageGen(),
+               AllOf(HasSubstr("Secondary"), Not(HasSubstr("Primary"))));
+}
+
+#endif  // GTEST_HAS_DEATH_TEST
+
+TEST(CHECKTest, TestBinaryChecksWithPrimitives) {
+  ABSL_TEST_CHECK_EQ(1, 1);
+  ABSL_TEST_CHECK_NE(1, 2);
+  ABSL_TEST_CHECK_GE(1, 1);
+  ABSL_TEST_CHECK_GE(2, 1);
+  ABSL_TEST_CHECK_LE(1, 1);
+  ABSL_TEST_CHECK_LE(1, 2);
+  ABSL_TEST_CHECK_GT(2, 1);
+  ABSL_TEST_CHECK_LT(1, 2);
+}
+
+// For testing using CHECK*() on anonymous enums.
+enum { CASE_A, CASE_B };
+
+TEST(CHECKTest, TestBinaryChecksWithEnumValues) {
+  // Tests using CHECK*() on anonymous enums.
+  ABSL_TEST_CHECK_EQ(CASE_A, CASE_A);
+  ABSL_TEST_CHECK_NE(CASE_A, CASE_B);
+  ABSL_TEST_CHECK_GE(CASE_A, CASE_A);
+  ABSL_TEST_CHECK_GE(CASE_B, CASE_A);
+  ABSL_TEST_CHECK_LE(CASE_A, CASE_A);
+  ABSL_TEST_CHECK_LE(CASE_A, CASE_B);
+  ABSL_TEST_CHECK_GT(CASE_B, CASE_A);
+  ABSL_TEST_CHECK_LT(CASE_A, CASE_B);
+}
+
+TEST(CHECKTest, TestBinaryChecksWithNullptr) {
+  const void* p_null = nullptr;
+  const void* p_not_null = &p_null;
+  ABSL_TEST_CHECK_EQ(p_null, nullptr);
+  ABSL_TEST_CHECK_EQ(nullptr, p_null);
+  ABSL_TEST_CHECK_NE(p_not_null, nullptr);
+  ABSL_TEST_CHECK_NE(nullptr, p_not_null);
+}
+
+#if GTEST_HAS_DEATH_TEST
+
+// Test logging of various char-typed values by failing CHECK*().
+TEST(CHECKDeathTest, TestComparingCharsValues) {
+  {
+    char a = ';';
+    char b = 'b';
+    EXPECT_DEATH(ABSL_TEST_CHECK_EQ(a, b),
+                 "Check failed: a == b \\(';' vs. 'b'\\)");
+    b = 1;
+    EXPECT_DEATH(ABSL_TEST_CHECK_EQ(a, b),
+                 "Check failed: a == b \\(';' vs. char value 1\\)");
+  }
+  {
+    signed char a = ';';
+    signed char b = 'b';
+    EXPECT_DEATH(ABSL_TEST_CHECK_EQ(a, b),
+                 "Check failed: a == b \\(';' vs. 'b'\\)");
+    b = -128;
+    EXPECT_DEATH(ABSL_TEST_CHECK_EQ(a, b),
+                 "Check failed: a == b \\(';' vs. signed char value -128\\)");
+  }
+  {
+    unsigned char a = ';';
+    unsigned char b = 'b';
+    EXPECT_DEATH(ABSL_TEST_CHECK_EQ(a, b),
+                 "Check failed: a == b \\(';' vs. 'b'\\)");
+    b = 128;
+    EXPECT_DEATH(ABSL_TEST_CHECK_EQ(a, b),
+                 "Check failed: a == b \\(';' vs. unsigned char value 128\\)");
+  }
+}
+
+TEST(CHECKDeathTest, TestNullValuesAreReportedCleanly) {
+  const char* a = nullptr;
+  const char* b = nullptr;
+  EXPECT_DEATH(ABSL_TEST_CHECK_NE(a, b),
+               "Check failed: a != b \\(\\(null\\) vs. \\(null\\)\\)");
+
+  a = "xx";
+  EXPECT_DEATH(ABSL_TEST_CHECK_EQ(a, b),
+               "Check failed: a == b \\(xx vs. \\(null\\)\\)");
+  EXPECT_DEATH(ABSL_TEST_CHECK_EQ(b, a),
+               "Check failed: b == a \\(\\(null\\) vs. xx\\)");
+
+  std::nullptr_t n{};
+  EXPECT_DEATH(ABSL_TEST_CHECK_NE(n, nullptr),
+               "Check failed: n != nullptr \\(\\(null\\) vs. \\(null\\)\\)");
+}
+
+#endif  // GTEST_HAS_DEATH_TEST
+
+TEST(CHECKTest, TestSTREQ) {
+  ABSL_TEST_CHECK_STREQ("this", "this");
+  ABSL_TEST_CHECK_STREQ(nullptr, nullptr);
+  ABSL_TEST_CHECK_STRCASEEQ("this", "tHiS");
+  ABSL_TEST_CHECK_STRCASEEQ(nullptr, nullptr);
+  ABSL_TEST_CHECK_STRNE("this", "tHiS");
+  ABSL_TEST_CHECK_STRNE("this", nullptr);
+  ABSL_TEST_CHECK_STRCASENE("this", "that");
+  ABSL_TEST_CHECK_STRCASENE(nullptr, "that");
+  ABSL_TEST_CHECK_STREQ((std::string("a") + "b").c_str(), "ab");
+  ABSL_TEST_CHECK_STREQ(std::string("test").c_str(),
+                        (std::string("te") + std::string("st")).c_str());
+}
+
+TEST(CHECKTest, TestComparisonPlacementsInCompoundStatements) {
+  // check placement inside if/else clauses
+  if (true) ABSL_TEST_CHECK_EQ(1, 1);
+  if (true) ABSL_TEST_CHECK_STREQ("c", "c");
+
+  if (false)
+    ;  // NOLINT
+  else
+    ABSL_TEST_CHECK_LE(0, 1);
+
+  if (false)
+    ;  // NOLINT
+  else
+    ABSL_TEST_CHECK_STRNE("a", "b");
+
+  switch (0)
+  case 0:
+    ABSL_TEST_CHECK_NE(1, 0);
+
+  switch (0)
+  case 0:
+    ABSL_TEST_CHECK_STRCASEEQ("A", "a");
+
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+  constexpr auto var = [](int i) {
+    ABSL_TEST_CHECK_GT(i, 0);
+    return i + 1;
+  }(global_var);
+  (void)var;
+
+  // CHECK_STR... checks are not supported in constexpr routines.
+  // constexpr auto var2 = [](int i) {
+  //  ABSL_TEST_CHECK_STRNE("c", "d");
+  //  return i + 1;
+  // }(global_var);
+
+#if defined(__GNUC__)
+  int var3 = (({ ABSL_TEST_CHECK_LE(1, 2); }), global_var < 10) ? 1 : 0;
+  (void)var3;
+
+  int var4 = (({ ABSL_TEST_CHECK_STREQ("a", "a"); }), global_var < 10) ? 1 : 0;
+  (void)var4;
+#endif  // __GNUC__
+#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG
+}
+
+TEST(CHECKTest, TestDCHECK) {
+#ifdef NDEBUG
+  ABSL_TEST_DCHECK(1 == 2) << " DCHECK's shouldn't be compiled in normal mode";
+#endif
+  ABSL_TEST_DCHECK(1 == 1);  // NOLINT(readability/check)
+  ABSL_TEST_DCHECK_EQ(1, 1);
+  ABSL_TEST_DCHECK_NE(1, 2);
+  ABSL_TEST_DCHECK_GE(1, 1);
+  ABSL_TEST_DCHECK_GE(2, 1);
+  ABSL_TEST_DCHECK_LE(1, 1);
+  ABSL_TEST_DCHECK_LE(1, 2);
+  ABSL_TEST_DCHECK_GT(2, 1);
+  ABSL_TEST_DCHECK_LT(1, 2);
+
+  // Test DCHECK on std::nullptr_t
+  const void* p_null = nullptr;
+  const void* p_not_null = &p_null;
+  ABSL_TEST_DCHECK_EQ(p_null, nullptr);
+  ABSL_TEST_DCHECK_EQ(nullptr, p_null);
+  ABSL_TEST_DCHECK_NE(p_not_null, nullptr);
+  ABSL_TEST_DCHECK_NE(nullptr, p_not_null);
+}
+
+TEST(CHECKTest, TestQCHECK) {
+  // The tests that QCHECK does the same as CHECK
+  ABSL_TEST_QCHECK(1 == 1);  // NOLINT(readability/check)
+  ABSL_TEST_QCHECK_EQ(1, 1);
+  ABSL_TEST_QCHECK_NE(1, 2);
+  ABSL_TEST_QCHECK_GE(1, 1);
+  ABSL_TEST_QCHECK_GE(2, 1);
+  ABSL_TEST_QCHECK_LE(1, 1);
+  ABSL_TEST_QCHECK_LE(1, 2);
+  ABSL_TEST_QCHECK_GT(2, 1);
+  ABSL_TEST_QCHECK_LT(1, 2);
+
+  // Tests using QCHECK*() on anonymous enums.
+  ABSL_TEST_QCHECK_EQ(CASE_A, CASE_A);
+  ABSL_TEST_QCHECK_NE(CASE_A, CASE_B);
+  ABSL_TEST_QCHECK_GE(CASE_A, CASE_A);
+  ABSL_TEST_QCHECK_GE(CASE_B, CASE_A);
+  ABSL_TEST_QCHECK_LE(CASE_A, CASE_A);
+  ABSL_TEST_QCHECK_LE(CASE_A, CASE_B);
+  ABSL_TEST_QCHECK_GT(CASE_B, CASE_A);
+  ABSL_TEST_QCHECK_LT(CASE_A, CASE_B);
+}
+
+TEST(CHECKTest, TestQCHECKPlacementsInCompoundStatements) {
+  // check placement inside if/else clauses
+  if (true) ABSL_TEST_QCHECK(true);
+
+  if (false)
+    ;  // NOLINT
+  else
+    ABSL_TEST_QCHECK(true);
+
+  if (false)
+    ;  // NOLINT
+  else
+    ABSL_TEST_QCHECK(true);
+
+  switch (0)
+  case 0:
+    ABSL_TEST_QCHECK(true);
+
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+  constexpr auto var = [](int i) {
+    ABSL_TEST_QCHECK(i > 0);  // NOLINT
+    return i + 1;
+  }(global_var);
+  (void)var;
+
+#if defined(__GNUC__)
+  int var2 = (({ ABSL_TEST_CHECK_LE(1, 2); }), global_var < 10) ? 1 : 0;
+  (void)var2;
+#endif  // __GNUC__
+#endif  // ABSL_INTERNAL_CPLUSPLUS_LANG
+}
+
+class ComparableType {
+ public:
+  explicit ComparableType(int v) : v_(v) {}
+
+  void MethodWithCheck(int i) {
+    ABSL_TEST_CHECK_EQ(*this, i);
+    ABSL_TEST_CHECK_EQ(i, *this);
+  }
+
+  int Get() const { return v_; }
+
+ private:
+  friend bool operator==(const ComparableType& lhs, const ComparableType& rhs) {
+    return lhs.v_ == rhs.v_;
+  }
+  friend bool operator!=(const ComparableType& lhs, const ComparableType& rhs) {
+    return lhs.v_ != rhs.v_;
+  }
+  friend bool operator<(const ComparableType& lhs, const ComparableType& rhs) {
+    return lhs.v_ < rhs.v_;
+  }
+  friend bool operator<=(const ComparableType& lhs, const ComparableType& rhs) {
+    return lhs.v_ <= rhs.v_;
+  }
+  friend bool operator>(const ComparableType& lhs, const ComparableType& rhs) {
+    return lhs.v_ > rhs.v_;
+  }
+  friend bool operator>=(const ComparableType& lhs, const ComparableType& rhs) {
+    return lhs.v_ >= rhs.v_;
+  }
+  friend bool operator==(const ComparableType& lhs, int rhs) {
+    return lhs.v_ == rhs;
+  }
+  friend bool operator==(int lhs, const ComparableType& rhs) {
+    return lhs == rhs.v_;
+  }
+
+  friend std::ostream& operator<<(std::ostream& out, const ComparableType& v) {
+    return out << "ComparableType{" << v.Get() << "}";
+  }
+
+  int v_;
+};
+
+TEST(CHECKTest, TestUserDefinedCompOp) {
+  ABSL_TEST_CHECK_EQ(ComparableType{0}, ComparableType{0});
+  ABSL_TEST_CHECK_NE(ComparableType{1}, ComparableType{2});
+  ABSL_TEST_CHECK_LT(ComparableType{1}, ComparableType{2});
+  ABSL_TEST_CHECK_LE(ComparableType{1}, ComparableType{2});
+  ABSL_TEST_CHECK_GT(ComparableType{2}, ComparableType{1});
+  ABSL_TEST_CHECK_GE(ComparableType{2}, ComparableType{2});
+}
+
+TEST(CHECKTest, TestCheckInMethod) {
+  ComparableType v{1};
+  v.MethodWithCheck(1);
+}
+
+TEST(CHECKDeathTest, TestUserDefinedStreaming) {
+  ComparableType v1{1};
+  ComparableType v2{2};
+
+  EXPECT_DEATH(
+      ABSL_TEST_CHECK_EQ(v1, v2),
+      HasSubstr(
+          "Check failed: v1 == v2 (ComparableType{1} vs. ComparableType{2})"));
+}
+
+}  // namespace absl_log_internal
+
+// NOLINTEND(misc-definitions-in-headers)
+
+#endif  // ABSL_LOG_CHECK_TEST_IMPL_H_
diff --git a/absl/log/flags.cc b/absl/log/flags.cc
index b530888..215b7bd 100644
--- a/absl/log/flags.cc
+++ b/absl/log/flags.cc
@@ -90,19 +90,27 @@
     .OnUpdate([] {
       const std::string log_backtrace_at =
           absl::GetFlag(FLAGS_log_backtrace_at);
-      if (log_backtrace_at.empty()) return;
+      if (log_backtrace_at.empty()) {
+        absl::ClearLogBacktraceLocation();
+        return;
+      }
 
       const size_t last_colon = log_backtrace_at.rfind(':');
-      if (last_colon == log_backtrace_at.npos) return;
+      if (last_colon == log_backtrace_at.npos) {
+        absl::ClearLogBacktraceLocation();
+        return;
+      }
 
       const absl::string_view file =
           absl::string_view(log_backtrace_at).substr(0, last_colon);
       int line;
-      if (absl::SimpleAtoi(
+      if (!absl::SimpleAtoi(
               absl::string_view(log_backtrace_at).substr(last_colon + 1),
               &line)) {
-        absl::SetLogBacktraceLocation(file, line);
+        absl::ClearLogBacktraceLocation();
+        return;
       }
+      absl::SetLogBacktraceLocation(file, line);
     });
 
 ABSL_FLAG(bool, log_prefix, true,
diff --git a/absl/log/flags_test.cc b/absl/log/flags_test.cc
index 7a80315..1080ea1 100644
--- a/absl/log/flags_test.cc
+++ b/absl/log/flags_test.cc
@@ -48,7 +48,10 @@
   absl::FlagSaver flag_saver_;
 };
 
-TEST_F(LogFlagsTest, StderrKnobsDefault) {
+// This test is disabled because it adds order dependency to the test suite.
+// This order dependency is currently not fixable due to the way the
+// stderrthreshold global value is out of sync with the stderrthreshold flag.
+TEST_F(LogFlagsTest, DISABLED_StderrKnobsDefault) {
   EXPECT_EQ(absl::StderrThreshold(), DefaultStderrThreshold());
 }
 
@@ -89,23 +92,23 @@
 
 TEST_F(LogFlagsTest, EmptyBacktraceAtFlag) {
   absl::SetMinLogLevel(absl::LogSeverityAtLeast::kInfo);
-  absl::SetFlag(&FLAGS_log_backtrace_at, "");
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
   EXPECT_CALL(test_sink, Send(TextMessage(Not(HasSubstr("(stacktrace:")))));
 
   test_sink.StartCapturingLogs();
+  absl::SetFlag(&FLAGS_log_backtrace_at, "");
   LOG(INFO) << "hello world";
 }
 
 TEST_F(LogFlagsTest, BacktraceAtNonsense) {
   absl::SetMinLogLevel(absl::LogSeverityAtLeast::kInfo);
-  absl::SetFlag(&FLAGS_log_backtrace_at, "gibberish");
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
   EXPECT_CALL(test_sink, Send(TextMessage(Not(HasSubstr("(stacktrace:")))));
 
   test_sink.StartCapturingLogs();
+  absl::SetFlag(&FLAGS_log_backtrace_at, "gibberish");
   LOG(INFO) << "hello world";
 }
 
@@ -113,13 +116,13 @@
   absl::SetMinLogLevel(absl::LogSeverityAtLeast::kInfo);
   const int log_line = __LINE__ + 1;
   auto do_log = [] { LOG(INFO) << "hello world"; };
-  absl::SetFlag(&FLAGS_log_backtrace_at,
-                absl::StrCat("some_other_file.cc:", log_line));
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
   EXPECT_CALL(test_sink, Send(TextMessage(Not(HasSubstr("(stacktrace:")))));
 
   test_sink.StartCapturingLogs();
+  absl::SetFlag(&FLAGS_log_backtrace_at,
+                absl::StrCat("some_other_file.cc:", log_line));
   do_log();
 }
 
@@ -127,13 +130,13 @@
   absl::SetMinLogLevel(absl::LogSeverityAtLeast::kInfo);
   const int log_line = __LINE__ + 1;
   auto do_log = [] { LOG(INFO) << "hello world"; };
-  absl::SetFlag(&FLAGS_log_backtrace_at,
-                absl::StrCat("flags_test.cc:", log_line + 1));
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
   EXPECT_CALL(test_sink, Send(TextMessage(Not(HasSubstr("(stacktrace:")))));
 
   test_sink.StartCapturingLogs();
+  absl::SetFlag(&FLAGS_log_backtrace_at,
+                absl::StrCat("flags_test.cc:", log_line + 1));
   do_log();
 }
 
@@ -141,12 +144,12 @@
   absl::SetMinLogLevel(absl::LogSeverityAtLeast::kInfo);
   const int log_line = __LINE__ + 1;
   auto do_log = [] { LOG(INFO) << "hello world"; };
-  absl::SetFlag(&FLAGS_log_backtrace_at, absl::StrCat(__FILE__, ":", log_line));
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
   EXPECT_CALL(test_sink, Send(TextMessage(Not(HasSubstr("(stacktrace:")))));
 
   test_sink.StartCapturingLogs();
+  absl::SetFlag(&FLAGS_log_backtrace_at, absl::StrCat(__FILE__, ":", log_line));
   do_log();
 }
 
@@ -154,13 +157,13 @@
   absl::SetMinLogLevel(absl::LogSeverityAtLeast::kInfo);
   const int log_line = __LINE__ + 1;
   auto do_log = [] { LOG(INFO) << "hello world"; };
-  absl::SetFlag(&FLAGS_log_backtrace_at,
-                absl::StrCat("flags_test.cc:", log_line, "gibberish"));
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
   EXPECT_CALL(test_sink, Send(TextMessage(Not(HasSubstr("(stacktrace:")))));
 
   test_sink.StartCapturingLogs();
+  absl::SetFlag(&FLAGS_log_backtrace_at,
+                absl::StrCat("flags_test.cc:", log_line, "gibberish"));
   do_log();
 }
 
@@ -168,13 +171,17 @@
   absl::SetMinLogLevel(absl::LogSeverityAtLeast::kInfo);
   const int log_line = __LINE__ + 1;
   auto do_log = [] { LOG(INFO) << "hello world"; };
-  absl::SetFlag(&FLAGS_log_backtrace_at,
-                absl::StrCat("flags_test.cc:", log_line));
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
+  testing::InSequence seq;
   EXPECT_CALL(test_sink, Send(TextMessage(HasSubstr("(stacktrace:"))));
+  EXPECT_CALL(test_sink, Send(TextMessage(Not(HasSubstr("(stacktrace:")))));
 
   test_sink.StartCapturingLogs();
+  absl::SetFlag(&FLAGS_log_backtrace_at,
+                absl::StrCat("flags_test.cc:", log_line));
+  do_log();
+  absl::SetFlag(&FLAGS_log_backtrace_at, "");
   do_log();
 }
 
diff --git a/absl/log/globals.cc b/absl/log/globals.cc
index 6dfe81f..cc85438 100644
--- a/absl/log/globals.cc
+++ b/absl/log/globals.cc
@@ -14,14 +14,17 @@
 
 #include "absl/log/globals.h"
 
-#include <stddef.h>
-#include <stdint.h>
-
 #include <atomic>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <string>
 
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/internal/atomic_hook.h"
+#include "absl/base/internal/raw_logging.h"
 #include "absl/base/log_severity.h"
 #include "absl/hash/hash.h"
 #include "absl/strings/string_view.h"
@@ -43,6 +46,9 @@
 ABSL_CONST_INIT std::atomic<size_t> log_backtrace_at_hash{0};
 ABSL_CONST_INIT std::atomic<bool> prepend_log_prefix{true};
 
+constexpr char kDefaultAndroidTag[] = "native";
+ABSL_CONST_INIT std::atomic<const char*> android_log_tag{kDefaultAndroidTag};
+
 ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
 absl::base_internal::AtomicHook<log_internal::LoggingGlobalsListener>
     logging_globals_listener;
@@ -121,9 +127,29 @@
 
 namespace log_internal {
 
+const char* GetAndroidNativeTag() {
+  return android_log_tag.load(std::memory_order_acquire);
+}
+
+}  // namespace log_internal
+
+void SetAndroidNativeTag(const char* tag) {
+  ABSL_CONST_INIT static std::atomic<const std::string*> user_log_tag(nullptr);
+  ABSL_INTERNAL_CHECK(tag, "tag must be non-null.");
+
+  const std::string* tag_str = new std::string(tag);
+  ABSL_INTERNAL_CHECK(
+      android_log_tag.exchange(tag_str->c_str(), std::memory_order_acq_rel) ==
+          kDefaultAndroidTag,
+      "SetAndroidNativeTag() must only be called once per process!");
+  user_log_tag.store(tag_str, std::memory_order_relaxed);
+}
+
+namespace log_internal {
+
 bool ShouldLogBacktraceAt(absl::string_view file, int line) {
   const size_t flag_hash =
-      log_backtrace_at_hash.load(std::memory_order_acquire);
+      log_backtrace_at_hash.load(std::memory_order_relaxed);
 
   return flag_hash != 0 && flag_hash == HashSiteForLogBacktraceAt(file, line);
 }
@@ -132,7 +158,11 @@
 
 void SetLogBacktraceLocation(absl::string_view file, int line) {
   log_backtrace_at_hash.store(HashSiteForLogBacktraceAt(file, line),
-                              std::memory_order_release);
+                              std::memory_order_relaxed);
+}
+
+void ClearLogBacktraceLocation() {
+  log_backtrace_at_hash.store(0, std::memory_order_relaxed);
 }
 
 bool ShouldPrependLogPrefix() {
diff --git a/absl/log/globals.h b/absl/log/globals.h
index 32b87db..bc3864c 100644
--- a/absl/log/globals.h
+++ b/absl/log/globals.h
@@ -110,8 +110,8 @@
 // Log Backtrace At
 //------------------------------------------------------------------------------
 //
-// Users can request backtrace to be logged at specific locations, specified
-// by file and line number.
+// Users can request an existing `LOG` statement, specified by file and line
+// number, to also include a backtrace when logged.
 
 // ShouldLogBacktraceAt()
 //
@@ -123,9 +123,16 @@
 
 // SetLogBacktraceLocation()
 //
-// Sets the location the backtrace should be logged at.
+// Sets the location the backtrace should be logged at.  If the specified
+// location isn't a `LOG` statement, the effect will be the same as
+// `ClearLogBacktraceLocation` (but less efficient).
 void SetLogBacktraceLocation(absl::string_view file, int line);
 
+// ClearLogBacktraceLocation()
+//
+// Clears the set location so that backtraces will no longer be logged at it.
+void ClearLogBacktraceLocation();
+
 //------------------------------------------------------------------------------
 // Prepend Log Prefix
 //------------------------------------------------------------------------------
@@ -145,6 +152,29 @@
 // This function is async-signal-safe.
 void EnableLogPrefix(bool on_off);
 
+//------------------------------------------------------------------------------
+// Configure Android Native Log Tag
+//------------------------------------------------------------------------------
+//
+// The logging library forwards to the Android system log API when built for
+// Android.  That API takes a string "tag" value in addition to a message and
+// severity level.  The tag is used to identify the source of messages and to
+// filter them.  This library uses the tag "native" by default.
+
+// SetAndroidNativeTag()
+//
+// Stores a copy of the string pointed to by `tag` and uses it as the Android
+// logging tag thereafter. `tag` must not be null.
+// This function must not be called more than once!
+void SetAndroidNativeTag(const char* tag);
+
+namespace log_internal {
+// GetAndroidNativeTag()
+//
+// Returns the configured Android logging tag.
+const char* GetAndroidNativeTag();
+}  // namespace log_internal
+
 namespace log_internal {
 
 using LoggingGlobalsListener = void (*)();
diff --git a/absl/log/globals_test.cc b/absl/log/globals_test.cc
index 6710c5a..f7af47c 100644
--- a/absl/log/globals_test.cc
+++ b/absl/log/globals_test.cc
@@ -15,8 +15,6 @@
 
 #include "absl/log/globals.h"
 
-#include <string>
-
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/base/attributes.h"
@@ -27,6 +25,8 @@
 #include "absl/log/scoped_mock_log.h"
 
 namespace {
+using ::testing::_;
+using ::testing::StrEq;
 
 auto* test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment(
     new absl::log_internal::LogTestEnvironment);
@@ -88,4 +88,17 @@
   EXPECT_TRUE(absl::ShouldPrependLogPrefix());
 }
 
+TEST(TestGlobals, AndroidLogTag) {
+  // Verify invalid tags result in a check failure.
+  EXPECT_DEATH_IF_SUPPORTED(absl::SetAndroidNativeTag(nullptr), ".*");
+
+  // Verify valid tags applied.
+  EXPECT_THAT(absl::log_internal::GetAndroidNativeTag(), StrEq("native"));
+  absl::SetAndroidNativeTag("test_tag");
+  EXPECT_THAT(absl::log_internal::GetAndroidNativeTag(), StrEq("test_tag"));
+
+  // Verify that additional calls (more than 1) result in a check failure.
+  EXPECT_DEATH_IF_SUPPORTED(absl::SetAndroidNativeTag("test_tag_fail"), ".*");
+}
+
 }  // namespace
diff --git a/absl/log/internal/BUILD.bazel b/absl/log/internal/BUILD.bazel
index 19243a5..555c5e5 100644
--- a/absl/log/internal/BUILD.bazel
+++ b/absl/log/internal/BUILD.bazel
@@ -28,6 +28,20 @@
 licenses(["notice"])
 
 cc_library(
+    name = "check_impl",
+    hdrs = ["check_impl.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":check_op",
+        ":conditions",
+        ":log_message",
+        ":strip",
+        "//absl/base:core_headers",
+    ],
+)
+
+cc_library(
     name = "check_op",
     srcs = ["check_op.cc"],
     hdrs = ["check_op.h"],
@@ -91,6 +105,7 @@
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
+        ":append_truncated",
         ":config",
         ":globals",
         "//absl/base:config",
@@ -123,6 +138,18 @@
 )
 
 cc_library(
+    name = "log_impl",
+    hdrs = ["log_impl.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":conditions",
+        ":log_message",
+        ":strip",
+    ],
+)
+
+cc_library(
     name = "log_message",
     srcs = ["log_message.cc"],
     hdrs = ["log_message.h"],
@@ -132,11 +159,12 @@
         "//absl/log:__pkg__",
     ],
     deps = [
-        ":config",
+        ":append_truncated",
         ":format",
         ":globals",
         ":log_sink_set",
         ":nullguard",
+        ":proto",
         "//absl/base",
         "//absl/base:config",
         "//absl/base:core_headers",
@@ -152,13 +180,24 @@
         "//absl/log:log_sink_registry",
         "//absl/memory",
         "//absl/strings",
-        "//absl/strings:str_format",
         "//absl/time",
         "//absl/types:span",
     ],
 )
 
 cc_library(
+    name = "append_truncated",
+    hdrs = ["append_truncated.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        "//absl/base:config",
+        "//absl/strings",
+        "//absl/types:span",
+    ],
+)
+
+cc_library(
     name = "log_sink_set",
     srcs = ["log_sink_set.cc"],
     hdrs = ["log_sink_set.h"],
@@ -187,11 +226,13 @@
 
 cc_library(
     name = "nullguard",
+    srcs = ["nullguard.cc"],
     hdrs = ["nullguard.h"],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         "//absl/base:config",
+        "//absl/base:core_headers",
     ],
 )
 
@@ -221,6 +262,18 @@
 )
 
 cc_library(
+    name = "structured",
+    hdrs = ["structured.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":log_message",
+        "//absl/base:config",
+        "//absl/strings",
+    ],
+)
+
+cc_library(
     name = "test_actions",
     testonly = True,
     srcs = ["test_actions.cc"],
@@ -228,13 +281,17 @@
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
-        ":config",
         "//absl/base:config",
+        "//absl/base:core_headers",
         "//absl/base:log_severity",
         "//absl/log:log_entry",
         "//absl/strings",
         "//absl/time",
-    ],
+    ] + select({
+        "//absl:msvc_compiler": [],
+        "//conditions:default": [
+        ],
+    }),
 )
 
 cc_library(
@@ -262,15 +319,19 @@
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
-        ":config",
         ":test_helpers",
         "//absl/base:config",
+        "//absl/base:core_headers",
         "//absl/base:log_severity",
         "//absl/log:log_entry",
         "//absl/strings",
         "//absl/time",
         "@com_google_googletest//:gtest",
-    ],
+    ] + select({
+        "//absl:msvc_compiler": [],
+        "//conditions:default": [
+        ],
+    }),
 )
 
 cc_library(
@@ -281,6 +342,21 @@
     deps = ["//absl/base:config"],
 )
 
+cc_library(
+    name = "proto",
+    srcs = ["proto.cc"],
+    hdrs = ["proto.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        "//absl/base",
+        "//absl/base:config",
+        "//absl/base:core_headers",
+        "//absl/strings",
+        "//absl/types:span",
+    ],
+)
+
 # Test targets
 cc_test(
     name = "stderr_log_sink_test",
diff --git a/absl/log/internal/append_truncated.h b/absl/log/internal/append_truncated.h
new file mode 100644
index 0000000..f0e7912
--- /dev/null
+++ b/absl/log/internal/append_truncated.h
@@ -0,0 +1,47 @@
+// Copyright 2022 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_LOG_INTERNAL_APPEND_TRUNCATED_H_
+#define ABSL_LOG_INTERNAL_APPEND_TRUNCATED_H_
+
+#include <cstddef>
+#include <cstring>
+
+#include "absl/base/config.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+// Copies into `dst` as many bytes of `src` as will fit, then truncates the
+// copied bytes from the front of `dst` and returns the number of bytes written.
+inline size_t AppendTruncated(absl::string_view src, absl::Span<char> &dst) {
+  if (src.size() > dst.size()) src = src.substr(0, dst.size());
+  memcpy(dst.data(), src.data(), src.size());
+  dst.remove_prefix(src.size());
+  return src.size();
+}
+// Likewise, but `n` copies of `c`.
+inline size_t AppendTruncated(char c, size_t n, absl::Span<char> &dst) {
+  if (n > dst.size()) n = dst.size();
+  memset(dst.data(), c, n);
+  dst.remove_prefix(n);
+  return n;
+}
+}  // namespace log_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_LOG_INTERNAL_APPEND_TRUNCATED_H_
diff --git a/absl/log/internal/check_impl.h b/absl/log/internal/check_impl.h
new file mode 100644
index 0000000..c9c28e3
--- /dev/null
+++ b/absl/log/internal/check_impl.h
@@ -0,0 +1,150 @@
+// Copyright 2022 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_LOG_INTERNAL_CHECK_IMPL_H_
+#define ABSL_LOG_INTERNAL_CHECK_IMPL_H_
+
+#include "absl/base/optimization.h"
+#include "absl/log/internal/check_op.h"
+#include "absl/log/internal/conditions.h"
+#include "absl/log/internal/log_message.h"
+#include "absl/log/internal/strip.h"
+
+// CHECK
+#define ABSL_CHECK_IMPL(condition, condition_text)                    \
+  ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS,                        \
+                                    ABSL_PREDICT_FALSE(!(condition))) \
+  ABSL_LOG_INTERNAL_CHECK(condition_text).InternalStream()
+
+#define ABSL_QCHECK_IMPL(condition, condition_text)                    \
+  ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS,                        \
+                                     ABSL_PREDICT_FALSE(!(condition))) \
+  ABSL_LOG_INTERNAL_QCHECK(condition_text).InternalStream()
+
+#define ABSL_PCHECK_IMPL(condition, condition_text) \
+  ABSL_CHECK_IMPL(condition, condition_text).WithPerror()
+
+#ifndef NDEBUG
+#define ABSL_DCHECK_IMPL(condition, condition_text) \
+  ABSL_CHECK_IMPL(condition, condition_text)
+#else
+#define ABSL_DCHECK_IMPL(condition, condition_text) \
+  ABSL_CHECK_IMPL(true || (condition), "true")
+#endif
+
+// CHECK_EQ
+#define ABSL_CHECK_EQ_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_CHECK_OP(Check_EQ, ==, val1, val1_text, val2, val2_text)
+#define ABSL_CHECK_NE_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_CHECK_OP(Check_NE, !=, val1, val1_text, val2, val2_text)
+#define ABSL_CHECK_LE_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_CHECK_OP(Check_LE, <=, val1, val1_text, val2, val2_text)
+#define ABSL_CHECK_LT_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_CHECK_OP(Check_LT, <, val1, val1_text, val2, val2_text)
+#define ABSL_CHECK_GE_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_CHECK_OP(Check_GE, >=, val1, val1_text, val2, val2_text)
+#define ABSL_CHECK_GT_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_CHECK_OP(Check_GT, >, val1, val1_text, val2, val2_text)
+#define ABSL_QCHECK_EQ_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_QCHECK_OP(Check_EQ, ==, val1, val1_text, val2, val2_text)
+#define ABSL_QCHECK_NE_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_QCHECK_OP(Check_NE, !=, val1, val1_text, val2, val2_text)
+#define ABSL_QCHECK_LE_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_QCHECK_OP(Check_LE, <=, val1, val1_text, val2, val2_text)
+#define ABSL_QCHECK_LT_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_QCHECK_OP(Check_LT, <, val1, val1_text, val2, val2_text)
+#define ABSL_QCHECK_GE_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_QCHECK_OP(Check_GE, >=, val1, val1_text, val2, val2_text)
+#define ABSL_QCHECK_GT_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_QCHECK_OP(Check_GT, >, val1, val1_text, val2, val2_text)
+#ifndef NDEBUG
+#define ABSL_DCHECK_EQ_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_CHECK_EQ_IMPL(val1, val1_text, val2, val2_text)
+#define ABSL_DCHECK_NE_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_CHECK_NE_IMPL(val1, val1_text, val2, val2_text)
+#define ABSL_DCHECK_LE_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_CHECK_LE_IMPL(val1, val1_text, val2, val2_text)
+#define ABSL_DCHECK_LT_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_CHECK_LT_IMPL(val1, val1_text, val2, val2_text)
+#define ABSL_DCHECK_GE_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_CHECK_GE_IMPL(val1, val1_text, val2, val2_text)
+#define ABSL_DCHECK_GT_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_CHECK_GT_IMPL(val1, val1_text, val2, val2_text)
+#else  // ndef NDEBUG
+#define ABSL_DCHECK_EQ_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2)
+#define ABSL_DCHECK_NE_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2)
+#define ABSL_DCHECK_LE_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2)
+#define ABSL_DCHECK_LT_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2)
+#define ABSL_DCHECK_GE_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2)
+#define ABSL_DCHECK_GT_IMPL(val1, val1_text, val2, val2_text) \
+  ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2)
+#endif  // def NDEBUG
+
+// CHECK_OK
+#define ABSL_CHECK_OK_IMPL(status, status_text) \
+  ABSL_LOG_INTERNAL_CHECK_OK(status, status_text)
+#define ABSL_QCHECK_OK_IMPL(status, status_text) \
+  ABSL_LOG_INTERNAL_QCHECK_OK(status, status_text)
+#ifndef NDEBUG
+#define ABSL_DCHECK_OK_IMPL(status, status_text) \
+  ABSL_LOG_INTERNAL_CHECK_OK(status, status_text)
+#else
+#define ABSL_DCHECK_OK_IMPL(status, status_text) \
+  ABSL_LOG_INTERNAL_DCHECK_NOP(status, nullptr)
+#endif
+
+// CHECK_STREQ
+#define ABSL_CHECK_STREQ_IMPL(s1, s1_text, s2, s2_text) \
+  ABSL_LOG_INTERNAL_CHECK_STROP(strcmp, ==, true, s1, s1_text, s2, s2_text)
+#define ABSL_CHECK_STRNE_IMPL(s1, s1_text, s2, s2_text) \
+  ABSL_LOG_INTERNAL_CHECK_STROP(strcmp, !=, false, s1, s1_text, s2, s2_text)
+#define ABSL_CHECK_STRCASEEQ_IMPL(s1, s1_text, s2, s2_text) \
+  ABSL_LOG_INTERNAL_CHECK_STROP(strcasecmp, ==, true, s1, s1_text, s2, s2_text)
+#define ABSL_CHECK_STRCASENE_IMPL(s1, s1_text, s2, s2_text) \
+  ABSL_LOG_INTERNAL_CHECK_STROP(strcasecmp, !=, false, s1, s1_text, s2, s2_text)
+#define ABSL_QCHECK_STREQ_IMPL(s1, s1_text, s2, s2_text) \
+  ABSL_LOG_INTERNAL_QCHECK_STROP(strcmp, ==, true, s1, s1_text, s2, s2_text)
+#define ABSL_QCHECK_STRNE_IMPL(s1, s1_text, s2, s2_text) \
+  ABSL_LOG_INTERNAL_QCHECK_STROP(strcmp, !=, false, s1, s1_text, s2, s2_text)
+#define ABSL_QCHECK_STRCASEEQ_IMPL(s1, s1_text, s2, s2_text) \
+  ABSL_LOG_INTERNAL_QCHECK_STROP(strcasecmp, ==, true, s1, s1_text, s2, s2_text)
+#define ABSL_QCHECK_STRCASENE_IMPL(s1, s1_text, s2, s2_text)             \
+  ABSL_LOG_INTERNAL_QCHECK_STROP(strcasecmp, !=, false, s1, s1_text, s2, \
+                                 s2_text)
+#ifndef NDEBUG
+#define ABSL_DCHECK_STREQ_IMPL(s1, s1_text, s2, s2_text) \
+  ABSL_CHECK_STREQ_IMPL(s1, s1_text, s2, s2_text)
+#define ABSL_DCHECK_STRCASEEQ_IMPL(s1, s1_text, s2, s2_text) \
+  ABSL_CHECK_STRCASEEQ_IMPL(s1, s1_text, s2, s2_text)
+#define ABSL_DCHECK_STRNE_IMPL(s1, s1_text, s2, s2_text) \
+  ABSL_CHECK_STRNE_IMPL(s1, s1_text, s2, s2_text)
+#define ABSL_DCHECK_STRCASENE_IMPL(s1, s1_text, s2, s2_text) \
+  ABSL_CHECK_STRCASENE_IMPL(s1, s1_text, s2, s2_text)
+#else  // ndef NDEBUG
+#define ABSL_DCHECK_STREQ_IMPL(s1, s1_text, s2, s2_text) \
+  ABSL_LOG_INTERNAL_DCHECK_NOP(s1, s2)
+#define ABSL_DCHECK_STRCASEEQ_IMPL(s1, s1_text, s2, s2_text) \
+  ABSL_LOG_INTERNAL_DCHECK_NOP(s1, s2)
+#define ABSL_DCHECK_STRNE_IMPL(s1, s1_text, s2, s2_text) \
+  ABSL_LOG_INTERNAL_DCHECK_NOP(s1, s2)
+#define ABSL_DCHECK_STRCASENE_IMPL(s1, s1_text, s2, s2_text) \
+  ABSL_LOG_INTERNAL_DCHECK_NOP(s1, s2)
+#endif  // def NDEBUG
+
+#endif  // ABSL_LOG_INTERNAL_CHECK_IMPL_H_
diff --git a/absl/log/internal/check_op.h b/absl/log/internal/check_op.h
index 559e5af..20b01b5 100644
--- a/absl/log/internal/check_op.h
+++ b/absl/log/internal/check_op.h
@@ -57,35 +57,40 @@
   ::absl::log_internal::NullStream().InternalStream()
 #endif
 
-#define ABSL_LOG_INTERNAL_CHECK_OP(name, op, val1, val2)                       \
+#define ABSL_LOG_INTERNAL_CHECK_OP(name, op, val1, val1_text, val2, val2_text) \
   while (                                                                      \
       ::std::string* absl_log_internal_check_op_result ABSL_ATTRIBUTE_UNUSED = \
           ::absl::log_internal::name##Impl(                                    \
               ::absl::log_internal::GetReferenceableValue(val1),               \
               ::absl::log_internal::GetReferenceableValue(val2),               \
-              ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#val1 " " #op             \
-                                                           " " #val2)))        \
+              ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val1_text                 \
+                                                     " " #op " " val2_text)))  \
   ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_op_result).InternalStream()
-#define ABSL_LOG_INTERNAL_QCHECK_OP(name, op, val1, val2)                  \
-  while (::std::string* absl_log_internal_qcheck_op_result =               \
-             ::absl::log_internal::name##Impl(                             \
-                 ::absl::log_internal::GetReferenceableValue(val1),        \
-                 ::absl::log_internal::GetReferenceableValue(val2),        \
-                 ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#val1 " " #op      \
-                                                              " " #val2))) \
+#define ABSL_LOG_INTERNAL_QCHECK_OP(name, op, val1, val1_text, val2, \
+                                    val2_text)                       \
+  while (::std::string* absl_log_internal_qcheck_op_result =         \
+             ::absl::log_internal::name##Impl(                       \
+                 ::absl::log_internal::GetReferenceableValue(val1),  \
+                 ::absl::log_internal::GetReferenceableValue(val2),  \
+                 ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(             \
+                     val1_text " " #op " " val2_text)))              \
   ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_op_result).InternalStream()
-#define ABSL_LOG_INTERNAL_CHECK_STROP(func, op, expected, s1, s2)              \
+#define ABSL_LOG_INTERNAL_CHECK_STROP(func, op, expected, s1, s1_text, s2,     \
+                                      s2_text)                                 \
   while (::std::string* absl_log_internal_check_strop_result =                 \
              ::absl::log_internal::Check##func##expected##Impl(                \
                  (s1), (s2),                                                   \
-                 ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#s1 " " #op " " #s2))) \
+                 ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(s1_text " " #op        \
+                                                                " " s2_text))) \
   ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_strop_result)               \
       .InternalStream()
-#define ABSL_LOG_INTERNAL_QCHECK_STROP(func, op, expected, s1, s2)             \
+#define ABSL_LOG_INTERNAL_QCHECK_STROP(func, op, expected, s1, s1_text, s2,    \
+                                       s2_text)                                \
   while (::std::string* absl_log_internal_qcheck_strop_result =                \
              ::absl::log_internal::Check##func##expected##Impl(                \
                  (s1), (s2),                                                   \
-                 ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#s1 " " #op " " #s2))) \
+                 ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(s1_text " " #op        \
+                                                                " " s2_text))) \
   ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_strop_result)             \
       .InternalStream()
 // This one is tricky:
@@ -108,33 +113,35 @@
 // * As usual, no braces so we can stream into the expansion with `operator<<`.
 // * Also as usual, it must expand to a single (partial) statement with no
 //   ambiguous-else problems.
-#define ABSL_LOG_INTERNAL_CHECK_OK(val)                                      \
-  for (::std::pair<const ::absl::Status*, ::std::string*>                    \
-           absl_log_internal_check_ok_goo;                                   \
-       absl_log_internal_check_ok_goo.first =                                \
-           ::absl::log_internal::AsStatus(val),                              \
-       absl_log_internal_check_ok_goo.second =                               \
-           ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok())     \
-               ? nullptr                                                     \
-               : ::absl::status_internal::MakeCheckFailString(               \
-                     absl_log_internal_check_ok_goo.first,                   \
-                     ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#val " is OK")), \
-       !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());)      \
-  ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_ok_goo.second)            \
+#define ABSL_LOG_INTERNAL_CHECK_OK(val, val_text)                        \
+  for (::std::pair<const ::absl::Status*, ::std::string*>                \
+           absl_log_internal_check_ok_goo;                               \
+       absl_log_internal_check_ok_goo.first =                            \
+           ::absl::log_internal::AsStatus(val),                          \
+       absl_log_internal_check_ok_goo.second =                           \
+           ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok()) \
+               ? nullptr                                                 \
+               : ::absl::status_internal::MakeCheckFailString(           \
+                     absl_log_internal_check_ok_goo.first,               \
+                     ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text     \
+                                                            " is OK")),  \
+       !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());)  \
+  ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_ok_goo.second)        \
       .InternalStream()
-#define ABSL_LOG_INTERNAL_QCHECK_OK(val)                                     \
-  for (::std::pair<const ::absl::Status*, ::std::string*>                    \
-           absl_log_internal_check_ok_goo;                                   \
-       absl_log_internal_check_ok_goo.first =                                \
-           ::absl::log_internal::AsStatus(val),                              \
-       absl_log_internal_check_ok_goo.second =                               \
-           ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok())     \
-               ? nullptr                                                     \
-               : ::absl::status_internal::MakeCheckFailString(               \
-                     absl_log_internal_check_ok_goo.first,                   \
-                     ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#val " is OK")), \
-       !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());)      \
-  ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_check_ok_goo.second)           \
+#define ABSL_LOG_INTERNAL_QCHECK_OK(val, val_text)                       \
+  for (::std::pair<const ::absl::Status*, ::std::string*>                \
+           absl_log_internal_check_ok_goo;                               \
+       absl_log_internal_check_ok_goo.first =                            \
+           ::absl::log_internal::AsStatus(val),                          \
+       absl_log_internal_check_ok_goo.second =                           \
+           ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok()) \
+               ? nullptr                                                 \
+               : ::absl::status_internal::MakeCheckFailString(           \
+                     absl_log_internal_check_ok_goo.first,               \
+                     ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text     \
+                                                            " is OK")),  \
+       !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());)  \
+  ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_check_ok_goo.second)       \
       .InternalStream()
 
 namespace absl {
@@ -364,7 +371,9 @@
   return t;
 }
 inline constexpr int GetReferenceableValue(int t) { return t; }
-inline unsigned int GetReferenceableValue(unsigned int t) { return t; }
+inline constexpr unsigned int GetReferenceableValue(unsigned int t) {
+  return t;
+}
 inline constexpr long GetReferenceableValue(long t) { return t; }  // NOLINT
 inline constexpr unsigned long GetReferenceableValue(              // NOLINT
     unsigned long t) {                                             // NOLINT
diff --git a/absl/log/internal/conditions.h b/absl/log/internal/conditions.h
index b89f1df..b3ce257 100644
--- a/absl/log/internal/conditions.h
+++ b/absl/log/internal/conditions.h
@@ -56,9 +56,15 @@
 // the ternary expression does a better job avoiding spurious diagnostics
 // (dangling else, missing switch case) and preserving noreturn semantics (e.g.
 // on `LOG(FATAL)`) without requiring braces.
+//
+// The `switch` ensures that this expansion is the begnning of a statement (as
+// opposed to an expression) and prevents shenanigans like
+// `AFunction(LOG(INFO))` and `decltype(LOG(INFO))`.  The apparently-redundant
+// `default` case makes the condition more amenable to Clang dataflow analysis.
 #define ABSL_LOG_INTERNAL_STATELESS_CONDITION(condition) \
   switch (0)                                             \
   case 0:                                                \
+  default:                                               \
     !(condition) ? (void)0 : ::absl::log_internal::Voidify()&&
 
 // `ABSL_LOG_INTERNAL_STATEFUL_CONDITION` applies a condition like
diff --git a/absl/log/internal/log_format.cc b/absl/log/internal/log_format.cc
index 5d40d25..0dcbc79 100644
--- a/absl/log/internal/log_format.cc
+++ b/absl/log/internal/log_format.cc
@@ -32,6 +32,7 @@
 #include "absl/base/config.h"
 #include "absl/base/log_severity.h"
 #include "absl/base/optimization.h"
+#include "absl/log/internal/append_truncated.h"
 #include "absl/log/internal/config.h"
 #include "absl/log/internal/globals.h"
 #include "absl/strings/numbers.h"
@@ -46,6 +47,31 @@
 namespace log_internal {
 namespace {
 
+// This templated function avoids compiler warnings about tautological
+// comparisons when log_internal::Tid is unsigned. It can be replaced with a
+// constexpr if once the minimum C++ version Abseil supports is C++17.
+template <typename T>
+inline std::enable_if_t<!std::is_signed<T>::value>
+PutLeadingWhitespace(T tid, char*& p) {
+  if (tid < 10) *p++ = ' ';
+  if (tid < 100) *p++ = ' ';
+  if (tid < 1000) *p++ = ' ';
+  if (tid < 10000) *p++ = ' ';
+  if (tid < 100000) *p++ = ' ';
+  if (tid < 1000000) *p++ = ' ';
+}
+
+template <typename T>
+inline std::enable_if_t<std::is_signed<T>::value>
+PutLeadingWhitespace(T tid, char*& p) {
+  if (tid >= 0 && tid < 10) *p++ = ' ';
+  if (tid > -10 && tid < 100) *p++ = ' ';
+  if (tid > -100 && tid < 1000) *p++ = ' ';
+  if (tid > -1000 && tid < 10000) *p++ = ' ';
+  if (tid > -10000 && tid < 100000) *p++ = ' ';
+  if (tid > -100000 && tid < 1000000) *p++ = ' ';
+}
+
 // The fields before the filename are all fixed-width except for the thread ID,
 // which is of bounded width.
 size_t FormatBoundedFields(absl::LogSeverity severity, absl::Time timestamp,
@@ -110,13 +136,7 @@
   absl::numbers_internal::PutTwoDigits(static_cast<size_t>(usecs % 100), p);
   p += 2;
   *p++ = ' ';
-  constexpr bool unsigned_tid_t = !std::is_signed<log_internal::Tid>::value;
-  if ((unsigned_tid_t || tid >= 0) && tid < 10) *p++ = ' ';
-  if ((unsigned_tid_t || tid > -10) && tid < 100) *p++ = ' ';
-  if ((unsigned_tid_t || tid > -100) && tid < 1000) *p++ = ' ';
-  if ((unsigned_tid_t || tid > -1000) && tid < 10000) *p++ = ' ';
-  if ((unsigned_tid_t || tid > -10000) && tid < 100000) *p++ = ' ';
-  if ((unsigned_tid_t || tid > -100000) && tid < 1000000) *p++ = ' ';
+  PutLeadingWhitespace(tid, p);
   p = absl::numbers_internal::FastIntToBuffer(tid, p);
   *p++ = ' ';
   const size_t bytes_formatted = static_cast<size_t>(p - buf.data());
@@ -124,15 +144,6 @@
   return bytes_formatted;
 }
 
-// Copies into `dst` as many bytes of `src` as will fit, then advances `dst`
-// past the copied bytes and returns the number of bytes written.
-size_t AppendTruncated(absl::string_view src, absl::Span<char>& dst) {
-  if (src.size() > dst.size()) src = src.substr(0, dst.size());
-  memcpy(dst.data(), src.data(), src.size());
-  dst.remove_prefix(src.size());
-  return src.size();
-}
-
 size_t FormatLineNumber(int line, absl::Span<char>& buf) {
   constexpr size_t kLineFieldMaxLen =
       sizeof(":] ") + (1 + std::numeric_limits<int>::digits10 + 1) - sizeof("");
@@ -158,13 +169,13 @@
                              absl::CivilSecond civil_second,
                              absl::Duration subsecond, log_internal::Tid tid,
                              absl::string_view basename, int line,
-                             absl::string_view message) {
+                             PrefixFormat format, absl::string_view message) {
   return absl::StrFormat(
-      "%c%02d%02d %02d:%02d:%02d.%06d %7d %s:%d] %s",
+      "%c%02d%02d %02d:%02d:%02d.%06d %7d %s:%d] %s%s",
       absl::LogSeverityName(severity)[0], civil_second.month(),
       civil_second.day(), civil_second.hour(), civil_second.minute(),
       civil_second.second(), absl::ToInt64Microseconds(subsecond), tid,
-      basename, line, message);
+      basename, line, format == PrefixFormat::kRaw ? "RAW: " : "", message);
 }
 
 // This method is fairly hot, and the library always passes a huge `buf`, so we
@@ -178,10 +189,12 @@
 // 3. line number and bracket
 size_t FormatLogPrefix(absl::LogSeverity severity, absl::Time timestamp,
                        log_internal::Tid tid, absl::string_view basename,
-                       int line, absl::Span<char>& buf) {
+                       int line, PrefixFormat format, absl::Span<char>& buf) {
   auto prefix_size = FormatBoundedFields(severity, timestamp, tid, buf);
-  prefix_size += AppendTruncated(basename, buf);
+  prefix_size += log_internal::AppendTruncated(basename, buf);
   prefix_size += FormatLineNumber(line, buf);
+  if (format == PrefixFormat::kRaw)
+    prefix_size += log_internal::AppendTruncated("RAW: ", buf);
   return prefix_size;
 }
 
diff --git a/absl/log/internal/log_format.h b/absl/log/internal/log_format.h
index a016328..95a45ed 100644
--- a/absl/log/internal/log_format.h
+++ b/absl/log/internal/log_format.h
@@ -38,12 +38,17 @@
 ABSL_NAMESPACE_BEGIN
 namespace log_internal {
 
+enum class PrefixFormat {
+  kNotRaw,
+  kRaw,
+};
+
 // Formats log message based on provided data.
 std::string FormatLogMessage(absl::LogSeverity severity,
                              absl::CivilSecond civil_second,
                              absl::Duration subsecond, log_internal::Tid tid,
                              absl::string_view basename, int line,
-                             absl::string_view message);
+                             PrefixFormat format, absl::string_view message);
 
 // Formats various entry metadata into a text string meant for use as a
 // prefix on a log message string.  Writes into `buf`, advances `buf` to point
@@ -64,7 +69,7 @@
 // see a thread ID.
 size_t FormatLogPrefix(absl::LogSeverity severity, absl::Time timestamp,
                        log_internal::Tid tid, absl::string_view basename,
-                       int line, absl::Span<char>& buf);
+                       int line, PrefixFormat format, absl::Span<char>& buf);
 
 }  // namespace log_internal
 ABSL_NAMESPACE_END
diff --git a/absl/log/internal/log_impl.h b/absl/log/internal/log_impl.h
new file mode 100644
index 0000000..82b5ed8
--- /dev/null
+++ b/absl/log/internal/log_impl.h
@@ -0,0 +1,212 @@
+// Copyright 2022 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_LOG_INTERNAL_LOG_IMPL_H_
+#define ABSL_LOG_INTERNAL_LOG_IMPL_H_
+
+#include "absl/log/internal/conditions.h"
+#include "absl/log/internal/log_message.h"
+#include "absl/log/internal/strip.h"
+
+// ABSL_LOG()
+#define ABSL_LOG_IMPL(severity)                          \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATELESS, true) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+// ABSL_PLOG()
+#define ABSL_PLOG_IMPL(severity)                           \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATELESS, true)   \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream() \
+          .WithPerror()
+
+// ABSL_DLOG()
+#ifndef NDEBUG
+#define ABSL_DLOG_IMPL(severity)                         \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATELESS, true) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+#else
+#define ABSL_DLOG_IMPL(severity)                          \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATELESS, false) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+#endif
+
+#define ABSL_LOG_IF_IMPL(severity, condition)                 \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATELESS, condition) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+#define ABSL_PLOG_IF_IMPL(severity, condition)                \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATELESS, condition) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()    \
+          .WithPerror()
+
+#ifndef NDEBUG
+#define ABSL_DLOG_IF_IMPL(severity, condition)                \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATELESS, condition) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+#else
+#define ABSL_DLOG_IF_IMPL(severity, condition)                           \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATELESS, false && (condition)) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+#endif
+
+// ABSL_LOG_EVERY_N
+#define ABSL_LOG_EVERY_N_IMPL(severity, n)                         \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, true)(EveryN, n) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+// ABSL_LOG_FIRST_N
+#define ABSL_LOG_FIRST_N_IMPL(severity, n)                         \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, true)(FirstN, n) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+// ABSL_LOG_EVERY_POW_2
+#define ABSL_LOG_EVERY_POW_2_IMPL(severity)                        \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, true)(EveryPow2) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+// ABSL_LOG_EVERY_N_SEC
+#define ABSL_LOG_EVERY_N_SEC_IMPL(severity, n_seconds)                        \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, true)(EveryNSec, n_seconds) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_PLOG_EVERY_N_IMPL(severity, n)                        \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, true)(EveryN, n) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()         \
+          .WithPerror()
+
+#define ABSL_PLOG_FIRST_N_IMPL(severity, n)                        \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, true)(FirstN, n) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()         \
+          .WithPerror()
+
+#define ABSL_PLOG_EVERY_POW_2_IMPL(severity)                       \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, true)(EveryPow2) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()         \
+          .WithPerror()
+
+#define ABSL_PLOG_EVERY_N_SEC_IMPL(severity, n_seconds)                       \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, true)(EveryNSec, n_seconds) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()                    \
+          .WithPerror()
+
+#ifndef NDEBUG
+#define ABSL_DLOG_EVERY_N_IMPL(severity, n)        \
+  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, true) \
+  (EveryN, n) ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_DLOG_FIRST_N_IMPL(severity, n)        \
+  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, true) \
+  (FirstN, n) ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_DLOG_EVERY_POW_2_IMPL(severity)       \
+  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, true) \
+  (EveryPow2) ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_DLOG_EVERY_N_SEC_IMPL(severity, n_seconds) \
+  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, true)      \
+  (EveryNSec, n_seconds) ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#else  // def NDEBUG
+#define ABSL_DLOG_EVERY_N_IMPL(severity, n)         \
+  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, false) \
+  (EveryN, n) ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_DLOG_FIRST_N_IMPL(severity, n)         \
+  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, false) \
+  (FirstN, n) ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_DLOG_EVERY_POW_2_IMPL(severity)        \
+  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, false) \
+  (EveryPow2) ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_DLOG_EVERY_N_SEC_IMPL(severity, n_seconds) \
+  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, false)     \
+  (EveryNSec, n_seconds) ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+#endif  // def NDEBUG
+
+#define ABSL_LOG_IF_EVERY_N_IMPL(severity, condition, n)                \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, condition)(EveryN, n) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_LOG_IF_FIRST_N_IMPL(severity, condition, n)                \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, condition)(FirstN, n) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_LOG_IF_EVERY_POW_2_IMPL(severity, condition)               \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, condition)(EveryPow2) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_LOG_IF_EVERY_N_SEC_IMPL(severity, condition, n_seconds)    \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, condition)(EveryNSec, \
+                                                             n_seconds) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_PLOG_IF_EVERY_N_IMPL(severity, condition, n)               \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, condition)(EveryN, n) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()              \
+          .WithPerror()
+
+#define ABSL_PLOG_IF_FIRST_N_IMPL(severity, condition, n)               \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, condition)(FirstN, n) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()              \
+          .WithPerror()
+
+#define ABSL_PLOG_IF_EVERY_POW_2_IMPL(severity, condition)              \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, condition)(EveryPow2) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()              \
+          .WithPerror()
+
+#define ABSL_PLOG_IF_EVERY_N_SEC_IMPL(severity, condition, n_seconds)   \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, condition)(EveryNSec, \
+                                                             n_seconds) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()              \
+          .WithPerror()
+
+#ifndef NDEBUG
+#define ABSL_DLOG_IF_EVERY_N_IMPL(severity, condition, n)               \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, condition)(EveryN, n) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_DLOG_IF_FIRST_N_IMPL(severity, condition, n)               \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, condition)(FirstN, n) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_DLOG_IF_EVERY_POW_2_IMPL(severity, condition)              \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, condition)(EveryPow2) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_DLOG_IF_EVERY_N_SEC_IMPL(severity, condition, n_seconds)   \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, condition)(EveryNSec, \
+                                                             n_seconds) \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#else  // def NDEBUG
+#define ABSL_DLOG_IF_EVERY_N_IMPL(severity, condition, n)                \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, false && (condition))( \
+      EveryN, n) ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_DLOG_IF_FIRST_N_IMPL(severity, condition, n)                \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, false && (condition))( \
+      FirstN, n) ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_DLOG_IF_EVERY_POW_2_IMPL(severity, condition)               \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, false && (condition))( \
+      EveryPow2) ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_DLOG_IF_EVERY_N_SEC_IMPL(severity, condition, n_seconds)    \
+  ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, false && (condition))( \
+      EveryNSec, n_seconds)                                              \
+      ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
+#endif  // def NDEBUG
+
+#endif  // ABSL_LOG_INTERNAL_LOG_IMPL_H_
diff --git a/absl/log/internal/log_message.cc b/absl/log/internal/log_message.cc
index 82833af..10ac245 100644
--- a/absl/log/internal/log_message.cc
+++ b/absl/log/internal/log_message.cc
@@ -41,15 +41,15 @@
 #include "absl/container/inlined_vector.h"
 #include "absl/debugging/internal/examine_stack.h"
 #include "absl/log/globals.h"
-#include "absl/log/internal/config.h"
+#include "absl/log/internal/append_truncated.h"
 #include "absl/log/internal/globals.h"
 #include "absl/log/internal/log_format.h"
 #include "absl/log/internal/log_sink_set.h"
+#include "absl/log/internal/proto.h"
 #include "absl/log/log_entry.h"
 #include "absl/log/log_sink.h"
 #include "absl/log/log_sink_registry.h"
 #include "absl/memory/memory.h"
-#include "absl/strings/str_format.h"
 #include "absl/strings/string_view.h"
 #include "absl/time/clock.h"
 #include "absl/time/time.h"
@@ -65,13 +65,39 @@
 namespace log_internal {
 
 namespace {
-// Copies into `dst` as many bytes of `src` as will fit, then truncates the
-// copied bytes from the front of `dst` and returns the number of bytes written.
-size_t AppendTruncated(absl::string_view src, absl::Span<char>* dst) {
-  if (src.size() > dst->size()) src = src.substr(0, dst->size());
-  memcpy(dst->data(), src.data(), src.size());
-  dst->remove_prefix(src.size());
-  return src.size();
+// message `logging.proto.Event`
+enum EventTag : uint8_t {
+  kValue = 7,
+};
+
+// message `logging.proto.Value`
+enum ValueTag : uint8_t {
+  kString = 1,
+  kStringLiteral = 6,
+};
+
+// Decodes a `logging.proto.Value` from `buf` and writes a string representation
+// into `dst`.  The string representation will be truncated if `dst` is not
+// large enough to hold it.  Returns false if `dst` has size zero or one (i.e.
+// sufficient only for a nul-terminator) and no decoded data could be written.
+// This function may or may not write a nul-terminator into `dst`, and it may or
+// may not truncate the data it writes in order to do make space for that nul
+// terminator.  In any case, `dst` will be advanced to point at the byte where
+// subsequent writes should begin.
+bool PrintValue(absl::Span<char>& dst, absl::Span<const char> buf) {
+  if (dst.size() <= 1) return false;
+  ProtoField field;
+  while (field.DecodeFrom(&buf)) {
+    switch (field.tag()) {
+      case ValueTag::kString:
+      case ValueTag::kStringLiteral:
+        if (field.type() == WireType::kLengthDelimited)
+          if (log_internal::AppendTruncated(field.string_value(), dst) <
+              field.string_value().size())
+            return false;
+    }
+  }
+  return true;
 }
 
 absl::string_view Basename(absl::string_view filepath) {
@@ -93,87 +119,6 @@
 }
 }  // namespace
 
-// A write-only `std::streambuf` that writes into an `absl::Span<char>`.
-//
-// This class is responsible for writing a metadata prefix just before the first
-// data are streamed in.  The metadata are subject to change (cf.
-// `LogMessage::AtLocation`) until then, so we wait as long as possible.
-//
-// This class is also responsible for reserving space for a trailing newline
-// so that one can be added later by `Finalize` no matter how many data are
-// streamed in.
-class LogEntryStreambuf final : public std::streambuf {
- public:
-  explicit LogEntryStreambuf(absl::Span<char> buf, const absl::LogEntry& entry)
-      : buf_(buf), entry_(entry), prefix_len_(0), finalized_(false) {
-    // To detect when data are first written, we leave the put area null,
-    // override `overflow`, and check ourselves in `xsputn`.
-  }
-
-  LogEntryStreambuf(LogEntryStreambuf&&) = delete;
-  LogEntryStreambuf& operator=(LogEntryStreambuf&&) = delete;
-
-  absl::Span<const char> Finalize() {
-    assert(!finalized_);
-    // If no data were ever streamed in, this is where we must write the prefix.
-    if (pbase() == nullptr) Initialize();
-    // Here we reclaim the two bytes we reserved.
-    ptrdiff_t idx = pptr() - pbase();
-    setp(buf_.data(), buf_.data() + buf_.size());
-    pbump(static_cast<int>(idx));
-    sputc('\n');
-    sputc('\0');
-    finalized_ = true;
-    return absl::Span<const char>(pbase(),
-                                  static_cast<size_t>(pptr() - pbase()));
-  }
-  size_t prefix_len() const { return prefix_len_; }
-
- protected:
-  std::streamsize xsputn(const char* s, std::streamsize n) override {
-    if (n < 0) return 0;
-    if (pbase() == nullptr) Initialize();
-    return static_cast<std::streamsize>(
-        Append(absl::string_view(s, static_cast<size_t>(n))));
-  }
-
-  int overflow(int ch = EOF) override {
-    if (pbase() == nullptr) Initialize();
-    if (ch == EOF) return 0;
-    if (pptr() == epptr()) return EOF;
-    *pptr() = static_cast<char>(ch);
-    pbump(1);
-    return 1;
-  }
-
- private:
-  void Initialize() {
-    // Here we reserve two bytes in our buffer to guarantee `Finalize` space to
-    // add a trailing "\n\0".
-    assert(buf_.size() >= 2);
-    setp(buf_.data(), buf_.data() + buf_.size() - 2);
-    if (entry_.prefix()) {
-      absl::Span<char> remaining = buf_;
-      prefix_len_ = log_internal::FormatLogPrefix(
-          entry_.log_severity(), entry_.timestamp(), entry_.tid(),
-          entry_.source_basename(), entry_.source_line(), remaining);
-      pbump(static_cast<int>(prefix_len_));
-    }
-  }
-
-  size_t Append(absl::string_view data) {
-    absl::Span<char> remaining(pptr(), static_cast<size_t>(epptr() - pptr()));
-    const size_t written = AppendTruncated(data, &remaining);
-    pbump(static_cast<int>(written));
-    return written;
-  }
-
-  const absl::Span<char> buf_;
-  const absl::LogEntry& entry_;
-  size_t prefix_len_;
-  bool finalized_;
-};
-
 struct LogMessage::LogMessageData final {
   LogMessageData(const char* file, int line, absl::LogSeverity severity,
                  absl::Time timestamp);
@@ -196,18 +141,32 @@
   // non-sink targets (e.g. stderr, log files).
   bool extra_sinks_only;
 
+  std::ostream manipulated;  // ostream with IO manipulators applied
+
+  // A `logging.proto.Event` proto message is built into `encoded_buf`.
+  std::array<char, kLogMessageBufferSize> encoded_buf;
+  // `encoded_remaining` is the suffix of `encoded_buf` that has not been filled
+  // yet.  If a datum to be encoded does not fit into `encoded_remaining` and
+  // cannot be truncated to fit, the size of `encoded_remaining` will be zeroed
+  // to prevent encoding of any further data.  Note that in this case its data()
+  // pointer will not point past the end of `encoded_buf`.
+  absl::Span<char> encoded_remaining;
+
   // A formatted string message is built in `string_buf`.
   std::array<char, kLogMessageBufferSize> string_buf;
 
-  // A `std::streambuf` that stores into `string_buf`.
-  LogEntryStreambuf streambuf_;
+  void FinalizeEncodingAndFormat();
 };
 
 LogMessage::LogMessageData::LogMessageData(const char* file, int line,
                                            absl::LogSeverity severity,
                                            absl::Time timestamp)
     : extra_sinks_only(false),
-      streambuf_(absl::MakeSpan(string_buf), entry) {
+      manipulated(nullptr),
+      // This `absl::MakeSpan` silences spurious -Wuninitialized from GCC:
+      encoded_remaining(absl::MakeSpan(encoded_buf)) {
+  // Legacy defaults for LOG's ostream:
+  manipulated.setf(std::ios_base::showbase | std::ios_base::boolalpha);
   entry.full_filename_ = file;
   entry.base_filename_ = Basename(file);
   entry.line_ = line;
@@ -218,27 +177,70 @@
   entry.tid_ = absl::base_internal::GetCachedTID();
 }
 
+void LogMessage::LogMessageData::FinalizeEncodingAndFormat() {
+  // Note that `encoded_remaining` may have zero size without pointing past the
+  // end of `encoded_buf`, so the difference between `data()` pointers is used
+  // to compute the size of `encoded_data`.
+  absl::Span<const char> encoded_data(
+      encoded_buf.data(),
+      static_cast<size_t>(encoded_remaining.data() - encoded_buf.data()));
+  // `string_remaining` is the suffix of `string_buf` that has not been filled
+  // yet.
+  absl::Span<char> string_remaining(string_buf);
+  // We may need to write a newline and nul-terminator at the end of the decoded
+  // string data.  Rather than worry about whether those should overwrite the
+  // end of the string (if the buffer is full) or be appended, we avoid writing
+  // into the last two bytes so we always have space to append.
+  string_remaining.remove_suffix(2);
+  entry.prefix_len_ =
+      entry.prefix() ? log_internal::FormatLogPrefix(
+                           entry.log_severity(), entry.timestamp(), entry.tid(),
+                           entry.source_basename(), entry.source_line(),
+                           log_internal::ThreadIsLoggingToLogSink()
+                               ? PrefixFormat::kRaw
+                               : PrefixFormat::kNotRaw,
+                           string_remaining)
+                     : 0;
+  // Decode data from `encoded_buf` until we run out of data or we run out of
+  // `string_remaining`.
+  ProtoField field;
+  while (field.DecodeFrom(&encoded_data)) {
+    switch (field.tag()) {
+      case EventTag::kValue:
+        if (field.type() != WireType::kLengthDelimited) continue;
+        if (PrintValue(string_remaining, field.bytes_value())) continue;
+        break;
+    }
+    break;
+  }
+  auto chars_written =
+      static_cast<size_t>(string_remaining.data() - string_buf.data());
+    string_buf[chars_written++] = '\n';
+  string_buf[chars_written++] = '\0';
+  entry.text_message_with_prefix_and_newline_and_nul_ =
+      absl::MakeSpan(string_buf).subspan(0, chars_written);
+}
+
 LogMessage::LogMessage(const char* file, int line, absl::LogSeverity severity)
-    : data_(
-          absl::make_unique<LogMessageData>(file, line, severity, absl::Now()))
-      ,
-      stream_(&data_->streambuf_)
-{
+    : data_(absl::make_unique<LogMessageData>(file, line, severity,
+                                              absl::Now())) {
   data_->first_fatal = false;
   data_->is_perror = false;
   data_->fail_quietly = false;
 
-  // Legacy defaults for LOG's ostream:
-  stream_.setf(std::ios_base::showbase | std::ios_base::boolalpha);
-  // `fill('0')` is omitted here because its effects are very different without
-  // structured logging.  Resolution is tracked in b/111310488.
-
   // This logs a backtrace even if the location is subsequently changed using
   // AtLocation.  This quirk, and the behavior when AtLocation is called twice,
   // are fixable but probably not worth fixing.
   LogBacktraceIfNeeded();
 }
 
+LogMessage::LogMessage(const char* file, int line, InfoTag)
+    : LogMessage(file, line, absl::LogSeverity::kInfo) {}
+LogMessage::LogMessage(const char* file, int line, WarningTag)
+    : LogMessage(file, line, absl::LogSeverity::kWarning) {}
+LogMessage::LogMessage(const char* file, int line, ErrorTag)
+    : LogMessage(file, line, absl::LogSeverity::kError) {}
+
 LogMessage::~LogMessage() {
 #ifdef ABSL_MIN_LOG_LEVEL
   if (data_->entry.log_severity() <
@@ -350,6 +352,25 @@
   _exit(1);
 }
 
+LogMessage& LogMessage::operator<<(const std::string& v) {
+  CopyToEncodedBuffer<StringType::kNotLiteral>(v);
+  return *this;
+}
+
+LogMessage& LogMessage::operator<<(absl::string_view v) {
+  CopyToEncodedBuffer<StringType::kNotLiteral>(v);
+  return *this;
+}
+LogMessage& LogMessage::operator<<(std::ostream& (*m)(std::ostream& os)) {
+  OstreamView view(*data_);
+  data_->manipulated << m;
+  return *this;
+}
+LogMessage& LogMessage::operator<<(std::ios_base& (*m)(std::ios_base& os)) {
+  OstreamView view(*data_);
+  data_->manipulated << m;
+  return *this;
+}
 template LogMessage& LogMessage::operator<<(const char& v);
 template LogMessage& LogMessage::operator<<(const signed char& v);
 template LogMessage& LogMessage::operator<<(const unsigned char& v);
@@ -367,12 +388,9 @@
 template LogMessage& LogMessage::operator<<(const float& v);
 template LogMessage& LogMessage::operator<<(const double& v);
 template LogMessage& LogMessage::operator<<(const bool& v);
-template LogMessage& LogMessage::operator<<(const std::string& v);
-template LogMessage& LogMessage::operator<<(const absl::string_view& v);
 
 void LogMessage::Flush() {
-  if (data_->entry.log_severity() < absl::MinLogLevel())
-    return;
+  if (data_->entry.log_severity() < absl::MinLogLevel()) return;
 
   if (data_->is_perror) {
     InternalStream() << ": " << absl::base_internal::StrError(errno_saver_())
@@ -380,22 +398,67 @@
   }
 
   // Have we already seen a fatal message?
-  ABSL_CONST_INIT static std::atomic_flag seen_fatal = ATOMIC_FLAG_INIT;
+  ABSL_CONST_INIT static std::atomic<bool> seen_fatal(false);
   if (data_->entry.log_severity() == absl::LogSeverity::kFatal &&
       absl::log_internal::ExitOnDFatal()) {
     // Exactly one LOG(FATAL) message is responsible for aborting the process,
     // even if multiple threads LOG(FATAL) concurrently.
-    data_->first_fatal = !seen_fatal.test_and_set(std::memory_order_relaxed);
+    bool expected_seen_fatal = false;
+    if (seen_fatal.compare_exchange_strong(expected_seen_fatal, true,
+                                           std::memory_order_relaxed)) {
+      data_->first_fatal = true;
+    }
   }
 
-  data_->entry.text_message_with_prefix_and_newline_and_nul_ =
-      data_->streambuf_.Finalize();
-  data_->entry.prefix_len_ = data_->streambuf_.prefix_len();
+  data_->FinalizeEncodingAndFormat();
+  data_->entry.encoding_ =
+      absl::string_view(data_->encoded_buf.data(),
+                        static_cast<size_t>(data_->encoded_remaining.data() -
+                                            data_->encoded_buf.data()));
   SendToLog();
 }
 
 void LogMessage::SetFailQuietly() { data_->fail_quietly = true; }
 
+LogMessage::OstreamView::OstreamView(LogMessageData& message_data)
+    : data_(message_data), encoded_remaining_copy_(data_.encoded_remaining) {
+  // This constructor sets the `streambuf` up so that streaming into an attached
+  // ostream encodes string data in-place.  To do that, we write appropriate
+  // headers into the buffer using a copy of the buffer view so that we can
+  // decide not to keep them later if nothing is ever streamed in.  We don't
+  // know how much data we'll get, but we can use the size of the remaining
+  // buffer as an upper bound and fill in the right size once we know it.
+  message_start_ =
+      EncodeMessageStart(EventTag::kValue, encoded_remaining_copy_.size(),
+                         &encoded_remaining_copy_);
+  string_start_ =
+      EncodeMessageStart(ValueTag::kString, encoded_remaining_copy_.size(),
+                         &encoded_remaining_copy_);
+  setp(encoded_remaining_copy_.data(),
+       encoded_remaining_copy_.data() + encoded_remaining_copy_.size());
+  data_.manipulated.rdbuf(this);
+}
+
+LogMessage::OstreamView::~OstreamView() {
+  data_.manipulated.rdbuf(nullptr);
+  if (!string_start_.data()) {
+    // The second field header didn't fit.  Whether the first one did or not, we
+    // shouldn't commit `encoded_remaining_copy_`, and we also need to zero the
+    // size of `data_->encoded_remaining` so that no more data are encoded.
+    data_.encoded_remaining.remove_suffix(data_.encoded_remaining.size());
+    return;
+  }
+  const absl::Span<const char> contents(pbase(),
+                                        static_cast<size_t>(pptr() - pbase()));
+  if (contents.empty()) return;
+  encoded_remaining_copy_.remove_prefix(contents.size());
+  EncodeMessageLength(string_start_, &encoded_remaining_copy_);
+  EncodeMessageLength(message_start_, &encoded_remaining_copy_);
+  data_.encoded_remaining = encoded_remaining_copy_;
+}
+
+std::ostream& LogMessage::OstreamView::stream() { return data_.manipulated; }
+
 bool LogMessage::IsFatal() const {
   return data_->entry.log_severity() == absl::LogSeverity::kFatal &&
          absl::log_internal::ExitOnDFatal();
@@ -449,13 +512,72 @@
   if (!absl::log_internal::ShouldLogBacktraceAt(data_->entry.source_basename(),
                                                 data_->entry.source_line()))
     return;
-  stream_ << " (stacktrace:\n";
+  OstreamView view(*data_);
+  view.stream() << " (stacktrace:\n";
   debugging_internal::DumpStackTrace(
       1, log_internal::MaxFramesInLogStackTrace(),
-      log_internal::ShouldSymbolizeLogStackTrace(), WriteToStream, &stream_);
-  stream_ << ") ";
+      log_internal::ShouldSymbolizeLogStackTrace(), WriteToStream,
+      &view.stream());
+  view.stream() << ") ";
 }
 
+// Encodes into `data_->encoded_remaining` a partial `logging.proto.Event`
+// containing the specified string data using a `Value` field appropriate to
+// `str_type`.  Truncates `str` if necessary, but emits nothing and marks the
+// buffer full if  even the field headers do not fit.
+template <LogMessage::StringType str_type>
+void LogMessage::CopyToEncodedBuffer(absl::string_view str) {
+  auto encoded_remaining_copy = data_->encoded_remaining;
+  auto start = EncodeMessageStart(
+      EventTag::kValue, BufferSizeFor(WireType::kLengthDelimited) + str.size(),
+      &encoded_remaining_copy);
+  // If the `logging.proto.Event.value` field header did not fit,
+  // `EncodeMessageStart` will have zeroed `encoded_remaining_copy`'s size and
+  // `EncodeStringTruncate` will fail too.
+  if (EncodeStringTruncate(str_type == StringType::kLiteral
+                               ? ValueTag::kStringLiteral
+                               : ValueTag::kString,
+                           str, &encoded_remaining_copy)) {
+    // The string may have been truncated, but the field header fit.
+    EncodeMessageLength(start, &encoded_remaining_copy);
+    data_->encoded_remaining = encoded_remaining_copy;
+  } else {
+    // The field header(s) did not fit; zero `encoded_remaining` so we don't
+    // write anything else later.
+    data_->encoded_remaining.remove_suffix(data_->encoded_remaining.size());
+  }
+}
+template void LogMessage::CopyToEncodedBuffer<LogMessage::StringType::kLiteral>(
+    absl::string_view str);
+template void LogMessage::CopyToEncodedBuffer<
+    LogMessage::StringType::kNotLiteral>(absl::string_view str);
+template <LogMessage::StringType str_type>
+void LogMessage::CopyToEncodedBuffer(char ch, size_t num) {
+  auto encoded_remaining_copy = data_->encoded_remaining;
+  auto value_start = EncodeMessageStart(
+      EventTag::kValue, BufferSizeFor(WireType::kLengthDelimited) + num,
+      &encoded_remaining_copy);
+  auto str_start = EncodeMessageStart(str_type == StringType::kLiteral
+                                          ? ValueTag::kStringLiteral
+                                          : ValueTag::kString,
+                                      num, &encoded_remaining_copy);
+  if (str_start.data()) {
+    // The field headers fit.
+    log_internal::AppendTruncated(ch, num, encoded_remaining_copy);
+    EncodeMessageLength(str_start, &encoded_remaining_copy);
+    EncodeMessageLength(value_start, &encoded_remaining_copy);
+    data_->encoded_remaining = encoded_remaining_copy;
+  } else {
+    // The field header(s) did not fit; zero `encoded_remaining` so we don't
+    // write anything else later.
+    data_->encoded_remaining.remove_suffix(data_->encoded_remaining.size());
+  }
+}
+template void LogMessage::CopyToEncodedBuffer<LogMessage::StringType::kLiteral>(
+    char ch, size_t num);
+template void LogMessage::CopyToEncodedBuffer<
+    LogMessage::StringType::kNotLiteral>(char ch, size_t num);
+
 LogMessageFatal::LogMessageFatal(const char* file, int line)
     : LogMessage(file, line, absl::LogSeverity::kFatal) {}
 
@@ -467,7 +589,7 @@
 
 // ABSL_ATTRIBUTE_NORETURN doesn't seem to work on destructors with msvc, so
 // disable msvc's warning about the d'tor never returning.
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && !defined(__clang__)
 #pragma warning(push)
 #pragma warning(disable : 4722)
 #endif
@@ -475,7 +597,7 @@
   Flush();
   FailWithoutStackTrace();
 }
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && !defined(__clang__)
 #pragma warning(pop)
 #endif
 
@@ -493,7 +615,7 @@
 
 // ABSL_ATTRIBUTE_NORETURN doesn't seem to work on destructors with msvc, so
 // disable msvc's warning about the d'tor never returning.
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && !defined(__clang__)
 #pragma warning(push)
 #pragma warning(disable : 4722)
 #endif
@@ -501,7 +623,7 @@
   Flush();
   FailQuietly();
 }
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && !defined(__clang__)
 #pragma warning(pop)
 #endif
 
diff --git a/absl/log/internal/log_message.h b/absl/log/internal/log_message.h
index 37a267c..4693772 100644
--- a/absl/log/internal/log_message.h
+++ b/absl/log/internal/log_message.h
@@ -37,24 +37,35 @@
 #include "absl/base/config.h"
 #include "absl/base/internal/errno_saver.h"
 #include "absl/base/log_severity.h"
-#include "absl/log/internal/config.h"
 #include "absl/log/internal/nullguard.h"
 #include "absl/log/log_entry.h"
 #include "absl/log/log_sink.h"
+#include "absl/strings/internal/has_absl_stringify.h"
 #include "absl/strings/string_view.h"
 #include "absl/time/time.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace log_internal {
-
 constexpr int kLogMessageBufferSize = 15000;
 
 class LogMessage {
  public:
+  struct InfoTag {};
+  struct WarningTag {};
+  struct ErrorTag {};
+
   // Used for `LOG`.
   LogMessage(const char* file, int line,
              absl::LogSeverity severity) ABSL_ATTRIBUTE_COLD;
+  // These constructors are slightly smaller/faster to call; the severity is
+  // curried into the function pointer.
+  LogMessage(const char* file, int line,
+             InfoTag) ABSL_ATTRIBUTE_COLD ABSL_ATTRIBUTE_NOINLINE;
+  LogMessage(const char* file, int line,
+             WarningTag) ABSL_ATTRIBUTE_COLD ABSL_ATTRIBUTE_NOINLINE;
+  LogMessage(const char* file, int line,
+             ErrorTag) ABSL_ATTRIBUTE_COLD ABSL_ATTRIBUTE_NOINLINE;
   LogMessage(const LogMessage&) = delete;
   LogMessage& operator=(const LogMessage&) = delete;
   ~LogMessage() ABSL_ATTRIBUTE_COLD;
@@ -130,6 +141,10 @@
   LogMessage& operator<<(bool v) { return operator<< <bool>(v); }
   // clang-format on
 
+  // These overloads are more efficient since no `ostream` is involved.
+  LogMessage& operator<<(const std::string& v);
+  LogMessage& operator<<(absl::string_view v);
+
   // Handle stream manipulators e.g. std::endl.
   LogMessage& operator<<(std::ostream& (*m)(std::ostream& os));
   LogMessage& operator<<(std::ios_base& (*m)(std::ios_base& os));
@@ -153,8 +168,17 @@
   template <int SIZE>
   LogMessage& operator<<(char (&buf)[SIZE]) ABSL_ATTRIBUTE_NOINLINE;
 
-  // Default: uses `ostream` logging to convert `v` to a string.
-  template <typename T>
+  // Types that support `AbslStringify()` are serialized that way.
+  template <typename T,
+            typename std::enable_if<
+                strings_internal::HasAbslStringify<T>::value, int>::type = 0>
+  LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE;
+
+  // Types that don't support `AbslStringify()` but do support streaming into a
+  // `std::ostream&` are serialized that way.
+  template <typename T,
+            typename std::enable_if<
+                !strings_internal::HasAbslStringify<T>::value, int>::type = 0>
   LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE;
 
   // Note: We explicitly do not support `operator<<` for non-const references
@@ -180,6 +204,37 @@
 
  private:
   struct LogMessageData;  // Opaque type containing message state
+  friend class AsLiteralImpl;
+  friend class StringifySink;
+
+  // This streambuf writes directly into the structured logging buffer so that
+  // arbitrary types can be encoded as string data (using
+  // `operator<<(std::ostream &, ...)` without any extra allocation or copying.
+  // Space is reserved before the data to store the length field, which is
+  // filled in by `~OstreamView`.
+  class OstreamView final : public std::streambuf {
+   public:
+    explicit OstreamView(LogMessageData& message_data);
+    ~OstreamView() override;
+    OstreamView(const OstreamView&) = delete;
+    OstreamView& operator=(const OstreamView&) = delete;
+    std::ostream& stream();
+
+   private:
+    LogMessageData& data_;
+    absl::Span<char> encoded_remaining_copy_;
+    absl::Span<char> message_start_;
+    absl::Span<char> string_start_;
+  };
+
+  enum class StringType {
+    kLiteral,
+    kNotLiteral,
+  };
+  template <StringType str_type>
+  void CopyToEncodedBuffer(absl::string_view str) ABSL_ATTRIBUTE_NOINLINE;
+  template <StringType str_type>
+  void CopyToEncodedBuffer(char ch, size_t num) ABSL_ATTRIBUTE_NOINLINE;
 
   // Returns `true` if the message is fatal or enabled debug-fatal.
   bool IsFatal() const;
@@ -201,35 +256,62 @@
   // We keep the data in a separate struct so that each instance of `LogMessage`
   // uses less stack space.
   std::unique_ptr<LogMessageData> data_;
+};
 
-  std::ostream stream_;
+// Helper class so that `AbslStringify()` can modify the LogMessage.
+class StringifySink final {
+ public:
+  explicit StringifySink(LogMessage& message) : message_(message) {}
+
+  void Append(size_t count, char ch) {
+    message_.CopyToEncodedBuffer<LogMessage::StringType::kNotLiteral>(ch,
+                                                                      count);
+  }
+
+  void Append(absl::string_view v) {
+    message_.CopyToEncodedBuffer<LogMessage::StringType::kNotLiteral>(v);
+  }
+
+  // For types that implement `AbslStringify` using `absl::Format()`.
+  friend void AbslFormatFlush(StringifySink* sink, absl::string_view v) {
+    sink->Append(v);
+  }
+
+ private:
+  LogMessage& message_;
 };
 
 // Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
-template <typename T>
+template <typename T,
+          typename std::enable_if<strings_internal::HasAbslStringify<T>::value,
+                                  int>::type>
 LogMessage& LogMessage::operator<<(const T& v) {
-  stream_ << log_internal::NullGuard<T>().Guard(v);
+  StringifySink sink(*this);
+  // Replace with public API.
+  AbslStringify(sink, v);
   return *this;
 }
-inline LogMessage& LogMessage::operator<<(
-    std::ostream& (*m)(std::ostream& os)) {
-  stream_ << m;
+
+// Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
+template <typename T,
+          typename std::enable_if<!strings_internal::HasAbslStringify<T>::value,
+                                  int>::type>
+LogMessage& LogMessage::operator<<(const T& v) {
+  OstreamView view(*data_);
+  view.stream() << log_internal::NullGuard<T>().Guard(v);
   return *this;
 }
-inline LogMessage& LogMessage::operator<<(
-    std::ios_base& (*m)(std::ios_base& os)) {
-  stream_ << m;
-  return *this;
-}
+
 template <int SIZE>
 LogMessage& LogMessage::operator<<(const char (&buf)[SIZE]) {
-  stream_ << buf;
+  CopyToEncodedBuffer<StringType::kLiteral>(buf);
   return *this;
 }
+
 // Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
 template <int SIZE>
 LogMessage& LogMessage::operator<<(char (&buf)[SIZE]) {
-  stream_ << buf;
+  CopyToEncodedBuffer<StringType::kNotLiteral>(buf);
   return *this;
 }
 // We instantiate these specializations in the library's TU to save space in
@@ -256,8 +338,16 @@
 extern template LogMessage& LogMessage::operator<<(const float& v);
 extern template LogMessage& LogMessage::operator<<(const double& v);
 extern template LogMessage& LogMessage::operator<<(const bool& v);
-extern template LogMessage& LogMessage::operator<<(const std::string& v);
-extern template LogMessage& LogMessage::operator<<(const absl::string_view& v);
+
+extern template void LogMessage::CopyToEncodedBuffer<
+    LogMessage::StringType::kLiteral>(absl::string_view str);
+extern template void LogMessage::CopyToEncodedBuffer<
+    LogMessage::StringType::kNotLiteral>(absl::string_view str);
+extern template void
+LogMessage::CopyToEncodedBuffer<LogMessage::StringType::kLiteral>(char ch,
+                                                                  size_t num);
+extern template void LogMessage::CopyToEncodedBuffer<
+    LogMessage::StringType::kNotLiteral>(char ch, size_t num);
 
 // `LogMessageFatal` ensures the process will exit in failure after logging this
 // message.
diff --git a/absl/log/internal/log_sink_set.cc b/absl/log/internal/log_sink_set.cc
index f9d030a..b7cbe36 100644
--- a/absl/log/internal/log_sink_set.cc
+++ b/absl/log/internal/log_sink_set.cc
@@ -122,11 +122,11 @@
 
   void Send(const absl::LogEntry& entry) override {
     const int level = AndroidLogLevel(entry);
-    // TODO(b/37587197): make the tag ("native") configurable.
-    __android_log_write(level, "native",
+    const char* const tag = GetAndroidNativeTag();
+    __android_log_write(level, tag,
                         entry.text_message_with_prefix_and_newline_c_str());
     if (entry.log_severity() == absl::LogSeverity::kFatal)
-      __android_log_write(ANDROID_LOG_FATAL, "native", "terminating.\n");
+      __android_log_write(ANDROID_LOG_FATAL, tag, "terminating.\n");
   }
 
  private:
diff --git a/absl/log/internal/nullguard.cc b/absl/log/internal/nullguard.cc
new file mode 100644
index 0000000..3296c01
--- /dev/null
+++ b/absl/log/internal/nullguard.cc
@@ -0,0 +1,35 @@
+// Copyright 2023 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/log/internal/nullguard.h"
+
+#include <array>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+
+ABSL_CONST_INIT ABSL_DLL const std::array<char, 7> kCharNull{
+    {'(', 'n', 'u', 'l', 'l', ')', '\0'}};
+ABSL_CONST_INIT ABSL_DLL const std::array<signed char, 7> kSignedCharNull{
+    {'(', 'n', 'u', 'l', 'l', ')', '\0'}};
+ABSL_CONST_INIT ABSL_DLL const std::array<unsigned char, 7> kUnsignedCharNull{
+    {'(', 'n', 'u', 'l', 'l', ')', '\0'}};
+
+}  // namespace log_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/log/internal/nullguard.h b/absl/log/internal/nullguard.h
index 147ca81..623943c 100644
--- a/absl/log/internal/nullguard.h
+++ b/absl/log/internal/nullguard.h
@@ -24,29 +24,61 @@
 #ifndef ABSL_LOG_INTERNAL_NULLGUARD_H_
 #define ABSL_LOG_INTERNAL_NULLGUARD_H_
 
+#include <array>
 #include <cstddef>
 
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace log_internal {
 
+ABSL_CONST_INIT ABSL_DLL extern const std::array<char, 7> kCharNull;
+ABSL_CONST_INIT ABSL_DLL extern const std::array<signed char, 7>
+    kSignedCharNull;
+ABSL_CONST_INIT ABSL_DLL extern const std::array<unsigned char, 7>
+    kUnsignedCharNull;
+
 template <typename T>
 struct NullGuard final {
   static const T& Guard(const T& v) { return v; }
 };
 template <>
 struct NullGuard<char*> final {
-  static const char* Guard(const char* v) { return v ? v : "(null)"; }
+  static const char* Guard(const char* v) { return v ? v : kCharNull.data(); }
 };
 template <>
 struct NullGuard<const char*> final {
-  static const char* Guard(const char* v) { return v ? v : "(null)"; }
+  static const char* Guard(const char* v) { return v ? v : kCharNull.data(); }
+};
+template <>
+struct NullGuard<signed char*> final {
+  static const signed char* Guard(const signed char* v) {
+    return v ? v : kSignedCharNull.data();
+  }
+};
+template <>
+struct NullGuard<const signed char*> final {
+  static const signed char* Guard(const signed char* v) {
+    return v ? v : kSignedCharNull.data();
+  }
+};
+template <>
+struct NullGuard<unsigned char*> final {
+  static const unsigned char* Guard(const unsigned char* v) {
+    return v ? v : kUnsignedCharNull.data();
+  }
+};
+template <>
+struct NullGuard<const unsigned char*> final {
+  static const unsigned char* Guard(const unsigned char* v) {
+    return v ? v : kUnsignedCharNull.data();
+  }
 };
 template <>
 struct NullGuard<std::nullptr_t> final {
-  static const char* Guard(const std::nullptr_t&) { return "(null)"; }
+  static const char* Guard(const std::nullptr_t&) { return kCharNull.data(); }
 };
 
 }  // namespace log_internal
diff --git a/absl/log/internal/nullstream.h b/absl/log/internal/nullstream.h
index 80c62c9..16f5f49 100644
--- a/absl/log/internal/nullstream.h
+++ b/absl/log/internal/nullstream.h
@@ -114,15 +114,15 @@
 // and expression-defined severity use `NullStreamMaybeFatal` above.
 class NullStreamFatal final : public NullStream {
  public:
-  NullStreamFatal() {}
+  NullStreamFatal() = default;
   // ABSL_ATTRIBUTE_NORETURN doesn't seem to work on destructors with msvc, so
   // disable msvc's warning about the d'tor never returning.
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && !defined(__clang__)
 #pragma warning(push)
 #pragma warning(disable : 4722)
 #endif
   ABSL_ATTRIBUTE_NORETURN ~NullStreamFatal() { _exit(1); }
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && !defined(__clang__)
 #pragma warning(pop)
 #endif
 };
diff --git a/absl/log/internal/proto.cc b/absl/log/internal/proto.cc
new file mode 100644
index 0000000..eb699ae
--- /dev/null
+++ b/absl/log/internal/proto.cc
@@ -0,0 +1,220 @@
+// 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/log/internal/proto.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/types/span.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+namespace {
+void EncodeRawVarint(uint64_t value, size_t size, absl::Span<char> *buf) {
+  for (size_t s = 0; s < size; s++) {
+    (*buf)[s] = static_cast<char>((value & 0x7f) | (s + 1 == size ? 0 : 0x80));
+    value >>= 7;
+  }
+  buf->remove_prefix(size);
+}
+constexpr uint64_t MakeTagType(uint64_t tag, WireType type) {
+  return tag << 3 | static_cast<uint64_t>(type);
+}
+}  // namespace
+
+bool EncodeVarint(uint64_t tag, uint64_t value, absl::Span<char> *buf) {
+  const uint64_t tag_type = MakeTagType(tag, WireType::kVarint);
+  const size_t tag_type_size = VarintSize(tag_type);
+  const size_t value_size = VarintSize(value);
+  if (tag_type_size + value_size > buf->size()) {
+    buf->remove_suffix(buf->size());
+    return false;
+  }
+  EncodeRawVarint(tag_type, tag_type_size, buf);
+  EncodeRawVarint(value, value_size, buf);
+  return true;
+}
+
+bool Encode64Bit(uint64_t tag, uint64_t value, absl::Span<char> *buf) {
+  const uint64_t tag_type = MakeTagType(tag, WireType::k64Bit);
+  const size_t tag_type_size = VarintSize(tag_type);
+  if (tag_type_size + sizeof(value) > buf->size()) {
+    buf->remove_suffix(buf->size());
+    return false;
+  }
+  EncodeRawVarint(tag_type, tag_type_size, buf);
+  for (size_t s = 0; s < sizeof(value); s++) {
+    (*buf)[s] = static_cast<char>(value & 0xff);
+    value >>= 8;
+  }
+  buf->remove_prefix(sizeof(value));
+  return true;
+}
+
+bool Encode32Bit(uint64_t tag, uint32_t value, absl::Span<char> *buf) {
+  const uint64_t tag_type = MakeTagType(tag, WireType::k32Bit);
+  const size_t tag_type_size = VarintSize(tag_type);
+  if (tag_type_size + sizeof(value) > buf->size()) {
+    buf->remove_suffix(buf->size());
+    return false;
+  }
+  EncodeRawVarint(tag_type, tag_type_size, buf);
+  for (size_t s = 0; s < sizeof(value); s++) {
+    (*buf)[s] = static_cast<char>(value & 0xff);
+    value >>= 8;
+  }
+  buf->remove_prefix(sizeof(value));
+  return true;
+}
+
+bool EncodeBytes(uint64_t tag, absl::Span<const char> value,
+                 absl::Span<char> *buf) {
+  const uint64_t tag_type = MakeTagType(tag, WireType::kLengthDelimited);
+  const size_t tag_type_size = VarintSize(tag_type);
+  uint64_t length = value.size();
+  const size_t length_size = VarintSize(length);
+  if (tag_type_size + length_size + value.size() > buf->size()) {
+    buf->remove_suffix(buf->size());
+    return false;
+  }
+  EncodeRawVarint(tag_type, tag_type_size, buf);
+  EncodeRawVarint(length, length_size, buf);
+  memcpy(buf->data(), value.data(), value.size());
+  buf->remove_prefix(value.size());
+  return true;
+}
+
+bool EncodeBytesTruncate(uint64_t tag, absl::Span<const char> value,
+                         absl::Span<char> *buf) {
+  const uint64_t tag_type = MakeTagType(tag, WireType::kLengthDelimited);
+  const size_t tag_type_size = VarintSize(tag_type);
+  uint64_t length = value.size();
+  const size_t length_size =
+      VarintSize(std::min<uint64_t>(length, buf->size()));
+  if (tag_type_size + length_size <= buf->size() &&
+      tag_type_size + length_size + value.size() > buf->size()) {
+    value.remove_suffix(tag_type_size + length_size + value.size() -
+                        buf->size());
+    length = value.size();
+  }
+  if (tag_type_size + length_size + value.size() > buf->size()) {
+    buf->remove_suffix(buf->size());
+    return false;
+  }
+  EncodeRawVarint(tag_type, tag_type_size, buf);
+  EncodeRawVarint(length, length_size, buf);
+  memcpy(buf->data(), value.data(), value.size());
+  buf->remove_prefix(value.size());
+  return true;
+}
+
+ABSL_MUST_USE_RESULT absl::Span<char> EncodeMessageStart(
+    uint64_t tag, uint64_t max_size, absl::Span<char> *buf) {
+  const uint64_t tag_type = MakeTagType(tag, WireType::kLengthDelimited);
+  const size_t tag_type_size = VarintSize(tag_type);
+  max_size = std::min<uint64_t>(max_size, buf->size());
+  const size_t length_size = VarintSize(max_size);
+  if (tag_type_size + length_size > buf->size()) {
+    buf->remove_suffix(buf->size());
+    return absl::Span<char>();
+  }
+  EncodeRawVarint(tag_type, tag_type_size, buf);
+  const absl::Span<char> ret = buf->subspan(0, length_size);
+  EncodeRawVarint(0, length_size, buf);
+  return ret;
+}
+
+void EncodeMessageLength(absl::Span<char> msg, const absl::Span<char> *buf) {
+  if (!msg.data()) return;
+  assert(buf->data() >= msg.data());
+  if (buf->data() < msg.data()) return;
+  EncodeRawVarint(
+      static_cast<uint64_t>(buf->data() - (msg.data() + msg.size())),
+      msg.size(), &msg);
+}
+
+namespace {
+uint64_t DecodeVarint(absl::Span<const char> *buf) {
+  uint64_t value = 0;
+  size_t s = 0;
+  while (s < buf->size()) {
+    value |= static_cast<uint64_t>(static_cast<unsigned char>((*buf)[s]) & 0x7f)
+             << 7 * s;
+    if (!((*buf)[s++] & 0x80)) break;
+  }
+  buf->remove_prefix(s);
+  return value;
+}
+
+uint64_t Decode64Bit(absl::Span<const char> *buf) {
+  uint64_t value = 0;
+  size_t s = 0;
+  while (s < buf->size()) {
+    value |= static_cast<uint64_t>(static_cast<unsigned char>((*buf)[s]))
+             << 8 * s;
+    if (++s == sizeof(value)) break;
+  }
+  buf->remove_prefix(s);
+  return value;
+}
+
+uint32_t Decode32Bit(absl::Span<const char> *buf) {
+  uint32_t value = 0;
+  size_t s = 0;
+  while (s < buf->size()) {
+    value |= static_cast<uint32_t>(static_cast<unsigned char>((*buf)[s]))
+             << 8 * s;
+    if (++s == sizeof(value)) break;
+  }
+  buf->remove_prefix(s);
+  return value;
+}
+}  // namespace
+
+bool ProtoField::DecodeFrom(absl::Span<const char> *data) {
+  if (data->empty()) return false;
+  const uint64_t tag_type = DecodeVarint(data);
+  tag_ = tag_type >> 3;
+  type_ = static_cast<WireType>(tag_type & 0x07);
+  switch (type_) {
+    case WireType::kVarint:
+      value_ = DecodeVarint(data);
+      break;
+    case WireType::k64Bit:
+      value_ = Decode64Bit(data);
+      break;
+    case WireType::kLengthDelimited: {
+      value_ = DecodeVarint(data);
+      data_ = data->subspan(
+          0, static_cast<size_t>(std::min<uint64_t>(value_, data->size())));
+      data->remove_prefix(data_.size());
+      break;
+    }
+    case WireType::k32Bit:
+      value_ = Decode32Bit(data);
+      break;
+  }
+  return true;
+}
+
+}  // namespace log_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/log/internal/proto.h b/absl/log/internal/proto.h
new file mode 100644
index 0000000..c8d14ac
--- /dev/null
+++ b/absl/log/internal/proto.h
@@ -0,0 +1,288 @@
+// 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.
+
+// -----------------------------------------------------------------------------
+// File: internal/proto.h
+// -----------------------------------------------------------------------------
+//
+// Declares functions for serializing and deserializing data to and from memory
+// buffers in protocol buffer wire format.  This library takes no steps to
+// ensure that the encoded data matches with any message specification.
+
+#ifndef ABSL_LOG_INTERNAL_PROTO_H_
+#define ABSL_LOG_INTERNAL_PROTO_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+
+#include "absl/base/attributes.h"
+#include "absl/base/casts.h"
+#include "absl/base/config.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+
+// absl::Span<char> represents a view into the available space in a mutable
+// buffer during encoding.  Encoding functions shrink the span as they go so
+// that the same view can be passed to a series of Encode functions.  If the
+// data do not fit, nothing is encoded, the view is set to size zero (so that
+// all subsequent encode calls fail), and false is returned.  Otherwise true is
+// returned.
+
+// In particular, attempting to encode a series of data into an insufficient
+// buffer has consistent and efficient behavior without any caller-side error
+// checking.  Individual values will be encoded in their entirety or not at all
+// (unless one of the `Truncate` functions is used).  Once a value is omitted
+// because it does not fit, no subsequent values will be encoded to preserve
+// ordering; the decoded sequence will be a prefix of the original sequence.
+
+// There are two ways to encode a message-typed field:
+//
+// * Construct its contents in a separate buffer and use `EncodeBytes` to copy
+//   it into the primary buffer with type, tag, and length.
+// * Use `EncodeMessageStart` to write type and tag fields and reserve space for
+//   the length field, then encode the contents directly into the buffer, then
+//   use `EncodeMessageLength` to write the actual length into the reserved
+//   bytes.  This works fine if the actual length takes fewer bytes to encode
+//   than were reserved, although you don't get your extra bytes back.
+//   This approach will always produce a valid encoding, but your protocol may
+//   require that the whole message field by omitted if the buffer is too small
+//   to contain all desired subfields.  In this case, operate on a copy of the
+//   buffer view and assign back only if everything fit, i.e. if the last
+//   `Encode` call returned true.
+
+// Encodes the specified integer as a varint field and returns true if it fits.
+// Used for int32_t, int64_t, uint32_t, uint64_t, bool, and enum field types.
+// Consumes up to kMaxVarintSize * 2 bytes (20).
+bool EncodeVarint(uint64_t tag, uint64_t value, absl::Span<char> *buf);
+inline bool EncodeVarint(uint64_t tag, int64_t value, absl::Span<char> *buf) {
+  return EncodeVarint(tag, static_cast<uint64_t>(value), buf);
+}
+inline bool EncodeVarint(uint64_t tag, uint32_t value, absl::Span<char> *buf) {
+  return EncodeVarint(tag, static_cast<uint64_t>(value), buf);
+}
+inline bool EncodeVarint(uint64_t tag, int32_t value, absl::Span<char> *buf) {
+  return EncodeVarint(tag, static_cast<uint64_t>(value), buf);
+}
+
+// Encodes the specified integer as a varint field using ZigZag encoding and
+// returns true if it fits.
+// Used for sint32 and sint64 field types.
+// Consumes up to kMaxVarintSize * 2 bytes (20).
+inline bool EncodeVarintZigZag(uint64_t tag, int64_t value,
+                               absl::Span<char> *buf) {
+  if (value < 0)
+    return EncodeVarint(tag, 2 * static_cast<uint64_t>(-(value + 1)) + 1, buf);
+  return EncodeVarint(tag, 2 * static_cast<uint64_t>(value), buf);
+}
+
+// Encodes the specified integer as a 64-bit field and returns true if it fits.
+// Used for fixed64 and sfixed64 field types.
+// Consumes up to kMaxVarintSize + 8 bytes (18).
+bool Encode64Bit(uint64_t tag, uint64_t value, absl::Span<char> *buf);
+inline bool Encode64Bit(uint64_t tag, int64_t value, absl::Span<char> *buf) {
+  return Encode64Bit(tag, static_cast<uint64_t>(value), buf);
+}
+inline bool Encode64Bit(uint64_t tag, uint32_t value, absl::Span<char> *buf) {
+  return Encode64Bit(tag, static_cast<uint64_t>(value), buf);
+}
+inline bool Encode64Bit(uint64_t tag, int32_t value, absl::Span<char> *buf) {
+  return Encode64Bit(tag, static_cast<uint64_t>(value), buf);
+}
+
+// Encodes the specified double as a 64-bit field and returns true if it fits.
+// Used for double field type.
+// Consumes up to kMaxVarintSize + 8 bytes (18).
+inline bool EncodeDouble(uint64_t tag, double value, absl::Span<char> *buf) {
+  return Encode64Bit(tag, absl::bit_cast<uint64_t>(value), buf);
+}
+
+// Encodes the specified integer as a 32-bit field and returns true if it fits.
+// Used for fixed32 and sfixed32 field types.
+// Consumes up to kMaxVarintSize + 4 bytes (14).
+bool Encode32Bit(uint64_t tag, uint32_t value, absl::Span<char> *buf);
+inline bool Encode32Bit(uint64_t tag, int32_t value, absl::Span<char> *buf) {
+  return Encode32Bit(tag, static_cast<uint32_t>(value), buf);
+}
+
+// Encodes the specified float as a 32-bit field and returns true if it fits.
+// Used for float field type.
+// Consumes up to kMaxVarintSize + 4 bytes (14).
+inline bool EncodeFloat(uint64_t tag, float value, absl::Span<char> *buf) {
+  return Encode32Bit(tag, absl::bit_cast<uint32_t>(value), buf);
+}
+
+// Encodes the specified bytes as a length-delimited field and returns true if
+// they fit.
+// Used for string, bytes, message, and packed-repeated field type.
+// Consumes up to kMaxVarintSize * 2 + value.size() bytes (20 + value.size()).
+bool EncodeBytes(uint64_t tag, absl::Span<const char> value,
+                 absl::Span<char> *buf);
+
+// Encodes as many of the specified bytes as will fit as a length-delimited
+// field and returns true as long as the field header (`tag_type` and `length`)
+// fits.
+// Used for string, bytes, message, and packed-repeated field type.
+// Consumes up to kMaxVarintSize * 2 + value.size() bytes (20 + value.size()).
+bool EncodeBytesTruncate(uint64_t tag, absl::Span<const char> value,
+                         absl::Span<char> *buf);
+
+// Encodes the specified string as a length-delimited field and returns true if
+// it fits.
+// Used for string, bytes, message, and packed-repeated field type.
+// Consumes up to kMaxVarintSize * 2 + value.size() bytes (20 + value.size()).
+inline bool EncodeString(uint64_t tag, absl::string_view value,
+                         absl::Span<char> *buf) {
+  return EncodeBytes(tag, value, buf);
+}
+
+// Encodes as much of the specified string as will fit as a length-delimited
+// field and returns true as long as the field header (`tag_type` and `length`)
+// fits.
+// Used for string, bytes, message, and packed-repeated field type.
+// Consumes up to kMaxVarintSize * 2 + value.size() bytes (20 + value.size()).
+inline bool EncodeStringTruncate(uint64_t tag, absl::string_view value,
+                                 absl::Span<char> *buf) {
+  return EncodeBytesTruncate(tag, value, buf);
+}
+
+// Encodes the header for a length-delimited field containing up to `max_size`
+// bytes or the number remaining in the buffer, whichever is less.  If the
+// header fits, a non-nullptr `Span` is returned; this must be passed to
+// `EncodeMessageLength` after all contents are encoded to finalize the length
+// field.  If the header does not fit, a nullptr `Span` is returned which is
+// safe to pass to `EncodeMessageLength` but need not be.
+// Used for string, bytes, message, and packed-repeated field type.
+// Consumes up to kMaxVarintSize * 2 bytes (20).
+ABSL_MUST_USE_RESULT absl::Span<char> EncodeMessageStart(uint64_t tag,
+                                                         uint64_t max_size,
+                                                         absl::Span<char> *buf);
+
+// Finalizes the length field in `msg` so that it encompasses all data encoded
+// since the call to `EncodeMessageStart` which returned `msg`.  Does nothing if
+// `msg` is a `nullptr` `Span`.
+void EncodeMessageLength(absl::Span<char> msg, const absl::Span<char> *buf);
+
+enum class WireType : uint64_t {
+  kVarint = 0,
+  k64Bit = 1,
+  kLengthDelimited = 2,
+  k32Bit = 5,
+};
+
+constexpr size_t VarintSize(uint64_t value) {
+  return value < 128 ? 1 : 1 + VarintSize(value >> 7);
+}
+constexpr size_t MinVarintSize() {
+  return VarintSize((std::numeric_limits<uint64_t>::min)());
+}
+constexpr size_t MaxVarintSize() {
+  return VarintSize((std::numeric_limits<uint64_t>::max)());
+}
+
+constexpr uint64_t MaxVarintForSize(size_t size) {
+  return size >= 10 ? (std::numeric_limits<uint64_t>::max)()
+                    : (static_cast<uint64_t>(1) << size * 7) - 1;
+}
+
+// `BufferSizeFor` returns a number of bytes guaranteed to be sufficient to
+// store encoded fields of the specified WireTypes regardless of tag numbers and
+// data values.  This only makes sense for `WireType::kLengthDelimited` if you
+// add in the length of the contents yourself, e.g. for string and bytes fields
+// by adding the lengths of any encoded strings to the return value or for
+// submessage fields by enumerating the fields you may encode into their
+// contents.
+constexpr size_t BufferSizeFor() { return 0; }
+template <typename... T>
+constexpr size_t BufferSizeFor(WireType type, T... tail) {
+  // tag_type + data + ...
+  return MaxVarintSize() +
+         (type == WireType::kVarint ? MaxVarintSize() :              //
+              type == WireType::k64Bit ? 8 :                         //
+                  type == WireType::k32Bit ? 4 : MaxVarintSize()) +  //
+         BufferSizeFor(tail...);
+}
+
+// absl::Span<const char> represents a view into the un-processed space in a
+// buffer during decoding.  Decoding functions shrink the span as they go so
+// that the same view can be decoded iteratively until all data are processed.
+// In general, if the buffer is exhausted but additional bytes are expected by
+// the decoder, it will return values as if the additional bytes were zeros.
+// Length-delimited fields are an exception - if the encoded length field
+// indicates more data bytes than are available in the buffer, the `bytes_value`
+// and `string_value` accessors will return truncated views.
+
+class ProtoField final {
+ public:
+  // Consumes bytes from `data` and returns true if there were any bytes to
+  // decode.
+  bool DecodeFrom(absl::Span<const char> *data);
+  uint64_t tag() const { return tag_; }
+  WireType type() const { return type_; }
+
+  // These value accessors will return nonsense if the data were not encoded in
+  // the corresponding wiretype from the corresponding C++ (or other language)
+  // type.
+
+  double double_value() const { return absl::bit_cast<double>(value_); }
+  float float_value() const {
+    return absl::bit_cast<float>(static_cast<uint32_t>(value_));
+  }
+  int32_t int32_value() const { return static_cast<int32_t>(value_); }
+  int64_t int64_value() const { return static_cast<int64_t>(value_); }
+  int32_t sint32_value() const {
+    if (value_ % 2) return static_cast<int32_t>(0 - ((value_ - 1) / 2) - 1);
+    return static_cast<int32_t>(value_ / 2);
+  }
+  int64_t sint64_value() const {
+    if (value_ % 2) return 0 - ((value_ - 1) / 2) - 1;
+    return value_ / 2;
+  }
+  uint32_t uint32_value() const { return static_cast<uint32_t>(value_); }
+  uint64_t uint64_value() const { return value_; }
+  bool bool_value() const { return value_ != 0; }
+  // To decode an enum, call int32_value() and cast to the appropriate type.
+  // Note that the official C++ proto compiler treats enum fields with values
+  // that do not correspond to a defined enumerator as unknown fields.
+
+  // To decode fields within a submessage field, call
+  // `DecodeNextField(field.BytesValue())`.
+  absl::Span<const char> bytes_value() const { return data_; }
+  absl::string_view string_value() const {
+    const auto data = bytes_value();
+    return absl::string_view(data.data(), data.size());
+  }
+  // Returns the encoded length of a length-delimited field.  This equals
+  // `bytes_value().size()` except when the latter has been truncated due to
+  // buffer underrun.
+  uint64_t encoded_length() const { return value_; }
+
+ private:
+  uint64_t tag_;
+  WireType type_;
+  // For `kTypeVarint`, `kType64Bit`, and `kType32Bit`, holds the decoded value.
+  // For `kTypeLengthDelimited`, holds the decoded length.
+  uint64_t value_;
+  absl::Span<const char> data_;
+};
+
+}  // namespace log_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_LOG_INTERNAL_PROTO_H_
diff --git a/absl/log/internal/strip.h b/absl/log/internal/strip.h
index 848c386..adc86ff 100644
--- a/absl/log/internal/strip.h
+++ b/absl/log/internal/strip.h
@@ -42,15 +42,15 @@
 #define ABSL_LOG_INTERNAL_QCHECK(failure_message) \
   ABSL_LOGGING_INTERNAL_LOG_QFATAL
 #else  // !defined(STRIP_LOG) || !STRIP_LOG
-#define ABSL_LOGGING_INTERNAL_LOG_INFO                 \
-  ::absl::log_internal::LogMessage(__FILE__, __LINE__, \
-                                   ::absl::LogSeverity::kInfo)
-#define ABSL_LOGGING_INTERNAL_LOG_WARNING              \
-  ::absl::log_internal::LogMessage(__FILE__, __LINE__, \
-                                   ::absl::LogSeverity::kWarning)
-#define ABSL_LOGGING_INTERNAL_LOG_ERROR                \
-  ::absl::log_internal::LogMessage(__FILE__, __LINE__, \
-                                   ::absl::LogSeverity::kError)
+#define ABSL_LOGGING_INTERNAL_LOG_INFO \
+  ::absl::log_internal::LogMessage(    \
+      __FILE__, __LINE__, ::absl::log_internal::LogMessage::InfoTag{})
+#define ABSL_LOGGING_INTERNAL_LOG_WARNING \
+  ::absl::log_internal::LogMessage(       \
+      __FILE__, __LINE__, ::absl::log_internal::LogMessage::WarningTag{})
+#define ABSL_LOGGING_INTERNAL_LOG_ERROR \
+  ::absl::log_internal::LogMessage(     \
+      __FILE__, __LINE__, ::absl::log_internal::LogMessage::ErrorTag{})
 #define ABSL_LOGGING_INTERNAL_LOG_FATAL \
   ::absl::log_internal::LogMessageFatal(__FILE__, __LINE__)
 #define ABSL_LOGGING_INTERNAL_LOG_QFATAL \
diff --git a/absl/log/internal/structured.h b/absl/log/internal/structured.h
new file mode 100644
index 0000000..5223dbc
--- /dev/null
+++ b/absl/log/internal/structured.h
@@ -0,0 +1,58 @@
+// Copyright 2022 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: log/internal/structured.h
+// -----------------------------------------------------------------------------
+
+#ifndef ABSL_LOG_INTERNAL_STRUCTURED_H_
+#define ABSL_LOG_INTERNAL_STRUCTURED_H_
+
+#include <ostream>
+
+#include "absl/base/config.h"
+#include "absl/log/internal/log_message.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+
+class ABSL_MUST_USE_RESULT AsLiteralImpl final {
+ public:
+  explicit AsLiteralImpl(absl::string_view str) : str_(str) {}
+  AsLiteralImpl(const AsLiteralImpl&) = default;
+  AsLiteralImpl& operator=(const AsLiteralImpl&) = default;
+
+ private:
+  absl::string_view str_;
+
+  friend std::ostream& operator<<(std::ostream& os, AsLiteralImpl as_literal) {
+    return os << as_literal.str_;
+  }
+  void AddToMessage(log_internal::LogMessage& m) {
+    m.CopyToEncodedBuffer<log_internal::LogMessage::StringType::kLiteral>(str_);
+  }
+  friend log_internal::LogMessage& operator<<(log_internal::LogMessage& m,
+                                              AsLiteralImpl as_literal) {
+    as_literal.AddToMessage(m);
+    return m;
+  }
+};
+
+}  // namespace log_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_LOG_INTERNAL_STRUCTURED_H_
diff --git a/absl/log/internal/test_actions.cc b/absl/log/internal/test_actions.cc
index 41ca988..bdfd637 100644
--- a/absl/log/internal/test_actions.cc
+++ b/absl/log/internal/test_actions.cc
@@ -18,10 +18,12 @@
 #include <cassert>
 #include <iostream>
 #include <string>
+#include <type_traits>
 
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
-#include "absl/log/internal/config.h"
 #include "absl/strings/escaping.h"
+#include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
 #include "absl/time/time.h"
 
@@ -30,37 +32,42 @@
 namespace log_internal {
 
 void WriteToStderrWithFilename::operator()(const absl::LogEntry& entry) const {
-  std::cerr << message << " (file: " << entry.source_filename() << ")"
-            << std::endl;
+  std::cerr << message << " (file: " << entry.source_filename() << ")\n";
 }
 
 void WriteEntryToStderr::operator()(const absl::LogEntry& entry) const {
-  if (!message.empty()) std::cerr << message << std::endl;
+  if (!message.empty()) std::cerr << message << "\n";
 
-  std::cerr << "LogEntry{\n"
-            << "  source_filename: \""
-            << absl::CHexEscape(entry.source_filename()) << "\"\n"
-            << "  source_basename: \""
-            << absl::CHexEscape(entry.source_basename()) << "\"\n"
-            << "  source_line: " << entry.source_line() << "\n"
-            << "  prefix: " << (entry.prefix() ? "true\n" : "false\n")
-            << "  log_severity: " << entry.log_severity() << "\n"
-            << "  timestamp: " << entry.timestamp() << "\n"
-            << "  text_message: \"" << absl::CHexEscape(entry.text_message())
-            << "\"\n  verbosity: " << entry.verbosity() << "\n"
-            << "}" << std::endl;
+  const std::string source_filename = absl::CHexEscape(entry.source_filename());
+  const std::string source_basename = absl::CHexEscape(entry.source_basename());
+  const std::string text_message = absl::CHexEscape(entry.text_message());
+  const std::string encoded_message = absl::CHexEscape(entry.encoded_message());
+  std::string encoded_message_str;
+  std::cerr << "LogEntry{\n"                                               //
+            << "  source_filename: \"" << source_filename << "\"\n"        //
+            << "  source_basename: \"" << source_basename << "\"\n"        //
+            << "  source_line: " << entry.source_line() << "\n"            //
+            << "  prefix: " << (entry.prefix() ? "true\n" : "false\n")     //
+            << "  log_severity: " << entry.log_severity() << "\n"          //
+            << "  timestamp: " << entry.timestamp() << "\n"                //
+            << "  text_message: \"" << text_message << "\"\n"              //
+            << "  verbosity: " << entry.verbosity() << "\n"                //
+            << "  encoded_message (raw): \"" << encoded_message << "\"\n"  //
+            << encoded_message_str                                         //
+            << "}\n";
 }
 
 void WriteEntryToStderr::operator()(absl::LogSeverity severity,
                                     absl::string_view filename,
                                     absl::string_view log_message) const {
-  if (!message.empty()) std::cerr << message << std::endl;
-
-  std::cerr << "LogEntry{\n"
-            << "  source_filename: \"" << absl::CHexEscape(filename) << "\"\n"
-            << "  log_severity: " << severity << "\n"
-            << "  text_message: \"" << absl::CHexEscape(log_message) << "}"
-            << std::endl;
+  if (!message.empty()) std::cerr << message << "\n";
+  const std::string source_filename = absl::CHexEscape(filename);
+  const std::string text_message = absl::CHexEscape(log_message);
+  std::cerr << "LogEntry{\n"                                         //
+            << "  source_filename: \"" << source_filename << "\"\n"  //
+            << "  log_severity: " << severity << "\n"                //
+            << "  text_message: \"" << text_message << "\"\n"        //
+            << "}\n";
 }
 
 }  // namespace log_internal
diff --git a/absl/log/internal/test_helpers.cc b/absl/log/internal/test_helpers.cc
index bff5cc1..bfcc967 100644
--- a/absl/log/internal/test_helpers.cc
+++ b/absl/log/internal/test_helpers.cc
@@ -46,7 +46,7 @@
   // Depending on NDEBUG and (configuration?) MSVC's abort either results
   // in error code 3 (SIGABRT) or error code 0x80000003 (breakpoint
   // triggered).
-  return ::testing::ExitedWithCode(3)(exit_status & ~0x80000000);
+  return ::testing::ExitedWithCode(3)(exit_status & 0x7fffffff);
 #elif defined(__Fuchsia__)
   // The Fuchsia death test implementation kill()'s the process when it detects
   // an exception, so it should exit with the corresponding code. See
@@ -68,7 +68,7 @@
 #endif
 
 // -----------------------------------------------------------------------------
-// Helper for Log inititalization in test
+// Helper for Log initialization in test
 // -----------------------------------------------------------------------------
 
 void LogTestEnvironment::SetUp() {
diff --git a/absl/log/internal/test_helpers.h b/absl/log/internal/test_helpers.h
index fd06e29..714bc7b 100644
--- a/absl/log/internal/test_helpers.h
+++ b/absl/log/internal/test_helpers.h
@@ -54,7 +54,7 @@
 #endif
 
 // -----------------------------------------------------------------------------
-// Helper for Log inititalization in test
+// Helper for Log initialization in test
 // -----------------------------------------------------------------------------
 
 class LogTestEnvironment : public ::testing::Environment {
diff --git a/absl/log/internal/test_matchers.cc b/absl/log/internal/test_matchers.cc
index ee32617..8c6515c 100644
--- a/absl/log/internal/test_matchers.cc
+++ b/absl/log/internal/test_matchers.cc
@@ -15,14 +15,16 @@
 
 #include "absl/log/internal/test_matchers.h"
 
+#include <ostream>
 #include <sstream>
 #include <string>
+#include <type_traits>
 #include <utility>
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
-#include "absl/log/internal/config.h"
 #include "absl/log/internal/test_helpers.h"
 #include "absl/strings/string_view.h"
 #include "absl/time/clock.h"
@@ -31,100 +33,50 @@
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace log_internal {
+namespace {
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::Ge;
+using ::testing::HasSubstr;
+using ::testing::MakeMatcher;
+using ::testing::Matcher;
+using ::testing::MatcherInterface;
+using ::testing::MatchResultListener;
+using ::testing::Not;
+using ::testing::Property;
+using ::testing::ResultOf;
+using ::testing::Truly;
 
-::testing::Matcher<const absl::LogEntry&> SourceFilename(
-    const ::testing::Matcher<absl::string_view>& source_filename) {
-  return Property("source_filename", &absl::LogEntry::source_filename,
-                  source_filename);
-}
+class AsStringImpl final
+    : public MatcherInterface<absl::string_view> {
+ public:
+  explicit AsStringImpl(
+      const Matcher<const std::string&>& str_matcher)
+      : str_matcher_(str_matcher) {}
+  bool MatchAndExplain(
+      absl::string_view actual,
+      MatchResultListener* listener) const override {
+    return str_matcher_.MatchAndExplain(std::string(actual), listener);
+  }
+  void DescribeTo(std::ostream* os) const override {
+    return str_matcher_.DescribeTo(os);
+  }
 
-::testing::Matcher<const absl::LogEntry&> SourceBasename(
-    const ::testing::Matcher<absl::string_view>& source_basename) {
-  return Property("source_basename", &absl::LogEntry::source_basename,
-                  source_basename);
-}
+  void DescribeNegationTo(std::ostream* os) const override {
+    return str_matcher_.DescribeNegationTo(os);
+  }
 
-::testing::Matcher<const absl::LogEntry&> SourceLine(
-    const ::testing::Matcher<int>& source_line) {
-  return Property("source_line", &absl::LogEntry::source_line, source_line);
-}
-
-::testing::Matcher<const absl::LogEntry&> Prefix(
-    const ::testing::Matcher<bool>& prefix) {
-  return Property("prefix", &absl::LogEntry::prefix, prefix);
-}
-
-::testing::Matcher<const absl::LogEntry&> LogSeverity(
-    const ::testing::Matcher<absl::LogSeverity>& log_severity) {
-  return Property("log_severity", &absl::LogEntry::log_severity, log_severity);
-}
-
-::testing::Matcher<const absl::LogEntry&> Timestamp(
-    const ::testing::Matcher<absl::Time>& timestamp) {
-  return Property("timestamp", &absl::LogEntry::timestamp, timestamp);
-}
-
-::testing::Matcher<const absl::LogEntry&> TimestampInMatchWindow() {
-  return Property("timestamp", &absl::LogEntry::timestamp,
-                  ::testing::AllOf(::testing::Ge(absl::Now()),
-                                   ::testing::Truly([](absl::Time arg) {
-                                     return arg <= absl::Now();
-                                   })));
-}
-
-::testing::Matcher<const absl::LogEntry&> ThreadID(
-    const ::testing::Matcher<absl::LogEntry::tid_t>& tid) {
-  return Property("tid", &absl::LogEntry::tid, tid);
-}
-
-::testing::Matcher<const absl::LogEntry&> TextMessageWithPrefixAndNewline(
-    const ::testing::Matcher<absl::string_view>&
-        text_message_with_prefix_and_newline) {
-  return Property("text_message_with_prefix_and_newline",
-                  &absl::LogEntry::text_message_with_prefix_and_newline,
-                  text_message_with_prefix_and_newline);
-}
-
-::testing::Matcher<const absl::LogEntry&> TextMessageWithPrefix(
-    const ::testing::Matcher<absl::string_view>& text_message_with_prefix) {
-  return Property("text_message_with_prefix",
-                  &absl::LogEntry::text_message_with_prefix,
-                  text_message_with_prefix);
-}
-
-::testing::Matcher<const absl::LogEntry&> TextMessage(
-    const ::testing::Matcher<absl::string_view>& text_message) {
-  return Property("text_message", &absl::LogEntry::text_message, text_message);
-}
-
-::testing::Matcher<const absl::LogEntry&> TextPrefix(
-    const ::testing::Matcher<absl::string_view>& text_prefix) {
-  return ResultOf(
-      [](const absl::LogEntry& entry) {
-        absl::string_view msg = entry.text_message_with_prefix();
-        msg.remove_suffix(entry.text_message().size());
-        return msg;
-      },
-      text_prefix);
-}
-
-::testing::Matcher<const absl::LogEntry&> Verbosity(
-    const ::testing::Matcher<int>& verbosity) {
-  return Property("verbosity", &absl::LogEntry::verbosity, verbosity);
-}
-
-::testing::Matcher<const absl::LogEntry&> Stacktrace(
-    const ::testing::Matcher<absl::string_view>& stacktrace) {
-  return Property("stacktrace", &absl::LogEntry::stacktrace, stacktrace);
-}
+ private:
+  const Matcher<const std::string&> str_matcher_;
+};
 
 class MatchesOstreamImpl final
-    : public ::testing::MatcherInterface<absl::string_view> {
+    : public MatcherInterface<absl::string_view> {
  public:
   explicit MatchesOstreamImpl(std::string expected)
       : expected_(std::move(expected)) {}
   bool MatchAndExplain(absl::string_view actual,
-                       ::testing::MatchResultListener*) const override {
+                       MatchResultListener*) const override {
     return actual == expected_;
   }
   void DescribeTo(std::ostream* os) const override {
@@ -140,9 +92,106 @@
  private:
   const std::string expected_;
 };
-::testing::Matcher<absl::string_view> MatchesOstream(
+}  // namespace
+
+Matcher<absl::string_view> AsString(
+    const Matcher<const std::string&>& str_matcher) {
+  return MakeMatcher(new AsStringImpl(str_matcher));
+}
+
+Matcher<const absl::LogEntry&> SourceFilename(
+    const Matcher<absl::string_view>& source_filename) {
+  return Property("source_filename", &absl::LogEntry::source_filename,
+                  source_filename);
+}
+
+Matcher<const absl::LogEntry&> SourceBasename(
+    const Matcher<absl::string_view>& source_basename) {
+  return Property("source_basename", &absl::LogEntry::source_basename,
+                  source_basename);
+}
+
+Matcher<const absl::LogEntry&> SourceLine(
+    const Matcher<int>& source_line) {
+  return Property("source_line", &absl::LogEntry::source_line, source_line);
+}
+
+Matcher<const absl::LogEntry&> Prefix(
+    const Matcher<bool>& prefix) {
+  return Property("prefix", &absl::LogEntry::prefix, prefix);
+}
+
+Matcher<const absl::LogEntry&> LogSeverity(
+    const Matcher<absl::LogSeverity>& log_severity) {
+  return Property("log_severity", &absl::LogEntry::log_severity, log_severity);
+}
+
+Matcher<const absl::LogEntry&> Timestamp(
+    const Matcher<absl::Time>& timestamp) {
+  return Property("timestamp", &absl::LogEntry::timestamp, timestamp);
+}
+
+Matcher<const absl::LogEntry&> TimestampInMatchWindow() {
+  return Property("timestamp", &absl::LogEntry::timestamp,
+                  AllOf(Ge(absl::Now()), Truly([](absl::Time arg) {
+                          return arg <= absl::Now();
+                        })));
+}
+
+Matcher<const absl::LogEntry&> ThreadID(
+    const Matcher<absl::LogEntry::tid_t>& tid) {
+  return Property("tid", &absl::LogEntry::tid, tid);
+}
+
+Matcher<const absl::LogEntry&> TextMessageWithPrefixAndNewline(
+    const Matcher<absl::string_view>&
+        text_message_with_prefix_and_newline) {
+  return Property("text_message_with_prefix_and_newline",
+                  &absl::LogEntry::text_message_with_prefix_and_newline,
+                  text_message_with_prefix_and_newline);
+}
+
+Matcher<const absl::LogEntry&> TextMessageWithPrefix(
+    const Matcher<absl::string_view>& text_message_with_prefix) {
+  return Property("text_message_with_prefix",
+                  &absl::LogEntry::text_message_with_prefix,
+                  text_message_with_prefix);
+}
+
+Matcher<const absl::LogEntry&> TextMessage(
+    const Matcher<absl::string_view>& text_message) {
+  return Property("text_message", &absl::LogEntry::text_message, text_message);
+}
+
+Matcher<const absl::LogEntry&> TextPrefix(
+    const Matcher<absl::string_view>& text_prefix) {
+  return ResultOf(
+      [](const absl::LogEntry& entry) {
+        absl::string_view msg = entry.text_message_with_prefix();
+        msg.remove_suffix(entry.text_message().size());
+        return msg;
+      },
+      text_prefix);
+}
+Matcher<const absl::LogEntry&> RawEncodedMessage(
+    const Matcher<absl::string_view>& raw_encoded_message) {
+  return Property("encoded_message", &absl::LogEntry::encoded_message,
+                  raw_encoded_message);
+}
+
+Matcher<const absl::LogEntry&> Verbosity(
+    const Matcher<int>& verbosity) {
+  return Property("verbosity", &absl::LogEntry::verbosity, verbosity);
+}
+
+Matcher<const absl::LogEntry&> Stacktrace(
+    const Matcher<absl::string_view>& stacktrace) {
+  return Property("stacktrace", &absl::LogEntry::stacktrace, stacktrace);
+}
+
+Matcher<absl::string_view> MatchesOstream(
     const std::ostringstream& stream) {
-  return ::testing::MakeMatcher(new MatchesOstreamImpl(stream.str()));
+  return MakeMatcher(new MatchesOstreamImpl(stream.str()));
 }
 
 // We need to validate what is and isn't logged as the process dies due to
@@ -151,16 +200,16 @@
 // Instead, we use the mock actions `DeathTestExpectedLogging` and
 // `DeathTestUnexpectedLogging` to write specific phrases to `stderr` that we
 // can validate in the parent process using this matcher.
-::testing::Matcher<const std::string&> DeathTestValidateExpectations() {
+Matcher<const std::string&> DeathTestValidateExpectations() {
   if (log_internal::LoggingEnabledAt(absl::LogSeverity::kFatal)) {
-    return ::testing::Matcher<const std::string&>(::testing::AllOf(
-        ::testing::HasSubstr("Mock received expected entry"),
-        Not(::testing::HasSubstr("Mock received unexpected entry"))));
+    return Matcher<const std::string&>(
+        AllOf(HasSubstr("Mock received expected entry"),
+              Not(HasSubstr("Mock received unexpected entry"))));
   }
   // If `FATAL` logging is disabled, neither message should have been written.
-  return ::testing::Matcher<const std::string&>(::testing::AllOf(
-      Not(::testing::HasSubstr("Mock received expected entry")),
-      Not(::testing::HasSubstr("Mock received unexpected entry"))));
+  return Matcher<const std::string&>(
+      AllOf(Not(HasSubstr("Mock received expected entry")),
+            Not(HasSubstr("Mock received unexpected entry"))));
 }
 
 }  // namespace log_internal
diff --git a/absl/log/internal/test_matchers.h b/absl/log/internal/test_matchers.h
index b8179cc..fc653a9 100644
--- a/absl/log/internal/test_matchers.h
+++ b/absl/log/internal/test_matchers.h
@@ -30,7 +30,6 @@
 #include "gtest/gtest.h"
 #include "absl/base/config.h"
 #include "absl/base/log_severity.h"
-#include "absl/log/internal/config.h"
 #include "absl/log/internal/test_helpers.h"
 #include "absl/log/log_entry.h"
 #include "absl/strings/string_view.h"
@@ -39,6 +38,10 @@
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace log_internal {
+// In some configurations, Googletest's string matchers (e.g.
+// `::testing::EndsWith`) need help to match `absl::string_view`.
+::testing::Matcher<absl::string_view> AsString(
+    const ::testing::Matcher<const std::string&>& str_matcher);
 
 // These matchers correspond to the components of `absl::LogEntry`.
 ::testing::Matcher<const absl::LogEntry&> SourceFilename(
@@ -80,7 +83,8 @@
     const std::ostringstream& stream);
 ::testing::Matcher<const std::string&> DeathTestValidateExpectations();
 
-// This feature coming soon =).
+::testing::Matcher<const absl::LogEntry&> RawEncodedMessage(
+    const ::testing::Matcher<absl::string_view>& raw_encoded_message);
 #define ENCODED_MESSAGE(message_matcher) ::testing::_
 
 }  // namespace log_internal
diff --git a/absl/log/log.h b/absl/log/log.h
index 13c4938..e060a0b 100644
--- a/absl/log/log.h
+++ b/absl/log/log.h
@@ -132,10 +132,45 @@
 // as they would be if streamed into a `std::ostream`, however it should be
 // noted that their actual type is unspecified.
 //
-// To implement a custom formatting operator for a type you own, define
+// To implement a custom formatting operator for a type you own, there are two
+// options: `AbslStringify()` or `std::ostream& operator<<(std::ostream&, ...)`.
+// It is recommended that users make their types loggable through
+// `AbslStringify()` as it is a universal stringification extension that also
+// enables `absl::StrFormat` and `absl::StrCat` support. If both
+// `AbslStringify()` and `std::ostream& operator<<(std::ostream&, ...)` are
+// defined, `AbslStringify()` will be used.
+//
+// To use the `AbslStringify()` API, define a friend function template in your
+// type's namespace with the following signature:
+//
+//   template <typename Sink>
+//   void AbslStringify(Sink& sink, const UserDefinedType& value);
+//
+// `Sink` has the same interface as `absl::FormatSink`, but without
+// `PutPaddedString()`.
+//
+// Example:
+//
+//   struct Point {
+//     template <typename Sink>
+//     friend void AbslStringify(Sink& sink, const Point& p) {
+//       absl::Format(&sink, "(%v, %v)", p.x, p.y);
+//     }
+//
+//     int x;
+//     int y;
+//   };
+//
+// To use `std::ostream& operator<<(std::ostream&, ...)`, define
 // `std::ostream& operator<<(std::ostream&, ...)` in your type's namespace (for
 // ADL) just as you would to stream it to `std::cout`.
 //
+// Currently `AbslStringify()` ignores output manipulators but this is not
+// guaranteed behavior and may be subject to change in the future. If you would
+// like guaranteed behavior regarding output manipulators, please use
+// `std::ostream& operator<<(std::ostream&, ...)` to make custom types loggable
+// instead.
+//
 // Those macros that support streaming honor output manipulators and `fmtflag`
 // changes that output data (e.g. `std::ends`) or control formatting of data
 // (e.g. `std::hex` and `std::fixed`), however flushing such a stream is
@@ -152,9 +187,7 @@
 #ifndef ABSL_LOG_LOG_H_
 #define ABSL_LOG_LOG_H_
 
-#include "absl/log/internal/conditions.h"
-#include "absl/log/internal/log_message.h"
-#include "absl/log/internal/strip.h"
+#include "absl/log/internal/log_impl.h"
 
 // LOG()
 //
@@ -163,59 +196,29 @@
 // Example:
 //
 //   LOG(INFO) << "Found " << num_cookies << " cookies";
-#define LOG(severity)                                     \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, true) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
+#define LOG(severity) ABSL_LOG_IMPL(_##severity)
 
 // PLOG()
 //
 // `PLOG` behaves like `LOG` except that a description of the current state of
 // `errno` is appended to the streamed message.
-#define PLOG(severity)                                      \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, true)   \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() \
-          .WithPerror()
+#define PLOG(severity) ABSL_PLOG_IMPL(_##severity)
 
 // DLOG()
 //
 // `DLOG` behaves like `LOG` in debug mode (i.e. `#ifndef NDEBUG`).  Otherwise
 // it compiles away and does nothing.  Note that `DLOG(FATAL)` does not
 // terminate the program if `NDEBUG` is defined.
-#ifndef NDEBUG
-#define DLOG(severity)                                    \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, true) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-#else
-#define DLOG(severity)                                     \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, false) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-#endif
+#define DLOG(severity) ABSL_DLOG_IMPL(_##severity)
 
 // `LOG_IF` and friends add a second argument which specifies a condition.  If
 // the condition is false, nothing is logged.
 // Example:
 //
 //   LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
-//
-// There is no `VLOG_IF` because the order of evaluation of the arguments is
-// ambiguous and the alternate spelling with an `if`-statement is trivial.
-#define LOG_IF(severity, condition)                            \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, condition) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-#define PLOG_IF(severity, condition)                           \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, condition) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()    \
-          .WithPerror()
-
-#ifndef NDEBUG
-#define DLOG_IF(severity, condition)                           \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, condition) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-#else
-#define DLOG_IF(severity, condition)                                      \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, false && (condition)) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-#endif
+#define LOG_IF(severity, condition) ABSL_LOG_IF_IMPL(_##severity, condition)
+#define PLOG_IF(severity, condition) ABSL_PLOG_IF_IMPL(_##severity, condition)
+#define DLOG_IF(severity, condition) ABSL_DLOG_IF_IMPL(_##severity, condition)
 
 // LOG_EVERY_N
 //
@@ -228,27 +231,21 @@
 //
 //   LOG_EVERY_N(WARNING, 1000) << "Got a packet with a bad CRC (" << COUNTER
 //                              << " total)";
-#define LOG_EVERY_N(severity, n)                                    \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(EveryN, n) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
+#define LOG_EVERY_N(severity, n) ABSL_LOG_EVERY_N_IMPL(_##severity, n)
 
 // LOG_FIRST_N
 //
 // `LOG_FIRST_N` behaves like `LOG_EVERY_N` except that the specified message is
 // logged when the counter's value is less than `n`.  `LOG_FIRST_N` is
 // thread-safe.
-#define LOG_FIRST_N(severity, n)                                    \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(FirstN, n) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
+#define LOG_FIRST_N(severity, n) ABSL_LOG_FIRST_N_IMPL(_##severity, n)
 
 // LOG_EVERY_POW_2
 //
 // `LOG_EVERY_POW_2` behaves like `LOG_EVERY_N` except that the specified
 // message is logged when the counter's value is a power of 2.
 // `LOG_EVERY_POW_2` is thread-safe.
-#define LOG_EVERY_POW_2(severity)                                   \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(EveryPow2) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
+#define LOG_EVERY_POW_2(severity) ABSL_LOG_EVERY_POW_2_IMPL(_##severity)
 
 // LOG_EVERY_N_SEC
 //
@@ -259,100 +256,20 @@
 // Example:
 //
 //   LOG_EVERY_N_SEC(INFO, 2.5) << "Got " << COUNTER << " cookies so far";
-#define LOG_EVERY_N_SEC(severity, n_seconds)                                   \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(EveryNSec, n_seconds) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
+#define LOG_EVERY_N_SEC(severity, n_seconds) \
+  ABSL_LOG_EVERY_N_SEC_IMPL(_##severity, n_seconds)
 
-#define PLOG_EVERY_N(severity, n)                                   \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(EveryN, n) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()         \
-          .WithPerror()
+#define PLOG_EVERY_N(severity, n) ABSL_PLOG_EVERY_N_IMPL(_##severity, n)
+#define PLOG_FIRST_N(severity, n) ABSL_PLOG_FIRST_N_IMPL(_##severity, n)
+#define PLOG_EVERY_POW_2(severity) ABSL_PLOG_EVERY_POW_2_IMPL(_##severity)
+#define PLOG_EVERY_N_SEC(severity, n_seconds) \
+  ABSL_PLOG_EVERY_N_SEC_IMPL(_##severity, n_seconds)
 
-#define PLOG_FIRST_N(severity, n)                                   \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(FirstN, n) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()         \
-          .WithPerror()
-
-#define PLOG_EVERY_POW_2(severity)                                  \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(EveryPow2) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()         \
-          .WithPerror()
-
-#define PLOG_EVERY_N_SEC(severity, n_seconds)                                  \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(EveryNSec, n_seconds) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()                    \
-          .WithPerror()
-
-#ifndef NDEBUG
-#define DLOG_EVERY_N(severity, n)                  \
-  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, true) \
-  (EveryN, n) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#define DLOG_FIRST_N(severity, n)                  \
-  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, true) \
-  (FirstN, n) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#define DLOG_EVERY_POW_2(severity)                 \
-  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, true) \
-  (EveryPow2) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#define DLOG_EVERY_N_SEC(severity, n_seconds)      \
-  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, true) \
-  (EveryNSec, n_seconds) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#else  // def NDEBUG
-#define DLOG_EVERY_N(severity, n)                   \
-  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, false) \
-  (EveryN, n) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#define DLOG_FIRST_N(severity, n)                   \
-  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, false) \
-  (FirstN, n) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#define DLOG_EVERY_POW_2(severity)                  \
-  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, false) \
-  (EveryPow2) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#define DLOG_EVERY_N_SEC(severity, n_seconds)       \
-  ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, false) \
-  (EveryNSec, n_seconds) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-#endif  // def NDEBUG
-
-#define VLOG_EVERY_N(verbose_level, n)                                       \
-  for (int absl_logging_internal_verbose_level = (verbose_level),            \
-           absl_logging_internal_log_loop = 1;                               \
-       absl_logging_internal_log_loop; absl_logging_internal_log_loop = 0)   \
-    ABSL_LOG_INTERNAL_CONDITION_INFO(                                        \
-        STATEFUL, VLOG_IS_ON(absl_logging_internal_verbose_level))           \
-  (EveryN, n) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream().WithVerbosity( \
-      absl_logging_internal_verbose_level)
-
-#define VLOG_FIRST_N(verbose_level, n)                                       \
-  for (int absl_logging_internal_verbose_level = (verbose_level),            \
-           absl_logging_internal_log_loop = 1;                               \
-       absl_logging_internal_log_loop; absl_logging_internal_log_loop = 0)   \
-    ABSL_LOG_INTERNAL_CONDITION_INFO(                                        \
-        STATEFUL, VLOG_IS_ON(absl_logging_internal_verbose_level))           \
-  (FirstN, n) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream().WithVerbosity( \
-      absl_logging_internal_verbose_level)
-
-#define VLOG_EVERY_POW_2(verbose_level)                                      \
-  for (int absl_logging_internal_verbose_level = (verbose_level),            \
-           absl_logging_internal_log_loop = 1;                               \
-       absl_logging_internal_log_loop; absl_logging_internal_log_loop = 0)   \
-    ABSL_LOG_INTERNAL_CONDITION_INFO(                                        \
-        STATEFUL, VLOG_IS_ON(absl_logging_internal_verbose_level))           \
-  (EveryPow2) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream().WithVerbosity( \
-      absl_logging_internal_verbose_level)
-
-#define VLOG_EVERY_N_SEC(verbose_level, n_seconds)                         \
-  for (int absl_logging_internal_verbose_level = (verbose_level),          \
-           absl_logging_internal_log_loop = 1;                             \
-       absl_logging_internal_log_loop; absl_logging_internal_log_loop = 0) \
-    ABSL_LOG_INTERNAL_CONDITION_INFO(                                      \
-        STATEFUL, VLOG_IS_ON(absl_logging_internal_verbose_level))         \
-  (EveryNSec, n_seconds) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream()   \
-      .WithVerbosity(absl_logging_internal_verbose_level)
+#define DLOG_EVERY_N(severity, n) ABSL_DLOG_EVERY_N_IMPL(_##severity, n)
+#define DLOG_FIRST_N(severity, n) ABSL_DLOG_FIRST_N_IMPL(_##severity, n)
+#define DLOG_EVERY_POW_2(severity) ABSL_DLOG_EVERY_POW_2_IMPL(_##severity)
+#define DLOG_EVERY_N_SEC(severity, n_seconds) \
+  ABSL_DLOG_EVERY_N_SEC_IMPL(_##severity, n_seconds)
 
 // `LOG_IF_EVERY_N` and friends behave as the corresponding `LOG_EVERY_N`
 // but neither increment a counter nor log a message if condition is false (as
@@ -361,79 +278,31 @@
 //
 //   LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << COUNTER
 //                                           << "th big cookie";
-#define LOG_IF_EVERY_N(severity, condition, n)                           \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryN, n) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
+#define LOG_IF_EVERY_N(severity, condition, n) \
+  ABSL_LOG_IF_EVERY_N_IMPL(_##severity, condition, n)
+#define LOG_IF_FIRST_N(severity, condition, n) \
+  ABSL_LOG_IF_FIRST_N_IMPL(_##severity, condition, n)
+#define LOG_IF_EVERY_POW_2(severity, condition) \
+  ABSL_LOG_IF_EVERY_POW_2_IMPL(_##severity, condition)
+#define LOG_IF_EVERY_N_SEC(severity, condition, n_seconds) \
+  ABSL_LOG_IF_EVERY_N_SEC_IMPL(_##severity, condition, n_seconds)
 
-#define LOG_IF_FIRST_N(severity, condition, n)                           \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(FirstN, n) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
+#define PLOG_IF_EVERY_N(severity, condition, n) \
+  ABSL_PLOG_IF_EVERY_N_IMPL(_##severity, condition, n)
+#define PLOG_IF_FIRST_N(severity, condition, n) \
+  ABSL_PLOG_IF_FIRST_N_IMPL(_##severity, condition, n)
+#define PLOG_IF_EVERY_POW_2(severity, condition) \
+  ABSL_PLOG_IF_EVERY_POW_2_IMPL(_##severity, condition)
+#define PLOG_IF_EVERY_N_SEC(severity, condition, n_seconds) \
+  ABSL_PLOG_IF_EVERY_N_SEC_IMPL(_##severity, condition, n_seconds)
 
-#define LOG_IF_EVERY_POW_2(severity, condition)                          \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryPow2) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#define LOG_IF_EVERY_N_SEC(severity, condition, n_seconds)               \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryNSec, \
-                                                              n_seconds) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#define PLOG_IF_EVERY_N(severity, condition, n)                          \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryN, n) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()              \
-          .WithPerror()
-
-#define PLOG_IF_FIRST_N(severity, condition, n)                          \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(FirstN, n) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()              \
-          .WithPerror()
-
-#define PLOG_IF_EVERY_POW_2(severity, condition)                         \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryPow2) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()              \
-          .WithPerror()
-
-#define PLOG_IF_EVERY_N_SEC(severity, condition, n_seconds)              \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryNSec, \
-                                                              n_seconds) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()              \
-          .WithPerror()
-
-#ifndef NDEBUG
-#define DLOG_IF_EVERY_N(severity, condition, n)                          \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryN, n) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#define DLOG_IF_FIRST_N(severity, condition, n)                          \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(FirstN, n) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#define DLOG_IF_EVERY_POW_2(severity, condition)                         \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryPow2) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#define DLOG_IF_EVERY_N_SEC(severity, condition, n_seconds)              \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryNSec, \
-                                                              n_seconds) \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#else  // def NDEBUG
-#define DLOG_IF_EVERY_N(severity, condition, n)                           \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, false && (condition))( \
-      EveryN, n) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#define DLOG_IF_FIRST_N(severity, condition, n)                           \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, false && (condition))( \
-      FirstN, n) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#define DLOG_IF_EVERY_POW_2(severity, condition)                          \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, false && (condition))( \
-      EveryPow2) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-
-#define DLOG_IF_EVERY_N_SEC(severity, condition, n_seconds)               \
-  ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, false && (condition))( \
-      EveryNSec, n_seconds)                                               \
-      ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream()
-#endif  // def NDEBUG
+#define DLOG_IF_EVERY_N(severity, condition, n) \
+  ABSL_DLOG_IF_EVERY_N_IMPL(_##severity, condition, n)
+#define DLOG_IF_FIRST_N(severity, condition, n) \
+  ABSL_DLOG_IF_FIRST_N_IMPL(_##severity, condition, n)
+#define DLOG_IF_EVERY_POW_2(severity, condition) \
+  ABSL_DLOG_IF_EVERY_POW_2_IMPL(_##severity, condition)
+#define DLOG_IF_EVERY_N_SEC(severity, condition, n_seconds) \
+  ABSL_DLOG_IF_EVERY_N_SEC_IMPL(_##severity, condition, n_seconds)
 
 #endif  // ABSL_LOG_LOG_H_
diff --git a/absl/log/log_basic_test.cc b/absl/log/log_basic_test.cc
new file mode 100644
index 0000000..b8d87c9
--- /dev/null
+++ b/absl/log/log_basic_test.cc
@@ -0,0 +1,21 @@
+//
+// Copyright 2022 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/log/log.h"
+
+#define ABSL_TEST_LOG LOG
+
+#include "gtest/gtest.h"
+#include "absl/log/log_basic_test_impl.h"
diff --git a/absl/log/basic_log_test.cc b/absl/log/log_basic_test_impl.h
similarity index 87%
rename from absl/log/basic_log_test.cc
rename to absl/log/log_basic_test_impl.h
index bc40f0d..35c0b69 100644
--- a/absl/log/basic_log_test.cc
+++ b/absl/log/log_basic_test_impl.h
@@ -16,6 +16,15 @@
 // The testcases in this file are expected to pass or be skipped with any value
 // of ABSL_MIN_LOG_LEVEL
 
+#ifndef ABSL_LOG_LOG_BASIC_TEST_IMPL_H_
+#define ABSL_LOG_LOG_BASIC_TEST_IMPL_H_
+
+// Verify that both sets of macros behave identically by parameterizing the
+// entire test file.
+#ifndef ABSL_TEST_LOG
+#error ABSL_TEST_LOG must be defined for these tests to work.
+#endif
+
 #include <cerrno>
 #include <sstream>
 #include <string>
@@ -28,11 +37,10 @@
 #include "absl/log/internal/test_actions.h"
 #include "absl/log/internal/test_helpers.h"
 #include "absl/log/internal/test_matchers.h"
-#include "absl/log/log.h"
 #include "absl/log/log_entry.h"
 #include "absl/log/scoped_mock_log.h"
 
-namespace {
+namespace absl_log_internal {
 #if GTEST_HAS_DEATH_TEST
 using ::absl::log_internal::DeathTestExpectedLogging;
 using ::absl::log_internal::DeathTestUnexpectedLogging;
@@ -80,13 +88,13 @@
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
   const int log_line = __LINE__ + 1;
-  auto do_log = [] { LOG(INFO) << "hello world"; };
+  auto do_log = [] { ABSL_TEST_LOG(INFO) << "hello world"; };
 
   if (LoggingEnabledAt(absl::LogSeverity::kInfo)) {
     EXPECT_CALL(
         test_sink,
         Send(AllOf(SourceFilename(Eq(__FILE__)),
-                   SourceBasename(Eq("basic_log_test.cc")),
+                   SourceBasename(Eq("log_basic_test_impl.h")),
                    SourceLine(Eq(log_line)), Prefix(IsTrue()),
                    LogSeverity(Eq(absl::LogSeverity::kInfo)),
                    TimestampInMatchWindow(),
@@ -109,13 +117,13 @@
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
   const int log_line = __LINE__ + 1;
-  auto do_log = [] { LOG(WARNING) << "hello world"; };
+  auto do_log = [] { ABSL_TEST_LOG(WARNING) << "hello world"; };
 
   if (LoggingEnabledAt(absl::LogSeverity::kWarning)) {
     EXPECT_CALL(
         test_sink,
         Send(AllOf(SourceFilename(Eq(__FILE__)),
-                   SourceBasename(Eq("basic_log_test.cc")),
+                   SourceBasename(Eq("log_basic_test_impl.h")),
                    SourceLine(Eq(log_line)), Prefix(IsTrue()),
                    LogSeverity(Eq(absl::LogSeverity::kWarning)),
                    TimestampInMatchWindow(),
@@ -138,13 +146,13 @@
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
   const int log_line = __LINE__ + 1;
-  auto do_log = [] { LOG(ERROR) << "hello world"; };
+  auto do_log = [] { ABSL_TEST_LOG(ERROR) << "hello world"; };
 
   if (LoggingEnabledAt(absl::LogSeverity::kError)) {
     EXPECT_CALL(
         test_sink,
         Send(AllOf(SourceFilename(Eq(__FILE__)),
-                   SourceBasename(Eq("basic_log_test.cc")),
+                   SourceBasename(Eq("log_basic_test_impl.h")),
                    SourceLine(Eq(log_line)), Prefix(IsTrue()),
                    LogSeverity(Eq(absl::LogSeverity::kError)),
                    TimestampInMatchWindow(),
@@ -174,7 +182,7 @@
   absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam());
 
   const int log_line = __LINE__ + 1;
-  auto do_log = [] { LOG(FATAL) << "hello world"; };
+  auto do_log = [] { ABSL_TEST_LOG(FATAL) << "hello world"; };
 
   EXPECT_EXIT(
       {
@@ -195,7 +203,7 @@
           EXPECT_CALL(
               test_sink,
               Send(AllOf(SourceFilename(Eq(__FILE__)),
-                         SourceBasename(Eq("basic_log_test.cc")),
+                         SourceBasename(Eq("log_basic_test_impl.h")),
                          SourceLine(Eq(log_line)), Prefix(IsTrue()),
                          LogSeverity(Eq(absl::LogSeverity::kFatal)),
                          TimestampInMatchWindow(),
@@ -211,7 +219,7 @@
           EXPECT_CALL(
               test_sink,
               Send(AllOf(SourceFilename(Eq(__FILE__)),
-                         SourceBasename(Eq("basic_log_test.cc")),
+                         SourceBasename(Eq("log_basic_test_impl.h")),
                          SourceLine(Eq(log_line)), Prefix(IsTrue()),
                          LogSeverity(Eq(absl::LogSeverity::kFatal)),
                          TimestampInMatchWindow(),
@@ -234,7 +242,7 @@
   absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam());
 
   const int log_line = __LINE__ + 1;
-  auto do_log = [] { LOG(QFATAL) << "hello world"; };
+  auto do_log = [] { ABSL_TEST_LOG(QFATAL) << "hello world"; };
 
   EXPECT_EXIT(
       {
@@ -249,7 +257,7 @@
           EXPECT_CALL(
               test_sink,
               Send(AllOf(SourceFilename(Eq(__FILE__)),
-                         SourceBasename(Eq("basic_log_test.cc")),
+                         SourceBasename(Eq("log_basic_test_impl.h")),
                          SourceLine(Eq(log_line)), Prefix(IsTrue()),
                          LogSeverity(Eq(absl::LogSeverity::kFatal)),
                          TimestampInMatchWindow(),
@@ -276,14 +284,16 @@
                         absl::LogSeverity::kError}) {
     absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
-    const int log_line = __LINE__ + 1;
-    auto do_log = [severity] { LOG(LEVEL(severity)) << "hello world"; };
+    const int log_line = __LINE__ + 2;
+    auto do_log = [severity] {
+      ABSL_TEST_LOG(LEVEL(severity)) << "hello world";
+    };
 
     if (LoggingEnabledAt(severity)) {
       EXPECT_CALL(
           test_sink,
           Send(AllOf(SourceFilename(Eq(__FILE__)),
-                     SourceBasename(Eq("basic_log_test.cc")),
+                     SourceBasename(Eq("log_basic_test_impl.h")),
                      SourceLine(Eq(log_line)), Prefix(IsTrue()),
                      LogSeverity(Eq(severity)), TimestampInMatchWindow(),
                      ThreadID(Eq(absl::base_internal::GetTID())),
@@ -309,7 +319,7 @@
   auto volatile severity = absl::LogSeverity::kFatal;
 
   const int log_line = __LINE__ + 1;
-  auto do_log = [severity] { LOG(LEVEL(severity)) << "hello world"; };
+  auto do_log = [severity] { ABSL_TEST_LOG(LEVEL(severity)) << "hello world"; };
 
   EXPECT_EXIT(
       {
@@ -326,7 +336,7 @@
           EXPECT_CALL(
               test_sink,
               Send(AllOf(SourceFilename(Eq(__FILE__)),
-                         SourceBasename(Eq("basic_log_test.cc")),
+                         SourceBasename(Eq("log_basic_test_impl.h")),
                          SourceLine(Eq(log_line)), Prefix(IsTrue()),
                          LogSeverity(Eq(absl::LogSeverity::kFatal)),
                          TimestampInMatchWindow(),
@@ -341,7 +351,7 @@
           EXPECT_CALL(
               test_sink,
               Send(AllOf(SourceFilename(Eq(__FILE__)),
-                         SourceBasename(Eq("basic_log_test.cc")),
+                         SourceBasename(Eq("log_basic_test_impl.h")),
                          SourceLine(Eq(log_line)), Prefix(IsTrue()),
                          LogSeverity(Eq(absl::LogSeverity::kFatal)),
                          TimestampInMatchWindow(),
@@ -374,7 +384,7 @@
   EXPECT_CALL(test_sink, Send(LogSeverity(Eq(absl::LogSeverity::kInfo))));
 
   test_sink.StartCapturingLogs();
-  LOG(LEVEL(-1)) << "hello world";
+  ABSL_TEST_LOG(LEVEL(-1)) << "hello world";
 }
 
 TEST_P(BasicLogTest, LevelClampsLargeValues) {
@@ -390,13 +400,14 @@
   EXPECT_CALL(test_sink, Send(LogSeverity(Eq(absl::LogSeverity::kError))));
 
   test_sink.StartCapturingLogs();
-  LOG(LEVEL(static_cast<int>(absl::LogSeverity::kFatal) + 1)) << "hello world";
+  ABSL_TEST_LOG(LEVEL(static_cast<int>(absl::LogSeverity::kFatal) + 1))
+      << "hello world";
 }
 
 TEST(ErrnoPreservationTest, InSeverityExpression) {
   errno = 77;
   int saved_errno;
-  LOG(LEVEL((saved_errno = errno, absl::LogSeverity::kInfo)));
+  ABSL_TEST_LOG(LEVEL((saved_errno = errno, absl::LogSeverity::kInfo)));
   EXPECT_THAT(saved_errno, Eq(77));
 }
 
@@ -408,13 +419,13 @@
 
   errno = 77;
   int saved_errno = 0;
-  LOG(INFO) << (saved_errno = errno, "hello world");
+  ABSL_TEST_LOG(INFO) << (saved_errno = errno, "hello world");
   EXPECT_THAT(saved_errno, Eq(77));
 }
 
 TEST(ErrnoPreservationTest, AfterStatement) {
   errno = 77;
-  LOG(INFO);
+  ABSL_TEST_LOG(INFO);
   const int saved_errno = errno;
   EXPECT_THAT(saved_errno, Eq(77));
 }
@@ -427,14 +438,18 @@
   // `kInfo`.
   static void LoggedVariable() {
     const int x = 0;
-    LOG(INFO) << x;
+    ABSL_TEST_LOG(INFO) << x;
   }
-  static void LoggedParameter(const int x) { LOG(INFO) << x; }
+  static void LoggedParameter(const int x) { ABSL_TEST_LOG(INFO) << x; }
   static void SeverityVariable() {
     const int x = 0;
-    LOG(LEVEL(x)) << "hello world";
+    ABSL_TEST_LOG(LEVEL(x)) << "hello world";
   }
-  static void SeverityParameter(const int x) { LOG(LEVEL(x)) << "hello world"; }
+  static void SeverityParameter(const int x) {
+    ABSL_TEST_LOG(LEVEL(x)) << "hello world";
+  }
 };
 
-}  // namespace
+}  // namespace absl_log_internal
+
+#endif  // ABSL_LOG_LOG_BASIC_TEST_IMPL_H_
diff --git a/absl/log/log_entry.h b/absl/log/log_entry.h
index 30114c3..9e4ae8e 100644
--- a/absl/log/log_entry.h
+++ b/absl/log/log_entry.h
@@ -49,7 +49,7 @@
 // Represents a single entry in a log, i.e., one `LOG` statement or failed
 // `CHECK`.
 //
-// `LogEntry` is copyable and thread-compatible.
+// `LogEntry` is thread-compatible.
 class LogEntry final {
  public:
   using tid_t = log_internal::Tid;
@@ -169,6 +169,15 @@
     return text_message_with_prefix_and_newline_and_nul_.data();
   }
 
+  // Returns a serialized protobuf holding the operands streamed into this
+  // log message.  The message definition is not yet published.
+  //
+  // The buffer does not outlive the entry; if you need the data later, you must
+  // copy them.
+  absl::string_view encoded_message() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return encoding_;
+  }
+
   // LogEntry::stacktrace()
   //
   // Optional stacktrace, e.g. for `FATAL` logs and failed `CHECK`s.
@@ -198,6 +207,7 @@
   tid_t tid_;
   absl::Span<const char> text_message_with_prefix_and_newline_and_nul_;
   size_t prefix_len_;
+  absl::string_view encoding_;
   std::string stacktrace_;
 
   friend class log_internal::LogEntryTestPeer;
diff --git a/absl/log/log_entry_test.cc b/absl/log/log_entry_test.cc
index 8d0afb3..d9bfa1f 100644
--- a/absl/log/log_entry_test.cc
+++ b/absl/log/log_entry_test.cc
@@ -30,6 +30,7 @@
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/log_severity.h"
+#include "absl/log/internal/append_truncated.h"
 #include "absl/log/internal/log_format.h"
 #include "absl/log/internal/test_helpers.h"
 #include "absl/strings/numbers.h"
@@ -40,7 +41,6 @@
 #include "absl/types/span.h"
 
 namespace {
-
 using ::absl::log_internal::LogEntryTestPeer;
 using ::testing::Eq;
 using ::testing::IsTrue;
@@ -49,16 +49,6 @@
 
 auto* test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment(
     new absl::log_internal::LogTestEnvironment);
-
-// Copies into `dst` as many bytes of `src` as will fit, then truncates the
-// copied bytes from the front of `dst` and returns the number of bytes written.
-size_t AppendTruncated(absl::string_view src, absl::Span<char>& dst) {
-  if (src.size() > dst.size()) src = src.substr(0, dst.size());
-  memcpy(dst.data(), src.data(), src.size());
-  dst.remove_prefix(src.size());
-  return src.size();
-}
-
 }  // namespace
 
 namespace absl {
@@ -69,8 +59,9 @@
  public:
   LogEntryTestPeer(absl::string_view base_filename, int line, bool prefix,
                    absl::LogSeverity severity, absl::string_view timestamp,
-                   absl::LogEntry::tid_t tid, absl::string_view text_message)
-      : buf_(15000, '\0') {
+                   absl::LogEntry::tid_t tid, PrefixFormat format,
+                   absl::string_view text_message)
+      : format_{format}, buf_(15000, '\0') {
     entry_.base_filename_ = base_filename;
     entry_.line_ = line;
     entry_.prefix_ = prefix;
@@ -98,12 +89,12 @@
         entry_.prefix_
             ? log_internal::FormatLogPrefix(
                   entry_.log_severity(), entry_.timestamp(), entry_.tid(),
-                  entry_.source_basename(), entry_.source_line(), view)
+                  entry_.source_basename(), entry_.source_line(), format_, view)
             : 0;
 
     EXPECT_THAT(entry_.prefix_len_,
                 Eq(static_cast<size_t>(view.data() - buf_.data())));
-    AppendTruncated(text_message, view);
+    log_internal::AppendTruncated(text_message, view);
     view = absl::Span<char>(view.data(), view.size() + 2);
     view[0] = '\n';
     view[1] = '\0';
@@ -117,14 +108,15 @@
   std::string FormatLogMessage() const {
     return log_internal::FormatLogMessage(
         entry_.log_severity(), ci_.cs, ci_.subsecond, entry_.tid(),
-        entry_.source_basename(), entry_.source_line(), entry_.text_message());
+        entry_.source_basename(), entry_.source_line(), format_,
+        entry_.text_message());
   }
   std::string FormatPrefixIntoSizedBuffer(size_t sz) {
     std::string str(sz, '\0');
     absl::Span<char> buf(&str[0], str.size());
     const size_t prefix_size = log_internal::FormatLogPrefix(
         entry_.log_severity(), entry_.timestamp(), entry_.tid(),
-        entry_.source_basename(), entry_.source_line(), buf);
+        entry_.source_basename(), entry_.source_line(), format_, buf);
     EXPECT_THAT(prefix_size, Eq(static_cast<size_t>(buf.data() - str.data())));
     str.resize(prefix_size);
     return str;
@@ -133,6 +125,7 @@
 
  private:
   absl::LogEntry entry_;
+  PrefixFormat format_;
   absl::TimeZone::CivilInfo ci_;
   std::vector<char> buf_;
 };
@@ -146,7 +139,9 @@
 
 TEST(LogEntryTest, Baseline) {
   LogEntryTestPeer entry("foo.cc", 1234, kUsePrefix, absl::LogSeverity::kInfo,
-                         "2020-01-02T03:04:05.6789", 451, "hello world");
+                         "2020-01-02T03:04:05.6789", 451,
+                         absl::log_internal::PrefixFormat::kNotRaw,
+                         "hello world");
   EXPECT_THAT(entry.FormatLogMessage(),
               Eq("I0102 03:04:05.678900     451 foo.cc:1234] hello world"));
   EXPECT_THAT(entry.FormatPrefixIntoSizedBuffer(1000),
@@ -168,7 +163,9 @@
 
 TEST(LogEntryTest, NoPrefix) {
   LogEntryTestPeer entry("foo.cc", 1234, kNoPrefix, absl::LogSeverity::kInfo,
-                         "2020-01-02T03:04:05.6789", 451, "hello world");
+                         "2020-01-02T03:04:05.6789", 451,
+                         absl::log_internal::PrefixFormat::kNotRaw,
+                         "hello world");
   EXPECT_THAT(entry.FormatLogMessage(),
               Eq("I0102 03:04:05.678900     451 foo.cc:1234] hello world"));
   // These methods are not responsible for honoring `prefix()`.
@@ -189,7 +186,8 @@
 
 TEST(LogEntryTest, EmptyFields) {
   LogEntryTestPeer entry("", 0, kUsePrefix, absl::LogSeverity::kInfo,
-                         "2020-01-02T03:04:05", 0, "");
+                         "2020-01-02T03:04:05", 0,
+                         absl::log_internal::PrefixFormat::kNotRaw, "");
   const std::string format_message = entry.FormatLogMessage();
   EXPECT_THAT(format_message, Eq("I0102 03:04:05.000000       0 :0] "));
   EXPECT_THAT(entry.FormatPrefixIntoSizedBuffer(1000), Eq(format_message));
@@ -208,10 +206,13 @@
 }
 
 TEST(LogEntryTest, NegativeFields) {
+  // When Abseil's minimum C++ version is C++17, this conditional can be
+  // converted to a constexpr if and the static_cast below removed.
   if (std::is_signed<absl::LogEntry::tid_t>::value) {
-    LogEntryTestPeer entry("foo.cc", -1234, kUsePrefix,
-                           absl::LogSeverity::kInfo, "2020-01-02T03:04:05.6789",
-                           -451, "hello world");
+    LogEntryTestPeer entry(
+        "foo.cc", -1234, kUsePrefix, absl::LogSeverity::kInfo,
+        "2020-01-02T03:04:05.6789", static_cast<absl::LogEntry::tid_t>(-451),
+        absl::log_internal::PrefixFormat::kNotRaw, "hello world");
     EXPECT_THAT(entry.FormatLogMessage(),
                 Eq("I0102 03:04:05.678900    -451 foo.cc:-1234] hello world"));
     EXPECT_THAT(entry.FormatPrefixIntoSizedBuffer(1000),
@@ -234,7 +235,8 @@
   } else {
     LogEntryTestPeer entry("foo.cc", -1234, kUsePrefix,
                            absl::LogSeverity::kInfo, "2020-01-02T03:04:05.6789",
-                           451, "hello world");
+                           451, absl::log_internal::PrefixFormat::kNotRaw,
+                           "hello world");
     EXPECT_THAT(entry.FormatLogMessage(),
                 Eq("I0102 03:04:05.678900     451 foo.cc:-1234] hello world"));
     EXPECT_THAT(entry.FormatPrefixIntoSizedBuffer(1000),
@@ -263,6 +265,7 @@
       "I've information vegetable, animal, and mineral.",
       2147483647, kUsePrefix, absl::LogSeverity::kInfo,
       "2020-01-02T03:04:05.678967896789", 2147483647,
+      absl::log_internal::PrefixFormat::kNotRaw,
       "I know the kings of England, and I quote the fights historical / "
       "From Marathon to Waterloo, in order categorical.");
   EXPECT_THAT(entry.FormatLogMessage(),
@@ -313,12 +316,16 @@
 }
 
 TEST(LogEntryTest, LongNegativeFields) {
+  // When Abseil's minimum C++ version is C++17, this conditional can be
+  // converted to a constexpr if and the static_cast below removed.
   if (std::is_signed<absl::LogEntry::tid_t>::value) {
     LogEntryTestPeer entry(
         "I am the very model of a modern Major-General / "
         "I've information vegetable, animal, and mineral.",
         -2147483647, kUsePrefix, absl::LogSeverity::kInfo,
-        "2020-01-02T03:04:05.678967896789", -2147483647,
+        "2020-01-02T03:04:05.678967896789",
+        static_cast<absl::LogEntry::tid_t>(-2147483647),
+        absl::log_internal::PrefixFormat::kNotRaw,
         "I know the kings of England, and I quote the fights historical / "
         "From Marathon to Waterloo, in order categorical.");
     EXPECT_THAT(
@@ -376,6 +383,7 @@
         "I've information vegetable, animal, and mineral.",
         -2147483647, kUsePrefix, absl::LogSeverity::kInfo,
         "2020-01-02T03:04:05.678967896789", 2147483647,
+        absl::log_internal::PrefixFormat::kNotRaw,
         "I know the kings of England, and I quote the fights historical / "
         "From Marathon to Waterloo, in order categorical.");
     EXPECT_THAT(
@@ -430,4 +438,31 @@
   }
 }
 
+TEST(LogEntryTest, Raw) {
+  LogEntryTestPeer entry("foo.cc", 1234, kUsePrefix, absl::LogSeverity::kInfo,
+                         "2020-01-02T03:04:05.6789", 451,
+                         absl::log_internal::PrefixFormat::kRaw, "hello world");
+  EXPECT_THAT(
+      entry.FormatLogMessage(),
+      Eq("I0102 03:04:05.678900     451 foo.cc:1234] RAW: hello world"));
+  EXPECT_THAT(entry.FormatPrefixIntoSizedBuffer(1000),
+              Eq("I0102 03:04:05.678900     451 foo.cc:1234] RAW: "));
+  for (size_t sz =
+           strlen("I0102 03:04:05.678900     451 foo.cc:1234] RAW: ") + 20;
+       sz != std::numeric_limits<size_t>::max(); sz--)
+    EXPECT_THAT("I0102 03:04:05.678900     451 foo.cc:1234] RAW: ",
+                StartsWith(entry.FormatPrefixIntoSizedBuffer(sz)));
+
+  EXPECT_THAT(
+      entry.entry().text_message_with_prefix_and_newline(),
+      Eq("I0102 03:04:05.678900     451 foo.cc:1234] RAW: hello world\n"));
+  EXPECT_THAT(
+      entry.entry().text_message_with_prefix_and_newline_c_str(),
+      StrEq("I0102 03:04:05.678900     451 foo.cc:1234] RAW: hello world\n"));
+  EXPECT_THAT(
+      entry.entry().text_message_with_prefix(),
+      Eq("I0102 03:04:05.678900     451 foo.cc:1234] RAW: hello world"));
+  EXPECT_THAT(entry.entry().text_message(), Eq("hello world"));
+}
+
 }  // namespace
diff --git a/absl/log/log_format_test.cc b/absl/log/log_format_test.cc
index c629fce..dbad5d9 100644
--- a/absl/log/log_format_test.cc
+++ b/absl/log/log_format_test.cc
@@ -16,7 +16,9 @@
 #include <math.h>
 
 #include <iomanip>
+#include <ios>
 #include <limits>
+#include <ostream>
 #include <sstream>
 #include <string>
 #include <type_traits>
@@ -26,30 +28,32 @@
 #endif
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "absl/log/internal/config.h"
+#include "absl/log/check.h"
 #include "absl/log/internal/test_matchers.h"
 #include "absl/log/log.h"
 #include "absl/log/scoped_mock_log.h"
 #include "absl/strings/match.h"
 #include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
 #include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
 
 namespace {
+using ::absl::log_internal::AsString;
 using ::absl::log_internal::MatchesOstream;
+using ::absl::log_internal::RawEncodedMessage;
 using ::absl::log_internal::TextMessage;
 using ::absl::log_internal::TextPrefix;
-
 using ::testing::AllOf;
 using ::testing::AnyOf;
-using ::testing::Eq;
-using ::testing::IsEmpty;
-using ::testing::Truly;
-using ::testing::Types;
-
 using ::testing::Each;
+using ::testing::EndsWith;
+using ::testing::Eq;
 using ::testing::Ge;
+using ::testing::IsEmpty;
 using ::testing::Le;
 using ::testing::SizeIs;
+using ::testing::Types;
 
 // Some aspects of formatting streamed data (e.g. pointer handling) are
 // implementation-defined.  Others are buggy in supported implementations.
@@ -69,15 +73,12 @@
   const int log_line = __LINE__ + 1;
   auto do_log = [] { LOG(INFO); };
 
-  EXPECT_CALL(
-      test_sink,
-      Send(AllOf(
-          TextMessage(MatchesOstream(ComparisonStream())),
-          TextPrefix(Truly([=](absl::string_view msg) {
-            return absl::EndsWith(
-                msg, absl::StrCat(" log_format_test.cc:", log_line, "] "));
-          })),
-          TextMessage(IsEmpty()), ENCODED_MESSAGE(EqualsProto(R"pb()pb")))));
+  EXPECT_CALL(test_sink,
+              Send(AllOf(TextMessage(MatchesOstream(ComparisonStream())),
+                         TextPrefix(AsString(EndsWith(absl::StrCat(
+                             " log_format_test.cc:", log_line, "] ")))),
+                         TextMessage(IsEmpty()),
+                         ENCODED_MESSAGE(EqualsProto(R"pb()pb")))));
 
   test_sink.StartCapturingLogs();
   do_log();
@@ -612,8 +613,10 @@
       Send(AllOf(
           TextMessage(MatchesOstream(comparison_stream)),
           TextMessage(AnyOf(Eq("-nan"), Eq("nan"), Eq("NaN"), Eq("-nan(ind)"))),
-          ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "-nan" })pb")))));
-
+          ENCODED_MESSAGE(
+              AnyOf(EqualsProto(R"pb(value { str: "-nan" })pb"),
+                    EqualsProto(R"pb(value { str: "nan" })pb"),
+                    EqualsProto(R"pb(value { str: "-nan(ind)" })pb"))))));
   test_sink.StartCapturingLogs();
   LOG(INFO) << value;
 }
@@ -647,24 +650,30 @@
   auto comparison_stream = ComparisonStream();
   comparison_stream << value;
 
-  EXPECT_CALL(test_sink,
-              Send(AllOf(TextMessage(MatchesOstream(comparison_stream)),
-                         TextMessage(AnyOf(Eq("0xdeadbeef"), Eq("DEADBEEF"),
-                                           Eq("00000000DEADBEEF"))),
-                         ENCODED_MESSAGE(EqualsProto(R"pb(value {
-                                                            str: "0xdeadbeef"
-                                                          })pb")))));
+  EXPECT_CALL(
+      test_sink,
+      Send(AllOf(
+          TextMessage(MatchesOstream(comparison_stream)),
+          TextMessage(
+              AnyOf(Eq("0xdeadbeef"), Eq("DEADBEEF"), Eq("00000000DEADBEEF"))),
+          ENCODED_MESSAGE(AnyOf(
+              EqualsProto(R"pb(value { str: "0xdeadbeef" })pb"),
+              EqualsProto(R"pb(value { str: "00000000DEADBEEF" })pb"))))));
 
   test_sink.StartCapturingLogs();
   LOG(INFO) << value;
 }
 
 template <typename T>
-class VolatileVoidPtrLogFormatTest : public testing::Test {};
-using VolatileVoidPtrTypes = Types<volatile void *, const volatile void *>;
-TYPED_TEST_SUITE(VolatileVoidPtrLogFormatTest, VolatileVoidPtrTypes);
+class VolatilePtrLogFormatTest : public testing::Test {};
+using VolatilePtrTypes =
+    Types<volatile void*, const volatile void*, volatile char*,
+          const volatile char*, volatile signed char*,
+          const volatile signed char*, volatile unsigned char*,
+          const volatile unsigned char*>;
+TYPED_TEST_SUITE(VolatilePtrLogFormatTest, VolatilePtrTypes);
 
-TYPED_TEST(VolatileVoidPtrLogFormatTest, Null) {
+TYPED_TEST(VolatilePtrLogFormatTest, Null) {
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
   const TypeParam value = nullptr;
@@ -682,7 +691,7 @@
   LOG(INFO) << value;
 }
 
-TYPED_TEST(VolatileVoidPtrLogFormatTest, NonNull) {
+TYPED_TEST(VolatilePtrLogFormatTest, NonNull) {
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
   const TypeParam value = reinterpret_cast<TypeParam>(0xdeadbeefLL);
@@ -702,7 +711,8 @@
 
 template <typename T>
 class CharPtrLogFormatTest : public testing::Test {};
-using CharPtrTypes = Types<char *, const char *>;
+using CharPtrTypes = Types<char, const char, signed char, const signed char,
+                           unsigned char, const unsigned char>;
 TYPED_TEST_SUITE(CharPtrLogFormatTest, CharPtrTypes);
 
 TYPED_TEST(CharPtrLogFormatTest, Null) {
@@ -712,7 +722,7 @@
   // standard library implementations choose to crash.  We take measures to log
   // something useful instead of crashing, even when that differs from the
   // standard library in use (and thus the behavior of `std::ostream`).
-  const TypeParam value = nullptr;
+  TypeParam* const value = nullptr;
 
   EXPECT_CALL(
       test_sink,
@@ -728,8 +738,8 @@
 TYPED_TEST(CharPtrLogFormatTest, NonNull) {
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
-  char data[] = "value";
-  const TypeParam value = data;
+  TypeParam data[] = {'v', 'a', 'l', 'u', 'e', '\0'};
+  TypeParam* const value = data;
   auto comparison_stream = ComparisonStream();
   comparison_stream << value;
 
@@ -865,6 +875,111 @@
   LOG(INFO) << value;
 }
 
+struct Point {
+  template <typename Sink>
+  friend void AbslStringify(Sink& sink, const Point& p) {
+    absl::Format(&sink, "(%d, %d)", p.x, p.y);
+  }
+
+  int x = 10;
+  int y = 20;
+};
+
+TEST(LogFormatTest, AbslStringifyExample) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  Point p;
+
+  EXPECT_CALL(
+      test_sink,
+      Send(AllOf(
+          TextMessage(Eq("(10, 20)")), TextMessage(Eq(absl::StrCat(p))),
+          ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "(10, 20)" })pb")))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << p;
+}
+
+struct PointWithAbslStringifiyAndOstream {
+  template <typename Sink>
+  friend void AbslStringify(Sink& sink,
+                            const PointWithAbslStringifiyAndOstream& p) {
+    absl::Format(&sink, "(%d, %d)", p.x, p.y);
+  }
+
+  int x = 10;
+  int y = 20;
+};
+
+ABSL_ATTRIBUTE_UNUSED std::ostream& operator<<(
+    std::ostream& os, const PointWithAbslStringifiyAndOstream&) {
+  return os << "Default to AbslStringify()";
+}
+
+TEST(LogFormatTest, CustomWithAbslStringifyAndOstream) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  PointWithAbslStringifiyAndOstream p;
+
+  EXPECT_CALL(
+      test_sink,
+      Send(AllOf(
+          TextMessage(Eq("(10, 20)")), TextMessage(Eq(absl::StrCat(p))),
+          ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "(10, 20)" })pb")))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << p;
+}
+
+struct PointStreamsNothing {
+  template <typename Sink>
+  friend void AbslStringify(Sink&, const PointStreamsNothing&) {}
+
+  int x = 10;
+  int y = 20;
+};
+
+TEST(LogFormatTest, AbslStringifyStreamsNothing) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  PointStreamsNothing p;
+
+  EXPECT_CALL(
+      test_sink,
+      Send(AllOf(TextMessage(Eq("77")), TextMessage(Eq(absl::StrCat(p, 77))),
+                 ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "77" })pb")))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << p << 77;
+}
+
+struct PointMultipleAppend {
+  template <typename Sink>
+  friend void AbslStringify(Sink& sink, const PointMultipleAppend& p) {
+    sink.Append("(");
+    sink.Append(absl::StrCat(p.x, ", ", p.y, ")"));
+  }
+
+  int x = 10;
+  int y = 20;
+};
+
+TEST(LogFormatTest, AbslStringifyMultipleAppend) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  PointMultipleAppend p;
+
+  EXPECT_CALL(
+      test_sink,
+      Send(AllOf(
+          TextMessage(Eq("(10, 20)")), TextMessage(Eq(absl::StrCat(p))),
+          ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "(" }
+                                           value { str: "10, 20)" })pb")))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << p;
+}
+
 TEST(ManipulatorLogFormatTest, BoolAlphaTrue) {
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
@@ -1180,8 +1295,11 @@
       Send(AllOf(TextMessage(MatchesOstream(comparison_stream)),
                  TextMessage(AnyOf(Eq("0x1.25bb50p+26"), Eq("0x1.25bb5p+26"),
                                    Eq("0x1.25bb500000000p+26"))),
-                 ENCODED_MESSAGE(EqualsProto(R"pb(
-                   value { str: "0x1.25bb5p+26" })pb")))));
+                 ENCODED_MESSAGE(
+                     AnyOf(EqualsProto(R"pb(value { str: "0x1.25bb5p+26" })pb"),
+                           EqualsProto(R"pb(value {
+                                              str: "0x1.25bb500000000p+26"
+                                            })pb"))))));
 
   test_sink.StartCapturingLogs();
 
@@ -1210,8 +1328,11 @@
       Send(AllOf(TextMessage(MatchesOstream(comparison_stream)),
                  TextMessage(AnyOf(Eq("0x1.25bb50p+26"), Eq("0x1.25bb5p+26"),
                                    Eq("0x1.25bb500000000p+26"))),
-                 ENCODED_MESSAGE(EqualsProto(R"pb(
-                   value { str: "0x1.25bb5p+26" })pb")))));
+                 ENCODED_MESSAGE(
+                     AnyOf(EqualsProto(R"pb(value { str: "0x1.25bb5p+26" })pb"),
+                           EqualsProto(R"pb(value {
+                                              str: "0x1.25bb500000000p+26"
+                                            })pb"))))));
 
   test_sink.StartCapturingLogs();
   LOG(INFO) << std::hexfloat << value;
@@ -1258,9 +1379,12 @@
   auto comparison_stream = ComparisonStream();
   comparison_stream << std::endl;
 
-  EXPECT_CALL(test_sink,
-              Send(AllOf(TextMessage(MatchesOstream(comparison_stream)),
-                         TextMessage(Eq("\n")))));
+  EXPECT_CALL(
+      test_sink,
+      Send(AllOf(
+          TextMessage(MatchesOstream(comparison_stream)),
+          TextMessage(Eq("\n")),
+          ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "\n" })pb")))));
 
   test_sink.StartCapturingLogs();
   LOG(INFO) << std::endl;
@@ -1501,25 +1625,248 @@
   LOG(INFO) << value << 77;
 }
 
-// Tests that verify the behavior when more data are streamed into a `LOG`
-// statement than fit in the buffer.
-// Structured logging scenario is tested in other unit tests since the output is
-// significantly different.
-TEST(OverflowTest, TruncatesStrings) {
+struct PointPercentV {
+  template <typename Sink>
+  friend void AbslStringify(Sink& sink, const PointPercentV& p) {
+    absl::Format(&sink, "(%v, %v)", p.x, p.y);
+  }
+
+  int x = 10;
+  int y = 20;
+};
+
+TEST(ManipulatorLogFormatTest, IOManipsDoNotAffectAbslStringify) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  PointPercentV p;
+
+  EXPECT_CALL(
+      test_sink,
+      Send(AllOf(
+          TextMessage(Eq("(10, 20)")), TextMessage(Eq(absl::StrCat(p))),
+          ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "(10, 20)" })pb")))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << std::hex << p;
+}
+
+TEST(StructuredLoggingOverflowTest, TruncatesStrings) {
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
   // This message is too long and should be truncated to some unspecified size
-  // no greater than the buffer size but not too much less either. It should be
+  // no greater than the buffer size but not too much less either.  It should be
   // truncated rather than discarded.
-  constexpr size_t buffer_size = 15000;
-
-  EXPECT_CALL(test_sink,
-              Send(TextMessage(
-                  AllOf(SizeIs(AllOf(Ge(buffer_size - 256), Le(buffer_size))),
-                        Each(Eq('x'))))));
+  EXPECT_CALL(
+      test_sink,
+      Send(AllOf(
+          TextMessage(AllOf(
+              SizeIs(AllOf(Ge(absl::log_internal::kLogMessageBufferSize - 256),
+                           Le(absl::log_internal::kLogMessageBufferSize))),
+              Each(Eq('x')))),
+          ENCODED_MESSAGE(HasOneStrThat(AllOf(
+              SizeIs(AllOf(Ge(absl::log_internal::kLogMessageBufferSize - 256),
+                           Le(absl::log_internal::kLogMessageBufferSize))),
+              Each(Eq('x'))))))));
 
   test_sink.StartCapturingLogs();
-  LOG(INFO) << std::string(2 * buffer_size, 'x');
+  LOG(INFO) << std::string(2 * absl::log_internal::kLogMessageBufferSize, 'x');
+}
+
+struct StringLike {
+  absl::string_view data;
+};
+std::ostream& operator<<(std::ostream& os, StringLike str) {
+  return os << str.data;
+}
+
+TEST(StructuredLoggingOverflowTest, TruncatesInsertionOperators) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  // This message is too long and should be truncated to some unspecified size
+  // no greater than the buffer size but not too much less either.  It should be
+  // truncated rather than discarded.
+  EXPECT_CALL(
+      test_sink,
+      Send(AllOf(
+          TextMessage(AllOf(
+              SizeIs(AllOf(Ge(absl::log_internal::kLogMessageBufferSize - 256),
+                           Le(absl::log_internal::kLogMessageBufferSize))),
+              Each(Eq('x')))),
+          ENCODED_MESSAGE(HasOneStrThat(AllOf(
+              SizeIs(AllOf(Ge(absl::log_internal::kLogMessageBufferSize - 256),
+                           Le(absl::log_internal::kLogMessageBufferSize))),
+              Each(Eq('x'))))))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << StringLike{
+      std::string(2 * absl::log_internal::kLogMessageBufferSize, 'x')};
+}
+
+// Returns the size of the largest string that will fit in a `LOG` message
+// buffer with no prefix.
+size_t MaxLogFieldLengthNoPrefix() {
+  class StringLengthExtractorSink : public absl::LogSink {
+   public:
+    void Send(const absl::LogEntry& entry) override {
+      CHECK(!size_.has_value());
+      CHECK_EQ(entry.text_message().find_first_not_of('x'),
+               absl::string_view::npos);
+      size_.emplace(entry.text_message().size());
+    }
+    size_t size() const {
+      CHECK(size_.has_value());
+      return *size_;
+    }
+
+   private:
+    absl::optional<size_t> size_;
+  } extractor_sink;
+  LOG(INFO).NoPrefix().ToSinkOnly(&extractor_sink)
+      << std::string(2 * absl::log_internal::kLogMessageBufferSize, 'x');
+  return extractor_sink.size();
+}
+
+TEST(StructuredLoggingOverflowTest, TruncatesStringsCleanly) {
+  const size_t longest_fit = MaxLogFieldLengthNoPrefix();
+  // To log a second value field, we need four bytes: two tag/type bytes and two
+  // sizes.  To put any data in the field we need a fifth byte.
+  {
+    absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+    EXPECT_CALL(test_sink,
+                Send(AllOf(ENCODED_MESSAGE(HasOneStrThat(
+                               AllOf(SizeIs(longest_fit), Each(Eq('x'))))),
+                           RawEncodedMessage(AsString(EndsWith("x"))))));
+    test_sink.StartCapturingLogs();
+    // x fits exactly, no part of y fits.
+    LOG(INFO).NoPrefix() << std::string(longest_fit, 'x') << "y";
+  }
+  {
+    absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+    EXPECT_CALL(test_sink,
+                Send(AllOf(ENCODED_MESSAGE(HasOneStrThat(
+                               AllOf(SizeIs(longest_fit - 1), Each(Eq('x'))))),
+                           RawEncodedMessage(AsString(EndsWith("x"))))));
+    test_sink.StartCapturingLogs();
+    // x fits, one byte from y's header fits but shouldn't be visible.
+    LOG(INFO).NoPrefix() << std::string(longest_fit - 1, 'x') << "y";
+  }
+  {
+    absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+    EXPECT_CALL(test_sink,
+                Send(AllOf(ENCODED_MESSAGE(HasOneStrThat(
+                               AllOf(SizeIs(longest_fit - 2), Each(Eq('x'))))),
+                           RawEncodedMessage(AsString(EndsWith("x"))))));
+    test_sink.StartCapturingLogs();
+    // x fits, two bytes from y's header fit but shouldn't be visible.
+    LOG(INFO).NoPrefix() << std::string(longest_fit - 2, 'x') << "y";
+  }
+  {
+    absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+    EXPECT_CALL(test_sink,
+                Send(AllOf(ENCODED_MESSAGE(HasOneStrThat(
+                               AllOf(SizeIs(longest_fit - 3), Each(Eq('x'))))),
+                           RawEncodedMessage(AsString(EndsWith("x"))))));
+    test_sink.StartCapturingLogs();
+    // x fits, three bytes from y's header fit but shouldn't be visible.
+    LOG(INFO).NoPrefix() << std::string(longest_fit - 3, 'x') << "y";
+  }
+  {
+    absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+    EXPECT_CALL(test_sink,
+                Send(AllOf(ENCODED_MESSAGE(HasOneStrAndOneLiteralThat(
+                               AllOf(SizeIs(longest_fit - 4), Each(Eq('x'))),
+                               IsEmpty())),
+                           RawEncodedMessage(Not(AsString(EndsWith("x")))))));
+    test_sink.StartCapturingLogs();
+    // x fits, all four bytes from y's header fit but no data bytes do, so we
+    // encode an empty string.
+    LOG(INFO).NoPrefix() << std::string(longest_fit - 4, 'x') << "y";
+  }
+  {
+    absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+    EXPECT_CALL(
+        test_sink,
+        Send(AllOf(ENCODED_MESSAGE(HasOneStrAndOneLiteralThat(
+                       AllOf(SizeIs(longest_fit - 5), Each(Eq('x'))), Eq("y"))),
+                   RawEncodedMessage(AsString(EndsWith("y"))))));
+    test_sink.StartCapturingLogs();
+    // x fits, y fits exactly.
+    LOG(INFO).NoPrefix() << std::string(longest_fit - 5, 'x') << "y";
+  }
+}
+
+TEST(StructuredLoggingOverflowTest, TruncatesInsertionOperatorsCleanly) {
+  const size_t longest_fit = MaxLogFieldLengthNoPrefix();
+  // To log a second value field, we need four bytes: two tag/type bytes and two
+  // sizes.  To put any data in the field we need a fifth byte.
+  {
+    absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+    EXPECT_CALL(test_sink,
+                Send(AllOf(ENCODED_MESSAGE(HasOneStrThat(
+                               AllOf(SizeIs(longest_fit), Each(Eq('x'))))),
+                           RawEncodedMessage(AsString(EndsWith("x"))))));
+    test_sink.StartCapturingLogs();
+    // x fits exactly, no part of y fits.
+    LOG(INFO).NoPrefix() << std::string(longest_fit, 'x') << StringLike{"y"};
+  }
+  {
+    absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+    EXPECT_CALL(test_sink,
+                Send(AllOf(ENCODED_MESSAGE(HasOneStrThat(
+                               AllOf(SizeIs(longest_fit - 1), Each(Eq('x'))))),
+                           RawEncodedMessage(AsString(EndsWith("x"))))));
+    test_sink.StartCapturingLogs();
+    // x fits, one byte from y's header fits but shouldn't be visible.
+    LOG(INFO).NoPrefix() << std::string(longest_fit - 1, 'x')
+                         << StringLike{"y"};
+  }
+  {
+    absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+    EXPECT_CALL(test_sink,
+                Send(AllOf(ENCODED_MESSAGE(HasOneStrThat(
+                               AllOf(SizeIs(longest_fit - 2), Each(Eq('x'))))),
+                           RawEncodedMessage(AsString(EndsWith("x"))))));
+    test_sink.StartCapturingLogs();
+    // x fits, two bytes from y's header fit but shouldn't be visible.
+    LOG(INFO).NoPrefix() << std::string(longest_fit - 2, 'x')
+                         << StringLike{"y"};
+  }
+  {
+    absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+    EXPECT_CALL(test_sink,
+                Send(AllOf(ENCODED_MESSAGE(HasOneStrThat(
+                               AllOf(SizeIs(longest_fit - 3), Each(Eq('x'))))),
+                           RawEncodedMessage(AsString(EndsWith("x"))))));
+    test_sink.StartCapturingLogs();
+    // x fits, three bytes from y's header fit but shouldn't be visible.
+    LOG(INFO).NoPrefix() << std::string(longest_fit - 3, 'x')
+                         << StringLike{"y"};
+  }
+  {
+    absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+    EXPECT_CALL(test_sink,
+                Send(AllOf(ENCODED_MESSAGE(HasOneStrThat(
+                               AllOf(SizeIs(longest_fit - 4), Each(Eq('x'))))),
+                           RawEncodedMessage(AsString(EndsWith("x"))))));
+    test_sink.StartCapturingLogs();
+    // x fits, all four bytes from y's header fit but no data bytes do.  We
+    // don't encode an empty string here because every I/O manipulator hits this
+    // codepath and those shouldn't leave empty strings behind.
+    LOG(INFO).NoPrefix() << std::string(longest_fit - 4, 'x')
+                         << StringLike{"y"};
+  }
+  {
+    absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+    EXPECT_CALL(
+        test_sink,
+        Send(AllOf(ENCODED_MESSAGE(HasTwoStrsThat(
+                       AllOf(SizeIs(longest_fit - 5), Each(Eq('x'))), Eq("y"))),
+                   RawEncodedMessage(AsString(EndsWith("y"))))));
+    test_sink.StartCapturingLogs();
+    // x fits, y fits exactly.
+    LOG(INFO).NoPrefix() << std::string(longest_fit - 5, 'x')
+                         << StringLike{"y"};
+  }
 }
 
 }  // namespace
diff --git a/absl/log/log_macro_hygiene_test.cc b/absl/log/log_macro_hygiene_test.cc
index ab6461f..dad9389 100644
--- a/absl/log/log_macro_hygiene_test.cc
+++ b/absl/log/log_macro_hygiene_test.cc
@@ -139,6 +139,22 @@
 
 #undef INFO
 
+#define _INFO Bogus
+TEST(LogHygieneTest, WorksWithUnderscoreINFODefined) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  EXPECT_CALL(test_sink, Log(absl::LogSeverity::kInfo, _, "Hello world"))
+      .Times(2 + (IsOptimized ? 2 : 0));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << "Hello world";
+  LOG_IF(INFO, true) << "Hello world";
+
+  DLOG(INFO) << "Hello world";
+  DLOG_IF(INFO, true) << "Hello world";
+}
+#undef _INFO
+
 TEST(LogHygieneTest, ExpressionEvaluationInLEVELSeverity) {
   auto i = static_cast<int>(absl::LogSeverity::kInfo);
   LOG(LEVEL(++i)) << "hello world";  // NOLINT
diff --git a/absl/log/log_modifier_methods_test.cc b/absl/log/log_modifier_methods_test.cc
index a9bf38b..42e13b1 100644
--- a/absl/log/log_modifier_methods_test.cc
+++ b/absl/log/log_modifier_methods_test.cc
@@ -130,7 +130,8 @@
 TEST(TailCallsModifiesTest, WithThreadID) {
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
-  EXPECT_CALL(test_sink, Send(AllOf(ThreadID(Eq(1234)))));
+  EXPECT_CALL(test_sink,
+              Send(AllOf(ThreadID(Eq(absl::LogEntry::tid_t{1234})))));
 
   test_sink.StartCapturingLogs();
   LOG(INFO).WithThreadID(1234) << "hello world";
@@ -152,7 +153,8 @@
       Send(AllOf(SourceFilename(Eq("fake/file")), SourceBasename(Eq("file")),
                  SourceLine(Eq(123)), Prefix(IsFalse()),
                  LogSeverity(Eq(absl::LogSeverity::kWarning)),
-                 Timestamp(Eq(absl::UnixEpoch())), ThreadID(Eq(456)),
+                 Timestamp(Eq(absl::UnixEpoch())),
+                 ThreadID(Eq(absl::LogEntry::tid_t{456})),
                  TextMessage(Eq("forwarded: hello world")), Verbosity(Eq(7)),
                  ENCODED_MESSAGE(
                      EqualsProto(R"pb(value { literal: "forwarded: " }
diff --git a/absl/log/log_streamer.h b/absl/log/log_streamer.h
index 2032745..2d41a07 100644
--- a/absl/log/log_streamer.h
+++ b/absl/log/log_streamer.h
@@ -30,7 +30,7 @@
 
 #include "absl/base/config.h"
 #include "absl/base/log_severity.h"
-#include "absl/log/log.h"
+#include "absl/log/absl_log.h"
 #include "absl/strings/internal/ostringstream.h"
 #include "absl/strings/string_view.h"
 #include "absl/types/optional.h"
@@ -99,7 +99,7 @@
     that.stream_.reset();
   }
   LogStreamer& operator=(LogStreamer&& that) {
-    LOG_IF(LEVEL(severity_), stream_).AtLocation(file_, line_) << buf_;
+    ABSL_LOG_IF(LEVEL(severity_), stream_).AtLocation(file_, line_) << buf_;
     severity_ = that.severity_;
     file_ = std::move(that.file_);
     line_ = that.line_;
@@ -114,7 +114,7 @@
   //
   // Logs this LogStreamer's buffered content as if by LOG.
   ~LogStreamer() {
-    LOG_IF(LEVEL(severity_), stream_.has_value()).AtLocation(file_, line_)
+    ABSL_LOG_IF(LEVEL(severity_), stream_.has_value()).AtLocation(file_, line_)
         << buf_;
   }
 
diff --git a/absl/log/scoped_mock_log.cc b/absl/log/scoped_mock_log.cc
index 4ebc0a9..39a0a52 100644
--- a/absl/log/scoped_mock_log.cc
+++ b/absl/log/scoped_mock_log.cc
@@ -30,7 +30,7 @@
 ABSL_NAMESPACE_BEGIN
 
 ScopedMockLog::ScopedMockLog(MockLogDefault default_exp)
-    : sink_(this), is_capturing_logs_(false) {
+    : sink_(this), is_capturing_logs_(false), is_triggered_(false) {
   if (default_exp == MockLogDefault::kIgnoreUnexpected) {
     // Ignore all calls to Log we did not set expectations for.
     EXPECT_CALL(*this, Log).Times(::testing::AnyNumber());
diff --git a/absl/log/scoped_mock_log.h b/absl/log/scoped_mock_log.h
index 44470c1..399e604 100644
--- a/absl/log/scoped_mock_log.h
+++ b/absl/log/scoped_mock_log.h
@@ -185,6 +185,9 @@
 
   ForwardingSink sink_;
   bool is_capturing_logs_;
+  // Until C++20, the default constructor leaves the underlying value wrapped in
+  // std::atomic uninitialized, so all constructors should be sure to initialize
+  // is_triggered_.
   std::atomic<bool> is_triggered_;
 };
 
diff --git a/absl/log/scoped_mock_log_test.cc b/absl/log/scoped_mock_log_test.cc
index 50689a0..4273693 100644
--- a/absl/log/scoped_mock_log_test.cc
+++ b/absl/log/scoped_mock_log_test.cc
@@ -71,6 +71,11 @@
       },
       "StopCapturingLogs");
 }
+
+TEST(ScopedMockLogDeathTest, FailsCheckIfStartCapturingLogsIsNeverCalled) {
+  EXPECT_DEATH({ absl::ScopedMockLog log; },
+               "Did you forget to call StartCapturingLogs");
+}
 #endif
 
 // Tests that ScopedMockLog intercepts LOG()s when it's alive.
@@ -98,7 +103,7 @@
       log,
       Send(AllOf(SourceFilename(Eq("/my/very/very/very_long_source_file.cc")),
                  SourceBasename(Eq("very_long_source_file.cc")),
-                 SourceLine(Eq(777)), ThreadID(Eq(1234)),
+                 SourceLine(Eq(777)), ThreadID(Eq(absl::LogEntry::tid_t{1234})),
                  TextMessageWithPrefix(Truly([](absl::string_view msg) {
                    return absl::EndsWith(
                        msg, " very_long_source_file.cc:777] Info message");
diff --git a/absl/log/stripping_test.cc b/absl/log/stripping_test.cc
index f37a0c5..8b711ec 100644
--- a/absl/log/stripping_test.cc
+++ b/absl/log/stripping_test.cc
@@ -49,6 +49,7 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/base/internal/strerror.h"
+#include "absl/base/log_severity.h"
 #include "absl/flags/internal/program_name.h"
 #include "absl/log/check.h"
 #include "absl/log/internal/test_helpers.h"
@@ -57,6 +58,10 @@
 #include "absl/strings/str_format.h"
 #include "absl/strings/string_view.h"
 
+// Set a flag that controls whether we actually execute fatal statements, but
+// prevent the compiler from optimizing it out.
+static volatile bool kReallyDie = false;
+
 namespace {
 using ::testing::_;
 using ::testing::Eq;
@@ -215,7 +220,8 @@
 #elif defined(_WIN32)
     std::basic_string<TCHAR> path(4096, _T('\0'));
     while (true) {
-      const uint32_t ret = ::GetModuleFileName(nullptr, &path[0], path.size());
+      const uint32_t ret = ::GetModuleFileName(nullptr, &path[0],
+                                               static_cast<DWORD>(path.size()));
       if (ret == 0) {
         absl::FPrintF(
             stderr,
@@ -303,7 +309,10 @@
   // as would happen if we used a literal.  We might (or might not) leave it
   // lying around later; that's what the tests are for!
   const std::string needle = absl::Base64Escape("StrippingTest.Fatal");
-  EXPECT_DEATH_IF_SUPPORTED(LOG(FATAL) << "U3RyaXBwaW5nVGVzdC5GYXRhbA==", "");
+  // We don't care if the LOG statement is actually executed, we're just
+  // checking that it's stripped.
+  if (kReallyDie) LOG(FATAL) << "U3RyaXBwaW5nVGVzdC5GYXRhbA==";
+
   std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
   ASSERT_THAT(exe, NotNull());
   if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) {
@@ -326,7 +335,7 @@
     // level that shouldn't be stripped.
     EXPECT_THAT(exe.get(), FileHasSubstr(needle));
   } else {
-#if defined(_MSC_VER) || defined(__APPLE__)
+#if (defined(_MSC_VER) && !defined(__clang__)) || defined(__APPLE__)
     // Dead code elimination misses this case.
 #else
     // All levels should be stripped, so it doesn't matter what the severity
diff --git a/absl/log/structured.h b/absl/log/structured.h
new file mode 100644
index 0000000..9ad69fb
--- /dev/null
+++ b/absl/log/structured.h
@@ -0,0 +1,70 @@
+// Copyright 2022 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: log/structured.h
+// -----------------------------------------------------------------------------
+//
+// This header declares APIs supporting structured logging, allowing log
+// statements to be more easily parsed, especially by automated processes.
+//
+// When structured logging is in use, data streamed into a `LOG` statement are
+// encoded as `Value` fields in a `logging.proto.Event` protocol buffer message.
+// The individual data are exposed programmatically to `LogSink`s and to the
+// user via some log reading tools which are able to query the structured data
+// more usefully than would be possible if each message was a single opaque
+// string.  These helpers allow user code to add additional structure to the
+// data they stream.
+
+#ifndef ABSL_LOG_STRUCTURED_H_
+#define ABSL_LOG_STRUCTURED_H_
+
+#include <ostream>
+
+#include "absl/base/config.h"
+#include "absl/log/internal/structured.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// LogAsLiteral()
+//
+// Annotates its argument as a string literal so that structured logging
+// captures it as a `literal` field instead of a `str` field (the default).
+// This does not affect the text representation, only the structure.
+//
+// Streaming `LogAsLiteral(s)` into a `std::ostream` behaves just like streaming
+// `s` directly.
+//
+// Using `LogAsLiteral()` is occasionally appropriate and useful when proxying
+// data logged from another system or another language.  For example:
+//
+//   void Logger::LogString(absl::string_view str, absl::LogSeverity severity,
+//                          const char *file, int line) {
+//     LOG(LEVEL(severity)).AtLocation(file, line) << str;
+//   }
+//   void Logger::LogStringLiteral(absl::string_view str,
+//                                 absl::LogSeverity severity, const char *file,
+//                                 int line) {
+//     LOG(LEVEL(severity)).AtLocation(file, line) << absl::LogAsLiteral(str);
+//   }
+inline log_internal::AsLiteralImpl LogAsLiteral(absl::string_view s) {
+  return log_internal::AsLiteralImpl(s);
+}
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_LOG_STRUCTURED_H_
diff --git a/absl/log/structured_test.cc b/absl/log/structured_test.cc
new file mode 100644
index 0000000..490a35d
--- /dev/null
+++ b/absl/log/structured_test.cc
@@ -0,0 +1,63 @@
+//
+// Copyright 2022 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/log/structured.h"
+
+#include <ios>
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/attributes.h"
+#include "absl/log/internal/test_helpers.h"
+#include "absl/log/internal/test_matchers.h"
+#include "absl/log/log.h"
+#include "absl/log/scoped_mock_log.h"
+
+namespace {
+using ::absl::log_internal::MatchesOstream;
+using ::absl::log_internal::TextMessage;
+using ::testing::Eq;
+
+auto *test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment(
+    new absl::log_internal::LogTestEnvironment);
+
+// Abseil Logging library uses these by default, so we set them on the
+// `std::ostream` we compare against too.
+std::ios &LoggingDefaults(std::ios &str) {
+  str.setf(std::ios_base::showbase | std::ios_base::boolalpha |
+           std::ios_base::internal);
+  return str;
+}
+
+TEST(StreamingFormatTest, LogAsLiteral) {
+  std::ostringstream stream;
+  const std::string not_a_literal("hello world");
+  stream << LoggingDefaults << absl::LogAsLiteral(not_a_literal);
+
+  absl::ScopedMockLog sink;
+
+  EXPECT_CALL(sink,
+              Send(AllOf(TextMessage(MatchesOstream(stream)),
+                         TextMessage(Eq("hello world")),
+                         ENCODED_MESSAGE(EqualsProto(
+                             R"pb(value { literal: "hello world" })pb")))));
+
+  sink.StartCapturingLogs();
+  LOG(INFO) << absl::LogAsLiteral(not_a_literal);
+}
+
+}  // namespace
diff --git a/absl/memory/BUILD.bazel b/absl/memory/BUILD.bazel
index 389aedf..a93f54a 100644
--- a/absl/memory/BUILD.bazel
+++ b/absl/memory/BUILD.bazel
@@ -50,18 +50,3 @@
         "@com_google_googletest//:gtest_main",
     ],
 )
-
-cc_test(
-    name = "memory_exception_safety_test",
-    srcs = [
-        "memory_exception_safety_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":memory",
-        "//absl/base:config",
-        "//absl/base:exception_safety_testing",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
diff --git a/absl/memory/CMakeLists.txt b/absl/memory/CMakeLists.txt
index 9d50e1d..c5ed4b4 100644
--- a/absl/memory/CMakeLists.txt
+++ b/absl/memory/CMakeLists.txt
@@ -39,17 +39,3 @@
     absl::core_headers
     GTest::gmock_main
 )
-
-absl_cc_test(
-  NAME
-    memory_exception_safety_test
-  SRCS
-    "memory_exception_safety_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::memory
-    absl::config
-    absl::exception_safety_testing
-    GTest::gmock_main
-)
diff --git a/absl/memory/memory.h b/absl/memory/memory.h
index d633260..3508135 100644
--- a/absl/memory/memory.h
+++ b/absl/memory/memory.h
@@ -75,32 +75,6 @@
   return std::unique_ptr<T>(ptr);
 }
 
-namespace memory_internal {
-
-// Traits to select proper overload and return type for `absl::make_unique<>`.
-template <typename T>
-struct MakeUniqueResult {
-  using scalar = std::unique_ptr<T>;
-};
-template <typename T>
-struct MakeUniqueResult<T[]> {
-  using array = std::unique_ptr<T[]>;
-};
-template <typename T, size_t N>
-struct MakeUniqueResult<T[N]> {
-  using invalid = void;
-};
-
-}  // namespace memory_internal
-
-// gcc 4.8 has __cplusplus at 201301 but the libstdc++ shipped with it doesn't
-// define make_unique.  Other supported compilers either just define __cplusplus
-// as 201103 but have make_unique (msvc), or have make_unique whenever
-// __cplusplus > 201103 (clang).
-#if (__cplusplus > 201103L || defined(_MSC_VER)) && \
-    !(defined(__GLIBCXX__) && !defined(__cpp_lib_make_unique))
-using std::make_unique;
-#else
 // -----------------------------------------------------------------------------
 // Function Template: make_unique<T>()
 // -----------------------------------------------------------------------------
@@ -109,82 +83,18 @@
 // during the construction process. `absl::make_unique<>` also avoids redundant
 // type declarations, by avoiding the need to explicitly use the `new` operator.
 //
-// This implementation of `absl::make_unique<>` is designed for C++11 code and
-// will be replaced in C++14 by the equivalent `std::make_unique<>` abstraction.
-// `absl::make_unique<>` is designed to be 100% compatible with
-// `std::make_unique<>` so that the eventual migration will involve a simple
-// rename operation.
+// https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique
 //
 // For more background on why `std::unique_ptr<T>(new T(a,b))` is problematic,
 // see Herb Sutter's explanation on
 // (Exception-Safe Function Calls)[https://herbsutter.com/gotw/_102/].
 // (In general, reviewers should treat `new T(a,b)` with scrutiny.)
 //
-// Example usage:
-//
-//    auto p = make_unique<X>(args...);  // 'p'  is a std::unique_ptr<X>
-//    auto pa = make_unique<X[]>(5);     // 'pa' is a std::unique_ptr<X[]>
-//
-// Three overloads of `absl::make_unique` are required:
-//
-//   - For non-array T:
-//
-//       Allocates a T with `new T(std::forward<Args> args...)`,
-//       forwarding all `args` to T's constructor.
-//       Returns a `std::unique_ptr<T>` owning that object.
-//
-//   - For an array of unknown bounds T[]:
-//
-//       `absl::make_unique<>` will allocate an array T of type U[] with
-//       `new U[n]()` and return a `std::unique_ptr<U[]>` owning that array.
-//
-//       Note that 'U[n]()' is different from 'U[n]', and elements will be
-//       value-initialized. Note as well that `std::unique_ptr` will perform its
-//       own destruction of the array elements upon leaving scope, even though
-//       the array [] does not have a default destructor.
-//
-//       NOTE: an array of unknown bounds T[] may still be (and often will be)
-//       initialized to have a size, and will still use this overload. E.g:
-//
-//         auto my_array = absl::make_unique<int[]>(10);
-//
-//   - For an array of known bounds T[N]:
-//
-//       `absl::make_unique<>` is deleted (like with `std::make_unique<>`) as
-//       this overload is not useful.
-//
-//       NOTE: an array of known bounds T[N] is not considered a useful
-//       construction, and may cause undefined behavior in templates. E.g:
-//
-//         auto my_array = absl::make_unique<int[10]>();
-//
-//       In those cases, of course, you can still use the overload above and
-//       simply initialize it to its desired size:
-//
-//         auto my_array = absl::make_unique<int[]>(10);
-
-// `absl::make_unique` overload for non-array types.
-template <typename T, typename... Args>
-typename memory_internal::MakeUniqueResult<T>::scalar make_unique(
-    Args&&... args) {
-  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-}
-
-// `absl::make_unique` overload for an array T[] of unknown bounds.
-// The array allocation needs to use the `new T[size]` form and cannot take
-// element constructor arguments. The `std::unique_ptr` will manage destructing
-// these array elements.
-template <typename T>
-typename memory_internal::MakeUniqueResult<T>::array make_unique(size_t n) {
-  return std::unique_ptr<T>(new typename absl::remove_extent_t<T>[n]());
-}
-
-// `absl::make_unique` overload for an array T[N] of known bounds.
-// This construction will be rejected.
-template <typename T, typename... Args>
-typename memory_internal::MakeUniqueResult<T>::invalid make_unique(
-    Args&&... /* args */) = delete;
-#endif
+// Historical note: Abseil once provided a C++11 compatible implementation of
+// the C++14's `std::make_unique`. Now that C++11 support has been sunsetted,
+// `absl::make_unique` simply uses the STL-provided implementation. New code
+// should use `std::make_unique`.
+using std::make_unique;
 
 // -----------------------------------------------------------------------------
 // Function Template: RawPtr()
@@ -248,6 +158,26 @@
   return std::weak_ptr<T>(ptr);
 }
 
+// -----------------------------------------------------------------------------
+// Class Template: pointer_traits
+// -----------------------------------------------------------------------------
+//
+// Historical note: Abseil once provided an implementation of
+// `std::pointer_traits` for platforms that had not yet provided it. Those
+// platforms are no longer supported. New code should simply use
+// `std::pointer_traits`.
+using std::pointer_traits;
+
+// -----------------------------------------------------------------------------
+// Class Template: allocator_traits
+// -----------------------------------------------------------------------------
+//
+// Historical note: Abseil once provided an implementation of
+// `std::allocator_traits` for platforms that had not yet provided it. Those
+// platforms are no longer supported. New code should simply use
+// `std::allocator_traits`.
+using std::allocator_traits;
+
 namespace memory_internal {
 
 // ExtractOr<E, O, D>::type evaluates to E<O> if possible. Otherwise, D.
@@ -265,357 +195,6 @@
 template <template <typename> class Extract, typename Obj, typename Default>
 using ExtractOrT = typename ExtractOr<Extract, Obj, Default, void>::type;
 
-// Extractors for the features of allocators.
-template <typename T>
-using GetPointer = typename T::pointer;
-
-template <typename T>
-using GetConstPointer = typename T::const_pointer;
-
-template <typename T>
-using GetVoidPointer = typename T::void_pointer;
-
-template <typename T>
-using GetConstVoidPointer = typename T::const_void_pointer;
-
-template <typename T>
-using GetDifferenceType = typename T::difference_type;
-
-template <typename T>
-using GetSizeType = typename T::size_type;
-
-template <typename T>
-using GetPropagateOnContainerCopyAssignment =
-    typename T::propagate_on_container_copy_assignment;
-
-template <typename T>
-using GetPropagateOnContainerMoveAssignment =
-    typename T::propagate_on_container_move_assignment;
-
-template <typename T>
-using GetPropagateOnContainerSwap = typename T::propagate_on_container_swap;
-
-template <typename T>
-using GetIsAlwaysEqual = typename T::is_always_equal;
-
-template <typename T>
-struct GetFirstArg;
-
-template <template <typename...> class Class, typename T, typename... Args>
-struct GetFirstArg<Class<T, Args...>> {
-  using type = T;
-};
-
-template <typename Ptr, typename = void>
-struct ElementType {
-  using type = typename GetFirstArg<Ptr>::type;
-};
-
-template <typename T>
-struct ElementType<T, void_t<typename T::element_type>> {
-  using type = typename T::element_type;
-};
-
-template <typename T, typename U>
-struct RebindFirstArg;
-
-template <template <typename...> class Class, typename T, typename... Args,
-          typename U>
-struct RebindFirstArg<Class<T, Args...>, U> {
-  using type = Class<U, Args...>;
-};
-
-template <typename T, typename U, typename = void>
-struct RebindPtr {
-  using type = typename RebindFirstArg<T, U>::type;
-};
-
-template <typename T, typename U>
-struct RebindPtr<T, U, void_t<typename T::template rebind<U>>> {
-  using type = typename T::template rebind<U>;
-};
-
-template <typename T, typename U>
-constexpr bool HasRebindAlloc(...) {
-  return false;
-}
-
-template <typename T, typename U>
-constexpr bool HasRebindAlloc(typename T::template rebind<U>::other*) {
-  return true;
-}
-
-template <typename T, typename U, bool = HasRebindAlloc<T, U>(nullptr)>
-struct RebindAlloc {
-  using type = typename RebindFirstArg<T, U>::type;
-};
-
-template <typename T, typename U>
-struct RebindAlloc<T, U, true> {
-  using type = typename T::template rebind<U>::other;
-};
-
-}  // namespace memory_internal
-
-// -----------------------------------------------------------------------------
-// Class Template: pointer_traits
-// -----------------------------------------------------------------------------
-//
-// An implementation of C++11's std::pointer_traits.
-//
-// Provided for portability on toolchains that have a working C++11 compiler,
-// but the standard library is lacking in C++11 support. For example, some
-// version of the Android NDK.
-//
-
-template <typename Ptr>
-struct pointer_traits {
-  using pointer = Ptr;
-
-  // element_type:
-  // Ptr::element_type if present. Otherwise T if Ptr is a template
-  // instantiation Template<T, Args...>
-  using element_type = typename memory_internal::ElementType<Ptr>::type;
-
-  // difference_type:
-  // Ptr::difference_type if present, otherwise std::ptrdiff_t
-  using difference_type =
-      memory_internal::ExtractOrT<memory_internal::GetDifferenceType, Ptr,
-                                  std::ptrdiff_t>;
-
-  // rebind:
-  // Ptr::rebind<U> if exists, otherwise Template<U, Args...> if Ptr is a
-  // template instantiation Template<T, Args...>
-  template <typename U>
-  using rebind = typename memory_internal::RebindPtr<Ptr, U>::type;
-
-  // pointer_to:
-  // Calls Ptr::pointer_to(r)
-  static pointer pointer_to(element_type& r) {  // NOLINT(runtime/references)
-    return Ptr::pointer_to(r);
-  }
-};
-
-// Specialization for T*.
-template <typename T>
-struct pointer_traits<T*> {
-  using pointer = T*;
-  using element_type = T;
-  using difference_type = std::ptrdiff_t;
-
-  template <typename U>
-  using rebind = U*;
-
-  // pointer_to:
-  // Calls std::addressof(r)
-  static pointer pointer_to(
-      element_type& r) noexcept {  // NOLINT(runtime/references)
-    return std::addressof(r);
-  }
-};
-
-// -----------------------------------------------------------------------------
-// Class Template: allocator_traits
-// -----------------------------------------------------------------------------
-//
-// A C++11 compatible implementation of C++17's std::allocator_traits.
-//
-#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
-using std::allocator_traits;
-#else  // __cplusplus >= 201703L
-template <typename Alloc>
-struct allocator_traits {
-  using allocator_type = Alloc;
-
-  // value_type:
-  // Alloc::value_type
-  using value_type = typename Alloc::value_type;
-
-  // pointer:
-  // Alloc::pointer if present, otherwise value_type*
-  using pointer = memory_internal::ExtractOrT<memory_internal::GetPointer,
-                                              Alloc, value_type*>;
-
-  // const_pointer:
-  // Alloc::const_pointer if present, otherwise
-  // absl::pointer_traits<pointer>::rebind<const value_type>
-  using const_pointer =
-      memory_internal::ExtractOrT<memory_internal::GetConstPointer, Alloc,
-                                  typename absl::pointer_traits<pointer>::
-                                      template rebind<const value_type>>;
-
-  // void_pointer:
-  // Alloc::void_pointer if present, otherwise
-  // absl::pointer_traits<pointer>::rebind<void>
-  using void_pointer = memory_internal::ExtractOrT<
-      memory_internal::GetVoidPointer, Alloc,
-      typename absl::pointer_traits<pointer>::template rebind<void>>;
-
-  // const_void_pointer:
-  // Alloc::const_void_pointer if present, otherwise
-  // absl::pointer_traits<pointer>::rebind<const void>
-  using const_void_pointer = memory_internal::ExtractOrT<
-      memory_internal::GetConstVoidPointer, Alloc,
-      typename absl::pointer_traits<pointer>::template rebind<const void>>;
-
-  // difference_type:
-  // Alloc::difference_type if present, otherwise
-  // absl::pointer_traits<pointer>::difference_type
-  using difference_type = memory_internal::ExtractOrT<
-      memory_internal::GetDifferenceType, Alloc,
-      typename absl::pointer_traits<pointer>::difference_type>;
-
-  // size_type:
-  // Alloc::size_type if present, otherwise
-  // std::make_unsigned<difference_type>::type
-  using size_type = memory_internal::ExtractOrT<
-      memory_internal::GetSizeType, Alloc,
-      typename std::make_unsigned<difference_type>::type>;
-
-  // propagate_on_container_copy_assignment:
-  // Alloc::propagate_on_container_copy_assignment if present, otherwise
-  // std::false_type
-  using propagate_on_container_copy_assignment = memory_internal::ExtractOrT<
-      memory_internal::GetPropagateOnContainerCopyAssignment, Alloc,
-      std::false_type>;
-
-  // propagate_on_container_move_assignment:
-  // Alloc::propagate_on_container_move_assignment if present, otherwise
-  // std::false_type
-  using propagate_on_container_move_assignment = memory_internal::ExtractOrT<
-      memory_internal::GetPropagateOnContainerMoveAssignment, Alloc,
-      std::false_type>;
-
-  // propagate_on_container_swap:
-  // Alloc::propagate_on_container_swap if present, otherwise std::false_type
-  using propagate_on_container_swap =
-      memory_internal::ExtractOrT<memory_internal::GetPropagateOnContainerSwap,
-                                  Alloc, std::false_type>;
-
-  // is_always_equal:
-  // Alloc::is_always_equal if present, otherwise std::is_empty<Alloc>::type
-  using is_always_equal =
-      memory_internal::ExtractOrT<memory_internal::GetIsAlwaysEqual, Alloc,
-                                  typename std::is_empty<Alloc>::type>;
-
-  // rebind_alloc:
-  // Alloc::rebind<T>::other if present, otherwise Alloc<T, Args> if this Alloc
-  // is Alloc<U, Args>
-  template <typename T>
-  using rebind_alloc = typename memory_internal::RebindAlloc<Alloc, T>::type;
-
-  // rebind_traits:
-  // absl::allocator_traits<rebind_alloc<T>>
-  template <typename T>
-  using rebind_traits = absl::allocator_traits<rebind_alloc<T>>;
-
-  // allocate(Alloc& a, size_type n):
-  // Calls a.allocate(n)
-  static pointer allocate(Alloc& a,  // NOLINT(runtime/references)
-                          size_type n) {
-    return a.allocate(n);
-  }
-
-  // allocate(Alloc& a, size_type n, const_void_pointer hint):
-  // Calls a.allocate(n, hint) if possible.
-  // If not possible, calls a.allocate(n)
-  static pointer allocate(Alloc& a, size_type n,  // NOLINT(runtime/references)
-                          const_void_pointer hint) {
-    return allocate_impl(0, a, n, hint);
-  }
-
-  // deallocate(Alloc& a, pointer p, size_type n):
-  // Calls a.deallocate(p, n)
-  static void deallocate(Alloc& a, pointer p,  // NOLINT(runtime/references)
-                         size_type n) {
-    a.deallocate(p, n);
-  }
-
-  // construct(Alloc& a, T* p, Args&&... args):
-  // Calls a.construct(p, std::forward<Args>(args)...) if possible.
-  // If not possible, calls
-  //   ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...)
-  template <typename T, typename... Args>
-  static void construct(Alloc& a, T* p,  // NOLINT(runtime/references)
-                        Args&&... args) {
-    construct_impl(0, a, p, std::forward<Args>(args)...);
-  }
-
-  // destroy(Alloc& a, T* p):
-  // Calls a.destroy(p) if possible. If not possible, calls p->~T().
-  template <typename T>
-  static void destroy(Alloc& a, T* p) {  // NOLINT(runtime/references)
-    destroy_impl(0, a, p);
-  }
-
-  // max_size(const Alloc& a):
-  // Returns a.max_size() if possible. If not possible, returns
-  //   std::numeric_limits<size_type>::max() / sizeof(value_type)
-  static size_type max_size(const Alloc& a) { return max_size_impl(0, a); }
-
-  // select_on_container_copy_construction(const Alloc& a):
-  // Returns a.select_on_container_copy_construction() if possible.
-  // If not possible, returns a.
-  static Alloc select_on_container_copy_construction(const Alloc& a) {
-    return select_on_container_copy_construction_impl(0, a);
-  }
-
- private:
-  template <typename A>
-  static auto allocate_impl(int, A& a,  // NOLINT(runtime/references)
-                            size_type n, const_void_pointer hint)
-      -> decltype(a.allocate(n, hint)) {
-    return a.allocate(n, hint);
-  }
-  static pointer allocate_impl(char, Alloc& a,  // NOLINT(runtime/references)
-                               size_type n, const_void_pointer) {
-    return a.allocate(n);
-  }
-
-  template <typename A, typename... Args>
-  static auto construct_impl(int, A& a,  // NOLINT(runtime/references)
-                             Args&&... args)
-      -> decltype(a.construct(std::forward<Args>(args)...)) {
-    a.construct(std::forward<Args>(args)...);
-  }
-
-  template <typename T, typename... Args>
-  static void construct_impl(char, Alloc&, T* p, Args&&... args) {
-    ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
-  }
-
-  template <typename A, typename T>
-  static auto destroy_impl(int, A& a,  // NOLINT(runtime/references)
-                           T* p) -> decltype(a.destroy(p)) {
-    a.destroy(p);
-  }
-  template <typename T>
-  static void destroy_impl(char, Alloc&, T* p) {
-    p->~T();
-  }
-
-  template <typename A>
-  static auto max_size_impl(int, const A& a) -> decltype(a.max_size()) {
-    return a.max_size();
-  }
-  static size_type max_size_impl(char, const Alloc&) {
-    return (std::numeric_limits<size_type>::max)() / sizeof(value_type);
-  }
-
-  template <typename A>
-  static auto select_on_container_copy_construction_impl(int, const A& a)
-      -> decltype(a.select_on_container_copy_construction()) {
-    return a.select_on_container_copy_construction();
-  }
-  static Alloc select_on_container_copy_construction_impl(char,
-                                                          const Alloc& a) {
-    return a;
-  }
-};
-#endif  // __cplusplus >= 201703L
-
-namespace memory_internal {
-
 // This template alias transforms Alloc::is_nothrow into a metafunction with
 // Alloc as a parameter so it can be used with ExtractOrT<>.
 template <typename Alloc>
diff --git a/absl/memory/memory_exception_safety_test.cc b/absl/memory/memory_exception_safety_test.cc
deleted file mode 100644
index 1df7261..0000000
--- a/absl/memory/memory_exception_safety_test.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/memory/memory.h"
-
-#include "absl/base/config.h"
-
-#ifdef ABSL_HAVE_EXCEPTIONS
-
-#include "gtest/gtest.h"
-#include "absl/base/internal/exception_safety_testing.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-constexpr int kLength = 50;
-using Thrower = testing::ThrowingValue<testing::TypeSpec::kEverythingThrows>;
-
-TEST(MakeUnique, CheckForLeaks) {
-  constexpr int kValue = 321;
-  auto tester = testing::MakeExceptionSafetyTester()
-                    .WithInitialValue(Thrower(kValue))
-                    // Ensures make_unique does not modify the input. The real
-                    // test, though, is ConstructorTracker checking for leaks.
-                    .WithContracts(testing::strong_guarantee);
-
-  EXPECT_TRUE(tester.Test([](Thrower* thrower) {
-    static_cast<void>(absl::make_unique<Thrower>(*thrower));
-  }));
-
-  EXPECT_TRUE(tester.Test([](Thrower* thrower) {
-    static_cast<void>(absl::make_unique<Thrower>(std::move(*thrower)));
-  }));
-
-  // Test T[n] overload
-  EXPECT_TRUE(tester.Test([&](Thrower*) {
-    static_cast<void>(absl::make_unique<Thrower[]>(kLength));
-  }));
-}
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_HAVE_EXCEPTIONS
diff --git a/absl/memory/memory_test.cc b/absl/memory/memory_test.cc
index 5d5719b..fafd3a4 100644
--- a/absl/memory/memory_test.cc
+++ b/absl/memory/memory_test.cc
@@ -63,12 +63,6 @@
   }
   EXPECT_EQ(0, DestructorVerifier::instance_count());
 }
-TEST(MakeUniqueTest, Basic) {
-  std::unique_ptr<std::string> p = absl::make_unique<std::string>();
-  EXPECT_EQ("", *p);
-  p = absl::make_unique<std::string>("hi");
-  EXPECT_EQ("hi", *p);
-}
 
 // InitializationVerifier fills in a pattern when allocated so we can
 // distinguish between its default and value initialized states (without
@@ -93,65 +87,6 @@
   int b;
 };
 
-TEST(Initialization, MakeUnique) {
-  auto p = absl::make_unique<InitializationVerifier>();
-
-  EXPECT_EQ(0, p->a);
-  EXPECT_EQ(0, p->b);
-}
-
-TEST(Initialization, MakeUniqueArray) {
-  auto p = absl::make_unique<InitializationVerifier[]>(2);
-
-  EXPECT_EQ(0, p[0].a);
-  EXPECT_EQ(0, p[0].b);
-  EXPECT_EQ(0, p[1].a);
-  EXPECT_EQ(0, p[1].b);
-}
-
-struct MoveOnly {
-  MoveOnly() = default;
-  explicit MoveOnly(int i1) : ip1{new int{i1}} {}
-  MoveOnly(int i1, int i2) : ip1{new int{i1}}, ip2{new int{i2}} {}
-  std::unique_ptr<int> ip1;
-  std::unique_ptr<int> ip2;
-};
-
-struct AcceptMoveOnly {
-  explicit AcceptMoveOnly(MoveOnly m) : m_(std::move(m)) {}
-  MoveOnly m_;
-};
-
-TEST(MakeUniqueTest, MoveOnlyTypeAndValue) {
-  using ExpectedType = std::unique_ptr<MoveOnly>;
-  {
-    auto p = absl::make_unique<MoveOnly>();
-    static_assert(std::is_same<decltype(p), ExpectedType>::value,
-                  "unexpected return type");
-    EXPECT_TRUE(!p->ip1);
-    EXPECT_TRUE(!p->ip2);
-  }
-  {
-    auto p = absl::make_unique<MoveOnly>(1);
-    static_assert(std::is_same<decltype(p), ExpectedType>::value,
-                  "unexpected return type");
-    EXPECT_TRUE(p->ip1 && *p->ip1 == 1);
-    EXPECT_TRUE(!p->ip2);
-  }
-  {
-    auto p = absl::make_unique<MoveOnly>(1, 2);
-    static_assert(std::is_same<decltype(p), ExpectedType>::value,
-                  "unexpected return type");
-    EXPECT_TRUE(p->ip1 && *p->ip1 == 1);
-    EXPECT_TRUE(p->ip2 && *p->ip2 == 2);
-  }
-}
-
-TEST(MakeUniqueTest, AcceptMoveOnly) {
-  auto p = absl::make_unique<AcceptMoveOnly>(MoveOnly());
-  p = std::unique_ptr<AcceptMoveOnly>(new AcceptMoveOnly(MoveOnly()));
-}
-
 struct ArrayWatch {
   void* operator new[](size_t n) {
     allocs().push_back(n);
@@ -164,38 +99,6 @@
   }
 };
 
-TEST(Make_UniqueTest, Array) {
-  // Ensure state is clean before we start so that these tests
-  // are order-agnostic.
-  ArrayWatch::allocs().clear();
-
-  auto p = absl::make_unique<ArrayWatch[]>(5);
-  static_assert(std::is_same<decltype(p), std::unique_ptr<ArrayWatch[]>>::value,
-                "unexpected return type");
-  EXPECT_THAT(ArrayWatch::allocs(), ElementsAre(5 * sizeof(ArrayWatch)));
-}
-
-TEST(Make_UniqueTest, NotAmbiguousWithStdMakeUnique) {
-  // Ensure that absl::make_unique is not ambiguous with std::make_unique.
-  // In C++14 mode, the below call to make_unique has both types as candidates.
-  struct TakesStdType {
-    explicit TakesStdType(const std::vector<int>& vec) {}
-  };
-  using absl::make_unique;
-  (void)make_unique<TakesStdType>(std::vector<int>());
-}
-
-#if 0
-// These tests shouldn't compile.
-TEST(MakeUniqueTestNC, AcceptMoveOnlyLvalue) {
-  auto m = MoveOnly();
-  auto p = absl::make_unique<AcceptMoveOnly>(m);
-}
-TEST(MakeUniqueTestNC, KnownBoundArray) {
-  auto p = absl::make_unique<ArrayWatch[5]>();
-}
-#endif
-
 TEST(RawPtrTest, RawPointer) {
   int i = 5;
   EXPECT_EQ(&i, absl::RawPtr(&i));
@@ -287,338 +190,6 @@
 }
 */
 
-template <typename T>
-struct SmartPointer {
-  using difference_type = char;
-};
-
-struct PointerWith {
-  using element_type = int32_t;
-  using difference_type = int16_t;
-  template <typename U>
-  using rebind = SmartPointer<U>;
-
-  static PointerWith pointer_to(
-      element_type& r) {  // NOLINT(runtime/references)
-    return PointerWith{&r};
-  }
-
-  element_type* ptr;
-};
-
-template <typename... Args>
-struct PointerWithout {};
-
-TEST(PointerTraits, Types) {
-  using TraitsWith = absl::pointer_traits<PointerWith>;
-  EXPECT_TRUE((std::is_same<TraitsWith::pointer, PointerWith>::value));
-  EXPECT_TRUE((std::is_same<TraitsWith::element_type, int32_t>::value));
-  EXPECT_TRUE((std::is_same<TraitsWith::difference_type, int16_t>::value));
-  EXPECT_TRUE((
-      std::is_same<TraitsWith::rebind<int64_t>, SmartPointer<int64_t>>::value));
-
-  using TraitsWithout = absl::pointer_traits<PointerWithout<double, int>>;
-  EXPECT_TRUE((std::is_same<TraitsWithout::pointer,
-                            PointerWithout<double, int>>::value));
-  EXPECT_TRUE((std::is_same<TraitsWithout::element_type, double>::value));
-  EXPECT_TRUE(
-      (std::is_same<TraitsWithout ::difference_type, std::ptrdiff_t>::value));
-  EXPECT_TRUE((std::is_same<TraitsWithout::rebind<int64_t>,
-                            PointerWithout<int64_t, int>>::value));
-
-  using TraitsRawPtr = absl::pointer_traits<char*>;
-  EXPECT_TRUE((std::is_same<TraitsRawPtr::pointer, char*>::value));
-  EXPECT_TRUE((std::is_same<TraitsRawPtr::element_type, char>::value));
-  EXPECT_TRUE(
-      (std::is_same<TraitsRawPtr::difference_type, std::ptrdiff_t>::value));
-  EXPECT_TRUE((std::is_same<TraitsRawPtr::rebind<int64_t>, int64_t*>::value));
-}
-
-TEST(PointerTraits, Functions) {
-  int i;
-  EXPECT_EQ(&i, absl::pointer_traits<PointerWith>::pointer_to(i).ptr);
-  EXPECT_EQ(&i, absl::pointer_traits<int*>::pointer_to(i));
-}
-
-TEST(AllocatorTraits, Typedefs) {
-  struct A {
-    struct value_type {};
-  };
-  EXPECT_TRUE((
-      std::is_same<A,
-                   typename absl::allocator_traits<A>::allocator_type>::value));
-  EXPECT_TRUE(
-      (std::is_same<A::value_type,
-                    typename absl::allocator_traits<A>::value_type>::value));
-
-  struct X {};
-  struct HasPointer {
-    using value_type = X;
-    using pointer = SmartPointer<X>;
-  };
-  EXPECT_TRUE((std::is_same<SmartPointer<X>, typename absl::allocator_traits<
-                                                 HasPointer>::pointer>::value));
-  EXPECT_TRUE(
-      (std::is_same<A::value_type*,
-                    typename absl::allocator_traits<A>::pointer>::value));
-
-  EXPECT_TRUE(
-      (std::is_same<
-          SmartPointer<const X>,
-          typename absl::allocator_traits<HasPointer>::const_pointer>::value));
-  EXPECT_TRUE(
-      (std::is_same<const A::value_type*,
-                    typename absl::allocator_traits<A>::const_pointer>::value));
-
-  struct HasVoidPointer {
-    using value_type = X;
-    struct void_pointer {};
-  };
-
-  EXPECT_TRUE((std::is_same<HasVoidPointer::void_pointer,
-                            typename absl::allocator_traits<
-                                HasVoidPointer>::void_pointer>::value));
-  EXPECT_TRUE(
-      (std::is_same<SmartPointer<void>, typename absl::allocator_traits<
-                                            HasPointer>::void_pointer>::value));
-
-  struct HasConstVoidPointer {
-    using value_type = X;
-    struct const_void_pointer {};
-  };
-
-  EXPECT_TRUE(
-      (std::is_same<HasConstVoidPointer::const_void_pointer,
-                    typename absl::allocator_traits<
-                        HasConstVoidPointer>::const_void_pointer>::value));
-  EXPECT_TRUE((std::is_same<SmartPointer<const void>,
-                            typename absl::allocator_traits<
-                                HasPointer>::const_void_pointer>::value));
-
-  struct HasDifferenceType {
-    using value_type = X;
-    using difference_type = int;
-  };
-  EXPECT_TRUE(
-      (std::is_same<int, typename absl::allocator_traits<
-                             HasDifferenceType>::difference_type>::value));
-  EXPECT_TRUE((std::is_same<char, typename absl::allocator_traits<
-                                      HasPointer>::difference_type>::value));
-
-  struct HasSizeType {
-    using value_type = X;
-    using size_type = unsigned int;
-  };
-  EXPECT_TRUE((std::is_same<unsigned int, typename absl::allocator_traits<
-                                              HasSizeType>::size_type>::value));
-  EXPECT_TRUE((std::is_same<unsigned char, typename absl::allocator_traits<
-                                               HasPointer>::size_type>::value));
-
-  struct HasPropagateOnCopy {
-    using value_type = X;
-    struct propagate_on_container_copy_assignment {};
-  };
-
-  EXPECT_TRUE(
-      (std::is_same<HasPropagateOnCopy::propagate_on_container_copy_assignment,
-                    typename absl::allocator_traits<HasPropagateOnCopy>::
-                        propagate_on_container_copy_assignment>::value));
-  EXPECT_TRUE(
-      (std::is_same<std::false_type,
-                    typename absl::allocator_traits<
-                        A>::propagate_on_container_copy_assignment>::value));
-
-  struct HasPropagateOnMove {
-    using value_type = X;
-    struct propagate_on_container_move_assignment {};
-  };
-
-  EXPECT_TRUE(
-      (std::is_same<HasPropagateOnMove::propagate_on_container_move_assignment,
-                    typename absl::allocator_traits<HasPropagateOnMove>::
-                        propagate_on_container_move_assignment>::value));
-  EXPECT_TRUE(
-      (std::is_same<std::false_type,
-                    typename absl::allocator_traits<
-                        A>::propagate_on_container_move_assignment>::value));
-
-  struct HasPropagateOnSwap {
-    using value_type = X;
-    struct propagate_on_container_swap {};
-  };
-
-  EXPECT_TRUE(
-      (std::is_same<HasPropagateOnSwap::propagate_on_container_swap,
-                    typename absl::allocator_traits<HasPropagateOnSwap>::
-                        propagate_on_container_swap>::value));
-  EXPECT_TRUE(
-      (std::is_same<std::false_type, typename absl::allocator_traits<A>::
-                                         propagate_on_container_swap>::value));
-
-  struct HasIsAlwaysEqual {
-    using value_type = X;
-    struct is_always_equal {};
-  };
-
-  EXPECT_TRUE((std::is_same<HasIsAlwaysEqual::is_always_equal,
-                            typename absl::allocator_traits<
-                                HasIsAlwaysEqual>::is_always_equal>::value));
-  EXPECT_TRUE((std::is_same<std::true_type, typename absl::allocator_traits<
-                                                A>::is_always_equal>::value));
-  struct NonEmpty {
-    using value_type = X;
-    int i;
-  };
-  EXPECT_TRUE(
-      (std::is_same<std::false_type,
-                    absl::allocator_traits<NonEmpty>::is_always_equal>::value));
-}
-
-template <typename T>
-struct AllocWithPrivateInheritance : private std::allocator<T> {
-  using value_type = T;
-};
-
-TEST(AllocatorTraits, RebindWithPrivateInheritance) {
-  // Regression test for some versions of gcc that do not like the sfinae we
-  // used in combination with private inheritance.
-  EXPECT_TRUE(
-      (std::is_same<AllocWithPrivateInheritance<int>,
-                    absl::allocator_traits<AllocWithPrivateInheritance<char>>::
-                        rebind_alloc<int>>::value));
-}
-
-template <typename T>
-struct Rebound {};
-
-struct AllocWithRebind {
-  using value_type = int;
-  template <typename T>
-  struct rebind {
-    using other = Rebound<T>;
-  };
-};
-
-template <typename T, typename U>
-struct AllocWithoutRebind {
-  using value_type = int;
-};
-
-TEST(AllocatorTraits, Rebind) {
-  EXPECT_TRUE(
-      (std::is_same<Rebound<int>,
-                    typename absl::allocator_traits<
-                        AllocWithRebind>::template rebind_alloc<int>>::value));
-  EXPECT_TRUE(
-      (std::is_same<absl::allocator_traits<Rebound<int>>,
-                    typename absl::allocator_traits<
-                        AllocWithRebind>::template rebind_traits<int>>::value));
-
-  EXPECT_TRUE(
-      (std::is_same<AllocWithoutRebind<double, char>,
-                    typename absl::allocator_traits<AllocWithoutRebind<
-                        int, char>>::template rebind_alloc<double>>::value));
-  EXPECT_TRUE(
-      (std::is_same<absl::allocator_traits<AllocWithoutRebind<double, char>>,
-                    typename absl::allocator_traits<AllocWithoutRebind<
-                        int, char>>::template rebind_traits<double>>::value));
-}
-
-struct TestValue {
-  TestValue() {}
-  explicit TestValue(int* trace) : trace(trace) { ++*trace; }
-  ~TestValue() {
-    if (trace) --*trace;
-  }
-  int* trace = nullptr;
-};
-
-struct MinimalMockAllocator {
-  MinimalMockAllocator() : value(0) {}
-  explicit MinimalMockAllocator(int value) : value(value) {}
-  MinimalMockAllocator(const MinimalMockAllocator& other)
-      : value(other.value) {}
-  using value_type = TestValue;
-  MOCK_METHOD(value_type*, allocate, (size_t));
-  MOCK_METHOD(void, deallocate, (value_type*, size_t));
-
-  int value;
-};
-
-TEST(AllocatorTraits, FunctionsMinimal) {
-  int trace = 0;
-  int hint;
-  alignas(TestValue) char buffer[sizeof(TestValue)];
-  auto* x = reinterpret_cast<TestValue*>(buffer);
-  MinimalMockAllocator mock;
-  using Traits = absl::allocator_traits<MinimalMockAllocator>;
-  EXPECT_CALL(mock, allocate(7)).WillRepeatedly(Return(x));
-  EXPECT_CALL(mock, deallocate(x, 7));
-
-  EXPECT_EQ(x, Traits::allocate(mock, 7));
-  static_cast<void>(Traits::allocate(mock, 7, static_cast<const void*>(&hint)));
-  EXPECT_EQ(x, Traits::allocate(mock, 7, static_cast<const void*>(&hint)));
-  Traits::deallocate(mock, x, 7);
-
-  EXPECT_EQ(0, trace);
-  Traits::construct(mock, x, &trace);
-  EXPECT_EQ(1, trace);
-  Traits::destroy(mock, x);
-  EXPECT_EQ(0, trace);
-
-  EXPECT_EQ(std::numeric_limits<size_t>::max() / sizeof(TestValue),
-            Traits::max_size(mock));
-
-  EXPECT_EQ(0, mock.value);
-  EXPECT_EQ(0, Traits::select_on_container_copy_construction(mock).value);
-}
-
-struct FullMockAllocator {
-  FullMockAllocator() : value(0) {}
-  explicit FullMockAllocator(int value) : value(value) {}
-  FullMockAllocator(const FullMockAllocator& other) : value(other.value) {}
-  using value_type = TestValue;
-  MOCK_METHOD(value_type*, allocate, (size_t));
-  MOCK_METHOD(value_type*, allocate, (size_t, const void*));
-  MOCK_METHOD(void, construct, (value_type*, int*));
-  MOCK_METHOD(void, destroy, (value_type*));
-  MOCK_METHOD(size_t, max_size, (),
-              (const));
-  MOCK_METHOD(FullMockAllocator, select_on_container_copy_construction, (),
-              (const));
-
-  int value;
-};
-
-TEST(AllocatorTraits, FunctionsFull) {
-  int trace = 0;
-  int hint;
-  TestValue x(&trace), y;
-  FullMockAllocator mock;
-  using Traits = absl::allocator_traits<FullMockAllocator>;
-  EXPECT_CALL(mock, allocate(7)).WillRepeatedly(Return(&x));
-  EXPECT_CALL(mock, allocate(13, &hint)).WillRepeatedly(Return(&y));
-  EXPECT_CALL(mock, construct(&x, &trace));
-  EXPECT_CALL(mock, destroy(&x));
-  EXPECT_CALL(mock, max_size()).WillRepeatedly(Return(17));
-  EXPECT_CALL(mock, select_on_container_copy_construction())
-      .WillRepeatedly(Return(FullMockAllocator(23)));
-
-  EXPECT_EQ(&x, Traits::allocate(mock, 7));
-  EXPECT_EQ(&y, Traits::allocate(mock, 13, static_cast<const void*>(&hint)));
-
-  EXPECT_EQ(1, trace);
-  Traits::construct(mock, &x, &trace);
-  EXPECT_EQ(1, trace);
-  Traits::destroy(mock, &x);
-  EXPECT_EQ(1, trace);
-
-  EXPECT_EQ(17, Traits::max_size(mock));
-
-  EXPECT_EQ(0, mock.value);
-  EXPECT_EQ(23, Traits::select_on_container_copy_construction(mock).value);
-}
-
 TEST(AllocatorNoThrowTest, DefaultAllocator) {
 #if defined(ABSL_ALLOCATOR_NOTHROW) && ABSL_ALLOCATOR_NOTHROW
   EXPECT_TRUE(absl::default_allocator_is_nothrow::value);
diff --git a/absl/meta/BUILD.bazel b/absl/meta/BUILD.bazel
index 4478ccf..125446f 100644
--- a/absl/meta/BUILD.bazel
+++ b/absl/meta/BUILD.bazel
@@ -42,7 +42,9 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":type_traits",
+        "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/time",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/meta/CMakeLists.txt b/absl/meta/CMakeLists.txt
index f16f17b..bb767d1 100644
--- a/absl/meta/CMakeLists.txt
+++ b/absl/meta/CMakeLists.txt
@@ -34,6 +34,8 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
+    absl::config
+    absl::time
     absl::core_headers
     absl::type_traits
     GTest::gmock_main
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h
index 6e6001f..abaf96a 100644
--- a/absl/meta/type_traits.h
+++ b/absl/meta/type_traits.h
@@ -41,12 +41,6 @@
 
 #include "absl/base/config.h"
 
-// MSVC constructibility traits do not detect destructor properties and so our
-// implementations should not use them as a source-of-truth.
-#if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__)
-#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__)
@@ -58,74 +52,13 @@
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
-// Defined and documented later on in this file.
-template <typename T>
-struct is_trivially_destructible;
-
-// Defined and documented later on in this file.
-template <typename T>
-struct is_trivially_move_assignable;
-
 namespace type_traits_internal {
 
-// Silence MSVC warnings about the destructor being defined as deleted.
-#if defined(_MSC_VER) && !defined(__GNUC__)
-#pragma warning(push)
-#pragma warning(disable : 4624)
-#endif  // defined(_MSC_VER) && !defined(__GNUC__)
-
-template <class T>
-union SingleMemberUnion {
-  T t;
-};
-
-// Restore the state of the destructor warning that was silenced above.
-#if defined(_MSC_VER) && !defined(__GNUC__)
-#pragma warning(pop)
-#endif  // defined(_MSC_VER) && !defined(__GNUC__)
-
-template <class T>
-struct IsTriviallyMoveConstructibleObject
-    : std::integral_constant<
-          bool, std::is_move_constructible<
-                    type_traits_internal::SingleMemberUnion<T>>::value &&
-                    absl::is_trivially_destructible<T>::value> {};
-
-template <class T>
-struct IsTriviallyCopyConstructibleObject
-    : std::integral_constant<
-          bool, std::is_copy_constructible<
-                    type_traits_internal::SingleMemberUnion<T>>::value &&
-                    absl::is_trivially_destructible<T>::value> {};
-
-template <class T>
-struct IsTriviallyMoveAssignableReference : std::false_type {};
-
-template <class T>
-struct IsTriviallyMoveAssignableReference<T&>
-    : absl::is_trivially_move_assignable<T>::type {};
-
-template <class T>
-struct IsTriviallyMoveAssignableReference<T&&>
-    : absl::is_trivially_move_assignable<T>::type {};
-
 template <typename... Ts>
 struct VoidTImpl {
   using type = void;
 };
 
-// This trick to retrieve a default alignment is necessary for our
-// implementation of aligned_storage_t to be consistent with any implementation
-// of std::aligned_storage.
-template <size_t Len, typename T = std::aligned_storage<Len>>
-struct default_alignment_of_aligned_storage;
-
-template <size_t Len, size_t Align>
-struct default_alignment_of_aligned_storage<Len,
-                                            std::aligned_storage<Len, Align>> {
-  static constexpr size_t value = Align;
-};
-
 ////////////////////////////////
 // Library Fundamentals V2 TS //
 ////////////////////////////////
@@ -169,39 +102,8 @@
 struct is_detected_convertible
     : is_detected_convertible_impl<void, To, Op, Args...>::type {};
 
-template <typename T>
-using IsCopyAssignableImpl =
-    decltype(std::declval<T&>() = std::declval<const T&>());
-
-template <typename T>
-using IsMoveAssignableImpl = decltype(std::declval<T&>() = std::declval<T&&>());
-
 }  // namespace type_traits_internal
 
-// MSVC 19.20 has a regression that causes our workarounds to fail, but their
-// std forms now appear to be compliant.
-#if defined(_MSC_VER) && !defined(__clang__) && (_MSC_VER >= 1920)
-
-template <typename T>
-using is_copy_assignable = std::is_copy_assignable<T>;
-
-template <typename T>
-using is_move_assignable = std::is_move_assignable<T>;
-
-#else
-
-template <typename T>
-struct is_copy_assignable : type_traits_internal::is_detected<
-                                type_traits_internal::IsCopyAssignableImpl, T> {
-};
-
-template <typename T>
-struct is_move_assignable : type_traits_internal::is_detected<
-                                type_traits_internal::IsMoveAssignableImpl, T> {
-};
-
-#endif
-
 // void_t()
 //
 // Ignores the type of any its arguments and returns `void`. In general, this
@@ -282,246 +184,29 @@
           bool, !(std::is_reference<T>::value ||
                   std::is_const<typename std::add_const<T>::type>::value)> {};
 
+// is_copy_assignable()
+// is_move_assignable()
 // is_trivially_destructible()
-//
-// Determines whether the passed type `T` is trivially destructible.
-//
-// This metafunction is designed to be a drop-in replacement for the C++11
-// `std::is_trivially_destructible()` metafunction for platforms that have
-// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
-// fully support C++11, we check whether this yields the same result as the std
-// implementation.
-//
-// NOTE: the extensions (__has_trivial_xxx) are implemented in gcc (version >=
-// 4.3) and clang. Since we are supporting libstdc++ > 4.7, they should always
-// be present. These  extensions are documented at
-// https://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html#Type-Traits.
-template <typename T>
-struct is_trivially_destructible
-#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
-    : std::is_trivially_destructible<T> {
-#else
-    : std::integral_constant<bool, __has_trivial_destructor(T) &&
-                                   std::is_destructible<T>::value> {
-#endif
-#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
- private:
-  static constexpr bool compliant = std::is_trivially_destructible<T>::value ==
-                                    is_trivially_destructible::value;
-  static_assert(compliant || std::is_trivially_destructible<T>::value,
-                "Not compliant with std::is_trivially_destructible; "
-                "Standard: false, Implementation: true");
-  static_assert(compliant || !std::is_trivially_destructible<T>::value,
-                "Not compliant with std::is_trivially_destructible; "
-                "Standard: true, Implementation: false");
-#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
-};
-
 // is_trivially_default_constructible()
-//
-// Determines whether the passed type `T` is trivially default constructible.
-//
-// This metafunction is designed to be a drop-in replacement for the C++11
-// `std::is_trivially_default_constructible()` metafunction for platforms that
-// have incomplete C++11 support (such as libstdc++ 4.x). On any platforms that
-// do fully support C++11, we check whether this yields the same result as the
-// std implementation.
-//
-// NOTE: according to the C++ standard, Section: 20.15.4.3 [meta.unary.prop]
-// "The predicate condition for a template specialization is_constructible<T,
-// Args...> shall be satisfied if and only if the following variable
-// definition would be well-formed for some invented variable t:
-//
-// T t(declval<Args>()...);
-//
-// is_trivially_constructible<T, Args...> additionally requires that the
-// variable definition does not call any operation that is not trivial.
-// For the purposes of this check, the call to std::declval is considered
-// trivial."
-//
-// Notes from https://en.cppreference.com/w/cpp/types/is_constructible:
-// In many implementations, is_nothrow_constructible also checks if the
-// destructor throws because it is effectively noexcept(T(arg)). Same
-// applies to is_trivially_constructible, which, in these implementations, also
-// requires that the destructor is trivial.
-// GCC bug 51452: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452
-// LWG issue 2116: http://cplusplus.github.io/LWG/lwg-active.html#2116.
-//
-// "T obj();" need to be well-formed and not call any nontrivial operation.
-// Nontrivially destructible types will cause the expression to be nontrivial.
-template <typename T>
-struct is_trivially_default_constructible
-#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE)
-    : std::is_trivially_default_constructible<T> {
-#else
-    : std::integral_constant<bool, __has_trivial_constructor(T) &&
-                                   std::is_default_constructible<T>::value &&
-                                   is_trivially_destructible<T>::value> {
-#endif
-#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \
-    !defined(                                            \
-        ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION)
- private:
-  static constexpr bool compliant =
-      std::is_trivially_default_constructible<T>::value ==
-      is_trivially_default_constructible::value;
-  static_assert(compliant || std::is_trivially_default_constructible<T>::value,
-                "Not compliant with std::is_trivially_default_constructible; "
-                "Standard: false, Implementation: true");
-  static_assert(compliant || !std::is_trivially_default_constructible<T>::value,
-                "Not compliant with std::is_trivially_default_constructible; "
-                "Standard: true, Implementation: false");
-#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
-};
-
 // is_trivially_move_constructible()
-//
-// Determines whether the passed type `T` is trivially move constructible.
-//
-// This metafunction is designed to be a drop-in replacement for the C++11
-// `std::is_trivially_move_constructible()` metafunction for platforms that have
-// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
-// fully support C++11, we check whether this yields the same result as the std
-// implementation.
-//
-// NOTE: `T obj(declval<T>());` needs to be well-formed and not call any
-// nontrivial operation.  Nontrivially destructible types will cause the
-// expression to be nontrivial.
-template <typename T>
-struct is_trivially_move_constructible
-#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE)
-    : std::is_trivially_move_constructible<T> {
-#else
-    : std::conditional<
-          std::is_object<T>::value && !std::is_array<T>::value,
-          type_traits_internal::IsTriviallyMoveConstructibleObject<T>,
-          std::is_reference<T>>::type::type {
-#endif
-#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \
-    !defined(                                            \
-        ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION)
- private:
-  static constexpr bool compliant =
-      std::is_trivially_move_constructible<T>::value ==
-      is_trivially_move_constructible::value;
-  static_assert(compliant || std::is_trivially_move_constructible<T>::value,
-                "Not compliant with std::is_trivially_move_constructible; "
-                "Standard: false, Implementation: true");
-  static_assert(compliant || !std::is_trivially_move_constructible<T>::value,
-                "Not compliant with std::is_trivially_move_constructible; "
-                "Standard: true, Implementation: false");
-#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
-};
-
 // is_trivially_copy_constructible()
-//
-// Determines whether the passed type `T` is trivially copy constructible.
-//
-// This metafunction is designed to be a drop-in replacement for the C++11
-// `std::is_trivially_copy_constructible()` metafunction for platforms that have
-// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
-// fully support C++11, we check whether this yields the same result as the std
-// implementation.
-//
-// NOTE: `T obj(declval<const T&>());` needs to be well-formed and not call any
-// nontrivial operation.  Nontrivially destructible types will cause the
-// expression to be nontrivial.
-template <typename T>
-struct is_trivially_copy_constructible
-    : std::conditional<
-          std::is_object<T>::value && !std::is_array<T>::value,
-          type_traits_internal::IsTriviallyCopyConstructibleObject<T>,
-          std::is_lvalue_reference<T>>::type::type {
-#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \
-    !defined(                                            \
-        ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION)
- private:
-  static constexpr bool compliant =
-      std::is_trivially_copy_constructible<T>::value ==
-      is_trivially_copy_constructible::value;
-  static_assert(compliant || std::is_trivially_copy_constructible<T>::value,
-                "Not compliant with std::is_trivially_copy_constructible; "
-                "Standard: false, Implementation: true");
-  static_assert(compliant || !std::is_trivially_copy_constructible<T>::value,
-                "Not compliant with std::is_trivially_copy_constructible; "
-                "Standard: true, Implementation: false");
-#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
-};
-
 // is_trivially_move_assignable()
-//
-// Determines whether the passed type `T` is trivially move assignable.
-//
-// This metafunction is designed to be a drop-in replacement for the C++11
-// `std::is_trivially_move_assignable()` metafunction for platforms that have
-// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
-// fully support C++11, we check whether this yields the same result as the std
-// implementation.
-//
-// NOTE: `is_assignable<T, U>::value` is `true` if the expression
-// `declval<T>() = declval<U>()` is well-formed when treated as an unevaluated
-// operand. `is_trivially_assignable<T, U>` requires the assignment to call no
-// operation that is not trivial. `is_trivially_copy_assignable<T>` is simply
-// `is_trivially_assignable<T&, T>`.
-template <typename T>
-struct is_trivially_move_assignable
-    : std::conditional<
-          std::is_object<T>::value && !std::is_array<T>::value &&
-              std::is_move_assignable<T>::value,
-          std::is_move_assignable<type_traits_internal::SingleMemberUnion<T>>,
-          type_traits_internal::IsTriviallyMoveAssignableReference<T>>::type::
-          type {
-#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
- private:
-  static constexpr bool compliant =
-      std::is_trivially_move_assignable<T>::value ==
-      is_trivially_move_assignable::value;
-  static_assert(compliant || std::is_trivially_move_assignable<T>::value,
-                "Not compliant with std::is_trivially_move_assignable; "
-                "Standard: false, Implementation: true");
-  static_assert(compliant || !std::is_trivially_move_assignable<T>::value,
-                "Not compliant with std::is_trivially_move_assignable; "
-                "Standard: true, Implementation: false");
-#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
-};
-
 // is_trivially_copy_assignable()
 //
-// Determines whether the passed type `T` is trivially copy assignable.
+// Historical note: Abseil once provided implementations of these type traits
+// for platforms that lacked full support. New code should prefer to use the
+// std variants.
 //
-// This metafunction is designed to be a drop-in replacement for the C++11
-// `std::is_trivially_copy_assignable()` metafunction for platforms that have
-// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
-// fully support C++11, we check whether this yields the same result as the std
-// implementation.
-//
-// NOTE: `is_assignable<T, U>::value` is `true` if the expression
-// `declval<T>() = declval<U>()` is well-formed when treated as an unevaluated
-// operand. `is_trivially_assignable<T, U>` requires the assignment to call no
-// operation that is not trivial. `is_trivially_copy_assignable<T>` is simply
-// `is_trivially_assignable<T&, const T&>`.
-template <typename T>
-struct is_trivially_copy_assignable
-#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
-    : std::is_trivially_copy_assignable<T> {
-#else
-    : std::integral_constant<
-          bool, __has_trivial_assign(typename std::remove_reference<T>::type) &&
-                    absl::is_copy_assignable<T>::value> {
-#endif
-#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
- private:
-  static constexpr bool compliant =
-      std::is_trivially_copy_assignable<T>::value ==
-      is_trivially_copy_assignable::value;
-  static_assert(compliant || std::is_trivially_copy_assignable<T>::value,
-                "Not compliant with std::is_trivially_copy_assignable; "
-                "Standard: false, Implementation: true");
-  static_assert(compliant || !std::is_trivially_copy_assignable<T>::value,
-                "Not compliant with std::is_trivially_copy_assignable; "
-                "Standard: true, Implementation: false");
-#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
-};
+// See the documentation for the STL <type_traits> header for more information:
+// https://en.cppreference.com/w/cpp/header/type_traits
+using std::is_copy_assignable;
+using std::is_move_assignable;
+using std::is_trivially_copy_assignable;
+using std::is_trivially_copy_constructible;
+using std::is_trivially_default_constructible;
+using std::is_trivially_destructible;
+using std::is_trivially_move_assignable;
+using std::is_trivially_move_constructible;
 
 #if defined(__cpp_lib_remove_cvref) && __cpp_lib_remove_cvref >= 201711L
 template <typename T>
@@ -544,55 +229,6 @@
 using remove_cvref_t = typename remove_cvref<T>::type;
 #endif
 
-namespace type_traits_internal {
-// is_trivially_copyable()
-//
-// Determines whether the passed type `T` is trivially copyable.
-//
-// This metafunction is designed to be a drop-in replacement for the C++11
-// `std::is_trivially_copyable()` metafunction for platforms that have
-// incomplete C++11 support (such as libstdc++ 4.x). We use the C++17 definition
-// of TriviallyCopyable.
-//
-// NOTE: `is_trivially_copyable<T>::value` is `true` if all of T's copy/move
-// constructors/assignment operators are trivial or deleted, T has at least
-// one non-deleted copy/move constructor/assignment operator, and T is trivially
-// destructible. Arrays of trivially copyable types are trivially copyable.
-//
-// We expose this metafunction only for internal use within absl.
-
-#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE)
-template <typename T>
-struct is_trivially_copyable : std::is_trivially_copyable<T> {};
-#else
-template <typename T>
-class is_trivially_copyable_impl {
-  using ExtentsRemoved = typename std::remove_all_extents<T>::type;
-  static constexpr bool kIsCopyOrMoveConstructible =
-      std::is_copy_constructible<ExtentsRemoved>::value ||
-      std::is_move_constructible<ExtentsRemoved>::value;
-  static constexpr bool kIsCopyOrMoveAssignable =
-      absl::is_copy_assignable<ExtentsRemoved>::value ||
-      absl::is_move_assignable<ExtentsRemoved>::value;
-
- public:
-  static constexpr bool kValue =
-      (__has_trivial_copy(ExtentsRemoved) || !kIsCopyOrMoveConstructible) &&
-      (__has_trivial_assign(ExtentsRemoved) || !kIsCopyOrMoveAssignable) &&
-      (kIsCopyOrMoveConstructible || kIsCopyOrMoveAssignable) &&
-      is_trivially_destructible<ExtentsRemoved>::value &&
-      // We need to check for this explicitly because otherwise we'll say
-      // references are trivial copyable when compiled by MSVC.
-      !std::is_reference<ExtentsRemoved>::value;
-};
-
-template <typename T>
-struct is_trivially_copyable
-    : std::integral_constant<
-          bool, type_traits_internal::is_trivially_copyable_impl<T>::kValue> {};
-#endif
-}  // namespace type_traits_internal
-
 // -----------------------------------------------------------------------------
 // C++14 "_t" trait aliases
 // -----------------------------------------------------------------------------
@@ -642,6 +278,21 @@
 template <typename T>
 using remove_all_extents_t = typename std::remove_all_extents<T>::type;
 
+namespace type_traits_internal {
+// This trick to retrieve a default alignment is necessary for our
+// implementation of aligned_storage_t to be consistent with any
+// implementation of std::aligned_storage.
+template <size_t Len, typename T = std::aligned_storage<Len>>
+struct default_alignment_of_aligned_storage;
+
+template <size_t Len, size_t Align>
+struct default_alignment_of_aligned_storage<
+    Len, std::aligned_storage<Len, Align>> {
+  static constexpr size_t value = Align;
+};
+}  // namespace type_traits_internal
+
+// TODO(b/260219225): std::aligned_storage(_t) is deprecated in C++23.
 template <size_t Len, size_t Align = type_traits_internal::
                           default_alignment_of_aligned_storage<Len>::value>
 using aligned_storage_t = typename std::aligned_storage<Len, Align>::type;
@@ -815,9 +466,14 @@
 }  // namespace type_traits_internal
 
 // absl::is_trivially_relocatable<T>
-// Detects whether a type is "trivially relocatable" -- meaning it can be
-// relocated without invoking the constructor/destructor, using a form of move
-// elision.
+//
+// Detects whether a type is known to be "trivially relocatable" -- meaning it
+// can be relocated without invoking the constructor/destructor, using a form of
+// move elision.
+//
+// This trait is conservative, for backwards compatibility. If it's true then
+// the type is definitely trivially relocatable, but if it's false then the type
+// may or may not be.
 //
 // Example:
 //
@@ -831,16 +487,70 @@
 // Upstream documentation:
 //
 // https://clang.llvm.org/docs/LanguageExtensions.html#:~:text=__is_trivially_relocatable
+
+// If the compiler offers a builtin that tells us the answer, we can use that.
+// This covers all of the cases in the fallback below, plus types that opt in
+// using e.g. [[clang::trivial_abi]].
 //
-#if ABSL_HAVE_BUILTIN(__is_trivially_relocatable)
+// Clang on Windows has the builtin, but it falsely claims types with a
+// user-provided destructor are trivial (http://b/275003464). So we opt out
+// there.
+//
+// TODO(b/275003464): remove the opt-out once the bug is fixed.
+#if ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \
+    !(defined(__clang__) && (defined(_WIN32) || defined(_WIN64)))
 template <class T>
 struct is_trivially_relocatable
     : std::integral_constant<bool, __is_trivially_relocatable(T)> {};
 #else
+// Otherwise we use a fallback that detects only those types we can feasibly
+// detect. Any time that has trivial move-construction and destruction
+// operations is by definition trivially relocatable.
 template <class T>
-struct is_trivially_relocatable : std::integral_constant<bool, false> {};
+struct is_trivially_relocatable
+    : absl::conjunction<absl::is_trivially_move_constructible<T>,
+                        absl::is_trivially_destructible<T>> {};
 #endif
 
+// absl::is_constant_evaluated()
+//
+// Detects whether the function call occurs within a constant-evaluated context.
+// Returns true if the evaluation of the call occurs within the evaluation of an
+// expression or conversion that is manifestly constant-evaluated; otherwise
+// returns false.
+//
+// This function is implemented in terms of `std::is_constant_evaluated` for
+// c++20 and up. For older c++ versions, the function is implemented in terms
+// of `__builtin_is_constant_evaluated` if available, otherwise the function
+// will fail to compile.
+//
+// Applications can inspect `ABSL_HAVE_CONSTANT_EVALUATED` at compile time
+// to check if this function is supported.
+//
+// Example:
+//
+// constexpr MyClass::MyClass(int param) {
+// #ifdef ABSL_HAVE_CONSTANT_EVALUATED
+//   if (!absl::is_constant_evaluated()) {
+//     ABSL_LOG(INFO) << "MyClass(" << param << ")";
+//   }
+// #endif  // ABSL_HAVE_CONSTANT_EVALUATED
+// }
+//
+// Upstream documentation:
+//
+// http://en.cppreference.com/w/cpp/types/is_constant_evaluated
+// http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#:~:text=__builtin_is_constant_evaluated
+//
+#if defined(ABSL_HAVE_CONSTANT_EVALUATED)
+constexpr bool is_constant_evaluated() noexcept {
+#ifdef __cpp_lib_is_constant_evaluated
+  return std::is_constant_evaluated();
+#elif ABSL_HAVE_BUILTIN(__builtin_is_constant_evaluated)
+  return __builtin_is_constant_evaluated();
+#endif
+}
+#endif  // ABSL_HAVE_CONSTANT_EVALUATED
 ABSL_NAMESPACE_END
 }  // namespace absl
 
diff --git a/absl/meta/type_traits_test.cc b/absl/meta/type_traits_test.cc
index d08d9ad..7412f33 100644
--- a/absl/meta/type_traits_test.cc
+++ b/absl/meta/type_traits_test.cc
@@ -22,6 +22,9 @@
 
 #include "gtest/gtest.h"
 #include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
 
 namespace {
 
@@ -349,29 +352,6 @@
   virtual ~Base() {}
 };
 
-// Old versions of libc++, around Clang 3.5 to 3.6, consider deleted destructors
-// as also being trivial. With the resolution of CWG 1928 and CWG 1734, this
-// is no longer considered true and has thus been amended.
-// Compiler Explorer: https://godbolt.org/g/zT59ZL
-// CWG issue 1734: http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1734
-// CWG issue 1928: http://open-std.org/JTC1/SC22/WG21/docs/cwg_closed.html#1928
-#if !defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 3700
-#define ABSL_TRIVIALLY_DESTRUCTIBLE_CONSIDER_DELETED_DESTRUCTOR_NOT_TRIVIAL 1
-#endif
-
-// As of the moment, GCC versions >5.1 have a problem compiling for
-// std::is_trivially_default_constructible<NontrivialDestructor[10]>, where
-// NontrivialDestructor is a struct with a custom nontrivial destructor. Note
-// that this problem only occurs for arrays of a known size, so something like
-// std::is_trivially_default_constructible<NontrivialDestructor[]> does not
-// have any problems.
-// Compiler Explorer: https://godbolt.org/g/dXRbdK
-// GCC bug 83689: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83689
-#if defined(__clang__) || defined(_MSC_VER) || \
-    (defined(__GNUC__) && __GNUC__ < 5)
-#define ABSL_GCC_BUG_TRIVIALLY_CONSTRUCTIBLE_ON_ARRAY_OF_NONTRIVIAL 1
-#endif
-
 TEST(TypeTraitsTest, TestIsFunction) {
   struct Callable {
     void operator()() {}
@@ -388,562 +368,6 @@
   EXPECT_FALSE(absl::is_function<Callable>::value);
 }
 
-TEST(TypeTraitsTest, TestTrivialDestructor) {
-  // Verify that arithmetic types and pointers have trivial destructors.
-  EXPECT_TRUE(absl::is_trivially_destructible<bool>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<char>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<unsigned char>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<signed char>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<wchar_t>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<int>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<unsigned int>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<int16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<uint16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<int64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<uint64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<float>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<double>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<long double>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<const std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<const Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<std::string**>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<Trivial**>::value);
-
-  // classes with destructors
-  EXPECT_TRUE(absl::is_trivially_destructible<Trivial>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<TrivialDestructor>::value);
-
-  // Verify that types with a nontrivial or deleted destructor
-  // are marked as such.
-  EXPECT_FALSE(absl::is_trivially_destructible<NontrivialDestructor>::value);
-#ifdef ABSL_TRIVIALLY_DESTRUCTIBLE_CONSIDER_DELETED_DESTRUCTOR_NOT_TRIVIAL
-  EXPECT_FALSE(absl::is_trivially_destructible<DeletedDestructor>::value);
-#endif
-
-  // simple_pair of such types is trivial
-  EXPECT_TRUE((absl::is_trivially_destructible<simple_pair<int, int>>::value));
-  EXPECT_TRUE((absl::is_trivially_destructible<
-               simple_pair<Trivial, TrivialDestructor>>::value));
-
-  // Verify that types without trivial destructors are correctly marked as such.
-  EXPECT_FALSE(absl::is_trivially_destructible<std::string>::value);
-  EXPECT_FALSE(absl::is_trivially_destructible<std::vector<int>>::value);
-
-  // Verify that simple_pairs of types without trivial destructors
-  // are not marked as trivial.
-  EXPECT_FALSE((absl::is_trivially_destructible<
-                simple_pair<int, std::string>>::value));
-  EXPECT_FALSE((absl::is_trivially_destructible<
-                simple_pair<std::string, int>>::value));
-
-  // array of such types is trivial
-  using int10 = int[10];
-  EXPECT_TRUE(absl::is_trivially_destructible<int10>::value);
-  using Trivial10 = Trivial[10];
-  EXPECT_TRUE(absl::is_trivially_destructible<Trivial10>::value);
-  using TrivialDestructor10 = TrivialDestructor[10];
-  EXPECT_TRUE(absl::is_trivially_destructible<TrivialDestructor10>::value);
-
-  // Conversely, the opposite also holds.
-  using NontrivialDestructor10 = NontrivialDestructor[10];
-  EXPECT_FALSE(absl::is_trivially_destructible<NontrivialDestructor10>::value);
-}
-
-TEST(TypeTraitsTest, TestTrivialDefaultCtor) {
-  // arithmetic types and pointers have trivial default constructors.
-  EXPECT_TRUE(absl::is_trivially_default_constructible<bool>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<char>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<unsigned char>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<signed char>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<wchar_t>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<int>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<unsigned int>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<int16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<uint16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<int64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<uint64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<float>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<double>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<long double>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<Trivial*>::value);
-  EXPECT_TRUE(
-      absl::is_trivially_default_constructible<const std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<const Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<std::string**>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<Trivial**>::value);
-
-  // types with compiler generated default ctors
-  EXPECT_TRUE(absl::is_trivially_default_constructible<Trivial>::value);
-  EXPECT_TRUE(
-      absl::is_trivially_default_constructible<TrivialDefaultCtor>::value);
-
-  // Verify that types without them are not.
-  EXPECT_FALSE(
-      absl::is_trivially_default_constructible<NontrivialDefaultCtor>::value);
-  EXPECT_FALSE(
-      absl::is_trivially_default_constructible<DeletedDefaultCtor>::value);
-
-  // types with nontrivial destructor are nontrivial
-  EXPECT_FALSE(
-      absl::is_trivially_default_constructible<NontrivialDestructor>::value);
-
-  // types with vtables
-  EXPECT_FALSE(absl::is_trivially_default_constructible<Base>::value);
-
-  // Verify that simple_pair has trivial constructors where applicable.
-  EXPECT_TRUE((absl::is_trivially_default_constructible<
-               simple_pair<int, char*>>::value));
-  EXPECT_TRUE((absl::is_trivially_default_constructible<
-               simple_pair<int, Trivial>>::value));
-  EXPECT_TRUE((absl::is_trivially_default_constructible<
-               simple_pair<int, TrivialDefaultCtor>>::value));
-
-  // Verify that types without trivial constructors are
-  // correctly marked as such.
-  EXPECT_FALSE(absl::is_trivially_default_constructible<std::string>::value);
-  EXPECT_FALSE(
-      absl::is_trivially_default_constructible<std::vector<int>>::value);
-
-  // Verify that simple_pairs of types without trivial constructors
-  // are not marked as trivial.
-  EXPECT_FALSE((absl::is_trivially_default_constructible<
-                simple_pair<int, std::string>>::value));
-  EXPECT_FALSE((absl::is_trivially_default_constructible<
-                simple_pair<std::string, int>>::value));
-
-  // Verify that arrays of such types are trivially default constructible
-  using int10 = int[10];
-  EXPECT_TRUE(absl::is_trivially_default_constructible<int10>::value);
-  using Trivial10 = Trivial[10];
-  EXPECT_TRUE(absl::is_trivially_default_constructible<Trivial10>::value);
-  using TrivialDefaultCtor10 = TrivialDefaultCtor[10];
-  EXPECT_TRUE(
-      absl::is_trivially_default_constructible<TrivialDefaultCtor10>::value);
-
-  // Conversely, the opposite also holds.
-#ifdef ABSL_GCC_BUG_TRIVIALLY_CONSTRUCTIBLE_ON_ARRAY_OF_NONTRIVIAL
-  using NontrivialDefaultCtor10 = NontrivialDefaultCtor[10];
-  EXPECT_FALSE(
-      absl::is_trivially_default_constructible<NontrivialDefaultCtor10>::value);
-#endif
-}
-
-// GCC prior to 7.4 had a bug in its trivially-constructible traits
-// (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80654).
-// This test makes sure that we do not depend on the trait in these cases when
-// implementing absl triviality traits.
-
-template <class T>
-struct BadConstructors {
-  BadConstructors() { static_assert(T::value, ""); }
-
-  BadConstructors(BadConstructors&&) { static_assert(T::value, ""); }
-
-  BadConstructors(const BadConstructors&) { static_assert(T::value, ""); }
-};
-
-TEST(TypeTraitsTest, TestTrivialityBadConstructors) {
-  using BadType = BadConstructors<int>;
-
-  EXPECT_FALSE(absl::is_trivially_default_constructible<BadType>::value);
-  EXPECT_FALSE(absl::is_trivially_move_constructible<BadType>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_constructible<BadType>::value);
-}
-
-TEST(TypeTraitsTest, TestTrivialMoveCtor) {
-  // Verify that arithmetic types and pointers have trivial move
-  // constructors.
-  EXPECT_TRUE(absl::is_trivially_move_constructible<bool>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<char>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<unsigned char>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<signed char>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<wchar_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<int>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<unsigned int>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<int16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<uint16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<int64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<uint64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<float>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<double>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<long double>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<const std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<const Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<std::string**>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<Trivial**>::value);
-
-  // Reference types
-  EXPECT_TRUE(absl::is_trivially_move_constructible<int&>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<int&&>::value);
-
-  // types with compiler generated move ctors
-  EXPECT_TRUE(absl::is_trivially_move_constructible<Trivial>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<TrivialMoveCtor>::value);
-
-  // Verify that types without them (i.e. nontrivial or deleted) are not.
-  EXPECT_FALSE(
-      absl::is_trivially_move_constructible<NontrivialCopyCtor>::value);
-  EXPECT_FALSE(absl::is_trivially_move_constructible<DeletedCopyCtor>::value);
-  EXPECT_FALSE(
-      absl::is_trivially_move_constructible<NonCopyableOrMovable>::value);
-
-  // type with nontrivial destructor are nontrivial move construbtible
-  EXPECT_FALSE(
-      absl::is_trivially_move_constructible<NontrivialDestructor>::value);
-
-  // types with vtables
-  EXPECT_FALSE(absl::is_trivially_move_constructible<Base>::value);
-
-  // Verify that simple_pair of such types is trivially move constructible
-  EXPECT_TRUE(
-      (absl::is_trivially_move_constructible<simple_pair<int, char*>>::value));
-  EXPECT_TRUE((
-      absl::is_trivially_move_constructible<simple_pair<int, Trivial>>::value));
-  EXPECT_TRUE((absl::is_trivially_move_constructible<
-               simple_pair<int, TrivialMoveCtor>>::value));
-
-  // Verify that types without trivial move constructors are
-  // correctly marked as such.
-  EXPECT_FALSE(absl::is_trivially_move_constructible<std::string>::value);
-  EXPECT_FALSE(absl::is_trivially_move_constructible<std::vector<int>>::value);
-
-  // Verify that simple_pairs of types without trivial move constructors
-  // are not marked as trivial.
-  EXPECT_FALSE((absl::is_trivially_move_constructible<
-                simple_pair<int, std::string>>::value));
-  EXPECT_FALSE((absl::is_trivially_move_constructible<
-                simple_pair<std::string, int>>::value));
-
-  // Verify that arrays are not
-  using int10 = int[10];
-  EXPECT_FALSE(absl::is_trivially_move_constructible<int10>::value);
-}
-
-TEST(TypeTraitsTest, TestTrivialCopyCtor) {
-  // Verify that arithmetic types and pointers have trivial copy
-  // constructors.
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<bool>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<char>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<unsigned char>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<signed char>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<wchar_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<int>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<unsigned int>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<int16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<uint16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<int64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<uint64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<float>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<double>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<long double>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<const std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<const Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<std::string**>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<Trivial**>::value);
-
-  // Reference types
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<int&>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_constructible<int&&>::value);
-
-  // types with compiler generated copy ctors
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<Trivial>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<TrivialCopyCtor>::value);
-
-  // Verify that types without them (i.e. nontrivial or deleted) are not.
-  EXPECT_FALSE(
-      absl::is_trivially_copy_constructible<NontrivialCopyCtor>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_constructible<DeletedCopyCtor>::value);
-  EXPECT_FALSE(
-      absl::is_trivially_copy_constructible<MovableNonCopyable>::value);
-  EXPECT_FALSE(
-      absl::is_trivially_copy_constructible<NonCopyableOrMovable>::value);
-
-  // type with nontrivial destructor are nontrivial copy construbtible
-  EXPECT_FALSE(
-      absl::is_trivially_copy_constructible<NontrivialDestructor>::value);
-
-  // types with vtables
-  EXPECT_FALSE(absl::is_trivially_copy_constructible<Base>::value);
-
-  // Verify that simple_pair of such types is trivially copy constructible
-  EXPECT_TRUE(
-      (absl::is_trivially_copy_constructible<simple_pair<int, char*>>::value));
-  EXPECT_TRUE((
-      absl::is_trivially_copy_constructible<simple_pair<int, Trivial>>::value));
-  EXPECT_TRUE((absl::is_trivially_copy_constructible<
-               simple_pair<int, TrivialCopyCtor>>::value));
-
-  // Verify that types without trivial copy constructors are
-  // correctly marked as such.
-  EXPECT_FALSE(absl::is_trivially_copy_constructible<std::string>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_constructible<std::vector<int>>::value);
-
-  // Verify that simple_pairs of types without trivial copy constructors
-  // are not marked as trivial.
-  EXPECT_FALSE((absl::is_trivially_copy_constructible<
-                simple_pair<int, std::string>>::value));
-  EXPECT_FALSE((absl::is_trivially_copy_constructible<
-                simple_pair<std::string, int>>::value));
-
-  // Verify that arrays are not
-  using int10 = int[10];
-  EXPECT_FALSE(absl::is_trivially_copy_constructible<int10>::value);
-}
-
-TEST(TypeTraitsTest, TestTrivialMoveAssign) {
-  // Verify that arithmetic types and pointers have trivial move
-  // assignment operators.
-  EXPECT_TRUE(absl::is_trivially_move_assignable<bool>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<char>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<unsigned char>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<signed char>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<wchar_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<int>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<unsigned int>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<int16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<uint16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<int64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<uint64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<float>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<double>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<long double>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<const std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<const Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<std::string**>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial**>::value);
-
-  // const qualified types are not assignable
-  EXPECT_FALSE(absl::is_trivially_move_assignable<const int>::value);
-
-  // types with compiler generated move assignment
-  EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<TrivialMoveAssign>::value);
-
-  // Verify that types without them (i.e. nontrivial or deleted) are not.
-  EXPECT_FALSE(absl::is_trivially_move_assignable<NontrivialCopyAssign>::value);
-  EXPECT_FALSE(absl::is_trivially_move_assignable<DeletedCopyAssign>::value);
-  EXPECT_FALSE(absl::is_trivially_move_assignable<NonCopyableOrMovable>::value);
-
-  // types with vtables
-  EXPECT_FALSE(absl::is_trivially_move_assignable<Base>::value);
-
-  // Verify that simple_pair is trivially assignable
-  EXPECT_TRUE(
-      (absl::is_trivially_move_assignable<simple_pair<int, char*>>::value));
-  EXPECT_TRUE(
-      (absl::is_trivially_move_assignable<simple_pair<int, Trivial>>::value));
-  EXPECT_TRUE((absl::is_trivially_move_assignable<
-               simple_pair<int, TrivialMoveAssign>>::value));
-
-  // Verify that types not trivially move assignable are
-  // correctly marked as such.
-  EXPECT_FALSE(absl::is_trivially_move_assignable<std::string>::value);
-  EXPECT_FALSE(absl::is_trivially_move_assignable<std::vector<int>>::value);
-
-  // Verify that simple_pairs of types not trivially move assignable
-  // are not marked as trivial.
-  EXPECT_FALSE((absl::is_trivially_move_assignable<
-                simple_pair<int, std::string>>::value));
-  EXPECT_FALSE((absl::is_trivially_move_assignable<
-                simple_pair<std::string, int>>::value));
-
-  // Verify that arrays are not trivially move assignable
-  using int10 = int[10];
-  EXPECT_FALSE(absl::is_trivially_move_assignable<int10>::value);
-
-  // Verify that references are handled correctly
-  EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial&&>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial&>::value);
-}
-
-TEST(TypeTraitsTest, TestTrivialCopyAssign) {
-  // Verify that arithmetic types and pointers have trivial copy
-  // assignment operators.
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<bool>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<char>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<unsigned char>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<signed char>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<wchar_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<int>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<unsigned int>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<int16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<uint16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<int64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<uint64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<float>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<double>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<long double>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<const std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<const Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<std::string**>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial**>::value);
-
-  // const qualified types are not assignable
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<const int>::value);
-
-  // types with compiler generated copy assignment
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<TrivialCopyAssign>::value);
-
-  // Verify that types without them (i.e. nontrivial or deleted) are not.
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<NontrivialCopyAssign>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<DeletedCopyAssign>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<MovableNonCopyable>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<NonCopyableOrMovable>::value);
-
-  // types with vtables
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<Base>::value);
-
-  // Verify that simple_pair is trivially assignable
-  EXPECT_TRUE(
-      (absl::is_trivially_copy_assignable<simple_pair<int, char*>>::value));
-  EXPECT_TRUE(
-      (absl::is_trivially_copy_assignable<simple_pair<int, Trivial>>::value));
-  EXPECT_TRUE((absl::is_trivially_copy_assignable<
-               simple_pair<int, TrivialCopyAssign>>::value));
-
-  // Verify that types not trivially copy assignable are
-  // correctly marked as such.
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<std::string>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<std::vector<int>>::value);
-
-  // Verify that simple_pairs of types not trivially copy assignable
-  // are not marked as trivial.
-  EXPECT_FALSE((absl::is_trivially_copy_assignable<
-                simple_pair<int, std::string>>::value));
-  EXPECT_FALSE((absl::is_trivially_copy_assignable<
-                simple_pair<std::string, int>>::value));
-
-  // Verify that arrays are not trivially copy assignable
-  using int10 = int[10];
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<int10>::value);
-
-  // Verify that references are handled correctly
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial&&>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial&>::value);
-}
-
-TEST(TypeTraitsTest, TestTriviallyCopyable) {
-  // Verify that arithmetic types and pointers are trivially copyable.
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<bool>::value);
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<char>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<unsigned char>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<signed char>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<wchar_t>::value);
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<int>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<unsigned int>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<int16_t>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<uint16_t>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<int64_t>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<uint64_t>::value);
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<float>::value);
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<double>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<long double>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<std::string*>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<Trivial*>::value);
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<
-              const std::string*>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<const Trivial*>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<std::string**>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<Trivial**>::value);
-
-  // const qualified types are not assignable but are constructible
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<const int>::value);
-
-  // Trivial copy constructor/assignment and destructor.
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<Trivial>::value);
-  // Trivial copy assignment, but non-trivial copy constructor/destructor.
-  EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable<
-               TrivialCopyAssign>::value);
-  // Trivial copy constructor, but non-trivial assignment.
-  EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable<
-               TrivialCopyCtor>::value);
-
-  // Types with a non-trivial copy constructor/assignment
-  EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable<
-               NontrivialCopyCtor>::value);
-  EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable<
-               NontrivialCopyAssign>::value);
-
-  // Types without copy constructor/assignment, but with move
-  // MSVC disagrees with other compilers about this:
-  // EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<
-  //             MovableNonCopyable>::value);
-
-  // Types without copy/move constructor/assignment
-  EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable<
-               NonCopyableOrMovable>::value);
-
-  // No copy assign, but has trivial copy constructor.
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<
-              DeletedCopyAssign>::value);
-
-  // types with vtables
-  EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable<Base>::value);
-
-  // Verify that simple_pair is trivially copyable if members are
-  EXPECT_TRUE((absl::type_traits_internal::is_trivially_copyable<
-               simple_pair<int, char*>>::value));
-  EXPECT_TRUE((absl::type_traits_internal::is_trivially_copyable<
-               simple_pair<int, Trivial>>::value));
-
-  // Verify that types not trivially copyable are
-  // correctly marked as such.
-  EXPECT_FALSE(
-      absl::type_traits_internal::is_trivially_copyable<std::string>::value);
-  EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable<
-               std::vector<int>>::value);
-
-  // Verify that simple_pairs of types not trivially copyable
-  // are not marked as trivial.
-  EXPECT_FALSE((absl::type_traits_internal::is_trivially_copyable<
-                simple_pair<int, std::string>>::value));
-  EXPECT_FALSE((absl::type_traits_internal::is_trivially_copyable<
-                simple_pair<std::string, int>>::value));
-  EXPECT_FALSE((absl::type_traits_internal::is_trivially_copyable<
-                simple_pair<int, TrivialCopyAssign>>::value));
-
-  // Verify that arrays of trivially copyable types are trivially copyable
-  using int10 = int[10];
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<int10>::value);
-  using int10x10 = int[10][10];
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<int10x10>::value);
-
-  // Verify that references are handled correctly
-  EXPECT_FALSE(
-      absl::type_traits_internal::is_trivially_copyable<Trivial&&>::value);
-  EXPECT_FALSE(
-      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));
@@ -1238,82 +662,6 @@
   EXPECT_EQ(TypeEnum::D, GetTypeExt(Wrap<TypeD>()));
 }
 
-template <typename T>
-bool TestCopyAssign() {
-  return absl::is_copy_assignable<T>::value ==
-         std::is_copy_assignable<T>::value;
-}
-
-TEST(TypeTraitsTest, IsCopyAssignable) {
-  EXPECT_TRUE(TestCopyAssign<int>());
-  EXPECT_TRUE(TestCopyAssign<int&>());
-  EXPECT_TRUE(TestCopyAssign<int&&>());
-
-  struct S {};
-  EXPECT_TRUE(TestCopyAssign<S>());
-  EXPECT_TRUE(TestCopyAssign<S&>());
-  EXPECT_TRUE(TestCopyAssign<S&&>());
-
-  class C {
-   public:
-    explicit C(C* c) : c_(c) {}
-    ~C() { delete c_; }
-
-   private:
-    C* c_;
-  };
-  EXPECT_TRUE(TestCopyAssign<C>());
-  EXPECT_TRUE(TestCopyAssign<C&>());
-  EXPECT_TRUE(TestCopyAssign<C&&>());
-
-  // Reason for ifndef: add_lvalue_reference<T> in libc++ breaks for these cases
-#ifndef _LIBCPP_VERSION
-  EXPECT_TRUE(TestCopyAssign<int()>());
-  EXPECT_TRUE(TestCopyAssign<int(int) const>());
-  EXPECT_TRUE(TestCopyAssign<int(...) volatile&>());
-  EXPECT_TRUE(TestCopyAssign<int(int, ...) const volatile&&>());
-#endif  // _LIBCPP_VERSION
-}
-
-template <typename T>
-bool TestMoveAssign() {
-  return absl::is_move_assignable<T>::value ==
-         std::is_move_assignable<T>::value;
-}
-
-TEST(TypeTraitsTest, IsMoveAssignable) {
-  EXPECT_TRUE(TestMoveAssign<int>());
-  EXPECT_TRUE(TestMoveAssign<int&>());
-  EXPECT_TRUE(TestMoveAssign<int&&>());
-
-  struct S {};
-  EXPECT_TRUE(TestMoveAssign<S>());
-  EXPECT_TRUE(TestMoveAssign<S&>());
-  EXPECT_TRUE(TestMoveAssign<S&&>());
-
-  class C {
-   public:
-    explicit C(C* c) : c_(c) {}
-    ~C() { delete c_; }
-    void operator=(const C&) = delete;
-    void operator=(C&&) = delete;
-
-   private:
-    C* c_;
-  };
-  EXPECT_TRUE(TestMoveAssign<C>());
-  EXPECT_TRUE(TestMoveAssign<C&>());
-  EXPECT_TRUE(TestMoveAssign<C&&>());
-
-  // Reason for ifndef: add_lvalue_reference<T> in libc++ breaks for these cases
-#ifndef _LIBCPP_VERSION
-  EXPECT_TRUE(TestMoveAssign<int()>());
-  EXPECT_TRUE(TestMoveAssign<int(int) const>());
-  EXPECT_TRUE(TestMoveAssign<int(...) volatile&>());
-  EXPECT_TRUE(TestMoveAssign<int(int, ...) const volatile&&>());
-#endif  // _LIBCPP_VERSION
-}
-
 namespace adl_namespace {
 
 struct DeletedSwap {
@@ -1395,22 +743,99 @@
   EXPECT_TRUE(IsNothrowSwappable<adl_namespace::SpecialNoexceptSwap>::value);
 }
 
-TEST(TrivallyRelocatable, Sanity) {
-#if !defined(ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI) || \
-    !ABSL_HAVE_BUILTIN(__is_trivially_relocatable)
-  GTEST_SKIP() << "No trivial ABI support.";
+TEST(TriviallyRelocatable, PrimitiveTypes) {
+  static_assert(absl::is_trivially_relocatable<int>::value, "");
+  static_assert(absl::is_trivially_relocatable<char>::value, "");
+  static_assert(absl::is_trivially_relocatable<void*>::value, "");
+}
+
+// User-defined types can be trivially relocatable as long as they don't have a
+// user-provided move constructor or destructor.
+TEST(TriviallyRelocatable, UserDefinedTriviallyReconstructible) {
+  struct S {
+    int x;
+    int y;
+  };
+
+  static_assert(absl::is_trivially_relocatable<S>::value, "");
+}
+
+// A user-provided move constructor disqualifies a type from being trivially
+// relocatable.
+TEST(TriviallyRelocatable, UserProvidedMoveConstructor) {
+  struct S {
+    S(S&&) {}  // NOLINT(modernize-use-equals-default)
+  };
+
+  static_assert(!absl::is_trivially_relocatable<S>::value, "");
+}
+
+// A user-provided copy constructor disqualifies a type from being trivially
+// relocatable.
+TEST(TriviallyRelocatable, UserProvidedCopyConstructor) {
+  struct S {
+    S(const S&) {}  // NOLINT(modernize-use-equals-default)
+  };
+
+  static_assert(!absl::is_trivially_relocatable<S>::value, "");
+}
+
+// A user-provided destructor disqualifies a type from being trivially
+// relocatable.
+TEST(TriviallyRelocatable, UserProvidedDestructor) {
+  struct S {
+    ~S() {}  // NOLINT(modernize-use-equals-default)
+  };
+
+  static_assert(!absl::is_trivially_relocatable<S>::value, "");
+}
+
+// TODO(b/275003464): remove the opt-out for Clang on Windows once
+// __is_trivially_relocatable is used there again.
+#if defined(ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI) &&      \
+    ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \
+    !(defined(__clang__) && (defined(_WIN32) || defined(_WIN64)))
+// A type marked with the "trivial ABI" attribute is trivially relocatable even
+// if it has user-provided move/copy constructors and a user-provided
+// destructor.
+TEST(TrivallyRelocatable, TrivialAbi) {
+  struct ABSL_ATTRIBUTE_TRIVIAL_ABI S {
+    S(S&&) {}       // NOLINT(modernize-use-equals-default)
+    S(const S&) {}  // NOLINT(modernize-use-equals-default)
+    ~S() {}         // NOLINT(modernize-use-equals-default)
+  };
+
+  static_assert(absl::is_trivially_relocatable<S>::value, "");
+}
 #endif
 
-  struct Trivial {};
-  struct NonTrivial {
-    NonTrivial(const NonTrivial&) {}  // NOLINT
-  };
-  struct ABSL_ATTRIBUTE_TRIVIAL_ABI TrivialAbi {
-    TrivialAbi(const TrivialAbi&) {}  // NOLINT
-  };
-  EXPECT_TRUE(absl::is_trivially_relocatable<Trivial>::value);
-  EXPECT_FALSE(absl::is_trivially_relocatable<NonTrivial>::value);
-  EXPECT_TRUE(absl::is_trivially_relocatable<TrivialAbi>::value);
+#ifdef ABSL_HAVE_CONSTANT_EVALUATED
+
+constexpr int64_t NegateIfConstantEvaluated(int64_t i) {
+  if (absl::is_constant_evaluated()) {
+    return -i;
+  } else {
+    return i;
+  }
 }
 
+#endif  // ABSL_HAVE_CONSTANT_EVALUATED
+
+TEST(TrivallyRelocatable, is_constant_evaluated) {
+#ifdef ABSL_HAVE_CONSTANT_EVALUATED
+  constexpr int64_t constant = NegateIfConstantEvaluated(42);
+  EXPECT_EQ(constant, -42);
+
+  int64_t now = absl::ToUnixSeconds(absl::Now());
+  int64_t not_constant = NegateIfConstantEvaluated(now);
+  EXPECT_EQ(not_constant, now);
+
+  static int64_t const_init = NegateIfConstantEvaluated(42);
+  EXPECT_EQ(const_init, -42);
+#else
+  GTEST_SKIP() << "absl::is_constant_evaluated is not defined";
+#endif  // ABSL_HAVE_CONSTANT_EVALUATED
+}
+
+
 }  // namespace
diff --git a/absl/numeric/BUILD.bazel b/absl/numeric/BUILD.bazel
index eaa27df..ec0b870 100644
--- a/absl/numeric/BUILD.bazel
+++ b/absl/numeric/BUILD.bazel
@@ -95,7 +95,6 @@
     deps = [
         ":int128",
         "//absl/base",
-        "//absl/base:core_headers",
         "//absl/hash:hash_testing",
         "//absl/meta:type_traits",
         "@com_google_googletest//:gtest_main",
diff --git a/absl/numeric/CMakeLists.txt b/absl/numeric/CMakeLists.txt
index 26df5cf..384c0fc 100644
--- a/absl/numeric/CMakeLists.txt
+++ b/absl/numeric/CMakeLists.txt
@@ -70,7 +70,6 @@
   DEPS
     absl::int128
     absl::base
-    absl::core_headers
     absl::hash_testing
     absl::type_traits
     GTest::gmock_main
diff --git a/absl/numeric/bits_benchmark.cc b/absl/numeric/bits_benchmark.cc
index b975958..2c89afd 100644
--- a/absl/numeric/bits_benchmark.cc
+++ b/absl/numeric/bits_benchmark.cc
@@ -24,50 +24,50 @@
 namespace {
 
 template <typename T>
-static void BM_bitwidth(benchmark::State& state) {
-  const int count = state.range(0);
+static void BM_bit_width(benchmark::State& state) {
+  const auto count = static_cast<size_t>(state.range(0));
 
   absl::BitGen rng;
   std::vector<T> values;
   values.reserve(count);
-  for (int i = 0; i < count; ++i) {
+  for (size_t i = 0; i < count; ++i) {
     values.push_back(absl::Uniform<T>(rng, 0, std::numeric_limits<T>::max()));
   }
 
-  while (state.KeepRunningBatch(count)) {
-    for (int i = 0; i < count; ++i) {
-      benchmark::DoNotOptimize(values[i]);
+  while (state.KeepRunningBatch(static_cast<int64_t>(count))) {
+    for (size_t i = 0; i < count; ++i) {
+      benchmark::DoNotOptimize(absl::bit_width(values[i]));
     }
   }
 }
-BENCHMARK_TEMPLATE(BM_bitwidth, uint8_t)->Range(1, 1 << 20);
-BENCHMARK_TEMPLATE(BM_bitwidth, uint16_t)->Range(1, 1 << 20);
-BENCHMARK_TEMPLATE(BM_bitwidth, uint32_t)->Range(1, 1 << 20);
-BENCHMARK_TEMPLATE(BM_bitwidth, uint64_t)->Range(1, 1 << 20);
+BENCHMARK_TEMPLATE(BM_bit_width, uint8_t)->Range(1, 1 << 20);
+BENCHMARK_TEMPLATE(BM_bit_width, uint16_t)->Range(1, 1 << 20);
+BENCHMARK_TEMPLATE(BM_bit_width, uint32_t)->Range(1, 1 << 20);
+BENCHMARK_TEMPLATE(BM_bit_width, uint64_t)->Range(1, 1 << 20);
 
 template <typename T>
-static void BM_bitwidth_nonzero(benchmark::State& state) {
-  const int count = state.range(0);
+static void BM_bit_width_nonzero(benchmark::State& state) {
+  const auto count = static_cast<size_t>(state.range(0));
 
   absl::BitGen rng;
   std::vector<T> values;
   values.reserve(count);
-  for (int i = 0; i < count; ++i) {
+  for (size_t i = 0; i < count; ++i) {
     values.push_back(absl::Uniform<T>(rng, 1, std::numeric_limits<T>::max()));
   }
 
-  while (state.KeepRunningBatch(count)) {
-    for (int i = 0; i < count; ++i) {
+  while (state.KeepRunningBatch(static_cast<int64_t>(count))) {
+    for (size_t i = 0; i < count; ++i) {
       const T value = values[i];
       ABSL_ASSUME(value > 0);
-      benchmark::DoNotOptimize(value);
+      benchmark::DoNotOptimize(absl::bit_width(value));
     }
   }
 }
-BENCHMARK_TEMPLATE(BM_bitwidth_nonzero, uint8_t)->Range(1, 1 << 20);
-BENCHMARK_TEMPLATE(BM_bitwidth_nonzero, uint16_t)->Range(1, 1 << 20);
-BENCHMARK_TEMPLATE(BM_bitwidth_nonzero, uint32_t)->Range(1, 1 << 20);
-BENCHMARK_TEMPLATE(BM_bitwidth_nonzero, uint64_t)->Range(1, 1 << 20);
+BENCHMARK_TEMPLATE(BM_bit_width_nonzero, uint8_t)->Range(1, 1 << 20);
+BENCHMARK_TEMPLATE(BM_bit_width_nonzero, uint16_t)->Range(1, 1 << 20);
+BENCHMARK_TEMPLATE(BM_bit_width_nonzero, uint32_t)->Range(1, 1 << 20);
+BENCHMARK_TEMPLATE(BM_bit_width_nonzero, uint64_t)->Range(1, 1 << 20);
 
 }  // namespace
 }  // namespace absl
diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc
index e5526c6..6ffe43d 100644
--- a/absl/numeric/int128.cc
+++ b/absl/numeric/int128.cc
@@ -111,7 +111,7 @@
   return MakeUint128(0, static_cast<uint64_t>(v));
 }
 
-#if defined(__clang__) && !defined(__SSE3__)
+#if defined(__clang__) && (__clang_major__ < 9) && !defined(__SSE3__)
 // Workaround for clang bug: https://bugs.llvm.org/show_bug.cgi?id=38289
 // Casting from long double to uint64_t is miscompiled and drops bits.
 // It is more work, so only use when we need the workaround.
@@ -131,7 +131,7 @@
   return (static_cast<uint128>(w0) << 100) | (static_cast<uint128>(w1) << 50) |
          static_cast<uint128>(w2);
 }
-#endif  // __clang__ && !__SSE3__
+#endif  // __clang__ && (__clang_major__ < 9) && !__SSE3__
 }  // namespace
 
 uint128::uint128(float v) : uint128(MakeUint128FromFloat(v)) {}
@@ -216,9 +216,9 @@
     } else if (adjustfield == std::ios::internal &&
                (flags & std::ios::showbase) &&
                (flags & std::ios::basefield) == std::ios::hex && v != 0) {
-      rep.insert(2, count, os.fill());
+      rep.insert(size_t{2}, count, os.fill());
     } else {
-      rep.insert(0, count, os.fill());
+      rep.insert(size_t{0}, count, os.fill());
     }
   }
 
@@ -314,16 +314,16 @@
         break;
       case std::ios::internal:
         if (print_as_decimal && (rep[0] == '+' || rep[0] == '-')) {
-          rep.insert(1, count, os.fill());
+          rep.insert(size_t{1}, count, os.fill());
         } else if ((flags & std::ios::basefield) == std::ios::hex &&
                    (flags & std::ios::showbase) && v != 0) {
-          rep.insert(2, count, os.fill());
+          rep.insert(size_t{2}, count, os.fill());
         } else {
-          rep.insert(0, count, os.fill());
+          rep.insert(size_t{0}, count, os.fill());
         }
         break;
       default:  // std::ios::right
-        rep.insert(0, count, os.fill());
+        rep.insert(size_t{0}, count, os.fill());
         break;
     }
   }
diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h
index 7a899ee..aa0f86f 100644
--- a/absl/numeric/int128.h
+++ b/absl/numeric/int128.h
@@ -119,8 +119,8 @@
 #ifdef ABSL_HAVE_INTRINSIC_INT128
   constexpr uint128(__int128 v);           // NOLINT(runtime/explicit)
   constexpr uint128(unsigned __int128 v);  // NOLINT(runtime/explicit)
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-  constexpr uint128(int128 v);  // NOLINT(runtime/explicit)
+#endif                                     // ABSL_HAVE_INTRINSIC_INT128
+  constexpr uint128(int128 v);             // NOLINT(runtime/explicit)
   explicit uint128(float v);
   explicit uint128(double v);
   explicit uint128(long double v);
@@ -286,9 +286,9 @@
 #endif  // ABSL_HAVE_INTRINSIC_INT128
   static constexpr bool tinyness_before = false;
 
-  static constexpr absl::uint128 (min)() { return 0; }
+  static constexpr absl::uint128(min)() { return 0; }
   static constexpr absl::uint128 lowest() { return 0; }
-  static constexpr absl::uint128 (max)() { return absl::Uint128Max(); }
+  static constexpr absl::uint128(max)() { return absl::Uint128Max(); }
   static constexpr absl::uint128 epsilon() { return 0; }
   static constexpr absl::uint128 round_error() { return 0; }
   static constexpr absl::uint128 infinity() { return 0; }
@@ -521,9 +521,9 @@
 #endif  // ABSL_HAVE_INTRINSIC_INT128
   static constexpr bool tinyness_before = false;
 
-  static constexpr absl::int128 (min)() { return absl::Int128Min(); }
+  static constexpr absl::int128(min)() { return absl::Int128Min(); }
   static constexpr absl::int128 lowest() { return absl::Int128Min(); }
-  static constexpr absl::int128 (max)() { return absl::Int128Max(); }
+  static constexpr absl::int128(max)() { return absl::Int128Max(); }
   static constexpr absl::int128 epsilon() { return 0; }
   static constexpr absl::int128 round_error() { return 0; }
   static constexpr absl::int128 infinity() { return 0; }
@@ -561,9 +561,7 @@
 }
 
 // NOLINTNEXTLINE(runtime/int)
-inline uint128& uint128::operator=(long long v) {
-  return *this = uint128(v);
-}
+inline uint128& uint128::operator=(long long v) { return *this = uint128(v); }
 
 // NOLINTNEXTLINE(runtime/int)
 inline uint128& uint128::operator=(unsigned long long v) {
@@ -571,18 +569,14 @@
 }
 
 #ifdef ABSL_HAVE_INTRINSIC_INT128
-inline uint128& uint128::operator=(__int128 v) {
-  return *this = uint128(v);
-}
+inline uint128& uint128::operator=(__int128 v) { return *this = uint128(v); }
 
 inline uint128& uint128::operator=(unsigned __int128 v) {
   return *this = uint128(v);
 }
 #endif  // ABSL_HAVE_INTRINSIC_INT128
 
-inline uint128& uint128::operator=(int128 v) {
-  return *this = uint128(v);
-}
+inline uint128& uint128::operator=(int128 v) { return *this = uint128(v); }
 
 // Arithmetic operators.
 
@@ -637,8 +631,7 @@
 
 #if defined(ABSL_IS_LITTLE_ENDIAN)
 
-constexpr uint128::uint128(uint64_t high, uint64_t low)
-    : lo_{low}, hi_{high} {}
+constexpr uint128::uint128(uint64_t high, uint64_t low) : lo_{low}, hi_{high} {}
 
 constexpr uint128::uint128(int v)
     : lo_{static_cast<uint64_t>(v)},
@@ -670,8 +663,7 @@
 
 #elif defined(ABSL_IS_BIG_ENDIAN)
 
-constexpr uint128::uint128(uint64_t high, uint64_t low)
-    : hi_{high}, lo_{low} {}
+constexpr uint128::uint128(uint64_t high, uint64_t low) : hi_{high}, lo_{low} {}
 
 constexpr uint128::uint128(int v)
     : hi_{v < 0 ? (std::numeric_limits<uint64_t>::max)() : 0},
@@ -817,13 +809,9 @@
 
 // Unary operators.
 
-constexpr inline uint128 operator+(uint128 val) {
-  return val;
-}
+constexpr inline uint128 operator+(uint128 val) { return val; }
 
-constexpr inline int128 operator+(int128 val) {
-  return val;
-}
+constexpr inline int128 operator+(int128 val) { return val; }
 
 constexpr uint128 operator-(uint128 val) {
 #if defined(ABSL_HAVE_INTRINSIC_INT128)
@@ -906,7 +894,7 @@
 #else
   // uint64_t shifts of >= 64 are undefined, so we will need some
   // special-casing.
-  return amount >= 64 ? 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)),
@@ -920,7 +908,7 @@
 #else
   // uint64_t shifts of >= 64 are undefined, so we will need some
   // special-casing.
-  return amount >= 64 ? 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) |
@@ -1042,27 +1030,19 @@
 }
 
 // Assignment from integer types.
-inline int128& int128::operator=(int v) {
-  return *this = int128(v);
-}
+inline int128& int128::operator=(int v) { return *this = int128(v); }
 
-inline int128& int128::operator=(unsigned int v) {
-  return *this = int128(v);
-}
+inline int128& int128::operator=(unsigned int v) { return *this = int128(v); }
 
 inline int128& int128::operator=(long v) {  // NOLINT(runtime/int)
   return *this = int128(v);
 }
 
 // NOLINTNEXTLINE(runtime/int)
-inline int128& int128::operator=(unsigned long v) {
-  return *this = int128(v);
-}
+inline int128& int128::operator=(unsigned long v) { return *this = int128(v); }
 
 // NOLINTNEXTLINE(runtime/int)
-inline int128& int128::operator=(long long v) {
-  return *this = int128(v);
-}
+inline int128& int128::operator=(long long v) { return *this = int128(v); }
 
 // NOLINTNEXTLINE(runtime/int)
 inline int128& int128::operator=(unsigned long long v) {
diff --git a/absl/numeric/int128_no_intrinsic.inc b/absl/numeric/int128_no_intrinsic.inc
index 8834804..6f5d837 100644
--- a/absl/numeric/int128_no_intrinsic.inc
+++ b/absl/numeric/int128_no_intrinsic.inc
@@ -23,8 +23,7 @@
 
 #if defined(ABSL_IS_LITTLE_ENDIAN)
 
-constexpr int128::int128(int64_t high, uint64_t low) :
-    lo_(low), hi_(high) {}
+constexpr int128::int128(int64_t high, uint64_t low) : lo_(low), hi_(high) {}
 
 constexpr int128::int128(int v)
     : lo_{static_cast<uint64_t>(v)}, hi_{v < 0 ? ~int64_t{0} : 0} {}
@@ -44,8 +43,7 @@
 
 #elif defined(ABSL_IS_BIG_ENDIAN)
 
-constexpr int128::int128(int64_t high, uint64_t low) :
-    hi_{high}, lo_{low} {}
+constexpr int128::int128(int64_t high, uint64_t low) : hi_{high}, lo_{low} {}
 
 constexpr int128::int128(int v)
     : hi_{v < 0 ? ~int64_t{0} : 0}, lo_{static_cast<uint64_t>(v)} {}
@@ -279,33 +277,52 @@
 }
 
 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);
+  // int64_t shifts of >= 63 are undefined, so we need some special-casing.
+  assert(amount >= 0 && amount < 127);
+  if (amount <= 0) {
+    return lhs;
+  } else if (amount < 63) {
+    return MakeInt128(
+        (Int128High64(lhs) << amount) |
+            static_cast<int64_t>(Int128Low64(lhs) >> (64 - amount)),
+        Int128Low64(lhs) << amount);
+  } else if (amount == 63) {
+    return MakeInt128(((Int128High64(lhs) << 32) << 31) |
+                          static_cast<int64_t>(Int128Low64(lhs) >> 1),
+                      (Int128Low64(lhs) << 32) << 31);
+  } else if (amount == 127) {
+    return MakeInt128(static_cast<int64_t>(Int128Low64(lhs) << 63), 0);
+  } else if (amount > 127) {
+    return MakeInt128(0, 0);
+  } else {
+    // amount >= 64 && amount < 127
+    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.
-  // 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)));
+  // int64_t shifts of >= 63 are undefined, so we need some special-casing.
+  assert(amount >= 0 && amount < 127);
+  if (amount <= 0) {
+    return lhs;
+  } else if (amount < 63) {
+    return MakeInt128(
+        Int128High64(lhs) >> amount,
+        Int128Low64(lhs) >> amount | static_cast<uint64_t>(Int128High64(lhs))
+                                         << (64 - amount));
+  } else if (amount == 63) {
+    return MakeInt128((Int128High64(lhs) >> 32) >> 31,
+                      static_cast<uint64_t>(Int128High64(lhs) << 1) |
+                          (Int128Low64(lhs) >> 32) >> 31);
+
+  } else if (amount >= 127) {
+    return MakeInt128((Int128High64(lhs) >> 32) >> 31,
+                      static_cast<uint64_t>((Int128High64(lhs) >> 32) >> 31));
+  } else {
+    // amount >= 64 && amount < 127
+    return MakeInt128(
+        (Int128High64(lhs) >> 32) >> 31,
+        static_cast<uint64_t>(Int128High64(lhs) >> (amount - 64)));
+  }
 }
diff --git a/absl/numeric/int128_stream_test.cc b/absl/numeric/int128_stream_test.cc
index 479ad66..81d2ade 100644
--- a/absl/numeric/int128_stream_test.cc
+++ b/absl/numeric/int128_stream_test.cc
@@ -76,16 +76,6 @@
   return msg.str();
 }
 
-void CheckUint128Case(const Uint128TestCase& test_case) {
-  std::ostringstream os;
-  os.flags(test_case.flags);
-  os.width(test_case.width);
-  os.fill(kFill);
-  os << test_case.value;
-  SCOPED_TRACE(StreamFormatToString(test_case.flags, test_case.width));
-  EXPECT_EQ(test_case.expected, os.str());
-}
-
 constexpr std::ios::fmtflags kDec = std::ios::dec;
 constexpr std::ios::fmtflags kOct = std::ios::oct;
 constexpr std::ios::fmtflags kHex = std::ios::hex;
@@ -96,6 +86,16 @@
 constexpr std::ios::fmtflags kBase = std::ios::showbase;
 constexpr std::ios::fmtflags kPos = std::ios::showpos;
 
+void CheckUint128Case(const Uint128TestCase& test_case) {
+  std::ostringstream os;
+  os.flags(test_case.flags);
+  os.width(test_case.width);
+  os.fill(kFill);
+  os << test_case.value;
+  SCOPED_TRACE(StreamFormatToString(test_case.flags, test_case.width));
+  EXPECT_EQ(os.str(), test_case.expected);
+}
+
 TEST(Uint128, OStreamValueTest) {
   CheckUint128Case({1, kDec, /*width = */ 0, "1"});
   CheckUint128Case({1, kOct, /*width = */ 0, "1"});
@@ -161,7 +161,7 @@
   os.fill(kFill);
   os << test_case.value;
   SCOPED_TRACE(StreamFormatToString(test_case.flags, test_case.width));
-  EXPECT_EQ(test_case.expected, os.str());
+  EXPECT_EQ(os.str(), test_case.expected);
 }
 
 TEST(Int128, OStreamValueTest) {
@@ -194,35 +194,33 @@
       {absl::MakeInt128(1, 0), kHex, /*width = */ 0, "10000000000000000"});
   CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::max(),
                                     std::numeric_limits<uint64_t>::max()),
-                   std::ios::dec, /*width = */ 0,
+                   kDec, /*width = */ 0,
                    "170141183460469231731687303715884105727"});
   CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::max(),
                                     std::numeric_limits<uint64_t>::max()),
-                   std::ios::oct, /*width = */ 0,
+                   kOct, /*width = */ 0,
                    "1777777777777777777777777777777777777777777"});
   CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::max(),
                                     std::numeric_limits<uint64_t>::max()),
-                   std::ios::hex, /*width = */ 0,
-                   "7fffffffffffffffffffffffffffffff"});
+                   kHex, /*width = */ 0, "7fffffffffffffffffffffffffffffff"});
   CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::min(), 0),
-                   std::ios::dec, /*width = */ 0,
+                   kDec, /*width = */ 0,
                    "-170141183460469231731687303715884105728"});
   CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::min(), 0),
-                   std::ios::oct, /*width = */ 0,
+                   kOct, /*width = */ 0,
                    "2000000000000000000000000000000000000000000"});
   CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::min(), 0),
-                   std::ios::hex, /*width = */ 0,
-                   "80000000000000000000000000000000"});
-  CheckInt128Case({-1, std::ios::dec, /*width = */ 0, "-1"});
-  CheckInt128Case({-1, std::ios::oct, /*width = */ 0,
+                   kHex, /*width = */ 0, "80000000000000000000000000000000"});
+  CheckInt128Case({-1, kDec, /*width = */ 0, "-1"});
+  CheckInt128Case({-1, kOct, /*width = */ 0,
                    "3777777777777777777777777777777777777777777"});
   CheckInt128Case(
-      {-1, std::ios::hex, /*width = */ 0, "ffffffffffffffffffffffffffffffff"});
-  CheckInt128Case({-12345, std::ios::dec, /*width = */ 0, "-12345"});
-  CheckInt128Case({-12345, std::ios::oct, /*width = */ 0,
+      {-1, kHex, /*width = */ 0, "ffffffffffffffffffffffffffffffff"});
+  CheckInt128Case({-12345, kDec, /*width = */ 0, "-12345"});
+  CheckInt128Case({-12345, kOct, /*width = */ 0,
                    "3777777777777777777777777777777777777747707"});
-  CheckInt128Case({-12345, std::ios::hex, /*width = */ 0,
-                   "ffffffffffffffffffffffffffffcfc7"});
+  CheckInt128Case(
+      {-12345, kHex, /*width = */ 0, "ffffffffffffffffffffffffffffcfc7"});
 }
 
 std::vector<Int128TestCase> GetInt128FormatCases();
diff --git a/absl/numeric/int128_test.cc b/absl/numeric/int128_test.cc
index dd9425d..01e3eb5 100644
--- a/absl/numeric/int128_test.cc
+++ b/absl/numeric/int128_test.cc
@@ -32,6 +32,8 @@
 #pragma warning(disable:4146)
 #endif
 
+#define MAKE_INT128(HI, LO) absl::MakeInt128(static_cast<int64_t>(HI), LO)
+
 namespace {
 
 template <typename T>
@@ -283,8 +285,9 @@
   EXPECT_EQ(from_precise_double, from_precise_ints);
   EXPECT_DOUBLE_EQ(static_cast<double>(from_precise_ints), precise_double);
 
-  double approx_double = 0xffffeeeeddddcccc * std::pow(2.0, 64.0) +
-                         0xbbbbaaaa99998888;
+  double approx_double =
+      static_cast<double>(0xffffeeeeddddcccc) * std::pow(2.0, 64.0) +
+      static_cast<double>(0xbbbbaaaa99998888);
   absl::uint128 from_approx_double(approx_double);
   EXPECT_DOUBLE_EQ(static_cast<double>(from_approx_double), approx_double);
 
@@ -1245,6 +1248,27 @@
                 absl::MakeInt128(uint64_t{1} << j, 0) >>= (j - i));
     }
   }
+
+  // Manually calculated cases with shift count for positive (val1) and negative
+  // (val2) values
+  absl::int128 val1 = MAKE_INT128(0x123456789abcdef0, 0x123456789abcdef0);
+  absl::int128 val2 = MAKE_INT128(0xfedcba0987654321, 0xfedcba0987654321);
+
+  EXPECT_EQ(val1 << 63, MAKE_INT128(0x91a2b3c4d5e6f78, 0x0));
+  EXPECT_EQ(val1 << 64, MAKE_INT128(0x123456789abcdef0, 0x0));
+  EXPECT_EQ(val2 << 63, MAKE_INT128(0xff6e5d04c3b2a190, 0x8000000000000000));
+  EXPECT_EQ(val2 << 64, MAKE_INT128(0xfedcba0987654321, 0x0));
+
+  EXPECT_EQ(val1 << 126, MAKE_INT128(0x0, 0x0));
+  EXPECT_EQ(val2 << 126, MAKE_INT128(0x4000000000000000, 0x0));
+
+  EXPECT_EQ(val1 >> 63, MAKE_INT128(0x0, 0x2468acf13579bde0));
+  EXPECT_EQ(val1 >> 64, MAKE_INT128(0x0, 0x123456789abcdef0));
+  EXPECT_EQ(val2 >> 63, MAKE_INT128(0xffffffffffffffff, 0xfdb974130eca8643));
+  EXPECT_EQ(val2 >> 64, MAKE_INT128(0xffffffffffffffff, 0xfedcba0987654321));
+
+  EXPECT_EQ(val1 >> 126, MAKE_INT128(0x0, 0x0));
+  EXPECT_EQ(val2 >> 126, MAKE_INT128(0xffffffffffffffff, 0xffffffffffffffff));
 }
 
 TEST(Int128, NumericLimitsTest) {
diff --git a/absl/profiling/internal/sample_recorder.h b/absl/profiling/internal/sample_recorder.h
index ef1489b..371f6c4 100644
--- a/absl/profiling/internal/sample_recorder.h
+++ b/absl/profiling/internal/sample_recorder.h
@@ -199,6 +199,14 @@
     sample = new T();
     {
       absl::MutexLock sample_lock(&sample->init_mu);
+      // If flag initialization happens to occur (perhaps in another thread)
+      // while in this block, it will lock `graveyard_` which is usually always
+      // locked before any sample. This will appear as a lock inversion.
+      // However, this code is run exactly once per sample, and this sample
+      // cannot be accessed until after it is returned from this method.  This
+      // means that this lock state can never be recreated, so we can safely
+      // inform the deadlock detector to ignore it.
+      sample->init_mu.ForgetDeadlockInfo();
       sample->PrepareForSampling(std::forward<Targs>(args)...);
     }
     PushNew(sample);
diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel
index ff20dd0..133c065 100644
--- a/absl/random/BUILD.bazel
+++ b/absl/random/BUILD.bazel
@@ -494,18 +494,6 @@
     ],
 )
 
-BENCHMARK_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",
-    "no_test_wasm",
-]
-
 # Benchmarks for various methods / test utilities
 cc_binary(
     name = "benchmarks",
@@ -515,7 +503,7 @@
     ],
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = BENCHMARK_TAGS,
+    tags = ["benchmark"],
     deps = [
         ":distributions",
         ":random",
diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt
index d04c708..c74fd30 100644
--- a/absl/random/CMakeLists.txt
+++ b/absl/random/CMakeLists.txt
@@ -569,7 +569,7 @@
     ${ABSL_DEFAULT_COPTS}
   LINKOPTS
     ${ABSL_DEFAULT_LINKOPTS}
-    $<$<BOOL:${MINGW}>:"bcrypt">
+    $<$<BOOL:${MINGW}>:-lbcrypt>
   DEPS
     absl::core_headers
     absl::optional
diff --git a/absl/random/benchmarks.cc b/absl/random/benchmarks.cc
index 87bbb98..0900e81 100644
--- a/absl/random/benchmarks.cc
+++ b/absl/random/benchmarks.cc
@@ -62,7 +62,7 @@
  public:
   using result_type = uint32_t;
 
-  PrecompiledSeedSeq() {}
+  PrecompiledSeedSeq() = default;
 
   template <typename Iterator>
   PrecompiledSeedSeq(Iterator begin, Iterator end) {}
diff --git a/absl/random/discrete_distribution_test.cc b/absl/random/discrete_distribution_test.cc
index 415b14c..e528a6a 100644
--- a/absl/random/discrete_distribution_test.cc
+++ b/absl/random/discrete_distribution_test.cc
@@ -146,7 +146,7 @@
   using absl::random_internal::kChiSquared;
 
   constexpr size_t kTrials = 10000;
-  constexpr int kBuckets = 50;  // inclusive, so actally +1
+  constexpr int kBuckets = 50;  // inclusive, so actually +1
 
   // 1-in-100000 threshold, but remember, there are about 8 tests
   // in this file. And the test could fail for other reasons.
diff --git a/absl/random/generators_test.cc b/absl/random/generators_test.cc
index 14fd24e..2009130 100644
--- a/absl/random/generators_test.cc
+++ b/absl/random/generators_test.cc
@@ -49,7 +49,7 @@
   // (a, b) semantics, inferred types.
   absl::Uniform(absl::IntervalOpenOpen, *gen, 0, 1.0);  // Promoted to double
 
-  // Explict overriding of types.
+  // Explicit overriding of types.
   absl::Uniform<int>(*gen, 0, 100);
   absl::Uniform<int8_t>(*gen, 0, 100);
   absl::Uniform<int16_t>(*gen, 0, 100);
@@ -117,6 +117,7 @@
   absl::Bernoulli(*gen, 0.5);
 }
 
+
 template <typename URBG>
 void TestZipf(URBG* gen) {
   absl::Zipf<int>(*gen, 100);
diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel
index fd5b619..a51c937 100644
--- a/absl/random/internal/BUILD.bazel
+++ b/absl/random/internal/BUILD.bazel
@@ -24,9 +24,11 @@
     "absl_random_randen_copts_init",
 )
 
-package(default_visibility = [
+default_package_visibility = [
     "//absl/random:__pkg__",
-])
+]
+
+package(default_visibility = default_package_visibility)
 
 licenses(["notice"])
 
@@ -80,6 +82,10 @@
     linkopts = ABSL_DEFAULT_LINKOPTS + select({
         "//absl:msvc_compiler": ["-DEFAULTLIB:bcrypt.lib"],
         "//absl:clang-cl_compiler": ["-DEFAULTLIB:bcrypt.lib"],
+        "//absl:mingw_compiler": [
+            "-DEFAULTLIB:bcrypt.lib",
+            "-lbcrypt",
+        ],
         "//conditions:default": [],
     }),
     deps = [
@@ -248,6 +254,8 @@
     hdrs = ["randen_engine.h"],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = default_package_visibility + [
+    ],
     deps = [
         ":iostream_state_saver",
         ":randen",
@@ -389,7 +397,7 @@
     "no_test_darwin_x86_64",
     "no_test_ios_x86_64",
     "no_test_loonix",
-    "no_test_msvc_x64",
+    "no_test_lexan",
     "no_test_wasm",
 ]
 
diff --git a/absl/random/internal/distribution_test_util.cc b/absl/random/internal/distribution_test_util.cc
index e900565..9fa37bd 100644
--- a/absl/random/internal/distribution_test_util.cc
+++ b/absl/random/internal/distribution_test_util.cc
@@ -213,7 +213,7 @@
   double result = 1.;
   int ns = static_cast<int>(q + xc * psq);
 
-  // Use the soper reduction forumla.
+  // Use the soper reduction formula.
   double rx = (ns == 0) ? x : x / xc;
   double temp = q - ai;
   for (;;) {
@@ -236,7 +236,7 @@
     }
   }
 
-  // NOTE: See also TOMS Alogrithm 708.
+  // NOTE: See also TOMS Algorithm 708.
   // http://www.netlib.org/toms/index.html
   //
   // NOTE: The NWSC library also includes BRATIO / ISUBX (p87)
@@ -247,7 +247,7 @@
 // https://www.jstor.org/stable/2346798?read-now=1&seq=4#page_scan_tab_contents
 // https://www.jstor.org/stable/2346887?seq=1#page_scan_tab_contents
 //
-// XINBTA(p, q, beta, alhpa)
+// XINBTA(p, q, beta, alpha)
 //  p:     the value of the parameter p.
 //  q:     the value of the parameter q.
 //  beta:  the value of ln B(p, q)
diff --git a/absl/random/internal/fast_uniform_bits.h b/absl/random/internal/fast_uniform_bits.h
index f3a5c00..83ee5c0 100644
--- a/absl/random/internal/fast_uniform_bits.h
+++ b/absl/random/internal/fast_uniform_bits.h
@@ -57,9 +57,10 @@
 // `PowerOfTwoVariate(urbg)`.
 template <typename URBG>
 constexpr size_t NumBits() {
-  return RangeSize<URBG>() == 0
-             ? std::numeric_limits<typename URBG::result_type>::digits
-             : IntegerLog2(RangeSize<URBG>());
+  return static_cast<size_t>(
+      RangeSize<URBG>() == 0
+          ? std::numeric_limits<typename URBG::result_type>::digits
+          : IntegerLog2(RangeSize<URBG>()));
 }
 
 // Given a shift value `n`, constructs a mask with exactly the low `n` bits set.
@@ -151,7 +152,8 @@
 
   result_type r = static_cast<result_type>(g() - kMin);
   for (size_t n = 1; n < kIters; ++n) {
-    r = (r << kShift) + static_cast<result_type>(g() - kMin);
+    r = static_cast<result_type>(r << kShift) +
+        static_cast<result_type>(g() - kMin);
   }
   return r;
 }
diff --git a/absl/random/internal/fast_uniform_bits_test.cc b/absl/random/internal/fast_uniform_bits_test.cc
index cee702d..34c2520 100644
--- a/absl/random/internal/fast_uniform_bits_test.cc
+++ b/absl/random/internal/fast_uniform_bits_test.cc
@@ -167,7 +167,7 @@
              FakeUrbg<uint64_t, 0, (std::numeric_limits<uint64_t>::max)()>>()));
 }
 
-// The constants need to be choosen so that an infinite rejection loop doesn't
+// The constants need to be chosen so that an infinite rejection loop doesn't
 // happen...
 using Urng1_5bit = FakeUrbg<uint8_t, 0, 2, 0>;  // ~1.5 bits (range 3)
 using Urng4bits = FakeUrbg<uint8_t, 1, 0x10, 2>;
diff --git a/absl/random/internal/generate_real.h b/absl/random/internal/generate_real.h
index b569450..9a6f400 100644
--- a/absl/random/internal/generate_real.h
+++ b/absl/random/internal/generate_real.h
@@ -78,7 +78,7 @@
       "GenerateRealFromBits must be parameterized by either float or double.");
 
   static_assert(sizeof(uint_type) == sizeof(real_type),
-                "Mismatched unsinged and real types.");
+                "Mismatched unsigned and real types.");
 
   static_assert((std::numeric_limits<real_type>::is_iec559 &&
                  std::numeric_limits<real_type>::radix == 2),
diff --git a/absl/random/internal/iostream_state_saver_test.cc b/absl/random/internal/iostream_state_saver_test.cc
index 6e66266..ea9d2af 100644
--- a/absl/random/internal/iostream_state_saver_test.cc
+++ b/absl/random/internal/iostream_state_saver_test.cc
@@ -345,8 +345,9 @@
     }
 
     // Avoid undefined behavior (overflow/underflow).
-    if (dd <= std::numeric_limits<int64_t>::max() &&
-        dd >= std::numeric_limits<int64_t>::lowest()) {
+    if (dd <= static_cast<long double>(std::numeric_limits<int64_t>::max()) &&
+        dd >=
+            static_cast<long double>(std::numeric_limits<int64_t>::lowest())) {
       int64_t x = static_cast<int64_t>(dd);
       EXPECT_EQ(x, StreamRoundTrip<int64_t>(x));
     }
diff --git a/absl/random/internal/mock_helpers.h b/absl/random/internal/mock_helpers.h
index 882b051..a7a97bf 100644
--- a/absl/random/internal/mock_helpers.h
+++ b/absl/random/internal/mock_helpers.h
@@ -101,7 +101,7 @@
   template <typename KeyT, typename URBG, typename... Args>
   static auto MaybeInvokeMock(URBG* urbg, Args&&... args)
       -> absl::optional<typename KeySignature<KeyT>::result_type> {
-    // Use function overloading to dispatch to the implemenation since
+    // Use function overloading to dispatch to the implementation since
     // more modern patterns (e.g. require + constexpr) are not supported in all
     // compiler configurations.
     return InvokeMockImpl<KeyT, typename KeySignature<KeyT>::result_type,
diff --git a/absl/random/internal/nanobenchmark.cc b/absl/random/internal/nanobenchmark.cc
index c918181..0f31a7d 100644
--- a/absl/random/internal/nanobenchmark.cc
+++ b/absl/random/internal/nanobenchmark.cc
@@ -361,7 +361,7 @@
   // Write that many copies of each unique value to the array.
   T* ABSL_RANDOM_INTERNAL_RESTRICT p = values;
   for (const auto& value_count : unique) {
-    std::fill(p, p + value_count.second, value_count.first);
+    std::fill_n(p, value_count.second, value_count.first);
     p += value_count.second;
   }
   ABSL_RAW_CHECK(p == values + num_values, "Did not produce enough output");
diff --git a/absl/random/internal/nonsecure_base.h b/absl/random/internal/nonsecure_base.h
index c7d7fa4..c3b8033 100644
--- a/absl/random/internal/nonsecure_base.h
+++ b/absl/random/internal/nonsecure_base.h
@@ -44,7 +44,7 @@
   // Generate random unsigned values directly into the buffer.
   template <typename Contiguous>
   void generate_impl(ContiguousTag, Contiguous begin, Contiguous end) {
-    const size_t n = std::distance(begin, end);
+    const size_t n = static_cast<size_t>(std::distance(begin, end));
     auto* a = &(*begin);
     RandenPool<uint8_t>::Fill(
         absl::MakeSpan(reinterpret_cast<uint8_t*>(a), sizeof(*a) * n));
diff --git a/absl/random/internal/platform.h b/absl/random/internal/platform.h
index bbdb4e6..d779f48 100644
--- a/absl/random/internal/platform.h
+++ b/absl/random/internal/platform.h
@@ -131,7 +131,7 @@
 
 // ABSL_RANDOM_INTERNAL_AES_DISPATCH indicates whether the currently active
 // platform has, or should use run-time dispatch for selecting the
-// acclerated Randen implementation.
+// accelerated Randen implementation.
 #define ABSL_RANDOM_INTERNAL_AES_DISPATCH 0
 
 #if defined(ABSL_ARCH_X86_64)
diff --git a/absl/random/internal/randen_engine.h b/absl/random/internal/randen_engine.h
index b470866..fe2d9f6 100644
--- a/absl/random/internal/randen_engine.h
+++ b/absl/random/internal/randen_engine.h
@@ -142,7 +142,7 @@
       // 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
-      // implies prefering seed bytes [16..31, ..., 208..223]. Since the
+      // implies preferring seed bytes [16..31, ..., 208..223]. Since the
       // buffer is 32-bit values, we swap the corresponding buffer positions in
       // 128-bit chunks.
       size_t dst = kBufferSize;
diff --git a/absl/random/internal/randen_hwaes.cc b/absl/random/internal/randen_hwaes.cc
index fee6677..f535f4c 100644
--- a/absl/random/internal/randen_hwaes.cc
+++ b/absl/random/internal/randen_hwaes.cc
@@ -31,7 +31,7 @@
 // a hardware accelerated implementation of randen, or whether it
 // will contain stubs that exit the process.
 #if ABSL_HAVE_ACCELERATED_AES
-// The following plaforms have implemented RandenHwAes.
+// The following platforms 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)
diff --git a/absl/random/internal/uniform_helper.h b/absl/random/internal/uniform_helper.h
index e68b82e..db737e1 100644
--- a/absl/random/internal/uniform_helper.h
+++ b/absl/random/internal/uniform_helper.h
@@ -217,7 +217,7 @@
 // UniformDistributionWrapper is used as the underlying distribution type
 // by the absl::Uniform template function. It selects the proper Abseil
 // uniform distribution and provides constructor overloads that match the
-// expected parameter order as well as adjusting distribtuion bounds based
+// expected parameter order as well as adjusting distribution bounds based
 // on the tag.
 template <typename NumType>
 struct UniformDistributionWrapper : public UniformDistribution<NumType> {
diff --git a/absl/random/random.h b/absl/random/random.h
index 71b6309..7672086 100644
--- a/absl/random/random.h
+++ b/absl/random/random.h
@@ -68,7 +68,7 @@
 //
 // `absl::BitGen` may be constructed with an optional seed sequence type,
 // conforming to [rand.req.seed_seq], which will be mixed with additional
-// non-deterministic data.
+// non-deterministic data as detailed below.
 //
 // Example:
 //
@@ -79,16 +79,16 @@
 //  // Generate an integer value in the closed interval [1,6]
 //  int die_roll2 = absl::uniform_int_distribution<int>(1, 6)(gen_with_seed);
 //
+// Constructing two `absl::BitGen`s with the same seed sequence in the same
+// process will produce the same sequence of variates, but need not do so across
+// multiple processes even if they're executing the same binary.
+//
 // `absl::BitGen` meets the requirements of the Uniform Random Bit Generator
 // (URBG) concept as per the C++17 standard [rand.req.urng] though differs
 // slightly with [rand.req.eng]. Like its standard library equivalents (e.g.
 // `std::mersenne_twister_engine`) `absl::BitGen` is not cryptographically
 // secure.
 //
-// Constructing two `absl::BitGen`s with the same seed sequence in the same
-// binary will produce the same sequence of variates within the same binary, but
-// need not do so across multiple binary invocations.
-//
 // This type has been optimized to perform better than Mersenne Twister
 // (https://en.wikipedia.org/wiki/Mersenne_Twister) and many other complex URBG
 // types on modern x86, ARM, and PPC architectures.
@@ -147,7 +147,7 @@
 //
 // `absl::InsecureBitGen` may be constructed with an optional seed sequence
 // type, conforming to [rand.req.seed_seq], which will be mixed with additional
-// non-deterministic data. (See std_seed_seq.h for more information.)
+// non-deterministic data, as detailed in the `absl::BitGen` comment.
 //
 // `absl::InsecureBitGen` meets the requirements of the Uniform Random Bit
 // Generator (URBG) concept as per the C++17 standard [rand.req.urng] though
diff --git a/absl/random/uniform_int_distribution_test.cc b/absl/random/uniform_int_distribution_test.cc
index 276d72a..a830117 100644
--- a/absl/random/uniform_int_distribution_test.cc
+++ b/absl/random/uniform_int_distribution_test.cc
@@ -19,6 +19,7 @@
 #include <iterator>
 #include <random>
 #include <sstream>
+#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
@@ -136,7 +137,7 @@
       typename absl::uniform_int_distribution<TypeParam>::param_type;
 
   // We use a fixed bit generator for distribution accuracy tests.  This allows
-  // these tests to be deterministic, while still testing the qualify of the
+  // these tests to be deterministic, while still testing the quality of the
   // implementation.
   absl::random_internal::pcg64_2018_engine rng{0x2B7E151628AED2A6};
 
@@ -172,7 +173,7 @@
   using absl::random_internal::kChiSquared;
 
   constexpr size_t kTrials = 1000;
-  constexpr int kBuckets = 50;  // inclusive, so actally +1
+  constexpr int kBuckets = 50;  // inclusive, so actually +1
   constexpr double kExpected =
       static_cast<double>(kTrials) / static_cast<double>(kBuckets);
 
@@ -184,7 +185,7 @@
   const TypeParam max = min + kBuckets;
 
   // We use a fixed bit generator for distribution accuracy tests.  This allows
-  // these tests to be deterministic, while still testing the qualify of the
+  // these tests to be deterministic, while still testing the quality of the
   // implementation.
   absl::random_internal::pcg64_2018_engine rng{0x2B7E151628AED2A6};
 
diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel
index ce0ea70..1f58b30 100644
--- a/absl/status/BUILD.bazel
+++ b/absl/status/BUILD.bazel
@@ -61,6 +61,7 @@
     name = "status_test",
     srcs = ["status_test.cc"],
     copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":status",
         "//absl/strings",
diff --git a/absl/status/status.cc b/absl/status/status.cc
index bbf2335..d011075 100644
--- a/absl/status/status.cc
+++ b/absl/status/status.cc
@@ -20,6 +20,7 @@
 
 #include "absl/base/internal/raw_logging.h"
 #include "absl/base/internal/strerror.h"
+#include "absl/base/macros.h"
 #include "absl/debugging/stacktrace.h"
 #include "absl/debugging/symbolize.h"
 #include "absl/status/status_payload_printer.h"
@@ -79,10 +80,8 @@
 namespace status_internal {
 
 static absl::optional<size_t> FindPayloadIndexByUrl(
-    const Payloads* payloads,
-    absl::string_view type_url) {
-  if (payloads == nullptr)
-    return absl::nullopt;
+    const Payloads* payloads, absl::string_view type_url) {
+  if (payloads == nullptr) return absl::nullopt;
 
   for (size_t i = 0; i < payloads->size(); ++i) {
     if ((*payloads)[i].type_url == type_url) return i;
@@ -124,8 +123,7 @@
   const auto* payloads = GetPayloads();
   absl::optional<size_t> index =
       status_internal::FindPayloadIndexByUrl(payloads, type_url);
-  if (index.has_value())
-    return (*payloads)[index.value()].payload;
+  if (index.has_value()) return (*payloads)[index.value()].payload;
 
   return absl::nullopt;
 }
@@ -302,7 +300,7 @@
   absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message());
 
   const bool with_payload = (mode & StatusToStringMode::kWithPayload) ==
-                      StatusToStringMode::kWithPayload;
+                            StatusToStringMode::kWithPayload;
 
   if (with_payload) {
     status_internal::StatusPayloadPrinter printer =
diff --git a/absl/status/status.h b/absl/status/status.h
index 4e8292f..c6c1cd4 100644
--- a/absl/status/status.h
+++ b/absl/status/status.h
@@ -398,7 +398,7 @@
 //
 //   * It may provide more fine-grained semantic information about the error to
 //     facilitate actionable remedies.
-//   * It may provide human-readable contexual information that is more
+//   * It may provide human-readable contextual information that is more
 //     appropriate to display to an end user.
 //
 // Example:
@@ -538,7 +538,7 @@
   //
   //   * It may provide more fine-grained semantic information about the error
   //     to facilitate actionable remedies.
-  //   * It may provide human-readable contexual information that is more
+  //   * It may provide human-readable contextual information that is more
   //     appropriate to display to an end user.
   //
   // A payload consists of a [key,value] pair, where the key is a string
diff --git a/absl/status/status_test.cc b/absl/status/status_test.cc
index 89cce7d..74a64ac 100644
--- a/absl/status/status_test.cc
+++ b/absl/status/status_test.cc
@@ -505,4 +505,5 @@
   EXPECT_EQ(status.code(), absl::StatusCode::kNotFound);
   EXPECT_EQ(status.message(), "Cannot open 'path': No such file or directory");
 }
+
 }  // namespace
diff --git a/absl/status/statusor.h b/absl/status/statusor.h
index a76e720..935366d 100644
--- a/absl/status/statusor.h
+++ b/absl/status/statusor.h
@@ -146,7 +146,7 @@
 //
 //   absl::StatusOr<int> i = GetCount();
 //   if (i.ok()) {
-//     updated_total += *i
+//     updated_total += *i;
 //   }
 //
 // NOTE: using `absl::StatusOr<T>::value()` when no valid value is present will
@@ -584,7 +584,7 @@
   // Reconstructs the inner value T in-place using the provided args, using the
   // T(args...) constructor. Returns reference to the reconstructed `T`.
   template <typename... Args>
-  T& emplace(Args&&... args) {
+  T& emplace(Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     if (ok()) {
       this->Clear();
       this->MakeValue(std::forward<Args>(args)...);
@@ -600,7 +600,8 @@
       absl::enable_if_t<
           std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value,
           int> = 0>
-  T& emplace(std::initializer_list<U> ilist, Args&&... args) {
+  T& emplace(std::initializer_list<U> ilist,
+             Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     if (ok()) {
       this->Clear();
       this->MakeValue(ilist, std::forward<Args>(args)...);
@@ -611,6 +612,21 @@
     return this->data_;
   }
 
+  // StatusOr<T>::AssignStatus()
+  //
+  // Sets the status of `absl::StatusOr<T>` to the given non-ok status value.
+  //
+  // NOTE: We recommend using the constructor and `operator=` where possible.
+  // This method is intended for use in generic programming, to enable setting
+  // the status of a `StatusOr<T>` when `T` may be `Status`. In that case, the
+  // constructor and `operator=` would assign into the inner value of type
+  // `Status`, rather than status of the `StatusOr` (b/280392796).
+  //
+  // REQUIRES: !Status(std::forward<U>(v)).ok(). This requirement is DCHECKed.
+  // In optimized builds, passing absl::OkStatus() here will have the effect
+  // of passing absl::StatusCode::kInternal as a fallback.
+  using internal_statusor::StatusOrData<T>::AssignStatus;
+
  private:
   using internal_statusor::StatusOrData<T>::Assign;
   template <typename U>
diff --git a/absl/status/statusor_test.cc b/absl/status/statusor_test.cc
index 2902154..e65f5d2 100644
--- a/absl/status/statusor_test.cc
+++ b/absl/status/statusor_test.cc
@@ -1844,4 +1844,37 @@
   }
 }
 
+TEST(StatusOr, StatusAssignmentFromStatusError) {
+  absl::StatusOr<absl::Status> statusor;
+  statusor.AssignStatus(absl::CancelledError());
+
+  EXPECT_FALSE(statusor.ok());
+  EXPECT_EQ(statusor.status(), absl::CancelledError());
+}
+
+#if GTEST_HAS_DEATH_TEST
+TEST(StatusOr, StatusAssignmentFromStatusOk) {
+  EXPECT_DEBUG_DEATH(
+      {
+        absl::StatusOr<absl::Status> statusor;
+        // This will DCHECK.
+        statusor.AssignStatus(absl::OkStatus());
+        // In optimized mode, we are actually going to get error::INTERNAL for
+        // status here, rather than crashing, so check that.
+        EXPECT_FALSE(statusor.ok());
+        EXPECT_EQ(statusor.status().code(), absl::StatusCode::kInternal);
+      },
+      "An OK status is not a valid constructor argument to StatusOr<T>");
+}
+#endif
+
+TEST(StatusOr, StatusAssignmentFromTypeConvertibleToStatus) {
+  CustomType<MyType, kConvToStatus> v;
+  absl::StatusOr<MyType> statusor;
+  statusor.AssignStatus(v);
+
+  EXPECT_FALSE(statusor.ok());
+  EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
+}
+
 }  // namespace
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index 5b12c01..e48a9a0 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -37,11 +37,14 @@
         "internal/charconv_bigint.h",
         "internal/charconv_parse.cc",
         "internal/charconv_parse.h",
+        "internal/damerau_levenshtein_distance.cc",
         "internal/memutil.cc",
         "internal/memutil.h",
         "internal/stl_type_traits.h",
         "internal/str_join_internal.h",
         "internal/str_split_internal.h",
+        "internal/stringify_sink.cc",
+        "internal/stringify_sink.h",
         "match.cc",
         "numbers.cc",
         "str_cat.cc",
@@ -54,6 +57,8 @@
         "ascii.h",
         "charconv.h",
         "escaping.h",
+        "internal/damerau_levenshtein_distance.h",
+        "internal/has_absl_stringify.h",
         "internal/string_constant.h",
         "match.h",
         "numbers.h",
@@ -179,6 +184,19 @@
 )
 
 cc_test(
+    name = "damerau_levenshtein_distance_test",
+    size = "small",
+    srcs = [
+        "internal/damerau_levenshtein_distance_test.cc",
+    ],
+    copts = ABSL_TEST_COPTS,
+    deps = [
+        "//absl/strings",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
     name = "memutil_benchmark",
     srcs = [
         "internal/memutil.h",
@@ -304,8 +322,10 @@
         "//absl/base:raw_logging_internal",
         "//absl/base:throw_delegate",
         "//absl/container:compressed_tuple",
+        "//absl/container:container_memory",
         "//absl/container:inlined_vector",
         "//absl/container:layout",
+        "//absl/crc:crc_cord_state",
         "//absl/functional:function_ref",
         "//absl/meta:type_traits",
         "//absl/types:span",
@@ -330,6 +350,7 @@
 cc_test(
     name = "cord_rep_btree_test",
     size = "medium",
+    timeout = "long",
     srcs = ["internal/cord_rep_btree_test.cc"],
     copts = ABSL_TEST_COPTS,
     visibility = ["//visibility:private"],
@@ -387,6 +408,7 @@
         ":cord_internal",
         ":cord_rep_test_util",
         "//absl/base:config",
+        "//absl/crc:crc_cord_state",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -436,7 +458,6 @@
         ":cordz_update_scope",
         ":cordz_update_tracker",
         ":internal",
-        ":str_format",
         ":strings",
         "//absl/base",
         "//absl/base:config",
@@ -445,6 +466,7 @@
         "//absl/base:raw_logging_internal",
         "//absl/container:fixed_array",
         "//absl/container:inlined_vector",
+        "//absl/crc:crc_cord_state",
         "//absl/functional:function_ref",
         "//absl/meta:type_traits",
         "//absl/numeric:bits",
@@ -492,6 +514,7 @@
         "//absl/container:inlined_vector",
         "//absl/debugging:stacktrace",
         "//absl/synchronization",
+        "//absl/time",
         "//absl/types:span",
     ],
 )
@@ -641,6 +664,7 @@
         ":cordz_update_scope",
         ":cordz_update_tracker",
         "//absl/base:config",
+        "//absl/crc:crc_cord_state",
         "//absl/synchronization",
         "//absl/synchronization:thread_pool",
         "@com_google_googletest//:gtest_main",
@@ -770,8 +794,8 @@
         "no_test_android_arm64",
         "no_test_android_x86",
         "no_test_ios_x86_64",
+        "no_test_lexan",
         "no_test_loonix",
-        "no_test_msvc_x64",
     ],
     visibility = ["//visibility:private"],
     deps = [
@@ -1132,6 +1156,7 @@
         "internal/str_format/arg.h",
         "internal/str_format/bind.h",
         "internal/str_format/checker.h",
+        "internal/str_format/constexpr_parser.h",
         "internal/str_format/extension.h",
         "internal/str_format/float_conversion.h",
         "internal/str_format/output.h",
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index 01f8618..d2928bd 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -21,7 +21,9 @@
     "ascii.h"
     "charconv.h"
     "escaping.h"
+    "internal/damerau_levenshtein_distance.h"
     "internal/string_constant.h"
+    "internal/has_absl_stringify.h"
     "match.h"
     "numbers.h"
     "str_cat.h"
@@ -39,8 +41,11 @@
     "internal/charconv_bigint.h"
     "internal/charconv_parse.cc"
     "internal/charconv_parse.h"
+    "internal/damerau_levenshtein_distance.cc"
     "internal/memutil.cc"
     "internal/memutil.h"
+    "internal/stringify_sink.h"
+    "internal/stringify_sink.cc"
     "internal/stl_type_traits.h"
     "internal/str_join_internal.h"
     "internal/str_split_internal.h"
@@ -134,6 +139,19 @@
 
 absl_cc_test(
   NAME
+    damerau_levenshtein_distance_test
+  SRCS
+    "internal/damerau_levenshtein_distance_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::strings
+    absl::base
+    GTest::gmock_main
+)
+
+absl_cc_test(
+  NAME
     memutil_test
   SRCS
     "internal/memutil.h"
@@ -395,6 +413,7 @@
     "internal/str_format/arg.h"
     "internal/str_format/bind.h"
     "internal/str_format/checker.h"
+    "internal/str_format/constexpr_parser.h"
     "internal/str_format/extension.h"
     "internal/str_format/float_conversion.h"
     "internal/str_format/output.h"
@@ -585,7 +604,9 @@
     absl::base_internal
     absl::compressed_tuple
     absl::config
+    absl::container_memory
     absl::core_headers
+    absl::crc_cord_state
     absl::endian
     absl::inlined_vector
     absl::layout
@@ -724,6 +745,7 @@
     absl::raw_logging_internal
     absl::stacktrace
     absl::synchronization
+    absl::time
 )
 
 absl_cc_test(
@@ -764,6 +786,7 @@
     absl::cordz_statistics
     absl::cordz_update_scope
     absl::cordz_update_tracker
+    absl::crc_cord_state
     absl::thread_pool
     GTest::gmock_main
 )
@@ -863,6 +886,7 @@
     absl::cordz_update_scope
     absl::cordz_update_tracker
     absl::core_headers
+    absl::crc_cord_state
     absl::endian
     absl::fixed_array
     absl::function_ref
@@ -1035,6 +1059,7 @@
     absl::config
     absl::cord_internal
     absl::cord_rep_test_util
+    absl::crc_cord_state
     GTest::gmock_main
 )
 
diff --git a/absl/strings/ascii.cc b/absl/strings/ascii.cc
index 868df2d..16c9689 100644
--- a/absl/strings/ascii.cc
+++ b/absl/strings/ascii.cc
@@ -14,6 +14,10 @@
 
 #include "absl/strings/ascii.h"
 
+#include <climits>
+#include <cstring>
+#include <string>
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace ascii_internal {
@@ -153,18 +157,62 @@
 };
 // clang-format on
 
-}  // namespace ascii_internal
+template <bool ToUpper>
+constexpr void AsciiStrCaseFold(char* p, char* end) {
+  // The upper- and lowercase versions of ASCII characters differ by only 1 bit.
+  // When we need to flip the case, we can xor with this bit to achieve the
+  // desired result. Note that the choice of 'a' and 'A' here is arbitrary. We
+  // could have chosen 'z' and 'Z', or any other pair of characters as they all
+  // have the same single bit difference.
+  constexpr unsigned char kAsciiCaseBitFlip = 'a' ^ 'A';
 
-void AsciiStrToLower(std::string* s) {
-  for (auto& ch : *s) {
-    ch = absl::ascii_tolower(static_cast<unsigned char>(ch));
+  constexpr char ch_a = ToUpper ? 'a' : 'A';
+  constexpr char ch_z = ToUpper ? 'z' : 'Z';
+  for (; p < end; ++p) {
+    unsigned char v = static_cast<unsigned char>(*p);
+    // We use & instead of && to ensure this always stays branchless
+    // We use static_cast<int> to suppress -Wbitwise-instead-of-logical
+    bool is_in_range = static_cast<bool>(static_cast<int>(ch_a <= v) &
+                                         static_cast<int>(v <= ch_z));
+    v ^= is_in_range ? kAsciiCaseBitFlip : 0;
+    *p = static_cast<char>(v);
   }
 }
 
-void AsciiStrToUpper(std::string* s) {
-  for (auto& ch : *s) {
-    ch = absl::ascii_toupper(static_cast<unsigned char>(ch));
+static constexpr size_t ValidateAsciiCasefold() {
+  constexpr size_t num_chars = 1 + CHAR_MAX - CHAR_MIN;
+  size_t incorrect_index = 0;
+  char lowered[num_chars] = {};
+  char uppered[num_chars] = {};
+  for (unsigned int i = 0; i < num_chars; ++i) {
+    uppered[i] = lowered[i] = static_cast<char>(i);
   }
+  AsciiStrCaseFold<false>(&lowered[0], &lowered[num_chars]);
+  AsciiStrCaseFold<true>(&uppered[0], &uppered[num_chars]);
+  for (size_t i = 0; i < num_chars; ++i) {
+    const char ch = static_cast<char>(i),
+               ch_upper = ('a' <= ch && ch <= 'z' ? 'A' + (ch - 'a') : ch),
+               ch_lower = ('A' <= ch && ch <= 'Z' ? 'a' + (ch - 'A') : ch);
+    if (uppered[i] != ch_upper || lowered[i] != ch_lower) {
+      incorrect_index = i > 0 ? i : num_chars;
+      break;
+    }
+  }
+  return incorrect_index;
+}
+
+static_assert(ValidateAsciiCasefold() == 0, "error in case conversion");
+
+}  // namespace ascii_internal
+
+void AsciiStrToLower(std::string* s) {
+  char* p = &(*s)[0];  // Guaranteed to be valid for empty strings
+  return ascii_internal::AsciiStrCaseFold<false>(p, p + s->size());
+}
+
+void AsciiStrToUpper(std::string* s) {
+  char* p = &(*s)[0];  // Guaranteed to be valid for empty strings
+  return ascii_internal::AsciiStrCaseFold<true>(p, p + s->size());
 }
 
 void RemoveExtraAsciiWhitespace(std::string* str) {
diff --git a/absl/strings/ascii_test.cc b/absl/strings/ascii_test.cc
index dfed114..4ea262f 100644
--- a/absl/strings/ascii_test.cc
+++ b/absl/strings/ascii_test.cc
@@ -14,6 +14,7 @@
 
 #include "absl/strings/ascii.h"
 
+#include <algorithm>
 #include <cctype>
 #include <clocale>
 #include <cstring>
@@ -189,14 +190,14 @@
   const std::string str("GHIJKL");
   const std::string str2("MNOPQR");
   const absl::string_view sp(str2);
-  std::string mutable_str("STUVWX");
+  std::string mutable_str("_`?@[{AMNOPQRSTUVWXYZ");
 
   EXPECT_EQ("abcdef", absl::AsciiStrToLower(buf));
   EXPECT_EQ("ghijkl", absl::AsciiStrToLower(str));
   EXPECT_EQ("mnopqr", absl::AsciiStrToLower(sp));
 
   absl::AsciiStrToLower(&mutable_str);
-  EXPECT_EQ("stuvwx", mutable_str);
+  EXPECT_EQ("_`?@[{amnopqrstuvwxyz", mutable_str);
 
   char mutable_buf[] = "Mutable";
   std::transform(mutable_buf, mutable_buf + strlen(mutable_buf),
@@ -207,12 +208,12 @@
 TEST(AsciiStrTo, Upper) {
   const char buf[] = "abcdef";
   const std::string str("ghijkl");
-  const std::string str2("mnopqr");
+  const std::string str2("_`?@[{amnopqrstuvwxyz");
   const absl::string_view sp(str2);
 
   EXPECT_EQ("ABCDEF", absl::AsciiStrToUpper(buf));
   EXPECT_EQ("GHIJKL", absl::AsciiStrToUpper(str));
-  EXPECT_EQ("MNOPQR", absl::AsciiStrToUpper(sp));
+  EXPECT_EQ("_`?@[{AMNOPQRSTUVWXYZ", absl::AsciiStrToUpper(sp));
 
   char mutable_buf[] = "Mutable";
   std::transform(mutable_buf, mutable_buf + strlen(mutable_buf),
diff --git a/absl/strings/charconv.cc b/absl/strings/charconv.cc
index 25ac449..778a1c7 100644
--- a/absl/strings/charconv.cc
+++ b/absl/strings/charconv.cc
@@ -21,6 +21,7 @@
 #include <limits>
 
 #include "absl/base/casts.h"
+#include "absl/base/config.h"
 #include "absl/numeric/bits.h"
 #include "absl/numeric/int128.h"
 #include "absl/strings/internal/charconv_bigint.h"
@@ -118,10 +119,17 @@
   static constexpr int kEiselLemireMaxExclusiveExp10 = 309;
 
   static double MakeNan(const char* tagp) {
+#if ABSL_HAVE_BUILTIN(__builtin_nan)
+    // Use __builtin_nan() if available since it has a fix for
+    // https://bugs.llvm.org/show_bug.cgi?id=37778
+    // std::nan may use the glibc implementation.
+    return __builtin_nan(tagp);
+#else
     // Support nan no matter which namespace it's in.  Some platforms
     // incorrectly don't put it in namespace std.
     using namespace std;  // NOLINT
     return nan(tagp);
+#endif
   }
 
   // Builds a nonzero floating point number out of the provided parts.
@@ -184,10 +192,17 @@
   static constexpr int kEiselLemireMaxExclusiveExp10 = 39;
 
   static float MakeNan(const char* tagp) {
+#if ABSL_HAVE_BUILTIN(__builtin_nanf)
+    // Use __builtin_nanf() if available since it has a fix for
+    // https://bugs.llvm.org/show_bug.cgi?id=37778
+    // std::nanf may use the glibc implementation.
+    return __builtin_nanf(tagp);
+#else
     // Support nanf no matter which namespace it's in.  Some platforms
     // incorrectly don't put it in namespace std.
     using namespace std;  // NOLINT
-    return nanf(tagp);
+    return std::nanf(tagp);
+#endif
   }
 
   static float Make(mantissa_t mantissa, int exponent, bool sign) {
@@ -203,7 +218,8 @@
     if (mantissa > kMantissaMask) {
       // Normal value.
       // Adjust by 127 for the exponent representation bias, and an additional
-      // 23 due to the implied decimal point in the IEEE mantissa represenation.
+      // 23 due to the implied decimal point in the IEEE mantissa
+      // representation.
       flt += static_cast<uint32_t>(exponent + 127 + kTargetMantissaBits - 1)
              << 23;
       mantissa &= kMantissaMask;
@@ -298,7 +314,9 @@
 // minus the number of leading zero bits.)
 int BitWidth(uint128 value) {
   if (Uint128High64(value) == 0) {
-    return bit_width(Uint128Low64(value));
+    // This static_cast is only needed when using a std::bit_width()
+    // implementation that does not have the fix for LWG 3656 applied.
+    return static_cast<int>(bit_width(Uint128Low64(value)));
   }
   return 128 - countl_zero(Uint128High64(value));
 }
@@ -461,7 +479,7 @@
     // the low bit of `value` is set.
     //
     // In inexact mode, the nonzero error means the actual value is greater
-    // than the halfway point and we must alway round up.
+    // than the halfway point and we must always round up.
     if ((value & 1) == 1 || !input_exact) {
       ++value;
     }
@@ -581,7 +599,9 @@
     const strings_internal::ParsedFloat& parsed_hex) {
   uint64_t mantissa = parsed_hex.mantissa;
   int exponent = parsed_hex.exponent;
-  int mantissa_width = bit_width(mantissa);
+  // This static_cast is only needed when using a std::bit_width()
+  // implementation that does not have the fix for LWG 3656 applied.
+  int mantissa_width = static_cast<int>(bit_width(mantissa));
   const int shift = NormalizedShiftSize<FloatType>(mantissa_width, exponent);
   bool result_exact;
   exponent += shift;
diff --git a/absl/strings/charconv.h b/absl/strings/charconv.h
index 7c50981..111c712 100644
--- a/absl/strings/charconv.h
+++ b/absl/strings/charconv.h
@@ -22,7 +22,7 @@
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
-// Workalike compatibilty version of std::chars_format from C++17.
+// Workalike compatibility version of std::chars_format from C++17.
 //
 // This is an bitfield enumerator which can be passed to absl::from_chars to
 // configure the string-to-float conversion.
@@ -48,7 +48,7 @@
   std::errc ec;
 };
 
-// Workalike compatibilty version of std::from_chars from C++17.  Currently
+// Workalike compatibility version of std::from_chars from C++17.  Currently
 // this only supports the `double` and `float` types.
 //
 // This interface incorporates the proposed resolutions for library issues
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc
index 66f45fe..14976ae 100644
--- a/absl/strings/cord.cc
+++ b/absl/strings/cord.cc
@@ -35,6 +35,7 @@
 #include "absl/base/port.h"
 #include "absl/container/fixed_array.h"
 #include "absl/container/inlined_vector.h"
+#include "absl/crc/internal/crc_cord_state.h"
 #include "absl/strings/cord_buffer.h"
 #include "absl/strings/escaping.h"
 #include "absl/strings/internal/cord_data_edge.h"
@@ -47,7 +48,6 @@
 #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"
 #include "absl/strings/str_join.h"
 #include "absl/strings/string_view.h"
 
@@ -167,9 +167,7 @@
 
 inline void Cord::InlineRep::set_data(const char* data, size_t n) {
   static_assert(kMaxInline == 15, "set_data is hard-coded for a length of 15");
-
-  cord_internal::SmallMemmove<true>(data_.as_chars(), data, n);
-  set_inline_size(n);
+  data_.set_inline_data(data, n);
 }
 
 inline char* Cord::InlineRep::set_data(size_t n) {
@@ -420,6 +418,7 @@
 // we keep it here to make diffs easier.
 void Cord::InlineRep::AppendArray(absl::string_view src,
                                   MethodIdentifier method) {
+  MaybeRemoveEmptyCrcNode();
   if (src.empty()) return;  // memcpy(_, nullptr, 0) is undefined.
 
   size_t appended = 0;
@@ -437,8 +436,8 @@
     size_t inline_length = inline_size();
     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());
       return;
     }
 
@@ -479,6 +478,10 @@
 template <typename C>
 inline void Cord::AppendImpl(C&& src) {
   auto constexpr method = CordzUpdateTracker::kAppendCord;
+
+  contents_.MaybeRemoveEmptyCrcNode();
+  if (src.empty()) return;
+
   if (empty()) {
     // Since destination is empty, we can avoid allocating a node,
     if (src.contents_.is_tree()) {
@@ -591,6 +594,9 @@
 template void Cord::Append(std::string&& src);
 
 void Cord::Prepend(const Cord& src) {
+  contents_.MaybeRemoveEmptyCrcNode();
+  if (src.empty()) return;
+
   CordRep* src_tree = src.contents_.tree();
   if (src_tree != nullptr) {
     CordRep::Ref(src_tree);
@@ -605,15 +611,17 @@
 }
 
 void Cord::PrependArray(absl::string_view src, MethodIdentifier method) {
+  contents_.MaybeRemoveEmptyCrcNode();
   if (src.empty()) return;  // memcpy(_, nullptr, 0) is undefined.
+
   if (!contents_.is_tree()) {
     size_t cur_size = contents_.inline_size();
     if (cur_size + src.size() <= InlineRep::kMaxInline) {
       // Use embedded storage.
       InlineData data;
+      data.set_inline_size(cur_size + src.size());
       memcpy(data.as_chars(), src.data(), src.size());
       memcpy(data.as_chars() + src.size(), contents_.data(), cur_size);
-      data.set_inline_size(cur_size + src.size());
       contents_.data_ = data;
       return;
     }
@@ -627,8 +635,8 @@
   assert(src.size() <= cord_internal::kMaxFlatLength);
   if (contents_.remaining_inline_capacity() >= src.size()) {
     const size_t inline_length = contents_.inline_size();
-    memcpy(contents_.data_.as_chars() + inline_length, src.data(), src.size());
     contents_.set_inline_size(inline_length + src.size());
+    memcpy(contents_.data_.as_chars() + inline_length, src.data(), src.size());
   } else {
     contents_.AppendTree(CordRepFlat::Create(src), method);
   }
@@ -640,9 +648,9 @@
   if (contents_.remaining_inline_capacity() >= src.size()) {
     const size_t cur_size = contents_.inline_size();
     InlineData data;
+    data.set_inline_size(cur_size + src.size());
     memcpy(data.as_chars(), src.data(), src.size());
     memcpy(data.as_chars() + src.size(), contents_.data(), cur_size);
-    data.set_inline_size(cur_size + src.size());
     contents_.data_ = data;
   } else {
     contents_.PrependTree(CordRepFlat::Create(src), method);
@@ -665,6 +673,7 @@
   ABSL_INTERNAL_CHECK(n <= size(),
                       absl::StrCat("Requested prefix size ", n,
                                    " exceeds Cord's size ", size()));
+  contents_.MaybeRemoveEmptyCrcNode();
   CordRep* tree = contents_.tree();
   if (tree == nullptr) {
     contents_.remove_prefix(n);
@@ -695,6 +704,7 @@
   ABSL_INTERNAL_CHECK(n <= size(),
                       absl::StrCat("Requested suffix size ", n,
                                    " exceeds Cord's size ", size()));
+  contents_.MaybeRemoveEmptyCrcNode();
   CordRep* tree = contents_.tree();
   if (tree == nullptr) {
     contents_.reduce_size(n);
@@ -733,6 +743,7 @@
   }
 
   if (new_size <= InlineRep::kMaxInline) {
+    sub_cord.contents_.set_inline_size(new_size);
     char* dest = sub_cord.contents_.data_.as_chars();
     Cord::ChunkIterator it = chunk_begin();
     it.AdvanceBytes(pos);
@@ -744,7 +755,6 @@
       ++it;
     }
     cord_internal::SmallMemmove(dest, it->data(), remaining_size);
-    sub_cord.contents_.set_inline_size(new_size);
     return sub_cord;
   }
 
@@ -784,7 +794,7 @@
 }
 
 // This overload set computes comparison results from memcmp result. This
-// interface is used inside GenericCompare below. Differet implementations
+// interface is used inside GenericCompare below. Different implementations
 // are specialized for int and bool. For int we clamp result to {-1, 0, 1}
 // set. For bool we just interested in "value == 0".
 template <typename ResultType>
@@ -842,26 +852,44 @@
   return absl::string_view(node->external()->base + offset, length);
 }
 
-void Cord::SetExpectedChecksum(uint32_t crc) {
+void Cord::SetCrcCordState(crc_internal::CrcCordState state) {
   auto constexpr method = CordzUpdateTracker::kSetExpectedChecksum;
-  if (empty()) return;
-
-  if (!contents_.is_tree()) {
+  if (empty()) {
+    contents_.MaybeRemoveEmptyCrcNode();
+    CordRep* rep = CordRepCrc::New(nullptr, std::move(state));
+    contents_.EmplaceTree(rep, method);
+  } else if (!contents_.is_tree()) {
     CordRep* rep = contents_.MakeFlatWithExtraCapacity(0);
-    rep = CordRepCrc::New(rep, crc);
+    rep = CordRepCrc::New(rep, std::move(state));
     contents_.EmplaceTree(rep, method);
   } else {
     const CordzUpdateScope scope(contents_.data_.cordz_info(), method);
-    CordRep* rep = CordRepCrc::New(contents_.data_.as_tree(), crc);
+    CordRep* rep = CordRepCrc::New(contents_.data_.as_tree(), std::move(state));
     contents_.SetTree(rep, scope);
   }
 }
 
+void Cord::SetExpectedChecksum(uint32_t crc) {
+  // Construct a CrcCordState with a single chunk.
+  crc_internal::CrcCordState state;
+  state.mutable_rep()->prefix_crc.push_back(
+      crc_internal::CrcCordState::PrefixCrc(size(), absl::crc32c_t{crc}));
+  SetCrcCordState(std::move(state));
+}
+
+const crc_internal::CrcCordState* Cord::MaybeGetCrcCordState() const {
+  if (!contents_.is_tree() || !contents_.tree()->IsCrc()) {
+    return nullptr;
+  }
+  return &contents_.tree()->crc()->crc_cord_state;
+}
+
 absl::optional<uint32_t> Cord::ExpectedChecksum() const {
   if (!contents_.is_tree() || !contents_.tree()->IsCrc()) {
     return absl::nullopt;
   }
-  return contents_.tree()->crc()->crc;
+  return static_cast<uint32_t>(
+      contents_.tree()->crc()->crc_cord_state.Checksum());
 }
 
 inline int Cord::CompareSlowPath(absl::string_view rhs, size_t compared_size,
@@ -929,6 +957,7 @@
 }
 
 inline absl::string_view Cord::GetFirstChunk(const Cord& c) {
+  if (c.empty()) return {};
   return c.contents_.FindFlatStartPiece();
 }
 inline absl::string_view Cord::GetFirstChunk(absl::string_view sv) {
@@ -1166,6 +1195,10 @@
 
 /* static */ bool Cord::GetFlatAux(CordRep* rep, absl::string_view* fragment) {
   assert(rep != nullptr);
+  if (rep->length == 0) {
+    *fragment = absl::string_view();
+    return true;
+  }
   rep = cord_internal::SkipCrcNode(rep);
   if (rep->IsFlat()) {
     *fragment = absl::string_view(rep->flat()->Data(), rep->length);
@@ -1197,6 +1230,7 @@
     absl::cord_internal::CordRep* rep,
     absl::FunctionRef<void(absl::string_view)> callback) {
   assert(rep != nullptr);
+  if (rep->length == 0) return;
   rep = cord_internal::SkipCrcNode(rep);
 
   if (rep->IsBtree()) {
@@ -1230,8 +1264,12 @@
     if (include_data) *os << static_cast<void*>(rep);
     *os << "]";
     *os << " " << std::setw(indent) << "";
-    if (rep->IsCrc()) {
-      *os << "CRC crc=" << rep->crc()->crc << "\n";
+    bool leaf = false;
+    if (rep == nullptr) {
+      *os << "NULL\n";
+      leaf = true;
+    } else if (rep->IsCrc()) {
+      *os << "CRC crc=" << rep->crc()->crc_cord_state.Checksum() << "\n";
       indent += kIndentStep;
       rep = rep->crc()->child;
     } else if (rep->IsSubstring()) {
@@ -1239,6 +1277,7 @@
       indent += kIndentStep;
       rep = rep->substring()->child;
     } else {  // Leaf or ring
+      leaf = true;
       if (rep->IsExternal()) {
         *os << "EXTERNAL [";
         if (include_data)
@@ -1252,6 +1291,8 @@
       } else {
         CordRepBtree::Dump(rep, /*label=*/ "", include_data, *os);
       }
+    }
+    if (leaf) {
       if (stack.empty()) break;
       rep = stack.back();
       stack.pop_back();
@@ -1297,11 +1338,14 @@
                               node->substring()->child->length,
                           ReportError(root, node));
     } else if (node->IsCrc()) {
-      ABSL_INTERNAL_CHECK(node->crc()->child != nullptr,
-                          ReportError(root, node));
-      ABSL_INTERNAL_CHECK(node->crc()->length == node->crc()->child->length,
-                          ReportError(root, node));
-      worklist.push_back(node->crc()->child);
+      ABSL_INTERNAL_CHECK(
+          node->crc()->child != nullptr || node->crc()->length == 0,
+          ReportError(root, node));
+      if (node->crc()->child != nullptr) {
+        ABSL_INTERNAL_CHECK(node->crc()->length == node->crc()->child->length,
+                            ReportError(root, node));
+        worklist.push_back(node->crc()->child);
+      }
     }
   } while (!worklist.empty());
   return true;
diff --git a/absl/strings/cord.h b/absl/strings/cord.h
index 88e1c85..f5a2da9 100644
--- a/absl/strings/cord.h
+++ b/absl/strings/cord.h
@@ -76,6 +76,7 @@
 #include "absl/base/macros.h"
 #include "absl/base/port.h"
 #include "absl/container/inlined_vector.h"
+#include "absl/crc/internal/crc_cord_state.h"
 #include "absl/functional/function_ref.h"
 #include "absl/meta/type_traits.h"
 #include "absl/strings/cord_analysis.h"
@@ -660,7 +661,7 @@
   class CharRange {
    public:
     // Fulfill minimum c++ container requirements [container.requirements]
-    // Theses (partial) container type definitions allow CharRange to be used
+    // These (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;
@@ -814,7 +815,7 @@
     InlineRep& operator=(const InlineRep& src);
     InlineRep& operator=(InlineRep&& src) noexcept;
 
-    explicit constexpr InlineRep(cord_internal::InlineData data);
+    explicit constexpr InlineRep(absl::string_view sv, CordRep* rep);
 
     void Swap(InlineRep* rhs);
     bool empty() const;
@@ -873,15 +874,14 @@
     void PrependTreeToTree(CordRep* tree, MethodIdentifier method);
     void PrependTree(CordRep* tree, MethodIdentifier method);
 
-    bool IsSame(const InlineRep& other) const {
-      return memcmp(&data_, &other.data_, sizeof(data_)) == 0;
-    }
+    bool IsSame(const InlineRep& other) const { return data_ == other.data_; }
+
     void CopyTo(std::string* dst) const {
       // memcpy is much faster when operating on a known size. On most supported
       // platforms, the small string optimization is large enough that resizing
       // to 15 bytes does not cause a memory allocation.
       absl::strings_internal::STLStringResizeUninitialized(dst, kMaxInline);
-      memcpy(&(*dst)[0], data_.as_chars(), kMaxInline);
+      data_.copy_max_inline_to(&(*dst)[0]);
       // erase is faster than resize because the logic for memory allocation is
       // not needed.
       dst->erase(inline_size());
@@ -926,6 +926,13 @@
     void set_inline_size(size_t size) { data_.set_inline_size(size); }
     size_t inline_size() const { return data_.inline_size(); }
 
+    // Empty cords that carry a checksum have a CordRepCrc node with a null
+    // child node. The code can avoid lots of special cases where it would
+    // otherwise transition from tree to inline storage if we just remove the
+    // CordRepCrc node before mutations. Must never be called inside a
+    // CordzUpdateScope since it untracks the cordz info.
+    void MaybeRemoveEmptyCrcNode();
+
     cord_internal::InlineData data_;
   };
   InlineRep contents_;
@@ -995,6 +1002,10 @@
     });
     return H::combine(combiner.finalize(std::move(hash_state)), size());
   }
+
+  friend class CrcCord;
+  void SetCrcCordState(crc_internal::CrcCordState state);
+  const crc_internal::CrcCordState* MaybeGetCrcCordState() const;
 };
 
 ABSL_NAMESPACE_END
@@ -1011,46 +1022,6 @@
 
 namespace cord_internal {
 
-// Fast implementation of memmove for up to 15 bytes. This implementation is
-// safe for overlapping regions. If nullify_tail is true, the destination is
-// padded with '\0' up to 15 bytes.
-template <bool nullify_tail = false>
-inline void SmallMemmove(char* dst, const char* src, size_t n) {
-  if (n >= 8) {
-    assert(n <= 15);
-    uint64_t buf1;
-    uint64_t buf2;
-    memcpy(&buf1, src, 8);
-    memcpy(&buf2, src + n - 8, 8);
-    if (nullify_tail) {
-      memset(dst + 7, 0, 8);
-    }
-    memcpy(dst, &buf1, 8);
-    memcpy(dst + n - 8, &buf2, 8);
-  } else if (n >= 4) {
-    uint32_t buf1;
-    uint32_t buf2;
-    memcpy(&buf1, src, 4);
-    memcpy(&buf2, src + n - 4, 4);
-    if (nullify_tail) {
-      memset(dst + 4, 0, 4);
-      memset(dst + 7, 0, 8);
-    }
-    memcpy(dst, &buf1, 4);
-    memcpy(dst + n - 4, &buf2, 4);
-  } else {
-    if (n != 0) {
-      dst[0] = src[0];
-      dst[n / 2] = src[n / 2];
-      dst[n - 1] = src[n - 1];
-    }
-    if (nullify_tail) {
-      memset(dst + 7, 0, 8);
-      memset(dst + n, 0, 8);
-    }
-  }
-}
-
 // Does non-template-specific `CordRepExternal` initialization.
 // Requires `data` to be non-empty.
 void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep);
@@ -1094,8 +1065,8 @@
   return cord;
 }
 
-constexpr Cord::InlineRep::InlineRep(cord_internal::InlineData data)
-    : data_(data) {}
+constexpr Cord::InlineRep::InlineRep(absl::string_view sv, CordRep* rep)
+    : data_(sv, rep) {}
 
 inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src)
     : data_(InlineData::kDefaultInit) {
@@ -1174,7 +1145,7 @@
   size_t len = data_.inline_size();
   auto* result = CordRepFlat::New(len + extra);
   result->length = len;
-  memcpy(result->Data(), data_.as_chars(), InlineRep::kMaxInline);
+  data_.copy_max_inline_to(result->Data());
   return result;
 }
 
@@ -1236,6 +1207,18 @@
   cord_internal::SmallMemmove(dst, data_.as_chars(), n);
 }
 
+inline void Cord::InlineRep::MaybeRemoveEmptyCrcNode() {
+  CordRep* rep = tree();
+  if (rep == nullptr || ABSL_PREDICT_TRUE(rep->length > 0)) {
+    return;
+  }
+  assert(rep->IsCrc());
+  assert(rep->crc()->child == nullptr);
+  CordzInfo::MaybeUntrackCord(cordz_info());
+  CordRep::Unref(rep);
+  ResetToEmpty();
+}
+
 constexpr inline Cord::Cord() noexcept {}
 
 inline Cord::Cord(absl::string_view src)
@@ -1243,13 +1226,12 @@
 
 template <typename T>
 constexpr Cord::Cord(strings_internal::StringConstant<T>)
-    : contents_(strings_internal::StringConstant<T>::value.size() <=
+    : contents_(strings_internal::StringConstant<T>::value,
+                strings_internal::StringConstant<T>::value.size() <=
                         cord_internal::kMaxInline
-                    ? cord_internal::InlineData(
-                          strings_internal::StringConstant<T>::value)
-                    : cord_internal::InlineData(
-                          &cord_internal::ConstInitExternalStorage<
-                              strings_internal::StringConstant<T>>::value)) {}
+                    ? nullptr
+                    : &cord_internal::ConstInitExternalStorage<
+                          strings_internal::StringConstant<T>>::value) {}
 
 inline Cord& Cord::operator=(const Cord& x) {
   contents_ = x.contents_;
@@ -1285,7 +1267,7 @@
   return contents_.size();
 }
 
-inline bool Cord::empty() const { return contents_.empty(); }
+inline bool Cord::empty() const { return size() == 0; }
 
 inline size_t Cord::EstimatedMemoryUsage(
     CordMemoryAccounting accounting_method) const {
@@ -1411,7 +1393,11 @@
 inline Cord::ChunkIterator::ChunkIterator(const Cord* cord) {
   if (CordRep* tree = cord->contents_.tree()) {
     bytes_remaining_ = tree->length;
-    InitTree(tree);
+    if (ABSL_PREDICT_TRUE(bytes_remaining_ != 0)) {
+      InitTree(tree);
+    } else {
+      current_chunk_ = {};
+    }
   } else {
     bytes_remaining_ = cord->contents_.inline_size();
     current_chunk_ = {cord->contents_.data(), bytes_remaining_};
@@ -1580,7 +1566,7 @@
   if (rep == nullptr) {
     callback(absl::string_view(contents_.data(), contents_.size()));
   } else {
-    return ForEachChunkAux(rep, callback);
+    ForEachChunkAux(rep, callback);
   }
 }
 
diff --git a/absl/strings/cord_buffer.h b/absl/strings/cord_buffer.h
index 15494b3..bc0e4e4 100644
--- a/absl/strings/cord_buffer.h
+++ b/absl/strings/cord_buffer.h
@@ -160,7 +160,6 @@
   // for more information on buffer capacities and intended usage.
   static CordBuffer CreateWithDefaultLimit(size_t capacity);
 
-
   // CordBuffer::CreateWithCustomLimit()
   //
   // Creates a CordBuffer instance of the desired `capacity` rounded to an
@@ -336,7 +335,7 @@
     }
 
     // Returns the available area of the internal SSO data
-    absl::Span<char> long_available() {
+    absl::Span<char> long_available() const {
       assert(!is_short());
       const size_t length = long_rep.rep->length;
       return absl::Span<char>(long_rep.rep->Data() + length,
@@ -460,9 +459,7 @@
 }
 
 inline constexpr size_t CordBuffer::MaximumPayload(size_t block_size) {
-  // TODO(absl-team): Use std::min when C++11 support is dropped.
-  return (kCustomLimit < block_size ? kCustomLimit : block_size) -
-         cord_internal::kFlatOverhead;
+  return (std::min)(kCustomLimit, block_size) - cord_internal::kFlatOverhead;
 }
 
 inline CordBuffer CordBuffer::CreateWithDefaultLimit(size_t capacity) {
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc
index 1fc4be6..3fe3967 100644
--- a/absl/strings/cord_test.cc
+++ b/absl/strings/cord_test.cc
@@ -58,6 +58,8 @@
 using absl::cord_internal::CordzUpdateTracker;
 using absl::cord_internal::kFlatOverhead;
 using absl::cord_internal::kMaxFlatLength;
+using ::testing::ElementsAre;
+using ::testing::Le;
 
 static std::string RandomLowercaseString(RandomEngine* rng);
 static std::string RandomLowercaseString(RandomEngine* rng, size_t length);
@@ -618,7 +620,7 @@
 TEST_P(CordTest, AppendSmallBuffer) {
   absl::Cord cord;
   absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(3);
-  ASSERT_THAT(buffer.capacity(), ::testing::Le(15));
+  ASSERT_THAT(buffer.capacity(), Le(15));
   memcpy(buffer.data(), "Abc", 3);
   buffer.SetLength(3);
   cord.Append(std::move(buffer));
@@ -632,7 +634,7 @@
   EXPECT_EQ(buffer.length(), 0);    // NOLINT
   EXPECT_GT(buffer.capacity(), 0);  // NOLINT
 
-  EXPECT_THAT(cord.Chunks(), ::testing::ElementsAre("Abcdefgh"));
+  EXPECT_THAT(cord.Chunks(), ElementsAre("Abcdefgh"));
 }
 
 TEST_P(CordTest, AppendAndPrependBufferArePrecise) {
@@ -671,7 +673,7 @@
 TEST_P(CordTest, PrependSmallBuffer) {
   absl::Cord cord;
   absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(3);
-  ASSERT_THAT(buffer.capacity(), ::testing::Le(15));
+  ASSERT_THAT(buffer.capacity(), Le(15));
   memcpy(buffer.data(), "Abc", 3);
   buffer.SetLength(3);
   cord.Prepend(std::move(buffer));
@@ -685,7 +687,7 @@
   EXPECT_EQ(buffer.length(), 0);    // NOLINT
   EXPECT_GT(buffer.capacity(), 0);  // NOLINT
 
-  EXPECT_THAT(cord.Chunks(), ::testing::ElementsAre("defghAbc"));
+  EXPECT_THAT(cord.Chunks(), ElementsAre("defghAbc"));
 }
 
 TEST_P(CordTest, AppendLargeBuffer) {
@@ -707,7 +709,7 @@
   EXPECT_EQ(buffer.length(), 0);    // NOLINT
   EXPECT_GT(buffer.capacity(), 0);  // NOLINT
 
-  EXPECT_THAT(cord.Chunks(), ::testing::ElementsAre(s1, s2));
+  EXPECT_THAT(cord.Chunks(), ElementsAre(s1, s2));
 }
 
 TEST_P(CordTest, PrependLargeBuffer) {
@@ -729,7 +731,7 @@
   EXPECT_EQ(buffer.length(), 0);    // NOLINT
   EXPECT_GT(buffer.capacity(), 0);  // NOLINT
 
-  EXPECT_THAT(cord.Chunks(), ::testing::ElementsAre(s2, s1));
+  EXPECT_THAT(cord.Chunks(), ElementsAre(s2, s1));
 }
 
 class CordAppendBufferTest : public testing::TestWithParam<bool> {
@@ -1988,6 +1990,12 @@
 
 // Tests that Append() works ok when handed a self reference
 TEST_P(CordTest, AppendSelf) {
+  // Test the empty case.
+  absl::Cord empty;
+  MaybeHarden(empty);
+  empty.Append(empty);
+  ASSERT_EQ(empty, "");
+
   // We run the test until data is ~16K
   // This guarantees it covers small, medium and large data.
   std::string control_data = "Abc";
@@ -2712,7 +2720,7 @@
 
 // clang-format off
 // This array is constant-initialized in conformant compilers.
-CordMutator cord_mutators[] ={
+CordMutator cord_mutators[] = {
   {"clear", [](absl::Cord& c) { c.Clear(); }},
   {"overwrite", [](absl::Cord& c) { c = "overwritten"; }},
   {
@@ -2742,6 +2750,25 @@
     [](absl::Cord& c) { c.RemoveSuffix(c.size() / 2); }
   },
   {
+    "append empty string",
+    [](absl::Cord& c) { c.Append(""); },
+    [](absl::Cord& c) { }
+  },
+  {
+    "append empty cord",
+    [](absl::Cord& c) { c.Append(absl::Cord()); },
+    [](absl::Cord& c) { }
+  },
+  {
+    "append empty checksummed cord",
+    [](absl::Cord& c) {
+      absl::Cord to_append;
+      to_append.SetExpectedChecksum(999);
+      c.Append(to_append);
+    },
+    [](absl::Cord& c) { }
+  },
+  {
     "prepend string",
     [](absl::Cord& c) { c.Prepend("9876543210"); },
     [](absl::Cord& c) { c.RemovePrefix(10); }
@@ -2763,12 +2790,33 @@
     [](absl::Cord& c) { c.RemovePrefix(10); }
   },
   {
+    "prepend empty string",
+    [](absl::Cord& c) { c.Prepend(""); },
+    [](absl::Cord& c) { }
+  },
+  {
+    "prepend empty cord",
+    [](absl::Cord& c) { c.Prepend(absl::Cord()); },
+    [](absl::Cord& c) { }
+  },
+  {
+    "prepend empty checksummed cord",
+    [](absl::Cord& c) {
+      absl::Cord to_prepend;
+      to_prepend.SetExpectedChecksum(999);
+      c.Prepend(to_prepend);
+    },
+    [](absl::Cord& c) { }
+  },
+  {
     "prepend self",
     [](absl::Cord& c) { c.Prepend(c); },
     [](absl::Cord& c) { c.RemovePrefix(c.size() / 2); }
   },
-  {"remove prefix", [](absl::Cord& c) { c.RemovePrefix(2); }},
-  {"remove suffix", [](absl::Cord& c) { c.RemoveSuffix(2); }},
+  {"remove prefix", [](absl::Cord& c) { c.RemovePrefix(c.size() / 2); }},
+  {"remove suffix", [](absl::Cord& c) { c.RemoveSuffix(c.size() / 2); }},
+  {"remove 0-prefix", [](absl::Cord& c) { c.RemovePrefix(0); }},
+  {"remove 0-suffix", [](absl::Cord& c) { c.RemoveSuffix(0); }},
   {"subcord", [](absl::Cord& c) { c = c.Subcord(1, c.size() - 2); }},
   {
     "swap inline",
@@ -2810,6 +2858,12 @@
       EXPECT_EQ(c1.ExpectedChecksum().value_or(0), 12345);
       EXPECT_EQ(c1, base_value);
 
+      // Test that setting an expected checksum again doesn't crash or leak
+      // memory.
+      c1.SetExpectedChecksum(12345);
+      EXPECT_EQ(c1.ExpectedChecksum().value_or(0), 12345);
+      EXPECT_EQ(c1, base_value);
+
       // CRC persists through copies, assignments, and moves:
       absl::Cord c1_copy_construct = c1;
       EXPECT_EQ(c1_copy_construct.ExpectedChecksum().value_or(0), 12345);
@@ -2834,6 +2888,13 @@
         c2.SetExpectedChecksum(24680);
 
         mutator.Mutate(c2);
+
+        if (c1 == c2) {
+          // Not a mutation (for example, appending the empty string).
+          // Whether the checksum is removed is not defined.
+          continue;
+        }
+
         EXPECT_EQ(c2.ExpectedChecksum(), absl::nullopt);
 
         if (mutator.CanUndo()) {
@@ -2903,3 +2964,164 @@
     }
   }
 }
+
+// Test the special cases encountered with an empty checksummed cord.
+TEST_P(CordTest, ChecksummedEmptyCord) {
+  absl::Cord c1;
+  EXPECT_FALSE(c1.ExpectedChecksum().has_value());
+
+  // Setting an expected checksum works.
+  c1.SetExpectedChecksum(12345);
+  EXPECT_EQ(c1.ExpectedChecksum().value_or(0), 12345);
+  EXPECT_EQ(c1, "");
+  EXPECT_TRUE(c1.empty());
+
+  // Test that setting an expected checksum again doesn't crash or leak memory.
+  c1.SetExpectedChecksum(12345);
+  EXPECT_EQ(c1.ExpectedChecksum().value_or(0), 12345);
+  EXPECT_EQ(c1, "");
+  EXPECT_TRUE(c1.empty());
+
+  // CRC persists through copies, assignments, and moves:
+  absl::Cord c1_copy_construct = c1;
+  EXPECT_EQ(c1_copy_construct.ExpectedChecksum().value_or(0), 12345);
+
+  absl::Cord c1_copy_assign;
+  c1_copy_assign = c1;
+  EXPECT_EQ(c1_copy_assign.ExpectedChecksum().value_or(0), 12345);
+
+  absl::Cord c1_move(std::move(c1_copy_assign));
+  EXPECT_EQ(c1_move.ExpectedChecksum().value_or(0), 12345);
+
+  EXPECT_EQ(c1.ExpectedChecksum().value_or(0), 12345);
+
+  // A CRC Cord compares equal to its non-CRC value.
+  EXPECT_EQ(c1, absl::Cord());
+
+  for (const CordMutator& mutator : cord_mutators) {
+    SCOPED_TRACE(mutator.Name());
+
+    // Exercise mutating an empty checksummed cord to catch crashes and exercise
+    // memory sanitizers.
+    absl::Cord c2;
+    c2.SetExpectedChecksum(24680);
+    mutator.Mutate(c2);
+
+    if (c2.empty()) {
+      // Not a mutation
+      continue;
+    }
+    EXPECT_EQ(c2.ExpectedChecksum(), absl::nullopt);
+
+    if (mutator.CanUndo()) {
+      mutator.Undo(c2);
+    }
+  }
+
+  absl::Cord c3;
+  c3.SetExpectedChecksum(999);
+  const absl::Cord& cc3 = c3;
+
+  // Test that all cord reading operations function in the face of an
+  // expected checksum.
+  EXPECT_TRUE(cc3.StartsWith(""));
+  EXPECT_TRUE(cc3.EndsWith(""));
+  EXPECT_TRUE(cc3.empty());
+  EXPECT_EQ(cc3, "");
+  EXPECT_EQ(cc3, absl::Cord());
+  EXPECT_EQ(cc3.size(), 0);
+  EXPECT_EQ(cc3.Compare(absl::Cord()), 0);
+  EXPECT_EQ(cc3.Compare(c1), 0);
+  EXPECT_EQ(cc3.Compare(cc3), 0);
+  EXPECT_EQ(cc3.Compare(""), 0);
+  EXPECT_EQ(cc3.Compare("wxyz"), -1);
+  EXPECT_EQ(cc3.Compare(absl::Cord("wxyz")), -1);
+  EXPECT_EQ(absl::Cord("wxyz").Compare(cc3), 1);
+  EXPECT_EQ(std::string(cc3), "");
+
+  std::string dest;
+  absl::CopyCordToString(cc3, &dest);
+  EXPECT_EQ(dest, "");
+
+  for (absl::string_view chunk : cc3.Chunks()) {  // NOLINT(unreachable loop)
+    static_cast<void>(chunk);
+    GTEST_FAIL() << "no chunks expected";
+  }
+  EXPECT_TRUE(cc3.chunk_begin() == cc3.chunk_end());
+
+  for (char ch : cc3.Chars()) {  // NOLINT(unreachable loop)
+    static_cast<void>(ch);
+    GTEST_FAIL() << "no chars expected";
+  }
+  EXPECT_TRUE(cc3.char_begin() == cc3.char_end());
+
+  EXPECT_EQ(cc3.TryFlat(), "");
+  EXPECT_EQ(absl::HashOf(c3), absl::HashOf(absl::Cord()));
+  EXPECT_EQ(absl::HashOf(c3), absl::HashOf(absl::string_view()));
+}
+
+#if defined(GTEST_HAS_DEATH_TEST) && defined(ABSL_INTERNAL_CORD_HAVE_SANITIZER)
+
+// Returns an expected poison / uninitialized death message expression.
+const char* MASanDeathExpr() {
+  return "(use-after-poison|use-of-uninitialized-value)";
+}
+
+TEST(CordSanitizerTest, SanitizesEmptyCord) {
+  absl::Cord cord;
+  const char* data = cord.Flatten().data();
+  EXPECT_DEATH(EXPECT_EQ(data[0], 0), MASanDeathExpr());
+}
+
+TEST(CordSanitizerTest, SanitizesSmallCord) {
+  absl::Cord cord("Hello");
+  const char* data = cord.Flatten().data();
+  EXPECT_DEATH(EXPECT_EQ(data[5], 0), MASanDeathExpr());
+}
+
+TEST(CordSanitizerTest, SanitizesCordOnSetSSOValue) {
+  absl::Cord cord("String that is too big to be an SSO value");
+  cord = "Hello";
+  const char* data = cord.Flatten().data();
+  EXPECT_DEATH(EXPECT_EQ(data[5], 0), MASanDeathExpr());
+}
+
+TEST(CordSanitizerTest, SanitizesCordOnCopyCtor) {
+  absl::Cord src("hello");
+  absl::Cord dst(src);
+  const char* data = dst.Flatten().data();
+  EXPECT_DEATH(EXPECT_EQ(data[5], 0), MASanDeathExpr());
+}
+
+TEST(CordSanitizerTest, SanitizesCordOnMoveCtor) {
+  absl::Cord src("hello");
+  absl::Cord dst(std::move(src));
+  const char* data = dst.Flatten().data();
+  EXPECT_DEATH(EXPECT_EQ(data[5], 0), MASanDeathExpr());
+}
+
+TEST(CordSanitizerTest, SanitizesCordOnAssign) {
+  absl::Cord src("hello");
+  absl::Cord dst;
+  dst = src;
+  const char* data = dst.Flatten().data();
+  EXPECT_DEATH(EXPECT_EQ(data[5], 0), MASanDeathExpr());
+}
+
+TEST(CordSanitizerTest, SanitizesCordOnMoveAssign) {
+  absl::Cord src("hello");
+  absl::Cord dst;
+  dst = std::move(src);
+  const char* data = dst.Flatten().data();
+  EXPECT_DEATH(EXPECT_EQ(data[5], 0), MASanDeathExpr());
+}
+
+TEST(CordSanitizerTest, SanitizesCordOnSsoAssign) {
+  absl::Cord src("hello");
+  absl::Cord dst("String that is too big to be an SSO value");
+  dst = src;
+  const char* data = dst.Flatten().data();
+  EXPECT_DEATH(EXPECT_EQ(data[5], 0), MASanDeathExpr());
+}
+
+#endif  // GTEST_HAS_DEATH_TEST && ABSL_INTERNAL_CORD_HAVE_SANITIZER
diff --git a/absl/strings/cord_test_helpers.h b/absl/strings/cord_test_helpers.h
index 31a1dc8..ca52240 100644
--- a/absl/strings/cord_test_helpers.h
+++ b/absl/strings/cord_test_helpers.h
@@ -51,7 +51,7 @@
   // 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.
+  // A string value large enough to cause it to be stored in multiple flats.
   kLarge = cord_internal::kMaxFlatLength * 4
 };
 
diff --git a/absl/strings/escaping.cc b/absl/strings/escaping.cc
index 7d97944..2827fba 100644
--- a/absl/strings/escaping.cc
+++ b/absl/strings/escaping.cc
@@ -443,6 +443,8 @@
   }
 }
 
+// Reverses the mapping in Base64EscapeInternal; see that method's
+// documentation for details of the mapping.
 bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest,
                             size_t szdest, const signed char* unbase64,
                             size_t* len) {
@@ -676,7 +678,10 @@
   return ok;
 }
 
-// The arrays below were generated by the following code
+// The arrays below map base64-escaped characters back to their original values.
+// For the inverse case, see k(WebSafe)Base64Chars in the internal
+// escaping.cc.
+// These arrays were generated by the following inversion code:
 // #include <sys/time.h>
 // #include <stdlib.h>
 // #include <string.h>
@@ -703,8 +708,8 @@
 //   }
 // }
 //
-// where the value of "Base64[]" was replaced by one of the base-64 conversion
-// tables from the functions below.
+// where the value of "Base64[]" was replaced by one of k(WebSafe)Base64Chars
+// in the internal escaping.cc.
 /* clang-format off */
 constexpr signed char kUnBase64[] = {
     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
@@ -777,16 +782,11 @@
 };
 /* clang-format on */
 
-constexpr char kWebSafeBase64Chars[] =
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
-
 template <typename String>
 bool Base64UnescapeInternal(const char* src, size_t slen, String* dest,
                             const signed char* unbase64) {
   // Determine the size of the output string.  Base64 encodes every 3 bytes into
-  // 4 characters.  any leftover chars are added directly for good measure.
-  // This is documented in the base64 RFC:
-  // https://datatracker.ietf.org/doc/html/rfc3548
+  // 4 characters.  Any leftover chars are added directly for good measure.
   const size_t dest_len = 3 * (slen / 4) + (slen % 4);
 
   strings_internal::STLStringResizeUninitialized(dest, dest_len);
@@ -882,30 +882,6 @@
   return CEscapeInternal(src, true, true);
 }
 
-// ----------------------------------------------------------------------
-// Base64Unescape() - base64 decoder
-// Base64Escape() - base64 encoder
-// WebSafeBase64Unescape() - Google's variation of base64 decoder
-// WebSafeBase64Escape() - Google's variation of base64 encoder
-//
-// Check out
-// https://datatracker.ietf.org/doc/html/rfc2045 for formal description, but
-// what we care about is that...
-//   Take the encoded stuff in groups of 4 characters and turn each
-//   character into a code 0 to 63 thus:
-//           A-Z map to 0 to 25
-//           a-z map to 26 to 51
-//           0-9 map to 52 to 61
-//           +(- for WebSafe) maps to 62
-//           /(_ for WebSafe) maps to 63
-//   There will be four numbers, all less than 64 which can be represented
-//   by a 6 digit binary number (aaaaaa, bbbbbb, cccccc, dddddd respectively).
-//   Arrange the 6 digit binary numbers into three bytes as such:
-//   aaaaaabb bbbbcccc ccdddddd
-//   Equals signs (one or two) are used at the end of the encoded block to
-//   indicate that the text was not an integer multiple of three bytes long.
-// ----------------------------------------------------------------------
-
 bool Base64Unescape(absl::string_view src, std::string* dest) {
   return Base64UnescapeInternal(src.data(), src.size(), dest, kUnBase64);
 }
@@ -923,7 +899,7 @@
 void WebSafeBase64Escape(absl::string_view src, std::string* dest) {
   strings_internal::Base64EscapeInternal(
       reinterpret_cast<const unsigned char*>(src.data()), src.size(), dest,
-      false, kWebSafeBase64Chars);
+      false, strings_internal::kWebSafeBase64Chars);
 }
 
 std::string Base64Escape(absl::string_view src) {
@@ -938,7 +914,7 @@
   std::string dest;
   strings_internal::Base64EscapeInternal(
       reinterpret_cast<const unsigned char*>(src.data()), src.size(), &dest,
-      false, kWebSafeBase64Chars);
+      false, strings_internal::kWebSafeBase64Chars);
   return dest;
 }
 
diff --git a/absl/strings/escaping.h b/absl/strings/escaping.h
index f5ca26c..bf2a589 100644
--- a/absl/strings/escaping.h
+++ b/absl/strings/escaping.h
@@ -117,35 +117,40 @@
 // conversion.
 std::string Utf8SafeCHexEscape(absl::string_view src);
 
-// Base64Unescape()
-//
-// Converts a `src` string encoded in Base64 to its binary equivalent, writing
-// it to a `dest` buffer, returning `true` on success. If `src` contains invalid
-// characters, `dest` is cleared and returns `false`.
-bool Base64Unescape(absl::string_view src, std::string* dest);
-
-// WebSafeBase64Unescape()
-//
-// Converts a `src` string encoded in Base64 to its binary equivalent, writing
-// it to a `dest` buffer, but using '-' instead of '+', and '_' instead of '/'.
-// If `src` contains invalid characters, `dest` is cleared and returns `false`.
-bool WebSafeBase64Unescape(absl::string_view src, std::string* dest);
-
 // Base64Escape()
 //
-// Encodes a `src` string into a base64-encoded string, with padding characters.
-// This function conforms with RFC 4648 section 4 (base64).
+// Encodes a `src` string into a base64-encoded 'dest' string with padding
+// characters. This function conforms with RFC 4648 section 4 (base64) and RFC
+// 2045.
 void Base64Escape(absl::string_view src, std::string* dest);
 std::string Base64Escape(absl::string_view src);
 
 // WebSafeBase64Escape()
 //
-// Encodes a `src` string into a base64-like string, using '-' instead of '+'
-// and '_' instead of '/', and without padding. This function conforms with RFC
-// 4648 section 5 (base64url).
+// Encodes a `src` string into a base64 string, like Base64Escape() does, but
+// outputs '-' instead of '+' and '_' instead of '/', and does not pad 'dest'.
+// This function conforms with RFC 4648 section 5 (base64url).
 void WebSafeBase64Escape(absl::string_view src, std::string* dest);
 std::string WebSafeBase64Escape(absl::string_view src);
 
+// Base64Unescape()
+//
+// Converts a `src` string encoded in Base64 (RFC 4648 section 4) to its binary
+// equivalent, writing it to a `dest` buffer, returning `true` on success. If
+// `src` contains invalid characters, `dest` is cleared and returns `false`.
+// If padding is included (note that `Base64Escape()` does produce it), it must
+// be correct. In the padding, '=' and '.' are treated identically.
+bool Base64Unescape(absl::string_view src, std::string* dest);
+
+// WebSafeBase64Unescape()
+//
+// Converts a `src` string encoded in "web safe" Base64 (RFC 4648 section 5) to
+// its binary equivalent, writing it to a `dest` buffer. If `src` contains
+// invalid characters, `dest` is cleared and returns `false`. If padding is
+// included (note that `WebSafeBase64Escape()` does not produce it), it must be
+// correct. In the padding, '=' and '.' are treated identically.
+bool WebSafeBase64Unescape(absl::string_view src, std::string* dest);
+
 // HexStringToBytes()
 //
 // Converts an ASCII hex string into bytes, returning binary data of length
diff --git a/absl/strings/escaping_test.cc b/absl/strings/escaping_test.cc
index 45671a0..9f62c1e 100644
--- a/absl/strings/escaping_test.cc
+++ b/absl/strings/escaping_test.cc
@@ -562,6 +562,7 @@
 void TestEscapeAndUnescape() {
   // Check the short strings; this tests the math (and boundaries)
   for (const auto& tc : base64_tests) {
+    // Test plain base64.
     StringType encoded("this junk should be ignored");
     absl::Base64Escape(tc.plaintext, &encoded);
     EXPECT_EQ(encoded, tc.cyphertext);
@@ -571,22 +572,26 @@
     EXPECT_TRUE(absl::Base64Unescape(encoded, &decoded));
     EXPECT_EQ(decoded, tc.plaintext);
 
-    StringType websafe(tc.cyphertext);
-    for (int c = 0; c < websafe.size(); ++c) {
-      if ('+' == websafe[c]) websafe[c] = '-';
-      if ('/' == websafe[c]) websafe[c] = '_';
+    StringType websafe_with_padding(tc.cyphertext);
+    for (unsigned int c = 0; c < websafe_with_padding.size(); ++c) {
+      if ('+' == websafe_with_padding[c]) websafe_with_padding[c] = '-';
+      if ('/' == websafe_with_padding[c]) websafe_with_padding[c] = '_';
+      // Intentionally keeping padding aka '='.
+    }
+
+    // Test plain websafe (aka without padding).
+    StringType websafe(websafe_with_padding);
+    for (unsigned int c = 0; c < websafe.size(); ++c) {
       if ('=' == websafe[c]) {
         websafe.resize(c);
         break;
       }
     }
-
     encoded = "this junk should be ignored";
     absl::WebSafeBase64Escape(tc.plaintext, &encoded);
     EXPECT_EQ(encoded, websafe);
     EXPECT_EQ(absl::WebSafeBase64Escape(tc.plaintext), websafe);
 
-    // Let's try the string version of the decoder
     decoded = "this junk should be ignored";
     EXPECT_TRUE(absl::WebSafeBase64Unescape(websafe, &decoded));
     EXPECT_EQ(decoded, tc.plaintext);
@@ -617,6 +622,48 @@
   TestEscapeAndUnescape<std::string>();
 }
 
+TEST(Base64, Padding) {
+  // Padding is optional.
+  // '.' is an acceptable padding character, just like '='.
+  std::initializer_list<absl::string_view> good_padding = {
+    "YQ",
+    "YQ==",
+    "YQ=.",
+    "YQ.=",
+    "YQ..",
+  };
+  for (absl::string_view b64 : good_padding) {
+    std::string decoded;
+    EXPECT_TRUE(absl::Base64Unescape(b64, &decoded));
+    EXPECT_EQ(decoded, "a");
+    std::string websafe_decoded;
+    EXPECT_TRUE(absl::WebSafeBase64Unescape(b64, &websafe_decoded));
+    EXPECT_EQ(websafe_decoded, "a");
+  }
+  std::initializer_list<absl::string_view> bad_padding = {
+    "YQ=",
+    "YQ.",
+    "YQ===",
+    "YQ==.",
+    "YQ=.=",
+    "YQ=..",
+    "YQ.==",
+    "YQ.=.",
+    "YQ..=",
+    "YQ...",
+    "YQ====",
+    "YQ....",
+    "YQ=====",
+    "YQ.....",
+  };
+  for (absl::string_view b64 : bad_padding) {
+    std::string decoded;
+    EXPECT_FALSE(absl::Base64Unescape(b64, &decoded));
+    std::string websafe_decoded;
+    EXPECT_FALSE(absl::WebSafeBase64Unescape(b64, &websafe_decoded));
+  }
+}
+
 TEST(Base64, DISABLED_HugeData) {
   const size_t kSize = size_t(3) * 1000 * 1000 * 1000;
   static_assert(kSize % 3 == 0, "kSize must be divisible by 3");
diff --git a/absl/strings/internal/char_map.h b/absl/strings/internal/char_map.h
index 5aabc1f..70a9034 100644
--- a/absl/strings/internal/char_map.h
+++ b/absl/strings/internal/char_map.h
@@ -73,10 +73,10 @@
   }
 
   // Containing all the chars in the C-string 's'.
-  // Note that this is expensively recursive because of the C++11 constexpr
-  // formulation. Use only in constexpr initializers.
   static constexpr Charmap FromString(const char* s) {
-    return *s == 0 ? Charmap() : (Char(*s) | FromString(s + 1));
+    Charmap ret;
+    while (*s) ret = ret | Char(*s++);
+    return ret;
   }
 
   // Containing all the chars in the closed interval [lo,hi].
diff --git a/absl/strings/internal/charconv_bigint.cc b/absl/strings/internal/charconv_bigint.cc
index 282b639..46b5289 100644
--- a/absl/strings/internal/charconv_bigint.cc
+++ b/absl/strings/internal/charconv_bigint.cc
@@ -296,10 +296,8 @@
         std::min(n / kLargePowerOfFiveStep, kLargestPowerOfFiveIndex);
     if (first_pass) {
       // just copy, rather than multiplying by 1
-      std::copy(
-          LargePowerOfFiveData(big_power),
-          LargePowerOfFiveData(big_power) + LargePowerOfFiveSize(big_power),
-          answer.words_);
+      std::copy_n(LargePowerOfFiveData(big_power),
+                  LargePowerOfFiveSize(big_power), answer.words_);
       answer.size_ = LargePowerOfFiveSize(big_power);
       first_pass = false;
     } else {
diff --git a/absl/strings/internal/charconv_bigint.h b/absl/strings/internal/charconv_bigint.h
index 8f70297..5c0c375 100644
--- a/absl/strings/internal/charconv_bigint.h
+++ b/absl/strings/internal/charconv_bigint.h
@@ -92,7 +92,7 @@
   // numbers with this many decimal digits or fewer are representable by this
   // type.
   //
-  // Analagous to std::numeric_limits<BigUnsigned>::digits10.
+  // Analogous to std::numeric_limits<BigUnsigned>::digits10.
   static constexpr int Digits10() {
     // 9975007/1035508 is very slightly less than log10(2**32).
     return static_cast<uint64_t>(max_words) * 9975007 / 1035508;
@@ -121,7 +121,7 @@
           ++size_;
         }
       }
-      std::fill(words_, words_ + word_shift, 0u);
+      std::fill_n(words_, word_shift, 0u);
     }
   }
 
@@ -197,7 +197,7 @@
   }
 
   void SetToZero() {
-    std::fill(words_, words_ + size_, 0u);
+    std::fill_n(words_, size_, 0u);
     size_ = 0;
   }
 
diff --git a/absl/strings/internal/cord_internal.cc b/absl/strings/internal/cord_internal.cc
index b6b06cf..b787438 100644
--- a/absl/strings/internal/cord_internal.cc
+++ b/absl/strings/internal/cord_internal.cc
@@ -33,7 +33,6 @@
     kCordEnableRingBufferDefault);
 ABSL_CONST_INIT std::atomic<bool> shallow_subcords_enabled(
     kCordShallowSubcordsDefault);
-ABSL_CONST_INIT std::atomic<bool> cord_btree_exhaustive_validation(false);
 
 void LogFatalNodeType(CordRep* rep) {
   ABSL_INTERNAL_LOG(FATAL, absl::StrCat("Unexpected node type: ",
diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h
index f32fd41..8d9836b 100644
--- a/absl/strings/internal/cord_internal.h
+++ b/absl/strings/internal/cord_internal.h
@@ -27,9 +27,20 @@
 #include "absl/base/internal/invoke.h"
 #include "absl/base/optimization.h"
 #include "absl/container/internal/compressed_tuple.h"
+#include "absl/container/internal/container_memory.h"
 #include "absl/meta/type_traits.h"
 #include "absl/strings/string_view.h"
 
+// We can only add poisoning if we can detect consteval executions.
+#if defined(ABSL_HAVE_CONSTANT_EVALUATED) && \
+    (defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+     defined(ABSL_HAVE_MEMORY_SANITIZER))
+#define ABSL_INTERNAL_CORD_HAVE_SANITIZER 1
+#endif
+
+#define ABSL_CORD_INTERNAL_NO_SANITIZE \
+  ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace cord_internal {
@@ -58,12 +69,6 @@
 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_ring_buffer(bool enable) {
   cord_ring_buffer_enabled.store(enable, std::memory_order_relaxed);
 }
@@ -91,6 +96,46 @@
 // Emits a fatal error "Unexpected node type: xyz" and aborts the program.
 ABSL_ATTRIBUTE_NORETURN void LogFatalNodeType(CordRep* rep);
 
+// Fast implementation of memmove for up to 15 bytes. This implementation is
+// safe for overlapping regions. If nullify_tail is true, the destination is
+// padded with '\0' up to 15 bytes.
+template <bool nullify_tail = false>
+inline void SmallMemmove(char* dst, const char* src, size_t n) {
+  if (n >= 8) {
+    assert(n <= 15);
+    uint64_t buf1;
+    uint64_t buf2;
+    memcpy(&buf1, src, 8);
+    memcpy(&buf2, src + n - 8, 8);
+    if (nullify_tail) {
+      memset(dst + 7, 0, 8);
+    }
+    memcpy(dst, &buf1, 8);
+    memcpy(dst + n - 8, &buf2, 8);
+  } else if (n >= 4) {
+    uint32_t buf1;
+    uint32_t buf2;
+    memcpy(&buf1, src, 4);
+    memcpy(&buf2, src + n - 4, 4);
+    if (nullify_tail) {
+      memset(dst + 4, 0, 4);
+      memset(dst + 7, 0, 8);
+    }
+    memcpy(dst, &buf1, 4);
+    memcpy(dst + n - 4, &buf2, 4);
+  } else {
+    if (n != 0) {
+      dst[0] = src[0];
+      dst[n / 2] = src[n / 2];
+      dst[n - 1] = src[n - 1];
+    }
+    if (nullify_tail) {
+      memset(dst + 7, 0, 8);
+      memset(dst + n, 0, 8);
+    }
+  }
+}
+
 // 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 {
@@ -225,7 +270,11 @@
       : 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.
+  // that is the smallest supported flat node size. Some code optimizations rely
+  // on the specific layout of these fields. Notably: the non-trivial field
+  // `refcount` being preceded by `length`, and being tailed by POD data
+  // members only.
+  // # LINT.IfChange
   size_t length;
   RefcountAndFlags refcount;
   // If tag < FLAT, it represents CordRepKind and indicates the type of node.
@@ -241,6 +290,7 @@
   // 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];
+  // # LINT.ThenChange(cord_rep_btree.h:copy_raw)
 
   // Returns true if this instance's tag matches the requested type.
   constexpr bool IsRing() const { return tag == RING; }
@@ -423,25 +473,25 @@
   return pos < data.size() ? data[pos] : '\0';
 }
 
-// We store cordz_info as 64 bit pointer value in big endian format. This
-// guarantees that the least significant byte of cordz_info matches the last
-// byte of the inline data representation in as_chars_, which holds the inlined
+// We store cordz_info as 64 bit pointer value in little endian format. This
+// guarantees that the least significant byte of cordz_info matches the first
+// byte of the inline data representation in `data`, which holds the inlined
 // size or the 'is_tree' bit.
 using cordz_info_t = int64_t;
 
 // Assert that the `cordz_info` pointer value perfectly overlaps the last half
-// of `as_chars_` and can hold a pointer value.
+// of `data` and can hold a pointer value.
 static_assert(sizeof(cordz_info_t) * 2 == kMaxInline + 1, "");
 static_assert(sizeof(cordz_info_t) >= sizeof(intptr_t), "");
 
-// BigEndianByte() creates a big endian representation of 'value', i.e.: a big
-// endian value where the last byte in the host's representation holds 'value`,
-// with all other bytes being 0.
-static constexpr cordz_info_t BigEndianByte(unsigned char value) {
+// LittleEndianByte() creates a little endian representation of 'value', i.e.:
+// a little endian value where the first byte in the host's representation
+// holds 'value`, with all other bytes being 0.
+static constexpr cordz_info_t LittleEndianByte(unsigned char value) {
 #if defined(ABSL_IS_BIG_ENDIAN)
-  return value;
-#else
   return static_cast<cordz_info_t>(value) << ((sizeof(cordz_info_t) - 1) * 8);
+#else
+  return value;
 #endif
 }
 
@@ -450,38 +500,80 @@
   // DefaultInitType forces the use of the default initialization constructor.
   enum DefaultInitType { kDefaultInit };
 
-  // kNullCordzInfo holds the big endian representation of intptr_t(1)
+  // kNullCordzInfo holds the little 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);
+  static constexpr cordz_info_t kNullCordzInfo = LittleEndianByte(1);
 
-  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_{
-            GetOrNull(chars, 0),  GetOrNull(chars, 1),
-            GetOrNull(chars, 2),  GetOrNull(chars, 3),
-            GetOrNull(chars, 4),  GetOrNull(chars, 5),
-            GetOrNull(chars, 6),  GetOrNull(chars, 7),
-            GetOrNull(chars, 8),  GetOrNull(chars, 9),
-            GetOrNull(chars, 10), GetOrNull(chars, 11),
-            GetOrNull(chars, 12), GetOrNull(chars, 13),
-            GetOrNull(chars, 14), static_cast<char>((chars.size() << 1))} {}
+  // kTagOffset contains the offset of the control byte / tag. This constant is
+  // intended mostly for debugging purposes: do not remove this constant as it
+  // is actively inspected and used by gdb pretty printing code.
+  static constexpr size_t kTagOffset = 0;
+
+  // Implement `~InlineData()` conditionally: we only need this destructor to
+  // unpoison poisoned instances under *SAN, and it will only compile correctly
+  // if the current compiler supports `absl::is_constant_evaluated()`.
+#ifdef ABSL_INTERNAL_CORD_HAVE_SANITIZER
+  ~InlineData() noexcept { unpoison(); }
+#endif
+
+  constexpr InlineData() noexcept { poison_this(); }
+
+  explicit InlineData(DefaultInitType) noexcept : rep_(kDefaultInit) {
+    poison_this();
+  }
+
+  explicit InlineData(CordRep* rep) noexcept : rep_(rep) {
+    ABSL_ASSERT(rep != nullptr);
+  }
+
+  // Explicit constexpr constructor to create a constexpr InlineData
+  // value. Creates an inlined SSO value if `rep` is null, otherwise
+  // creates a tree instance value.
+  constexpr InlineData(absl::string_view sv, CordRep* rep) noexcept
+      : rep_(rep ? Rep(rep) : Rep(sv)) {
+    poison();
+  }
+
+  constexpr InlineData(const InlineData& rhs) noexcept;
+  InlineData& operator=(const InlineData& rhs) noexcept;
+
+  friend bool operator==(const InlineData& lhs, const InlineData& rhs) {
+#ifdef ABSL_INTERNAL_CORD_HAVE_SANITIZER
+    const Rep l = lhs.rep_.SanitizerSafeCopy();
+    const Rep r = rhs.rep_.SanitizerSafeCopy();
+    return memcmp(&l, &r, sizeof(l)) == 0;
+#else
+    return memcmp(&lhs, &rhs, sizeof(lhs)) == 0;
+#endif
+  }
+  friend bool operator!=(const InlineData& lhs, const InlineData& rhs) {
+    return !operator==(lhs, rhs);
+  }
+
+  // Poisons the unused inlined SSO data if the current instance
+  // is inlined, else un-poisons the entire instance.
+  constexpr void poison();
+
+  // Un-poisons this instance.
+  constexpr void unpoison();
+
+  // Poisons the current instance. This is used on default initialization.
+  constexpr void poison_this();
 
   // Returns true if the current instance is empty.
   // The 'empty value' is an inlined data value of zero length.
-  bool is_empty() const { return tag() == 0; }
+  bool is_empty() const { return rep_.tag() == 0; }
 
   // Returns true if the current instance holds a tree value.
-  bool is_tree() const { return (tag() & 1) != 0; }
+  bool is_tree() const { return (rep_.tag() & 1) != 0; }
 
   // Returns true if the current instance holds a cordz_info value.
   // Requires the current instance to hold a tree value.
   bool is_profiled() const {
     assert(is_tree());
-    return as_tree_.cordz_info != kNullCordzInfo;
+    return rep_.cordz_info() != kNullCordzInfo;
   }
 
   // Returns true if either of the provided instances hold a cordz_info value.
@@ -490,7 +582,7 @@
   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) !=
+    return (data1.rep_.cordz_info() | data2.rep_.cordz_info()) !=
            kNullCordzInfo;
   }
 
@@ -499,8 +591,8 @@
   // Requires the current instance to hold a tree value.
   CordzInfo* cordz_info() const {
     assert(is_tree());
-    intptr_t info = static_cast<intptr_t>(
-        absl::big_endian::ToHost64(static_cast<uint64_t>(as_tree_.cordz_info)));
+    intptr_t info = static_cast<intptr_t>(absl::little_endian::ToHost64(
+        static_cast<uint64_t>(rep_.cordz_info())));
     assert(info & 1);
     return reinterpret_cast<CordzInfo*>(info - 1);
   }
@@ -511,21 +603,21 @@
   void set_cordz_info(CordzInfo* cordz_info) {
     assert(is_tree());
     uintptr_t info = reinterpret_cast<uintptr_t>(cordz_info) | 1;
-    as_tree_.cordz_info =
-        static_cast<cordz_info_t>(absl::big_endian::FromHost64(info));
+    rep_.set_cordz_info(
+        static_cast<cordz_info_t>(absl::little_endian::FromHost64(info)));
   }
 
   // Resets the current cordz_info to null / empty.
   void clear_cordz_info() {
     assert(is_tree());
-    as_tree_.cordz_info = kNullCordzInfo;
+    rep_.set_cordz_info(kNullCordzInfo);
   }
 
   // Returns a read only pointer to the character data inside this instance.
   // Requires the current instance to hold inline data.
   const char* as_chars() const {
     assert(!is_tree());
-    return as_chars_;
+    return rep_.as_chars();
   }
 
   // Returns a mutable pointer to the character data inside this instance.
@@ -543,20 +635,33 @@
   //
   // It's an error to read from the returned pointer without a preceding write
   // if the current instance does not hold inline data, i.e.: is_tree() == true.
-  char* as_chars() { return as_chars_; }
+  char* as_chars() { return rep_.as_chars(); }
 
   // Returns the tree value of this value.
   // Requires the current instance to hold a tree value.
   CordRep* as_tree() const {
     assert(is_tree());
-    return as_tree_.rep;
+    return rep_.tree();
+  }
+
+  void set_inline_data(const char* data, size_t n) {
+    ABSL_ASSERT(n <= kMaxInline);
+    unpoison();
+    rep_.set_tag(static_cast<int8_t>(n << 1));
+    SmallMemmove<true>(rep_.as_chars(), data, n);
+    poison();
+  }
+
+  void copy_max_inline_to(char* dst) const {
+    assert(!is_tree());
+    memcpy(dst, rep_.SanitizerSafeCopy().as_chars(), kMaxInline);
   }
 
   // Initialize this instance to holding the tree value `rep`,
   // initializing the cordz_info to null, i.e.: 'not profiled'.
   void make_tree(CordRep* rep) {
-    as_tree_.rep = rep;
-    as_tree_.cordz_info = kNullCordzInfo;
+    unpoison();
+    rep_.make_tree(rep);
   }
 
   // Set the tree value of this instance to 'rep`.
@@ -564,22 +669,20 @@
   // Does not affect the value of cordz_info.
   void set_tree(CordRep* rep) {
     assert(is_tree());
-    as_tree_.rep = rep;
+    rep_.set_tree(rep);
   }
 
   // Returns the size of the inlined character data inside this instance.
   // Requires the current instance to hold inline data.
-  size_t inline_size() const {
-    assert(!is_tree());
-    return static_cast<size_t>(tag()) >> 1;
-  }
+  size_t inline_size() const { return rep_.inline_size(); }
 
   // Sets the size of the inlined character data inside this instance.
   // Requires `size` to be <= kMaxInline.
   // See the documentation on 'as_chars()' for more information and examples.
   void set_inline_size(size_t size) {
-    ABSL_ASSERT(size <= kMaxInline);
-    tag() = static_cast<char>(size << 1);
+    unpoison();
+    rep_.set_inline_size(size);
+    poison();
   }
 
   // Compares 'this' inlined data  with rhs. The comparison is a straightforward
@@ -589,15 +692,115 @@
   //    0  the InlineData instances are equal
   //    1  'this' InlineData instance larger
   int Compare(const InlineData& rhs) const {
+    return Compare(rep_.SanitizerSafeCopy(), rhs.rep_.SanitizerSafeCopy());
+  }
+
+ private:
+  struct Rep {
+    // See cordz_info_t for forced alignment and size of `cordz_info` details.
+    struct AsTree {
+      explicit constexpr AsTree(absl::cord_internal::CordRep* tree)
+          : rep(tree) {}
+      cordz_info_t cordz_info = kNullCordzInfo;
+      absl::cord_internal::CordRep* rep;
+    };
+
+    explicit Rep(DefaultInitType) {}
+    constexpr Rep() : data{0} {}
+    constexpr Rep(const Rep&) = default;
+    constexpr Rep& operator=(const Rep&) = default;
+
+    explicit constexpr Rep(CordRep* rep) : as_tree(rep) {}
+
+    explicit constexpr Rep(absl::string_view chars)
+        : data{static_cast<char>((chars.size() << 1)),
+               GetOrNull(chars, 0),
+               GetOrNull(chars, 1),
+               GetOrNull(chars, 2),
+               GetOrNull(chars, 3),
+               GetOrNull(chars, 4),
+               GetOrNull(chars, 5),
+               GetOrNull(chars, 6),
+               GetOrNull(chars, 7),
+               GetOrNull(chars, 8),
+               GetOrNull(chars, 9),
+               GetOrNull(chars, 10),
+               GetOrNull(chars, 11),
+               GetOrNull(chars, 12),
+               GetOrNull(chars, 13),
+               GetOrNull(chars, 14)} {}
+
+    // Disable sanitizer as we must always be able to read `tag`.
+    ABSL_CORD_INTERNAL_NO_SANITIZE
+    int8_t tag() const { return reinterpret_cast<const int8_t*>(this)[0]; }
+    void set_tag(int8_t rhs) { reinterpret_cast<int8_t*>(this)[0] = rhs; }
+
+    char* as_chars() { return data + 1; }
+    const char* as_chars() const { return data + 1; }
+
+    bool is_tree() const { return (tag() & 1) != 0; }
+
+    size_t inline_size() const {
+      ABSL_ASSERT(!is_tree());
+      return static_cast<size_t>(tag()) >> 1;
+    }
+
+    void set_inline_size(size_t size) {
+      ABSL_ASSERT(size <= kMaxInline);
+      set_tag(static_cast<int8_t>(size << 1));
+    }
+
+    CordRep* tree() const { return as_tree.rep; }
+    void set_tree(CordRep* rhs) { as_tree.rep = rhs; }
+
+    cordz_info_t cordz_info() const { return as_tree.cordz_info; }
+    void set_cordz_info(cordz_info_t rhs) { as_tree.cordz_info = rhs; }
+
+    void make_tree(CordRep* tree) {
+      as_tree.rep = tree;
+      as_tree.cordz_info = kNullCordzInfo;
+    }
+
+#ifdef ABSL_INTERNAL_CORD_HAVE_SANITIZER
+    constexpr Rep SanitizerSafeCopy() const {
+      if (!absl::is_constant_evaluated()) {
+        Rep res;
+        if (is_tree()) {
+          res = *this;
+        } else {
+          res.set_tag(tag());
+          memcpy(res.as_chars(), as_chars(), inline_size());
+        }
+        return res;
+      } else {
+        return *this;
+      }
+    }
+#else
+    constexpr const Rep& SanitizerSafeCopy() const { return *this; }
+#endif
+
+    // If the data has length <= kMaxInline, we store it in `data`, and
+    // store the size in the first char of `data` shifted left + 1.
+    // Else we store it in a tree and store a pointer to that tree in
+    // `as_tree.rep` with a tagged pointer to make `tag() & 1` non zero.
+    union {
+      char data[kMaxInline + 1];
+      AsTree as_tree;
+    };
+  };
+
+  // Private implementation of `Compare()`
+  static inline int Compare(const Rep& lhs, const Rep& rhs) {
     uint64_t x, y;
-    memcpy(&x, as_chars(), sizeof(x));
+    memcpy(&x, lhs.as_chars(), sizeof(x));
     memcpy(&y, rhs.as_chars(), sizeof(y));
     if (x == y) {
-      memcpy(&x, as_chars() + 7, sizeof(x));
+      memcpy(&x, lhs.as_chars() + 7, sizeof(x));
       memcpy(&y, rhs.as_chars() + 7, sizeof(y));
       if (x == y) {
-        if (inline_size() == rhs.inline_size()) return 0;
-        return inline_size() < rhs.inline_size() ? -1 : 1;
+        if (lhs.inline_size() == rhs.inline_size()) return 0;
+        return lhs.inline_size() < rhs.inline_size() ? -1 : 1;
       }
     }
     x = absl::big_endian::FromHost64(x);
@@ -605,36 +808,63 @@
     return x < y ? -1 : 1;
   }
 
- private:
-  // See cordz_info_t for forced alignment and size of `cordz_info` details.
-  struct AsTree {
-    explicit constexpr AsTree(absl::cord_internal::CordRep* tree)
-        : rep(tree), cordz_info(kNullCordzInfo) {}
-    // This union uses up extra space so that whether rep is 32 or 64 bits,
-    // cordz_info will still start at the eighth byte, and the last
-    // byte of cordz_info will still be the last byte of InlineData.
-    union {
-      absl::cord_internal::CordRep* rep;
-      cordz_info_t unused_aligner;
-    };
-    cordz_info_t cordz_info;
-  };
-
-  char& tag() { return reinterpret_cast<char*>(this)[kMaxInline]; }
-  char tag() const { return reinterpret_cast<const char*>(this)[kMaxInline]; }
-
-  // If the data has length <= kMaxInline, we store it in `as_chars_`, and
-  // 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 {
-    char as_chars_[kMaxInline + 1];
-    AsTree as_tree_;
-  };
+  Rep rep_;
 };
 
 static_assert(sizeof(InlineData) == kMaxInline + 1, "");
 
+#ifdef ABSL_INTERNAL_CORD_HAVE_SANITIZER
+
+constexpr InlineData::InlineData(const InlineData& rhs) noexcept
+    : rep_(rhs.rep_.SanitizerSafeCopy()) {
+  poison();
+}
+
+inline InlineData& InlineData::operator=(const InlineData& rhs) noexcept {
+  unpoison();
+  rep_ = rhs.rep_.SanitizerSafeCopy();
+  poison();
+  return *this;
+}
+
+constexpr void InlineData::poison_this() {
+  if (!absl::is_constant_evaluated()) {
+    container_internal::SanitizerPoisonObject(this);
+  }
+}
+
+constexpr void InlineData::unpoison() {
+  if (!absl::is_constant_evaluated()) {
+    container_internal::SanitizerUnpoisonObject(this);
+  }
+}
+
+constexpr void InlineData::poison() {
+  if (!absl::is_constant_evaluated()) {
+    if (is_tree()) {
+      container_internal::SanitizerUnpoisonObject(this);
+    } else if (const size_t size = inline_size()) {
+      if (size < kMaxInline) {
+        const char* end = rep_.as_chars() + size;
+        container_internal::SanitizerPoisonMemoryRegion(end, kMaxInline - size);
+      }
+    } else {
+      container_internal::SanitizerPoisonObject(this);
+    }
+  }
+}
+
+#else  // ABSL_INTERNAL_CORD_HAVE_SANITIZER
+
+constexpr InlineData::InlineData(const InlineData&) noexcept = default;
+inline InlineData& InlineData::operator=(const InlineData&) noexcept = default;
+
+constexpr void InlineData::poison_this() {}
+constexpr void InlineData::unpoison() {}
+constexpr void InlineData::poison() {}
+
+#endif  // ABSL_INTERNAL_CORD_HAVE_SANITIZER
+
 inline CordRepSubstring* CordRep::substring() {
   assert(IsSubstring());
   return static_cast<CordRepSubstring*>(this);
diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc
index 7ce3612..05bd0e2 100644
--- a/absl/strings/internal/cord_rep_btree.cc
+++ b/absl/strings/internal/cord_rep_btree.cc
@@ -14,6 +14,7 @@
 
 #include "absl/strings/internal/cord_rep_btree.h"
 
+#include <atomic>
 #include <cassert>
 #include <cstdint>
 #include <iostream>
@@ -23,6 +24,7 @@
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/internal/raw_logging.h"
+#include "absl/base/optimization.h"
 #include "absl/strings/internal/cord_data_edge.h"
 #include "absl/strings/internal/cord_internal.h"
 #include "absl/strings/internal/cord_rep_consume.h"
@@ -48,9 +50,7 @@
 constexpr auto kFront = CordRepBtree::kFront;
 constexpr auto kBack = CordRepBtree::kBack;
 
-inline bool exhaustive_validation() {
-  return cord_btree_exhaustive_validation.load(std::memory_order_relaxed);
-}
+ABSL_CONST_INIT std::atomic<bool> cord_btree_exhaustive_validation(false);
 
 // Implementation of the various 'Dump' functions.
 // Prints the entire tree structure or 'rep'. External callers should
@@ -286,7 +286,7 @@
       case CordRepBtree::kSelf:
         return result.tree;
     }
-    ABSL_INTERNAL_UNREACHABLE;
+    ABSL_UNREACHABLE();
     return result.tree;
   }
 
@@ -361,6 +361,15 @@
 
 }  // namespace
 
+void SetCordBtreeExhaustiveValidation(bool do_exaustive_validation) {
+  cord_btree_exhaustive_validation.store(do_exaustive_validation,
+                                         std::memory_order_relaxed);
+}
+
+bool IsCordBtreeExhaustiveValidationEnabled() {
+  return cord_btree_exhaustive_validation.load(std::memory_order_relaxed);
+}
+
 void CordRepBtree::Dump(const CordRep* rep, absl::string_view label,
                         bool include_contents, std::ostream& stream) {
   stream << "===================================\n";
@@ -449,7 +458,8 @@
     child_length += edge->length;
   }
   NODE_CHECK_EQ(child_length, tree->length);
-  if ((!shallow || exhaustive_validation()) && tree->height() > 0) {
+  if ((!shallow || IsCordBtreeExhaustiveValidationEnabled()) &&
+      tree->height() > 0) {
     for (CordRep* edge : tree->Edges()) {
       if (!IsValid(edge->btree(), shallow)) return false;
     }
@@ -502,7 +512,7 @@
     // 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};
+    result = {CopyRaw(length), kCopied};
     constexpr int shift = edge_type == kFront ? 1 : 0;
     for (CordRep* r : Edges(begin() + shift, back() + shift)) {
       CordRep::Ref(r);
diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h
index eed5609..be94b62 100644
--- a/absl/strings/internal/cord_rep_btree.h
+++ b/absl/strings/internal/cord_rep_btree.h
@@ -32,6 +32,14 @@
 ABSL_NAMESPACE_BEGIN
 namespace cord_internal {
 
+// `SetCordBtreeExhaustiveValidation()` 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).
+void SetCordBtreeExhaustiveValidation(bool do_exaustive_validation);
+bool IsCordBtreeExhaustiveValidationEnabled();
+
 class CordRepBtreeNavigator;
 
 // CordRepBtree is as the name implies a btree implementation of a Cordrep tree.
@@ -446,9 +454,9 @@
   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 raw copy of this Btree node with the specified length, copying
+  // all properties, but without adding any references to existing edges.
+  CordRepBtree* CopyRaw(size_t new_length) const;
 
   // Creates a full copy of this Btree node, adding a reference on all edges.
   CordRepBtree* Copy() const;
@@ -666,15 +674,28 @@
   }
 }
 
-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;
+inline CordRepBtree* CordRepBtree::CopyRaw(size_t new_length) const {
+  CordRepBtree* tree = new CordRepBtree;
+
+  // `length` and `refcount` are the first members of `CordRepBtree`.
+  // We initialize `length` using the given length, have `refcount` be set to
+  // ref = 1 through its default constructor, and copy all data beyond
+  // 'refcount' which starts with `tag` using a single memcpy: all contents
+  // except `refcount` is trivially copyable, and the compiler does not
+  // efficiently coalesce member-wise copy of these members.
+  // See https://gcc.godbolt.org/z/qY8zsca6z
+  // # LINT.IfChange(copy_raw)
+  tree->length = new_length;
+  uint8_t* dst = &tree->tag;
+  const uint8_t* src = &tag;
+  const ptrdiff_t offset = src - reinterpret_cast<const uint8_t*>(this);
+  memcpy(dst, src, sizeof(CordRepBtree) - static_cast<size_t>(offset));
   return tree;
+  // # LINT.ThenChange()
 }
 
 inline CordRepBtree* CordRepBtree::Copy() const {
-  CordRepBtree* tree = CopyRaw();
+  CordRepBtree* tree = CopyRaw(length);
   for (CordRep* rep : Edges()) CordRep::Ref(rep);
   return tree;
 }
@@ -683,8 +704,7 @@
                                                  size_t new_length) const {
   assert(begin >= this->begin());
   assert(begin <= this->end());
-  CordRepBtree* tree = CopyRaw();
-  tree->length = new_length;
+  CordRepBtree* tree = CopyRaw(new_length);
   tree->set_begin(begin);
   for (CordRep* edge : tree->Edges()) CordRep::Ref(edge);
   return tree;
@@ -694,8 +714,7 @@
                                                size_t new_length) const {
   assert(end <= capacity());
   assert(end >= this->begin());
-  CordRepBtree* tree = CopyRaw();
-  tree->length = new_length;
+  CordRepBtree* tree = CopyRaw(new_length);
   tree->set_end(end);
   for (CordRep* edge : tree->Edges()) CordRep::Ref(edge);
   return tree;
diff --git a/absl/strings/internal/cord_rep_btree_test.cc b/absl/strings/internal/cord_rep_btree_test.cc
index 9d6ce48..840acf9 100644
--- a/absl/strings/internal/cord_rep_btree_test.cc
+++ b/absl/strings/internal/cord_rep_btree_test.cc
@@ -507,7 +507,7 @@
   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
+    // iterations 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());
@@ -568,7 +568,7 @@
   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
+    // iterations 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());
@@ -1355,9 +1355,9 @@
 
 TEST(CordRepBtreeTest, CheckAssertValidShallowVsDeep) {
   // Restore exhaustive validation on any exit.
-  const bool exhaustive_validation = cord_btree_exhaustive_validation.load();
+  const bool exhaustive_validation = IsCordBtreeExhaustiveValidationEnabled();
   auto cleanup = absl::MakeCleanup([exhaustive_validation] {
-    cord_btree_exhaustive_validation.store(exhaustive_validation);
+    SetCordBtreeExhaustiveValidation(exhaustive_validation);
   });
 
   // Create a tree of at least 2 levels, and mess with the original flat, which
@@ -1372,7 +1372,7 @@
   }
   flat->length = 100;
 
-  cord_btree_exhaustive_validation.store(false);
+  SetCordBtreeExhaustiveValidation(false);
   EXPECT_FALSE(CordRepBtree::IsValid(tree));
   EXPECT_TRUE(CordRepBtree::IsValid(tree, true));
   EXPECT_FALSE(CordRepBtree::IsValid(tree, false));
@@ -1382,7 +1382,7 @@
   EXPECT_DEBUG_DEATH(CordRepBtree::AssertValid(tree, false), ".*");
 #endif
 
-  cord_btree_exhaustive_validation.store(true);
+  SetCordBtreeExhaustiveValidation(true);
   EXPECT_FALSE(CordRepBtree::IsValid(tree));
   EXPECT_FALSE(CordRepBtree::IsValid(tree, true));
   EXPECT_FALSE(CordRepBtree::IsValid(tree, false));
diff --git a/absl/strings/internal/cord_rep_consume.cc b/absl/strings/internal/cord_rep_consume.cc
index 20a5579..db7d4fe 100644
--- a/absl/strings/internal/cord_rep_consume.cc
+++ b/absl/strings/internal/cord_rep_consume.cc
@@ -42,7 +42,8 @@
 
 }  // namespace
 
-void Consume(CordRep* rep, ConsumeFn consume_fn) {
+void Consume(CordRep* rep,
+             FunctionRef<void(CordRep*, size_t, size_t)> consume_fn) {
   size_t offset = 0;
   size_t length = rep->length;
 
@@ -53,8 +54,9 @@
   consume_fn(rep, offset, length);
 }
 
-void ReverseConsume(CordRep* rep, ConsumeFn consume_fn) {
-  return Consume(rep, std::move(consume_fn));
+void ReverseConsume(CordRep* rep,
+                    FunctionRef<void(CordRep*, size_t, size_t)> consume_fn) {
+  return Consume(rep, consume_fn);
 }
 
 }  // namespace cord_internal
diff --git a/absl/strings/internal/cord_rep_consume.h b/absl/strings/internal/cord_rep_consume.h
index d46fca2..bece187 100644
--- a/absl/strings/internal/cord_rep_consume.h
+++ b/absl/strings/internal/cord_rep_consume.h
@@ -24,11 +24,6 @@
 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.
@@ -40,8 +35,10 @@
 // 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);
+void Consume(CordRep* rep,
+             FunctionRef<void(CordRep*, size_t, size_t)> consume_fn);
+void ReverseConsume(CordRep* rep,
+                    FunctionRef<void(CordRep*, size_t, size_t)> consume_fn);
 
 }  // namespace cord_internal
 ABSL_NAMESPACE_END
diff --git a/absl/strings/internal/cord_rep_crc.cc b/absl/strings/internal/cord_rep_crc.cc
index ee14035..dbe54cc 100644
--- a/absl/strings/internal/cord_rep_crc.cc
+++ b/absl/strings/internal/cord_rep_crc.cc
@@ -16,6 +16,7 @@
 
 #include <cassert>
 #include <cstdint>
+#include <utility>
 
 #include "absl/base/config.h"
 #include "absl/strings/internal/cord_internal.h"
@@ -24,11 +25,10 @@
 ABSL_NAMESPACE_BEGIN
 namespace cord_internal {
 
-CordRepCrc* CordRepCrc::New(CordRep* child, uint32_t crc) {
-  assert(child != nullptr);
-  if (child->IsCrc()) {
+CordRepCrc* CordRepCrc::New(CordRep* child, crc_internal::CrcCordState state) {
+  if (child != nullptr && child->IsCrc()) {
     if (child->refcount.IsOne()) {
-      child->crc()->crc = crc;
+      child->crc()->crc_cord_state = std::move(state);
       return child->crc();
     }
     CordRep* old = child;
@@ -37,15 +37,17 @@
     CordRep::Unref(old);
   }
   auto* new_cordrep = new CordRepCrc;
-  new_cordrep->length = child->length;
+  new_cordrep->length = child != nullptr ? child->length : 0;
   new_cordrep->tag = cord_internal::CRC;
   new_cordrep->child = child;
-  new_cordrep->crc = crc;
+  new_cordrep->crc_cord_state = std::move(state);
   return new_cordrep;
 }
 
 void CordRepCrc::Destroy(CordRepCrc* node) {
-  CordRep::Unref(node->child);
+  if (node->child != nullptr) {
+    CordRep::Unref(node->child);
+  }
   delete node;
 }
 
diff --git a/absl/strings/internal/cord_rep_crc.h b/absl/strings/internal/cord_rep_crc.h
index 5294b0d..379d7a6 100644
--- a/absl/strings/internal/cord_rep_crc.h
+++ b/absl/strings/internal/cord_rep_crc.h
@@ -20,6 +20,7 @@
 
 #include "absl/base/config.h"
 #include "absl/base/optimization.h"
+#include "absl/crc/internal/crc_cord_state.h"
 #include "absl/strings/internal/cord_internal.h"
 
 namespace absl {
@@ -34,14 +35,14 @@
 // the contained checksum is the user's responsibility.
 struct CordRepCrc : public CordRep {
   CordRep* child;
-  uint32_t crc;
+  absl::crc_internal::CrcCordState crc_cord_state;
 
   // Consumes `child` and returns a CordRepCrc prefixed tree containing `child`.
   // If the specified `child` is itself a CordRepCrc node, then this method
-  // either replaces the existing node, or directly updates the crc value in it
+  // either replaces the existing node, or directly updates the crc state in it
   // depending on the node being shared or not, i.e.: refcount.IsOne().
-  // `child` must not be null. Never returns null.
-  static CordRepCrc* New(CordRep* child, uint32_t crc);
+  // `child` must only be null if the Cord is empty. Never returns null.
+  static CordRepCrc* New(CordRep* child, crc_internal::CrcCordState state);
 
   // Destroys (deletes) the provided node. `node` must not be null.
   static void Destroy(CordRepCrc* node);
diff --git a/absl/strings/internal/cord_rep_crc_test.cc b/absl/strings/internal/cord_rep_crc_test.cc
index d73ea7b..3d27c33 100644
--- a/absl/strings/internal/cord_rep_crc_test.cc
+++ b/absl/strings/internal/cord_rep_crc_test.cc
@@ -17,6 +17,7 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/base/config.h"
+#include "absl/crc/internal/crc_cord_state.h"
 #include "absl/strings/internal/cord_internal.h"
 #include "absl/strings/internal/cord_rep_test_util.h"
 
@@ -27,47 +28,51 @@
 
 using ::absl::cordrep_testing::MakeFlat;
 using ::testing::Eq;
+using ::testing::IsNull;
 using ::testing::Ne;
 
 #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
 
-TEST(CordRepCrc, NewWithNullPtr) {
-  EXPECT_DEATH(CordRepCrc::New(nullptr, 0), "");
-}
-
 TEST(CordRepCrc, RemoveCrcWithNullptr) {
   EXPECT_DEATH(RemoveCrcNode(nullptr), "");
 }
 
 #endif  // !NDEBUG && GTEST_HAS_DEATH_TEST
 
+absl::crc_internal::CrcCordState MakeCrcCordState(uint32_t crc) {
+  crc_internal::CrcCordState state;
+  state.mutable_rep()->prefix_crc.push_back(
+      crc_internal::CrcCordState::PrefixCrc(42, crc32c_t{crc}));
+  return state;
+}
+
 TEST(CordRepCrc, NewDestroy) {
   CordRep* rep = cordrep_testing::MakeFlat("Hello world");
-  CordRepCrc* crc = CordRepCrc::New(rep, 12345);
+  CordRepCrc* crc = CordRepCrc::New(rep, MakeCrcCordState(12345));
   EXPECT_TRUE(crc->refcount.IsOne());
   EXPECT_THAT(crc->child, Eq(rep));
-  EXPECT_THAT(crc->crc, Eq(12345u));
+  EXPECT_THAT(crc->crc_cord_state.Checksum(), Eq(crc32c_t{12345u}));
   EXPECT_TRUE(rep->refcount.IsOne());
   CordRepCrc::Destroy(crc);
 }
 
 TEST(CordRepCrc, NewExistingCrcNotShared) {
   CordRep* rep = cordrep_testing::MakeFlat("Hello world");
-  CordRepCrc* crc = CordRepCrc::New(rep, 12345);
-  CordRepCrc* new_crc = CordRepCrc::New(crc, 54321);
+  CordRepCrc* crc = CordRepCrc::New(rep, MakeCrcCordState(12345));
+  CordRepCrc* new_crc = CordRepCrc::New(crc, MakeCrcCordState(54321));
   EXPECT_THAT(new_crc, Eq(crc));
   EXPECT_TRUE(new_crc->refcount.IsOne());
   EXPECT_THAT(new_crc->child, Eq(rep));
-  EXPECT_THAT(new_crc->crc, Eq(54321u));
+  EXPECT_THAT(new_crc->crc_cord_state.Checksum(), Eq(crc32c_t{54321u}));
   EXPECT_TRUE(rep->refcount.IsOne());
   CordRepCrc::Destroy(new_crc);
 }
 
 TEST(CordRepCrc, NewExistingCrcShared) {
   CordRep* rep = cordrep_testing::MakeFlat("Hello world");
-  CordRepCrc* crc = CordRepCrc::New(rep, 12345);
+  CordRepCrc* crc = CordRepCrc::New(rep, MakeCrcCordState(12345));
   CordRep::Ref(crc);
-  CordRepCrc* new_crc = CordRepCrc::New(crc, 54321);
+  CordRepCrc* new_crc = CordRepCrc::New(crc, MakeCrcCordState(54321));
 
   EXPECT_THAT(new_crc, Ne(crc));
   EXPECT_TRUE(new_crc->refcount.IsOne());
@@ -75,13 +80,23 @@
   EXPECT_FALSE(rep->refcount.IsOne());
   EXPECT_THAT(crc->child, Eq(rep));
   EXPECT_THAT(new_crc->child, Eq(rep));
-  EXPECT_THAT(crc->crc, Eq(12345u));
-  EXPECT_THAT(new_crc->crc, Eq(54321u));
+  EXPECT_THAT(crc->crc_cord_state.Checksum(), Eq(crc32c_t{12345u}));
+  EXPECT_THAT(new_crc->crc_cord_state.Checksum(), Eq(crc32c_t{54321u}));
 
   CordRep::Unref(crc);
   CordRep::Unref(new_crc);
 }
 
+TEST(CordRepCrc, NewEmpty) {
+  CordRepCrc* crc = CordRepCrc::New(nullptr, MakeCrcCordState(12345));
+  EXPECT_TRUE(crc->refcount.IsOne());
+  EXPECT_THAT(crc->child, IsNull());
+  EXPECT_THAT(crc->length, Eq(0u));
+  EXPECT_THAT(crc->crc_cord_state.Checksum(), Eq(crc32c_t{12345u}));
+  EXPECT_TRUE(crc->refcount.IsOne());
+  CordRepCrc::Destroy(crc);
+}
+
 TEST(CordRepCrc, RemoveCrcNotCrc) {
   CordRep* rep = cordrep_testing::MakeFlat("Hello world");
   CordRep* nocrc = RemoveCrcNode(rep);
@@ -91,7 +106,7 @@
 
 TEST(CordRepCrc, RemoveCrcNotShared) {
   CordRep* rep = cordrep_testing::MakeFlat("Hello world");
-  CordRepCrc* crc = CordRepCrc::New(rep, 12345);
+  CordRepCrc* crc = CordRepCrc::New(rep, MakeCrcCordState(12345));
   CordRep* nocrc = RemoveCrcNode(crc);
   EXPECT_THAT(nocrc, Eq(rep));
   EXPECT_TRUE(rep->refcount.IsOne());
@@ -100,7 +115,7 @@
 
 TEST(CordRepCrc, RemoveCrcShared) {
   CordRep* rep = cordrep_testing::MakeFlat("Hello world");
-  CordRepCrc* crc = CordRepCrc::New(rep, 12345);
+  CordRepCrc* crc = CordRepCrc::New(rep, MakeCrcCordState(12345));
   CordRep::Ref(crc);
   CordRep* nocrc = RemoveCrcNode(crc);
   EXPECT_THAT(nocrc, Eq(rep));
diff --git a/absl/strings/internal/cord_rep_ring.h b/absl/strings/internal/cord_rep_ring.h
index 2000e21..79a2fdb 100644
--- a/absl/strings/internal/cord_rep_ring.h
+++ b/absl/strings/internal/cord_rep_ring.h
@@ -430,7 +430,7 @@
   // capacity to satisfy `extra` extra nodes, and unref the old `rep` instance.
   //
   // If a new CordRepRing can not be allocated, or the new capacity would exceed
-  // the maxmimum capacity, then the input is consumed only, and an exception is
+  // the maximum capacity, then the input is consumed only, and an exception is
   // thrown.
   static CordRepRing* Mutable(CordRepRing* rep, size_t extra);
 
@@ -472,7 +472,7 @@
   // Increases the data offset for entry `index` by `n`.
   void AddDataOffset(index_type index, size_t n);
 
-  // Descreases the length for entry `index` by `n`.
+  // Decreases the length for entry `index` by `n`.
   void SubLength(index_type index, size_t n);
 
   index_type head_;
diff --git a/absl/strings/internal/cordz_functions.h b/absl/strings/internal/cordz_functions.h
index 93f46ec..ed108bf 100644
--- a/absl/strings/internal/cordz_functions.h
+++ b/absl/strings/internal/cordz_functions.h
@@ -32,18 +32,10 @@
 // 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__)
+// Cordz is only enabled on Linux with thread_local support.
+#if defined(ABSL_INTERNAL_CORDZ_ENABLED)
+#error ABSL_INTERNAL_CORDZ_ENABLED cannot be set directly
+#elif defined(__linux__) && defined(ABSL_HAVE_THREAD_LOCAL)
 #define ABSL_INTERNAL_CORDZ_ENABLED 1
 #endif
 
diff --git a/absl/strings/internal/cordz_functions_test.cc b/absl/strings/internal/cordz_functions_test.cc
index 350623c..b70a685 100644
--- a/absl/strings/internal/cordz_functions_test.cc
+++ b/absl/strings/internal/cordz_functions_test.cc
@@ -38,7 +38,7 @@
 }
 
 // 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
+// should_profile will return false when cordz is disabled, so we might want to
 // avoid those tests.
 #ifdef ABSL_INTERNAL_CORDZ_ENABLED
 
diff --git a/absl/strings/internal/cordz_handle.cc b/absl/strings/internal/cordz_handle.cc
index a73fefe..a7061db 100644
--- a/absl/strings/internal/cordz_handle.cc
+++ b/absl/strings/internal/cordz_handle.cc
@@ -16,34 +16,60 @@
 #include <atomic>
 
 #include "absl/base/internal/raw_logging.h"  // For ABSL_RAW_CHECK
-#include "absl/base/internal/spinlock.h"
+#include "absl/synchronization/mutex.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace cord_internal {
 
-using ::absl::base_internal::SpinLockHolder;
+namespace {
 
-ABSL_CONST_INIT CordzHandle::Queue CordzHandle::global_queue_(absl::kConstInit);
+struct Queue {
+  Queue() = default;
+
+  absl::Mutex 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;
+  }
+};
+
+static Queue* GlobalQueue() {
+  static Queue* global_queue = new Queue;
+  return global_queue;
+}
+
+}  // namespace
 
 CordzHandle::CordzHandle(bool is_snapshot) : is_snapshot_(is_snapshot) {
+  Queue* global_queue = GlobalQueue();
   if (is_snapshot) {
-    SpinLockHolder lock(&queue_->mutex);
-    CordzHandle* dq_tail = queue_->dq_tail.load(std::memory_order_acquire);
+    MutexLock lock(&global_queue->mutex);
+    CordzHandle* dq_tail =
+        global_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);
+    global_queue->dq_tail.store(this, std::memory_order_release);
   }
 }
 
 CordzHandle::~CordzHandle() {
-  ODRCheck();
+  Queue* global_queue = GlobalQueue();
   if (is_snapshot_) {
     std::vector<CordzHandle*> to_delete;
     {
-      SpinLockHolder lock(&queue_->mutex);
+      MutexLock lock(&global_queue->mutex);
       CordzHandle* next = dq_next_;
       if (dq_prev_ == nullptr) {
         // We were head of the queue, delete every CordzHandle until we reach
@@ -59,7 +85,7 @@
       if (next) {
         next->dq_prev_ = dq_prev_;
       } else {
-        queue_->dq_tail.store(dq_prev_, std::memory_order_release);
+        global_queue->dq_tail.store(dq_prev_, std::memory_order_release);
       }
     }
     for (CordzHandle* handle : to_delete) {
@@ -69,16 +95,15 @@
 }
 
 bool CordzHandle::SafeToDelete() const {
-  return is_snapshot_ || queue_->IsEmpty();
+  return is_snapshot_ || GlobalQueue()->IsEmpty();
 }
 
 void CordzHandle::Delete(CordzHandle* handle) {
   assert(handle);
   if (handle) {
-    handle->ODRCheck();
-    Queue* const queue = handle->queue_;
+    Queue* const queue = GlobalQueue();
     if (!handle->SafeToDelete()) {
-      SpinLockHolder lock(&queue->mutex);
+      MutexLock lock(&queue->mutex);
       CordzHandle* dq_tail = queue->dq_tail.load(std::memory_order_acquire);
       if (dq_tail != nullptr) {
         handle->dq_prev_ = dq_tail;
@@ -93,8 +118,9 @@
 
 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);
+  Queue* global_queue = GlobalQueue();
+  MutexLock 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);
   }
@@ -103,13 +129,13 @@
 
 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_) {
+  Queue* global_queue = GlobalQueue();
+  MutexLock lock(&global_queue->mutex);
+  for (const CordzHandle* p = global_queue->dq_tail; p; p = p->dq_prev_) {
     if (p == handle) return !snapshot_found;
     if (p == this) snapshot_found = true;
   }
@@ -119,13 +145,13 @@
 
 std::vector<const CordzHandle*>
 CordzHandle::DiagnosticsGetSafeToInspectDeletedHandles() {
-  ODRCheck();
   std::vector<const CordzHandle*> handles;
   if (!is_snapshot()) {
     return handles;
   }
 
-  SpinLockHolder lock(&queue_->mutex);
+  Queue* global_queue = GlobalQueue();
+  MutexLock lock(&global_queue->mutex);
   for (const CordzHandle* p = dq_next_; p != nullptr; p = p->dq_next_) {
     if (!p->is_snapshot()) {
       handles.push_back(p);
diff --git a/absl/strings/internal/cordz_handle.h b/absl/strings/internal/cordz_handle.h
index 3c800b4..08e3f0d 100644
--- a/absl/strings/internal/cordz_handle.h
+++ b/absl/strings/internal/cordz_handle.h
@@ -20,8 +20,6 @@
 
 #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
@@ -34,7 +32,7 @@
 // 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 {
+class ABSL_DLL CordzHandle {
  public:
   CordzHandle() : CordzHandle(false) {}
 
@@ -79,37 +77,6 @@
   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.
diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc
index 530f33b..515dfaf 100644
--- a/absl/strings/internal/cordz_info.cc
+++ b/absl/strings/internal/cordz_info.cc
@@ -26,6 +26,7 @@
 #include "absl/strings/internal/cordz_statistics.h"
 #include "absl/strings/internal/cordz_update_tracker.h"
 #include "absl/synchronization/mutex.h"
+#include "absl/time/clock.h"
 #include "absl/types/span.h"
 
 namespace absl {
@@ -53,7 +54,7 @@
 // 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
+// subtract 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:
diff --git a/absl/strings/internal/cordz_info_statistics_test.cc b/absl/strings/internal/cordz_info_statistics_test.cc
index 6d6feb5..53d2f2e 100644
--- a/absl/strings/internal/cordz_info_statistics_test.cc
+++ b/absl/strings/internal/cordz_info_statistics_test.cc
@@ -19,6 +19,7 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/base/config.h"
+#include "absl/crc/internal/crc_cord_state.h"
 #include "absl/strings/cord.h"
 #include "absl/strings/internal/cord_internal.h"
 #include "absl/strings/internal/cord_rep_btree.h"
@@ -451,7 +452,8 @@
 TEST(CordzInfoStatisticsTest, Crc) {
   RefHelper ref;
   auto* left = Flat(1000);
-  auto* crc = ref.NeedsUnref(CordRepCrc::New(left, 12345));
+  auto* crc =
+      ref.NeedsUnref(CordRepCrc::New(left, crc_internal::CrcCordState()));
 
   CordzStatistics expected;
   expected.size = left->length;
diff --git a/absl/strings/internal/cordz_sample_token.h b/absl/strings/internal/cordz_sample_token.h
index b58022c..2a86bc3 100644
--- a/absl/strings/internal/cordz_sample_token.h
+++ b/absl/strings/internal/cordz_sample_token.h
@@ -33,11 +33,11 @@
 //   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.
+// holding ST1 might have a reference 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
diff --git a/absl/strings/internal/damerau_levenshtein_distance.cc b/absl/strings/internal/damerau_levenshtein_distance.cc
new file mode 100644
index 0000000..a084568
--- /dev/null
+++ b/absl/strings/internal/damerau_levenshtein_distance.cc
@@ -0,0 +1,93 @@
+// Copyright 2022 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/damerau_levenshtein_distance.h"
+
+#include <algorithm>
+#include <array>
+#include <numeric>
+
+#include "absl/strings/string_view.h"
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace strings_internal {
+// Calculate DamerauLevenshtein (adjacent transpositions) distance
+// between two strings,
+// https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance. The
+// algorithm follows the condition that no substring is edited more than once.
+// While this can reduce is larger distance, it's a) a much simpler algorithm
+// and b) more realistic for the case that typographic mistakes should be
+// detected.
+// When the distance is larger than cutoff, or one of the strings has more
+// than MAX_SIZE=100 characters, the code returns min(MAX_SIZE, cutoff) + 1.
+uint8_t CappedDamerauLevenshteinDistance(absl::string_view s1,
+                                         absl::string_view s2, uint8_t cutoff) {
+  const uint8_t MAX_SIZE = 100;
+  const uint8_t _cutoff = std::min(MAX_SIZE, cutoff);
+  const uint8_t cutoff_plus_1 = static_cast<uint8_t>(_cutoff + 1);
+
+  if (s1.size() > s2.size()) std::swap(s1, s2);
+  if (s1.size() + _cutoff < s2.size() || s2.size() > MAX_SIZE)
+    return cutoff_plus_1;
+
+  if (s1.empty())
+    return static_cast<uint8_t>(s2.size());
+
+  // Lower diagonal bound: y = x - lower_diag
+  const uint8_t lower_diag =
+      _cutoff - static_cast<uint8_t>(s2.size() - s1.size());
+  // Upper diagonal bound: y = x + upper_diag
+  const uint8_t upper_diag = _cutoff;
+
+  // d[i][j] is the number of edits required to convert s1[0, i] to s2[0, j]
+  std::array<std::array<uint8_t, MAX_SIZE + 2>, MAX_SIZE + 2> d;
+  std::iota(d[0].begin(), d[0].begin() + upper_diag + 1, 0);
+  d[0][cutoff_plus_1] = cutoff_plus_1;
+  for (size_t i = 1; i <= s1.size(); ++i) {
+    // Deduce begin of relevant window.
+    size_t j_begin = 1;
+    if (i > lower_diag) {
+      j_begin = i - lower_diag;
+      d[i][j_begin - 1] = cutoff_plus_1;
+    } else {
+      d[i][0] = static_cast<uint8_t>(i);
+    }
+
+    // Deduce end of relevant window.
+    size_t j_end = i + upper_diag;
+    if (j_end > s2.size()) {
+      j_end = s2.size();
+    } else {
+      d[i][j_end + 1] = cutoff_plus_1;
+    }
+
+    for (size_t j = j_begin; j <= j_end; ++j) {
+      const uint8_t deletion_distance = d[i - 1][j] + 1;
+      const uint8_t insertion_distance = d[i][j - 1] + 1;
+      const uint8_t mismatched_tail_cost = s1[i - 1] == s2[j - 1] ? 0 : 1;
+      const uint8_t mismatch_distance = d[i - 1][j - 1] + mismatched_tail_cost;
+      uint8_t transposition_distance = _cutoff + 1;
+      if (i > 1 && j > 1 && s1[i - 1] == s2[j - 2] && s1[i - 2] == s2[j - 1])
+        transposition_distance = d[i - 2][j - 2] + 1;
+      d[i][j] = std::min({cutoff_plus_1, deletion_distance, insertion_distance,
+                          mismatch_distance, transposition_distance});
+    }
+  }
+  return d[s1.size()][s2.size()];
+}
+
+}  // namespace strings_internal
+
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/damerau_levenshtein_distance.h b/absl/strings/internal/damerau_levenshtein_distance.h
new file mode 100644
index 0000000..7a4bd64
--- /dev/null
+++ b/absl/strings/internal/damerau_levenshtein_distance.h
@@ -0,0 +1,34 @@
+// Copyright 2022 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_DAMERAU_LEVENSHTEIN_DISTANCE_H_
+#define ABSL_STRINGS_INTERNAL_DAMERAU_LEVENSHTEIN_DISTANCE_H_
+
+#include <cstdint>
+
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace strings_internal {
+// Calculate DamerauLevenshtein distance between two strings.
+// When the distance is larger than cutoff, the code just returns cutoff + 1.
+uint8_t CappedDamerauLevenshteinDistance(absl::string_view s1,
+                                         absl::string_view s2, uint8_t cutoff);
+
+}  // namespace strings_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_DAMERAU_LEVENSHTEIN_DISTANCE_H_
diff --git a/absl/strings/internal/damerau_levenshtein_distance_test.cc b/absl/strings/internal/damerau_levenshtein_distance_test.cc
new file mode 100644
index 0000000..49dd105
--- /dev/null
+++ b/absl/strings/internal/damerau_levenshtein_distance_test.cc
@@ -0,0 +1,99 @@
+// Copyright 2022 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/damerau_levenshtein_distance.h"
+
+#include <cstdint>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using absl::strings_internal::CappedDamerauLevenshteinDistance;
+
+TEST(Distance, TestDistances) {
+  EXPECT_THAT(CappedDamerauLevenshteinDistance("ab", "ab", 6), uint8_t{0});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance("a", "b", 6), uint8_t{1});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance("ca", "abc", 6), uint8_t{3});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance("abcd", "ad", 6), uint8_t{2});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance("abcd", "cadb", 6), uint8_t{4});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance("abcd", "bdac", 6), uint8_t{4});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance("ab", "ab", 0), uint8_t{0});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance("", "", 0), uint8_t{0});
+  // combinations for 3-character strings:
+  // 1, 2, 3 removals, insertions or replacements and transpositions
+  EXPECT_THAT(CappedDamerauLevenshteinDistance("abc", "abc", 6), uint8_t{0});
+  for (auto res :
+       {"", "ca", "efg", "ea", "ce", "ceb", "eca", "cae", "cea", "bea"}) {
+    EXPECT_THAT(CappedDamerauLevenshteinDistance("abc", res, 6), uint8_t{3});
+    EXPECT_THAT(CappedDamerauLevenshteinDistance(res, "abc", 6), uint8_t{3});
+  }
+  for (auto res :
+       {"a",   "b",   "c",   "ba",  "cb",  "bca", "cab", "cba", "ace",
+        "efc", "ebf", "aef", "ae",  "be",  "eb",  "ec",  "ecb", "bec",
+        "bce", "cbe", "ace", "eac", "aeb", "bae", "eab", "eba"}) {
+    EXPECT_THAT(CappedDamerauLevenshteinDistance("abc", res, 6), uint8_t{2});
+    EXPECT_THAT(CappedDamerauLevenshteinDistance(res, "abc", 6), uint8_t{2});
+  }
+  for (auto res : {"ab", "ac", "bc", "acb", "bac", "ebc", "aec", "abe"}) {
+    EXPECT_THAT(CappedDamerauLevenshteinDistance("abc", res, 6), uint8_t{1});
+    EXPECT_THAT(CappedDamerauLevenshteinDistance(res, "abc", 6), uint8_t{1});
+  }
+}
+
+TEST(Distance, TestCutoff) {
+  // Returning cutoff + 1 if the value is larger than cutoff or string longer
+  // than MAX_SIZE.
+  EXPECT_THAT(CappedDamerauLevenshteinDistance("abcd", "a", 3), uint8_t{3});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance("abcd", "a", 2), uint8_t{3});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance("abcd", "a", 1), uint8_t{2});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance("abcdefg", "a", 2), uint8_t{3});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance("a", "abcde", 2), uint8_t{3});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance(std::string(102, 'a'),
+                                               std::string(102, 'a'), 105),
+              uint8_t{101});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance(std::string(100, 'a'),
+                                               std::string(100, 'a'), 100),
+              uint8_t{0});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance(std::string(100, 'a'),
+                                               std::string(100, 'b'), 100),
+              uint8_t{100});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance(std::string(100, 'a'),
+                                               std::string(99, 'a'), 2),
+              uint8_t{1});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance(std::string(100, 'a'),
+                                               std::string(101, 'a'), 2),
+              uint8_t{3});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance(std::string(100, 'a'),
+                                               std::string(101, 'a'), 2),
+              uint8_t{3});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance(std::string(UINT8_MAX + 1, 'a'),
+                                               std::string(UINT8_MAX + 1, 'b'),
+                                               UINT8_MAX),
+              uint8_t{101});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance(std::string(UINT8_MAX - 1, 'a'),
+                                               std::string(UINT8_MAX - 1, 'b'),
+                                               UINT8_MAX),
+              uint8_t{101});
+  EXPECT_THAT(
+      CappedDamerauLevenshteinDistance(std::string(UINT8_MAX, 'a'),
+                                       std::string(UINT8_MAX, 'b'), UINT8_MAX),
+      uint8_t{101});
+  EXPECT_THAT(CappedDamerauLevenshteinDistance(std::string(UINT8_MAX - 1, 'a'),
+                                               std::string(UINT8_MAX - 1, 'a'),
+                                               UINT8_MAX),
+              uint8_t{101});
+}
+}  // namespace
diff --git a/absl/strings/internal/escaping.cc b/absl/strings/internal/escaping.cc
index cfea096..56a4cbe 100644
--- a/absl/strings/internal/escaping.cc
+++ b/absl/strings/internal/escaping.cc
@@ -21,26 +21,26 @@
 ABSL_NAMESPACE_BEGIN
 namespace strings_internal {
 
+// The two strings below provide maps from normal 6-bit characters to their
+// base64-escaped equivalent.
+// For the inverse case, see kUn(WebSafe)Base64 in the external
+// escaping.cc.
 ABSL_CONST_INIT const char kBase64Chars[] =
     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
+ABSL_CONST_INIT const char kWebSafeBase64Chars[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+
 size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding) {
   // Base64 encodes three bytes of input at a time. If the input is not
   // divisible by three, we pad as appropriate.
   //
-  // (from https://tools.ietf.org/html/rfc3548)
-  // Special processing is performed if fewer than 24 bits are available
-  // at the end of the data being encoded.  A full encoding quantum is
-  // always completed at the end of a quantity.  When fewer than 24 input
-  // bits are available in an input group, zero bits are added (on the
-  // right) to form an integral number of 6-bit groups.  Padding at the
-  // end of the data is performed using the '=' character.  Since all base
-  // 64 input is an integral number of octets, only the following cases
-  // can arise:
-
   // Base64 encodes each three bytes of input into four bytes of output.
   size_t len = (input_len / 3) * 4;
 
+  // Since all base 64 input is an integral number of octets, only the following
+  // cases can arise:
   if (input_len % 3 == 0) {
     // (from https://tools.ietf.org/html/rfc3548)
     // (1) the final quantum of encoding input is an integral multiple of 24
@@ -70,6 +70,21 @@
   return len;
 }
 
+// ----------------------------------------------------------------------
+//   Take the input in groups of 4 characters and turn each
+//   character into a code 0 to 63 thus:
+//           A-Z map to 0 to 25
+//           a-z map to 26 to 51
+//           0-9 map to 52 to 61
+//           +(- for WebSafe) maps to 62
+//           /(_ for WebSafe) maps to 63
+//   There will be four numbers, all less than 64 which can be represented
+//   by a 6 digit binary number (aaaaaa, bbbbbb, cccccc, dddddd respectively).
+//   Arrange the 6 digit binary numbers into three bytes as such:
+//   aaaaaabb bbbbcccc ccdddddd
+//   Equals signs (one or two) are used at the end of the encoded block to
+//   indicate that the text was not an integer multiple of three bytes long.
+// ----------------------------------------------------------------------
 size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest,
                             size_t szdest, const char* base64,
                             bool do_padding) {
@@ -83,6 +98,16 @@
   char* const limit_dest = dest + szdest;
   const unsigned char* const limit_src = src + szsrc;
 
+  // (from https://tools.ietf.org/html/rfc3548)
+  // Special processing is performed if fewer than 24 bits are available
+  // at the end of the data being encoded.  A full encoding quantum is
+  // always completed at the end of a quantity.  When fewer than 24 input
+  // bits are available in an input group, zero bits are added (on the
+  // right) to form an integral number of 6-bit groups.
+  //
+  // If do_padding is true, padding at the end of the data is performed. This
+  // output padding uses the '=' character.
+
   // Three bytes of data encodes to four characters of cyphertext.
   // So we can pump through three-byte chunks atomically.
   if (szsrc >= 3) {                    // "limit_src - 3" is UB if szsrc < 3.
diff --git a/absl/strings/internal/escaping.h b/absl/strings/internal/escaping.h
index 6a9ce60..2186f77 100644
--- a/absl/strings/internal/escaping.h
+++ b/absl/strings/internal/escaping.h
@@ -24,20 +24,19 @@
 namespace strings_internal {
 
 ABSL_CONST_INIT extern const char kBase64Chars[];
+ABSL_CONST_INIT extern const char kWebSafeBase64Chars[];
 
-// Calculates how long a string will be when it is base64 encoded given its
-// length and whether or not the result should be padded.
+// Calculates the length of a Base64 encoding (RFC 4648) of a string of length
+// `input_len`, with or without padding per `do_padding`. Note that 'web-safe'
+// encoding (section 5 of the RFC) does not change this length.
 size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding);
 
-// Base64-encodes `src` using the alphabet provided in `base64` and writes the
-// result to `dest`. If `do_padding` is true, `dest` is padded with '=' chars
-// until its length is a multiple of 3. Returns the length of `dest`.
+// Base64-encodes `src` using the alphabet provided in `base64` (which
+// determines whether to do web-safe encoding or not) and writes the result to
+// `dest`. If `do_padding` is true, `dest` is padded with '=' chars until its
+// length is a multiple of 3. Returns the length of `dest`.
 size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest,
                             size_t szdest, const char* base64, bool do_padding);
-
-// Base64-encodes `src` using the alphabet provided in `base64` and writes the
-// result to `dest`. If `do_padding` is true, `dest` is padded with '=' chars
-// until its length is a multiple of 3.
 template <typename String>
 void Base64EscapeInternal(const unsigned char* src, size_t szsrc, String* dest,
                           bool do_padding, const char* base64_chars) {
diff --git a/absl/strings/internal/has_absl_stringify.h b/absl/strings/internal/has_absl_stringify.h
new file mode 100644
index 0000000..55a0850
--- /dev/null
+++ b/absl/strings/internal/has_absl_stringify.h
@@ -0,0 +1,55 @@
+// Copyright 2022 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_HAS_ABSL_STRINGIFY_H_
+#define ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+namespace strings_internal {
+
+// This is an empty class not intended to be used. It exists so that
+// `HasAbslStringify` can reference a universal class rather than needing to be
+// copied for each new sink.
+class UnimplementedSink {
+ public:
+  void Append(size_t count, char ch);
+
+  void Append(string_view v);
+
+  // Support `absl::Format(&sink, format, args...)`.
+  friend void AbslFormatFlush(UnimplementedSink* sink, absl::string_view v);
+};
+
+template <typename T, typename = void>
+struct HasAbslStringify : std::false_type {};
+
+template <typename T>
+struct HasAbslStringify<
+    T, std::enable_if_t<std::is_void<decltype(AbslStringify(
+           std::declval<strings_internal::UnimplementedSink&>(),
+           std::declval<const T&>()))>::value>> : std::true_type {};
+
+}  // namespace strings_internal
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_
diff --git a/absl/strings/internal/stl_type_traits.h b/absl/strings/internal/stl_type_traits.h
index 6035ca4..e50468b 100644
--- a/absl/strings/internal/stl_type_traits.h
+++ b/absl/strings/internal/stl_type_traits.h
@@ -13,7 +13,7 @@
 // limitations under the License.
 //
 
-// Thie file provides the IsStrictlyBaseOfAndConvertibleToSTLContainer type
+// The file provides the IsStrictlyBaseOfAndConvertibleToSTLContainer type
 // trait metafunction to assist in working with the _GLIBCXX_DEBUG debug
 // wrappers of STL containers.
 //
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
index 967fe9c..018dd05 100644
--- a/absl/strings/internal/str_format/arg.cc
+++ b/absl/strings/internal/str_format/arg.cc
@@ -297,6 +297,37 @@
 }
 
 template <typename T>
+bool ConvertFloatArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
+  if (conv.conversion_char() == FormatConversionCharInternal::v) {
+    conv.set_conversion_char(FormatConversionCharInternal::g);
+  }
+
+  return FormatConversionCharIsFloat(conv.conversion_char()) &&
+         ConvertFloatImpl(v, conv, sink);
+}
+
+inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv,
+                             FormatSinkImpl *sink) {
+  if (conv.is_basic()) {
+    sink->Append(v);
+    return true;
+  }
+  return sink->PutPaddedString(v, conv.width(), conv.precision(),
+                               conv.has_left_flag());
+}
+
+}  // namespace
+
+bool ConvertBoolArg(bool v, FormatSinkImpl *sink) {
+  if (v) {
+    sink->Append("true");
+  } else {
+    sink->Append("false");
+  }
+  return true;
+}
+
+template <typename T>
 bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
   using U = typename MakeUnsigned<T>::type;
   IntDigits as_digits;
@@ -354,36 +385,37 @@
   return ConvertIntImplInnerSlow(as_digits, conv, sink);
 }
 
-template <typename T>
-bool ConvertFloatArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
-  if (conv.conversion_char() == FormatConversionCharInternal::v) {
-    conv.set_conversion_char(FormatConversionCharInternal::g);
-  }
-
-  return FormatConversionCharIsFloat(conv.conversion_char()) &&
-         ConvertFloatImpl(v, conv, sink);
-}
-
-inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv,
-                             FormatSinkImpl *sink) {
-  if (conv.is_basic()) {
-    sink->Append(v);
-    return true;
-  }
-  return sink->PutPaddedString(v, conv.width(), conv.precision(),
-                               conv.has_left_flag());
-}
-
-}  // namespace
-
-bool ConvertBoolArg(bool v, FormatSinkImpl *sink) {
-  if (v) {
-    sink->Append("true");
-  } else {
-    sink->Append("false");
-  }
-  return true;
-}
+template bool ConvertIntArg<char>(char v, FormatConversionSpecImpl conv,
+                                  FormatSinkImpl *sink);
+template bool ConvertIntArg<signed char>(signed char v,
+                                         FormatConversionSpecImpl conv,
+                                         FormatSinkImpl *sink);
+template bool ConvertIntArg<unsigned char>(unsigned char v,
+                                           FormatConversionSpecImpl conv,
+                                           FormatSinkImpl *sink);
+template bool ConvertIntArg<short>(short v,  // NOLINT
+                                   FormatConversionSpecImpl conv,
+                                   FormatSinkImpl *sink);
+template bool ConvertIntArg<unsigned short>(unsigned short v,  // NOLINT
+                                            FormatConversionSpecImpl conv,
+                                            FormatSinkImpl *sink);
+template bool ConvertIntArg<int>(int v, FormatConversionSpecImpl conv,
+                                 FormatSinkImpl *sink);
+template bool ConvertIntArg<unsigned int>(unsigned int v,
+                                          FormatConversionSpecImpl conv,
+                                          FormatSinkImpl *sink);
+template bool ConvertIntArg<long>(long v,  // NOLINT
+                                  FormatConversionSpecImpl conv,
+                                  FormatSinkImpl *sink);
+template bool ConvertIntArg<unsigned long>(unsigned long v,  // NOLINT
+                                           FormatConversionSpecImpl conv,
+                                           FormatSinkImpl *sink);
+template bool ConvertIntArg<long long>(long long v,  // NOLINT
+                                       FormatConversionSpecImpl conv,
+                                       FormatSinkImpl *sink);
+template bool ConvertIntArg<unsigned long long>(unsigned long long v,  // NOLINT
+                                                FormatConversionSpecImpl conv,
+                                                FormatSinkImpl *sink);
 
 // ==================== Strings ====================
 StringConvertResult FormatConvertImpl(const std::string &v,
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
index b3e4ff1..e4b1662 100644
--- a/absl/strings/internal/str_format/arg.h
+++ b/absl/strings/internal/str_format/arg.h
@@ -18,6 +18,7 @@
 #include <string.h>
 #include <wchar.h>
 
+#include <algorithm>
 #include <cstdio>
 #include <iomanip>
 #include <limits>
@@ -25,10 +26,12 @@
 #include <sstream>
 #include <string>
 #include <type_traits>
+#include <utility>
 
 #include "absl/base/port.h"
 #include "absl/meta/type_traits.h"
 #include "absl/numeric/int128.h"
+#include "absl/strings/internal/has_absl_stringify.h"
 #include "absl/strings/internal/str_format/extension.h"
 #include "absl/strings/string_view.h"
 
@@ -50,6 +53,19 @@
   bool value;
 };
 
+using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
+    FormatConversionCharSetInternal::c,
+    FormatConversionCharSetInternal::kNumeric,
+    FormatConversionCharSetInternal::kStar,
+    FormatConversionCharSetInternal::v)>;
+using FloatingConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
+    FormatConversionCharSetInternal::kFloating,
+    FormatConversionCharSetInternal::v)>;
+using CharConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
+    FormatConversionCharSetInternal::c,
+    FormatConversionCharSetInternal::kNumeric,
+    FormatConversionCharSetInternal::kStar)>;
+
 template <typename T, typename = void>
 struct HasUserDefinedConvert : std::false_type {};
 
@@ -67,6 +83,44 @@
 void AbslStringify();
 
 template <typename T>
+bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink);
+
+// Forward declarations of internal `ConvertIntArg` function template
+// instantiations are here to avoid including the template body in the headers
+// and instantiating it in large numbers of translation units. Explicit
+// instantiations can be found in "absl/strings/internal/str_format/arg.cc"
+extern template bool ConvertIntArg<char>(char v, FormatConversionSpecImpl conv,
+                                         FormatSinkImpl* sink);
+extern template bool ConvertIntArg<signed char>(signed char v,
+                                                FormatConversionSpecImpl conv,
+                                                FormatSinkImpl* sink);
+extern template bool ConvertIntArg<unsigned char>(unsigned char v,
+                                                  FormatConversionSpecImpl conv,
+                                                  FormatSinkImpl* sink);
+extern template bool ConvertIntArg<short>(short v,  // NOLINT
+                                          FormatConversionSpecImpl conv,
+                                          FormatSinkImpl* sink);
+extern template bool ConvertIntArg<unsigned short>(   // NOLINT
+    unsigned short v, FormatConversionSpecImpl conv,  // NOLINT
+    FormatSinkImpl* sink);
+extern template bool ConvertIntArg<int>(int v, FormatConversionSpecImpl conv,
+                                        FormatSinkImpl* sink);
+extern template bool ConvertIntArg<unsigned int>(unsigned int v,
+                                                 FormatConversionSpecImpl conv,
+                                                 FormatSinkImpl* sink);
+extern template bool ConvertIntArg<long>(                           // NOLINT
+    long v, FormatConversionSpecImpl conv, FormatSinkImpl* sink);   // NOLINT
+extern template bool ConvertIntArg<unsigned long>(unsigned long v,  // NOLINT
+                                                  FormatConversionSpecImpl conv,
+                                                  FormatSinkImpl* sink);
+extern template bool ConvertIntArg<long long>(long long v,  // NOLINT
+                                              FormatConversionSpecImpl conv,
+                                              FormatSinkImpl* sink);
+extern template bool ConvertIntArg<unsigned long long>(   // NOLINT
+    unsigned long long v, FormatConversionSpecImpl conv,  // NOLINT
+    FormatSinkImpl* sink);
+
+template <typename T>
 auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv,
                        FormatSinkImpl* sink)
     -> decltype(AbslFormatConvert(v,
@@ -82,10 +136,30 @@
 }
 
 template <typename T>
+auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv,
+                       FormatSinkImpl* sink)
+    -> std::enable_if_t<std::is_enum<T>::value &&
+                            std::is_void<decltype(AbslStringify(
+                                std::declval<FormatSink&>(), v))>::value,
+                        IntegralConvertResult> {
+  if (conv.conversion_char() == FormatConversionCharInternal::v) {
+    using FormatSinkT =
+        absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>;
+    auto fs = sink->Wrap<FormatSinkT>();
+    AbslStringify(fs, v);
+    return {true};
+  } else {
+    return {ConvertIntArg(
+        static_cast<typename std::underlying_type<T>::type>(v), conv, sink)};
+  }
+}
+
+template <typename T>
 auto FormatConvertImpl(const T& v, FormatConversionSpecImpl,
                        FormatSinkImpl* sink)
-    -> std::enable_if_t<std::is_void<decltype(AbslStringify(
-                            std::declval<FormatSink&>(), v))>::value,
+    -> std::enable_if_t<!std::is_enum<T>::value &&
+                            std::is_void<decltype(AbslStringify(
+                                std::declval<FormatSink&>(), v))>::value,
                         ArgConvertResult<FormatConversionCharSetInternal::v>> {
   using FormatSinkT =
       absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>;
@@ -191,19 +265,6 @@
   return {true};
 }
 
-using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
-    FormatConversionCharSetInternal::c,
-    FormatConversionCharSetInternal::kNumeric,
-    FormatConversionCharSetInternal::kStar,
-    FormatConversionCharSetInternal::v)>;
-using FloatingConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
-    FormatConversionCharSetInternal::kFloating,
-    FormatConversionCharSetInternal::v)>;
-using CharConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
-    FormatConversionCharSetInternal::c,
-    FormatConversionCharSetInternal::kNumeric,
-    FormatConversionCharSetInternal::kStar)>;
-
 bool ConvertBoolArg(bool v, FormatSinkImpl* sink);
 
 // Floats.
@@ -271,7 +332,8 @@
 // FormatArgImpl will use the underlying Convert functions instead.
 template <typename T>
 typename std::enable_if<std::is_enum<T>::value &&
-                            !HasUserDefinedConvert<T>::value,
+                            !HasUserDefinedConvert<T>::value &&
+                            !strings_internal::HasAbslStringify<T>::value,
                         IntegralConvertResult>::type
 FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink);
 
@@ -384,7 +446,8 @@
   template <typename T, typename = void>
   struct DecayType {
     static constexpr bool kHasUserDefined =
-        str_format_internal::HasUserDefinedConvert<T>::value;
+        str_format_internal::HasUserDefinedConvert<T>::value ||
+        strings_internal::HasAbslStringify<T>::value;
     using type = typename std::conditional<
         !kHasUserDefined && std::is_convertible<T, const char*>::value,
         const char*,
@@ -396,6 +459,7 @@
   struct DecayType<T,
                    typename std::enable_if<
                        !str_format_internal::HasUserDefinedConvert<T>::value &&
+                       !strings_internal::HasAbslStringify<T>::value &&
                        std::is_enum<T>::value>::type> {
     using type = typename std::underlying_type<T>::type;
   };
diff --git a/absl/strings/internal/str_format/checker.h b/absl/strings/internal/str_format/checker.h
index aeb9d48..eab6ab9 100644
--- a/absl/strings/internal/str_format/checker.h
+++ b/absl/strings/internal/str_format/checker.h
@@ -15,8 +15,11 @@
 #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
 #define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
 
+#include <algorithm>
+
 #include "absl/base/attributes.h"
 #include "absl/strings/internal/str_format/arg.h"
+#include "absl/strings/internal/str_format/constexpr_parser.h"
 #include "absl/strings/internal/str_format/extension.h"
 
 // Compile time check support for entry points.
@@ -36,333 +39,56 @@
 ABSL_NAMESPACE_BEGIN
 namespace str_format_internal {
 
-constexpr bool AllOf() { return true; }
-
-template <typename... T>
-constexpr bool AllOf(bool b, T... t) {
-  return b && AllOf(t...);
-}
-
 #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
 
-constexpr bool ContainsChar(const char* chars, char c) {
-  return *chars == c || (*chars && ContainsChar(chars + 1, c));
-}
-
-// A constexpr compatible list of Convs.
-struct ConvList {
-  const FormatConversionCharSet* array;
-  int count;
-
-  // We do the bound check here to avoid having to do it on the callers.
-  // Returning an empty FormatConversionCharSet has the same effect as
-  // short circuiting because it will never match any conversion.
-  constexpr FormatConversionCharSet operator[](int i) const {
-    return i < count ? array[i] : FormatConversionCharSet{};
-  }
-
-  constexpr ConvList without_front() const {
-    return count != 0 ? ConvList{array + 1, count - 1} : *this;
-  }
-};
-
-template <size_t count>
-struct ConvListT {
-  // Make sure the array has size > 0.
-  FormatConversionCharSet list[count ? count : 1];
-};
-
-constexpr char GetChar(string_view str, size_t index) {
-  return index < str.size() ? str[index] : char{};
-}
-
-constexpr string_view ConsumeFront(string_view str, size_t len = 1) {
-  return len <= str.size() ? string_view(str.data() + len, str.size() - len)
-                           : string_view();
-}
-
-constexpr string_view ConsumeAnyOf(string_view format, const char* chars) {
-  while (ContainsChar(chars, GetChar(format, 0))) {
-    format = ConsumeFront(format);
-  }
-  return format;
-}
-
-constexpr bool IsDigit(char c) { return c >= '0' && c <= '9'; }
-
-// Helper class for the ParseDigits function.
-// It encapsulates the two return values we need there.
-struct Integer {
-  string_view format;
-  int value;
-
-  // If the next character is a '$', consume it.
-  // Otherwise, make `this` an invalid positional argument.
-  constexpr Integer ConsumePositionalDollar() const {
-    if (GetChar(format, 0) == '$') {
-      return Integer{ConsumeFront(format), value};
-    } else {
-      return Integer{format, 0};
-    }
-  }
-};
-
-constexpr Integer ParseDigits(string_view format) {
-  int value = 0;
-  while (IsDigit(GetChar(format, 0))) {
-    value = 10 * value + GetChar(format, 0) - '0';
-    format = ConsumeFront(format);
-  }
-
-  return Integer{format, value};
-}
-
-// Parse digits for a positional argument.
-// The parsing also consumes the '$'.
-constexpr Integer ParsePositional(string_view format) {
-  return ParseDigits(format).ConsumePositionalDollar();
-}
-
-// Parses a single conversion specifier.
-// See ConvParser::Run() for post conditions.
-class ConvParser {
-  constexpr ConvParser SetFormat(string_view format) const {
-    return ConvParser(format, args_, error_, arg_position_, is_positional_);
-  }
-
-  constexpr ConvParser SetArgs(ConvList args) const {
-    return ConvParser(format_, args, error_, arg_position_, is_positional_);
-  }
-
-  constexpr ConvParser SetError(bool error) const {
-    return ConvParser(format_, args_, error_ || error, arg_position_,
-                      is_positional_);
-  }
-
-  constexpr ConvParser SetArgPosition(int arg_position) const {
-    return ConvParser(format_, args_, error_, arg_position, is_positional_);
-  }
-
-  // Consumes the next arg and verifies that it matches `conv`.
-  // `error_` is set if there is no next arg or if it doesn't match `conv`.
-  constexpr ConvParser ConsumeNextArg(char conv) const {
-    return SetArgs(args_.without_front()).SetError(!Contains(args_[0], conv));
-  }
-
-  // Verify that positional argument `i.value` matches `conv`.
-  // `error_` is set if `i.value` is not a valid argument or if it doesn't
-  // match.
-  constexpr ConvParser VerifyPositional(Integer i, char conv) const {
-    return SetFormat(i.format).SetError(!Contains(args_[i.value - 1], conv));
-  }
-
-  // Parse the position of the arg and store it in `arg_position_`.
-  constexpr ConvParser ParseArgPosition(Integer arg) const {
-    return SetFormat(arg.format).SetArgPosition(arg.value);
-  }
-
-  // Consume the flags.
-  constexpr ConvParser ParseFlags() const {
-    return SetFormat(ConsumeAnyOf(format_, "-+ #0"));
-  }
-
-  // Consume the width.
-  // If it is '*', we verify that it matches `args_`. `error_` is set if it
-  // doesn't match.
-  constexpr ConvParser ParseWidth() const {
-    char first_char = GetChar(format_, 0);
-
-    if (IsDigit(first_char)) {
-      return SetFormat(ParseDigits(format_).format);
-    } else if (first_char == '*') {
-      if (is_positional_) {
-        return VerifyPositional(ParsePositional(ConsumeFront(format_)), '*');
-      } else {
-        return SetFormat(ConsumeFront(format_)).ConsumeNextArg('*');
-      }
-    } else {
-      return *this;
-    }
-  }
-
-  // Consume the precision.
-  // If it is '*', we verify that it matches `args_`. `error_` is set if it
-  // doesn't match.
-  constexpr ConvParser ParsePrecision() const {
-    if (GetChar(format_, 0) != '.') {
-      return *this;
-    } else if (GetChar(format_, 1) == '*') {
-      if (is_positional_) {
-        return VerifyPositional(ParsePositional(ConsumeFront(format_, 2)), '*');
-      } else {
-        return SetFormat(ConsumeFront(format_, 2)).ConsumeNextArg('*');
-      }
-    } else {
-      return SetFormat(ParseDigits(ConsumeFront(format_)).format);
-    }
-  }
-
-  // Consume the length characters.
-  constexpr ConvParser ParseLength() const {
-    return SetFormat(ConsumeAnyOf(format_, "lLhjztq"));
-  }
-
-  // Consume the conversion character and verify that it matches `args_`.
-  // `error_` is set if it doesn't match.
-  constexpr ConvParser ParseConversion() const {
-    char first_char = GetChar(format_, 0);
-
-    if (first_char == 'v' && *(format_.data() - 1) != '%') {
-      return SetError(true);
-    }
-
-    if (is_positional_) {
-      return VerifyPositional({ConsumeFront(format_), arg_position_},
-                              first_char);
-    } else {
-      return ConsumeNextArg(first_char).SetFormat(ConsumeFront(format_));
-    }
-  }
-
-  constexpr ConvParser(string_view format, ConvList args, bool error,
-                       int arg_position, bool is_positional)
-      : format_(format),
-        args_(args),
-        error_(error),
-        arg_position_(arg_position),
-        is_positional_(is_positional) {}
-
- public:
-  constexpr ConvParser(string_view format, ConvList args, bool is_positional)
-      : format_(format),
-        args_(args),
-        error_(false),
-        arg_position_(0),
-        is_positional_(is_positional) {}
-
-  // Consume the whole conversion specifier.
-  // `format()` will be set to the character after the conversion character.
-  // `error()` will be set if any of the arguments do not match.
-  constexpr ConvParser Run() const {
-    ConvParser parser = *this;
-
-    if (is_positional_) {
-      parser = ParseArgPosition(ParsePositional(format_));
-    }
-
-    return parser.ParseFlags()
-        .ParseWidth()
-        .ParsePrecision()
-        .ParseLength()
-        .ParseConversion();
-  }
-
-  constexpr string_view format() const { return format_; }
-  constexpr ConvList args() const { return args_; }
-  constexpr bool error() const { return error_; }
-  constexpr bool is_positional() const { return is_positional_; }
-
- private:
-  string_view format_;
-  // Current list of arguments. If we are not in positional mode we will consume
-  // from the front.
-  ConvList args_;
-  bool error_;
-  // Holds the argument position of the conversion character, if we are in
-  // positional mode. Otherwise, it is unspecified.
-  int arg_position_;
-  // Whether we are in positional mode.
-  // It changes the behavior of '*' and where to find the converted argument.
-  bool is_positional_;
-};
-
-// Parses a whole format expression.
-// See FormatParser::Run().
-class FormatParser {
-  static constexpr bool FoundPercent(string_view format) {
-    return format.empty() ||
-           (GetChar(format, 0) == '%' && GetChar(format, 1) != '%');
-  }
-
-  // We use an inner function to increase the recursion limit.
-  // The inner function consumes up to `limit` characters on every run.
-  // This increases the limit from 512 to ~512*limit.
-  static constexpr string_view ConsumeNonPercentInner(string_view format) {
-    int limit = 20;
-    while (!FoundPercent(format) && limit != 0) {
-      size_t len = 0;
-
-      if (GetChar(format, 0) == '%' && GetChar(format, 1) == '%') {
-        len = 2;
-      } else {
-        len = 1;
-      }
-
-      format = ConsumeFront(format, len);
-      --limit;
-    }
-
-    return format;
-  }
-
-  // Consume characters until the next conversion spec %.
-  // It skips %%.
-  static constexpr string_view ConsumeNonPercent(string_view format) {
-    while (!FoundPercent(format)) {
-      format = ConsumeNonPercentInner(format);
-    }
-
-    return format;
-  }
-
-  static constexpr bool IsPositional(string_view format) {
-    while (IsDigit(GetChar(format, 0))) {
-      format = ConsumeFront(format);
-    }
-
-    return GetChar(format, 0) == '$';
-  }
-
-  constexpr bool RunImpl(bool is_positional) const {
-    // In non-positional mode we require all arguments to be consumed.
-    // In positional mode just reaching the end of the format without errors is
-    // enough.
-    return (format_.empty() && (is_positional || args_.count == 0)) ||
-           (!format_.empty() &&
-            ValidateArg(
-                ConvParser(ConsumeFront(format_), args_, is_positional).Run()));
-  }
-
-  constexpr bool ValidateArg(ConvParser conv) const {
-    return !conv.error() && FormatParser(conv.format(), conv.args())
-                                .RunImpl(conv.is_positional());
-  }
-
- public:
-  constexpr FormatParser(string_view format, ConvList args)
-      : format_(ConsumeNonPercent(format)), args_(args) {}
-
-  // Runs the parser for `format` and `args`.
-  // It verifies that the format is valid and that all conversion specifiers
-  // match the arguments passed.
-  // In non-positional mode it also verfies that all arguments are consumed.
-  constexpr bool Run() const {
-    return RunImpl(!format_.empty() && IsPositional(ConsumeFront(format_)));
-  }
-
- private:
-  string_view format_;
-  // Current list of arguments.
-  // If we are not in positional mode we will consume from the front and will
-  // have to be empty in the end.
-  ConvList args_;
-};
-
 template <FormatConversionCharSet... C>
 constexpr bool ValidFormatImpl(string_view format) {
-  return FormatParser(format,
-                      {ConvListT<sizeof...(C)>{{C...}}.list, sizeof...(C)})
-      .Run();
+  int next_arg = 0;
+  const char* p = format.data();
+  const char* const end = p + format.size();
+  constexpr FormatConversionCharSet
+      kAllowedConvs[(std::max)(sizeof...(C), size_t{1})] = {C...};
+  bool used[(std::max)(sizeof...(C), size_t{1})]{};
+  constexpr int kNumArgs = sizeof...(C);
+  while (p != end) {
+    while (p != end && *p != '%') ++p;
+    if (p == end) {
+      break;
+    }
+    if (p + 1 >= end) return false;
+    if (p[1] == '%') {
+      // %%
+      p += 2;
+      continue;
+    }
+
+    UnboundConversion conv(absl::kConstInit);
+    p = ConsumeUnboundConversion(p + 1, end, &conv, &next_arg);
+    if (p == nullptr) return false;
+    if (conv.arg_position <= 0 || conv.arg_position > kNumArgs) {
+      return false;
+    }
+    if (!Contains(kAllowedConvs[conv.arg_position - 1], conv.conv)) {
+      return false;
+    }
+    used[conv.arg_position - 1] = true;
+    for (auto extra : {conv.width, conv.precision}) {
+      if (extra.is_from_arg()) {
+        int pos = extra.get_from_arg();
+        if (pos <= 0 || pos > kNumArgs) return false;
+        used[pos - 1] = true;
+        if (!Contains(kAllowedConvs[pos - 1], '*')) {
+          return false;
+        }
+      }
+    }
+  }
+  if (sizeof...(C) != 0) {
+    for (bool b : used) {
+      if (!b) return false;
+    }
+  }
+  return true;
 }
 
 #endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
diff --git a/absl/strings/internal/str_format/checker_test.cc b/absl/strings/internal/str_format/checker_test.cc
index 680517f..a86bed3 100644
--- a/absl/strings/internal/str_format/checker_test.cc
+++ b/absl/strings/internal/str_format/checker_test.cc
@@ -93,6 +93,7 @@
       ValidFormat<void (*)(), volatile int*>("%p %p"),  //
       ValidFormat<string_view, const char*, double, void*>(
           "string_view=%s const char*=%s double=%f void*=%p)"),
+      ValidFormat<int>("%v"),  //
 
       ValidFormat<int>("%% %1$d"),               //
       ValidFormat<int>("%1$ld"),                 //
@@ -109,7 +110,9 @@
       ValidFormat<int, double>("%2$.*1$f"),      //
       ValidFormat<void*, string_view, const char*, double>(
           "string_view=%2$s const char*=%3$s double=%4$f void*=%1$p "
-          "repeat=%3$s)")};
+          "repeat=%3$s)"),
+      ValidFormat<std::string>("%1$v"),
+  };
 
   for (Case c : trues) {
     EXPECT_TRUE(c.result) << c.format;
@@ -130,6 +133,8 @@
       ValidFormat<int>("%*d"),               //
       ValidFormat<std::string>("%p"),        //
       ValidFormat<int (*)(int)>("%d"),       //
+      ValidFormat<int>("%1v"),               //
+      ValidFormat<int>("%.1v"),              //
 
       ValidFormat<>("%3$d"),                     //
       ValidFormat<>("%1$r"),                     //
@@ -138,13 +143,14 @@
       ValidFormat<int>("%1$*2$1d"),              //
       ValidFormat<int>("%1$1-d"),                //
       ValidFormat<std::string, int>("%2$*1$s"),  //
-      ValidFormat<std::string>("%1$p"),
+      ValidFormat<std::string>("%1$p"),          //
+      ValidFormat<int>("%1$*2$v"),               //
 
       ValidFormat<int, int>("%d %2$d"),  //
   };
 
   for (Case c : falses) {
-    EXPECT_FALSE(c.result) << c.format;
+    EXPECT_FALSE(c.result) << "format<" << c.format << ">";
   }
 }
 
diff --git a/absl/strings/internal/str_format/constexpr_parser.h b/absl/strings/internal/str_format/constexpr_parser.h
new file mode 100644
index 0000000..3dc1776
--- /dev/null
+++ b/absl/strings/internal/str_format/constexpr_parser.h
@@ -0,0 +1,351 @@
+// Copyright 2022 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_STR_FORMAT_CONSTEXPR_PARSER_H_
+#define ABSL_STRINGS_INTERNAL_STR_FORMAT_CONSTEXPR_PARSER_H_
+
+#include <cassert>
+#include <cstdint>
+#include <limits>
+
+#include "absl/base/const_init.h"
+#include "absl/strings/internal/str_format/extension.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace str_format_internal {
+
+enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none };
+
+// The analyzed properties of a single specified conversion.
+struct UnboundConversion {
+  // This is a user defined default constructor on purpose to skip the
+  // initialization of parts of the object that are not necessary.
+  UnboundConversion() {}  // NOLINT
+
+  // This constructor is provided for the static checker. We don't want to do
+  // the unnecessary initialization in the normal case.
+  explicit constexpr UnboundConversion(absl::ConstInitType)
+      : arg_position{}, width{}, precision{} {}
+
+  class InputValue {
+   public:
+    constexpr void set_value(int value) {
+      assert(value >= 0);
+      value_ = value;
+    }
+    constexpr int value() const { return value_; }
+
+    // Marks the value as "from arg". aka the '*' format.
+    // Requires `value >= 1`.
+    // When set, is_from_arg() return true and get_from_arg() returns the
+    // original value.
+    // `value()`'s return value is unspecified in this state.
+    constexpr void set_from_arg(int value) {
+      assert(value > 0);
+      value_ = -value - 1;
+    }
+    constexpr bool is_from_arg() const { return value_ < -1; }
+    constexpr int get_from_arg() const {
+      assert(is_from_arg());
+      return -value_ - 1;
+    }
+
+   private:
+    int value_ = -1;
+  };
+
+  // No need to initialize. It will always be set in the parser.
+  int arg_position;
+
+  InputValue width;
+  InputValue precision;
+
+  Flags flags = Flags::kBasic;
+  LengthMod length_mod = LengthMod::none;
+  FormatConversionChar conv = FormatConversionCharInternal::kNone;
+};
+
+// Helper tag class for the table below.
+// It allows fast `char -> ConversionChar/LengthMod/Flags` checking and
+// conversions.
+class ConvTag {
+ public:
+  constexpr ConvTag(FormatConversionChar conversion_char)  // NOLINT
+      : tag_(static_cast<uint8_t>(conversion_char)) {}
+  constexpr ConvTag(LengthMod length_mod)  // NOLINT
+      : tag_(0x80 | static_cast<uint8_t>(length_mod)) {}
+  constexpr ConvTag(Flags flags)  // NOLINT
+      : tag_(0xc0 | static_cast<uint8_t>(flags)) {}
+  constexpr ConvTag() : tag_(0xFF) {}
+
+  constexpr bool is_conv() const { return (tag_ & 0x80) == 0; }
+  constexpr bool is_length() const { return (tag_ & 0xC0) == 0x80; }
+  constexpr bool is_flags() const { return (tag_ & 0xE0) == 0xC0; }
+
+  constexpr FormatConversionChar as_conv() const {
+    assert(is_conv());
+    assert(!is_length());
+    assert(!is_flags());
+    return static_cast<FormatConversionChar>(tag_);
+  }
+  constexpr LengthMod as_length() const {
+    assert(!is_conv());
+    assert(is_length());
+    assert(!is_flags());
+    return static_cast<LengthMod>(tag_ & 0x3F);
+  }
+  constexpr Flags as_flags() const {
+    assert(!is_conv());
+    assert(!is_length());
+    assert(is_flags());
+    return static_cast<Flags>(tag_ & 0x1F);
+  }
+
+ private:
+  uint8_t tag_;
+};
+
+struct ConvTagHolder {
+  using CC = FormatConversionCharInternal;
+  using LM = LengthMod;
+
+  // Abbreviations to fit in the table below.
+  static constexpr auto kFSign = Flags::kSignCol;
+  static constexpr auto kFAlt = Flags::kAlt;
+  static constexpr auto kFPos = Flags::kShowPos;
+  static constexpr auto kFLeft = Flags::kLeft;
+  static constexpr auto kFZero = Flags::kZero;
+
+  static constexpr ConvTag value[256] = {
+      {},     {},    {},    {},    {},    {},     {},    {},     // 00-07
+      {},     {},    {},    {},    {},    {},     {},    {},     // 08-0f
+      {},     {},    {},    {},    {},    {},     {},    {},     // 10-17
+      {},     {},    {},    {},    {},    {},     {},    {},     // 18-1f
+      kFSign, {},    {},    kFAlt, {},    {},     {},    {},     //  !"#$%&'
+      {},     {},    {},    kFPos, {},    kFLeft, {},    {},     // ()*+,-./
+      kFZero, {},    {},    {},    {},    {},     {},    {},     // 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,  CC::v, {},     // 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
+  };
+};
+
+// Keep a single table for all the conversion chars and length modifiers.
+constexpr ConvTag GetTagForChar(char c) {
+  return ConvTagHolder::value[static_cast<unsigned char>(c)];
+}
+
+constexpr bool CheckFastPathSetting(const UnboundConversion& conv) {
+  bool width_precision_needed =
+      conv.width.value() >= 0 || conv.precision.value() >= 0;
+  if (width_precision_needed && conv.flags == Flags::kBasic) {
+#if defined(__clang__)
+    // Some compilers complain about this in constexpr even when not executed,
+    // so only enable the error dump in clang.
+    fprintf(stderr,
+            "basic=%d left=%d show_pos=%d sign_col=%d alt=%d zero=%d "
+            "width=%d precision=%d\n",
+            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());
+#endif  // defined(__clang__)
+    return false;
+  }
+  return true;
+}
+
+constexpr int ParseDigits(char& c, const char*& pos, const char* const end) {
+  int digits = c - '0';
+  // We do not want to overflow `digits` so we consume at most digits10
+  // digits. If there are more digits the parsing will fail later on when the
+  // digit doesn't match the expected characters.
+  int num_digits = std::numeric_limits<int>::digits10;
+  for (;;) {
+    if (ABSL_PREDICT_FALSE(pos == end)) break;
+    c = *pos++;
+    if ('0' > c || c > '9') break;
+    --num_digits;
+    if (ABSL_PREDICT_FALSE(!num_digits)) break;
+    digits = 10 * digits + c - '0';
+  }
+  return digits;
+}
+
+template <bool is_positional>
+constexpr const char* ConsumeConversion(const char* pos, const char* const end,
+                                        UnboundConversion* conv,
+                                        int* next_arg) {
+  const char* const original_pos = pos;
+  char c = 0;
+  // Read the next char into `c` and update `pos`. Returns false if there are
+  // no more chars to read.
+#define ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR()          \
+  do {                                                  \
+    if (ABSL_PREDICT_FALSE(pos == end)) return nullptr; \
+    c = *pos++;                                         \
+  } while (0)
+
+  if (is_positional) {
+    ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+    if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr;
+    conv->arg_position = ParseDigits(c, pos, end);
+    assert(conv->arg_position > 0);
+    if (ABSL_PREDICT_FALSE(c != '$')) return nullptr;
+  }
+
+  ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+
+  // We should start with the basic flag on.
+  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') {
+    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;
+      }
+    }
+
+    if (c <= '9') {
+      if (c >= '0') {
+        int maybe_width = ParseDigits(c, pos, end);
+        if (!is_positional && c == '$') {
+          if (ABSL_PREDICT_FALSE(*next_arg != 0)) return nullptr;
+          // Positional conversion.
+          *next_arg = -1;
+          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;
+          conv->width.set_from_arg(ParseDigits(c, pos, end));
+          if (ABSL_PREDICT_FALSE(c != '$')) return nullptr;
+          ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+        } else {
+          conv->width.set_from_arg(++*next_arg);
+        }
+      }
+    }
+
+    if (c == '.') {
+      conv->flags = conv->flags | Flags::kNonBasic;
+      ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+      if ('0' <= c && c <= '9') {
+        conv->precision.set_value(ParseDigits(c, pos, end));
+      } else if (c == '*') {
+        ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+        if (is_positional) {
+          if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr;
+          conv->precision.set_from_arg(ParseDigits(c, pos, end));
+          if (c != '$') return nullptr;
+          ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+        } else {
+          conv->precision.set_from_arg(++*next_arg);
+        }
+      } else {
+        conv->precision.set_value(0);
+      }
+    }
+  }
+
+  auto tag = GetTagForChar(c);
+
+  if (ABSL_PREDICT_FALSE(c == 'v' && conv->flags != Flags::kBasic)) {
+    return nullptr;
+  }
+
+  if (ABSL_PREDICT_FALSE(!tag.is_conv())) {
+    if (ABSL_PREDICT_FALSE(!tag.is_length())) return nullptr;
+
+    // It is a length modifier.
+    using str_format_internal::LengthMod;
+    LengthMod length_mod = tag.as_length();
+    ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+    if (c == 'h' && length_mod == LengthMod::h) {
+      conv->length_mod = LengthMod::hh;
+      ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+    } else if (c == 'l' && length_mod == LengthMod::l) {
+      conv->length_mod = LengthMod::ll;
+      ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+    } else {
+      conv->length_mod = length_mod;
+    }
+    tag = GetTagForChar(c);
+
+    if (ABSL_PREDICT_FALSE(c == 'v')) return nullptr;
+    if (ABSL_PREDICT_FALSE(!tag.is_conv())) return nullptr;
+  }
+
+  assert(CheckFastPathSetting(*conv));
+  (void)(&CheckFastPathSetting);
+
+  conv->conv = tag.as_conv();
+  if (!is_positional) conv->arg_position = ++*next_arg;
+  return pos;
+}
+
+// Consume conversion spec prefix (not including '%') of [p, end) if valid.
+// Examples of valid specs would be e.g.: "s", "d", "-12.6f".
+// If valid, it returns the first character following the conversion spec,
+// and the spec part is broken down and returned in 'conv'.
+// If invalid, returns nullptr.
+constexpr const char* ConsumeUnboundConversion(const char* p, const char* end,
+                                               UnboundConversion* conv,
+                                               int* next_arg) {
+  if (*next_arg < 0) return ConsumeConversion<true>(p, end, conv, next_arg);
+  return ConsumeConversion<false>(p, end, conv, next_arg);
+}
+
+}  // namespace str_format_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_CONSTEXPR_PARSER_H_
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc
index 300612b..8b5a27e 100644
--- a/absl/strings/internal/str_format/convert_test.cc
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -1241,9 +1241,9 @@
   const NativePrintfTraits &native_traits = VerifyNativeImplementation();
   // If one of the following tests break then it is either because the above PP
   // macro guards failed to exclude a new platform (likely) or because something
-  // has changed in the implemention of glibc sprintf float formatting behavior.
-  // If the latter, then the code that computes these flags needs to be
-  // revisited and/or possibly the StrFormat implementation.
+  // has changed in the implementation of glibc sprintf float formatting
+  // behavior.  If the latter, then the code that computes these flags needs to
+  // be revisited and/or possibly the StrFormat implementation.
   EXPECT_TRUE(native_traits.hex_float_has_glibc_rounding);
   EXPECT_TRUE(native_traits.hex_float_prefers_denormal_repr);
   EXPECT_TRUE(
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h
index 603bd49..8de42d2 100644
--- a/absl/strings/internal/str_format/extension.h
+++ b/absl/strings/internal/str_format/extension.h
@@ -273,7 +273,7 @@
 
 class FormatConversionSpecImpl {
  public:
-  // Width and precison are not specified, no flags are set.
+  // Width and precision are not specified, no flags are set.
   bool is_basic() const { return flags_ == Flags::kBasic; }
   bool has_left_flag() const { return FlagsContains(flags_, Flags::kLeft); }
   bool has_show_pos_flag() const {
diff --git a/absl/strings/internal/str_format/float_conversion.cc b/absl/strings/internal/str_format/float_conversion.cc
index 8e49785..8edf520 100644
--- a/absl/strings/internal/str_format/float_conversion.cc
+++ b/absl/strings/internal/str_format/float_conversion.cc
@@ -711,12 +711,12 @@
   constexpr size_t kShift = sizeof(Int) * 8 - 1;
   constexpr size_t kNumNibbles = sizeof(Int) * 8 / 4;
   Int before = *n >> kShift;
-  // Here we essentially want to take the number 1 and move it into the requsted
-  // nibble, then add it to *n to effectively increment the nibble. However,
-  // ASan will complain if we try to shift the 1 beyond the limits of the Int,
-  // i.e., if the nibble_index is out of range. So therefore we check for this
-  // and if we are out of range we just add 0 which leaves *n unchanged, which
-  // seems like the reasonable thing to do in that case.
+  // Here we essentially want to take the number 1 and move it into the
+  // requested nibble, then add it to *n to effectively increment the nibble.
+  // However, ASan will complain if we try to shift the 1 beyond the limits of
+  // the Int, i.e., if the nibble_index is out of range. So therefore we check
+  // for this and if we are out of range we just add 0 which leaves *n
+  // unchanged, which seems like the reasonable thing to do in that case.
   *n += ((nibble_index >= kNumNibbles)
              ? 0
              : (Int{1} << static_cast<int>(nibble_index * 4)));
@@ -937,7 +937,7 @@
 
   // =============== Exponent ==================
   constexpr size_t kBufSizeForExpDecRepr =
-      numbers_internal::kFastToBufferSize  // requred for FastIntToBuffer
+      numbers_internal::kFastToBufferSize  // required for FastIntToBuffer
       + 1                                  // 'p' or 'P'
       + 1;                                 // '+' or '-'
   char exp_buffer[kBufSizeForExpDecRepr];
@@ -1015,7 +1015,7 @@
     --end;
   }
 
-  char &back() {
+  char &back() const {
     assert(begin < end);
     return end[-1];
   }
@@ -1102,7 +1102,7 @@
 template <typename Float, typename Int>
 constexpr bool CanFitMantissa() {
   return
-#if defined(__clang__) && !defined(__SSE3__)
+#if defined(__clang__) && (__clang_major__ < 9) && !defined(__SSE3__)
       // Workaround for clang bug: https://bugs.llvm.org/show_bug.cgi?id=38289
       // Casting from long double to uint64_t is miscompiled and drops bits.
       (!std::is_same<Float, long double>::value ||
diff --git a/absl/strings/internal/str_format/parser.cc b/absl/strings/internal/str_format/parser.cc
index f9bb661..5aaab69 100644
--- a/absl/strings/internal/str_format/parser.cc
+++ b/absl/strings/internal/str_format/parser.cc
@@ -31,211 +31,14 @@
 ABSL_NAMESPACE_BEGIN
 namespace str_format_internal {
 
-using CC = FormatConversionCharInternal;
-using LM = LengthMod;
+// Define the array for non-constexpr uses.
+constexpr ConvTag ConvTagHolder::value[256];
 
-// 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
-    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,  CC::v, {},     // 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 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 == 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 true;
+ABSL_ATTRIBUTE_NOINLINE const char* ConsumeUnboundConversionNoInline(
+    const char* p, const char* end, UnboundConversion* conv, int* next_arg) {
+  return ConsumeUnboundConversion(p, end, conv, next_arg);
 }
 
-template <bool is_positional>
-const char *ConsumeConversion(const char *pos, const char *const end,
-                              UnboundConversion *conv, int *next_arg) {
-  const char* const original_pos = pos;
-  char c;
-  // Read the next char into `c` and update `pos`. Returns false if there are
-  // no more chars to read.
-#define ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR()          \
-  do {                                                  \
-    if (ABSL_PREDICT_FALSE(pos == end)) return nullptr; \
-    c = *pos++;                                         \
-  } while (0)
-
-  const auto parse_digits = [&] {
-    int digits = c - '0';
-    // We do not want to overflow `digits` so we consume at most digits10
-    // digits. If there are more digits the parsing will fail later on when the
-    // digit doesn't match the expected characters.
-    int num_digits = std::numeric_limits<int>::digits10;
-    for (;;) {
-      if (ABSL_PREDICT_FALSE(pos == end)) break;
-      c = *pos++;
-      if (!std::isdigit(c)) break;
-      --num_digits;
-      if (ABSL_PREDICT_FALSE(!num_digits)) break;
-      digits = 10 * digits + c - '0';
-    }
-    return digits;
-  };
-
-  if (is_positional) {
-    ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-    if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr;
-    conv->arg_position = parse_digits();
-    assert(conv->arg_position > 0);
-    if (ABSL_PREDICT_FALSE(c != '$')) return nullptr;
-  }
-
-  ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-
-  // We should start with the basic flag on.
-  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') {
-    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;
-      }
-    }
-
-    if (c <= '9') {
-      if (c >= '0') {
-        int maybe_width = parse_digits();
-        if (!is_positional && c == '$') {
-          if (ABSL_PREDICT_FALSE(*next_arg != 0)) return nullptr;
-          // Positional conversion.
-          *next_arg = -1;
-          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;
-          conv->width.set_from_arg(parse_digits());
-          if (ABSL_PREDICT_FALSE(c != '$')) return nullptr;
-          ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-        } else {
-          conv->width.set_from_arg(++*next_arg);
-        }
-      }
-    }
-
-    if (c == '.') {
-      conv->flags = conv->flags | Flags::kNonBasic;
-      ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-      if (std::isdigit(c)) {
-        conv->precision.set_value(parse_digits());
-      } else if (c == '*') {
-        ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-        if (is_positional) {
-          if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr;
-          conv->precision.set_from_arg(parse_digits());
-          if (c != '$') return nullptr;
-          ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-        } else {
-          conv->precision.set_from_arg(++*next_arg);
-        }
-      } else {
-        conv->precision.set_value(0);
-      }
-    }
-  }
-
-  auto tag = GetTagForChar(c);
-
-  if (*(pos - 1) == 'v' && *(pos - 2) != '%') {
-    return nullptr;
-  }
-
-  if (ABSL_PREDICT_FALSE(!tag.is_conv())) {
-    if (ABSL_PREDICT_FALSE(!tag.is_length())) return nullptr;
-
-    // It is a length modifier.
-    using str_format_internal::LengthMod;
-    LengthMod length_mod = tag.as_length();
-    ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-    if (c == 'h' && length_mod == LengthMod::h) {
-      conv->length_mod = LengthMod::hh;
-      ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-    } else if (c == 'l' && length_mod == LengthMod::l) {
-      conv->length_mod = LengthMod::ll;
-      ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-    } else {
-      conv->length_mod = length_mod;
-    }
-    tag = GetTagForChar(c);
-    if (ABSL_PREDICT_FALSE(!tag.is_conv())) return nullptr;
-  }
-
-  assert(CheckFastPathSetting(*conv));
-  (void)(&CheckFastPathSetting);
-
-  conv->conv = tag.as_conv();
-  if (!is_positional) conv->arg_position = ++*next_arg;
-  return pos;
-}
-
-}  // namespace
-
 std::string LengthModToString(LengthMod v) {
   switch (v) {
     case LengthMod::h:
@@ -262,12 +65,6 @@
   return "";
 }
 
-const char *ConsumeUnboundConversion(const char *p, const char *end,
-                                     UnboundConversion *conv, int *next_arg) {
-  if (*next_arg < 0) return ConsumeConversion<true>(p, end, conv, next_arg);
-  return ConsumeConversion<false>(p, end, conv, next_arg);
-}
-
 struct ParsedFormatBase::ParsedFormatConsumer {
   explicit ParsedFormatConsumer(ParsedFormatBase *parsedformat)
       : parsed(parsedformat), data_pos(parsedformat->data_.get()) {}
diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h
index a81bac8..35b6d49 100644
--- a/absl/strings/internal/str_format/parser.h
+++ b/absl/strings/internal/str_format/parser.h
@@ -29,111 +29,18 @@
 #include <vector>
 
 #include "absl/strings/internal/str_format/checker.h"
+#include "absl/strings/internal/str_format/constexpr_parser.h"
 #include "absl/strings/internal/str_format/extension.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace str_format_internal {
 
-enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none };
-
 std::string LengthModToString(LengthMod v);
 
-// The analyzed properties of a single specified conversion.
-struct UnboundConversion {
-  UnboundConversion() {}
-
-  class InputValue {
-   public:
-    void set_value(int value) {
-      assert(value >= 0);
-      value_ = value;
-    }
-    int value() const { return value_; }
-
-    // Marks the value as "from arg". aka the '*' format.
-    // Requires `value >= 1`.
-    // When set, is_from_arg() return true and get_from_arg() returns the
-    // original value.
-    // `value()`'s return value is unspecfied in this state.
-    void set_from_arg(int value) {
-      assert(value > 0);
-      value_ = -value - 1;
-    }
-    bool is_from_arg() const { return value_ < -1; }
-    int get_from_arg() const {
-      assert(is_from_arg());
-      return -value_ - 1;
-    }
-
-   private:
-    int value_ = -1;
-  };
-
-  // No need to initialize. It will always be set in the parser.
-  int arg_position;
-
-  InputValue width;
-  InputValue precision;
-
-  Flags flags = Flags::kBasic;
-  LengthMod length_mod = LengthMod::none;
-  FormatConversionChar conv = FormatConversionCharInternal::kNone;
-};
-
-// Consume conversion spec prefix (not including '%') of [p, end) if valid.
-// Examples of valid specs would be e.g.: "s", "d", "-12.6f".
-// If valid, it returns the first character following the conversion spec,
-// and the spec part is broken down and returned in 'conv'.
-// If invalid, returns nullptr.
-const char* ConsumeUnboundConversion(const char* p, const char* end,
-                                     UnboundConversion* conv, int* next_arg);
-
-// Helper tag class for the table below.
-// It allows fast `char -> ConversionChar/LengthMod/Flags` checking and
-// conversions.
-class ConvTag {
- public:
-  constexpr ConvTag(FormatConversionChar conversion_char)  // NOLINT
-      : tag_(static_cast<uint8_t>(conversion_char)) {}
-  constexpr ConvTag(LengthMod length_mod)  // NOLINT
-      : 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_ & 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());
-    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:
-  uint8_t tag_;
-};
-
-extern const ConvTag kTags[256];
-// Keep a single table for all the conversion chars and length modifiers.
-inline ConvTag GetTagForChar(char c) {
-  return kTags[static_cast<unsigned char>(c)];
-}
+const char* ConsumeUnboundConversionNoInline(const char* p, const char* end,
+                                             UnboundConversion* conv,
+                                             int* next_arg);
 
 // Parse the format string provided in 'src' and pass the identified items into
 // 'consumer'.
@@ -187,7 +94,7 @@
       }
     } else if (percent[1] != '%') {
       UnboundConversion conv;
-      p = ConsumeUnboundConversion(percent + 1, end, &conv, &next_arg);
+      p = ConsumeUnboundConversionNoInline(percent + 1, end, &conv, &next_arg);
       if (ABSL_PREDICT_FALSE(p == nullptr)) return false;
       if (ABSL_PREDICT_FALSE(!consumer.ConvertOne(
               conv, string_view(percent + 1,
diff --git a/absl/strings/internal/str_format/parser_test.cc b/absl/strings/internal/str_format/parser_test.cc
index fe0d296..021f6a8 100644
--- a/absl/strings/internal/str_format/parser_test.cc
+++ b/absl/strings/internal/str_format/parser_test.cc
@@ -110,10 +110,14 @@
     {__LINE__, "ba",   "",     "ba"},  // 'b' is invalid
     {__LINE__, "l",    "",     "l" },  // just length mod isn't okay
     {__LINE__, "d",    "d",    ""  },  // basic
+    {__LINE__, "v",    "v",    ""  },  // basic
     {__LINE__, "d ",   "d",    " " },  // leave suffix
     {__LINE__, "dd",   "d",    "d" },  // don't be greedy
     {__LINE__, "d9",   "d",    "9" },  // leave non-space suffix
     {__LINE__, "dzz",  "d",    "zz"},  // length mod as suffix
+    {__LINE__, "3v",   "",     "3v"},  // 'v' cannot have modifiers
+    {__LINE__, "hv",   "",     "hv"},  // 'v' cannot have modifiers
+    {__LINE__, "1$v",   "1$v",     ""},  // 'v' can have use posix syntax
     {__LINE__, "1$*2$d", "1$*2$d", ""  },  // arg indexing and * allowed.
     {__LINE__, "0-14.3hhd", "0-14.3hhd", ""},  // precision, width
     {__LINE__, " 0-+#14.3hhd", " 0-+#14.3hhd", ""},  // flags
diff --git a/absl/strings/internal/stringify_sink.cc b/absl/strings/internal/stringify_sink.cc
new file mode 100644
index 0000000..7c6995a
--- /dev/null
+++ b/absl/strings/internal/stringify_sink.cc
@@ -0,0 +1,28 @@
+// Copyright 2022 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/stringify_sink.h"
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace strings_internal {
+
+void StringifySink::Append(size_t count, char ch) { buffer_.append(count, ch); }
+
+void StringifySink::Append(string_view v) {
+  buffer_.append(v.data(), v.size());
+}
+
+}  // namespace strings_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/strings/internal/stringify_sink.h b/absl/strings/internal/stringify_sink.h
new file mode 100644
index 0000000..fc3747b
--- /dev/null
+++ b/absl/strings/internal/stringify_sink.h
@@ -0,0 +1,57 @@
+// Copyright 2022 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_STRINGIFY_SINK_H_
+#define ABSL_STRINGS_INTERNAL_STRINGIFY_SINK_H_
+
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+namespace strings_internal {
+class StringifySink {
+ public:
+  void Append(size_t count, char ch);
+
+  void Append(string_view v);
+
+  // Support `absl::Format(&sink, format, args...)`.
+  friend void AbslFormatFlush(StringifySink* sink, absl::string_view v) {
+    sink->Append(v);
+  }
+
+ private:
+  template <typename T>
+  friend string_view ExtractStringification(StringifySink& sink, const T& v);
+
+  std::string buffer_;
+};
+
+template <typename T>
+string_view ExtractStringification(StringifySink& sink, const T& v) {
+  AbslStringify(sink, v);
+  return sink.buffer_;
+}
+
+}  // namespace strings_internal
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_STRINGIFY_SINK_H_
diff --git a/absl/strings/match.cc b/absl/strings/match.cc
index 2d67250..b65cbc6 100644
--- a/absl/strings/match.cc
+++ b/absl/strings/match.cc
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "absl/strings/match.h"
+#include "absl/strings/ascii.h"
 
 #include "absl/strings/internal/memutil.h"
 
@@ -27,6 +28,27 @@
   // memcasecmp uses absl::ascii_tolower().
 }
 
+bool StrContainsIgnoreCase(absl::string_view haystack,
+                           absl::string_view needle) noexcept {
+  while (haystack.size() >= needle.size()) {
+    if (StartsWithIgnoreCase(haystack, needle)) return true;
+    haystack.remove_prefix(1);
+  }
+  return false;
+}
+
+bool StrContainsIgnoreCase(absl::string_view haystack,
+                           char needle) noexcept {
+  char upper_needle = absl::ascii_toupper(static_cast<unsigned char>(needle));
+  char lower_needle = absl::ascii_tolower(static_cast<unsigned char>(needle));
+  if (upper_needle == lower_needle) {
+    return StrContains(haystack, needle);
+  } else {
+    const char both_cstr[3] = {lower_needle, upper_needle, '\0'};
+    return haystack.find_first_of(both_cstr) != absl::string_view::npos;
+  }
+}
+
 bool StartsWithIgnoreCase(absl::string_view text,
                           absl::string_view prefix) noexcept {
   return (text.size() >= prefix.size()) &&
diff --git a/absl/strings/match.h b/absl/strings/match.h
index 038cbb3..1dc0bea 100644
--- a/absl/strings/match.h
+++ b/absl/strings/match.h
@@ -72,6 +72,15 @@
           memcmp(text.data() + (text.size() - suffix.size()), suffix.data(),
                  suffix.size()) == 0);
 }
+// StrContainsIgnoreCase()
+//
+// Returns whether a given ASCII string `haystack` contains the ASCII substring
+// `needle`, ignoring case in the comparison.
+bool StrContainsIgnoreCase(absl::string_view haystack,
+                           absl::string_view needle) noexcept;
+
+bool StrContainsIgnoreCase(absl::string_view haystack,
+                           char needle) noexcept;
 
 // EqualsIgnoreCase()
 //
diff --git a/absl/strings/match_test.cc b/absl/strings/match_test.cc
index 5841bc1..f063b4e 100644
--- a/absl/strings/match_test.cc
+++ b/absl/strings/match_test.cc
@@ -124,4 +124,48 @@
   EXPECT_FALSE(absl::EndsWithIgnoreCase("", "fo"));
 }
 
+TEST(MatchTest, ContainsIgnoreCase) {
+  EXPECT_TRUE(absl::StrContainsIgnoreCase("foo", "foo"));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase("FOO", "Foo"));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase("--FOO", "Foo"));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase("FOO--", "Foo"));
+  EXPECT_FALSE(absl::StrContainsIgnoreCase("BAR", "Foo"));
+  EXPECT_FALSE(absl::StrContainsIgnoreCase("BAR", "Foo"));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase("123456", "123456"));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase("123456", "234"));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase("", ""));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase("abc", ""));
+  EXPECT_FALSE(absl::StrContainsIgnoreCase("", "a"));
+}
+
+TEST(MatchTest, ContainsCharIgnoreCase) {
+  absl::string_view a("AaBCdefg!");
+  absl::string_view b("AaBCd!");
+  EXPECT_TRUE(absl::StrContainsIgnoreCase(a, 'a'));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase(a, 'A'));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase(a, 'b'));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase(a, 'B'));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase(a, 'e'));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase(a, 'E'));
+  EXPECT_FALSE(absl::StrContainsIgnoreCase(a, 'h'));
+  EXPECT_FALSE(absl::StrContainsIgnoreCase(a, 'H'));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase(a, '!'));
+  EXPECT_FALSE(absl::StrContainsIgnoreCase(a, '?'));
+
+  EXPECT_TRUE(absl::StrContainsIgnoreCase(b, 'a'));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase(b, 'A'));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase(b, 'b'));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase(b, 'B'));
+  EXPECT_FALSE(absl::StrContainsIgnoreCase(b, 'e'));
+  EXPECT_FALSE(absl::StrContainsIgnoreCase(b, 'E'));
+  EXPECT_FALSE(absl::StrContainsIgnoreCase(b, 'h'));
+  EXPECT_FALSE(absl::StrContainsIgnoreCase(b, 'H'));
+  EXPECT_TRUE(absl::StrContainsIgnoreCase(b, '!'));
+  EXPECT_FALSE(absl::StrContainsIgnoreCase(b, '?'));
+
+  EXPECT_FALSE(absl::StrContainsIgnoreCase("", 'a'));
+  EXPECT_FALSE(absl::StrContainsIgnoreCase("", 'A'));
+  EXPECT_FALSE(absl::StrContainsIgnoreCase("", '0'));
+}
+
 }  // namespace
diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc
index 2987158..c2b861a 100644
--- a/absl/strings/numbers.cc
+++ b/absl/strings/numbers.cc
@@ -219,7 +219,7 @@
   if (i < 0) {
     *buffer++ = '-';
     // We need to do the negation in modular (i.e., "unsigned")
-    // arithmetic; MSVC++ apprently warns for plain "-u", so
+    // arithmetic; MSVC++ apparently warns for plain "-u", so
     // we write the equivalent expression "0 - u" instead.
     u = 0 - u;
   }
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc
index 6981347..6c198f8 100644
--- a/absl/strings/str_cat.cc
+++ b/absl/strings/str_cat.cc
@@ -30,88 +30,6 @@
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
-namespace strings_internal {
-void StringifySink::Append(size_t count, char ch) { buffer_.append(count, ch); }
-
-void StringifySink::Append(string_view v) {
-  buffer_.append(v.data(), v.size());
-}
-
-bool StringifySink::PutPaddedString(string_view v, int width, int precision,
-                                    bool left) {
-  size_t space_remaining = 0;
-
-  if (width >= 0) space_remaining = static_cast<size_t>(width);
-
-  size_t n = v.size();
-
-  if (precision >= 0) n = (std::min)(n, static_cast<size_t>(precision));
-
-  string_view shown(v.data(), n);
-
-  if (shown.size() < space_remaining) {
-    space_remaining = space_remaining - shown.size();
-  } else {
-    space_remaining = 0;
-  }
-
-  if (!left) Append(space_remaining, ' ');
-  Append(shown);
-  if (left) Append(space_remaining, ' ');
-  return true;
-}
-
-}  // namespace strings_internal
-
-AlphaNum::AlphaNum(Hex hex) {
-  static_assert(numbers_internal::kFastToBufferSize >= 32,
-                "This function only works when output buffer >= 32 bytes long");
-  char* const end = &digits_[numbers_internal::kFastToBufferSize];
-  auto real_width =
-      absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16);
-  if (real_width >= hex.width) {
-    piece_ = absl::string_view(end - real_width, real_width);
-  } else {
-    // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and
-    // max pad width can be up to 20.
-    std::memset(end - 32, hex.fill, 16);
-    // Patch up everything else up to the real_width.
-    std::memset(end - real_width - 16, hex.fill, 16);
-    piece_ = absl::string_view(end - hex.width, hex.width);
-  }
-}
-
-AlphaNum::AlphaNum(Dec dec) {
-  assert(dec.width <= numbers_internal::kFastToBufferSize);
-  char* const end = &digits_[numbers_internal::kFastToBufferSize];
-  char* const minfill = end - dec.width;
-  char* writer = end;
-  uint64_t value = dec.value;
-  bool neg = dec.neg;
-  while (value > 9) {
-    *--writer = '0' + (value % 10);
-    value /= 10;
-  }
-  *--writer = '0' + static_cast<char>(value);
-  if (neg) *--writer = '-';
-
-  ptrdiff_t fillers = writer - minfill;
-  if (fillers > 0) {
-    // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
-    // But...: if the fill character is '0', then it's <+/-><fill><digits>
-    bool add_sign_again = false;
-    if (neg && dec.fill == '0') {  // If filling with '0',
-      ++writer;                    // ignore the sign we just added
-      add_sign_again = true;       // and re-add the sign later.
-    }
-    writer -= fillers;
-    std::fill_n(writer, fillers, dec.fill);
-    if (add_sign_again) *--writer = '-';
-  }
-
-  piece_ = absl::string_view(writer, static_cast<size_t>(end - writer));
-}
-
 // ----------------------------------------------------------------------
 // StrCat()
 //    This merges the given strings or integers, with no delimiter. This
@@ -177,12 +95,12 @@
 std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
   std::string result;
   size_t total_size = 0;
-  for (const absl::string_view& piece : pieces) total_size += piece.size();
+  for (absl::string_view piece : pieces) total_size += piece.size();
   strings_internal::STLStringResizeUninitialized(&result, total_size);
 
   char* const begin = &result[0];
   char* out = begin;
-  for (const absl::string_view& piece : pieces) {
+  for (absl::string_view piece : pieces) {
     const size_t this_size = piece.size();
     if (this_size != 0) {
       memcpy(out, piece.data(), this_size);
@@ -206,7 +124,7 @@
                   std::initializer_list<absl::string_view> pieces) {
   size_t old_size = dest->size();
   size_t total_size = old_size;
-  for (const absl::string_view& piece : pieces) {
+  for (absl::string_view piece : pieces) {
     ASSERT_NO_OVERLAP(*dest, piece);
     total_size += piece.size();
   }
@@ -214,7 +132,7 @@
 
   char* const begin = &(*dest)[0];
   char* out = begin + old_size;
-  for (const absl::string_view& piece : pieces) {
+  for (absl::string_view piece : pieces) {
     const size_t this_size = piece.size();
     if (this_size != 0) {
       memcpy(out, piece.data(), this_size);
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h
index 6ee88f1..fcd48c4 100644
--- a/absl/strings/str_cat.h
+++ b/absl/strings/str_cat.h
@@ -48,19 +48,58 @@
 // `StrCat()` or `StrAppend()`. You may specify a minimum hex field width using
 // a `PadSpec` enum.
 //
+// User-defined types can be formatted with the `AbslStringify()` customization
+// point. The API relies on detecting an overload in the user-defined type's
+// namespace of a free (non-member) `AbslStringify()` function as a definition
+// (typically declared as a friend and implemented in-line.
+// with the following signature:
+//
+// class MyClass { ... };
+//
+// template <typename Sink>
+// void AbslStringify(Sink& sink, const MyClass& value);
+//
+// An `AbslStringify()` overload for a type should only be declared in the same
+// file and namespace as said type.
+//
+// Note that `AbslStringify()` also supports use with `absl::StrFormat()` and
+// `absl::Substitute()`.
+//
+// Example:
+//
+// struct Point {
+//   // To add formatting support to `Point`, we simply need to add a free
+//   // (non-member) function `AbslStringify()`. This method specifies how
+//   // Point should be printed when absl::StrCat() is called on it. You can add
+//   // such a free function using a friend declaration within the body of the
+//   // class. The sink parameter is a templated type to avoid requiring
+//   // dependencies.
+//   template <typename Sink> friend void AbslStringify(Sink&
+//   sink, const Point& p) {
+//     absl::Format(&sink, "(%v, %v)", p.x, p.y);
+//   }
+//
+//   int x;
+//   int y;
+// };
 // -----------------------------------------------------------------------------
 
 #ifndef ABSL_STRINGS_STR_CAT_H_
 #define ABSL_STRINGS_STR_CAT_H_
 
+#include <algorithm>
 #include <array>
 #include <cstdint>
+#include <cstring>
 #include <string>
 #include <type_traits>
 #include <utility>
 #include <vector>
 
+#include "absl/base/attributes.h"
 #include "absl/base/port.h"
+#include "absl/strings/internal/has_absl_stringify.h"
+#include "absl/strings/internal/stringify_sink.h"
 #include "absl/strings/numbers.h"
 #include "absl/strings/string_view.h"
 
@@ -77,32 +116,6 @@
   size_t size;
 };
 
-class StringifySink {
- public:
-  void Append(size_t count, char ch);
-
-  void Append(string_view v);
-
-  bool PutPaddedString(string_view v, int width, int precision, bool left);
-
-  // Support `absl::Format(&sink, format, args...)`.
-  friend void AbslFormatFlush(StringifySink* sink, absl::string_view v) {
-    sink->Append(v);
-  }
-
-  template <typename T>
-  friend string_view ExtractStringification(StringifySink& sink, const T& v);
-
- private:
-  std::string buffer_;
-};
-
-template <typename T>
-string_view ExtractStringification(StringifySink& sink, const T& v) {
-  AbslStringify(sink, v);
-  return sink.buffer_;
-}
-
 }  // namespace strings_internal
 
 // Enum that specifies the number of significant digits to return in a `Hex` or
@@ -191,6 +204,27 @@
   explicit Hex(Pointee* v, PadSpec spec = absl::kNoPad)
       : Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
 
+  template <typename S>
+  friend void AbslStringify(S& sink, Hex hex) {
+    static_assert(
+        numbers_internal::kFastToBufferSize >= 32,
+        "This function only works when output buffer >= 32 bytes long");
+    char buffer[numbers_internal::kFastToBufferSize];
+    char* const end = &buffer[numbers_internal::kFastToBufferSize];
+    auto real_width =
+        absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16);
+    if (real_width >= hex.width) {
+      sink.Append(absl::string_view(end - real_width, real_width));
+    } else {
+      // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and
+      // max pad width can be up to 20.
+      std::memset(end - 32, hex.fill, 16);
+      // Patch up everything else up to the real_width.
+      std::memset(end - real_width - 16, hex.fill, 16);
+      sink.Append(absl::string_view(end - hex.width, hex.width));
+    }
+  }
+
  private:
   Hex(PadSpec spec, uint64_t v)
       : value(v),
@@ -225,6 +259,38 @@
                                              : spec - absl::kZeroPad2 + 2),
         fill(spec >= absl::kSpacePad2 ? ' ' : '0'),
         neg(v < 0) {}
+
+  template <typename S>
+  friend void AbslStringify(S& sink, Dec dec) {
+    assert(dec.width <= numbers_internal::kFastToBufferSize);
+    char buffer[numbers_internal::kFastToBufferSize];
+    char* const end = &buffer[numbers_internal::kFastToBufferSize];
+    char* const minfill = end - dec.width;
+    char* writer = end;
+    uint64_t val = dec.value;
+    while (val > 9) {
+      *--writer = '0' + (val % 10);
+      val /= 10;
+    }
+    *--writer = '0' + static_cast<char>(val);
+    if (dec.neg) *--writer = '-';
+
+    ptrdiff_t fillers = writer - minfill;
+    if (fillers > 0) {
+      // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
+      // But...: if the fill character is '0', then it's <+/-><fill><digits>
+      bool add_sign_again = false;
+      if (dec.neg && dec.fill == '0') {  // If filling with '0',
+        ++writer;                    // ignore the sign we just added
+        add_sign_again = true;       // and re-add the sign later.
+      }
+      writer -= fillers;
+      std::fill_n(writer, fillers, dec.fill);
+      if (add_sign_again) *--writer = '-';
+    }
+
+    sink.Append(absl::string_view(writer, static_cast<size_t>(end - writer)));
+  }
 };
 
 // -----------------------------------------------------------------------------
@@ -232,17 +298,10 @@
 // -----------------------------------------------------------------------------
 //
 // The `AlphaNum` class acts as the main parameter type for `StrCat()` and
-// `StrAppend()`, providing efficient conversion of numeric, boolean, and
-// hexadecimal values (through the `Hex` type) into strings.
-
-template <typename T, typename = void>
-struct HasAbslStringify : std::false_type {};
-
-template <typename T>
-struct HasAbslStringify<T, std::enable_if_t<std::is_void<decltype(AbslStringify(
-                               std::declval<strings_internal::StringifySink&>(),
-                               std::declval<const T&>()))>::value>>
-    : std::true_type {};
+// `StrAppend()`, providing efficient conversion of numeric, boolean, decimal,
+// and hexadecimal values (through the `Dec` and `Hex` types) into strings.
+// `AlphaNum` should only be used as a function parameter. Do not instantiate
+//  `AlphaNum` directly as a stack variable.
 
 class AlphaNum {
  public:
@@ -279,28 +338,30 @@
   AlphaNum(double f)  // NOLINT(runtime/explicit)
       : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
 
-  AlphaNum(Hex hex);  // NOLINT(runtime/explicit)
-  AlphaNum(Dec dec);  // NOLINT(runtime/explicit)
-
   template <size_t size>
   AlphaNum(  // NOLINT(runtime/explicit)
-      const strings_internal::AlphaNumBuffer<size>& buf)
+      const strings_internal::AlphaNumBuffer<size>& buf
+          ABSL_ATTRIBUTE_LIFETIME_BOUND)
       : piece_(&buf.data[0], buf.size) {}
 
-  AlphaNum(const char* c_str)                     // NOLINT(runtime/explicit)
-      : piece_(NullSafeStringView(c_str)) {}      // NOLINT(runtime/explicit)
-  AlphaNum(absl::string_view pc) : piece_(pc) {}  // NOLINT(runtime/explicit)
+  AlphaNum(const char* c_str  // NOLINT(runtime/explicit)
+               ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      : piece_(NullSafeStringView(c_str)) {}
+  AlphaNum(absl::string_view pc  // NOLINT(runtime/explicit)
+               ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      : piece_(pc) {}
 
   template <typename T, typename = typename std::enable_if<
-                            HasAbslStringify<T>::value>::type>
-  AlphaNum(                                         // NOLINT(runtime/explicit)
-      const T& v,                                   // NOLINT(runtime/explicit)
-      strings_internal::StringifySink&& sink = {})  // NOLINT(runtime/explicit)
+                            strings_internal::HasAbslStringify<T>::value>::type>
+  AlphaNum(  // NOLINT(runtime/explicit)
+      const T& v ABSL_ATTRIBUTE_LIFETIME_BOUND,
+      strings_internal::StringifySink&& sink ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
       : piece_(strings_internal::ExtractStringification(sink, v)) {}
 
   template <typename Allocator>
   AlphaNum(  // NOLINT(runtime/explicit)
-      const std::basic_string<char, std::char_traits<char>, Allocator>& str)
+      const std::basic_string<char, std::char_traits<char>, Allocator>& str
+          ABSL_ATTRIBUTE_LIFETIME_BOUND)
       : piece_(str) {}
 
   // Use string literals ":" instead of character literals ':'.
@@ -317,7 +378,8 @@
   // This overload matches only scoped enums.
   template <typename T,
             typename = typename std::enable_if<
-                std::is_enum<T>{} && !std::is_convertible<T, int>{}>::type>
+                std::is_enum<T>{} && !std::is_convertible<T, int>{} &&
+                !strings_internal::HasAbslStringify<T>::value>::type>
   AlphaNum(T e)  // NOLINT(runtime/explicit)
       : AlphaNum(static_cast<typename std::underlying_type<T>::type>(e)) {}
 
diff --git a/absl/strings/str_cat_test.cc b/absl/strings/str_cat_test.cc
index 1b3b7ec..2d74245 100644
--- a/absl/strings/str_cat_test.cc
+++ b/absl/strings/str_cat_test.cc
@@ -443,7 +443,7 @@
   EXPECT_EQ(result, "12345");
 }
 
-#ifdef GTEST_HAS_DEATH_TEST
+#if GTEST_HAS_DEATH_TEST
 TEST(StrAppend, Death) {
   std::string s = "self";
   // on linux it's "assertion", on mac it's "Assertion",
@@ -650,4 +650,16 @@
   EXPECT_EQ(absl::StrCat("a ", p, " z"), "a (10, 20) z");
 }
 
+enum class EnumWithStringify { Many = 0, Choices = 1 };
+
+template <typename Sink>
+void AbslStringify(Sink& sink, EnumWithStringify e) {
+  absl::Format(&sink, "%s", e == EnumWithStringify::Many ? "Many" : "Choices");
+}
+
+TEST(StrCat, AbslStringifyWithEnum) {
+  const auto e = EnumWithStringify::Choices;
+  EXPECT_EQ(absl::StrCat(e), "Choices");
+}
+
 }  // namespace
diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h
index ffbcb9a..fc4bf39 100644
--- a/absl/strings/str_format.h
+++ b/absl/strings/str_format.h
@@ -36,10 +36,12 @@
 //   * `absl::StreamFormat()` to more efficiently write a format string to a
 //     stream, such as`std::cout`.
 //   * `absl::PrintF()`, `absl::FPrintF()` and `absl::SNPrintF()` as
-//     replacements for `std::printf()`, `std::fprintf()` and `std::snprintf()`.
+//     drop-in replacements for `std::printf()`, `std::fprintf()` and
+//     `std::snprintf()`.
 //
-//     Note: a version of `std::sprintf()` is not supported as it is
-//     generally unsafe due to buffer overflows.
+//     Note: An `absl::SPrintF()` drop-in replacement is not supported as it
+//     is generally unsafe due to buffer overflows. Use `absl::StrFormat` which
+//     returns the string as output instead of expecting a pre-allocated buffer.
 //
 // Additionally, you can provide a format string (and its associated arguments)
 // using one of the following abstractions:
@@ -191,9 +193,9 @@
 //   absl::StrFormat(formatString, "TheVillage", 6);
 //
 // A format string generally follows the POSIX syntax as used within the POSIX
-// `printf` specification.
+// `printf` specification. (Exceptions are noted below.)
 //
-// (See http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html.)
+// (See http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html)
 //
 // In specific, the `FormatSpec` supports the following type specifiers:
 //   * `c` for characters
@@ -211,6 +213,10 @@
 //   * `n` for the special case of writing out the number of characters
 //     written to this point. The resulting value must be captured within an
 //     `absl::FormatCountCapture` type.
+//   * `v` for values using the default format for a deduced type. These deduced
+//     types include many of the primitive types denoted here as well as
+//     user-defined types containing the proper extensions. (See below for more
+//     information.)
 //
 // Implementation-defined behavior:
 //   * A null pointer provided to "%s" or "%p" is output as "(nil)".
@@ -239,6 +245,15 @@
 //         "%s%d%n", "hello", 123, absl::FormatCountCapture(&n));
 //     EXPECT_EQ(8, n);
 //
+// NOTE: the `v` specifier (for "value") is a type specifier not present in the
+// POSIX specification. %v will format values according to their deduced type.
+// `v` uses `d` for signed integer values, `u` for unsigned integer values, `g`
+// for floating point values, and formats boolean values as "true"/"false"
+// (instead of 1 or 0 for booleans formatted using d). `const char*` is not
+// supported; please use `std:string` and `string_view`. `char` is also not
+// supported due to ambiguity of the type. This specifier does not support
+// modifiers.
+//
 // The `FormatSpec` intrinsically supports all of these fundamental C++ types:
 //
 // *   Characters: `char`, `signed char`, `unsigned char`
@@ -570,6 +585,41 @@
 // StrFormat Extensions
 //------------------------------------------------------------------------------
 //
+// AbslStringify()
+//
+// A simpler customization API for formatting user-defined types using
+// absl::StrFormat(). The API relies on detecting an overload in the
+// user-defined type's namespace of a free (non-member) `AbslStringify()`
+// function as a friend definition with the following signature:
+//
+// template <typename Sink>
+// void AbslStringify(Sink& sink, const X& value);
+//
+// An `AbslStringify()` overload for a type should only be declared in the same
+// file and namespace as said type.
+//
+// Note that unlike with AbslFormatConvert(), AbslStringify() does not allow
+// customization of allowed conversion characters. AbslStringify() uses `%v` as
+// the underlying conversion specififer. Additionally, AbslStringify() supports
+// use with absl::StrCat while AbslFormatConvert() does not.
+//
+// Example:
+//
+// struct Point {
+//   // To add formatting support to `Point`, we simply need to add a free
+//   // (non-member) function `AbslStringify()`. This method prints in the
+//   // request format using the underlying `%v` specifier. You can add such a
+//   // free function using a friend declaration within the body of the class.
+//   // The sink parameter is a templated type to avoid requiring dependencies.
+//   template <typename Sink>
+//   friend void AbslStringify(Sink& sink, const Point& p) {
+//     absl::Format(&sink, "(%v, %v)", p.x, p.y);
+//   }
+//
+//   int x;
+//   int y;
+// };
+//
 // AbslFormatConvert()
 //
 // The StrFormat library provides a customization API for formatting
@@ -616,9 +666,9 @@
 //   AbslFormatConvert(const Point& p, const absl::FormatConversionSpec& spec,
 //                     absl::FormatSink* s) {
 //     if (spec.conversion_char() == absl::FormatConversionChar::s) {
-//       s->Append(absl::StrCat("x=", p.x, " y=", p.y));
+//       absl::Format(s, "x=%vy=%v", p.x, p.y);
 //     } else {
-//       s->Append(absl::StrCat(p.x, ",", p.y));
+//       absl::Format(s, "%v,%v", p.x, p.y);
 //     }
 //     return {true};
 //   }
@@ -772,17 +822,25 @@
 
 // FormatSink
 //
-// An abstraction to which conversions write their string data.
+// A format sink is a generic abstraction to which conversions may write their
+// formatted string data. `absl::FormatConvert()` uses this sink to write its
+// formatted string.
 //
 class FormatSink {
  public:
-  // Appends `count` copies of `ch`.
+  // FormatSink::Append()
+  //
+  // Appends `count` copies of `ch` to the format sink.
   void Append(size_t count, char ch) { sink_->Append(count, ch); }
 
+  // Overload of FormatSink::Append() for appending the characters of a string
+  // view to a format sink.
   void Append(string_view v) { sink_->Append(v); }
 
-  // Appends the first `precision` bytes of `v`. If this is less than
-  // `width`, spaces will be appended first (if `left` is false), or
+  // FormatSink::PutPaddedString()
+  //
+  // Appends `precision` number of bytes of `v` to the format sink. If this is
+  // less than `width`, spaces will be appended first (if `left` is false), or
   // after (if `left` is true) to ensure the total amount appended is
   // at least `width`.
   bool PutPaddedString(string_view v, int width, int precision, bool left) {
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc
index 62ed262..5198fb3 100644
--- a/absl/strings/str_format_test.cc
+++ b/absl/strings/str_format_test.cc
@@ -143,13 +143,20 @@
 }
 
 TEST_F(FormatEntryPointTest, ManyArgs) {
-  EXPECT_EQ("24", StrFormat("%24$d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
-                            14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24));
-  EXPECT_EQ("60", StrFormat("%60$d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
-                            14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
-                            27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
-                            40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
-                            53, 54, 55, 56, 57, 58, 59, 60));
+  EXPECT_EQ(
+      "60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 "
+      "36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 "
+      "12 11 10 9 8 7 6 5 4 3 2 1",
+      StrFormat("%60$d %59$d %58$d %57$d %56$d %55$d %54$d %53$d %52$d %51$d "
+                "%50$d %49$d %48$d %47$d %46$d %45$d %44$d %43$d %42$d %41$d "
+                "%40$d %39$d %38$d %37$d %36$d %35$d %34$d %33$d %32$d %31$d "
+                "%30$d %29$d %28$d %27$d %26$d %25$d %24$d %23$d %22$d %21$d "
+                "%20$d %19$d %18$d %17$d %16$d %15$d %14$d %13$d %12$d %11$d "
+                "%10$d %9$d %8$d %7$d %6$d %5$d %4$d %3$d %2$d %1$d",
+                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+                19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+                35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+                51, 52, 53, 54, 55, 56, 57, 58, 59, 60));
 }
 
 TEST_F(FormatEntryPointTest, Preparsed) {
@@ -1135,6 +1142,51 @@
   EXPECT_EQ(absl::StrFormat("a %v z", p), "a (10, 20) z");
 }
 
+enum class EnumClassWithStringify { Many = 0, Choices = 1 };
+
+template <typename Sink>
+void AbslStringify(Sink& sink, EnumClassWithStringify e) {
+  absl::Format(&sink, "%s",
+               e == EnumClassWithStringify::Many ? "Many" : "Choices");
+}
+
+enum EnumWithStringify { Many, Choices };
+
+template <typename Sink>
+void AbslStringify(Sink& sink, EnumWithStringify e) {
+  absl::Format(&sink, "%s", e == EnumWithStringify::Many ? "Many" : "Choices");
+}
+
+TEST_F(FormatExtensionTest, AbslStringifyWithEnumWithV) {
+  const auto e_class = EnumClassWithStringify::Choices;
+  EXPECT_EQ(absl::StrFormat("My choice is %v", e_class),
+            "My choice is Choices");
+
+  const auto e = EnumWithStringify::Choices;
+  EXPECT_EQ(absl::StrFormat("My choice is %v", e), "My choice is Choices");
+}
+
+TEST_F(FormatExtensionTest, AbslStringifyEnumWithD) {
+  const auto e_class = EnumClassWithStringify::Many;
+  EXPECT_EQ(absl::StrFormat("My choice is %d", e_class), "My choice is 0");
+
+  const auto e = EnumWithStringify::Choices;
+  EXPECT_EQ(absl::StrFormat("My choice is %d", e), "My choice is 1");
+}
+
+enum class EnumWithLargerValue { x = 32 };
+
+template <typename Sink>
+void AbslStringify(Sink& sink, EnumWithLargerValue e) {
+  absl::Format(&sink, "%s", "Many");
+}
+
+TEST_F(FormatExtensionTest, AbslStringifyEnumOtherSpecifiers) {
+  const auto e = EnumWithLargerValue::x;
+  EXPECT_EQ(absl::StrFormat("My choice is %g", e), "My choice is 32");
+  EXPECT_EQ(absl::StrFormat("My choice is %x", e), "My choice is 20");
+}
+
 }  // namespace
 
 // Some codegen thunks that we can use to easily dump the generated assembly for
diff --git a/absl/strings/str_split.cc b/absl/strings/str_split.cc
index e08c26b..72ba7c0 100644
--- a/absl/strings/str_split.cc
+++ b/absl/strings/str_split.cc
@@ -60,19 +60,23 @@
 // Finds using absl::string_view::find(), therefore the length of the found
 // delimiter is delimiter.length().
 struct LiteralPolicy {
-  size_t Find(absl::string_view text, absl::string_view delimiter, size_t pos) {
+  static size_t Find(absl::string_view text, absl::string_view delimiter,
+                     size_t pos) {
     return text.find(delimiter, pos);
   }
-  size_t Length(absl::string_view delimiter) { return delimiter.length(); }
+  static size_t Length(absl::string_view delimiter) {
+    return delimiter.length();
+  }
 };
 
 // Finds using absl::string_view::find_first_of(), therefore the length of the
 // found delimiter is 1.
 struct AnyOfPolicy {
-  size_t Find(absl::string_view text, absl::string_view delimiter, size_t pos) {
+  static size_t Find(absl::string_view text, absl::string_view delimiter,
+                     size_t pos) {
     return text.find_first_of(delimiter, pos);
   }
-  size_t Length(absl::string_view /* delimiter */) { return 1; }
+  static size_t Length(absl::string_view /* delimiter */) { return 1; }
 };
 
 }  // namespace
@@ -123,8 +127,7 @@
   ABSL_RAW_CHECK(length > 0, "");
 }
 
-absl::string_view ByLength::Find(absl::string_view text,
-                                      size_t pos) const {
+absl::string_view ByLength::Find(absl::string_view text, size_t pos) const {
   pos = std::min(pos, text.size());  // truncate `pos`
   absl::string_view substr = text.substr(pos);
   // If the string is shorter than the chunk size we say we
diff --git a/absl/strings/str_split_test.cc b/absl/strings/str_split_test.cc
index 1b4427b..04a64a4 100644
--- a/absl/strings/str_split_test.cc
+++ b/absl/strings/str_split_test.cc
@@ -369,7 +369,7 @@
 TEST(Splitter, RangeIterators) {
   auto splitter = absl::StrSplit("a,b,c", ',');
   std::vector<absl::string_view> output;
-  for (const absl::string_view& p : splitter) {
+  for (absl::string_view p : splitter) {
     output.push_back(p);
   }
   EXPECT_THAT(output, ElementsAre("a", "b", "c"));
diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h
index 692fd03..d6a5a69 100644
--- a/absl/strings/substitute.h
+++ b/absl/strings/substitute.h
@@ -55,6 +55,8 @@
 //   * bool (Printed as "true" or "false")
 //   * pointer types other than char* (Printed as "0x<lower case hex string>",
 //     except that null is printed as "NULL")
+//   * user-defined types via the `AbslStringify()` customization point. See the
+//     documentation for `absl::StrCat` for an explanation on how to use this.
 //
 // If an invalid format string is provided, Substitute returns an empty string
 // and SubstituteAndAppend does not change the provided output string.
@@ -79,6 +81,7 @@
 #include "absl/base/port.h"
 #include "absl/strings/ascii.h"
 #include "absl/strings/escaping.h"
+#include "absl/strings/internal/stringify_sink.h"
 #include "absl/strings/numbers.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_split.h"
@@ -102,14 +105,14 @@
   // Overloads for string-y things
   //
   // Explicitly overload `const char*` so the compiler doesn't cast to `bool`.
-  Arg(const char* value)  // NOLINT(runtime/explicit)
+  Arg(const char* value)  // NOLINT(google-explicit-constructor)
       : piece_(absl::NullSafeStringView(value)) {}
   template <typename Allocator>
   Arg(  // NOLINT
       const std::basic_string<char, std::char_traits<char>, Allocator>&
           value) noexcept
       : piece_(value) {}
-  Arg(absl::string_view value)  // NOLINT(runtime/explicit)
+  Arg(absl::string_view value)  // NOLINT(google-explicit-constructor)
       : piece_(value) {}
 
   // Overloads for primitives
@@ -119,7 +122,7 @@
   // probably using them as 8-bit integers and would probably prefer an integer
   // representation. However, we can't really know, so we make the caller decide
   // what to do.
-  Arg(char value)  // NOLINT(runtime/explicit)
+  Arg(char value)  // NOLINT(google-explicit-constructor)
       : piece_(scratch_, 1) {
     scratch_[0] = value;
   }
@@ -133,12 +136,12 @@
                static_cast<size_t>(
                    numbers_internal::FastIntToBuffer(value, scratch_) -
                    scratch_)) {}
-  Arg(int value)  // NOLINT(runtime/explicit)
+  Arg(int value)  // NOLINT(google-explicit-constructor)
       : piece_(scratch_,
                static_cast<size_t>(
                    numbers_internal::FastIntToBuffer(value, scratch_) -
                    scratch_)) {}
-  Arg(unsigned int value)  // NOLINT(runtime/explicit)
+  Arg(unsigned int value)  // NOLINT(google-explicit-constructor)
       : piece_(scratch_,
                static_cast<size_t>(
                    numbers_internal::FastIntToBuffer(value, scratch_) -
@@ -163,17 +166,23 @@
                static_cast<size_t>(
                    numbers_internal::FastIntToBuffer(value, scratch_) -
                    scratch_)) {}
-  Arg(float value)  // NOLINT(runtime/explicit)
+  Arg(float value)  // NOLINT(google-explicit-constructor)
       : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) {
   }
-  Arg(double value)  // NOLINT(runtime/explicit)
+  Arg(double value)  // NOLINT(google-explicit-constructor)
       : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) {
   }
-  Arg(bool value)  // NOLINT(runtime/explicit)
+  Arg(bool value)  // NOLINT(google-explicit-constructor)
       : piece_(value ? "true" : "false") {}
 
-  Arg(Hex hex);  // NOLINT(runtime/explicit)
-  Arg(Dec dec);  // NOLINT(runtime/explicit)
+  template <typename T, typename = typename std::enable_if<
+                            strings_internal::HasAbslStringify<T>::value>::type>
+  Arg(  // NOLINT(google-explicit-constructor)
+      const T& v, strings_internal::StringifySink&& sink = {})
+      : piece_(strings_internal::ExtractStringification(sink, v)) {}
+
+  Arg(Hex hex);  // NOLINT(google-explicit-constructor)
+  Arg(Dec dec);  // NOLINT(google-explicit-constructor)
 
   // vector<bool>::reference and const_reference require special help to convert
   // to `Arg` because it requires two user defined conversions.
@@ -188,13 +197,14 @@
 
   // `void*` values, with the exception of `char*`, are printed as
   // "0x<hex value>". However, in the case of `nullptr`, "NULL" is printed.
-  Arg(const void* value);  // NOLINT(runtime/explicit)
+  Arg(const void* value);  // NOLINT(google-explicit-constructor)
 
   // Normal enums are already handled by the integer formatters.
   // This overload matches only scoped enums.
   template <typename T,
             typename = typename std::enable_if<
-                std::is_enum<T>{} && !std::is_convertible<T, int>{}>::type>
+                std::is_enum<T>{} && !std::is_convertible<T, int>{} &&
+                !strings_internal::HasAbslStringify<T>::value>::type>
   Arg(T value)  // NOLINT(google-explicit-constructor)
       : Arg(static_cast<typename std::underlying_type<T>::type>(value)) {}
 
diff --git a/absl/strings/substitute_test.cc b/absl/strings/substitute_test.cc
index 9e6b940..ecf78d6 100644
--- a/absl/strings/substitute_test.cc
+++ b/absl/strings/substitute_test.cc
@@ -22,6 +22,16 @@
 
 namespace {
 
+struct MyStruct {
+  template <typename Sink>
+  friend void AbslStringify(Sink& sink, const MyStruct& s) {
+    sink.Append("MyStruct{.value = ");
+    sink.Append(absl::StrCat(s.value));
+    sink.Append("}");
+  }
+  int value;
+};
+
 TEST(SubstituteTest, Substitute) {
   // Basic.
   EXPECT_EQ("Hello, world!", absl::Substitute("$0, $1!", "Hello", "world"));
@@ -70,7 +80,7 @@
   // Volatile Pointer.
   // Like C++ streamed I/O, such pointers implicitly become bool
   volatile int vol = 237;
-  volatile int *volatile volptr = &vol;
+  volatile int* volatile volptr = &vol;
   str = absl::Substitute("$0", volptr);
   EXPECT_EQ("true", str);
 
@@ -128,6 +138,11 @@
 
   const char* null_cstring = nullptr;
   EXPECT_EQ("Text: ''", absl::Substitute("Text: '$0'", null_cstring));
+
+  MyStruct s1 = MyStruct{17};
+  MyStruct s2 = MyStruct{1043};
+  EXPECT_EQ("MyStruct{.value = 17}, MyStruct{.value = 1043}",
+            absl::Substitute("$0, $1", s1, s2));
 }
 
 TEST(SubstituteTest, SubstituteAndAppend) {
@@ -171,6 +186,12 @@
   absl::SubstituteAndAppend(&str, "$0 $1 $2 $3 $4 $5 $6 $7 $8 $9", "a", "b",
                             "c", "d", "e", "f", "g", "h", "i", "j");
   EXPECT_EQ("a b c d e f g h i j", str);
+
+  str.clear();
+  MyStruct s1 = MyStruct{17};
+  MyStruct s2 = MyStruct{1043};
+  absl::SubstituteAndAppend(&str, "$0, $1", s1, s2);
+  EXPECT_EQ("MyStruct{.value = 17}, MyStruct{.value = 1043}", str);
 }
 
 TEST(SubstituteTest, VectorBoolRef) {
@@ -232,7 +253,19 @@
                                         ScopedEnumUInt16::kEnum1));
 }
 
-#ifdef GTEST_HAS_DEATH_TEST
+enum class EnumWithStringify { Many = 0, Choices = 1 };
+
+template <typename Sink>
+void AbslStringify(Sink& sink, EnumWithStringify e) {
+  sink.Append(e == EnumWithStringify::Many ? "Many" : "Choices");
+}
+
+TEST(SubstituteTest, AbslStringifyWithEnum) {
+  const auto e = EnumWithStringify::Choices;
+  EXPECT_EQ(absl::Substitute("$0", e), "Choices");
+}
+
+#if GTEST_HAS_DEATH_TEST
 
 TEST(SubstituteDeathTest, SubstituteDeath) {
   EXPECT_DEBUG_DEATH(
diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel
index 3a37816..5074044 100644
--- a/absl/synchronization/BUILD.bazel
+++ b/absl/synchronization/BUILD.bazel
@@ -53,6 +53,7 @@
 
 cc_library(
     name = "kernel_timeout_internal",
+    srcs = ["internal/kernel_timeout.cc"],
     hdrs = ["internal/kernel_timeout.h"],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
@@ -60,20 +61,42 @@
         "//absl/synchronization:__pkg__",
     ],
     deps = [
+        "//absl/base",
+        "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:raw_logging_internal",
         "//absl/time",
     ],
 )
 
+cc_test(
+    name = "kernel_timeout_internal_test",
+    srcs = ["internal/kernel_timeout_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    flaky = 1,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":kernel_timeout_internal",
+        "//absl/base:config",
+        "//absl/random",
+        "//absl/time",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
 cc_library(
     name = "synchronization",
     srcs = [
         "barrier.cc",
         "blocking_counter.cc",
         "internal/create_thread_identity.cc",
+        "internal/futex_waiter.cc",
         "internal/per_thread_sem.cc",
-        "internal/waiter.cc",
+        "internal/pthread_waiter.cc",
+        "internal/sem_waiter.cc",
+        "internal/stdcpp_waiter.cc",
+        "internal/waiter_base.cc",
+        "internal/win32_waiter.cc",
         "mutex.cc",
         "notification.cc",
     ],
@@ -82,8 +105,14 @@
         "blocking_counter.h",
         "internal/create_thread_identity.h",
         "internal/futex.h",
+        "internal/futex_waiter.h",
         "internal/per_thread_sem.h",
+        "internal/pthread_waiter.h",
+        "internal/sem_waiter.h",
+        "internal/stdcpp_waiter.h",
         "internal/waiter.h",
+        "internal/waiter_base.h",
+        "internal/win32_waiter.h",
         "mutex.h",
         "notification.h",
     ],
@@ -97,8 +126,8 @@
     deps = [
         ":graphcycles_internal",
         ":kernel_timeout_internal",
-        "//absl/base:atomic_hook",
         "//absl/base",
+        "//absl/base:atomic_hook",
         "//absl/base:base_internal",
         "//absl/base:config",
         "//absl/base:core_headers",
@@ -200,6 +229,7 @@
     deps = [
         ":synchronization",
         "//absl/base:core_headers",
+        "//absl/functional:any_invocable",
     ],
 )
 
@@ -208,6 +238,7 @@
     size = "large",
     srcs = ["mutex_test.cc"],
     copts = ABSL_TEST_COPTS,
+    flaky = 1,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     shard_count = 25,
     deps = [
@@ -223,6 +254,18 @@
     ],
 )
 
+cc_test(
+    name = "mutex_method_pointer_test",
+    srcs = ["mutex_method_pointer_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":synchronization",
+        "//absl/base:config",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
 cc_library(
     name = "mutex_benchmark_common",
     testonly = 1,
@@ -258,6 +301,7 @@
     size = "small",
     srcs = ["notification_test.cc"],
     copts = ABSL_TEST_COPTS,
+    flaky = 1,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     tags = ["no_test_lexan"],
     deps = [
@@ -286,7 +330,7 @@
 
 cc_test(
     name = "per_thread_sem_test",
-    size = "medium",
+    size = "large",
     copts = ABSL_TEST_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
     tags = [
@@ -302,6 +346,23 @@
 )
 
 cc_test(
+    name = "waiter_test",
+    srcs = ["internal/waiter_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    flaky = 1,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":kernel_timeout_internal",
+        ":synchronization",
+        ":thread_pool",
+        "//absl/base:config",
+        "//absl/random",
+        "//absl/time",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
     name = "lifetime_test",
     srcs = [
         "lifetime_test.cc",
diff --git a/absl/synchronization/CMakeLists.txt b/absl/synchronization/CMakeLists.txt
index 9335c26..8627834 100644
--- a/absl/synchronization/CMakeLists.txt
+++ b/absl/synchronization/CMakeLists.txt
@@ -39,14 +39,33 @@
     kernel_timeout_internal
   HDRS
     "internal/kernel_timeout.h"
+  SRCS
+    "internal/kernel_timeout.cc"
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
+    absl::base
+    absl::config
     absl::core_headers
     absl::raw_logging_internal
     absl::time
 )
 
+absl_cc_test(
+  NAME
+    kernel_timeout_internal_test
+  SRCS
+    "internal/kernel_timeout_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::kernel_timeout_internal
+    absl::config
+    absl::random_random
+    absl::time
+    GTest::gmock_main
+)
+
 absl_cc_library(
   NAME
     synchronization
@@ -55,16 +74,27 @@
     "blocking_counter.h"
     "internal/create_thread_identity.h"
     "internal/futex.h"
+    "internal/futex_waiter.h"
     "internal/per_thread_sem.h"
+    "internal/pthread_waiter.h"
+    "internal/sem_waiter.h"
+    "internal/stdcpp_waiter.h"
     "internal/waiter.h"
+    "internal/waiter_base.h"
+    "internal/win32_waiter.h"
     "mutex.h"
     "notification.h"
   SRCS
     "barrier.cc"
     "blocking_counter.cc"
     "internal/create_thread_identity.cc"
+    "internal/futex_waiter.cc"
     "internal/per_thread_sem.cc"
-    "internal/waiter.cc"
+    "internal/pthread_waiter.cc"
+    "internal/sem_waiter.cc"
+    "internal/stdcpp_waiter.cc"
+    "internal/waiter_base.cc"
+    "internal/win32_waiter.cc"
     "notification.cc"
     "mutex.cc"
   COPTS
@@ -136,8 +166,9 @@
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
-    absl::synchronization
+    absl::any_invocable
     absl::core_headers
+    absl::synchronization
   TESTONLY
 )
 
@@ -162,6 +193,20 @@
 
 absl_cc_test(
   NAME
+    mutex_method_pointer_test
+  SRCS
+    "mutex_method_pointer_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::base
+    absl::config
+    absl::synchronization
+    GTest::gmock_main
+)
+
+absl_cc_test(
+  NAME
     notification_test
   SRCS
     "notification_test.cc"
@@ -208,6 +253,23 @@
 
 absl_cc_test(
   NAME
+    waiter_test
+  SRCS
+    "internal/waiter_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::config
+    absl::kernel_timeout_internal
+    absl::random_random
+    absl::synchronization
+    absl::thread_pool
+    absl::time
+    GTest::gmock_main
+)
+
+absl_cc_test(
+  NAME
     lifetime_test
   SRCS
     "lifetime_test.cc"
diff --git a/absl/synchronization/internal/create_thread_identity.cc b/absl/synchronization/internal/create_thread_identity.cc
index 44e6129..f1ddbbb 100644
--- a/absl/synchronization/internal/create_thread_identity.cc
+++ b/absl/synchronization/internal/create_thread_identity.cc
@@ -17,6 +17,7 @@
 
 // This file is a no-op if the required LowLevelAlloc support is missing.
 #include "absl/base/internal/low_level_alloc.h"
+#include "absl/synchronization/internal/waiter.h"
 #ifndef ABSL_LOW_LEVEL_ALLOC_MISSING
 
 #include <string.h>
@@ -71,6 +72,9 @@
 
 void OneTimeInitThreadIdentity(base_internal::ThreadIdentity* identity) {
   PerThreadSem::Init(identity);
+  identity->ticker.store(0, std::memory_order_relaxed);
+  identity->wait_start.store(0, std::memory_order_relaxed);
+  identity->is_idle.store(false, std::memory_order_relaxed);
 }
 
 static void ResetThreadIdentityBetweenReuse(
diff --git a/absl/synchronization/internal/futex.h b/absl/synchronization/internal/futex.h
index cb97da0..573c01b 100644
--- a/absl/synchronization/internal/futex.h
+++ b/absl/synchronization/internal/futex.h
@@ -16,9 +16,7 @@
 
 #include "absl/base/config.h"
 
-#ifdef _WIN32
-#include <windows.h>
-#else
+#ifndef _WIN32
 #include <sys/time.h>
 #include <unistd.h>
 #endif
@@ -34,6 +32,7 @@
 
 #include <atomic>
 #include <cstdint>
+#include <limits>
 
 #include "absl/base/optimization.h"
 #include "absl/synchronization/internal/kernel_timeout.h"
@@ -81,51 +80,64 @@
 
 #if defined(SYS_futex_time64) && !defined(SYS_futex)
 #define SYS_futex SYS_futex_time64
+using FutexTimespec = struct timespec;
+#else
+// Some libc implementations have switched to an unconditional 64-bit `time_t`
+// definition. This means that `struct timespec` may not match the layout
+// expected by the kernel ABI on 32-bit platforms. So we define the
+// FutexTimespec that matches the kernel timespec definition. It should be safe
+// to use this struct for 64-bit userspace builds too, since it will use another
+// SYS_futex kernel call with 64-bit tv_sec inside timespec.
+struct FutexTimespec {
+  long tv_sec;   // NOLINT
+  long tv_nsec;  // NOLINT
+};
 #endif
 
 class FutexImpl {
  public:
-  static int WaitUntil(std::atomic<int32_t> *v, int32_t val,
-                       KernelTimeout t) {
-    long err = 0;  // NOLINT(runtime/int)
-    if (t.has_timeout()) {
-      // https://locklessinc.com/articles/futex_cheat_sheet/
-      // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time.
-      struct timespec abs_timeout = t.MakeAbsTimespec();
-      // Atomically check that the futex value is still 0, and if it
-      // is, sleep until abs_timeout or until woken by FUTEX_WAKE.
-      err = syscall(
-          SYS_futex, reinterpret_cast<int32_t *>(v),
-          FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, val,
-          &abs_timeout, nullptr, FUTEX_BITSET_MATCH_ANY);
-    } else {
-      // Atomically check that the futex value is still 0, and if it
-      // is, sleep until woken by FUTEX_WAKE.
-      err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v),
-                    FUTEX_WAIT | FUTEX_PRIVATE_FLAG, val, nullptr);
-    }
-    if (ABSL_PREDICT_FALSE(err != 0)) {
+  // Atomically check that `*v == val`, and if it is, then sleep until the until
+  // woken by `Wake()`.
+  static int Wait(std::atomic<int32_t>* v, int32_t val) {
+    return WaitAbsoluteTimeout(v, val, nullptr);
+  }
+
+  // Atomically check that `*v == val`, and if it is, then sleep until
+  // CLOCK_REALTIME reaches `*abs_timeout`, or until woken by `Wake()`.
+  static int WaitAbsoluteTimeout(std::atomic<int32_t>* v, int32_t val,
+                                 const struct timespec* abs_timeout) {
+    FutexTimespec ts;
+    // https://locklessinc.com/articles/futex_cheat_sheet/
+    // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time.
+    auto err = syscall(
+        SYS_futex, reinterpret_cast<int32_t*>(v),
+        FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, val,
+        ToFutexTimespec(abs_timeout, &ts), nullptr, FUTEX_BITSET_MATCH_ANY);
+    if (err != 0) {
       return -errno;
     }
     return 0;
   }
 
-  static int WaitBitsetAbsoluteTimeout(std::atomic<int32_t> *v, int32_t val,
-                                       int32_t bits,
-                                       const struct timespec *abstime) {
-    // NOLINTNEXTLINE(runtime/int)
-    long err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v),
-                       FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG, val, abstime,
-                       nullptr, bits);
-    if (ABSL_PREDICT_FALSE(err != 0)) {
+  // Atomically check that `*v == val`, and if it is, then sleep until
+  // `*rel_timeout` has elapsed, or until woken by `Wake()`.
+  static int WaitRelativeTimeout(std::atomic<int32_t>* v, int32_t val,
+                                 const struct timespec* rel_timeout) {
+    FutexTimespec ts;
+    // Atomically check that the futex value is still 0, and if it
+    // is, sleep until abs_timeout or until woken by FUTEX_WAKE.
+    auto err =
+        syscall(SYS_futex, reinterpret_cast<int32_t*>(v), FUTEX_PRIVATE_FLAG,
+                val, ToFutexTimespec(rel_timeout, &ts));
+    if (err != 0) {
       return -errno;
     }
     return 0;
   }
 
-  static int Wake(std::atomic<int32_t> *v, int32_t count) {
-    // NOLINTNEXTLINE(runtime/int)
-    long err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v),
+  // Wakes at most `count` waiters that have entered the sleep state on `v`.
+  static int Wake(std::atomic<int32_t>* v, int32_t count) {
+    auto err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v),
                        FUTEX_WAKE | FUTEX_PRIVATE_FLAG, count);
     if (ABSL_PREDICT_FALSE(err < 0)) {
       return -errno;
@@ -133,16 +145,24 @@
     return 0;
   }
 
-  // FUTEX_WAKE_BITSET
-  static int WakeBitset(std::atomic<int32_t> *v, int32_t count, int32_t bits) {
-    // NOLINTNEXTLINE(runtime/int)
-    long err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v),
-                       FUTEX_WAKE_BITSET | FUTEX_PRIVATE_FLAG, count, nullptr,
-                       nullptr, bits);
-    if (ABSL_PREDICT_FALSE(err < 0)) {
-      return -errno;
+ private:
+  static FutexTimespec* ToFutexTimespec(const struct timespec* userspace_ts,
+                                        FutexTimespec* futex_ts) {
+    if (userspace_ts == nullptr) {
+      return nullptr;
     }
-    return 0;
+
+    using FutexSeconds = decltype(futex_ts->tv_sec);
+    using FutexNanoseconds = decltype(futex_ts->tv_nsec);
+
+    constexpr auto kMaxSeconds{(std::numeric_limits<FutexSeconds>::max)()};
+    if (userspace_ts->tv_sec > kMaxSeconds) {
+      futex_ts->tv_sec = kMaxSeconds;
+    } else {
+      futex_ts->tv_sec = static_cast<FutexSeconds>(userspace_ts->tv_sec);
+    }
+    futex_ts->tv_nsec = static_cast<FutexNanoseconds>(userspace_ts->tv_nsec);
+    return futex_ts;
   }
 };
 
diff --git a/absl/synchronization/internal/futex_waiter.cc b/absl/synchronization/internal/futex_waiter.cc
new file mode 100644
index 0000000..87eb3b2
--- /dev/null
+++ b/absl/synchronization/internal/futex_waiter.cc
@@ -0,0 +1,111 @@
+// Copyright 2023 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/synchronization/internal/futex_waiter.h"
+
+#ifdef ABSL_INTERNAL_HAVE_FUTEX_WAITER
+
+#include <atomic>
+#include <cstdint>
+#include <cerrno>
+
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/internal/thread_identity.h"
+#include "absl/base/optimization.h"
+#include "absl/synchronization/internal/kernel_timeout.h"
+#include "absl/synchronization/internal/futex.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace synchronization_internal {
+
+#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
+constexpr char FutexWaiter::kName[];
+#endif
+
+int FutexWaiter::WaitUntil(std::atomic<int32_t>* v, int32_t val,
+                           KernelTimeout t) {
+#ifdef CLOCK_MONOTONIC
+  constexpr bool kHasClockMonotonic = true;
+#else
+  constexpr bool kHasClockMonotonic = false;
+#endif
+
+  // We can't call Futex::WaitUntil() here because the prodkernel implementation
+  // does not know about KernelTimeout::SupportsSteadyClock().
+  if (!t.has_timeout()) {
+    return Futex::Wait(v, val);
+  } else if (kHasClockMonotonic && KernelTimeout::SupportsSteadyClock() &&
+             t.is_relative_timeout()) {
+    auto rel_timespec = t.MakeRelativeTimespec();
+    return Futex::WaitRelativeTimeout(v, val, &rel_timespec);
+  } else {
+    auto abs_timespec = t.MakeAbsTimespec();
+    return Futex::WaitAbsoluteTimeout(v, val, &abs_timespec);
+  }
+}
+
+bool FutexWaiter::Wait(KernelTimeout t) {
+  // Loop until we can atomically decrement futex from a positive
+  // value, waiting on a futex while we believe it is zero.
+  // 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) {
+      if (!futex_.compare_exchange_weak(x, x - 1,
+                                        std::memory_order_acquire,
+                                        std::memory_order_relaxed)) {
+        continue;  // Raced with someone, retry.
+      }
+      return true;  // Consumed a wakeup, we are done.
+    }
+
+    if (!first_pass) MaybeBecomeIdle();
+    const int err = WaitUntil(&futex_, 0, t);
+    if (err != 0) {
+      if (err == -EINTR || err == -EWOULDBLOCK) {
+        // Do nothing, the loop will retry.
+      } else if (err == -ETIMEDOUT) {
+        return false;
+      } else {
+        ABSL_RAW_LOG(FATAL, "Futex operation failed with error %d\n", err);
+      }
+    }
+    first_pass = false;
+  }
+}
+
+void FutexWaiter::Post() {
+  if (futex_.fetch_add(1, std::memory_order_release) == 0) {
+    // We incremented from 0, need to wake a potential waiter.
+    Poke();
+  }
+}
+
+void FutexWaiter::Poke() {
+  // Wake one thread waiting on the futex.
+  const int err = Futex::Wake(&futex_, 1);
+  if (ABSL_PREDICT_FALSE(err < 0)) {
+    ABSL_RAW_LOG(FATAL, "Futex operation failed with error %d\n", err);
+  }
+}
+
+}  // namespace synchronization_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_INTERNAL_HAVE_FUTEX_WAITER
diff --git a/absl/synchronization/internal/futex_waiter.h b/absl/synchronization/internal/futex_waiter.h
new file mode 100644
index 0000000..11dfa93
--- /dev/null
+++ b/absl/synchronization/internal/futex_waiter.h
@@ -0,0 +1,63 @@
+// Copyright 2023 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_SYNCHRONIZATION_INTERNAL_FUTEX_WAITER_H_
+#define ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_WAITER_H_
+
+#include <atomic>
+#include <cstdint>
+
+#include "absl/base/config.h"
+#include "absl/synchronization/internal/kernel_timeout.h"
+#include "absl/synchronization/internal/futex.h"
+#include "absl/synchronization/internal/waiter_base.h"
+
+#ifdef ABSL_INTERNAL_HAVE_FUTEX
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace synchronization_internal {
+
+#define ABSL_INTERNAL_HAVE_FUTEX_WAITER 1
+
+class FutexWaiter : public WaiterCrtp<FutexWaiter> {
+ public:
+  FutexWaiter() : futex_(0) {}
+
+  bool Wait(KernelTimeout t);
+  void Post();
+  void Poke();
+
+  static constexpr char kName[] = "FutexWaiter";
+
+ private:
+  // Atomically check that `*v == val`, and if it is, then sleep until the
+  // timeout `t` has been reached, or until woken by `Wake()`.
+  static int WaitUntil(std::atomic<int32_t>* v, int32_t val,
+                       KernelTimeout t);
+
+  // Futexes are defined by specification to be 32-bits.
+  // Thus std::atomic<int32_t> must be just an int32_t with lockfree methods.
+  std::atomic<int32_t> futex_;
+  static_assert(sizeof(int32_t) == sizeof(futex_), "Wrong size for futex");
+};
+
+}  // namespace synchronization_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_INTERNAL_HAVE_FUTEX
+
+#endif  // ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_WAITER_H_
diff --git a/absl/synchronization/internal/graphcycles.cc b/absl/synchronization/internal/graphcycles.cc
index feec458..0148954 100644
--- a/absl/synchronization/internal/graphcycles.cc
+++ b/absl/synchronization/internal/graphcycles.cc
@@ -114,7 +114,7 @@
     if (src->ptr_ == src->space_) {
       // Need to actually copy
       resize(src->size_);
-      std::copy(src->ptr_, src->ptr_ + src->size_, ptr_);
+      std::copy_n(src->ptr_, src->size_, ptr_);
       src->size_ = 0;
     } else {
       Discard();
@@ -148,7 +148,7 @@
     size_t request = static_cast<size_t>(capacity_) * sizeof(T);
     T* copy = static_cast<T*>(
         base_internal::LowLevelAlloc::AllocWithArena(request, arena));
-    std::copy(ptr_, ptr_ + size_, copy);
+    std::copy_n(ptr_, size_, copy);
     Discard();
     ptr_ = copy;
   }
diff --git a/absl/synchronization/internal/kernel_timeout.cc b/absl/synchronization/internal/kernel_timeout.cc
new file mode 100644
index 0000000..48ea628
--- /dev/null
+++ b/absl/synchronization/internal/kernel_timeout.cc
@@ -0,0 +1,225 @@
+// Copyright 2023 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/synchronization/internal/kernel_timeout.h"
+
+#ifndef _WIN32
+#include <sys/types.h>
+#endif
+
+#include <algorithm>
+#include <chrono>  // NOLINT(build/c++11)
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <limits>
+
+#include "absl/base/attributes.h"
+#include "absl/base/call_once.h"
+#include "absl/base/config.h"
+#include "absl/time/time.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace synchronization_internal {
+
+#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
+constexpr uint64_t KernelTimeout::kNoTimeout;
+constexpr int64_t KernelTimeout::kMaxNanos;
+#endif
+
+int64_t KernelTimeout::SteadyClockNow() {
+  if (!SupportsSteadyClock()) {
+    return absl::GetCurrentTimeNanos();
+  }
+  return std::chrono::duration_cast<std::chrono::nanoseconds>(
+             std::chrono::steady_clock::now().time_since_epoch())
+      .count();
+}
+
+KernelTimeout::KernelTimeout(absl::Time t) {
+  // `absl::InfiniteFuture()` is a common "no timeout" value and cheaper to
+  // compare than convert.
+  if (t == absl::InfiniteFuture()) {
+    rep_ = kNoTimeout;
+    return;
+  }
+
+  int64_t unix_nanos = absl::ToUnixNanos(t);
+
+  // A timeout that lands before the unix epoch is converted to 0.
+  // In theory implementations should expire these timeouts immediately.
+  if (unix_nanos < 0) {
+    unix_nanos = 0;
+  }
+
+  // Values greater than or equal to kMaxNanos are converted to infinite.
+  if (unix_nanos >= kMaxNanos) {
+    rep_ = kNoTimeout;
+    return;
+  }
+
+  rep_ = static_cast<uint64_t>(unix_nanos) << 1;
+}
+
+KernelTimeout::KernelTimeout(absl::Duration d) {
+  // `absl::InfiniteDuration()` is a common "no timeout" value and cheaper to
+  // compare than convert.
+  if (d == absl::InfiniteDuration()) {
+    rep_ = kNoTimeout;
+    return;
+  }
+
+  int64_t nanos = absl::ToInt64Nanoseconds(d);
+
+  // Negative durations are normalized to 0.
+  // In theory implementations should expire these timeouts immediately.
+  if (nanos < 0) {
+    nanos = 0;
+  }
+
+  int64_t now = SteadyClockNow();
+  if (nanos > kMaxNanos - now) {
+    // Durations that would be greater than kMaxNanos are converted to infinite.
+    rep_ = kNoTimeout;
+    return;
+  }
+
+  nanos += now;
+  rep_ = (static_cast<uint64_t>(nanos) << 1) | uint64_t{1};
+}
+
+int64_t KernelTimeout::MakeAbsNanos() const {
+  if (!has_timeout()) {
+    return kMaxNanos;
+  }
+
+  int64_t nanos = RawAbsNanos();
+
+  if (is_relative_timeout()) {
+    // We need to change epochs, because the relative timeout might be
+    // represented by an absolute timestamp from another clock.
+    nanos = std::max<int64_t>(nanos - SteadyClockNow(), 0);
+    int64_t now = absl::GetCurrentTimeNanos();
+    if (nanos > kMaxNanos - now) {
+      // Overflow.
+      nanos = kMaxNanos;
+    } else {
+      nanos += now;
+    }
+  } else if (nanos == 0) {
+    // Some callers have assumed that 0 means no timeout, so instead we return a
+    // time of 1 nanosecond after the epoch.
+    nanos = 1;
+  }
+
+  return nanos;
+}
+
+int64_t KernelTimeout::InNanosecondsFromNow() const {
+  if (!has_timeout()) {
+    return kMaxNanos;
+  }
+
+  int64_t nanos = RawAbsNanos();
+  if (is_absolute_timeout()) {
+    return std::max<int64_t>(nanos - absl::GetCurrentTimeNanos(), 0);
+  }
+  return std::max<int64_t>(nanos - SteadyClockNow(), 0);
+}
+
+struct timespec KernelTimeout::MakeAbsTimespec() const {
+  return absl::ToTimespec(absl::Nanoseconds(MakeAbsNanos()));
+}
+
+struct timespec KernelTimeout::MakeRelativeTimespec() const {
+  return absl::ToTimespec(absl::Nanoseconds(InNanosecondsFromNow()));
+}
+
+#ifndef _WIN32
+struct timespec KernelTimeout::MakeClockAbsoluteTimespec(clockid_t c) const {
+  if (!has_timeout()) {
+    return absl::ToTimespec(absl::Nanoseconds(kMaxNanos));
+  }
+
+  int64_t nanos = RawAbsNanos();
+  if (is_absolute_timeout()) {
+    nanos -= absl::GetCurrentTimeNanos();
+  } else {
+    nanos -= SteadyClockNow();
+  }
+
+  struct timespec now;
+  ABSL_RAW_CHECK(clock_gettime(c, &now) == 0, "clock_gettime() failed");
+  absl::Duration from_clock_epoch =
+      absl::DurationFromTimespec(now) + absl::Nanoseconds(nanos);
+  if (from_clock_epoch <= absl::ZeroDuration()) {
+    // Some callers have assumed that 0 means no timeout, so instead we return a
+    // time of 1 nanosecond after the epoch. For safety we also do not return
+    // negative values.
+    return absl::ToTimespec(absl::Nanoseconds(1));
+  }
+  return absl::ToTimespec(from_clock_epoch);
+}
+#endif
+
+KernelTimeout::DWord KernelTimeout::InMillisecondsFromNow() const {
+  constexpr DWord kInfinite = std::numeric_limits<DWord>::max();
+
+  if (!has_timeout()) {
+    return kInfinite;
+  }
+
+  constexpr uint64_t kNanosInMillis = uint64_t{1'000'000};
+  constexpr uint64_t kMaxValueNanos =
+      std::numeric_limits<int64_t>::max() - kNanosInMillis + 1;
+
+  uint64_t ns_from_now = static_cast<uint64_t>(InNanosecondsFromNow());
+  if (ns_from_now >= kMaxValueNanos) {
+    // Rounding up would overflow.
+    return kInfinite;
+  }
+  // Convert to milliseconds, always rounding up.
+  uint64_t ms_from_now = (ns_from_now + kNanosInMillis - 1) / kNanosInMillis;
+  if (ms_from_now > kInfinite) {
+    return kInfinite;
+  }
+  return static_cast<DWord>(ms_from_now);
+}
+
+std::chrono::time_point<std::chrono::system_clock>
+KernelTimeout::ToChronoTimePoint() const {
+  if (!has_timeout()) {
+    return std::chrono::time_point<std::chrono::system_clock>::max();
+  }
+
+  // The cast to std::microseconds is because (on some platforms) the
+  // std::ratio used by std::chrono::steady_clock doesn't convert to
+  // std::nanoseconds, so it doesn't compile.
+  auto micros = std::chrono::duration_cast<std::chrono::microseconds>(
+      std::chrono::nanoseconds(MakeAbsNanos()));
+  return std::chrono::system_clock::from_time_t(0) + micros;
+}
+
+std::chrono::nanoseconds KernelTimeout::ToChronoDuration() const {
+  if (!has_timeout()) {
+    return std::chrono::nanoseconds::max();
+  }
+  return std::chrono::nanoseconds(InNanosecondsFromNow());
+}
+
+}  // namespace synchronization_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/synchronization/internal/kernel_timeout.h b/absl/synchronization/internal/kernel_timeout.h
index 44a3a2e..06404a7 100644
--- a/absl/synchronization/internal/kernel_timeout.h
+++ b/absl/synchronization/internal/kernel_timeout.h
@@ -11,25 +11,21 @@
 // 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.
-//
-
-// An optional absolute timeout, with nanosecond granularity,
-// compatible with absl::Time. Suitable for in-register
-// parameter-passing (e.g. syscalls.)
-// Constructible from a absl::Time (for a timeout to be respected) or {}
-// (for "no timeout".)
-// This is a private low-level API for use by a handful of low-level
-// components that are friends of this class. Higher-level components
-// should build APIs based on absl::Time and absl::Duration.
 
 #ifndef ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_
 #define ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_
 
-#include <time.h>
+#ifndef _WIN32
+#include <sys/types.h>
+#endif
 
 #include <algorithm>
+#include <chrono>  // NOLINT(build/c++11)
+#include <cstdint>
+#include <ctime>
 #include <limits>
 
+#include "absl/base/config.h"
 #include "absl/base/internal/raw_logging.h"
 #include "absl/time/clock.h"
 #include "absl/time/time.h"
@@ -38,56 +34,73 @@
 ABSL_NAMESPACE_BEGIN
 namespace synchronization_internal {
 
-class Futex;
-class Waiter;
-
+// An optional timeout, with nanosecond granularity.
+//
+// This is a private low-level API for use by a handful of low-level
+// components. Higher-level components should build APIs based on
+// absl::Time and absl::Duration.
 class KernelTimeout {
  public:
-  // A timeout that should expire at <t>.  Any value, in the full
-  // InfinitePast() to InfiniteFuture() range, is valid here and will be
-  // respected.
-  explicit KernelTimeout(absl::Time t) : ns_(MakeNs(t)) {}
-  // No timeout.
-  KernelTimeout() : ns_(0) {}
+  // Construct an absolute timeout that should expire at `t`.
+  explicit KernelTimeout(absl::Time t);
 
-  // A more explicit factory for those who prefer it.  Equivalent to {}.
-  static KernelTimeout Never() { return {}; }
+  // Construct a relative timeout that should expire after `d`.
+  explicit KernelTimeout(absl::Duration d);
 
-  // We explicitly do not support other custom formats: timespec, int64_t nanos.
-  // Unify on this and absl::Time, please.
+  // Infinite timeout.
+  constexpr KernelTimeout() : rep_(kNoTimeout) {}
 
-  bool has_timeout() const { return ns_ != 0; }
+  // A more explicit factory for those who prefer it.
+  // Equivalent to `KernelTimeout()`.
+  static constexpr KernelTimeout Never() { return KernelTimeout(); }
 
-  // Convert to parameter for sem_timedwait/futex/similar.  Only for approved
-  // users.  Do not call if !has_timeout.
-  struct timespec MakeAbsTimespec();
+  // Returns true if there is a timeout that will eventually expire.
+  // Returns false if the timeout is infinite.
+  bool has_timeout() const { return rep_ != kNoTimeout; }
 
- private:
-  // internal rep, not user visible: ns after unix epoch.
-  // zero = no timeout.
-  // Negative we treat as an unlikely (and certainly expired!) but valid
-  // timeout.
-  int64_t ns_;
+  // If `has_timeout()` is true, returns true if the timeout was provided as an
+  // `absl::Time`. The return value is undefined if `has_timeout()` is false
+  // because all indefinite timeouts are equivalent.
+  bool is_absolute_timeout() const { return (rep_ & 1) == 0; }
 
-  static int64_t MakeNs(absl::Time t) {
-    // optimization--InfiniteFuture is common "no timeout" value
-    // and cheaper to compare than convert.
-    if (t == absl::InfiniteFuture()) return 0;
-    int64_t x = ToUnixNanos(t);
+  // If `has_timeout()` is true, returns true if the timeout was provided as an
+  // `absl::Duration`. The return value is undefined if `has_timeout()` is false
+  // because all indefinite timeouts are equivalent.
+  bool is_relative_timeout() const { return (rep_ & 1) == 1; }
 
-    // A timeout that lands exactly on the epoch (x=0) needs to be respected,
-    // so we alter it unnoticably to 1.  Negative timeouts are in
-    // theory supported, but handled poorly by the kernel (long
-    // delays) so push them forward too; since all such times have
-    // already passed, it's indistinguishable.
-    if (x <= 0) x = 1;
-    // A time larger than what can be represented to the kernel is treated
-    // as no timeout.
-    if (x == (std::numeric_limits<int64_t>::max)()) x = 0;
-    return x;
-  }
+  // Convert to `struct timespec` for interfaces that expect an absolute
+  // timeout. If !has_timeout() or is_relative_timeout(), attempts to convert to
+  // a reasonable absolute timeout, but callers should to test has_timeout() and
+  // is_relative_timeout() and prefer to use a more appropriate interface.
+  struct timespec MakeAbsTimespec() const;
 
-#ifdef _WIN32
+  // Convert to `struct timespec` for interfaces that expect a relative
+  // timeout. If !has_timeout() or is_absolute_timeout(), attempts to convert to
+  // a reasonable relative timeout, but callers should to test has_timeout() and
+  // is_absolute_timeout() and prefer to use a more appropriate interface. Since
+  // the return value is a relative duration, it should be recomputed by calling
+  // this method in the case of a spurious wakeup.
+  struct timespec MakeRelativeTimespec() const;
+
+#ifndef _WIN32
+  // Convert to `struct timespec` for interfaces that expect an absolute timeout
+  // on a specific clock `c`. This is similar to `MakeAbsTimespec()`, but
+  // callers usually want to use this method with `CLOCK_MONOTONIC` when
+  // relative timeouts are requested, and when the appropriate interface expects
+  // an absolute timeout relative to a specific clock (for example,
+  // pthread_cond_clockwait() or sem_clockwait()). If !has_timeout(), attempts
+  // to convert to a reasonable absolute timeout, but callers should to test
+  // has_timeout() prefer to use a more appropriate interface.
+  struct timespec MakeClockAbsoluteTimespec(clockid_t c) const;
+#endif
+
+  // Convert to unix epoch nanos for interfaces that expect an absolute timeout
+  // in nanoseconds. If !has_timeout() or is_relative_timeout(), attempts to
+  // convert to a reasonable absolute timeout, but callers should to test
+  // has_timeout() and is_relative_timeout() and prefer to use a more
+  // appropriate interface.
+  int64_t MakeAbsNanos() const;
+
   // Converts to milliseconds from now, or INFINITE when
   // !has_timeout(). For use by SleepConditionVariableSRW on
   // Windows. Callers should recognize that the return value is a
@@ -97,59 +110,67 @@
   // so we define our own DWORD and INFINITE instead of getting them from
   // <intsafe.h> and <WinBase.h>.
   typedef unsigned long DWord;  // NOLINT
-  DWord InMillisecondsFromNow() const {
-    constexpr DWord kInfinite = (std::numeric_limits<DWord>::max)();
-    if (!has_timeout()) {
-      return kInfinite;
-    }
-    // The use of absl::Now() to convert from absolute time to
-    // relative time means that absl::Now() cannot use anything that
-    // depends on KernelTimeout (for example, Mutex) on Windows.
-    int64_t now = ToUnixNanos(absl::Now());
-    if (ns_ >= now) {
-      // Round up so that Now() + ms_from_now >= ns_.
-      constexpr uint64_t max_nanos =
-          (std::numeric_limits<int64_t>::max)() - 999999u;
-      uint64_t ms_from_now =
-          ((std::min)(max_nanos, static_cast<uint64_t>(ns_ - now)) + 999999u) /
-          1000000u;
-      if (ms_from_now > kInfinite) {
-        return kInfinite;
-      }
-      return static_cast<DWord>(ms_from_now);
-    }
-    return 0;
-  }
-#endif
+  DWord InMillisecondsFromNow() const;
 
-  friend class Futex;
-  friend class Waiter;
+  // Convert to std::chrono::time_point for interfaces that expect an absolute
+  // timeout, like std::condition_variable::wait_until(). If !has_timeout() or
+  // is_relative_timeout(), attempts to convert to a reasonable absolute
+  // timeout, but callers should test has_timeout() and is_relative_timeout()
+  // and prefer to use a more appropriate interface.
+  std::chrono::time_point<std::chrono::system_clock> ToChronoTimePoint() const;
+
+  // Convert to std::chrono::time_point for interfaces that expect a relative
+  // timeout, like std::condition_variable::wait_for(). If !has_timeout() or
+  // is_absolute_timeout(), attempts to convert to a reasonable relative
+  // timeout, but callers should test has_timeout() and is_absolute_timeout()
+  // and prefer to use a more appropriate interface. Since the return value is a
+  // relative duration, it should be recomputed by calling this method in the
+  // case of a spurious wakeup.
+  std::chrono::nanoseconds ToChronoDuration() const;
+
+  // Returns true if steady (aka monotonic) clocks are supported by the system.
+  // This method exists because go/btm requires synchronized clocks, and
+  // thus requires we use the system (aka walltime) clock.
+  static constexpr bool SupportsSteadyClock() { return true; }
+
+ private:
+  // Returns the current time, expressed as a count of nanoseconds since the
+  // epoch used by an arbitrary clock. The implementation tries to use a steady
+  // (monotonic) clock if one is available.
+  static int64_t SteadyClockNow();
+
+  // Internal representation.
+  //   - If the value is kNoTimeout, then the timeout is infinite, and
+  //     has_timeout() will return true.
+  //   - If the low bit is 0, then the high 63 bits is the number of nanoseconds
+  //     after the unix epoch.
+  //   - If the low bit is 1, then the high 63 bits is the number of nanoseconds
+  //     after the epoch used by SteadyClockNow().
+  //
+  // In all cases the time is stored as an absolute time, the only difference is
+  // the clock epoch. The use of absolute times is important since in the case
+  // of a relative timeout with a spurious wakeup, the program would have to
+  // restart the wait, and thus needs a way of recomputing the remaining time.
+  uint64_t rep_;
+
+  // Returns the number of nanoseconds stored in the internal representation.
+  // When combined with the clock epoch indicated by the low bit (which is
+  // accessed through is_absolute_timeout() and is_relative_timeout()), the
+  // return value is used to compute when the timeout should occur.
+  int64_t RawAbsNanos() const { return static_cast<int64_t>(rep_ >> 1); }
+
+  // Converts to nanoseconds from now. Since the return value is a relative
+  // duration, it should be recomputed by calling this method in the case of a
+  // spurious wakeup.
+  int64_t InNanosecondsFromNow() const;
+
+  // A value that represents no timeout (or an infinite timeout).
+  static constexpr uint64_t kNoTimeout = (std::numeric_limits<uint64_t>::max)();
+
+  // The maximum value that can be stored in the high 63 bits.
+  static constexpr int64_t kMaxNanos = (std::numeric_limits<int64_t>::max)();
 };
 
-inline struct timespec KernelTimeout::MakeAbsTimespec() {
-  int64_t n = ns_;
-  static const int64_t kNanosPerSecond = 1000 * 1000 * 1000;
-  if (n == 0) {
-    ABSL_RAW_LOG(
-        ERROR, "Tried to create a timespec from a non-timeout; never do this.");
-    // But we'll try to continue sanely.  no-timeout ~= saturated timeout.
-    n = (std::numeric_limits<int64_t>::max)();
-  }
-
-  // Kernel APIs validate timespecs as being at or after the epoch,
-  // despite the kernel time type being signed.  However, no one can
-  // tell the difference between a timeout at or before the epoch (since
-  // all such timeouts have expired!)
-  if (n < 0) n = 0;
-
-  struct timespec abstime;
-  int64_t seconds = (std::min)(n / kNanosPerSecond,
-                               int64_t{(std::numeric_limits<time_t>::max)()});
-  abstime.tv_sec = static_cast<time_t>(seconds);
-  abstime.tv_nsec = static_cast<decltype(abstime.tv_nsec)>(n % kNanosPerSecond);
-  return abstime;
-}
-
 }  // namespace synchronization_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/synchronization/internal/kernel_timeout_test.cc b/absl/synchronization/internal/kernel_timeout_test.cc
new file mode 100644
index 0000000..92ed269
--- /dev/null
+++ b/absl/synchronization/internal/kernel_timeout_test.cc
@@ -0,0 +1,394 @@
+// Copyright 2023 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/synchronization/internal/kernel_timeout.h"
+
+#include <ctime>
+#include <chrono>  // NOLINT(build/c++11)
+#include <limits>
+
+#include "absl/base/config.h"
+#include "absl/random/random.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
+#include "gtest/gtest.h"
+
+// Test go/btm support by randomizing the value of clock_gettime() for
+// CLOCK_MONOTONIC. This works by overriding a weak symbol in glibc.
+// We should be resistant to this randomization when !SupportsSteadyClock().
+#if defined(__GOOGLE_GRTE_VERSION__) &&      \
+    !defined(ABSL_HAVE_ADDRESS_SANITIZER) && \
+    !defined(ABSL_HAVE_MEMORY_SANITIZER) &&  \
+    !defined(ABSL_HAVE_THREAD_SANITIZER)
+extern "C" int __clock_gettime(clockid_t c, struct timespec* ts);
+
+extern "C" int clock_gettime(clockid_t c, struct timespec* ts) {
+  if (c == CLOCK_MONOTONIC &&
+      !absl::synchronization_internal::KernelTimeout::SupportsSteadyClock()) {
+    absl::SharedBitGen gen;
+    ts->tv_sec = absl::Uniform(gen, 0, 1'000'000'000);
+    ts->tv_nsec = absl::Uniform(gen, 0, 1'000'000'000);
+    return 0;
+  }
+  return __clock_gettime(c, ts);
+}
+#endif
+
+namespace {
+
+#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+    defined(ABSL_HAVE_MEMORY_SANITIZER) ||  \
+    defined(ABSL_HAVE_THREAD_SANITIZER) || \
+    defined(__ANDROID__) || \
+    defined(_WIN32) || defined(_WIN64)
+constexpr absl::Duration kTimingBound = absl::Milliseconds(5);
+#else
+constexpr absl::Duration kTimingBound = absl::Microseconds(250);
+#endif
+
+using absl::synchronization_internal::KernelTimeout;
+
+TEST(KernelTimeout, FiniteTimes) {
+  constexpr absl::Duration kDurationsToTest[] = {
+    absl::ZeroDuration(),
+    absl::Nanoseconds(1),
+    absl::Microseconds(1),
+    absl::Milliseconds(1),
+    absl::Seconds(1),
+    absl::Minutes(1),
+    absl::Hours(1),
+    absl::Hours(1000),
+    -absl::Nanoseconds(1),
+    -absl::Microseconds(1),
+    -absl::Milliseconds(1),
+    -absl::Seconds(1),
+    -absl::Minutes(1),
+    -absl::Hours(1),
+    -absl::Hours(1000),
+  };
+
+  for (auto duration : kDurationsToTest) {
+    const absl::Time now = absl::Now();
+    const absl::Time when = now + duration;
+    SCOPED_TRACE(duration);
+    KernelTimeout t(when);
+    EXPECT_TRUE(t.has_timeout());
+    EXPECT_TRUE(t.is_absolute_timeout());
+    EXPECT_FALSE(t.is_relative_timeout());
+    EXPECT_EQ(absl::TimeFromTimespec(t.MakeAbsTimespec()), when);
+#ifndef _WIN32
+    EXPECT_LE(
+        absl::AbsDuration(absl::Now() + duration -
+                          absl::TimeFromTimespec(
+                              t.MakeClockAbsoluteTimespec(CLOCK_REALTIME))),
+        absl::Milliseconds(10));
+#endif
+    EXPECT_LE(
+        absl::AbsDuration(absl::DurationFromTimespec(t.MakeRelativeTimespec()) -
+                          std::max(duration, absl::ZeroDuration())),
+        kTimingBound);
+    EXPECT_EQ(absl::FromUnixNanos(t.MakeAbsNanos()), when);
+    EXPECT_LE(absl::AbsDuration(absl::Milliseconds(t.InMillisecondsFromNow()) -
+                                std::max(duration, absl::ZeroDuration())),
+              absl::Milliseconds(5));
+    EXPECT_LE(absl::AbsDuration(absl::FromChrono(t.ToChronoTimePoint()) - when),
+              absl::Microseconds(1));
+    EXPECT_LE(absl::AbsDuration(absl::FromChrono(t.ToChronoDuration()) -
+                                std::max(duration, absl::ZeroDuration())),
+              kTimingBound);
+  }
+}
+
+TEST(KernelTimeout, InfiniteFuture) {
+  KernelTimeout t(absl::InfiniteFuture());
+  EXPECT_FALSE(t.has_timeout());
+  // Callers are expected to check has_timeout() instead of using the methods
+  // below, but we do try to do something reasonable if they don't. We may not
+  // be able to round-trip back to absl::InfiniteDuration() or
+  // absl::InfiniteFuture(), but we should return a very large value.
+  EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()),
+            absl::Now() + absl::Hours(100000));
+#ifndef _WIN32
+  EXPECT_GT(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)),
+            absl::Now() + absl::Hours(100000));
+#endif
+  EXPECT_GT(absl::DurationFromTimespec(t.MakeRelativeTimespec()),
+            absl::Hours(100000));
+  EXPECT_GT(absl::FromUnixNanos(t.MakeAbsNanos()),
+            absl::Now() + absl::Hours(100000));
+  EXPECT_EQ(t.InMillisecondsFromNow(),
+            std::numeric_limits<KernelTimeout::DWord>::max());
+  EXPECT_EQ(t.ToChronoTimePoint(),
+            std::chrono::time_point<std::chrono::system_clock>::max());
+  EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max());
+}
+
+TEST(KernelTimeout, DefaultConstructor) {
+  // The default constructor is equivalent to absl::InfiniteFuture().
+  KernelTimeout t;
+  EXPECT_FALSE(t.has_timeout());
+  // Callers are expected to check has_timeout() instead of using the methods
+  // below, but we do try to do something reasonable if they don't. We may not
+  // be able to round-trip back to absl::InfiniteDuration() or
+  // absl::InfiniteFuture(), but we should return a very large value.
+  EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()),
+            absl::Now() + absl::Hours(100000));
+#ifndef _WIN32
+  EXPECT_GT(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)),
+            absl::Now() + absl::Hours(100000));
+#endif
+  EXPECT_GT(absl::DurationFromTimespec(t.MakeRelativeTimespec()),
+            absl::Hours(100000));
+  EXPECT_GT(absl::FromUnixNanos(t.MakeAbsNanos()),
+            absl::Now() + absl::Hours(100000));
+  EXPECT_EQ(t.InMillisecondsFromNow(),
+            std::numeric_limits<KernelTimeout::DWord>::max());
+  EXPECT_EQ(t.ToChronoTimePoint(),
+            std::chrono::time_point<std::chrono::system_clock>::max());
+  EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max());
+}
+
+TEST(KernelTimeout, TimeMaxNanos) {
+  // Time >= kMaxNanos should behave as no timeout.
+  KernelTimeout t(absl::FromUnixNanos(std::numeric_limits<int64_t>::max()));
+  EXPECT_FALSE(t.has_timeout());
+  // Callers are expected to check has_timeout() instead of using the methods
+  // below, but we do try to do something reasonable if they don't. We may not
+  // be able to round-trip back to absl::InfiniteDuration() or
+  // absl::InfiniteFuture(), but we should return a very large value.
+  EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()),
+            absl::Now() + absl::Hours(100000));
+#ifndef _WIN32
+  EXPECT_GT(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)),
+            absl::Now() + absl::Hours(100000));
+#endif
+  EXPECT_GT(absl::DurationFromTimespec(t.MakeRelativeTimespec()),
+            absl::Hours(100000));
+  EXPECT_GT(absl::FromUnixNanos(t.MakeAbsNanos()),
+            absl::Now() + absl::Hours(100000));
+  EXPECT_EQ(t.InMillisecondsFromNow(),
+            std::numeric_limits<KernelTimeout::DWord>::max());
+  EXPECT_EQ(t.ToChronoTimePoint(),
+            std::chrono::time_point<std::chrono::system_clock>::max());
+  EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max());
+}
+
+TEST(KernelTimeout, Never) {
+  // KernelTimeout::Never() is equivalent to absl::InfiniteFuture().
+  KernelTimeout t = KernelTimeout::Never();
+  EXPECT_FALSE(t.has_timeout());
+  // Callers are expected to check has_timeout() instead of using the methods
+  // below, but we do try to do something reasonable if they don't. We may not
+  // be able to round-trip back to absl::InfiniteDuration() or
+  // absl::InfiniteFuture(), but we should return a very large value.
+  EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()),
+            absl::Now() + absl::Hours(100000));
+#ifndef _WIN32
+  EXPECT_GT(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)),
+            absl::Now() + absl::Hours(100000));
+#endif
+  EXPECT_GT(absl::DurationFromTimespec(t.MakeRelativeTimespec()),
+            absl::Hours(100000));
+  EXPECT_GT(absl::FromUnixNanos(t.MakeAbsNanos()),
+            absl::Now() + absl::Hours(100000));
+  EXPECT_EQ(t.InMillisecondsFromNow(),
+            std::numeric_limits<KernelTimeout::DWord>::max());
+  EXPECT_EQ(t.ToChronoTimePoint(),
+            std::chrono::time_point<std::chrono::system_clock>::max());
+  EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max());
+}
+
+TEST(KernelTimeout, InfinitePast) {
+  KernelTimeout t(absl::InfinitePast());
+  EXPECT_TRUE(t.has_timeout());
+  EXPECT_TRUE(t.is_absolute_timeout());
+  EXPECT_FALSE(t.is_relative_timeout());
+  EXPECT_LE(absl::TimeFromTimespec(t.MakeAbsTimespec()),
+            absl::FromUnixNanos(1));
+#ifndef _WIN32
+  EXPECT_LE(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)),
+            absl::FromUnixSeconds(1));
+#endif
+  EXPECT_EQ(absl::DurationFromTimespec(t.MakeRelativeTimespec()),
+            absl::ZeroDuration());
+  EXPECT_LE(absl::FromUnixNanos(t.MakeAbsNanos()), absl::FromUnixNanos(1));
+  EXPECT_EQ(t.InMillisecondsFromNow(), KernelTimeout::DWord{0});
+  EXPECT_LT(t.ToChronoTimePoint(), std::chrono::system_clock::from_time_t(0) +
+                                       std::chrono::seconds(1));
+  EXPECT_EQ(t.ToChronoDuration(), std::chrono::nanoseconds(0));
+}
+
+TEST(KernelTimeout, FiniteDurations) {
+  constexpr absl::Duration kDurationsToTest[] = {
+    absl::ZeroDuration(),
+    absl::Nanoseconds(1),
+    absl::Microseconds(1),
+    absl::Milliseconds(1),
+    absl::Seconds(1),
+    absl::Minutes(1),
+    absl::Hours(1),
+    absl::Hours(1000),
+  };
+
+  for (auto duration : kDurationsToTest) {
+    SCOPED_TRACE(duration);
+    KernelTimeout t(duration);
+    EXPECT_TRUE(t.has_timeout());
+    EXPECT_FALSE(t.is_absolute_timeout());
+    EXPECT_TRUE(t.is_relative_timeout());
+    EXPECT_LE(absl::AbsDuration(absl::Now() + duration -
+                                absl::TimeFromTimespec(t.MakeAbsTimespec())),
+              absl::Milliseconds(5));
+#ifndef _WIN32
+    EXPECT_LE(
+        absl::AbsDuration(absl::Now() + duration -
+                          absl::TimeFromTimespec(
+                              t.MakeClockAbsoluteTimespec(CLOCK_REALTIME))),
+        absl::Milliseconds(5));
+#endif
+    EXPECT_LE(
+        absl::AbsDuration(absl::DurationFromTimespec(t.MakeRelativeTimespec()) -
+                          duration),
+        kTimingBound);
+    EXPECT_LE(absl::AbsDuration(absl::Now() + duration -
+                                absl::FromUnixNanos(t.MakeAbsNanos())),
+              absl::Milliseconds(5));
+    EXPECT_LE(absl::Milliseconds(t.InMillisecondsFromNow()) - duration,
+              absl::Milliseconds(5));
+    EXPECT_LE(absl::AbsDuration(absl::Now() + duration -
+                                absl::FromChrono(t.ToChronoTimePoint())),
+              kTimingBound);
+    EXPECT_LE(
+        absl::AbsDuration(absl::FromChrono(t.ToChronoDuration()) - duration),
+        kTimingBound);
+  }
+}
+
+TEST(KernelTimeout, NegativeDurations) {
+  constexpr absl::Duration kDurationsToTest[] = {
+    -absl::ZeroDuration(),
+    -absl::Nanoseconds(1),
+    -absl::Microseconds(1),
+    -absl::Milliseconds(1),
+    -absl::Seconds(1),
+    -absl::Minutes(1),
+    -absl::Hours(1),
+    -absl::Hours(1000),
+    -absl::InfiniteDuration(),
+  };
+
+  for (auto duration : kDurationsToTest) {
+    // Negative durations should all be converted to zero durations or "now".
+    SCOPED_TRACE(duration);
+    KernelTimeout t(duration);
+    EXPECT_TRUE(t.has_timeout());
+    EXPECT_FALSE(t.is_absolute_timeout());
+    EXPECT_TRUE(t.is_relative_timeout());
+    EXPECT_LE(absl::AbsDuration(absl::Now() -
+                                absl::TimeFromTimespec(t.MakeAbsTimespec())),
+              absl::Milliseconds(5));
+#ifndef _WIN32
+    EXPECT_LE(absl::AbsDuration(absl::Now() - absl::TimeFromTimespec(
+                                                  t.MakeClockAbsoluteTimespec(
+                                                      CLOCK_REALTIME))),
+              absl::Milliseconds(5));
+#endif
+    EXPECT_EQ(absl::DurationFromTimespec(t.MakeRelativeTimespec()),
+              absl::ZeroDuration());
+    EXPECT_LE(
+        absl::AbsDuration(absl::Now() - absl::FromUnixNanos(t.MakeAbsNanos())),
+        absl::Milliseconds(5));
+    EXPECT_EQ(t.InMillisecondsFromNow(), KernelTimeout::DWord{0});
+    EXPECT_LE(absl::AbsDuration(absl::Now() -
+                                absl::FromChrono(t.ToChronoTimePoint())),
+              absl::Milliseconds(5));
+    EXPECT_EQ(t.ToChronoDuration(), std::chrono::nanoseconds(0));
+  }
+}
+
+TEST(KernelTimeout, InfiniteDuration) {
+  KernelTimeout t(absl::InfiniteDuration());
+  EXPECT_FALSE(t.has_timeout());
+  // Callers are expected to check has_timeout() instead of using the methods
+  // below, but we do try to do something reasonable if they don't. We may not
+  // be able to round-trip back to absl::InfiniteDuration() or
+  // absl::InfiniteFuture(), but we should return a very large value.
+  EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()),
+            absl::Now() + absl::Hours(100000));
+#ifndef _WIN32
+  EXPECT_GT(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)),
+            absl::Now() + absl::Hours(100000));
+#endif
+  EXPECT_GT(absl::DurationFromTimespec(t.MakeRelativeTimespec()),
+            absl::Hours(100000));
+  EXPECT_GT(absl::FromUnixNanos(t.MakeAbsNanos()),
+            absl::Now() + absl::Hours(100000));
+  EXPECT_EQ(t.InMillisecondsFromNow(),
+            std::numeric_limits<KernelTimeout::DWord>::max());
+  EXPECT_EQ(t.ToChronoTimePoint(),
+            std::chrono::time_point<std::chrono::system_clock>::max());
+  EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max());
+}
+
+TEST(KernelTimeout, DurationMaxNanos) {
+  // Duration >= kMaxNanos should behave as no timeout.
+  KernelTimeout t(absl::Nanoseconds(std::numeric_limits<int64_t>::max()));
+  EXPECT_FALSE(t.has_timeout());
+  // Callers are expected to check has_timeout() instead of using the methods
+  // below, but we do try to do something reasonable if they don't. We may not
+  // be able to round-trip back to absl::InfiniteDuration() or
+  // absl::InfiniteFuture(), but we should return a very large value.
+  EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()),
+            absl::Now() + absl::Hours(100000));
+#ifndef _WIN32
+  EXPECT_GT(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)),
+            absl::Now() + absl::Hours(100000));
+#endif
+  EXPECT_GT(absl::DurationFromTimespec(t.MakeRelativeTimespec()),
+            absl::Hours(100000));
+  EXPECT_GT(absl::FromUnixNanos(t.MakeAbsNanos()),
+            absl::Now() + absl::Hours(100000));
+  EXPECT_EQ(t.InMillisecondsFromNow(),
+            std::numeric_limits<KernelTimeout::DWord>::max());
+  EXPECT_EQ(t.ToChronoTimePoint(),
+            std::chrono::time_point<std::chrono::system_clock>::max());
+  EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max());
+}
+
+TEST(KernelTimeout, OverflowNanos) {
+  // Test what happens when KernelTimeout is constructed with an absl::Duration
+  // that would overflow now_nanos + duration.
+  int64_t now_nanos = absl::ToUnixNanos(absl::Now());
+  int64_t limit = std::numeric_limits<int64_t>::max() - now_nanos;
+  absl::Duration duration = absl::Nanoseconds(limit) + absl::Seconds(1);
+  KernelTimeout t(duration);
+  // Timeouts should still be far in the future.
+  EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()),
+            absl::Now() + absl::Hours(100000));
+#ifndef _WIN32
+  EXPECT_GT(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)),
+            absl::Now() + absl::Hours(100000));
+#endif
+  EXPECT_GT(absl::DurationFromTimespec(t.MakeRelativeTimespec()),
+            absl::Hours(100000));
+  EXPECT_GT(absl::FromUnixNanos(t.MakeAbsNanos()),
+            absl::Now() + absl::Hours(100000));
+  EXPECT_LE(absl::Milliseconds(t.InMillisecondsFromNow()) - duration,
+            absl::Milliseconds(5));
+  EXPECT_GT(t.ToChronoTimePoint(),
+            std::chrono::system_clock::now() + std::chrono::hours(100000));
+  EXPECT_GT(t.ToChronoDuration(), std::chrono::hours(100000));
+}
+
+}  // namespace
diff --git a/absl/synchronization/internal/per_thread_sem.cc b/absl/synchronization/internal/per_thread_sem.cc
index 469e8f3..c9b8dc1 100644
--- a/absl/synchronization/internal/per_thread_sem.cc
+++ b/absl/synchronization/internal/per_thread_sem.cc
@@ -40,13 +40,6 @@
   return identity->blocked_count_ptr;
 }
 
-void PerThreadSem::Init(base_internal::ThreadIdentity *identity) {
-  new (Waiter::GetWaiter(identity)) Waiter();
-  identity->ticker.store(0, std::memory_order_relaxed);
-  identity->wait_start.store(0, std::memory_order_relaxed);
-  identity->is_idle.store(false, std::memory_order_relaxed);
-}
-
 void PerThreadSem::Tick(base_internal::ThreadIdentity *identity) {
   const int ticker =
       identity->ticker.fetch_add(1, std::memory_order_relaxed) + 1;
@@ -54,7 +47,7 @@
   const bool is_idle = identity->is_idle.load(std::memory_order_relaxed);
   if (wait_start && (ticker - wait_start > Waiter::kIdlePeriods) && !is_idle) {
     // Wakeup the waiting thread since it is time for it to become idle.
-    Waiter::GetWaiter(identity)->Poke();
+    ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemPoke)(identity);
   }
 }
 
@@ -64,11 +57,22 @@
 
 extern "C" {
 
+ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemInit)(
+    absl::base_internal::ThreadIdentity *identity) {
+  new (absl::synchronization_internal::Waiter::GetWaiter(identity))
+      absl::synchronization_internal::Waiter();
+}
+
 ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemPost)(
     absl::base_internal::ThreadIdentity *identity) {
   absl::synchronization_internal::Waiter::GetWaiter(identity)->Post();
 }
 
+ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemPoke)(
+    absl::base_internal::ThreadIdentity *identity) {
+  absl::synchronization_internal::Waiter::GetWaiter(identity)->Poke();
+}
+
 ABSL_ATTRIBUTE_WEAK bool ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemWait)(
     absl::synchronization_internal::KernelTimeout t) {
   bool timeout = false;
diff --git a/absl/synchronization/internal/per_thread_sem.h b/absl/synchronization/internal/per_thread_sem.h
index 90a8880..144ab3c 100644
--- a/absl/synchronization/internal/per_thread_sem.h
+++ b/absl/synchronization/internal/per_thread_sem.h
@@ -64,7 +64,7 @@
  private:
   // Create the PerThreadSem associated with "identity".  Initializes count=0.
   // REQUIRES: May only be called by ThreadIdentity.
-  static void Init(base_internal::ThreadIdentity* identity);
+  static inline void Init(base_internal::ThreadIdentity* identity);
 
   // Increments "identity"'s count.
   static inline void Post(base_internal::ThreadIdentity* identity);
@@ -91,12 +91,21 @@
 // By changing our extension points to be extern "C", we dodge this
 // check.
 extern "C" {
+void ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemInit)(
+    absl::base_internal::ThreadIdentity* identity);
 void ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemPost)(
     absl::base_internal::ThreadIdentity* identity);
 bool ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemWait)(
     absl::synchronization_internal::KernelTimeout t);
+void ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemPoke)(
+    absl::base_internal::ThreadIdentity* identity);
 }  // extern "C"
 
+void absl::synchronization_internal::PerThreadSem::Init(
+    absl::base_internal::ThreadIdentity* identity) {
+  ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemInit)(identity);
+}
+
 void absl::synchronization_internal::PerThreadSem::Post(
     absl::base_internal::ThreadIdentity* identity) {
   ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemPost)(identity);
diff --git a/absl/synchronization/internal/pthread_waiter.cc b/absl/synchronization/internal/pthread_waiter.cc
new file mode 100644
index 0000000..bf700e9
--- /dev/null
+++ b/absl/synchronization/internal/pthread_waiter.cc
@@ -0,0 +1,167 @@
+// Copyright 2023 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/synchronization/internal/pthread_waiter.h"
+
+#ifdef ABSL_INTERNAL_HAVE_PTHREAD_WAITER
+
+#include <pthread.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <cassert>
+#include <cerrno>
+
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/internal/thread_identity.h"
+#include "absl/base/optimization.h"
+#include "absl/synchronization/internal/kernel_timeout.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace synchronization_internal {
+
+namespace {
+class PthreadMutexHolder {
+ public:
+  explicit PthreadMutexHolder(pthread_mutex_t *mu) : mu_(mu) {
+    const int err = pthread_mutex_lock(mu_);
+    if (err != 0) {
+      ABSL_RAW_LOG(FATAL, "pthread_mutex_lock failed: %d", err);
+    }
+  }
+
+  PthreadMutexHolder(const PthreadMutexHolder &rhs) = delete;
+  PthreadMutexHolder &operator=(const PthreadMutexHolder &rhs) = delete;
+
+  ~PthreadMutexHolder() {
+    const int err = pthread_mutex_unlock(mu_);
+    if (err != 0) {
+      ABSL_RAW_LOG(FATAL, "pthread_mutex_unlock failed: %d", err);
+    }
+  }
+
+ private:
+  pthread_mutex_t *mu_;
+};
+}  // namespace
+
+#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
+constexpr char PthreadWaiter::kName[];
+#endif
+
+PthreadWaiter::PthreadWaiter() : waiter_count_(0), wakeup_count_(0) {
+  const int err = pthread_mutex_init(&mu_, 0);
+  if (err != 0) {
+    ABSL_RAW_LOG(FATAL, "pthread_mutex_init failed: %d", err);
+  }
+
+  const int err2 = pthread_cond_init(&cv_, 0);
+  if (err2 != 0) {
+    ABSL_RAW_LOG(FATAL, "pthread_cond_init failed: %d", err2);
+  }
+}
+
+#ifdef __APPLE__
+#define ABSL_INTERNAL_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP 1
+#endif
+
+#if defined(__GLIBC__) && \
+    (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30))
+#define ABSL_INTERNAL_HAVE_PTHREAD_COND_CLOCKWAIT 1
+#elif defined(__ANDROID_API__) && __ANDROID_API__ >= 30
+#define ABSL_INTERNAL_HAVE_PTHREAD_COND_CLOCKWAIT 1
+#endif
+
+// Calls pthread_cond_timedwait() or possibly something else like
+// pthread_cond_timedwait_relative_np() depending on the platform and
+// KernelTimeout requested. The return value is the same as the return
+// value of pthread_cond_timedwait().
+int PthreadWaiter::TimedWait(KernelTimeout t) {
+  assert(t.has_timeout());
+  if (KernelTimeout::SupportsSteadyClock() && t.is_relative_timeout()) {
+#ifdef ABSL_INTERNAL_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP
+    const auto rel_timeout = t.MakeRelativeTimespec();
+    return pthread_cond_timedwait_relative_np(&cv_, &mu_, &rel_timeout);
+#elif defined(ABSL_INTERNAL_HAVE_PTHREAD_COND_CLOCKWAIT) && \
+    defined(CLOCK_MONOTONIC)
+    const auto abs_clock_timeout = t.MakeClockAbsoluteTimespec(CLOCK_MONOTONIC);
+    return pthread_cond_clockwait(&cv_, &mu_, CLOCK_MONOTONIC,
+                                  &abs_clock_timeout);
+#endif
+  }
+
+  const auto abs_timeout = t.MakeAbsTimespec();
+  return pthread_cond_timedwait(&cv_, &mu_, &abs_timeout);
+}
+
+bool PthreadWaiter::Wait(KernelTimeout t) {
+  PthreadMutexHolder h(&mu_);
+  ++waiter_count_;
+  // Loop until we find a wakeup to consume or timeout.
+  // 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 (wakeup_count_ == 0) {
+    if (!first_pass) MaybeBecomeIdle();
+    // No wakeups available, time to wait.
+    if (!t.has_timeout()) {
+      const int err = pthread_cond_wait(&cv_, &mu_);
+      if (err != 0) {
+        ABSL_RAW_LOG(FATAL, "pthread_cond_wait failed: %d", err);
+      }
+    } else {
+      const int err = TimedWait(t);
+      if (err == ETIMEDOUT) {
+        --waiter_count_;
+        return false;
+      }
+      if (err != 0) {
+        ABSL_RAW_LOG(FATAL, "PthreadWaiter::TimedWait() failed: %d", err);
+      }
+    }
+    first_pass = false;
+  }
+  // Consume a wakeup and we're done.
+  --wakeup_count_;
+  --waiter_count_;
+  return true;
+}
+
+void PthreadWaiter::Post() {
+  PthreadMutexHolder h(&mu_);
+  ++wakeup_count_;
+  InternalCondVarPoke();
+}
+
+void PthreadWaiter::Poke() {
+  PthreadMutexHolder h(&mu_);
+  InternalCondVarPoke();
+}
+
+void PthreadWaiter::InternalCondVarPoke() {
+  if (waiter_count_ != 0) {
+    const int err = pthread_cond_signal(&cv_);
+    if (ABSL_PREDICT_FALSE(err != 0)) {
+      ABSL_RAW_LOG(FATAL, "pthread_cond_signal failed: %d", err);
+    }
+  }
+}
+
+}  // namespace synchronization_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_INTERNAL_HAVE_PTHREAD_WAITER
diff --git a/absl/synchronization/internal/pthread_waiter.h b/absl/synchronization/internal/pthread_waiter.h
new file mode 100644
index 0000000..206aefa
--- /dev/null
+++ b/absl/synchronization/internal/pthread_waiter.h
@@ -0,0 +1,60 @@
+// Copyright 2023 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_SYNCHRONIZATION_INTERNAL_PTHREAD_WAITER_H_
+#define ABSL_SYNCHRONIZATION_INTERNAL_PTHREAD_WAITER_H_
+
+#ifndef _WIN32
+#include <pthread.h>
+
+#include "absl/base/config.h"
+#include "absl/synchronization/internal/kernel_timeout.h"
+#include "absl/synchronization/internal/waiter_base.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace synchronization_internal {
+
+#define ABSL_INTERNAL_HAVE_PTHREAD_WAITER 1
+
+class PthreadWaiter : public WaiterCrtp<PthreadWaiter> {
+ public:
+  PthreadWaiter();
+
+  bool Wait(KernelTimeout t);
+  void Post();
+  void Poke();
+
+  static constexpr char kName[] = "PthreadWaiter";
+
+ private:
+  int TimedWait(KernelTimeout t);
+
+  // REQUIRES: mu_ must be held.
+  void InternalCondVarPoke();
+
+  pthread_mutex_t mu_;
+  pthread_cond_t cv_;
+  int waiter_count_;
+  int wakeup_count_;  // Unclaimed wakeups.
+};
+
+}  // namespace synchronization_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ndef _WIN32
+
+#endif  // ABSL_SYNCHRONIZATION_INTERNAL_PTHREAD_WAITER_H_
diff --git a/absl/synchronization/internal/sem_waiter.cc b/absl/synchronization/internal/sem_waiter.cc
new file mode 100644
index 0000000..d62dbdc
--- /dev/null
+++ b/absl/synchronization/internal/sem_waiter.cc
@@ -0,0 +1,122 @@
+// Copyright 2023 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/synchronization/internal/sem_waiter.h"
+
+#ifdef ABSL_INTERNAL_HAVE_SEM_WAITER
+
+#include <semaphore.h>
+
+#include <atomic>
+#include <cassert>
+#include <cstdint>
+#include <cerrno>
+
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/internal/thread_identity.h"
+#include "absl/base/optimization.h"
+#include "absl/synchronization/internal/kernel_timeout.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace synchronization_internal {
+
+#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
+constexpr char SemWaiter::kName[];
+#endif
+
+SemWaiter::SemWaiter() : wakeups_(0) {
+  if (sem_init(&sem_, 0, 0) != 0) {
+    ABSL_RAW_LOG(FATAL, "sem_init failed with errno %d\n", errno);
+  }
+}
+
+#if defined(__GLIBC__) && \
+    (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30))
+#define ABSL_INTERNAL_HAVE_SEM_CLOCKWAIT 1
+#elif defined(__ANDROID_API__) && __ANDROID_API__ >= 30
+#define ABSL_INTERNAL_HAVE_SEM_CLOCKWAIT 1
+#endif
+
+// Calls sem_timedwait() or possibly something else like
+// sem_clockwait() depending on the platform and
+// KernelTimeout requested. The return value is the same as a call to the return
+// value to a call to sem_timedwait().
+int SemWaiter::TimedWait(KernelTimeout t) {
+  if (KernelTimeout::SupportsSteadyClock() && t.is_relative_timeout()) {
+#if defined(ABSL_INTERNAL_HAVE_SEM_CLOCKWAIT) && defined(CLOCK_MONOTONIC)
+    const auto abs_clock_timeout = t.MakeClockAbsoluteTimespec(CLOCK_MONOTONIC);
+    return sem_clockwait(&sem_, CLOCK_MONOTONIC, &abs_clock_timeout);
+#endif
+  }
+
+  const auto abs_timeout = t.MakeAbsTimespec();
+  return sem_timedwait(&sem_, &abs_timeout);
+}
+
+bool SemWaiter::Wait(KernelTimeout t) {
+  // Loop until we timeout or consume a wakeup.
+  // 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) {
+    int x = wakeups_.load(std::memory_order_relaxed);
+    while (x != 0) {
+      if (!wakeups_.compare_exchange_weak(x, x - 1,
+                                          std::memory_order_acquire,
+                                          std::memory_order_relaxed)) {
+        continue;  // Raced with someone, retry.
+      }
+      // Successfully consumed a wakeup, we're done.
+      return true;
+    }
+
+    if (!first_pass) MaybeBecomeIdle();
+    // Nothing to consume, wait (looping on EINTR).
+    while (true) {
+      if (!t.has_timeout()) {
+        if (sem_wait(&sem_) == 0) break;
+        if (errno == EINTR) continue;
+        ABSL_RAW_LOG(FATAL, "sem_wait failed: %d", errno);
+      } else {
+        if (TimedWait(t) == 0) break;
+        if (errno == EINTR) continue;
+        if (errno == ETIMEDOUT) return false;
+        ABSL_RAW_LOG(FATAL, "SemWaiter::TimedWait() failed: %d", errno);
+      }
+    }
+    first_pass = false;
+  }
+}
+
+void SemWaiter::Post() {
+  // Post a wakeup.
+  if (wakeups_.fetch_add(1, std::memory_order_release) == 0) {
+    // We incremented from 0, need to wake a potential waiter.
+    Poke();
+  }
+}
+
+void SemWaiter::Poke() {
+  if (sem_post(&sem_) != 0) {  // Wake any semaphore waiter.
+    ABSL_RAW_LOG(FATAL, "sem_post failed with errno %d\n", errno);
+  }
+}
+
+}  // namespace synchronization_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_INTERNAL_HAVE_SEM_WAITER
diff --git a/absl/synchronization/internal/sem_waiter.h b/absl/synchronization/internal/sem_waiter.h
new file mode 100644
index 0000000..c22746f
--- /dev/null
+++ b/absl/synchronization/internal/sem_waiter.h
@@ -0,0 +1,65 @@
+// Copyright 2023 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_SYNCHRONIZATION_INTERNAL_SEM_WAITER_H_
+#define ABSL_SYNCHRONIZATION_INTERNAL_SEM_WAITER_H_
+
+#include "absl/base/config.h"
+
+#ifdef ABSL_HAVE_SEMAPHORE_H
+#include <semaphore.h>
+
+#include <atomic>
+#include <cstdint>
+
+#include "absl/base/internal/thread_identity.h"
+#include "absl/synchronization/internal/futex.h"
+#include "absl/synchronization/internal/kernel_timeout.h"
+#include "absl/synchronization/internal/waiter_base.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace synchronization_internal {
+
+#define ABSL_INTERNAL_HAVE_SEM_WAITER 1
+
+class SemWaiter : public WaiterCrtp<SemWaiter> {
+ public:
+  SemWaiter();
+
+  bool Wait(KernelTimeout t);
+  void Post();
+  void Poke();
+
+  static constexpr char kName[] = "SemWaiter";
+
+ private:
+  int TimedWait(KernelTimeout t);
+
+  sem_t sem_;
+
+  // This seems superfluous, but for Poke() we need to cause spurious
+  // wakeups on the semaphore. Hence we can't actually use the
+  // semaphore's count.
+  std::atomic<int> wakeups_;
+};
+
+}  // namespace synchronization_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_HAVE_SEMAPHORE_H
+
+#endif  // ABSL_SYNCHRONIZATION_INTERNAL_SEM_WAITER_H_
diff --git a/absl/synchronization/internal/stdcpp_waiter.cc b/absl/synchronization/internal/stdcpp_waiter.cc
new file mode 100644
index 0000000..355718a
--- /dev/null
+++ b/absl/synchronization/internal/stdcpp_waiter.cc
@@ -0,0 +1,91 @@
+// Copyright 2023 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/synchronization/internal/stdcpp_waiter.h"
+
+#ifdef ABSL_INTERNAL_HAVE_STDCPP_WAITER
+
+#include <chrono>  // NOLINT(build/c++11)
+#include <condition_variable>  // NOLINT(build/c++11)
+#include <mutex>  // NOLINT(build/c++11)
+
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/internal/thread_identity.h"
+#include "absl/base/optimization.h"
+#include "absl/synchronization/internal/kernel_timeout.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace synchronization_internal {
+
+#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
+constexpr char StdcppWaiter::kName[];
+#endif
+
+StdcppWaiter::StdcppWaiter() : waiter_count_(0), wakeup_count_(0) {}
+
+bool StdcppWaiter::Wait(KernelTimeout t) {
+  std::unique_lock<std::mutex> lock(mu_);
+  ++waiter_count_;
+
+  // Loop until we find a wakeup to consume or timeout.
+  // 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 (wakeup_count_ == 0) {
+    if (!first_pass) MaybeBecomeIdle();
+    // No wakeups available, time to wait.
+    if (!t.has_timeout()) {
+      cv_.wait(lock);
+    } else {
+      auto wait_result = t.SupportsSteadyClock() && t.is_relative_timeout()
+                             ? cv_.wait_for(lock, t.ToChronoDuration())
+                             : cv_.wait_until(lock, t.ToChronoTimePoint());
+      if (wait_result == std::cv_status::timeout) {
+        --waiter_count_;
+        return false;
+      }
+    }
+    first_pass = false;
+  }
+
+  // Consume a wakeup and we're done.
+  --wakeup_count_;
+  --waiter_count_;
+  return true;
+}
+
+void StdcppWaiter::Post() {
+  std::lock_guard<std::mutex> lock(mu_);
+  ++wakeup_count_;
+  InternalCondVarPoke();
+}
+
+void StdcppWaiter::Poke() {
+  std::lock_guard<std::mutex> lock(mu_);
+  InternalCondVarPoke();
+}
+
+void StdcppWaiter::InternalCondVarPoke() {
+  if (waiter_count_ != 0) {
+    cv_.notify_one();
+  }
+}
+
+}  // namespace synchronization_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_INTERNAL_HAVE_STDCPP_WAITER
diff --git a/absl/synchronization/internal/stdcpp_waiter.h b/absl/synchronization/internal/stdcpp_waiter.h
new file mode 100644
index 0000000..e592a27
--- /dev/null
+++ b/absl/synchronization/internal/stdcpp_waiter.h
@@ -0,0 +1,56 @@
+// Copyright 2023 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_SYNCHRONIZATION_INTERNAL_STDCPP_WAITER_H_
+#define ABSL_SYNCHRONIZATION_INTERNAL_STDCPP_WAITER_H_
+
+#include <condition_variable>  // NOLINT(build/c++11)
+#include <mutex>  // NOLINT(build/c++11)
+
+#include "absl/base/config.h"
+#include "absl/synchronization/internal/kernel_timeout.h"
+#include "absl/synchronization/internal/waiter_base.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace synchronization_internal {
+
+#define ABSL_INTERNAL_HAVE_STDCPP_WAITER 1
+
+class StdcppWaiter : public WaiterCrtp<StdcppWaiter> {
+ public:
+  StdcppWaiter();
+
+  bool Wait(KernelTimeout t);
+  void Post();
+  void Poke();
+
+  static constexpr char kName[] = "StdcppWaiter";
+
+ private:
+  // REQUIRES: mu_ must be held.
+  void InternalCondVarPoke();
+
+  std::mutex mu_;
+  std::condition_variable cv_;
+  int waiter_count_;
+  int wakeup_count_;  // Unclaimed wakeups.
+};
+
+}  // namespace synchronization_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_SYNCHRONIZATION_INTERNAL_STDCPP_WAITER_H_
diff --git a/absl/synchronization/internal/thread_pool.h b/absl/synchronization/internal/thread_pool.h
index 0cb96da..5eb0bb6 100644
--- a/absl/synchronization/internal/thread_pool.h
+++ b/absl/synchronization/internal/thread_pool.h
@@ -20,9 +20,11 @@
 #include <functional>
 #include <queue>
 #include <thread>  // NOLINT(build/c++11)
+#include <utility>
 #include <vector>
 
 #include "absl/base/thread_annotations.h"
+#include "absl/functional/any_invocable.h"
 #include "absl/synchronization/mutex.h"
 
 namespace absl {
@@ -33,6 +35,7 @@
 class ThreadPool {
  public:
   explicit ThreadPool(int num_threads) {
+    threads_.reserve(num_threads);
     for (int i = 0; i < num_threads; ++i) {
       threads_.push_back(std::thread(&ThreadPool::WorkLoop, this));
     }
@@ -54,7 +57,7 @@
   }
 
   // Schedule a function to be run on a ThreadPool thread immediately.
-  void Schedule(std::function<void()> func) {
+  void Schedule(absl::AnyInvocable<void()> func) {
     assert(func != nullptr);
     absl::MutexLock l(&mu_);
     queue_.push(std::move(func));
@@ -67,7 +70,7 @@
 
   void WorkLoop() {
     while (true) {
-      std::function<void()> func;
+      absl::AnyInvocable<void()> func;
       {
         absl::MutexLock l(&mu_);
         mu_.Await(absl::Condition(this, &ThreadPool::WorkAvailable));
@@ -82,7 +85,7 @@
   }
 
   absl::Mutex mu_;
-  std::queue<std::function<void()>> queue_ ABSL_GUARDED_BY(mu_);
+  std::queue<absl::AnyInvocable<void()>> queue_ ABSL_GUARDED_BY(mu_);
   std::vector<std::thread> threads_;
 };
 
diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc
deleted file mode 100644
index f2051d6..0000000
--- a/absl/synchronization/internal/waiter.cc
+++ /dev/null
@@ -1,403 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/synchronization/internal/waiter.h"
-
-#include "absl/base/config.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#else
-#include <pthread.h>
-#include <sys/time.h>
-#include <unistd.h>
-#endif
-
-#ifdef __linux__
-#include <linux/futex.h>
-#include <sys/syscall.h>
-#endif
-
-#ifdef ABSL_HAVE_SEMAPHORE_H
-#include <semaphore.h>
-#endif
-
-#include <errno.h>
-#include <stdio.h>
-#include <time.h>
-
-#include <atomic>
-#include <cassert>
-#include <cstdint>
-#include <new>
-#include <type_traits>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/thread_identity.h"
-#include "absl/base/optimization.h"
-#include "absl/synchronization/internal/kernel_timeout.h"
-
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace synchronization_internal {
-
-static void MaybeBecomeIdle() {
-  base_internal::ThreadIdentity *identity =
-      base_internal::CurrentThreadIdentityIfPresent();
-  assert(identity != nullptr);
-  const bool is_idle = identity->is_idle.load(std::memory_order_relaxed);
-  const int ticker = identity->ticker.load(std::memory_order_relaxed);
-  const int wait_start = identity->wait_start.load(std::memory_order_relaxed);
-  if (!is_idle && ticker - wait_start > Waiter::kIdlePeriods) {
-    identity->is_idle.store(true, std::memory_order_relaxed);
-  }
-}
-
-#if ABSL_WAITER_MODE == ABSL_WAITER_MODE_FUTEX
-
-Waiter::Waiter() {
-  futex_.store(0, std::memory_order_relaxed);
-}
-
-bool Waiter::Wait(KernelTimeout t) {
-  // Loop until we can atomically decrement futex from a positive
-  // value, waiting on a futex while we believe it is zero.
-  // 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) {
-      if (!futex_.compare_exchange_weak(x, x - 1,
-                                        std::memory_order_acquire,
-                                        std::memory_order_relaxed)) {
-        continue;  // Raced with someone, retry.
-      }
-      return true;  // Consumed a wakeup, we are done.
-    }
-
-    if (!first_pass) MaybeBecomeIdle();
-    const int err = Futex::WaitUntil(&futex_, 0, t);
-    if (err != 0) {
-      if (err == -EINTR || err == -EWOULDBLOCK) {
-        // Do nothing, the loop will retry.
-      } else if (err == -ETIMEDOUT) {
-        return false;
-      } else {
-        ABSL_RAW_LOG(FATAL, "Futex operation failed with error %d\n", err);
-      }
-    }
-    first_pass = false;
-  }
-}
-
-void Waiter::Post() {
-  if (futex_.fetch_add(1, std::memory_order_release) == 0) {
-    // We incremented from 0, need to wake a potential waiter.
-    Poke();
-  }
-}
-
-void Waiter::Poke() {
-  // Wake one thread waiting on the futex.
-  const int err = Futex::Wake(&futex_, 1);
-  if (ABSL_PREDICT_FALSE(err < 0)) {
-    ABSL_RAW_LOG(FATAL, "Futex operation failed with error %d\n", err);
-  }
-}
-
-#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_CONDVAR
-
-class PthreadMutexHolder {
- public:
-  explicit PthreadMutexHolder(pthread_mutex_t *mu) : mu_(mu) {
-    const int err = pthread_mutex_lock(mu_);
-    if (err != 0) {
-      ABSL_RAW_LOG(FATAL, "pthread_mutex_lock failed: %d", err);
-    }
-  }
-
-  PthreadMutexHolder(const PthreadMutexHolder &rhs) = delete;
-  PthreadMutexHolder &operator=(const PthreadMutexHolder &rhs) = delete;
-
-  ~PthreadMutexHolder() {
-    const int err = pthread_mutex_unlock(mu_);
-    if (err != 0) {
-      ABSL_RAW_LOG(FATAL, "pthread_mutex_unlock failed: %d", err);
-    }
-  }
-
- private:
-  pthread_mutex_t *mu_;
-};
-
-Waiter::Waiter() {
-  const int err = pthread_mutex_init(&mu_, 0);
-  if (err != 0) {
-    ABSL_RAW_LOG(FATAL, "pthread_mutex_init failed: %d", err);
-  }
-
-  const int err2 = pthread_cond_init(&cv_, 0);
-  if (err2 != 0) {
-    ABSL_RAW_LOG(FATAL, "pthread_cond_init failed: %d", err2);
-  }
-
-  waiter_count_ = 0;
-  wakeup_count_ = 0;
-}
-
-bool Waiter::Wait(KernelTimeout t) {
-  struct timespec abs_timeout;
-  if (t.has_timeout()) {
-    abs_timeout = t.MakeAbsTimespec();
-  }
-
-  PthreadMutexHolder h(&mu_);
-  ++waiter_count_;
-  // Loop until we find a wakeup to consume or timeout.
-  // 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 (wakeup_count_ == 0) {
-    if (!first_pass) MaybeBecomeIdle();
-    // No wakeups available, time to wait.
-    if (!t.has_timeout()) {
-      const int err = pthread_cond_wait(&cv_, &mu_);
-      if (err != 0) {
-        ABSL_RAW_LOG(FATAL, "pthread_cond_wait failed: %d", err);
-      }
-    } else {
-      const int err = pthread_cond_timedwait(&cv_, &mu_, &abs_timeout);
-      if (err == ETIMEDOUT) {
-        --waiter_count_;
-        return false;
-      }
-      if (err != 0) {
-        ABSL_RAW_LOG(FATAL, "pthread_cond_timedwait failed: %d", err);
-      }
-    }
-    first_pass = false;
-  }
-  // Consume a wakeup and we're done.
-  --wakeup_count_;
-  --waiter_count_;
-  return true;
-}
-
-void Waiter::Post() {
-  PthreadMutexHolder h(&mu_);
-  ++wakeup_count_;
-  InternalCondVarPoke();
-}
-
-void Waiter::Poke() {
-  PthreadMutexHolder h(&mu_);
-  InternalCondVarPoke();
-}
-
-void Waiter::InternalCondVarPoke() {
-  if (waiter_count_ != 0) {
-    const int err = pthread_cond_signal(&cv_);
-    if (ABSL_PREDICT_FALSE(err != 0)) {
-      ABSL_RAW_LOG(FATAL, "pthread_cond_signal failed: %d", err);
-    }
-  }
-}
-
-#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_SEM
-
-Waiter::Waiter() {
-  if (sem_init(&sem_, 0, 0) != 0) {
-    ABSL_RAW_LOG(FATAL, "sem_init failed with errno %d\n", errno);
-  }
-  wakeups_.store(0, std::memory_order_relaxed);
-}
-
-bool Waiter::Wait(KernelTimeout t) {
-  struct timespec abs_timeout;
-  if (t.has_timeout()) {
-    abs_timeout = t.MakeAbsTimespec();
-  }
-
-  // Loop until we timeout or consume a wakeup.
-  // 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) {
-    int x = wakeups_.load(std::memory_order_relaxed);
-    while (x != 0) {
-      if (!wakeups_.compare_exchange_weak(x, x - 1,
-                                          std::memory_order_acquire,
-                                          std::memory_order_relaxed)) {
-        continue;  // Raced with someone, retry.
-      }
-      // Successfully consumed a wakeup, we're done.
-      return true;
-    }
-
-    if (!first_pass) MaybeBecomeIdle();
-    // Nothing to consume, wait (looping on EINTR).
-    while (true) {
-      if (!t.has_timeout()) {
-        if (sem_wait(&sem_) == 0) break;
-        if (errno == EINTR) continue;
-        ABSL_RAW_LOG(FATAL, "sem_wait failed: %d", errno);
-      } else {
-        if (sem_timedwait(&sem_, &abs_timeout) == 0) break;
-        if (errno == EINTR) continue;
-        if (errno == ETIMEDOUT) return false;
-        ABSL_RAW_LOG(FATAL, "sem_timedwait failed: %d", errno);
-      }
-    }
-    first_pass = false;
-  }
-}
-
-void Waiter::Post() {
-  // Post a wakeup.
-  if (wakeups_.fetch_add(1, std::memory_order_release) == 0) {
-    // We incremented from 0, need to wake a potential waiter.
-    Poke();
-  }
-}
-
-void Waiter::Poke() {
-  if (sem_post(&sem_) != 0) {  // Wake any semaphore waiter.
-    ABSL_RAW_LOG(FATAL, "sem_post failed with errno %d\n", errno);
-  }
-}
-
-#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_WIN32
-
-class Waiter::WinHelper {
- public:
-  static SRWLOCK *GetLock(Waiter *w) {
-    return reinterpret_cast<SRWLOCK *>(&w->mu_storage_);
-  }
-
-  static CONDITION_VARIABLE *GetCond(Waiter *w) {
-    return reinterpret_cast<CONDITION_VARIABLE *>(&w->cv_storage_);
-  }
-
-  static_assert(sizeof(SRWLOCK) == sizeof(void *),
-                "`mu_storage_` does not have the same size as SRWLOCK");
-  static_assert(alignof(SRWLOCK) == alignof(void *),
-                "`mu_storage_` does not have the same alignment as SRWLOCK");
-
-  static_assert(sizeof(CONDITION_VARIABLE) == sizeof(void *),
-                "`ABSL_CONDITION_VARIABLE_STORAGE` does not have the same size "
-                "as `CONDITION_VARIABLE`");
-  static_assert(
-      alignof(CONDITION_VARIABLE) == alignof(void *),
-      "`cv_storage_` does not have the same alignment as `CONDITION_VARIABLE`");
-
-  // The SRWLOCK and CONDITION_VARIABLE types must be trivially constructible
-  // and destructible because we never call their constructors or destructors.
-  static_assert(std::is_trivially_constructible<SRWLOCK>::value,
-                "The `SRWLOCK` type must be trivially constructible");
-  static_assert(
-      std::is_trivially_constructible<CONDITION_VARIABLE>::value,
-      "The `CONDITION_VARIABLE` type must be trivially constructible");
-  static_assert(std::is_trivially_destructible<SRWLOCK>::value,
-                "The `SRWLOCK` type must be trivially destructible");
-  static_assert(std::is_trivially_destructible<CONDITION_VARIABLE>::value,
-                "The `CONDITION_VARIABLE` type must be trivially destructible");
-};
-
-class LockHolder {
- public:
-  explicit LockHolder(SRWLOCK* mu) : mu_(mu) {
-    AcquireSRWLockExclusive(mu_);
-  }
-
-  LockHolder(const LockHolder&) = delete;
-  LockHolder& operator=(const LockHolder&) = delete;
-
-  ~LockHolder() {
-    ReleaseSRWLockExclusive(mu_);
-  }
-
- private:
-  SRWLOCK* mu_;
-};
-
-Waiter::Waiter() {
-  auto *mu = ::new (static_cast<void *>(&mu_storage_)) SRWLOCK;
-  auto *cv = ::new (static_cast<void *>(&cv_storage_)) CONDITION_VARIABLE;
-  InitializeSRWLock(mu);
-  InitializeConditionVariable(cv);
-  waiter_count_ = 0;
-  wakeup_count_ = 0;
-}
-
-bool Waiter::Wait(KernelTimeout t) {
-  SRWLOCK *mu = WinHelper::GetLock(this);
-  CONDITION_VARIABLE *cv = WinHelper::GetCond(this);
-
-  LockHolder h(mu);
-  ++waiter_count_;
-
-  // Loop until we find a wakeup to consume or timeout.
-  // 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 (wakeup_count_ == 0) {
-    if (!first_pass) MaybeBecomeIdle();
-    // No wakeups available, time to wait.
-    if (!SleepConditionVariableSRW(cv, mu, t.InMillisecondsFromNow(), 0)) {
-      // GetLastError() returns a Win32 DWORD, but we assign to
-      // unsigned long to simplify the ABSL_RAW_LOG case below.  The uniform
-      // initialization guarantees this is not a narrowing conversion.
-      const unsigned long err{GetLastError()};  // NOLINT(runtime/int)
-      if (err == ERROR_TIMEOUT) {
-        --waiter_count_;
-        return false;
-      } else {
-        ABSL_RAW_LOG(FATAL, "SleepConditionVariableSRW failed: %lu", err);
-      }
-    }
-    first_pass = false;
-  }
-  // Consume a wakeup and we're done.
-  --wakeup_count_;
-  --waiter_count_;
-  return true;
-}
-
-void Waiter::Post() {
-  LockHolder h(WinHelper::GetLock(this));
-  ++wakeup_count_;
-  InternalCondVarPoke();
-}
-
-void Waiter::Poke() {
-  LockHolder h(WinHelper::GetLock(this));
-  InternalCondVarPoke();
-}
-
-void Waiter::InternalCondVarPoke() {
-  if (waiter_count_ != 0) {
-    WakeConditionVariable(WinHelper::GetCond(this));
-  }
-}
-
-#else
-#error Unknown ABSL_WAITER_MODE
-#endif
-
-}  // namespace synchronization_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h
index b8adfeb..1a8b0b8 100644
--- a/absl/synchronization/internal/waiter.h
+++ b/absl/synchronization/internal/waiter.h
@@ -17,142 +17,48 @@
 #define ABSL_SYNCHRONIZATION_INTERNAL_WAITER_H_
 
 #include "absl/base/config.h"
-
-#ifdef _WIN32
-#include <sdkddkver.h>
-#else
-#include <pthread.h>
-#endif
-
-#ifdef __linux__
-#include <linux/futex.h>
-#endif
-
-#ifdef ABSL_HAVE_SEMAPHORE_H
-#include <semaphore.h>
-#endif
-
-#include <atomic>
-#include <cstdint>
-
-#include "absl/base/internal/thread_identity.h"
-#include "absl/synchronization/internal/futex.h"
-#include "absl/synchronization/internal/kernel_timeout.h"
+#include "absl/synchronization/internal/futex_waiter.h"
+#include "absl/synchronization/internal/pthread_waiter.h"
+#include "absl/synchronization/internal/sem_waiter.h"
+#include "absl/synchronization/internal/stdcpp_waiter.h"
+#include "absl/synchronization/internal/win32_waiter.h"
 
 // May be chosen at compile time via -DABSL_FORCE_WAITER_MODE=<index>
 #define ABSL_WAITER_MODE_FUTEX 0
 #define ABSL_WAITER_MODE_SEM 1
 #define ABSL_WAITER_MODE_CONDVAR 2
 #define ABSL_WAITER_MODE_WIN32 3
+#define ABSL_WAITER_MODE_STDCPP 4
 
 #if defined(ABSL_FORCE_WAITER_MODE)
 #define ABSL_WAITER_MODE ABSL_FORCE_WAITER_MODE
-#elif defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#elif defined(ABSL_INTERNAL_HAVE_WIN32_WAITER)
 #define ABSL_WAITER_MODE ABSL_WAITER_MODE_WIN32
-#elif defined(ABSL_INTERNAL_HAVE_FUTEX)
+#elif defined(ABSL_INTERNAL_HAVE_FUTEX_WAITER)
 #define ABSL_WAITER_MODE ABSL_WAITER_MODE_FUTEX
-#elif defined(ABSL_HAVE_SEMAPHORE_H)
+#elif defined(ABSL_INTERNAL_HAVE_SEM_WAITER)
 #define ABSL_WAITER_MODE ABSL_WAITER_MODE_SEM
-#else
+#elif defined(ABSL_INTERNAL_HAVE_PTHREAD_WAITER)
 #define ABSL_WAITER_MODE ABSL_WAITER_MODE_CONDVAR
+#else
+#error ABSL_WAITER_MODE is undefined
 #endif
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace synchronization_internal {
 
-// Waiter is an OS-specific semaphore.
-class Waiter {
- public:
-  // Prepare any data to track waits.
-  Waiter();
-
-  // Not copyable or movable
-  Waiter(const Waiter&) = delete;
-  Waiter& operator=(const Waiter&) = delete;
-
-  // Blocks the calling thread until a matching call to `Post()` or
-  // `t` has passed. Returns `true` if woken (`Post()` called),
-  // `false` on timeout.
-  bool Wait(KernelTimeout t);
-
-  // Restart the caller of `Wait()` as with a normal semaphore.
-  void Post();
-
-  // If anyone is waiting, wake them up temporarily and cause them to
-  // call `MaybeBecomeIdle()`. They will then return to waiting for a
-  // `Post()` or timeout.
-  void Poke();
-
-  // Returns the Waiter associated with the identity.
-  static Waiter* GetWaiter(base_internal::ThreadIdentity* identity) {
-    static_assert(
-        sizeof(Waiter) <= sizeof(base_internal::ThreadIdentity::WaiterState),
-        "Insufficient space for Waiter");
-    return reinterpret_cast<Waiter*>(identity->waiter_state.data);
-  }
-
-  // How many periods to remain idle before releasing resources
-#ifndef ABSL_HAVE_THREAD_SANITIZER
-  static constexpr int kIdlePeriods = 60;
-#else
-  // Memory consumption under ThreadSanitizer is a serious concern,
-  // so we release resources sooner. The value of 1 leads to 1 to 2 second
-  // delay before marking a thread as idle.
-  static const int kIdlePeriods = 1;
-#endif
-
- private:
-  // The destructor must not be called since Mutex/CondVar
-  // can use PerThreadSem/Waiter after the thread exits.
-  // Waiter objects are embedded in ThreadIdentity objects,
-  // which are reused via a freelist and are never destroyed.
-  ~Waiter() = delete;
-
 #if ABSL_WAITER_MODE == ABSL_WAITER_MODE_FUTEX
-  // Futexes are defined by specification to be 32-bits.
-  // Thus std::atomic<int32_t> must be just an int32_t with lockfree methods.
-  std::atomic<int32_t> futex_;
-  static_assert(sizeof(int32_t) == sizeof(futex_), "Wrong size for futex");
-
-#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_CONDVAR
-  // REQUIRES: mu_ must be held.
-  void InternalCondVarPoke();
-
-  pthread_mutex_t mu_;
-  pthread_cond_t cv_;
-  int waiter_count_;
-  int wakeup_count_;  // Unclaimed wakeups.
-
+using Waiter = FutexWaiter;
 #elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_SEM
-  sem_t sem_;
-  // This seems superfluous, but for Poke() we need to cause spurious
-  // wakeups on the semaphore. Hence we can't actually use the
-  // semaphore's count.
-  std::atomic<int> wakeups_;
-
+using Waiter = SemWaiter;
+#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_CONDVAR
+using Waiter = PthreadWaiter;
 #elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_WIN32
-  // WinHelper - Used to define utilities for accessing the lock and
-  // condition variable storage once the types are complete.
-  class WinHelper;
-
-  // REQUIRES: WinHelper::GetLock(this) must be held.
-  void InternalCondVarPoke();
-
-  // We can't include Windows.h in our headers, so we use aligned character
-  // buffers to define the storage of SRWLOCK and CONDITION_VARIABLE.
-  // SRW locks and condition variables do not need to be explicitly destroyed.
-  // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-initializesrwlock
-  // https://stackoverflow.com/questions/28975958/why-does-windows-have-no-deleteconditionvariable-function-to-go-together-with
-  alignas(void*) unsigned char mu_storage_[sizeof(void*)];
-  alignas(void*) unsigned char cv_storage_[sizeof(void*)];
-  int waiter_count_;
-  int wakeup_count_;
-
-#else
-  #error Unknown ABSL_WAITER_MODE
+using Waiter = Win32Waiter;
+#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_STDCPP
+using Waiter = StdcppWaiter;
 #endif
-};
 
 }  // namespace synchronization_internal
 ABSL_NAMESPACE_END
diff --git a/absl/synchronization/internal/waiter_base.cc b/absl/synchronization/internal/waiter_base.cc
new file mode 100644
index 0000000..46928b4
--- /dev/null
+++ b/absl/synchronization/internal/waiter_base.cc
@@ -0,0 +1,42 @@
+// Copyright 2023 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/synchronization/internal/waiter_base.h"
+
+#include "absl/base/config.h"
+#include "absl/base/internal/thread_identity.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace synchronization_internal {
+
+#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
+constexpr int WaiterBase::kIdlePeriods;
+#endif
+
+void WaiterBase::MaybeBecomeIdle() {
+  base_internal::ThreadIdentity *identity =
+      base_internal::CurrentThreadIdentityIfPresent();
+  assert(identity != nullptr);
+  const bool is_idle = identity->is_idle.load(std::memory_order_relaxed);
+  const int ticker = identity->ticker.load(std::memory_order_relaxed);
+  const int wait_start = identity->wait_start.load(std::memory_order_relaxed);
+  if (!is_idle && ticker - wait_start > kIdlePeriods) {
+    identity->is_idle.store(true, std::memory_order_relaxed);
+  }
+}
+
+}  // namespace synchronization_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/synchronization/internal/waiter_base.h b/absl/synchronization/internal/waiter_base.h
new file mode 100644
index 0000000..cf17548
--- /dev/null
+++ b/absl/synchronization/internal/waiter_base.h
@@ -0,0 +1,90 @@
+// Copyright 2023 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_SYNCHRONIZATION_INTERNAL_WAITER_BASE_H_
+#define ABSL_SYNCHRONIZATION_INTERNAL_WAITER_BASE_H_
+
+#include "absl/base/config.h"
+#include "absl/base/internal/thread_identity.h"
+#include "absl/synchronization/internal/kernel_timeout.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace synchronization_internal {
+
+// `Waiter` is a platform specific semaphore implementation that `PerThreadSem`
+// waits on to implement blocking in `absl::Mutex`.  Implementations should
+// inherit from `WaiterCrtp` and must implement `Wait()`, `Post()`, and `Poke()`
+// as described in `WaiterBase`.  `waiter.h` selects the implementation and uses
+// static-dispatch for performance.
+class WaiterBase {
+ public:
+  WaiterBase() = default;
+
+  // Not copyable or movable
+  WaiterBase(const WaiterBase&) = delete;
+  WaiterBase& operator=(const WaiterBase&) = delete;
+
+  // Blocks the calling thread until a matching call to `Post()` or
+  // `t` has passed. Returns `true` if woken (`Post()` called),
+  // `false` on timeout.
+  //
+  // bool Wait(KernelTimeout t);
+
+  // Restart the caller of `Wait()` as with a normal semaphore.
+  //
+  // void Post();
+
+  // If anyone is waiting, wake them up temporarily and cause them to
+  // call `MaybeBecomeIdle()`. They will then return to waiting for a
+  // `Post()` or timeout.
+  //
+  // void Poke();
+
+  // Returns the name of this implementation. Used only for debugging.
+  //
+  // static constexpr char kName[];
+
+  // How many periods to remain idle before releasing resources
+#ifndef ABSL_HAVE_THREAD_SANITIZER
+  static constexpr int kIdlePeriods = 60;
+#else
+  // Memory consumption under ThreadSanitizer is a serious concern,
+  // so we release resources sooner. The value of 1 leads to 1 to 2 second
+  // delay before marking a thread as idle.
+  static constexpr int kIdlePeriods = 1;
+#endif
+
+ protected:
+  static void MaybeBecomeIdle();
+};
+
+template <typename T>
+class WaiterCrtp : public WaiterBase {
+ public:
+  // Returns the Waiter associated with the identity.
+  static T* GetWaiter(base_internal::ThreadIdentity* identity) {
+    static_assert(
+        sizeof(T) <= sizeof(base_internal::ThreadIdentity::WaiterState),
+        "Insufficient space for Waiter");
+    return reinterpret_cast<T*>(identity->waiter_state.data);
+  }
+};
+
+}  // namespace synchronization_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_SYNCHRONIZATION_INTERNAL_WAITER_BASE_H_
diff --git a/absl/synchronization/internal/waiter_test.cc b/absl/synchronization/internal/waiter_test.cc
new file mode 100644
index 0000000..992db29
--- /dev/null
+++ b/absl/synchronization/internal/waiter_test.cc
@@ -0,0 +1,180 @@
+// Copyright 2023 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/synchronization/internal/waiter.h"
+
+#include <ctime>
+#include <iostream>
+#include <ostream>
+
+#include "absl/base/config.h"
+#include "absl/random/random.h"
+#include "absl/synchronization/internal/create_thread_identity.h"
+#include "absl/synchronization/internal/futex_waiter.h"
+#include "absl/synchronization/internal/kernel_timeout.h"
+#include "absl/synchronization/internal/pthread_waiter.h"
+#include "absl/synchronization/internal/sem_waiter.h"
+#include "absl/synchronization/internal/stdcpp_waiter.h"
+#include "absl/synchronization/internal/thread_pool.h"
+#include "absl/synchronization/internal/win32_waiter.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
+#include "gtest/gtest.h"
+
+// Test go/btm support by randomizing the value of clock_gettime() for
+// CLOCK_MONOTONIC. This works by overriding a weak symbol in glibc.
+// We should be resistant to this randomization when !SupportsSteadyClock().
+#if defined(__GOOGLE_GRTE_VERSION__) &&      \
+    !defined(ABSL_HAVE_ADDRESS_SANITIZER) && \
+    !defined(ABSL_HAVE_MEMORY_SANITIZER) &&  \
+    !defined(ABSL_HAVE_THREAD_SANITIZER)
+extern "C" int __clock_gettime(clockid_t c, struct timespec* ts);
+
+extern "C" int clock_gettime(clockid_t c, struct timespec* ts) {
+  if (c == CLOCK_MONOTONIC &&
+      !absl::synchronization_internal::KernelTimeout::SupportsSteadyClock()) {
+    absl::SharedBitGen gen;
+    ts->tv_sec = absl::Uniform(gen, 0, 1'000'000'000);
+    ts->tv_nsec = absl::Uniform(gen, 0, 1'000'000'000);
+    return 0;
+  }
+  return __clock_gettime(c, ts);
+}
+#endif
+
+namespace {
+
+TEST(Waiter, PrintPlatformImplementation) {
+  // Allows us to verify that the platform is using the expected implementation.
+  std::cout << absl::synchronization_internal::Waiter::kName << std::endl;
+}
+
+template <typename T>
+class WaiterTest : public ::testing::Test {
+ public:
+  // Waiter implementations assume that a ThreadIdentity has already been
+  // created.
+  WaiterTest() {
+    absl::synchronization_internal::GetOrCreateCurrentThreadIdentity();
+  }
+};
+
+TYPED_TEST_SUITE_P(WaiterTest);
+
+absl::Duration WithTolerance(absl::Duration d) { return d * 0.95; }
+
+TYPED_TEST_P(WaiterTest, WaitNoTimeout) {
+  absl::synchronization_internal::ThreadPool tp(1);
+  TypeParam waiter;
+  tp.Schedule([&]() {
+    // Include some `Poke()` calls to ensure they don't cause `waiter` to return
+    // from `Wait()`.
+    waiter.Poke();
+    absl::SleepFor(absl::Seconds(1));
+    waiter.Poke();
+    absl::SleepFor(absl::Seconds(1));
+    waiter.Post();
+  });
+  absl::Time start = absl::Now();
+  EXPECT_TRUE(
+      waiter.Wait(absl::synchronization_internal::KernelTimeout::Never()));
+  absl::Duration waited = absl::Now() - start;
+  EXPECT_GE(waited, WithTolerance(absl::Seconds(2)));
+}
+
+TYPED_TEST_P(WaiterTest, WaitDurationWoken) {
+  absl::synchronization_internal::ThreadPool tp(1);
+  TypeParam waiter;
+  tp.Schedule([&]() {
+    // Include some `Poke()` calls to ensure they don't cause `waiter` to return
+    // from `Wait()`.
+    waiter.Poke();
+    absl::SleepFor(absl::Milliseconds(500));
+    waiter.Post();
+  });
+  absl::Time start = absl::Now();
+  EXPECT_TRUE(waiter.Wait(
+      absl::synchronization_internal::KernelTimeout(absl::Seconds(10))));
+  absl::Duration waited = absl::Now() - start;
+  EXPECT_GE(waited, WithTolerance(absl::Milliseconds(500)));
+  EXPECT_LT(waited, absl::Seconds(2));
+}
+
+TYPED_TEST_P(WaiterTest, WaitTimeWoken) {
+  absl::synchronization_internal::ThreadPool tp(1);
+  TypeParam waiter;
+  tp.Schedule([&]() {
+    // Include some `Poke()` calls to ensure they don't cause `waiter` to return
+    // from `Wait()`.
+    waiter.Poke();
+    absl::SleepFor(absl::Milliseconds(500));
+    waiter.Post();
+  });
+  absl::Time start = absl::Now();
+  EXPECT_TRUE(waiter.Wait(absl::synchronization_internal::KernelTimeout(
+      start + absl::Seconds(10))));
+  absl::Duration waited = absl::Now() - start;
+  EXPECT_GE(waited, WithTolerance(absl::Milliseconds(500)));
+  EXPECT_LT(waited, absl::Seconds(2));
+}
+
+TYPED_TEST_P(WaiterTest, WaitDurationReached) {
+  TypeParam waiter;
+  absl::Time start = absl::Now();
+  EXPECT_FALSE(waiter.Wait(
+      absl::synchronization_internal::KernelTimeout(absl::Milliseconds(500))));
+  absl::Duration waited = absl::Now() - start;
+  EXPECT_GE(waited, WithTolerance(absl::Milliseconds(500)));
+  EXPECT_LT(waited, absl::Seconds(1));
+}
+
+TYPED_TEST_P(WaiterTest, WaitTimeReached) {
+  TypeParam waiter;
+  absl::Time start = absl::Now();
+  EXPECT_FALSE(waiter.Wait(absl::synchronization_internal::KernelTimeout(
+      start + absl::Milliseconds(500))));
+  absl::Duration waited = absl::Now() - start;
+  EXPECT_GE(waited, WithTolerance(absl::Milliseconds(500)));
+  EXPECT_LT(waited, absl::Seconds(1));
+}
+
+REGISTER_TYPED_TEST_SUITE_P(WaiterTest,
+                            WaitNoTimeout,
+                            WaitDurationWoken,
+                            WaitTimeWoken,
+                            WaitDurationReached,
+                            WaitTimeReached);
+
+#ifdef ABSL_INTERNAL_HAVE_FUTEX_WAITER
+INSTANTIATE_TYPED_TEST_SUITE_P(Futex, WaiterTest,
+                               absl::synchronization_internal::FutexWaiter);
+#endif
+#ifdef ABSL_INTERNAL_HAVE_PTHREAD_WAITER
+INSTANTIATE_TYPED_TEST_SUITE_P(Pthread, WaiterTest,
+                               absl::synchronization_internal::PthreadWaiter);
+#endif
+#ifdef ABSL_INTERNAL_HAVE_SEM_WAITER
+INSTANTIATE_TYPED_TEST_SUITE_P(Sem, WaiterTest,
+                               absl::synchronization_internal::SemWaiter);
+#endif
+#ifdef ABSL_INTERNAL_HAVE_WIN32_WAITER
+INSTANTIATE_TYPED_TEST_SUITE_P(Win32, WaiterTest,
+                               absl::synchronization_internal::Win32Waiter);
+#endif
+#ifdef ABSL_INTERNAL_HAVE_STDCPP_WAITER
+INSTANTIATE_TYPED_TEST_SUITE_P(Stdcpp, WaiterTest,
+                               absl::synchronization_internal::StdcppWaiter);
+#endif
+
+}  // namespace
diff --git a/absl/synchronization/internal/win32_waiter.cc b/absl/synchronization/internal/win32_waiter.cc
new file mode 100644
index 0000000..bd95ff0
--- /dev/null
+++ b/absl/synchronization/internal/win32_waiter.cc
@@ -0,0 +1,151 @@
+// Copyright 2023 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/synchronization/internal/win32_waiter.h"
+
+#ifdef ABSL_INTERNAL_HAVE_WIN32_WAITER
+
+#include <windows.h>
+
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/internal/thread_identity.h"
+#include "absl/base/optimization.h"
+#include "absl/synchronization/internal/kernel_timeout.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace synchronization_internal {
+
+#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
+constexpr char Win32Waiter::kName[];
+#endif
+
+class Win32Waiter::WinHelper {
+ public:
+  static SRWLOCK *GetLock(Win32Waiter *w) {
+    return reinterpret_cast<SRWLOCK *>(&w->mu_storage_);
+  }
+
+  static CONDITION_VARIABLE *GetCond(Win32Waiter *w) {
+    return reinterpret_cast<CONDITION_VARIABLE *>(&w->cv_storage_);
+  }
+
+  static_assert(sizeof(SRWLOCK) == sizeof(void *),
+                "`mu_storage_` does not have the same size as SRWLOCK");
+  static_assert(alignof(SRWLOCK) == alignof(void *),
+                "`mu_storage_` does not have the same alignment as SRWLOCK");
+
+  static_assert(sizeof(CONDITION_VARIABLE) == sizeof(void *),
+                "`ABSL_CONDITION_VARIABLE_STORAGE` does not have the same size "
+                "as `CONDITION_VARIABLE`");
+  static_assert(
+      alignof(CONDITION_VARIABLE) == alignof(void *),
+      "`cv_storage_` does not have the same alignment as `CONDITION_VARIABLE`");
+
+  // The SRWLOCK and CONDITION_VARIABLE types must be trivially constructible
+  // and destructible because we never call their constructors or destructors.
+  static_assert(std::is_trivially_constructible<SRWLOCK>::value,
+                "The `SRWLOCK` type must be trivially constructible");
+  static_assert(
+      std::is_trivially_constructible<CONDITION_VARIABLE>::value,
+      "The `CONDITION_VARIABLE` type must be trivially constructible");
+  static_assert(std::is_trivially_destructible<SRWLOCK>::value,
+                "The `SRWLOCK` type must be trivially destructible");
+  static_assert(std::is_trivially_destructible<CONDITION_VARIABLE>::value,
+                "The `CONDITION_VARIABLE` type must be trivially destructible");
+};
+
+class LockHolder {
+ public:
+  explicit LockHolder(SRWLOCK* mu) : mu_(mu) {
+    AcquireSRWLockExclusive(mu_);
+  }
+
+  LockHolder(const LockHolder&) = delete;
+  LockHolder& operator=(const LockHolder&) = delete;
+
+  ~LockHolder() {
+    ReleaseSRWLockExclusive(mu_);
+  }
+
+ private:
+  SRWLOCK* mu_;
+};
+
+Win32Waiter::Win32Waiter() {
+  auto *mu = ::new (static_cast<void *>(&mu_storage_)) SRWLOCK;
+  auto *cv = ::new (static_cast<void *>(&cv_storage_)) CONDITION_VARIABLE;
+  InitializeSRWLock(mu);
+  InitializeConditionVariable(cv);
+  waiter_count_ = 0;
+  wakeup_count_ = 0;
+}
+
+bool Win32Waiter::Wait(KernelTimeout t) {
+  SRWLOCK *mu = WinHelper::GetLock(this);
+  CONDITION_VARIABLE *cv = WinHelper::GetCond(this);
+
+  LockHolder h(mu);
+  ++waiter_count_;
+
+  // Loop until we find a wakeup to consume or timeout.
+  // 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 (wakeup_count_ == 0) {
+    if (!first_pass) MaybeBecomeIdle();
+    // No wakeups available, time to wait.
+    if (!SleepConditionVariableSRW(cv, mu, t.InMillisecondsFromNow(), 0)) {
+      // GetLastError() returns a Win32 DWORD, but we assign to
+      // unsigned long to simplify the ABSL_RAW_LOG case below.  The uniform
+      // initialization guarantees this is not a narrowing conversion.
+      const unsigned long err{GetLastError()};  // NOLINT(runtime/int)
+      if (err == ERROR_TIMEOUT) {
+        --waiter_count_;
+        return false;
+      } else {
+        ABSL_RAW_LOG(FATAL, "SleepConditionVariableSRW failed: %lu", err);
+      }
+    }
+    first_pass = false;
+  }
+  // Consume a wakeup and we're done.
+  --wakeup_count_;
+  --waiter_count_;
+  return true;
+}
+
+void Win32Waiter::Post() {
+  LockHolder h(WinHelper::GetLock(this));
+  ++wakeup_count_;
+  InternalCondVarPoke();
+}
+
+void Win32Waiter::Poke() {
+  LockHolder h(WinHelper::GetLock(this));
+  InternalCondVarPoke();
+}
+
+void Win32Waiter::InternalCondVarPoke() {
+  if (waiter_count_ != 0) {
+    WakeConditionVariable(WinHelper::GetCond(this));
+  }
+}
+
+}  // namespace synchronization_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_INTERNAL_HAVE_WIN32_WAITER
diff --git a/absl/synchronization/internal/win32_waiter.h b/absl/synchronization/internal/win32_waiter.h
new file mode 100644
index 0000000..87eb617
--- /dev/null
+++ b/absl/synchronization/internal/win32_waiter.h
@@ -0,0 +1,70 @@
+// Copyright 2023 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_SYNCHRONIZATION_INTERNAL_WIN32_WAITER_H_
+#define ABSL_SYNCHRONIZATION_INTERNAL_WIN32_WAITER_H_
+
+#ifdef _WIN32
+#include <sdkddkver.h>
+#endif
+
+#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+
+#include "absl/base/config.h"
+#include "absl/synchronization/internal/kernel_timeout.h"
+#include "absl/synchronization/internal/waiter_base.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace synchronization_internal {
+
+#define ABSL_INTERNAL_HAVE_WIN32_WAITER 1
+
+class Win32Waiter : public WaiterCrtp<Win32Waiter> {
+ public:
+  Win32Waiter();
+
+  bool Wait(KernelTimeout t);
+  void Post();
+  void Poke();
+
+  static constexpr char kName[] = "Win32Waiter";
+
+ private:
+  // WinHelper - Used to define utilities for accessing the lock and
+  // condition variable storage once the types are complete.
+  class WinHelper;
+
+  // REQUIRES: WinHelper::GetLock(this) must be held.
+  void InternalCondVarPoke();
+
+  // We can't include Windows.h in our headers, so we use aligned character
+  // buffers to define the storage of SRWLOCK and CONDITION_VARIABLE.
+  // SRW locks and condition variables do not need to be explicitly destroyed.
+  // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-initializesrwlock
+  // https://stackoverflow.com/questions/28975958/why-does-windows-have-no-deleteconditionvariable-function-to-go-together-with
+  alignas(void*) unsigned char mu_storage_[sizeof(void*)];
+  alignas(void*) unsigned char cv_storage_[sizeof(void*)];
+  int waiter_count_;
+  int wakeup_count_;
+};
+
+}  // namespace synchronization_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+
+#endif  // ABSL_SYNCHRONIZATION_INTERNAL_WIN32_WAITER_H_
diff --git a/absl/synchronization/lifetime_test.cc b/absl/synchronization/lifetime_test.cc
index cc973a3..e627423 100644
--- a/absl/synchronization/lifetime_test.cc
+++ b/absl/synchronization/lifetime_test.cc
@@ -123,10 +123,10 @@
 };
 
 // These tests require that the compiler correctly supports C++11 constant
-// initialization... but MSVC has a known regression since v19.10:
+// initialization... but MSVC has a known regression since v19.10 till v19.25:
 // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html
-// TODO(epastor): Limit the affected range once MSVC fixes this bug.
-#if defined(__clang__) || !(defined(_MSC_VER) && _MSC_VER > 1900)
+#if defined(__clang__) || \
+    !(defined(_MSC_VER) && _MSC_VER > 1900 && _MSC_VER < 1925)
 // kConstInit
 // Test early usage.  (Declaration comes first; definitions must appear after
 // the test runner.)
diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc
index c0268b6..16a8fbf 100644
--- a/absl/synchronization/mutex.cc
+++ b/absl/synchronization/mutex.cc
@@ -37,6 +37,8 @@
 #include <atomic>
 #include <cinttypes>
 #include <cstddef>
+#include <cstring>
+#include <iterator>
 #include <thread>  // NOLINT(build/c++11)
 
 #include "absl/base/attributes.h"
@@ -52,6 +54,7 @@
 #include "absl/base/internal/sysinfo.h"
 #include "absl/base/internal/thread_identity.h"
 #include "absl/base/internal/tsan_mutex_interface.h"
+#include "absl/base/optimization.h"
 #include "absl/base/port.h"
 #include "absl/debugging/stacktrace.h"
 #include "absl/debugging/symbolize.h"
@@ -100,9 +103,6 @@
 ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
     absl::base_internal::AtomicHook<void (*)(const char *msg, const void *cv)>
         cond_var_tracer;
-ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES absl::base_internal::AtomicHook<
-    bool (*)(const void *pc, char *out, int out_size)>
-    symbolizer(absl::Symbolize);
 
 }  // namespace
 
@@ -123,10 +123,6 @@
   cond_var_tracer.Store(fn);
 }
 
-void RegisterSymbolizer(bool (*fn)(const void *pc, char *out, int out_size)) {
-  symbolizer.Store(fn);
-}
-
 namespace {
 // Represents the strategy for spin and yield.
 // See the comment in GetMutexGlobals() for more information.
@@ -135,25 +131,42 @@
 struct ABSL_CACHELINE_ALIGNED MutexGlobals {
   absl::once_flag once;
   int spinloop_iterations = 0;
-  int32_t mutex_sleep_limit[2] = {};
+  int32_t mutex_sleep_spins[2] = {};
+  absl::Duration mutex_sleep_time;
 };
 
+absl::Duration MeasureTimeToYield() {
+  absl::Time before = absl::Now();
+  ABSL_INTERNAL_C_SYMBOL(AbslInternalMutexYield)();
+  return absl::Now() - before;
+}
+
 const MutexGlobals &GetMutexGlobals() {
   ABSL_CONST_INIT static MutexGlobals data;
   absl::base_internal::LowLevelCallOnce(&data.once, [&]() {
     const int num_cpus = absl::base_internal::NumCPUs();
     data.spinloop_iterations = num_cpus > 1 ? 1500 : 0;
-    // If this a uniprocessor, only yield/sleep.  Otherwise, if the mode is
+    // If this a uniprocessor, only yield/sleep.
+    // Real-time threads are often unable to yield, so the sleep time needs
+    // to be long enough to keep the calling thread asleep until scheduling
+    // happens.
+    // If this is multiprocessor, allow spinning. If the mode is
     // aggressive then spin many times before yielding.  If the mode is
     // gentle then spin only a few times before yielding.  Aggressive spinning
     // is used to ensure that an Unlock() call, which must get the spin lock
     // for any thread to make progress gets it without undue delay.
     if (num_cpus > 1) {
-      data.mutex_sleep_limit[AGGRESSIVE] = 5000;
-      data.mutex_sleep_limit[GENTLE] = 250;
+      data.mutex_sleep_spins[AGGRESSIVE] = 5000;
+      data.mutex_sleep_spins[GENTLE] = 250;
+      data.mutex_sleep_time = absl::Microseconds(10);
     } else {
-      data.mutex_sleep_limit[AGGRESSIVE] = 0;
-      data.mutex_sleep_limit[GENTLE] = 0;
+      data.mutex_sleep_spins[AGGRESSIVE] = 0;
+      data.mutex_sleep_spins[GENTLE] = 0;
+      data.mutex_sleep_time = MeasureTimeToYield() * 5;
+      data.mutex_sleep_time =
+          std::min(data.mutex_sleep_time, absl::Milliseconds(1));
+      data.mutex_sleep_time =
+          std::max(data.mutex_sleep_time, absl::Microseconds(10));
     }
   });
   return data;
@@ -164,7 +177,8 @@
 // Returns the Mutex delay on iteration `c` depending on the given `mode`.
 // The returned value should be used as `c` for the next call to `MutexDelay`.
 int MutexDelay(int32_t c, int mode) {
-  const int32_t limit = GetMutexGlobals().mutex_sleep_limit[mode];
+  const int32_t limit = GetMutexGlobals().mutex_sleep_spins[mode];
+  const absl::Duration sleep_time = GetMutexGlobals().mutex_sleep_time;
   if (c < limit) {
     // Spin.
     c++;
@@ -177,7 +191,7 @@
       c++;
     } else {
       // Then wait.
-      absl::SleepFor(absl::Microseconds(10));
+      absl::SleepFor(sleep_time);
       c = 0;
     }
     ABSL_TSAN_MUTEX_POST_DIVERT(nullptr, 0);
@@ -571,10 +585,15 @@
 void Mutex::IncrementSynchSem(Mutex *mu, PerThreadSynch *w) {
   if (mu) {
     ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0);
-  }
-  PerThreadSem::Post(w->thread_identity());
-  if (mu) {
+    // We miss synchronization around passing PerThreadSynch between threads
+    // since it happens inside of the Mutex code, so we need to ignore all
+    // accesses to the object.
+    ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN();
+    PerThreadSem::Post(w->thread_identity());
+    ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END();
     ABSL_TSAN_MUTEX_POST_DIVERT(mu, 0);
+  } else {
+    PerThreadSem::Post(w->thread_identity());
   }
 }
 
@@ -609,21 +628,6 @@
                                  std::memory_order_release);
 }
 
-// --------------------------time support
-
-// Return the current time plus the timeout.  Use the same clock as
-// PerThreadSem::Wait() for consistency.  Unfortunately, we don't have
-// such a choice when a deadline is given directly.
-static absl::Time DeadlineFromTimeout(absl::Duration timeout) {
-#ifndef _WIN32
-  struct timeval tv;
-  gettimeofday(&tv, nullptr);
-  return absl::TimeFromTimeval(tv) + timeout;
-#else
-  return absl::Now() + timeout;
-#endif
-}
-
 // --------------------------Mutexes
 
 // In the layout below, the msb of the bottom byte is currently unused.  Also,
@@ -1129,7 +1133,7 @@
 // if the wait extends past the absolute time specified, even if "s" is still
 // on the mutex queue.  In this case, remove "s" from the queue and return
 // true, otherwise return false.
-ABSL_XRAY_LOG_ARGS(1) void Mutex::Block(PerThreadSynch *s) {
+void Mutex::Block(PerThreadSynch *s) {
   while (s->state.load(std::memory_order_acquire) == PerThreadSynch::kQueued) {
     if (!DecrementSynchSem(this, s, s->waitp->timeout)) {
       // After a timeout, we go into a spin loop until we remove ourselves
@@ -1278,7 +1282,7 @@
 
 static char *StackString(void **pcs, int n, char *buf, int maxlen,
                          bool symbolize) {
-  static const int kSymLen = 200;
+  static constexpr int kSymLen = 200;
   char sym[kSymLen];
   int len = 0;
   for (int i = 0; i != n; i++) {
@@ -1286,7 +1290,7 @@
       return buf;
     size_t count = static_cast<size_t>(maxlen - len);
     if (symbolize) {
-      if (!symbolizer(pcs[i], sym, kSymLen)) {
+      if (!absl::Symbolize(pcs[i], sym, kSymLen)) {
         sym[0] = '\0';
       }
       snprintf(buf + len, count, "%s\t@ %p %s\n", (i == 0 ? "\n" : ""), pcs[i],
@@ -1392,7 +1396,7 @@
       ABSL_RAW_LOG(ERROR, "Cycle: ");
       int path_len = deadlock_graph->FindPath(
           mu_id, other_node_id, ABSL_ARRAYSIZE(b->path), b->path);
-      for (int j = 0; j != path_len; j++) {
+      for (int j = 0; j != path_len && j != ABSL_ARRAYSIZE(b->path); j++) {
         GraphId id = b->path[j];
         Mutex *path_mu = static_cast<Mutex *>(deadlock_graph->Ptr(id));
         if (path_mu == nullptr) continue;
@@ -1405,6 +1409,9 @@
                     symbolize);
         ABSL_RAW_LOG(ERROR, "%s", b->buf);
       }
+      if (path_len > static_cast<int>(ABSL_ARRAYSIZE(b->path))) {
+        ABSL_RAW_LOG(ERROR, "(long cycle; list truncated)");
+      }
       if (synch_deadlock_detection.load(std::memory_order_acquire) ==
           OnDeadlockCycle::kAbort) {
         deadlock_graph_mu.Unlock();  // avoid deadlock in fatal sighandler
@@ -1478,7 +1485,7 @@
   return false;
 }
 
-ABSL_XRAY_LOG_ARGS(1) void Mutex::Lock() {
+void Mutex::Lock() {
   ABSL_TSAN_MUTEX_PRE_LOCK(this, 0);
   GraphId id = DebugOnlyDeadlockCheck(this);
   intptr_t v = mu_.load(std::memory_order_relaxed);
@@ -1496,7 +1503,7 @@
   ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0);
 }
 
-ABSL_XRAY_LOG_ARGS(1) void Mutex::ReaderLock() {
+void Mutex::ReaderLock() {
   ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock);
   GraphId id = DebugOnlyDeadlockCheck(this);
   intptr_t v = mu_.load(std::memory_order_relaxed);
@@ -1520,7 +1527,13 @@
 }
 
 bool Mutex::LockWhenWithTimeout(const Condition &cond, absl::Duration timeout) {
-  return LockWhenWithDeadline(cond, DeadlineFromTimeout(timeout));
+  ABSL_TSAN_MUTEX_PRE_LOCK(this, 0);
+  GraphId id = DebugOnlyDeadlockCheck(this);
+  bool res = LockSlowWithDeadline(kExclusive, &cond,
+                                  KernelTimeout(timeout), 0);
+  DebugOnlyLockEnter(this, id);
+  ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0);
+  return res;
 }
 
 bool Mutex::LockWhenWithDeadline(const Condition &cond, absl::Time deadline) {
@@ -1543,7 +1556,12 @@
 
 bool Mutex::ReaderLockWhenWithTimeout(const Condition &cond,
                                       absl::Duration timeout) {
-  return ReaderLockWhenWithDeadline(cond, DeadlineFromTimeout(timeout));
+  ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock);
+  GraphId id = DebugOnlyDeadlockCheck(this);
+  bool res = LockSlowWithDeadline(kShared, &cond, KernelTimeout(timeout), 0);
+  DebugOnlyLockEnter(this, id);
+  ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_read_lock, 0);
+  return res;
 }
 
 bool Mutex::ReaderLockWhenWithDeadline(const Condition &cond,
@@ -1568,7 +1586,18 @@
 }
 
 bool Mutex::AwaitWithTimeout(const Condition &cond, absl::Duration timeout) {
-  return AwaitWithDeadline(cond, DeadlineFromTimeout(timeout));
+  if (cond.Eval()) {      // condition already true; nothing to do
+    if (kDebugMode) {
+      this->AssertReaderHeld();
+    }
+    return true;
+  }
+
+  KernelTimeout t{timeout};
+  bool res = this->AwaitCommon(cond, t);
+  ABSL_RAW_CHECK(res || t.has_timeout(),
+                 "condition untrue on return from Await");
+  return res;
 }
 
 bool Mutex::AwaitWithDeadline(const Condition &cond, absl::Time deadline) {
@@ -1609,7 +1638,7 @@
   return res;
 }
 
-ABSL_XRAY_LOG_ARGS(1) bool Mutex::TryLock() {
+bool Mutex::TryLock() {
   ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_try_lock);
   intptr_t v = mu_.load(std::memory_order_relaxed);
   if ((v & (kMuWriter | kMuReader | kMuEvent)) == 0 &&  // try fast acquire
@@ -1638,7 +1667,7 @@
   return false;
 }
 
-ABSL_XRAY_LOG_ARGS(1) bool Mutex::ReaderTryLock() {
+bool Mutex::ReaderTryLock() {
   ABSL_TSAN_MUTEX_PRE_LOCK(this,
                            __tsan_mutex_read_lock | __tsan_mutex_try_lock);
   intptr_t v = mu_.load(std::memory_order_relaxed);
@@ -1684,7 +1713,7 @@
   return false;
 }
 
-ABSL_XRAY_LOG_ARGS(1) void Mutex::Unlock() {
+void Mutex::Unlock() {
   ABSL_TSAN_MUTEX_PRE_UNLOCK(this, 0);
   DebugOnlyLockLeave(this);
   intptr_t v = mu_.load(std::memory_order_relaxed);
@@ -1736,7 +1765,7 @@
   return (v & kMuMultipleWaitersMask) == 0;
 }
 
-ABSL_XRAY_LOG_ARGS(1) void Mutex::ReaderUnlock() {
+void Mutex::ReaderUnlock() {
   ABSL_TSAN_MUTEX_PRE_UNLOCK(this, __tsan_mutex_read_lock);
   DebugOnlyLockLeave(this);
   intptr_t v = mu_.load(std::memory_order_relaxed);
@@ -1766,7 +1795,7 @@
     case 1:  // blocked; turn off the designated waker bit
       return ~static_cast<intptr_t>(kMuDesig);
   }
-  ABSL_INTERNAL_UNREACHABLE;
+  ABSL_UNREACHABLE();
 }
 
 // Conditionally ignores the existence of waiting writers if a reader that has
@@ -1780,7 +1809,7 @@
     case 1:  // blocked; pretend there are no waiting writers
       return ~static_cast<intptr_t>(kMuWrWait);
   }
-  ABSL_INTERNAL_UNREACHABLE;
+  ABSL_UNREACHABLE();
 }
 
 // Internal version of LockWhen().  See LockSlowWithDeadline()
@@ -2342,22 +2371,26 @@
   }                            // end of for(;;)-loop
 
   if (wake_list != kPerThreadSynchNull) {
-    int64_t wait_cycles = 0;
+    int64_t total_wait_cycles = 0;
+    int64_t max_wait_cycles = 0;
     int64_t now = base_internal::CycleClock::Now();
     do {
-      // Sample lock contention events only if the waiter was trying to acquire
+      // Profile lock contention events only if the waiter was trying to acquire
       // the lock, not waiting on a condition variable or Condition.
       if (!wake_list->cond_waiter) {
-        wait_cycles += (now - wake_list->waitp->contention_start_cycles);
+        int64_t cycles_waited =
+            (now - wake_list->waitp->contention_start_cycles);
+        total_wait_cycles += cycles_waited;
+        if (max_wait_cycles == 0) max_wait_cycles = cycles_waited;
         wake_list->waitp->contention_start_cycles = now;
         wake_list->waitp->should_submit_contention_data = true;
       }
       wake_list = Wakeup(wake_list);              // wake waiters
     } while (wake_list != kPerThreadSynchNull);
-    if (wait_cycles > 0) {
-      mutex_tracer("slow release", this, wait_cycles);
+    if (total_wait_cycles > 0) {
+      mutex_tracer("slow release", this, total_wait_cycles);
       ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0);
-      submit_profile_data(wait_cycles);
+      submit_profile_data(total_wait_cycles);
       ABSL_TSAN_MUTEX_POST_DIVERT(this, 0);
     }
   }
@@ -2630,7 +2663,7 @@
 }
 
 bool CondVar::WaitWithTimeout(Mutex *mu, absl::Duration timeout) {
-  return WaitWithDeadline(mu, DeadlineFromTimeout(timeout));
+  return WaitCommon(mu, KernelTimeout(timeout));
 }
 
 bool CondVar::WaitWithDeadline(Mutex *mu, absl::Time deadline) {
@@ -2758,25 +2791,31 @@
   return *(static_cast<bool *>(arg));
 }
 
-Condition::Condition() {}   // null constructor, used for kTrue only
-const Condition Condition::kTrue;
+ABSL_CONST_INIT const Condition Condition::kTrue;
 
 Condition::Condition(bool (*func)(void *), void *arg)
     : eval_(&CallVoidPtrFunction),
-      function_(func),
-      method_(nullptr),
-      arg_(arg) {}
+      arg_(arg) {
+  static_assert(sizeof(&func) <= sizeof(callback_),
+                "An overlarge function pointer passed to Condition.");
+  StoreCallback(func);
+}
 
 bool Condition::CallVoidPtrFunction(const Condition *c) {
-  return (*c->function_)(c->arg_);
+  using FunctionPointer = bool (*)(void *);
+  FunctionPointer function_pointer;
+  std::memcpy(&function_pointer, c->callback_, sizeof(function_pointer));
+  return (*function_pointer)(c->arg_);
 }
 
 Condition::Condition(const bool *cond)
     : eval_(CallVoidPtrFunction),
-      function_(Dereference),
-      method_(nullptr),
       // const_cast is safe since Dereference does not modify arg
-      arg_(const_cast<bool *>(cond)) {}
+      arg_(const_cast<bool *>(cond)) {
+  using FunctionPointer = bool (*)(void *);
+  const FunctionPointer dereference = Dereference;
+  StoreCallback(dereference);
+}
 
 bool Condition::Eval() const {
   // eval_ == null for kTrue
@@ -2784,14 +2823,15 @@
 }
 
 bool Condition::GuaranteedEqual(const Condition *a, const Condition *b) {
-  if (a == nullptr) {
+  // kTrue logic.
+  if (a == nullptr || a->eval_ == nullptr) {
     return b == nullptr || b->eval_ == nullptr;
+  } else if (b == nullptr || b->eval_ == nullptr) {
+    return false;
   }
-  if (b == nullptr || b->eval_ == nullptr) {
-    return a->eval_ == nullptr;
-  }
-  return a->eval_ == b->eval_ && a->function_ == b->function_ &&
-         a->arg_ == b->arg_ && a->method_ == b->method_;
+  // Check equality of the representative fields.
+  return a->eval_ == b->eval_ && a->arg_ == b->arg_ &&
+         !memcmp(a->callback_, b->callback_, sizeof(a->callback_));
 }
 
 ABSL_NAMESPACE_END
diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h
index 8694bb7..0b6a9e1 100644
--- a/absl/synchronization/mutex.h
+++ b/absl/synchronization/mutex.h
@@ -60,6 +60,8 @@
 
 #include <atomic>
 #include <cstdint>
+#include <cstring>
+#include <iterator>
 #include <string>
 
 #include "absl/base/const_init.h"
@@ -90,26 +92,42 @@
 //
 // A `Mutex` has two basic operations: `Mutex::Lock()` and `Mutex::Unlock()`.
 // The `Lock()` operation *acquires* a `Mutex` (in a state known as an
-// *exclusive* -- or write -- lock), while the `Unlock()` operation *releases* a
+// *exclusive* -- or *write* -- lock), and the `Unlock()` operation *releases* a
 // Mutex. During the span of time between the Lock() and Unlock() operations,
-// a mutex is said to be *held*. By design all mutexes support exclusive/write
+// a mutex is said to be *held*. By design, all mutexes support exclusive/write
 // locks, as this is the most common way to use a mutex.
 //
+// Mutex operations are only allowed under certain conditions; otherwise an
+// operation is "invalid", and disallowed by the API. The conditions concern
+// both the current state of the mutex and the identity of the threads that
+// are performing the operations.
+//
 // The `Mutex` state machine for basic lock/unlock operations is quite simple:
 //
-// |                | Lock()     | Unlock() |
-// |----------------+------------+----------|
-// | Free           | Exclusive  | invalid  |
-// | Exclusive      | blocks     | Free     |
+// |                | Lock()                 | Unlock() |
+// |----------------+------------------------+----------|
+// | Free           | Exclusive              | invalid  |
+// | Exclusive      | blocks, then exclusive | Free     |
 //
-// Attempts to `Unlock()` must originate from the thread that performed the
-// corresponding `Lock()` operation.
+// The full conditions are as follows.
 //
-// An "invalid" operation is disallowed by the API. The `Mutex` implementation
-// is allowed to do anything on an invalid call, including but not limited to
+// * Calls to `Unlock()` require that the mutex be held, and must be made in the
+//   same thread that performed the corresponding `Lock()` operation which
+//   acquired the mutex; otherwise the call is invalid.
+//
+// * The mutex being non-reentrant (or non-recursive) means that a call to
+//   `Lock()` or `TryLock()` must not be made in a thread that already holds the
+//   mutex; such a call is invalid.
+//
+// * In other words, the state of being "held" has both a temporal component
+//   (from `Lock()` until `Unlock()`) as well as a thread identity component:
+//   the mutex is held *by a particular thread*.
+//
+// An "invalid" operation has undefined behavior. The `Mutex` implementation
+// is allowed to do anything on an invalid call, including, but not limited to,
 // crashing with a useful error message, silently succeeding, or corrupting
-// data structures. In debug mode, the implementation attempts to crash with a
-// useful error message.
+// data structures. In debug mode, the implementation may crash with a useful
+// error message.
 //
 // `Mutex` is not guaranteed to be "fair" in prioritizing waiting threads; it
 // is, however, approximately fair over long periods, and starvation-free for
@@ -255,7 +273,7 @@
   // Aliases for `Mutex::Lock()`, `Mutex::Unlock()`, and `Mutex::TryLock()`.
   //
   // These methods may be used (along with the complementary `Reader*()`
-  // methods) to distingish simple exclusive `Mutex` usage (`Lock()`,
+  // methods) to distinguish simple exclusive `Mutex` usage (`Lock()`,
   // etc.) from reader/writer lock usage.
   void WriterLock() ABSL_EXCLUSIVE_LOCK_FUNCTION() { this->Lock(); }
 
@@ -612,12 +630,12 @@
 // Condition
 // -----------------------------------------------------------------------------
 //
-// As noted above, `Mutex` contains a number of member functions which take a
-// `Condition` as an argument; clients can wait for conditions to become `true`
-// before attempting to acquire the mutex. These sections are known as
-// "condition critical" sections. To use a `Condition`, you simply need to
-// construct it, and use within an appropriate `Mutex` member function;
-// everything else in the `Condition` class is an implementation detail.
+// `Mutex` contains a number of member functions which take a `Condition` as an
+// argument; clients can wait for conditions to become `true` before attempting
+// to acquire the mutex. These sections are known as "condition critical"
+// sections. To use a `Condition`, you simply need to construct it, and use
+// within an appropriate `Mutex` member function; everything else in the
+// `Condition` class is an implementation detail.
 //
 // A `Condition` is specified as a function pointer which returns a boolean.
 // `Condition` functions should be pure functions -- their results should depend
@@ -677,6 +695,20 @@
   template<typename T>
   Condition(bool (*func)(T *), T *arg);
 
+  // Same as above, but allows for cases where `arg` comes from a pointer that
+  // is convertible to the function parameter type `T*` but not an exact match.
+  //
+  // For example, the argument might be `X*` but the function takes `const X*`,
+  // or the argument might be `Derived*` while the function takes `Base*`, and
+  // so on for cases where the argument pointer can be implicitly converted.
+  //
+  // Implementation notes: This constructor overload is required in addition to
+  // the one above to allow deduction of `T` from `arg` for cases such as where
+  // a function template is passed as `func`. Also, the dummy `typename = void`
+  // template parameter exists just to work around a MSVC mangling bug.
+  template <typename T, typename = void>
+  Condition(bool (*func)(T *), typename absl::internal::identity<T>::type *arg);
+
   // Templated version for invoking a method that returns a `bool`.
   //
   // `Condition(object, &Class::Method)` constructs a `Condition` that evaluates
@@ -727,7 +759,7 @@
       : Condition(obj, static_cast<bool (T::*)() const>(&T::operator())) {}
 
   // A Condition that always returns `true`.
-  static const Condition kTrue;
+  ABSL_CONST_INIT static const Condition kTrue;
 
   // Evaluates the condition.
   bool Eval() const;
@@ -742,22 +774,54 @@
   static bool GuaranteedEqual(const Condition *a, const Condition *b);
 
  private:
-  typedef bool (*InternalFunctionType)(void * arg);
-  typedef bool (Condition::*InternalMethodType)();
-  typedef bool (*InternalMethodCallerType)(void * arg,
-                                           InternalMethodType internal_method);
+  // Sizing an allocation for a method pointer can be subtle. In the Itanium
+  // specifications, a method pointer has a predictable, uniform size. On the
+  // other hand, MSVC ABI, method pointer sizes vary based on the
+  // inheritance of the class. Specifically, method pointers from classes with
+  // multiple inheritance are bigger than those of classes with single
+  // inheritance. Other variations also exist.
 
-  bool (*eval_)(const Condition*);  // Actual evaluator
-  InternalFunctionType function_;   // function taking pointer returning bool
-  InternalMethodType method_;       // method returning bool
-  void *arg_;                       // arg of function_ or object of method_
+#ifndef _MSC_VER
+  // Allocation for a function pointer or method pointer.
+  // The {0} initializer ensures that all unused bytes of this buffer are
+  // always zeroed out.  This is necessary, because GuaranteedEqual() compares
+  // all of the bytes, unaware of which bytes are relevant to a given `eval_`.
+  using MethodPtr = bool (Condition::*)();
+  char callback_[sizeof(MethodPtr)] = {0};
+#else
+  // It is well known that the larget MSVC pointer-to-member is 24 bytes. This
+  // may be the largest known pointer-to-member of any platform. For this
+  // reason we will allocate 24 bytes for MSVC platform toolchains.
+  char callback_[24] = {0};
+#endif
 
-  Condition();        // null constructor used only to create kTrue
+  // Function with which to evaluate callbacks and/or arguments.
+  bool (*eval_)(const Condition*) = nullptr;
+
+  // Either an argument for a function call or an object for a method call.
+  void *arg_ = nullptr;
 
   // Various functions eval_ can point to:
   static bool CallVoidPtrFunction(const Condition*);
   template <typename T> static bool CastAndCallFunction(const Condition* c);
   template <typename T> static bool CastAndCallMethod(const Condition* c);
+
+  // Helper methods for storing, validating, and reading callback arguments.
+  template <typename T>
+  inline void StoreCallback(T callback) {
+    static_assert(
+        sizeof(callback) <= sizeof(callback_),
+        "An overlarge pointer was passed as a callback to Condition.");
+    std::memcpy(callback_, &callback, sizeof(callback));
+  }
+
+  template <typename T>
+  inline void ReadCallback(T *callback) const {
+    std::memcpy(callback, callback_, sizeof(*callback));
+  }
+
+  // Used only to create kTrue.
+  constexpr Condition() = default;
 };
 
 // -----------------------------------------------------------------------------
@@ -949,44 +1013,54 @@
 // static
 template <typename T>
 bool Condition::CastAndCallMethod(const Condition *c) {
-  typedef bool (T::*MemberType)();
-  MemberType rm = reinterpret_cast<MemberType>(c->method_);
-  T *x = static_cast<T *>(c->arg_);
-  return (x->*rm)();
+  T *object = static_cast<T *>(c->arg_);
+  bool (T::*method_pointer)();
+  c->ReadCallback(&method_pointer);
+  return (object->*method_pointer)();
 }
 
 // static
 template <typename T>
 bool Condition::CastAndCallFunction(const Condition *c) {
-  typedef bool (*FuncType)(T *);
-  FuncType fn = reinterpret_cast<FuncType>(c->function_);
-  T *x = static_cast<T *>(c->arg_);
-  return (*fn)(x);
+  bool (*function)(T *);
+  c->ReadCallback(&function);
+  T *argument = static_cast<T *>(c->arg_);
+  return (*function)(argument);
 }
 
 template <typename T>
 inline Condition::Condition(bool (*func)(T *), T *arg)
     : eval_(&CastAndCallFunction<T>),
-      function_(reinterpret_cast<InternalFunctionType>(func)),
-      method_(nullptr),
-      arg_(const_cast<void *>(static_cast<const void *>(arg))) {}
+      arg_(const_cast<void *>(static_cast<const void *>(arg))) {
+  static_assert(sizeof(&func) <= sizeof(callback_),
+                "An overlarge function pointer was passed to Condition.");
+  StoreCallback(func);
+}
+
+template <typename T, typename>
+inline Condition::Condition(bool (*func)(T *),
+                            typename absl::internal::identity<T>::type *arg)
+    // Just delegate to the overload above.
+    : Condition(func, arg) {}
 
 template <typename T>
 inline Condition::Condition(T *object,
                             bool (absl::internal::identity<T>::type::*method)())
     : eval_(&CastAndCallMethod<T>),
-      function_(nullptr),
-      method_(reinterpret_cast<InternalMethodType>(method)),
-      arg_(object) {}
+      arg_(object) {
+  static_assert(sizeof(&method) <= sizeof(callback_),
+                "An overlarge method pointer was passed to Condition.");
+  StoreCallback(method);
+}
 
 template <typename T>
 inline Condition::Condition(const T *object,
                             bool (absl::internal::identity<T>::type::*method)()
                                 const)
     : eval_(&CastAndCallMethod<T>),
-      function_(nullptr),
-      method_(reinterpret_cast<InternalMethodType>(method)),
-      arg_(reinterpret_cast<void *>(const_cast<T *>(object))) {}
+      arg_(reinterpret_cast<void *>(const_cast<T *>(object))) {
+  StoreCallback(method);
+}
 
 // Register hooks for profiling support.
 //
@@ -995,10 +1069,11 @@
 // measured by //absl/base/internal/cycleclock.h, and which may not
 // be real "cycle" counts.)
 //
-// Calls to this function do not race or block, but there is no ordering
-// guaranteed between calls to this function and call to the provided hook.
-// In particular, the previously registered hook may still be called for some
-// time after this function returns.
+// There is no ordering guarantee between when the hook is registered and when
+// callbacks will begin.  Only a single profiler can be installed in a running
+// binary; if this function is called a second time with a different function
+// pointer, the value is ignored (and will cause an assertion failure in debug
+// mode.)
 void RegisterMutexProfiler(void (*fn)(int64_t wait_cycles));
 
 // Register a hook for Mutex tracing.
@@ -1011,13 +1086,11 @@
 //
 // The only event name currently sent is "slow release".
 //
-// This has the same memory ordering concerns as RegisterMutexProfiler() above.
+// This has the same ordering and single-use limitations as
+// RegisterMutexProfiler() above.
 void RegisterMutexTracer(void (*fn)(const char *msg, const void *obj,
                                     int64_t wait_cycles));
 
-// TODO(gfalcon): Combine RegisterMutexProfiler() and RegisterMutexTracer()
-// into a single interface, since they are only ever called in pairs.
-
 // Register a hook for CondVar tracing.
 //
 // The function pointer registered here will be called here on various CondVar
@@ -1028,25 +1101,10 @@
 // Events that can be sent are "Wait", "Unwait", "Signal wakeup", and
 // "SignalAll wakeup".
 //
-// This has the same memory ordering concerns as RegisterMutexProfiler() above.
+// This has the same ordering and single-use limitations as
+// RegisterMutexProfiler() above.
 void RegisterCondVarTracer(void (*fn)(const char *msg, const void *cv));
 
-// Register a hook for symbolizing stack traces in deadlock detector reports.
-//
-// 'pc' is the program counter being symbolized, 'out' is the buffer to write
-// into, and 'out_size' is the size of the buffer.  This function can return
-// false if symbolizing failed, or true if a NUL-terminated symbol was written
-// to 'out.'
-//
-// This has the same memory ordering concerns as RegisterMutexProfiler() above.
-//
-// DEPRECATED: The default symbolizer function is absl::Symbolize() and the
-// ability to register a different hook for symbolizing stack traces will be
-// removed on or after 2023-05-01.
-ABSL_DEPRECATED("absl::RegisterSymbolizer() is deprecated and will be removed "
-                "on or after 2023-05-01")
-void RegisterSymbolizer(bool (*fn)(const void *pc, char *out, int out_size));
-
 // EnableMutexInvariantDebugging()
 //
 // Enable or disable global support for Mutex invariant debugging.  If enabled,
diff --git a/absl/synchronization/mutex_method_pointer_test.cc b/absl/synchronization/mutex_method_pointer_test.cc
new file mode 100644
index 0000000..f4c82d2
--- /dev/null
+++ b/absl/synchronization/mutex_method_pointer_test.cc
@@ -0,0 +1,138 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/synchronization/mutex.h"
+
+#include <cstdlib>
+#include <string>
+
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+
+namespace {
+
+class IncompleteClass;
+
+#ifdef _MSC_VER
+// These tests verify expectations about sizes of MSVC pointers to methods.
+// Pointers to methods are distinguished by whether their class hierarchies
+// contain single inheritance, multiple inheritance, or virtual inheritance.
+
+// Declare classes of the various MSVC inheritance types.
+class __single_inheritance SingleInheritance{};
+class __multiple_inheritance MultipleInheritance;
+class __virtual_inheritance VirtualInheritance;
+
+TEST(MutexMethodPointerTest, MicrosoftMethodPointerSize) {
+  void (SingleInheritance::*single_inheritance_method_pointer)();
+  void (MultipleInheritance::*multiple_inheritance_method_pointer)();
+  void (VirtualInheritance::*virtual_inheritance_method_pointer)();
+
+#if defined(_M_IX86) || defined(_M_ARM)
+  static_assert(sizeof(single_inheritance_method_pointer) == 4,
+                "Unexpected sizeof(single_inheritance_method_pointer).");
+  static_assert(sizeof(multiple_inheritance_method_pointer) == 8,
+                "Unexpected sizeof(multiple_inheritance_method_pointer).");
+  static_assert(sizeof(virtual_inheritance_method_pointer) == 12,
+                "Unexpected sizeof(virtual_inheritance_method_pointer).");
+#elif defined(_M_X64) || defined(__aarch64__)
+  static_assert(sizeof(single_inheritance_method_pointer) == 8,
+                "Unexpected sizeof(single_inheritance_method_pointer).");
+  static_assert(sizeof(multiple_inheritance_method_pointer) == 16,
+                "Unexpected sizeof(multiple_inheritance_method_pointer).");
+  static_assert(sizeof(virtual_inheritance_method_pointer) == 16,
+                "Unexpected sizeof(virtual_inheritance_method_pointer).");
+#endif
+  void (IncompleteClass::*incomplete_class_method_pointer)();
+  static_assert(sizeof(incomplete_class_method_pointer) >=
+                    sizeof(virtual_inheritance_method_pointer),
+                "Failed invariant: sizeof(incomplete_class_method_pointer) >= "
+                "sizeof(virtual_inheritance_method_pointer)!");
+}
+
+class Callback {
+  bool x = true;
+
+ public:
+  Callback() {}
+  bool method() {
+    x = !x;
+    return x;
+  }
+};
+
+class M2 {
+  bool x = true;
+
+ public:
+  M2() {}
+  bool method2() {
+    x = !x;
+    return x;
+  }
+};
+
+class MultipleInheritance : public Callback, public M2 {};
+
+TEST(MutexMethodPointerTest, ConditionWithMultipleInheritanceMethod) {
+  // This test ensures that Condition can deal with method pointers from classes
+  // with multiple inheritance.
+  MultipleInheritance object = MultipleInheritance();
+  absl::Condition condition(&object, &MultipleInheritance::method);
+  EXPECT_FALSE(condition.Eval());
+  EXPECT_TRUE(condition.Eval());
+}
+
+class __virtual_inheritance VirtualInheritance : virtual public Callback {
+  bool x = false;
+
+ public:
+  VirtualInheritance() {}
+  bool method() {
+    x = !x;
+    return x;
+  }
+};
+
+TEST(MutexMethodPointerTest, ConditionWithVirtualInheritanceMethod) {
+  // This test ensures that Condition can deal with method pointers from classes
+  // with virtual inheritance.
+  VirtualInheritance object = VirtualInheritance();
+  absl::Condition condition(&object, &VirtualInheritance::method);
+  EXPECT_TRUE(condition.Eval());
+  EXPECT_FALSE(condition.Eval());
+}
+#endif  // #ifdef _MSC_VER
+
+TEST(MutexMethodPointerTest, ConditionWithIncompleteClassMethod) {
+  using IncompleteClassMethodPointer = void (IncompleteClass::*)();
+
+  union CallbackSlot {
+    void (*anonymous_function_pointer)();
+    IncompleteClassMethodPointer incomplete_class_method_pointer;
+  };
+
+  static_assert(sizeof(CallbackSlot) >= sizeof(IncompleteClassMethodPointer),
+                "The callback slot is not big enough for method pointers.");
+  static_assert(
+      sizeof(CallbackSlot) == sizeof(IncompleteClassMethodPointer),
+      "The callback slot is not big enough for anonymous function pointers.");
+
+#if defined(_MSC_VER)
+  static_assert(sizeof(IncompleteClassMethodPointer) <= 24,
+                "The pointer to a method of an incomplete class is too big.");
+#endif
+}
+
+}  // namespace
diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc
index 99bb017..ec039a7 100644
--- a/absl/synchronization/mutex_test.cc
+++ b/absl/synchronization/mutex_test.cc
@@ -295,8 +295,9 @@
                      "TestTime failed");
     }
     elapsed = absl::Now() - start;
-    ABSL_RAW_CHECK(absl::Seconds(0.9) <= elapsed &&
-                   elapsed <= absl::Seconds(2.0), "TestTime failed");
+    ABSL_RAW_CHECK(
+        absl::Seconds(0.9) <= elapsed && elapsed <= absl::Seconds(2.0),
+        "TestTime failed");
     ABSL_RAW_CHECK(cxt->g0 == cxt->threads, "TestTime failed");
 
   } else if (c == 1) {
@@ -343,7 +344,7 @@
 static void TestCVTime(TestContext *cxt, int c) { TestTime(cxt, c, true); }
 
 static void EndTest(int *c0, int *c1, absl::Mutex *mu, absl::CondVar *cv,
-                    const std::function<void(int)>& cb) {
+                    const std::function<void(int)> &cb) {
   mu->Lock();
   int c = (*c0)++;
   mu->Unlock();
@@ -366,9 +367,9 @@
   cxt->threads = threads;
   absl::synchronization_internal::ThreadPool tp(threads);
   for (int i = 0; i != threads; i++) {
-    tp.Schedule(std::bind(&EndTest, &c0, &c1, &mu2, &cv2,
-                          std::function<void(int)>(
-                              std::bind(test, cxt, std::placeholders::_1))));
+    tp.Schedule(std::bind(
+        &EndTest, &c0, &c1, &mu2, &cv2,
+        std::function<void(int)>(std::bind(test, cxt, std::placeholders::_1))));
   }
   mu2.Lock();
   while (c1 != threads) {
@@ -682,14 +683,14 @@
   bool waiting = false;
 };
 
-static bool LockWhenTestIsCond(LockWhenTestStruct* s) {
+static bool LockWhenTestIsCond(LockWhenTestStruct *s) {
   s->mu2.Lock();
   s->waiting = true;
   s->mu2.Unlock();
   return s->cond;
 }
 
-static void LockWhenTestWaitForIsCond(LockWhenTestStruct* s) {
+static void LockWhenTestWaitForIsCond(LockWhenTestStruct *s) {
   s->mu1.LockWhen(absl::Condition(&LockWhenTestIsCond, s));
   s->mu1.Unlock();
 }
@@ -871,6 +872,111 @@
   }
 }
 
+// Some functions taking pointers to non-const.
+bool Equals42(int *p) { return *p == 42; }
+bool Equals43(int *p) { return *p == 43; }
+
+// Some functions taking pointers to const.
+bool ConstEquals42(const int *p) { return *p == 42; }
+bool ConstEquals43(const int *p) { return *p == 43; }
+
+// Some function templates taking pointers. Note it's possible for `T` to be
+// deduced as non-const or const, which creates the potential for ambiguity,
+// but which the implementation is careful to avoid.
+template <typename T>
+bool TemplateEquals42(T *p) {
+  return *p == 42;
+}
+template <typename T>
+bool TemplateEquals43(T *p) {
+  return *p == 43;
+}
+
+TEST(Mutex, FunctionPointerCondition) {
+  // Some arguments.
+  int x = 42;
+  const int const_x = 42;
+
+  // Parameter non-const, argument non-const.
+  EXPECT_TRUE(absl::Condition(Equals42, &x).Eval());
+  EXPECT_FALSE(absl::Condition(Equals43, &x).Eval());
+
+  // Parameter const, argument non-const.
+  EXPECT_TRUE(absl::Condition(ConstEquals42, &x).Eval());
+  EXPECT_FALSE(absl::Condition(ConstEquals43, &x).Eval());
+
+  // Parameter const, argument const.
+  EXPECT_TRUE(absl::Condition(ConstEquals42, &const_x).Eval());
+  EXPECT_FALSE(absl::Condition(ConstEquals43, &const_x).Eval());
+
+  // Parameter type deduced, argument non-const.
+  EXPECT_TRUE(absl::Condition(TemplateEquals42, &x).Eval());
+  EXPECT_FALSE(absl::Condition(TemplateEquals43, &x).Eval());
+
+  // Parameter type deduced, argument const.
+  EXPECT_TRUE(absl::Condition(TemplateEquals42, &const_x).Eval());
+  EXPECT_FALSE(absl::Condition(TemplateEquals43, &const_x).Eval());
+
+  // Parameter non-const, argument const is not well-formed.
+  EXPECT_FALSE((std::is_constructible<absl::Condition, decltype(Equals42),
+                                      decltype(&const_x)>::value));
+  // Validate use of is_constructible by contrasting to a well-formed case.
+  EXPECT_TRUE((std::is_constructible<absl::Condition, decltype(ConstEquals42),
+                                     decltype(&const_x)>::value));
+}
+
+// Example base and derived class for use in predicates and test below. Not a
+// particularly realistic example, but it suffices for testing purposes.
+struct Base {
+  explicit Base(int v) : value(v) {}
+  int value;
+};
+struct Derived : Base {
+  explicit Derived(int v) : Base(v) {}
+};
+
+// Some functions taking pointer to non-const `Base`.
+bool BaseEquals42(Base *p) { return p->value == 42; }
+bool BaseEquals43(Base *p) { return p->value == 43; }
+
+// Some functions taking pointer to const `Base`.
+bool ConstBaseEquals42(const Base *p) { return p->value == 42; }
+bool ConstBaseEquals43(const Base *p) { return p->value == 43; }
+
+TEST(Mutex, FunctionPointerConditionWithDerivedToBaseConversion) {
+  // Some arguments.
+  Derived derived(42);
+  const Derived const_derived(42);
+
+  // Parameter non-const base, argument derived non-const.
+  EXPECT_TRUE(absl::Condition(BaseEquals42, &derived).Eval());
+  EXPECT_FALSE(absl::Condition(BaseEquals43, &derived).Eval());
+
+  // Parameter const base, argument derived non-const.
+  EXPECT_TRUE(absl::Condition(ConstBaseEquals42, &derived).Eval());
+  EXPECT_FALSE(absl::Condition(ConstBaseEquals43, &derived).Eval());
+
+  // Parameter const base, argument derived const.
+  EXPECT_TRUE(absl::Condition(ConstBaseEquals42, &const_derived).Eval());
+  EXPECT_FALSE(absl::Condition(ConstBaseEquals43, &const_derived).Eval());
+
+  // Parameter const base, argument derived const.
+  EXPECT_TRUE(absl::Condition(ConstBaseEquals42, &const_derived).Eval());
+  EXPECT_FALSE(absl::Condition(ConstBaseEquals43, &const_derived).Eval());
+
+  // Parameter derived, argument base is not well-formed.
+  bool (*derived_pred)(const Derived *) = [](const Derived *) { return true; };
+  EXPECT_FALSE((std::is_constructible<absl::Condition, decltype(derived_pred),
+                                      Base *>::value));
+  EXPECT_FALSE((std::is_constructible<absl::Condition, decltype(derived_pred),
+                                      const Base *>::value));
+  // Validate use of is_constructible by contrasting to well-formed cases.
+  EXPECT_TRUE((std::is_constructible<absl::Condition, decltype(derived_pred),
+                                     Derived *>::value));
+  EXPECT_TRUE((std::is_constructible<absl::Condition, decltype(derived_pred),
+                                     const Derived *>::value));
+}
+
 struct True {
   template <class... Args>
   bool operator()(Args...) const {
@@ -1130,6 +1236,25 @@
   absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort);
 }
 
+TEST(Mutex, DeadlockDetectorLongCycle) {
+  absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kReport);
+
+  // This test generates a warning if it passes, and crashes otherwise.
+  // Cause bazel to ignore the warning.
+  ScopedDisableBazelTestWarnings disable_bazel_test_warnings;
+
+  // Check that we survive a deadlock with a lock cycle.
+  std::vector<absl::Mutex> mutex(100);
+  for (size_t i = 0; i != mutex.size(); i++) {
+    mutex[i].Lock();
+    mutex[(i + 1) % mutex.size()].Lock();
+    mutex[i].Unlock();
+    mutex[(i + 1) % mutex.size()].Unlock();
+  }
+
+  absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort);
+}
+
 // This test is tagged with NO_THREAD_SAFETY_ANALYSIS because the
 // annotation-based static thread-safety analysis is not currently
 // predicate-aware and cannot tell if the two for-loops that acquire and
@@ -1694,8 +1819,7 @@
 TEST(Mutex, CVTime) {
   int threads = 10;  // Use a fixed thread count of 10
   int iterations = 1;
-  EXPECT_EQ(RunTest(&TestCVTime, threads, iterations, 1),
-            threads * iterations);
+  EXPECT_EQ(RunTest(&TestCVTime, threads, iterations, 1), threads * iterations);
 }
 
 TEST(Mutex, MuTime) {
diff --git a/absl/synchronization/notification_test.cc b/absl/synchronization/notification_test.cc
index 100ea76..49ce61a 100644
--- a/absl/synchronization/notification_test.cc
+++ b/absl/synchronization/notification_test.cc
@@ -79,7 +79,7 @@
 
   // 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(5);
   EXPECT_LE(delay - slop, elapsed)
       << "WaitForNotificationWithTimeout returned " << delay - elapsed
       << " early (with " << slop << " slop), start time was " << start;
diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel
index aa07df3..88d2088 100644
--- a/absl/time/BUILD.bazel
+++ b/absl/time/BUILD.bazel
@@ -45,22 +45,21 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         "//absl/base",
+        "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/base:raw_logging_internal",
         "//absl/numeric:int128",
         "//absl/strings",
         "//absl/time/internal/cctz:civil_time",
         "//absl/time/internal/cctz:time_zone",
+        "//absl/types:optional",
     ],
 )
 
 cc_library(
     name = "test_util",
     testonly = 1,
-    srcs = [
-        "internal/test_util.cc",
-        "internal/zoneinfo.inc",
-    ],
+    srcs = ["internal/test_util.cc"],
     hdrs = ["internal/test_util.h"],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
@@ -69,8 +68,6 @@
         ":time",
         "//absl/base:config",
         "//absl/base:raw_logging_internal",
-        "//absl/time/internal/cctz:time_zone",
-        "@com_google_googletest//:gtest",
     ],
 )
 
@@ -85,6 +82,8 @@
         "time_zone_test.cc",
     ],
     copts = ABSL_TEST_COPTS,
+    data = ["//absl/time/internal/cctz:zoneinfo"],
+    env = {"TZDIR": "absl/time/internal/cctz/testdata/zoneinfo"},
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":test_util",
@@ -92,12 +91,37 @@
         "//absl/base:config",
         "//absl/base:core_headers",
         "//absl/numeric:int128",
+        "//absl/strings:str_format",
         "//absl/time/internal/cctz:time_zone",
         "@com_google_googletest//:gtest_main",
     ],
 )
 
 cc_test(
+    name = "flag_test",
+    srcs = [
+        "flag_test.cc",
+    ],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    tags = [
+        "no_test_android_arm",
+        "no_test_android_arm64",
+        "no_test_android_x86",
+        "no_test_ios_x86_64",
+        "no_test_lexan",
+        "no_test_loonix",
+        "no_test_wasm",
+    ],
+    deps = [
+        ":time",
+        "//absl/flags:flag",
+        "//absl/flags:reflection",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
     name = "time_benchmark",
     srcs = [
         "civil_time_benchmark.cc",
@@ -107,6 +131,8 @@
         "time_benchmark.cc",
     ],
     copts = ABSL_TEST_COPTS,
+    data = ["//absl/time/internal/cctz:zoneinfo"],
+    env = {"TZDIR": "absl/time/internal/cctz/testdata/zoneinfo"},
     linkopts = ABSL_DEFAULT_LINKOPTS,
     tags = [
         "benchmark",
diff --git a/absl/time/CMakeLists.txt b/absl/time/CMakeLists.txt
index debab3b..b312425 100644
--- a/absl/time/CMakeLists.txt
+++ b/absl/time/CMakeLists.txt
@@ -95,7 +95,6 @@
     "internal/test_util.h"
   SRCS
     "internal/test_util.cc"
-    "internal/zoneinfo.inc"
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
@@ -103,7 +102,6 @@
     absl::config
     absl::raw_logging_internal
     absl::time_zone
-    GTest::gmock
   TESTONLY
 )
 
@@ -124,6 +122,21 @@
     absl::time
     absl::config
     absl::core_headers
+    absl::strings
+    absl::str_format
     absl::time_zone
     GTest::gmock_main
 )
+
+absl_cc_test(
+  NAME
+    flag_test
+  SRCS
+    "flag_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::flags
+    absl::flags_reflection
+    GTest::gmock_main
+)
diff --git a/absl/time/civil_time.cc b/absl/time/civil_time.cc
index 6a231ed..65df39d 100644
--- a/absl/time/civil_time.cc
+++ b/absl/time/civil_time.cc
@@ -15,6 +15,7 @@
 #include "absl/time/civil_time.h"
 
 #include <cstdlib>
+#include <ostream>
 #include <string>
 
 #include "absl/strings/str_cat.h"
@@ -167,6 +168,31 @@
   return os << FormatCivilTime(s);
 }
 
+bool AbslParseFlag(string_view s, CivilSecond* c, std::string*) {
+  return ParseLenientCivilTime(s, c);
+}
+bool AbslParseFlag(string_view s, CivilMinute* c, std::string*) {
+  return ParseLenientCivilTime(s, c);
+}
+bool AbslParseFlag(string_view s, CivilHour* c, std::string*) {
+  return ParseLenientCivilTime(s, c);
+}
+bool AbslParseFlag(string_view s, CivilDay* c, std::string*) {
+  return ParseLenientCivilTime(s, c);
+}
+bool AbslParseFlag(string_view s, CivilMonth* c, std::string*) {
+  return ParseLenientCivilTime(s, c);
+}
+bool AbslParseFlag(string_view s, CivilYear* c, std::string*) {
+  return ParseLenientCivilTime(s, c);
+}
+std::string AbslUnparseFlag(CivilSecond c) { return FormatCivilTime(c); }
+std::string AbslUnparseFlag(CivilMinute c) { return FormatCivilTime(c); }
+std::string AbslUnparseFlag(CivilHour c) { return FormatCivilTime(c); }
+std::string AbslUnparseFlag(CivilDay c) { return FormatCivilTime(c); }
+std::string AbslUnparseFlag(CivilMonth c) { return FormatCivilTime(c); }
+std::string AbslUnparseFlag(CivilYear c) { return FormatCivilTime(c); }
+
 }  // namespace time_internal
 
 ABSL_NAMESPACE_END
diff --git a/absl/time/civil_time.h b/absl/time/civil_time.h
index bb46004..5855bc7 100644
--- a/absl/time/civil_time.h
+++ b/absl/time/civil_time.h
@@ -70,8 +70,10 @@
 #ifndef ABSL_TIME_CIVIL_TIME_H_
 #define ABSL_TIME_CIVIL_TIME_H_
 
+#include <iosfwd>
 #include <string>
 
+#include "absl/base/config.h"
 #include "absl/strings/string_view.h"
 #include "absl/time/internal/cctz/include/cctz/civil_time.h"
 
@@ -530,6 +532,29 @@
 std::ostream& operator<<(std::ostream& os, CivilMinute m);
 std::ostream& operator<<(std::ostream& os, CivilSecond s);
 
+// AbslParseFlag()
+//
+// Parses the command-line flag string representation `s` into a civil-time
+// value. Flags must be specified in a format that is valid for
+// `absl::ParseLenientCivilTime()`.
+bool AbslParseFlag(absl::string_view s, CivilSecond* c, std::string* error);
+bool AbslParseFlag(absl::string_view s, CivilMinute* c, std::string* error);
+bool AbslParseFlag(absl::string_view s, CivilHour* c, std::string* error);
+bool AbslParseFlag(absl::string_view s, CivilDay* c, std::string* error);
+bool AbslParseFlag(absl::string_view s, CivilMonth* c, std::string* error);
+bool AbslParseFlag(absl::string_view s, CivilYear* c, std::string* error);
+
+// AbslUnparseFlag()
+//
+// Unparses a civil-time value into a command-line string representation using
+// the format specified by `absl::ParseCivilTime()`.
+std::string AbslUnparseFlag(CivilSecond c);
+std::string AbslUnparseFlag(CivilMinute c);
+std::string AbslUnparseFlag(CivilHour c);
+std::string AbslUnparseFlag(CivilDay c);
+std::string AbslUnparseFlag(CivilMonth c);
+std::string AbslUnparseFlag(CivilYear c);
+
 }  // namespace time_internal
 
 ABSL_NAMESPACE_END
diff --git a/absl/time/civil_time_test.cc b/absl/time/civil_time_test.cc
index 0ebd97a..ec435ac 100644
--- a/absl/time/civil_time_test.cc
+++ b/absl/time/civil_time_test.cc
@@ -1228,7 +1228,7 @@
   EXPECT_EQ(0, day_floor.hour());  // 09:09:09 is floored
   EXPECT_EQ(absl::CivilDay(2015, 1, 2), day_floor);
 
-  // Unspecified fields default to their minium value
+  // Unspecified fields default to their minimum value
   absl::CivilDay day_default(2015);  // Defaults to Jan 1
   EXPECT_EQ(absl::CivilDay(2015, 1, 1), day_default);
 
diff --git a/absl/time/duration.cc b/absl/time/duration.cc
index 911e80f..634e5d5 100644
--- a/absl/time/duration.cc
+++ b/absl/time/duration.cc
@@ -96,13 +96,6 @@
   return d != 0.0;
 }
 
-// Can't use std::round() because it is only available in C++11.
-// Note that we ignore the possibility of floating-point over/underflow.
-template <typename Double>
-inline double Round(Double d) {
-  return d < 0 ? std::ceil(d - 0.5) : std::floor(d + 0.5);
-}
-
 // *sec may be positive or negative.  *ticks must be in the range
 // -kTicksPerSecond < *ticks < kTicksPerSecond.  If *ticks is negative it
 // will be normalized to a positive value by adjusting *sec accordingly.
@@ -260,7 +253,7 @@
   double lo_frac = std::modf(lo_doub, &lo_int);
 
   // Rolls lo into hi if necessary.
-  int64_t lo64 = Round(lo_frac * kTicksPerSecond);
+  int64_t lo64 = std::round(lo_frac * kTicksPerSecond);
 
   Duration ans;
   if (!SafeAddRepHi(hi_int, lo_int, &ans)) return ans;
@@ -407,16 +400,18 @@
 Duration& Duration::operator+=(Duration rhs) {
   if (time_internal::IsInfiniteDuration(*this)) return *this;
   if (time_internal::IsInfiniteDuration(rhs)) return *this = rhs;
-  const int64_t orig_rep_hi = rep_hi_;
-  rep_hi_ =
-      DecodeTwosComp(EncodeTwosComp(rep_hi_) + EncodeTwosComp(rhs.rep_hi_));
+  const int64_t orig_rep_hi = rep_hi_.Get();
+  rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_.Get()) +
+                           EncodeTwosComp(rhs.rep_hi_.Get()));
   if (rep_lo_ >= kTicksPerSecond - rhs.rep_lo_) {
-    rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_) + 1);
+    rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_.Get()) + 1);
     rep_lo_ -= kTicksPerSecond;
   }
   rep_lo_ += rhs.rep_lo_;
-  if (rhs.rep_hi_ < 0 ? rep_hi_ > orig_rep_hi : rep_hi_ < orig_rep_hi) {
-    return *this = rhs.rep_hi_ < 0 ? -InfiniteDuration() : InfiniteDuration();
+  if (rhs.rep_hi_.Get() < 0 ? rep_hi_.Get() > orig_rep_hi
+                            : rep_hi_.Get() < orig_rep_hi) {
+    return *this =
+               rhs.rep_hi_.Get() < 0 ? -InfiniteDuration() : InfiniteDuration();
   }
   return *this;
 }
@@ -424,18 +419,21 @@
 Duration& Duration::operator-=(Duration rhs) {
   if (time_internal::IsInfiniteDuration(*this)) return *this;
   if (time_internal::IsInfiniteDuration(rhs)) {
-    return *this = rhs.rep_hi_ >= 0 ? -InfiniteDuration() : InfiniteDuration();
+    return *this = rhs.rep_hi_.Get() >= 0 ? -InfiniteDuration()
+                                          : InfiniteDuration();
   }
-  const int64_t orig_rep_hi = rep_hi_;
-  rep_hi_ =
-      DecodeTwosComp(EncodeTwosComp(rep_hi_) - EncodeTwosComp(rhs.rep_hi_));
+  const int64_t orig_rep_hi = rep_hi_.Get();
+  rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_.Get()) -
+                           EncodeTwosComp(rhs.rep_hi_.Get()));
   if (rep_lo_ < rhs.rep_lo_) {
-    rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_) - 1);
+    rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_.Get()) - 1);
     rep_lo_ += kTicksPerSecond;
   }
   rep_lo_ -= rhs.rep_lo_;
-  if (rhs.rep_hi_ < 0 ? rep_hi_ < orig_rep_hi : rep_hi_ > orig_rep_hi) {
-    return *this = rhs.rep_hi_ >= 0 ? -InfiniteDuration() : InfiniteDuration();
+  if (rhs.rep_hi_.Get() < 0 ? rep_hi_.Get() < orig_rep_hi
+                            : rep_hi_.Get() > orig_rep_hi) {
+    return *this = rhs.rep_hi_.Get() >= 0 ? -InfiniteDuration()
+                                          : InfiniteDuration();
   }
   return *this;
 }
@@ -446,7 +444,7 @@
 
 Duration& Duration::operator*=(int64_t r) {
   if (time_internal::IsInfiniteDuration(*this)) {
-    const bool is_neg = (r < 0) != (rep_hi_ < 0);
+    const bool is_neg = (r < 0) != (rep_hi_.Get() < 0);
     return *this = is_neg ? -InfiniteDuration() : InfiniteDuration();
   }
   return *this = ScaleFixed<SafeMultiply>(*this, r);
@@ -454,7 +452,7 @@
 
 Duration& Duration::operator*=(double r) {
   if (time_internal::IsInfiniteDuration(*this) || !IsFinite(r)) {
-    const bool is_neg = (std::signbit(r) != 0) != (rep_hi_ < 0);
+    const bool is_neg = std::signbit(r) != (rep_hi_.Get() < 0);
     return *this = is_neg ? -InfiniteDuration() : InfiniteDuration();
   }
   return *this = ScaleDouble<std::multiplies>(*this, r);
@@ -462,7 +460,7 @@
 
 Duration& Duration::operator/=(int64_t r) {
   if (time_internal::IsInfiniteDuration(*this) || r == 0) {
-    const bool is_neg = (r < 0) != (rep_hi_ < 0);
+    const bool is_neg = (r < 0) != (rep_hi_.Get() < 0);
     return *this = is_neg ? -InfiniteDuration() : InfiniteDuration();
   }
   return *this = ScaleFixed<std::divides>(*this, r);
@@ -470,7 +468,7 @@
 
 Duration& Duration::operator/=(double r) {
   if (time_internal::IsInfiniteDuration(*this) || !IsValidDivisor(r)) {
-    const bool is_neg = (std::signbit(r) != 0) != (rep_hi_ < 0);
+    const bool is_neg = std::signbit(r) != (rep_hi_.Get() < 0);
     return *this = is_neg ? -InfiniteDuration() : InfiniteDuration();
   }
   return *this = ScaleDouble<std::divides>(*this, r);
@@ -741,7 +739,7 @@
   char buf[kBufferSize];  // also large enough to hold integer part
   char* ep = buf + sizeof(buf);
   double d = 0;
-  int64_t frac_part = Round(std::modf(n, &d) * unit.pow10);
+  int64_t frac_part = std::round(std::modf(n, &d) * unit.pow10);
   int64_t int_part = d;
   if (int_part != 0 || frac_part != 0) {
     char* bp = Format64(ep, 0, int_part);  // always < 1000
diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc
index b7abf4b..dcf7aad 100644
--- a/absl/time/duration_test.cc
+++ b/absl/time/duration_test.cc
@@ -16,8 +16,9 @@
 #include <winsock2.h>  // for timeval
 #endif
 
-#include <chrono>  // NOLINT(build/c++11)
+#include <array>
 #include <cfloat>
+#include <chrono>  // NOLINT(build/c++11)
 #include <cmath>
 #include <cstdint>
 #include <ctime>
@@ -28,6 +29,7 @@
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/strings/str_format.h"
 #include "absl/time/time.h"
 
 namespace {
@@ -349,11 +351,6 @@
 }
 
 TEST(Duration, FactoryOverloads) {
-#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \
-    ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT
-  GTEST_SKIP();
-#endif
-
   enum E { kOne = 1 };
 #define TEST_FACTORY_OVERLOADS(NAME)                                          \
   EXPECT_EQ(1, NAME(kOne) / NAME(kOne));                                      \
@@ -884,11 +881,6 @@
 }
 
 TEST(Duration, Addition) {
-#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \
-    ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT
-  GTEST_SKIP();
-#endif
-
 #define TEST_ADD_OPS(UNIT)                  \
   do {                                      \
     EXPECT_EQ(UNIT(2), UNIT(1) + UNIT(1));  \
@@ -982,11 +974,6 @@
 }
 
 TEST(Duration, AbsoluteValue) {
-#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \
-    ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT
-  GTEST_SKIP();
-#endif
-
   EXPECT_EQ(absl::ZeroDuration(), AbsDuration(absl::ZeroDuration()));
   EXPECT_EQ(absl::Seconds(1), AbsDuration(absl::Seconds(1)));
   EXPECT_EQ(absl::Seconds(1), AbsDuration(absl::Seconds(-1)));
@@ -1004,11 +991,6 @@
 }
 
 TEST(Duration, Multiplication) {
-#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \
-    ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT
-  GTEST_SKIP();
-#endif
-
 #define TEST_MUL_OPS(UNIT)                                    \
   do {                                                        \
     EXPECT_EQ(UNIT(5), UNIT(2) * 2.5);                        \
@@ -1261,11 +1243,6 @@
 }
 
 TEST(Duration, TruncConversions) {
-#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \
-    ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT
-  GTEST_SKIP();
-#endif
-
   // Tests ToTimespec()/DurationFromTimespec()
   const struct {
     absl::Duration d;
@@ -1562,11 +1539,6 @@
 }
 
 TEST(Duration, FormatDuration) {
-#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \
-    ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT
-  GTEST_SKIP();
-#endif
-
   // Example from Go's docs.
   EXPECT_EQ("72h3m0.5s",
             absl::FormatDuration(absl::Hours(72) + absl::Minutes(3) +
@@ -1701,11 +1673,6 @@
 }
 
 TEST(Duration, ParseDuration) {
-#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \
-    ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT
-  GTEST_SKIP();
-#endif
-
   absl::Duration d;
 
   // No specified unit. Should only work for zero and infinity.
@@ -1853,4 +1820,18 @@
 #undef TEST_PARSE_ROUNDTRIP
 }
 
+TEST(Duration, AbslStringify) {
+  // FormatDuration is already well tested, so just use one test case here to
+  // verify that StrFormat("%v", d) works as expected.
+  absl::Duration d = absl::Seconds(1);
+  EXPECT_EQ(absl::StrFormat("%v", d), absl::FormatDuration(d));
+}
+
+TEST(Duration, NoPadding) {
+  // Should match the size of a struct with uint32_t alignment and no padding.
+  using NoPadding = std::array<uint32_t, 3>;
+  EXPECT_EQ(sizeof(NoPadding), sizeof(absl::Duration));
+  EXPECT_EQ(alignof(NoPadding), alignof(absl::Duration));
+}
+
 }  // namespace
diff --git a/absl/time/flag_test.cc b/absl/time/flag_test.cc
new file mode 100644
index 0000000..8f8532b
--- /dev/null
+++ b/absl/time/flag_test.cc
@@ -0,0 +1,147 @@
+// 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/flags/flag.h"
+
+#include <string>
+
+#include "gtest/gtest.h"
+#include "absl/flags/reflection.h"
+#include "absl/time/civil_time.h"
+#include "absl/time/time.h"
+
+ABSL_FLAG(absl::CivilSecond, test_flag_civil_second,
+          absl::CivilSecond(2015, 1, 2, 3, 4, 5), "");
+ABSL_FLAG(absl::CivilMinute, test_flag_civil_minute,
+          absl::CivilMinute(2015, 1, 2, 3, 4), "");
+ABSL_FLAG(absl::CivilHour, test_flag_civil_hour, absl::CivilHour(2015, 1, 2, 3),
+          "");
+ABSL_FLAG(absl::CivilDay, test_flag_civil_day, absl::CivilDay(2015, 1, 2), "");
+ABSL_FLAG(absl::CivilMonth, test_flag_civil_month, absl::CivilMonth(2015, 1),
+          "");
+ABSL_FLAG(absl::CivilYear, test_flag_civil_year, absl::CivilYear(2015), "");
+
+ABSL_FLAG(absl::Duration, test_duration_flag, absl::Seconds(5),
+          "For testing support for Duration flags");
+ABSL_FLAG(absl::Time, test_time_flag, absl::InfinitePast(),
+          "For testing support for Time flags");
+
+namespace {
+
+bool SetFlagValue(absl::string_view flag_name, absl::string_view value) {
+  auto* flag = absl::FindCommandLineFlag(flag_name);
+  if (!flag) return false;
+  std::string err;
+  return flag->ParseFrom(value, &err);
+}
+
+bool GetFlagValue(absl::string_view flag_name, std::string& value) {
+  auto* flag = absl::FindCommandLineFlag(flag_name);
+  if (!flag) return false;
+  value = flag->CurrentValue();
+  return true;
+}
+
+TEST(CivilTime, FlagSupport) {
+  // Tests the default setting of the flags.
+  const absl::CivilSecond kDefaultSec(2015, 1, 2, 3, 4, 5);
+  EXPECT_EQ(absl::CivilSecond(kDefaultSec),
+            absl::GetFlag(FLAGS_test_flag_civil_second));
+  EXPECT_EQ(absl::CivilMinute(kDefaultSec),
+            absl::GetFlag(FLAGS_test_flag_civil_minute));
+  EXPECT_EQ(absl::CivilHour(kDefaultSec),
+            absl::GetFlag(FLAGS_test_flag_civil_hour));
+  EXPECT_EQ(absl::CivilDay(kDefaultSec),
+            absl::GetFlag(FLAGS_test_flag_civil_day));
+  EXPECT_EQ(absl::CivilMonth(kDefaultSec),
+            absl::GetFlag(FLAGS_test_flag_civil_month));
+  EXPECT_EQ(absl::CivilYear(kDefaultSec),
+            absl::GetFlag(FLAGS_test_flag_civil_year));
+
+  // Sets flags to a new value.
+  const absl::CivilSecond kNewSec(2016, 6, 7, 8, 9, 10);
+  absl::SetFlag(&FLAGS_test_flag_civil_second, absl::CivilSecond(kNewSec));
+  absl::SetFlag(&FLAGS_test_flag_civil_minute, absl::CivilMinute(kNewSec));
+  absl::SetFlag(&FLAGS_test_flag_civil_hour, absl::CivilHour(kNewSec));
+  absl::SetFlag(&FLAGS_test_flag_civil_day, absl::CivilDay(kNewSec));
+  absl::SetFlag(&FLAGS_test_flag_civil_month, absl::CivilMonth(kNewSec));
+  absl::SetFlag(&FLAGS_test_flag_civil_year, absl::CivilYear(kNewSec));
+
+  EXPECT_EQ(absl::CivilSecond(kNewSec),
+            absl::GetFlag(FLAGS_test_flag_civil_second));
+  EXPECT_EQ(absl::CivilMinute(kNewSec),
+            absl::GetFlag(FLAGS_test_flag_civil_minute));
+  EXPECT_EQ(absl::CivilHour(kNewSec),
+            absl::GetFlag(FLAGS_test_flag_civil_hour));
+  EXPECT_EQ(absl::CivilDay(kNewSec), absl::GetFlag(FLAGS_test_flag_civil_day));
+  EXPECT_EQ(absl::CivilMonth(kNewSec),
+            absl::GetFlag(FLAGS_test_flag_civil_month));
+  EXPECT_EQ(absl::CivilYear(kNewSec),
+            absl::GetFlag(FLAGS_test_flag_civil_year));
+}
+
+TEST(Duration, FlagSupport) {
+  EXPECT_EQ(absl::Seconds(5), absl::GetFlag(FLAGS_test_duration_flag));
+
+  absl::SetFlag(&FLAGS_test_duration_flag, absl::Seconds(10));
+  EXPECT_EQ(absl::Seconds(10), absl::GetFlag(FLAGS_test_duration_flag));
+
+  EXPECT_TRUE(SetFlagValue("test_duration_flag", "20s"));
+  EXPECT_EQ(absl::Seconds(20), absl::GetFlag(FLAGS_test_duration_flag));
+
+  std::string current_flag_value;
+  EXPECT_TRUE(GetFlagValue("test_duration_flag", current_flag_value));
+  EXPECT_EQ("20s", current_flag_value);
+}
+
+TEST(Time, FlagSupport) {
+  EXPECT_EQ(absl::InfinitePast(), absl::GetFlag(FLAGS_test_time_flag));
+
+  const absl::Time t = absl::FromCivil(absl::CivilSecond(2016, 1, 2, 3, 4, 5),
+                                       absl::UTCTimeZone());
+  absl::SetFlag(&FLAGS_test_time_flag, t);
+  EXPECT_EQ(t, absl::GetFlag(FLAGS_test_time_flag));
+
+  // Successful parse
+  EXPECT_TRUE(SetFlagValue("test_time_flag", "2016-01-02T03:04:06Z"));
+  EXPECT_EQ(t + absl::Seconds(1), absl::GetFlag(FLAGS_test_time_flag));
+  EXPECT_TRUE(SetFlagValue("test_time_flag", "2016-01-02T03:04:07.0Z"));
+  EXPECT_EQ(t + absl::Seconds(2), absl::GetFlag(FLAGS_test_time_flag));
+  EXPECT_TRUE(SetFlagValue("test_time_flag", "2016-01-02T03:04:08.000Z"));
+  EXPECT_EQ(t + absl::Seconds(3), absl::GetFlag(FLAGS_test_time_flag));
+  EXPECT_TRUE(SetFlagValue("test_time_flag", "2016-01-02T03:04:09+00:00"));
+  EXPECT_EQ(t + absl::Seconds(4), absl::GetFlag(FLAGS_test_time_flag));
+  EXPECT_TRUE(SetFlagValue("test_time_flag", "2016-01-02T03:04:05.123+00:00"));
+  EXPECT_EQ(t + absl::Milliseconds(123), absl::GetFlag(FLAGS_test_time_flag));
+  EXPECT_TRUE(SetFlagValue("test_time_flag", "2016-01-02T03:04:05.123+08:00"));
+  EXPECT_EQ(t + absl::Milliseconds(123) - absl::Hours(8),
+            absl::GetFlag(FLAGS_test_time_flag));
+  EXPECT_TRUE(SetFlagValue("test_time_flag", "infinite-future"));
+  EXPECT_EQ(absl::InfiniteFuture(), absl::GetFlag(FLAGS_test_time_flag));
+  EXPECT_TRUE(SetFlagValue("test_time_flag", "infinite-past"));
+  EXPECT_EQ(absl::InfinitePast(), absl::GetFlag(FLAGS_test_time_flag));
+
+  EXPECT_FALSE(SetFlagValue("test_time_flag", "2016-01-02T03:04:06"));
+  EXPECT_FALSE(SetFlagValue("test_time_flag", "2016-01-02"));
+  EXPECT_FALSE(SetFlagValue("test_time_flag", "2016-01-02Z"));
+  EXPECT_FALSE(SetFlagValue("test_time_flag", "2016-01-02+00:00"));
+  EXPECT_FALSE(SetFlagValue("test_time_flag", "2016-99-99T03:04:06Z"));
+
+  EXPECT_TRUE(SetFlagValue("test_time_flag", "2016-01-02T03:04:05Z"));
+  std::string current_flag_value;
+  EXPECT_TRUE(GetFlagValue("test_time_flag", current_flag_value));
+  EXPECT_EQ("2016-01-02T03:04:05+00:00", current_flag_value);
+}
+
+}  // namespace
diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel
index 7304d40..edeabd8 100644
--- a/absl/time/internal/cctz/BUILD.bazel
+++ b/absl/time/internal/cctz/BUILD.bazel
@@ -16,25 +16,6 @@
 
 licenses(["notice"])
 
-filegroup(
-    name = "zoneinfo",
-    srcs = glob(["testdata/zoneinfo/**"]),
-)
-
-config_setting(
-    name = "osx",
-    constraint_values = [
-        "@platforms//os:osx",
-    ],
-)
-
-config_setting(
-    name = "ios",
-    constraint_values = [
-        "@platforms//os:ios",
-    ],
-)
-
 ### libraries
 
 cc_library(
@@ -72,15 +53,10 @@
         "include/cctz/time_zone.h",
         "include/cctz/zone_info_source.h",
     ],
-    linkopts = select({
-        ":osx": [
-            "-framework Foundation",
-        ],
-        ":ios": [
-            "-framework Foundation",
-        ],
-        "//conditions:default": [],
-    }),
+    # OS X and iOS no longer use `linkopts = ["-framework CoreFoundation"]`
+    # as (1) bazel adds it automatically, and (2) it caused problems when
+    # cross-compiling for Android.
+    # See https://github.com/abseil/abseil-cpp/issues/326 for details.
     visibility = ["//visibility:public"],
     deps = [
         ":civil_time",
@@ -115,6 +91,7 @@
     size = "small",
     srcs = ["src/time_zone_format_test.cc"],
     data = [":zoneinfo"],
+    env = {"TZDIR": "absl/time/internal/cctz/testdata/zoneinfo"},
     tags = [
         "no_test_android_arm",
         "no_test_android_arm64",
@@ -135,6 +112,7 @@
     timeout = "moderate",
     srcs = ["src/time_zone_lookup_test.cc"],
     data = [":zoneinfo"],
+    env = {"TZDIR": "absl/time/internal/cctz/testdata/zoneinfo"},
     tags = [
         "no_test_android_arm",
         "no_test_android_arm64",
@@ -170,6 +148,12 @@
     ],
 )
 
+filegroup(
+    name = "zoneinfo",
+    srcs = glob(["testdata/zoneinfo/**"]),
+    visibility = ["//absl/time:__subpackages__"],
+)
+
 ### examples
 
 ### binaries
diff --git a/absl/time/internal/cctz/src/time_zone_fixed.cc b/absl/time/internal/cctz/src/time_zone_fixed.cc
index f2b3294..e09654e 100644
--- a/absl/time/internal/cctz/src/time_zone_fixed.cc
+++ b/absl/time/internal/cctz/src/time_zone_fixed.cc
@@ -105,7 +105,7 @@
   offset_minutes %= 60;
   const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
   char buf[prefix_len + sizeof("-24:00:00")];
-  char* ep = std::copy(kFixedZonePrefix, kFixedZonePrefix + prefix_len, buf);
+  char* ep = std::copy_n(kFixedZonePrefix, prefix_len, buf);
   *ep++ = sign;
   ep = Format02d(ep, offset_hours);
   *ep++ = ':';
diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc
index 8966f7a..787426f 100644
--- a/absl/time/internal/cctz/src/time_zone_info.cc
+++ b/absl/time/internal/cctz/src/time_zone_info.cc
@@ -502,9 +502,9 @@
   // encoded zoneinfo. The ttisstd/ttisgmt indicators only apply when
   // interpreting a POSIX spec that does not include start/end rules, and
   // that isn't the case here (see "zic -p").
-  bp += (8 + 4) * hdr.leapcnt;  // leap-time + TAI-UTC
-  bp += 1 * hdr.ttisstdcnt;     // UTC/local indicators
-  bp += 1 * hdr.ttisutcnt;      // standard/wall indicators
+  bp += (time_len + 4) * hdr.leapcnt;  // leap-time + TAI-UTC
+  bp += 1 * hdr.ttisstdcnt;            // UTC/local indicators
+  bp += 1 * hdr.ttisutcnt;             // standard/wall indicators
   assert(bp == tbuf.data() + tbuf.size());
 
   future_spec_.clear();
@@ -533,8 +533,8 @@
 
   // Trim redundant transitions. zic may have added these to work around
   // differences between the glibc and reference implementations (see
-  // zic.c:dontmerge) and the Qt library (see zic.c:WORK_AROUND_QTBUG_53071).
-  // For us, they just get in the way when we do future_spec_ extension.
+  // zic.c:dontmerge) or to avoid bugs in old readers. For us, they just
+  // get in the way when we do future_spec_ extension.
   while (hdr.timecnt > 1) {
     if (!EquivTransitions(transitions_[hdr.timecnt - 1].type_index,
                           transitions_[hdr.timecnt - 2].type_index)) {
diff --git a/absl/time/internal/cctz/src/time_zone_posix.h b/absl/time/internal/cctz/src/time_zone_posix.h
index 0cf2905..7fd2b9e 100644
--- a/absl/time/internal/cctz/src/time_zone_posix.h
+++ b/absl/time/internal/cctz/src/time_zone_posix.h
@@ -104,7 +104,7 @@
 
 // The entirety of a POSIX-string specified time-zone rule. The standard
 // abbreviation and offset are always given. If the time zone includes
-// daylight saving, then the daylight abbrevation is non-empty and the
+// daylight saving, then the daylight abbreviation is non-empty and the
 // remaining fields are also valid. Note that the start/end transitions
 // are not ordered---in the southern hemisphere the transition to end
 // daylight time occurs first in any particular year.
diff --git a/absl/time/internal/cctz/src/tzfile.h b/absl/time/internal/cctz/src/tzfile.h
index 31e8598..9613055 100644
--- a/absl/time/internal/cctz/src/tzfile.h
+++ b/absl/time/internal/cctz/src/tzfile.h
@@ -102,20 +102,24 @@
 */
 
 #ifndef TZ_MAX_TIMES
+/* This must be at least 242 for Europe/London with 'zic -b fat'.  */
 #define TZ_MAX_TIMES 2000
 #endif /* !defined TZ_MAX_TIMES */
 
 #ifndef TZ_MAX_TYPES
-/* This must be at least 17 for Europe/Samara and Europe/Vilnius.  */
+/* This must be at least 18 for Europe/Vilnius with 'zic -b fat'.  */
 #define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
 #endif                   /* !defined TZ_MAX_TYPES */
 
 #ifndef TZ_MAX_CHARS
+/* This must be at least 40 for America/Anchorage.  */
 #define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
                         /* (limited by what unsigned chars can hold) */
 #endif                  /* !defined TZ_MAX_CHARS */
 
 #ifndef TZ_MAX_LEAPS
+/* This must be at least 27 for leap seconds from 1972 through mid-2023.
+   There's a plan to discontinue leap seconds by 2035.  */
 #define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
 #endif                  /* !defined TZ_MAX_LEAPS */
 
diff --git a/absl/time/internal/cctz/src/zone_info_source.cc b/absl/time/internal/cctz/src/zone_info_source.cc
index 5ab5a59..9bc8197 100644
--- a/absl/time/internal/cctz/src/zone_info_source.cc
+++ b/absl/time/internal/cctz/src/zone_info_source.cc
@@ -58,7 +58,8 @@
 // MinGW is GCC on Windows, so while it asserts __has_attribute(weak), the
 // Windows linker cannot handle that. Nor does the MinGW compiler know how to
 // pass "#pragma comment(linker, ...)" to the Windows linker.
-#if (__has_attribute(weak) || defined(__GNUC__)) && !defined(__MINGW32__)
+#if (__has_attribute(weak) || defined(__GNUC__)) && !defined(__MINGW32__) && \
+    !defined(__CYGWIN__)
 ZoneInfoSourceFactory zone_info_source_factory __attribute__((weak)) =
     DefaultFactory;
 #elif defined(_MSC_VER) && !defined(__MINGW32__) && !defined(_LIBCPP_VERSION)
diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version
index 9caed31..7daa77e 100644
--- a/absl/time/internal/cctz/testdata/version
+++ b/absl/time/internal/cctz/testdata/version
@@ -1 +1 @@
-2022d
+2023c
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo
index ea38c97..1e6d48d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca
index 0263c90..240ebb2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun b/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun
index 772e23c..909c5f9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas b/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas
index cbe22a7..48faea2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota b/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota
index 6cb53d4..85b9033 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Bay b/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Bay
index 0a22252..1092f4b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Bay
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Bay
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua b/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua
index e1780a5..5e0a54f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Ciudad_Juarez b/absl/time/internal/cctz/testdata/zoneinfo/America/Ciudad_Juarez
new file mode 100644
index 0000000..f636ee6
--- /dev/null
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Ciudad_Juarez
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada b/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
index 19ccd35..e8be26b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab b/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab
index 4ddc99d..00b57bb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo b/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo
index 8283239..5c92e29 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik b/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik
index af3107d..86639f6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluit b/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluit
index eb2c99c..95e055c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluit
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluit
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros b/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros
index 722751b..88cabcd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan b/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan
index 4c819fa..97d4d36 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Merida b/absl/time/internal/cctz/testdata/zoneinfo/America/Merida
index d3b0ca1..e5de113 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Merida
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Merida
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City b/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City
index ffcf8be..80a415c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey b/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey
index dea9e3f..a5822e2 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigon b/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigon
index b9f67a9..fe6be8e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigon
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigon
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk b/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk
index 4ddc99d..00b57bb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga b/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga
index da0909c..2fc74e9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtung b/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtung
index 5be6f9b..95e055c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtung
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtung
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River b/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River
index d6ddda4..7e646d1 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet b/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet
index 92e2ed2..6d1d90d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute b/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute
index a84d1df..97eb8a9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel b/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
index 19ccd35..e8be26b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Bay b/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Bay
index fcb0328..fe6be8e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Bay
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Bay
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana b/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
index 19ccd35..e8be26b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse b/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse
index 878b6a9..40baa9a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife b/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife
index c779cef..645ee94 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman
index d97d308..a3f9dff 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/Damascus b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus
index 168ef9b..bd1624d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
index bed968e..7e83389 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 3ce1bac..fcf923b 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/Asia/Kuala_Lumpur b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur
index e93dd51..b396dec 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore
index 350d77e..dbbdea3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon
index 878b6a9..40baa9a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Egypt b/absl/time/internal/cctz/testdata/zoneinfo/Egypt
index ea38c97..1e6d48d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Egypt
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Egypt
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov
index d1c93c5..bfac561 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd
index c517002..0715d58 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
index 19ccd35..e8be26b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur
index 4c819fa..97d4d36 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General
index ffcf8be..80a415c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
index 8b2dd52..610b850 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/Singapore b/absl/time/internal/cctz/testdata/zoneinfo/Singapore
index 350d77e..dbbdea3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Singapore
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Singapore
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab b/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab
index a4ff61a..be3348d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab
@@ -3,13 +3,13 @@
 # This file is in the public domain, so clarified as of
 # 2009-05-17 by Arthur David Olson.
 #
-# From Paul Eggert (2015-05-02):
+# From Paul Eggert (2022-11-18):
 # This file contains a table of two-letter country codes.  Columns are
 # separated by a single tab.  Lines beginning with '#' are comments.
 # All text uses UTF-8 encoding.  The columns of the table are as follows:
 #
 # 1.  ISO 3166-1 alpha-2 country code, current as of
-#     ISO 3166-1 N976 (2018-11-06).  See: Updates on ISO 3166-1
+#     ISO 3166-1 N1087 (2022-09-02).  See: Updates on ISO 3166-1
 #     https://isotc.iso.org/livelink/livelink/Open/16944257
 # 2.  The usual English name for the coded region,
 #     chosen so that alphabetic sorting of subsets produces helpful lists.
@@ -238,7 +238,7 @@
 SZ	Eswatini (Swaziland)
 TC	Turks & Caicos Is
 TD	Chad
-TF	French Southern & Antarctic Lands
+TF	French S. Terr.
 TG	Togo
 TH	Thailand
 TJ	Tajikistan
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
index cf9cf20..1f1cecb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
@@ -18,7 +18,10 @@
 #     Please see the theory.html file for how these names are chosen.
 #     If multiple timezones overlap a country, each has a row in the
 #     table, with each column 1 containing the country code.
-# 4.  Comments; present if and only if a country has multiple timezones.
+# 4.  Comments; present if and only if countries have multiple timezones,
+#     and useful only for those countries.  For example, the comments
+#     for the row with countries CH,DE,LI and name Europe/Zurich
+#     are useful only for DE, since CH and LI have no other timezones.
 #
 # If a timezone covers multiple countries, the most-populous city is used,
 # and that country is listed first in column 1; any other countries
@@ -34,7 +37,7 @@
 #country-
 #codes	coordinates	TZ	comments
 AD	+4230+00131	Europe/Andorra
-AE,OM,RE,SC,TF	+2518+05518	Asia/Dubai	UAE, Oman, Réunion, Seychelles, Crozet, Scattered Is
+AE,OM,RE,SC,TF	+2518+05518	Asia/Dubai	Crozet, Scattered Is
 AF	+3431+06912	Asia/Kabul
 AL	+4120+01950	Europe/Tirane
 AM	+4011+04430	Asia/Yerevan
@@ -45,7 +48,7 @@
 AQ	-6734-06808	Antarctica/Rothera	Rothera
 AQ	-720041+0023206	Antarctica/Troll	Troll
 AR	-3436-05827	America/Argentina/Buenos_Aires	Buenos Aires (BA, CF)
-AR	-3124-06411	America/Argentina/Cordoba	Argentina (most areas: CB, CC, CN, ER, FM, MN, SE, SF)
+AR	-3124-06411	America/Argentina/Cordoba	most areas: CB, CC, CN, ER, FM, MN, SE, SF
 AR	-2447-06525	America/Argentina/Salta	Salta (SA, LP, NQ, RN)
 AR	-2411-06518	America/Argentina/Jujuy	Jujuy (JY)
 AR	-2649-06513	America/Argentina/Tucuman	Tucumán (TM)
@@ -56,7 +59,7 @@
 AR	-3319-06621	America/Argentina/San_Luis	San Luis (SL)
 AR	-5138-06913	America/Argentina/Rio_Gallegos	Santa Cruz (SC)
 AR	-5448-06818	America/Argentina/Ushuaia	Tierra del Fuego (TF)
-AS,UM	-1416-17042	Pacific/Pago_Pago	Samoa, Midway
+AS,UM	-1416-17042	Pacific/Pago_Pago	Midway
 AT	+4813+01620	Europe/Vienna
 AU	-3133+15905	Australia/Lord_Howe	Lord Howe Island
 AU	-5430+15857	Antarctica/Macquarie	Macquarie Island
@@ -101,30 +104,25 @@
 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,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,BS	+4339-07923	America/Toronto	Eastern - ON, QC (most areas)
+CA	+6344-06828	America/Iqaluit	Eastern - NU (most areas)
 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)
 CA	+624900-0920459	America/Rankin_Inlet	Central - NU (central)
 CA	+5024-10439	America/Regina	CST - SK (most areas)
 CA	+5017-10750	America/Swift_Current	CST - SK (midwest)
-CA	+5333-11328	America/Edmonton	Mountain - AB; BC (E); SK (W)
+CA	+5333-11328	America/Edmonton	Mountain - AB; BC (E); NT (E); SK (W)
 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	+5546-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)
 CA	+6404-13925	America/Dawson	MST - Yukon (west)
 CA	+4916-12307	America/Vancouver	Pacific - BC (most areas)
-CH,DE,LI	+4723+00832	Europe/Zurich	Swiss time
+CH,DE,LI	+4723+00832	Europe/Zurich	Büsingen
 CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG	+0519-00402	Africa/Abidjan
 CK	-2114-15946	Pacific/Rarotonga
-CL	-3327-07040	America/Santiago	Chile (most areas)
+CL	-3327-07040	America/Santiago	most of Chile
 CL	-5309-07055	America/Punta_Arenas	Region of Magallanes
 CL	-2709-10926	Pacific/Easter	Easter Island
 CN	+3114+12128	Asia/Shanghai	Beijing Time
@@ -133,10 +131,10 @@
 CR	+0956-08405	America/Costa_Rica
 CU	+2308-08222	America/Havana
 CV	+1455-02331	Atlantic/Cape_Verde
-CY	+3510+03322	Asia/Nicosia	Cyprus (most areas)
+CY	+3510+03322	Asia/Nicosia	most of Cyprus
 CY	+3507+03357	Asia/Famagusta	Northern Cyprus
 CZ,SK	+5005+01426	Europe/Prague
-DE,DK,NO,SE,SJ	+5230+01322	Europe/Berlin	Germany (most areas), Scandinavia
+DE,DK,NO,SE,SJ	+5230+01322	Europe/Berlin	most of Germany
 DO	+1828-06954	America/Santo_Domingo
 DZ	+3647+00303	Africa/Algiers
 EC	-0210-07950	America/Guayaquil	Ecuador (mainland)
@@ -157,7 +155,7 @@
 GE	+4143+04449	Asia/Tbilisi
 GF	+0456-05220	America/Cayenne
 GI	+3608-00521	Europe/Gibraltar
-GL	+6411-05144	America/Nuuk	Greenland (most areas)
+GL	+6411-05144	America/Nuuk	most of Greenland
 GL	+7646-01840	America/Danmarkshavn	National Park (east coast)
 GL	+7029-02158	America/Scoresbysund	Scoresbysund/Ittoqqortoormiit
 GL	+7634-06847	America/Thule	Thule/Pituffik
@@ -187,12 +185,12 @@
 JP	+353916+1394441	Asia/Tokyo
 KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT	-0117+03649	Africa/Nairobi
 KG	+4254+07436	Asia/Bishkek
-KI,MH,TV,UM,WF	+0125+17300	Pacific/Tarawa	Gilberts, Marshalls, Tuvalu, Wallis & Futuna, Wake
+KI,MH,TV,UM,WF	+0125+17300	Pacific/Tarawa	Gilberts, Marshalls, Wake
 KI	-0247-17143	Pacific/Kanton	Phoenix Islands
 KI	+0152-15720	Pacific/Kiritimati	Line Islands
 KP	+3901+12545	Asia/Pyongyang
 KR	+3733+12658	Asia/Seoul
-KZ	+4315+07657	Asia/Almaty	Kazakhstan (most areas)
+KZ	+4315+07657	Asia/Almaty	most of Kazakhstan
 KZ	+4448+06528	Asia/Qyzylorda	Qyzylorda/Kyzylorda/Kzyl-Orda
 KZ	+5312+06337	Asia/Qostanay	Qostanay/Kostanay/Kustanay
 KZ	+5017+05710	Asia/Aqtobe	Aqtöbe/Aktobe
@@ -209,26 +207,27 @@
 MD	+4700+02850	Europe/Chisinau
 MH	+0905+16720	Pacific/Kwajalein	Kwajalein
 MM,CC	+1647+09610	Asia/Yangon
-MN	+4755+10653	Asia/Ulaanbaatar	Mongolia (most areas)
+MN	+4755+10653	Asia/Ulaanbaatar	most of Mongolia
 MN	+4801+09139	Asia/Hovd	Bayan-Ölgii, Govi-Altai, Hovd, Uvs, Zavkhan
 MN	+4804+11430	Asia/Choibalsan	Dornod, Sükhbaatar
 MO	+221150+1133230	Asia/Macau
 MQ	+1436-06105	America/Martinique
 MT	+3554+01431	Europe/Malta
 MU	-2010+05730	Indian/Mauritius
-MV,TF	+0410+07330	Indian/Maldives	Maldives, Kerguelen, St Paul I, Amsterdam I
-MX	+1924-09909	America/Mexico_City	Central Time
-MX	+2105-08646	America/Cancun	Eastern Standard Time - Quintana Roo
-MX	+2058-08937	America/Merida	Central Time - Campeche, Yucatán
-MX	+2540-10019	America/Monterrey	Central Time - Durango; Coahuila, Nuevo León, Tamaulipas (most areas)
-MX	+2550-09730	America/Matamoros	Central Time US - Coahuila, Nuevo León, Tamaulipas (US border)
-MX	+2313-10625	America/Mazatlan	Mountain Time - Baja California Sur, Nayarit, Sinaloa
-MX	+2838-10605	America/Chihuahua	Mountain Time - Chihuahua (most areas)
-MX	+2934-10425	America/Ojinaga	Mountain Time US - Chihuahua (US border)
-MX	+2904-11058	America/Hermosillo	Mountain Standard Time - Sonora
-MX	+3232-11701	America/Tijuana	Pacific Time US - Baja California
-MX	+2048-10515	America/Bahia_Banderas	Central Time - Bahía de Banderas
-MY,BN	+0133+11020	Asia/Kuching	Sabah, Sarawak, Brunei
+MV,TF	+0410+07330	Indian/Maldives	Kerguelen, St Paul I, Amsterdam I
+MX	+1924-09909	America/Mexico_City	Central Mexico
+MX	+2105-08646	America/Cancun	Quintana Roo
+MX	+2058-08937	America/Merida	Campeche, Yucatán
+MX	+2540-10019	America/Monterrey	Durango; Coahuila, Nuevo León, Tamaulipas (most areas)
+MX	+2550-09730	America/Matamoros	Coahuila, Nuevo León, Tamaulipas (US border)
+MX	+2838-10605	America/Chihuahua	Chihuahua (most areas)
+MX	+3144-10629	America/Ciudad_Juarez	Chihuahua (US border - west)
+MX	+2934-10425	America/Ojinaga	Chihuahua (US border - east)
+MX	+2313-10625	America/Mazatlan	Baja California Sur, Nayarit (most areas), Sinaloa
+MX	+2048-10515	America/Bahia_Banderas	Bahía de Banderas
+MX	+2904-11058	America/Hermosillo	Sonora
+MX	+3232-11701	America/Tijuana	Baja California
+MY,BN	+0133+11020	Asia/Kuching	Sabah, Sarawak
 MZ,BI,BW,CD,MW,RW,ZM,ZW	-2558+03235	Africa/Maputo	Central Africa Time
 NA	-2234+01706	Africa/Windhoek
 NC	-2216+16627	Pacific/Noumea
@@ -240,7 +239,7 @@
 NU	-1901-16955	Pacific/Niue
 NZ,AQ	-3652+17446	Pacific/Auckland	New Zealand time
 NZ	-4357-17633	Pacific/Chatham	Chatham Islands
-PA,CA,KY	+0858-07932	America/Panama	EST - Panama, Cayman, ON (Atikokan), NU (Coral H)
+PA,CA,KY	+0858-07932	America/Panama	EST - ON (Atikokan), NU (Coral H)
 PE	-1203-07703	America/Lima
 PF	-1732-14934	Pacific/Tahiti	Society Islands
 PF	-0900-13930	Pacific/Marquesas	Marquesas Islands
@@ -288,13 +287,13 @@
 RU	+643337+1431336	Asia/Ust-Nera	MSK+07 - Oymyakonsky
 RU	+5934+15048	Asia/Magadan	MSK+08 - Magadan
 RU	+4658+14242	Asia/Sakhalin	MSK+08 - Sakhalin Island
-RU	+6728+15343	Asia/Srednekolymsk	MSK+08 - Sakha (E); North Kuril Is
+RU	+6728+15343	Asia/Srednekolymsk	MSK+08 - Sakha (E); N Kuril Is
 RU	+5301+15839	Asia/Kamchatka	MSK+09 - Kamchatka
 RU	+6445+17729	Asia/Anadyr	MSK+09 - Bering Sea
-SA,AQ,KW,YE	+2438+04643	Asia/Riyadh	Arabia, Syowa
-SB,FM	-0932+16012	Pacific/Guadalcanal	Solomons, Pohnpei
+SA,AQ,KW,YE	+2438+04643	Asia/Riyadh	Syowa
+SB,FM	-0932+16012	Pacific/Guadalcanal	Pohnpei
 SD	+1536+03232	Africa/Khartoum
-SG,MY	+0117+10351	Asia/Singapore	Singapore, peninsular Malaysia
+SG,MY	+0117+10351	Asia/Singapore	peninsular Malaysia
 SR	+0550-05510	America/Paramaribo
 SS	+0451+03137	Africa/Juba
 ST	+0020+00644	Africa/Sao_Tome
@@ -302,7 +301,7 @@
 SY	+3330+03618	Asia/Damascus
 TC	+2128-07108	America/Grand_Turk
 TD	+1207+01503	Africa/Ndjamena
-TH,CX,KH,LA,VN	+1345+10031	Asia/Bangkok	Indochina (most areas)
+TH,CX,KH,LA,VN	+1345+10031	Asia/Bangkok	north Vietnam
 TJ	+3835+06848	Asia/Dushanbe
 TK	-0922-17114	Pacific/Fakaofo
 TL	-0833+12535	Asia/Dili
@@ -311,7 +310,7 @@
 TO	-210800-1751200	Pacific/Tongatapu
 TR	+4101+02858	Europe/Istanbul
 TW	+2503+12130	Asia/Taipei
-UA	+5026+03031	Europe/Kyiv	Ukraine (most areas)
+UA	+5026+03031	Europe/Kyiv	most of Ukraine
 US	+404251-0740023	America/New_York	Eastern (most areas)
 US	+421953-0830245	America/Detroit	Eastern - MI (most areas)
 US	+381515-0854534	America/Kentucky/Louisville	Eastern - KY (Louisville area)
@@ -331,7 +330,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,CA	+332654-1120424	America/Phoenix	MST - Arizona (except Navajo), Creston BC
+US,CA	+332654-1120424	America/Phoenix	MST - AZ (most areas), 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
@@ -339,13 +338,13 @@
 US	+550737-1313435	America/Metlakatla	Alaska - Annette Island
 US	+593249-1394338	America/Yakutat	Alaska - Yakutat
 US	+643004-1652423	America/Nome	Alaska (west)
-US	+515248-1763929	America/Adak	Aleutian Islands
-US,UM	+211825-1575130	Pacific/Honolulu	Hawaii
+US	+515248-1763929	America/Adak	Alaska - western Aleutians
+US	+211825-1575130	Pacific/Honolulu	Hawaii
 UY	-345433-0561245	America/Montevideo
 UZ	+3940+06648	Asia/Samarkand	Uzbekistan (west)
 UZ	+4120+06918	Asia/Tashkent	Uzbekistan (east)
 VE	+1030-06656	America/Caracas
-VN	+1045+10640	Asia/Ho_Chi_Minh	Vietnam (south)
+VN	+1045+10640	Asia/Ho_Chi_Minh	south Vietnam
 VU	-1740+16825	Pacific/Efate
 WS	-1350-17144	Pacific/Apia
 ZA,LS,SZ	-2615+02800	Africa/Johannesburg
diff --git a/absl/time/internal/test_util.cc b/absl/time/internal/test_util.cc
index 4b7849c..3e2452e 100644
--- a/absl/time/internal/test_util.cc
+++ b/absl/time/internal/test_util.cc
@@ -14,16 +14,8 @@
 
 #include "absl/time/internal/test_util.h"
 
-#include <algorithm>
-#include <cstddef>
-#include <cstring>
-#include <memory>
-
 #include "absl/base/config.h"
 #include "absl/base/internal/raw_logging.h"
-#include "absl/time/internal/cctz/include/cctz/zone_info_source.h"
-
-namespace cctz = absl::time_internal::cctz;
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -38,95 +30,3 @@
 }  // namespace time_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz_extension {
-namespace {
-
-// Embed the zoneinfo data for time zones used during tests and benchmarks.
-// The data was generated using "xxd -i zoneinfo-file".  There is no need
-// to update the data as long as the tests do not depend on recent changes
-// (and the past rules remain the same).
-#include "absl/time/internal/zoneinfo.inc"
-
-const struct ZoneInfo {
-  const char* name;
-  const char* data;
-  std::size_t length;
-} kZoneInfo[] = {
-    // The three real time zones used by :time_test and :time_benchmark.
-    {"America/Los_Angeles",  //
-     reinterpret_cast<char*>(America_Los_Angeles), America_Los_Angeles_len},
-    {"America/New_York",  //
-     reinterpret_cast<char*>(America_New_York), America_New_York_len},
-    {"Australia/Sydney",  //
-     reinterpret_cast<char*>(Australia_Sydney), Australia_Sydney_len},
-
-    // Other zones named in tests but which should fail to load.
-    {"Invalid/TimeZone", nullptr, 0},
-    {"", nullptr, 0},
-
-    // Allows use of the local time zone from a system-specific location.
-#ifdef _MSC_VER
-    {"localtime",  //
-     reinterpret_cast<char*>(America_Los_Angeles), America_Los_Angeles_len},
-#else
-    {"/etc/localtime",  //
-     reinterpret_cast<char*>(America_Los_Angeles), America_Los_Angeles_len},
-#endif
-};
-
-class TestZoneInfoSource : public cctz::ZoneInfoSource {
- public:
-  TestZoneInfoSource(const char* data, std::size_t size)
-      : data_(data), end_(data + size) {}
-
-  std::size_t Read(void* ptr, std::size_t size) override {
-    const std::size_t len =
-        std::min(size, static_cast<std::size_t>(end_ - data_));
-    memcpy(ptr, data_, len);
-    data_ += len;
-    return len;
-  }
-
-  int Skip(std::size_t offset) override {
-    data_ += std::min(offset, static_cast<std::size_t>(end_ - data_));
-    return 0;
-  }
-
- private:
-  const char* data_;
-  const char* const end_;
-};
-
-std::unique_ptr<cctz::ZoneInfoSource> TestFactory(
-    const std::string& name,
-    const std::function<std::unique_ptr<cctz::ZoneInfoSource>(
-        const std::string& name)>& /*fallback_factory*/) {
-  for (const ZoneInfo& zoneinfo : kZoneInfo) {
-    if (name == zoneinfo.name) {
-      if (zoneinfo.data == nullptr) return nullptr;
-      return std::unique_ptr<cctz::ZoneInfoSource>(
-          new TestZoneInfoSource(zoneinfo.data, zoneinfo.length));
-    }
-  }
-
-  // The embedded zoneinfo data does not include the zone, so fallback to
-  // built-in UTC. The tests have been crafted so that this should only
-  // happen when testing absl::LocalTimeZone() with an unconstrained ${TZ}.
-  return nullptr;
-}
-
-}  // namespace
-
-#if !defined(__MINGW32__)
-// MinGW does not support the weak symbol extension mechanism.
-ZoneInfoSourceFactory zone_info_source_factory = TestFactory;
-#endif
-
-}  // namespace cctz_extension
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/absl/time/internal/zoneinfo.inc b/absl/time/internal/zoneinfo.inc
deleted file mode 100644
index 7d8b3ff..0000000
--- a/absl/time/internal/zoneinfo.inc
+++ /dev/null
@@ -1,724 +0,0 @@
-unsigned char America_Los_Angeles[] = {
-  0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
-  0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba,
-  0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00,
-  0x9e, 0xa6, 0x48, 0xa0, 0x9f, 0xbb, 0x15, 0x90, 0xa0, 0x86, 0x2a, 0xa0,
-  0xa1, 0x9a, 0xf7, 0x90, 0xcb, 0x89, 0x1a, 0xa0, 0xd2, 0x23, 0xf4, 0x70,
-  0xd2, 0x61, 0x26, 0x10, 0xd6, 0xfe, 0x74, 0x5c, 0xd8, 0x80, 0xad, 0x90,
-  0xda, 0xfe, 0xc3, 0x90, 0xdb, 0xc0, 0x90, 0x10, 0xdc, 0xde, 0xa5, 0x90,
-  0xdd, 0xa9, 0xac, 0x90, 0xde, 0xbe, 0x87, 0x90, 0xdf, 0x89, 0x8e, 0x90,
-  0xe0, 0x9e, 0x69, 0x90, 0xe1, 0x69, 0x70, 0x90, 0xe2, 0x7e, 0x4b, 0x90,
-  0xe3, 0x49, 0x52, 0x90, 0xe4, 0x5e, 0x2d, 0x90, 0xe5, 0x29, 0x34, 0x90,
-  0xe6, 0x47, 0x4a, 0x10, 0xe7, 0x12, 0x51, 0x10, 0xe8, 0x27, 0x2c, 0x10,
-  0xe8, 0xf2, 0x33, 0x10, 0xea, 0x07, 0x0e, 0x10, 0xea, 0xd2, 0x15, 0x10,
-  0xeb, 0xe6, 0xf0, 0x10, 0xec, 0xb1, 0xf7, 0x10, 0xed, 0xc6, 0xd2, 0x10,
-  0xee, 0x91, 0xd9, 0x10, 0xef, 0xaf, 0xee, 0x90, 0xf0, 0x71, 0xbb, 0x10,
-  0xf1, 0x8f, 0xd0, 0x90, 0xf2, 0x7f, 0xc1, 0x90, 0xf3, 0x6f, 0xb2, 0x90,
-  0xf4, 0x5f, 0xa3, 0x90, 0xf5, 0x4f, 0x94, 0x90, 0xf6, 0x3f, 0x85, 0x90,
-  0xf7, 0x2f, 0x76, 0x90, 0xf8, 0x28, 0xa2, 0x10, 0xf9, 0x0f, 0x58, 0x90,
-  0xfa, 0x08, 0x84, 0x10, 0xfa, 0xf8, 0x83, 0x20, 0xfb, 0xe8, 0x66, 0x10,
-  0xfc, 0xd8, 0x65, 0x20, 0xfd, 0xc8, 0x48, 0x10, 0xfe, 0xb8, 0x47, 0x20,
-  0xff, 0xa8, 0x2a, 0x10, 0x00, 0x98, 0x29, 0x20, 0x01, 0x88, 0x0c, 0x10,
-  0x02, 0x78, 0x0b, 0x20, 0x03, 0x71, 0x28, 0x90, 0x04, 0x61, 0x27, 0xa0,
-  0x05, 0x51, 0x0a, 0x90, 0x06, 0x41, 0x09, 0xa0, 0x07, 0x30, 0xec, 0x90,
-  0x07, 0x8d, 0x43, 0xa0, 0x09, 0x10, 0xce, 0x90, 0x09, 0xad, 0xbf, 0x20,
-  0x0a, 0xf0, 0xb0, 0x90, 0x0b, 0xe0, 0xaf, 0xa0, 0x0c, 0xd9, 0xcd, 0x10,
-  0x0d, 0xc0, 0x91, 0xa0, 0x0e, 0xb9, 0xaf, 0x10, 0x0f, 0xa9, 0xae, 0x20,
-  0x10, 0x99, 0x91, 0x10, 0x11, 0x89, 0x90, 0x20, 0x12, 0x79, 0x73, 0x10,
-  0x13, 0x69, 0x72, 0x20, 0x14, 0x59, 0x55, 0x10, 0x15, 0x49, 0x54, 0x20,
-  0x16, 0x39, 0x37, 0x10, 0x17, 0x29, 0x36, 0x20, 0x18, 0x22, 0x53, 0x90,
-  0x19, 0x09, 0x18, 0x20, 0x1a, 0x02, 0x35, 0x90, 0x1a, 0xf2, 0x34, 0xa0,
-  0x1b, 0xe2, 0x17, 0x90, 0x1c, 0xd2, 0x16, 0xa0, 0x1d, 0xc1, 0xf9, 0x90,
-  0x1e, 0xb1, 0xf8, 0xa0, 0x1f, 0xa1, 0xdb, 0x90, 0x20, 0x76, 0x2b, 0x20,
-  0x21, 0x81, 0xbd, 0x90, 0x22, 0x56, 0x0d, 0x20, 0x23, 0x6a, 0xda, 0x10,
-  0x24, 0x35, 0xef, 0x20, 0x25, 0x4a, 0xbc, 0x10, 0x26, 0x15, 0xd1, 0x20,
-  0x27, 0x2a, 0x9e, 0x10, 0x27, 0xfe, 0xed, 0xa0, 0x29, 0x0a, 0x80, 0x10,
-  0x29, 0xde, 0xcf, 0xa0, 0x2a, 0xea, 0x62, 0x10, 0x2b, 0xbe, 0xb1, 0xa0,
-  0x2c, 0xd3, 0x7e, 0x90, 0x2d, 0x9e, 0x93, 0xa0, 0x2e, 0xb3, 0x60, 0x90,
-  0x2f, 0x7e, 0x75, 0xa0, 0x30, 0x93, 0x42, 0x90, 0x31, 0x67, 0x92, 0x20,
-  0x32, 0x73, 0x24, 0x90, 0x33, 0x47, 0x74, 0x20, 0x34, 0x53, 0x06, 0x90,
-  0x35, 0x27, 0x56, 0x20, 0x36, 0x32, 0xe8, 0x90, 0x37, 0x07, 0x38, 0x20,
-  0x38, 0x1c, 0x05, 0x10, 0x38, 0xe7, 0x1a, 0x20, 0x39, 0xfb, 0xe7, 0x10,
-  0x3a, 0xc6, 0xfc, 0x20, 0x3b, 0xdb, 0xc9, 0x10, 0x3c, 0xb0, 0x18, 0xa0,
-  0x3d, 0xbb, 0xab, 0x10, 0x3e, 0x8f, 0xfa, 0xa0, 0x3f, 0x9b, 0x8d, 0x10,
-  0x40, 0x6f, 0xdc, 0xa0, 0x41, 0x84, 0xa9, 0x90, 0x42, 0x4f, 0xbe, 0xa0,
-  0x43, 0x64, 0x8b, 0x90, 0x44, 0x2f, 0xa0, 0xa0, 0x45, 0x44, 0x6d, 0x90,
-  0x45, 0xf3, 0xd3, 0x20, 0x47, 0x2d, 0x8a, 0x10, 0x47, 0xd3, 0xb5, 0x20,
-  0x49, 0x0d, 0x6c, 0x10, 0x49, 0xb3, 0x97, 0x20, 0x4a, 0xed, 0x4e, 0x10,
-  0x4b, 0x9c, 0xb3, 0xa0, 0x4c, 0xd6, 0x6a, 0x90, 0x4d, 0x7c, 0x95, 0xa0,
-  0x4e, 0xb6, 0x4c, 0x90, 0x4f, 0x5c, 0x77, 0xa0, 0x50, 0x96, 0x2e, 0x90,
-  0x51, 0x3c, 0x59, 0xa0, 0x52, 0x76, 0x10, 0x90, 0x53, 0x1c, 0x3b, 0xa0,
-  0x54, 0x55, 0xf2, 0x90, 0x54, 0xfc, 0x1d, 0xa0, 0x56, 0x35, 0xd4, 0x90,
-  0x56, 0xe5, 0x3a, 0x20, 0x58, 0x1e, 0xf1, 0x10, 0x58, 0xc5, 0x1c, 0x20,
-  0x59, 0xfe, 0xd3, 0x10, 0x5a, 0xa4, 0xfe, 0x20, 0x5b, 0xde, 0xb5, 0x10,
-  0x5c, 0x84, 0xe0, 0x20, 0x5d, 0xbe, 0x97, 0x10, 0x5e, 0x64, 0xc2, 0x20,
-  0x5f, 0x9e, 0x79, 0x10, 0x60, 0x4d, 0xde, 0xa0, 0x61, 0x87, 0x95, 0x90,
-  0x62, 0x2d, 0xc0, 0xa0, 0x63, 0x67, 0x77, 0x90, 0x64, 0x0d, 0xa2, 0xa0,
-  0x65, 0x47, 0x59, 0x90, 0x65, 0xed, 0x84, 0xa0, 0x67, 0x27, 0x3b, 0x90,
-  0x67, 0xcd, 0x66, 0xa0, 0x69, 0x07, 0x1d, 0x90, 0x69, 0xad, 0x48, 0xa0,
-  0x6a, 0xe6, 0xff, 0x90, 0x6b, 0x96, 0x65, 0x20, 0x6c, 0xd0, 0x1c, 0x10,
-  0x6d, 0x76, 0x47, 0x20, 0x6e, 0xaf, 0xfe, 0x10, 0x6f, 0x56, 0x29, 0x20,
-  0x70, 0x8f, 0xe0, 0x10, 0x71, 0x36, 0x0b, 0x20, 0x72, 0x6f, 0xc2, 0x10,
-  0x73, 0x15, 0xed, 0x20, 0x74, 0x4f, 0xa4, 0x10, 0x74, 0xff, 0x09, 0xa0,
-  0x76, 0x38, 0xc0, 0x90, 0x76, 0xde, 0xeb, 0xa0, 0x78, 0x18, 0xa2, 0x90,
-  0x78, 0xbe, 0xcd, 0xa0, 0x79, 0xf8, 0x84, 0x90, 0x7a, 0x9e, 0xaf, 0xa0,
-  0x7b, 0xd8, 0x66, 0x90, 0x7c, 0x7e, 0x91, 0xa0, 0x7d, 0xb8, 0x48, 0x90,
-  0x7e, 0x5e, 0x73, 0xa0, 0x7f, 0x98, 0x2a, 0x90, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0xff, 0xff, 0x91, 0x26, 0x00, 0x00, 0xff, 0xff, 0x9d, 0x90,
-  0x01, 0x04, 0xff, 0xff, 0x8f, 0x80, 0x00, 0x08, 0xff, 0xff, 0x9d, 0x90,
-  0x01, 0x0c, 0xff, 0xff, 0x9d, 0x90, 0x01, 0x10, 0x4c, 0x4d, 0x54, 0x00,
-  0x50, 0x44, 0x54, 0x00, 0x50, 0x53, 0x54, 0x00, 0x50, 0x57, 0x54, 0x00,
-  0x50, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-  0x00, 0x01, 0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0xba, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0xff, 0xff,
-  0xff, 0xff, 0x5e, 0x04, 0x1a, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x9e, 0xa6,
-  0x48, 0xa0, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xbb, 0x15, 0x90, 0xff, 0xff,
-  0xff, 0xff, 0xa0, 0x86, 0x2a, 0xa0, 0xff, 0xff, 0xff, 0xff, 0xa1, 0x9a,
-  0xf7, 0x90, 0xff, 0xff, 0xff, 0xff, 0xcb, 0x89, 0x1a, 0xa0, 0xff, 0xff,
-  0xff, 0xff, 0xd2, 0x23, 0xf4, 0x70, 0xff, 0xff, 0xff, 0xff, 0xd2, 0x61,
-  0x26, 0x10, 0xff, 0xff, 0xff, 0xff, 0xd6, 0xfe, 0x74, 0x5c, 0xff, 0xff,
-  0xff, 0xff, 0xd8, 0x80, 0xad, 0x90, 0xff, 0xff, 0xff, 0xff, 0xda, 0xfe,
-  0xc3, 0x90, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xc0, 0x90, 0x10, 0xff, 0xff,
-  0xff, 0xff, 0xdc, 0xde, 0xa5, 0x90, 0xff, 0xff, 0xff, 0xff, 0xdd, 0xa9,
-  0xac, 0x90, 0xff, 0xff, 0xff, 0xff, 0xde, 0xbe, 0x87, 0x90, 0xff, 0xff,
-  0xff, 0xff, 0xdf, 0x89, 0x8e, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x9e,
-  0x69, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe1, 0x69, 0x70, 0x90, 0xff, 0xff,
-  0xff, 0xff, 0xe2, 0x7e, 0x4b, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x49,
-  0x52, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe4, 0x5e, 0x2d, 0x90, 0xff, 0xff,
-  0xff, 0xff, 0xe5, 0x29, 0x34, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x47,
-  0x4a, 0x10, 0xff, 0xff, 0xff, 0xff, 0xe7, 0x12, 0x51, 0x10, 0xff, 0xff,
-  0xff, 0xff, 0xe8, 0x27, 0x2c, 0x10, 0xff, 0xff, 0xff, 0xff, 0xe8, 0xf2,
-  0x33, 0x10, 0xff, 0xff, 0xff, 0xff, 0xea, 0x07, 0x0e, 0x10, 0xff, 0xff,
-  0xff, 0xff, 0xea, 0xd2, 0x15, 0x10, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xe6,
-  0xf0, 0x10, 0xff, 0xff, 0xff, 0xff, 0xec, 0xb1, 0xf7, 0x10, 0xff, 0xff,
-  0xff, 0xff, 0xed, 0xc6, 0xd2, 0x10, 0xff, 0xff, 0xff, 0xff, 0xee, 0x91,
-  0xd9, 0x10, 0xff, 0xff, 0xff, 0xff, 0xef, 0xaf, 0xee, 0x90, 0xff, 0xff,
-  0xff, 0xff, 0xf0, 0x71, 0xbb, 0x10, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x8f,
-  0xd0, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf2, 0x7f, 0xc1, 0x90, 0xff, 0xff,
-  0xff, 0xff, 0xf3, 0x6f, 0xb2, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x5f,
-  0xa3, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x4f, 0x94, 0x90, 0xff, 0xff,
-  0xff, 0xff, 0xf6, 0x3f, 0x85, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x2f,
-  0x76, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x28, 0xa2, 0x10, 0xff, 0xff,
-  0xff, 0xff, 0xf9, 0x0f, 0x58, 0x90, 0xff, 0xff, 0xff, 0xff, 0xfa, 0x08,
-  0x84, 0x10, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xf8, 0x83, 0x20, 0xff, 0xff,
-  0xff, 0xff, 0xfb, 0xe8, 0x66, 0x10, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xd8,
-  0x65, 0x20, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xc8, 0x48, 0x10, 0xff, 0xff,
-  0xff, 0xff, 0xfe, 0xb8, 0x47, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa8,
-  0x2a, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x29, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x01, 0x88, 0x0c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x78,
-  0x0b, 0x20, 0x00, 0x00, 0x00, 0x00, 0x03, 0x71, 0x28, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x04, 0x61, 0x27, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x05, 0x51,
-  0x0a, 0x90, 0x00, 0x00, 0x00, 0x00, 0x06, 0x41, 0x09, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x07, 0x30, 0xec, 0x90, 0x00, 0x00, 0x00, 0x00, 0x07, 0x8d,
-  0x43, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, 0xce, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x09, 0xad, 0xbf, 0x20, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0,
-  0xb0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xe0, 0xaf, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x0c, 0xd9, 0xcd, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xc0,
-  0x91, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xb9, 0xaf, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x0f, 0xa9, 0xae, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99,
-  0x91, 0x10, 0x00, 0x00, 0x00, 0x00, 0x11, 0x89, 0x90, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x12, 0x79, 0x73, 0x10, 0x00, 0x00, 0x00, 0x00, 0x13, 0x69,
-  0x72, 0x20, 0x00, 0x00, 0x00, 0x00, 0x14, 0x59, 0x55, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x15, 0x49, 0x54, 0x20, 0x00, 0x00, 0x00, 0x00, 0x16, 0x39,
-  0x37, 0x10, 0x00, 0x00, 0x00, 0x00, 0x17, 0x29, 0x36, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x18, 0x22, 0x53, 0x90, 0x00, 0x00, 0x00, 0x00, 0x19, 0x09,
-  0x18, 0x20, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x02, 0x35, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x1a, 0xf2, 0x34, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xe2,
-  0x17, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xd2, 0x16, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x1d, 0xc1, 0xf9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xb1,
-  0xf8, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xa1, 0xdb, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x20, 0x76, 0x2b, 0x20, 0x00, 0x00, 0x00, 0x00, 0x21, 0x81,
-  0xbd, 0x90, 0x00, 0x00, 0x00, 0x00, 0x22, 0x56, 0x0d, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x23, 0x6a, 0xda, 0x10, 0x00, 0x00, 0x00, 0x00, 0x24, 0x35,
-  0xef, 0x20, 0x00, 0x00, 0x00, 0x00, 0x25, 0x4a, 0xbc, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x26, 0x15, 0xd1, 0x20, 0x00, 0x00, 0x00, 0x00, 0x27, 0x2a,
-  0x9e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x27, 0xfe, 0xed, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x29, 0x0a, 0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x29, 0xde,
-  0xcf, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xea, 0x62, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x2b, 0xbe, 0xb1, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x2c, 0xd3,
-  0x7e, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x9e, 0x93, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x2e, 0xb3, 0x60, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x7e,
-  0x75, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x93, 0x42, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x31, 0x67, 0x92, 0x20, 0x00, 0x00, 0x00, 0x00, 0x32, 0x73,
-  0x24, 0x90, 0x00, 0x00, 0x00, 0x00, 0x33, 0x47, 0x74, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x34, 0x53, 0x06, 0x90, 0x00, 0x00, 0x00, 0x00, 0x35, 0x27,
-  0x56, 0x20, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0xe8, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x37, 0x07, 0x38, 0x20, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1c,
-  0x05, 0x10, 0x00, 0x00, 0x00, 0x00, 0x38, 0xe7, 0x1a, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x39, 0xfb, 0xe7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xc6,
-  0xfc, 0x20, 0x00, 0x00, 0x00, 0x00, 0x3b, 0xdb, 0xc9, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x3c, 0xb0, 0x18, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xbb,
-  0xab, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x8f, 0xfa, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x3f, 0x9b, 0x8d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6f,
-  0xdc, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0xa9, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x42, 0x4f, 0xbe, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64,
-  0x8b, 0x90, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2f, 0xa0, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x45, 0x44, 0x6d, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0xf3,
-  0xd3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2d, 0x8a, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x47, 0xd3, 0xb5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0d,
-  0x6c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0xb3, 0x97, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x4a, 0xed, 0x4e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x9c,
-  0xb3, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xd6, 0x6a, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x4d, 0x7c, 0x95, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x4e, 0xb6,
-  0x4c, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x5c, 0x77, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x50, 0x96, 0x2e, 0x90, 0x00, 0x00, 0x00, 0x00, 0x51, 0x3c,
-  0x59, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x52, 0x76, 0x10, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x53, 0x1c, 0x3b, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55,
-  0xf2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfc, 0x1d, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x56, 0x35, 0xd4, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0xe5,
-  0x3a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1e, 0xf1, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x58, 0xc5, 0x1c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x59, 0xfe,
-  0xd3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5a, 0xa4, 0xfe, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x5b, 0xde, 0xb5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x84,
-  0xe0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xbe, 0x97, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x5e, 0x64, 0xc2, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x9e,
-  0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x60, 0x4d, 0xde, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x61, 0x87, 0x95, 0x90, 0x00, 0x00, 0x00, 0x00, 0x62, 0x2d,
-  0xc0, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x77, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x64, 0x0d, 0xa2, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47,
-  0x59, 0x90, 0x00, 0x00, 0x00, 0x00, 0x65, 0xed, 0x84, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x67, 0x27, 0x3b, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0xcd,
-  0x66, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x69, 0x07, 0x1d, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x69, 0xad, 0x48, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x6a, 0xe6,
-  0xff, 0x90, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x96, 0x65, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x6c, 0xd0, 0x1c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x76,
-  0x47, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6e, 0xaf, 0xfe, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x6f, 0x56, 0x29, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x8f,
-  0xe0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x71, 0x36, 0x0b, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x72, 0x6f, 0xc2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x73, 0x15,
-  0xed, 0x20, 0x00, 0x00, 0x00, 0x00, 0x74, 0x4f, 0xa4, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x74, 0xff, 0x09, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x76, 0x38,
-  0xc0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x76, 0xde, 0xeb, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x78, 0x18, 0xa2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x78, 0xbe,
-  0xcd, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x79, 0xf8, 0x84, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x7a, 0x9e, 0xaf, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xd8,
-  0x66, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7e, 0x91, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x7d, 0xb8, 0x48, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x5e,
-  0x73, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x98, 0x2a, 0x90, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0xff, 0xff, 0x91, 0x26, 0x00, 0x00, 0xff, 0xff,
-  0x9d, 0x90, 0x01, 0x04, 0xff, 0xff, 0x8f, 0x80, 0x00, 0x08, 0xff, 0xff,
-  0x9d, 0x90, 0x01, 0x0c, 0xff, 0xff, 0x9d, 0x90, 0x01, 0x10, 0x4c, 0x4d,
-  0x54, 0x00, 0x50, 0x44, 0x54, 0x00, 0x50, 0x53, 0x54, 0x00, 0x50, 0x57,
-  0x54, 0x00, 0x50, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
-  0x00, 0x00, 0x00, 0x01, 0x0a, 0x50, 0x53, 0x54, 0x38, 0x50, 0x44, 0x54,
-  0x2c, 0x4d, 0x33, 0x2e, 0x32, 0x2e, 0x30, 0x2c, 0x4d, 0x31, 0x31, 0x2e,
-  0x31, 0x2e, 0x30, 0x0a
-};
-unsigned int America_Los_Angeles_len = 2836;
-unsigned char America_New_York[] = {
-  0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
-  0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec,
-  0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00,
-  0x9e, 0xa6, 0x1e, 0x70, 0x9f, 0xba, 0xeb, 0x60, 0xa0, 0x86, 0x00, 0x70,
-  0xa1, 0x9a, 0xcd, 0x60, 0xa2, 0x65, 0xe2, 0x70, 0xa3, 0x83, 0xe9, 0xe0,
-  0xa4, 0x6a, 0xae, 0x70, 0xa5, 0x35, 0xa7, 0x60, 0xa6, 0x53, 0xca, 0xf0,
-  0xa7, 0x15, 0x89, 0x60, 0xa8, 0x33, 0xac, 0xf0, 0xa8, 0xfe, 0xa5, 0xe0,
-  0xaa, 0x13, 0x8e, 0xf0, 0xaa, 0xde, 0x87, 0xe0, 0xab, 0xf3, 0x70, 0xf0,
-  0xac, 0xbe, 0x69, 0xe0, 0xad, 0xd3, 0x52, 0xf0, 0xae, 0x9e, 0x4b, 0xe0,
-  0xaf, 0xb3, 0x34, 0xf0, 0xb0, 0x7e, 0x2d, 0xe0, 0xb1, 0x9c, 0x51, 0x70,
-  0xb2, 0x67, 0x4a, 0x60, 0xb3, 0x7c, 0x33, 0x70, 0xb4, 0x47, 0x2c, 0x60,
-  0xb5, 0x5c, 0x15, 0x70, 0xb6, 0x27, 0x0e, 0x60, 0xb7, 0x3b, 0xf7, 0x70,
-  0xb8, 0x06, 0xf0, 0x60, 0xb9, 0x1b, 0xd9, 0x70, 0xb9, 0xe6, 0xd2, 0x60,
-  0xbb, 0x04, 0xf5, 0xf0, 0xbb, 0xc6, 0xb4, 0x60, 0xbc, 0xe4, 0xd7, 0xf0,
-  0xbd, 0xaf, 0xd0, 0xe0, 0xbe, 0xc4, 0xb9, 0xf0, 0xbf, 0x8f, 0xb2, 0xe0,
-  0xc0, 0xa4, 0x9b, 0xf0, 0xc1, 0x6f, 0x94, 0xe0, 0xc2, 0x84, 0x7d, 0xf0,
-  0xc3, 0x4f, 0x76, 0xe0, 0xc4, 0x64, 0x5f, 0xf0, 0xc5, 0x2f, 0x58, 0xe0,
-  0xc6, 0x4d, 0x7c, 0x70, 0xc7, 0x0f, 0x3a, 0xe0, 0xc8, 0x2d, 0x5e, 0x70,
-  0xc8, 0xf8, 0x57, 0x60, 0xca, 0x0d, 0x40, 0x70, 0xca, 0xd8, 0x39, 0x60,
-  0xcb, 0x88, 0xf0, 0x70, 0xd2, 0x23, 0xf4, 0x70, 0xd2, 0x60, 0xfb, 0xe0,
-  0xd3, 0x75, 0xe4, 0xf0, 0xd4, 0x40, 0xdd, 0xe0, 0xd5, 0x55, 0xc6, 0xf0,
-  0xd6, 0x20, 0xbf, 0xe0, 0xd7, 0x35, 0xa8, 0xf0, 0xd8, 0x00, 0xa1, 0xe0,
-  0xd9, 0x15, 0x8a, 0xf0, 0xd9, 0xe0, 0x83, 0xe0, 0xda, 0xfe, 0xa7, 0x70,
-  0xdb, 0xc0, 0x65, 0xe0, 0xdc, 0xde, 0x89, 0x70, 0xdd, 0xa9, 0x82, 0x60,
-  0xde, 0xbe, 0x6b, 0x70, 0xdf, 0x89, 0x64, 0x60, 0xe0, 0x9e, 0x4d, 0x70,
-  0xe1, 0x69, 0x46, 0x60, 0xe2, 0x7e, 0x2f, 0x70, 0xe3, 0x49, 0x28, 0x60,
-  0xe4, 0x5e, 0x11, 0x70, 0xe5, 0x57, 0x2e, 0xe0, 0xe6, 0x47, 0x2d, 0xf0,
-  0xe7, 0x37, 0x10, 0xe0, 0xe8, 0x27, 0x0f, 0xf0, 0xe9, 0x16, 0xf2, 0xe0,
-  0xea, 0x06, 0xf1, 0xf0, 0xea, 0xf6, 0xd4, 0xe0, 0xeb, 0xe6, 0xd3, 0xf0,
-  0xec, 0xd6, 0xb6, 0xe0, 0xed, 0xc6, 0xb5, 0xf0, 0xee, 0xbf, 0xd3, 0x60,
-  0xef, 0xaf, 0xd2, 0x70, 0xf0, 0x9f, 0xb5, 0x60, 0xf1, 0x8f, 0xb4, 0x70,
-  0xf2, 0x7f, 0x97, 0x60, 0xf3, 0x6f, 0x96, 0x70, 0xf4, 0x5f, 0x79, 0x60,
-  0xf5, 0x4f, 0x78, 0x70, 0xf6, 0x3f, 0x5b, 0x60, 0xf7, 0x2f, 0x5a, 0x70,
-  0xf8, 0x28, 0x77, 0xe0, 0xf9, 0x0f, 0x3c, 0x70, 0xfa, 0x08, 0x59, 0xe0,
-  0xfa, 0xf8, 0x58, 0xf0, 0xfb, 0xe8, 0x3b, 0xe0, 0xfc, 0xd8, 0x3a, 0xf0,
-  0xfd, 0xc8, 0x1d, 0xe0, 0xfe, 0xb8, 0x1c, 0xf0, 0xff, 0xa7, 0xff, 0xe0,
-  0x00, 0x97, 0xfe, 0xf0, 0x01, 0x87, 0xe1, 0xe0, 0x02, 0x77, 0xe0, 0xf0,
-  0x03, 0x70, 0xfe, 0x60, 0x04, 0x60, 0xfd, 0x70, 0x05, 0x50, 0xe0, 0x60,
-  0x06, 0x40, 0xdf, 0x70, 0x07, 0x30, 0xc2, 0x60, 0x07, 0x8d, 0x19, 0x70,
-  0x09, 0x10, 0xa4, 0x60, 0x09, 0xad, 0x94, 0xf0, 0x0a, 0xf0, 0x86, 0x60,
-  0x0b, 0xe0, 0x85, 0x70, 0x0c, 0xd9, 0xa2, 0xe0, 0x0d, 0xc0, 0x67, 0x70,
-  0x0e, 0xb9, 0x84, 0xe0, 0x0f, 0xa9, 0x83, 0xf0, 0x10, 0x99, 0x66, 0xe0,
-  0x11, 0x89, 0x65, 0xf0, 0x12, 0x79, 0x48, 0xe0, 0x13, 0x69, 0x47, 0xf0,
-  0x14, 0x59, 0x2a, 0xe0, 0x15, 0x49, 0x29, 0xf0, 0x16, 0x39, 0x0c, 0xe0,
-  0x17, 0x29, 0x0b, 0xf0, 0x18, 0x22, 0x29, 0x60, 0x19, 0x08, 0xed, 0xf0,
-  0x1a, 0x02, 0x0b, 0x60, 0x1a, 0xf2, 0x0a, 0x70, 0x1b, 0xe1, 0xed, 0x60,
-  0x1c, 0xd1, 0xec, 0x70, 0x1d, 0xc1, 0xcf, 0x60, 0x1e, 0xb1, 0xce, 0x70,
-  0x1f, 0xa1, 0xb1, 0x60, 0x20, 0x76, 0x00, 0xf0, 0x21, 0x81, 0x93, 0x60,
-  0x22, 0x55, 0xe2, 0xf0, 0x23, 0x6a, 0xaf, 0xe0, 0x24, 0x35, 0xc4, 0xf0,
-  0x25, 0x4a, 0x91, 0xe0, 0x26, 0x15, 0xa6, 0xf0, 0x27, 0x2a, 0x73, 0xe0,
-  0x27, 0xfe, 0xc3, 0x70, 0x29, 0x0a, 0x55, 0xe0, 0x29, 0xde, 0xa5, 0x70,
-  0x2a, 0xea, 0x37, 0xe0, 0x2b, 0xbe, 0x87, 0x70, 0x2c, 0xd3, 0x54, 0x60,
-  0x2d, 0x9e, 0x69, 0x70, 0x2e, 0xb3, 0x36, 0x60, 0x2f, 0x7e, 0x4b, 0x70,
-  0x30, 0x93, 0x18, 0x60, 0x31, 0x67, 0x67, 0xf0, 0x32, 0x72, 0xfa, 0x60,
-  0x33, 0x47, 0x49, 0xf0, 0x34, 0x52, 0xdc, 0x60, 0x35, 0x27, 0x2b, 0xf0,
-  0x36, 0x32, 0xbe, 0x60, 0x37, 0x07, 0x0d, 0xf0, 0x38, 0x1b, 0xda, 0xe0,
-  0x38, 0xe6, 0xef, 0xf0, 0x39, 0xfb, 0xbc, 0xe0, 0x3a, 0xc6, 0xd1, 0xf0,
-  0x3b, 0xdb, 0x9e, 0xe0, 0x3c, 0xaf, 0xee, 0x70, 0x3d, 0xbb, 0x80, 0xe0,
-  0x3e, 0x8f, 0xd0, 0x70, 0x3f, 0x9b, 0x62, 0xe0, 0x40, 0x6f, 0xb2, 0x70,
-  0x41, 0x84, 0x7f, 0x60, 0x42, 0x4f, 0x94, 0x70, 0x43, 0x64, 0x61, 0x60,
-  0x44, 0x2f, 0x76, 0x70, 0x45, 0x44, 0x43, 0x60, 0x45, 0xf3, 0xa8, 0xf0,
-  0x47, 0x2d, 0x5f, 0xe0, 0x47, 0xd3, 0x8a, 0xf0, 0x49, 0x0d, 0x41, 0xe0,
-  0x49, 0xb3, 0x6c, 0xf0, 0x4a, 0xed, 0x23, 0xe0, 0x4b, 0x9c, 0x89, 0x70,
-  0x4c, 0xd6, 0x40, 0x60, 0x4d, 0x7c, 0x6b, 0x70, 0x4e, 0xb6, 0x22, 0x60,
-  0x4f, 0x5c, 0x4d, 0x70, 0x50, 0x96, 0x04, 0x60, 0x51, 0x3c, 0x2f, 0x70,
-  0x52, 0x75, 0xe6, 0x60, 0x53, 0x1c, 0x11, 0x70, 0x54, 0x55, 0xc8, 0x60,
-  0x54, 0xfb, 0xf3, 0x70, 0x56, 0x35, 0xaa, 0x60, 0x56, 0xe5, 0x0f, 0xf0,
-  0x58, 0x1e, 0xc6, 0xe0, 0x58, 0xc4, 0xf1, 0xf0, 0x59, 0xfe, 0xa8, 0xe0,
-  0x5a, 0xa4, 0xd3, 0xf0, 0x5b, 0xde, 0x8a, 0xe0, 0x5c, 0x84, 0xb5, 0xf0,
-  0x5d, 0xbe, 0x6c, 0xe0, 0x5e, 0x64, 0x97, 0xf0, 0x5f, 0x9e, 0x4e, 0xe0,
-  0x60, 0x4d, 0xb4, 0x70, 0x61, 0x87, 0x6b, 0x60, 0x62, 0x2d, 0x96, 0x70,
-  0x63, 0x67, 0x4d, 0x60, 0x64, 0x0d, 0x78, 0x70, 0x65, 0x47, 0x2f, 0x60,
-  0x65, 0xed, 0x5a, 0x70, 0x67, 0x27, 0x11, 0x60, 0x67, 0xcd, 0x3c, 0x70,
-  0x69, 0x06, 0xf3, 0x60, 0x69, 0xad, 0x1e, 0x70, 0x6a, 0xe6, 0xd5, 0x60,
-  0x6b, 0x96, 0x3a, 0xf0, 0x6c, 0xcf, 0xf1, 0xe0, 0x6d, 0x76, 0x1c, 0xf0,
-  0x6e, 0xaf, 0xd3, 0xe0, 0x6f, 0x55, 0xfe, 0xf0, 0x70, 0x8f, 0xb5, 0xe0,
-  0x71, 0x35, 0xe0, 0xf0, 0x72, 0x6f, 0x97, 0xe0, 0x73, 0x15, 0xc2, 0xf0,
-  0x74, 0x4f, 0x79, 0xe0, 0x74, 0xfe, 0xdf, 0x70, 0x76, 0x38, 0x96, 0x60,
-  0x76, 0xde, 0xc1, 0x70, 0x78, 0x18, 0x78, 0x60, 0x78, 0xbe, 0xa3, 0x70,
-  0x79, 0xf8, 0x5a, 0x60, 0x7a, 0x9e, 0x85, 0x70, 0x7b, 0xd8, 0x3c, 0x60,
-  0x7c, 0x7e, 0x67, 0x70, 0x7d, 0xb8, 0x1e, 0x60, 0x7e, 0x5e, 0x49, 0x70,
-  0x7f, 0x98, 0x00, 0x60, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0xff, 0xff, 0xba, 0x9e, 0x00, 0x00, 0xff, 0xff, 0xc7, 0xc0, 0x01, 0x04,
-  0xff, 0xff, 0xb9, 0xb0, 0x00, 0x08, 0xff, 0xff, 0xc7, 0xc0, 0x01, 0x0c,
-  0xff, 0xff, 0xc7, 0xc0, 0x01, 0x10, 0x4c, 0x4d, 0x54, 0x00, 0x45, 0x44,
-  0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x45, 0x57, 0x54, 0x00, 0x45, 0x50,
-  0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
-  0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
-  0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec,
-  0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0xff, 0xff, 0xff, 0xff,
-  0x5e, 0x03, 0xf0, 0x90, 0xff, 0xff, 0xff, 0xff, 0x9e, 0xa6, 0x1e, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0x9f, 0xba, 0xeb, 0x60, 0xff, 0xff, 0xff, 0xff,
-  0xa0, 0x86, 0x00, 0x70, 0xff, 0xff, 0xff, 0xff, 0xa1, 0x9a, 0xcd, 0x60,
-  0xff, 0xff, 0xff, 0xff, 0xa2, 0x65, 0xe2, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xa3, 0x83, 0xe9, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xa4, 0x6a, 0xae, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xa5, 0x35, 0xa7, 0x60, 0xff, 0xff, 0xff, 0xff,
-  0xa6, 0x53, 0xca, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xa7, 0x15, 0x89, 0x60,
-  0xff, 0xff, 0xff, 0xff, 0xa8, 0x33, 0xac, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xa8, 0xfe, 0xa5, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xaa, 0x13, 0x8e, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xaa, 0xde, 0x87, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xab, 0xf3, 0x70, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xac, 0xbe, 0x69, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xad, 0xd3, 0x52, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xae, 0x9e, 0x4b, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xaf, 0xb3, 0x34, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xb0, 0x7e, 0x2d, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xb1, 0x9c, 0x51, 0x70, 0xff, 0xff, 0xff, 0xff, 0xb2, 0x67, 0x4a, 0x60,
-  0xff, 0xff, 0xff, 0xff, 0xb3, 0x7c, 0x33, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xb4, 0x47, 0x2c, 0x60, 0xff, 0xff, 0xff, 0xff, 0xb5, 0x5c, 0x15, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xb6, 0x27, 0x0e, 0x60, 0xff, 0xff, 0xff, 0xff,
-  0xb7, 0x3b, 0xf7, 0x70, 0xff, 0xff, 0xff, 0xff, 0xb8, 0x06, 0xf0, 0x60,
-  0xff, 0xff, 0xff, 0xff, 0xb9, 0x1b, 0xd9, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xb9, 0xe6, 0xd2, 0x60, 0xff, 0xff, 0xff, 0xff, 0xbb, 0x04, 0xf5, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xbb, 0xc6, 0xb4, 0x60, 0xff, 0xff, 0xff, 0xff,
-  0xbc, 0xe4, 0xd7, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xbd, 0xaf, 0xd0, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xbe, 0xc4, 0xb9, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xbf, 0x8f, 0xb2, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xa4, 0x9b, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xc1, 0x6f, 0x94, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xc2, 0x84, 0x7d, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x4f, 0x76, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xc4, 0x64, 0x5f, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xc5, 0x2f, 0x58, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xc6, 0x4d, 0x7c, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xc7, 0x0f, 0x3a, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xc8, 0x2d, 0x5e, 0x70, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xf8, 0x57, 0x60,
-  0xff, 0xff, 0xff, 0xff, 0xca, 0x0d, 0x40, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xca, 0xd8, 0x39, 0x60, 0xff, 0xff, 0xff, 0xff, 0xcb, 0x88, 0xf0, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xd2, 0x23, 0xf4, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xd2, 0x60, 0xfb, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xd3, 0x75, 0xe4, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xd4, 0x40, 0xdd, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xd5, 0x55, 0xc6, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xd6, 0x20, 0xbf, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xd7, 0x35, 0xa8, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xd8, 0x00, 0xa1, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xd9, 0x15, 0x8a, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xd9, 0xe0, 0x83, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xda, 0xfe, 0xa7, 0x70, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xc0, 0x65, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xdc, 0xde, 0x89, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xdd, 0xa9, 0x82, 0x60, 0xff, 0xff, 0xff, 0xff, 0xde, 0xbe, 0x6b, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xdf, 0x89, 0x64, 0x60, 0xff, 0xff, 0xff, 0xff,
-  0xe0, 0x9e, 0x4d, 0x70, 0xff, 0xff, 0xff, 0xff, 0xe1, 0x69, 0x46, 0x60,
-  0xff, 0xff, 0xff, 0xff, 0xe2, 0x7e, 0x2f, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xe3, 0x49, 0x28, 0x60, 0xff, 0xff, 0xff, 0xff, 0xe4, 0x5e, 0x11, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xe5, 0x57, 0x2e, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xe6, 0x47, 0x2d, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xe7, 0x37, 0x10, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xe8, 0x27, 0x0f, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xe9, 0x16, 0xf2, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xea, 0x06, 0xf1, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xea, 0xf6, 0xd4, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xeb, 0xe6, 0xd3, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xec, 0xd6, 0xb6, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xed, 0xc6, 0xb5, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xee, 0xbf, 0xd3, 0x60, 0xff, 0xff, 0xff, 0xff, 0xef, 0xaf, 0xd2, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xf0, 0x9f, 0xb5, 0x60, 0xff, 0xff, 0xff, 0xff,
-  0xf1, 0x8f, 0xb4, 0x70, 0xff, 0xff, 0xff, 0xff, 0xf2, 0x7f, 0x97, 0x60,
-  0xff, 0xff, 0xff, 0xff, 0xf3, 0x6f, 0x96, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xf4, 0x5f, 0x79, 0x60, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x4f, 0x78, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xf6, 0x3f, 0x5b, 0x60, 0xff, 0xff, 0xff, 0xff,
-  0xf7, 0x2f, 0x5a, 0x70, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x28, 0x77, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xf9, 0x0f, 0x3c, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xfa, 0x08, 0x59, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xf8, 0x58, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xfb, 0xe8, 0x3b, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xfc, 0xd8, 0x3a, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xc8, 0x1d, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xfe, 0xb8, 0x1c, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xa7, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0xfe, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x01, 0x87, 0xe1, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x02, 0x77, 0xe0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x70, 0xfe, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x04, 0x60, 0xfd, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x05, 0x50, 0xe0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x06, 0x40, 0xdf, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x07, 0x30, 0xc2, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x07, 0x8d, 0x19, 0x70, 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, 0xa4, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x09, 0xad, 0x94, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x0a, 0xf0, 0x86, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xe0, 0x85, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x0c, 0xd9, 0xa2, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x0d, 0xc0, 0x67, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xb9, 0x84, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x0f, 0xa9, 0x83, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x10, 0x99, 0x66, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x11, 0x89, 0x65, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x12, 0x79, 0x48, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x13, 0x69, 0x47, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x14, 0x59, 0x2a, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x15, 0x49, 0x29, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x16, 0x39, 0x0c, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x17, 0x29, 0x0b, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x18, 0x22, 0x29, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x19, 0x08, 0xed, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x02, 0x0b, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x1a, 0xf2, 0x0a, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x1b, 0xe1, 0xed, 0x60, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xd1, 0xec, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x1d, 0xc1, 0xcf, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x1e, 0xb1, 0xce, 0x70, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xa1, 0xb1, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x20, 0x76, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x21, 0x81, 0x93, 0x60, 0x00, 0x00, 0x00, 0x00, 0x22, 0x55, 0xe2, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x23, 0x6a, 0xaf, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x24, 0x35, 0xc4, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x25, 0x4a, 0x91, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x26, 0x15, 0xa6, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x27, 0x2a, 0x73, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x27, 0xfe, 0xc3, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x29, 0x0a, 0x55, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x29, 0xde, 0xa5, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xea, 0x37, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x2b, 0xbe, 0x87, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x2c, 0xd3, 0x54, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x9e, 0x69, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x2e, 0xb3, 0x36, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x2f, 0x7e, 0x4b, 0x70, 0x00, 0x00, 0x00, 0x00, 0x30, 0x93, 0x18, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x31, 0x67, 0x67, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x32, 0x72, 0xfa, 0x60, 0x00, 0x00, 0x00, 0x00, 0x33, 0x47, 0x49, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x34, 0x52, 0xdc, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x35, 0x27, 0x2b, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0xbe, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x37, 0x07, 0x0d, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x38, 0x1b, 0xda, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x38, 0xe6, 0xef, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x39, 0xfb, 0xbc, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x3a, 0xc6, 0xd1, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x3b, 0xdb, 0x9e, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x3c, 0xaf, 0xee, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x3d, 0xbb, 0x80, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x8f, 0xd0, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x3f, 0x9b, 0x62, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x40, 0x6f, 0xb2, 0x70, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0x7f, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x42, 0x4f, 0x94, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x43, 0x64, 0x61, 0x60, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2f, 0x76, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x45, 0x44, 0x43, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x45, 0xf3, 0xa8, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2d, 0x5f, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x47, 0xd3, 0x8a, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x49, 0x0d, 0x41, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x49, 0xb3, 0x6c, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x4a, 0xed, 0x23, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x4b, 0x9c, 0x89, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xd6, 0x40, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x4d, 0x7c, 0x6b, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x4e, 0xb6, 0x22, 0x60, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x5c, 0x4d, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x50, 0x96, 0x04, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x51, 0x3c, 0x2f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x52, 0x75, 0xe6, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x53, 0x1c, 0x11, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x54, 0x55, 0xc8, 0x60, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfb, 0xf3, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x56, 0x35, 0xaa, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x56, 0xe5, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1e, 0xc6, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x58, 0xc4, 0xf1, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x59, 0xfe, 0xa8, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x5a, 0xa4, 0xd3, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x5b, 0xde, 0x8a, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x5c, 0x84, 0xb5, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xbe, 0x6c, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x5e, 0x64, 0x97, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x5f, 0x9e, 0x4e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x60, 0x4d, 0xb4, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x61, 0x87, 0x6b, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x62, 0x2d, 0x96, 0x70, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x4d, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x64, 0x0d, 0x78, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x65, 0x47, 0x2f, 0x60, 0x00, 0x00, 0x00, 0x00, 0x65, 0xed, 0x5a, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x67, 0x27, 0x11, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x67, 0xcd, 0x3c, 0x70, 0x00, 0x00, 0x00, 0x00, 0x69, 0x06, 0xf3, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x69, 0xad, 0x1e, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x6a, 0xe6, 0xd5, 0x60, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x96, 0x3a, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x6c, 0xcf, 0xf1, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x6d, 0x76, 0x1c, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x6e, 0xaf, 0xd3, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x6f, 0x55, 0xfe, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x70, 0x8f, 0xb5, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x71, 0x35, 0xe0, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x72, 0x6f, 0x97, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x73, 0x15, 0xc2, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x74, 0x4f, 0x79, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x74, 0xfe, 0xdf, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x76, 0x38, 0x96, 0x60, 0x00, 0x00, 0x00, 0x00, 0x76, 0xde, 0xc1, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x78, 0x18, 0x78, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x78, 0xbe, 0xa3, 0x70, 0x00, 0x00, 0x00, 0x00, 0x79, 0xf8, 0x5a, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x7a, 0x9e, 0x85, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x7b, 0xd8, 0x3c, 0x60, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7e, 0x67, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x7d, 0xb8, 0x1e, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x7e, 0x5e, 0x49, 0x70, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x98, 0x00, 0x60,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0xff, 0xff, 0xba, 0x9e,
-  0x00, 0x00, 0xff, 0xff, 0xc7, 0xc0, 0x01, 0x04, 0xff, 0xff, 0xb9, 0xb0,
-  0x00, 0x08, 0xff, 0xff, 0xc7, 0xc0, 0x01, 0x0c, 0xff, 0xff, 0xc7, 0xc0,
-  0x01, 0x10, 0x4c, 0x4d, 0x54, 0x00, 0x45, 0x44, 0x54, 0x00, 0x45, 0x53,
-  0x54, 0x00, 0x45, 0x57, 0x54, 0x00, 0x45, 0x50, 0x54, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x45, 0x53, 0x54,
-  0x35, 0x45, 0x44, 0x54, 0x2c, 0x4d, 0x33, 0x2e, 0x32, 0x2e, 0x30, 0x2c,
-  0x4d, 0x31, 0x31, 0x2e, 0x31, 0x2e, 0x30, 0x0a
-};
-unsigned int America_New_York_len = 3536;
-unsigned char Australia_Sydney[] = {
-  0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e,
-  0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x00, 0x00, 0x00,
-  0x9c, 0x4e, 0xc2, 0x80, 0x9c, 0xbc, 0x2f, 0x00, 0xcb, 0x54, 0xb3, 0x00,
-  0xcb, 0xc7, 0x65, 0x80, 0xcc, 0xb7, 0x56, 0x80, 0xcd, 0xa7, 0x47, 0x80,
-  0xce, 0xa0, 0x73, 0x00, 0xcf, 0x87, 0x29, 0x80, 0x03, 0x70, 0x39, 0x80,
-  0x04, 0x0d, 0x1c, 0x00, 0x05, 0x50, 0x1b, 0x80, 0x05, 0xf6, 0x38, 0x80,
-  0x07, 0x2f, 0xfd, 0x80, 0x07, 0xd6, 0x1a, 0x80, 0x09, 0x0f, 0xdf, 0x80,
-  0x09, 0xb5, 0xfc, 0x80, 0x0a, 0xef, 0xc1, 0x80, 0x0b, 0x9f, 0x19, 0x00,
-  0x0c, 0xd8, 0xde, 0x00, 0x0d, 0x7e, 0xfb, 0x00, 0x0e, 0xb8, 0xc0, 0x00,
-  0x0f, 0x5e, 0xdd, 0x00, 0x10, 0x98, 0xa2, 0x00, 0x11, 0x3e, 0xbf, 0x00,
-  0x12, 0x78, 0x84, 0x00, 0x13, 0x1e, 0xa1, 0x00, 0x14, 0x58, 0x66, 0x00,
-  0x14, 0xfe, 0x83, 0x00, 0x16, 0x38, 0x48, 0x00, 0x17, 0x0c, 0x89, 0x80,
-  0x18, 0x21, 0x64, 0x80, 0x18, 0xc7, 0x81, 0x80, 0x1a, 0x01, 0x46, 0x80,
-  0x1a, 0xa7, 0x63, 0x80, 0x1b, 0xe1, 0x28, 0x80, 0x1c, 0x87, 0x45, 0x80,
-  0x1d, 0xc1, 0x0a, 0x80, 0x1e, 0x79, 0x9c, 0x80, 0x1f, 0x97, 0xb2, 0x00,
-  0x20, 0x59, 0x7e, 0x80, 0x21, 0x80, 0xce, 0x80, 0x22, 0x42, 0x9b, 0x00,
-  0x23, 0x69, 0xeb, 0x00, 0x24, 0x22, 0x7d, 0x00, 0x25, 0x49, 0xcd, 0x00,
-  0x25, 0xef, 0xea, 0x00, 0x27, 0x29, 0xaf, 0x00, 0x27, 0xcf, 0xcc, 0x00,
-  0x29, 0x09, 0x91, 0x00, 0x29, 0xaf, 0xae, 0x00, 0x2a, 0xe9, 0x73, 0x00,
-  0x2b, 0x98, 0xca, 0x80, 0x2c, 0xd2, 0x8f, 0x80, 0x2d, 0x78, 0xac, 0x80,
-  0x2e, 0xb2, 0x71, 0x80, 0x2f, 0x58, 0x8e, 0x80, 0x30, 0x92, 0x53, 0x80,
-  0x31, 0x5d, 0x5a, 0x80, 0x32, 0x72, 0x35, 0x80, 0x33, 0x3d, 0x3c, 0x80,
-  0x34, 0x52, 0x17, 0x80, 0x35, 0x1d, 0x1e, 0x80, 0x36, 0x31, 0xf9, 0x80,
-  0x36, 0xfd, 0x00, 0x80, 0x38, 0x1b, 0x16, 0x00, 0x38, 0xdc, 0xe2, 0x80,
-  0x39, 0xa7, 0xe9, 0x80, 0x3a, 0xbc, 0xc4, 0x80, 0x3b, 0xda, 0xda, 0x00,
-  0x3c, 0xa5, 0xe1, 0x00, 0x3d, 0xba, 0xbc, 0x00, 0x3e, 0x85, 0xc3, 0x00,
-  0x3f, 0x9a, 0x9e, 0x00, 0x40, 0x65, 0xa5, 0x00, 0x41, 0x83, 0xba, 0x80,
-  0x42, 0x45, 0x87, 0x00, 0x43, 0x63, 0x9c, 0x80, 0x44, 0x2e, 0xa3, 0x80,
-  0x45, 0x43, 0x7e, 0x80, 0x46, 0x05, 0x4b, 0x00, 0x47, 0x23, 0x60, 0x80,
-  0x47, 0xf7, 0xa2, 0x00, 0x48, 0xe7, 0x93, 0x00, 0x49, 0xd7, 0x84, 0x00,
-  0x4a, 0xc7, 0x75, 0x00, 0x4b, 0xb7, 0x66, 0x00, 0x4c, 0xa7, 0x57, 0x00,
-  0x4d, 0x97, 0x48, 0x00, 0x4e, 0x87, 0x39, 0x00, 0x4f, 0x77, 0x2a, 0x00,
-  0x50, 0x70, 0x55, 0x80, 0x51, 0x60, 0x46, 0x80, 0x52, 0x50, 0x37, 0x80,
-  0x53, 0x40, 0x28, 0x80, 0x54, 0x30, 0x19, 0x80, 0x55, 0x20, 0x0a, 0x80,
-  0x56, 0x0f, 0xfb, 0x80, 0x56, 0xff, 0xec, 0x80, 0x57, 0xef, 0xdd, 0x80,
-  0x58, 0xdf, 0xce, 0x80, 0x59, 0xcf, 0xbf, 0x80, 0x5a, 0xbf, 0xb0, 0x80,
-  0x5b, 0xb8, 0xdc, 0x00, 0x5c, 0xa8, 0xcd, 0x00, 0x5d, 0x98, 0xbe, 0x00,
-  0x5e, 0x88, 0xaf, 0x00, 0x5f, 0x78, 0xa0, 0x00, 0x60, 0x68, 0x91, 0x00,
-  0x61, 0x58, 0x82, 0x00, 0x62, 0x48, 0x73, 0x00, 0x63, 0x38, 0x64, 0x00,
-  0x64, 0x28, 0x55, 0x00, 0x65, 0x18, 0x46, 0x00, 0x66, 0x11, 0x71, 0x80,
-  0x67, 0x01, 0x62, 0x80, 0x67, 0xf1, 0x53, 0x80, 0x68, 0xe1, 0x44, 0x80,
-  0x69, 0xd1, 0x35, 0x80, 0x6a, 0xc1, 0x26, 0x80, 0x6b, 0xb1, 0x17, 0x80,
-  0x6c, 0xa1, 0x08, 0x80, 0x6d, 0x90, 0xf9, 0x80, 0x6e, 0x80, 0xea, 0x80,
-  0x6f, 0x70, 0xdb, 0x80, 0x70, 0x6a, 0x07, 0x00, 0x71, 0x59, 0xf8, 0x00,
-  0x72, 0x49, 0xe9, 0x00, 0x73, 0x39, 0xda, 0x00, 0x74, 0x29, 0xcb, 0x00,
-  0x75, 0x19, 0xbc, 0x00, 0x76, 0x09, 0xad, 0x00, 0x76, 0xf9, 0x9e, 0x00,
-  0x77, 0xe9, 0x8f, 0x00, 0x78, 0xd9, 0x80, 0x00, 0x79, 0xc9, 0x71, 0x00,
-  0x7a, 0xb9, 0x62, 0x00, 0x7b, 0xb2, 0x8d, 0x80, 0x7c, 0xa2, 0x7e, 0x80,
-  0x7d, 0x92, 0x6f, 0x80, 0x7e, 0x82, 0x60, 0x80, 0x7f, 0x72, 0x51, 0x80,
-  0x03, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x00, 0x00,
-  0x8d, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x9a, 0xb0, 0x01, 0x04, 0x00, 0x00,
-  0x8c, 0xa0, 0x00, 0x09, 0x00, 0x00, 0x8c, 0xa0, 0x00, 0x09, 0x4c, 0x4d,
-  0x54, 0x00, 0x41, 0x45, 0x44, 0x54, 0x00, 0x41, 0x45, 0x53, 0x54, 0x00,
-  0x00, 0x01, 0x01, 0x00, 0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0e,
-  0xff, 0xff, 0xff, 0xff, 0x73, 0x16, 0x7f, 0x3c, 0xff, 0xff, 0xff, 0xff,
-  0x9c, 0x4e, 0xc2, 0x80, 0xff, 0xff, 0xff, 0xff, 0x9c, 0xbc, 0x2f, 0x00,
-  0xff, 0xff, 0xff, 0xff, 0xcb, 0x54, 0xb3, 0x00, 0xff, 0xff, 0xff, 0xff,
-  0xcb, 0xc7, 0x65, 0x80, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xb7, 0x56, 0x80,
-  0xff, 0xff, 0xff, 0xff, 0xcd, 0xa7, 0x47, 0x80, 0xff, 0xff, 0xff, 0xff,
-  0xce, 0xa0, 0x73, 0x00, 0xff, 0xff, 0xff, 0xff, 0xcf, 0x87, 0x29, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x03, 0x70, 0x39, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x04, 0x0d, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x50, 0x1b, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x05, 0xf6, 0x38, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x07, 0x2f, 0xfd, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0xd6, 0x1a, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x09, 0x0f, 0xdf, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x09, 0xb5, 0xfc, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xef, 0xc1, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x0b, 0x9f, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x0c, 0xd8, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x7e, 0xfb, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x0e, 0xb8, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x0f, 0x5e, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x98, 0xa2, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x11, 0x3e, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x12, 0x78, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x1e, 0xa1, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x14, 0x58, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x14, 0xfe, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x38, 0x48, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x17, 0x0c, 0x89, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x18, 0x21, 0x64, 0x80, 0x00, 0x00, 0x00, 0x00, 0x18, 0xc7, 0x81, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x1a, 0x01, 0x46, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x1a, 0xa7, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xe1, 0x28, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x1c, 0x87, 0x45, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x1d, 0xc1, 0x0a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x79, 0x9c, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x1f, 0x97, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x20, 0x59, 0x7e, 0x80, 0x00, 0x00, 0x00, 0x00, 0x21, 0x80, 0xce, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x22, 0x42, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x23, 0x69, 0xeb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x22, 0x7d, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x25, 0x49, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x25, 0xef, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x29, 0xaf, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x27, 0xcf, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x29, 0x09, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xaf, 0xae, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x2a, 0xe9, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x2b, 0x98, 0xca, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2c, 0xd2, 0x8f, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x2d, 0x78, 0xac, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x2e, 0xb2, 0x71, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x58, 0x8e, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x30, 0x92, 0x53, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x31, 0x5d, 0x5a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x32, 0x72, 0x35, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x33, 0x3d, 0x3c, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x34, 0x52, 0x17, 0x80, 0x00, 0x00, 0x00, 0x00, 0x35, 0x1d, 0x1e, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x36, 0x31, 0xf9, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x36, 0xfd, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1b, 0x16, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x38, 0xdc, 0xe2, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x39, 0xa7, 0xe9, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xbc, 0xc4, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x3b, 0xda, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x3c, 0xa5, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xba, 0xbc, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x3e, 0x85, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x3f, 0x9a, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x65, 0xa5, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x41, 0x83, 0xba, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x42, 0x45, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x63, 0x9c, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x44, 0x2e, 0xa3, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x45, 0x43, 0x7e, 0x80, 0x00, 0x00, 0x00, 0x00, 0x46, 0x05, 0x4b, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x47, 0x23, 0x60, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x47, 0xf7, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xe7, 0x93, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x49, 0xd7, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x4a, 0xc7, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0xb7, 0x66, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x4c, 0xa7, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x4d, 0x97, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x87, 0x39, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x4f, 0x77, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x50, 0x70, 0x55, 0x80, 0x00, 0x00, 0x00, 0x00, 0x51, 0x60, 0x46, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x52, 0x50, 0x37, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x53, 0x40, 0x28, 0x80, 0x00, 0x00, 0x00, 0x00, 0x54, 0x30, 0x19, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x55, 0x20, 0x0a, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x56, 0x0f, 0xfb, 0x80, 0x00, 0x00, 0x00, 0x00, 0x56, 0xff, 0xec, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x57, 0xef, 0xdd, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x58, 0xdf, 0xce, 0x80, 0x00, 0x00, 0x00, 0x00, 0x59, 0xcf, 0xbf, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x5a, 0xbf, 0xb0, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x5b, 0xb8, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0xa8, 0xcd, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x5d, 0x98, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x5e, 0x88, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x78, 0xa0, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x60, 0x68, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x61, 0x58, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x48, 0x73, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x63, 0x38, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x64, 0x28, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x18, 0x46, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x66, 0x11, 0x71, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x67, 0x01, 0x62, 0x80, 0x00, 0x00, 0x00, 0x00, 0x67, 0xf1, 0x53, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x68, 0xe1, 0x44, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x69, 0xd1, 0x35, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6a, 0xc1, 0x26, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x6b, 0xb1, 0x17, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x6c, 0xa1, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x90, 0xf9, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x6e, 0x80, 0xea, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x6f, 0x70, 0xdb, 0x80, 0x00, 0x00, 0x00, 0x00, 0x70, 0x6a, 0x07, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x71, 0x59, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x72, 0x49, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x39, 0xda, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x74, 0x29, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x75, 0x19, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x09, 0xad, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x76, 0xf9, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x77, 0xe9, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xd9, 0x80, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x79, 0xc9, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x7a, 0xb9, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xb2, 0x8d, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x7c, 0xa2, 0x7e, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x7d, 0x92, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x82, 0x60, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x7f, 0x72, 0x51, 0x80, 0x03, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x00, 0x00, 0x8d, 0xc4, 0x00, 0x00,
-  0x00, 0x00, 0x9a, 0xb0, 0x01, 0x04, 0x00, 0x00, 0x8c, 0xa0, 0x00, 0x09,
-  0x00, 0x00, 0x8c, 0xa0, 0x00, 0x09, 0x4c, 0x4d, 0x54, 0x00, 0x41, 0x45,
-  0x44, 0x54, 0x00, 0x41, 0x45, 0x53, 0x54, 0x00, 0x00, 0x01, 0x01, 0x00,
-  0x0a, 0x41, 0x45, 0x53, 0x54, 0x2d, 0x31, 0x30, 0x41, 0x45, 0x44, 0x54,
-  0x2c, 0x4d, 0x31, 0x30, 0x2e, 0x31, 0x2e, 0x30, 0x2c, 0x4d, 0x34, 0x2e,
-  0x31, 0x2e, 0x30, 0x2f, 0x33, 0x0a
-};
-unsigned int Australia_Sydney_len = 2190;
diff --git a/absl/time/time.cc b/absl/time/time.cc
index 7256a69..a11e8e9 100644
--- a/absl/time/time.cc
+++ b/absl/time/time.cc
@@ -137,7 +137,7 @@
 }
 
 // Makes a Time from sec, overflowing to InfiniteFuture/InfinitePast as
-// necessary. If sec is min/max, then consult cs+tz to check for overlow.
+// necessary. If sec is min/max, then consult cs+tz to check for overflow.
 Time MakeTimeWithOverflow(const cctz::time_point<cctz::seconds>& sec,
                           const cctz::civil_second& cs,
                           const cctz::time_zone& tz,
diff --git a/absl/time/time.h b/absl/time/time.h
index 11796b4..29178c7 100644
--- a/absl/time/time.h
+++ b/absl/time/time.h
@@ -78,11 +78,13 @@
 #include <cmath>
 #include <cstdint>
 #include <ctime>
+#include <limits>
 #include <ostream>
 #include <string>
 #include <type_traits>
 #include <utility>
 
+#include "absl/base/config.h"
 #include "absl/base/macros.h"
 #include "absl/strings/string_view.h"
 #include "absl/time/civil_time.h"
@@ -97,19 +99,24 @@
 
 namespace time_internal {
 int64_t IDivDuration(bool satq, Duration num, Duration den, Duration* rem);
-constexpr Time FromUnixDuration(Duration d);
-constexpr Duration ToUnixDuration(Time t);
-constexpr int64_t GetRepHi(Duration d);
-constexpr uint32_t GetRepLo(Duration d);
-constexpr Duration MakeDuration(int64_t hi, uint32_t lo);
-constexpr Duration MakeDuration(int64_t hi, int64_t lo);
-inline Duration MakePosDoubleDuration(double n);
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixDuration(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration ToUnixDuration(Time t);
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr int64_t GetRepHi(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr uint32_t GetRepLo(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration MakeDuration(int64_t hi,
+                                                              uint32_t lo);
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration MakeDuration(int64_t hi,
+                                                              int64_t lo);
+ABSL_ATTRIBUTE_CONST_FUNCTION inline Duration MakePosDoubleDuration(double n);
 constexpr int64_t kTicksPerNanosecond = 4;
 constexpr int64_t kTicksPerSecond = 1000 * 1000 * 1000 * kTicksPerNanosecond;
 template <std::intmax_t N>
-constexpr Duration FromInt64(int64_t v, std::ratio<1, N>);
-constexpr Duration FromInt64(int64_t v, std::ratio<60>);
-constexpr Duration FromInt64(int64_t v, std::ratio<3600>);
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration FromInt64(int64_t v,
+                                                           std::ratio<1, N>);
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration FromInt64(int64_t v,
+                                                           std::ratio<60>);
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration FromInt64(int64_t v,
+                                                           std::ratio<3600>);
 template <typename T>
 using EnableIfIntegral = typename std::enable_if<
     std::is_integral<T>::value || std::is_enum<T>::value, int>::type;
@@ -181,7 +188,12 @@
   Duration& operator%=(Duration rhs);
 
   // Overloads that forward to either the int64_t or double overloads above.
-  // Integer operands must be representable as int64_t.
+  // Integer operands must be representable as int64_t. Integer division is
+  // truncating, so values less than the resolution will be returned as zero.
+  // Floating-point multiplication and division is rounding (halfway cases
+  // rounding away from zero), so values less than the resolution may be
+  // returned as either the resolution or zero.  In particular, `d / 2.0`
+  // can produce `d` when it is the resolution and "even".
   template <typename T, time_internal::EnableIfIntegral<T> = 0>
   Duration& operator*=(T r) {
     int64_t x = r;
@@ -208,7 +220,7 @@
 
   template <typename H>
   friend H AbslHashValue(H h, Duration d) {
-    return H::combine(std::move(h), d.rep_hi_, d.rep_lo_);
+    return H::combine(std::move(h), d.rep_hi_.Get(), d.rep_lo_);
   }
 
  private:
@@ -217,42 +229,138 @@
   friend constexpr Duration time_internal::MakeDuration(int64_t hi,
                                                         uint32_t lo);
   constexpr Duration(int64_t hi, uint32_t lo) : rep_hi_(hi), rep_lo_(lo) {}
-  int64_t rep_hi_;
+
+  // We store `rep_hi_` 4-byte rather than 8-byte aligned to avoid 4 bytes of
+  // tail padding.
+  class HiRep {
+   public:
+    // Default constructor default-initializes `hi_`, which has the same
+    // semantics as default-initializing an `int64_t` (undetermined value).
+    HiRep() = default;
+
+    HiRep(const HiRep&) = default;
+    HiRep& operator=(const HiRep&) = default;
+
+    explicit constexpr HiRep(const int64_t value)
+        :  // C++17 forbids default-initialization in constexpr contexts. We can
+           // remove this in C++20.
+#if defined(ABSL_IS_BIG_ENDIAN) && ABSL_IS_BIG_ENDIAN
+          hi_(0),
+          lo_(0)
+#else
+          lo_(0),
+          hi_(0)
+#endif
+    {
+      *this = value;
+    }
+
+    constexpr int64_t Get() const {
+      const uint64_t unsigned_value =
+          (static_cast<uint64_t>(hi_) << 32) | static_cast<uint64_t>(lo_);
+      // `static_cast<int64_t>(unsigned_value)` is implementation-defined
+      // before c++20. On all supported platforms the behaviour is that mandated
+      // by c++20, i.e. "If the destination type is signed, [...] the result is
+      // the unique value of the destination type equal to the source value
+      // modulo 2^n, where n is the number of bits used to represent the
+      // destination type."
+      static_assert(
+          (static_cast<int64_t>((std::numeric_limits<uint64_t>::max)()) ==
+           int64_t{-1}) &&
+              (static_cast<int64_t>(static_cast<uint64_t>(
+                                        (std::numeric_limits<int64_t>::max)()) +
+                                    1) ==
+               (std::numeric_limits<int64_t>::min)()),
+          "static_cast<int64_t>(uint64_t) does not have c++20 semantics");
+      return static_cast<int64_t>(unsigned_value);
+    }
+
+    constexpr HiRep& operator=(const int64_t value) {
+      // "If the destination type is unsigned, the resulting value is the
+      // smallest unsigned value equal to the source value modulo 2^n
+      // where `n` is the number of bits used to represent the destination
+      // type".
+      const auto unsigned_value = static_cast<uint64_t>(value);
+      hi_ = static_cast<uint32_t>(unsigned_value >> 32);
+      lo_ = static_cast<uint32_t>(unsigned_value);
+      return *this;
+    }
+
+   private:
+    // Notes:
+    //  - Ideally we would use a `char[]` and `std::bitcast`, but the latter
+    //    does not exist (and is not constexpr in `absl`) before c++20.
+    //  - Order is optimized depending on endianness so that the compiler can
+    //    turn `Get()` (resp. `operator=()`) into a single 8-byte load (resp.
+    //    store).
+#if defined(ABSL_IS_BIG_ENDIAN) && ABSL_IS_BIG_ENDIAN
+    uint32_t hi_;
+    uint32_t lo_;
+#else
+    uint32_t lo_;
+    uint32_t hi_;
+#endif
+  };
+  HiRep rep_hi_;
   uint32_t rep_lo_;
 };
 
 // Relational Operators
-constexpr bool operator<(Duration lhs, Duration rhs);
-constexpr bool operator>(Duration lhs, Duration rhs) { return rhs < lhs; }
-constexpr bool operator>=(Duration lhs, Duration rhs) { return !(lhs < rhs); }
-constexpr bool operator<=(Duration lhs, Duration rhs) { return !(rhs < lhs); }
-constexpr bool operator==(Duration lhs, Duration rhs);
-constexpr bool operator!=(Duration lhs, Duration rhs) { return !(lhs == rhs); }
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator<(Duration lhs,
+                                                       Duration rhs);
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator>(Duration lhs,
+                                                       Duration rhs) {
+  return rhs < lhs;
+}
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator>=(Duration lhs,
+                                                        Duration rhs) {
+  return !(lhs < rhs);
+}
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator<=(Duration lhs,
+                                                        Duration rhs) {
+  return !(rhs < lhs);
+}
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator==(Duration lhs,
+                                                        Duration rhs);
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator!=(Duration lhs,
+                                                        Duration rhs) {
+  return !(lhs == rhs);
+}
 
 // Additive Operators
-constexpr Duration operator-(Duration d);
-inline Duration operator+(Duration lhs, Duration rhs) { return lhs += rhs; }
-inline Duration operator-(Duration lhs, Duration rhs) { return lhs -= rhs; }
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration operator-(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION inline Duration operator+(Duration lhs,
+                                                        Duration rhs) {
+  return lhs += rhs;
+}
+ABSL_ATTRIBUTE_CONST_FUNCTION inline Duration operator-(Duration lhs,
+                                                        Duration rhs) {
+  return lhs -= rhs;
+}
 
 // Multiplicative Operators
 // Integer operands must be representable as int64_t.
 template <typename T>
-Duration operator*(Duration lhs, T rhs) {
+ABSL_ATTRIBUTE_CONST_FUNCTION Duration operator*(Duration lhs, T rhs) {
   return lhs *= rhs;
 }
 template <typename T>
-Duration operator*(T lhs, Duration rhs) {
+ABSL_ATTRIBUTE_CONST_FUNCTION Duration operator*(T lhs, Duration rhs) {
   return rhs *= lhs;
 }
 template <typename T>
-Duration operator/(Duration lhs, T rhs) {
+ABSL_ATTRIBUTE_CONST_FUNCTION Duration operator/(Duration lhs, T rhs) {
   return lhs /= rhs;
 }
-inline int64_t operator/(Duration lhs, Duration rhs) {
+ABSL_ATTRIBUTE_CONST_FUNCTION inline int64_t operator/(Duration lhs,
+                                                       Duration rhs) {
   return time_internal::IDivDuration(true, lhs, rhs,
                                      &lhs);  // trunc towards zero
 }
-inline Duration operator%(Duration lhs, Duration rhs) { return lhs %= rhs; }
+ABSL_ATTRIBUTE_CONST_FUNCTION inline Duration operator%(Duration lhs,
+                                                        Duration rhs) {
+  return lhs %= rhs;
+}
 
 // IDivDuration()
 //
@@ -299,18 +407,20 @@
 //
 //   double d = absl::FDivDuration(absl::Milliseconds(1500), absl::Seconds(1));
 //   // d == 1.5
-double FDivDuration(Duration num, Duration den);
+ABSL_ATTRIBUTE_CONST_FUNCTION double FDivDuration(Duration num, Duration den);
 
 // ZeroDuration()
 //
 // Returns a zero-length duration. This function behaves just like the default
 // constructor, but the name helps make the semantics clear at call sites.
-constexpr Duration ZeroDuration() { return Duration(); }
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration ZeroDuration() {
+  return Duration();
+}
 
 // AbsDuration()
 //
 // Returns the absolute value of a duration.
-inline Duration AbsDuration(Duration d) {
+ABSL_ATTRIBUTE_CONST_FUNCTION inline Duration AbsDuration(Duration d) {
   return (d < ZeroDuration()) ? -d : d;
 }
 
@@ -322,7 +432,7 @@
 //
 //   absl::Duration d = absl::Nanoseconds(123456789);
 //   absl::Duration a = absl::Trunc(d, absl::Microseconds(1));  // 123456us
-Duration Trunc(Duration d, Duration unit);
+ABSL_ATTRIBUTE_CONST_FUNCTION Duration Trunc(Duration d, Duration unit);
 
 // Floor()
 //
@@ -333,7 +443,7 @@
 //
 //   absl::Duration d = absl::Nanoseconds(123456789);
 //   absl::Duration b = absl::Floor(d, absl::Microseconds(1));  // 123456us
-Duration Floor(Duration d, Duration unit);
+ABSL_ATTRIBUTE_CONST_FUNCTION Duration Floor(Duration d, Duration unit);
 
 // Ceil()
 //
@@ -344,7 +454,7 @@
 //
 //   absl::Duration d = absl::Nanoseconds(123456789);
 //   absl::Duration c = absl::Ceil(d, absl::Microseconds(1));   // 123457us
-Duration Ceil(Duration d, Duration unit);
+ABSL_ATTRIBUTE_CONST_FUNCTION Duration Ceil(Duration d, Duration unit);
 
 // InfiniteDuration()
 //
@@ -380,7 +490,7 @@
 //
 // The examples involving the `/` operator above also apply to `IDivDuration()`
 // and `FDivDuration()`.
-constexpr Duration InfiniteDuration();
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration InfiniteDuration();
 
 // Nanoseconds()
 // Microseconds()
@@ -404,27 +514,27 @@
 //   absl::Duration a = absl::Seconds(60);
 //   absl::Duration b = absl::Minutes(1);  // b == a
 template <typename T, time_internal::EnableIfIntegral<T> = 0>
-constexpr Duration Nanoseconds(T n) {
+ABSL_ATTRIBUTE_CONST_FUNCTION 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) {
+ABSL_ATTRIBUTE_CONST_FUNCTION 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) {
+ABSL_ATTRIBUTE_CONST_FUNCTION 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) {
+ABSL_ATTRIBUTE_CONST_FUNCTION 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) {
+ABSL_ATTRIBUTE_CONST_FUNCTION 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) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration Hours(T n) {
   return time_internal::FromInt64(n, std::ratio<3600>{});
 }
 
@@ -438,19 +548,19 @@
 //   auto a = absl::Seconds(1.5);        // OK
 //   auto b = absl::Milliseconds(1500);  // BETTER
 template <typename T, time_internal::EnableIfFloat<T> = 0>
-Duration Nanoseconds(T n) {
+ABSL_ATTRIBUTE_CONST_FUNCTION Duration Nanoseconds(T n) {
   return n * Nanoseconds(1);
 }
 template <typename T, time_internal::EnableIfFloat<T> = 0>
-Duration Microseconds(T n) {
+ABSL_ATTRIBUTE_CONST_FUNCTION Duration Microseconds(T n) {
   return n * Microseconds(1);
 }
 template <typename T, time_internal::EnableIfFloat<T> = 0>
-Duration Milliseconds(T n) {
+ABSL_ATTRIBUTE_CONST_FUNCTION Duration Milliseconds(T n) {
   return n * Milliseconds(1);
 }
 template <typename T, time_internal::EnableIfFloat<T> = 0>
-Duration Seconds(T n) {
+ABSL_ATTRIBUTE_CONST_FUNCTION Duration Seconds(T n) {
   if (n >= 0) {  // Note: `NaN >= 0` is false.
     if (n >= static_cast<T>((std::numeric_limits<int64_t>::max)())) {
       return InfiniteDuration();
@@ -464,11 +574,11 @@
   }
 }
 template <typename T, time_internal::EnableIfFloat<T> = 0>
-Duration Minutes(T n) {
+ABSL_ATTRIBUTE_CONST_FUNCTION Duration Minutes(T n) {
   return n * Minutes(1);
 }
 template <typename T, time_internal::EnableIfFloat<T> = 0>
-Duration Hours(T n) {
+ABSL_ATTRIBUTE_CONST_FUNCTION Duration Hours(T n) {
   return n * Hours(1);
 }
 
@@ -488,12 +598,12 @@
 //
 //   absl::Duration d = absl::Milliseconds(1500);
 //   int64_t isec = absl::ToInt64Seconds(d);  // isec == 1
-ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Nanoseconds(Duration d);
-ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Microseconds(Duration d);
-ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Milliseconds(Duration d);
-ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Seconds(Duration d);
-ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Minutes(Duration d);
-ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Hours(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Nanoseconds(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Microseconds(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Milliseconds(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Seconds(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Minutes(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Hours(Duration d);
 
 // ToDoubleNanoseconds()
 // ToDoubleMicroseconds()
@@ -510,12 +620,12 @@
 //
 //   absl::Duration d = absl::Milliseconds(1500);
 //   double dsec = absl::ToDoubleSeconds(d);  // dsec == 1.5
-ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleNanoseconds(Duration d);
-ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleMicroseconds(Duration d);
-ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleMilliseconds(Duration d);
-ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleSeconds(Duration d);
-ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleMinutes(Duration d);
-ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleHours(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION double ToDoubleNanoseconds(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION double ToDoubleMicroseconds(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION double ToDoubleMilliseconds(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION double ToDoubleSeconds(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION double ToDoubleMinutes(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION double ToDoubleHours(Duration d);
 
 // FromChrono()
 //
@@ -525,12 +635,18 @@
 //
 //   std::chrono::milliseconds ms(123);
 //   absl::Duration d = absl::FromChrono(ms);
-constexpr Duration FromChrono(const std::chrono::nanoseconds& d);
-constexpr Duration FromChrono(const std::chrono::microseconds& d);
-constexpr Duration FromChrono(const std::chrono::milliseconds& d);
-constexpr Duration FromChrono(const std::chrono::seconds& d);
-constexpr Duration FromChrono(const std::chrono::minutes& d);
-constexpr Duration FromChrono(const std::chrono::hours& d);
+ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono(
+    const std::chrono::nanoseconds& d);
+ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono(
+    const std::chrono::microseconds& d);
+ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono(
+    const std::chrono::milliseconds& d);
+ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono(
+    const std::chrono::seconds& d);
+ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono(
+    const std::chrono::minutes& d);
+ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono(
+    const std::chrono::hours& d);
 
 // ToChronoNanoseconds()
 // ToChronoMicroseconds()
@@ -550,24 +666,33 @@
 //   auto y = absl::ToChronoNanoseconds(d);  // x == y
 //   auto z = absl::ToChronoSeconds(absl::InfiniteDuration());
 //   // z == std::chrono::seconds::max()
-std::chrono::nanoseconds ToChronoNanoseconds(Duration d);
-std::chrono::microseconds ToChronoMicroseconds(Duration d);
-std::chrono::milliseconds ToChronoMilliseconds(Duration d);
-std::chrono::seconds ToChronoSeconds(Duration d);
-std::chrono::minutes ToChronoMinutes(Duration d);
-std::chrono::hours ToChronoHours(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION std::chrono::nanoseconds ToChronoNanoseconds(
+    Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION std::chrono::microseconds ToChronoMicroseconds(
+    Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION std::chrono::milliseconds ToChronoMilliseconds(
+    Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION std::chrono::seconds ToChronoSeconds(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION std::chrono::minutes ToChronoMinutes(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION std::chrono::hours ToChronoHours(Duration d);
 
 // FormatDuration()
 //
 // Returns a string representing the duration in the form "72h3m0.5s".
 // Returns "inf" or "-inf" for +/- `InfiniteDuration()`.
-std::string FormatDuration(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION std::string FormatDuration(Duration d);
 
 // Output stream operator.
 inline std::ostream& operator<<(std::ostream& os, Duration d) {
   return os << FormatDuration(d);
 }
 
+// Support for StrFormat(), StrCat() etc.
+template <typename Sink>
+void AbslStringify(Sink& sink, Duration d) {
+  sink.Append(FormatDuration(d));
+}
+
 // ParseDuration()
 //
 // Parses a duration string consisting of a possibly signed sequence of
@@ -725,29 +850,49 @@
 };
 
 // Relational Operators
-constexpr bool operator<(Time lhs, Time rhs) { return lhs.rep_ < rhs.rep_; }
-constexpr bool operator>(Time lhs, Time rhs) { return rhs < lhs; }
-constexpr bool operator>=(Time lhs, Time rhs) { return !(lhs < rhs); }
-constexpr bool operator<=(Time lhs, Time rhs) { return !(rhs < lhs); }
-constexpr bool operator==(Time lhs, Time rhs) { return lhs.rep_ == rhs.rep_; }
-constexpr bool operator!=(Time lhs, Time rhs) { return !(lhs == rhs); }
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator<(Time lhs, Time rhs) {
+  return lhs.rep_ < rhs.rep_;
+}
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator>(Time lhs, Time rhs) {
+  return rhs < lhs;
+}
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator>=(Time lhs, Time rhs) {
+  return !(lhs < rhs);
+}
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator<=(Time lhs, Time rhs) {
+  return !(rhs < lhs);
+}
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator==(Time lhs, Time rhs) {
+  return lhs.rep_ == rhs.rep_;
+}
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator!=(Time lhs, Time rhs) {
+  return !(lhs == rhs);
+}
 
 // Additive Operators
-inline Time operator+(Time lhs, Duration rhs) { return lhs += rhs; }
-inline Time operator+(Duration lhs, Time rhs) { return rhs += lhs; }
-inline Time operator-(Time lhs, Duration rhs) { return lhs -= rhs; }
-inline Duration operator-(Time lhs, Time rhs) { return lhs.rep_ - rhs.rep_; }
+ABSL_ATTRIBUTE_CONST_FUNCTION inline Time operator+(Time lhs, Duration rhs) {
+  return lhs += rhs;
+}
+ABSL_ATTRIBUTE_CONST_FUNCTION inline Time operator+(Duration lhs, Time rhs) {
+  return rhs += lhs;
+}
+ABSL_ATTRIBUTE_CONST_FUNCTION inline Time operator-(Time lhs, Duration rhs) {
+  return lhs -= rhs;
+}
+ABSL_ATTRIBUTE_CONST_FUNCTION inline Duration operator-(Time lhs, Time rhs) {
+  return lhs.rep_ - rhs.rep_;
+}
 
 // UnixEpoch()
 //
 // Returns the `absl::Time` representing "1970-01-01 00:00:00.0 +0000".
-constexpr Time UnixEpoch() { return Time(); }
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time UnixEpoch() { return Time(); }
 
 // UniversalEpoch()
 //
 // Returns the `absl::Time` representing "0001-01-01 00:00:00.0 +0000", the
 // epoch of the ICU Universal Time Scale.
-constexpr Time UniversalEpoch() {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time UniversalEpoch() {
   // 719162 is the number of days from 0001-01-01 to 1970-01-01,
   // assuming the Gregorian calendar.
   return Time(
@@ -757,7 +902,7 @@
 // InfiniteFuture()
 //
 // Returns an `absl::Time` that is infinitely far in the future.
-constexpr Time InfiniteFuture() {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time InfiniteFuture() {
   return Time(time_internal::MakeDuration((std::numeric_limits<int64_t>::max)(),
                                           ~uint32_t{0}));
 }
@@ -765,7 +910,7 @@
 // InfinitePast()
 //
 // Returns an `absl::Time` that is infinitely far in the past.
-constexpr Time InfinitePast() {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time InfinitePast() {
   return Time(time_internal::MakeDuration((std::numeric_limits<int64_t>::min)(),
                                           ~uint32_t{0}));
 }
@@ -778,14 +923,15 @@
 // FromUDate()
 // FromUniversal()
 //
-// Creates an `absl::Time` from a variety of other representations.
-constexpr Time FromUnixNanos(int64_t ns);
-constexpr Time FromUnixMicros(int64_t us);
-constexpr Time FromUnixMillis(int64_t ms);
-constexpr Time FromUnixSeconds(int64_t s);
-constexpr Time FromTimeT(time_t t);
-Time FromUDate(double udate);
-Time FromUniversal(int64_t universal);
+// Creates an `absl::Time` from a variety of other representations.  See
+// https://unicode-org.github.io/icu/userguide/datetime/universaltimescale.html
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixNanos(int64_t ns);
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixMicros(int64_t us);
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixMillis(int64_t ms);
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixSeconds(int64_t s);
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromTimeT(time_t t);
+ABSL_ATTRIBUTE_CONST_FUNCTION Time FromUDate(double udate);
+ABSL_ATTRIBUTE_CONST_FUNCTION Time FromUniversal(int64_t universal);
 
 // ToUnixNanos()
 // ToUnixMicros()
@@ -795,17 +941,19 @@
 // ToUDate()
 // ToUniversal()
 //
-// Converts an `absl::Time` to a variety of other representations.  Note that
-// these operations round down toward negative infinity where necessary to
-// adjust to the resolution of the result type.  Beware of possible time_t
-// over/underflow in ToTime{T,val,spec}() on 32-bit platforms.
-int64_t ToUnixNanos(Time t);
-int64_t ToUnixMicros(Time t);
-int64_t ToUnixMillis(Time t);
-int64_t ToUnixSeconds(Time t);
-time_t ToTimeT(Time t);
-double ToUDate(Time t);
-int64_t ToUniversal(Time t);
+// Converts an `absl::Time` to a variety of other representations.  See
+// https://unicode-org.github.io/icu/userguide/datetime/universaltimescale.html
+//
+// Note that these operations round down toward negative infinity where
+// necessary to adjust to the resolution of the result type.  Beware of
+// possible time_t over/underflow in ToTime{T,val,spec}() on 32-bit platforms.
+ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToUnixNanos(Time t);
+ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToUnixMicros(Time t);
+ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToUnixMillis(Time t);
+ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToUnixSeconds(Time t);
+ABSL_ATTRIBUTE_CONST_FUNCTION time_t ToTimeT(Time t);
+ABSL_ATTRIBUTE_CONST_FUNCTION double ToUDate(Time t);
+ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToUniversal(Time t);
 
 // DurationFromTimespec()
 // DurationFromTimeval()
@@ -821,14 +969,14 @@
 // and gettimeofday(2)), so conversion functions are provided for both cases.
 // The "to timespec/val" direction is easily handled via overloading, but
 // for "from timespec/val" the desired type is part of the function name.
-Duration DurationFromTimespec(timespec ts);
-Duration DurationFromTimeval(timeval tv);
-timespec ToTimespec(Duration d);
-timeval ToTimeval(Duration d);
-Time TimeFromTimespec(timespec ts);
-Time TimeFromTimeval(timeval tv);
-timespec ToTimespec(Time t);
-timeval ToTimeval(Time t);
+ABSL_ATTRIBUTE_CONST_FUNCTION Duration DurationFromTimespec(timespec ts);
+ABSL_ATTRIBUTE_CONST_FUNCTION Duration DurationFromTimeval(timeval tv);
+ABSL_ATTRIBUTE_CONST_FUNCTION timespec ToTimespec(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION timeval ToTimeval(Duration d);
+ABSL_ATTRIBUTE_CONST_FUNCTION Time TimeFromTimespec(timespec ts);
+ABSL_ATTRIBUTE_CONST_FUNCTION Time TimeFromTimeval(timeval tv);
+ABSL_ATTRIBUTE_CONST_FUNCTION timespec ToTimespec(Time t);
+ABSL_ATTRIBUTE_CONST_FUNCTION timeval ToTimeval(Time t);
 
 // FromChrono()
 //
@@ -839,7 +987,8 @@
 //   auto tp = std::chrono::system_clock::from_time_t(123);
 //   absl::Time t = absl::FromChrono(tp);
 //   // t == absl::FromTimeT(123)
-Time FromChrono(const std::chrono::system_clock::time_point& tp);
+ABSL_ATTRIBUTE_PURE_FUNCTION Time
+FromChrono(const std::chrono::system_clock::time_point& tp);
 
 // ToChronoTime()
 //
@@ -852,7 +1001,8 @@
 //   absl::Time t = absl::FromTimeT(123);
 //   auto tp = absl::ToChronoTime(t);
 //   // tp == std::chrono::system_clock::from_time_t(123);
-std::chrono::system_clock::time_point ToChronoTime(Time);
+ABSL_ATTRIBUTE_CONST_FUNCTION std::chrono::system_clock::time_point
+    ToChronoTime(Time);
 
 // AbslParseFlag()
 //
@@ -1124,22 +1274,25 @@
 //   absl::Time t = ...;
 //   absl::TimeZone tz = ...;
 //   const auto cd = absl::ToCivilDay(t, tz);
-inline CivilSecond ToCivilSecond(Time t, TimeZone tz) {
+ABSL_ATTRIBUTE_PURE_FUNCTION inline CivilSecond ToCivilSecond(Time t,
+                                                              TimeZone tz) {
   return tz.At(t).cs;  // already a CivilSecond
 }
-inline CivilMinute ToCivilMinute(Time t, TimeZone tz) {
+ABSL_ATTRIBUTE_PURE_FUNCTION inline CivilMinute ToCivilMinute(Time t,
+                                                              TimeZone tz) {
   return CivilMinute(tz.At(t).cs);
 }
-inline CivilHour ToCivilHour(Time t, TimeZone tz) {
+ABSL_ATTRIBUTE_PURE_FUNCTION inline CivilHour ToCivilHour(Time t, TimeZone tz) {
   return CivilHour(tz.At(t).cs);
 }
-inline CivilDay ToCivilDay(Time t, TimeZone tz) {
+ABSL_ATTRIBUTE_PURE_FUNCTION inline CivilDay ToCivilDay(Time t, TimeZone tz) {
   return CivilDay(tz.At(t).cs);
 }
-inline CivilMonth ToCivilMonth(Time t, TimeZone tz) {
+ABSL_ATTRIBUTE_PURE_FUNCTION inline CivilMonth ToCivilMonth(Time t,
+                                                            TimeZone tz) {
   return CivilMonth(tz.At(t).cs);
 }
-inline CivilYear ToCivilYear(Time t, TimeZone tz) {
+ABSL_ATTRIBUTE_PURE_FUNCTION inline CivilYear ToCivilYear(Time t, TimeZone tz) {
   return CivilYear(tz.At(t).cs);
 }
 
@@ -1155,7 +1308,8 @@
 // being when two non-existent civil times map to the same transition time.
 //
 // Note: Accepts civil times of any alignment.
-inline Time FromCivil(CivilSecond ct, TimeZone tz) {
+ABSL_ATTRIBUTE_PURE_FUNCTION inline Time FromCivil(CivilSecond ct,
+                                                   TimeZone tz) {
   const auto ti = tz.At(ct);
   if (ti.kind == TimeZone::TimeInfo::SKIPPED) return ti.trans;
   return ti.pre;
@@ -1240,13 +1394,13 @@
 // instant, so `tm_isdst != 0` returns the DST instant, and `tm_isdst == 0`
 // returns the non-DST instant, that would have matched if the transition never
 // happened.
-Time FromTM(const struct tm& tm, TimeZone tz);
+ABSL_ATTRIBUTE_PURE_FUNCTION Time FromTM(const struct tm& tm, TimeZone tz);
 
 // ToTM()
 //
 // Converts the given `absl::Time` to a struct tm using the given time zone.
 // See ctime(3) for a description of the values of the tm fields.
-struct tm ToTM(Time t, TimeZone tz);
+ABSL_ATTRIBUTE_PURE_FUNCTION struct tm ToTM(Time t, TimeZone tz);
 
 // RFC3339_full
 // RFC3339_sec
@@ -1305,19 +1459,26 @@
 // `absl::InfinitePast()`, the returned string will be exactly "infinite-past".
 // In both cases the given format string and `absl::TimeZone` are ignored.
 //
-std::string FormatTime(absl::string_view format, Time t, TimeZone tz);
+ABSL_ATTRIBUTE_PURE_FUNCTION std::string FormatTime(absl::string_view format,
+                                                    Time t, TimeZone tz);
 
 // Convenience functions that format the given time using the RFC3339_full
 // format.  The first overload uses the provided TimeZone, while the second
 // uses LocalTimeZone().
-std::string FormatTime(Time t, TimeZone tz);
-std::string FormatTime(Time t);
+ABSL_ATTRIBUTE_PURE_FUNCTION std::string FormatTime(Time t, TimeZone tz);
+ABSL_ATTRIBUTE_PURE_FUNCTION std::string FormatTime(Time t);
 
 // Output stream operator.
 inline std::ostream& operator<<(std::ostream& os, Time t) {
   return os << FormatTime(t);
 }
 
+// Support for StrFormat(), StrCat() etc.
+template <typename Sink>
+void AbslStringify(Sink& sink, Time t) {
+  sink.Append(FormatTime(t));
+}
+
 // ParseTime()
 //
 // Parses an input string according to the provided format string and
@@ -1389,18 +1550,20 @@
 // Creates a Duration with a given representation.
 // REQUIRES: hi,lo is a valid representation of a Duration as specified
 // in time/duration.cc.
-constexpr Duration MakeDuration(int64_t hi, uint32_t lo = 0) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration MakeDuration(int64_t hi,
+                                                              uint32_t lo = 0) {
   return Duration(hi, lo);
 }
 
-constexpr Duration MakeDuration(int64_t hi, int64_t lo) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration MakeDuration(int64_t hi,
+                                                              int64_t lo) {
   return MakeDuration(hi, static_cast<uint32_t>(lo));
 }
 
 // Make a Duration value from a floating-point number, as long as that number
 // is in the range [ 0 .. numeric_limits<int64_t>::max ), that is, as long as
 // it's positive and can be converted to int64_t without risk of UB.
-inline Duration MakePosDoubleDuration(double n) {
+ABSL_ATTRIBUTE_CONST_FUNCTION inline Duration MakePosDoubleDuration(double n) {
   const int64_t int_secs = static_cast<int64_t>(n);
   const uint32_t ticks = static_cast<uint32_t>(
       std::round((n - static_cast<double>(int_secs)) * kTicksPerSecond));
@@ -1413,23 +1576,28 @@
 // pair. sec may be positive or negative.  ticks must be in the range
 // -kTicksPerSecond < *ticks < kTicksPerSecond.  If ticks is negative it
 // will be normalized to a positive value in the resulting Duration.
-constexpr Duration MakeNormalizedDuration(int64_t sec, int64_t ticks) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration MakeNormalizedDuration(
+    int64_t sec, int64_t ticks) {
   return (ticks < 0) ? MakeDuration(sec - 1, ticks + kTicksPerSecond)
                      : MakeDuration(sec, ticks);
 }
 
 // Provide access to the Duration representation.
-constexpr int64_t GetRepHi(Duration d) { return d.rep_hi_; }
-constexpr uint32_t GetRepLo(Duration d) { return d.rep_lo_; }
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr int64_t GetRepHi(Duration d) {
+  return d.rep_hi_.Get();
+}
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr uint32_t GetRepLo(Duration d) {
+  return d.rep_lo_;
+}
 
 // Returns true iff d is positive or negative infinity.
-constexpr bool IsInfiniteDuration(Duration d) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool IsInfiniteDuration(Duration d) {
   return GetRepLo(d) == ~uint32_t{0};
 }
 
 // Returns an infinite Duration with the opposite sign.
 // REQUIRES: IsInfiniteDuration(d)
-constexpr Duration OppositeInfinity(Duration d) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration OppositeInfinity(Duration d) {
   return GetRepHi(d) < 0
              ? MakeDuration((std::numeric_limits<int64_t>::max)(), ~uint32_t{0})
              : MakeDuration((std::numeric_limits<int64_t>::min)(),
@@ -1437,7 +1605,8 @@
 }
 
 // Returns (-n)-1 (equivalently -(n+1)) without avoidable overflow.
-constexpr int64_t NegateAndSubtractOne(int64_t n) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr int64_t NegateAndSubtractOne(
+    int64_t n) {
   // Note: Good compilers will optimize this expression to ~n when using
   // a two's-complement representation (which is required for int64_t).
   return (n < 0) ? -(n + 1) : (-n) - 1;
@@ -1447,23 +1616,30 @@
 // functions depend on the above mentioned choice of the Unix epoch for the
 // Time representation (and both need to be Time friends).  Without this
 // knowledge, we would need to add-in/subtract-out UnixEpoch() respectively.
-constexpr Time FromUnixDuration(Duration d) { return Time(d); }
-constexpr Duration ToUnixDuration(Time t) { return t.rep_; }
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixDuration(Duration d) {
+  return Time(d);
+}
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration ToUnixDuration(Time t) {
+  return t.rep_;
+}
 
 template <std::intmax_t N>
-constexpr Duration FromInt64(int64_t v, std::ratio<1, N>) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration FromInt64(int64_t v,
+                                                           std::ratio<1, N>) {
   static_assert(0 < N && N <= 1000 * 1000 * 1000, "Unsupported ratio");
   // Subsecond ratios cannot overflow.
   return MakeNormalizedDuration(
       v / N, v % N * kTicksPerNanosecond * 1000 * 1000 * 1000 / N);
 }
-constexpr Duration FromInt64(int64_t v, std::ratio<60>) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration FromInt64(int64_t v,
+                                                           std::ratio<60>) {
   return (v <= (std::numeric_limits<int64_t>::max)() / 60 &&
           v >= (std::numeric_limits<int64_t>::min)() / 60)
              ? MakeDuration(v * 60)
              : v > 0 ? InfiniteDuration() : -InfiniteDuration();
 }
-constexpr Duration FromInt64(int64_t v, std::ratio<3600>) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration FromInt64(int64_t v,
+                                                           std::ratio<3600>) {
   return (v <= (std::numeric_limits<int64_t>::max)() / 3600 &&
           v >= (std::numeric_limits<int64_t>::min)() / 3600)
              ? MakeDuration(v * 3600)
@@ -1483,40 +1659,44 @@
 
 // Converts a std::chrono::duration to an absl::Duration.
 template <typename Rep, typename Period>
-constexpr Duration FromChrono(const std::chrono::duration<Rep, Period>& d) {
+ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono(
+    const std::chrono::duration<Rep, Period>& d) {
   static_assert(IsValidRep64<Rep>(0), "duration::rep is invalid");
   return FromInt64(int64_t{d.count()}, Period{});
 }
 
 template <typename Ratio>
-int64_t ToInt64(Duration d, Ratio) {
+ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64(Duration d, Ratio) {
   // Note: This may be used on MSVC, which may have a system_clock period of
   // std::ratio<1, 10 * 1000 * 1000>
   return ToInt64Seconds(d * Ratio::den / Ratio::num);
 }
 // Fastpath implementations for the 6 common duration units.
-inline int64_t ToInt64(Duration d, std::nano) {
+ABSL_ATTRIBUTE_CONST_FUNCTION inline int64_t ToInt64(Duration d, std::nano) {
   return ToInt64Nanoseconds(d);
 }
-inline int64_t ToInt64(Duration d, std::micro) {
+ABSL_ATTRIBUTE_CONST_FUNCTION inline int64_t ToInt64(Duration d, std::micro) {
   return ToInt64Microseconds(d);
 }
-inline int64_t ToInt64(Duration d, std::milli) {
+ABSL_ATTRIBUTE_CONST_FUNCTION inline int64_t ToInt64(Duration d, std::milli) {
   return ToInt64Milliseconds(d);
 }
-inline int64_t ToInt64(Duration d, std::ratio<1>) {
+ABSL_ATTRIBUTE_CONST_FUNCTION inline int64_t ToInt64(Duration d,
+                                                     std::ratio<1>) {
   return ToInt64Seconds(d);
 }
-inline int64_t ToInt64(Duration d, std::ratio<60>) {
+ABSL_ATTRIBUTE_CONST_FUNCTION inline int64_t ToInt64(Duration d,
+                                                     std::ratio<60>) {
   return ToInt64Minutes(d);
 }
-inline int64_t ToInt64(Duration d, std::ratio<3600>) {
+ABSL_ATTRIBUTE_CONST_FUNCTION inline int64_t ToInt64(Duration d,
+                                                     std::ratio<3600>) {
   return ToInt64Hours(d);
 }
 
 // Converts an absl::Duration to a chrono duration of type T.
 template <typename T>
-T ToChronoDuration(Duration d) {
+ABSL_ATTRIBUTE_CONST_FUNCTION T ToChronoDuration(Duration d) {
   using Rep = typename T::rep;
   using Period = typename T::period;
   static_assert(IsValidRep64<Rep>(0), "duration::rep is invalid");
@@ -1530,7 +1710,8 @@
 
 }  // namespace time_internal
 
-constexpr bool operator<(Duration lhs, Duration rhs) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator<(Duration lhs,
+                                                       Duration rhs) {
   return time_internal::GetRepHi(lhs) != time_internal::GetRepHi(rhs)
              ? time_internal::GetRepHi(lhs) < time_internal::GetRepHi(rhs)
          : time_internal::GetRepHi(lhs) == (std::numeric_limits<int64_t>::min)()
@@ -1539,12 +1720,13 @@
              : time_internal::GetRepLo(lhs) < time_internal::GetRepLo(rhs);
 }
 
-constexpr bool operator==(Duration lhs, Duration rhs) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr bool operator==(Duration lhs,
+                                                        Duration rhs) {
   return time_internal::GetRepHi(lhs) == time_internal::GetRepHi(rhs) &&
          time_internal::GetRepLo(lhs) == time_internal::GetRepLo(rhs);
 }
 
-constexpr Duration operator-(Duration d) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration operator-(Duration d) {
   // This is a little interesting because of the special cases.
   //
   // If rep_lo_ is zero, we have it easy; it's safe to negate rep_hi_, we're
@@ -1570,47 +1752,53 @@
                              time_internal::GetRepLo(d));
 }
 
-constexpr Duration InfiniteDuration() {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration InfiniteDuration() {
   return time_internal::MakeDuration((std::numeric_limits<int64_t>::max)(),
                                      ~uint32_t{0});
 }
 
-constexpr Duration FromChrono(const std::chrono::nanoseconds& d) {
+ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono(
+    const std::chrono::nanoseconds& d) {
   return time_internal::FromChrono(d);
 }
-constexpr Duration FromChrono(const std::chrono::microseconds& d) {
+ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono(
+    const std::chrono::microseconds& d) {
   return time_internal::FromChrono(d);
 }
-constexpr Duration FromChrono(const std::chrono::milliseconds& d) {
+ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono(
+    const std::chrono::milliseconds& d) {
   return time_internal::FromChrono(d);
 }
-constexpr Duration FromChrono(const std::chrono::seconds& d) {
+ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono(
+    const std::chrono::seconds& d) {
   return time_internal::FromChrono(d);
 }
-constexpr Duration FromChrono(const std::chrono::minutes& d) {
+ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono(
+    const std::chrono::minutes& d) {
   return time_internal::FromChrono(d);
 }
-constexpr Duration FromChrono(const std::chrono::hours& d) {
+ABSL_ATTRIBUTE_PURE_FUNCTION constexpr Duration FromChrono(
+    const std::chrono::hours& d) {
   return time_internal::FromChrono(d);
 }
 
-constexpr Time FromUnixNanos(int64_t ns) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixNanos(int64_t ns) {
   return time_internal::FromUnixDuration(Nanoseconds(ns));
 }
 
-constexpr Time FromUnixMicros(int64_t us) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixMicros(int64_t us) {
   return time_internal::FromUnixDuration(Microseconds(us));
 }
 
-constexpr Time FromUnixMillis(int64_t ms) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixMillis(int64_t ms) {
   return time_internal::FromUnixDuration(Milliseconds(ms));
 }
 
-constexpr Time FromUnixSeconds(int64_t s) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromUnixSeconds(int64_t s) {
   return time_internal::FromUnixDuration(Seconds(s));
 }
 
-constexpr Time FromTimeT(time_t t) {
+ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Time FromTimeT(time_t t) {
   return time_internal::FromUnixDuration(Seconds(t));
 }
 
diff --git a/absl/time/time_benchmark.cc b/absl/time/time_benchmark.cc
index 99e6279..93a7c41 100644
--- a/absl/time/time_benchmark.cc
+++ b/absl/time/time_benchmark.cc
@@ -185,9 +185,11 @@
   int i = 0;
   while (state.KeepRunning()) {
     if ((i & 1) == 0) {
-      absl::FromCivil(absl::CivilSecond(2014, 12, 18, 20, 16, 18), tz);
+      benchmark::DoNotOptimize(
+          absl::FromCivil(absl::CivilSecond(2014, 12, 18, 20, 16, 18), tz));
     } else {
-      absl::FromCivil(absl::CivilSecond(2013, 11, 15, 18, 30, 27), tz);
+      benchmark::DoNotOptimize(
+          absl::FromCivil(absl::CivilSecond(2013, 11, 15, 18, 30, 27), tz));
     }
     ++i;
   }
@@ -224,7 +226,8 @@
 void BM_Time_FromCivilUTC_Absl(benchmark::State& state) {
   const absl::TimeZone tz = absl::UTCTimeZone();
   while (state.KeepRunning()) {
-    absl::FromCivil(absl::CivilSecond(2014, 12, 18, 20, 16, 18), tz);
+    benchmark::DoNotOptimize(
+        absl::FromCivil(absl::CivilSecond(2014, 12, 18, 20, 16, 18), tz));
   }
 }
 BENCHMARK(BM_Time_FromCivilUTC_Absl);
@@ -235,9 +238,11 @@
   int i = 0;
   while (state.KeepRunning()) {
     if ((i & 1) == 0) {
-      absl::FromCivil(absl::CivilSecond(2014, 12, 0, 20, 16, 18), tz);
+      benchmark::DoNotOptimize(
+          absl::FromCivil(absl::CivilSecond(2014, 12, 0, 20, 16, 18), tz));
     } else {
-      absl::FromCivil(absl::CivilSecond(2013, 11, 0, 18, 30, 27), tz);
+      benchmark::DoNotOptimize(
+          absl::FromCivil(absl::CivilSecond(2013, 11, 0, 18, 30, 27), tz));
     }
     ++i;
   }
diff --git a/absl/time/time_test.cc b/absl/time/time_test.cc
index d235e9a..bcf4f2a 100644
--- a/absl/time/time_test.cc
+++ b/absl/time/time_test.cc
@@ -28,6 +28,7 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/numeric/int128.h"
+#include "absl/strings/str_format.h"
 #include "absl/time/clock.h"
 #include "absl/time/internal/test_util.h"
 
@@ -377,11 +378,6 @@
 }
 
 TEST(Time, RoundtripConversion) {
-#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \
-    ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT
-  GTEST_SKIP();
-#endif
-
 #define TEST_CONVERSION_ROUND_TRIP(SOURCE, FROM, TO, MATCHER) \
   EXPECT_THAT(TO(FROM(SOURCE)), MATCHER(SOURCE))
 
@@ -563,11 +559,6 @@
 }
 
 TEST(Time, ToChronoTime) {
-#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \
-    ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT
-  GTEST_SKIP();
-#endif
-
   EXPECT_EQ(std::chrono::system_clock::from_time_t(-1),
             absl::ToChronoTime(absl::FromTimeT(-1)));
   EXPECT_EQ(std::chrono::system_clock::from_time_t(0),
@@ -1287,4 +1278,11 @@
   // We have a transition but we don't know which one.
 }
 
+TEST(Time, AbslStringify) {
+  // FormatTime is already well tested, so just use one test case here to
+  // verify that StrFormat("%v", t) works as expected.
+  absl::Time t = absl::Now();
+  EXPECT_EQ(absl::StrFormat("%v", t), absl::FormatTime(t));
+}
+
 }  // namespace
diff --git a/absl/types/any.h b/absl/types/any.h
index 204da26..61f071f 100644
--- a/absl/types/any.h
+++ b/absl/types/any.h
@@ -53,6 +53,7 @@
 #ifndef ABSL_TYPES_ANY_H_
 #define ABSL_TYPES_ANY_H_
 
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/utility/utility.h"
 
@@ -288,7 +289,7 @@
       typename T, typename... Args, typename VT = absl::decay_t<T>,
       absl::enable_if_t<std::is_copy_constructible<VT>::value &&
                         std::is_constructible<VT, Args...>::value>* = nullptr>
-  VT& emplace(Args&&... args) {
+  VT& emplace(Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     reset();  // NOTE: reset() is required here even in the world of exceptions.
     Obj<VT>* const object_ptr =
         new Obj<VT>(in_place, std::forward<Args>(args)...);
@@ -312,7 +313,8 @@
       absl::enable_if_t<std::is_copy_constructible<VT>::value &&
                         std::is_constructible<VT, std::initializer_list<U>&,
                                               Args...>::value>* = nullptr>
-  VT& emplace(std::initializer_list<U> ilist, Args&&... args) {
+  VT& emplace(std::initializer_list<U> ilist,
+              Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     reset();  // NOTE: reset() is required here even in the world of exceptions.
     Obj<VT>* const object_ptr =
         new Obj<VT>(in_place, ilist, std::forward<Args>(args)...);
diff --git a/absl/types/compare.h b/absl/types/compare.h
index 19b076e..2b89b69 100644
--- a/absl/types/compare.h
+++ b/absl/types/compare.h
@@ -36,6 +36,7 @@
 #include <type_traits>
 
 #include "absl/base/attributes.h"
+#include "absl/base/macros.h"
 #include "absl/meta/type_traits.h"
 
 namespace absl {
@@ -44,29 +45,37 @@
 
 using value_type = int8_t;
 
-template <typename T>
-struct Fail {
-  static_assert(sizeof(T) < 0, "Only literal `0` is allowed.");
-};
-
-// We need the NullPtrT template to avoid triggering the modernize-use-nullptr
-// ClangTidy warning in user code.
-template <typename NullPtrT = std::nullptr_t>
-struct OnlyLiteralZero {
-  constexpr OnlyLiteralZero(NullPtrT) noexcept {}  // NOLINT
+class OnlyLiteralZero {
+ public:
+#if ABSL_HAVE_ATTRIBUTE(enable_if)
+  // On clang, we can avoid triggering modernize-use-nullptr by only enabling
+  // this overload when the value is a compile time integer constant equal to 0.
+  //
+  // In c++20, this could be a static_assert in a consteval function.
+  constexpr OnlyLiteralZero(int n)  // NOLINT
+      __attribute__((enable_if(n == 0, "Only literal `0` is allowed."))) {}
+#else  // ABSL_HAVE_ATTRIBUTE(enable_if)
+  // Accept only literal zero since it can be implicitly converted to a pointer
+  // to member type. nullptr constants will be caught by the other constructor
+  // which accepts a nullptr_t.
+  //
+  // This constructor is not used for clang since it triggers
+  // modernize-use-nullptr.
+  constexpr OnlyLiteralZero(int OnlyLiteralZero::*) noexcept {}  // NOLINT
+#endif
 
   // Fails compilation when `nullptr` or integral type arguments other than
   // `int` are passed. This constructor doesn't accept `int` because literal `0`
   // has type `int`. Literal `0` arguments will be implicitly converted to
   // `std::nullptr_t` and accepted by the above constructor, while other `int`
   // arguments will fail to be converted and cause compilation failure.
-  template <
-      typename T,
-      typename = typename std::enable_if<
-          std::is_same<T, std::nullptr_t>::value ||
-          (std::is_integral<T>::value && !std::is_same<T, int>::value)>::type,
-      typename = typename Fail<T>::type>
-  OnlyLiteralZero(T);  // NOLINT
+  template <typename T, typename = typename std::enable_if<
+                            std::is_same<T, std::nullptr_t>::value ||
+                            (std::is_integral<T>::value &&
+                             !std::is_same<T, int>::value)>::type>
+  OnlyLiteralZero(T) {  // NOLINT
+    static_assert(sizeof(T) < 0, "Only literal `0` is allowed.");
+  }
 };
 
 enum class eq : value_type {
@@ -163,18 +172,18 @@
 
   // Comparisons
   friend constexpr bool operator==(
-      weak_equality v, compare_internal::OnlyLiteralZero<>) noexcept {
+      weak_equality v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ == 0;
   }
   friend constexpr bool operator!=(
-      weak_equality v, compare_internal::OnlyLiteralZero<>) noexcept {
+      weak_equality v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ != 0;
   }
-  friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator==(compare_internal::OnlyLiteralZero,
                                    weak_equality v) noexcept {
     return 0 == v.value_;
   }
-  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero,
                                    weak_equality v) noexcept {
     return 0 != v.value_;
   }
@@ -214,18 +223,18 @@
   }
   // Comparisons
   friend constexpr bool operator==(
-      strong_equality v, compare_internal::OnlyLiteralZero<>) noexcept {
+      strong_equality v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ == 0;
   }
   friend constexpr bool operator!=(
-      strong_equality v, compare_internal::OnlyLiteralZero<>) noexcept {
+      strong_equality v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ != 0;
   }
-  friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator==(compare_internal::OnlyLiteralZero,
                                    strong_equality v) noexcept {
     return 0 == v.value_;
   }
-  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero,
                                    strong_equality v) noexcept {
     return 0 != v.value_;
   }
@@ -277,50 +286,50 @@
   }
   // Comparisons
   friend constexpr bool operator==(
-      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      partial_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.is_ordered() && v.value_ == 0;
   }
   friend constexpr bool operator!=(
-      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      partial_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return !v.is_ordered() || v.value_ != 0;
   }
   friend constexpr bool operator<(
-      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      partial_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.is_ordered() && v.value_ < 0;
   }
   friend constexpr bool operator<=(
-      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      partial_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.is_ordered() && v.value_ <= 0;
   }
   friend constexpr bool operator>(
-      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      partial_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.is_ordered() && v.value_ > 0;
   }
   friend constexpr bool operator>=(
-      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      partial_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.is_ordered() && v.value_ >= 0;
   }
-  friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator==(compare_internal::OnlyLiteralZero,
                                    partial_ordering v) noexcept {
     return v.is_ordered() && 0 == v.value_;
   }
-  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero,
                                    partial_ordering v) noexcept {
     return !v.is_ordered() || 0 != v.value_;
   }
-  friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator<(compare_internal::OnlyLiteralZero,
                                   partial_ordering v) noexcept {
     return v.is_ordered() && 0 < v.value_;
   }
-  friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator<=(compare_internal::OnlyLiteralZero,
                                    partial_ordering v) noexcept {
     return v.is_ordered() && 0 <= v.value_;
   }
-  friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator>(compare_internal::OnlyLiteralZero,
                                   partial_ordering v) noexcept {
     return v.is_ordered() && 0 > v.value_;
   }
-  friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator>=(compare_internal::OnlyLiteralZero,
                                    partial_ordering v) noexcept {
     return v.is_ordered() && 0 >= v.value_;
   }
@@ -369,50 +378,50 @@
   }
   // Comparisons
   friend constexpr bool operator==(
-      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      weak_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ == 0;
   }
   friend constexpr bool operator!=(
-      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      weak_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ != 0;
   }
   friend constexpr bool operator<(
-      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      weak_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ < 0;
   }
   friend constexpr bool operator<=(
-      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      weak_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ <= 0;
   }
   friend constexpr bool operator>(
-      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      weak_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ > 0;
   }
   friend constexpr bool operator>=(
-      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      weak_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ >= 0;
   }
-  friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator==(compare_internal::OnlyLiteralZero,
                                    weak_ordering v) noexcept {
     return 0 == v.value_;
   }
-  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero,
                                    weak_ordering v) noexcept {
     return 0 != v.value_;
   }
-  friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator<(compare_internal::OnlyLiteralZero,
                                   weak_ordering v) noexcept {
     return 0 < v.value_;
   }
-  friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator<=(compare_internal::OnlyLiteralZero,
                                    weak_ordering v) noexcept {
     return 0 <= v.value_;
   }
-  friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator>(compare_internal::OnlyLiteralZero,
                                   weak_ordering v) noexcept {
     return 0 > v.value_;
   }
-  friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator>=(compare_internal::OnlyLiteralZero,
                                    weak_ordering v) noexcept {
     return 0 >= v.value_;
   }
@@ -468,50 +477,50 @@
   }
   // Comparisons
   friend constexpr bool operator==(
-      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      strong_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ == 0;
   }
   friend constexpr bool operator!=(
-      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      strong_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ != 0;
   }
   friend constexpr bool operator<(
-      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      strong_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ < 0;
   }
   friend constexpr bool operator<=(
-      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      strong_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ <= 0;
   }
   friend constexpr bool operator>(
-      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      strong_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ > 0;
   }
   friend constexpr bool operator>=(
-      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
+      strong_ordering v, compare_internal::OnlyLiteralZero) noexcept {
     return v.value_ >= 0;
   }
-  friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator==(compare_internal::OnlyLiteralZero,
                                    strong_ordering v) noexcept {
     return 0 == v.value_;
   }
-  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero,
                                    strong_ordering v) noexcept {
     return 0 != v.value_;
   }
-  friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator<(compare_internal::OnlyLiteralZero,
                                   strong_ordering v) noexcept {
     return 0 < v.value_;
   }
-  friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator<=(compare_internal::OnlyLiteralZero,
                                    strong_ordering v) noexcept {
     return 0 <= v.value_;
   }
-  friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator>(compare_internal::OnlyLiteralZero,
                                   strong_ordering v) noexcept {
     return 0 > v.value_;
   }
-  friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>,
+  friend constexpr bool operator>=(compare_internal::OnlyLiteralZero,
                                    strong_ordering v) noexcept {
     return 0 >= v.value_;
   }
@@ -544,9 +553,9 @@
 // Helper functions to do a boolean comparison of two keys given a boolean
 // or three-way comparator.
 // SFINAE prevents implicit conversions to bool (such as from int).
-template <typename Bool,
-          absl::enable_if_t<std::is_same<bool, Bool>::value, int> = 0>
-constexpr bool compare_result_as_less_than(const Bool r) { return r; }
+template <typename BoolT,
+          absl::enable_if_t<std::is_same<bool, BoolT>::value, int> = 0>
+constexpr bool compare_result_as_less_than(const BoolT r) { return r; }
 constexpr bool compare_result_as_less_than(const absl::weak_ordering r) {
   return r < 0;
 }
diff --git a/absl/types/internal/optional.h b/absl/types/internal/optional.h
index 6ed0c66..a96d260 100644
--- a/absl/types/internal/optional.h
+++ b/absl/types/internal/optional.h
@@ -25,34 +25,6 @@
 #include "absl/meta/type_traits.h"
 #include "absl/utility/utility.h"
 
-// ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
-//
-// Inheriting constructors is supported in GCC 4.8+, Clang 3.3+ and MSVC 2015.
-// __cpp_inheriting_constructors is a predefined macro and a recommended way to
-// check for this language feature, but GCC doesn't support it until 5.0 and
-// Clang doesn't support it until 3.6.
-// Also, MSVC 2015 has a bug: it doesn't inherit the constexpr template
-// constructor. For example, the following code won't work on MSVC 2015 Update3:
-// struct Base {
-//   int t;
-//   template <typename T>
-//   constexpr Base(T t_) : t(t_) {}
-// };
-// struct Foo : Base {
-//   using Base::Base;
-// }
-// constexpr Foo foo(0);  // doesn't work on MSVC 2015
-#if defined(__clang__)
-#if __has_feature(cxx_inheriting_constructors)
-#define ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1
-#endif
-#elif (defined(__GNUC__) &&                                       \
-       (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 8)) || \
-    (__cpp_inheriting_constructors >= 200802) ||                  \
-    (defined(_MSC_VER) && _MSC_VER >= 1910)
-#define ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1
-#endif
-
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
@@ -145,15 +117,7 @@
 class optional_data_base : public optional_data_dtor_base<T> {
  protected:
   using base = optional_data_dtor_base<T>;
-#ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
   using base::base;
-#else
-  optional_data_base() = default;
-
-  template <typename... Args>
-  constexpr explicit optional_data_base(in_place_t t, Args&&... args)
-      : base(t, absl::forward<Args>(args)...) {}
-#endif
 
   template <typename... Args>
   void construct(Args&&... args) {
@@ -188,27 +152,13 @@
 template <typename T>
 class optional_data<T, true> : public optional_data_base<T> {
  protected:
-#ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
   using optional_data_base<T>::optional_data_base;
-#else
-  optional_data() = default;
-
-  template <typename... Args>
-  constexpr explicit optional_data(in_place_t t, Args&&... args)
-      : optional_data_base<T>(t, absl::forward<Args>(args)...) {}
-#endif
 };
 
 template <typename T>
 class optional_data<T, false> : public optional_data_base<T> {
  protected:
-#ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
   using optional_data_base<T>::optional_data_base;
-#else
-  template <typename... Args>
-  constexpr explicit optional_data(in_place_t t, Args&&... args)
-      : optional_data_base<T>(t, absl::forward<Args>(args)...) {}
-#endif
 
   optional_data() = default;
 
@@ -399,6 +349,4 @@
 ABSL_NAMESPACE_END
 }  // namespace absl
 
-#undef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
-
 #endif  // ABSL_TYPES_INTERNAL_OPTIONAL_H_
diff --git a/absl/types/internal/span.h b/absl/types/internal/span.h
index 1920a89..ab89ba3 100644
--- a/absl/types/internal/span.h
+++ b/absl/types/internal/span.h
@@ -32,9 +32,6 @@
 class Span;
 
 namespace span_internal {
-// A constexpr min function
-constexpr size_t Min(size_t a, size_t b) noexcept { return a < b ? a : b; }
-
 // Wrappers for access to container data pointers.
 template <typename C>
 constexpr auto GetDataImpl(C& c, char) noexcept  // NOLINT(runtime/references)
@@ -91,7 +88,7 @@
 template <template <typename> class SpanT, typename T>
 bool EqualImpl(SpanT<T> a, SpanT<T> b) {
   static_assert(std::is_const<T>::value, "");
-  return absl::equal(a.begin(), a.end(), b.begin(), b.end());
+  return std::equal(a.begin(), a.end(), b.begin(), b.end());
 }
 
 template <template <typename> class SpanT, typename T>
@@ -102,28 +99,9 @@
   return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
 }
 
-// The `IsConvertible` classes here are needed because of the
-// `std::is_convertible` bug in libcxx when compiled with GCC. This build
-// configuration is used by Android NDK toolchain. Reference link:
-// https://bugs.llvm.org/show_bug.cgi?id=27538.
-template <typename From, typename To>
-struct IsConvertibleHelper {
- private:
-  static std::true_type testval(To);
-  static std::false_type testval(...);
-
- public:
-  using type = decltype(testval(std::declval<From>()));
-};
-
-template <typename From, typename To>
-struct IsConvertible : IsConvertibleHelper<From, To>::type {};
-
-// TODO(zhangxy): replace `IsConvertible` with `std::is_convertible` once the
-// older version of libcxx is not supported.
 template <typename From, typename To>
 using EnableIfConvertibleTo =
-    typename std::enable_if<IsConvertible<From, To>::value>::type;
+    typename std::enable_if<std::is_convertible<From, To>::value>::type;
 
 // IsView is true for types where the return type of .data() is the same for
 // mutable and const instances. This isn't foolproof, but it's only used to
@@ -147,7 +125,7 @@
 };
 
 // These enablers result in 'int' so they can be used as typenames or defaults
-// in template paramters lists.
+// in template parameters lists.
 template <typename T>
 using EnableIfIsView = std::enable_if_t<IsView<T>::value, int>;
 
diff --git a/absl/types/internal/variant.h b/absl/types/internal/variant.h
index c82ded4..fc8829e 100644
--- a/absl/types/internal/variant.h
+++ b/absl/types/internal/variant.h
@@ -877,8 +877,8 @@
 template <std::size_t... Is>
 struct ContainsVariantNPos
     : absl::negation<std::is_same<  // NOLINT
-          absl::integer_sequence<bool, 0 <= Is...>,
-          absl::integer_sequence<bool, Is != absl::variant_npos...>>> {};
+          std::integer_sequence<bool, 0 <= Is...>,
+          std::integer_sequence<bool, Is != absl::variant_npos...>>> {};
 
 template <class Op, class... QualifiedVariants>
 using RawVisitResult =
diff --git a/absl/types/optional.h b/absl/types/optional.h
index 134b2af..0a8080d 100644
--- a/absl/types/optional.h
+++ b/absl/types/optional.h
@@ -130,7 +130,7 @@
 
   // Constructs an `optional` holding an empty value, NOT a default constructed
   // `T`.
-  constexpr optional() noexcept {}
+  constexpr optional() noexcept = default;
 
   // Constructs an `optional` initialized with `nullopt` to hold an empty value.
   constexpr optional(nullopt_t) noexcept {}  // NOLINT(runtime/explicit)
@@ -357,7 +357,7 @@
   template <typename... Args,
             typename = typename std::enable_if<
                 std::is_constructible<T, Args&&...>::value>::type>
-  T& emplace(Args&&... args) {
+  T& emplace(Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     this->destruct();
     this->construct(std::forward<Args>(args)...);
     return reference();
@@ -377,7 +377,8 @@
   template <typename U, typename... Args,
             typename = typename std::enable_if<std::is_constructible<
                 T, std::initializer_list<U>&, Args&&...>::value>::type>
-  T& emplace(std::initializer_list<U> il, Args&&... args) {
+  T& emplace(std::initializer_list<U> il,
+             Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
     this->destruct();
     this->construct(il, std::forward<Args>(args)...);
     return reference();
@@ -414,11 +415,11 @@
   // `optional` is empty, behavior is undefined.
   //
   // If you need myOpt->foo in constexpr, use (*myOpt).foo instead.
-  const T* operator->() const {
+  const T* operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(this->engaged_);
     return std::addressof(this->data_);
   }
-  T* operator->() {
+  T* operator->() ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(this->engaged_);
     return std::addressof(this->data_);
   }
@@ -427,17 +428,17 @@
   //
   // Accesses the underlying `T` value of an `optional`. If the `optional` is
   // empty, behavior is undefined.
-  constexpr const T& operator*() const& {
+  constexpr const T& operator*() const& ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return ABSL_HARDENING_ASSERT(this->engaged_), reference();
   }
-  T& operator*() & {
+  T& operator*() & ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(this->engaged_);
     return reference();
   }
-  constexpr const T&& operator*() const && {
+  constexpr const T&& operator*() const&& ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return ABSL_HARDENING_ASSERT(this->engaged_), absl::move(reference());
   }
-  T&& operator*() && {
+  T&& operator*() && ABSL_ATTRIBUTE_LIFETIME_BOUND {
     ABSL_HARDENING_ASSERT(this->engaged_);
     return std::move(reference());
   }
@@ -472,23 +473,24 @@
   // and lvalue/rvalue-ness of the `optional` is preserved to the view of
   // the `T` sub-object. Throws `absl::bad_optional_access` when the `optional`
   // is empty.
-  constexpr const T& value() const & {
+  constexpr const T& value() const& ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return static_cast<bool>(*this)
                ? reference()
                : (optional_internal::throw_bad_optional_access(), reference());
   }
-  T& value() & {
+  T& value() & ABSL_ATTRIBUTE_LIFETIME_BOUND {
     return static_cast<bool>(*this)
                ? reference()
                : (optional_internal::throw_bad_optional_access(), reference());
   }
-  T&& value() && {  // NOLINT(build/c++11)
+  T&& value() && ABSL_ATTRIBUTE_LIFETIME_BOUND {  // NOLINT(build/c++11)
     return std::move(
         static_cast<bool>(*this)
             ? reference()
             : (optional_internal::throw_bad_optional_access(), reference()));
   }
-  constexpr const T&& value() const && {  // NOLINT(build/c++11)
+  constexpr const T&& value()
+      const&& ABSL_ATTRIBUTE_LIFETIME_BOUND {  // NOLINT(build/c++11)
     return absl::move(
         static_cast<bool>(*this)
             ? reference()
diff --git a/absl/types/optional_test.cc b/absl/types/optional_test.cc
index 21653a9..bd5fe08 100644
--- a/absl/types/optional_test.cc
+++ b/absl/types/optional_test.cc
@@ -97,9 +97,9 @@
 // 4522: multiple assignment operators specified
 // We wrote multiple of them to test that the correct overloads are selected.
 #ifdef _MSC_VER
-#pragma warning( push )
-#pragma warning( disable : 4521)
-#pragma warning( disable : 4522)
+#pragma warning(push)
+#pragma warning(disable : 4521)
+#pragma warning(disable : 4522)
 #endif
 struct Listenable {
   static StructorListener* listener;
@@ -133,20 +133,11 @@
   ~Listenable() { ++listener->destruct; }
 };
 #ifdef _MSC_VER
-#pragma warning( pop )
+#pragma warning(pop)
 #endif
 
 StructorListener* Listenable::listener = nullptr;
 
-// ABSL_HAVE_NO_CONSTEXPR_INITIALIZER_LIST is defined to 1 when the standard
-// library implementation doesn't marked initializer_list's default constructor
-// constexpr. The C++11 standard doesn't specify constexpr on it, but C++14
-// added it. However, libstdc++ 4.7 marked it constexpr.
-#if defined(_LIBCPP_VERSION) && \
-    (_LIBCPP_STD_VER <= 11 || defined(_LIBCPP_HAS_NO_CXX14_CONSTEXPR))
-#define ABSL_HAVE_NO_CONSTEXPR_INITIALIZER_LIST 1
-#endif
-
 struct ConstexprType {
   enum CtorTypes {
     kCtorDefault,
@@ -156,10 +147,8 @@
   };
   constexpr ConstexprType() : x(kCtorDefault) {}
   constexpr explicit ConstexprType(int i) : x(kCtorInt) {}
-#ifndef ABSL_HAVE_NO_CONSTEXPR_INITIALIZER_LIST
   constexpr ConstexprType(std::initializer_list<int> il)
       : x(kCtorInitializerList) {}
-#endif
   constexpr ConstexprType(const char*)  // NOLINT(runtime/explicit)
       : x(kCtorConstChar) {}
   int x;
@@ -352,11 +341,9 @@
   constexpr absl::optional<ConstexprType> opt1{absl::in_place_t(), 1};
   static_assert(opt1, "");
   static_assert((*opt1).x == ConstexprType::kCtorInt, "");
-#ifndef ABSL_HAVE_NO_CONSTEXPR_INITIALIZER_LIST
   constexpr absl::optional<ConstexprType> opt2{absl::in_place_t(), {1, 2}};
   static_assert(opt2, "");
   static_assert((*opt2).x == ConstexprType::kCtorInitializerList, "");
-#endif
 
   EXPECT_FALSE((std::is_constructible<absl::optional<ConvertsFromInPlaceT>,
                                       absl::in_place_t>::value));
@@ -1000,9 +987,8 @@
 // Skip that test to make the build green again when using the old compiler.
 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59296 is fixed in 4.9.1.
 #if defined(__GNUC__) && !defined(__clang__)
-#define GCC_VERSION (__GNUC__ * 10000 \
-                     + __GNUC_MINOR__ * 100 \
-                     + __GNUC_PATCHLEVEL__)
+#define GCC_VERSION \
+  (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
 #if GCC_VERSION < 40901
 #define ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG
 #endif
@@ -1010,7 +996,7 @@
 
 // MSVC has a bug with "cv-qualifiers in class construction", fixed in 2017. See
 // https://docs.microsoft.com/en-us/cpp/cpp-conformance-improvements-2017#bug-fixes
-// The compiler some incorrectly ingores the cv-qualifier when generating a
+// The compiler some incorrectly ignores the cv-qualifier when generating a
 // class object via a constructor call. For example:
 //
 // class optional {
@@ -1214,7 +1200,6 @@
   EXPECT_TRUE(x >= y);
 }
 
-
 template <typename T, typename U, typename V>
 void TestComparisons() {
   absl::optional<T> ae, a2{2}, a4{4};
@@ -1307,7 +1292,6 @@
   EXPECT_TRUE(e1 == e2);
 }
 
-
 TEST(optionalTest, SwapRegression) {
   StructorListener listener;
   Listenable::listener = &listener;
diff --git a/absl/types/span.h b/absl/types/span.h
index cd863a9..70ed8eb 100644
--- a/absl/types/span.h
+++ b/absl/types/span.h
@@ -296,8 +296,7 @@
   //
   // Returns a reference to the i'th element of this span.
   constexpr reference operator[](size_type i) const noexcept {
-    // MSVC 2015 accepts this as constexpr, but not ptr_[i]
-    return ABSL_HARDENING_ASSERT(i < size()), *(data() + i);
+    return ABSL_HARDENING_ASSERT(i < size()), ptr_[i];
   }
 
   // Span::at()
@@ -420,7 +419,7 @@
   //   absl::MakeSpan(vec).subspan(5);     // throws std::out_of_range
   constexpr Span subspan(size_type pos = 0, size_type len = npos) const {
     return (pos <= size())
-               ? Span(data() + pos, span_internal::Min(size() - pos, len))
+               ? Span(data() + pos, (std::min)(size() - pos, len))
                : (base_internal::ThrowStdOutOfRange("pos > size()"), Span());
   }
 
diff --git a/absl/types/span_test.cc b/absl/types/span_test.cc
index 13264aa..29e8681 100644
--- a/absl/types/span_test.cc
+++ b/absl/types/span_test.cc
@@ -191,7 +191,7 @@
 }
 
 void TestInitializerList(absl::Span<const int> s, const std::vector<int>& v) {
-  EXPECT_TRUE(absl::equal(s.begin(), s.end(), v.begin(), v.end()));
+  EXPECT_TRUE(std::equal(s.begin(), s.end(), v.begin(), v.end()));
 }
 
 TEST(ConstIntSpan, InitializerListConversion) {
diff --git a/absl/utility/BUILD.bazel b/absl/utility/BUILD.bazel
index ca4bc0a..061f4c5 100644
--- a/absl/utility/BUILD.bazel
+++ b/absl/utility/BUILD.bazel
@@ -52,3 +52,26 @@
         "@com_google_googletest//:gtest_main",
     ],
 )
+
+cc_library(
+    name = "if_constexpr",
+    hdrs = [
+        "internal/if_constexpr.h",
+    ],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        "//absl/base:config",
+    ],
+)
+
+cc_test(
+    name = "if_constexpr_test",
+    srcs = ["internal/if_constexpr_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":if_constexpr",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
diff --git a/absl/utility/CMakeLists.txt b/absl/utility/CMakeLists.txt
index 865b758..27ee0de 100644
--- a/absl/utility/CMakeLists.txt
+++ b/absl/utility/CMakeLists.txt
@@ -42,3 +42,27 @@
     absl::strings
     GTest::gmock_main
 )
+
+absl_cc_library(
+  NAME
+    if_constexpr
+  HDRS
+    "internal/if_constexpr.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::config
+  PUBLIC
+)
+
+absl_cc_test(
+  NAME
+    if_constexpr_test
+  SRCS
+    "internal/if_constexpr_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::if_constexpr
+    GTest::gmock_main
+)
diff --git a/absl/utility/internal/if_constexpr.h b/absl/utility/internal/if_constexpr.h
new file mode 100644
index 0000000..7a26311
--- /dev/null
+++ b/absl/utility/internal/if_constexpr.h
@@ -0,0 +1,70 @@
+// Copyright 2023 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The IfConstexpr and IfConstexprElse utilities in this file are meant to be
+// used to emulate `if constexpr` in pre-C++17 mode in library implementation.
+// The motivation is to allow for avoiding complex SFINAE.
+//
+// The functions passed in must depend on the type(s) of the object(s) that
+// require SFINAE. For example:
+// template<typename T>
+// int MaybeFoo(T& t) {
+//   if constexpr (HasFoo<T>::value) return t.foo();
+//   return 0;
+// }
+//
+// can be written in pre-C++17 as:
+//
+// template<typename T>
+// int MaybeFoo(T& t) {
+//   int i = 0;
+//   absl::utility_internal::IfConstexpr<HasFoo<T>::value>(
+//       [&](const auto& fooer) { i = fooer.foo(); }, t);
+//   return i;
+// }
+
+#ifndef ABSL_UTILITY_INTERNAL_IF_CONSTEXPR_H_
+#define ABSL_UTILITY_INTERNAL_IF_CONSTEXPR_H_
+
+#include <tuple>
+#include <utility>
+
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+namespace utility_internal {
+
+template <bool condition, typename TrueFunc, typename FalseFunc,
+          typename... Args>
+auto IfConstexprElse(TrueFunc&& true_func, FalseFunc&& false_func,
+                     Args&&... args) {
+  return std::get<condition>(std::forward_as_tuple(
+      std::forward<FalseFunc>(false_func), std::forward<TrueFunc>(true_func)))(
+      std::forward<Args>(args)...);
+}
+
+template <bool condition, typename Func, typename... Args>
+void IfConstexpr(Func&& func, Args&&... args) {
+  IfConstexprElse<condition>(std::forward<Func>(func), [](auto&&...){},
+                             std::forward<Args>(args)...);
+}
+
+}  // namespace utility_internal
+
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_UTILITY_INTERNAL_IF_CONSTEXPR_H_
diff --git a/absl/utility/internal/if_constexpr_test.cc b/absl/utility/internal/if_constexpr_test.cc
new file mode 100644
index 0000000..d1ee723
--- /dev/null
+++ b/absl/utility/internal/if_constexpr_test.cc
@@ -0,0 +1,79 @@
+// Copyright 2023 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/utility/internal/if_constexpr.h"
+
+#include <utility>
+
+#include "gtest/gtest.h"
+
+namespace {
+
+struct Empty {};
+struct HasFoo {
+  int foo() const { return 1; }
+};
+
+TEST(IfConstexpr, Basic) {
+  int i = 0;
+  absl::utility_internal::IfConstexpr<false>(
+      [&](const auto& t) { i = t.foo(); }, Empty{});
+  EXPECT_EQ(i, 0);
+
+  absl::utility_internal::IfConstexpr<false>(
+      [&](const auto& t) { i = t.foo(); }, HasFoo{});
+  EXPECT_EQ(i, 0);
+
+  absl::utility_internal::IfConstexpr<true>(
+      [&](const auto& t) { i = t.foo(); }, HasFoo{});
+  EXPECT_EQ(i, 1);
+}
+
+TEST(IfConstexprElse, Basic) {
+  EXPECT_EQ(absl::utility_internal::IfConstexprElse<false>(
+      [&](const auto& t) { return t.foo(); }, [&](const auto&) { return 2; },
+      Empty{}), 2);
+
+  EXPECT_EQ(absl::utility_internal::IfConstexprElse<false>(
+      [&](const auto& t) { return t.foo(); }, [&](const auto&) { return 2; },
+      HasFoo{}), 2);
+
+  EXPECT_EQ(absl::utility_internal::IfConstexprElse<true>(
+      [&](const auto& t) { return t.foo(); }, [&](const auto&) { return 2; },
+      HasFoo{}), 1);
+}
+
+struct HasFooRValue {
+  int foo() && { return 1; }
+};
+struct RValueFunc {
+  void operator()(HasFooRValue&& t) && { *i = std::move(t).foo(); }
+
+  int* i = nullptr;
+};
+
+TEST(IfConstexpr, RValues) {
+  int i = 0;
+  RValueFunc func = {&i};
+  absl::utility_internal::IfConstexpr<false>(
+      std::move(func), HasFooRValue{});
+  EXPECT_EQ(i, 0);
+
+  func = RValueFunc{&i};
+  absl::utility_internal::IfConstexpr<true>(
+      std::move(func), HasFooRValue{});
+  EXPECT_EQ(i, 1);
+}
+
+}  // namespace
diff --git a/ci/cmake_common.sh b/ci/cmake_common.sh
index 372038a..f62bb8a 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="86add13493e5c881d7e4ba77fb91c1f57752b3a4"
+readonly ABSL_GOOGLETEST_COMMIT="2d4f208765af7fa376b878860a7677ecc0bc390a"
 
 # 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/linux_clang-latest_libcxx_tsan_bazel.sh b/ci/linux_clang-latest_libcxx_tsan_bazel.sh
index 5652ebe..34d7940 100755
--- a/ci/linux_clang-latest_libcxx_tsan_bazel.sh
+++ b/ci/linux_clang-latest_libcxx_tsan_bazel.sh
@@ -87,7 +87,6 @@
           --keep_going \
           --linkopt="-fsanitize=thread" \
           --show_timestamps \
-          --test_env="TSAN_OPTIONS=report_atomic_races=0" \
           --test_env="TSAN_SYMBOLIZER_PATH=/opt/llvm/clang/bin/llvm-symbolizer" \
           --test_env="TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo" \
           --test_output=errors \
diff --git a/ci/linux_clang-latest_libstdcxx_bazel.sh b/ci/linux_clang-latest_libstdcxx_bazel.sh
index 720e776..13d56fc 100755
--- a/ci/linux_clang-latest_libstdcxx_bazel.sh
+++ b/ci/linux_clang-latest_libstdcxx_bazel.sh
@@ -77,6 +77,7 @@
           --copt="--gcc-toolchain=/usr/local" \
           --copt="-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1" \
           --copt="${exceptions_mode}" \
+          --copt="-march=haswell" \
           --copt=-Werror \
           --define="absl=1" \
           --distdir="/bazel-distdir" \
diff --git a/ci/linux_docker_containers.sh b/ci/linux_docker_containers.sh
index f55e153..dcaf18b 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:20220217"
-readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20220217"
-readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20220621"
+readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20230217"
+readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20230217"
+readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20230120"
diff --git a/ci/linux_gcc-latest_libstdcxx_cmake.sh b/ci/linux_gcc-latest_libstdcxx_cmake.sh
index ba65853..1f72123 100755
--- a/ci/linux_gcc-latest_libstdcxx_cmake.sh
+++ b/ci/linux_gcc-latest_libstdcxx_cmake.sh
@@ -59,6 +59,7 @@
             -DCMAKE_CXX_STANDARD=${std} \
             -DCMAKE_MODULE_LINKER_FLAGS=\"-Wl,--no-undefined\" && \
           make -j$(nproc) && \
+          TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo \
           ctest -j$(nproc) --output-on-failure"
     done
   done
diff --git a/ci/linux_gcc_alpine_cmake.sh b/ci/linux_gcc_alpine_cmake.sh
index 1054013..b784456 100755
--- a/ci/linux_gcc_alpine_cmake.sh
+++ b/ci/linux_gcc_alpine_cmake.sh
@@ -58,6 +58,7 @@
             -DCMAKE_CXX_STANDARD=${std} \
             -DCMAKE_MODULE_LINKER_FLAGS=\"-Wl,--no-undefined\" && \
           make -j$(nproc) && \
+          TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo \
           ctest -j$(nproc) --output-on-failure"
     done
   done
diff --git a/ci/macos_xcode_cmake.sh b/ci/macos_xcode_cmake.sh
index 9798863..690f86b 100755
--- a/ci/macos_xcode_cmake.sh
+++ b/ci/macos_xcode_cmake.sh
@@ -51,6 +51,7 @@
       -DCMAKE_MODULE_LINKER_FLAGS="-Wl,--no-undefined" \
       -DABSL_GOOGLETEST_DOWNLOAD_URL="${ABSL_GOOGLETEST_DOWNLOAD_URL}"
     time cmake --build .
-    time ctest -C ${compilation_mode} --output-on-failure
+    time TZDIR=${ABSEIL_ROOT}/absl/time/internal/cctz/testdata/zoneinfo \
+      ctest -C ${compilation_mode} --output-on-failure
   done
 done
diff --git a/ci/windows_clangcl_bazel.bat b/ci/windows_clangcl_bazel.bat
new file mode 100755
index 0000000..21230e1
--- /dev/null
+++ b/ci/windows_clangcl_bazel.bat
@@ -0,0 +1,59 @@
+:: Copyright 2023 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.
+
+SETLOCAL ENABLEDELAYEDEXPANSION
+
+:: Set LLVM directory.
+SET BAZEL_LLVM=C:\Program Files\LLVM
+
+:: Change directory to the root of the project.
+CD %~dp0\..
+if %errorlevel% neq 0 EXIT /B 1
+
+:: Set the standard version, [c++14|c++17|c++20|c++latest]
+:: https://msdn.microsoft.com/en-us/library/mt490614.aspx
+:: The default is c++14 if not set on command line.
+IF "%STD%"=="" SET STD=c++14
+
+:: Set the compilation_mode (fastbuild|opt|dbg)
+:: https://docs.bazel.build/versions/master/user-manual.html#flag--compilation_mode
+:: The default is fastbuild
+IF "%COMPILATION_MODE%"=="" SET COMPILATION_MODE=fastbuild
+
+:: Copy the alternate option file, if specified.
+IF NOT "%ALTERNATE_OPTIONS%"=="" copy %ALTERNATE_OPTIONS% absl\base\options.h
+
+:: To upgrade Bazel, first download a new binary from
+:: https://github.com/bazelbuild/bazel/releases and copy it to
+:: /google/data/rw/teams/absl/kokoro/windows.
+::
+:: TODO(absl-team): Remove -Wno-microsoft-cast
+%KOKORO_GFILE_DIR%\bazel-5.1.1-windows-x86_64.exe ^
+  test ... ^
+  --compilation_mode=%COMPILATION_MODE% ^
+  --compiler=clang-cl ^
+  --copt=/std:%STD% ^
+  --copt=/WX ^
+  --copt=-Wno-microsoft-cast ^
+  --define=absl=1 ^
+  --distdir=%KOKORO_GFILE_DIR%\distdir ^
+  --features=external_include_paths ^
+  --keep_going ^
+  --test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" ^
+  --test_env=TZDIR="%CD%\absl\time\internal\cctz\testdata\zoneinfo" ^
+  --test_output=errors ^
+  --test_tag_filters=-benchmark
+
+if %errorlevel% neq 0 EXIT /B 1
+EXIT /B 0
diff --git a/ci/windows_msvc_bazel.bat b/ci/windows_msvc_bazel.bat
new file mode 100755
index 0000000..11d9f35
--- /dev/null
+++ b/ci/windows_msvc_bazel.bat
@@ -0,0 +1,52 @@
+:: Copyright 2023 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.
+
+SETLOCAL ENABLEDELAYEDEXPANSION
+
+:: Change directory to the root of the project.
+CD %~dp0\..
+if %errorlevel% neq 0 EXIT /B 1
+
+:: Set the standard version, [c++14|c++latest]
+:: https://msdn.microsoft.com/en-us/library/mt490614.aspx
+:: The default is c++14 if not set on command line.
+IF "%STD%"=="" SET STD=c++14
+
+:: Set the compilation_mode (fastbuild|opt|dbg)
+:: https://docs.bazel.build/versions/master/user-manual.html#flag--compilation_mode
+:: The default is fastbuild
+IF "%COMPILATION_MODE%"=="" SET COMPILATION_MODE=fastbuild
+
+:: Copy the alternate option file, if specified.
+IF NOT "%ALTERNATE_OPTIONS%"=="" copy %ALTERNATE_OPTIONS% absl\base\options.h
+
+:: To upgrade Bazel, first download a new binary from
+:: https://github.com/bazelbuild/bazel/releases and copy it to
+:: /google/data/rw/teams/absl/kokoro/windows.
+%KOKORO_GFILE_DIR%\bazel-5.1.1-windows-x86_64.exe ^
+  test ... ^
+  --compilation_mode=%COMPILATION_MODE% ^
+  --copt=/WX ^
+  --copt=/std:%STD% ^
+  --define=absl=1 ^
+  --distdir=%KOKORO_GFILE_DIR%\distdir ^
+  --features=external_include_paths ^
+  --keep_going ^
+  --test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" ^
+  --test_env=TZDIR="%CD%\absl\time\internal\cctz\testdata\zoneinfo" ^
+  --test_output=errors ^
+  --test_tag_filters=-benchmark
+
+if %errorlevel% neq 0 EXIT /B 1
+EXIT /B 0
diff --git a/ci/windows_msvc_cmake.bat b/ci/windows_msvc_cmake.bat
new file mode 100755
index 0000000..b69e03e
--- /dev/null
+++ b/ci/windows_msvc_cmake.bat
@@ -0,0 +1,68 @@
+:: Copyright 2023 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.
+
+SETLOCAL ENABLEDELAYEDEXPANSION
+
+SET ABSL_GOOGLETEST_COMMIT=934542165899c786cb5d8a710529c37184730183
+
+IF EXIST %KOKORO_GFILE_DIR%\distdir\%ABSL_GOOGLETEST_COMMIT%.zip (
+  SET ABSL_GOOGLETEST_DOWNLOAD_URL=file://%KOKORO_GFILE_DIR%\distdir\%ABSL_GOOGLETEST_COMMIT%.zip
+) ELSE (
+  SET ABSL_GOOGLETEST_DOWNLOAD_URL=https://github.com/google/googletest/archive/%ABSL_GOOGLETEST_COMMIT%.zip
+)
+
+:: Replace '\' with '/' in Windows paths for CMake.
+:: Note that this cannot go inside the IF block above, because BAT files are weird.
+SET ABSL_GOOGLETEST_DOWNLOAD_URL=%ABSL_GOOGLETEST_DOWNLOAD_URL:\=/%
+
+IF EXIST "C:\Program Files\CMake\bin\" (
+  SET CMAKE_BIN="C:\Program Files\CMake\bin\cmake.exe"
+  SET CTEST_BIN="C:\Program Files\CMake\bin\ctest.exe"
+) ELSE (
+  SET CMAKE_BIN="cmake.exe"
+  SET CTEST_BIN="ctest.exe"
+)
+
+SET CTEST_OUTPUT_ON_FAILURE=1
+SET CMAKE_BUILD_PARALLEL_LEVEL=16
+SET CTEST_PARALLEL_LEVEL=16
+
+:: Change directory to the root of the project.
+CD %~dp0\..
+if %errorlevel% neq 0 EXIT /B 1
+
+SET TZDIR=%CD%\absl\time\internal\cctz\testdata\zoneinfo
+
+MKDIR "build"
+CD "build"
+
+SET CXXFLAGS="/WX"
+
+%CMAKE_BIN% ^
+  -DABSL_BUILD_TEST_HELPERS=ON ^
+  -DABSL_BUILD_TESTING=ON ^
+  -DABSL_GOOGLETEST_DOWNLOAD_URL=%ABSL_GOOGLETEST_DOWNLOAD_URL% ^
+  -DBUILD_SHARED_LIBS=%ABSL_CMAKE_BUILD_SHARED% ^
+  -DCMAKE_CXX_STANDARD=%ABSL_CMAKE_CXX_STANDARD% ^
+  -G "%ABSL_CMAKE_GENERATOR%" ^
+  ..
+IF %errorlevel% neq 0 EXIT /B 1
+
+%CMAKE_BIN% --build . --target ALL_BUILD --config %ABSL_CMAKE_BUILD_TYPE%
+IF %errorlevel% neq 0 EXIT /B 1
+
+%CTEST_BIN% -C %ABSL_CMAKE_BUILD_TYPE% -E "absl_lifetime_test|absl_symbolize_test"
+IF %errorlevel% neq 0 EXIT /B 1
+
+EXIT /B 0
diff --git a/create_lts.py b/create_lts.py
index 5617080..642b884 100755
--- a/create_lts.py
+++ b/create_lts.py
@@ -33,7 +33,7 @@
       values
 
   Raises:
-    Exception: A failure occured
+    Exception: A failure occurred
   """
   f = open(filename, 'r')
   content = f.read()
@@ -62,7 +62,7 @@
     strip_end_tag: the end of the content to be removed
 
   Raises:
-    Exception: A failure occured
+    Exception: A failure occurred
   """
   f = open(filename, 'r')
   content = f.read()