about summary refs log tree commit diff
path: root/absl
diff options
context:
space:
mode:
Diffstat (limited to 'absl')
-rw-r--r--absl/BUILD.bazel7
-rw-r--r--absl/algorithm/container.h2
-rw-r--r--absl/base/BUILD.bazel7
-rw-r--r--absl/base/CMakeLists.txt2
-rw-r--r--absl/base/casts.h51
-rw-r--r--absl/base/internal/direct_mmap.h12
-rw-r--r--absl/base/internal/endian_test.cc20
-rw-r--r--absl/base/internal/identity.h4
-rw-r--r--absl/base/internal/raw_logging.cc18
-rw-r--r--absl/base/internal/raw_logging.h43
-rw-r--r--absl/base/internal/spinlock_wait.h2
-rw-r--r--absl/base/macros.h5
-rw-r--r--absl/base/raw_logging_test.cc29
-rw-r--r--absl/base/thread_annotations.h1
-rw-r--r--absl/compiler_config_setting.bzl39
-rw-r--r--absl/container/BUILD.bazel11
-rw-r--r--absl/container/CMakeLists.txt19
-rw-r--r--absl/container/fixed_array.h294
-rw-r--r--absl/container/fixed_array_exception_safety_test.cc117
-rw-r--r--absl/container/inlined_vector.h36
-rw-r--r--absl/container/inlined_vector_benchmark.cc37
-rw-r--r--absl/container/inlined_vector_test.cc26
-rw-r--r--absl/copts.bzl1
-rw-r--r--absl/debugging/internal/examine_stack.cc6
-rw-r--r--absl/debugging/internal/stacktrace_config.h37
-rw-r--r--absl/memory/BUILD.bazel14
-rw-r--r--absl/memory/CMakeLists.txt19
-rw-r--r--absl/memory/memory.h33
-rw-r--r--absl/memory/memory_exception_safety_test.cc63
-rw-r--r--absl/memory/memory_test.cc43
-rw-r--r--absl/meta/type_traits.h2
-rw-r--r--absl/numeric/int128.cc26
-rw-r--r--absl/numeric/int128.h58
-rw-r--r--absl/numeric/int128_test.cc11
-rw-r--r--absl/strings/BUILD.bazel229
-rw-r--r--absl/strings/CMakeLists.txt159
-rw-r--r--absl/strings/charconv.cc982
-rw-r--r--absl/strings/charconv.h115
-rw-r--r--absl/strings/charconv_benchmark.cc204
-rw-r--r--absl/strings/charconv_test.cc766
-rw-r--r--absl/strings/internal/charconv_bigint.cc357
-rw-r--r--absl/strings/internal/charconv_bigint.h426
-rw-r--r--absl/strings/internal/charconv_bigint_test.cc203
-rw-r--r--absl/strings/internal/charconv_parse.cc496
-rw-r--r--absl/strings/internal/charconv_parse.h96
-rw-r--r--absl/strings/internal/charconv_parse_test.cc357
-rw-r--r--absl/strings/internal/str_format/arg.cc399
-rw-r--r--absl/strings/internal/str_format/arg.h434
-rw-r--r--absl/strings/internal/str_format/arg_test.cc111
-rw-r--r--absl/strings/internal/str_format/bind.cc232
-rw-r--r--absl/strings/internal/str_format/bind.h189
-rw-r--r--absl/strings/internal/str_format/bind_test.cc131
-rw-r--r--absl/strings/internal/str_format/checker.h325
-rw-r--r--absl/strings/internal/str_format/checker_test.cc150
-rw-r--r--absl/strings/internal/str_format/convert_test.cc575
-rw-r--r--absl/strings/internal/str_format/extension.cc84
-rw-r--r--absl/strings/internal/str_format/extension.h406
-rw-r--r--absl/strings/internal/str_format/extension_test.cc65
-rw-r--r--absl/strings/internal/str_format/float_conversion.cc476
-rw-r--r--absl/strings/internal/str_format/float_conversion.h21
-rw-r--r--absl/strings/internal/str_format/output.cc47
-rw-r--r--absl/strings/internal/str_format/output.h101
-rw-r--r--absl/strings/internal/str_format/output_test.cc78
-rw-r--r--absl/strings/internal/str_format/parser.cc294
-rw-r--r--absl/strings/internal/str_format/parser.h291
-rw-r--r--absl/strings/internal/str_format/parser_test.cc379
-rw-r--r--absl/strings/internal/str_split_internal.h26
-rw-r--r--absl/strings/numbers.cc86
-rw-r--r--absl/strings/numbers_benchmark.cc263
-rw-r--r--absl/strings/str_format.h512
-rw-r--r--absl/strings/str_format_test.cc603
-rw-r--r--absl/strings/str_split.h10
-rw-r--r--absl/strings/str_split_test.cc28
-rw-r--r--absl/synchronization/BUILD.bazel7
-rw-r--r--absl/synchronization/mutex.h4
-rw-r--r--absl/synchronization/mutex_test.cc575
-rw-r--r--absl/time/BUILD.bazel4
-rw-r--r--absl/time/CMakeLists.txt2
-rw-r--r--absl/time/clock_test.cc94
-rw-r--r--absl/time/duration.cc3
-rw-r--r--absl/time/format.cc15
-rw-r--r--absl/time/internal/cctz/include/cctz/civil_time_detail.h16
-rw-r--r--absl/time/internal/cctz/include/cctz/time_zone.h116
-rw-r--r--absl/time/internal/cctz/include/cctz/zone_info_source.h5
-rw-r--r--absl/time/internal/cctz/src/cctz_benchmark.cc14
-rw-r--r--absl/time/internal/cctz/src/time_zone_fixed.cc12
-rw-r--r--absl/time/internal/cctz/src/time_zone_fixed.h6
-rw-r--r--absl/time/internal/cctz/src/time_zone_format.cc15
-rw-r--r--absl/time/internal/cctz/src/time_zone_format_test.cc302
-rw-r--r--absl/time/internal/cctz/src/time_zone_if.h28
-rw-r--r--absl/time/internal/cctz/src/time_zone_impl.cc13
-rw-r--r--absl/time/internal/cctz/src/time_zone_impl.h41
-rw-r--r--absl/time/internal/cctz/src/time_zone_info.cc108
-rw-r--r--absl/time/internal/cctz/src/time_zone_info.h12
-rw-r--r--absl/time/internal/cctz/src/time_zone_libc.cc18
-rw-r--r--absl/time/internal/cctz/src/time_zone_libc.h9
-rw-r--r--absl/time/internal/cctz/src/time_zone_lookup.cc37
-rw-r--r--absl/time/internal/cctz/src/time_zone_lookup_test.cc348
-rw-r--r--absl/time/internal/cctz/src/zone_info_source.cc11
-rw-r--r--absl/time/time.cc13
-rw-r--r--absl/time/time.h8
-rw-r--r--absl/time/time_zone_test.cc2
-rw-r--r--absl/types/BUILD.bazel19
-rw-r--r--absl/types/bad_any_cast.cc4
-rw-r--r--absl/types/bad_any_cast.h14
-rw-r--r--absl/types/bad_optional_access.cc4
-rw-r--r--absl/types/bad_optional_access.h14
-rw-r--r--absl/types/bad_variant_access.cc4
-rw-r--r--absl/types/bad_variant_access.h14
-rw-r--r--absl/types/internal/variant.h362
-rw-r--r--absl/types/optional.h2
-rw-r--r--absl/types/variant.h75
-rw-r--r--absl/types/variant_benchmark.cc220
113 files changed, 13216 insertions, 1155 deletions
diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel
index 439addbfc828..edd0274c5d83 100644
--- a/absl/BUILD.bazel
+++ b/absl/BUILD.bazel
@@ -18,11 +18,10 @@ package(default_visibility = ["//visibility:public"])
 
 licenses(["notice"])  # Apache 2.0
 
-config_setting(
+load(":compiler_config_setting.bzl", "create_llvm_config")
+
+create_llvm_config(
     name = "llvm_compiler",
-    values = {
-        "compiler": "llvm",
-    },
     visibility = [":__subpackages__"],
 )
 
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h
index ebe3244555fa..acddec484b0a 100644
--- a/absl/algorithm/container.h
+++ b/absl/algorithm/container.h
@@ -634,7 +634,7 @@ container_algorithm_internal::ContainerIter<C> c_generate_n(C& c, Size n,
 
 // Note: `c_xx()` <algorithm> container versions for `remove()`, `remove_if()`,
 // and `unique()` are omitted, because it's not clear whether or not such
-// functions should call erase their supplied sequences afterwards. Either
+// functions should call erase on their supplied sequences afterwards. Either
 // behavior would be surprising for a different set of users.
 //
 
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index 1e93d97ebd40..06d092ebdfa8 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -362,6 +362,7 @@ cc_test(
     copts = ABSL_TEST_COPTS,
     deps = [
         ":base",
+        "//absl/strings",
         "@com_google_googletest//:gtest_main",
     ],
 )
@@ -371,11 +372,6 @@ cc_test(
     size = "small",
     srcs = ["internal/sysinfo_test.cc"],
     copts = ABSL_TEST_COPTS,
-    tags = [
-        "no_test_android_arm",
-        "no_test_android_arm64",
-        "no_test_android_x86",
-    ],
     deps = [
         ":base",
         "//absl/synchronization",
@@ -392,6 +388,7 @@ cc_test(
         "//absl:windows": [],
         "//conditions:default": ["-pthread"],
     }),
+    tags = ["no_test_ios_x86_64"],
     deps = [":malloc_internal"],
 )
 
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index 303533e26d1b..01d2af085f58 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -310,7 +310,7 @@ absl_test(
 
 # test raw_logging_test
 set(RAW_LOGGING_TEST_SRC "raw_logging_test.cc")
-set(RAW_LOGGING_TEST_PUBLIC_LIBRARIES absl::base)
+set(RAW_LOGGING_TEST_PUBLIC_LIBRARIES absl::base absl::strings)
 
 absl_test(
   TARGET
diff --git a/absl/base/casts.h b/absl/base/casts.h
index 8bd5264d9780..20fd34da7010 100644
--- a/absl/base/casts.h
+++ b/absl/base/casts.h
@@ -25,12 +25,36 @@
 #define ABSL_BASE_CASTS_H_
 
 #include <cstring>
+#include <memory>
 #include <type_traits>
 
 #include "absl/base/internal/identity.h"
+#include "absl/base/macros.h"
 
 namespace absl {
 
+namespace internal_casts {
+
+// NOTE: Not a fully compliant implementation of `std::is_trivially_copyable`.
+// TODO(calabrese) Branch on implementations that directly provide
+// `std::is_trivially_copyable`, create a more rigorous workaround, and publicly
+// expose in meta/type_traits.
+template <class T>
+struct is_trivially_copyable
+    : std::integral_constant<
+          bool, std::is_destructible<T>::value&& __has_trivial_destructor(T) &&
+                    __has_trivial_copy(T) && __has_trivial_assign(T)> {};
+
+template <class Dest, class Source>
+struct is_bitcastable
+    : std::integral_constant<bool,
+                             sizeof(Dest) == sizeof(Source) &&
+                                 is_trivially_copyable<Source>::value &&
+                                 is_trivially_copyable<Dest>::value &&
+                                 std::is_default_constructible<Dest>::value> {};
+
+}  // namespace internal_casts
+
 // implicit_cast()
 //
 // Performs an implicit conversion between types following the language
@@ -125,7 +149,32 @@ inline To implicit_cast(typename absl::internal::identity_t<To> to) {
 // and reading its bits back using a different type. A `bit_cast()` avoids this
 // issue by implementing its casts using `memcpy()`, which avoids introducing
 // this undefined behavior.
-template <typename Dest, typename Source>
+//
+// NOTE: The requirements here are more strict than the bit_cast of standard
+// proposal p0476 due to the need for workarounds and lack of intrinsics.
+// Specifically, this implementation also requires `Dest` to be
+// default-constructible.
+template <
+    typename Dest, typename Source,
+    typename std::enable_if<internal_casts::is_bitcastable<Dest, Source>::value,
+                            int>::type = 0>
+inline Dest bit_cast(const Source& source) {
+  Dest dest;
+  memcpy(static_cast<void*>(std::addressof(dest)),
+         static_cast<const void*>(std::addressof(source)), sizeof(dest));
+  return dest;
+}
+
+// NOTE: This overload is only picked if the requirements of bit_cast are not
+// met. It is therefore UB, but is provided temporarily as previous versions of
+// this function template were unchecked. Do not use this in new code.
+template <
+    typename Dest, typename Source,
+    typename std::enable_if<
+        !internal_casts::is_bitcastable<Dest, Source>::value, int>::type = 0>
+ABSL_DEPRECATED(
+    "absl::bit_cast type requirements were violated. Update the types being "
+    "used such that they are the same size and are both TriviallyCopyable.")
 inline Dest bit_cast(const Source& source) {
   static_assert(sizeof(Dest) == sizeof(Source),
                 "Source and destination types should have equal sizes.");
diff --git a/absl/base/internal/direct_mmap.h b/absl/base/internal/direct_mmap.h
index 2fe345fc85aa..0426e11890b6 100644
--- a/absl/base/internal/direct_mmap.h
+++ b/absl/base/internal/direct_mmap.h
@@ -92,11 +92,13 @@ inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd,
 #endif
 #elif defined(__s390x__)
   // On s390x, mmap() arguments are passed in memory.
-  uint32_t buf[6] = {
-      reinterpret_cast<uint32_t>(start), static_cast<uint32_t>(length),
-      static_cast<uint32_t>(prot),       static_cast<uint32_t>(flags),
-      static_cast<uint32_t>(fd),         static_cast<uint32_t>(offset)};
-  return reintrepret_cast<void*>(syscall(SYS_mmap, buf));
+  unsigned long buf[6] = {reinterpret_cast<unsigned long>(start),  // NOLINT
+                          static_cast<unsigned long>(length),      // NOLINT
+                          static_cast<unsigned long>(prot),        // NOLINT
+                          static_cast<unsigned long>(flags),       // NOLINT
+                          static_cast<unsigned long>(fd),          // NOLINT
+                          static_cast<unsigned long>(offset)};     // NOLINT
+  return reinterpret_cast<void*>(syscall(SYS_mmap, buf));
 #elif defined(__x86_64__)
 // The x32 ABI has 32 bit longs, but the syscall interface is 64 bit.
 // We need to explicitly cast to an unsigned 64 bit type to avoid implicit
diff --git a/absl/base/internal/endian_test.cc b/absl/base/internal/endian_test.cc
index f3ff4b39bb65..e27691553bc6 100644
--- a/absl/base/internal/endian_test.cc
+++ b/absl/base/internal/endian_test.cc
@@ -33,32 +33,16 @@ const uint16_t k16Value{0x0123};
 const int kNumValuesToTest = 1000000;
 const int kRandomSeed = 12345;
 
-#ifdef ABSL_IS_BIG_ENDIAN
+#if defined(ABSL_IS_BIG_ENDIAN)
 const uint64_t kInitialInNetworkOrder{kInitialNumber};
 const uint64_t k64ValueLE{0xefcdab8967452301};
 const uint32_t k32ValueLE{0x67452301};
 const uint16_t k16ValueLE{0x2301};
-const uint8_t k8ValueLE{k8Value};
-const uint64_t k64IValueLE{0xefcdab89674523a1};
-const uint32_t k32IValueLE{0x67452391};
-const uint16_t k16IValueLE{0x85ff};
-const uint8_t k8IValueLE{0xff};
-const uint64_t kDoubleValueLE{0x6e861bf0f9210940};
-const uint32_t kFloatValueLE{0xd00f4940};
-const uint8_t kBoolValueLE{0x1};
 
 const uint64_t k64ValueBE{kInitialNumber};
 const uint32_t k32ValueBE{k32Value};
 const uint16_t k16ValueBE{k16Value};
-const uint8_t k8ValueBE{k8Value};
-const uint64_t k64IValueBE{0xa123456789abcdef};
-const uint32_t k32IValueBE{0x91234567};
-const uint16_t k16IValueBE{0xff85};
-const uint8_t k8IValueBE{0xff};
-const uint64_t kDoubleValueBE{0x400921f9f01b866e};
-const uint32_t kFloatValueBE{0x40490fd0};
-const uint8_t kBoolValueBE{0x1};
-#elif defined ABSL_IS_LITTLE_ENDIAN
+#elif defined(ABSL_IS_LITTLE_ENDIAN)
 const uint64_t kInitialInNetworkOrder{0xefcdab8967452301};
 const uint64_t k64ValueLE{kInitialNumber};
 const uint32_t k32ValueLE{k32Value};
diff --git a/absl/base/internal/identity.h b/absl/base/internal/identity.h
index a6734b4d3537..a1a5d70a84dd 100644
--- a/absl/base/internal/identity.h
+++ b/absl/base/internal/identity.h
@@ -27,7 +27,7 @@ struct identity {
 template <typename T>
 using identity_t = typename identity<T>::type;
 
-}  //  namespace internal
-}  //  namespace absl
+}  // namespace internal
+}  // namespace absl
 
 #endif  // ABSL_BASE_INTERNAL_IDENTITY_H_
diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc
index 1ce138887252..d9485a66cc64 100644
--- a/absl/base/internal/raw_logging.cc
+++ b/absl/base/internal/raw_logging.cc
@@ -139,7 +139,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line,
 #endif
 
 #ifdef ABSL_MIN_LOG_LEVEL
-  if (static_cast<int>(severity) < ABSL_MIN_LOG_LEVEL &&
+  if (severity < static_cast<absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) &&
       severity < absl::LogSeverity::kFatal) {
     enabled = false;
   }
@@ -206,6 +206,15 @@ void RawLog(absl::LogSeverity severity, const char* file, int line,
   va_end(ap);
 }
 
+// Non-formatting version of RawLog().
+//
+// TODO(gfalcon): When string_view no longer depends on base, change this
+// interface to take its message as a string_view instead.
+static void DefaultInternalLog(absl::LogSeverity severity, const char* file,
+                               int line, const std::string& message) {
+  RawLog(severity, file, line, "%s", message.c_str());
+}
+
 bool RawLoggingFullySupported() {
 #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
   return true;
@@ -214,5 +223,12 @@ bool RawLoggingFullySupported() {
 #endif  // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
 }
 
+ABSL_CONST_INIT absl::base_internal::AtomicHook<InternalLogFunction>
+    internal_log_function(DefaultInternalLog);
+
+void RegisterInternalLogFunction(InternalLogFunction func) {
+  internal_log_function.Store(func);
+}
+
 }  // namespace raw_logging_internal
 }  // namespace absl
diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h
index a2b7207a032c..67abfd30798d 100644
--- a/absl/base/internal/raw_logging.h
+++ b/absl/base/internal/raw_logging.h
@@ -19,7 +19,10 @@
 #ifndef ABSL_BASE_INTERNAL_RAW_LOGGING_H_
 #define ABSL_BASE_INTERNAL_RAW_LOGGING_H_
 
+#include <string>
+
 #include "absl/base/attributes.h"
+#include "absl/base/internal/atomic_hook.h"
 #include "absl/base/log_severity.h"
 #include "absl/base/macros.h"
 #include "absl/base/port.h"
@@ -57,6 +60,34 @@
     }                                                                  \
   } while (0)
 
+// ABSL_INTERNAL_LOG and ABSL_INTERNAL_CHECK work like the RAW variants above,
+// except that if the richer log library is linked into the binary, we dispatch
+// to that instead.  This is potentially useful for internal logging and
+// assertions, where we are using RAW_LOG neither for its async-signal-safety
+// nor for its non-allocating nature, but rather because raw logging has very
+// few other dependencies.
+//
+// The API is a subset of the above: each macro only takes two arguments.  Use
+// StrCat if you need to build a richer message.
+#define ABSL_INTERNAL_LOG(severity, message)                          \
+  do {                                                                \
+    constexpr const char* absl_raw_logging_internal_basename =        \
+        ::absl::raw_logging_internal::Basename(__FILE__,              \
+                                               sizeof(__FILE__) - 1); \
+    ::absl::raw_logging_internal::internal_log_function(              \
+        ABSL_RAW_LOGGING_INTERNAL_##severity,                         \
+        absl_raw_logging_internal_basename, __LINE__, message);       \
+  } while (0)
+
+#define ABSL_INTERNAL_CHECK(condition, message)               \
+  do {                                                        \
+    if (ABSL_PREDICT_FALSE(!(condition))) {                   \
+      std::string death_message = "Check " #condition " failed: "; \
+      death_message += std::string(message);                       \
+      ABSL_INTERNAL_LOG(FATAL, death_message);                \
+    }                                                         \
+  } while (0)
+
 #define ABSL_RAW_LOGGING_INTERNAL_INFO ::absl::LogSeverity::kInfo
 #define ABSL_RAW_LOGGING_INTERNAL_WARNING ::absl::LogSeverity::kWarning
 #define ABSL_RAW_LOGGING_INTERNAL_ERROR ::absl::LogSeverity::kError
@@ -131,6 +162,18 @@ using LogPrefixHook = bool (*)(absl::LogSeverity severity, const char* file,
 using AbortHook = void (*)(const char* file, int line, const char* buf_start,
                            const char* prefix_end, const char* buf_end);
 
+// Internal logging function for ABSL_INTERNAL_LOG to dispatch to.
+//
+// TODO(gfalcon): When string_view no longer depends on base, change this
+// interface to take its message as a string_view instead.
+using InternalLogFunction = void (*)(absl::LogSeverity severity,
+                                     const char* file, int line,
+                                     const std::string& message);
+
+extern base_internal::AtomicHook<InternalLogFunction> internal_log_function;
+
+void RegisterInternalLogFunction(InternalLogFunction func);
+
 }  // namespace raw_logging_internal
 }  // namespace absl
 
diff --git a/absl/base/internal/spinlock_wait.h b/absl/base/internal/spinlock_wait.h
index 5f658211cc56..5c6cc7fdba4c 100644
--- a/absl/base/internal/spinlock_wait.h
+++ b/absl/base/internal/spinlock_wait.h
@@ -84,7 +84,7 @@ inline void absl::base_internal::SpinLockWake(std::atomic<uint32_t> *w,
 
 inline void absl::base_internal::SpinLockDelay(
     std::atomic<uint32_t> *w, uint32_t value, int loop,
-    base_internal::SchedulingMode scheduling_mode) {
+    absl::base_internal::SchedulingMode scheduling_mode) {
   AbslInternalSpinLockDelay(w, value, loop, scheduling_mode);
 }
 
diff --git a/absl/base/macros.h b/absl/base/macros.h
index afa30300bc3e..ca3d5edb6536 100644
--- a/absl/base/macros.h
+++ b/absl/base/macros.h
@@ -194,8 +194,9 @@ enum LinkerInitialized {
 #if defined(NDEBUG)
 #define ABSL_ASSERT(expr) (false ? (void)(expr) : (void)0)
 #else
-#define ABSL_ASSERT(expr) \
-  (ABSL_PREDICT_TRUE((expr)) ? (void)0 : [] { assert(false && #expr); }())
+#define ABSL_ASSERT(expr)              \
+  (ABSL_PREDICT_TRUE((expr)) ? (void)0 \
+                             : [] { assert(false && #expr); }())  // NOLINT
 #endif
 
 #endif  // ABSL_BASE_MACROS_H_
diff --git a/absl/base/raw_logging_test.cc b/absl/base/raw_logging_test.cc
index dae4b35138c5..ebbc5db90672 100644
--- a/absl/base/raw_logging_test.cc
+++ b/absl/base/raw_logging_test.cc
@@ -18,12 +18,20 @@
 
 #include "absl/base/internal/raw_logging.h"
 
+#include <tuple>
+
 #include "gtest/gtest.h"
+#include "absl/strings/str_cat.h"
 
 namespace {
 
 TEST(RawLoggingCompilationTest, Log) {
   ABSL_RAW_LOG(INFO, "RAW INFO: %d", 1);
+  ABSL_RAW_LOG(INFO, "RAW INFO: %d %d", 1, 2);
+  ABSL_RAW_LOG(INFO, "RAW INFO: %d %d %d", 1, 2, 3);
+  ABSL_RAW_LOG(INFO, "RAW INFO: %d %d %d %d", 1, 2, 3, 4);
+  ABSL_RAW_LOG(INFO, "RAW INFO: %d %d %d %d %d", 1, 2, 3, 4, 5);
+  ABSL_RAW_LOG(WARNING, "RAW WARNING: %d", 1);
   ABSL_RAW_LOG(ERROR, "RAW ERROR: %d", 1);
 }
 
@@ -47,4 +55,25 @@ TEST(RawLoggingDeathTest, LogFatal) {
                             kExpectedDeathOutput);
 }
 
+TEST(InternalLog, CompilationTest) {
+  ABSL_INTERNAL_LOG(INFO, "Internal Log");
+  std::string log_msg = "Internal Log";
+  ABSL_INTERNAL_LOG(INFO, log_msg);
+
+  ABSL_INTERNAL_LOG(INFO, log_msg + " 2");
+
+  float d = 1.1f;
+  ABSL_INTERNAL_LOG(INFO, absl::StrCat("Internal log ", 3, " + ", d));
+}
+
+TEST(InternalLogDeathTest, FailingCheck) {
+  EXPECT_DEATH_IF_SUPPORTED(ABSL_INTERNAL_CHECK(1 == 0, "explanation"),
+                            kExpectedDeathOutput);
+}
+
+TEST(InternalLogDeathTest, LogFatal) {
+  EXPECT_DEATH_IF_SUPPORTED(ABSL_INTERNAL_LOG(FATAL, "my dog has fleas"),
+                            kExpectedDeathOutput);
+}
+
 }  // namespace
diff --git a/absl/base/thread_annotations.h b/absl/base/thread_annotations.h
index 8d30b9324b52..fbb2797b825f 100644
--- a/absl/base/thread_annotations.h
+++ b/absl/base/thread_annotations.h
@@ -31,7 +31,6 @@
 // that evaluate to a concrete mutex object whenever possible. If the mutex
 // you want to refer to is not in scope, you may use a member pointer
 // (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object.
-//
 
 #ifndef ABSL_BASE_THREAD_ANNOTATIONS_H_
 #define ABSL_BASE_THREAD_ANNOTATIONS_H_
diff --git a/absl/compiler_config_setting.bzl b/absl/compiler_config_setting.bzl
new file mode 100644
index 000000000000..b77c4f563b95
--- /dev/null
+++ b/absl/compiler_config_setting.bzl
@@ -0,0 +1,39 @@
+#
+# 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
+#
+#      http://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.
+#
+
+"""Creates config_setting that allows selecting based on 'compiler' value."""
+
+def create_llvm_config(name, visibility):
+  # The "do_not_use_tools_cpp_compiler_present" attribute exists to
+  # distinguish between older versions of Bazel that do not support
+  # "@bazel_tools//tools/cpp:compiler" flag_value, and newer ones that do.
+  # In the future, the only way to select on the compiler will be through
+  # flag_values{"@bazel_tools//tools/cpp:compiler"} and the else branch can
+  # be removed.
+  if hasattr(cc_common, "do_not_use_tools_cpp_compiler_present"):
+    native.config_setting(
+      name = name,
+      flag_values = {
+          "@bazel_tools//tools/cpp:compiler": "llvm",
+      },
+      visibility = visibility,
+    )
+  else:
+    native.config_setting(
+        name = name,
+        values = {"compiler": "llvm"},
+        visibility = visibility,
+    )
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index 119d5c88de2a..07df3675207e 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -63,6 +63,17 @@ cc_test(
 )
 
 cc_test(
+    name = "fixed_array_exception_safety_test",
+    srcs = ["fixed_array_exception_safety_test.cc"],
+    copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+    deps = [
+        ":fixed_array",
+        "//absl/base:exception_safety_testing",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
     name = "fixed_array_benchmark",
     srcs = ["fixed_array_benchmark.cc"],
     copts = ABSL_TEST_COPTS + ["$(STACK_FRAME_UNLIMITED)"],
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt
index f56ce92d81b5..d580b48976cb 100644
--- a/absl/container/CMakeLists.txt
+++ b/absl/container/CMakeLists.txt
@@ -84,6 +84,25 @@ absl_test(
 )
 
 
+# test fixed_array_exception_safety_test
+set(FIXED_ARRAY_EXCEPTION_SAFETY_TEST_SRC "fixed_array_exception_safety_test.cc")
+set(FIXED_ARRAY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES
+  absl::container
+  absl_base_internal_exception_safety_testing
+)
+
+absl_test(
+  TARGET
+    fixed_array_exception_safety_test
+  SOURCES
+    ${FIXED_ARRAY_EXCEPTION_SAFETY_TEST_SRC}
+  PUBLIC_LIBRARIES
+    ${FIXED_ARRAY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES}
+  PRIVATE_COMPILE_FLAGS
+    ${ABSL_EXCEPTIONS_FLAG}
+)
+
+
 # test inlined_vector_test
 set(INLINED_VECTOR_TEST_SRC "inlined_vector_test.cc")
 set(INLINED_VECTOR_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate test_instance_tracker_lib)
diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h
index 06bc80095c98..62600df05be3 100644
--- a/absl/container/fixed_array.h
+++ b/absl/container/fixed_array.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Abseil Authors.
+// 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.
@@ -57,13 +57,13 @@ constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
 // FixedArray
 // -----------------------------------------------------------------------------
 //
-// A `FixedArray` provides a run-time fixed-size array, allocating small arrays
-// inline for efficiency and correctness.
+// 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
+// `FixedArray` automatically determine the number of elements
 // to store inline based on `sizeof(T)`. If `inline_elements` is specified, the
-// `FixedArray<>` implementation will inline arrays of
+// `FixedArray` implementation will use inline storage for arrays with a
 // length <= `inline_elements`.
 //
 // Note that a `FixedArray` constructed with a `size_type` argument will
@@ -78,19 +78,18 @@ constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
 // operators.
 template <typename T, size_t inlined = kFixedArrayUseDefault>
 class FixedArray {
+  static_assert(!std::is_array<T>::value || std::extent<T>::value > 0,
+                "Arrays with unknown bounds cannot be used with FixedArray.");
   static constexpr size_t kInlineBytesDefault = 256;
 
   // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17,
   // but this seems to be mostly pedantic.
-  template <typename Iter>
-  using EnableIfForwardIterator = typename std::enable_if<
-      std::is_convertible<
-          typename std::iterator_traits<Iter>::iterator_category,
-          std::forward_iterator_tag>::value,
-      int>::type;
+  template <typename Iterator>
+  using EnableIfForwardIterator = absl::enable_if_t<std::is_convertible<
+      typename std::iterator_traits<Iterator>::iterator_category,
+      std::forward_iterator_tag>::value>;
 
  public:
-  // For playing nicely with stl:
   using value_type = T;
   using iterator = T*;
   using const_iterator = const T*;
@@ -108,33 +107,44 @@ class FixedArray {
           ? kInlineBytesDefault / sizeof(value_type)
           : inlined;
 
-  FixedArray(const FixedArray& other) : rep_(other.begin(), other.end()) {}
+  FixedArray(const FixedArray& other)
+      : FixedArray(other.begin(), other.end()) {}
+
   FixedArray(FixedArray&& other) noexcept(
-  // clang-format off
-      absl::allocator_is_nothrow<std::allocator<value_type>>::value &&
-  // clang-format on
-          std::is_nothrow_move_constructible<value_type>::value)
-      : rep_(std::make_move_iterator(other.begin()),
-             std::make_move_iterator(other.end())) {}
+      absl::conjunction<absl::allocator_is_nothrow<std::allocator<value_type>>,
+                        std::is_nothrow_move_constructible<value_type>>::value)
+      : FixedArray(std::make_move_iterator(other.begin()),
+                   std::make_move_iterator(other.end())) {}
 
   // Creates an array object that can store `n` elements.
   // Note that trivially constructible elements will be uninitialized.
-  explicit FixedArray(size_type n) : rep_(n) {}
+  explicit FixedArray(size_type n) : storage_(n) {
+    absl::memory_internal::uninitialized_default_construct_n(storage_.begin(),
+                                                             size());
+  }
 
   // Creates an array initialized with `n` copies of `val`.
-  FixedArray(size_type n, const value_type& val) : rep_(n, val) {}
+  FixedArray(size_type n, const value_type& val) : storage_(n) {
+    std::uninitialized_fill_n(data(), size(), val);
+  }
 
   // Creates an array initialized with the elements from the input
   // range. The array's size will always be `std::distance(first, last)`.
-  // REQUIRES: Iter must be a forward_iterator or better.
-  template <typename Iter, EnableIfForwardIterator<Iter> = 0>
-  FixedArray(Iter first, Iter last) : rep_(first, last) {}
+  // REQUIRES: Iterator must be a forward_iterator or better.
+  template <typename Iterator, EnableIfForwardIterator<Iterator>* = nullptr>
+  FixedArray(Iterator first, Iterator last)
+      : storage_(std::distance(first, last)) {
+    std::uninitialized_copy(first, last, data());
+  }
 
-  // Creates the array from an initializer_list.
-  FixedArray(std::initializer_list<T> init_list)
+  FixedArray(std::initializer_list<value_type> init_list)
       : FixedArray(init_list.begin(), init_list.end()) {}
 
-  ~FixedArray() {}
+  ~FixedArray() noexcept {
+    for (const StorageElement& cur : storage_) {
+      cur.~StorageElement();
+    }
+  }
 
   // Assignments are deleted because they break the invariant that the size of a
   // `FixedArray` never changes.
@@ -144,7 +154,7 @@ class FixedArray {
   // FixedArray::size()
   //
   // Returns the length of the fixed array.
-  size_type size() const { return rep_.size(); }
+  size_type size() const { return storage_.size(); }
 
   // FixedArray::max_size()
   //
@@ -169,12 +179,12 @@ class FixedArray {
   //
   // 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 AsValue(rep_.begin()); }
+  const_pointer data() const { 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 AsValue(rep_.begin()); }
+  pointer data() { return AsValueType(storage_.begin()); }
 
   // FixedArray::operator[]
   //
@@ -294,7 +304,7 @@ class FixedArray {
   // FixedArray::fill()
   //
   // Assigns the given `value` to all elements in the fixed array.
-  void fill(const T& value) { std::fill(begin(), end(), value); }
+  void fill(const value_type& val) { std::fill(begin(), end(), val); }
 
   // Relational operators. Equality operators are elementwise using
   // `operator==`, while order operators order FixedArrays lexicographically.
@@ -324,17 +334,18 @@ class FixedArray {
   }
 
  private:
-  // HolderTraits
+  // StorageElement
   //
-  // Wrapper to hold elements of type T for the case where T is an array type.
-  // If 'T' is an array type, HolderTraits::type is a struct with a 'T v;'.
-  // Otherwise, HolderTraits::type is simply 'T'.
+  // For FixedArrays with a C-style-array value_type, StorageElement is a POD
+  // wrapper struct called StorageElementWrapper that holds the value_type
+  // instance inside. This is needed for construction and destruction of the
+  // entire array regardless of how many dimensions it has. For all other cases,
+  // StorageElement is just an alias of value_type.
   //
-  // Maintainer's Note: The simpler solution would be to simply wrap T in a
-  // struct whether it's an array or not: 'struct Holder { T v; };', but
-  // that causes some paranoid diagnostics to misfire about uses of data(),
-  // believing that 'data()' (aka '&rep_.begin().v') is a pointer to a single
-  // element, rather than the packed array that it really is.
+  // Maintainer's Note: The simpler solution would be to simply wrap value_type
+  // in a struct whether it's an array or not. That causes some paranoid
+  // diagnostics to misfire, believing that 'data()' returns a pointer to a
+  // single element, rather than the packed array that it really is.
   // e.g.:
   //
   //     FixedArray<char> buf(1);
@@ -343,149 +354,98 @@ class FixedArray {
   //     error: call to int __builtin___sprintf_chk(etc...)
   //     will always overflow destination buffer [-Werror]
   //
-  class HolderTraits {
-    template <typename U>
-    struct SelectImpl {
-      using type = U;
-      static pointer AsValue(type* p) { return p; }
-    };
-
-    // Partial specialization for elements of array type.
-    template <typename U, size_t N>
-    struct SelectImpl<U[N]> {
-      struct Holder { U v[N]; };
-      using type = Holder;
-      static pointer AsValue(type* p) { return &p->v; }
-    };
-    using Impl = SelectImpl<value_type>;
-
-   public:
-    using type = typename Impl::type;
-
-    static pointer AsValue(type *p) { return Impl::AsValue(p); }
-
-    // TODO(billydonahue): fix the type aliasing violation
-    // this assertion hints at.
-    static_assert(sizeof(type) == sizeof(value_type),
-                  "Holder must be same size as value_type");
+  template <typename OuterT = value_type,
+            typename InnerT = absl::remove_extent_t<OuterT>,
+            size_t InnerN = std::extent<OuterT>::value>
+  struct StorageElementWrapper {
+    InnerT array[InnerN];
   };
 
-  using Holder = typename HolderTraits::type;
-  static pointer AsValue(Holder *p) { return HolderTraits::AsValue(p); }
+  using StorageElement =
+      absl::conditional_t<std::is_array<value_type>::value,
+                          StorageElementWrapper<value_type>, value_type>;
 
-  // InlineSpace
-  //
-  // Allocate some space, not an array of elements of type T, so that we can
-  // skip calling the T constructors and destructors for space we never use.
-  // How many elements should we store inline?
-  //   a. If not specified, use a default of kInlineBytesDefault bytes (This is
-  //   currently 256 bytes, which seems small enough to not cause stack overflow
-  //   or unnecessary stack pollution, while still allowing stack allocation for
-  //   reasonably long character arrays).
-  //   b. Never use 0 length arrays (not ISO C++)
-  //
-  template <size_type N, typename = void>
-  class InlineSpace {
-   public:
-    Holder* data() { return reinterpret_cast<Holder*>(space_.data()); }
-    void AnnotateConstruct(size_t n) const { Annotate(n, true); }
-    void AnnotateDestruct(size_t n) const { Annotate(n, false); }
+  static pointer AsValueType(pointer ptr) { return ptr; }
+  static pointer AsValueType(StorageElementWrapper<value_type>* ptr) {
+    return std::addressof(ptr->array);
+  }
 
-   private:
-#ifndef ADDRESS_SANITIZER
-    void Annotate(size_t, bool) const { }
-#else
-    void Annotate(size_t n, bool creating) const {
-      if (!n) return;
-      const void* bot = &left_redzone_;
-      const void* beg = space_.data();
-      const void* end = space_.data() + n;
-      const void* top = &right_redzone_ + 1;
-      // args: (beg, end, old_mid, new_mid)
-      if (creating) {
-        ANNOTATE_CONTIGUOUS_CONTAINER(beg, top, top, end);
-        ANNOTATE_CONTIGUOUS_CONTAINER(bot, beg, beg, bot);
-      } else {
-        ANNOTATE_CONTIGUOUS_CONTAINER(beg, top, end, top);
-        ANNOTATE_CONTIGUOUS_CONTAINER(bot, beg, bot, beg);
-      }
+  static_assert(sizeof(StorageElement) == sizeof(value_type), "");
+  static_assert(alignof(StorageElement) == alignof(value_type), "");
+
+  struct NonEmptyInlinedStorage {
+    using StorageElementBuffer =
+        absl::aligned_storage_t<sizeof(StorageElement),
+                                alignof(StorageElement)>;
+    StorageElement* data() {
+      return reinterpret_cast<StorageElement*>(inlined_storage_.data());
     }
+
+#ifdef ADDRESS_SANITIZER
+    void* RedzoneBegin() { return &redzone_begin_; }
+    void* RedzoneEnd() { return &redzone_end_ + 1; }
 #endif  // ADDRESS_SANITIZER
 
-    using Buffer =
-        typename std::aligned_storage<sizeof(Holder), alignof(Holder)>::type;
+    void AnnotateConstruct(size_t);
+    void AnnotateDestruct(size_t);
 
-    ADDRESS_SANITIZER_REDZONE(left_redzone_);
-    std::array<Buffer, N> space_;
-    ADDRESS_SANITIZER_REDZONE(right_redzone_);
+    ADDRESS_SANITIZER_REDZONE(redzone_begin_);
+    std::array<StorageElementBuffer, inline_elements> inlined_storage_;
+    ADDRESS_SANITIZER_REDZONE(redzone_end_);
   };
 
-  // specialization when N = 0.
-  template <typename U>
-  class InlineSpace<0, U> {
-   public:
-    Holder* data() { return nullptr; }
-    void AnnotateConstruct(size_t) const {}
-    void AnnotateDestruct(size_t) const {}
+  struct EmptyInlinedStorage {
+    StorageElement* data() { return nullptr; }
+    void AnnotateConstruct(size_t) {}
+    void AnnotateDestruct(size_t) {}
   };
 
-  // Rep
+  using InlinedStorage =
+      absl::conditional_t<inline_elements == 0, EmptyInlinedStorage,
+                          NonEmptyInlinedStorage>;
+
+  // Storage
   //
-  // A const Rep object holds FixedArray's size and data pointer.
+  // An instance of Storage manages the inline and out-of-line memory for
+  // instances of FixedArray. This guarantees that even when construction of
+  // individual elements fails in the FixedArray constructor body, the
+  // destructor for Storage will still be called and out-of-line memory will be
+  // properly deallocated.
   //
-  class Rep : public InlineSpace<inline_elements> {
+  class Storage : public InlinedStorage {
    public:
-    Rep(size_type n, const value_type& val) : n_(n), p_(MakeHolder(n)) {
-      std::uninitialized_fill_n(p_, n, val);
-    }
-
-    explicit Rep(size_type n) : n_(n), p_(MakeHolder(n)) {
-      // Loop optimizes to nothing for trivially constructible T.
-      for (Holder* p = p_; p != p_ + n; ++p)
-        // Note: no parens: default init only.
-        // Also note '::' to avoid Holder class placement new operator.
-        ::new (static_cast<void*>(p)) Holder;
-    }
-
-    template <typename Iter>
-    Rep(Iter first, Iter last)
-        : n_(std::distance(first, last)), p_(MakeHolder(n_)) {
-      std::uninitialized_copy(first, last, AsValue(p_));
-    }
-
-    ~Rep() {
-      // Destruction must be in reverse order.
-      // Loop optimizes to nothing for trivially destructible T.
-      for (Holder* p = end(); p != begin();) (--p)->~Holder();
-      if (IsAllocated(size())) {
-        std::allocator<Holder>().deallocate(p_, n_);
-      } else {
+    explicit Storage(size_type n) : data_(CreateStorage(n)), size_(n) {}
+    ~Storage() noexcept {
+      if (UsingInlinedStorage(size())) {
         this->AnnotateDestruct(size());
+      } else {
+        std::allocator<StorageElement>().deallocate(begin(), size());
       }
     }
-    Holder* begin() const { return p_; }
-    Holder* end() const { return p_ + n_; }
-    size_type size() const { return n_; }
+
+    size_type size() const { return size_; }
+    StorageElement* begin() const { return data_; }
+    StorageElement* end() const { return begin() + size(); }
 
    private:
-    Holder* MakeHolder(size_type n) {
-      if (IsAllocated(n)) {
-        return std::allocator<Holder>().allocate(n);
-      } else {
+    static bool UsingInlinedStorage(size_type n) {
+      return n <= inline_elements;
+    }
+
+    StorageElement* CreateStorage(size_type n) {
+      if (UsingInlinedStorage(n)) {
         this->AnnotateConstruct(n);
-        return this->data();
+        return InlinedStorage::data();
+      } else {
+        return std::allocator<StorageElement>().allocate(n);
       }
     }
 
-    bool IsAllocated(size_type n) const { return n > inline_elements; }
-
-    const size_type n_;
-    Holder* const p_;
+    StorageElement* const data_;
+    const size_type size_;
   };
 
-
-  // Data members
-  Rep rep_;
+  const Storage storage_;
 };
 
 template <typename T, size_t N>
@@ -494,5 +454,25 @@ constexpr size_t FixedArray<T, N>::inline_elements;
 template <typename T, size_t N>
 constexpr size_t FixedArray<T, N>::kInlineBytesDefault;
 
+template <typename T, size_t N>
+void FixedArray<T, N>::NonEmptyInlinedStorage::AnnotateConstruct(size_t n) {
+#ifdef ADDRESS_SANITIZER
+  if (!n) return;
+  ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(), data() + n);
+  ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(), RedzoneBegin());
+#endif                   // ADDRESS_SANITIZER
+  static_cast<void>(n);  // Mark used when not in asan mode
+}
+
+template <typename T, size_t N>
+void FixedArray<T, N>::NonEmptyInlinedStorage::AnnotateDestruct(size_t n) {
+#ifdef ADDRESS_SANITIZER
+  if (!n) return;
+  ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n, RedzoneEnd());
+  ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(), data());
+#endif                   // ADDRESS_SANITIZER
+  static_cast<void>(n);  // Mark used when not in asan mode
+}
+
 }  // namespace absl
 #endif  // ABSL_CONTAINER_FIXED_ARRAY_H_
diff --git a/absl/container/fixed_array_exception_safety_test.cc b/absl/container/fixed_array_exception_safety_test.cc
new file mode 100644
index 000000000000..c123c2a1c0d2
--- /dev/null
+++ b/absl/container/fixed_array_exception_safety_test.cc
@@ -0,0 +1,117 @@
+// 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
+//
+//      http://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 <initializer_list>
+
+#include "absl/container/fixed_array.h"
+
+#include "gtest/gtest.h"
+#include "absl/base/internal/exception_safety_testing.h"
+
+namespace absl {
+
+namespace {
+
+constexpr size_t kInlined = 25;
+constexpr size_t kSmallSize = kInlined / 2;
+constexpr size_t kLargeSize = kInlined * 2;
+
+constexpr int kInitialValue = 5;
+constexpr int kUpdatedValue = 10;
+
+using ::testing::TestThrowingCtor;
+
+using Thrower = testing::ThrowingValue<testing::TypeSpec::kEverythingThrows>;
+using FixedArr = absl::FixedArray<Thrower, kInlined>;
+
+using MoveThrower = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>;
+using MoveFixedArr = absl::FixedArray<MoveThrower, kInlined>;
+
+TEST(FixedArrayExceptionSafety, CopyConstructor) {
+  auto small = FixedArr(kSmallSize);
+  TestThrowingCtor<FixedArr>(small);
+
+  auto large = FixedArr(kLargeSize);
+  TestThrowingCtor<FixedArr>(large);
+}
+
+TEST(FixedArrayExceptionSafety, MoveConstructor) {
+  TestThrowingCtor<FixedArr>(FixedArr(kSmallSize));
+  TestThrowingCtor<FixedArr>(FixedArr(kLargeSize));
+
+  // TypeSpec::kNoThrowMove
+  TestThrowingCtor<MoveFixedArr>(MoveFixedArr(kSmallSize));
+  TestThrowingCtor<MoveFixedArr>(MoveFixedArr(kLargeSize));
+}
+
+TEST(FixedArrayExceptionSafety, SizeConstructor) {
+  TestThrowingCtor<FixedArr>(kSmallSize);
+  TestThrowingCtor<FixedArr>(kLargeSize);
+}
+
+TEST(FixedArrayExceptionSafety, SizeValueConstructor) {
+  TestThrowingCtor<FixedArr>(kSmallSize, Thrower());
+  TestThrowingCtor<FixedArr>(kLargeSize, Thrower());
+}
+
+TEST(FixedArrayExceptionSafety, IteratorConstructor) {
+  auto small = FixedArr(kSmallSize);
+  TestThrowingCtor<FixedArr>(small.begin(), small.end());
+
+  auto large = FixedArr(kLargeSize);
+  TestThrowingCtor<FixedArr>(large.begin(), large.end());
+}
+
+TEST(FixedArrayExceptionSafety, InitListConstructor) {
+  constexpr int small_inlined = 3;
+  using SmallFixedArr = absl::FixedArray<Thrower, small_inlined>;
+
+  TestThrowingCtor<SmallFixedArr>(std::initializer_list<Thrower>{});
+  // Test inlined allocation
+  TestThrowingCtor<SmallFixedArr>(
+      std::initializer_list<Thrower>{Thrower{}, Thrower{}});
+  // Test out of line allocation
+  TestThrowingCtor<SmallFixedArr>(std::initializer_list<Thrower>{
+      Thrower{}, Thrower{}, Thrower{}, Thrower{}, Thrower{}});
+}
+
+testing::AssertionResult ReadMemory(FixedArr* fixed_arr) {
+  // Marked volatile to prevent optimization. Used for running asan tests.
+  volatile int sum = 0;
+  for (const auto& thrower : *fixed_arr) {
+    sum += thrower.Get();
+  }
+  return testing::AssertionSuccess() << "Values sum to [" << sum << "]";
+}
+
+TEST(FixedArrayExceptionSafety, Fill) {
+  auto test_fill = testing::MakeExceptionSafetyTester()
+                       .WithInvariants(ReadMemory)
+                       .WithOperation([&](FixedArr* fixed_arr_ptr) {
+                         auto thrower =
+                             Thrower(kUpdatedValue, testing::nothrow_ctor);
+                         fixed_arr_ptr->fill(thrower);
+                       });
+
+  EXPECT_TRUE(
+      test_fill.WithInitialValue(FixedArr(kSmallSize, Thrower(kInitialValue)))
+          .Test());
+  EXPECT_TRUE(
+      test_fill.WithInitialValue(FixedArr(kLargeSize, Thrower(kInitialValue)))
+          .Test());
+}
+
+}  // namespace
+
+}  // namespace absl
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h
index 78f78ea70221..75d980274a57 100644
--- a/absl/container/inlined_vector.h
+++ b/absl/container/inlined_vector.h
@@ -89,7 +89,9 @@ class InlinedVector {
       : allocator_and_tag_(alloc) {}
 
   // Create a vector with n copies of value_type().
-  explicit InlinedVector(size_type n) : allocator_and_tag_(allocator_type()) {
+  explicit InlinedVector(size_type n,
+                         const allocator_type& alloc = allocator_type())
+      : allocator_and_tag_(alloc) {
     InitAssign(n);
   }
 
@@ -643,12 +645,12 @@ class InlinedVector {
   class AllocatorAndTag : private allocator_type {
    public:
     explicit AllocatorAndTag(const allocator_type& a, Tag t = Tag())
-        : allocator_type(a), tag_(t) {
-    }
+        : allocator_type(a), tag_(t) {}
     Tag& tag() { return tag_; }
     const Tag& tag() const { return tag_; }
     allocator_type& allocator() { return *this; }
     const allocator_type& allocator() const { return *this; }
+
    private:
     Tag tag_;
   };
@@ -687,26 +689,23 @@ class InlinedVector {
     new (&rep_.allocation_storage.allocation) Allocation(allocation);
   }
 
+  // TODO(absl-team): investigate whether the reinterpret_cast is appropriate.
   value_type* inlined_space() {
-    return reinterpret_cast<value_type*>(&rep_.inlined_storage.inlined);
+    return reinterpret_cast<value_type*>(
+        std::addressof(rep_.inlined_storage.inlined[0]));
   }
   const value_type* inlined_space() const {
-    return reinterpret_cast<const value_type*>(&rep_.inlined_storage.inlined);
+    return reinterpret_cast<const value_type*>(
+        std::addressof(rep_.inlined_storage.inlined[0]));
   }
 
-  value_type* allocated_space() {
-    return allocation().buffer();
-  }
-  const value_type* allocated_space() const {
-    return allocation().buffer();
-  }
+  value_type* allocated_space() { return allocation().buffer(); }
+  const value_type* allocated_space() const { return allocation().buffer(); }
 
   const allocator_type& allocator() const {
     return allocator_and_tag_.allocator();
   }
-  allocator_type& allocator() {
-    return allocator_and_tag_.allocator();
-  }
+  allocator_type& allocator() { return allocator_and_tag_.allocator(); }
 
   bool allocated() const { return tag().allocated(); }
 
@@ -1126,8 +1125,7 @@ void InlinedVector<T, N, A>::swap(InlinedVector& other) {
     const size_type b_size = b->size();
     assert(a_size >= b_size);
     // 'a' is larger. Swap the elements up to the smaller array size.
-    std::swap_ranges(a->inlined_space(),
-                     a->inlined_space() + b_size,
+    std::swap_ranges(a->inlined_space(), a->inlined_space() + b_size,
                      b->inlined_space());
 
     // Move the remaining elements: A[b_size,a_size) -> B[b_size,a_size)
@@ -1271,8 +1269,7 @@ void InlinedVector<T, N, A>::Destroy(value_type* ptr, value_type* ptr_last) {
   // scribbling on a vtable pointer.
 #ifndef NDEBUG
   if (ptr != ptr_last) {
-    memset(reinterpret_cast<void*>(ptr), 0xab,
-           sizeof(*ptr) * (ptr_last - ptr));
+    memset(reinterpret_cast<void*>(ptr), 0xab, sizeof(*ptr) * (ptr_last - ptr));
   }
 #endif
 }
@@ -1300,8 +1297,9 @@ void InlinedVector<T, N, A>::AssignRange(Iter first, Iter last,
   // Optimized to avoid reallocation.
   // Prefer reassignment to copy construction for elements.
   iterator out = begin();
-  for ( ; first != last && out != end(); ++first, ++out)
+  for (; first != last && out != end(); ++first, ++out) {
     *out = *first;
+  }
   erase(out, end());
   std::copy(first, last, std::back_inserter(*this));
 }
diff --git a/absl/container/inlined_vector_benchmark.cc b/absl/container/inlined_vector_benchmark.cc
index 5977bc934f0e..24f2174928a3 100644
--- a/absl/container/inlined_vector_benchmark.cc
+++ b/absl/container/inlined_vector_benchmark.cc
@@ -63,18 +63,34 @@ void BM_StdVectorFill(benchmark::State& state) {
 }
 BENCHMARK(BM_StdVectorFill)->Range(0, 1024);
 
+// The purpose of the next two benchmarks is to verify that
+// absl::InlinedVector is efficient when moving is more efficent than
+// copying. To do so, we use strings that are larger than the short
+// std::string optimization.
 bool StringRepresentedInline(std::string s) {
   const char* chars = s.data();
   std::string s1 = std::move(s);
   return s1.data() != chars;
 }
 
+int GetNonShortStringOptimizationSize() {
+  for (int i = 24; i <= 192; i *= 2) {
+    if (!StringRepresentedInline(std::string(i, 'A'))) {
+      return i;
+    }
+  }
+  ABSL_RAW_LOG(
+      FATAL,
+      "Failed to find a std::string larger than the short std::string optimization");
+  return -1;
+}
+
 void BM_InlinedVectorFillString(benchmark::State& state) {
   const int len = state.range(0);
-  std::string strings[4] = {"a quite long string",
-                       "another long string",
-                       "012345678901234567",
-                       "to cause allocation"};
+  const int no_sso = GetNonShortStringOptimizationSize();
+  std::string strings[4] = {std::string(no_sso, 'A'), std::string(no_sso, 'B'),
+                       std::string(no_sso, 'C'), std::string(no_sso, 'D')};
+
   for (auto _ : state) {
     absl::InlinedVector<std::string, 8> v;
     for (int i = 0; i < len; i++) {
@@ -87,10 +103,10 @@ BENCHMARK(BM_InlinedVectorFillString)->Range(0, 1024);
 
 void BM_StdVectorFillString(benchmark::State& state) {
   const int len = state.range(0);
-  std::string strings[4] = {"a quite long string",
-                       "another long string",
-                       "012345678901234567",
-                       "to cause allocation"};
+  const int no_sso = GetNonShortStringOptimizationSize();
+  std::string strings[4] = {std::string(no_sso, 'A'), std::string(no_sso, 'B'),
+                       std::string(no_sso, 'C'), std::string(no_sso, 'D')};
+
   for (auto _ : state) {
     std::vector<std::string> v;
     for (int i = 0; i < len; i++) {
@@ -98,11 +114,6 @@ void BM_StdVectorFillString(benchmark::State& state) {
     }
   }
   state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
-  // The purpose of the benchmark is to verify that inlined vector is
-  // efficient when moving is more efficent than copying. To do so, we
-  // use strings that are larger than the small std::string optimization.
-  ABSL_RAW_CHECK(!StringRepresentedInline(strings[0]),
-                 "benchmarked with strings that are too small");
 }
 BENCHMARK(BM_StdVectorFillString)->Range(0, 1024);
 
diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc
index 26a7d5bca86f..f81fad569dcd 100644
--- a/absl/container/inlined_vector_test.cc
+++ b/absl/container/inlined_vector_test.cc
@@ -1763,4 +1763,30 @@ TEST(AllocatorSupportTest, ScopedAllocatorWorks) {
   EXPECT_EQ(allocated, 0);
 }
 
+TEST(AllocatorSupportTest, SizeAllocConstructor) {
+  constexpr int inlined_size = 4;
+  using Alloc = CountingAllocator<int>;
+  using AllocVec = absl::InlinedVector<int, inlined_size, Alloc>;
+
+  {
+    auto len = inlined_size / 2;
+    int64_t allocated = 0;
+    auto v = AllocVec(len, Alloc(&allocated));
+
+    // Inline storage used; allocator should not be invoked
+    EXPECT_THAT(allocated, 0);
+    EXPECT_THAT(v, AllOf(SizeIs(len), Each(0)));
+  }
+
+  {
+    auto len = inlined_size * 2;
+    int64_t allocated = 0;
+    auto v = AllocVec(len, Alloc(&allocated));
+
+    // Out of line storage used; allocation of 8 elements expected
+    EXPECT_THAT(allocated, len * sizeof(int));
+    EXPECT_THAT(v, AllOf(SizeIs(len), Each(0)));
+  }
+}
+
 }  // anonymous namespace
diff --git a/absl/copts.bzl b/absl/copts.bzl
index 20c9b6190dc9..0168ac5abddc 100644
--- a/absl/copts.bzl
+++ b/absl/copts.bzl
@@ -31,7 +31,6 @@ GCC_TEST_FLAGS = [
     "-Wno-unused-private-field",
 ]
 
-
 # Docs on single flags is preceded by a comment.
 # Docs on groups of flags is preceded by ###.
 
diff --git a/absl/debugging/internal/examine_stack.cc b/absl/debugging/internal/examine_stack.cc
index 261daae932f1..faf888367356 100644
--- a/absl/debugging/internal/examine_stack.cc
+++ b/absl/debugging/internal/examine_stack.cc
@@ -46,10 +46,16 @@ void* GetProgramCounter(void* vuc) {
 #elif defined(__i386__)
     if (14 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs))
       return reinterpret_cast<void*>(context->uc_mcontext.gregs[14]);
+#elif defined(__mips__)
+    return reinterpret_cast<void*>(context->uc_mcontext.pc);
 #elif defined(__powerpc64__)
     return reinterpret_cast<void*>(context->uc_mcontext.gp_regs[32]);
 #elif defined(__powerpc__)
     return reinterpret_cast<void*>(context->uc_mcontext.regs->nip);
+#elif defined(__s390__) && !defined(__s390x__)
+    return reinterpret_cast<void*>(context->uc_mcontext.psw.addr & 0x7fffffff);
+#elif defined(__s390__) && defined(__s390x__)
+    return reinterpret_cast<void*>(context->uc_mcontext.psw.addr);
 #elif defined(__x86_64__)
     if (16 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs))
       return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]);
diff --git a/absl/debugging/internal/stacktrace_config.h b/absl/debugging/internal/stacktrace_config.h
index 48adfccc62e5..dd713da8c0c7 100644
--- a/absl/debugging/internal/stacktrace_config.h
+++ b/absl/debugging/internal/stacktrace_config.h
@@ -21,26 +21,16 @@
 #ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
 #define ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
 
-// First, test platforms which only support a stub.
-#if ABSL_STACKTRACE_INL_HEADER
+#if defined(ABSL_STACKTRACE_INL_HEADER)
 #error ABSL_STACKTRACE_INL_HEADER cannot be directly set
-#elif defined(__native_client__) || defined(__APPLE__) || \
-    defined(__FreeBSD__) || defined(__ANDROID__) || defined(__myriad2__) || \
-    defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__)
-#define ABSL_STACKTRACE_INL_HEADER \
-    "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
 
-// Next, test for Mips and Windows.
-// TODO(marmstrong): Mips case, remove the check for ABSL_STACKTRACE_INL_HEADER
-#elif defined(__mips__) && !defined(ABSL_STACKTRACE_INL_HEADER)
-#define ABSL_STACKTRACE_INL_HEADER \
-    "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
-#elif defined(_WIN32)  // windows
+#elif defined(_WIN32)
 #define ABSL_STACKTRACE_INL_HEADER \
     "absl/debugging/internal/stacktrace_win32-inl.inc"
 
-// Finally, test NO_FRAME_POINTER.
-#elif !defined(NO_FRAME_POINTER)
+#elif defined(__linux__) && !defined(__ANDROID__)
+
+#if !defined(NO_FRAME_POINTER)
 # if defined(__i386__) || defined(__x86_64__)
 #define ABSL_STACKTRACE_INL_HEADER \
     "absl/debugging/internal/stacktrace_x86-inl.inc"
@@ -53,22 +43,27 @@
 # elif defined(__arm__)
 #define ABSL_STACKTRACE_INL_HEADER \
     "absl/debugging/internal/stacktrace_arm-inl.inc"
+# else
+#define ABSL_STACKTRACE_INL_HEADER \
+   "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
 # endif
 #else  // defined(NO_FRAME_POINTER)
 # if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
 #define ABSL_STACKTRACE_INL_HEADER \
-    "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
+    "absl/debugging/internal/stacktrace_generic-inl.inc"
 # elif defined(__ppc__) || defined(__PPC__)
-//  Use glibc's backtrace.
 #define ABSL_STACKTRACE_INL_HEADER \
     "absl/debugging/internal/stacktrace_generic-inl.inc"
-# elif defined(__arm__)
-#   error stacktrace without frame pointer is not supported on ARM
+# else
+#define ABSL_STACKTRACE_INL_HEADER \
+   "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
 # endif
 #endif  // NO_FRAME_POINTER
 
-#if !defined(ABSL_STACKTRACE_INL_HEADER)
-#error Not supported yet
+#else
+#define ABSL_STACKTRACE_INL_HEADER \
+  "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
+
 #endif
 
 #endif  // ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
diff --git a/absl/memory/BUILD.bazel b/absl/memory/BUILD.bazel
index d5c62265a8c5..46f47b12bb2a 100644
--- a/absl/memory/BUILD.bazel
+++ b/absl/memory/BUILD.bazel
@@ -18,6 +18,7 @@ load(
     "//absl:copts.bzl",
     "ABSL_DEFAULT_COPTS",
     "ABSL_TEST_COPTS",
+    "ABSL_EXCEPTIONS_FLAG",
 )
 
 package(default_visibility = ["//visibility:public"])
@@ -45,3 +46,16 @@ cc_test(
         "@com_google_googletest//:gtest_main",
     ],
 )
+
+cc_test(
+    name = "memory_exception_safety_test",
+    srcs = [
+        "memory_exception_safety_test.cc",
+    ],
+    copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+    deps = [
+        ":memory",
+        "//absl/base:exception_safety_testing",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
diff --git a/absl/memory/CMakeLists.txt b/absl/memory/CMakeLists.txt
index 21bfc87ed6e1..5958f5c52bfb 100644
--- a/absl/memory/CMakeLists.txt
+++ b/absl/memory/CMakeLists.txt
@@ -49,4 +49,23 @@ absl_test(
 )
 
 
+# test memory_exception_safety_test
+set(MEMORY_EXCEPTION_SAFETY_TEST_SRC "memory_exception_safety_test.cc")
+set(MEMORY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES
+  absl::memory
+  absl_base_internal_exception_safety_testing
+)
+
+absl_test(
+  TARGET
+    memory_exception_safety_test
+  SOURCES
+    ${MEMORY_EXCEPTION_SAFETY_TEST_SRC}
+  PUBLIC_LIBRARIES
+    ${MEMORY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES}
+  PRIVATE_COMPILE_FLAGS
+    ${ABSL_EXCEPTIONS_FLAG}
+)
+
+
 
diff --git a/absl/memory/memory.h b/absl/memory/memory.h
index cd818cff4ffa..c43e156682fd 100644
--- a/absl/memory/memory.h
+++ b/absl/memory/memory.h
@@ -636,6 +636,39 @@ struct default_allocator_is_nothrow : std::true_type {};
 struct default_allocator_is_nothrow : std::false_type {};
 #endif
 
+namespace memory_internal {
+// TODO(b110200014): Implement proper backports
+template <typename ForwardIt>
+void DefaultConstruct(ForwardIt it) {
+  using value_type = typename std::iterator_traits<ForwardIt>::value_type;
+  ::new (static_cast<void*>(std::addressof(*it))) value_type;
+}  // namespace memory_internal
+
+#ifdef ABSL_HAVE_EXCEPTIONS
+template <typename ForwardIt, typename Size>
+void uninitialized_default_construct_n(ForwardIt first, Size size) {
+  for (ForwardIt cur = first; size > 0; static_cast<void>(++cur), --size) {
+    try {
+      absl::memory_internal::DefaultConstruct(cur);
+    } catch (...) {
+      using value_type = typename std::iterator_traits<ForwardIt>::value_type;
+      for (; first != cur; ++first) {
+        first->~value_type();
+      }
+      throw;
+    }
+  }
+}
+#else   // ABSL_HAVE_EXCEPTIONS
+template <typename ForwardIt, typename Size>
+void uninitialized_default_construct_n(ForwardIt first, Size size) {
+  for (; size > 0; static_cast<void>(++first), --size) {
+    absl::memory_internal::DefaultConstruct(first);
+  }
+}
+#endif  // ABSL_HAVE_EXCEPTIONS
+}  // namespace memory_internal
+
 }  // namespace absl
 
 #endif  // ABSL_MEMORY_MEMORY_H_
diff --git a/absl/memory/memory_exception_safety_test.cc b/absl/memory/memory_exception_safety_test.cc
new file mode 100644
index 000000000000..fb8b561d59fb
--- /dev/null
+++ b/absl/memory/memory_exception_safety_test.cc
@@ -0,0 +1,63 @@
+// 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
+//
+//      http://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 "gtest/gtest.h"
+#include "absl/base/internal/exception_safety_testing.h"
+
+namespace absl {
+namespace {
+
+constexpr int kLength = 50;
+using Thrower = testing::ThrowingValue<testing::TypeSpec::kEverythingThrows>;
+using ThrowerStorage =
+    absl::aligned_storage_t<sizeof(Thrower), alignof(Thrower)>;
+using ThrowerList = std::array<ThrowerStorage, kLength>;
+
+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.
+                    .WithInvariants(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));
+  }));
+}
+
+TEST(MemoryInternal, UninitDefaultConstructNNonTrivial) {
+  EXPECT_TRUE(testing::MakeExceptionSafetyTester()
+                  .WithInitialValue(ThrowerList{})
+                  .WithOperation([&](ThrowerList* list_ptr) {
+                    absl::memory_internal::uninitialized_default_construct_n(
+                        list_ptr->data(), kLength);
+                  })
+                  .WithInvariants([&](...) { return true; })
+                  .Test());
+}
+
+}  // namespace
+}  // namespace absl
diff --git a/absl/memory/memory_test.cc b/absl/memory/memory_test.cc
index dee9b486a30d..8ff1945debe5 100644
--- a/absl/memory/memory_test.cc
+++ b/absl/memory/memory_test.cc
@@ -611,4 +611,47 @@ TEST(AllocatorNoThrowTest, CustomAllocator) {
   EXPECT_FALSE(absl::allocator_is_nothrow<UnspecifiedAllocator>::value);
 }
 
+TEST(MemoryInternal, UninitDefaultConstructNTrivial) {
+  constexpr int kInitialValue = 123;
+  constexpr int kExpectedValue = kInitialValue;  // Expect no-op behavior
+  constexpr int len = 5;
+
+  struct TestObj {
+    int val;
+  };
+  static_assert(absl::is_trivially_default_constructible<TestObj>::value, "");
+  static_assert(absl::is_trivially_destructible<TestObj>::value, "");
+
+  TestObj objs[len];
+  for (auto& obj : objs) {
+    obj.val = kInitialValue;
+  }
+
+  absl::memory_internal::uninitialized_default_construct_n(objs, len);
+  for (auto& obj : objs) {
+    EXPECT_EQ(obj.val, kExpectedValue);
+  }
+}
+
+TEST(MemoryInternal, UninitDefaultConstructNNonTrivial) {
+  constexpr int kInitialValue = 123;
+  constexpr int kExpectedValue = 0;  // Expect value-construction behavior
+  constexpr int len = 5;
+
+  struct TestObj {
+    int val{kExpectedValue};
+  };
+  static_assert(absl::is_trivially_destructible<TestObj>::value, "");
+
+  TestObj objs[len];
+  for (auto& obj : objs) {
+    obj.val = kInitialValue;
+  }
+
+  absl::memory_internal::uninitialized_default_construct_n(objs, len);
+  for (auto& obj : objs) {
+    EXPECT_EQ(obj.val, kExpectedValue);
+  }
+}
+
 }  // namespace
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h
index 88af17c395df..c3e01fe2990c 100644
--- a/absl/meta/type_traits.h
+++ b/absl/meta/type_traits.h
@@ -369,4 +369,6 @@ struct IsHashEnabled
 }  // namespace type_traits_internal
 
 }  // namespace absl
+
+
 #endif  // ABSL_META_TYPE_TRAITS_H_
diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc
index 3688e5efef45..cd79534f3bfd 100644
--- a/absl/numeric/int128.cc
+++ b/absl/numeric/int128.cc
@@ -223,3 +223,29 @@ std::ostream& operator<<(std::ostream& os, uint128 v) {
 }
 
 }  // namespace absl
+
+namespace std {
+constexpr bool numeric_limits<absl::uint128>::is_specialized;
+constexpr bool numeric_limits<absl::uint128>::is_signed;
+constexpr bool numeric_limits<absl::uint128>::is_integer;
+constexpr bool numeric_limits<absl::uint128>::is_exact;
+constexpr bool numeric_limits<absl::uint128>::has_infinity;
+constexpr bool numeric_limits<absl::uint128>::has_quiet_NaN;
+constexpr bool numeric_limits<absl::uint128>::has_signaling_NaN;
+constexpr float_denorm_style numeric_limits<absl::uint128>::has_denorm;
+constexpr bool numeric_limits<absl::uint128>::has_denorm_loss;
+constexpr float_round_style numeric_limits<absl::uint128>::round_style;
+constexpr bool numeric_limits<absl::uint128>::is_iec559;
+constexpr bool numeric_limits<absl::uint128>::is_bounded;
+constexpr bool numeric_limits<absl::uint128>::is_modulo;
+constexpr int numeric_limits<absl::uint128>::digits;
+constexpr int numeric_limits<absl::uint128>::digits10;
+constexpr int numeric_limits<absl::uint128>::max_digits10;
+constexpr int numeric_limits<absl::uint128>::radix;
+constexpr int numeric_limits<absl::uint128>::min_exponent;
+constexpr int numeric_limits<absl::uint128>::min_exponent10;
+constexpr int numeric_limits<absl::uint128>::max_exponent;
+constexpr int numeric_limits<absl::uint128>::max_exponent10;
+constexpr bool numeric_limits<absl::uint128>::traps;
+constexpr bool numeric_limits<absl::uint128>::tinyness_before;
+}  // namespace std
diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h
index bc7dbb47ae81..e4f39c305152 100644
--- a/absl/numeric/int128.h
+++ b/absl/numeric/int128.h
@@ -219,21 +219,69 @@ std::ostream& operator<<(std::ostream& os, uint128 v);
 
 // TODO(strel) add operator>>(std::istream&, uint128)
 
+constexpr uint128 Uint128Max() {
+  return uint128(std::numeric_limits<uint64_t>::max(),
+                 std::numeric_limits<uint64_t>::max());
+}
+
+}  // namespace absl
+
+// Specialized numeric_limits for uint128.
+namespace std {
+template <>
+class numeric_limits<absl::uint128> {
+ public:
+  static constexpr bool is_specialized = true;
+  static constexpr bool is_signed = false;
+  static constexpr bool is_integer = true;
+  static constexpr bool is_exact = true;
+  static constexpr bool has_infinity = false;
+  static constexpr bool has_quiet_NaN = false;
+  static constexpr bool has_signaling_NaN = false;
+  static constexpr float_denorm_style has_denorm = denorm_absent;
+  static constexpr bool has_denorm_loss = false;
+  static constexpr float_round_style round_style = round_toward_zero;
+  static constexpr bool is_iec559 = false;
+  static constexpr bool is_bounded = true;
+  static constexpr bool is_modulo = true;
+  static constexpr int digits = 128;
+  static constexpr int digits10 = 38;
+  static constexpr int max_digits10 = 0;
+  static constexpr int radix = 2;
+  static constexpr int min_exponent = 0;
+  static constexpr int min_exponent10 = 0;
+  static constexpr int max_exponent = 0;
+  static constexpr int max_exponent10 = 0;
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+  static constexpr bool traps = numeric_limits<unsigned __int128>::traps;
+#else   // ABSL_HAVE_INTRINSIC_INT128
+  static constexpr bool traps = numeric_limits<uint64_t>::traps;
+#endif  // ABSL_HAVE_INTRINSIC_INT128
+  static constexpr bool tinyness_before = false;
+
+  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 epsilon() { return 0; }
+  static constexpr absl::uint128 round_error() { return 0; }
+  static constexpr absl::uint128 infinity() { return 0; }
+  static constexpr absl::uint128 quiet_NaN() { return 0; }
+  static constexpr absl::uint128 signaling_NaN() { return 0; }
+  static constexpr absl::uint128 denorm_min() { return 0; }
+};
+}  // namespace std
+
 // TODO(absl-team): Implement signed 128-bit type
 
 // --------------------------------------------------------------------------
 //                      Implementation details follow
 // --------------------------------------------------------------------------
+namespace absl {
 
 constexpr uint128 MakeUint128(uint64_t high, uint64_t low) {
   return uint128(high, low);
 }
 
-constexpr uint128 Uint128Max() {
-  return uint128(std::numeric_limits<uint64_t>::max(),
-                 std::numeric_limits<uint64_t>::max());
-}
-
 // Assignment from integer types.
 
 inline uint128& uint128::operator=(int v) { return *this = uint128(v); }
diff --git a/absl/numeric/int128_test.cc b/absl/numeric/int128_test.cc
index 79bcca907ae9..1eb3e0ec8961 100644
--- a/absl/numeric/int128_test.cc
+++ b/absl/numeric/int128_test.cc
@@ -428,4 +428,15 @@ TEST(Uint128, ConstexprTest) {
   EXPECT_EQ(minus_two, absl::MakeUint128(-1, -2));
 }
 
+TEST(Uint128, NumericLimitsTest) {
+  static_assert(std::numeric_limits<absl::uint128>::is_specialized, "");
+  static_assert(!std::numeric_limits<absl::uint128>::is_signed, "");
+  static_assert(std::numeric_limits<absl::uint128>::is_integer, "");
+  EXPECT_EQ(static_cast<int>(128 * std::log10(2)),
+            std::numeric_limits<absl::uint128>::digits10);
+  EXPECT_EQ(0, std::numeric_limits<absl::uint128>::min());
+  EXPECT_EQ(0, std::numeric_limits<absl::uint128>::lowest());
+  EXPECT_EQ(absl::Uint128Max(), std::numeric_limits<absl::uint128>::max());
+}
+
 }  // namespace
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index f06bdc0d01cf..3b1e067597e2 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -32,7 +32,12 @@ cc_library(
     name = "strings",
     srcs = [
         "ascii.cc",
+        "charconv.cc",
         "escaping.cc",
+        "internal/charconv_bigint.cc",
+        "internal/charconv_bigint.h",
+        "internal/charconv_parse.cc",
+        "internal/charconv_parse.h",
         "internal/memutil.cc",
         "internal/memutil.h",
         "internal/stl_type_traits.h",
@@ -48,6 +53,7 @@ cc_library(
     ],
     hdrs = [
         "ascii.h",
+        "charconv.h",
         "escaping.h",
         "match.h",
         "numbers.h",
@@ -144,11 +150,6 @@ cc_test(
     size = "small",
     srcs = ["ascii_test.cc"],
     copts = ABSL_TEST_COPTS,
-    tags = [
-        "no_test_android_arm",
-        "no_test_android_arm64",
-        "no_test_android_x86",
-    ],
     visibility = ["//visibility:private"],
     deps = [
         ":strings",
@@ -398,12 +399,6 @@ cc_test(
         "numbers_test.cc",
     ],
     copts = ABSL_TEST_COPTS,
-    tags = [
-        "no_test_android_arm",
-        "no_test_android_arm64",
-        "no_test_android_x86",
-        "no_test_loonix",
-    ],
     visibility = ["//visibility:private"],
     deps = [
         ":strings",
@@ -414,6 +409,19 @@ cc_test(
 )
 
 cc_test(
+    name = "numbers_benchmark",
+    srcs = ["numbers_benchmark.cc"],
+    copts = ABSL_TEST_COPTS,
+    tags = ["benchmark"],
+    visibility = ["//visibility:private"],
+    deps = [
+        ":strings",
+        "//absl/base",
+        "@com_github_google_benchmark//:benchmark_main",
+    ],
+)
+
+cc_test(
     name = "strip_test",
     size = "small",
     srcs = ["strip_test.cc"],
@@ -429,11 +437,6 @@ cc_test(
     name = "char_map_test",
     srcs = ["internal/char_map_test.cc"],
     copts = ABSL_TEST_COPTS,
-    tags = [
-        "no_test_android_arm",
-        "no_test_android_arm64",
-        "no_test_android_x86",
-    ],
     deps = [
         ":internal",
         "@com_google_googletest//:gtest_main",
@@ -450,3 +453,197 @@ cc_test(
         "@com_github_google_benchmark//:benchmark_main",
     ],
 )
+
+cc_test(
+    name = "charconv_test",
+    srcs = ["charconv_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    deps = [
+        ":strings",
+        "//absl/base",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "charconv_parse_test",
+    srcs = [
+        "internal/charconv_parse.h",
+        "internal/charconv_parse_test.cc",
+    ],
+    copts = ABSL_TEST_COPTS,
+    deps = [
+        ":strings",
+        "//absl/base",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "charconv_bigint_test",
+    srcs = [
+        "internal/charconv_bigint.h",
+        "internal/charconv_bigint_test.cc",
+        "internal/charconv_parse.h",
+    ],
+    copts = ABSL_TEST_COPTS,
+    deps = [
+        ":strings",
+        "//absl/base",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "charconv_benchmark",
+    srcs = [
+        "charconv_benchmark.cc",
+    ],
+    tags = [
+        "benchmark",
+    ],
+    deps = [
+        ":strings",
+        "//absl/base",
+        "@com_github_google_benchmark//:benchmark_main",
+    ],
+)
+
+cc_library(
+    name = "str_format",
+    hdrs = [
+        "str_format.h",
+    ],
+    copts = ABSL_DEFAULT_COPTS,
+    deps = [
+        ":str_format_internal",
+    ],
+)
+
+cc_library(
+    name = "str_format_internal",
+    srcs = [
+        "internal/str_format/arg.cc",
+        "internal/str_format/bind.cc",
+        "internal/str_format/extension.cc",
+        "internal/str_format/float_conversion.cc",
+        "internal/str_format/output.cc",
+        "internal/str_format/parser.cc",
+    ],
+    hdrs = [
+        "internal/str_format/arg.h",
+        "internal/str_format/bind.h",
+        "internal/str_format/checker.h",
+        "internal/str_format/extension.h",
+        "internal/str_format/float_conversion.h",
+        "internal/str_format/output.h",
+        "internal/str_format/parser.h",
+    ],
+    copts = ABSL_DEFAULT_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":strings",
+        "//absl/base:core_headers",
+        "//absl/container:inlined_vector",
+        "//absl/meta:type_traits",
+        "//absl/numeric:int128",
+        "//absl/types:span",
+    ],
+)
+
+cc_test(
+    name = "str_format_test",
+    srcs = ["str_format_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":str_format",
+        ":strings",
+        "//absl/base:core_headers",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "str_format_extension_test",
+    srcs = [
+        "internal/str_format/extension_test.cc",
+    ],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":str_format",
+        ":str_format_internal",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "str_format_arg_test",
+    srcs = ["internal/str_format/arg_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":str_format",
+        ":str_format_internal",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "str_format_bind_test",
+    srcs = ["internal/str_format/bind_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":str_format_internal",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "str_format_checker_test",
+    srcs = ["internal/str_format/checker_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":str_format",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "str_format_convert_test",
+    size = "small",
+    srcs = ["internal/str_format/convert_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":str_format_internal",
+        "//absl/numeric:int128",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "str_format_output_test",
+    srcs = ["internal/str_format/output_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":str_format_internal",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_test(
+    name = "str_format_parser_test",
+    srcs = ["internal/str_format/parser_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":str_format_internal",
+        "//absl/base:core_headers",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index 9dc47328c731..cd122134729f 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -17,6 +17,7 @@
 
 list(APPEND STRINGS_PUBLIC_HEADERS
   "ascii.h"
+  "charconv.h"
   "escaping.h"
   "match.h"
   "numbers.h"
@@ -33,6 +34,8 @@ list(APPEND STRINGS_PUBLIC_HEADERS
 list(APPEND STRINGS_INTERNAL_HEADERS
   "internal/bits.h"
   "internal/char_map.h"
+  "internal/charconv_bigint.h"
+  "internal/charconv_parse.h"
   "internal/memutil.h"
   "internal/ostringstream.h"
   "internal/resize_uninitialized.h"
@@ -47,7 +50,10 @@ list(APPEND STRINGS_INTERNAL_HEADERS
 # add string library
 list(APPEND STRINGS_SRC
   "ascii.cc"
+  "charconv.cc"
   "escaping.cc"
+  "internal/charconv_bigint.cc"
+  "internal/charconv_parse.cc"
   "internal/memutil.cc"
   "internal/memutil.h"
   "internal/utf8.cc"
@@ -75,6 +81,56 @@ absl_library(
     strings
 )
 
+# add str_format library
+absl_header_library(
+  TARGET
+    absl_str_format
+  PUBLIC_LIBRARIES
+    str_format_internal
+  EXPORT_NAME
+    str_format
+)
+
+# str_format_internal
+absl_library(
+  TARGET
+    str_format_internal
+  SOURCES
+    "internal/str_format/arg.cc"
+    "internal/str_format/bind.cc"
+    "internal/str_format/extension.cc"
+    "internal/str_format/float_conversion.cc"
+    "internal/str_format/output.cc"
+    "internal/str_format/parser.cc"
+    "internal/str_format/arg.h"
+    "internal/str_format/bind.h"
+    "internal/str_format/checker.h"
+    "internal/str_format/extension.h"
+    "internal/str_format/float_conversion.h"
+    "internal/str_format/output.h"
+    "internal/str_format/parser.h"
+  PUBLIC_LIBRARIES
+    str_format_extension_internal
+    absl::strings
+    absl::base
+    absl::numeric
+    absl::container
+    absl::span
+)
+
+# str_format_extension_internal
+absl_library(
+  TARGET
+    str_format_extension_internal
+  SOURCES
+    "internal/str_format/extension.cc"
+    "internal/str_format/extension.h"
+    "internal/str_format/output.cc"
+    "internal/str_format/output.h"
+  PUBLIC_LIBRARIES
+    absl::base
+    absl::strings
+)
 
 #
 ## TESTS
@@ -301,5 +357,108 @@ absl_test(
 )
 
 
+# test charconv_test
+set(CHARCONV_TEST_SRC "charconv_test.cc")
+set(CHARCONV_TEST_PUBLIC_LIBRARIES absl::strings)
+
+absl_test(
+  TARGET
+    charconv_test
+  SOURCES
+    ${CHARCONV_TEST_SRC}
+  PUBLIC_LIBRARIES
+    ${CHARCONV_TEST_PUBLIC_LIBRARIES}
+)
+
+
+# test charconv_parse_test
+set(CHARCONV_PARSE_TEST_SRC "internal/charconv_parse_test.cc")
+set(CHARCONV_PARSE_TEST_PUBLIC_LIBRARIES absl::strings)
+
+absl_test(
+  TARGET
+    charconv_parse_test
+  SOURCES
+    ${CHARCONV_PARSE_TEST_SRC}
+  PUBLIC_LIBRARIES
+    ${CHARCONV_PARSE_TEST_PUBLIC_LIBRARIES}
+)
+
+
+# test charconv_bigint_test
+set(CHARCONV_BIGINT_TEST_SRC "internal/charconv_bigint_test.cc")
+set(CHARCONV_BIGINT_TEST_PUBLIC_LIBRARIES absl::strings)
+
+absl_test(
+  TARGET
+    charconv_bigint_test
+  SOURCES
+    ${CHARCONV_BIGINT_TEST_SRC}
+  PUBLIC_LIBRARIES
+    ${CHARCONV_BIGINT_TEST_PUBLIC_LIBRARIES}
+)
+# test str_format_test
+absl_test(
+  TARGET
+    str_format_test
+  SOURCES
+    "str_format_test.cc"
+  PUBLIC_LIBRARIES
+    absl::base
+    absl::str_format
+    absl::strings
+)
+
+# test str_format_bind_test
+absl_test(
+  TARGET
+    str_format_bind_test
+  SOURCES
+    "internal/str_format/bind_test.cc"
+  PUBLIC_LIBRARIES
+    str_format_internal
+)
+
+# test str_format_checker_test
+absl_test(
+  TARGET
+    str_format_checker_test
+  SOURCES
+    "internal/str_format/checker_test.cc"
+  PUBLIC_LIBRARIES
+    absl::str_format
+)
+
+# test str_format_convert_test
+absl_test(
+  TARGET
+    str_format_convert_test
+  SOURCES
+    "internal/str_format/convert_test.cc"
+  PUBLIC_LIBRARIES
+    str_format_internal
+    absl::numeric
+)
+
+# test str_format_output_test
+absl_test(
+  TARGET
+    str_format_output_test
+  SOURCES
+    "internal/str_format/output_test.cc"
+  PUBLIC_LIBRARIES
+    str_format_extension_internal
+)
+
+# test str_format_parser_test
+absl_test(
+  TARGET
+    str_format_parser_test
+  SOURCES
+    "internal/str_format/parser_test.cc"
+  PUBLIC_LIBRARIES
+    str_format_internal
+    absl::base
+)
 
 
diff --git a/absl/strings/charconv.cc b/absl/strings/charconv.cc
new file mode 100644
index 000000000000..08c3947eccd4
--- /dev/null
+++ b/absl/strings/charconv.cc
@@ -0,0 +1,982 @@
+// 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
+//
+//      http://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/charconv.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstring>
+
+#include "absl/base/casts.h"
+#include "absl/numeric/int128.h"
+#include "absl/strings/internal/bits.h"
+#include "absl/strings/internal/charconv_bigint.h"
+#include "absl/strings/internal/charconv_parse.h"
+
+// The macro ABSL_BIT_PACK_FLOATS is defined on x86-64, where IEEE floating
+// point numbers have the same endianness in memory as a bitfield struct
+// containing the corresponding parts.
+//
+// When set, we replace calls to ldexp() with manual bit packing, which is
+// faster and is unaffected by floating point environment.
+#ifdef ABSL_BIT_PACK_FLOATS
+#error ABSL_BIT_PACK_FLOATS cannot be directly set
+#elif defined(__x86_64__) || defined(_M_X64)
+#define ABSL_BIT_PACK_FLOATS 1
+#endif
+
+// A note about subnormals:
+//
+// The code below talks about "normals" and "subnormals".  A normal IEEE float
+// has a fixed-width mantissa and power of two exponent.  For example, a normal
+// `double` has a 53-bit mantissa.  Because the high bit is always 1, it is not
+// stored in the representation.  The implicit bit buys an extra bit of
+// resolution in the datatype.
+//
+// The downside of this scheme is that there is a large gap between DBL_MIN and
+// zero.  (Large, at least, relative to the different between DBL_MIN and the
+// next representable number).  This gap is softened by the "subnormal" numbers,
+// which have the same power-of-two exponent as DBL_MIN, but no implicit 53rd
+// bit.  An all-bits-zero exponent in the encoding represents subnormals.  (Zero
+// is represented as a subnormal with an all-bits-zero mantissa.)
+//
+// The code below, in calculations, represents the mantissa as a uint64_t.  The
+// end result normally has the 53rd bit set.  It represents subnormals by using
+// narrower mantissas.
+
+namespace absl {
+namespace {
+
+template <typename FloatType>
+struct FloatTraits;
+
+template <>
+struct FloatTraits<double> {
+  // The number of mantissa bits in the given float type.  This includes the
+  // implied high bit.
+  static constexpr int kTargetMantissaBits = 53;
+
+  // The largest supported IEEE exponent, in our integral mantissa
+  // representation.
+  //
+  // If `m` is the largest possible int kTargetMantissaBits bits wide, then
+  // m * 2**kMaxExponent is exactly equal to DBL_MAX.
+  static constexpr int kMaxExponent = 971;
+
+  // The smallest supported IEEE normal exponent, in our integral mantissa
+  // representation.
+  //
+  // If `m` is the smallest possible int kTargetMantissaBits bits wide, then
+  // m * 2**kMinNormalExponent is exactly equal to DBL_MIN.
+  static constexpr int kMinNormalExponent = -1074;
+
+  static double MakeNan(const char* tagp) {
+    // 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);
+  }
+
+  // Builds a nonzero floating point number out of the provided parts.
+  //
+  // This is intended to do the same operation as ldexp(mantissa, exponent),
+  // but using purely integer math, to avoid -ffastmath and floating
+  // point environment issues.  Using type punning is also faster. We fall back
+  // to ldexp on a per-platform basis for portability.
+  //
+  // `exponent` must be between kMinNormalExponent and kMaxExponent.
+  //
+  // `mantissa` must either be exactly kTargetMantissaBits wide, in which case
+  // a normal value is made, or it must be less narrow than that, in which case
+  // `exponent` must be exactly kMinNormalExponent, and a subnormal value is
+  // made.
+  static double Make(uint64_t mantissa, int exponent, bool sign) {
+#ifndef ABSL_BIT_PACK_FLOATS
+    // Support ldexp no matter which namespace it's in.  Some platforms
+    // incorrectly don't put it in namespace std.
+    using namespace std;  // NOLINT
+    return sign ? -ldexp(mantissa, exponent) : ldexp(mantissa, exponent);
+#else
+    constexpr uint64_t kMantissaMask =
+        (uint64_t(1) << (kTargetMantissaBits - 1)) - 1;
+    uint64_t dbl = static_cast<uint64_t>(sign) << 63;
+    if (mantissa > kMantissaMask) {
+      // Normal value.
+      // Adjust by 1023 for the exponent representation bias, and an additional
+      // 52 due to the implied decimal point in the IEEE mantissa represenation.
+      dbl += uint64_t{exponent + 1023u + kTargetMantissaBits - 1} << 52;
+      mantissa &= kMantissaMask;
+    } else {
+      // subnormal value
+      assert(exponent == kMinNormalExponent);
+    }
+    dbl += mantissa;
+    return absl::bit_cast<double>(dbl);
+#endif  // ABSL_BIT_PACK_FLOATS
+  }
+};
+
+// Specialization of floating point traits for the `float` type.  See the
+// FloatTraits<double> specialization above for meaning of each of the following
+// members and methods.
+template <>
+struct FloatTraits<float> {
+  static constexpr int kTargetMantissaBits = 24;
+  static constexpr int kMaxExponent = 104;
+  static constexpr int kMinNormalExponent = -149;
+  static float MakeNan(const char* tagp) {
+    // 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);
+  }
+  static float Make(uint32_t mantissa, int exponent, bool sign) {
+#ifndef ABSL_BIT_PACK_FLOATS
+    // Support ldexpf no matter which namespace it's in.  Some platforms
+    // incorrectly don't put it in namespace std.
+    using namespace std;  // NOLINT
+    return sign ? -ldexpf(mantissa, exponent) : ldexpf(mantissa, exponent);
+#else
+    constexpr uint32_t kMantissaMask =
+        (uint32_t(1) << (kTargetMantissaBits - 1)) - 1;
+    uint32_t flt = static_cast<uint32_t>(sign) << 31;
+    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.
+      flt += uint32_t{exponent + 127u + kTargetMantissaBits - 1} << 23;
+      mantissa &= kMantissaMask;
+    } else {
+      // subnormal value
+      assert(exponent == kMinNormalExponent);
+    }
+    flt += mantissa;
+    return absl::bit_cast<float>(flt);
+#endif  // ABSL_BIT_PACK_FLOATS
+  }
+};
+
+// Decimal-to-binary conversions require coercing powers of 10 into a mantissa
+// and a power of 2.  The two helper functions Power10Mantissa(n) and
+// Power10Exponent(n) perform this task.  Together, these represent a hand-
+// rolled floating point value which is equal to or just less than 10**n.
+//
+// The return values satisfy two range guarantees:
+//
+//   Power10Mantissa(n) * 2**Power10Exponent(n) <= 10**n
+//     < (Power10Mantissa(n) + 1) * 2**Power10Exponent(n)
+//
+//   2**63 <= Power10Mantissa(n) < 2**64.
+//
+// Lookups into the power-of-10 table must first check the Power10Overflow() and
+// Power10Underflow() functions, to avoid out-of-bounds table access.
+//
+// Indexes into these tables are biased by -kPower10TableMin, and the table has
+// values in the range [kPower10TableMin, kPower10TableMax].
+extern const uint64_t kPower10MantissaTable[];
+extern const int16_t kPower10ExponentTable[];
+
+// The smallest allowed value for use with the Power10Mantissa() and
+// Power10Exponent() functions below.  (If a smaller exponent is needed in
+// calculations, the end result is guaranteed to underflow.)
+constexpr int kPower10TableMin = -342;
+
+// The largest allowed value for use with the Power10Mantissa() and
+// Power10Exponent() functions below.  (If a smaller exponent is needed in
+// calculations, the end result is guaranteed to overflow.)
+constexpr int kPower10TableMax = 308;
+
+uint64_t Power10Mantissa(int n) {
+  return kPower10MantissaTable[n - kPower10TableMin];
+}
+
+int Power10Exponent(int n) {
+  return kPower10ExponentTable[n - kPower10TableMin];
+}
+
+// Returns true if n is large enough that 10**n always results in an IEEE
+// overflow.
+bool Power10Overflow(int n) { return n > kPower10TableMax; }
+
+// Returns true if n is small enough that 10**n times a ParsedFloat mantissa
+// always results in an IEEE underflow.
+bool Power10Underflow(int n) { return n < kPower10TableMin; }
+
+// Returns true if Power10Mantissa(n) * 2**Power10Exponent(n) is exactly equal
+// to 10**n numerically.  Put another way, this returns true if there is no
+// truncation error in Power10Mantissa(n).
+bool Power10Exact(int n) { return n >= 0 && n <= 27; }
+
+// Sentinel exponent values for representing numbers too large or too close to
+// zero to represent in a double.
+constexpr int kOverflow = 99999;
+constexpr int kUnderflow = -99999;
+
+// Struct representing the calculated conversion result of a positive (nonzero)
+// floating point number.
+//
+// The calculated number is mantissa * 2**exponent (mantissa is treated as an
+// integer.)  `mantissa` is chosen to be the correct width for the IEEE float
+// representation being calculated.  (`mantissa` will always have the same bit
+// width for normal values, and narrower bit widths for subnormals.)
+//
+// If the result of conversion was an underflow or overflow, exponent is set
+// to kUnderflow or kOverflow.
+struct CalculatedFloat {
+  uint64_t mantissa = 0;
+  int exponent = 0;
+};
+
+// Returns the bit width of the given uint128.  (Equivalently, returns 128
+// minus the number of leading zero bits.)
+int BitWidth(uint128 value) {
+  if (Uint128High64(value) == 0) {
+    return 64 - strings_internal::CountLeadingZeros64(Uint128Low64(value));
+  }
+  return 128 - strings_internal::CountLeadingZeros64(Uint128High64(value));
+}
+
+// Calculates how far to the right a mantissa needs to be shifted to create a
+// properly adjusted mantissa for an IEEE floating point number.
+//
+// `mantissa_width` is the bit width of the mantissa to be shifted, and
+// `binary_exponent` is the exponent of the number before the shift.
+//
+// This accounts for subnormal values, and will return a larger-than-normal
+// shift if binary_exponent would otherwise be too low.
+template <typename FloatType>
+int NormalizedShiftSize(int mantissa_width, int binary_exponent) {
+  const int normal_shift =
+      mantissa_width - FloatTraits<FloatType>::kTargetMantissaBits;
+  const int minimum_shift =
+      FloatTraits<FloatType>::kMinNormalExponent - binary_exponent;
+  return std::max(normal_shift, minimum_shift);
+}
+
+// Right shifts a uint128 so that it has the requested bit width.  (The
+// resulting value will have 128 - bit_width leading zeroes.)  The initial
+// `value` must be wider than the requested bit width.
+//
+// Returns the number of bits shifted.
+int TruncateToBitWidth(int bit_width, uint128* value) {
+  const int current_bit_width = BitWidth(*value);
+  const int shift = current_bit_width - bit_width;
+  *value >>= shift;
+  return shift;
+}
+
+// Checks if the given ParsedFloat represents one of the edge cases that are
+// not dependent on number base: zero, infinity, or NaN.  If so, sets *value
+// the appropriate double, and returns true.
+template <typename FloatType>
+bool HandleEdgeCase(const strings_internal::ParsedFloat& input, bool negative,
+                    FloatType* value) {
+  if (input.type == strings_internal::FloatType::kNan) {
+    // A bug in both clang and gcc would cause the compiler to optimize away the
+    // buffer we are building below.  Declaring the buffer volatile avoids the
+    // issue, and has no measurable performance impact in microbenchmarks.
+    //
+    // https://bugs.llvm.org/show_bug.cgi?id=37778
+    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86113
+    constexpr ptrdiff_t kNanBufferSize = 128;
+    volatile char n_char_sequence[kNanBufferSize];
+    if (input.subrange_begin == nullptr) {
+      n_char_sequence[0] = '\0';
+    } else {
+      ptrdiff_t nan_size = input.subrange_end - input.subrange_begin;
+      nan_size = std::min(nan_size, kNanBufferSize - 1);
+      std::copy_n(input.subrange_begin, nan_size, n_char_sequence);
+      n_char_sequence[nan_size] = '\0';
+    }
+    char* nan_argument = const_cast<char*>(n_char_sequence);
+    *value = negative ? -FloatTraits<FloatType>::MakeNan(nan_argument)
+                      : FloatTraits<FloatType>::MakeNan(nan_argument);
+    return true;
+  }
+  if (input.type == strings_internal::FloatType::kInfinity) {
+    *value = negative ? -std::numeric_limits<FloatType>::infinity()
+                      : std::numeric_limits<FloatType>::infinity();
+    return true;
+  }
+  if (input.mantissa == 0) {
+    *value = negative ? -0.0 : 0.0;
+    return true;
+  }
+  return false;
+}
+
+// Given a CalculatedFloat result of a from_chars conversion, generate the
+// correct output values.
+//
+// CalculatedFloat can represent an underflow or overflow, in which case the
+// error code in *result is set.  Otherwise, the calculated floating point
+// number is stored in *value.
+template <typename FloatType>
+void EncodeResult(const CalculatedFloat& calculated, bool negative,
+                  absl::from_chars_result* result, FloatType* value) {
+  if (calculated.exponent == kOverflow) {
+    result->ec = std::errc::result_out_of_range;
+    *value = negative ? -std::numeric_limits<FloatType>::max()
+                      : std::numeric_limits<FloatType>::max();
+    return;
+  } else if (calculated.mantissa == 0 || calculated.exponent == kUnderflow) {
+    result->ec = std::errc::result_out_of_range;
+    *value = negative ? -0.0 : 0.0;
+    return;
+  }
+  *value = FloatTraits<FloatType>::Make(calculated.mantissa,
+                                        calculated.exponent, negative);
+}
+
+// Returns the given uint128 shifted to the right by `shift` bits, and rounds
+// the remaining bits using round_to_nearest logic.  The value is returned as a
+// uint64_t, since this is the type used by this library for storing calculated
+// floating point mantissas.
+//
+// It is expected that the width of the input value shifted by `shift` will
+// be the correct bit-width for the target mantissa, which is strictly narrower
+// than a uint64_t.
+//
+// If `input_exact` is false, then a nonzero error epsilon is assumed.  For
+// rounding purposes, the true value being rounded is strictly greater than the
+// input value.  The error may represent a single lost carry bit.
+//
+// When input_exact, shifted bits of the form 1000000... represent a tie, which
+// is broken by rounding to even -- the rounding direction is chosen so the low
+// bit of the returned value is 0.
+//
+// When !input_exact, shifted bits of the form 10000000... represent a value
+// strictly greater than one half (due to the error epsilon), and so ties are
+// always broken by rounding up.
+//
+// When !input_exact, shifted bits of the form 01111111... are uncertain;
+// the true value may or may not be greater than 10000000..., due to the
+// possible lost carry bit.  The correct rounding direction is unknown.  In this
+// case, the result is rounded down, and `output_exact` is set to false.
+//
+// Zero and negative values of `shift` are accepted, in which case the word is
+// shifted left, as necessary.
+uint64_t ShiftRightAndRound(uint128 value, int shift, bool input_exact,
+                            bool* output_exact) {
+  if (shift <= 0) {
+    *output_exact = input_exact;
+    return static_cast<uint64_t>(value << -shift);
+  }
+  if (shift >= 128) {
+    // Exponent is so small that we are shifting away all significant bits.
+    // Answer will not be representable, even as a subnormal, so return a zero
+    // mantissa (which represents underflow).
+    *output_exact = true;
+    return 0;
+  }
+
+  *output_exact = true;
+  const uint128 shift_mask = (uint128(1) << shift) - 1;
+  const uint128 halfway_point = uint128(1) << (shift - 1);
+
+  const uint128 shifted_bits = value & shift_mask;
+  value >>= shift;
+  if (shifted_bits > halfway_point) {
+    // Shifted bits greater than 10000... require rounding up.
+    return static_cast<uint64_t>(value + 1);
+  }
+  if (shifted_bits == halfway_point) {
+    // In exact mode, shifted bits of 10000... mean we're exactly halfway
+    // between two numbers, and we must round to even.  So only round up if
+    // 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.
+    if ((value & 1) == 1 || !input_exact) {
+      ++value;
+    }
+    return static_cast<uint64_t>(value);
+  }
+  if (!input_exact && shifted_bits == halfway_point - 1) {
+    // Rounding direction is unclear, due to error.
+    *output_exact = false;
+  }
+  // Otherwise, round down.
+  return static_cast<uint64_t>(value);
+}
+
+// Checks if a floating point guess needs to be rounded up, using high precision
+// math.
+//
+// `guess_mantissa` and `guess_exponent` represent a candidate guess for the
+// number represented by `parsed_decimal`.
+//
+// The exact number represented by `parsed_decimal` must lie between the two
+// numbers:
+//   A = `guess_mantissa * 2**guess_exponent`
+//   B = `(guess_mantissa + 1) * 2**guess_exponent`
+//
+// This function returns false if `A` is the better guess, and true if `B` is
+// the better guess, with rounding ties broken by rounding to even.
+bool MustRoundUp(uint64_t guess_mantissa, int guess_exponent,
+                 const strings_internal::ParsedFloat& parsed_decimal) {
+  // 768 is the number of digits needed in the worst case.  We could determine a
+  // better limit dynamically based on the value of parsed_decimal.exponent.
+  // This would optimize pathological input cases only.  (Sane inputs won't have
+  // hundreds of digits of mantissa.)
+  absl::strings_internal::BigUnsigned<84> exact_mantissa;
+  int exact_exponent = exact_mantissa.ReadFloatMantissa(parsed_decimal, 768);
+
+  // Adjust the `guess` arguments to be halfway between A and B.
+  guess_mantissa = guess_mantissa * 2 + 1;
+  guess_exponent -= 1;
+
+  // In our comparison:
+  // lhs = exact = exact_mantissa * 10**exact_exponent
+  //             = exact_mantissa * 5**exact_exponent * 2**exact_exponent
+  // rhs = guess = guess_mantissa * 2**guess_exponent
+  //
+  // Because we are doing integer math, we can't directly deal with negative
+  // exponents.  We instead move these to the other side of the inequality.
+  absl::strings_internal::BigUnsigned<84>& lhs = exact_mantissa;
+  int comparison;
+  if (exact_exponent >= 0) {
+    lhs.MultiplyByFiveToTheNth(exact_exponent);
+    absl::strings_internal::BigUnsigned<84> rhs(guess_mantissa);
+    // There are powers of 2 on both sides of the inequality; reduce this to
+    // a single bit-shift.
+    if (exact_exponent > guess_exponent) {
+      lhs.ShiftLeft(exact_exponent - guess_exponent);
+    } else {
+      rhs.ShiftLeft(guess_exponent - exact_exponent);
+    }
+    comparison = Compare(lhs, rhs);
+  } else {
+    // Move the power of 5 to the other side of the equation, giving us:
+    // lhs = exact_mantissa * 2**exact_exponent
+    // rhs = guess_mantissa * 5**(-exact_exponent) * 2**guess_exponent
+    absl::strings_internal::BigUnsigned<84> rhs =
+        absl::strings_internal::BigUnsigned<84>::FiveToTheNth(-exact_exponent);
+    rhs.MultiplyBy(guess_mantissa);
+    if (exact_exponent > guess_exponent) {
+      lhs.ShiftLeft(exact_exponent - guess_exponent);
+    } else {
+      rhs.ShiftLeft(guess_exponent - exact_exponent);
+    }
+    comparison = Compare(lhs, rhs);
+  }
+  if (comparison < 0) {
+    return false;
+  } else if (comparison > 0) {
+    return true;
+  } else {
+    // When lhs == rhs, the decimal input is exactly between A and B.
+    // Round towards even -- round up only if the low bit of the initial
+    // `guess_mantissa` was a 1.  We shifted guess_mantissa left 1 bit at
+    // the beginning of this function, so test the 2nd bit here.
+    return (guess_mantissa & 2) == 2;
+  }
+}
+
+// Constructs a CalculatedFloat from a given mantissa and exponent, but
+// with the following normalizations applied:
+//
+// If rounding has caused mantissa to increase just past the allowed bit
+// width, shift and adjust exponent.
+//
+// If exponent is too high, sets kOverflow.
+//
+// If mantissa is zero (representing a non-zero value not representable, even
+// as a subnormal), sets kUnderflow.
+template <typename FloatType>
+CalculatedFloat CalculatedFloatFromRawValues(uint64_t mantissa, int exponent) {
+  CalculatedFloat result;
+  if (mantissa == uint64_t(1) << FloatTraits<FloatType>::kTargetMantissaBits) {
+    mantissa >>= 1;
+    exponent += 1;
+  }
+  if (exponent > FloatTraits<FloatType>::kMaxExponent) {
+    result.exponent = kOverflow;
+  } else if (mantissa == 0) {
+    result.exponent = kUnderflow;
+  } else {
+    result.exponent = exponent;
+    result.mantissa = mantissa;
+  }
+  return result;
+}
+
+template <typename FloatType>
+CalculatedFloat CalculateFromParsedHexadecimal(
+    const strings_internal::ParsedFloat& parsed_hex) {
+  uint64_t mantissa = parsed_hex.mantissa;
+  int exponent = parsed_hex.exponent;
+  int mantissa_width = 64 - strings_internal::CountLeadingZeros64(mantissa);
+  const int shift = NormalizedShiftSize<FloatType>(mantissa_width, exponent);
+  bool result_exact;
+  exponent += shift;
+  mantissa = ShiftRightAndRound(mantissa, shift,
+                                /* input exact= */ true, &result_exact);
+  // ParseFloat handles rounding in the hexadecimal case, so we don't have to
+  // check `result_exact` here.
+  return CalculatedFloatFromRawValues<FloatType>(mantissa, exponent);
+}
+
+template <typename FloatType>
+CalculatedFloat CalculateFromParsedDecimal(
+    const strings_internal::ParsedFloat& parsed_decimal) {
+  CalculatedFloat result;
+
+  // Large or small enough decimal exponents will always result in overflow
+  // or underflow.
+  if (Power10Underflow(parsed_decimal.exponent)) {
+    result.exponent = kUnderflow;
+    return result;
+  } else if (Power10Overflow(parsed_decimal.exponent)) {
+    result.exponent = kOverflow;
+    return result;
+  }
+
+  // Otherwise convert our power of 10 into a power of 2 times an integer
+  // mantissa, and multiply this by our parsed decimal mantissa.
+  uint128 wide_binary_mantissa = parsed_decimal.mantissa;
+  wide_binary_mantissa *= Power10Mantissa(parsed_decimal.exponent);
+  int binary_exponent = Power10Exponent(parsed_decimal.exponent);
+
+  // Discard bits that are inaccurate due to truncation error.  The magic
+  // `mantissa_width` constants below are justified in charconv_algorithm.md.
+  // They represent the number of bits in `wide_binary_mantissa` that are
+  // guaranteed to be unaffected by error propagation.
+  bool mantissa_exact;
+  int mantissa_width;
+  if (parsed_decimal.subrange_begin) {
+    // Truncated mantissa
+    mantissa_width = 58;
+    mantissa_exact = false;
+    binary_exponent +=
+        TruncateToBitWidth(mantissa_width, &wide_binary_mantissa);
+  } else if (!Power10Exact(parsed_decimal.exponent)) {
+    // Exact mantissa, truncated power of ten
+    mantissa_width = 63;
+    mantissa_exact = false;
+    binary_exponent +=
+        TruncateToBitWidth(mantissa_width, &wide_binary_mantissa);
+  } else {
+    // Product is exact
+    mantissa_width = BitWidth(wide_binary_mantissa);
+    mantissa_exact = true;
+  }
+
+  // Shift into an FloatType-sized mantissa, and round to nearest.
+  const int shift =
+      NormalizedShiftSize<FloatType>(mantissa_width, binary_exponent);
+  bool result_exact;
+  binary_exponent += shift;
+  uint64_t binary_mantissa = ShiftRightAndRound(wide_binary_mantissa, shift,
+                                                mantissa_exact, &result_exact);
+  if (!result_exact) {
+    // We could not determine the rounding direction using int128 math.  Use
+    // full resolution math instead.
+    if (MustRoundUp(binary_mantissa, binary_exponent, parsed_decimal)) {
+      binary_mantissa += 1;
+    }
+  }
+
+  return CalculatedFloatFromRawValues<FloatType>(binary_mantissa,
+                                                 binary_exponent);
+}
+
+template <typename FloatType>
+from_chars_result FromCharsImpl(const char* first, const char* last,
+                                FloatType& value, chars_format fmt_flags) {
+  from_chars_result result;
+  result.ptr = first;  // overwritten on successful parse
+  result.ec = std::errc();
+
+  bool negative = false;
+  if (first != last && *first == '-') {
+    ++first;
+    negative = true;
+  }
+  // If the `hex` flag is *not* set, then we will accept a 0x prefix and try
+  // to parse a hexadecimal float.
+  if ((fmt_flags & chars_format::hex) == chars_format{} && last - first >= 2 &&
+      *first == '0' && (first[1] == 'x' || first[1] == 'X')) {
+    const char* hex_first = first + 2;
+    strings_internal::ParsedFloat hex_parse =
+        strings_internal::ParseFloat<16>(hex_first, last, fmt_flags);
+    if (hex_parse.end == nullptr ||
+        hex_parse.type != strings_internal::FloatType::kNumber) {
+      // Either we failed to parse a hex float after the "0x", or we read
+      // "0xinf" or "0xnan" which we don't want to match.
+      //
+      // However, a std::string that begins with "0x" also begins with "0", which
+      // is normally a valid match for the number zero.  So we want these
+      // strings to match zero unless fmt_flags is `scientific`.  (This flag
+      // means an exponent is required, which the std::string "0" does not have.)
+      if (fmt_flags == chars_format::scientific) {
+        result.ec = std::errc::invalid_argument;
+      } else {
+        result.ptr = first + 1;
+        value = negative ? -0.0 : 0.0;
+      }
+      return result;
+    }
+    // We matched a value.
+    result.ptr = hex_parse.end;
+    if (HandleEdgeCase(hex_parse, negative, &value)) {
+      return result;
+    }
+    CalculatedFloat calculated =
+        CalculateFromParsedHexadecimal<FloatType>(hex_parse);
+    EncodeResult(calculated, negative, &result, &value);
+    return result;
+  }
+  // Otherwise, we choose the number base based on the flags.
+  if ((fmt_flags & chars_format::hex) == chars_format::hex) {
+    strings_internal::ParsedFloat hex_parse =
+        strings_internal::ParseFloat<16>(first, last, fmt_flags);
+    if (hex_parse.end == nullptr) {
+      result.ec = std::errc::invalid_argument;
+      return result;
+    }
+    result.ptr = hex_parse.end;
+    if (HandleEdgeCase(hex_parse, negative, &value)) {
+      return result;
+    }
+    CalculatedFloat calculated =
+        CalculateFromParsedHexadecimal<FloatType>(hex_parse);
+    EncodeResult(calculated, negative, &result, &value);
+    return result;
+  } else {
+    strings_internal::ParsedFloat decimal_parse =
+        strings_internal::ParseFloat<10>(first, last, fmt_flags);
+    if (decimal_parse.end == nullptr) {
+      result.ec = std::errc::invalid_argument;
+      return result;
+    }
+    result.ptr = decimal_parse.end;
+    if (HandleEdgeCase(decimal_parse, negative, &value)) {
+      return result;
+    }
+    CalculatedFloat calculated =
+        CalculateFromParsedDecimal<FloatType>(decimal_parse);
+    EncodeResult(calculated, negative, &result, &value);
+    return result;
+  }
+  return result;
+}
+}  // namespace
+
+from_chars_result from_chars(const char* first, const char* last, double& value,
+                             chars_format fmt) {
+  return FromCharsImpl(first, last, value, fmt);
+}
+
+from_chars_result from_chars(const char* first, const char* last, float& value,
+                             chars_format fmt) {
+  return FromCharsImpl(first, last, value, fmt);
+}
+
+namespace {
+
+// Table of powers of 10, from kPower10TableMin to kPower10TableMax.
+//
+// kPower10MantissaTable[i - kPower10TableMin] stores the 64-bit mantissa (high
+// bit always on), and kPower10ExponentTable[i - kPower10TableMin] stores the
+// power-of-two exponent.  For a given number i, this gives the unique mantissa
+// and exponent such that mantissa * 2**exponent <= 10**i < (mantissa + 1) *
+// 2**exponent.
+
+const uint64_t kPower10MantissaTable[] = {
+    0xeef453d6923bd65aU, 0x9558b4661b6565f8U, 0xbaaee17fa23ebf76U,
+    0xe95a99df8ace6f53U, 0x91d8a02bb6c10594U, 0xb64ec836a47146f9U,
+    0xe3e27a444d8d98b7U, 0x8e6d8c6ab0787f72U, 0xb208ef855c969f4fU,
+    0xde8b2b66b3bc4723U, 0x8b16fb203055ac76U, 0xaddcb9e83c6b1793U,
+    0xd953e8624b85dd78U, 0x87d4713d6f33aa6bU, 0xa9c98d8ccb009506U,
+    0xd43bf0effdc0ba48U, 0x84a57695fe98746dU, 0xa5ced43b7e3e9188U,
+    0xcf42894a5dce35eaU, 0x818995ce7aa0e1b2U, 0xa1ebfb4219491a1fU,
+    0xca66fa129f9b60a6U, 0xfd00b897478238d0U, 0x9e20735e8cb16382U,
+    0xc5a890362fddbc62U, 0xf712b443bbd52b7bU, 0x9a6bb0aa55653b2dU,
+    0xc1069cd4eabe89f8U, 0xf148440a256e2c76U, 0x96cd2a865764dbcaU,
+    0xbc807527ed3e12bcU, 0xeba09271e88d976bU, 0x93445b8731587ea3U,
+    0xb8157268fdae9e4cU, 0xe61acf033d1a45dfU, 0x8fd0c16206306babU,
+    0xb3c4f1ba87bc8696U, 0xe0b62e2929aba83cU, 0x8c71dcd9ba0b4925U,
+    0xaf8e5410288e1b6fU, 0xdb71e91432b1a24aU, 0x892731ac9faf056eU,
+    0xab70fe17c79ac6caU, 0xd64d3d9db981787dU, 0x85f0468293f0eb4eU,
+    0xa76c582338ed2621U, 0xd1476e2c07286faaU, 0x82cca4db847945caU,
+    0xa37fce126597973cU, 0xcc5fc196fefd7d0cU, 0xff77b1fcbebcdc4fU,
+    0x9faacf3df73609b1U, 0xc795830d75038c1dU, 0xf97ae3d0d2446f25U,
+    0x9becce62836ac577U, 0xc2e801fb244576d5U, 0xf3a20279ed56d48aU,
+    0x9845418c345644d6U, 0xbe5691ef416bd60cU, 0xedec366b11c6cb8fU,
+    0x94b3a202eb1c3f39U, 0xb9e08a83a5e34f07U, 0xe858ad248f5c22c9U,
+    0x91376c36d99995beU, 0xb58547448ffffb2dU, 0xe2e69915b3fff9f9U,
+    0x8dd01fad907ffc3bU, 0xb1442798f49ffb4aU, 0xdd95317f31c7fa1dU,
+    0x8a7d3eef7f1cfc52U, 0xad1c8eab5ee43b66U, 0xd863b256369d4a40U,
+    0x873e4f75e2224e68U, 0xa90de3535aaae202U, 0xd3515c2831559a83U,
+    0x8412d9991ed58091U, 0xa5178fff668ae0b6U, 0xce5d73ff402d98e3U,
+    0x80fa687f881c7f8eU, 0xa139029f6a239f72U, 0xc987434744ac874eU,
+    0xfbe9141915d7a922U, 0x9d71ac8fada6c9b5U, 0xc4ce17b399107c22U,
+    0xf6019da07f549b2bU, 0x99c102844f94e0fbU, 0xc0314325637a1939U,
+    0xf03d93eebc589f88U, 0x96267c7535b763b5U, 0xbbb01b9283253ca2U,
+    0xea9c227723ee8bcbU, 0x92a1958a7675175fU, 0xb749faed14125d36U,
+    0xe51c79a85916f484U, 0x8f31cc0937ae58d2U, 0xb2fe3f0b8599ef07U,
+    0xdfbdcece67006ac9U, 0x8bd6a141006042bdU, 0xaecc49914078536dU,
+    0xda7f5bf590966848U, 0x888f99797a5e012dU, 0xaab37fd7d8f58178U,
+    0xd5605fcdcf32e1d6U, 0x855c3be0a17fcd26U, 0xa6b34ad8c9dfc06fU,
+    0xd0601d8efc57b08bU, 0x823c12795db6ce57U, 0xa2cb1717b52481edU,
+    0xcb7ddcdda26da268U, 0xfe5d54150b090b02U, 0x9efa548d26e5a6e1U,
+    0xc6b8e9b0709f109aU, 0xf867241c8cc6d4c0U, 0x9b407691d7fc44f8U,
+    0xc21094364dfb5636U, 0xf294b943e17a2bc4U, 0x979cf3ca6cec5b5aU,
+    0xbd8430bd08277231U, 0xece53cec4a314ebdU, 0x940f4613ae5ed136U,
+    0xb913179899f68584U, 0xe757dd7ec07426e5U, 0x9096ea6f3848984fU,
+    0xb4bca50b065abe63U, 0xe1ebce4dc7f16dfbU, 0x8d3360f09cf6e4bdU,
+    0xb080392cc4349decU, 0xdca04777f541c567U, 0x89e42caaf9491b60U,
+    0xac5d37d5b79b6239U, 0xd77485cb25823ac7U, 0x86a8d39ef77164bcU,
+    0xa8530886b54dbdebU, 0xd267caa862a12d66U, 0x8380dea93da4bc60U,
+    0xa46116538d0deb78U, 0xcd795be870516656U, 0x806bd9714632dff6U,
+    0xa086cfcd97bf97f3U, 0xc8a883c0fdaf7df0U, 0xfad2a4b13d1b5d6cU,
+    0x9cc3a6eec6311a63U, 0xc3f490aa77bd60fcU, 0xf4f1b4d515acb93bU,
+    0x991711052d8bf3c5U, 0xbf5cd54678eef0b6U, 0xef340a98172aace4U,
+    0x9580869f0e7aac0eU, 0xbae0a846d2195712U, 0xe998d258869facd7U,
+    0x91ff83775423cc06U, 0xb67f6455292cbf08U, 0xe41f3d6a7377eecaU,
+    0x8e938662882af53eU, 0xb23867fb2a35b28dU, 0xdec681f9f4c31f31U,
+    0x8b3c113c38f9f37eU, 0xae0b158b4738705eU, 0xd98ddaee19068c76U,
+    0x87f8a8d4cfa417c9U, 0xa9f6d30a038d1dbcU, 0xd47487cc8470652bU,
+    0x84c8d4dfd2c63f3bU, 0xa5fb0a17c777cf09U, 0xcf79cc9db955c2ccU,
+    0x81ac1fe293d599bfU, 0xa21727db38cb002fU, 0xca9cf1d206fdc03bU,
+    0xfd442e4688bd304aU, 0x9e4a9cec15763e2eU, 0xc5dd44271ad3cdbaU,
+    0xf7549530e188c128U, 0x9a94dd3e8cf578b9U, 0xc13a148e3032d6e7U,
+    0xf18899b1bc3f8ca1U, 0x96f5600f15a7b7e5U, 0xbcb2b812db11a5deU,
+    0xebdf661791d60f56U, 0x936b9fcebb25c995U, 0xb84687c269ef3bfbU,
+    0xe65829b3046b0afaU, 0x8ff71a0fe2c2e6dcU, 0xb3f4e093db73a093U,
+    0xe0f218b8d25088b8U, 0x8c974f7383725573U, 0xafbd2350644eeacfU,
+    0xdbac6c247d62a583U, 0x894bc396ce5da772U, 0xab9eb47c81f5114fU,
+    0xd686619ba27255a2U, 0x8613fd0145877585U, 0xa798fc4196e952e7U,
+    0xd17f3b51fca3a7a0U, 0x82ef85133de648c4U, 0xa3ab66580d5fdaf5U,
+    0xcc963fee10b7d1b3U, 0xffbbcfe994e5c61fU, 0x9fd561f1fd0f9bd3U,
+    0xc7caba6e7c5382c8U, 0xf9bd690a1b68637bU, 0x9c1661a651213e2dU,
+    0xc31bfa0fe5698db8U, 0xf3e2f893dec3f126U, 0x986ddb5c6b3a76b7U,
+    0xbe89523386091465U, 0xee2ba6c0678b597fU, 0x94db483840b717efU,
+    0xba121a4650e4ddebU, 0xe896a0d7e51e1566U, 0x915e2486ef32cd60U,
+    0xb5b5ada8aaff80b8U, 0xe3231912d5bf60e6U, 0x8df5efabc5979c8fU,
+    0xb1736b96b6fd83b3U, 0xddd0467c64bce4a0U, 0x8aa22c0dbef60ee4U,
+    0xad4ab7112eb3929dU, 0xd89d64d57a607744U, 0x87625f056c7c4a8bU,
+    0xa93af6c6c79b5d2dU, 0xd389b47879823479U, 0x843610cb4bf160cbU,
+    0xa54394fe1eedb8feU, 0xce947a3da6a9273eU, 0x811ccc668829b887U,
+    0xa163ff802a3426a8U, 0xc9bcff6034c13052U, 0xfc2c3f3841f17c67U,
+    0x9d9ba7832936edc0U, 0xc5029163f384a931U, 0xf64335bcf065d37dU,
+    0x99ea0196163fa42eU, 0xc06481fb9bcf8d39U, 0xf07da27a82c37088U,
+    0x964e858c91ba2655U, 0xbbe226efb628afeaU, 0xeadab0aba3b2dbe5U,
+    0x92c8ae6b464fc96fU, 0xb77ada0617e3bbcbU, 0xe55990879ddcaabdU,
+    0x8f57fa54c2a9eab6U, 0xb32df8e9f3546564U, 0xdff9772470297ebdU,
+    0x8bfbea76c619ef36U, 0xaefae51477a06b03U, 0xdab99e59958885c4U,
+    0x88b402f7fd75539bU, 0xaae103b5fcd2a881U, 0xd59944a37c0752a2U,
+    0x857fcae62d8493a5U, 0xa6dfbd9fb8e5b88eU, 0xd097ad07a71f26b2U,
+    0x825ecc24c873782fU, 0xa2f67f2dfa90563bU, 0xcbb41ef979346bcaU,
+    0xfea126b7d78186bcU, 0x9f24b832e6b0f436U, 0xc6ede63fa05d3143U,
+    0xf8a95fcf88747d94U, 0x9b69dbe1b548ce7cU, 0xc24452da229b021bU,
+    0xf2d56790ab41c2a2U, 0x97c560ba6b0919a5U, 0xbdb6b8e905cb600fU,
+    0xed246723473e3813U, 0x9436c0760c86e30bU, 0xb94470938fa89bceU,
+    0xe7958cb87392c2c2U, 0x90bd77f3483bb9b9U, 0xb4ecd5f01a4aa828U,
+    0xe2280b6c20dd5232U, 0x8d590723948a535fU, 0xb0af48ec79ace837U,
+    0xdcdb1b2798182244U, 0x8a08f0f8bf0f156bU, 0xac8b2d36eed2dac5U,
+    0xd7adf884aa879177U, 0x86ccbb52ea94baeaU, 0xa87fea27a539e9a5U,
+    0xd29fe4b18e88640eU, 0x83a3eeeef9153e89U, 0xa48ceaaab75a8e2bU,
+    0xcdb02555653131b6U, 0x808e17555f3ebf11U, 0xa0b19d2ab70e6ed6U,
+    0xc8de047564d20a8bU, 0xfb158592be068d2eU, 0x9ced737bb6c4183dU,
+    0xc428d05aa4751e4cU, 0xf53304714d9265dfU, 0x993fe2c6d07b7fabU,
+    0xbf8fdb78849a5f96U, 0xef73d256a5c0f77cU, 0x95a8637627989aadU,
+    0xbb127c53b17ec159U, 0xe9d71b689dde71afU, 0x9226712162ab070dU,
+    0xb6b00d69bb55c8d1U, 0xe45c10c42a2b3b05U, 0x8eb98a7a9a5b04e3U,
+    0xb267ed1940f1c61cU, 0xdf01e85f912e37a3U, 0x8b61313bbabce2c6U,
+    0xae397d8aa96c1b77U, 0xd9c7dced53c72255U, 0x881cea14545c7575U,
+    0xaa242499697392d2U, 0xd4ad2dbfc3d07787U, 0x84ec3c97da624ab4U,
+    0xa6274bbdd0fadd61U, 0xcfb11ead453994baU, 0x81ceb32c4b43fcf4U,
+    0xa2425ff75e14fc31U, 0xcad2f7f5359a3b3eU, 0xfd87b5f28300ca0dU,
+    0x9e74d1b791e07e48U, 0xc612062576589ddaU, 0xf79687aed3eec551U,
+    0x9abe14cd44753b52U, 0xc16d9a0095928a27U, 0xf1c90080baf72cb1U,
+    0x971da05074da7beeU, 0xbce5086492111aeaU, 0xec1e4a7db69561a5U,
+    0x9392ee8e921d5d07U, 0xb877aa3236a4b449U, 0xe69594bec44de15bU,
+    0x901d7cf73ab0acd9U, 0xb424dc35095cd80fU, 0xe12e13424bb40e13U,
+    0x8cbccc096f5088cbU, 0xafebff0bcb24aafeU, 0xdbe6fecebdedd5beU,
+    0x89705f4136b4a597U, 0xabcc77118461cefcU, 0xd6bf94d5e57a42bcU,
+    0x8637bd05af6c69b5U, 0xa7c5ac471b478423U, 0xd1b71758e219652bU,
+    0x83126e978d4fdf3bU, 0xa3d70a3d70a3d70aU, 0xccccccccccccccccU,
+    0x8000000000000000U, 0xa000000000000000U, 0xc800000000000000U,
+    0xfa00000000000000U, 0x9c40000000000000U, 0xc350000000000000U,
+    0xf424000000000000U, 0x9896800000000000U, 0xbebc200000000000U,
+    0xee6b280000000000U, 0x9502f90000000000U, 0xba43b74000000000U,
+    0xe8d4a51000000000U, 0x9184e72a00000000U, 0xb5e620f480000000U,
+    0xe35fa931a0000000U, 0x8e1bc9bf04000000U, 0xb1a2bc2ec5000000U,
+    0xde0b6b3a76400000U, 0x8ac7230489e80000U, 0xad78ebc5ac620000U,
+    0xd8d726b7177a8000U, 0x878678326eac9000U, 0xa968163f0a57b400U,
+    0xd3c21bcecceda100U, 0x84595161401484a0U, 0xa56fa5b99019a5c8U,
+    0xcecb8f27f4200f3aU, 0x813f3978f8940984U, 0xa18f07d736b90be5U,
+    0xc9f2c9cd04674edeU, 0xfc6f7c4045812296U, 0x9dc5ada82b70b59dU,
+    0xc5371912364ce305U, 0xf684df56c3e01bc6U, 0x9a130b963a6c115cU,
+    0xc097ce7bc90715b3U, 0xf0bdc21abb48db20U, 0x96769950b50d88f4U,
+    0xbc143fa4e250eb31U, 0xeb194f8e1ae525fdU, 0x92efd1b8d0cf37beU,
+    0xb7abc627050305adU, 0xe596b7b0c643c719U, 0x8f7e32ce7bea5c6fU,
+    0xb35dbf821ae4f38bU, 0xe0352f62a19e306eU, 0x8c213d9da502de45U,
+    0xaf298d050e4395d6U, 0xdaf3f04651d47b4cU, 0x88d8762bf324cd0fU,
+    0xab0e93b6efee0053U, 0xd5d238a4abe98068U, 0x85a36366eb71f041U,
+    0xa70c3c40a64e6c51U, 0xd0cf4b50cfe20765U, 0x82818f1281ed449fU,
+    0xa321f2d7226895c7U, 0xcbea6f8ceb02bb39U, 0xfee50b7025c36a08U,
+    0x9f4f2726179a2245U, 0xc722f0ef9d80aad6U, 0xf8ebad2b84e0d58bU,
+    0x9b934c3b330c8577U, 0xc2781f49ffcfa6d5U, 0xf316271c7fc3908aU,
+    0x97edd871cfda3a56U, 0xbde94e8e43d0c8ecU, 0xed63a231d4c4fb27U,
+    0x945e455f24fb1cf8U, 0xb975d6b6ee39e436U, 0xe7d34c64a9c85d44U,
+    0x90e40fbeea1d3a4aU, 0xb51d13aea4a488ddU, 0xe264589a4dcdab14U,
+    0x8d7eb76070a08aecU, 0xb0de65388cc8ada8U, 0xdd15fe86affad912U,
+    0x8a2dbf142dfcc7abU, 0xacb92ed9397bf996U, 0xd7e77a8f87daf7fbU,
+    0x86f0ac99b4e8dafdU, 0xa8acd7c0222311bcU, 0xd2d80db02aabd62bU,
+    0x83c7088e1aab65dbU, 0xa4b8cab1a1563f52U, 0xcde6fd5e09abcf26U,
+    0x80b05e5ac60b6178U, 0xa0dc75f1778e39d6U, 0xc913936dd571c84cU,
+    0xfb5878494ace3a5fU, 0x9d174b2dcec0e47bU, 0xc45d1df942711d9aU,
+    0xf5746577930d6500U, 0x9968bf6abbe85f20U, 0xbfc2ef456ae276e8U,
+    0xefb3ab16c59b14a2U, 0x95d04aee3b80ece5U, 0xbb445da9ca61281fU,
+    0xea1575143cf97226U, 0x924d692ca61be758U, 0xb6e0c377cfa2e12eU,
+    0xe498f455c38b997aU, 0x8edf98b59a373fecU, 0xb2977ee300c50fe7U,
+    0xdf3d5e9bc0f653e1U, 0x8b865b215899f46cU, 0xae67f1e9aec07187U,
+    0xda01ee641a708de9U, 0x884134fe908658b2U, 0xaa51823e34a7eedeU,
+    0xd4e5e2cdc1d1ea96U, 0x850fadc09923329eU, 0xa6539930bf6bff45U,
+    0xcfe87f7cef46ff16U, 0x81f14fae158c5f6eU, 0xa26da3999aef7749U,
+    0xcb090c8001ab551cU, 0xfdcb4fa002162a63U, 0x9e9f11c4014dda7eU,
+    0xc646d63501a1511dU, 0xf7d88bc24209a565U, 0x9ae757596946075fU,
+    0xc1a12d2fc3978937U, 0xf209787bb47d6b84U, 0x9745eb4d50ce6332U,
+    0xbd176620a501fbffU, 0xec5d3fa8ce427affU, 0x93ba47c980e98cdfU,
+    0xb8a8d9bbe123f017U, 0xe6d3102ad96cec1dU, 0x9043ea1ac7e41392U,
+    0xb454e4a179dd1877U, 0xe16a1dc9d8545e94U, 0x8ce2529e2734bb1dU,
+    0xb01ae745b101e9e4U, 0xdc21a1171d42645dU, 0x899504ae72497ebaU,
+    0xabfa45da0edbde69U, 0xd6f8d7509292d603U, 0x865b86925b9bc5c2U,
+    0xa7f26836f282b732U, 0xd1ef0244af2364ffU, 0x8335616aed761f1fU,
+    0xa402b9c5a8d3a6e7U, 0xcd036837130890a1U, 0x802221226be55a64U,
+    0xa02aa96b06deb0fdU, 0xc83553c5c8965d3dU, 0xfa42a8b73abbf48cU,
+    0x9c69a97284b578d7U, 0xc38413cf25e2d70dU, 0xf46518c2ef5b8cd1U,
+    0x98bf2f79d5993802U, 0xbeeefb584aff8603U, 0xeeaaba2e5dbf6784U,
+    0x952ab45cfa97a0b2U, 0xba756174393d88dfU, 0xe912b9d1478ceb17U,
+    0x91abb422ccb812eeU, 0xb616a12b7fe617aaU, 0xe39c49765fdf9d94U,
+    0x8e41ade9fbebc27dU, 0xb1d219647ae6b31cU, 0xde469fbd99a05fe3U,
+    0x8aec23d680043beeU, 0xada72ccc20054ae9U, 0xd910f7ff28069da4U,
+    0x87aa9aff79042286U, 0xa99541bf57452b28U, 0xd3fa922f2d1675f2U,
+    0x847c9b5d7c2e09b7U, 0xa59bc234db398c25U, 0xcf02b2c21207ef2eU,
+    0x8161afb94b44f57dU, 0xa1ba1ba79e1632dcU, 0xca28a291859bbf93U,
+    0xfcb2cb35e702af78U, 0x9defbf01b061adabU, 0xc56baec21c7a1916U,
+    0xf6c69a72a3989f5bU, 0x9a3c2087a63f6399U, 0xc0cb28a98fcf3c7fU,
+    0xf0fdf2d3f3c30b9fU, 0x969eb7c47859e743U, 0xbc4665b596706114U,
+    0xeb57ff22fc0c7959U, 0x9316ff75dd87cbd8U, 0xb7dcbf5354e9beceU,
+    0xe5d3ef282a242e81U, 0x8fa475791a569d10U, 0xb38d92d760ec4455U,
+    0xe070f78d3927556aU, 0x8c469ab843b89562U, 0xaf58416654a6babbU,
+    0xdb2e51bfe9d0696aU, 0x88fcf317f22241e2U, 0xab3c2fddeeaad25aU,
+    0xd60b3bd56a5586f1U, 0x85c7056562757456U, 0xa738c6bebb12d16cU,
+    0xd106f86e69d785c7U, 0x82a45b450226b39cU, 0xa34d721642b06084U,
+    0xcc20ce9bd35c78a5U, 0xff290242c83396ceU, 0x9f79a169bd203e41U,
+    0xc75809c42c684dd1U, 0xf92e0c3537826145U, 0x9bbcc7a142b17ccbU,
+    0xc2abf989935ddbfeU, 0xf356f7ebf83552feU, 0x98165af37b2153deU,
+    0xbe1bf1b059e9a8d6U, 0xeda2ee1c7064130cU, 0x9485d4d1c63e8be7U,
+    0xb9a74a0637ce2ee1U, 0xe8111c87c5c1ba99U, 0x910ab1d4db9914a0U,
+    0xb54d5e4a127f59c8U, 0xe2a0b5dc971f303aU, 0x8da471a9de737e24U,
+    0xb10d8e1456105dadU, 0xdd50f1996b947518U, 0x8a5296ffe33cc92fU,
+    0xace73cbfdc0bfb7bU, 0xd8210befd30efa5aU, 0x8714a775e3e95c78U,
+    0xa8d9d1535ce3b396U, 0xd31045a8341ca07cU, 0x83ea2b892091e44dU,
+    0xa4e4b66b68b65d60U, 0xce1de40642e3f4b9U, 0x80d2ae83e9ce78f3U,
+    0xa1075a24e4421730U, 0xc94930ae1d529cfcU, 0xfb9b7cd9a4a7443cU,
+    0x9d412e0806e88aa5U, 0xc491798a08a2ad4eU, 0xf5b5d7ec8acb58a2U,
+    0x9991a6f3d6bf1765U, 0xbff610b0cc6edd3fU, 0xeff394dcff8a948eU,
+    0x95f83d0a1fb69cd9U, 0xbb764c4ca7a4440fU, 0xea53df5fd18d5513U,
+    0x92746b9be2f8552cU, 0xb7118682dbb66a77U, 0xe4d5e82392a40515U,
+    0x8f05b1163ba6832dU, 0xb2c71d5bca9023f8U, 0xdf78e4b2bd342cf6U,
+    0x8bab8eefb6409c1aU, 0xae9672aba3d0c320U, 0xda3c0f568cc4f3e8U,
+    0x8865899617fb1871U, 0xaa7eebfb9df9de8dU, 0xd51ea6fa85785631U,
+    0x8533285c936b35deU, 0xa67ff273b8460356U, 0xd01fef10a657842cU,
+    0x8213f56a67f6b29bU, 0xa298f2c501f45f42U, 0xcb3f2f7642717713U,
+    0xfe0efb53d30dd4d7U, 0x9ec95d1463e8a506U, 0xc67bb4597ce2ce48U,
+    0xf81aa16fdc1b81daU, 0x9b10a4e5e9913128U, 0xc1d4ce1f63f57d72U,
+    0xf24a01a73cf2dccfU, 0x976e41088617ca01U, 0xbd49d14aa79dbc82U,
+    0xec9c459d51852ba2U, 0x93e1ab8252f33b45U, 0xb8da1662e7b00a17U,
+    0xe7109bfba19c0c9dU, 0x906a617d450187e2U, 0xb484f9dc9641e9daU,
+    0xe1a63853bbd26451U, 0x8d07e33455637eb2U, 0xb049dc016abc5e5fU,
+    0xdc5c5301c56b75f7U, 0x89b9b3e11b6329baU, 0xac2820d9623bf429U,
+    0xd732290fbacaf133U, 0x867f59a9d4bed6c0U, 0xa81f301449ee8c70U,
+    0xd226fc195c6a2f8cU, 0x83585d8fd9c25db7U, 0xa42e74f3d032f525U,
+    0xcd3a1230c43fb26fU, 0x80444b5e7aa7cf85U, 0xa0555e361951c366U,
+    0xc86ab5c39fa63440U, 0xfa856334878fc150U, 0x9c935e00d4b9d8d2U,
+    0xc3b8358109e84f07U, 0xf4a642e14c6262c8U, 0x98e7e9cccfbd7dbdU,
+    0xbf21e44003acdd2cU, 0xeeea5d5004981478U, 0x95527a5202df0ccbU,
+    0xbaa718e68396cffdU, 0xe950df20247c83fdU, 0x91d28b7416cdd27eU,
+    0xb6472e511c81471dU, 0xe3d8f9e563a198e5U, 0x8e679c2f5e44ff8fU,
+};
+
+const int16_t kPower10ExponentTable[] = {
+    -1200, -1196, -1193, -1190, -1186, -1183, -1180, -1176, -1173, -1170, -1166,
+    -1163, -1160, -1156, -1153, -1150, -1146, -1143, -1140, -1136, -1133, -1130,
+    -1127, -1123, -1120, -1117, -1113, -1110, -1107, -1103, -1100, -1097, -1093,
+    -1090, -1087, -1083, -1080, -1077, -1073, -1070, -1067, -1063, -1060, -1057,
+    -1053, -1050, -1047, -1043, -1040, -1037, -1034, -1030, -1027, -1024, -1020,
+    -1017, -1014, -1010, -1007, -1004, -1000, -997,  -994,  -990,  -987,  -984,
+    -980,  -977,  -974,  -970,  -967,  -964,  -960,  -957,  -954,  -950,  -947,
+    -944,  -940,  -937,  -934,  -931,  -927,  -924,  -921,  -917,  -914,  -911,
+    -907,  -904,  -901,  -897,  -894,  -891,  -887,  -884,  -881,  -877,  -874,
+    -871,  -867,  -864,  -861,  -857,  -854,  -851,  -847,  -844,  -841,  -838,
+    -834,  -831,  -828,  -824,  -821,  -818,  -814,  -811,  -808,  -804,  -801,
+    -798,  -794,  -791,  -788,  -784,  -781,  -778,  -774,  -771,  -768,  -764,
+    -761,  -758,  -754,  -751,  -748,  -744,  -741,  -738,  -735,  -731,  -728,
+    -725,  -721,  -718,  -715,  -711,  -708,  -705,  -701,  -698,  -695,  -691,
+    -688,  -685,  -681,  -678,  -675,  -671,  -668,  -665,  -661,  -658,  -655,
+    -651,  -648,  -645,  -642,  -638,  -635,  -632,  -628,  -625,  -622,  -618,
+    -615,  -612,  -608,  -605,  -602,  -598,  -595,  -592,  -588,  -585,  -582,
+    -578,  -575,  -572,  -568,  -565,  -562,  -558,  -555,  -552,  -549,  -545,
+    -542,  -539,  -535,  -532,  -529,  -525,  -522,  -519,  -515,  -512,  -509,
+    -505,  -502,  -499,  -495,  -492,  -489,  -485,  -482,  -479,  -475,  -472,
+    -469,  -465,  -462,  -459,  -455,  -452,  -449,  -446,  -442,  -439,  -436,
+    -432,  -429,  -426,  -422,  -419,  -416,  -412,  -409,  -406,  -402,  -399,
+    -396,  -392,  -389,  -386,  -382,  -379,  -376,  -372,  -369,  -366,  -362,
+    -359,  -356,  -353,  -349,  -346,  -343,  -339,  -336,  -333,  -329,  -326,
+    -323,  -319,  -316,  -313,  -309,  -306,  -303,  -299,  -296,  -293,  -289,
+    -286,  -283,  -279,  -276,  -273,  -269,  -266,  -263,  -259,  -256,  -253,
+    -250,  -246,  -243,  -240,  -236,  -233,  -230,  -226,  -223,  -220,  -216,
+    -213,  -210,  -206,  -203,  -200,  -196,  -193,  -190,  -186,  -183,  -180,
+    -176,  -173,  -170,  -166,  -163,  -160,  -157,  -153,  -150,  -147,  -143,
+    -140,  -137,  -133,  -130,  -127,  -123,  -120,  -117,  -113,  -110,  -107,
+    -103,  -100,  -97,   -93,   -90,   -87,   -83,   -80,   -77,   -73,   -70,
+    -67,   -63,   -60,   -57,   -54,   -50,   -47,   -44,   -40,   -37,   -34,
+    -30,   -27,   -24,   -20,   -17,   -14,   -10,   -7,    -4,    0,     3,
+    6,     10,    13,    16,    20,    23,    26,    30,    33,    36,    39,
+    43,    46,    49,    53,    56,    59,    63,    66,    69,    73,    76,
+    79,    83,    86,    89,    93,    96,    99,    103,   106,   109,   113,
+    116,   119,   123,   126,   129,   132,   136,   139,   142,   146,   149,
+    152,   156,   159,   162,   166,   169,   172,   176,   179,   182,   186,
+    189,   192,   196,   199,   202,   206,   209,   212,   216,   219,   222,
+    226,   229,   232,   235,   239,   242,   245,   249,   252,   255,   259,
+    262,   265,   269,   272,   275,   279,   282,   285,   289,   292,   295,
+    299,   302,   305,   309,   312,   315,   319,   322,   325,   328,   332,
+    335,   338,   342,   345,   348,   352,   355,   358,   362,   365,   368,
+    372,   375,   378,   382,   385,   388,   392,   395,   398,   402,   405,
+    408,   412,   415,   418,   422,   425,   428,   431,   435,   438,   441,
+    445,   448,   451,   455,   458,   461,   465,   468,   471,   475,   478,
+    481,   485,   488,   491,   495,   498,   501,   505,   508,   511,   515,
+    518,   521,   524,   528,   531,   534,   538,   541,   544,   548,   551,
+    554,   558,   561,   564,   568,   571,   574,   578,   581,   584,   588,
+    591,   594,   598,   601,   604,   608,   611,   614,   617,   621,   624,
+    627,   631,   634,   637,   641,   644,   647,   651,   654,   657,   661,
+    664,   667,   671,   674,   677,   681,   684,   687,   691,   694,   697,
+    701,   704,   707,   711,   714,   717,   720,   724,   727,   730,   734,
+    737,   740,   744,   747,   750,   754,   757,   760,   764,   767,   770,
+    774,   777,   780,   784,   787,   790,   794,   797,   800,   804,   807,
+    810,   813,   817,   820,   823,   827,   830,   833,   837,   840,   843,
+    847,   850,   853,   857,   860,   863,   867,   870,   873,   877,   880,
+    883,   887,   890,   893,   897,   900,   903,   907,   910,   913,   916,
+    920,   923,   926,   930,   933,   936,   940,   943,   946,   950,   953,
+    956,   960,
+};
+
+}  // namespace
+}  // namespace absl
diff --git a/absl/strings/charconv.h b/absl/strings/charconv.h
new file mode 100644
index 000000000000..3e313679c961
--- /dev/null
+++ b/absl/strings/charconv.h
@@ -0,0 +1,115 @@
+// 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
+//
+//      http://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_CHARCONV_H_
+#define ABSL_STRINGS_CHARCONV_H_
+
+#include <system_error>  // NOLINT(build/c++11)
+
+namespace absl {
+
+// Workalike compatibilty version of std::chars_format from C++17.
+//
+// This is an bitfield enumerator which can be passed to absl::from_chars to
+// configure the std::string-to-float conversion.
+enum class chars_format {
+  scientific = 1,
+  fixed = 2,
+  hex = 4,
+  general = fixed | scientific,
+};
+
+// The return result of a std::string-to-number conversion.
+//
+// `ec` will be set to `invalid_argument` if a well-formed number was not found
+// at the start of the input range, `result_out_of_range` if a well-formed
+// number was found, but it was out of the representable range of the requested
+// type, or to std::errc() otherwise.
+//
+// If a well-formed number was found, `ptr` is set to one past the sequence of
+// characters that were successfully parsed.  If none was found, `ptr` is set
+// to the `first` argument to from_chars.
+struct from_chars_result {
+  const char* ptr;
+  std::errc ec;
+};
+
+// Workalike compatibilty 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
+// DR 3800 and DR 3801.  If these are adopted with different wording,
+// Abseil's behavior will change to match the standard.  (The behavior most
+// likely to change is for DR 3801, which says what `value` will be set to in
+// the case of overflow and underflow.  Code that wants to avoid possible
+// breaking changes in this area should not depend on `value` when the returned
+// from_chars_result indicates a range error.)
+//
+// Searches the range [first, last) for the longest matching pattern beginning
+// at `first` that represents a floating point number.  If one is found, store
+// the result in `value`.
+//
+// The matching pattern format is almost the same as that of strtod(), except
+// that C locale is not respected, and an initial '+' character in the input
+// range will never be matched.
+//
+// If `fmt` is set, it must be one of the enumerator values of the chars_format.
+// (This is despite the fact that chars_format is a bitmask type.)  If set to
+// `scientific`, a matching number must contain an exponent.  If set to `fixed`,
+// then an exponent will never match.  (For example, the std::string "1e5" will be
+// parsed as "1".)  If set to `hex`, then a hexadecimal float is parsed in the
+// format that strtod() accepts, except that a "0x" prefix is NOT matched.
+// (In particular, in `hex` mode, the input "0xff" results in the largest
+// matching pattern "0".)
+absl::from_chars_result from_chars(const char* first, const char* last,
+                                   double& value,  // NOLINT
+                                   chars_format fmt = chars_format::general);
+
+absl::from_chars_result from_chars(const char* first, const char* last,
+                                   float& value,  // NOLINT
+                                   chars_format fmt = chars_format::general);
+
+// std::chars_format is specified as a bitmask type, which means the following
+// operations must be provided:
+inline constexpr chars_format operator&(chars_format lhs, chars_format rhs) {
+  return static_cast<chars_format>(static_cast<int>(lhs) &
+                                   static_cast<int>(rhs));
+}
+inline constexpr chars_format operator|(chars_format lhs, chars_format rhs) {
+  return static_cast<chars_format>(static_cast<int>(lhs) |
+                                   static_cast<int>(rhs));
+}
+inline constexpr chars_format operator^(chars_format lhs, chars_format rhs) {
+  return static_cast<chars_format>(static_cast<int>(lhs) ^
+                                   static_cast<int>(rhs));
+}
+inline constexpr chars_format operator~(chars_format arg) {
+  return static_cast<chars_format>(~static_cast<int>(arg));
+}
+inline chars_format& operator&=(chars_format& lhs, chars_format rhs) {
+  lhs = lhs & rhs;
+  return lhs;
+}
+inline chars_format& operator|=(chars_format& lhs, chars_format rhs) {
+  lhs = lhs | rhs;
+  return lhs;
+}
+inline chars_format& operator^=(chars_format& lhs, chars_format rhs) {
+  lhs = lhs ^ rhs;
+  return lhs;
+}
+
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_CHARCONV_H_
diff --git a/absl/strings/charconv_benchmark.cc b/absl/strings/charconv_benchmark.cc
new file mode 100644
index 000000000000..fd83f44e3d09
--- /dev/null
+++ b/absl/strings/charconv_benchmark.cc
@@ -0,0 +1,204 @@
+// 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
+//
+//      http://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/charconv.h"
+
+#include <cstdlib>
+#include <cstring>
+#include <string>
+
+#include "benchmark/benchmark.h"
+
+namespace {
+
+void BM_Strtod_Pi(benchmark::State& state) {
+  const char* pi = "3.14159";
+  for (auto s : state) {
+    benchmark::DoNotOptimize(pi);
+    benchmark::DoNotOptimize(strtod(pi, nullptr));
+  }
+}
+BENCHMARK(BM_Strtod_Pi);
+
+void BM_Absl_Pi(benchmark::State& state) {
+  const char* pi = "3.14159";
+  const char* pi_end = pi + strlen(pi);
+  for (auto s : state) {
+    benchmark::DoNotOptimize(pi);
+    double v;
+    absl::from_chars(pi, pi_end, v);
+    benchmark::DoNotOptimize(v);
+  }
+}
+BENCHMARK(BM_Absl_Pi);
+
+void BM_Strtod_Pi_float(benchmark::State& state) {
+  const char* pi = "3.14159";
+  for (auto s : state) {
+    benchmark::DoNotOptimize(pi);
+    benchmark::DoNotOptimize(strtof(pi, nullptr));
+  }
+}
+BENCHMARK(BM_Strtod_Pi_float);
+
+void BM_Absl_Pi_float(benchmark::State& state) {
+  const char* pi = "3.14159";
+  const char* pi_end = pi + strlen(pi);
+  for (auto s : state) {
+    benchmark::DoNotOptimize(pi);
+    float v;
+    absl::from_chars(pi, pi_end, v);
+    benchmark::DoNotOptimize(v);
+  }
+}
+BENCHMARK(BM_Absl_Pi_float);
+
+void BM_Strtod_HardLarge(benchmark::State& state) {
+  const char* num = "272104041512242479.e200";
+  for (auto s : state) {
+    benchmark::DoNotOptimize(num);
+    benchmark::DoNotOptimize(strtod(num, nullptr));
+  }
+}
+BENCHMARK(BM_Strtod_HardLarge);
+
+void BM_Absl_HardLarge(benchmark::State& state) {
+  const char* numstr = "272104041512242479.e200";
+  const char* numstr_end = numstr + strlen(numstr);
+  for (auto s : state) {
+    benchmark::DoNotOptimize(numstr);
+    double v;
+    absl::from_chars(numstr, numstr_end, v);
+    benchmark::DoNotOptimize(v);
+  }
+}
+BENCHMARK(BM_Absl_HardLarge);
+
+void BM_Strtod_HardSmall(benchmark::State& state) {
+  const char* num = "94080055902682397.e-242";
+  for (auto s : state) {
+    benchmark::DoNotOptimize(num);
+    benchmark::DoNotOptimize(strtod(num, nullptr));
+  }
+}
+BENCHMARK(BM_Strtod_HardSmall);
+
+void BM_Absl_HardSmall(benchmark::State& state) {
+  const char* numstr = "94080055902682397.e-242";
+  const char* numstr_end = numstr + strlen(numstr);
+  for (auto s : state) {
+    benchmark::DoNotOptimize(numstr);
+    double v;
+    absl::from_chars(numstr, numstr_end, v);
+    benchmark::DoNotOptimize(v);
+  }
+}
+BENCHMARK(BM_Absl_HardSmall);
+
+void BM_Strtod_HugeMantissa(benchmark::State& state) {
+  std::string huge(200, '3');
+  const char* num = huge.c_str();
+  for (auto s : state) {
+    benchmark::DoNotOptimize(num);
+    benchmark::DoNotOptimize(strtod(num, nullptr));
+  }
+}
+BENCHMARK(BM_Strtod_HugeMantissa);
+
+void BM_Absl_HugeMantissa(benchmark::State& state) {
+  std::string huge(200, '3');
+  const char* num = huge.c_str();
+  const char* num_end = num + 200;
+  for (auto s : state) {
+    benchmark::DoNotOptimize(num);
+    double v;
+    absl::from_chars(num, num_end, v);
+    benchmark::DoNotOptimize(v);
+  }
+}
+BENCHMARK(BM_Absl_HugeMantissa);
+
+std::string MakeHardCase(int length) {
+  // The number 1.1521...e-297 is exactly halfway between 12345 * 2**-1000 and
+  // the next larger representable number.  The digits of this number are in
+  // the std::string below.
+  const std::string digits =
+      "1."
+      "152113937042223790993097181572444900347587985074226836242307364987727724"
+      "831384300183638649152607195040591791364113930628852279348613864894524591"
+      "272746490313676832900762939595690019745859128071117417798540258114233761"
+      "012939937017879509401007964861774960297319002612457273148497158989073482"
+      "171377406078223015359818300988676687994537274548940612510414856761641652"
+      "513434981938564294004070500716200446656421722229202383105446378511678258"
+      "370570631774499359748259931676320916632111681001853983492795053244971606"
+      "922718923011680846577744433974087653954904214152517799883551075537146316"
+      "168973685866425605046988661997658648354773076621610279716804960009043764"
+      "038392994055171112475093876476783502487512538082706095923790634572014823"
+      "78877699375152587890625" +
+      std::string(5000, '0');
+  // generate the hard cases on either side for the given length.
+  // Lengths between 3 and 1000 are reasonable.
+  return digits.substr(0, length) + "1e-297";
+}
+
+void BM_Strtod_Big_And_Difficult(benchmark::State& state) {
+  std::string testcase = MakeHardCase(state.range(0));
+  const char* begin = testcase.c_str();
+  for (auto s : state) {
+    benchmark::DoNotOptimize(begin);
+    benchmark::DoNotOptimize(strtod(begin, nullptr));
+  }
+}
+BENCHMARK(BM_Strtod_Big_And_Difficult)->Range(3, 5000);
+
+void BM_Absl_Big_And_Difficult(benchmark::State& state) {
+  std::string testcase = MakeHardCase(state.range(0));
+  const char* begin = testcase.c_str();
+  const char* end = begin + testcase.size();
+  for (auto s : state) {
+    benchmark::DoNotOptimize(begin);
+    double v;
+    absl::from_chars(begin, end, v);
+    benchmark::DoNotOptimize(v);
+  }
+}
+BENCHMARK(BM_Absl_Big_And_Difficult)->Range(3, 5000);
+
+}  // namespace
+
+// ------------------------------------------------------------------------
+// Benchmark                                 Time           CPU Iterations
+// ------------------------------------------------------------------------
+// BM_Strtod_Pi                             96 ns         96 ns    6337454
+// BM_Absl_Pi                               35 ns         35 ns   20031996
+// BM_Strtod_Pi_float                       91 ns         91 ns    7745851
+// BM_Absl_Pi_float                         35 ns         35 ns   20430298
+// BM_Strtod_HardLarge                     133 ns        133 ns    5288341
+// BM_Absl_HardLarge                       181 ns        181 ns    3855615
+// BM_Strtod_HardSmall                     279 ns        279 ns    2517243
+// BM_Absl_HardSmall                       287 ns        287 ns    2458744
+// BM_Strtod_HugeMantissa                  433 ns        433 ns    1604293
+// BM_Absl_HugeMantissa                    160 ns        160 ns    4403671
+// BM_Strtod_Big_And_Difficult/3           236 ns        236 ns    2942496
+// BM_Strtod_Big_And_Difficult/8           232 ns        232 ns    2983796
+// BM_Strtod_Big_And_Difficult/64          437 ns        437 ns    1591951
+// BM_Strtod_Big_And_Difficult/512        1738 ns       1738 ns     402519
+// BM_Strtod_Big_And_Difficult/4096       3943 ns       3943 ns     176128
+// BM_Strtod_Big_And_Difficult/5000       4397 ns       4397 ns     157878
+// BM_Absl_Big_And_Difficult/3              39 ns         39 ns   17799583
+// BM_Absl_Big_And_Difficult/8              43 ns         43 ns   16096859
+// BM_Absl_Big_And_Difficult/64            550 ns        550 ns    1259717
+// BM_Absl_Big_And_Difficult/512          4167 ns       4167 ns     171414
+// BM_Absl_Big_And_Difficult/4096         9160 ns       9159 ns      76297
+// BM_Absl_Big_And_Difficult/5000         9738 ns       9738 ns      70140
diff --git a/absl/strings/charconv_test.cc b/absl/strings/charconv_test.cc
new file mode 100644
index 000000000000..f8d71cc6c448
--- /dev/null
+++ b/absl/strings/charconv_test.cc
@@ -0,0 +1,766 @@
+// 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
+//
+//      http://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/charconv.h"
+
+#include <cstdlib>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/strings/str_cat.h"
+
+#ifdef _MSC_FULL_VER
+#define ABSL_COMPILER_DOES_EXACT_ROUNDING 0
+#define ABSL_STRTOD_HANDLES_NAN_CORRECTLY 0
+#else
+#define ABSL_COMPILER_DOES_EXACT_ROUNDING 1
+#define ABSL_STRTOD_HANDLES_NAN_CORRECTLY 1
+#endif
+
+namespace {
+
+#if ABSL_COMPILER_DOES_EXACT_ROUNDING
+
+// Tests that the given std::string is accepted by absl::from_chars, and that it
+// converts exactly equal to the given number.
+void TestDoubleParse(absl::string_view str, double expected_number) {
+  SCOPED_TRACE(str);
+  double actual_number = 0.0;
+  absl::from_chars_result result =
+      absl::from_chars(str.data(), str.data() + str.length(), actual_number);
+  EXPECT_EQ(result.ec, std::errc());
+  EXPECT_EQ(result.ptr, str.data() + str.length());
+  EXPECT_EQ(actual_number, expected_number);
+}
+
+void TestFloatParse(absl::string_view str, float expected_number) {
+  SCOPED_TRACE(str);
+  float actual_number = 0.0;
+  absl::from_chars_result result =
+      absl::from_chars(str.data(), str.data() + str.length(), actual_number);
+  EXPECT_EQ(result.ec, std::errc());
+  EXPECT_EQ(result.ptr, str.data() + str.length());
+  EXPECT_EQ(actual_number, expected_number);
+}
+
+// Tests that the given double or single precision floating point literal is
+// parsed correctly by absl::from_chars.
+//
+// These convenience macros assume that the C++ compiler being used also does
+// fully correct decimal-to-binary conversions.
+#define FROM_CHARS_TEST_DOUBLE(number)     \
+  {                                        \
+    TestDoubleParse(#number, number);      \
+    TestDoubleParse("-" #number, -number); \
+  }
+
+#define FROM_CHARS_TEST_FLOAT(number)        \
+  {                                          \
+    TestFloatParse(#number, number##f);      \
+    TestFloatParse("-" #number, -number##f); \
+  }
+
+TEST(FromChars, NearRoundingCases) {
+  // Cases from "A Program for Testing IEEE Decimal-Binary Conversion"
+  // by Vern Paxson.
+
+  // Forms that should round towards zero.  (These are the hardest cases for
+  // each decimal mantissa size.)
+  FROM_CHARS_TEST_DOUBLE(5.e125);
+  FROM_CHARS_TEST_DOUBLE(69.e267);
+  FROM_CHARS_TEST_DOUBLE(999.e-026);
+  FROM_CHARS_TEST_DOUBLE(7861.e-034);
+  FROM_CHARS_TEST_DOUBLE(75569.e-254);
+  FROM_CHARS_TEST_DOUBLE(928609.e-261);
+  FROM_CHARS_TEST_DOUBLE(9210917.e080);
+  FROM_CHARS_TEST_DOUBLE(84863171.e114);
+  FROM_CHARS_TEST_DOUBLE(653777767.e273);
+  FROM_CHARS_TEST_DOUBLE(5232604057.e-298);
+  FROM_CHARS_TEST_DOUBLE(27235667517.e-109);
+  FROM_CHARS_TEST_DOUBLE(653532977297.e-123);
+  FROM_CHARS_TEST_DOUBLE(3142213164987.e-294);
+  FROM_CHARS_TEST_DOUBLE(46202199371337.e-072);
+  FROM_CHARS_TEST_DOUBLE(231010996856685.e-073);
+  FROM_CHARS_TEST_DOUBLE(9324754620109615.e212);
+  FROM_CHARS_TEST_DOUBLE(78459735791271921.e049);
+  FROM_CHARS_TEST_DOUBLE(272104041512242479.e200);
+  FROM_CHARS_TEST_DOUBLE(6802601037806061975.e198);
+  FROM_CHARS_TEST_DOUBLE(20505426358836677347.e-221);
+  FROM_CHARS_TEST_DOUBLE(836168422905420598437.e-234);
+  FROM_CHARS_TEST_DOUBLE(4891559871276714924261.e222);
+  FROM_CHARS_TEST_FLOAT(5.e-20);
+  FROM_CHARS_TEST_FLOAT(67.e14);
+  FROM_CHARS_TEST_FLOAT(985.e15);
+  FROM_CHARS_TEST_FLOAT(7693.e-42);
+  FROM_CHARS_TEST_FLOAT(55895.e-16);
+  FROM_CHARS_TEST_FLOAT(996622.e-44);
+  FROM_CHARS_TEST_FLOAT(7038531.e-32);
+  FROM_CHARS_TEST_FLOAT(60419369.e-46);
+  FROM_CHARS_TEST_FLOAT(702990899.e-20);
+  FROM_CHARS_TEST_FLOAT(6930161142.e-48);
+  FROM_CHARS_TEST_FLOAT(25933168707.e-13);
+  FROM_CHARS_TEST_FLOAT(596428896559.e20);
+
+  // Similarly, forms that should round away from zero.
+  FROM_CHARS_TEST_DOUBLE(9.e-265);
+  FROM_CHARS_TEST_DOUBLE(85.e-037);
+  FROM_CHARS_TEST_DOUBLE(623.e100);
+  FROM_CHARS_TEST_DOUBLE(3571.e263);
+  FROM_CHARS_TEST_DOUBLE(81661.e153);
+  FROM_CHARS_TEST_DOUBLE(920657.e-023);
+  FROM_CHARS_TEST_DOUBLE(4603285.e-024);
+  FROM_CHARS_TEST_DOUBLE(87575437.e-309);
+  FROM_CHARS_TEST_DOUBLE(245540327.e122);
+  FROM_CHARS_TEST_DOUBLE(6138508175.e120);
+  FROM_CHARS_TEST_DOUBLE(83356057653.e193);
+  FROM_CHARS_TEST_DOUBLE(619534293513.e124);
+  FROM_CHARS_TEST_DOUBLE(2335141086879.e218);
+  FROM_CHARS_TEST_DOUBLE(36167929443327.e-159);
+  FROM_CHARS_TEST_DOUBLE(609610927149051.e-255);
+  FROM_CHARS_TEST_DOUBLE(3743626360493413.e-165);
+  FROM_CHARS_TEST_DOUBLE(94080055902682397.e-242);
+  FROM_CHARS_TEST_DOUBLE(899810892172646163.e283);
+  FROM_CHARS_TEST_DOUBLE(7120190517612959703.e120);
+  FROM_CHARS_TEST_DOUBLE(25188282901709339043.e-252);
+  FROM_CHARS_TEST_DOUBLE(308984926168550152811.e-052);
+  FROM_CHARS_TEST_DOUBLE(6372891218502368041059.e064);
+  FROM_CHARS_TEST_FLOAT(3.e-23);
+  FROM_CHARS_TEST_FLOAT(57.e18);
+  FROM_CHARS_TEST_FLOAT(789.e-35);
+  FROM_CHARS_TEST_FLOAT(2539.e-18);
+  FROM_CHARS_TEST_FLOAT(76173.e28);
+  FROM_CHARS_TEST_FLOAT(887745.e-11);
+  FROM_CHARS_TEST_FLOAT(5382571.e-37);
+  FROM_CHARS_TEST_FLOAT(82381273.e-35);
+  FROM_CHARS_TEST_FLOAT(750486563.e-38);
+  FROM_CHARS_TEST_FLOAT(3752432815.e-39);
+  FROM_CHARS_TEST_FLOAT(75224575729.e-45);
+  FROM_CHARS_TEST_FLOAT(459926601011.e15);
+}
+
+#undef FROM_CHARS_TEST_DOUBLE
+#undef FROM_CHARS_TEST_FLOAT
+#endif
+
+float ToFloat(absl::string_view s) {
+  float f;
+  absl::from_chars(s.data(), s.data() + s.size(), f);
+  return f;
+}
+
+double ToDouble(absl::string_view s) {
+  double d;
+  absl::from_chars(s.data(), s.data() + s.size(), d);
+  return d;
+}
+
+// A duplication of the test cases in "NearRoundingCases" above, but with
+// expected values expressed with integers, using ldexp/ldexpf.  These test
+// cases will work even on compilers that do not accurately round floating point
+// literals.
+TEST(FromChars, NearRoundingCasesExplicit) {
+  EXPECT_EQ(ToDouble("5.e125"), ldexp(6653062250012735, 365));
+  EXPECT_EQ(ToDouble("69.e267"), ldexp(4705683757438170, 841));
+  EXPECT_EQ(ToDouble("999.e-026"), ldexp(6798841691080350, -129));
+  EXPECT_EQ(ToDouble("7861.e-034"), ldexp(8975675289889240, -153));
+  EXPECT_EQ(ToDouble("75569.e-254"), ldexp(6091718967192243, -880));
+  EXPECT_EQ(ToDouble("928609.e-261"), ldexp(7849264900213743, -900));
+  EXPECT_EQ(ToDouble("9210917.e080"), ldexp(8341110837370930, 236));
+  EXPECT_EQ(ToDouble("84863171.e114"), ldexp(4625202867375927, 353));
+  EXPECT_EQ(ToDouble("653777767.e273"), ldexp(5068902999763073, 884));
+  EXPECT_EQ(ToDouble("5232604057.e-298"), ldexp(5741343011915040, -1010));
+  EXPECT_EQ(ToDouble("27235667517.e-109"), ldexp(6707124626673586, -380));
+  EXPECT_EQ(ToDouble("653532977297.e-123"), ldexp(7078246407265384, -422));
+  EXPECT_EQ(ToDouble("3142213164987.e-294"), ldexp(8219991337640559, -988));
+  EXPECT_EQ(ToDouble("46202199371337.e-072"), ldexp(5224462102115359, -246));
+  EXPECT_EQ(ToDouble("231010996856685.e-073"), ldexp(5224462102115359, -247));
+  EXPECT_EQ(ToDouble("9324754620109615.e212"), ldexp(5539753864394442, 705));
+  EXPECT_EQ(ToDouble("78459735791271921.e049"), ldexp(8388176519442766, 166));
+  EXPECT_EQ(ToDouble("272104041512242479.e200"), ldexp(5554409530847367, 670));
+  EXPECT_EQ(ToDouble("6802601037806061975.e198"), ldexp(5554409530847367, 668));
+  EXPECT_EQ(ToDouble("20505426358836677347.e-221"),
+            ldexp(4524032052079546, -722));
+  EXPECT_EQ(ToDouble("836168422905420598437.e-234"),
+            ldexp(5070963299887562, -760));
+  EXPECT_EQ(ToDouble("4891559871276714924261.e222"),
+            ldexp(6452687840519111, 757));
+  EXPECT_EQ(ToFloat("5.e-20"), ldexpf(15474250, -88));
+  EXPECT_EQ(ToFloat("67.e14"), ldexpf(12479722, 29));
+  EXPECT_EQ(ToFloat("985.e15"), ldexpf(14333636, 36));
+  EXPECT_EQ(ToFloat("7693.e-42"), ldexpf(10979816, -150));
+  EXPECT_EQ(ToFloat("55895.e-16"), ldexpf(12888509, -61));
+  EXPECT_EQ(ToFloat("996622.e-44"), ldexpf(14224264, -150));
+  EXPECT_EQ(ToFloat("7038531.e-32"), ldexpf(11420669, -107));
+  EXPECT_EQ(ToFloat("60419369.e-46"), ldexpf(8623340, -150));
+  EXPECT_EQ(ToFloat("702990899.e-20"), ldexpf(16209866, -61));
+  EXPECT_EQ(ToFloat("6930161142.e-48"), ldexpf(9891056, -150));
+  EXPECT_EQ(ToFloat("25933168707.e-13"), ldexpf(11138211, -32));
+  EXPECT_EQ(ToFloat("596428896559.e20"), ldexpf(12333860, 82));
+
+
+  EXPECT_EQ(ToDouble("9.e-265"), ldexp(8168427841980010, -930));
+  EXPECT_EQ(ToDouble("85.e-037"), ldexp(6360455125664090, -169));
+  EXPECT_EQ(ToDouble("623.e100"), ldexp(6263531988747231, 289));
+  EXPECT_EQ(ToDouble("3571.e263"), ldexp(6234526311072170, 833));
+  EXPECT_EQ(ToDouble("81661.e153"), ldexp(6696636728760206, 472));
+  EXPECT_EQ(ToDouble("920657.e-023"), ldexp(5975405561110124, -109));
+  EXPECT_EQ(ToDouble("4603285.e-024"), ldexp(5975405561110124, -110));
+  EXPECT_EQ(ToDouble("87575437.e-309"), ldexp(8452160731874668, -1053));
+  EXPECT_EQ(ToDouble("245540327.e122"), ldexp(4985336549131723, 381));
+  EXPECT_EQ(ToDouble("6138508175.e120"), ldexp(4985336549131723, 379));
+  EXPECT_EQ(ToDouble("83356057653.e193"), ldexp(5986732817132056, 625));
+  EXPECT_EQ(ToDouble("619534293513.e124"), ldexp(4798406992060657, 399));
+  EXPECT_EQ(ToDouble("2335141086879.e218"), ldexp(5419088166961646, 713));
+  EXPECT_EQ(ToDouble("36167929443327.e-159"), ldexp(8135819834632444, -536));
+  EXPECT_EQ(ToDouble("609610927149051.e-255"), ldexp(4576664294594737, -850));
+  EXPECT_EQ(ToDouble("3743626360493413.e-165"), ldexp(6898586531774201, -549));
+  EXPECT_EQ(ToDouble("94080055902682397.e-242"), ldexp(6273271706052298, -800));
+  EXPECT_EQ(ToDouble("899810892172646163.e283"), ldexp(7563892574477827, 947));
+  EXPECT_EQ(ToDouble("7120190517612959703.e120"), ldexp(5385467232557565, 409));
+  EXPECT_EQ(ToDouble("25188282901709339043.e-252"),
+            ldexp(5635662608542340, -825));
+  EXPECT_EQ(ToDouble("308984926168550152811.e-052"),
+            ldexp(5644774693823803, -157));
+  EXPECT_EQ(ToDouble("6372891218502368041059.e064"),
+            ldexp(4616868614322430, 233));
+
+  EXPECT_EQ(ToFloat("3.e-23"), ldexpf(9507380, -98));
+  EXPECT_EQ(ToFloat("57.e18"), ldexpf(12960300, 42));
+  EXPECT_EQ(ToFloat("789.e-35"), ldexpf(10739312, -130));
+  EXPECT_EQ(ToFloat("2539.e-18"), ldexpf(11990089, -72));
+  EXPECT_EQ(ToFloat("76173.e28"), ldexpf(9845130, 86));
+  EXPECT_EQ(ToFloat("887745.e-11"), ldexpf(9760860, -40));
+  EXPECT_EQ(ToFloat("5382571.e-37"), ldexpf(11447463, -124));
+  EXPECT_EQ(ToFloat("82381273.e-35"), ldexpf(8554961, -113));
+  EXPECT_EQ(ToFloat("750486563.e-38"), ldexpf(9975678, -120));
+  EXPECT_EQ(ToFloat("3752432815.e-39"), ldexpf(9975678, -121));
+  EXPECT_EQ(ToFloat("75224575729.e-45"), ldexpf(13105970, -137));
+  EXPECT_EQ(ToFloat("459926601011.e15"), ldexpf(12466336, 65));
+}
+
+// Common test logic for converting a std::string which lies exactly halfway between
+// two target floats.
+//
+// mantissa and exponent represent the precise value between two floating point
+// numbers, `expected_low` and `expected_high`.  The floating point
+// representation to parse in `StrCat(mantissa, "e", exponent)`.
+//
+// This function checks that an input just slightly less than the exact value
+// is rounded down to `expected_low`, and an input just slightly greater than
+// the exact value is rounded up to `expected_high`.
+//
+// The exact value should round to `expected_half`, which must be either
+// `expected_low` or `expected_high`.
+template <typename FloatType>
+void TestHalfwayValue(const std::string& mantissa, int exponent,
+                      FloatType expected_low, FloatType expected_high,
+                      FloatType expected_half) {
+  std::string low_rep = mantissa;
+  low_rep[low_rep.size() - 1] -= 1;
+  absl::StrAppend(&low_rep, std::string(1000, '9'), "e", exponent);
+
+  FloatType actual_low = 0;
+  absl::from_chars(low_rep.data(), low_rep.data() + low_rep.size(), actual_low);
+  EXPECT_EQ(expected_low, actual_low);
+
+  std::string high_rep = absl::StrCat(mantissa, std::string(1000, '0'), "1e", exponent);
+  FloatType actual_high = 0;
+  absl::from_chars(high_rep.data(), high_rep.data() + high_rep.size(),
+                   actual_high);
+  EXPECT_EQ(expected_high, actual_high);
+
+  std::string halfway_rep = absl::StrCat(mantissa, "e", exponent);
+  FloatType actual_half = 0;
+  absl::from_chars(halfway_rep.data(), halfway_rep.data() + halfway_rep.size(),
+                   actual_half);
+  EXPECT_EQ(expected_half, actual_half);
+}
+
+TEST(FromChars, DoubleRounding) {
+  const double zero = 0.0;
+  const double first_subnormal = nextafter(zero, 1.0);
+  const double second_subnormal = nextafter(first_subnormal, 1.0);
+
+  const double first_normal = DBL_MIN;
+  const double last_subnormal = nextafter(first_normal, 0.0);
+  const double second_normal = nextafter(first_normal, 1.0);
+
+  const double last_normal = DBL_MAX;
+  const double penultimate_normal = nextafter(last_normal, 0.0);
+
+  // Various test cases for numbers between two representable floats.  Each
+  // call to TestHalfwayValue tests a number just below and just above the
+  // halfway point, as well as the number exactly between them.
+
+  // Test between zero and first_subnormal.  Round-to-even tie rounds down.
+  TestHalfwayValue(
+      "2."
+      "470328229206232720882843964341106861825299013071623822127928412503377536"
+      "351043759326499181808179961898982823477228588654633283551779698981993873"
+      "980053909390631503565951557022639229085839244910518443593180284993653615"
+      "250031937045767824921936562366986365848075700158576926990370631192827955"
+      "855133292783433840935197801553124659726357957462276646527282722005637400"
+      "648549997709659947045402082816622623785739345073633900796776193057750674"
+      "017632467360096895134053553745851666113422376667860416215968046191446729"
+      "184030053005753084904876539171138659164623952491262365388187963623937328"
+      "042389101867234849766823508986338858792562830275599565752445550725518931"
+      "369083625477918694866799496832404970582102851318545139621383772282614543"
+      "7693412532098591327667236328125",
+      -324, zero, first_subnormal, zero);
+
+  // first_subnormal and second_subnormal.  Round-to-even tie rounds up.
+  TestHalfwayValue(
+      "7."
+      "410984687618698162648531893023320585475897039214871466383785237510132609"
+      "053131277979497545424539885696948470431685765963899850655339096945981621"
+      "940161728171894510697854671067917687257517734731555330779540854980960845"
+      "750095811137303474765809687100959097544227100475730780971111893578483867"
+      "565399878350301522805593404659373979179073872386829939581848166016912201"
+      "945649993128979841136206248449867871357218035220901702390328579173252022"
+      "052897402080290685402160661237554998340267130003581248647904138574340187"
+      "552090159017259254714629617513415977493871857473787096164563890871811984"
+      "127167305601704549300470526959016576377688490826798697257336652176556794"
+      "107250876433756084600398490497214911746308553955635418864151316847843631"
+      "3080237596295773983001708984375",
+      -324, first_subnormal, second_subnormal, second_subnormal);
+
+  // last_subnormal and first_normal.  Round-to-even tie rounds up.
+  TestHalfwayValue(
+      "2."
+      "225073858507201136057409796709131975934819546351645648023426109724822222"
+      "021076945516529523908135087914149158913039621106870086438694594645527657"
+      "207407820621743379988141063267329253552286881372149012981122451451889849"
+      "057222307285255133155755015914397476397983411801999323962548289017107081"
+      "850690630666655994938275772572015763062690663332647565300009245888316433"
+      "037779791869612049497390377829704905051080609940730262937128958950003583"
+      "799967207254304360284078895771796150945516748243471030702609144621572289"
+      "880258182545180325707018860872113128079512233426288368622321503775666622"
+      "503982534335974568884423900265498198385487948292206894721689831099698365"
+      "846814022854243330660339850886445804001034933970427567186443383770486037"
+      "86162277173854562306587467901408672332763671875",
+      -308, last_subnormal, first_normal, first_normal);
+
+  // first_normal and second_normal.  Round-to-even tie rounds down.
+  TestHalfwayValue(
+      "2."
+      "225073858507201630123055637955676152503612414573018013083228724049586647"
+      "606759446192036794116886953213985520549032000903434781884412325572184367"
+      "563347617020518175998922941393629966742598285899994830148971433555578567"
+      "693279306015978183162142425067962460785295885199272493577688320732492479"
+      "924816869232247165964934329258783950102250973957579510571600738343645738"
+      "494324192997092179207389919761694314131497173265255020084997973676783743"
+      "155205818804439163810572367791175177756227497413804253387084478193655533"
+      "073867420834526162513029462022730109054820067654020201547112002028139700"
+      "141575259123440177362244273712468151750189745559978653234255886219611516"
+      "335924167958029604477064946470184777360934300451421683607013647479513962"
+      "13837722826145437693412532098591327667236328125",
+      -308, first_normal, second_normal, first_normal);
+
+  // penultimate_normal and last_normal.  Round-to-even rounds down.
+  TestHalfwayValue(
+      "1."
+      "797693134862315608353258760581052985162070023416521662616611746258695532"
+      "672923265745300992879465492467506314903358770175220871059269879629062776"
+      "047355692132901909191523941804762171253349609463563872612866401980290377"
+      "995141836029815117562837277714038305214839639239356331336428021390916694"
+      "57927874464075218944",
+      308, penultimate_normal, last_normal, penultimate_normal);
+}
+
+// Same test cases as DoubleRounding, now with new and improved Much Smaller
+// Precision!
+TEST(FromChars, FloatRounding) {
+  const float zero = 0.0;
+  const float first_subnormal = nextafterf(zero, 1.0);
+  const float second_subnormal = nextafterf(first_subnormal, 1.0);
+
+  const float first_normal = FLT_MIN;
+  const float last_subnormal = nextafterf(first_normal, 0.0);
+  const float second_normal = nextafterf(first_normal, 1.0);
+
+  const float last_normal = FLT_MAX;
+  const float penultimate_normal = nextafterf(last_normal, 0.0);
+
+  // Test between zero and first_subnormal.  Round-to-even tie rounds down.
+  TestHalfwayValue(
+      "7."
+      "006492321624085354618647916449580656401309709382578858785341419448955413"
+      "42930300743319094181060791015625",
+      -46, zero, first_subnormal, zero);
+
+  // first_subnormal and second_subnormal.  Round-to-even tie rounds up.
+  TestHalfwayValue(
+      "2."
+      "101947696487225606385594374934874196920392912814773657635602425834686624"
+      "028790902229957282543182373046875",
+      -45, first_subnormal, second_subnormal, second_subnormal);
+
+  // last_subnormal and first_normal.  Round-to-even tie rounds up.
+  TestHalfwayValue(
+      "1."
+      "175494280757364291727882991035766513322858992758990427682963118425003064"
+      "9651730385585324256680905818939208984375",
+      -38, last_subnormal, first_normal, first_normal);
+
+  // first_normal and second_normal.  Round-to-even tie rounds down.
+  TestHalfwayValue(
+      "1."
+      "175494420887210724209590083408724842314472120785184615334540294131831453"
+      "9442813071445925743319094181060791015625",
+      -38, first_normal, second_normal, first_normal);
+
+  // penultimate_normal and last_normal.  Round-to-even rounds down.
+  TestHalfwayValue("3.40282336497324057985868971510891282432", 38,
+                   penultimate_normal, last_normal, penultimate_normal);
+}
+
+TEST(FromChars, Underflow) {
+  // Check that underflow is handled correctly, according to the specification
+  // in DR 3081.
+  double d;
+  float f;
+  absl::from_chars_result result;
+
+  std::string negative_underflow = "-1e-1000";
+  const char* begin = negative_underflow.data();
+  const char* end = begin + negative_underflow.size();
+  d = 100.0;
+  result = absl::from_chars(begin, end, d);
+  EXPECT_EQ(result.ptr, end);
+  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
+  EXPECT_TRUE(std::signbit(d));  // negative
+  EXPECT_GE(d, -std::numeric_limits<double>::min());
+  f = 100.0;
+  result = absl::from_chars(begin, end, f);
+  EXPECT_EQ(result.ptr, end);
+  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
+  EXPECT_TRUE(std::signbit(f));  // negative
+  EXPECT_GE(f, -std::numeric_limits<float>::min());
+
+  std::string positive_underflow = "1e-1000";
+  begin = positive_underflow.data();
+  end = begin + positive_underflow.size();
+  d = -100.0;
+  result = absl::from_chars(begin, end, d);
+  EXPECT_EQ(result.ptr, end);
+  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
+  EXPECT_FALSE(std::signbit(d));  // positive
+  EXPECT_LE(d, std::numeric_limits<double>::min());
+  f = -100.0;
+  result = absl::from_chars(begin, end, f);
+  EXPECT_EQ(result.ptr, end);
+  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
+  EXPECT_FALSE(std::signbit(f));  // positive
+  EXPECT_LE(f, std::numeric_limits<float>::min());
+}
+
+TEST(FromChars, Overflow) {
+  // Check that overflow is handled correctly, according to the specification
+  // in DR 3081.
+  double d;
+  float f;
+  absl::from_chars_result result;
+
+  std::string negative_overflow = "-1e1000";
+  const char* begin = negative_overflow.data();
+  const char* end = begin + negative_overflow.size();
+  d = 100.0;
+  result = absl::from_chars(begin, end, d);
+  EXPECT_EQ(result.ptr, end);
+  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
+  EXPECT_TRUE(std::signbit(d));  // negative
+  EXPECT_EQ(d, -std::numeric_limits<double>::max());
+  f = 100.0;
+  result = absl::from_chars(begin, end, f);
+  EXPECT_EQ(result.ptr, end);
+  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
+  EXPECT_TRUE(std::signbit(f));  // negative
+  EXPECT_EQ(f, -std::numeric_limits<float>::max());
+
+  std::string positive_overflow = "1e1000";
+  begin = positive_overflow.data();
+  end = begin + positive_overflow.size();
+  d = -100.0;
+  result = absl::from_chars(begin, end, d);
+  EXPECT_EQ(result.ptr, end);
+  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
+  EXPECT_FALSE(std::signbit(d));  // positive
+  EXPECT_EQ(d, std::numeric_limits<double>::max());
+  f = -100.0;
+  result = absl::from_chars(begin, end, f);
+  EXPECT_EQ(result.ptr, end);
+  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
+  EXPECT_FALSE(std::signbit(f));  // positive
+  EXPECT_EQ(f, std::numeric_limits<float>::max());
+}
+
+TEST(FromChars, ReturnValuePtr) {
+  // Check that `ptr` points one past the number scanned, even if that number
+  // is not representable.
+  double d;
+  absl::from_chars_result result;
+
+  std::string normal = "3.14@#$%@#$%";
+  result = absl::from_chars(normal.data(), normal.data() + normal.size(), d);
+  EXPECT_EQ(result.ec, std::errc());
+  EXPECT_EQ(result.ptr - normal.data(), 4);
+
+  std::string overflow = "1e1000@#$%@#$%";
+  result = absl::from_chars(overflow.data(),
+                            overflow.data() + overflow.size(), d);
+  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
+  EXPECT_EQ(result.ptr - overflow.data(), 6);
+
+  std::string garbage = "#$%@#$%";
+  result = absl::from_chars(garbage.data(),
+                            garbage.data() + garbage.size(), d);
+  EXPECT_EQ(result.ec, std::errc::invalid_argument);
+  EXPECT_EQ(result.ptr - garbage.data(), 0);
+}
+
+// Check for a wide range of inputs that strtod() and absl::from_chars() exactly
+// agree on the conversion amount.
+//
+// This test assumes the platform's strtod() uses perfect round_to_nearest
+// rounding.
+TEST(FromChars, TestVersusStrtod) {
+  for (int mantissa = 1000000; mantissa <= 9999999; mantissa += 501) {
+    for (int exponent = -300; exponent < 300; ++exponent) {
+      std::string candidate = absl::StrCat(mantissa, "e", exponent);
+      double strtod_value = strtod(candidate.c_str(), nullptr);
+      double absl_value = 0;
+      absl::from_chars(candidate.data(), candidate.data() + candidate.size(),
+                       absl_value);
+      ASSERT_EQ(strtod_value, absl_value) << candidate;
+    }
+  }
+}
+
+// Check for a wide range of inputs that strtof() and absl::from_chars() exactly
+// agree on the conversion amount.
+//
+// This test assumes the platform's strtof() uses perfect round_to_nearest
+// rounding.
+TEST(FromChars, TestVersusStrtof) {
+  for (int mantissa = 1000000; mantissa <= 9999999; mantissa += 501) {
+    for (int exponent = -43; exponent < 32; ++exponent) {
+      std::string candidate = absl::StrCat(mantissa, "e", exponent);
+      float strtod_value = strtof(candidate.c_str(), nullptr);
+      float absl_value = 0;
+      absl::from_chars(candidate.data(), candidate.data() + candidate.size(),
+                       absl_value);
+      ASSERT_EQ(strtod_value, absl_value) << candidate;
+    }
+  }
+}
+
+// Tests if two floating point values have identical bit layouts.  (EXPECT_EQ
+// is not suitable for NaN testing, since NaNs are never equal.)
+template <typename Float>
+bool Identical(Float a, Float b) {
+  return 0 == memcmp(&a, &b, sizeof(Float));
+}
+
+// Check that NaNs are parsed correctly.  The spec requires that
+// std::from_chars on "NaN(123abc)" return the same value as std::nan("123abc").
+// How such an n-char-sequence affects the generated NaN is unspecified, so we
+// just test for symmetry with std::nan and strtod here.
+//
+// (In Linux, this parses the value as a number and stuffs that number into the
+// free bits of a quiet NaN.)
+TEST(FromChars, NaNDoubles) {
+  for (std::string n_char_sequence :
+       {"", "1", "2", "3", "fff", "FFF", "200000", "400000", "4000000000000",
+        "8000000000000", "abc123", "legal_but_unexpected",
+        "99999999999999999999999", "_"}) {
+    std::string input = absl::StrCat("nan(", n_char_sequence, ")");
+    SCOPED_TRACE(input);
+    double from_chars_double;
+    absl::from_chars(input.data(), input.data() + input.size(),
+                     from_chars_double);
+    double std_nan_double = std::nan(n_char_sequence.c_str());
+    EXPECT_TRUE(Identical(from_chars_double, std_nan_double));
+
+    // Also check that we match strtod()'s behavior.  This test assumes that the
+    // platform has a compliant strtod().
+#if ABSL_STRTOD_HANDLES_NAN_CORRECTLY
+    double strtod_double = strtod(input.c_str(), nullptr);
+    EXPECT_TRUE(Identical(from_chars_double, strtod_double));
+#endif  // ABSL_STRTOD_HANDLES_NAN_CORRECTLY
+
+    // Check that we can parse a negative NaN
+    std::string negative_input = "-" + input;
+    double negative_from_chars_double;
+    absl::from_chars(negative_input.data(),
+                     negative_input.data() + negative_input.size(),
+                     negative_from_chars_double);
+    EXPECT_TRUE(std::signbit(negative_from_chars_double));
+    EXPECT_FALSE(Identical(negative_from_chars_double, from_chars_double));
+    from_chars_double = std::copysign(from_chars_double, -1.0);
+    EXPECT_TRUE(Identical(negative_from_chars_double, from_chars_double));
+  }
+}
+
+TEST(FromChars, NaNFloats) {
+  for (std::string n_char_sequence :
+       {"", "1", "2", "3", "fff", "FFF", "200000", "400000", "4000000000000",
+        "8000000000000", "abc123", "legal_but_unexpected",
+        "99999999999999999999999", "_"}) {
+    std::string input = absl::StrCat("nan(", n_char_sequence, ")");
+    SCOPED_TRACE(input);
+    float from_chars_float;
+    absl::from_chars(input.data(), input.data() + input.size(),
+                     from_chars_float);
+    float std_nan_float = std::nanf(n_char_sequence.c_str());
+    EXPECT_TRUE(Identical(from_chars_float, std_nan_float));
+
+    // Also check that we match strtof()'s behavior.  This test assumes that the
+    // platform has a compliant strtof().
+#if ABSL_STRTOD_HANDLES_NAN_CORRECTLY
+    float strtof_float = strtof(input.c_str(), nullptr);
+    EXPECT_TRUE(Identical(from_chars_float, strtof_float));
+#endif  // ABSL_STRTOD_HANDLES_NAN_CORRECTLY
+
+    // Check that we can parse a negative NaN
+    std::string negative_input = "-" + input;
+    float negative_from_chars_float;
+    absl::from_chars(negative_input.data(),
+                     negative_input.data() + negative_input.size(),
+                     negative_from_chars_float);
+    EXPECT_TRUE(std::signbit(negative_from_chars_float));
+    EXPECT_FALSE(Identical(negative_from_chars_float, from_chars_float));
+    from_chars_float = std::copysign(from_chars_float, -1.0);
+    EXPECT_TRUE(Identical(negative_from_chars_float, from_chars_float));
+  }
+}
+
+// Returns an integer larger than step.  The values grow exponentially.
+int NextStep(int step) {
+  return step + (step >> 2) + 1;
+}
+
+// Test a conversion on a family of input strings, checking that the calculation
+// is correct for in-bounds values, and that overflow and underflow are done
+// correctly for out-of-bounds values.
+//
+// input_generator maps from an integer index to a std::string to test.
+// expected_generator maps from an integer index to an expected Float value.
+// from_chars conversion of input_generator(i) should result in
+// expected_generator(i).
+//
+// lower_bound and upper_bound denote the smallest and largest values for which
+// the conversion is expected to succeed.
+template <typename Float>
+void TestOverflowAndUnderflow(
+    const std::function<std::string(int)>& input_generator,
+    const std::function<Float(int)>& expected_generator, int lower_bound,
+    int upper_bound) {
+  // test legal values near lower_bound
+  int index, step;
+  for (index = lower_bound, step = 1; index < upper_bound;
+       index += step, step = NextStep(step)) {
+    std::string input = input_generator(index);
+    SCOPED_TRACE(input);
+    Float expected = expected_generator(index);
+    Float actual;
+    auto result =
+        absl::from_chars(input.data(), input.data() + input.size(), actual);
+    EXPECT_EQ(result.ec, std::errc());
+    EXPECT_EQ(expected, actual);
+  }
+  // test legal values near upper_bound
+  for (index = upper_bound, step = 1; index > lower_bound;
+       index -= step, step = NextStep(step)) {
+    std::string input = input_generator(index);
+    SCOPED_TRACE(input);
+    Float expected = expected_generator(index);
+    Float actual;
+    auto result =
+        absl::from_chars(input.data(), input.data() + input.size(), actual);
+    EXPECT_EQ(result.ec, std::errc());
+    EXPECT_EQ(expected, actual);
+  }
+  // Test underflow values below lower_bound
+  for (index = lower_bound - 1, step = 1; index > -1000000;
+       index -= step, step = NextStep(step)) {
+    std::string input = input_generator(index);
+    SCOPED_TRACE(input);
+    Float actual;
+    auto result =
+        absl::from_chars(input.data(), input.data() + input.size(), actual);
+    EXPECT_EQ(result.ec, std::errc::result_out_of_range);
+    EXPECT_LT(actual, 1.0);  // check for underflow
+  }
+  // Test overflow values above upper_bound
+  for (index = upper_bound + 1, step = 1; index < 1000000;
+       index += step, step = NextStep(step)) {
+    std::string input = input_generator(index);
+    SCOPED_TRACE(input);
+    Float actual;
+    auto result =
+        absl::from_chars(input.data(), input.data() + input.size(), actual);
+    EXPECT_EQ(result.ec, std::errc::result_out_of_range);
+    EXPECT_GT(actual, 1.0);  // check for overflow
+  }
+}
+
+// Check that overflow and underflow are caught correctly for hex doubles.
+//
+// The largest representable double is 0x1.fffffffffffffp+1023, and the
+// smallest representable subnormal is 0x0.0000000000001p-1022, which equals
+// 0x1p-1074.  Therefore 1023 and -1074 are the limits of acceptable exponents
+// in this test.
+TEST(FromChars, HexdecimalDoubleLimits) {
+  auto input_gen = [](int index) { return absl::StrCat("0x1.0p", index); };
+  auto expected_gen = [](int index) { return std::ldexp(1.0, index); };
+  TestOverflowAndUnderflow<double>(input_gen, expected_gen, -1074, 1023);
+}
+
+// Check that overflow and underflow are caught correctly for hex floats.
+//
+// The largest representable float is 0x1.fffffep+127, and the smallest
+// representable subnormal is 0x0.000002p-126, which equals 0x1p-149.
+// Therefore 127 and -149 are the limits of acceptable exponents in this test.
+TEST(FromChars, HexdecimalFloatLimits) {
+  auto input_gen = [](int index) { return absl::StrCat("0x1.0p", index); };
+  auto expected_gen = [](int index) { return std::ldexp(1.0f, index); };
+  TestOverflowAndUnderflow<float>(input_gen, expected_gen, -149, 127);
+}
+
+// Check that overflow and underflow are caught correctly for decimal doubles.
+//
+// The largest representable double is about 1.8e308, and the smallest
+// representable subnormal is about 5e-324.  '1e-324' therefore rounds away from
+// the smallest representable positive value.  -323 and 308 are the limits of
+// acceptable exponents in this test.
+TEST(FromChars, DecimalDoubleLimits) {
+  auto input_gen = [](int index) { return absl::StrCat("1.0e", index); };
+  auto expected_gen = [](int index) { return std::pow(10.0, index); };
+  TestOverflowAndUnderflow<double>(input_gen, expected_gen, -323, 308);
+}
+
+// Check that overflow and underflow are caught correctly for decimal floats.
+//
+// The largest representable float is about 3.4e38, and the smallest
+// representable subnormal is about 1.45e-45.  '1e-45' therefore rounds towards
+// the smallest representable positive value.  -45 and 38 are the limits of
+// acceptable exponents in this test.
+TEST(FromChars, DecimalFloatLimits) {
+  auto input_gen = [](int index) { return absl::StrCat("1.0e", index); };
+  auto expected_gen = [](int index) { return std::pow(10.0, index); };
+  TestOverflowAndUnderflow<float>(input_gen, expected_gen, -45, 38);
+}
+
+}  // namespace
diff --git a/absl/strings/internal/charconv_bigint.cc b/absl/strings/internal/charconv_bigint.cc
new file mode 100644
index 000000000000..3e7296e7068a
--- /dev/null
+++ b/absl/strings/internal/charconv_bigint.cc
@@ -0,0 +1,357 @@
+// 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
+//
+//      http://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/charconv_bigint.h"
+
+#include <algorithm>
+#include <cassert>
+#include <string>
+
+namespace absl {
+namespace strings_internal {
+
+namespace {
+
+// Table containing some large powers of 5, for fast computation.
+
+// Constant step size for entries in the kLargePowersOfFive table.  Each entry
+// is larger than the previous entry by a factor of 5**kLargePowerOfFiveStep
+// (or 5**27).
+//
+// In other words, the Nth entry in the table is 5**(27*N).
+//
+// 5**27 is the largest power of 5 that fits in 64 bits.
+constexpr int kLargePowerOfFiveStep = 27;
+
+// The largest legal index into the kLargePowersOfFive table.
+//
+// In other words, the largest precomputed power of 5 is 5**(27*20).
+constexpr int kLargestPowerOfFiveIndex = 20;
+
+// Table of powers of (5**27), up to (5**27)**20 == 5**540.
+//
+// Used to generate large powers of 5 while limiting the number of repeated
+// multiplications required.
+//
+// clang-format off
+const uint32_t kLargePowersOfFive[] = {
+// 5**27 (i=1), start=0, end=2
+  0xfa10079dU, 0x6765c793U,
+// 5**54 (i=2), start=2, end=6
+  0x97d9f649U, 0x6664242dU, 0x29939b14U, 0x29c30f10U,
+// 5**81 (i=3), start=6, end=12
+  0xc4f809c5U, 0x7bf3f22aU, 0x67bdae34U, 0xad340517U, 0x369d1b5fU, 0x10de1593U,
+// 5**108 (i=4), start=12, end=20
+  0x92b260d1U, 0x9efff7c7U, 0x81de0ec6U, 0xaeba5d56U, 0x410664a4U, 0x4f40737aU,
+  0x20d3846fU, 0x06d00f73U,
+// 5**135 (i=5), start=20, end=30
+  0xff1b172dU, 0x13a1d71cU, 0xefa07617U, 0x7f682d3dU, 0xff8c90c0U, 0x3f0131e7U,
+  0x3fdcb9feU, 0x917b0177U, 0x16c407a7U, 0x02c06b9dU,
+// 5**162 (i=6), start=30, end=42
+  0x960f7199U, 0x056667ecU, 0xe07aefd8U, 0x80f2b9ccU, 0x8273f5e3U, 0xeb9a214aU,
+  0x40b38005U, 0x0e477ad4U, 0x277d08e6U, 0xfa28b11eU, 0xd3f7d784U, 0x011c835bU,
+// 5**189 (i=7), start=42, end=56
+  0xf723d9d5U, 0x3282d3f3U, 0xe00857d1U, 0x69659d25U, 0x2cf117cfU, 0x24da6d07U,
+  0x954d1417U, 0x3e5d8cedU, 0x7a8bb766U, 0xfd785ae6U, 0x645436d2U, 0x40c78b34U,
+  0x94151217U, 0x0072e9f7U,
+// 5**216 (i=8), start=56, end=72
+  0x2b416aa1U, 0x7893c5a7U, 0xe37dc6d4U, 0x2bad2beaU, 0xf0fc846cU, 0x7575ae4bU,
+  0x62587b14U, 0x83b67a34U, 0x02110cdbU, 0xf7992f55U, 0x00deb022U, 0xa4a23becU,
+  0x8af5c5cdU, 0xb85b654fU, 0x818df38bU, 0x002e69d2U,
+// 5**243 (i=9), start=72, end=90
+  0x3518cbbdU, 0x20b0c15fU, 0x38756c2fU, 0xfb5dc3ddU, 0x22ad2d94U, 0xbf35a952U,
+  0xa699192aU, 0x9a613326U, 0xad2a9cedU, 0xd7f48968U, 0xe87dfb54U, 0xc8f05db6U,
+  0x5ef67531U, 0x31c1ab49U, 0xe202ac9fU, 0x9b2957b5U, 0xa143f6d3U, 0x0012bf07U,
+// 5**270 (i=10), start=90, end=110
+  0x8b971de9U, 0x21aba2e1U, 0x63944362U, 0x57172336U, 0xd9544225U, 0xfb534166U,
+  0x08c563eeU, 0x14640ee2U, 0x24e40d31U, 0x02b06537U, 0x03887f14U, 0x0285e533U,
+  0xb744ef26U, 0x8be3a6c4U, 0x266979b4U, 0x6761ece2U, 0xd9cb39e4U, 0xe67de319U,
+  0x0d39e796U, 0x00079250U,
+// 5**297 (i=11), start=110, end=132
+  0x260eb6e5U, 0xf414a796U, 0xee1a7491U, 0xdb9368ebU, 0xf50c105bU, 0x59157750U,
+  0x9ed2fb5cU, 0xf6e56d8bU, 0xeaee8d23U, 0x0f319f75U, 0x2aa134d6U, 0xac2908e9U,
+  0xd4413298U, 0x02f02a55U, 0x989d5a7aU, 0x70dde184U, 0xba8040a7U, 0x03200981U,
+  0xbe03b11cU, 0x3c1c2a18U, 0xd60427a1U, 0x00030ee0U,
+// 5**324 (i=12), start=132, end=156
+  0xce566d71U, 0xf1c4aa25U, 0x4e93ca53U, 0xa72283d0U, 0x551a73eaU, 0x3d0538e2U,
+  0x8da4303fU, 0x6a58de60U, 0x0e660221U, 0x49cf61a6U, 0x8d058fc1U, 0xb9d1a14cU,
+  0x4bab157dU, 0xc85c6932U, 0x518c8b9eU, 0x9b92b8d0U, 0x0d8a0e21U, 0xbd855df9U,
+  0xb3ea59a1U, 0x8da29289U, 0x4584d506U, 0x3752d80fU, 0xb72569c6U, 0x00013c33U,
+// 5**351 (i=13), start=156, end=182
+  0x190f354dU, 0x83695cfeU, 0xe5a4d0c7U, 0xb60fb7e8U, 0xee5bbcc4U, 0xb922054cU,
+  0xbb4f0d85U, 0x48394028U, 0x1d8957dbU, 0x0d7edb14U, 0x4ecc7587U, 0x505e9e02U,
+  0x4c87f36bU, 0x99e66bd6U, 0x44b9ed35U, 0x753037d4U, 0xe5fe5f27U, 0x2742c203U,
+  0x13b2ed2bU, 0xdc525d2cU, 0xe6fde59aU, 0x77ffb18fU, 0x13c5752cU, 0x08a84bccU,
+  0x859a4940U, 0x00007fb6U,
+// 5**378 (i=14), start=182, end=210
+  0x4f98cb39U, 0xa60edbbcU, 0x83b5872eU, 0xa501acffU, 0x9cc76f78U, 0xbadd4c73U,
+  0x43e989faU, 0xca7acf80U, 0x2e0c824fU, 0xb19f4ffcU, 0x092fd81cU, 0xe4eb645bU,
+  0xa1ff84c2U, 0x8a5a83baU, 0xa8a1fae9U, 0x1db43609U, 0xb0fed50bU, 0x0dd7d2bdU,
+  0x7d7accd8U, 0x91fa640fU, 0x37dcc6c5U, 0x1c417fd5U, 0xe4d462adU, 0xe8a43399U,
+  0x131bf9a5U, 0x8df54d29U, 0x36547dc1U, 0x00003395U,
+// 5**405 (i=15), start=210, end=240
+  0x5bd330f5U, 0x77d21967U, 0x1ac481b7U, 0x6be2f7ceU, 0x7f4792a9U, 0xe84c2c52U,
+  0x84592228U, 0x9dcaf829U, 0xdab44ce1U, 0x3d0c311bU, 0x532e297dU, 0x4704e8b4U,
+  0x9cdc32beU, 0x41e64d9dU, 0x7717bea1U, 0xa824c00dU, 0x08f50b27U, 0x0f198d77U,
+  0x49bbfdf0U, 0x025c6c69U, 0xd4e55cd3U, 0xf083602bU, 0xb9f0fecdU, 0xc0864aeaU,
+  0x9cb98681U, 0xaaf620e9U, 0xacb6df30U, 0x4faafe66U, 0x8af13c3bU, 0x000014d5U,
+// 5**432 (i=16), start=240, end=272
+  0x682bb941U, 0x89a9f297U, 0xcba75d7bU, 0x404217b1U, 0xb4e519e9U, 0xa1bc162bU,
+  0xf7f5910aU, 0x98715af5U, 0x2ff53e57U, 0xe3ef118cU, 0x490c4543U, 0xbc9b1734U,
+  0x2affbe4dU, 0x4cedcb4cU, 0xfb14e99eU, 0x35e34212U, 0xece39c24U, 0x07673ab3U,
+  0xe73115ddU, 0xd15d38e7U, 0x093eed3bU, 0xf8e7eac5U, 0x78a8cc80U, 0x25227aacU,
+  0x3f590551U, 0x413da1cbU, 0xdf643a55U, 0xab65ad44U, 0xd70b23d7U, 0xc672cd76U,
+  0x3364ea62U, 0x0000086aU,
+// 5**459 (i=17), start=272, end=306
+  0x22f163ddU, 0x23cf07acU, 0xbe2af6c2U, 0xf412f6f6U, 0xc3ff541eU, 0x6eeaf7deU,
+  0xa47047e0U, 0x408cda92U, 0x0f0eeb08U, 0x56deba9dU, 0xcfc6b090U, 0x8bbbdf04U,
+  0x3933cdb3U, 0x9e7bb67dU, 0x9f297035U, 0x38946244U, 0xee1d37bbU, 0xde898174U,
+  0x63f3559dU, 0x705b72fbU, 0x138d27d9U, 0xf8603a78U, 0x735eec44U, 0xe30987d5U,
+  0xc6d38070U, 0x9cfe548eU, 0x9ff01422U, 0x7c564aa8U, 0x91cc60baU, 0xcbc3565dU,
+  0x7550a50bU, 0x6909aeadU, 0x13234c45U, 0x00000366U,
+// 5**486 (i=18), start=306, end=342
+  0x17954989U, 0x3a7d7709U, 0x98042de5U, 0xa9011443U, 0x45e723c2U, 0x269ffd6fU,
+  0x58852a46U, 0xaaa1042aU, 0x2eee8153U, 0xb2b6c39eU, 0xaf845b65U, 0xf6c365d7U,
+  0xe4cffb2bU, 0xc840e90cU, 0xabea8abbU, 0x5c58f8d2U, 0x5c19fa3aU, 0x4670910aU,
+  0x4449f21cU, 0xefa645b3U, 0xcc427decU, 0x083c3d73U, 0x467cb413U, 0x6fe10ae4U,
+  0x3caffc72U, 0x9f8da55eU, 0x5e5c8ea7U, 0x490594bbU, 0xf0871b0bU, 0xdd89816cU,
+  0x8e931df8U, 0xe85ce1c9U, 0xcca090a5U, 0x575fa16bU, 0x6b9f106cU, 0x0000015fU,
+// 5**513 (i=19), start=342, end=380
+  0xee20d805U, 0x57bc3c07U, 0xcdea624eU, 0xd3f0f52dU, 0x9924b4f4U, 0xcf968640U,
+  0x61d41962U, 0xe87fb464U, 0xeaaf51c7U, 0x564c8b60U, 0xccda4028U, 0x529428bbU,
+  0x313a1fa8U, 0x96bd0f94U, 0x7a82ebaaU, 0xad99e7e9U, 0xf2668cd4U, 0xbe33a45eU,
+  0xfd0db669U, 0x87ee369fU, 0xd3ec20edU, 0x9c4d7db7U, 0xdedcf0d8U, 0x7cd2ca64U,
+  0xe25a6577U, 0x61003fd4U, 0xe56f54ccU, 0x10b7c748U, 0x40526e5eU, 0x7300ae87U,
+  0x5c439261U, 0x2c0ff469U, 0xbf723f12U, 0xb2379b61U, 0xbf59b4f5U, 0xc91b1c3fU,
+  0xf0046d27U, 0x0000008dU,
+// 5**540 (i=20), start=380, end=420
+  0x525c9e11U, 0xf4e0eb41U, 0xebb2895dU, 0x5da512f9U, 0x7d9b29d4U, 0x452f4edcU,
+  0x0b90bc37U, 0x341777cbU, 0x63d269afU, 0x1da77929U, 0x0a5c1826U, 0x77991898U,
+  0x5aeddf86U, 0xf853a877U, 0x538c31ccU, 0xe84896daU, 0xb7a0010bU, 0x17ef4de5U,
+  0xa52a2adeU, 0x029fd81cU, 0x987ce701U, 0x27fefd77U, 0xdb46c66fU, 0x5d301900U,
+  0x496998c0U, 0xbb6598b9U, 0x5eebb607U, 0xe547354aU, 0xdf4a2f7eU, 0xf06c4955U,
+  0x96242ffaU, 0x1775fb27U, 0xbecc58ceU, 0xebf2a53bU, 0x3eaad82aU, 0xf41137baU,
+  0x573e6fbaU, 0xfb4866b8U, 0x54002148U, 0x00000039U,
+};
+// clang-format on
+
+// Returns a pointer to the big integer data for (5**27)**i.  i must be
+// between 1 and 20, inclusive.
+const uint32_t* LargePowerOfFiveData(int i) {
+  return kLargePowersOfFive + i * (i - 1);
+}
+
+// Returns the size of the big integer data for (5**27)**i, in words.  i must be
+// between 1 and 20, inclusive.
+int LargePowerOfFiveSize(int i) { return 2 * i; }
+}  // namespace
+
+const uint32_t kFiveToNth[14] = {
+    1,     5,      25,      125,     625,      3125,      15625,
+    78125, 390625, 1953125, 9765625, 48828125, 244140625, 1220703125,
+};
+
+const uint32_t kTenToNth[10] = {
+    1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000,
+};
+
+template <int max_words>
+int BigUnsigned<max_words>::ReadFloatMantissa(const ParsedFloat& fp,
+                                              int significant_digits) {
+  SetToZero();
+  assert(fp.type == FloatType::kNumber);
+
+  if (fp.subrange_begin == nullptr) {
+    // We already exactly parsed the mantissa, so no more work is necessary.
+    words_[0] = fp.mantissa & 0xffffffffu;
+    words_[1] = fp.mantissa >> 32;
+    if (words_[1]) {
+      size_ = 2;
+    } else if (words_[0]) {
+      size_ = 1;
+    }
+    return fp.exponent;
+  }
+  int exponent_adjust =
+      ReadDigits(fp.subrange_begin, fp.subrange_end, significant_digits);
+  return fp.literal_exponent + exponent_adjust;
+}
+
+template <int max_words>
+int BigUnsigned<max_words>::ReadDigits(const char* begin, const char* end,
+                                       int significant_digits) {
+  assert(significant_digits <= Digits10() + 1);
+  SetToZero();
+
+  bool after_decimal_point = false;
+  // Discard any leading zeroes before the decimal point
+  while (begin < end && *begin == '0') {
+    ++begin;
+  }
+  int dropped_digits = 0;
+  // Discard any trailing zeroes.  These may or may not be after the decimal
+  // point.
+  while (begin < end && *std::prev(end) == '0') {
+    --end;
+    ++dropped_digits;
+  }
+  if (begin < end && *std::prev(end) == '.') {
+    // If the std::string ends in '.', either before or after dropping zeroes, then
+    // drop the decimal point and look for more digits to drop.
+    dropped_digits = 0;
+    --end;
+    while (begin < end && *std::prev(end) == '0') {
+      --end;
+      ++dropped_digits;
+    }
+  } else if (dropped_digits) {
+    // We dropped digits, and aren't sure if they're before or after the decimal
+    // point.  Figure that out now.
+    const char* dp = std::find(begin, end, '.');
+    if (dp != end) {
+      // The dropped trailing digits were after the decimal point, so don't
+      // count them.
+      dropped_digits = 0;
+    }
+  }
+  // Any non-fraction digits we dropped need to be accounted for in our exponent
+  // adjustment.
+  int exponent_adjust = dropped_digits;
+
+  uint32_t queued = 0;
+  int digits_queued = 0;
+  for (; begin != end && significant_digits > 0; ++begin) {
+    if (*begin == '.') {
+      after_decimal_point = true;
+      continue;
+    }
+    if (after_decimal_point) {
+      // For each fractional digit we emit in our parsed integer, adjust our
+      // decimal exponent to compensate.
+      --exponent_adjust;
+    }
+    int digit = (*begin - '0');
+    --significant_digits;
+    if (significant_digits == 0 && std::next(begin) != end &&
+        (digit == 0 || digit == 5)) {
+      // If this is the very last significant digit, but insignificant digits
+      // remain, we know that the last of those remaining significant digits is
+      // nonzero.  (If it wasn't, we would have stripped it before we got here.)
+      // So if this final digit is a 0 or 5, adjust it upward by 1.
+      //
+      // This adjustment is what allows incredibly large mantissas ending in
+      // 500000...000000000001 to correctly round up, rather than to nearest.
+      ++digit;
+    }
+    queued = 10 * queued + digit;
+    ++digits_queued;
+    if (digits_queued == kMaxSmallPowerOfTen) {
+      MultiplyBy(kTenToNth[kMaxSmallPowerOfTen]);
+      AddWithCarry(0, queued);
+      queued = digits_queued = 0;
+    }
+  }
+  // Encode any remaining digits.
+  if (digits_queued) {
+    MultiplyBy(kTenToNth[digits_queued]);
+    AddWithCarry(0, queued);
+  }
+
+  // If any insignificant digits remain, we will drop them.  But if we have not
+  // yet read the decimal point, then we have to adjust the exponent to account
+  // for the dropped digits.
+  if (begin < end && !after_decimal_point) {
+    // This call to std::find will result in a pointer either to the decimal
+    // point, or to the end of our buffer if there was none.
+    //
+    // Either way, [begin, decimal_point) will contain the set of dropped digits
+    // that require an exponent adjustment.
+    const char* decimal_point = std::find(begin, end, '.');
+    exponent_adjust += (decimal_point - begin);
+  }
+  return exponent_adjust;
+}
+
+template <int max_words>
+/* static */ BigUnsigned<max_words> BigUnsigned<max_words>::FiveToTheNth(
+    int n) {
+  BigUnsigned answer(1u);
+
+  // Seed from the table of large powers, if possible.
+  bool first_pass = true;
+  while (n >= kLargePowerOfFiveStep) {
+    int big_power =
+        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_);
+      answer.size_ = LargePowerOfFiveSize(big_power);
+      first_pass = false;
+    } else {
+      answer.MultiplyBy(LargePowerOfFiveSize(big_power),
+                        LargePowerOfFiveData(big_power));
+    }
+    n -= kLargePowerOfFiveStep * big_power;
+  }
+  answer.MultiplyByFiveToTheNth(n);
+  return answer;
+}
+
+template <int max_words>
+void BigUnsigned<max_words>::MultiplyStep(int original_size,
+                                          const uint32_t* other_words,
+                                          int other_size, int step) {
+  int this_i = std::min(original_size - 1, step);
+  int other_i = step - this_i;
+
+  uint64_t this_word = 0;
+  uint64_t carry = 0;
+  for (; this_i >= 0 && other_i < other_size; --this_i, ++other_i) {
+    uint64_t product = words_[this_i];
+    product *= other_words[other_i];
+    this_word += product;
+    carry += (this_word >> 32);
+    this_word &= 0xffffffff;
+  }
+  AddWithCarry(step + 1, carry);
+  words_[step] = this_word & 0xffffffff;
+  if (this_word > 0 && size_ <= step) {
+    size_ = step + 1;
+  }
+}
+
+template <int max_words>
+std::string BigUnsigned<max_words>::ToString() const {
+  BigUnsigned<max_words> copy = *this;
+  std::string result;
+  // Build result in reverse order
+  while (copy.size() > 0) {
+    int next_digit = copy.DivMod<10>();
+    result.push_back('0' + next_digit);
+  }
+  if (result.empty()) {
+    result.push_back('0');
+  }
+  std::reverse(result.begin(), result.end());
+  return result;
+}
+
+template class BigUnsigned<4>;
+template class BigUnsigned<84>;
+
+}  // namespace strings_internal
+}  // namespace absl
diff --git a/absl/strings/internal/charconv_bigint.h b/absl/strings/internal/charconv_bigint.h
new file mode 100644
index 000000000000..aa70af2c2894
--- /dev/null
+++ b/absl/strings/internal/charconv_bigint.h
@@ -0,0 +1,426 @@
+// 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
+//
+//      http://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_CHARCONV_BIGINT_H_
+#define ABSL_STRINGS_INTERNAL_CHARCONV_BIGINT_H_
+
+#include <algorithm>
+#include <cstdint>
+#include <iostream>
+#include <string>
+
+#include "absl/strings/ascii.h"
+#include "absl/strings/internal/charconv_parse.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+namespace strings_internal {
+
+// The largest power that 5 that can be raised to, and still fit in a uint32_t.
+constexpr int kMaxSmallPowerOfFive = 13;
+// The largest power that 10 that can be raised to, and still fit in a uint32_t.
+constexpr int kMaxSmallPowerOfTen = 9;
+
+extern const uint32_t kFiveToNth[kMaxSmallPowerOfFive + 1];
+extern const uint32_t kTenToNth[kMaxSmallPowerOfTen + 1];
+
+// Large, fixed-width unsigned integer.
+//
+// Exact rounding for decimal-to-binary floating point conversion requires very
+// large integer math, but a design goal of absl::from_chars is to avoid
+// allocating memory.  The integer precision needed for decimal-to-binary
+// conversions is large but bounded, so a huge fixed-width integer class
+// suffices.
+//
+// This is an intentionally limited big integer class.  Only needed operations
+// are implemented.  All storage lives in an array data member, and all
+// arithmetic is done in-place, to avoid requiring separate storage for operand
+// and result.
+//
+// This is an internal class.  Some methods live in the .cc file, and are
+// instantiated only for the values of max_words we need.
+template <int max_words>
+class BigUnsigned {
+ public:
+  static_assert(max_words == 4 || max_words == 84,
+                "unsupported max_words value");
+
+  BigUnsigned() : size_(0), words_{} {}
+  explicit BigUnsigned(uint32_t v) : size_(v > 0 ? 1 : 0), words_{v} {}
+  explicit BigUnsigned(uint64_t v)
+      : size_(0),
+        words_{static_cast<uint32_t>(v & 0xffffffff),
+               static_cast<uint32_t>(v >> 32)} {
+    if (words_[1]) {
+      size_ = 2;
+    } else if (words_[0]) {
+      size_ = 1;
+    }
+  }
+
+  // Constructs a BigUnsigned from the given string_view containing a decimal
+  // value.  If the input std::string is not a decimal integer, constructs a 0
+  // instead.
+  explicit BigUnsigned(absl::string_view sv) : size_(0), words_{} {
+    // Check for valid input, returning a 0 otherwise.  This is reasonable
+    // behavior only because this constructor is for unit tests.
+    if (std::find_if_not(sv.begin(), sv.end(), ascii_isdigit) != sv.end() ||
+        sv.empty()) {
+      return;
+    }
+    int exponent_adjust =
+        ReadDigits(sv.data(), sv.data() + sv.size(), Digits10() + 1);
+    if (exponent_adjust > 0) {
+      MultiplyByTenToTheNth(exponent_adjust);
+    }
+  }
+
+  // Loads the mantissa value of a previously-parsed float.
+  //
+  // Returns the associated decimal exponent.  The value of the parsed float is
+  // exactly *this * 10**exponent.
+  int ReadFloatMantissa(const ParsedFloat& fp, int significant_digits);
+
+  // Returns the number of decimal digits of precision this type provides.  All
+  // numbers with this many decimal digits or fewer are representable by this
+  // type.
+  //
+  // Analagous 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;
+  }
+
+  // Shifts left by the given number of bits.
+  void ShiftLeft(int count) {
+    if (count > 0) {
+      const int word_shift = count / 32;
+      if (word_shift >= max_words) {
+        SetToZero();
+        return;
+      }
+      size_ = std::min(size_ + word_shift, max_words);
+      count %= 32;
+      if (count == 0) {
+        std::copy_backward(words_, words_ + size_ - word_shift, words_ + size_);
+      } else {
+        for (int i = std::min(size_, max_words - 1); i > word_shift; --i) {
+          words_[i] = (words_[i - word_shift] << count) |
+                      (words_[i - word_shift - 1] >> (32 - count));
+        }
+        words_[word_shift] = words_[0] << count;
+        // Grow size_ if necessary.
+        if (size_ < max_words && words_[size_]) {
+          ++size_;
+        }
+      }
+      std::fill(words_, words_ + word_shift, 0u);
+    }
+  }
+
+
+  // Multiplies by v in-place.
+  void MultiplyBy(uint32_t v) {
+    if (size_ == 0 || v == 1) {
+      return;
+    }
+    if (v == 0) {
+      SetToZero();
+      return;
+    }
+    const uint64_t factor = v;
+    uint64_t window = 0;
+    for (int i = 0; i < size_; ++i) {
+      window += factor * words_[i];
+      words_[i] = window & 0xffffffff;
+      window >>= 32;
+    }
+    // If carry bits remain and there's space for them, grow size_.
+    if (window && size_ < max_words) {
+      words_[size_] = window & 0xffffffff;
+      ++size_;
+    }
+  }
+
+  void MultiplyBy(uint64_t v) {
+    uint32_t words[2];
+    words[0] = static_cast<uint32_t>(v);
+    words[1] = static_cast<uint32_t>(v >> 32);
+    if (words[1] == 0) {
+      MultiplyBy(words[0]);
+    } else {
+      MultiplyBy(2, words);
+    }
+  }
+
+  // Multiplies in place by 5 to the power of n.  n must be non-negative.
+  void MultiplyByFiveToTheNth(int n) {
+    while (n >= kMaxSmallPowerOfFive) {
+      MultiplyBy(kFiveToNth[kMaxSmallPowerOfFive]);
+      n -= kMaxSmallPowerOfFive;
+    }
+    if (n > 0) {
+      MultiplyBy(kFiveToNth[n]);
+    }
+  }
+
+  // Multiplies in place by 10 to the power of n.  n must be non-negative.
+  void MultiplyByTenToTheNth(int n) {
+    if (n > kMaxSmallPowerOfTen) {
+      // For large n, raise to a power of 5, then shift left by the same amount.
+      // (10**n == 5**n * 2**n.)  This requires fewer multiplications overall.
+      MultiplyByFiveToTheNth(n);
+      ShiftLeft(n);
+    } else if (n > 0) {
+      // We can do this more quickly for very small N by using a single
+      // multiplication.
+      MultiplyBy(kTenToNth[n]);
+    }
+  }
+
+  // Returns the value of 5**n, for non-negative n.  This implementation uses
+  // a lookup table, and is faster then seeding a BigUnsigned with 1 and calling
+  // MultiplyByFiveToTheNth().
+  static BigUnsigned FiveToTheNth(int n);
+
+  // Multiplies by another BigUnsigned, in-place.
+  template <int M>
+  void MultiplyBy(const BigUnsigned<M>& other) {
+    MultiplyBy(other.size(), other.words());
+  }
+
+  void SetToZero() {
+    std::fill(words_, words_ + size_, 0u);
+    size_ = 0;
+  }
+
+  // Returns the value of the nth word of this BigUnsigned.  This is
+  // range-checked, and returns 0 on out-of-bounds accesses.
+  uint32_t GetWord(int index) const {
+    if (index < 0 || index >= size_) {
+      return 0;
+    }
+    return words_[index];
+  }
+
+  // Returns this integer as a decimal std::string.  This is not used in the decimal-
+  // to-binary conversion; it is intended to aid in testing.
+  std::string ToString() const;
+
+  int size() const { return size_; }
+  const uint32_t* words() const { return words_; }
+
+ private:
+  // Reads the number between [begin, end), possibly containing a decimal point,
+  // into this BigUnsigned.
+  //
+  // Callers are required to ensure [begin, end) contains a valid number, with
+  // one or more decimal digits and at most one decimal point.  This routine
+  // will behave unpredictably if these preconditions are not met.
+  //
+  // Only the first `significant_digits` digits are read.  Digits beyond this
+  // limit are "sticky": If the final significant digit is 0 or 5, and if any
+  // dropped digit is nonzero, then that final significant digit is adjusted up
+  // to 1 or 6.  This adjustment allows for precise rounding.
+  //
+  // Returns `exponent_adjustment`, a power-of-ten exponent adjustment to
+  // account for the decimal point and for dropped significant digits.  After
+  // this function returns,
+  //   actual_value_of_parsed_string ~= *this * 10**exponent_adjustment.
+  int ReadDigits(const char* begin, const char* end, int significant_digits);
+
+  // Performs a step of big integer multiplication.  This computes the full
+  // (64-bit-wide) values that should be added at the given index (step), and
+  // adds to that location in-place.
+  //
+  // Because our math all occurs in place, we must multiply starting from the
+  // highest word working downward.  (This is a bit more expensive due to the
+  // extra carries involved.)
+  //
+  // This must be called in steps, for each word to be calculated, starting from
+  // the high end and working down to 0.  The first value of `step` should be
+  //   `std::min(original_size + other.size_ - 2, max_words - 1)`.
+  // The reason for this expression is that multiplying the i'th word from one
+  // multiplicand and the j'th word of another multiplicand creates a
+  // two-word-wide value to be stored at the (i+j)'th element.  The highest
+  // word indices we will access are `original_size - 1` from this object, and
+  // `other.size_ - 1` from our operand.  Therefore,
+  // `original_size + other.size_ - 2` is the first step we should calculate,
+  // but limited on an upper bound by max_words.
+
+  // Working from high-to-low ensures that we do not overwrite the portions of
+  // the initial value of *this which are still needed for later steps.
+  //
+  // Once called with step == 0, *this contains the result of the
+  // multiplication.
+  //
+  // `original_size` is the size_ of *this before the first call to
+  // MultiplyStep().  `other_words` and `other_size` are the contents of our
+  // operand.  `step` is the step to perform, as described above.
+  void MultiplyStep(int original_size, const uint32_t* other_words,
+                    int other_size, int step);
+
+  void MultiplyBy(int other_size, const uint32_t* other_words) {
+    const int original_size = size_;
+    const int first_step =
+        std::min(original_size + other_size - 2, max_words - 1);
+    for (int step = first_step; step >= 0; --step) {
+      MultiplyStep(original_size, other_words, other_size, step);
+    }
+  }
+
+  // Adds a 32-bit value to the index'th word, with carry.
+  void AddWithCarry(int index, uint32_t value) {
+    if (value) {
+      while (index < max_words && value > 0) {
+        words_[index] += value;
+        // carry if we overflowed in this word:
+        if (value > words_[index]) {
+          value = 1;
+          ++index;
+        } else {
+          value = 0;
+        }
+      }
+      size_ = std::min(max_words, std::max(index + 1, size_));
+    }
+  }
+
+  void AddWithCarry(int index, uint64_t value) {
+    if (value && index < max_words) {
+      uint32_t high = value >> 32;
+      uint32_t low = value & 0xffffffff;
+      words_[index] += low;
+      if (words_[index] < low) {
+        ++high;
+        if (high == 0) {
+          // Carry from the low word caused our high word to overflow.
+          // Short circuit here to do the right thing.
+          AddWithCarry(index + 2, static_cast<uint32_t>(1));
+          return;
+        }
+      }
+      if (high > 0) {
+        AddWithCarry(index + 1, high);
+      } else {
+        // Normally 32-bit AddWithCarry() sets size_, but since we don't call
+        // it when `high` is 0, do it ourselves here.
+        size_ = std::min(max_words, std::max(index + 1, size_));
+      }
+    }
+  }
+
+  // Divide this in place by a constant divisor.  Returns the remainder of the
+  // division.
+  template <uint32_t divisor>
+  uint32_t DivMod() {
+    uint64_t accumulator = 0;
+    for (int i = size_ - 1; i >= 0; --i) {
+      accumulator <<= 32;
+      accumulator += words_[i];
+      // accumulator / divisor will never overflow an int32_t in this loop
+      words_[i] = static_cast<uint32_t>(accumulator / divisor);
+      accumulator = accumulator % divisor;
+    }
+    while (size_ > 0 && words_[size_ - 1] == 0) {
+      --size_;
+    }
+    return static_cast<uint32_t>(accumulator);
+  }
+
+  // The number of elements in words_ that may carry significant values.
+  // All elements beyond this point are 0.
+  //
+  // When size_ is 0, this BigUnsigned stores the value 0.
+  // When size_ is nonzero, is *not* guaranteed that words_[size_ - 1] is
+  // nonzero.  This can occur due to overflow truncation.
+  // In particular, x.size_ != y.size_ does *not* imply x != y.
+  int size_;
+  uint32_t words_[max_words];
+};
+
+// Compares two big integer instances.
+//
+// Returns -1 if lhs < rhs, 0 if lhs == rhs, and 1 if lhs > rhs.
+template <int N, int M>
+int Compare(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) {
+  int limit = std::max(lhs.size(), rhs.size());
+  for (int i = limit - 1; i >= 0; --i) {
+    const uint32_t lhs_word = lhs.GetWord(i);
+    const uint32_t rhs_word = rhs.GetWord(i);
+    if (lhs_word < rhs_word) {
+      return -1;
+    } else if (lhs_word > rhs_word) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+template <int N, int M>
+bool operator==(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) {
+  int limit = std::max(lhs.size(), rhs.size());
+  for (int i = 0; i < limit; ++i) {
+    if (lhs.GetWord(i) != rhs.GetWord(i)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+template <int N, int M>
+bool operator!=(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) {
+  return !(lhs == rhs);
+}
+
+template <int N, int M>
+bool operator<(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) {
+  return Compare(lhs, rhs) == -1;
+}
+
+template <int N, int M>
+bool operator>(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) {
+  return rhs < lhs;
+}
+template <int N, int M>
+bool operator<=(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) {
+  return !(rhs < lhs);
+}
+template <int N, int M>
+bool operator>=(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) {
+  return !(lhs < rhs);
+}
+
+// Output operator for BigUnsigned, for testing purposes only.
+template <int N>
+std::ostream& operator<<(std::ostream& os, const BigUnsigned<N>& num) {
+  return os << num.ToString();
+}
+
+// Explicit instantiation declarations for the sizes of BigUnsigned that we
+// are using.
+//
+// For now, the choices of 4 and 84 are arbitrary; 4 is a small value that is
+// still bigger than an int128, and 84 is a large value we will want to use
+// in the from_chars implementation.
+//
+// Comments justifying the use of 84 belong in the from_chars implementation,
+// and will be added in a follow-up CL.
+extern template class BigUnsigned<4>;
+extern template class BigUnsigned<84>;
+
+}  // namespace strings_internal
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_CHARCONV_BIGINT_H_
diff --git a/absl/strings/internal/charconv_bigint_test.cc b/absl/strings/internal/charconv_bigint_test.cc
new file mode 100644
index 000000000000..9b6357888fbf
--- /dev/null
+++ b/absl/strings/internal/charconv_bigint_test.cc
@@ -0,0 +1,203 @@
+// 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
+//
+//      http://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/charconv_bigint.h"
+
+#include <string>
+
+#include "gtest/gtest.h"
+
+namespace absl {
+namespace strings_internal {
+
+TEST(BigUnsigned, ShiftLeft) {
+  {
+    // Check that 3 * 2**100 is calculated correctly
+    BigUnsigned<4> num(3u);
+    num.ShiftLeft(100);
+    EXPECT_EQ(num, BigUnsigned<4>("3802951800684688204490109616128"));
+  }
+  {
+    // Test that overflow is truncated properly.
+    // 15 is 4 bits long, and BigUnsigned<4> is a 128-bit bigint.
+    // Shifting left by 125 bits should truncate off the high bit, so that
+    //   15 << 125 == 7 << 125
+    // after truncation.
+    BigUnsigned<4> a(15u);
+    BigUnsigned<4> b(7u);
+    BigUnsigned<4> c(3u);
+    a.ShiftLeft(125);
+    b.ShiftLeft(125);
+    c.ShiftLeft(125);
+    EXPECT_EQ(a, b);
+    EXPECT_NE(a, c);
+  }
+  {
+    // Same test, larger bigint:
+    BigUnsigned<84> a(15u);
+    BigUnsigned<84> b(7u);
+    BigUnsigned<84> c(3u);
+    a.ShiftLeft(84 * 32 - 3);
+    b.ShiftLeft(84 * 32 - 3);
+    c.ShiftLeft(84 * 32 - 3);
+    EXPECT_EQ(a, b);
+    EXPECT_NE(a, c);
+  }
+  {
+    // Check that incrementally shifting has the same result as doing it all at
+    // once (attempting to capture corner cases.)
+    const std::string seed = "1234567890123456789012345678901234567890";
+    BigUnsigned<84> a(seed);
+    for (int i = 1; i <= 84 * 32; ++i) {
+      a.ShiftLeft(1);
+      BigUnsigned<84> b(seed);
+      b.ShiftLeft(i);
+      EXPECT_EQ(a, b);
+    }
+    // And we should have fully rotated all bits off by now:
+    EXPECT_EQ(a, BigUnsigned<84>(0u));
+  }
+}
+
+TEST(BigUnsigned, MultiplyByUint32) {
+  const BigUnsigned<84> factorial_100(
+      "933262154439441526816992388562667004907159682643816214685929638952175999"
+      "932299156089414639761565182862536979208272237582511852109168640000000000"
+      "00000000000000");
+  BigUnsigned<84> a(1u);
+  for (uint32_t i = 1; i <= 100; ++i) {
+    a.MultiplyBy(i);
+  }
+  EXPECT_EQ(a, BigUnsigned<84>(factorial_100));
+}
+
+TEST(BigUnsigned, MultiplyByBigUnsigned) {
+  {
+    // Put the terms of factorial_200 into two bigints, and multiply them
+    // together.
+    const BigUnsigned<84> factorial_200(
+        "7886578673647905035523632139321850622951359776871732632947425332443594"
+        "4996340334292030428401198462390417721213891963883025764279024263710506"
+        "1926624952829931113462857270763317237396988943922445621451664240254033"
+        "2918641312274282948532775242424075739032403212574055795686602260319041"
+        "7032406235170085879617892222278962370389737472000000000000000000000000"
+        "0000000000000000000000000");
+    BigUnsigned<84> evens(1u);
+    BigUnsigned<84> odds(1u);
+    for (uint32_t i = 1; i < 200; i += 2) {
+      odds.MultiplyBy(i);
+      evens.MultiplyBy(i + 1);
+    }
+    evens.MultiplyBy(odds);
+    EXPECT_EQ(evens, factorial_200);
+  }
+  {
+    // Multiply various powers of 10 together.
+    for (int a = 0 ; a < 700; a += 25) {
+      SCOPED_TRACE(a);
+      BigUnsigned<84> a_value("3" + std::string(a, '0'));
+      for (int b = 0; b < (700 - a); b += 25) {
+        SCOPED_TRACE(b);
+        BigUnsigned<84> b_value("2" + std::string(b, '0'));
+        BigUnsigned<84> expected_product("6" + std::string(a + b, '0'));
+        b_value.MultiplyBy(a_value);
+        EXPECT_EQ(b_value, expected_product);
+      }
+    }
+  }
+}
+
+TEST(BigUnsigned, MultiplyByOverflow) {
+  {
+    // Check that multiplcation overflow predictably truncates.
+
+    // A big int with all bits on.
+    BigUnsigned<4> all_bits_on("340282366920938463463374607431768211455");
+    // Modulo 2**128, this is equal to -1.  Therefore the square of this,
+    // modulo 2**128, should be 1.
+    all_bits_on.MultiplyBy(all_bits_on);
+    EXPECT_EQ(all_bits_on, BigUnsigned<4>(1u));
+  }
+  {
+    // Try multiplying a large bigint by 2**50, and compare the result to
+    // shifting.
+    BigUnsigned<4> value_1("12345678901234567890123456789012345678");
+    BigUnsigned<4> value_2("12345678901234567890123456789012345678");
+    BigUnsigned<4> two_to_fiftieth(1u);
+    two_to_fiftieth.ShiftLeft(50);
+
+    value_1.ShiftLeft(50);
+    value_2.MultiplyBy(two_to_fiftieth);
+    EXPECT_EQ(value_1, value_2);
+  }
+}
+
+TEST(BigUnsigned, FiveToTheNth) {
+  {
+    // Sanity check that MultiplyByFiveToTheNth gives consistent answers, up to
+    // and including overflow.
+    for (int i = 0; i < 1160; ++i) {
+      SCOPED_TRACE(i);
+      BigUnsigned<84> value_1(123u);
+      BigUnsigned<84> value_2(123u);
+      value_1.MultiplyByFiveToTheNth(i);
+      for (int j = 0; j < i; j++) {
+        value_2.MultiplyBy(5u);
+      }
+      EXPECT_EQ(value_1, value_2);
+    }
+  }
+  {
+    // Check that the faster, table-lookup-based static method returns the same
+    // result that multiplying in-place would return, up to and including
+    // overflow.
+    for (int i = 0; i < 1160; ++i) {
+      SCOPED_TRACE(i);
+      BigUnsigned<84> value_1(1u);
+      value_1.MultiplyByFiveToTheNth(i);
+      BigUnsigned<84> value_2 = BigUnsigned<84>::FiveToTheNth(i);
+      EXPECT_EQ(value_1, value_2);
+    }
+  }
+}
+
+TEST(BigUnsigned, TenToTheNth) {
+  {
+    // Sanity check MultiplyByTenToTheNth.
+    for (int i = 0; i < 800; ++i) {
+      SCOPED_TRACE(i);
+      BigUnsigned<84> value_1(123u);
+      BigUnsigned<84> value_2(123u);
+      value_1.MultiplyByTenToTheNth(i);
+      for (int j = 0; j < i; j++) {
+        value_2.MultiplyBy(10u);
+      }
+      EXPECT_EQ(value_1, value_2);
+    }
+  }
+  {
+    // Alternate testing approach, taking advantage of the decimal parser.
+    for (int i = 0; i < 200; ++i) {
+      SCOPED_TRACE(i);
+      BigUnsigned<84> value_1(135u);
+      value_1.MultiplyByTenToTheNth(i);
+      BigUnsigned<84> value_2("135" + std::string(i, '0'));
+      EXPECT_EQ(value_1, value_2);
+    }
+  }
+}
+
+
+}  // namespace strings_internal
+}  // namespace absl
diff --git a/absl/strings/internal/charconv_parse.cc b/absl/strings/internal/charconv_parse.cc
new file mode 100644
index 000000000000..a04cc67669a7
--- /dev/null
+++ b/absl/strings/internal/charconv_parse.cc
@@ -0,0 +1,496 @@
+// 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
+//
+//      http://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/charconv_parse.h"
+#include "absl/strings/charconv.h"
+
+#include <cassert>
+#include <cstdint>
+#include <limits>
+
+#include "absl/strings/internal/memutil.h"
+
+namespace absl {
+namespace {
+
+// ParseFloat<10> will read the first 19 significant digits of the mantissa.
+// This number was chosen for multiple reasons.
+//
+// (a) First, for whatever integer type we choose to represent the mantissa, we
+// want to choose the largest possible number of decimal digits for that integer
+// type.  We are using uint64_t, which can express any 19-digit unsigned
+// integer.
+//
+// (b) Second, we need to parse enough digits that the binary value of any
+// mantissa we capture has more bits of resolution than the mantissa
+// representation in the target float.  Our algorithm requires at least 3 bits
+// of headway, but 19 decimal digits give a little more than that.
+//
+// The following static assertions verify the above comments:
+constexpr int kDecimalMantissaDigitsMax = 19;
+
+static_assert(std::numeric_limits<uint64_t>::digits10 ==
+                  kDecimalMantissaDigitsMax,
+              "(a) above");
+
+// IEEE doubles, which we assume in Abseil, have 53 binary bits of mantissa.
+static_assert(std::numeric_limits<double>::is_iec559, "IEEE double assumed");
+static_assert(std::numeric_limits<double>::radix == 2, "IEEE double fact");
+static_assert(std::numeric_limits<double>::digits == 53, "IEEE double fact");
+
+// The lowest valued 19-digit decimal mantissa we can read still contains
+// sufficient information to reconstruct a binary mantissa.
+static_assert(1000000000000000000u > (uint64_t(1) << (53 + 3)), "(b) above");
+
+// ParseFloat<16> will read the first 15 significant digits of the mantissa.
+//
+// Because a base-16-to-base-2 conversion can be done exactly, we do not need
+// to maximize the number of scanned hex digits to improve our conversion.  What
+// is required is to scan two more bits than the mantissa can represent, so that
+// we always round correctly.
+//
+// (One extra bit does not suffice to perform correct rounding, since a number
+// exactly halfway between two representable floats has unique rounding rules,
+// so we need to differentiate between a "halfway between" number and a "closer
+// to the larger value" number.)
+constexpr int kHexadecimalMantissaDigitsMax = 15;
+
+// The minimum number of significant bits that will be read from
+// kHexadecimalMantissaDigitsMax hex digits.  We must subtract by three, since
+// the most significant digit can be a "1", which only contributes a single
+// significant bit.
+constexpr int kGuaranteedHexadecimalMantissaBitPrecision =
+    4 * kHexadecimalMantissaDigitsMax - 3;
+
+static_assert(kGuaranteedHexadecimalMantissaBitPrecision >
+                  std::numeric_limits<double>::digits + 2,
+              "kHexadecimalMantissaDigitsMax too small");
+
+// We also impose a limit on the number of significant digits we will read from
+// an exponent, to avoid having to deal with integer overflow.  We use 9 for
+// this purpose.
+//
+// If we read a 9 digit exponent, the end result of the conversion will
+// necessarily be infinity or zero, depending on the sign of the exponent.
+// Therefore we can just drop extra digits on the floor without any extra
+// logic.
+constexpr int kDecimalExponentDigitsMax = 9;
+static_assert(std::numeric_limits<int>::digits10 >= kDecimalExponentDigitsMax,
+              "int type too small");
+
+// To avoid incredibly large inputs causing integer overflow for our exponent,
+// we impose an arbitrary but very large limit on the number of significant
+// digits we will accept.  The implementation refuses to match a std::string with
+// more consecutive significant mantissa digits than this.
+constexpr int kDecimalDigitLimit = 50000000;
+
+// Corresponding limit for hexadecimal digit inputs.  This is one fourth the
+// amount of kDecimalDigitLimit, since each dropped hexadecimal digit requires
+// a binary exponent adjustment of 4.
+constexpr int kHexadecimalDigitLimit = kDecimalDigitLimit / 4;
+
+// The largest exponent we can read is 999999999 (per
+// kDecimalExponentDigitsMax), and the largest exponent adjustment we can get
+// from dropped mantissa digits is 2 * kDecimalDigitLimit, and the sum of these
+// comfortably fits in an integer.
+//
+// We count kDecimalDigitLimit twice because there are independent limits for
+// numbers before and after the decimal point.  (In the case where there are no
+// significant digits before the decimal point, there are independent limits for
+// post-decimal-point leading zeroes and for significant digits.)
+static_assert(999999999 + 2 * kDecimalDigitLimit <
+                  std::numeric_limits<int>::max(),
+              "int type too small");
+static_assert(999999999 + 2 * (4 * kHexadecimalDigitLimit) <
+                  std::numeric_limits<int>::max(),
+              "int type too small");
+
+// Returns true if the provided bitfield allows parsing an exponent value
+// (e.g., "1.5e100").
+bool AllowExponent(chars_format flags) {
+  bool fixed = (flags & chars_format::fixed) == chars_format::fixed;
+  bool scientific =
+      (flags & chars_format::scientific) == chars_format::scientific;
+  return scientific || !fixed;
+}
+
+// Returns true if the provided bitfield requires an exponent value be present.
+bool RequireExponent(chars_format flags) {
+  bool fixed = (flags & chars_format::fixed) == chars_format::fixed;
+  bool scientific =
+      (flags & chars_format::scientific) == chars_format::scientific;
+  return scientific && !fixed;
+}
+
+const int8_t kAsciiToInt[256] = {
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0,  1,  2,  3,  4,  5,  6,  7,  8,
+    9,  -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1};
+
+// Returns true if `ch` is a digit in the given base
+template <int base>
+bool IsDigit(char ch);
+
+// Converts a valid `ch` to its digit value in the given base.
+template <int base>
+unsigned ToDigit(char ch);
+
+// Returns true if `ch` is the exponent delimiter for the given base.
+template <int base>
+bool IsExponentCharacter(char ch);
+
+// Returns the maximum number of significant digits we will read for a float
+// in the given base.
+template <int base>
+constexpr int MantissaDigitsMax();
+
+// Returns the largest consecutive run of digits we will accept when parsing a
+// number in the given base.
+template <int base>
+constexpr int DigitLimit();
+
+// Returns the amount the exponent must be adjusted by for each dropped digit.
+// (For decimal this is 1, since the digits are in base 10 and the exponent base
+// is also 10, but for hexadecimal this is 4, since the digits are base 16 but
+// the exponent base is 2.)
+template <int base>
+constexpr int DigitMagnitude();
+
+template <>
+bool IsDigit<10>(char ch) {
+  return ch >= '0' && ch <= '9';
+}
+template <>
+bool IsDigit<16>(char ch) {
+  return kAsciiToInt[static_cast<unsigned char>(ch)] >= 0;
+}
+
+template <>
+unsigned ToDigit<10>(char ch) {
+  return ch - '0';
+}
+template <>
+unsigned ToDigit<16>(char ch) {
+  return kAsciiToInt[static_cast<unsigned char>(ch)];
+}
+
+template <>
+bool IsExponentCharacter<10>(char ch) {
+  return ch == 'e' || ch == 'E';
+}
+
+template <>
+bool IsExponentCharacter<16>(char ch) {
+  return ch == 'p' || ch == 'P';
+}
+
+template <>
+constexpr int MantissaDigitsMax<10>() {
+  return kDecimalMantissaDigitsMax;
+}
+template <>
+constexpr int MantissaDigitsMax<16>() {
+  return kHexadecimalMantissaDigitsMax;
+}
+
+template <>
+constexpr int DigitLimit<10>() {
+  return kDecimalDigitLimit;
+}
+template <>
+constexpr int DigitLimit<16>() {
+  return kHexadecimalDigitLimit;
+}
+
+template <>
+constexpr int DigitMagnitude<10>() {
+  return 1;
+}
+template <>
+constexpr int DigitMagnitude<16>() {
+  return 4;
+}
+
+// Reads decimal digits from [begin, end) into *out.  Returns the number of
+// digits consumed.
+//
+// After max_digits has been read, keeps consuming characters, but no longer
+// adjusts *out.  If a nonzero digit is dropped this way, *dropped_nonzero_digit
+// is set; otherwise, it is left unmodified.
+//
+// If no digits are matched, returns 0 and leaves *out unchanged.
+//
+// ConsumeDigits does not protect against overflow on *out; max_digits must
+// be chosen with respect to type T to avoid the possibility of overflow.
+template <int base, typename T>
+std::size_t ConsumeDigits(const char* begin, const char* end, int max_digits,
+                          T* out, bool* dropped_nonzero_digit) {
+  if (base == 10) {
+    assert(max_digits <= std::numeric_limits<T>::digits10);
+  } else if (base == 16) {
+    assert(max_digits * 4 <= std::numeric_limits<T>::digits);
+  }
+  const char* const original_begin = begin;
+  T accumulator = *out;
+  const char* significant_digits_end =
+      (end - begin > max_digits) ? begin + max_digits : end;
+  while (begin < significant_digits_end && IsDigit<base>(*begin)) {
+    // Do not guard against *out overflow; max_digits was chosen to avoid this.
+    // Do assert against it, to detect problems in debug builds.
+    auto digit = static_cast<T>(ToDigit<base>(*begin));
+    assert(accumulator * base >= accumulator);
+    accumulator *= base;
+    assert(accumulator + digit >= accumulator);
+    accumulator += digit;
+    ++begin;
+  }
+  bool dropped_nonzero = false;
+  while (begin < end && IsDigit<base>(*begin)) {
+    dropped_nonzero = dropped_nonzero || (*begin != '0');
+    ++begin;
+  }
+  if (dropped_nonzero && dropped_nonzero_digit != nullptr) {
+    *dropped_nonzero_digit = true;
+  }
+  *out = accumulator;
+  return begin - original_begin;
+}
+
+// Returns true if `v` is one of the chars allowed inside parentheses following
+// a NaN.
+bool IsNanChar(char v) {
+  return (v == '_') || (v >= '0' && v <= '9') || (v >= 'a' && v <= 'z') ||
+         (v >= 'A' && v <= 'Z');
+}
+
+// Checks the range [begin, end) for a strtod()-formatted infinity or NaN.  If
+// one is found, sets `out` appropriately and returns true.
+bool ParseInfinityOrNan(const char* begin, const char* end,
+                        strings_internal::ParsedFloat* out) {
+  if (end - begin < 3) {
+    return false;
+  }
+  switch (*begin) {
+    case 'i':
+    case 'I': {
+      // An infinity std::string consists of the characters "inf" or "infinity",
+      // case insensitive.
+      if (strings_internal::memcasecmp(begin + 1, "nf", 2) != 0) {
+        return false;
+      }
+      out->type = strings_internal::FloatType::kInfinity;
+      if (end - begin >= 8 &&
+          strings_internal::memcasecmp(begin + 3, "inity", 5) == 0) {
+        out->end = begin + 8;
+      } else {
+        out->end = begin + 3;
+      }
+      return true;
+    }
+    case 'n':
+    case 'N': {
+      // A NaN consists of the characters "nan", case insensitive, optionally
+      // followed by a parenthesized sequence of zero or more alphanumeric
+      // characters and/or underscores.
+      if (strings_internal::memcasecmp(begin + 1, "an", 2) != 0) {
+        return false;
+      }
+      out->type = strings_internal::FloatType::kNan;
+      out->end = begin + 3;
+      // NaN is allowed to be followed by a parenthesized std::string, consisting of
+      // only the characters [a-zA-Z0-9_].  Match that if it's present.
+      begin += 3;
+      if (begin < end && *begin == '(') {
+        const char* nan_begin = begin + 1;
+        while (nan_begin < end && IsNanChar(*nan_begin)) {
+          ++nan_begin;
+        }
+        if (nan_begin < end && *nan_begin == ')') {
+          // We found an extra NaN specifier range
+          out->subrange_begin = begin + 1;
+          out->subrange_end = nan_begin;
+          out->end = nan_begin + 1;
+        }
+      }
+      return true;
+    }
+    default:
+      return false;
+  }
+}
+}  // namespace
+
+namespace strings_internal {
+
+template <int base>
+strings_internal::ParsedFloat ParseFloat(const char* begin, const char* end,
+                                         chars_format format_flags) {
+  strings_internal::ParsedFloat result;
+
+  // Exit early if we're given an empty range.
+  if (begin == end) return result;
+
+  // Handle the infinity and NaN cases.
+  if (ParseInfinityOrNan(begin, end, &result)) {
+    return result;
+  }
+
+  const char* const mantissa_begin = begin;
+  while (begin < end && *begin == '0') {
+    ++begin;  // skip leading zeros
+  }
+  uint64_t mantissa = 0;
+
+  int exponent_adjustment = 0;
+  bool mantissa_is_inexact = false;
+  std::size_t pre_decimal_digits = ConsumeDigits<base>(
+      begin, end, MantissaDigitsMax<base>(), &mantissa, &mantissa_is_inexact);
+  begin += pre_decimal_digits;
+  int digits_left;
+  if (pre_decimal_digits >= DigitLimit<base>()) {
+    // refuse to parse pathological inputs
+    return result;
+  } else if (pre_decimal_digits > MantissaDigitsMax<base>()) {
+    // We dropped some non-fraction digits on the floor.  Adjust our exponent
+    // to compensate.
+    exponent_adjustment =
+        static_cast<int>(pre_decimal_digits - MantissaDigitsMax<base>());
+    digits_left = 0;
+  } else {
+    digits_left =
+        static_cast<int>(MantissaDigitsMax<base>() - pre_decimal_digits);
+  }
+  if (begin < end && *begin == '.') {
+    ++begin;
+    if (mantissa == 0) {
+      // If we haven't seen any nonzero digits yet, keep skipping zeros.  We
+      // have to adjust the exponent to reflect the changed place value.
+      const char* begin_zeros = begin;
+      while (begin < end && *begin == '0') {
+        ++begin;
+      }
+      std::size_t zeros_skipped = begin - begin_zeros;
+      if (zeros_skipped >= DigitLimit<base>()) {
+        // refuse to parse pathological inputs
+        return result;
+      }
+      exponent_adjustment -= static_cast<int>(zeros_skipped);
+    }
+    std::size_t post_decimal_digits = ConsumeDigits<base>(
+        begin, end, digits_left, &mantissa, &mantissa_is_inexact);
+    begin += post_decimal_digits;
+
+    // Since `mantissa` is an integer, each significant digit we read after
+    // the decimal point requires an adjustment to the exponent. "1.23e0" will
+    // be stored as `mantissa` == 123 and `exponent` == -2 (that is,
+    // "123e-2").
+    if (post_decimal_digits >= DigitLimit<base>()) {
+      // refuse to parse pathological inputs
+      return result;
+    } else if (post_decimal_digits > digits_left) {
+      exponent_adjustment -= digits_left;
+    } else {
+      exponent_adjustment -= post_decimal_digits;
+    }
+  }
+  // If we've found no mantissa whatsoever, this isn't a number.
+  if (mantissa_begin == begin) {
+    return result;
+  }
+  // A bare "." doesn't count as a mantissa either.
+  if (begin - mantissa_begin == 1 && *mantissa_begin == '.') {
+    return result;
+  }
+
+  if (mantissa_is_inexact) {
+    // We dropped significant digits on the floor.  Handle this appropriately.
+    if (base == 10) {
+      // If we truncated significant decimal digits, store the full range of the
+      // mantissa for future big integer math for exact rounding.
+      result.subrange_begin = mantissa_begin;
+      result.subrange_end = begin;
+    } else if (base == 16) {
+      // If we truncated hex digits, reflect this fact by setting the low
+      // ("sticky") bit.  This allows for correct rounding in all cases.
+      mantissa |= 1;
+    }
+  }
+  result.mantissa = mantissa;
+
+  const char* const exponent_begin = begin;
+  result.literal_exponent = 0;
+  bool found_exponent = false;
+  if (AllowExponent(format_flags) && begin < end &&
+      IsExponentCharacter<base>(*begin)) {
+    bool negative_exponent = false;
+    ++begin;
+    if (begin < end && *begin == '-') {
+      negative_exponent = true;
+      ++begin;
+    } else if (begin < end && *begin == '+') {
+      ++begin;
+    }
+    const char* const exponent_digits_begin = begin;
+    // Exponent is always expressed in decimal, even for hexadecimal floats.
+    begin += ConsumeDigits<10>(begin, end, kDecimalExponentDigitsMax,
+                               &result.literal_exponent, nullptr);
+    if (begin == exponent_digits_begin) {
+      // there were no digits where we expected an exponent.  We failed to read
+      // an exponent and should not consume the 'e' after all.  Rewind 'begin'.
+      found_exponent = false;
+      begin = exponent_begin;
+    } else {
+      found_exponent = true;
+      if (negative_exponent) {
+        result.literal_exponent = -result.literal_exponent;
+      }
+    }
+  }
+
+  if (!found_exponent && RequireExponent(format_flags)) {
+    // Provided flags required an exponent, but none was found.  This results
+    // in a failure to scan.
+    return result;
+  }
+
+  // Success!
+  result.type = strings_internal::FloatType::kNumber;
+  if (result.mantissa > 0) {
+    result.exponent = result.literal_exponent +
+                      (DigitMagnitude<base>() * exponent_adjustment);
+  } else {
+    result.exponent = 0;
+  }
+  result.end = begin;
+  return result;
+}
+
+template ParsedFloat ParseFloat<10>(const char* begin, const char* end,
+                                    chars_format format_flags);
+template ParsedFloat ParseFloat<16>(const char* begin, const char* end,
+                                    chars_format format_flags);
+
+}  // namespace strings_internal
+}  // namespace absl
diff --git a/absl/strings/internal/charconv_parse.h b/absl/strings/internal/charconv_parse.h
new file mode 100644
index 000000000000..7a5c0874b804
--- /dev/null
+++ b/absl/strings/internal/charconv_parse.h
@@ -0,0 +1,96 @@
+// 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
+//
+//      http://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_CHARCONV_PARSE_H_
+#define ABSL_STRINGS_INTERNAL_CHARCONV_PARSE_H_
+
+#include <cstdint>
+
+#include "absl/strings/charconv.h"
+
+namespace absl {
+namespace strings_internal {
+
+// Enum indicating whether a parsed float is a number or special value.
+enum class FloatType { kNumber, kInfinity, kNan };
+
+// The decomposed parts of a parsed `float` or `double`.
+struct ParsedFloat {
+  // Representation of the parsed mantissa, with the decimal point adjusted to
+  // make it an integer.
+  //
+  // During decimal scanning, this contains 19 significant digits worth of
+  // mantissa value.  If digits beyond this point are found, they
+  // are truncated, and if any of these dropped digits are nonzero, then
+  // `mantissa` is inexact, and the full mantissa is stored in [subrange_begin,
+  // subrange_end).
+  //
+  // During hexadecimal scanning, this contains 15 significant hex digits worth
+  // of mantissa value.  Digits beyond this point are sticky -- they are
+  // truncated, but if any dropped digits are nonzero, the low bit of mantissa
+  // will be set.  (This allows for precise rounding, and avoids the need
+  // to store the full mantissa in [subrange_begin, subrange_end).)
+  uint64_t mantissa = 0;
+
+  // Floating point expontent.  This reflects any decimal point adjustments and
+  // any truncated digits from the mantissa.  The absolute value of the parsed
+  // number is represented by mantissa * (base ** exponent), where base==10 for
+  // decimal floats, and base==2 for hexadecimal floats.
+  int exponent = 0;
+
+  // The literal exponent value scanned from the input, or 0 if none was
+  // present.  This does not reflect any adjustments applied to mantissa.
+  int literal_exponent = 0;
+
+  // The type of number scanned.
+  FloatType type = FloatType::kNumber;
+
+  // When non-null, [subrange_begin, subrange_end) marks a range of characters
+  // that require further processing.  The meaning is dependent on float type.
+  // If type == kNumber and this is set, this is a "wide input": the input
+  // mantissa contained more than 19 digits.  The range contains the full
+  // mantissa.  It plus `literal_exponent` need to be examined to find the best
+  // floating point match.
+  // If type == kNan and this is set, the range marks the contents of a
+  // matched parenthesized character region after the NaN.
+  const char* subrange_begin = nullptr;
+  const char* subrange_end = nullptr;
+
+  // One-past-the-end of the successfully parsed region, or nullptr if no
+  // matching pattern was found.
+  const char* end = nullptr;
+};
+
+// Read the floating point number in the provided range, and populate
+// ParsedFloat accordingly.
+//
+// format_flags is a bitmask value specifying what patterns this API will match.
+// `scientific` and `fixed`  are honored per std::from_chars rules
+// ([utility.from.chars], C++17): if exactly one of these bits is set, then an
+// exponent is required, or dislallowed, respectively.
+//
+// Template parameter `base` must be either 10 or 16.  For base 16, a "0x" is
+// *not* consumed.  The `hex` bit from format_flags is ignored by ParseFloat.
+template <int base>
+ParsedFloat ParseFloat(const char* begin, const char* end,
+                       absl::chars_format format_flags);
+
+extern template ParsedFloat ParseFloat<10>(const char* begin, const char* end,
+                                           absl::chars_format format_flags);
+extern template ParsedFloat ParseFloat<16>(const char* begin, const char* end,
+                                           absl::chars_format format_flags);
+
+}  // namespace strings_internal
+}  // namespace absl
+#endif  // ABSL_STRINGS_INTERNAL_CHARCONV_PARSE_H_
diff --git a/absl/strings/internal/charconv_parse_test.cc b/absl/strings/internal/charconv_parse_test.cc
new file mode 100644
index 000000000000..1ff86004973a
--- /dev/null
+++ b/absl/strings/internal/charconv_parse_test.cc
@@ -0,0 +1,357 @@
+// 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
+//
+//      http://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/charconv_parse.h"
+
+#include <string>
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/strings/str_cat.h"
+
+using absl::chars_format;
+using absl::strings_internal::FloatType;
+using absl::strings_internal::ParsedFloat;
+using absl::strings_internal::ParseFloat;
+
+namespace {
+
+// Check that a given std::string input is parsed to the expected mantissa and
+// exponent.
+//
+// Input std::string `s` must contain a '$' character.  It marks the end of the
+// characters that should be consumed by the match.  It is stripped from the
+// input to ParseFloat.
+//
+// If input std::string `s` contains '[' and ']' characters, these mark the region
+// of characters that should be marked as the "subrange".  For NaNs, this is
+// the location of the extended NaN std::string.  For numbers, this is the location
+// of the full, over-large mantissa.
+template <int base>
+void ExpectParsedFloat(std::string s, absl::chars_format format_flags,
+                       FloatType expected_type, uint64_t expected_mantissa,
+                       int expected_exponent,
+                       int expected_literal_exponent = -999) {
+  SCOPED_TRACE(s);
+
+  int begin_subrange = -1;
+  int end_subrange = -1;
+  // If s contains '[' and ']', then strip these characters and set the subrange
+  // indices appropriately.
+  std::string::size_type open_bracket_pos = s.find('[');
+  if (open_bracket_pos != std::string::npos) {
+    begin_subrange = static_cast<int>(open_bracket_pos);
+    s.replace(open_bracket_pos, 1, "");
+    std::string::size_type close_bracket_pos = s.find(']');
+    ABSL_RAW_CHECK(close_bracket_pos != absl::string_view::npos,
+                   "Test input contains [ without matching ]");
+    end_subrange = static_cast<int>(close_bracket_pos);
+    s.replace(close_bracket_pos, 1, "");
+  }
+  const std::string::size_type expected_characters_matched = s.find('$');
+  ABSL_RAW_CHECK(expected_characters_matched != std::string::npos,
+                 "Input std::string must contain $");
+  s.replace(expected_characters_matched, 1, "");
+
+  ParsedFloat parsed =
+      ParseFloat<base>(s.data(), s.data() + s.size(), format_flags);
+
+  EXPECT_NE(parsed.end, nullptr);
+  if (parsed.end == nullptr) {
+    return;  // The following tests are not useful if we fully failed to parse
+  }
+  EXPECT_EQ(parsed.type, expected_type);
+  if (begin_subrange == -1) {
+    EXPECT_EQ(parsed.subrange_begin, nullptr);
+    EXPECT_EQ(parsed.subrange_end, nullptr);
+  } else {
+    EXPECT_EQ(parsed.subrange_begin, s.data() + begin_subrange);
+    EXPECT_EQ(parsed.subrange_end, s.data() + end_subrange);
+  }
+  if (parsed.type == FloatType::kNumber) {
+    EXPECT_EQ(parsed.mantissa, expected_mantissa);
+    EXPECT_EQ(parsed.exponent, expected_exponent);
+    if (expected_literal_exponent != -999) {
+      EXPECT_EQ(parsed.literal_exponent, expected_literal_exponent);
+    }
+  }
+  auto characters_matched = static_cast<int>(parsed.end - s.data());
+  EXPECT_EQ(characters_matched, expected_characters_matched);
+}
+
+// Check that a given std::string input is parsed to the expected mantissa and
+// exponent.
+//
+// Input std::string `s` must contain a '$' character.  It marks the end of the
+// characters that were consumed by the match.
+template <int base>
+void ExpectNumber(std::string s, absl::chars_format format_flags,
+                  uint64_t expected_mantissa, int expected_exponent,
+                  int expected_literal_exponent = -999) {
+  ExpectParsedFloat<base>(std::move(s), format_flags, FloatType::kNumber,
+                          expected_mantissa, expected_exponent,
+                          expected_literal_exponent);
+}
+
+// Check that a given std::string input is parsed to the given special value.
+//
+// This tests against both number bases, since infinities and NaNs have
+// identical representations in both modes.
+void ExpectSpecial(const std::string& s, absl::chars_format format_flags,
+                   FloatType type) {
+  ExpectParsedFloat<10>(s, format_flags, type, 0, 0);
+  ExpectParsedFloat<16>(s, format_flags, type, 0, 0);
+}
+
+// Check that a given input std::string is not matched by Float.
+template <int base>
+void ExpectFailedParse(absl::string_view s, absl::chars_format format_flags) {
+  ParsedFloat parsed =
+      ParseFloat<base>(s.data(), s.data() + s.size(), format_flags);
+  EXPECT_EQ(parsed.end, nullptr);
+}
+
+TEST(ParseFloat, SimpleValue) {
+  // Test that various forms of floating point numbers all parse correctly.
+  ExpectNumber<10>("1.23456789e5$", chars_format::general, 123456789, -3);
+  ExpectNumber<10>("1.23456789e+5$", chars_format::general, 123456789, -3);
+  ExpectNumber<10>("1.23456789E5$", chars_format::general, 123456789, -3);
+  ExpectNumber<10>("1.23456789e05$", chars_format::general, 123456789, -3);
+  ExpectNumber<10>("123.456789e3$", chars_format::general, 123456789, -3);
+  ExpectNumber<10>("0.000123456789e9$", chars_format::general, 123456789, -3);
+  ExpectNumber<10>("123456.789$", chars_format::general, 123456789, -3);
+  ExpectNumber<10>("123456789e-3$", chars_format::general, 123456789, -3);
+
+  ExpectNumber<16>("1.234abcdefp28$", chars_format::general, 0x1234abcdef, -8);
+  ExpectNumber<16>("1.234abcdefp+28$", chars_format::general, 0x1234abcdef, -8);
+  ExpectNumber<16>("1.234ABCDEFp28$", chars_format::general, 0x1234abcdef, -8);
+  ExpectNumber<16>("1.234AbCdEfP0028$", chars_format::general, 0x1234abcdef,
+                   -8);
+  ExpectNumber<16>("123.4abcdefp20$", chars_format::general, 0x1234abcdef, -8);
+  ExpectNumber<16>("0.0001234abcdefp44$", chars_format::general, 0x1234abcdef,
+                   -8);
+  ExpectNumber<16>("1234abcd.ef$", chars_format::general, 0x1234abcdef, -8);
+  ExpectNumber<16>("1234abcdefp-8$", chars_format::general, 0x1234abcdef, -8);
+
+  // ExpectNumber does not attempt to drop trailing zeroes.
+  ExpectNumber<10>("0001.2345678900e005$", chars_format::general, 12345678900,
+                   -5);
+  ExpectNumber<16>("0001.234abcdef000p28$", chars_format::general,
+                   0x1234abcdef000, -20);
+
+  // Ensure non-matching characters after a number are ignored, even when they
+  // look like potentially matching characters.
+  ExpectNumber<10>("1.23456789e5$   ", chars_format::general, 123456789, -3);
+  ExpectNumber<10>("1.23456789e5$e5e5", chars_format::general, 123456789, -3);
+  ExpectNumber<10>("1.23456789e5$.25", chars_format::general, 123456789, -3);
+  ExpectNumber<10>("1.23456789e5$-", chars_format::general, 123456789, -3);
+  ExpectNumber<10>("1.23456789e5$PUPPERS!!!", chars_format::general, 123456789,
+                   -3);
+  ExpectNumber<10>("123456.789$efghij", chars_format::general, 123456789, -3);
+  ExpectNumber<10>("123456.789$e", chars_format::general, 123456789, -3);
+  ExpectNumber<10>("123456.789$p5", chars_format::general, 123456789, -3);
+  ExpectNumber<10>("123456.789$.10", chars_format::general, 123456789, -3);
+
+  ExpectNumber<16>("1.234abcdefp28$   ", chars_format::general, 0x1234abcdef,
+                   -8);
+  ExpectNumber<16>("1.234abcdefp28$p28", chars_format::general, 0x1234abcdef,
+                   -8);
+  ExpectNumber<16>("1.234abcdefp28$.125", chars_format::general, 0x1234abcdef,
+                   -8);
+  ExpectNumber<16>("1.234abcdefp28$-", chars_format::general, 0x1234abcdef, -8);
+  ExpectNumber<16>("1.234abcdefp28$KITTEHS!!!", chars_format::general,
+                   0x1234abcdef, -8);
+  ExpectNumber<16>("1234abcd.ef$ghijk", chars_format::general, 0x1234abcdef,
+                   -8);
+  ExpectNumber<16>("1234abcd.ef$p", chars_format::general, 0x1234abcdef, -8);
+  ExpectNumber<16>("1234abcd.ef$.10", chars_format::general, 0x1234abcdef, -8);
+
+  // Ensure we can read a full resolution mantissa without overflow.
+  ExpectNumber<10>("9999999999999999999$", chars_format::general,
+                   9999999999999999999u, 0);
+  ExpectNumber<16>("fffffffffffffff$", chars_format::general,
+                   0xfffffffffffffffu, 0);
+
+  // Check that zero is consistently read.
+  ExpectNumber<10>("0$", chars_format::general, 0, 0);
+  ExpectNumber<16>("0$", chars_format::general, 0, 0);
+  ExpectNumber<10>("000000000000000000000000000000000000000$",
+                   chars_format::general, 0, 0);
+  ExpectNumber<16>("000000000000000000000000000000000000000$",
+                   chars_format::general, 0, 0);
+  ExpectNumber<10>("0000000000000000000000.000000000000000000$",
+                   chars_format::general, 0, 0);
+  ExpectNumber<16>("0000000000000000000000.000000000000000000$",
+                   chars_format::general, 0, 0);
+  ExpectNumber<10>("0.00000000000000000000000000000000e123456$",
+                   chars_format::general, 0, 0);
+  ExpectNumber<16>("0.00000000000000000000000000000000p123456$",
+                   chars_format::general, 0, 0);
+}
+
+TEST(ParseFloat, LargeDecimalMantissa) {
+  // After 19 significant decimal digits in the mantissa, ParsedFloat will
+  // truncate additional digits.  We need to test that:
+  //   1) the truncation to 19 digits happens
+  //   2) the returned exponent reflects the dropped significant digits
+  //   3) a correct literal_exponent is set
+  //
+  // If and only if a significant digit is found after 19 digits, then the
+  // entirety of the mantissa in case the exact value is needed to make a
+  // rounding decision.  The [ and ] characters below denote where such a
+  // subregion was marked by by ParseFloat.  They are not part of the input.
+
+  // Mark a capture group only if a dropped digit is significant (nonzero).
+  ExpectNumber<10>("100000000000000000000000000$", chars_format::general,
+                   1000000000000000000,
+                   /* adjusted exponent */ 8);
+
+  ExpectNumber<10>("123456789123456789100000000$", chars_format::general,
+                   1234567891234567891,
+                   /* adjusted exponent */ 8);
+
+  ExpectNumber<10>("[123456789123456789123456789]$", chars_format::general,
+                   1234567891234567891,
+                   /* adjusted exponent */ 8,
+                   /* literal exponent */ 0);
+
+  ExpectNumber<10>("[123456789123456789100000009]$", chars_format::general,
+                   1234567891234567891,
+                   /* adjusted exponent */ 8,
+                   /* literal exponent */ 0);
+
+  ExpectNumber<10>("[123456789123456789120000000]$", chars_format::general,
+                   1234567891234567891,
+                   /* adjusted exponent */ 8,
+                   /* literal exponent */ 0);
+
+  // Leading zeroes should not count towards the 19 significant digit limit
+  ExpectNumber<10>("[00000000123456789123456789123456789]$",
+                   chars_format::general, 1234567891234567891,
+                   /* adjusted exponent */ 8,
+                   /* literal exponent */ 0);
+
+  ExpectNumber<10>("00000000123456789123456789100000000$",
+                   chars_format::general, 1234567891234567891,
+                   /* adjusted exponent */ 8);
+
+  // Truncated digits after the decimal point should not cause a further
+  // exponent adjustment.
+  ExpectNumber<10>("1.234567891234567891e123$", chars_format::general,
+                   1234567891234567891, 105);
+  ExpectNumber<10>("[1.23456789123456789123456789]e123$", chars_format::general,
+                   1234567891234567891,
+                   /* adjusted exponent */ 105,
+                   /* literal exponent */ 123);
+
+  // Ensure we truncate, and not round.  (The from_chars algorithm we use
+  // depends on our guess missing low, if it misses, so we need the rounding
+  // error to be downward.)
+  ExpectNumber<10>("[1999999999999999999999]$", chars_format::general,
+                   1999999999999999999,
+                   /* adjusted exponent */ 3,
+                   /* literal exponent */ 0);
+}
+
+TEST(ParseFloat, LargeHexadecimalMantissa) {
+  // After 15 significant hex digits in the mantissa, ParsedFloat will treat
+  // additional digits as sticky,  We need to test that:
+  //   1) The truncation to 15 digits happens
+  //   2) The returned exponent reflects the dropped significant digits
+  //   3) If a nonzero digit is dropped, the low bit of mantissa is set.
+
+  ExpectNumber<16>("123456789abcdef123456789abcdef$", chars_format::general,
+                   0x123456789abcdef, 60);
+
+  // Leading zeroes should not count towards the 15 significant digit limit
+  ExpectNumber<16>("000000123456789abcdef123456789abcdef$",
+                   chars_format::general, 0x123456789abcdef, 60);
+
+  // Truncated digits after the radix point should not cause a further
+  // exponent adjustment.
+  ExpectNumber<16>("1.23456789abcdefp100$", chars_format::general,
+                   0x123456789abcdef, 44);
+  ExpectNumber<16>("1.23456789abcdef123456789abcdefp100$",
+                   chars_format::general, 0x123456789abcdef, 44);
+
+  // test sticky digit behavior.  The low bit should be set iff any dropped
+  // digit is nonzero.
+  ExpectNumber<16>("123456789abcdee123456789abcdee$", chars_format::general,
+                   0x123456789abcdef, 60);
+  ExpectNumber<16>("123456789abcdee000000000000001$", chars_format::general,
+                   0x123456789abcdef, 60);
+  ExpectNumber<16>("123456789abcdee000000000000000$", chars_format::general,
+                   0x123456789abcdee, 60);
+}
+
+TEST(ParseFloat, ScientificVsFixed) {
+  // In fixed mode, an exponent is never matched (but the remainder of the
+  // number will be matched.)
+  ExpectNumber<10>("1.23456789$e5", chars_format::fixed, 123456789, -8);
+  ExpectNumber<10>("123456.789$", chars_format::fixed, 123456789, -3);
+  ExpectNumber<16>("1.234abcdef$p28", chars_format::fixed, 0x1234abcdef, -36);
+  ExpectNumber<16>("1234abcd.ef$", chars_format::fixed, 0x1234abcdef, -8);
+
+  // In scientific mode, numbers don't match *unless* they have an exponent.
+  ExpectNumber<10>("1.23456789e5$", chars_format::scientific, 123456789, -3);
+  ExpectFailedParse<10>("-123456.789$", chars_format::scientific);
+  ExpectNumber<16>("1.234abcdefp28$", chars_format::scientific, 0x1234abcdef,
+                   -8);
+  ExpectFailedParse<16>("1234abcd.ef$", chars_format::scientific);
+}
+
+TEST(ParseFloat, Infinity) {
+  ExpectFailedParse<10>("in", chars_format::general);
+  ExpectFailedParse<16>("in", chars_format::general);
+  ExpectFailedParse<10>("inx", chars_format::general);
+  ExpectFailedParse<16>("inx", chars_format::general);
+  ExpectSpecial("inf$", chars_format::general, FloatType::kInfinity);
+  ExpectSpecial("Inf$", chars_format::general, FloatType::kInfinity);
+  ExpectSpecial("INF$", chars_format::general, FloatType::kInfinity);
+  ExpectSpecial("inf$inite", chars_format::general, FloatType::kInfinity);
+  ExpectSpecial("iNfInItY$", chars_format::general, FloatType::kInfinity);
+  ExpectSpecial("infinity$!!!", chars_format::general, FloatType::kInfinity);
+}
+
+TEST(ParseFloat, NaN) {
+  ExpectFailedParse<10>("na", chars_format::general);
+  ExpectFailedParse<16>("na", chars_format::general);
+  ExpectFailedParse<10>("nah", chars_format::general);
+  ExpectFailedParse<16>("nah", chars_format::general);
+  ExpectSpecial("nan$", chars_format::general, FloatType::kNan);
+  ExpectSpecial("NaN$", chars_format::general, FloatType::kNan);
+  ExpectSpecial("nAn$", chars_format::general, FloatType::kNan);
+  ExpectSpecial("NAN$", chars_format::general, FloatType::kNan);
+  ExpectSpecial("NaN$aNaNaNaNaBatman!", chars_format::general, FloatType::kNan);
+
+  // A parenthesized sequence of the characters [a-zA-Z0-9_] is allowed to
+  // appear after an NaN.  Check that this is allowed, and that the correct
+  // characters are grouped.
+  //
+  // (The characters [ and ] in the pattern below delimit the expected matched
+  // subgroup; they are not part of the input passed to ParseFloat.)
+  ExpectSpecial("nan([0xabcdef])$", chars_format::general, FloatType::kNan);
+  ExpectSpecial("nan([0xabcdef])$...", chars_format::general, FloatType::kNan);
+  ExpectSpecial("nan([0xabcdef])$)...", chars_format::general, FloatType::kNan);
+  ExpectSpecial("nan([])$", chars_format::general, FloatType::kNan);
+  ExpectSpecial("nan([aAzZ09_])$", chars_format::general, FloatType::kNan);
+  // If the subgroup contains illegal characters, don't match it at all.
+  ExpectSpecial("nan$(bad-char)", chars_format::general, FloatType::kNan);
+  // Also cope with a missing close paren.
+  ExpectSpecial("nan$(0xabcdef", chars_format::general, FloatType::kNan);
+}
+
+}  // namespace
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
new file mode 100644
index 000000000000..eafb068fe286
--- /dev/null
+++ b/absl/strings/internal/str_format/arg.cc
@@ -0,0 +1,399 @@
+//
+// POSIX spec:
+//   http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html
+//
+#include "absl/strings/internal/str_format/arg.h"
+
+#include <cassert>
+#include <cerrno>
+#include <cstdlib>
+#include <string>
+#include <type_traits>
+
+#include "absl/base/port.h"
+#include "absl/strings/internal/str_format/float_conversion.h"
+
+namespace absl {
+namespace str_format_internal {
+namespace {
+
+const char kDigit[2][32] = { "0123456789abcdef", "0123456789ABCDEF" };
+
+// Reduce *capacity by s.size(), clipped to a 0 minimum.
+void ReducePadding(string_view s, size_t *capacity) {
+  *capacity = Excess(s.size(), *capacity);
+}
+
+// Reduce *capacity by n, clipped to a 0 minimum.
+void ReducePadding(size_t n, size_t *capacity) {
+  *capacity = Excess(n, *capacity);
+}
+
+template <typename T>
+struct MakeUnsigned : std::make_unsigned<T> {};
+template <>
+struct MakeUnsigned<absl::uint128> {
+  using type = absl::uint128;
+};
+
+template <typename T>
+struct IsSigned : std::is_signed<T> {};
+template <>
+struct IsSigned<absl::uint128> : std::false_type {};
+
+class ConvertedIntInfo {
+ public:
+  template <typename T>
+  ConvertedIntInfo(T v, ConversionChar conv) {
+    using Unsigned = typename MakeUnsigned<T>::type;
+    auto u = static_cast<Unsigned>(v);
+    if (IsNeg(v)) {
+      is_neg_ = true;
+      u = Unsigned{} - u;
+    } else {
+      is_neg_ = false;
+    }
+    UnsignedToStringRight(u, conv);
+  }
+
+  string_view digits() const {
+    return {end() - size_, static_cast<size_t>(size_)};
+  }
+  bool is_neg() const { return is_neg_; }
+
+ private:
+  template <typename T, bool IsSigned>
+  struct IsNegImpl {
+    static bool Eval(T v) { return v < 0; }
+  };
+  template <typename T>
+  struct IsNegImpl<T, false> {
+    static bool Eval(T) {
+      return false;
+    }
+  };
+
+  template <typename T>
+  bool IsNeg(T v) {
+    return IsNegImpl<T, IsSigned<T>::value>::Eval(v);
+  }
+
+  template <typename T>
+  void UnsignedToStringRight(T u, ConversionChar conv) {
+    char *p = end();
+    switch (conv.radix()) {
+      default:
+      case 10:
+        for (; u; u /= 10)
+          *--p = static_cast<char>('0' + static_cast<size_t>(u % 10));
+        break;
+      case 8:
+        for (; u; u /= 8)
+          *--p = static_cast<char>('0' + static_cast<size_t>(u % 8));
+        break;
+      case 16: {
+        const char *digits = kDigit[conv.upper() ? 1 : 0];
+        for (; u; u /= 16) *--p = digits[static_cast<size_t>(u % 16)];
+        break;
+      }
+    }
+    size_ = static_cast<int>(end() - p);
+  }
+
+  const char *end() const { return storage_ + sizeof(storage_); }
+  char *end() { return storage_ + sizeof(storage_); }
+
+  bool is_neg_;
+  int size_;
+  // Max size: 128 bit value as octal -> 43 digits
+  char storage_[128 / 3 + 1];
+};
+
+// Note: 'o' conversions do not have a base indicator, it's just that
+// the '#' flag is specified to modify the precision for 'o' conversions.
+string_view BaseIndicator(const ConvertedIntInfo &info,
+                          const ConversionSpec &conv) {
+  bool alt = conv.flags().alt;
+  int radix = conv.conv().radix();
+  if (conv.conv().id() == ConversionChar::p)
+    alt = true;  // always show 0x for %p.
+  // From the POSIX description of '#' flag:
+  //   "For x or X conversion specifiers, a non-zero result shall have
+  //   0x (or 0X) prefixed to it."
+  if (alt && radix == 16 && !info.digits().empty()) {
+    if (conv.conv().upper()) return "0X";
+    return "0x";
+  }
+  return {};
+}
+
+string_view SignColumn(bool neg, const ConversionSpec &conv) {
+  if (conv.conv().is_signed()) {
+    if (neg) return "-";
+    if (conv.flags().show_pos) return "+";
+    if (conv.flags().sign_col) return " ";
+  }
+  return {};
+}
+
+bool ConvertCharImpl(unsigned char v, const ConversionSpec &conv,
+                     FormatSinkImpl *sink) {
+  size_t fill = 0;
+  if (conv.width() >= 0) fill = conv.width();
+  ReducePadding(1, &fill);
+  if (!conv.flags().left) sink->Append(fill, ' ');
+  sink->Append(1, v);
+  if (conv.flags().left) sink->Append(fill, ' ');
+  return true;
+}
+
+bool ConvertIntImplInner(const ConvertedIntInfo &info,
+                         const ConversionSpec &conv, FormatSinkImpl *sink) {
+  // Print as a sequence of Substrings:
+  //   [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces]
+  size_t fill = 0;
+  if (conv.width() >= 0) fill = conv.width();
+
+  string_view formatted = info.digits();
+  ReducePadding(formatted, &fill);
+
+  string_view sign = SignColumn(info.is_neg(), conv);
+  ReducePadding(sign, &fill);
+
+  string_view base_indicator = BaseIndicator(info, conv);
+  ReducePadding(base_indicator, &fill);
+
+  int precision = conv.precision();
+  bool precision_specified = precision >= 0;
+  if (!precision_specified)
+    precision = 1;
+
+  if (conv.flags().alt && conv.conv().id() == ConversionChar::o) {
+    // From POSIX description of the '#' (alt) flag:
+    //   "For o conversion, it increases the precision (if necessary) to
+    //   force the first digit of the result to be zero."
+    if (formatted.empty() || *formatted.begin() != '0') {
+      int needed = static_cast<int>(formatted.size()) + 1;
+      precision = std::max(precision, needed);
+    }
+  }
+
+  size_t num_zeroes = Excess(formatted.size(), precision);
+  ReducePadding(num_zeroes, &fill);
+
+  size_t num_left_spaces = !conv.flags().left ? fill : 0;
+  size_t num_right_spaces = conv.flags().left ? fill : 0;
+
+  // From POSIX description of the '0' (zero) flag:
+  //   "For d, i, o, u, x, and X conversion specifiers, if a precision
+  //   is specified, the '0' flag is ignored."
+  if (!precision_specified && conv.flags().zero) {
+    num_zeroes += num_left_spaces;
+    num_left_spaces = 0;
+  }
+
+  sink->Append(num_left_spaces, ' ');
+  sink->Append(sign);
+  sink->Append(base_indicator);
+  sink->Append(num_zeroes, '0');
+  sink->Append(formatted);
+  sink->Append(num_right_spaces, ' ');
+  return true;
+}
+
+template <typename T>
+bool ConvertIntImplInner(T v, const ConversionSpec &conv,
+                         FormatSinkImpl *sink) {
+  ConvertedIntInfo info(v, conv.conv());
+  if (conv.flags().basic && conv.conv().id() != ConversionChar::p) {
+    if (info.is_neg()) sink->Append(1, '-');
+    if (info.digits().empty()) {
+      sink->Append(1, '0');
+    } else {
+      sink->Append(info.digits());
+    }
+    return true;
+  }
+  return ConvertIntImplInner(info, conv, sink);
+}
+
+template <typename T>
+bool ConvertIntArg(T v, const ConversionSpec &conv, FormatSinkImpl *sink) {
+  if (conv.conv().is_float()) {
+    return FormatConvertImpl(static_cast<double>(v), conv, sink).value;
+  }
+  if (conv.conv().id() == ConversionChar::c)
+    return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink);
+  if (!conv.conv().is_integral())
+    return false;
+  if (!conv.conv().is_signed() && IsSigned<T>::value) {
+    using U = typename MakeUnsigned<T>::type;
+    return FormatConvertImpl(static_cast<U>(v), conv, sink).value;
+  }
+  return ConvertIntImplInner(v, conv, sink);
+}
+
+template <typename T>
+bool ConvertFloatArg(T v, const ConversionSpec &conv, FormatSinkImpl *sink) {
+  return conv.conv().is_float() && ConvertFloatImpl(v, conv, sink);
+}
+
+inline bool ConvertStringArg(string_view v, const ConversionSpec &conv,
+                             FormatSinkImpl *sink) {
+  if (conv.conv().id() != ConversionChar::s)
+    return false;
+  if (conv.flags().basic) {
+    sink->Append(v);
+    return true;
+  }
+  return sink->PutPaddedString(v, conv.width(), conv.precision(),
+                               conv.flags().left);
+}
+
+}  // namespace
+
+// ==================== Strings ====================
+ConvertResult<Conv::s> FormatConvertImpl(const std::string &v,
+                                         const ConversionSpec &conv,
+                                         FormatSinkImpl *sink) {
+  return {ConvertStringArg(v, conv, sink)};
+}
+
+ConvertResult<Conv::s> FormatConvertImpl(string_view v,
+                                         const ConversionSpec &conv,
+                                         FormatSinkImpl *sink) {
+  return {ConvertStringArg(v, conv, sink)};
+}
+
+ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v,
+                                                   const ConversionSpec &conv,
+                                                   FormatSinkImpl *sink) {
+  if (conv.conv().id() == ConversionChar::p)
+    return {FormatConvertImpl(VoidPtr(v), conv, sink).value};
+  size_t len;
+  if (v == nullptr) {
+    len = 0;
+  } else if (conv.precision() < 0) {
+    len = std::strlen(v);
+  } else {
+    // If precision is set, we look for the null terminator on the valid range.
+    len = std::find(v, v + conv.precision(), '\0') - v;
+  }
+  return {ConvertStringArg(string_view(v, len), conv, sink)};
+}
+
+// ==================== Raw pointers ====================
+ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec &conv,
+                                         FormatSinkImpl *sink) {
+  if (conv.conv().id() != ConversionChar::p)
+    return {false};
+  if (!v.value) {
+    sink->Append("(nil)");
+    return {true};
+  }
+  return {ConvertIntImplInner(v.value, conv, sink)};
+}
+
+// ==================== Floats ====================
+FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec &conv,
+                                        FormatSinkImpl *sink) {
+  return {ConvertFloatArg(v, conv, sink)};
+}
+FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec &conv,
+                                        FormatSinkImpl *sink) {
+  return {ConvertFloatArg(v, conv, sink)};
+}
+FloatingConvertResult FormatConvertImpl(long double v,
+                                        const ConversionSpec &conv,
+                                        FormatSinkImpl *sink) {
+  return {ConvertFloatArg(v, conv, sink)};
+}
+
+// ==================== Chars ====================
+IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec &conv,
+                                        FormatSinkImpl *sink) {
+  return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(signed char v,
+                                        const ConversionSpec &conv,
+                                        FormatSinkImpl *sink) {
+  return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(unsigned char v,
+                                        const ConversionSpec &conv,
+                                        FormatSinkImpl *sink) {
+  return {ConvertIntArg(v, conv, sink)};
+}
+
+// ==================== Ints ====================
+IntegralConvertResult FormatConvertImpl(short v,  // NOLINT
+                                        const ConversionSpec &conv,
+                                        FormatSinkImpl *sink) {
+  return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(unsigned short v,  // NOLINT
+                                        const ConversionSpec &conv,
+                                        FormatSinkImpl *sink) {
+  return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec &conv,
+                                        FormatSinkImpl *sink) {
+  return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec &conv,
+                                        FormatSinkImpl *sink) {
+  return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(long v,  // NOLINT
+                                        const ConversionSpec &conv,
+                                        FormatSinkImpl *sink) {
+  return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(unsigned long v,  // NOLINT
+                                        const ConversionSpec &conv,
+                                        FormatSinkImpl *sink) {
+  return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(long long v,  // NOLINT
+                                        const ConversionSpec &conv,
+                                        FormatSinkImpl *sink) {
+  return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(unsigned long long v,  // NOLINT
+                                        const ConversionSpec &conv,
+                                        FormatSinkImpl *sink) {
+  return {ConvertIntArg(v, conv, sink)};
+}
+IntegralConvertResult FormatConvertImpl(absl::uint128 v,
+                                        const ConversionSpec &conv,
+                                        FormatSinkImpl *sink) {
+  return {ConvertIntArg(v, conv, sink)};
+}
+
+template struct FormatArgImpl::TypedVTable<str_format_internal::VoidPtr>;
+
+template struct FormatArgImpl::TypedVTable<bool>;
+template struct FormatArgImpl::TypedVTable<char>;
+template struct FormatArgImpl::TypedVTable<signed char>;
+template struct FormatArgImpl::TypedVTable<unsigned char>;
+template struct FormatArgImpl::TypedVTable<short>;           // NOLINT
+template struct FormatArgImpl::TypedVTable<unsigned short>;  // NOLINT
+template struct FormatArgImpl::TypedVTable<int>;
+template struct FormatArgImpl::TypedVTable<unsigned>;
+template struct FormatArgImpl::TypedVTable<long>;                // NOLINT
+template struct FormatArgImpl::TypedVTable<unsigned long>;       // NOLINT
+template struct FormatArgImpl::TypedVTable<long long>;           // NOLINT
+template struct FormatArgImpl::TypedVTable<unsigned long long>;  // NOLINT
+template struct FormatArgImpl::TypedVTable<absl::uint128>;
+
+template struct FormatArgImpl::TypedVTable<float>;
+template struct FormatArgImpl::TypedVTable<double>;
+template struct FormatArgImpl::TypedVTable<long double>;
+
+template struct FormatArgImpl::TypedVTable<const char *>;
+template struct FormatArgImpl::TypedVTable<std::string>;
+template struct FormatArgImpl::TypedVTable<string_view>;
+
+}  // namespace str_format_internal
+
+}  // namespace absl
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
new file mode 100644
index 000000000000..a9562188ea91
--- /dev/null
+++ b/absl/strings/internal/str_format/arg.h
@@ -0,0 +1,434 @@
+#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_
+#define ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_
+
+#include <string.h>
+#include <wchar.h>
+
+#include <cstdio>
+#include <iomanip>
+#include <limits>
+#include <sstream>
+#include <string>
+#include <type_traits>
+
+#include "absl/base/port.h"
+#include "absl/meta/type_traits.h"
+#include "absl/numeric/int128.h"
+#include "absl/strings/internal/str_format/extension.h"
+#include "absl/strings/string_view.h"
+
+class Cord;
+class CordReader;
+
+namespace absl {
+
+class FormatCountCapture;
+class FormatSink;
+
+namespace str_format_internal {
+
+template <typename T, typename = void>
+struct HasUserDefinedConvert : std::false_type {};
+
+template <typename T>
+struct HasUserDefinedConvert<
+    T, void_t<decltype(AbslFormatConvert(
+           std::declval<const T&>(), std::declval<const ConversionSpec&>(),
+           std::declval<FormatSink*>()))>> : std::true_type {};
+template <typename T>
+class StreamedWrapper;
+
+// If 'v' can be converted (in the printf sense) according to 'conv',
+// then convert it, appending to `sink` and return `true`.
+// Otherwise fail and return `false`.
+// Raw pointers.
+struct VoidPtr {
+  VoidPtr() = default;
+  template <typename T,
+            decltype(reinterpret_cast<uintptr_t>(std::declval<T*>())) = 0>
+  VoidPtr(T* ptr)  // NOLINT
+      : value(ptr ? reinterpret_cast<uintptr_t>(ptr) : 0) {}
+  uintptr_t value;
+};
+ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec& conv,
+                                         FormatSinkImpl* sink);
+
+// Strings.
+ConvertResult<Conv::s> FormatConvertImpl(const std::string& v,
+                                         const ConversionSpec& conv,
+                                         FormatSinkImpl* sink);
+ConvertResult<Conv::s> FormatConvertImpl(string_view v,
+                                         const ConversionSpec& conv,
+                                         FormatSinkImpl* sink);
+ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char* v,
+                                                   const ConversionSpec& conv,
+                                                   FormatSinkImpl* sink);
+template <class AbslCord,
+          typename std::enable_if<
+              std::is_same<AbslCord, ::Cord>::value>::type* = nullptr,
+          class AbslCordReader = ::CordReader>
+ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value,
+                                         const ConversionSpec& conv,
+                                         FormatSinkImpl* sink) {
+  if (conv.conv().id() != ConversionChar::s) return {false};
+
+  bool is_left = conv.flags().left;
+  size_t space_remaining = 0;
+
+  int width = conv.width();
+  if (width >= 0) space_remaining = width;
+
+  size_t to_write = value.size();
+
+  int precision = conv.precision();
+  if (precision >= 0)
+    to_write = std::min(to_write, static_cast<size_t>(precision));
+
+  space_remaining = Excess(to_write, space_remaining);
+
+  if (space_remaining > 0 && !is_left) sink->Append(space_remaining, ' ');
+
+  string_view piece;
+  for (AbslCordReader reader(value);
+       to_write > 0 && reader.ReadFragment(&piece); to_write -= piece.size()) {
+    if (piece.size() > to_write) piece.remove_suffix(piece.size() - to_write);
+    sink->Append(piece);
+  }
+
+  if (space_remaining > 0 && is_left) sink->Append(space_remaining, ' ');
+  return {true};
+}
+
+using IntegralConvertResult =
+    ConvertResult<Conv::c | Conv::numeric | Conv::star>;
+using FloatingConvertResult = ConvertResult<Conv::floating>;
+
+// Floats.
+FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec& conv,
+                                        FormatSinkImpl* sink);
+FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec& conv,
+                                        FormatSinkImpl* sink);
+FloatingConvertResult FormatConvertImpl(long double v,
+                                        const ConversionSpec& conv,
+                                        FormatSinkImpl* sink);
+
+// Chars.
+IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec& conv,
+                                        FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(signed char v,
+                                        const ConversionSpec& conv,
+                                        FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned char v,
+                                        const ConversionSpec& conv,
+                                        FormatSinkImpl* sink);
+
+// Ints.
+IntegralConvertResult FormatConvertImpl(short v,  // NOLINT
+                                        const ConversionSpec& conv,
+                                        FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned short v,  // NOLINT
+                                        const ConversionSpec& conv,
+                                        FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec& conv,
+                                        FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec& conv,
+                                        FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(long v,  // NOLINT
+                                        const ConversionSpec& conv,
+                                        FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned long v,  // NOLINT
+                                        const ConversionSpec& conv,
+                                        FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(long long v,  // NOLINT
+                                        const ConversionSpec& conv,
+                                        FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned long long v,  // NOLINT
+                                        const ConversionSpec& conv,
+                                        FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(uint128 v, const ConversionSpec& conv,
+                                        FormatSinkImpl* sink);
+template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0>
+IntegralConvertResult FormatConvertImpl(T v, const ConversionSpec& conv,
+                                        FormatSinkImpl* sink) {
+  return FormatConvertImpl(static_cast<int>(v), conv, sink);
+}
+
+// We provide this function to help the checker, but it is never defined.
+// FormatArgImpl will use the underlying Convert functions instead.
+template <typename T>
+typename std::enable_if<std::is_enum<T>::value &&
+                            !HasUserDefinedConvert<T>::value,
+                        IntegralConvertResult>::type
+FormatConvertImpl(T v, const ConversionSpec& conv, FormatSinkImpl* sink);
+
+template <typename T>
+ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<T>& v,
+                                         const ConversionSpec& conv,
+                                         FormatSinkImpl* out) {
+  std::ostringstream oss;
+  oss << v.v_;
+  if (!oss) return {false};
+  return str_format_internal::FormatConvertImpl(oss.str(), conv, out);
+}
+
+// Use templates and dependent types to delay evaluation of the function
+// until after FormatCountCapture is fully defined.
+struct FormatCountCaptureHelper {
+  template <class T = int>
+  static ConvertResult<Conv::n> ConvertHelper(const FormatCountCapture& v,
+                                              const ConversionSpec& conv,
+                                              FormatSinkImpl* sink) {
+    const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v;
+
+    if (conv.conv().id() != str_format_internal::ConversionChar::n)
+      return {false};
+    *v2.p_ = static_cast<int>(sink->size());
+    return {true};
+  }
+};
+
+template <class T = int>
+ConvertResult<Conv::n> FormatConvertImpl(const FormatCountCapture& v,
+                                         const ConversionSpec& conv,
+                                         FormatSinkImpl* sink) {
+  return FormatCountCaptureHelper::ConvertHelper(v, conv, sink);
+}
+
+// Helper friend struct to hide implementation details from the public API of
+// FormatArgImpl.
+struct FormatArgImplFriend {
+  template <typename Arg>
+  static bool ToInt(Arg arg, int* out) {
+    if (!arg.vtbl_->to_int) return false;
+    *out = arg.vtbl_->to_int(arg.data_);
+    return true;
+  }
+
+  template <typename Arg>
+  static bool Convert(Arg arg, const str_format_internal::ConversionSpec& conv,
+                      FormatSinkImpl* out) {
+    return arg.vtbl_->convert(arg.data_, conv, out);
+  }
+
+  template <typename Arg>
+  static const void* GetVTablePtrForTest(Arg arg) {
+    return arg.vtbl_;
+  }
+};
+
+// A type-erased handle to a format argument.
+class FormatArgImpl {
+ private:
+  enum { kInlinedSpace = 8 };
+
+  using VoidPtr = str_format_internal::VoidPtr;
+
+  union Data {
+    const void* ptr;
+    const volatile void* volatile_ptr;
+    char buf[kInlinedSpace];
+  };
+
+  struct VTable {
+    bool (*convert)(Data, const str_format_internal::ConversionSpec& conv,
+                    FormatSinkImpl* out);
+    int (*to_int)(Data);
+  };
+
+  template <typename T>
+  struct store_by_value
+      : std::integral_constant<bool, (sizeof(T) <= kInlinedSpace) &&
+                                         (std::is_integral<T>::value ||
+                                          std::is_floating_point<T>::value ||
+                                          std::is_pointer<T>::value ||
+                                          std::is_same<VoidPtr, T>::value)> {};
+
+  enum StoragePolicy { ByPointer, ByVolatilePointer, ByValue };
+  template <typename T>
+  struct storage_policy
+      : std::integral_constant<StoragePolicy,
+                               (std::is_volatile<T>::value
+                                    ? ByVolatilePointer
+                                    : (store_by_value<T>::value ? ByValue
+                                                                : ByPointer))> {
+  };
+
+  // An instance of an FormatArgImpl::VTable suitable for 'T'.
+  template <typename T>
+  struct TypedVTable;
+
+  // To reduce the number of vtables we will decay values before hand.
+  // Anything with a user-defined Convert will get its own vtable.
+  // For everything else:
+  //   - Decay char* and char arrays into `const char*`
+  //   - Decay any other pointer to `const void*`
+  //   - Decay all enums to their underlying type.
+  //   - Decay function pointers to void*.
+  template <typename T, typename = void>
+  struct DecayType {
+    static constexpr bool kHasUserDefined =
+        str_format_internal::HasUserDefinedConvert<T>::value;
+    using type = typename std::conditional<
+        !kHasUserDefined && std::is_convertible<T, const char*>::value,
+        const char*,
+        typename std::conditional<!kHasUserDefined &&
+                                      std::is_convertible<T, VoidPtr>::value,
+                                  VoidPtr, const T&>::type>::type;
+  };
+  template <typename T>
+  struct DecayType<T,
+                   typename std::enable_if<
+                       !str_format_internal::HasUserDefinedConvert<T>::value &&
+                       std::is_enum<T>::value>::type> {
+    using type = typename std::underlying_type<T>::type;
+  };
+
+ public:
+  template <typename T>
+  explicit FormatArgImpl(const T& value) {
+    using D = typename DecayType<T>::type;
+    static_assert(
+        std::is_same<D, const T&>::value || storage_policy<D>::value == ByValue,
+        "Decayed types must be stored by value");
+    Init(static_cast<D>(value));
+  }
+
+ private:
+  friend struct str_format_internal::FormatArgImplFriend;
+  template <typename T, StoragePolicy = storage_policy<T>::value>
+  struct Manager;
+
+  template <typename T>
+  struct Manager<T, ByPointer> {
+    static Data SetValue(const T& value) {
+      Data data;
+      data.ptr = &value;
+      return data;
+    }
+
+    static const T& Value(Data arg) { return *static_cast<const T*>(arg.ptr); }
+  };
+
+  template <typename T>
+  struct Manager<T, ByVolatilePointer> {
+    static Data SetValue(const T& value) {
+      Data data;
+      data.volatile_ptr = &value;
+      return data;
+    }
+
+    static const T& Value(Data arg) {
+      return *static_cast<const T*>(arg.volatile_ptr);
+    }
+  };
+
+  template <typename T>
+  struct Manager<T, ByValue> {
+    static Data SetValue(const T& value) {
+      Data data;
+      memcpy(data.buf, &value, sizeof(value));
+      return data;
+    }
+
+    static T Value(Data arg) {
+      T value;
+      memcpy(&value, arg.buf, sizeof(T));
+      return value;
+    }
+  };
+
+  template <typename T>
+  void Init(const T& value);
+
+  template <typename T>
+  static int ToIntVal(const T& val) {
+    using CommonType = typename std::conditional<std::is_signed<T>::value,
+                                                 int64_t, uint64_t>::type;
+    if (static_cast<CommonType>(val) >
+        static_cast<CommonType>(std::numeric_limits<int>::max())) {
+      return std::numeric_limits<int>::max();
+    } else if (std::is_signed<T>::value &&
+               static_cast<CommonType>(val) <
+                   static_cast<CommonType>(std::numeric_limits<int>::min())) {
+      return std::numeric_limits<int>::min();
+    }
+    return static_cast<int>(val);
+  }
+
+  Data data_;
+  const VTable* vtbl_;
+};
+
+template <typename T>
+struct FormatArgImpl::TypedVTable {
+ private:
+  static bool ConvertImpl(Data arg,
+                          const str_format_internal::ConversionSpec& conv,
+                          FormatSinkImpl* out) {
+    return str_format_internal::FormatConvertImpl(Manager<T>::Value(arg), conv,
+                                                  out)
+        .value;
+  }
+
+  template <typename U = T, typename = void>
+  struct ToIntImpl {
+    static constexpr int (*value)(Data) = nullptr;
+  };
+
+  template <typename U>
+  struct ToIntImpl<U,
+                   typename std::enable_if<std::is_integral<U>::value>::type> {
+    static int Invoke(Data arg) { return ToIntVal(Manager<T>::Value(arg)); }
+    static constexpr int (*value)(Data) = &Invoke;
+  };
+
+  template <typename U>
+  struct ToIntImpl<U, typename std::enable_if<std::is_enum<U>::value>::type> {
+    static int Invoke(Data arg) {
+      return ToIntVal(static_cast<typename std::underlying_type<T>::type>(
+          Manager<T>::Value(arg)));
+    }
+    static constexpr int (*value)(Data) = &Invoke;
+  };
+
+ public:
+  static constexpr VTable value{&ConvertImpl, ToIntImpl<>::value};
+};
+
+template <typename T>
+constexpr FormatArgImpl::VTable FormatArgImpl::TypedVTable<T>::value;
+
+template <typename T>
+void FormatArgImpl::Init(const T& value) {
+  data_ = Manager<T>::SetValue(value);
+  vtbl_ = &TypedVTable<T>::value;
+}
+
+extern template struct FormatArgImpl::TypedVTable<str_format_internal::VoidPtr>;
+
+extern template struct FormatArgImpl::TypedVTable<bool>;
+extern template struct FormatArgImpl::TypedVTable<char>;
+extern template struct FormatArgImpl::TypedVTable<signed char>;
+extern template struct FormatArgImpl::TypedVTable<unsigned char>;
+extern template struct FormatArgImpl::TypedVTable<short>;           // NOLINT
+extern template struct FormatArgImpl::TypedVTable<unsigned short>;  // NOLINT
+extern template struct FormatArgImpl::TypedVTable<int>;
+extern template struct FormatArgImpl::TypedVTable<unsigned>;
+extern template struct FormatArgImpl::TypedVTable<long>;           // NOLINT
+extern template struct FormatArgImpl::TypedVTable<unsigned long>;  // NOLINT
+extern template struct FormatArgImpl::TypedVTable<long long>;      // NOLINT
+extern template struct FormatArgImpl::TypedVTable<
+    unsigned long long>;  // NOLINT
+extern template struct FormatArgImpl::TypedVTable<uint128>;
+
+extern template struct FormatArgImpl::TypedVTable<float>;
+extern template struct FormatArgImpl::TypedVTable<double>;
+extern template struct FormatArgImpl::TypedVTable<long double>;
+
+extern template struct FormatArgImpl::TypedVTable<const char*>;
+extern template struct FormatArgImpl::TypedVTable<std::string>;
+extern template struct FormatArgImpl::TypedVTable<string_view>;
+}  // namespace str_format_internal
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_
diff --git a/absl/strings/internal/str_format/arg_test.cc b/absl/strings/internal/str_format/arg_test.cc
new file mode 100644
index 000000000000..83d59048ea27
--- /dev/null
+++ b/absl/strings/internal/str_format/arg_test.cc
@@ -0,0 +1,111 @@
+// 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
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+#include "absl/strings/internal/str_format/arg.h"
+
+#include <ostream>
+#include <string>
+#include "gtest/gtest.h"
+#include "absl/strings/str_format.h"
+
+namespace absl {
+namespace str_format_internal {
+namespace {
+
+class FormatArgImplTest : public ::testing::Test {
+ public:
+  enum Color { kRed, kGreen, kBlue };
+
+  static const char *hi() { return "hi"; }
+};
+
+TEST_F(FormatArgImplTest, ToInt) {
+  int out = 0;
+  EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(1), &out));
+  EXPECT_EQ(1, out);
+  EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(-1), &out));
+  EXPECT_EQ(-1, out);
+  EXPECT_TRUE(
+      FormatArgImplFriend::ToInt(FormatArgImpl(static_cast<char>(64)), &out));
+  EXPECT_EQ(64, out);
+  EXPECT_TRUE(FormatArgImplFriend::ToInt(
+      FormatArgImpl(static_cast<unsigned long long>(123456)), &out));  // NOLINT
+  EXPECT_EQ(123456, out);
+  EXPECT_TRUE(FormatArgImplFriend::ToInt(
+      FormatArgImpl(static_cast<unsigned long long>(  // NOLINT
+                        std::numeric_limits<int>::max()) +
+                    1),
+      &out));
+  EXPECT_EQ(std::numeric_limits<int>::max(), out);
+  EXPECT_TRUE(FormatArgImplFriend::ToInt(
+      FormatArgImpl(static_cast<long long>(  // NOLINT
+                        std::numeric_limits<int>::min()) -
+                    10),
+      &out));
+  EXPECT_EQ(std::numeric_limits<int>::min(), out);
+  EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(false), &out));
+  EXPECT_EQ(0, out);
+  EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(true), &out));
+  EXPECT_EQ(1, out);
+  EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(2.2), &out));
+  EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(3.2f), &out));
+  EXPECT_FALSE(FormatArgImplFriend::ToInt(
+      FormatArgImpl(static_cast<int *>(nullptr)), &out));
+  EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(hi()), &out));
+  EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl("hi"), &out));
+  EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(kBlue), &out));
+  EXPECT_EQ(2, out);
+}
+
+extern const char kMyArray[];
+
+TEST_F(FormatArgImplTest, CharArraysDecayToCharPtr) {
+  const char* a = "";
+  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+            FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl("")));
+  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+            FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl("A")));
+  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+            FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl("ABC")));
+  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+            FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(kMyArray)));
+}
+
+TEST_F(FormatArgImplTest, OtherPtrDecayToVoidPtr) {
+  auto expected = FormatArgImplFriend::GetVTablePtrForTest(
+      FormatArgImpl(static_cast<void *>(nullptr)));
+  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(
+                FormatArgImpl(static_cast<int *>(nullptr))),
+            expected);
+  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(
+                FormatArgImpl(static_cast<volatile int *>(nullptr))),
+            expected);
+
+  auto p = static_cast<void (*)()>([] {});
+  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(p)),
+            expected);
+}
+
+TEST_F(FormatArgImplTest, WorksWithCharArraysOfUnknownSize) {
+  std::string s;
+  FormatSinkImpl sink(&s);
+  ConversionSpec conv;
+  conv.set_conv(ConversionChar::FromChar('s'));
+  conv.set_flags(Flags());
+  conv.set_width(-1);
+  conv.set_precision(-1);
+  EXPECT_TRUE(
+      FormatArgImplFriend::Convert(FormatArgImpl(kMyArray), conv, &sink));
+  sink.Flush();
+  EXPECT_EQ("ABCDE", s);
+}
+const char kMyArray[] = "ABCDE";
+
+}  // namespace
+}  // namespace str_format_internal
+}  // namespace absl
diff --git a/absl/strings/internal/str_format/bind.cc b/absl/strings/internal/str_format/bind.cc
new file mode 100644
index 000000000000..33e8641558ab
--- /dev/null
+++ b/absl/strings/internal/str_format/bind.cc
@@ -0,0 +1,232 @@
+#include "absl/strings/internal/str_format/bind.h"
+
+#include <cerrno>
+#include <limits>
+#include <sstream>
+#include <string>
+
+namespace absl {
+namespace str_format_internal {
+
+namespace {
+
+inline bool BindFromPosition(int position, int* value,
+                             absl::Span<const FormatArgImpl> pack) {
+  assert(position > 0);
+  if (static_cast<size_t>(position) > pack.size()) {
+    return false;
+  }
+  // -1 because positions are 1-based
+  return FormatArgImplFriend::ToInt(pack[position - 1], value);
+}
+
+class ArgContext {
+ public:
+  explicit ArgContext(absl::Span<const FormatArgImpl> pack) : pack_(pack) {}
+
+  // Fill 'bound' with the results of applying the context's argument pack
+  // to the specified 'props'. We synthesize a BoundConversion by
+  // lining up a UnboundConversion with a user argument. We also
+  // resolve any '*' specifiers for width and precision, so after
+  // this call, 'bound' has all the information it needs to be formatted.
+  // Returns false on failure.
+  bool Bind(const UnboundConversion *props, BoundConversion *bound);
+
+ private:
+  absl::Span<const FormatArgImpl> pack_;
+};
+
+inline bool ArgContext::Bind(const UnboundConversion* unbound,
+                             BoundConversion* bound) {
+  const FormatArgImpl* arg = nullptr;
+  int arg_position = unbound->arg_position;
+  if (static_cast<size_t>(arg_position - 1) >= pack_.size()) return false;
+  arg = &pack_[arg_position - 1];  // 1-based
+
+  if (!unbound->flags.basic) {
+    int width = unbound->width.value();
+    bool force_left = false;
+    if (unbound->width.is_from_arg()) {
+      if (!BindFromPosition(unbound->width.get_from_arg(), &width, pack_))
+        return false;
+      if (width < 0) {
+        // "A negative field width is taken as a '-' flag followed by a
+        // positive field width."
+        force_left = true;
+        width = -width;
+      }
+    }
+
+    int precision = unbound->precision.value();
+    if (unbound->precision.is_from_arg()) {
+      if (!BindFromPosition(unbound->precision.get_from_arg(), &precision,
+                            pack_))
+        return false;
+    }
+
+    bound->set_width(width);
+    bound->set_precision(precision);
+    bound->set_flags(unbound->flags);
+    if (force_left)
+      bound->set_left(true);
+  } else {
+    bound->set_flags(unbound->flags);
+    bound->set_width(-1);
+    bound->set_precision(-1);
+  }
+
+  bound->set_length_mod(unbound->length_mod);
+  bound->set_conv(unbound->conv);
+  bound->set_arg(arg);
+  return true;
+}
+
+template <typename Converter>
+class ConverterConsumer {
+ public:
+  ConverterConsumer(Converter converter, absl::Span<const FormatArgImpl> pack)
+      : converter_(converter), arg_context_(pack) {}
+
+  bool Append(string_view s) {
+    converter_.Append(s);
+    return true;
+  }
+  bool ConvertOne(const UnboundConversion& conv, string_view conv_string) {
+    BoundConversion bound;
+    if (!arg_context_.Bind(&conv, &bound)) return false;
+    return converter_.ConvertOne(bound, conv_string);
+  }
+
+ private:
+  Converter converter_;
+  ArgContext arg_context_;
+};
+
+template <typename Converter>
+bool ConvertAll(const UntypedFormatSpecImpl& format,
+                absl::Span<const FormatArgImpl> args,
+                const Converter& converter) {
+  const ParsedFormatBase* pc = format.parsed_conversion();
+  if (pc)
+    return pc->ProcessFormat(ConverterConsumer<Converter>(converter, args));
+
+  return ParseFormatString(format.str(),
+                           ConverterConsumer<Converter>(converter, args));
+}
+
+class DefaultConverter {
+ public:
+  explicit DefaultConverter(FormatSinkImpl* sink) : sink_(sink) {}
+
+  void Append(string_view s) const { sink_->Append(s); }
+
+  bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const {
+    return FormatArgImplFriend::Convert(*bound.arg(), bound, sink_);
+  }
+
+ private:
+  FormatSinkImpl* sink_;
+};
+
+class SummarizingConverter {
+ public:
+  explicit SummarizingConverter(FormatSinkImpl* sink) : sink_(sink) {}
+
+  void Append(string_view s) const { sink_->Append(s); }
+
+  bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const {
+    UntypedFormatSpecImpl spec("%d");
+
+    std::ostringstream ss;
+    ss << "{" << Streamable(spec, {*bound.arg()}) << ":" << bound.flags();
+    if (bound.width() >= 0) ss << bound.width();
+    if (bound.precision() >= 0) ss << "." << bound.precision();
+    ss << bound.length_mod() << bound.conv() << "}";
+    Append(ss.str());
+    return true;
+  }
+
+ private:
+  FormatSinkImpl* sink_;
+};
+
+}  // namespace
+
+bool BindWithPack(const UnboundConversion* props,
+                  absl::Span<const FormatArgImpl> pack,
+                  BoundConversion* bound) {
+  return ArgContext(pack).Bind(props, bound);
+}
+
+std::string Summarize(const UntypedFormatSpecImpl& format,
+                 absl::Span<const FormatArgImpl> args) {
+  typedef SummarizingConverter Converter;
+  std::string out;
+  {
+    // inner block to destroy sink before returning out. It ensures a last
+    // flush.
+    FormatSinkImpl sink(&out);
+    if (!ConvertAll(format, args, Converter(&sink))) {
+      sink.Flush();
+      out.clear();
+    }
+  }
+  return out;
+}
+
+bool FormatUntyped(FormatRawSinkImpl raw_sink,
+                   const UntypedFormatSpecImpl& format,
+                   absl::Span<const FormatArgImpl> args) {
+  FormatSinkImpl sink(raw_sink);
+  using Converter = DefaultConverter;
+  if (!ConvertAll(format, args, Converter(&sink))) {
+    sink.Flush();
+    return false;
+  }
+  return true;
+}
+
+std::ostream& Streamable::Print(std::ostream& os) const {
+  if (!FormatUntyped(&os, format_, args_)) os.setstate(std::ios::failbit);
+  return os;
+}
+
+std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl& format,
+                   absl::Span<const FormatArgImpl> args) {
+  size_t orig = out->size();
+  if (!FormatUntyped(out, format, args)) out->resize(orig);
+  return *out;
+}
+
+int FprintF(std::FILE* output, const UntypedFormatSpecImpl& format,
+            absl::Span<const FormatArgImpl> args) {
+  FILERawSink sink(output);
+  if (!FormatUntyped(&sink, format, args)) {
+    errno = EINVAL;
+    return -1;
+  }
+  if (sink.error()) {
+    errno = sink.error();
+    return -1;
+  }
+  if (sink.count() > std::numeric_limits<int>::max()) {
+    errno = EFBIG;
+    return -1;
+  }
+  return static_cast<int>(sink.count());
+}
+
+int SnprintF(char* output, size_t size, const UntypedFormatSpecImpl& format,
+             absl::Span<const FormatArgImpl> args) {
+  BufferRawSink sink(output, size ? size - 1 : 0);
+  if (!FormatUntyped(&sink, format, args)) {
+    errno = EINVAL;
+    return -1;
+  }
+  size_t total = sink.total_written();
+  if (size) output[std::min(total, size - 1)] = 0;
+  return static_cast<int>(total);
+}
+
+}  // namespace str_format_internal
+}  // namespace absl
diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h
new file mode 100644
index 000000000000..4008611211cf
--- /dev/null
+++ b/absl/strings/internal/str_format/bind.h
@@ -0,0 +1,189 @@
+#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
+#define ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
+
+#include <array>
+#include <cstdio>
+#include <sstream>
+#include <string>
+
+#include "absl/base/port.h"
+#include "absl/container/inlined_vector.h"
+#include "absl/strings/internal/str_format/arg.h"
+#include "absl/strings/internal/str_format/checker.h"
+#include "absl/strings/internal/str_format/parser.h"
+#include "absl/types/span.h"
+
+namespace absl {
+
+class UntypedFormatSpec;
+
+namespace str_format_internal {
+
+class BoundConversion : public ConversionSpec {
+ public:
+  const FormatArgImpl* arg() const { return arg_; }
+  void set_arg(const FormatArgImpl* a) { arg_ = a; }
+
+ private:
+  const FormatArgImpl* arg_;
+};
+
+// This is the type-erased class that the implementation uses.
+class UntypedFormatSpecImpl {
+ public:
+  UntypedFormatSpecImpl() = delete;
+
+  explicit UntypedFormatSpecImpl(string_view s) : str_(s), pc_() {}
+  explicit UntypedFormatSpecImpl(
+      const str_format_internal::ParsedFormatBase* pc)
+      : pc_(pc) {}
+  string_view str() const { return str_; }
+  const str_format_internal::ParsedFormatBase* parsed_conversion() const {
+    return pc_;
+  }
+
+  template <typename T>
+  static const UntypedFormatSpecImpl& Extract(const T& s) {
+    return s.spec_;
+  }
+
+ private:
+  string_view str_;
+  const str_format_internal::ParsedFormatBase* pc_;
+};
+
+template <typename T, typename...>
+struct MakeDependent {
+  using type = T;
+};
+
+// Implicitly convertible from `const char*`, `string_view`, and the
+// `ExtendedParsedFormat` type. This abstraction allows all format functions to
+// operate on any without providing too many overloads.
+template <typename... Args>
+class FormatSpecTemplate
+    : public MakeDependent<UntypedFormatSpec, Args...>::type {
+  using Base = typename MakeDependent<UntypedFormatSpec, Args...>::type;
+
+ public:
+#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+
+  // Honeypot overload for when the std::string is not constexpr.
+  // We use the 'unavailable' attribute to give a better compiler error than
+  // just 'method is deleted'.
+  FormatSpecTemplate(...)  // NOLINT
+      __attribute__((unavailable("Format std::string is not constexpr.")));
+
+  // Honeypot overload for when the format is constexpr and invalid.
+  // We use the 'unavailable' attribute to give a better compiler error than
+  // just 'method is deleted'.
+  // To avoid checking the format twice, we just check that the format is
+  // constexpr. If is it valid, then the overload below will kick in.
+  // We add the template here to make this overload have lower priority.
+  template <typename = void>
+  FormatSpecTemplate(const char* s)  // NOLINT
+      __attribute__((
+          enable_if(str_format_internal::EnsureConstexpr(s), "constexpr trap"),
+          unavailable(
+              "Format specified does not match the arguments passed.")));
+
+  template <typename T = void>
+  FormatSpecTemplate(string_view s)  // NOLINT
+      __attribute__((enable_if(str_format_internal::EnsureConstexpr(s),
+                               "constexpr trap"))) {
+    static_assert(sizeof(T*) == 0,
+                  "Format specified does not match the arguments passed.");
+  }
+
+  // Good format overload.
+  FormatSpecTemplate(const char* s)  // NOLINT
+      __attribute__((enable_if(ValidFormatImpl<ArgumentToConv<Args>()...>(s),
+                               "bad format trap")))
+      : Base(s) {}
+
+  FormatSpecTemplate(string_view s)  // NOLINT
+      __attribute__((enable_if(ValidFormatImpl<ArgumentToConv<Args>()...>(s),
+                               "bad format trap")))
+      : Base(s) {}
+
+#else  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+
+  FormatSpecTemplate(const char* s) : Base(s) {}  // NOLINT
+  FormatSpecTemplate(string_view s) : Base(s) {}  // NOLINT
+
+#endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+
+  template <Conv... C, typename = typename std::enable_if<
+                           sizeof...(C) == sizeof...(Args) &&
+                           AllOf(Contains(ArgumentToConv<Args>(),
+                                          C)...)>::type>
+  FormatSpecTemplate(const ExtendedParsedFormat<C...>& pc)  // NOLINT
+      : Base(&pc) {}
+};
+
+template <typename... Args>
+struct FormatSpecDeductionBarrier {
+  using type = FormatSpecTemplate<Args...>;
+};
+
+class Streamable {
+ public:
+  Streamable(const UntypedFormatSpecImpl& format,
+             absl::Span<const FormatArgImpl> args)
+      : format_(format), args_(args.begin(), args.end()) {}
+
+  std::ostream& Print(std::ostream& os) const;
+
+  friend std::ostream& operator<<(std::ostream& os, const Streamable& l) {
+    return l.Print(os);
+  }
+
+ private:
+  const UntypedFormatSpecImpl& format_;
+  absl::InlinedVector<FormatArgImpl, 4> args_;
+};
+
+// for testing
+std::string Summarize(const UntypedFormatSpecImpl& format,
+                 absl::Span<const FormatArgImpl> args);
+bool BindWithPack(const UnboundConversion* props,
+                  absl::Span<const FormatArgImpl> pack, BoundConversion* bound);
+
+bool FormatUntyped(FormatRawSinkImpl raw_sink,
+                   const UntypedFormatSpecImpl& format,
+                   absl::Span<const FormatArgImpl> args);
+
+std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl& format,
+                   absl::Span<const FormatArgImpl> args);
+
+inline std::string FormatPack(const UntypedFormatSpecImpl& format,
+                         absl::Span<const FormatArgImpl> args) {
+  std::string out;
+  AppendPack(&out, format, args);
+  return out;
+}
+
+int FprintF(std::FILE* output, const UntypedFormatSpecImpl& format,
+            absl::Span<const FormatArgImpl> args);
+int SnprintF(char* output, size_t size, const UntypedFormatSpecImpl& format,
+             absl::Span<const FormatArgImpl> args);
+
+// Returned by Streamed(v). Converts via '%s' to the std::string created
+// by std::ostream << v.
+template <typename T>
+class StreamedWrapper {
+ public:
+  explicit StreamedWrapper(const T& v) : v_(v) { }
+
+ private:
+  template <typename S>
+  friend ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<S>& v,
+                                                  const ConversionSpec& conv,
+                                                  FormatSinkImpl* out);
+  const T& v_;
+};
+
+}  // namespace str_format_internal
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
diff --git a/absl/strings/internal/str_format/bind_test.cc b/absl/strings/internal/str_format/bind_test.cc
new file mode 100644
index 000000000000..47575739ba13
--- /dev/null
+++ b/absl/strings/internal/str_format/bind_test.cc
@@ -0,0 +1,131 @@
+#include "absl/strings/internal/str_format/bind.h"
+
+#include <string.h>
+
+#include "gtest/gtest.h"
+
+namespace absl {
+namespace str_format_internal {
+namespace {
+
+template <typename T, size_t N>
+size_t ArraySize(T (&)[N]) {
+  return N;
+}
+
+class FormatBindTest : public ::testing::Test {
+ public:
+  bool Extract(const char *s, UnboundConversion *props, int *next) const {
+    absl::string_view src = s;
+    return ConsumeUnboundConversion(&src, props, next) && src.empty();
+  }
+};
+
+TEST_F(FormatBindTest, BindSingle) {
+  struct Expectation {
+    int line;
+    const char *fmt;
+    int ok_phases;
+    const FormatArgImpl *arg;
+    int width;
+    int precision;
+    int next_arg;
+  };
+  const int no = -1;
+  const int ia[] = { 10, 20, 30, 40};
+  const FormatArgImpl args[] = {FormatArgImpl(ia[0]), FormatArgImpl(ia[1]),
+                                FormatArgImpl(ia[2]), FormatArgImpl(ia[3])};
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+  const Expectation kExpect[] = {
+    {__LINE__, "d",          2, &args[0], no, no, 2},
+    {__LINE__, "4d",         2, &args[0],  4, no, 2},
+    {__LINE__, ".5d",        2, &args[0], no,  5, 2},
+    {__LINE__, "4.5d",       2, &args[0],  4,  5, 2},
+    {__LINE__, "*d",         2, &args[1], 10, no, 3},
+    {__LINE__, ".*d",        2, &args[1], no, 10, 3},
+    {__LINE__, "*.*d",       2, &args[2], 10, 20, 4},
+    {__LINE__, "1$d",        2, &args[0], no, no, 0},
+    {__LINE__, "2$d",        2, &args[1], no, no, 0},
+    {__LINE__, "3$d",        2, &args[2], no, no, 0},
+    {__LINE__, "4$d",        2, &args[3], no, no, 0},
+    {__LINE__, "2$*1$d",     2, &args[1], 10, no, 0},
+    {__LINE__, "2$*2$d",     2, &args[1], 20, no, 0},
+    {__LINE__, "2$*3$d",     2, &args[1], 30, no, 0},
+    {__LINE__, "2$.*1$d",    2, &args[1], no, 10, 0},
+    {__LINE__, "2$.*2$d",    2, &args[1], no, 20, 0},
+    {__LINE__, "2$.*3$d",    2, &args[1], no, 30, 0},
+    {__LINE__, "2$*3$.*1$d", 2, &args[1], 30, 10, 0},
+    {__LINE__, "2$*2$.*2$d", 2, &args[1], 20, 20, 0},
+    {__LINE__, "2$*1$.*3$d", 2, &args[1], 10, 30, 0},
+    {__LINE__, "2$*3$.*1$d", 2, &args[1], 30, 10, 0},
+    {__LINE__, "1$*d",       0},  // indexed, then positional
+    {__LINE__, "*2$d",       0},  // positional, then indexed
+    {__LINE__, "6$d",        1},  // arg position out of bounds
+    {__LINE__, "1$6$d",      0},  // width position incorrectly specified
+    {__LINE__, "1$.6$d",     0},  // precision position incorrectly specified
+    {__LINE__, "1$*6$d",     1},  // width position out of bounds
+    {__LINE__, "1$.*6$d",    1},  // precision position out of bounds
+  };
+#pragma GCC diagnostic pop
+  for (const Expectation &e : kExpect) {
+    SCOPED_TRACE(e.line);
+    SCOPED_TRACE(e.fmt);
+    UnboundConversion props;
+    BoundConversion bound;
+    int ok_phases = 0;
+    int next = 0;
+    if (Extract(e.fmt, &props, &next)) {
+      ++ok_phases;
+      if (BindWithPack(&props, args, &bound)) {
+        ++ok_phases;
+      }
+    }
+    EXPECT_EQ(e.ok_phases, ok_phases);
+    if (e.ok_phases < 2) continue;
+    if (e.arg != nullptr) {
+      EXPECT_EQ(e.arg, bound.arg());
+    }
+    EXPECT_EQ(e.width, bound.width());
+    EXPECT_EQ(e.precision, bound.precision());
+  }
+}
+
+TEST_F(FormatBindTest, FormatPack) {
+  struct Expectation {
+    int line;
+    const char *fmt;
+    const char *summary;
+  };
+  const int ia[] = { 10, 20, 30, 40, -10 };
+  const FormatArgImpl args[] = {FormatArgImpl(ia[0]), FormatArgImpl(ia[1]),
+                                FormatArgImpl(ia[2]), FormatArgImpl(ia[3]),
+                                FormatArgImpl(ia[4])};
+  const Expectation kExpect[] = {
+    {__LINE__, "a%4db%dc", "a{10:4d}b{20:d}c"},
+    {__LINE__, "a%.4db%dc", "a{10:.4d}b{20:d}c"},
+    {__LINE__, "a%4.5db%dc", "a{10:4.5d}b{20:d}c"},
+    {__LINE__, "a%db%4.5dc", "a{10:d}b{20:4.5d}c"},
+    {__LINE__, "a%db%*.*dc", "a{10:d}b{40:20.30d}c"},
+    {__LINE__, "a%.*fb", "a{20:.10f}b"},
+    {__LINE__, "a%1$db%2$*3$.*4$dc", "a{10:d}b{20:30.40d}c"},
+    {__LINE__, "a%4$db%3$*2$.*1$dc", "a{40:d}b{30:20.10d}c"},
+    {__LINE__, "a%04ldb", "a{10:04ld}b"},
+    {__LINE__, "a%-#04lldb", "a{10:-#04lld}b"},
+    {__LINE__, "a%1$*5$db", "a{10:-10d}b"},
+    {__LINE__, "a%1$.*5$db", "a{10:d}b"},
+  };
+  for (const Expectation &e : kExpect) {
+    absl::string_view fmt = e.fmt;
+    SCOPED_TRACE(e.line);
+    SCOPED_TRACE(e.fmt);
+    UntypedFormatSpecImpl format(fmt);
+    EXPECT_EQ(e.summary,
+              str_format_internal::Summarize(format, absl::MakeSpan(args)))
+        << "line:" << e.line;
+  }
+}
+
+}  // namespace
+}  // namespace str_format_internal
+}  // namespace absl
diff --git a/absl/strings/internal/str_format/checker.h b/absl/strings/internal/str_format/checker.h
new file mode 100644
index 000000000000..8b594f2d5cc6
--- /dev/null
+++ b/absl/strings/internal/str_format/checker.h
@@ -0,0 +1,325 @@
+#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
+#define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
+
+#include "absl/strings/internal/str_format/arg.h"
+#include "absl/strings/internal/str_format/extension.h"
+
+// Compile time check support for entry points.
+
+#ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+#if defined(__clang__) && !defined(__native_client__)
+#if __has_attribute(enable_if)
+#define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1
+#endif  // __has_attribute(enable_if)
+#endif  // defined(__clang__) && !defined(__native_client__)
+#endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+
+namespace absl {
+namespace str_format_internal {
+
+constexpr bool AllOf() { return true; }
+
+template <typename... T>
+constexpr bool AllOf(bool b, T... t) {
+  return b && AllOf(t...);
+}
+
+template <typename Arg>
+constexpr Conv ArgumentToConv() {
+  return decltype(str_format_internal::FormatConvertImpl(
+      std::declval<const Arg&>(), std::declval<const ConversionSpec&>(),
+      std::declval<FormatSinkImpl*>()))::kConv;
+}
+
+#if 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 Conv* array;
+  int count;
+
+  // We do the bound check here to avoid having to do it on the callers.
+  // Returning an empty Conv has the same effect as short circuiting because it
+  // will never match any conversion.
+  constexpr Conv operator[](int i) const {
+    return i < count ? array[i] : Conv{};
+  }
+
+  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.
+  Conv 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) {
+  return ContainsChar(chars, GetChar(format, 0))
+             ? ConsumeAnyOf(ConsumeFront(format), chars)
+             : 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 {
+    return GetChar(format, 0) == '$' ? Integer{ConsumeFront(format), value}
+                                     : Integer{format, 0};
+  }
+};
+
+constexpr Integer ParseDigits(string_view format, int value = 0) {
+  return IsDigit(GetChar(format, 0))
+             ? ParseDigits(ConsumeFront(format),
+                           10 * value + GetChar(format, 0) - '0')
+             : 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 {
+    return IsDigit(GetChar(format_, 0))
+               ? SetFormat(ParseDigits(format_).format)
+               : GetChar(format_, 0) == '*'
+                     ? is_positional_
+                           ? VerifyPositional(
+                                 ParsePositional(ConsumeFront(format_)), '*')
+                           : SetFormat(ConsumeFront(format_))
+                                 .ConsumeNextArg('*')
+                     : *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 {
+    return GetChar(format_, 0) != '.'
+               ? *this
+               : GetChar(format_, 1) == '*'
+                     ? is_positional_
+                           ? VerifyPositional(
+                                 ParsePositional(ConsumeFront(format_, 2)), '*')
+                           : SetFormat(ConsumeFront(format_, 2))
+                                 .ConsumeNextArg('*')
+                     : 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 {
+    return is_positional_
+               ? VerifyPositional({ConsumeFront(format_), arg_position_},
+                                  GetChar(format_, 0))
+               : ConsumeNextArg(GetChar(format_, 0))
+                     .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 {
+    return (is_positional_ ? ParseArgPosition(ParsePositional(format_)) : *this)
+        .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) {
+    return FoundPercent(format) || !limit
+               ? format
+               : ConsumeNonPercentInner(
+                     ConsumeFront(format, GetChar(format, 0) == '%' &&
+                                                  GetChar(format, 1) == '%'
+                                              ? 2
+                                              : 1),
+                     limit - 1);
+  }
+
+  // Consume characters until the next conversion spec %.
+  // It skips %%.
+  static constexpr string_view ConsumeNonPercent(string_view format) {
+    return FoundPercent(format)
+               ? format
+               : ConsumeNonPercent(ConsumeNonPercentInner(format));
+  }
+
+  static constexpr bool IsPositional(string_view format) {
+    return IsDigit(GetChar(format, 0)) ? IsPositional(ConsumeFront(format))
+                                       : 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 <Conv... C>
+constexpr bool ValidFormatImpl(string_view format) {
+  return FormatParser(format,
+                      {ConvListT<sizeof...(C)>{{C...}}.list, sizeof...(C)})
+      .Run();
+}
+
+#endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+
+}  // namespace str_format_internal
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
diff --git a/absl/strings/internal/str_format/checker_test.cc b/absl/strings/internal/str_format/checker_test.cc
new file mode 100644
index 000000000000..14d11ea8bd30
--- /dev/null
+++ b/absl/strings/internal/str_format/checker_test.cc
@@ -0,0 +1,150 @@
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/strings/str_format.h"
+
+namespace absl {
+namespace str_format_internal {
+namespace {
+
+std::string ConvToString(Conv conv) {
+  std::string out;
+#define CONV_SET_CASE(c) \
+  if (Contains(conv, Conv::c)) out += #c;
+  ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, )
+#undef CONV_SET_CASE
+  if (Contains(conv, Conv::star)) out += "*";
+  return out;
+}
+
+TEST(StrFormatChecker, ArgumentToConv) {
+  Conv conv = ArgumentToConv<std::string>();
+  EXPECT_EQ(ConvToString(conv), "s");
+
+  conv = ArgumentToConv<const char*>();
+  EXPECT_EQ(ConvToString(conv), "sp");
+
+  conv = ArgumentToConv<double>();
+  EXPECT_EQ(ConvToString(conv), "fFeEgGaA");
+
+  conv = ArgumentToConv<int>();
+  EXPECT_EQ(ConvToString(conv), "cdiouxXfFeEgGaA*");
+
+  conv = ArgumentToConv<std::string*>();
+  EXPECT_EQ(ConvToString(conv), "p");
+}
+
+#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+
+struct Case {
+  bool result;
+  const char* format;
+};
+
+template <typename... Args>
+constexpr Case ValidFormat(const char* format) {
+  return {ValidFormatImpl<ArgumentToConv<Args>()...>(format), format};
+}
+
+TEST(StrFormatChecker, ValidFormat) {
+  // We want to make sure these expressions are constexpr and they have the
+  // expected value.
+  // If they are not constexpr the attribute will just ignore them and not give
+  // a compile time error.
+  enum e {};
+  enum class e2 {};
+  constexpr Case trues[] = {
+      ValidFormat<>("abc"),  //
+
+      ValidFormat<e>("%d"),                             //
+      ValidFormat<e2>("%d"),                            //
+      ValidFormat<int>("%% %d"),                        //
+      ValidFormat<int>("%ld"),                          //
+      ValidFormat<int>("%lld"),                         //
+      ValidFormat<std::string>("%s"),                        //
+      ValidFormat<std::string>("%10s"),                      //
+      ValidFormat<int>("%.10x"),                        //
+      ValidFormat<int, int>("%*.3x"),                   //
+      ValidFormat<int>("%1.d"),                         //
+      ValidFormat<int>("%.d"),                          //
+      ValidFormat<int, double>("%d %g"),                //
+      ValidFormat<int, std::string>("%*s"),                  //
+      ValidFormat<int, double>("%.*f"),                 //
+      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>("%% %1$d"),            //
+      ValidFormat<int>("%1$ld"),              //
+      ValidFormat<int>("%1$lld"),             //
+      ValidFormat<std::string>("%1$s"),            //
+      ValidFormat<std::string>("%1$10s"),          //
+      ValidFormat<int>("%1$.10x"),            //
+      ValidFormat<int>("%1$*1$.*1$d"),        //
+      ValidFormat<int, int>("%1$*2$.3x"),     //
+      ValidFormat<int>("%1$1.d"),             //
+      ValidFormat<int>("%1$.d"),              //
+      ValidFormat<double, int>("%2$d %1$g"),  //
+      ValidFormat<int, std::string>("%2$*1$s"),    //
+      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)")};
+
+  for (Case c : trues) {
+    EXPECT_TRUE(c.result) << c.format;
+  }
+
+  constexpr Case falses[] = {
+      ValidFormat<int>(""),  //
+
+      ValidFormat<e>("%s"),             //
+      ValidFormat<e2>("%s"),            //
+      ValidFormat<>("%s"),              //
+      ValidFormat<>("%r"),              //
+      ValidFormat<int>("%s"),           //
+      ValidFormat<int>("%.1.d"),        //
+      ValidFormat<int>("%*1d"),         //
+      ValidFormat<int>("%1-d"),         //
+      ValidFormat<std::string, int>("%*s"),  //
+      ValidFormat<int>("%*d"),          //
+      ValidFormat<std::string>("%p"),        //
+      ValidFormat<int (*)(int)>("%d"),  //
+
+      ValidFormat<>("%3$d"),                //
+      ValidFormat<>("%1$r"),                //
+      ValidFormat<int>("%1$s"),             //
+      ValidFormat<int>("%1$.1.d"),          //
+      ValidFormat<int>("%1$*2$1d"),         //
+      ValidFormat<int>("%1$1-d"),           //
+      ValidFormat<std::string, int>("%2$*1$s"),  //
+      ValidFormat<std::string>("%1$p"),
+
+      ValidFormat<int, int>("%d %2$d"),  //
+  };
+
+  for (Case c : falses) {
+    EXPECT_FALSE(c.result) << c.format;
+  }
+}
+
+TEST(StrFormatChecker, LongFormat) {
+#define CHARS_X_40 "1234567890123456789012345678901234567890"
+#define CHARS_X_400                                                            \
+  CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 \
+      CHARS_X_40 CHARS_X_40 CHARS_X_40
+#define CHARS_X_4000                                                      \
+  CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 \
+      CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400
+  constexpr char long_format[] =
+      CHARS_X_4000 "%d" CHARS_X_4000 "%s" CHARS_X_4000;
+  constexpr bool is_valid = ValidFormat<int, std::string>(long_format).result;
+  EXPECT_TRUE(is_valid);
+}
+
+#endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+
+}  // namespace
+}  // namespace str_format_internal
+}  // namespace absl
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc
new file mode 100644
index 000000000000..32f8a0f9ad1a
--- /dev/null
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -0,0 +1,575 @@
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <cmath>
+#include <string>
+
+#include "gtest/gtest.h"
+#include "absl/strings/internal/str_format/bind.h"
+
+namespace absl {
+namespace str_format_internal {
+namespace {
+
+template <typename T, size_t N>
+size_t ArraySize(T (&)[N]) {
+  return N;
+}
+
+std::string LengthModFor(float) { return ""; }
+std::string LengthModFor(double) { return ""; }
+std::string LengthModFor(long double) { return "L"; }
+std::string LengthModFor(char) { return "hh"; }
+std::string LengthModFor(signed char) { return "hh"; }
+std::string LengthModFor(unsigned char) { return "hh"; }
+std::string LengthModFor(short) { return "h"; }           // NOLINT
+std::string LengthModFor(unsigned short) { return "h"; }  // NOLINT
+std::string LengthModFor(int) { return ""; }
+std::string LengthModFor(unsigned) { return ""; }
+std::string LengthModFor(long) { return "l"; }                 // NOLINT
+std::string LengthModFor(unsigned long) { return "l"; }        // NOLINT
+std::string LengthModFor(long long) { return "ll"; }           // NOLINT
+std::string LengthModFor(unsigned long long) { return "ll"; }  // NOLINT
+
+std::string EscCharImpl(int v) {
+  if (isprint(v)) return std::string(1, static_cast<char>(v));
+  char buf[64];
+  int n = snprintf(buf, sizeof(buf), "\\%#.2x",
+                   static_cast<unsigned>(v & 0xff));
+  assert(n > 0 && n < sizeof(buf));
+  return std::string(buf, n);
+}
+
+std::string Esc(char v) { return EscCharImpl(v); }
+std::string Esc(signed char v) { return EscCharImpl(v); }
+std::string Esc(unsigned char v) { return EscCharImpl(v); }
+
+template <typename T>
+std::string Esc(const T &v) {
+  std::ostringstream oss;
+  oss << v;
+  return oss.str();
+}
+
+void StrAppend(std::string *dst, const char *format, va_list ap) {
+  // First try with a small fixed size buffer
+  static const int kSpaceLength = 1024;
+  char space[kSpaceLength];
+
+  // It's possible for methods that use a va_list to invalidate
+  // the data in it upon use.  The fix is to make a copy
+  // of the structure before using it and use that copy instead.
+  va_list backup_ap;
+  va_copy(backup_ap, ap);
+  int result = vsnprintf(space, kSpaceLength, format, backup_ap);
+  va_end(backup_ap);
+  if (result < kSpaceLength) {
+    if (result >= 0) {
+      // Normal case -- everything fit.
+      dst->append(space, result);
+      return;
+    }
+    if (result < 0) {
+      // Just an error.
+      return;
+    }
+  }
+
+  // Increase the buffer size to the size requested by vsnprintf,
+  // plus one for the closing \0.
+  int length = result + 1;
+  char *buf = new char[length];
+
+  // Restore the va_list before we use it again
+  va_copy(backup_ap, ap);
+  result = vsnprintf(buf, length, format, backup_ap);
+  va_end(backup_ap);
+
+  if (result >= 0 && result < length) {
+    // It fit
+    dst->append(buf, result);
+  }
+  delete[] buf;
+}
+
+std::string StrPrint(const char *format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  std::string result;
+  StrAppend(&result, format, ap);
+  va_end(ap);
+  return result;
+}
+
+class FormatConvertTest : public ::testing::Test { };
+
+template <typename T>
+void TestStringConvert(const T& str) {
+  const FormatArgImpl args[] = {FormatArgImpl(str)};
+  struct Expectation {
+    const char *out;
+    const char *fmt;
+  };
+  const Expectation kExpect[] = {
+    {"hello",  "%1$s"      },
+    {"",       "%1$.s"     },
+    {"",       "%1$.0s"    },
+    {"h",      "%1$.1s"    },
+    {"he",     "%1$.2s"    },
+    {"hello",  "%1$.10s"   },
+    {" hello", "%1$6s"     },
+    {"   he",  "%1$5.2s"   },
+    {"he   ",  "%1$-5.2s"  },
+    {"hello ", "%1$-6.10s" },
+  };
+  for (const Expectation &e : kExpect) {
+    UntypedFormatSpecImpl format(e.fmt);
+    EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args)));
+  }
+}
+
+TEST_F(FormatConvertTest, BasicString) {
+  TestStringConvert("hello");  // As char array.
+  TestStringConvert(static_cast<const char*>("hello"));
+  TestStringConvert(std::string("hello"));
+  TestStringConvert(string_view("hello"));
+}
+
+TEST_F(FormatConvertTest, NullString) {
+  const char* p = nullptr;
+  UntypedFormatSpecImpl format("%s");
+  EXPECT_EQ("", FormatPack(format, {FormatArgImpl(p)}));
+}
+
+TEST_F(FormatConvertTest, StringPrecision) {
+  // We cap at the precision.
+  char c = 'a';
+  const char* p = &c;
+  UntypedFormatSpecImpl format("%.1s");
+  EXPECT_EQ("a", FormatPack(format, {FormatArgImpl(p)}));
+
+  // We cap at the nul terminator.
+  p = "ABC";
+  UntypedFormatSpecImpl format2("%.10s");
+  EXPECT_EQ("ABC", FormatPack(format2, {FormatArgImpl(p)}));
+}
+
+TEST_F(FormatConvertTest, Pointer) {
+#if _MSC_VER
+  // MSVC's printf implementation prints pointers differently. We can't easily
+  // compare our implementation to theirs.
+  return;
+#endif
+  static int x = 0;
+  const int *xp = &x;
+  char c = 'h';
+  char *mcp = &c;
+  const char *cp = "hi";
+  const char *cnil = nullptr;
+  const int *inil = nullptr;
+  using VoidF = void (*)();
+  VoidF fp = [] {}, fnil = nullptr;
+  volatile char vc;
+  volatile char* vcp = &vc;
+  volatile char* vcnil = nullptr;
+  const FormatArgImpl args[] = {
+      FormatArgImpl(xp),   FormatArgImpl(cp),  FormatArgImpl(inil),
+      FormatArgImpl(cnil), FormatArgImpl(mcp), FormatArgImpl(fp),
+      FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vcnil),
+  };
+  struct Expectation {
+    std::string out;
+    const char *fmt;
+  };
+  const Expectation kExpect[] = {
+      {StrPrint("%p", &x), "%p"},
+      {StrPrint("%20p", &x), "%20p"},
+      {StrPrint("%.1p", &x), "%.1p"},
+      {StrPrint("%.20p", &x), "%.20p"},
+      {StrPrint("%30.20p", &x), "%30.20p"},
+
+      {StrPrint("%-p", &x), "%-p"},
+      {StrPrint("%-20p", &x), "%-20p"},
+      {StrPrint("%-.1p", &x), "%-.1p"},
+      {StrPrint("%.20p", &x), "%.20p"},
+      {StrPrint("%-30.20p", &x), "%-30.20p"},
+
+      {StrPrint("%p", cp), "%2$p"},   // const char*
+      {"(nil)", "%3$p"},              // null const char *
+      {"(nil)", "%4$p"},              // null const int *
+      {StrPrint("%p", mcp), "%5$p"},  // nonconst char*
+
+      {StrPrint("%p", fp), "%6$p"},   // function pointer
+      {StrPrint("%p", vcp), "%8$p"},  // function pointer
+
+#ifndef __APPLE__
+      // Apple's printf differs here (0x0 vs. nil)
+      {StrPrint("%p", fnil), "%7$p"},   // null function pointer
+      {StrPrint("%p", vcnil), "%9$p"},  // null function pointer
+#endif
+  };
+  for (const Expectation &e : kExpect) {
+    UntypedFormatSpecImpl format(e.fmt);
+    EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args))) << e.fmt;
+  }
+}
+
+struct Cardinal {
+  enum Pos { k1 = 1, k2 = 2, k3 = 3 };
+  enum Neg { kM1 = -1, kM2 = -2, kM3 = -3 };
+};
+
+TEST_F(FormatConvertTest, Enum) {
+  const Cardinal::Pos k3 = Cardinal::k3;
+  const Cardinal::Neg km3 = Cardinal::kM3;
+  const FormatArgImpl args[] = {FormatArgImpl(k3), FormatArgImpl(km3)};
+  UntypedFormatSpecImpl format("%1$d");
+  UntypedFormatSpecImpl format2("%2$d");
+  EXPECT_EQ("3", FormatPack(format, absl::MakeSpan(args)));
+  EXPECT_EQ("-3", FormatPack(format2, absl::MakeSpan(args)));
+}
+
+template <typename T>
+class TypedFormatConvertTest : public FormatConvertTest { };
+
+TYPED_TEST_CASE_P(TypedFormatConvertTest);
+
+std::vector<std::string> AllFlagCombinations() {
+  const char kFlags[] = {'-', '#', '0', '+', ' '};
+  std::vector<std::string> result;
+  for (size_t fsi = 0; fsi < (1ull << ArraySize(kFlags)); ++fsi) {
+    std::string flag_set;
+    for (size_t fi = 0; fi < ArraySize(kFlags); ++fi)
+      if (fsi & (1ull << fi))
+        flag_set += kFlags[fi];
+    result.push_back(flag_set);
+  }
+  return result;
+}
+
+TYPED_TEST_P(TypedFormatConvertTest, AllIntsWithFlags) {
+  typedef TypeParam T;
+  typedef typename std::make_unsigned<T>::type UnsignedT;
+  using remove_volatile_t = typename std::remove_volatile<T>::type;
+  const T kMin = std::numeric_limits<remove_volatile_t>::min();
+  const T kMax = std::numeric_limits<remove_volatile_t>::max();
+  const T kVals[] = {
+      remove_volatile_t(1),
+      remove_volatile_t(2),
+      remove_volatile_t(3),
+      remove_volatile_t(123),
+      remove_volatile_t(-1),
+      remove_volatile_t(-2),
+      remove_volatile_t(-3),
+      remove_volatile_t(-123),
+      remove_volatile_t(0),
+      kMax - remove_volatile_t(1),
+      kMax,
+      kMin + remove_volatile_t(1),
+      kMin,
+  };
+  const char kConvChars[] = {'d', 'i', 'u', 'o', 'x', 'X'};
+  const std::string kWid[] = {"", "4", "10"};
+  const std::string kPrec[] = {"", ".", ".0", ".4", ".10"};
+
+  const std::vector<std::string> flag_sets = AllFlagCombinations();
+
+  for (size_t vi = 0; vi < ArraySize(kVals); ++vi) {
+    const T val = kVals[vi];
+    SCOPED_TRACE(Esc(val));
+    const FormatArgImpl args[] = {FormatArgImpl(val)};
+    for (size_t ci = 0; ci < ArraySize(kConvChars); ++ci) {
+      const char conv_char = kConvChars[ci];
+      for (size_t fsi = 0; fsi < flag_sets.size(); ++fsi) {
+        const std::string &flag_set = flag_sets[fsi];
+        for (size_t wi = 0; wi < ArraySize(kWid); ++wi) {
+          const std::string &wid = kWid[wi];
+          for (size_t pi = 0; pi < ArraySize(kPrec); ++pi) {
+            const std::string &prec = kPrec[pi];
+
+            const bool is_signed_conv = (conv_char == 'd' || conv_char == 'i');
+            const bool is_unsigned_to_signed =
+                !std::is_signed<T>::value && is_signed_conv;
+            // Don't consider sign-related flags '+' and ' ' when doing
+            // unsigned to signed conversions.
+            if (is_unsigned_to_signed &&
+                flag_set.find_first_of("+ ") != std::string::npos) {
+              continue;
+            }
+
+            std::string new_fmt("%");
+            new_fmt += flag_set;
+            new_fmt += wid;
+            new_fmt += prec;
+            // old and new always agree up to here.
+            std::string old_fmt = new_fmt;
+            new_fmt += conv_char;
+            std::string old_result;
+            if (is_unsigned_to_signed) {
+              // don't expect agreement on unsigned formatted as signed,
+              // as printf can't do that conversion properly. For those
+              // cases, we do expect agreement with printf with a "%u"
+              // and the unsigned equivalent of 'val'.
+              UnsignedT uval = val;
+              old_fmt += LengthModFor(uval);
+              old_fmt += "u";
+              old_result = StrPrint(old_fmt.c_str(), uval);
+            } else {
+              old_fmt += LengthModFor(val);
+              old_fmt += conv_char;
+              old_result = StrPrint(old_fmt.c_str(), val);
+            }
+
+            SCOPED_TRACE(std::string() + " old_fmt: \"" + old_fmt +
+                         "\"'"
+                         " new_fmt: \"" +
+                         new_fmt + "\"");
+            UntypedFormatSpecImpl format(new_fmt);
+            EXPECT_EQ(old_result, FormatPack(format, absl::MakeSpan(args)));
+          }
+        }
+      }
+    }
+  }
+}
+
+TYPED_TEST_P(TypedFormatConvertTest, Char) {
+  typedef TypeParam T;
+  using remove_volatile_t = typename std::remove_volatile<T>::type;
+  static const T kMin = std::numeric_limits<remove_volatile_t>::min();
+  static const T kMax = std::numeric_limits<remove_volatile_t>::max();
+  T kVals[] = {
+    remove_volatile_t(1), remove_volatile_t(2), remove_volatile_t(10),
+    remove_volatile_t(-1), remove_volatile_t(-2), remove_volatile_t(-10),
+    remove_volatile_t(0),
+    kMin + remove_volatile_t(1), kMin,
+    kMax - remove_volatile_t(1), kMax
+  };
+  for (const T &c : kVals) {
+    const FormatArgImpl args[] = {FormatArgImpl(c)};
+    UntypedFormatSpecImpl format("%c");
+    EXPECT_EQ(StrPrint("%c", c), FormatPack(format, absl::MakeSpan(args)));
+  }
+}
+
+REGISTER_TYPED_TEST_CASE_P(TypedFormatConvertTest, AllIntsWithFlags, Char);
+
+typedef ::testing::Types<
+    int, unsigned, volatile int,
+    short, unsigned short,
+    long, unsigned long,
+    long long, unsigned long long,
+    signed char, unsigned char, char>
+    AllIntTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(TypedFormatConvertTestWithAllIntTypes,
+                              TypedFormatConvertTest, AllIntTypes);
+TEST_F(FormatConvertTest, Uint128) {
+  absl::uint128 v = static_cast<absl::uint128>(0x1234567890abcdef) * 1979;
+  absl::uint128 max = absl::Uint128Max();
+  const FormatArgImpl args[] = {FormatArgImpl(v), FormatArgImpl(max)};
+
+  struct Case {
+    const char* format;
+    const char* expected;
+  } cases[] = {
+      {"%1$d", "2595989796776606496405"},
+      {"%1$30d", "        2595989796776606496405"},
+      {"%1$-30d", "2595989796776606496405        "},
+      {"%1$u", "2595989796776606496405"},
+      {"%1$x", "8cba9876066020f695"},
+      {"%2$d", "340282366920938463463374607431768211455"},
+      {"%2$u", "340282366920938463463374607431768211455"},
+      {"%2$x", "ffffffffffffffffffffffffffffffff"},
+  };
+
+  for (auto c : cases) {
+    UntypedFormatSpecImpl format(c.format);
+    EXPECT_EQ(c.expected, FormatPack(format, absl::MakeSpan(args)));
+  }
+}
+
+TEST_F(FormatConvertTest, Float) {
+#if _MSC_VER
+  // MSVC has a different rounding policy than us so we can't test our
+  // implementation against the native one there.
+  return;
+#endif  // _MSC_VER
+
+  const char *const kFormats[] = {
+      "%",  "%.3",  "%8.5",   "%9",   "%.60", "%.30",   "%03",    "%+",
+      "% ", "%-10", "%#15.3", "%#.0", "%.0",  "%1$*2$", "%1$.*2$"};
+
+  std::vector<double> doubles = {0.0,
+                                 -0.0,
+                                 .99999999999999,
+                                 99999999999999.,
+                                 std::numeric_limits<double>::max(),
+                                 -std::numeric_limits<double>::max(),
+                                 std::numeric_limits<double>::min(),
+                                 -std::numeric_limits<double>::min(),
+                                 std::numeric_limits<double>::lowest(),
+                                 -std::numeric_limits<double>::lowest(),
+                                 std::numeric_limits<double>::epsilon(),
+                                 std::numeric_limits<double>::epsilon() + 1,
+                                 std::numeric_limits<double>::infinity(),
+                                 -std::numeric_limits<double>::infinity()};
+
+#ifndef __APPLE__
+  // Apple formats NaN differently (+nan) vs. (nan)
+  doubles.push_back(std::nan(""));
+#endif
+
+  // Some regression tests.
+  doubles.push_back(0.99999999999999989);
+
+  if (std::numeric_limits<double>::has_denorm != std::denorm_absent) {
+    doubles.push_back(std::numeric_limits<double>::denorm_min());
+    doubles.push_back(-std::numeric_limits<double>::denorm_min());
+  }
+
+  for (double base :
+       {1., 12., 123., 1234., 12345., 123456., 1234567., 12345678., 123456789.,
+        1234567890., 12345678901., 123456789012., 1234567890123.}) {
+    for (int exp = -123; exp <= 123; ++exp) {
+      for (int sign : {1, -1}) {
+        doubles.push_back(sign * std::ldexp(base, exp));
+      }
+    }
+  }
+
+  for (const char *fmt : kFormats) {
+    for (char f : {'f', 'F',  //
+                   'g', 'G',  //
+                   'a', 'A',  //
+                   'e', 'E'}) {
+      std::string fmt_str = std::string(fmt) + f;
+      for (double d : doubles) {
+        int i = -10;
+        FormatArgImpl args[2] = {FormatArgImpl(d), FormatArgImpl(i)};
+        UntypedFormatSpecImpl format(fmt_str);
+        // We use ASSERT_EQ here because failures are usually correlated and a
+        // bug would print way too many failed expectations causing the test to
+        // time out.
+        ASSERT_EQ(StrPrint(fmt_str.c_str(), d, i),
+                  FormatPack(format, absl::MakeSpan(args)))
+            << fmt_str << " " << StrPrint("%.18g", d) << " "
+            << StrPrint("%.999f", d);
+      }
+    }
+  }
+}
+
+TEST_F(FormatConvertTest, LongDouble) {
+  const char *const kFormats[] = {"%",    "%.3", "%8.5", "%9",
+                                  "%.60", "%+",  "% ",   "%-10"};
+
+  // This value is not representable in double, but it is in long double that
+  // uses the extended format.
+  // This is to verify that we are not truncating the value mistakenly through a
+  // double.
+  long double very_precise = 10000000000000000.25L;
+
+  std::vector<long double> doubles = {
+      0.0,
+      -0.0,
+      very_precise,
+      1 / very_precise,
+      std::numeric_limits<long double>::max(),
+      -std::numeric_limits<long double>::max(),
+      std::numeric_limits<long double>::min(),
+      -std::numeric_limits<long double>::min(),
+      std::numeric_limits<long double>::infinity(),
+      -std::numeric_limits<long double>::infinity()};
+
+  for (const char *fmt : kFormats) {
+    for (char f : {'f', 'F',  //
+                   'g', 'G',  //
+                   'a', 'A',  //
+                   'e', 'E'}) {
+      std::string fmt_str = std::string(fmt) + 'L' + f;
+      for (auto d : doubles) {
+        FormatArgImpl arg(d);
+        UntypedFormatSpecImpl format(fmt_str);
+        // We use ASSERT_EQ here because failures are usually correlated and a
+        // bug would print way too many failed expectations causing the test to
+        // time out.
+        ASSERT_EQ(StrPrint(fmt_str.c_str(), d),
+                  FormatPack(format, {&arg, 1}))
+            << fmt_str << " " << StrPrint("%.18Lg", d) << " "
+            << StrPrint("%.999Lf", d);
+      }
+    }
+  }
+}
+
+TEST_F(FormatConvertTest, IntAsFloat) {
+  const int kMin = std::numeric_limits<int>::min();
+  const int kMax = std::numeric_limits<int>::max();
+  const int ia[] = {
+    1, 2, 3, 123,
+    -1, -2, -3, -123,
+    0, kMax - 1, kMax, kMin + 1, kMin };
+  for (const int fx : ia) {
+    SCOPED_TRACE(fx);
+    const FormatArgImpl args[] = {FormatArgImpl(fx)};
+    struct Expectation {
+      int line;
+      std::string out;
+      const char *fmt;
+    };
+    const double dx = static_cast<double>(fx);
+    const Expectation kExpect[] = {
+      { __LINE__, StrPrint("%f", dx), "%f" },
+      { __LINE__, StrPrint("%12f", dx), "%12f" },
+      { __LINE__, StrPrint("%.12f", dx), "%.12f" },
+      { __LINE__, StrPrint("%12a", dx), "%12a" },
+      { __LINE__, StrPrint("%.12a", dx), "%.12a" },
+    };
+    for (const Expectation &e : kExpect) {
+      SCOPED_TRACE(e.line);
+      SCOPED_TRACE(e.fmt);
+      UntypedFormatSpecImpl format(e.fmt);
+      EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args)));
+    }
+  }
+}
+
+template <typename T>
+bool FormatFails(const char* test_format, T value) {
+  std::string format_string = std::string("<<") + test_format + ">>";
+  UntypedFormatSpecImpl format(format_string);
+
+  int one = 1;
+  const FormatArgImpl args[] = {FormatArgImpl(value), FormatArgImpl(one)};
+  EXPECT_EQ(FormatPack(format, absl::MakeSpan(args)), "")
+      << "format=" << test_format << " value=" << value;
+  return FormatPack(format, absl::MakeSpan(args)).empty();
+}
+
+TEST_F(FormatConvertTest, ExpectedFailures) {
+  // Int input
+  EXPECT_TRUE(FormatFails("%p", 1));
+  EXPECT_TRUE(FormatFails("%s", 1));
+  EXPECT_TRUE(FormatFails("%n", 1));
+
+  // Double input
+  EXPECT_TRUE(FormatFails("%p", 1.));
+  EXPECT_TRUE(FormatFails("%s", 1.));
+  EXPECT_TRUE(FormatFails("%n", 1.));
+  EXPECT_TRUE(FormatFails("%c", 1.));
+  EXPECT_TRUE(FormatFails("%d", 1.));
+  EXPECT_TRUE(FormatFails("%x", 1.));
+  EXPECT_TRUE(FormatFails("%*d", 1.));
+
+  // String input
+  EXPECT_TRUE(FormatFails("%n", ""));
+  EXPECT_TRUE(FormatFails("%c", ""));
+  EXPECT_TRUE(FormatFails("%d", ""));
+  EXPECT_TRUE(FormatFails("%x", ""));
+  EXPECT_TRUE(FormatFails("%f", ""));
+  EXPECT_TRUE(FormatFails("%*d", ""));
+}
+
+}  // namespace
+}  // namespace str_format_internal
+}  // namespace absl
diff --git a/absl/strings/internal/str_format/extension.cc b/absl/strings/internal/str_format/extension.cc
new file mode 100644
index 000000000000..c2174703c3e8
--- /dev/null
+++ b/absl/strings/internal/str_format/extension.cc
@@ -0,0 +1,84 @@
+//
+// 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
+//
+//      http://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/str_format/extension.h"
+
+#include <errno.h>
+#include <algorithm>
+#include <string>
+
+namespace absl {
+namespace str_format_internal {
+namespace {
+// clang-format off
+#define ABSL_LENGTH_MODS_EXPAND_ \
+  X_VAL(h) X_SEP \
+  X_VAL(hh) X_SEP \
+  X_VAL(l) X_SEP \
+  X_VAL(ll) X_SEP \
+  X_VAL(L) X_SEP \
+  X_VAL(j) X_SEP \
+  X_VAL(z) X_SEP \
+  X_VAL(t) X_SEP \
+  X_VAL(q)
+// clang-format on
+}  // namespace
+
+const LengthMod::Spec LengthMod::kSpecs[] = {
+#define X_VAL(id) { LengthMod::id, #id, strlen(#id) }
+#define X_SEP ,
+    ABSL_LENGTH_MODS_EXPAND_, {LengthMod::none, "", 0}
+#undef X_VAL
+#undef X_SEP
+};
+
+const ConversionChar::Spec ConversionChar::kSpecs[] = {
+#define X_VAL(id) { ConversionChar::id, #id[0] }
+#define X_SEP ,
+    ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP),
+    {ConversionChar::none, '\0'},
+#undef X_VAL
+#undef X_SEP
+};
+
+std::string Flags::ToString() const {
+  std::string s;
+  s.append(left     ? "-" : "");
+  s.append(show_pos ? "+" : "");
+  s.append(sign_col ? " " : "");
+  s.append(alt      ? "#" : "");
+  s.append(zero     ? "0" : "");
+  return s;
+}
+
+const size_t LengthMod::kNumValues;
+
+const size_t ConversionChar::kNumValues;
+
+bool FormatSinkImpl::PutPaddedString(string_view v, int w, int p, bool l) {
+  size_t space_remaining = 0;
+  if (w >= 0) space_remaining = w;
+  size_t n = v.size();
+  if (p >= 0) n = std::min(n, static_cast<size_t>(p));
+  string_view shown(v.data(), n);
+  space_remaining = Excess(shown.size(), space_remaining);
+  if (!l) Append(space_remaining, ' ');
+  Append(shown);
+  if (l) Append(space_remaining, ' ');
+  return true;
+}
+
+}  // namespace str_format_internal
+}  // namespace absl
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h
new file mode 100644
index 000000000000..810330b9d71b
--- /dev/null
+++ b/absl/strings/internal/str_format/extension.h
@@ -0,0 +1,406 @@
+//
+// 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
+//
+//      http://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_EXTENSION_H_
+#define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
+
+#include <limits.h>
+#include <cstring>
+#include <ostream>
+
+#include "absl/base/port.h"
+#include "absl/strings/internal/str_format/output.h"
+#include "absl/strings/string_view.h"
+
+class Cord;
+
+namespace absl {
+
+namespace str_format_internal {
+
+class FormatRawSinkImpl {
+ public:
+  // Implicitly convert from any type that provides the hook function as
+  // described above.
+  template <typename T, decltype(str_format_internal::InvokeFlush(
+                            std::declval<T*>(), string_view()))* = nullptr>
+  FormatRawSinkImpl(T* raw)  // NOLINT
+      : sink_(raw), write_(&FormatRawSinkImpl::Flush<T>) {}
+
+  void Write(string_view s) { write_(sink_, s); }
+
+  template <typename T>
+  static FormatRawSinkImpl Extract(T s) {
+    return s.sink_;
+  }
+
+ private:
+  template <typename T>
+  static void Flush(void* r, string_view s) {
+    str_format_internal::InvokeFlush(static_cast<T*>(r), s);
+  }
+
+  void* sink_;
+  void (*write_)(void*, string_view);
+};
+
+// An abstraction to which conversions write their std::string data.
+class FormatSinkImpl {
+ public:
+  explicit FormatSinkImpl(FormatRawSinkImpl raw) : raw_(raw) {}
+
+  ~FormatSinkImpl() { Flush(); }
+
+  void Flush() {
+    raw_.Write(string_view(buf_, pos_ - buf_));
+    pos_ = buf_;
+  }
+
+  void Append(size_t n, char c) {
+    if (n == 0) return;
+    size_ += n;
+    auto raw_append = [&](size_t count) {
+      memset(pos_, c, count);
+      pos_ += count;
+    };
+    while (n > Avail()) {
+      n -= Avail();
+      if (Avail() > 0) {
+        raw_append(Avail());
+      }
+      Flush();
+    }
+    raw_append(n);
+  }
+
+  void Append(string_view v) {
+    size_t n = v.size();
+    if (n == 0) return;
+    size_ += n;
+    if (n >= Avail()) {
+      Flush();
+      raw_.Write(v);
+      return;
+    }
+    memcpy(pos_, v.data(), n);
+    pos_ += n;
+  }
+
+  size_t size() const { return size_; }
+
+  // Put 'v' to 'sink' with specified width, precision, and left flag.
+  bool PutPaddedString(string_view v, int w, int p, bool l);
+
+  template <typename T>
+  T Wrap() {
+    return T(this);
+  }
+
+  template <typename T>
+  static FormatSinkImpl* Extract(T* s) {
+    return s->sink_;
+  }
+
+ private:
+  size_t Avail() const { return buf_ + sizeof(buf_) - pos_; }
+
+  FormatRawSinkImpl raw_;
+  size_t size_ = 0;
+  char* pos_ = buf_;
+  char buf_[1024];
+};
+
+struct Flags {
+  bool basic : 1;     // fastest conversion: no flags, width, or precision
+  bool left : 1;      // "-"
+  bool show_pos : 1;  // "+"
+  bool sign_col : 1;  // " "
+  bool alt : 1;       // "#"
+  bool zero : 1;      // "0"
+  std::string ToString() const;
+  friend std::ostream& operator<<(std::ostream& os, const Flags& v) {
+    return os << v.ToString();
+  }
+};
+
+struct LengthMod {
+ public:
+  enum Id : uint8_t {
+    h, hh, l, ll, L, j, z, t, q, none
+  };
+  static const size_t kNumValues = none + 1;
+
+  LengthMod() : id_(none) {}
+
+  // Index into the opaque array of LengthMod enums.
+  // Requires: i < kNumValues
+  static LengthMod FromIndex(size_t i) {
+    return LengthMod(kSpecs[i].value);
+  }
+
+  static LengthMod FromId(Id id) { return LengthMod(id); }
+
+  // The length modifier std::string associated with a specified LengthMod.
+  string_view name() const {
+    const Spec& spec = kSpecs[id_];
+    return {spec.name, spec.name_length};
+  }
+
+  Id id() const { return id_; }
+
+  friend bool operator==(const LengthMod& a, const LengthMod& b) {
+    return a.id() == b.id();
+  }
+  friend bool operator!=(const LengthMod& a, const LengthMod& b) {
+    return !(a == b);
+  }
+  friend std::ostream& operator<<(std::ostream& os, const LengthMod& v) {
+    return os << v.name();
+  }
+
+ private:
+  struct Spec {
+    Id value;
+    const char *name;
+    size_t name_length;
+  };
+  static const Spec kSpecs[];
+
+  explicit LengthMod(Id id) : id_(id) {}
+
+  Id id_;
+};
+
+// clang-format off
+#define ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \
+  /* text */ \
+  X_VAL(c) X_SEP X_VAL(C) X_SEP X_VAL(s) X_SEP X_VAL(S) X_SEP \
+  /* ints */ \
+  X_VAL(d) X_SEP X_VAL(i) X_SEP X_VAL(o) X_SEP \
+  X_VAL(u) X_SEP X_VAL(x) X_SEP X_VAL(X) X_SEP \
+  /* floats */ \
+  X_VAL(f) X_SEP X_VAL(F) X_SEP X_VAL(e) X_SEP X_VAL(E) X_SEP \
+  X_VAL(g) X_SEP X_VAL(G) X_SEP X_VAL(a) X_SEP X_VAL(A) X_SEP \
+  /* misc */ \
+  X_VAL(n) X_SEP X_VAL(p)
+// clang-format on
+
+struct ConversionChar {
+ public:
+  enum Id : uint8_t {
+    c, C, s, S,              // text
+    d, i, o, u, x, X,        // int
+    f, F, e, E, g, G, a, A,  // float
+    n, p,                    // misc
+    none
+  };
+  static const size_t kNumValues = none + 1;
+
+  ConversionChar() : id_(none) {}
+
+ public:
+  // Index into the opaque array of ConversionChar enums.
+  // Requires: i < kNumValues
+  static ConversionChar FromIndex(size_t i) {
+    return ConversionChar(kSpecs[i].value);
+  }
+
+  static ConversionChar FromChar(char c) {
+    ConversionChar::Id out_id = ConversionChar::none;
+    switch (c) {
+#define X_VAL(id)                \
+  case #id[0]:                   \
+    out_id = ConversionChar::id; \
+    break;
+      ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, )
+#undef X_VAL
+      default:
+        break;
+    }
+    return ConversionChar(out_id);
+  }
+
+  static ConversionChar FromId(Id id) { return ConversionChar(id); }
+  Id id() const { return id_; }
+
+  int radix() const {
+    switch (id()) {
+      case x: case X: case a: case A: case p: return 16;
+      case o: return 8;
+      default: return 10;
+    }
+  }
+
+  bool upper() const {
+    switch (id()) {
+      case X: case F: case E: case G: case A: return true;
+      default: return false;
+    }
+  }
+
+  bool is_signed() const {
+    switch (id()) {
+      case d: case i: return true;
+      default: return false;
+    }
+  }
+
+  bool is_integral() const {
+    switch (id()) {
+      case d: case i: case u: case o: case x: case X:
+        return true;
+      default: return false;
+    }
+  }
+
+  bool is_float() const {
+    switch (id()) {
+      case a: case e: case f: case g: case A: case E: case F: case G:
+        return true;
+      default: return false;
+    }
+  }
+
+  bool IsValid() const { return id() != none; }
+
+  // The associated char.
+  char Char() const { return kSpecs[id_].name; }
+
+  friend bool operator==(const ConversionChar& a, const ConversionChar& b) {
+    return a.id() == b.id();
+  }
+  friend bool operator!=(const ConversionChar& a, const ConversionChar& b) {
+    return !(a == b);
+  }
+  friend std::ostream& operator<<(std::ostream& os, const ConversionChar& v) {
+    char c = v.Char();
+    if (!c) c = '?';
+    return os << c;
+  }
+
+ private:
+  struct Spec {
+    Id value;
+    char name;
+  };
+  static const Spec kSpecs[];
+
+  explicit ConversionChar(Id id) : id_(id) {}
+
+  Id id_;
+};
+
+class ConversionSpec {
+ public:
+  Flags flags() const { return flags_; }
+  LengthMod length_mod() const { return length_mod_; }
+  ConversionChar conv() const { return conv_; }
+
+  // Returns the specified width. If width is unspecfied, it returns a negative
+  // value.
+  int width() const { return width_; }
+  // Returns the specified precision. If precision is unspecfied, it returns a
+  // negative value.
+  int precision() const { return precision_; }
+
+  void set_flags(Flags f) { flags_ = f; }
+  void set_length_mod(LengthMod lm) { length_mod_ = lm; }
+  void set_conv(ConversionChar c) { conv_ = c; }
+  void set_width(int w) { width_ = w; }
+  void set_precision(int p) { precision_ = p; }
+  void set_left(bool b) { flags_.left = b; }
+
+ private:
+  Flags flags_;
+  LengthMod length_mod_;
+  ConversionChar conv_;
+  int width_;
+  int precision_;
+};
+
+constexpr uint64_t ConversionCharToConvValue(char conv) {
+  return
+#define CONV_SET_CASE(c) \
+  conv == #c[0] ? (uint64_t{1} << (1 + ConversionChar::Id::c)):
+      ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, )
+#undef CONV_SET_CASE
+                  conv == '*'
+          ? 1
+          : 0;
+}
+
+enum class Conv : uint64_t {
+#define CONV_SET_CASE(c) c = ConversionCharToConvValue(#c[0]),
+  ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, )
+#undef CONV_SET_CASE
+
+  // Used for width/precision '*' specification.
+  star = ConversionCharToConvValue('*'),
+
+  // Some predefined values:
+  integral = d | i | u | o | x | X,
+  floating = a | e | f | g | A | E | F | G,
+  numeric = integral | floating,
+  string = s,  // absl:ignore(std::string)
+  pointer = p
+};
+
+// Type safe OR operator.
+// We need this for two reasons:
+//  1. operator| on enums makes them decay to integers and the result is an
+//     integer. We need the result to stay as an enum.
+//  2. We use "enum class" which would not work even if we accepted the decay.
+constexpr Conv operator|(Conv a, Conv b) {
+  return Conv(static_cast<uint64_t>(a) | static_cast<uint64_t>(b));
+}
+
+// Get a conversion with a single character in it.
+constexpr Conv ConversionCharToConv(char c) {
+  return Conv(ConversionCharToConvValue(c));
+}
+
+// Checks whether `c` exists in `set`.
+constexpr bool Contains(Conv set, char c) {
+  return (static_cast<uint64_t>(set) & ConversionCharToConvValue(c)) != 0;
+}
+
+// Checks whether all the characters in `c` are contained in `set`
+constexpr bool Contains(Conv set, Conv c) {
+  return (static_cast<uint64_t>(set) & static_cast<uint64_t>(c)) ==
+         static_cast<uint64_t>(c);
+}
+
+// Return type of the AbslFormatConvert() functions.
+// The Conv template parameter is used to inform the framework of what
+// conversion characters are supported by that AbslFormatConvert routine.
+template <Conv C>
+struct ConvertResult {
+  static constexpr Conv kConv = C;
+  bool value;
+};
+template <Conv C>
+constexpr Conv ConvertResult<C>::kConv;
+
+// Return capacity - used, clipped to a minimum of 0.
+inline size_t Excess(size_t used, size_t capacity) {
+  return used < capacity ? capacity - used : 0;
+}
+
+}  // namespace str_format_internal
+
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_STR_FORMAT_EXTENSION_H_
diff --git a/absl/strings/internal/str_format/extension_test.cc b/absl/strings/internal/str_format/extension_test.cc
new file mode 100644
index 000000000000..224fc923d3e3
--- /dev/null
+++ b/absl/strings/internal/str_format/extension_test.cc
@@ -0,0 +1,65 @@
+//
+// 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
+//
+//      http://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/str_format/extension.h"
+
+#include <random>
+#include <string>
+#include "absl/strings/str_format.h"
+
+#include "gtest/gtest.h"
+
+namespace {
+
+std::string MakeRandomString(size_t len) {
+  std::random_device rd;
+  std::mt19937 gen(rd());
+  std::uniform_int_distribution<> dis('a', 'z');
+  std::string s(len, '0');
+  for (char& c : s) {
+    c = dis(gen);
+  }
+  return s;
+}
+
+TEST(FormatExtensionTest, SinkAppendSubstring) {
+  for (size_t chunk_size : {1, 10, 100, 1000, 10000}) {
+    std::string expected, actual;
+    absl::str_format_internal::FormatSinkImpl sink(&actual);
+    for (size_t chunks = 0; chunks < 10; ++chunks) {
+      std::string rand = MakeRandomString(chunk_size);
+      expected += rand;
+      sink.Append(rand);
+    }
+    sink.Flush();
+    EXPECT_EQ(actual, expected);
+  }
+}
+
+TEST(FormatExtensionTest, SinkAppendChars) {
+  for (size_t chunk_size : {1, 10, 100, 1000, 10000}) {
+    std::string expected, actual;
+    absl::str_format_internal::FormatSinkImpl sink(&actual);
+    for (size_t chunks = 0; chunks < 10; ++chunks) {
+      std::string rand = MakeRandomString(1);
+      expected.append(chunk_size, rand[0]);
+      sink.Append(chunk_size, rand[0]);
+    }
+    sink.Flush();
+    EXPECT_EQ(actual, expected);
+  }
+}
+}  // namespace
diff --git a/absl/strings/internal/str_format/float_conversion.cc b/absl/strings/internal/str_format/float_conversion.cc
new file mode 100644
index 000000000000..37952b4699e9
--- /dev/null
+++ b/absl/strings/internal/str_format/float_conversion.cc
@@ -0,0 +1,476 @@
+#include "absl/strings/internal/str_format/float_conversion.h"
+
+#include <string.h>
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <string>
+
+namespace absl {
+namespace str_format_internal {
+
+namespace {
+
+char *CopyStringTo(string_view v, char *out) {
+  std::memcpy(out, v.data(), v.size());
+  return out + v.size();
+}
+
+template <typename Float>
+bool FallbackToSnprintf(const Float v, const ConversionSpec &conv,
+                        FormatSinkImpl *sink) {
+  int w = conv.width() >= 0 ? conv.width() : 0;
+  int p = conv.precision() >= 0 ? conv.precision() : -1;
+  char fmt[32];
+  {
+    char *fp = fmt;
+    *fp++ = '%';
+    fp = CopyStringTo(conv.flags().ToString(), fp);
+    fp = CopyStringTo("*.*", fp);
+    if (std::is_same<long double, Float>()) {
+      *fp++ = 'L';
+    }
+    *fp++ = conv.conv().Char();
+    *fp = 0;
+    assert(fp < fmt + sizeof(fmt));
+  }
+  std::string space(512, '\0');
+  string_view result;
+  while (true) {
+    int n = snprintf(&space[0], space.size(), fmt, w, p, v);
+    if (n < 0) return false;
+    if (static_cast<size_t>(n) < space.size()) {
+      result = string_view(space.data(), n);
+      break;
+    }
+    space.resize(n + 1);
+  }
+  sink->Append(result);
+  return true;
+}
+
+// 128-bits in decimal: ceil(128*log(2)/log(10))
+//   or std::numeric_limits<__uint128_t>::digits10
+constexpr int kMaxFixedPrecision = 39;
+
+constexpr int kBufferLength = /*sign*/ 1 +
+                              /*integer*/ kMaxFixedPrecision +
+                              /*point*/ 1 +
+                              /*fraction*/ kMaxFixedPrecision +
+                              /*exponent e+123*/ 5;
+
+struct Buffer {
+  void push_front(char c) {
+    assert(begin > data);
+    *--begin = c;
+  }
+  void push_back(char c) {
+    assert(end < data + sizeof(data));
+    *end++ = c;
+  }
+  void pop_back() {
+    assert(begin < end);
+    --end;
+  }
+
+  char &back() {
+    assert(begin < end);
+    return end[-1];
+  }
+
+  char last_digit() const { return end[-1] == '.' ? end[-2] : end[-1]; }
+
+  int size() const { return static_cast<int>(end - begin); }
+
+  char data[kBufferLength];
+  char *begin;
+  char *end;
+};
+
+enum class FormatStyle { Fixed, Precision };
+
+// If the value is Inf or Nan, print it and return true.
+// Otherwise, return false.
+template <typename Float>
+bool ConvertNonNumericFloats(char sign_char, Float v,
+                             const ConversionSpec &conv, FormatSinkImpl *sink) {
+  char text[4], *ptr = text;
+  if (sign_char) *ptr++ = sign_char;
+  if (std::isnan(v)) {
+    ptr = std::copy_n(conv.conv().upper() ? "NAN" : "nan", 3, ptr);
+  } else if (std::isinf(v)) {
+    ptr = std::copy_n(conv.conv().upper() ? "INF" : "inf", 3, ptr);
+  } else {
+    return false;
+  }
+
+  return sink->PutPaddedString(string_view(text, ptr - text), conv.width(), -1,
+                               conv.flags().left);
+}
+
+// Round up the last digit of the value.
+// It will carry over and potentially overflow. 'exp' will be adjusted in that
+// case.
+template <FormatStyle mode>
+void RoundUp(Buffer *buffer, int *exp) {
+  char *p = &buffer->back();
+  while (p >= buffer->begin && (*p == '9' || *p == '.')) {
+    if (*p == '9') *p = '0';
+    --p;
+  }
+
+  if (p < buffer->begin) {
+    *p = '1';
+    buffer->begin = p;
+    if (mode == FormatStyle::Precision) {
+      std::swap(p[1], p[2]);  // move the .
+      ++*exp;
+      buffer->pop_back();
+    }
+  } else {
+    ++*p;
+  }
+}
+
+void PrintExponent(int exp, char e, Buffer *out) {
+  out->push_back(e);
+  if (exp < 0) {
+    out->push_back('-');
+    exp = -exp;
+  } else {
+    out->push_back('+');
+  }
+  // Exponent digits.
+  if (exp > 99) {
+    out->push_back(exp / 100 + '0');
+    out->push_back(exp / 10 % 10 + '0');
+    out->push_back(exp % 10 + '0');
+  } else {
+    out->push_back(exp / 10 + '0');
+    out->push_back(exp % 10 + '0');
+  }
+}
+
+template <typename Float, typename Int>
+constexpr bool CanFitMantissa() {
+  return std::numeric_limits<Float>::digits <= std::numeric_limits<Int>::digits;
+}
+
+template <typename Float>
+struct Decomposed {
+  Float mantissa;
+  int exponent;
+};
+
+// Decompose the double into an integer mantissa and an exponent.
+template <typename Float>
+Decomposed<Float> Decompose(Float v) {
+  int exp;
+  Float m = std::frexp(v, &exp);
+  m = std::ldexp(m, std::numeric_limits<Float>::digits);
+  exp -= std::numeric_limits<Float>::digits;
+  return {m, exp};
+}
+
+// Print 'digits' as decimal.
+// In Fixed mode, we add a '.' at the end.
+// In Precision mode, we add a '.' after the first digit.
+template <FormatStyle mode, typename Int>
+int PrintIntegralDigits(Int digits, Buffer *out) {
+  int printed = 0;
+  if (digits) {
+    for (; digits; digits /= 10) out->push_front(digits % 10 + '0');
+    printed = out->size();
+    if (mode == FormatStyle::Precision) {
+      out->push_front(*out->begin);
+      out->begin[1] = '.';
+    } else {
+      out->push_back('.');
+    }
+  } else if (mode == FormatStyle::Fixed) {
+    out->push_front('0');
+    out->push_back('.');
+    printed = 1;
+  }
+  return printed;
+}
+
+// Back out 'extra_digits' digits and round up if necessary.
+bool RemoveExtraPrecision(int extra_digits, bool has_leftover_value,
+                          Buffer *out, int *exp_out) {
+  if (extra_digits <= 0) return false;
+
+  // Back out the extra digits
+  out->end -= extra_digits;
+
+  bool needs_to_round_up = [&] {
+    // We look at the digit just past the end.
+    // There must be 'extra_digits' extra valid digits after end.
+    if (*out->end > '5') return true;
+    if (*out->end < '5') return false;
+    if (has_leftover_value || std::any_of(out->end + 1, out->end + extra_digits,
+                                          [](char c) { return c != '0'; }))
+      return true;
+
+    // Ends in ...50*, round to even.
+    return out->last_digit() % 2 == 1;
+  }();
+
+  if (needs_to_round_up) {
+    RoundUp<FormatStyle::Precision>(out, exp_out);
+  }
+  return true;
+}
+
+// Print the value into the buffer.
+// This will not include the exponent, which will be returned in 'exp_out' for
+// Precision mode.
+template <typename Int, typename Float, FormatStyle mode>
+bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out,
+                       int *exp_out) {
+  assert((CanFitMantissa<Float, Int>()));
+
+  const int int_bits = std::numeric_limits<Int>::digits;
+
+  // In precision mode, we start printing one char to the right because it will
+  // also include the '.'
+  // In fixed mode we put the dot afterwards on the right.
+  out->begin = out->end =
+      out->data + 1 + kMaxFixedPrecision + (mode == FormatStyle::Precision);
+
+  if (exp >= 0) {
+    if (std::numeric_limits<Float>::digits + exp > int_bits) {
+      // The value will overflow the Int
+      return false;
+    }
+    int digits_printed = PrintIntegralDigits<mode>(int_mantissa << exp, out);
+    int digits_to_zero_pad = precision;
+    if (mode == FormatStyle::Precision) {
+      *exp_out = digits_printed - 1;
+      digits_to_zero_pad -= digits_printed - 1;
+      if (RemoveExtraPrecision(-digits_to_zero_pad, false, out, exp_out)) {
+        return true;
+      }
+    }
+    for (; digits_to_zero_pad-- > 0;) out->push_back('0');
+    return true;
+  }
+
+  exp = -exp;
+  // We need at least 4 empty bits for the next decimal digit.
+  // We will multiply by 10.
+  if (exp > int_bits - 4) return false;
+
+  const Int mask = (Int{1} << exp) - 1;
+
+  // Print the integral part first.
+  int digits_printed = PrintIntegralDigits<mode>(int_mantissa >> exp, out);
+  int_mantissa &= mask;
+
+  int fractional_count = precision;
+  if (mode == FormatStyle::Precision) {
+    if (digits_printed == 0) {
+      // Find the first non-zero digit, when in Precision mode.
+      *exp_out = 0;
+      if (int_mantissa) {
+        while (int_mantissa <= mask) {
+          int_mantissa *= 10;
+          --*exp_out;
+        }
+      }
+      out->push_front(static_cast<char>(int_mantissa >> exp) + '0');
+      out->push_back('.');
+      int_mantissa &= mask;
+    } else {
+      // We already have a digit, and a '.'
+      *exp_out = digits_printed - 1;
+      fractional_count -= *exp_out;
+      if (RemoveExtraPrecision(-fractional_count, int_mantissa != 0, out,
+                               exp_out)) {
+        // If we had enough digits, return right away.
+        // The code below will try to round again otherwise.
+        return true;
+      }
+    }
+  }
+
+  auto get_next_digit = [&] {
+    int_mantissa *= 10;
+    int digit = static_cast<int>(int_mantissa >> exp);
+    int_mantissa &= mask;
+    return digit;
+  };
+
+  // Print fractional_count more digits, if available.
+  for (; fractional_count > 0; --fractional_count) {
+    out->push_back(get_next_digit() + '0');
+  }
+
+  int next_digit = get_next_digit();
+  if (next_digit > 5 ||
+      (next_digit == 5 && (int_mantissa || out->last_digit() % 2 == 1))) {
+    RoundUp<mode>(out, exp_out);
+  }
+
+  return true;
+}
+
+template <FormatStyle mode, typename Float>
+bool FloatToBuffer(Decomposed<Float> decomposed, int precision, Buffer *out,
+                   int *exp) {
+  if (precision > kMaxFixedPrecision) return false;
+
+  // Try with uint64_t.
+  if (CanFitMantissa<Float, std::uint64_t>() &&
+      FloatToBufferImpl<std::uint64_t, Float, mode>(
+          static_cast<std::uint64_t>(decomposed.mantissa),
+          static_cast<std::uint64_t>(decomposed.exponent), precision, out, exp))
+    return true;
+
+#if defined(__SIZEOF_INT128__)
+  // If that is not enough, try with __uint128_t.
+  return CanFitMantissa<Float, __uint128_t>() &&
+         FloatToBufferImpl<__uint128_t, Float, mode>(
+             static_cast<__uint128_t>(decomposed.mantissa),
+             static_cast<__uint128_t>(decomposed.exponent), precision, out,
+             exp);
+#endif
+  return false;
+}
+
+void WriteBufferToSink(char sign_char, string_view str,
+                       const ConversionSpec &conv, FormatSinkImpl *sink) {
+  int left_spaces = 0, zeros = 0, right_spaces = 0;
+  int missing_chars =
+      conv.width() >= 0 ? std::max(conv.width() - static_cast<int>(str.size()) -
+                                       static_cast<int>(sign_char != 0),
+                                   0)
+                        : 0;
+  if (conv.flags().left) {
+    right_spaces = missing_chars;
+  } else if (conv.flags().zero) {
+    zeros = missing_chars;
+  } else {
+    left_spaces = missing_chars;
+  }
+
+  sink->Append(left_spaces, ' ');
+  if (sign_char) sink->Append(1, sign_char);
+  sink->Append(zeros, '0');
+  sink->Append(str);
+  sink->Append(right_spaces, ' ');
+}
+
+template <typename Float>
+bool FloatToSink(const Float v, const ConversionSpec &conv,
+                 FormatSinkImpl *sink) {
+  // Print the sign or the sign column.
+  Float abs_v = v;
+  char sign_char = 0;
+  if (std::signbit(abs_v)) {
+    sign_char = '-';
+    abs_v = -abs_v;
+  } else if (conv.flags().show_pos) {
+    sign_char = '+';
+  } else if (conv.flags().sign_col) {
+    sign_char = ' ';
+  }
+
+  // Print nan/inf.
+  if (ConvertNonNumericFloats(sign_char, abs_v, conv, sink)) {
+    return true;
+  }
+
+  int precision = conv.precision() < 0 ? 6 : conv.precision();
+
+  int exp = 0;
+
+  auto decomposed = Decompose(abs_v);
+
+  Buffer buffer;
+
+  switch (conv.conv().id()) {
+    case ConversionChar::f:
+    case ConversionChar::F:
+      if (!FloatToBuffer<FormatStyle::Fixed>(decomposed, precision, &buffer,
+                                             nullptr)) {
+        return FallbackToSnprintf(v, conv, sink);
+      }
+      if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back();
+      break;
+
+    case ConversionChar::e:
+    case ConversionChar::E:
+      if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer,
+                                                 &exp)) {
+        return FallbackToSnprintf(v, conv, sink);
+      }
+      if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back();
+      PrintExponent(exp, conv.conv().upper() ? 'E' : 'e', &buffer);
+      break;
+
+    case ConversionChar::g:
+    case ConversionChar::G:
+      precision = std::max(0, precision - 1);
+      if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer,
+                                                 &exp)) {
+        return FallbackToSnprintf(v, conv, sink);
+      }
+      if (precision + 1 > exp && exp >= -4) {
+        if (exp < 0) {
+          // Have 1.23456, needs 0.00123456
+          // Move the first digit
+          buffer.begin[1] = *buffer.begin;
+          // Add some zeros
+          for (; exp < -1; ++exp) *buffer.begin-- = '0';
+          *buffer.begin-- = '.';
+          *buffer.begin = '0';
+        } else if (exp > 0) {
+          // Have 1.23456, needs 1234.56
+          // Move the '.' exp positions to the right.
+          std::rotate(buffer.begin + 1, buffer.begin + 2,
+                      buffer.begin + exp + 2);
+        }
+        exp = 0;
+      }
+      if (!conv.flags().alt) {
+        while (buffer.back() == '0') buffer.pop_back();
+        if (buffer.back() == '.') buffer.pop_back();
+      }
+      if (exp) PrintExponent(exp, conv.conv().upper() ? 'E' : 'e', &buffer);
+      break;
+
+    case ConversionChar::a:
+    case ConversionChar::A:
+      return FallbackToSnprintf(v, conv, sink);
+
+    default:
+      return false;
+  }
+
+  WriteBufferToSink(sign_char,
+                    string_view(buffer.begin, buffer.end - buffer.begin), conv,
+                    sink);
+
+  return true;
+}
+
+}  // namespace
+
+bool ConvertFloatImpl(long double v, const ConversionSpec &conv,
+                      FormatSinkImpl *sink) {
+  return FloatToSink(v, conv, sink);
+}
+
+bool ConvertFloatImpl(float v, const ConversionSpec &conv,
+                      FormatSinkImpl *sink) {
+  return FloatToSink(v, conv, sink);
+}
+
+bool ConvertFloatImpl(double v, const ConversionSpec &conv,
+                      FormatSinkImpl *sink) {
+  return FloatToSink(v, conv, sink);
+}
+
+}  // namespace str_format_internal
+}  // namespace absl
diff --git a/absl/strings/internal/str_format/float_conversion.h b/absl/strings/internal/str_format/float_conversion.h
new file mode 100644
index 000000000000..8ba5566d3eef
--- /dev/null
+++ b/absl/strings/internal/str_format/float_conversion.h
@@ -0,0 +1,21 @@
+#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_
+#define ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_
+
+#include "absl/strings/internal/str_format/extension.h"
+
+namespace absl {
+namespace str_format_internal {
+
+bool ConvertFloatImpl(float v, const ConversionSpec &conv,
+                      FormatSinkImpl *sink);
+
+bool ConvertFloatImpl(double v, const ConversionSpec &conv,
+                      FormatSinkImpl *sink);
+
+bool ConvertFloatImpl(long double v, const ConversionSpec &conv,
+                      FormatSinkImpl *sink);
+
+}  // namespace str_format_internal
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_
diff --git a/absl/strings/internal/str_format/output.cc b/absl/strings/internal/str_format/output.cc
new file mode 100644
index 000000000000..5c3795b737ca
--- /dev/null
+++ b/absl/strings/internal/str_format/output.cc
@@ -0,0 +1,47 @@
+// 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
+//
+//      http://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/str_format/output.h"
+
+#include <errno.h>
+#include <cstring>
+
+namespace absl {
+namespace str_format_internal {
+
+void BufferRawSink::Write(string_view v) {
+  size_t to_write = std::min(v.size(), size_);
+  std::memcpy(buffer_, v.data(), to_write);
+  buffer_ += to_write;
+  size_ -= to_write;
+  total_written_ += v.size();
+}
+
+void FILERawSink::Write(string_view v) {
+  while (!v.empty() && !error_) {
+    if (size_t result = std::fwrite(v.data(), 1, v.size(), output_)) {
+      // Some progress was made.
+      count_ += result;
+      v.remove_prefix(result);
+    } else {
+      // Some error occurred.
+      if (errno != EINTR) {
+        error_ = errno;
+      }
+    }
+  }
+}
+
+}  // namespace str_format_internal
+}  // namespace absl
diff --git a/absl/strings/internal/str_format/output.h b/absl/strings/internal/str_format/output.h
new file mode 100644
index 000000000000..3b0aa5e7157e
--- /dev/null
+++ b/absl/strings/internal/str_format/output.h
@@ -0,0 +1,101 @@
+// 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
+//
+//      http://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.
+//
+// Output extension hooks for the Format library.
+// `internal::InvokeFlush` calls the appropriate flush function for the
+// specified output argument.
+// `BufferRawSink` is a simple output sink for a char buffer. Used by SnprintF.
+// `FILERawSink` is a std::FILE* based sink. Used by PrintF and FprintF.
+
+#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_
+#define ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_
+
+#include <cstdio>
+#include <ostream>
+#include <string>
+
+#include "absl/base/port.h"
+#include "absl/strings/string_view.h"
+
+class Cord;
+
+namespace absl {
+namespace str_format_internal {
+
+// RawSink implementation that writes into a char* buffer.
+// It will not overflow the buffer, but will keep the total count of chars
+// that would have been written.
+class BufferRawSink {
+ public:
+  BufferRawSink(char* buffer, size_t size) : buffer_(buffer), size_(size) {}
+
+  size_t total_written() const { return total_written_; }
+  void Write(string_view v);
+
+ private:
+  char* buffer_;
+  size_t size_;
+  size_t total_written_ = 0;
+};
+
+// RawSink implementation that writes into a FILE*.
+// It keeps track of the total number of bytes written and any error encountered
+// during the writes.
+class FILERawSink {
+ public:
+  explicit FILERawSink(std::FILE* output) : output_(output) {}
+
+  void Write(string_view v);
+
+  size_t count() const { return count_; }
+  int error() const { return error_; }
+
+ private:
+  std::FILE* output_;
+  int error_ = 0;
+  size_t count_ = 0;
+};
+
+// Provide RawSink integration with common types from the STL.
+inline void AbslFormatFlush(std::string* out, string_view s) {
+  out->append(s.begin(), s.size());
+}
+inline void AbslFormatFlush(std::ostream* out, string_view s) {
+  out->write(s.begin(), s.size());
+}
+
+template <class AbslCord, typename = typename std::enable_if<
+                              std::is_same<AbslCord, ::Cord>::value>::type>
+inline void AbslFormatFlush(AbslCord* out, string_view s) {
+  out->Append(s);
+}
+
+inline void AbslFormatFlush(FILERawSink* sink, string_view v) {
+  sink->Write(v);
+}
+
+inline void AbslFormatFlush(BufferRawSink* sink, string_view v) {
+  sink->Write(v);
+}
+
+template <typename T>
+auto InvokeFlush(T* out, string_view s)
+    -> decltype(str_format_internal::AbslFormatFlush(out, s)) {
+  str_format_internal::AbslFormatFlush(out, s);
+}
+
+}  // namespace str_format_internal
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_
diff --git a/absl/strings/internal/str_format/output_test.cc b/absl/strings/internal/str_format/output_test.cc
new file mode 100644
index 000000000000..cc3c615557fe
--- /dev/null
+++ b/absl/strings/internal/str_format/output_test.cc
@@ -0,0 +1,78 @@
+// 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
+//
+//      http://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/str_format/output.h"
+
+#include <sstream>
+#include <string>
+
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace absl {
+namespace {
+
+TEST(InvokeFlush, String) {
+  std::string str = "ABC";
+  str_format_internal::InvokeFlush(&str, "DEF");
+  EXPECT_EQ(str, "ABCDEF");
+
+#if UTIL_FORMAT_HAS_GLOBAL_STRING
+  std::string str2 = "ABC";
+  str_format_internal::InvokeFlush(&str2, "DEF");
+  EXPECT_EQ(str2, "ABCDEF");
+#endif  // UTIL_FORMAT_HAS_GLOBAL_STRING
+}
+
+TEST(InvokeFlush, Stream) {
+  std::stringstream str;
+  str << "ABC";
+  str_format_internal::InvokeFlush(&str, "DEF");
+  EXPECT_EQ(str.str(), "ABCDEF");
+}
+
+TEST(BufferRawSink, Limits) {
+  char buf[16];
+  {
+    std::fill(std::begin(buf), std::end(buf), 'x');
+    str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
+    str_format_internal::InvokeFlush(&bufsink, "Hello World237");
+    EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World237xx");
+  }
+  {
+    std::fill(std::begin(buf), std::end(buf), 'x');
+    str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
+    str_format_internal::InvokeFlush(&bufsink, "Hello World237237");
+    EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World2372x");
+  }
+  {
+    std::fill(std::begin(buf), std::end(buf), 'x');
+    str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
+    str_format_internal::InvokeFlush(&bufsink, "Hello World");
+    str_format_internal::InvokeFlush(&bufsink, "237");
+    EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World237xx");
+  }
+  {
+    std::fill(std::begin(buf), std::end(buf), 'x');
+    str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
+    str_format_internal::InvokeFlush(&bufsink, "Hello World");
+    str_format_internal::InvokeFlush(&bufsink, "237237");
+    EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World2372x");
+  }
+}
+
+}  // namespace
+}  // namespace absl
+
diff --git a/absl/strings/internal/str_format/parser.cc b/absl/strings/internal/str_format/parser.cc
new file mode 100644
index 000000000000..10114f489c01
--- /dev/null
+++ b/absl/strings/internal/str_format/parser.cc
@@ -0,0 +1,294 @@
+#include "absl/strings/internal/str_format/parser.h"
+
+#include <assert.h>
+#include <string.h>
+#include <wchar.h>
+#include <cctype>
+#include <cstdint>
+
+#include <algorithm>
+#include <initializer_list>
+#include <limits>
+#include <ostream>
+#include <string>
+#include <unordered_set>
+
+namespace absl {
+namespace str_format_internal {
+namespace {
+
+bool CheckFastPathSetting(const UnboundConversion& conv) {
+  bool should_be_basic = !conv.flags.left &&      //
+                         !conv.flags.show_pos &&  //
+                         !conv.flags.sign_col &&  //
+                         !conv.flags.alt &&       //
+                         !conv.flags.zero &&      //
+                         (conv.width.value() == -1) &&
+                         (conv.precision.value() == -1);
+  if (should_be_basic != conv.flags.basic) {
+    fprintf(stderr,
+            "basic=%d left=%d show_pos=%d sign_col=%d alt=%d zero=%d "
+            "width=%d precision=%d\n",
+            conv.flags.basic, conv.flags.left, conv.flags.show_pos,
+            conv.flags.sign_col, conv.flags.alt, conv.flags.zero,
+            conv.width.value(), conv.precision.value());
+  }
+  return should_be_basic == conv.flags.basic;
+}
+
+// Keep a single table for all the conversion chars and length modifiers.
+// We invert the length modifiers to make them negative so that we can easily
+// test for them.
+// Everything else is `none`, which is a negative constant.
+using CC = ConversionChar::Id;
+using LM = LengthMod::Id;
+static constexpr std::int8_t none = -128;
+static constexpr std::int8_t kIds[] = {
+    none,   none,   none,   none,  none,   none,  none,  none,   // 00-07
+    none,   none,   none,   none,  none,   none,  none,  none,   // 08-0f
+    none,   none,   none,   none,  none,   none,  none,  none,   // 10-17
+    none,   none,   none,   none,  none,   none,  none,  none,   // 18-1f
+    none,   none,   none,   none,  none,   none,  none,  none,   // 20-27
+    none,   none,   none,   none,  none,   none,  none,  none,   // 28-2f
+    none,   none,   none,   none,  none,   none,  none,  none,   // 30-37
+    none,   none,   none,   none,  none,   none,  none,  none,   // 38-3f
+    none,   CC::A,  none,   CC::C, none,   CC::E, CC::F, CC::G,  // @ABCDEFG
+    none,   none,   none,   none,  ~LM::L, none,  none,  none,   // HIJKLMNO
+    none,   none,   none,   CC::S, none,   none,  none,  none,   // PQRSTUVW
+    CC::X,  none,   none,   none,  none,   none,  none,  none,   // XYZ[\]^_
+    none,   CC::a,  none,   CC::c, CC::d,  CC::e, CC::f, CC::g,  // `abcdefg
+    ~LM::h, CC::i,  ~LM::j, none,  ~LM::l, none,  CC::n, CC::o,  // hijklmno
+    CC::p,  ~LM::q, none,   CC::s, ~LM::t, CC::u, none,  none,   // pqrstuvw
+    CC::x,  none,   ~LM::z, none,  none,   none,  none,  none,   // xyz{|}~!
+    none,   none,   none,   none,  none,   none,  none,  none,   // 80-87
+    none,   none,   none,   none,  none,   none,  none,  none,   // 88-8f
+    none,   none,   none,   none,  none,   none,  none,  none,   // 90-97
+    none,   none,   none,   none,  none,   none,  none,  none,   // 98-9f
+    none,   none,   none,   none,  none,   none,  none,  none,   // a0-a7
+    none,   none,   none,   none,  none,   none,  none,  none,   // a8-af
+    none,   none,   none,   none,  none,   none,  none,  none,   // b0-b7
+    none,   none,   none,   none,  none,   none,  none,  none,   // b8-bf
+    none,   none,   none,   none,  none,   none,  none,  none,   // c0-c7
+    none,   none,   none,   none,  none,   none,  none,  none,   // c8-cf
+    none,   none,   none,   none,  none,   none,  none,  none,   // d0-d7
+    none,   none,   none,   none,  none,   none,  none,  none,   // d8-df
+    none,   none,   none,   none,  none,   none,  none,  none,   // e0-e7
+    none,   none,   none,   none,  none,   none,  none,  none,   // e8-ef
+    none,   none,   none,   none,  none,   none,  none,  none,   // f0-f7
+    none,   none,   none,   none,  none,   none,  none,  none,   // f8-ff
+};
+
+template <bool is_positional>
+bool ConsumeConversion(string_view *src, UnboundConversion *conv,
+                       int *next_arg) {
+  const char *pos = src->begin();
+  const char *const end = src->end();
+  char c;
+  // Read the next char into `c` and update `pos`. Reads '\0' if at end.
+  const auto get_char = [&] { c = pos == end ? '\0' : *pos++; };
+
+  const auto parse_digits = [&] {
+    int digits = c - '0';
+    // We do not want to overflow `digits` so we consume at most digits10-1
+    // 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 - 2;
+    for (get_char(); num_digits && std::isdigit(c); get_char()) {
+      --num_digits;
+      digits = 10 * digits + c - '0';
+    }
+    return digits;
+  };
+
+  if (is_positional) {
+    get_char();
+    if (c < '1' || c > '9') return false;
+    conv->arg_position = parse_digits();
+    assert(conv->arg_position > 0);
+    if (c != '$') return false;
+  }
+
+  get_char();
+
+  // We should start with the basic flag on.
+  assert(conv->flags.basic);
+
+  // Any non alpha character makes this conversion not basic.
+  // This includes flags (-+ #0), width (1-9, *) or precision (.).
+  // All conversion characters and length modifiers are alpha characters.
+  if (c < 'A') {
+    conv->flags.basic = false;
+
+    for (; c <= '0'; get_char()) {
+      switch (c) {
+        case '-':
+          conv->flags.left = true;
+          continue;
+        case '+':
+          conv->flags.show_pos = true;
+          continue;
+        case ' ':
+          conv->flags.sign_col = true;
+          continue;
+        case '#':
+          conv->flags.alt = true;
+          continue;
+        case '0':
+          conv->flags.zero = true;
+          continue;
+      }
+      break;
+    }
+
+    if (c <= '9') {
+      if (c >= '0') {
+        int maybe_width = parse_digits();
+        if (!is_positional && c == '$') {
+          if (*next_arg != 0) return false;
+          // Positional conversion.
+          *next_arg = -1;
+          conv->flags = Flags();
+          conv->flags.basic = true;
+          return ConsumeConversion<true>(src, conv, next_arg);
+        }
+        conv->width.set_value(maybe_width);
+      } else if (c == '*') {
+        get_char();
+        if (is_positional) {
+          if (c < '1' || c > '9') return false;
+          conv->width.set_from_arg(parse_digits());
+          if (c != '$') return false;
+          get_char();
+        } else {
+          conv->width.set_from_arg(++*next_arg);
+        }
+      }
+    }
+
+    if (c == '.') {
+      get_char();
+      if (std::isdigit(c)) {
+        conv->precision.set_value(parse_digits());
+      } else if (c == '*') {
+        get_char();
+        if (is_positional) {
+          if (c < '1' || c > '9') return false;
+          conv->precision.set_from_arg(parse_digits());
+          if (c != '$') return false;
+          get_char();
+        } else {
+          conv->precision.set_from_arg(++*next_arg);
+        }
+      } else {
+        conv->precision.set_value(0);
+      }
+    }
+  }
+
+  std::int8_t id = kIds[static_cast<unsigned char>(c)];
+
+  if (id < 0) {
+    if (id == none) return false;
+
+    // It is a length modifier.
+    using str_format_internal::LengthMod;
+    LengthMod length_mod = LengthMod::FromId(static_cast<LM>(~id));
+    get_char();
+    if (c == 'h' && length_mod.id() == LengthMod::h) {
+      conv->length_mod = LengthMod::FromId(LengthMod::hh);
+      get_char();
+    } else if (c == 'l' && length_mod.id() == LengthMod::l) {
+      conv->length_mod = LengthMod::FromId(LengthMod::ll);
+      get_char();
+    } else {
+      conv->length_mod = length_mod;
+    }
+    id = kIds[static_cast<unsigned char>(c)];
+    if (id < 0) return false;
+  }
+
+  assert(CheckFastPathSetting(*conv));
+  (void)(&CheckFastPathSetting);
+
+  conv->conv = ConversionChar::FromId(static_cast<CC>(id));
+  if (!is_positional) conv->arg_position = ++*next_arg;
+  *src = string_view(pos, end - pos);
+  return true;
+}
+
+}  // namespace
+
+bool ConsumeUnboundConversion(string_view *src, UnboundConversion *conv,
+                              int *next_arg) {
+  if (*next_arg < 0) return ConsumeConversion<true>(src, conv, next_arg);
+  return ConsumeConversion<false>(src, conv, next_arg);
+}
+
+struct ParsedFormatBase::ParsedFormatConsumer {
+  explicit ParsedFormatConsumer(ParsedFormatBase *parsedformat)
+      : parsed(parsedformat), data_pos(parsedformat->data_.get()) {}
+
+  bool Append(string_view s) {
+    if (s.empty()) return true;
+
+    size_t text_end = AppendText(s);
+
+    if (!parsed->items_.empty() && !parsed->items_.back().is_conversion) {
+      // Let's extend the existing text run.
+      parsed->items_.back().text_end = text_end;
+    } else {
+      // Let's make a new text run.
+      parsed->items_.push_back({false, text_end, {}});
+    }
+    return true;
+  }
+
+  bool ConvertOne(const UnboundConversion &conv, string_view s) {
+    size_t text_end = AppendText(s);
+    parsed->items_.push_back({true, text_end, conv});
+    return true;
+  }
+
+  size_t AppendText(string_view s) {
+    memcpy(data_pos, s.data(), s.size());
+    data_pos += s.size();
+    return static_cast<size_t>(data_pos - parsed->data_.get());
+  }
+
+  ParsedFormatBase *parsed;
+  char* data_pos;
+};
+
+ParsedFormatBase::ParsedFormatBase(string_view format, bool allow_ignored,
+                                   std::initializer_list<Conv> convs)
+    : data_(format.empty() ? nullptr : new char[format.size()]) {
+  has_error_ = !ParseFormatString(format, ParsedFormatConsumer(this)) ||
+               !MatchesConversions(allow_ignored, convs);
+}
+
+bool ParsedFormatBase::MatchesConversions(
+    bool allow_ignored, std::initializer_list<Conv> convs) const {
+  std::unordered_set<int> used;
+  auto add_if_valid_conv = [&](int pos, char c) {
+      if (static_cast<size_t>(pos) > convs.size() ||
+          !Contains(convs.begin()[pos - 1], c))
+        return false;
+      used.insert(pos);
+      return true;
+  };
+  for (const ConversionItem &item : items_) {
+    if (!item.is_conversion) continue;
+    auto &conv = item.conv;
+    if (conv.precision.is_from_arg() &&
+        !add_if_valid_conv(conv.precision.get_from_arg(), '*'))
+      return false;
+    if (conv.width.is_from_arg() &&
+        !add_if_valid_conv(conv.width.get_from_arg(), '*'))
+      return false;
+    if (!add_if_valid_conv(conv.arg_position, conv.conv.Char())) return false;
+  }
+  return used.size() == convs.size() || allow_ignored;
+}
+
+}  // namespace str_format_internal
+}  // namespace absl
diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h
new file mode 100644
index 000000000000..5bebc95540e6
--- /dev/null
+++ b/absl/strings/internal/str_format/parser.h
@@ -0,0 +1,291 @@
+#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
+#define ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
+
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <cassert>
+#include <initializer_list>
+#include <iosfwd>
+#include <iterator>
+#include <memory>
+#include <vector>
+
+#include "absl/strings/internal/str_format/checker.h"
+#include "absl/strings/internal/str_format/extension.h"
+
+namespace absl {
+namespace str_format_internal {
+
+// The analyzed properties of a single specified conversion.
+struct UnboundConversion {
+  UnboundConversion()
+      : flags() /* This is required to zero all the fields of flags. */ {
+    flags.basic = true;
+  }
+
+  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;
+  LengthMod length_mod;
+  ConversionChar conv;
+};
+
+// Consume conversion spec prefix (not including '%') of '*src' if valid.
+// Examples of valid specs would be e.g.: "s", "d", "-12.6f".
+// If valid, the front of src is advanced such that src becomes the
+// part following the conversion spec, and the spec part is broken down and
+// returned in 'conv'.
+// If invalid, returns false and leaves 'src' unmodified.
+// For example:
+//   Given "d9", returns "d", and leaves src="9",
+//   Given "!", returns "" and leaves src="!".
+bool ConsumeUnboundConversion(string_view* src, UnboundConversion* conv,
+                              int* next_arg);
+
+// Parse the format std::string provided in 'src' and pass the identified items into
+// 'consumer'.
+// Text runs will be passed by calling
+//   Consumer::Append(string_view);
+// ConversionItems will be passed by calling
+//   Consumer::ConvertOne(UnboundConversion, string_view);
+// In the case of ConvertOne, the string_view that is passed is the
+// portion of the format std::string corresponding to the conversion, not including
+// the leading %. On success, it returns true. On failure, it stops and returns
+// false.
+template <typename Consumer>
+bool ParseFormatString(string_view src, Consumer consumer) {
+  int next_arg = 0;
+  while (!src.empty()) {
+    const char* percent =
+        static_cast<const char*>(memchr(src.begin(), '%', src.size()));
+    if (!percent) {
+      // We found the last substring.
+      return consumer.Append(src);
+    }
+    // We found a percent, so push the text run then process the percent.
+    size_t percent_loc = percent - src.data();
+    if (!consumer.Append(string_view(src.data(), percent_loc))) return false;
+    if (percent + 1 >= src.end()) return false;
+
+    UnboundConversion conv;
+
+    switch (percent[1]) {
+      case '%':
+        if (!consumer.Append("%")) return false;
+        src.remove_prefix(percent_loc + 2);
+        continue;
+
+#define PARSER_CASE(ch)                                     \
+  case #ch[0]:                                              \
+    src.remove_prefix(percent_loc + 2);                     \
+    conv.conv = ConversionChar::FromId(ConversionChar::ch); \
+    conv.arg_position = ++next_arg;                         \
+    break;
+        ABSL_CONVERSION_CHARS_EXPAND_(PARSER_CASE, );
+#undef PARSER_CASE
+
+      default:
+        src.remove_prefix(percent_loc + 1);
+        if (!ConsumeUnboundConversion(&src, &conv, &next_arg)) return false;
+        break;
+    }
+    if (next_arg == 0) {
+      // This indicates an error in the format std::string.
+      // The only way to get next_arg == 0 is to have a positional argument
+      // first which sets next_arg to -1 and then a non-positional argument
+      // which does ++next_arg.
+      // Checking here seems to be the cheapeast place to do it.
+      return false;
+    }
+    if (!consumer.ConvertOne(
+            conv, string_view(percent + 1, src.data() - (percent + 1)))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+// Always returns true, or fails to compile in a constexpr context if s does not
+// point to a constexpr char array.
+constexpr bool EnsureConstexpr(string_view s) {
+  return s.empty() || s[0] == s[0];
+}
+
+class ParsedFormatBase {
+ public:
+  explicit ParsedFormatBase(string_view format, bool allow_ignored,
+                            std::initializer_list<Conv> convs);
+
+  ParsedFormatBase(const ParsedFormatBase& other) { *this = other; }
+
+  ParsedFormatBase(ParsedFormatBase&& other) { *this = std::move(other); }
+
+  ParsedFormatBase& operator=(const ParsedFormatBase& other) {
+    if (this == &other) return *this;
+    has_error_ = other.has_error_;
+    items_ = other.items_;
+    size_t text_size = items_.empty() ? 0 : items_.back().text_end;
+    data_.reset(new char[text_size]);
+    memcpy(data_.get(), other.data_.get(), text_size);
+    return *this;
+  }
+
+  ParsedFormatBase& operator=(ParsedFormatBase&& other) {
+    if (this == &other) return *this;
+    has_error_ = other.has_error_;
+    data_ = std::move(other.data_);
+    items_ = std::move(other.items_);
+    // Reset the vector to make sure the invariants hold.
+    other.items_.clear();
+    return *this;
+  }
+
+  template <typename Consumer>
+  bool ProcessFormat(Consumer consumer) const {
+    const char* const base = data_.get();
+    string_view text(base, 0);
+    for (const auto& item : items_) {
+      text = string_view(text.end(), (base + item.text_end) - text.end());
+      if (item.is_conversion) {
+        if (!consumer.ConvertOne(item.conv, text)) return false;
+      } else {
+        if (!consumer.Append(text)) return false;
+      }
+    }
+    return !has_error_;
+  }
+
+  bool has_error() const { return has_error_; }
+
+ private:
+  // Returns whether the conversions match and if !allow_ignored it verifies
+  // that all conversions are used by the format.
+  bool MatchesConversions(bool allow_ignored,
+                          std::initializer_list<Conv> convs) const;
+
+  struct ParsedFormatConsumer;
+
+  struct ConversionItem {
+    bool is_conversion;
+    // Points to the past-the-end location of this element in the data_ array.
+    size_t text_end;
+    UnboundConversion conv;
+  };
+
+  bool has_error_;
+  std::unique_ptr<char[]> data_;
+  std::vector<ConversionItem> items_;
+};
+
+
+// A value type representing a preparsed format.  These can be created, copied
+// around, and reused to speed up formatting loops.
+// The user must specify through the template arguments the conversion
+// characters used in the format. This will be checked at compile time.
+//
+// This class uses Conv enum values to specify each argument.
+// This allows for more flexibility as you can specify multiple possible
+// conversion characters for each argument.
+// ParsedFormat<char...> is a simplified alias for when the user only
+// needs to specify a single conversion character for each argument.
+//
+// Example:
+//   // Extended format supports multiple characters per argument:
+//   using MyFormat = ExtendedParsedFormat<Conv::d | Conv::x>;
+//   MyFormat GetFormat(bool use_hex) {
+//     if (use_hex) return MyFormat("foo %x bar");
+//     return MyFormat("foo %d bar");
+//   }
+//   // 'format' can be used with any value that supports 'd' and 'x',
+//   // like `int`.
+//   auto format = GetFormat(use_hex);
+//   value = StringF(format, i);
+//
+// This class also supports runtime format checking with the ::New() and
+// ::NewAllowIgnored() factory functions.
+// This is the only API that allows the user to pass a runtime specified format
+// std::string. These factory functions will return NULL if the format does not match
+// the conversions requested by the user.
+template <str_format_internal::Conv... C>
+class ExtendedParsedFormat : public str_format_internal::ParsedFormatBase {
+ public:
+  explicit ExtendedParsedFormat(string_view format)
+#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+      __attribute__((
+          enable_if(str_format_internal::EnsureConstexpr(format),
+                    "Format std::string is not constexpr."),
+          enable_if(str_format_internal::ValidFormatImpl<C...>(format),
+                    "Format specified does not match the template arguments.")))
+#endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+      : ExtendedParsedFormat(format, false) {
+  }
+
+  // ExtendedParsedFormat factory function.
+  // The user still has to specify the conversion characters, but they will not
+  // be checked at compile time. Instead, it will be checked at runtime.
+  // This delays the checking to runtime, but allows the user to pass
+  // dynamically sourced formats.
+  // It returns NULL if the format does not match the conversion characters.
+  // The user is responsible for checking the return value before using it.
+  //
+  // The 'New' variant will check that all the specified arguments are being
+  // consumed by the format and return NULL if any argument is being ignored.
+  // The 'NewAllowIgnored' variant will not verify this and will allow formats
+  // that ignore arguments.
+  static std::unique_ptr<ExtendedParsedFormat> New(string_view format) {
+    return New(format, false);
+  }
+  static std::unique_ptr<ExtendedParsedFormat> NewAllowIgnored(
+      string_view format) {
+    return New(format, true);
+  }
+
+ private:
+  static std::unique_ptr<ExtendedParsedFormat> New(string_view format,
+                                                   bool allow_ignored) {
+    std::unique_ptr<ExtendedParsedFormat> conv(
+        new ExtendedParsedFormat(format, allow_ignored));
+    if (conv->has_error()) return nullptr;
+    return conv;
+  }
+
+  ExtendedParsedFormat(string_view s, bool allow_ignored)
+      : ParsedFormatBase(s, allow_ignored, {C...}) {}
+};
+}  // namespace str_format_internal
+}  // namespace absl
+
+#endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
diff --git a/absl/strings/internal/str_format/parser_test.cc b/absl/strings/internal/str_format/parser_test.cc
new file mode 100644
index 000000000000..e698020b1aba
--- /dev/null
+++ b/absl/strings/internal/str_format/parser_test.cc
@@ -0,0 +1,379 @@
+#include "absl/strings/internal/str_format/parser.h"
+
+#include <string.h>
+#include "gtest/gtest.h"
+#include "absl/base/macros.h"
+
+namespace absl {
+namespace str_format_internal {
+
+namespace {
+
+TEST(LengthModTest, Names) {
+  struct Expectation {
+    int line;
+    LengthMod::Id id;
+    const char *name;
+  };
+  const Expectation kExpect[] = {
+    {__LINE__, LengthMod::none, ""  },
+    {__LINE__, LengthMod::h,    "h" },
+    {__LINE__, LengthMod::hh,   "hh"},
+    {__LINE__, LengthMod::l,    "l" },
+    {__LINE__, LengthMod::ll,   "ll"},
+    {__LINE__, LengthMod::L,    "L" },
+    {__LINE__, LengthMod::j,    "j" },
+    {__LINE__, LengthMod::z,    "z" },
+    {__LINE__, LengthMod::t,    "t" },
+    {__LINE__, LengthMod::q,    "q" },
+  };
+  EXPECT_EQ(ABSL_ARRAYSIZE(kExpect), LengthMod::kNumValues);
+  for (auto e : kExpect) {
+    SCOPED_TRACE(e.line);
+    LengthMod mod = LengthMod::FromId(e.id);
+    EXPECT_EQ(e.id, mod.id());
+    EXPECT_EQ(e.name, mod.name());
+  }
+}
+
+TEST(ConversionCharTest, Names) {
+  struct Expectation {
+    ConversionChar::Id id;
+    char name;
+  };
+  // clang-format off
+  const Expectation kExpect[] = {
+#define X(c) {ConversionChar::c, #c[0]}
+    X(c), X(C), X(s), X(S),                          // text
+    X(d), X(i), X(o), X(u), X(x), X(X),              // int
+    X(f), X(F), X(e), X(E), X(g), X(G), X(a), X(A),  // float
+    X(n), X(p),                                      // misc
+#undef X
+    {ConversionChar::none, '\0'},
+  };
+  // clang-format on
+  EXPECT_EQ(ABSL_ARRAYSIZE(kExpect), ConversionChar::kNumValues);
+  for (auto e : kExpect) {
+    SCOPED_TRACE(e.name);
+    ConversionChar v = ConversionChar::FromId(e.id);
+    EXPECT_EQ(e.id, v.id());
+    EXPECT_EQ(e.name, v.Char());
+  }
+}
+
+class ConsumeUnboundConversionTest : public ::testing::Test {
+ public:
+  typedef UnboundConversion Props;
+  string_view Consume(string_view* src) {
+    int next = 0;
+    const char* prev_begin = src->begin();
+    o = UnboundConversion();  // refresh
+    ConsumeUnboundConversion(src, &o, &next);
+    return {prev_begin, static_cast<size_t>(src->begin() - prev_begin)};
+  }
+
+  bool Run(const char *fmt, bool force_positional = false) {
+    string_view src = fmt;
+    int next = force_positional ? -1 : 0;
+    o = UnboundConversion();  // refresh
+    return ConsumeUnboundConversion(&src, &o, &next) && src.empty();
+  }
+  UnboundConversion o;
+};
+
+TEST_F(ConsumeUnboundConversionTest, ConsumeSpecification) {
+  struct Expectation {
+    int line;
+    const char *src;
+    const char *out;
+    const char *src_post;
+  };
+  const Expectation kExpect[] = {
+    {__LINE__, "",     "",     ""  },
+    {__LINE__, "b",    "",     "b" },  // 'b' is invalid
+    {__LINE__, "ba",   "",     "ba"},  // 'b' is invalid
+    {__LINE__, "l",    "",     "l" },  // just length mod isn't okay
+    {__LINE__, "d",    "d",    ""  },  // 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__, "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
+  };
+  for (const auto& e : kExpect) {
+    SCOPED_TRACE(e.line);
+    string_view src = e.src;
+    EXPECT_EQ(e.src, src);
+    string_view out = Consume(&src);
+    EXPECT_EQ(e.out, out);
+    EXPECT_EQ(e.src_post, src);
+  }
+}
+
+TEST_F(ConsumeUnboundConversionTest, BasicConversion) {
+  EXPECT_FALSE(Run(""));
+  EXPECT_FALSE(Run("z"));
+
+  EXPECT_FALSE(Run("dd"));  // no excess allowed
+
+  EXPECT_TRUE(Run("d"));
+  EXPECT_EQ('d', o.conv.Char());
+  EXPECT_FALSE(o.width.is_from_arg());
+  EXPECT_LT(o.width.value(), 0);
+  EXPECT_FALSE(o.precision.is_from_arg());
+  EXPECT_LT(o.precision.value(), 0);
+  EXPECT_EQ(1, o.arg_position);
+  EXPECT_EQ(LengthMod::none, o.length_mod.id());
+}
+
+TEST_F(ConsumeUnboundConversionTest, ArgPosition) {
+  EXPECT_TRUE(Run("d"));
+  EXPECT_EQ(1, o.arg_position);
+  EXPECT_TRUE(Run("3$d"));
+  EXPECT_EQ(3, o.arg_position);
+  EXPECT_TRUE(Run("1$d"));
+  EXPECT_EQ(1, o.arg_position);
+  EXPECT_TRUE(Run("1$d", true));
+  EXPECT_EQ(1, o.arg_position);
+  EXPECT_TRUE(Run("123$d"));
+  EXPECT_EQ(123, o.arg_position);
+  EXPECT_TRUE(Run("123$d", true));
+  EXPECT_EQ(123, o.arg_position);
+  EXPECT_TRUE(Run("10$d"));
+  EXPECT_EQ(10, o.arg_position);
+  EXPECT_TRUE(Run("10$d", true));
+  EXPECT_EQ(10, o.arg_position);
+
+  // Position can't be zero.
+  EXPECT_FALSE(Run("0$d"));
+  EXPECT_FALSE(Run("0$d", true));
+  EXPECT_FALSE(Run("1$*0$d"));
+  EXPECT_FALSE(Run("1$.*0$d"));
+
+  // Position can't start with a zero digit at all. That is not a 'decimal'.
+  EXPECT_FALSE(Run("01$p"));
+  EXPECT_FALSE(Run("01$p", true));
+  EXPECT_FALSE(Run("1$*01$p"));
+  EXPECT_FALSE(Run("1$.*01$p"));
+}
+
+TEST_F(ConsumeUnboundConversionTest, WidthAndPrecision) {
+  EXPECT_TRUE(Run("14d"));
+  EXPECT_EQ('d', o.conv.Char());
+  EXPECT_FALSE(o.width.is_from_arg());
+  EXPECT_EQ(14, o.width.value());
+  EXPECT_FALSE(o.precision.is_from_arg());
+  EXPECT_LT(o.precision.value(), 0);
+
+  EXPECT_TRUE(Run("14.d"));
+  EXPECT_FALSE(o.width.is_from_arg());
+  EXPECT_FALSE(o.precision.is_from_arg());
+  EXPECT_EQ(14, o.width.value());
+  EXPECT_EQ(0, o.precision.value());
+
+  EXPECT_TRUE(Run(".d"));
+  EXPECT_FALSE(o.width.is_from_arg());
+  EXPECT_LT(o.width.value(), 0);
+  EXPECT_FALSE(o.precision.is_from_arg());
+  EXPECT_EQ(0, o.precision.value());
+
+  EXPECT_TRUE(Run(".5d"));
+  EXPECT_FALSE(o.width.is_from_arg());
+  EXPECT_LT(o.width.value(), 0);
+  EXPECT_FALSE(o.precision.is_from_arg());
+  EXPECT_EQ(5, o.precision.value());
+
+  EXPECT_TRUE(Run(".0d"));
+  EXPECT_FALSE(o.width.is_from_arg());
+  EXPECT_LT(o.width.value(), 0);
+  EXPECT_FALSE(o.precision.is_from_arg());
+  EXPECT_EQ(0, o.precision.value());
+
+  EXPECT_TRUE(Run("14.5d"));
+  EXPECT_FALSE(o.width.is_from_arg());
+  EXPECT_FALSE(o.precision.is_from_arg());
+  EXPECT_EQ(14, o.width.value());
+  EXPECT_EQ(5, o.precision.value());
+
+  EXPECT_TRUE(Run("*.*d"));
+  EXPECT_TRUE(o.width.is_from_arg());
+  EXPECT_EQ(1, o.width.get_from_arg());
+  EXPECT_TRUE(o.precision.is_from_arg());
+  EXPECT_EQ(2, o.precision.get_from_arg());
+  EXPECT_EQ(3, o.arg_position);
+
+  EXPECT_TRUE(Run("*d"));
+  EXPECT_TRUE(o.width.is_from_arg());
+  EXPECT_EQ(1, o.width.get_from_arg());
+  EXPECT_FALSE(o.precision.is_from_arg());
+  EXPECT_LT(o.precision.value(), 0);
+  EXPECT_EQ(2, o.arg_position);
+
+  EXPECT_TRUE(Run(".*d"));
+  EXPECT_FALSE(o.width.is_from_arg());
+  EXPECT_LT(o.width.value(), 0);
+  EXPECT_TRUE(o.precision.is_from_arg());
+  EXPECT_EQ(1, o.precision.get_from_arg());
+  EXPECT_EQ(2, o.arg_position);
+
+  // mixed implicit and explicit: didn't specify arg position.
+  EXPECT_FALSE(Run("*23$.*34$d"));
+
+  EXPECT_TRUE(Run("12$*23$.*34$d"));
+  EXPECT_EQ(12, o.arg_position);
+  EXPECT_TRUE(o.width.is_from_arg());
+  EXPECT_EQ(23, o.width.get_from_arg());
+  EXPECT_TRUE(o.precision.is_from_arg());
+  EXPECT_EQ(34, o.precision.get_from_arg());
+
+  EXPECT_TRUE(Run("2$*5$.*9$d"));
+  EXPECT_EQ(2, o.arg_position);
+  EXPECT_TRUE(o.width.is_from_arg());
+  EXPECT_EQ(5, o.width.get_from_arg());
+  EXPECT_TRUE(o.precision.is_from_arg());
+  EXPECT_EQ(9, o.precision.get_from_arg());
+
+  EXPECT_FALSE(Run(".*0$d")) << "no arg 0";
+}
+
+TEST_F(ConsumeUnboundConversionTest, Flags) {
+  static const char kAllFlags[] = "-+ #0";
+  static const int kNumFlags = ABSL_ARRAYSIZE(kAllFlags) - 1;
+  for (int rev = 0; rev < 2; ++rev) {
+    for (int i = 0; i < 1 << kNumFlags; ++i) {
+      std::string fmt;
+      for (int k = 0; k < kNumFlags; ++k)
+        if ((i >> k) & 1) fmt += kAllFlags[k];
+      // flag order shouldn't matter
+      if (rev == 1) { std::reverse(fmt.begin(), fmt.end()); }
+      fmt += 'd';
+      SCOPED_TRACE(fmt);
+      EXPECT_TRUE(Run(fmt.c_str()));
+      EXPECT_EQ(fmt.find('-') == std::string::npos, !o.flags.left);
+      EXPECT_EQ(fmt.find('+') == std::string::npos, !o.flags.show_pos);
+      EXPECT_EQ(fmt.find(' ') == std::string::npos, !o.flags.sign_col);
+      EXPECT_EQ(fmt.find('#') == std::string::npos, !o.flags.alt);
+      EXPECT_EQ(fmt.find('0') == std::string::npos, !o.flags.zero);
+    }
+  }
+}
+
+TEST_F(ConsumeUnboundConversionTest, BasicFlag) {
+  // Flag is on
+  for (const char* fmt : {"d", "llx", "G", "1$X"}) {
+    SCOPED_TRACE(fmt);
+    EXPECT_TRUE(Run(fmt));
+    EXPECT_TRUE(o.flags.basic);
+  }
+
+  // Flag is off
+  for (const char* fmt : {"3d", ".llx", "-G", "1$#X"}) {
+    SCOPED_TRACE(fmt);
+    EXPECT_TRUE(Run(fmt));
+    EXPECT_FALSE(o.flags.basic);
+  }
+}
+
+struct SummarizeConsumer {
+  std::string* out;
+  explicit SummarizeConsumer(std::string* out) : out(out) {}
+
+  bool Append(string_view s) {
+    *out += "[" + std::string(s) + "]";
+    return true;
+  }
+
+  bool ConvertOne(const UnboundConversion& conv, string_view s) {
+    *out += "{";
+    *out += std::string(s);
+    *out += ":";
+    *out += std::to_string(conv.arg_position) + "$";
+    if (conv.width.is_from_arg()) {
+      *out += std::to_string(conv.width.get_from_arg()) + "$*";
+    }
+    if (conv.precision.is_from_arg()) {
+      *out += "." + std::to_string(conv.precision.get_from_arg()) + "$*";
+    }
+    *out += conv.conv.Char();
+    *out += "}";
+    return true;
+  }
+};
+
+std::string SummarizeParsedFormat(const ParsedFormatBase& pc) {
+  std::string out;
+  if (!pc.ProcessFormat(SummarizeConsumer(&out))) out += "!";
+  return out;
+}
+
+class ParsedFormatTest : public testing::Test {};
+
+TEST_F(ParsedFormatTest, ValueSemantics) {
+  ParsedFormatBase p1({}, true, {});  // empty format
+  EXPECT_EQ("", SummarizeParsedFormat(p1));
+
+  ParsedFormatBase p2 = p1;  // copy construct (empty)
+  EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p2));
+
+  p1 = ParsedFormatBase("hello%s", true, {Conv::s});  // move assign
+  EXPECT_EQ("[hello]{s:1$s}", SummarizeParsedFormat(p1));
+
+  ParsedFormatBase p3 = p1;  // copy construct (nonempty)
+  EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p3));
+
+  using std::swap;
+  swap(p1, p2);
+  EXPECT_EQ("", SummarizeParsedFormat(p1));
+  EXPECT_EQ("[hello]{s:1$s}", SummarizeParsedFormat(p2));
+  swap(p1, p2);  // undo
+
+  p2 = p1;  // copy assign
+  EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p2));
+}
+
+struct ExpectParse {
+  const char* in;
+  std::initializer_list<Conv> conv_set;
+  const char* out;
+};
+
+TEST_F(ParsedFormatTest, Parsing) {
+  // Parse should be equivalent to that obtained by ConversionParseIterator.
+  // No need to retest the parsing edge cases here.
+  const ExpectParse kExpect[] = {
+      {"", {}, ""},
+      {"ab", {}, "[ab]"},
+      {"a%d", {Conv::d}, "[a]{d:1$d}"},
+      {"a%+d", {Conv::d}, "[a]{+d:1$d}"},
+      {"a% d", {Conv::d}, "[a]{ d:1$d}"},
+      {"a%b %d", {}, "[a]!"},  // stop after error
+  };
+  for (const auto& e : kExpect) {
+    SCOPED_TRACE(e.in);
+    EXPECT_EQ(e.out,
+              SummarizeParsedFormat(ParsedFormatBase(e.in, false, e.conv_set)));
+  }
+}
+
+TEST_F(ParsedFormatTest, ParsingFlagOrder) {
+  const ExpectParse kExpect[] = {
+      {"a%+ 0d", {Conv::d}, "[a]{+ 0d:1$d}"},
+      {"a%+0 d", {Conv::d}, "[a]{+0 d:1$d}"},
+      {"a%0+ d", {Conv::d}, "[a]{0+ d:1$d}"},
+      {"a% +0d", {Conv::d}, "[a]{ +0d:1$d}"},
+      {"a%0 +d", {Conv::d}, "[a]{0 +d:1$d}"},
+      {"a% 0+d", {Conv::d}, "[a]{ 0+d:1$d}"},
+      {"a%+   0+d", {Conv::d}, "[a]{+   0+d:1$d}"},
+  };
+  for (const auto& e : kExpect) {
+    SCOPED_TRACE(e.in);
+    EXPECT_EQ(e.out,
+              SummarizeParsedFormat(ParsedFormatBase(e.in, false, e.conv_set)));
+  }
+}
+
+}  // namespace
+}  // namespace str_format_internal
+}  // namespace absl
diff --git a/absl/strings/internal/str_split_internal.h b/absl/strings/internal/str_split_internal.h
index a1b10f3addcf..9cf0833f4902 100644
--- a/absl/strings/internal/str_split_internal.h
+++ b/absl/strings/internal/str_split_internal.h
@@ -228,14 +228,31 @@ struct IsInitializerList
 // compiled in C++11 will get an error due to ambiguous conversion paths (in
 // C++11 std::vector<T>::operator= is overloaded to take either a std::vector<T>
 // or an std::initializer_list<T>).
+
+template <typename C, bool has_value_type, bool has_mapped_type>
+struct SplitterIsConvertibleToImpl : std::false_type {};
+
+template <typename C>
+struct SplitterIsConvertibleToImpl<C, true, false>
+    : std::is_constructible<typename C::value_type, absl::string_view> {};
+
+template <typename C>
+struct SplitterIsConvertibleToImpl<C, true, true>
+    : absl::conjunction<
+          std::is_constructible<typename C::key_type, absl::string_view>,
+          std::is_constructible<typename C::mapped_type, absl::string_view>> {};
+
 template <typename C>
 struct SplitterIsConvertibleTo
-    : std::enable_if<
+    : SplitterIsConvertibleToImpl<
+          C,
 #ifdef _GLIBCXX_DEBUG
           !IsStrictlyBaseOfAndConvertibleToSTLContainer<C>::value &&
 #endif  // _GLIBCXX_DEBUG
-          !IsInitializerList<C>::value && HasValueType<C>::value &&
-          HasConstIterator<C>::value> {
+              !IsInitializerList<
+                  typename std::remove_reference<C>::type>::value &&
+              HasValueType<C>::value && HasConstIterator<C>::value,
+          HasMappedType<C>::value> {
 };
 
 // This class implements the range that is returned by absl::StrSplit(). This
@@ -281,7 +298,8 @@ class Splitter {
   // An implicit conversion operator that is restricted to only those containers
   // that the splitter is convertible to.
   template <typename Container,
-            typename OnlyIf = typename SplitterIsConvertibleTo<Container>::type>
+            typename = typename std::enable_if<
+                SplitterIsConvertibleTo<Container>::value>::type>
   operator Container() const {  // NOLINT(runtime/explicit)
     return ConvertToContainer<Container, typename Container::value_type,
                               HasMappedType<Container>::value>()(*this);
diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc
index 68ef7999a981..f842ed85e9f5 100644
--- a/absl/strings/numbers.cc
+++ b/absl/strings/numbers.cc
@@ -32,6 +32,7 @@
 
 #include "absl/base/internal/raw_logging.h"
 #include "absl/strings/ascii.h"
+#include "absl/strings/charconv.h"
 #include "absl/strings/internal/bits.h"
 #include "absl/strings/internal/memutil.h"
 #include "absl/strings/str_cat.h"
@@ -40,51 +41,54 @@ namespace absl {
 
 bool SimpleAtof(absl::string_view str, float* value) {
   *value = 0.0;
-  if (str.empty()) return false;
-  char buf[32];
-  std::unique_ptr<char[]> bigbuf;
-  char* ptr = buf;
-  if (str.size() > sizeof(buf) - 1) {
-    bigbuf.reset(new char[str.size() + 1]);
-    ptr = bigbuf.get();
-  }
-  memcpy(ptr, str.data(), str.size());
-  ptr[str.size()] = '\0';
-
-  char* endptr;
-  *value = strtof(ptr, &endptr);
-  if (endptr != ptr) {
-    while (absl::ascii_isspace(*endptr)) ++endptr;
-  }
-  // Ignore range errors from strtod/strtof.
-  // The values it returns on underflow and
-  // overflow are the right fallback in a
-  // robust setting.
-  return *ptr != '\0' && *endptr == '\0';
+  str = StripAsciiWhitespace(str);
+  if (!str.empty() && str[0] == '+') {
+    str.remove_prefix(1);
+  }
+  auto result = absl::from_chars(str.data(), str.data() + str.size(), *value);
+  if (result.ec == std::errc::invalid_argument) {
+    return false;
+  }
+  if (result.ptr != str.data() + str.size()) {
+    // not all non-whitespace characters consumed
+    return false;
+  }
+  // from_chars() with DR 3801's current wording will return max() on
+  // overflow.  SimpleAtof returns infinity instead.
+  if (result.ec == std::errc::result_out_of_range) {
+    if (*value > 1.0) {
+      *value = std::numeric_limits<float>::infinity();
+    } else if (*value < -1.0) {
+      *value = -std::numeric_limits<float>::infinity();
+    }
+  }
+  return true;
 }
 
 bool SimpleAtod(absl::string_view str, double* value) {
   *value = 0.0;
-  if (str.empty()) return false;
-  char buf[32];
-  std::unique_ptr<char[]> bigbuf;
-  char* ptr = buf;
-  if (str.size() > sizeof(buf) - 1) {
-    bigbuf.reset(new char[str.size() + 1]);
-    ptr = bigbuf.get();
-  }
-  memcpy(ptr, str.data(), str.size());
-  ptr[str.size()] = '\0';
-
-  char* endptr;
-  *value = strtod(ptr, &endptr);
-  if (endptr != ptr) {
-    while (absl::ascii_isspace(*endptr)) ++endptr;
-  }
-  // Ignore range errors from strtod.  The values it
-  // returns on underflow and overflow are the right
-  // fallback in a robust setting.
-  return *ptr != '\0' && *endptr == '\0';
+  str = StripAsciiWhitespace(str);
+  if (!str.empty() && str[0] == '+') {
+    str.remove_prefix(1);
+  }
+  auto result = absl::from_chars(str.data(), str.data() + str.size(), *value);
+  if (result.ec == std::errc::invalid_argument) {
+    return false;
+  }
+  if (result.ptr != str.data() + str.size()) {
+    // not all non-whitespace characters consumed
+    return false;
+  }
+  // from_chars() with DR 3801's current wording will return max() on
+  // overflow.  SimpleAtod returns infinity instead.
+  if (result.ec == std::errc::result_out_of_range) {
+    if (*value > 1.0) {
+      *value = std::numeric_limits<double>::infinity();
+    } else if (*value < -1.0) {
+      *value = -std::numeric_limits<double>::infinity();
+    }
+  }
+  return true;
 }
 
 namespace {
diff --git a/absl/strings/numbers_benchmark.cc b/absl/strings/numbers_benchmark.cc
new file mode 100644
index 000000000000..8ef650b9a3ec
--- /dev/null
+++ b/absl/strings/numbers_benchmark.cc
@@ -0,0 +1,263 @@
+// 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
+//
+//      http://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 <random>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/strings/numbers.h"
+
+namespace {
+
+template <typename T>
+void BM_FastIntToBuffer(benchmark::State& state) {
+  const int inc = state.range(0);
+  char buf[absl::numbers_internal::kFastToBufferSize];
+  // Use the unsigned type to increment to take advantage of well-defined
+  // modular arithmetic.
+  typename std::make_unsigned<T>::type x = 0;
+  for (auto _ : state) {
+    absl::numbers_internal::FastIntToBuffer(static_cast<T>(x), buf);
+    x += inc;
+  }
+}
+BENCHMARK_TEMPLATE(BM_FastIntToBuffer, int32_t)->Range(0, 1 << 15);
+BENCHMARK_TEMPLATE(BM_FastIntToBuffer, int64_t)->Range(0, 1 << 30);
+
+// Creates an integer that would be printed as `num_digits` repeated 7s in the
+// given `base`. `base` must be greater than or equal to 8.
+int64_t RepeatedSevens(int num_digits, int base) {
+  ABSL_RAW_CHECK(base >= 8, "");
+  int64_t num = 7;
+  while (--num_digits) num = base * num + 7;
+  return num;
+}
+
+void BM_safe_strto32_string(benchmark::State& state) {
+  const int digits = state.range(0);
+  const int base = state.range(1);
+  std::string str(digits, '7');  // valid in octal, decimal and hex
+  int32_t value = 0;
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(
+        absl::numbers_internal::safe_strto32_base(str, &value, base));
+  }
+  ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), "");
+}
+BENCHMARK(BM_safe_strto32_string)
+    ->ArgPair(1, 8)
+    ->ArgPair(1, 10)
+    ->ArgPair(1, 16)
+    ->ArgPair(2, 8)
+    ->ArgPair(2, 10)
+    ->ArgPair(2, 16)
+    ->ArgPair(4, 8)
+    ->ArgPair(4, 10)
+    ->ArgPair(4, 16)
+    ->ArgPair(8, 8)
+    ->ArgPair(8, 10)
+    ->ArgPair(8, 16)
+    ->ArgPair(10, 8)
+    ->ArgPair(9, 10);
+
+void BM_safe_strto64_string(benchmark::State& state) {
+  const int digits = state.range(0);
+  const int base = state.range(1);
+  std::string str(digits, '7');  // valid in octal, decimal and hex
+  int64_t value = 0;
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(
+        absl::numbers_internal::safe_strto64_base(str, &value, base));
+  }
+  ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), "");
+}
+BENCHMARK(BM_safe_strto64_string)
+    ->ArgPair(1, 8)
+    ->ArgPair(1, 10)
+    ->ArgPair(1, 16)
+    ->ArgPair(2, 8)
+    ->ArgPair(2, 10)
+    ->ArgPair(2, 16)
+    ->ArgPair(4, 8)
+    ->ArgPair(4, 10)
+    ->ArgPair(4, 16)
+    ->ArgPair(8, 8)
+    ->ArgPair(8, 10)
+    ->ArgPair(8, 16)
+    ->ArgPair(16, 8)
+    ->ArgPair(16, 10)
+    ->ArgPair(16, 16);
+
+void BM_safe_strtou32_string(benchmark::State& state) {
+  const int digits = state.range(0);
+  const int base = state.range(1);
+  std::string str(digits, '7');  // valid in octal, decimal and hex
+  uint32_t value = 0;
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(
+        absl::numbers_internal::safe_strtou32_base(str, &value, base));
+  }
+  ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), "");
+}
+BENCHMARK(BM_safe_strtou32_string)
+    ->ArgPair(1, 8)
+    ->ArgPair(1, 10)
+    ->ArgPair(1, 16)
+    ->ArgPair(2, 8)
+    ->ArgPair(2, 10)
+    ->ArgPair(2, 16)
+    ->ArgPair(4, 8)
+    ->ArgPair(4, 10)
+    ->ArgPair(4, 16)
+    ->ArgPair(8, 8)
+    ->ArgPair(8, 10)
+    ->ArgPair(8, 16)
+    ->ArgPair(10, 8)
+    ->ArgPair(9, 10);
+
+void BM_safe_strtou64_string(benchmark::State& state) {
+  const int digits = state.range(0);
+  const int base = state.range(1);
+  std::string str(digits, '7');  // valid in octal, decimal and hex
+  uint64_t value = 0;
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(
+        absl::numbers_internal::safe_strtou64_base(str, &value, base));
+  }
+  ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), "");
+}
+BENCHMARK(BM_safe_strtou64_string)
+    ->ArgPair(1, 8)
+    ->ArgPair(1, 10)
+    ->ArgPair(1, 16)
+    ->ArgPair(2, 8)
+    ->ArgPair(2, 10)
+    ->ArgPair(2, 16)
+    ->ArgPair(4, 8)
+    ->ArgPair(4, 10)
+    ->ArgPair(4, 16)
+    ->ArgPair(8, 8)
+    ->ArgPair(8, 10)
+    ->ArgPair(8, 16)
+    ->ArgPair(16, 8)
+    ->ArgPair(16, 10)
+    ->ArgPair(16, 16);
+
+// Returns a vector of `num_strings` strings. Each std::string represents a
+// floating point number with `num_digits` digits before the decimal point and
+// another `num_digits` digits after.
+std::vector<std::string> MakeFloatStrings(int num_strings, int num_digits) {
+  // For convenience, use a random number generator to generate the test data.
+  // We don't actually need random properties, so use a fixed seed.
+  std::minstd_rand0 rng(1);
+  std::uniform_int_distribution<int> random_digit('0', '9');
+
+  std::vector<std::string> float_strings(num_strings);
+  for (std::string& s : float_strings) {
+    s.reserve(2 * num_digits + 1);
+    for (int i = 0; i < num_digits; ++i) {
+      s.push_back(static_cast<char>(random_digit(rng)));
+    }
+    s.push_back('.');
+    for (int i = 0; i < num_digits; ++i) {
+      s.push_back(static_cast<char>(random_digit(rng)));
+    }
+  }
+  return float_strings;
+}
+
+template <typename StringType>
+StringType GetStringAs(const std::string& s) {
+  return static_cast<StringType>(s);
+}
+template <>
+const char* GetStringAs<const char*>(const std::string& s) {
+  return s.c_str();
+}
+
+template <typename StringType>
+std::vector<StringType> GetStringsAs(const std::vector<std::string>& strings) {
+  std::vector<StringType> result;
+  result.reserve(strings.size());
+  for (const std::string& s : strings) {
+    result.push_back(GetStringAs<StringType>(s));
+  }
+  return result;
+}
+
+template <typename T>
+void BM_SimpleAtof(benchmark::State& state) {
+  const int num_strings = state.range(0);
+  const int num_digits = state.range(1);
+  std::vector<std::string> backing_strings =
+      MakeFloatStrings(num_strings, num_digits);
+  std::vector<T> inputs = GetStringsAs<T>(backing_strings);
+  float value;
+  for (auto _ : state) {
+    for (const T& input : inputs) {
+      benchmark::DoNotOptimize(absl::SimpleAtof(input, &value));
+    }
+  }
+}
+BENCHMARK_TEMPLATE(BM_SimpleAtof, absl::string_view)
+    ->ArgPair(10, 1)
+    ->ArgPair(10, 2)
+    ->ArgPair(10, 4)
+    ->ArgPair(10, 8);
+BENCHMARK_TEMPLATE(BM_SimpleAtof, const char*)
+    ->ArgPair(10, 1)
+    ->ArgPair(10, 2)
+    ->ArgPair(10, 4)
+    ->ArgPair(10, 8);
+BENCHMARK_TEMPLATE(BM_SimpleAtof, std::string)
+    ->ArgPair(10, 1)
+    ->ArgPair(10, 2)
+    ->ArgPair(10, 4)
+    ->ArgPair(10, 8);
+
+template <typename T>
+void BM_SimpleAtod(benchmark::State& state) {
+  const int num_strings = state.range(0);
+  const int num_digits = state.range(1);
+  std::vector<std::string> backing_strings =
+      MakeFloatStrings(num_strings, num_digits);
+  std::vector<T> inputs = GetStringsAs<T>(backing_strings);
+  double value;
+  for (auto _ : state) {
+    for (const T& input : inputs) {
+      benchmark::DoNotOptimize(absl::SimpleAtod(input, &value));
+    }
+  }
+}
+BENCHMARK_TEMPLATE(BM_SimpleAtod, absl::string_view)
+    ->ArgPair(10, 1)
+    ->ArgPair(10, 2)
+    ->ArgPair(10, 4)
+    ->ArgPair(10, 8);
+BENCHMARK_TEMPLATE(BM_SimpleAtod, const char*)
+    ->ArgPair(10, 1)
+    ->ArgPair(10, 2)
+    ->ArgPair(10, 4)
+    ->ArgPair(10, 8);
+BENCHMARK_TEMPLATE(BM_SimpleAtod, std::string)
+    ->ArgPair(10, 1)
+    ->ArgPair(10, 2)
+    ->ArgPair(10, 4)
+    ->ArgPair(10, 8);
+
+}  // namespace
diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h
new file mode 100644
index 000000000000..70a811b75e2f
--- /dev/null
+++ b/absl/strings/str_format.h
@@ -0,0 +1,512 @@
+//
+// 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
+//
+//      http://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: str_format.h
+// -----------------------------------------------------------------------------
+//
+// The `str_format` library is a typesafe replacement for the family of
+// `printf()` std::string formatting routines within the `<cstdio>` standard library
+// header. Like the `printf` family, the `str_format` uses a "format string" to
+// perform argument substitutions based on types.
+//
+// Example:
+//
+//   std::string s = absl::StrFormat("%s %s You have $%d!", "Hello", name, dollars);
+//
+// The library consists of the following basic utilities:
+//
+//   * `absl::StrFormat()`, a type-safe replacement for `std::sprintf()`, to
+//     write a format std::string to a `string` value.
+//   * `absl::StrAppendFormat()` to append a format std::string to a `string`
+//   * `absl::StreamFormat()` to more efficiently write a format std::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()`.
+//
+//     Note: a version of `std::sprintf()` is not supported as it is
+//     generally unsafe due to buffer overflows.
+//
+// Additionally, you can provide a format std::string (and its associated arguments)
+// using one of the following abstractions:
+//
+//   * A `FormatSpec` class template fully encapsulates a format std::string and its
+//     type arguments and is usually provided to `str_format` functions as a
+//     variadic argument of type `FormatSpec<Arg...>`. The `FormatSpec<Args...>`
+//     template is evaluated at compile-time, providing type safety.
+//   * A `ParsedFormat` instance, which encapsulates a specific, pre-compiled
+//     format std::string for a specific set of type(s), and which can be passed
+//     between API boundaries. (The `FormatSpec` type should not be used
+//     directly.)
+//
+// The `str_format` library provides the ability to output its format strings to
+// arbitrary sink types:
+//
+//   * A generic `Format()` function to write outputs to arbitrary sink types,
+//     which must implement a `RawSinkFormat` interface. (See
+//     `str_format_sink.h` for more information.)
+//
+//   * A `FormatUntyped()` function that is similar to `Format()` except it is
+//     loosely typed. `FormatUntyped()` is not a template and does not perform
+//     any compile-time checking of the format std::string; instead, it returns a
+//     boolean from a runtime check.
+//
+// In addition, the `str_format` library provides extension points for
+// augmenting formatting to new types. These extensions are fully documented
+// within the `str_format_extension.h` header file.
+#ifndef ABSL_STRINGS_STR_FORMAT_H_
+#define ABSL_STRINGS_STR_FORMAT_H_
+
+#include <cstdio>
+#include <string>
+
+#include "absl/strings/internal/str_format/arg.h"  // IWYU pragma: export
+#include "absl/strings/internal/str_format/bind.h"  // IWYU pragma: export
+#include "absl/strings/internal/str_format/checker.h"  // IWYU pragma: export
+#include "absl/strings/internal/str_format/extension.h"  // IWYU pragma: export
+#include "absl/strings/internal/str_format/parser.h"  // IWYU pragma: export
+
+namespace absl {
+
+// UntypedFormatSpec
+//
+// A type-erased class that can be used directly within untyped API entry
+// points. An `UntypedFormatSpec` is specifically used as an argument to
+// `FormatUntyped()`.
+//
+// Example:
+//
+//   absl::UntypedFormatSpec format("%d");
+//   std::string out;
+//   CHECK(absl::FormatUntyped(&out, format, {absl::FormatArg(1)}));
+class UntypedFormatSpec {
+ public:
+  UntypedFormatSpec() = delete;
+  UntypedFormatSpec(const UntypedFormatSpec&) = delete;
+  UntypedFormatSpec& operator=(const UntypedFormatSpec&) = delete;
+
+  explicit UntypedFormatSpec(string_view s) : spec_(s) {}
+
+ protected:
+  explicit UntypedFormatSpec(const str_format_internal::ParsedFormatBase* pc)
+      : spec_(pc) {}
+
+ private:
+  friend str_format_internal::UntypedFormatSpecImpl;
+  str_format_internal::UntypedFormatSpecImpl spec_;
+};
+
+// FormatStreamed()
+//
+// Takes a streamable argument and returns an object that can print it
+// with '%s'. Allows printing of types that have an `operator<<` but no
+// intrinsic type support within `StrFormat()` itself.
+//
+// Example:
+//
+//   absl::StrFormat("%s", absl::FormatStreamed(obj));
+template <typename T>
+str_format_internal::StreamedWrapper<T> FormatStreamed(const T& v) {
+  return str_format_internal::StreamedWrapper<T>(v);
+}
+
+// FormatCountCapture
+//
+// This class provides a way to safely wrap `StrFormat()` captures of `%n`
+// conversions, which denote the number of characters written by a formatting
+// operation to this point, into an integer value.
+//
+// This wrapper is designed to allow safe usage of `%n` within `StrFormat(); in
+// the `printf()` family of functions, `%n` is not safe to use, as the `int *`
+// buffer can be used to capture arbitrary data.
+//
+// Example:
+//
+//   int n = 0;
+//   std::string s = absl::StrFormat("%s%d%n", "hello", 123,
+//                   absl::FormatCountCapture(&n));
+//   EXPECT_EQ(8, n);
+class FormatCountCapture {
+ public:
+  explicit FormatCountCapture(int* p) : p_(p) {}
+
+ private:
+  // FormatCountCaptureHelper is used to define FormatConvertImpl() for this
+  // class.
+  friend struct str_format_internal::FormatCountCaptureHelper;
+  // Unused() is here because of the false positive from -Wunused-private-field
+  // p_ is used in the templated function of the friend FormatCountCaptureHelper
+  // class.
+  int* Unused() { return p_; }
+  int* p_;
+};
+
+// FormatSpec
+//
+// The `FormatSpec` type defines the makeup of a format std::string within the
+// `str_format` library. You should not need to use or manipulate this type
+// directly. A `FormatSpec` is a variadic class template that is evaluated at
+// compile-time, according to the format std::string and arguments that are passed
+// to it.
+//
+// For a `FormatSpec` to be valid at compile-time, it must be provided as
+// either:
+//
+// * A `constexpr` literal or `absl::string_view`, which is how it most often
+//   used.
+// * A `ParsedFormat` instantiation, which ensures the format std::string is
+//   valid before use. (See below.)
+//
+// Example:
+//
+//   // Provided as a std::string literal.
+//   absl::StrFormat("Welcome to %s, Number %d!", "The Village", 6);
+//
+//   // Provided as a constexpr absl::string_view.
+//   constexpr absl::string_view formatString = "Welcome to %s, Number %d!";
+//   absl::StrFormat(formatString, "The Village", 6);
+//
+//   // Provided as a pre-compiled ParsedFormat object.
+//   // Note that this example is useful only for illustration purposes.
+//   absl::ParsedFormat<'s', 'd'> formatString("Welcome to %s, Number %d!");
+//   absl::StrFormat(formatString, "TheVillage", 6);
+//
+// A format std::string generally follows the POSIX syntax as used within the POSIX
+// `printf` specification.
+//
+// (See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html.)
+//
+// In specific, the `FormatSpec` supports the following type specifiers:
+//   * `c` for characters
+//   * `s` for strings
+//   * `d` or `i` for integers
+//   * `o` for unsigned integer conversions into octal
+//   * `x` or `X` for unsigned integer conversions into hex
+//   * `u` for unsigned integers
+//   * `f` or `F` for floating point values into decimal notation
+//   * `e` or `E` for floating point values into exponential notation
+//   * `a` or `A` for floating point values into hex exponential notation
+//   * `g` or `G` for floating point values into decimal or exponential
+//     notation based on their precision
+//   * `p` for pointer address values
+//   * `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.
+//
+// NOTE: `o`, `x\X` and `u` will convert signed values to their unsigned
+// counterpart before formatting.
+//
+// Examples:
+//     "%c", 'a'                -> "a"
+//     "%c", 32                 -> " "
+//     "%s", "C"                -> "C"
+//     "%s", std::string("C++") -> "C++"
+//     "%d", -10                -> "-10"
+//     "%o", 10                 -> "12"
+//     "%x", 16                 -> "10"
+//     "%f", 123456789          -> "123456789.000000"
+//     "%e", .01                -> "1.00000e-2"
+//     "%a", -3.0               -> "-0x1.8p+1"
+//     "%g", .01                -> "1e-2"
+//     "%p", *int               -> "0x7ffdeb6ad2a4"
+//
+//     int n = 0;
+//     std::string s = absl::StrFormat(
+//         "%s%d%n", "hello", 123, absl::FormatCountCapture(&n));
+//     EXPECT_EQ(8, n);
+//
+// The `FormatSpec` intrinsically supports all of these fundamental C++ types:
+//
+// *   Characters: `char`, `signed char`, `unsigned char`
+// *   Integers: `int`, `short`, `unsigned short`, `unsigned`, `long`,
+//         `unsigned long`, `long long`, `unsigned long long`
+// *   Floating-point: `float`, `double`, `long double`
+//
+// However, in the `str_format` library, a format conversion specifies a broader
+// C++ conceptual category instead of an exact type. For example, `%s` binds to
+// any std::string-like argument, so `std::string`, `absl::string_view`, and
+// `const char*` are all accepted. Likewise, `%d` accepts any integer-like
+// argument, etc.
+
+template <typename... Args>
+using FormatSpec =
+    typename str_format_internal::FormatSpecDeductionBarrier<Args...>::type;
+
+// ParsedFormat
+//
+// A `ParsedFormat` is a class template representing a preparsed `FormatSpec`,
+// with template arguments specifying the conversion characters used within the
+// format std::string. Such characters must be valid format type specifiers, and
+// these type specifiers are checked at compile-time.
+//
+// Instances of `ParsedFormat` can be created, copied, and reused to speed up
+// formatting loops. A `ParsedFormat` may either be constructed statically, or
+// dynamically through its `New()` factory function, which only constructs a
+// runtime object if the format is valid at that time.
+//
+// Example:
+//
+//   // Verified at compile time.
+//   absl::ParsedFormat<'s', 'd'> formatString("Welcome to %s, Number %d!");
+//   absl::StrFormat(formatString, "TheVillage", 6);
+//
+//   // Verified at runtime.
+//   auto format_runtime = absl::ParsedFormat<'d'>::New(format_string);
+//   if (format_runtime) {
+//     value = absl::StrFormat(*format_runtime, i);
+//   } else {
+//     ... error case ...
+//   }
+template <char... Conv>
+using ParsedFormat = str_format_internal::ExtendedParsedFormat<
+    str_format_internal::ConversionCharToConv(Conv)...>;
+
+// StrFormat()
+//
+// Returns a `string` given a `printf()`-style format std::string and zero or more
+// additional arguments. Use it as you would `sprintf()`. `StrFormat()` is the
+// primary formatting function within the `str_format` library, and should be
+// used in most cases where you need type-safe conversion of types into
+// formatted strings.
+//
+// The format std::string generally consists of ordinary character data along with
+// one or more format conversion specifiers (denoted by the `%` character).
+// Ordinary character data is returned unchanged into the result std::string, while
+// each conversion specification performs a type substitution from
+// `StrFormat()`'s other arguments. See the comments for `FormatSpec` for full
+// information on the makeup of this format std::string.
+//
+// Example:
+//
+//   std::string s = absl::StrFormat(
+//       "Welcome to %s, Number %d!", "The Village", 6);
+//   EXPECT_EQ("Welcome to The Village, Number 6!", s);
+//
+// Returns an empty std::string in case of error.
+template <typename... Args>
+ABSL_MUST_USE_RESULT std::string StrFormat(const FormatSpec<Args...>& format,
+                                      const Args&... args) {
+  return str_format_internal::FormatPack(
+      str_format_internal::UntypedFormatSpecImpl::Extract(format),
+      {str_format_internal::FormatArgImpl(args)...});
+}
+
+// StrAppendFormat()
+//
+// Appends to a `dst` std::string given a format std::string, and zero or more additional
+// arguments, returning `*dst` as a convenience for chaining purposes. Appends
+// nothing in case of error (but possibly alters its capacity).
+//
+// Example:
+//
+//   std::string orig("For example PI is approximately ");
+//   std::cout << StrAppendFormat(&orig, "%12.6f", 3.14);
+template <typename... Args>
+std::string& StrAppendFormat(std::string* dst, const FormatSpec<Args...>& format,
+                        const Args&... args) {
+  return str_format_internal::AppendPack(
+      dst, str_format_internal::UntypedFormatSpecImpl::Extract(format),
+      {str_format_internal::FormatArgImpl(args)...});
+}
+
+// StreamFormat()
+//
+// Writes to an output stream given a format std::string and zero or more arguments,
+// generally in a manner that is more efficient than streaming the result of
+// `absl:: StrFormat()`. The returned object must be streamed before the full
+// expression ends.
+//
+// Example:
+//
+//   std::cout << StreamFormat("%12.6f", 3.14);
+template <typename... Args>
+ABSL_MUST_USE_RESULT str_format_internal::Streamable StreamFormat(
+    const FormatSpec<Args...>& format, const Args&... args) {
+  return str_format_internal::Streamable(
+      str_format_internal::UntypedFormatSpecImpl::Extract(format),
+      {str_format_internal::FormatArgImpl(args)...});
+}
+
+// PrintF()
+//
+// Writes to stdout given a format std::string and zero or more arguments. This
+// function is functionally equivalent to `std::printf()` (and type-safe);
+// prefer `absl::PrintF()` over `std::printf()`.
+//
+// Example:
+//
+//   std::string_view s = "Ulaanbaatar";
+//   absl::PrintF("The capital of Mongolia is %s", s);
+//
+//   Outputs: "The capital of Mongolia is Ulaanbaatar"
+//
+template <typename... Args>
+int PrintF(const FormatSpec<Args...>& format, const Args&... args) {
+  return str_format_internal::FprintF(
+      stdout, str_format_internal::UntypedFormatSpecImpl::Extract(format),
+      {str_format_internal::FormatArgImpl(args)...});
+}
+
+// FPrintF()
+//
+// Writes to a file given a format std::string and zero or more arguments. This
+// function is functionally equivalent to `std::fprintf()` (and type-safe);
+// prefer `absl::FPrintF()` over `std::fprintf()`.
+//
+// Example:
+//
+//   std::string_view s = "Ulaanbaatar";
+//   absl::FPrintF("The capital of Mongolia is %s", s);
+//
+//   Outputs: "The capital of Mongolia is Ulaanbaatar"
+//
+template <typename... Args>
+int FPrintF(std::FILE* output, const FormatSpec<Args...>& format,
+            const Args&... args) {
+  return str_format_internal::FprintF(
+      output, str_format_internal::UntypedFormatSpecImpl::Extract(format),
+      {str_format_internal::FormatArgImpl(args)...});
+}
+
+// SNPrintF()
+//
+// Writes to a sized buffer given a format std::string and zero or more arguments.
+// This function is functionally equivalent to `std::snprintf()` (and
+// type-safe); prefer `absl::SNPrintF()` over `std::snprintf()`.
+//
+// Example:
+//
+//   std::string_view s = "Ulaanbaatar";
+//   char output[128];
+//   absl::SNPrintF(output, sizeof(output),
+//                  "The capital of Mongolia is %s", s);
+//
+//   Post-condition: output == "The capital of Mongolia is Ulaanbaatar"
+//
+template <typename... Args>
+int SNPrintF(char* output, std::size_t size, const FormatSpec<Args...>& format,
+             const Args&... args) {
+  return str_format_internal::SnprintF(
+      output, size, str_format_internal::UntypedFormatSpecImpl::Extract(format),
+      {str_format_internal::FormatArgImpl(args)...});
+}
+
+// -----------------------------------------------------------------------------
+// Custom Output Formatting Functions
+// -----------------------------------------------------------------------------
+
+// FormatRawSink
+//
+// FormatRawSink is a type erased wrapper around arbitrary sink objects
+// specifically used as an argument to `Format()`.
+// FormatRawSink does not own the passed sink object. The passed object must
+// outlive the FormatRawSink.
+class FormatRawSink {
+ public:
+  // Implicitly convert from any type that provides the hook function as
+  // described above.
+  template <typename T,
+            typename = typename std::enable_if<std::is_constructible<
+                str_format_internal::FormatRawSinkImpl, T*>::value>::type>
+  FormatRawSink(T* raw)  // NOLINT
+      : sink_(raw) {}
+
+ private:
+  friend str_format_internal::FormatRawSinkImpl;
+  str_format_internal::FormatRawSinkImpl sink_;
+};
+
+// Format()
+//
+// Writes a formatted std::string to an arbitrary sink object (implementing the
+// `absl::FormatRawSink` interface), using a format std::string and zero or more
+// additional arguments.
+//
+// By default, `string` and `std::ostream` are supported as destination objects.
+//
+// `absl::Format()` is a generic version of `absl::StrFormat(), for custom
+// sinks. The format std::string, like format strings for `StrFormat()`, is checked
+// at compile-time.
+//
+// On failure, this function returns `false` and the state of the sink is
+// unspecified.
+template <typename... Args>
+bool Format(FormatRawSink raw_sink, const FormatSpec<Args...>& format,
+            const Args&... args) {
+  return str_format_internal::FormatUntyped(
+      str_format_internal::FormatRawSinkImpl::Extract(raw_sink),
+      str_format_internal::UntypedFormatSpecImpl::Extract(format),
+      {str_format_internal::FormatArgImpl(args)...});
+}
+
+// FormatArg
+//
+// A type-erased handle to a format argument specifically used as an argument to
+// `FormatUntyped()`. You may construct `FormatArg` by passing
+// reference-to-const of any printable type. `FormatArg` is both copyable and
+// assignable. The source data must outlive the `FormatArg` instance. See
+// example below.
+//
+using FormatArg = str_format_internal::FormatArgImpl;
+
+// FormatUntyped()
+//
+// Writes a formatted std::string to an arbitrary sink object (implementing the
+// `absl::FormatRawSink` interface), using an `UntypedFormatSpec` and zero or
+// more additional arguments.
+//
+// This function acts as the most generic formatting function in the
+// `str_format` library. The caller provides a raw sink, an unchecked format
+// std::string, and (usually) a runtime specified list of arguments; no compile-time
+// checking of formatting is performed within this function. As a result, a
+// caller should check the return value to verify that no error occurred.
+// On failure, this function returns `false` and the state of the sink is
+// unspecified.
+//
+// The arguments are provided in an `absl::Span<const absl::FormatArg>`.
+// Each `absl::FormatArg` object binds to a single argument and keeps a
+// reference to it. The values used to create the `FormatArg` objects must
+// outlive this function call. (See `str_format_arg.h` for information on
+// the `FormatArg` class.)_
+//
+// Example:
+//
+//   std::optional<std::string> FormatDynamic(const std::string& in_format,
+//                                       const vector<std::string>& in_args) {
+//     std::string out;
+//     std::vector<absl::FormatArg> args;
+//     for (const auto& v : in_args) {
+//       // It is important that 'v' is a reference to the objects in in_args.
+//       // The values we pass to FormatArg must outlive the call to
+//       // FormatUntyped.
+//       args.emplace_back(v);
+//     }
+//     absl::UntypedFormatSpec format(in_format);
+//     if (!absl::FormatUntyped(&out, format, args)) {
+//       return std::nullopt;
+//     }
+//     return std::move(out);
+//   }
+//
+ABSL_MUST_USE_RESULT inline bool FormatUntyped(
+    FormatRawSink raw_sink, const UntypedFormatSpec& format,
+    absl::Span<const FormatArg> args) {
+  return str_format_internal::FormatUntyped(
+      str_format_internal::FormatRawSinkImpl::Extract(raw_sink),
+      str_format_internal::UntypedFormatSpecImpl::Extract(format), args);
+}
+
+}  // namespace absl
+#endif  // ABSL_STRINGS_STR_FORMAT_H_
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc
new file mode 100644
index 000000000000..fed75fafb5c6
--- /dev/null
+++ b/absl/strings/str_format_test.cc
@@ -0,0 +1,603 @@
+
+#include <cstdarg>
+#include <cstdint>
+#include <cstdio>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+namespace {
+using str_format_internal::FormatArgImpl;
+
+class FormatEntryPointTest : public ::testing::Test { };
+
+TEST_F(FormatEntryPointTest, Format) {
+  std::string sink;
+  EXPECT_TRUE(Format(&sink, "A format %d", 123));
+  EXPECT_EQ("A format 123", sink);
+  sink.clear();
+
+  ParsedFormat<'d'> pc("A format %d");
+  EXPECT_TRUE(Format(&sink, pc, 123));
+  EXPECT_EQ("A format 123", sink);
+}
+TEST_F(FormatEntryPointTest, UntypedFormat) {
+  constexpr const char* formats[] = {
+    "",
+    "a",
+    "%80d",
+#if !defined(_MSC_VER) && !defined(__ANDROID__)
+    // MSVC and Android don't support positional syntax.
+    "complicated multipart %% %1$d format %1$0999d",
+#endif  // _MSC_VER
+  };
+  for (const char* fmt : formats) {
+    std::string actual;
+    int i = 123;
+    FormatArgImpl arg_123(i);
+    absl::Span<const FormatArgImpl> args(&arg_123, 1);
+    UntypedFormatSpec format(fmt);
+
+    EXPECT_TRUE(FormatUntyped(&actual, format, args));
+    char buf[4096]{};
+    snprintf(buf, sizeof(buf), fmt, 123);
+    EXPECT_EQ(
+        str_format_internal::FormatPack(
+            str_format_internal::UntypedFormatSpecImpl::Extract(format), args),
+        buf);
+    EXPECT_EQ(actual, buf);
+  }
+  // The internal version works with a preparsed format.
+  ParsedFormat<'d'> pc("A format %d");
+  int i = 345;
+  FormatArg arg(i);
+  std::string out;
+  EXPECT_TRUE(str_format_internal::FormatUntyped(
+      &out, str_format_internal::UntypedFormatSpecImpl(&pc), {&arg, 1}));
+  EXPECT_EQ("A format 345", out);
+}
+
+TEST_F(FormatEntryPointTest, StringFormat) {
+  EXPECT_EQ("123", StrFormat("%d", 123));
+  constexpr absl::string_view view("=%d=", 4);
+  EXPECT_EQ("=123=", StrFormat(view, 123));
+}
+
+TEST_F(FormatEntryPointTest, AppendFormat) {
+  std::string s;
+  std::string& r = StrAppendFormat(&s, "%d", 123);
+  EXPECT_EQ(&s, &r);  // should be same object
+  EXPECT_EQ("123", r);
+}
+
+TEST_F(FormatEntryPointTest, AppendFormatFail) {
+  std::string s = "orig";
+
+  UntypedFormatSpec format(" more %d");
+  FormatArgImpl arg("not an int");
+
+  EXPECT_EQ("orig",
+            str_format_internal::AppendPack(
+                &s, str_format_internal::UntypedFormatSpecImpl::Extract(format),
+                {&arg, 1}));
+}
+
+
+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));
+}
+
+TEST_F(FormatEntryPointTest, Preparsed) {
+  ParsedFormat<'d'> pc("%d");
+  EXPECT_EQ("123", StrFormat(pc, 123));
+  // rvalue ok?
+  EXPECT_EQ("123", StrFormat(ParsedFormat<'d'>("%d"), 123));
+  constexpr absl::string_view view("=%d=", 4);
+  EXPECT_EQ("=123=", StrFormat(ParsedFormat<'d'>(view), 123));
+}
+
+TEST_F(FormatEntryPointTest, FormatCountCapture) {
+  int n = 0;
+  EXPECT_EQ("", StrFormat("%n", FormatCountCapture(&n)));
+  EXPECT_EQ(0, n);
+  EXPECT_EQ("123", StrFormat("%d%n", 123, FormatCountCapture(&n)));
+  EXPECT_EQ(3, n);
+}
+
+TEST_F(FormatEntryPointTest, FormatCountCaptureWrongType) {
+  // Should reject int*.
+  int n = 0;
+  UntypedFormatSpec format("%d%n");
+  int i = 123, *ip = &n;
+  FormatArgImpl args[2] = {FormatArgImpl(i), FormatArgImpl(ip)};
+
+  EXPECT_EQ("", str_format_internal::FormatPack(
+                    str_format_internal::UntypedFormatSpecImpl::Extract(format),
+                    absl::MakeSpan(args)));
+}
+
+TEST_F(FormatEntryPointTest, FormatCountCaptureMultiple) {
+  int n1 = 0;
+  int n2 = 0;
+  EXPECT_EQ("    1         2",
+            StrFormat("%5d%n%10d%n", 1, FormatCountCapture(&n1), 2,
+                      FormatCountCapture(&n2)));
+  EXPECT_EQ(5, n1);
+  EXPECT_EQ(15, n2);
+}
+
+TEST_F(FormatEntryPointTest, FormatCountCaptureExample) {
+  int n;
+  std::string s;
+  StrAppendFormat(&s, "%s: %n%s\n", "(1,1)", FormatCountCapture(&n), "(1,2)");
+  StrAppendFormat(&s, "%*s%s\n", n, "", "(2,2)");
+  EXPECT_EQ(7, n);
+  EXPECT_EQ(
+      "(1,1): (1,2)\n"
+      "       (2,2)\n",
+      s);
+}
+
+TEST_F(FormatEntryPointTest, Stream) {
+  const std::string formats[] = {
+    "",
+    "a",
+    "%80d",
+#if !defined(_MSC_VER) && !defined(__ANDROID__)
+    // MSVC doesn't support positional syntax.
+    "complicated multipart %% %1$d format %1$080d",
+#endif  // _MSC_VER
+  };
+  std::string buf(4096, '\0');
+  for (const auto& fmt : formats) {
+    const auto parsed = ParsedFormat<'d'>::NewAllowIgnored(fmt);
+    std::ostringstream oss;
+    oss << StreamFormat(*parsed, 123);
+    int fmt_result = snprintf(&*buf.begin(), buf.size(), fmt.c_str(), 123);
+    ASSERT_TRUE(oss) << fmt;
+    ASSERT_TRUE(fmt_result >= 0 && static_cast<size_t>(fmt_result) < buf.size())
+        << fmt_result;
+    EXPECT_EQ(buf.c_str(), oss.str());
+  }
+}
+
+TEST_F(FormatEntryPointTest, StreamOk) {
+  std::ostringstream oss;
+  oss << StreamFormat("hello %d", 123);
+  EXPECT_EQ("hello 123", oss.str());
+  EXPECT_TRUE(oss.good());
+}
+
+TEST_F(FormatEntryPointTest, StreamFail) {
+  std::ostringstream oss;
+  UntypedFormatSpec format("hello %d");
+  FormatArgImpl arg("non-numeric");
+  oss << str_format_internal::Streamable(
+      str_format_internal::UntypedFormatSpecImpl::Extract(format), {&arg, 1});
+  EXPECT_EQ("hello ", oss.str());  // partial write
+  EXPECT_TRUE(oss.fail());
+}
+
+std::string WithSnprintf(const char* fmt, ...) {
+  std::string buf;
+  buf.resize(128);
+  va_list va;
+  va_start(va, fmt);
+  int r = vsnprintf(&*buf.begin(), buf.size(), fmt, va);
+  va_end(va);
+  EXPECT_GE(r, 0);
+  EXPECT_LT(r, buf.size());
+  buf.resize(r);
+  return buf;
+}
+
+TEST_F(FormatEntryPointTest, FloatPrecisionArg) {
+  // Test that positional parameters for width and precision
+  // are indexed to precede the value.
+  // Also sanity check the same formats against snprintf.
+  EXPECT_EQ("0.1", StrFormat("%.1f", 0.1));
+  EXPECT_EQ("0.1", WithSnprintf("%.1f", 0.1));
+  EXPECT_EQ("  0.1", StrFormat("%*.1f", 5, 0.1));
+  EXPECT_EQ("  0.1", WithSnprintf("%*.1f", 5, 0.1));
+  EXPECT_EQ("0.1", StrFormat("%.*f", 1, 0.1));
+  EXPECT_EQ("0.1", WithSnprintf("%.*f", 1, 0.1));
+  EXPECT_EQ("  0.1", StrFormat("%*.*f", 5, 1, 0.1));
+  EXPECT_EQ("  0.1", WithSnprintf("%*.*f", 5, 1, 0.1));
+}
+namespace streamed_test {
+struct X {};
+std::ostream& operator<<(std::ostream& os, const X&) {
+  return os << "X";
+}
+}  // streamed_test
+
+TEST_F(FormatEntryPointTest, FormatStreamed) {
+  EXPECT_EQ("123", StrFormat("%s", FormatStreamed(123)));
+  EXPECT_EQ("  123", StrFormat("%5s", FormatStreamed(123)));
+  EXPECT_EQ("123  ", StrFormat("%-5s", FormatStreamed(123)));
+  EXPECT_EQ("X", StrFormat("%s", FormatStreamed(streamed_test::X())));
+  EXPECT_EQ("123", StrFormat("%s", FormatStreamed(StreamFormat("%d", 123))));
+}
+
+// Helper class that creates a temporary file and exposes a FILE* to it.
+// It will close the file on destruction.
+class TempFile {
+ public:
+  TempFile() : file_(std::tmpfile()) {}
+  ~TempFile() { std::fclose(file_); }
+
+  std::FILE* file() const { return file_; }
+
+  // Read the file into a std::string.
+  std::string ReadFile() {
+    std::fseek(file_, 0, SEEK_END);
+    int size = std::ftell(file_);
+    std::rewind(file_);
+    std::string str(2 * size, ' ');
+    int read_bytes = std::fread(&str[0], 1, str.size(), file_);
+    EXPECT_EQ(read_bytes, size);
+    str.resize(read_bytes);
+    EXPECT_TRUE(std::feof(file_));
+    return str;
+  }
+
+ private:
+  std::FILE* file_;
+};
+
+TEST_F(FormatEntryPointTest, FPrintF) {
+  TempFile tmp;
+  int result =
+      FPrintF(tmp.file(), "STRING: %s NUMBER: %010d", std::string("ABC"), -19);
+  EXPECT_EQ(result, 30);
+  EXPECT_EQ(tmp.ReadFile(), "STRING: ABC NUMBER: -000000019");
+}
+
+TEST_F(FormatEntryPointTest, FPrintFError) {
+  errno = 0;
+  int result = FPrintF(stdin, "ABC");
+  EXPECT_LT(result, 0);
+  EXPECT_EQ(errno, EBADF);
+}
+
+#if __GNUC__
+TEST_F(FormatEntryPointTest, FprintfTooLarge) {
+  std::FILE* f = std::fopen("/dev/null", "w");
+  int width = 2000000000;
+  errno = 0;
+  int result = FPrintF(f, "%*d %*d", width, 0, width, 0);
+  EXPECT_LT(result, 0);
+  EXPECT_EQ(errno, EFBIG);
+  std::fclose(f);
+}
+
+TEST_F(FormatEntryPointTest, PrintF) {
+  int stdout_tmp = dup(STDOUT_FILENO);
+
+  TempFile tmp;
+  std::fflush(stdout);
+  dup2(fileno(tmp.file()), STDOUT_FILENO);
+
+  int result = PrintF("STRING: %s NUMBER: %010d", std::string("ABC"), -19);
+
+  std::fflush(stdout);
+  dup2(stdout_tmp, STDOUT_FILENO);
+  close(stdout_tmp);
+
+  EXPECT_EQ(result, 30);
+  EXPECT_EQ(tmp.ReadFile(), "STRING: ABC NUMBER: -000000019");
+}
+#endif  // __GNUC__
+
+TEST_F(FormatEntryPointTest, SNPrintF) {
+  char buffer[16];
+  int result =
+      SNPrintF(buffer, sizeof(buffer), "STRING: %s", std::string("ABC"));
+  EXPECT_EQ(result, 11);
+  EXPECT_EQ(std::string(buffer), "STRING: ABC");
+
+  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 123456);
+  EXPECT_EQ(result, 14);
+  EXPECT_EQ(std::string(buffer), "NUMBER: 123456");
+
+  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 1234567);
+  EXPECT_EQ(result, 15);
+  EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
+
+  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 12345678);
+  EXPECT_EQ(result, 16);
+  EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
+
+  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 123456789);
+  EXPECT_EQ(result, 17);
+  EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
+
+  result = SNPrintF(nullptr, 0, "Just checking the %s of the output.", "size");
+  EXPECT_EQ(result, 37);
+}
+
+TEST(StrFormat, BehavesAsDocumented) {
+  std::string s = absl::StrFormat("%s, %d!", "Hello", 123);
+  EXPECT_EQ("Hello, 123!", s);
+  // The format of a replacement is
+  // '%'[position][flags][width['.'precision]][length_modifier][format]
+  EXPECT_EQ(absl::StrFormat("%1$+3.2Lf", 1.1), "+1.10");
+  // Text conversion:
+  //     "c" - Character.              Eg: 'a' -> "A", 20 -> " "
+  EXPECT_EQ(StrFormat("%c", 'a'), "a");
+  EXPECT_EQ(StrFormat("%c", 0x20), " ");
+  //           Formats char and integral types: int, long, uint64_t, etc.
+  EXPECT_EQ(StrFormat("%c", int{'a'}), "a");
+  EXPECT_EQ(StrFormat("%c", long{'a'}), "a");  // NOLINT
+  EXPECT_EQ(StrFormat("%c", uint64_t{'a'}), "a");
+  //     "s" - std::string                  Eg: "C" -> "C", std::string("C++") -> "C++"
+  //           Formats std::string, char*, string_view, and Cord.
+  EXPECT_EQ(StrFormat("%s", "C"), "C");
+  EXPECT_EQ(StrFormat("%s", std::string("C++")), "C++");
+  EXPECT_EQ(StrFormat("%s", string_view("view")), "view");
+  // Integral Conversion
+  //     These format integral types: char, int, long, uint64_t, etc.
+  EXPECT_EQ(StrFormat("%d", char{10}), "10");
+  EXPECT_EQ(StrFormat("%d", int{10}), "10");
+  EXPECT_EQ(StrFormat("%d", long{10}), "10");  // NOLINT
+  EXPECT_EQ(StrFormat("%d", uint64_t{10}), "10");
+  //     d,i - signed decimal          Eg: -10 -> "-10"
+  EXPECT_EQ(StrFormat("%d", -10), "-10");
+  EXPECT_EQ(StrFormat("%i", -10), "-10");
+  //      o  - octal                   Eg:  10 -> "12"
+  EXPECT_EQ(StrFormat("%o", 10), "12");
+  //      u  - unsigned decimal        Eg:  10 -> "10"
+  EXPECT_EQ(StrFormat("%u", 10), "10");
+  //     x/X - lower,upper case hex    Eg:  10 -> "a"/"A"
+  EXPECT_EQ(StrFormat("%x", 10), "a");
+  EXPECT_EQ(StrFormat("%X", 10), "A");
+  // Floating-point, with upper/lower-case output.
+  //     These format floating points types: float, double, long double, etc.
+  EXPECT_EQ(StrFormat("%.1f", float{1}), "1.0");
+  EXPECT_EQ(StrFormat("%.1f", double{1}), "1.0");
+  const long double long_double = 1.0;
+  EXPECT_EQ(StrFormat("%.1f", long_double), "1.0");
+  //     These also format integral types: char, int, long, uint64_t, etc.:
+  EXPECT_EQ(StrFormat("%.1f", char{1}), "1.0");
+  EXPECT_EQ(StrFormat("%.1f", int{1}), "1.0");
+  EXPECT_EQ(StrFormat("%.1f", long{1}), "1.0");  // NOLINT
+  EXPECT_EQ(StrFormat("%.1f", uint64_t{1}), "1.0");
+  //     f/F - decimal.                Eg: 123456789 -> "123456789.000000"
+  EXPECT_EQ(StrFormat("%f", 123456789), "123456789.000000");
+  EXPECT_EQ(StrFormat("%F", 123456789), "123456789.000000");
+  //     e/E - exponentiated           Eg: .01 -> "1.00000e-2"/"1.00000E-2"
+  EXPECT_EQ(StrFormat("%e", .01), "1.000000e-02");
+  EXPECT_EQ(StrFormat("%E", .01), "1.000000E-02");
+  //     g/G - exponentiate to fit     Eg: .01 -> "0.01", 1e10 ->"1e+10"/"1E+10"
+  EXPECT_EQ(StrFormat("%g", .01), "0.01");
+  EXPECT_EQ(StrFormat("%g", 1e10), "1e+10");
+  EXPECT_EQ(StrFormat("%G", 1e10), "1E+10");
+  //     a/A - lower,upper case hex    Eg: -3.0 -> "-0x1.8p+1"/"-0X1.8P+1"
+
+// On Android platform <=21, there is a regression in hexfloat formatting.
+#if !defined(__ANDROID_API__) || __ANDROID_API__ > 21
+  EXPECT_EQ(StrFormat("%.1a", -3.0), "-0x1.8p+1");  // .1 to fix MSVC output
+  EXPECT_EQ(StrFormat("%.1A", -3.0), "-0X1.8P+1");  // .1 to fix MSVC output
+#endif
+
+  // Other conversion
+  int64_t value = 0x7ffdeb6;
+  auto ptr_value = static_cast<uintptr_t>(value);
+  const int& something = *reinterpret_cast<const int*>(ptr_value);
+  EXPECT_EQ(StrFormat("%p", &something), StrFormat("0x%x", ptr_value));
+
+  // Output widths are supported, with optional flags.
+  EXPECT_EQ(StrFormat("%3d", 1), "  1");
+  EXPECT_EQ(StrFormat("%3d", 123456), "123456");
+  EXPECT_EQ(StrFormat("%06.2f", 1.234), "001.23");
+  EXPECT_EQ(StrFormat("%+d", 1), "+1");
+  EXPECT_EQ(StrFormat("% d", 1), " 1");
+  EXPECT_EQ(StrFormat("%-4d", -1), "-1  ");
+  EXPECT_EQ(StrFormat("%#o", 10), "012");
+  EXPECT_EQ(StrFormat("%#x", 15), "0xf");
+  EXPECT_EQ(StrFormat("%04d", 8), "0008");
+  // Posix positional substitution.
+  EXPECT_EQ(absl::StrFormat("%2$s, %3$s, %1$s!", "vici", "veni", "vidi"),
+            "veni, vidi, vici!");
+  // Length modifiers are ignored.
+  EXPECT_EQ(StrFormat("%hhd", int{1}), "1");
+  EXPECT_EQ(StrFormat("%hd", int{1}), "1");
+  EXPECT_EQ(StrFormat("%ld", int{1}), "1");
+  EXPECT_EQ(StrFormat("%lld", int{1}), "1");
+  EXPECT_EQ(StrFormat("%Ld", int{1}), "1");
+  EXPECT_EQ(StrFormat("%jd", int{1}), "1");
+  EXPECT_EQ(StrFormat("%zd", int{1}), "1");
+  EXPECT_EQ(StrFormat("%td", int{1}), "1");
+  EXPECT_EQ(StrFormat("%qd", int{1}), "1");
+}
+
+using str_format_internal::ExtendedParsedFormat;
+using str_format_internal::ParsedFormatBase;
+
+struct SummarizeConsumer {
+  std::string* out;
+  explicit SummarizeConsumer(std::string* out) : out(out) {}
+
+  bool Append(string_view s) {
+    *out += "[" + std::string(s) + "]";
+    return true;
+  }
+
+  bool ConvertOne(const str_format_internal::UnboundConversion& conv,
+                  string_view s) {
+    *out += "{";
+    *out += std::string(s);
+    *out += ":";
+    *out += std::to_string(conv.arg_position) + "$";
+    if (conv.width.is_from_arg()) {
+      *out += std::to_string(conv.width.get_from_arg()) + "$*";
+    }
+    if (conv.precision.is_from_arg()) {
+      *out += "." + std::to_string(conv.precision.get_from_arg()) + "$*";
+    }
+    *out += conv.conv.Char();
+    *out += "}";
+    return true;
+  }
+};
+
+std::string SummarizeParsedFormat(const ParsedFormatBase& pc) {
+  std::string out;
+  if (!pc.ProcessFormat(SummarizeConsumer(&out))) out += "!";
+  return out;
+}
+
+class ParsedFormatTest : public testing::Test {};
+
+TEST_F(ParsedFormatTest, SimpleChecked) {
+  EXPECT_EQ("[ABC]{d:1$d}[DEF]",
+            SummarizeParsedFormat(ParsedFormat<'d'>("ABC%dDEF")));
+  EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}",
+            SummarizeParsedFormat(ParsedFormat<'s', 'd', 'f'>("%sFFF%dZZZ%f")));
+  EXPECT_EQ("{s:1$s}[ ]{.*d:3$.2$*d}",
+            SummarizeParsedFormat(ParsedFormat<'s', '*', 'd'>("%s %.*d")));
+}
+
+TEST_F(ParsedFormatTest, SimpleUncheckedCorrect) {
+  auto f = ParsedFormat<'d'>::New("ABC%dDEF");
+  ASSERT_TRUE(f);
+  EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f));
+
+  std::string format = "%sFFF%dZZZ%f";
+  auto f2 = ParsedFormat<'s', 'd', 'f'>::New(format);
+
+  ASSERT_TRUE(f2);
+  EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2));
+
+  f2 = ParsedFormat<'s', 'd', 'f'>::New("%s %d %f");
+
+  ASSERT_TRUE(f2);
+  EXPECT_EQ("{s:1$s}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2));
+
+  auto star = ParsedFormat<'*', 'd'>::New("%*d");
+  ASSERT_TRUE(star);
+  EXPECT_EQ("{*d:2$1$*d}", SummarizeParsedFormat(*star));
+
+  auto dollar = ParsedFormat<'d', 's'>::New("%2$s %1$d");
+  ASSERT_TRUE(dollar);
+  EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}", SummarizeParsedFormat(*dollar));
+  // with reuse
+  dollar = ParsedFormat<'d', 's'>::New("%2$s %1$d %1$d");
+  ASSERT_TRUE(dollar);
+  EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}[ ]{1$d:1$d}",
+            SummarizeParsedFormat(*dollar));
+}
+
+TEST_F(ParsedFormatTest, SimpleUncheckedIgnoredArgs) {
+  EXPECT_FALSE((ParsedFormat<'d', 's'>::New("ABC")));
+  EXPECT_FALSE((ParsedFormat<'d', 's'>::New("%dABC")));
+  EXPECT_FALSE((ParsedFormat<'d', 's'>::New("ABC%2$s")));
+  auto f = ParsedFormat<'d', 's'>::NewAllowIgnored("ABC");
+  ASSERT_TRUE(f);
+  EXPECT_EQ("[ABC]", SummarizeParsedFormat(*f));
+  f = ParsedFormat<'d', 's'>::NewAllowIgnored("%dABC");
+  ASSERT_TRUE(f);
+  EXPECT_EQ("{d:1$d}[ABC]", SummarizeParsedFormat(*f));
+  f = ParsedFormat<'d', 's'>::NewAllowIgnored("ABC%2$s");
+  ASSERT_TRUE(f);
+  EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f));
+}
+
+TEST_F(ParsedFormatTest, SimpleUncheckedUnsupported) {
+  EXPECT_FALSE(ParsedFormat<'d'>::New("%1$d %1$x"));
+  EXPECT_FALSE(ParsedFormat<'x'>::New("%1$d %1$x"));
+}
+
+TEST_F(ParsedFormatTest, SimpleUncheckedIncorrect) {
+  EXPECT_FALSE(ParsedFormat<'d'>::New(""));
+
+  EXPECT_FALSE(ParsedFormat<'d'>::New("ABC%dDEF%d"));
+
+  std::string format = "%sFFF%dZZZ%f";
+  EXPECT_FALSE((ParsedFormat<'s', 'd', 'g'>::New(format)));
+}
+
+using str_format_internal::Conv;
+
+TEST_F(ParsedFormatTest, UncheckedCorrect) {
+  auto f = ExtendedParsedFormat<Conv::d>::New("ABC%dDEF");
+  ASSERT_TRUE(f);
+  EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f));
+
+  std::string format = "%sFFF%dZZZ%f";
+  auto f2 =
+      ExtendedParsedFormat<Conv::string, Conv::d, Conv::floating>::New(format);
+
+  ASSERT_TRUE(f2);
+  EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2));
+
+  f2 = ExtendedParsedFormat<Conv::string, Conv::d, Conv::floating>::New(
+      "%s %d %f");
+
+  ASSERT_TRUE(f2);
+  EXPECT_EQ("{s:1$s}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2));
+
+  auto star = ExtendedParsedFormat<Conv::star, Conv::d>::New("%*d");
+  ASSERT_TRUE(star);
+  EXPECT_EQ("{*d:2$1$*d}", SummarizeParsedFormat(*star));
+
+  auto dollar = ExtendedParsedFormat<Conv::d, Conv::s>::New("%2$s %1$d");
+  ASSERT_TRUE(dollar);
+  EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}", SummarizeParsedFormat(*dollar));
+  // with reuse
+  dollar = ExtendedParsedFormat<Conv::d, Conv::s>::New("%2$s %1$d %1$d");
+  ASSERT_TRUE(dollar);
+  EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}[ ]{1$d:1$d}",
+            SummarizeParsedFormat(*dollar));
+}
+
+TEST_F(ParsedFormatTest, UncheckedIgnoredArgs) {
+  EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::s>::New("ABC")));
+  EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::s>::New("%dABC")));
+  EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::s>::New("ABC%2$s")));
+  auto f = ExtendedParsedFormat<Conv::d, Conv::s>::NewAllowIgnored("ABC");
+  ASSERT_TRUE(f);
+  EXPECT_EQ("[ABC]", SummarizeParsedFormat(*f));
+  f = ExtendedParsedFormat<Conv::d, Conv::s>::NewAllowIgnored("%dABC");
+  ASSERT_TRUE(f);
+  EXPECT_EQ("{d:1$d}[ABC]", SummarizeParsedFormat(*f));
+  f = ExtendedParsedFormat<Conv::d, Conv::s>::NewAllowIgnored("ABC%2$s");
+  ASSERT_TRUE(f);
+  EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f));
+}
+
+TEST_F(ParsedFormatTest, UncheckedMultipleTypes) {
+  auto dx = ExtendedParsedFormat<Conv::d | Conv::x>::New("%1$d %1$x");
+  EXPECT_TRUE(dx);
+  EXPECT_EQ("{1$d:1$d}[ ]{1$x:1$x}", SummarizeParsedFormat(*dx));
+
+  dx = ExtendedParsedFormat<Conv::d | Conv::x>::New("%1$d");
+  EXPECT_TRUE(dx);
+  EXPECT_EQ("{1$d:1$d}", SummarizeParsedFormat(*dx));
+}
+
+TEST_F(ParsedFormatTest, UncheckedIncorrect) {
+  EXPECT_FALSE(ExtendedParsedFormat<Conv::d>::New(""));
+
+  EXPECT_FALSE(ExtendedParsedFormat<Conv::d>::New("ABC%dDEF%d"));
+
+  std::string format = "%sFFF%dZZZ%f";
+  EXPECT_FALSE((ExtendedParsedFormat<Conv::s, Conv::d, Conv::g>::New(format)));
+}
+
+TEST_F(ParsedFormatTest, RegressionMixPositional) {
+  EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::o>::New("%1$d %o")));
+}
+
+}  // namespace
+}  // namespace absl
diff --git a/absl/strings/str_split.h b/absl/strings/str_split.h
index 1f089b9377b2..9a7be2b05344 100644
--- a/absl/strings/str_split.h
+++ b/absl/strings/str_split.h
@@ -373,11 +373,11 @@ struct SkipWhitespace {
 
 // StrSplit()
 //
-// Splits a given `std::string` based on the provided `Delimiter` object,
-// returning the elements within the type specified by the caller. Optionally,
-// you may also pass a `Predicate` to `StrSplit()` indicating whether to include
-// or exclude the resulting element within the final result set. (See the
-// overviews for Delimiters and Predicates above.)
+// Splits a given std::string based on the provided `Delimiter` object, returning the
+// elements within the type specified by the caller. Optionally, you may pass a
+// `Predicate` to `StrSplit()` indicating whether to include or exclude the
+// resulting element within the final result set. (See the overviews for
+// Delimiters and Predicates above.)
 //
 // Example:
 //
diff --git a/absl/strings/str_split_test.cc b/absl/strings/str_split_test.cc
index c172a7629933..c6898863dc0e 100644
--- a/absl/strings/str_split_test.cc
+++ b/absl/strings/str_split_test.cc
@@ -37,6 +37,34 @@ using ::testing::ElementsAre;
 using ::testing::Pair;
 using ::testing::UnorderedElementsAre;
 
+TEST(Split, TraitsTest) {
+  static_assert(!absl::strings_internal::SplitterIsConvertibleTo<int>::value,
+                "");
+  static_assert(!absl::strings_internal::SplitterIsConvertibleTo<std::string>::value,
+                "");
+  static_assert(absl::strings_internal::SplitterIsConvertibleTo<
+                    std::vector<std::string>>::value,
+                "");
+  static_assert(
+      !absl::strings_internal::SplitterIsConvertibleTo<std::vector<int>>::value,
+      "");
+  static_assert(absl::strings_internal::SplitterIsConvertibleTo<
+                    std::vector<absl::string_view>>::value,
+                "");
+  static_assert(absl::strings_internal::SplitterIsConvertibleTo<
+                    std::map<std::string, std::string>>::value,
+                "");
+  static_assert(absl::strings_internal::SplitterIsConvertibleTo<
+                    std::map<absl::string_view, absl::string_view>>::value,
+                "");
+  static_assert(!absl::strings_internal::SplitterIsConvertibleTo<
+                    std::map<int, std::string>>::value,
+                "");
+  static_assert(!absl::strings_internal::SplitterIsConvertibleTo<
+                    std::map<std::string, int>>::value,
+                "");
+}
+
 // This tests the overall split API, which is made up of the absl::StrSplit()
 // function and the Delimiter objects in the absl:: namespace.
 // This TEST macro is outside of any namespace to require full specification of
diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel
index 123536ea4d10..8d302e01223b 100644
--- a/absl/synchronization/BUILD.bazel
+++ b/absl/synchronization/BUILD.bazel
@@ -149,12 +149,6 @@ cc_test(
     size = "large",
     srcs = ["mutex_test.cc"],
     copts = ABSL_TEST_COPTS,
-    tags = [
-        "no_test_android_arm",
-        "no_test_android_arm64",
-        "no_test_android_x86",
-        "no_test_loonix",  # Too slow.
-    ],
     deps = [
         ":synchronization",
         ":thread_pool",
@@ -231,6 +225,7 @@ cc_test(
         "//absl:windows": [],
         "//conditions:default": ["-pthread"],
     }),
+    tags = ["no_test_ios_x86_64"],
     deps = [
         ":synchronization",
         "//absl/base",
diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h
index 840b9d6b0e70..4d9e03123e3a 100644
--- a/absl/synchronization/mutex.h
+++ b/absl/synchronization/mutex.h
@@ -24,7 +24,7 @@
 // Unlike a `std::mutex`, the Abseil `Mutex` provides the following additional
 // features:
 //   * Conditional predicates intrinsic to the `Mutex` object
-//   * Reader/writer locks, in addition to standard exclusive/writer locks
+//   * Shared/reader locks, in addition to standard exclusive/writer locks
 //   * Deadlock detection and debug support.
 //
 // The following helper classes are also defined within this file:
@@ -290,7 +290,7 @@ class LOCKABLE Mutex {
   // Mutex::ReaderLockWhen()
   // Mutex::WriterLockWhen()
   //
-  // Blocks until simultaneously both `cond` is `true` and this` Mutex` can
+  // Blocks until simultaneously both `cond` is `true` and this `Mutex` can
   // be acquired, then atomically acquires this `Mutex`. `LockWhen()` is
   // logically equivalent to `*Lock(); Await();` though they may have different
   // performance characteristics.
diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc
index 53b937843a34..b2820e2068d5 100644
--- a/absl/synchronization/mutex_test.cc
+++ b/absl/synchronization/mutex_test.cc
@@ -29,6 +29,7 @@
 #include <vector>
 
 #include "gtest/gtest.h"
+#include "absl/base/attributes.h"
 #include "absl/base/internal/raw_logging.h"
 #include "absl/base/internal/sysinfo.h"
 #include "absl/memory/memory.h"
@@ -54,8 +55,8 @@ CreateDefaultPool() {
 // Hack to schedule a function to run on a thread pool thread after a
 // duration has elapsed.
 static void ScheduleAfter(absl::synchronization_internal::ThreadPool *tp,
-                          const std::function<void()> &func,
-                          absl::Duration after) {
+                          absl::Duration after,
+                          const std::function<void()> &func) {
   tp->Schedule([func, after] {
     absl::SleepFor(after);
     func();
@@ -1150,249 +1151,369 @@ TEST(Mutex, DeadlockIdBug) NO_THREAD_SAFETY_ANALYSIS {
 // and so never expires/passes, and one that will expire/pass in the near
 // future.
 
-// Encapsulate a Mutex-protected bool with its associated Condition/CondVar.
-class Cond {
- public:
-  explicit Cond(bool use_deadline) : use_deadline_(use_deadline), c_(&b_) {}
-
-  void Set(bool v) {
-    absl::MutexLock lock(&mu_);
-    b_ = v;
+static absl::Duration TimeoutTestAllowedSchedulingDelay() {
+  // Note: we use a function here because Microsoft Visual Studio fails to
+  // properly initialize constexpr static absl::Duration variables.
+  return absl::Milliseconds(150);
+}
+
+// Returns true if `actual_delay` is close enough to `expected_delay` to pass
+// the timeouts/deadlines test.  Otherwise, logs warnings and returns false.
+ABSL_MUST_USE_RESULT
+static bool DelayIsWithinBounds(absl::Duration expected_delay,
+                                absl::Duration actual_delay) {
+  bool pass = true;
+  // Do not allow the observed delay to be less than expected.  This may occur
+  // in practice due to clock skew or when the synchronization primitives use a
+  // different clock than absl::Now(), but these cases should be handled by the
+  // the retry mechanism in each TimeoutTest.
+  if (actual_delay < expected_delay) {
+    ABSL_RAW_LOG(WARNING,
+                 "Actual delay %s was too short, expected %s (difference %s)",
+                 absl::FormatDuration(actual_delay).c_str(),
+                 absl::FormatDuration(expected_delay).c_str(),
+                 absl::FormatDuration(actual_delay - expected_delay).c_str());
+    pass = false;
   }
-
-  bool AwaitWithTimeout(absl::Duration timeout) {
-    absl::MutexLock lock(&mu_);
-    return use_deadline_ ? mu_.AwaitWithDeadline(c_, absl::Now() + timeout)
-                         : mu_.AwaitWithTimeout(c_, timeout);
+  // If the expected delay is <= zero then allow a small error tolerance, since
+  // we do not expect context switches to occur during test execution.
+  // Otherwise, thread scheduling delays may be substantial in rare cases, so
+  // tolerate up to kTimeoutTestAllowedSchedulingDelay of error.
+  absl::Duration tolerance = expected_delay <= absl::ZeroDuration()
+                                 ? absl::Milliseconds(10)
+                                 : TimeoutTestAllowedSchedulingDelay();
+  if (actual_delay > expected_delay + tolerance) {
+    ABSL_RAW_LOG(WARNING,
+                 "Actual delay %s was too long, expected %s (difference %s)",
+                 absl::FormatDuration(actual_delay).c_str(),
+                 absl::FormatDuration(expected_delay).c_str(),
+                 absl::FormatDuration(actual_delay - expected_delay).c_str());
+    pass = false;
   }
+  return pass;
+}
+
+// Parameters for TimeoutTest, below.
+struct TimeoutTestParam {
+  // The file and line number (used for logging purposes only).
+  const char *from_file;
+  int from_line;
+
+  // Should the absolute deadline API based on absl::Time be tested?  If false,
+  // the relative deadline API based on absl::Duration is tested.
+  bool use_absolute_deadline;
+
+  // The deadline/timeout used when calling the API being tested
+  // (e.g. Mutex::LockWhenWithDeadline).
+  absl::Duration wait_timeout;
+
+  // The delay before the condition will be set true by the test code.  If zero
+  // or negative, the condition is set true immediately (before calling the API
+  // being tested).  Otherwise, if infinite, the condition is never set true.
+  // Otherwise a closure is scheduled for the future that sets the condition
+  // true.
+  absl::Duration satisfy_condition_delay;
+
+  // The expected result of the condition after the call to the API being
+  // tested. Generally `true` means the condition was true when the API returns,
+  // `false` indicates an expected timeout.
+  bool expected_result;
+
+  // The expected delay before the API under test returns.  This is inherently
+  // flaky, so some slop is allowed (see `DelayIsWithinBounds` above), and the
+  // test keeps trying indefinitely until this constraint passes.
+  absl::Duration expected_delay;
+};
 
-  bool LockWhenWithTimeout(absl::Duration timeout) {
-    bool b = use_deadline_ ? mu_.LockWhenWithDeadline(c_, absl::Now() + timeout)
-                           : mu_.LockWhenWithTimeout(c_, timeout);
-    mu_.Unlock();
-    return b;
+// Print a `TimeoutTestParam` to a debug log.
+std::ostream &operator<<(std::ostream &os, const TimeoutTestParam &param) {
+  return os << "from: " << param.from_file << ":" << param.from_line
+            << " use_absolute_deadline: "
+            << (param.use_absolute_deadline ? "true" : "false")
+            << " wait_timeout: " << param.wait_timeout
+            << " satisfy_condition_delay: " << param.satisfy_condition_delay
+            << " expected_result: "
+            << (param.expected_result ? "true" : "false")
+            << " expected_delay: " << param.expected_delay;
+}
+
+std::string FormatString(const TimeoutTestParam &param) {
+  std::ostringstream os;
+  os << param;
+  return os.str();
+}
+
+// Like `thread::Executor::ScheduleAt` except:
+// a) Delays zero or negative are executed immediately in the current thread.
+// b) Infinite delays are never scheduled.
+// c) Calls this test's `ScheduleAt` helper instead of using `pool` directly.
+static void RunAfterDelay(absl::Duration delay,
+                          absl::synchronization_internal::ThreadPool *pool,
+                          const std::function<void()> &callback) {
+  if (delay <= absl::ZeroDuration()) {
+    callback();  // immediate
+  } else if (delay != absl::InfiniteDuration()) {
+    ScheduleAfter(pool, delay, callback);
   }
+}
 
-  bool ReaderLockWhenWithTimeout(absl::Duration timeout) {
-    bool b = use_deadline_
-                 ? mu_.ReaderLockWhenWithDeadline(c_, absl::Now() + timeout)
-                 : mu_.ReaderLockWhenWithTimeout(c_, timeout);
-    mu_.ReaderUnlock();
-    return b;
-  }
+class TimeoutTest : public ::testing::Test,
+                    public ::testing::WithParamInterface<TimeoutTestParam> {};
 
-  void Await() {
-    absl::MutexLock lock(&mu_);
-    mu_.Await(c_);
-  }
+std::vector<TimeoutTestParam> MakeTimeoutTestParamValues() {
+  // The `finite` delay is a finite, relatively short, delay.  We make it larger
+  // than our allowed scheduling delay (slop factor) to avoid confusion when
+  // diagnosing test failures.  The other constants here have clear meanings.
+  const absl::Duration finite = 3 * TimeoutTestAllowedSchedulingDelay();
+  const absl::Duration never = absl::InfiniteDuration();
+  const absl::Duration negative = -absl::InfiniteDuration();
+  const absl::Duration immediate = absl::ZeroDuration();
 
-  void Signal(bool v) {
-    absl::MutexLock lock(&mu_);
-    b_ = v;
-    cv_.Signal();
+  // Every test case is run twice; once using the absolute deadline API and once
+  // using the relative timeout API.
+  std::vector<TimeoutTestParam> values;
+  for (bool use_absolute_deadline : {false, true}) {
+    // Tests with a negative timeout (deadline in the past), which should
+    // immediately return current state of the condition.
+
+    // The condition is already true:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        negative,   // wait_timeout
+        immediate,  // satisfy_condition_delay
+        true,       // expected_result
+        immediate,  // expected_delay
+    });
+
+    // The condition becomes true, but the timeout has already expired:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        negative,  // wait_timeout
+        finite,    // satisfy_condition_delay
+        false,     // expected_result
+        immediate  // expected_delay
+    });
+
+    // The condition never becomes true:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        negative,  // wait_timeout
+        never,     // satisfy_condition_delay
+        false,     // expected_result
+        immediate  // expected_delay
+    });
+
+    // Tests with an infinite timeout (deadline in the infinite future), which
+    // should only return when the condition becomes true.
+
+    // The condition is already true:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        never,      // wait_timeout
+        immediate,  // satisfy_condition_delay
+        true,       // expected_result
+        immediate   // expected_delay
+    });
+
+    // The condition becomes true before the (infinite) expiry:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        never,   // wait_timeout
+        finite,  // satisfy_condition_delay
+        true,    // expected_result
+        finite,  // expected_delay
+    });
+
+    // Tests with a (small) finite timeout (deadline soon), with the condition
+    // becoming true both before and after its expiry.
+
+    // The condition is already true:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        never,      // wait_timeout
+        immediate,  // satisfy_condition_delay
+        true,       // expected_result
+        immediate   // expected_delay
+    });
+
+    // The condition becomes true before the expiry:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        finite * 2,  // wait_timeout
+        finite,      // satisfy_condition_delay
+        true,        // expected_result
+        finite       // expected_delay
+    });
+
+    // The condition becomes true, but the timeout has already expired:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        finite,      // wait_timeout
+        finite * 2,  // satisfy_condition_delay
+        false,       // expected_result
+        finite       // expected_delay
+    });
+
+    // The condition never becomes true:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        finite,  // wait_timeout
+        never,   // satisfy_condition_delay
+        false,   // expected_result
+        finite   // expected_delay
+    });
   }
-
-  bool WaitWithTimeout(absl::Duration timeout) {
-    absl::MutexLock lock(&mu_);
-    absl::Time deadline = absl::Now() + timeout;
-    if (use_deadline_) {
-      while (!b_ && !cv_.WaitWithDeadline(&mu_, deadline)) {
-      }
-    } else {
-      while (!b_ && !cv_.WaitWithTimeout(&mu_, timeout)) {
-        timeout = deadline - absl::Now();  // recompute timeout
-      }
+  return values;
+}
+
+// Instantiate `TimeoutTest` with `MakeTimeoutTestParamValues()`.
+INSTANTIATE_TEST_CASE_P(All, TimeoutTest,
+                        testing::ValuesIn(MakeTimeoutTestParamValues()));
+
+TEST_P(TimeoutTest, Await) {
+  const TimeoutTestParam params = GetParam();
+  ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
+
+  // Because this test asserts bounds on scheduling delays it is flaky.  To
+  // compensate it loops forever until it passes.  Failures express as test
+  // timeouts, in which case the test log can be used to diagnose the issue.
+  for (int attempt = 1;; ++attempt) {
+    ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
+
+    absl::Mutex mu;
+    bool value = false;  // condition value (under mu)
+
+    std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
+        CreateDefaultPool();
+    RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
+      absl::MutexLock l(&mu);
+      value = true;
+    });
+
+    absl::MutexLock lock(&mu);
+    absl::Time start_time = absl::Now();
+    absl::Condition cond(&value);
+    bool result =
+        params.use_absolute_deadline
+            ? mu.AwaitWithDeadline(cond, start_time + params.wait_timeout)
+            : mu.AwaitWithTimeout(cond, params.wait_timeout);
+    if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
+      EXPECT_EQ(params.expected_result, result);
+      break;
     }
-    return b_;
-  }
-
-  void Wait() {
-    absl::MutexLock lock(&mu_);
-    while (!b_) cv_.Wait(&mu_);
-  }
-
- private:
-  const bool use_deadline_;
-
-  bool b_;
-  absl::Condition c_;
-  absl::CondVar cv_;
-  absl::Mutex mu_;
-};
-
-class OperationTimer {
- public:
-  OperationTimer() : start_(absl::Now()) {}
-  absl::Duration Get() const { return absl::Now() - start_; }
-
- private:
-  const absl::Time start_;
-};
-
-static void CheckResults(bool exp_result, bool act_result,
-                         absl::Duration exp_duration,
-                         absl::Duration act_duration) {
-  ABSL_RAW_CHECK(exp_result == act_result, "CheckResults failed");
-  // Allow for some worse-case scheduling delay and clock skew.
-  if ((exp_duration - absl::Milliseconds(40) > act_duration) ||
-      (exp_duration + absl::Milliseconds(150) < act_duration)) {
-    ABSL_RAW_LOG(FATAL, "CheckResults failed: operation took %s, expected %s",
-                 absl::FormatDuration(act_duration).c_str(),
-                 absl::FormatDuration(exp_duration).c_str());
   }
 }
 
-static void TestAwaitTimeout(Cond *cp, absl::Duration timeout, bool exp_result,
-                             absl::Duration exp_duration) {
-  OperationTimer t;
-  bool act_result = cp->AwaitWithTimeout(timeout);
-  CheckResults(exp_result, act_result, exp_duration, t.Get());
-}
-
-static void TestLockWhenTimeout(Cond *cp, absl::Duration timeout,
-                                bool exp_result, absl::Duration exp_duration) {
-  OperationTimer t;
-  bool act_result = cp->LockWhenWithTimeout(timeout);
-  CheckResults(exp_result, act_result, exp_duration, t.Get());
-}
+TEST_P(TimeoutTest, LockWhen) {
+  const TimeoutTestParam params = GetParam();
+  ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
+
+  // Because this test asserts bounds on scheduling delays it is flaky.  To
+  // compensate it loops forever until it passes.  Failures express as test
+  // timeouts, in which case the test log can be used to diagnose the issue.
+  for (int attempt = 1;; ++attempt) {
+    ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
+
+    absl::Mutex mu;
+    bool value = false;  // condition value (under mu)
+
+    std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
+        CreateDefaultPool();
+    RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
+      absl::MutexLock l(&mu);
+      value = true;
+    });
+
+    absl::Time start_time = absl::Now();
+    absl::Condition cond(&value);
+    bool result =
+        params.use_absolute_deadline
+            ? mu.LockWhenWithDeadline(cond, start_time + params.wait_timeout)
+            : mu.LockWhenWithTimeout(cond, params.wait_timeout);
+    mu.Unlock();
 
-static void TestReaderLockWhenTimeout(Cond *cp, absl::Duration timeout,
-                                      bool exp_result,
-                                      absl::Duration exp_duration) {
-  OperationTimer t;
-  bool act_result = cp->ReaderLockWhenWithTimeout(timeout);
-  CheckResults(exp_result, act_result, exp_duration, t.Get());
+    if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
+      EXPECT_EQ(params.expected_result, result);
+      break;
+    }
+  }
 }
 
-static void TestWaitTimeout(Cond *cp, absl::Duration timeout, bool exp_result,
-                            absl::Duration exp_duration) {
-  OperationTimer t;
-  bool act_result = cp->WaitWithTimeout(timeout);
-  CheckResults(exp_result, act_result, exp_duration, t.Get());
+TEST_P(TimeoutTest, ReaderLockWhen) {
+  const TimeoutTestParam params = GetParam();
+  ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
+
+  // Because this test asserts bounds on scheduling delays it is flaky.  To
+  // compensate it loops forever until it passes.  Failures express as test
+  // timeouts, in which case the test log can be used to diagnose the issue.
+  for (int attempt = 0;; ++attempt) {
+    ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
+
+    absl::Mutex mu;
+    bool value = false;  // condition value (under mu)
+
+    std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
+        CreateDefaultPool();
+    RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
+      absl::MutexLock l(&mu);
+      value = true;
+    });
+
+    absl::Time start_time = absl::Now();
+    bool result =
+        params.use_absolute_deadline
+            ? mu.ReaderLockWhenWithDeadline(absl::Condition(&value),
+                                            start_time + params.wait_timeout)
+            : mu.ReaderLockWhenWithTimeout(absl::Condition(&value),
+                                           params.wait_timeout);
+    mu.ReaderUnlock();
+
+    if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
+      EXPECT_EQ(params.expected_result, result);
+      break;
+    }
+  }
 }
 
-// Tests with a negative timeout (deadline in the past), which should
-// immediately return the current state of the condition.
-static void TestNegativeTimeouts(absl::synchronization_internal::ThreadPool *tp,
-                                 Cond *cp) {
-  const absl::Duration negative = -absl::InfiniteDuration();
-  const absl::Duration immediate = absl::ZeroDuration();
-
-  // The condition is already true:
-  cp->Set(true);
-  TestAwaitTimeout(cp, negative, true, immediate);
-  TestLockWhenTimeout(cp, negative, true, immediate);
-  TestReaderLockWhenTimeout(cp, negative, true, immediate);
-  TestWaitTimeout(cp, negative, true, immediate);
-
-  // The condition becomes true, but the timeout has already expired:
-  const absl::Duration delay = absl::Milliseconds(200);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), 3 * delay);
-  TestAwaitTimeout(cp, negative, false, immediate);
-  TestLockWhenTimeout(cp, negative, false, immediate);
-  TestReaderLockWhenTimeout(cp, negative, false, immediate);
-  cp->Await();  // wait for the scheduled Set() to complete
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay);
-  TestWaitTimeout(cp, negative, false, immediate);
-  cp->Wait();  // wait for the scheduled Signal() to complete
-
-  // The condition never becomes true:
-  cp->Set(false);
-  TestAwaitTimeout(cp, negative, false, immediate);
-  TestLockWhenTimeout(cp, negative, false, immediate);
-  TestReaderLockWhenTimeout(cp, negative, false, immediate);
-  TestWaitTimeout(cp, negative, false, immediate);
-}
-
-// Tests with an infinite timeout (deadline in the infinite future), which
-// should only return when the condition becomes true.
-static void TestInfiniteTimeouts(absl::synchronization_internal::ThreadPool *tp,
-                                 Cond *cp) {
-  const absl::Duration infinite = absl::InfiniteDuration();
-  const absl::Duration immediate = absl::ZeroDuration();
-
-  // The condition is already true:
-  cp->Set(true);
-  TestAwaitTimeout(cp, infinite, true, immediate);
-  TestLockWhenTimeout(cp, infinite, true, immediate);
-  TestReaderLockWhenTimeout(cp, infinite, true, immediate);
-  TestWaitTimeout(cp, infinite, true, immediate);
-
-  // The condition becomes true before the (infinite) expiry:
-  const absl::Duration delay = absl::Milliseconds(200);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay);
-  TestAwaitTimeout(cp, infinite, true, delay);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay);
-  TestLockWhenTimeout(cp, infinite, true, delay);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay);
-  TestReaderLockWhenTimeout(cp, infinite, true, delay);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay);
-  TestWaitTimeout(cp, infinite, true, delay);
-}
-
-// Tests with a (small) finite timeout (deadline soon), with the condition
-// becoming true both before and after its expiry.
-static void TestFiniteTimeouts(absl::synchronization_internal::ThreadPool *tp,
-                               Cond *cp) {
-  const absl::Duration finite = absl::Milliseconds(400);
-  const absl::Duration immediate = absl::ZeroDuration();
+TEST_P(TimeoutTest, Wait) {
+  const TimeoutTestParam params = GetParam();
+  ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
+
+  // Because this test asserts bounds on scheduling delays it is flaky.  To
+  // compensate it loops forever until it passes.  Failures express as test
+  // timeouts, in which case the test log can be used to diagnose the issue.
+  for (int attempt = 0;; ++attempt) {
+    ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
+
+    absl::Mutex mu;
+    bool value = false;  // condition value (under mu)
+    absl::CondVar cv;    // signals a change of `value`
+
+    std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
+        CreateDefaultPool();
+    RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
+      absl::MutexLock l(&mu);
+      value = true;
+      cv.Signal();
+    });
+
+    absl::MutexLock lock(&mu);
+    absl::Time start_time = absl::Now();
+    absl::Duration timeout = params.wait_timeout;
+    absl::Time deadline = start_time + timeout;
+    while (!value) {
+      if (params.use_absolute_deadline ? cv.WaitWithDeadline(&mu, deadline)
+                                       : cv.WaitWithTimeout(&mu, timeout)) {
+        break;  // deadline/timeout exceeded
+      }
+      timeout = deadline - absl::Now();  // recompute
+    }
+    bool result = value;  // note: `mu` is still held
 
-  // The condition is already true:
-  cp->Set(true);
-  TestAwaitTimeout(cp, finite, true, immediate);
-  TestLockWhenTimeout(cp, finite, true, immediate);
-  TestReaderLockWhenTimeout(cp, finite, true, immediate);
-  TestWaitTimeout(cp, finite, true, immediate);
-
-  // The condition becomes true before the expiry:
-  const absl::Duration delay1 = finite / 2;
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay1);
-  TestAwaitTimeout(cp, finite, true, delay1);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay1);
-  TestLockWhenTimeout(cp, finite, true, delay1);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay1);
-  TestReaderLockWhenTimeout(cp, finite, true, delay1);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay1);
-  TestWaitTimeout(cp, finite, true, delay1);
-
-  // The condition becomes true, but the timeout has already expired:
-  const absl::Duration delay2 = finite * 2;
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), 3 * delay2);
-  TestAwaitTimeout(cp, finite, false, finite);
-  TestLockWhenTimeout(cp, finite, false, finite);
-  TestReaderLockWhenTimeout(cp, finite, false, finite);
-  cp->Await();  // wait for the scheduled Set() to complete
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay2);
-  TestWaitTimeout(cp, finite, false, finite);
-  cp->Wait();  // wait for the scheduled Signal() to complete
-
-  // The condition never becomes true:
-  cp->Set(false);
-  TestAwaitTimeout(cp, finite, false, finite);
-  TestLockWhenTimeout(cp, finite, false, finite);
-  TestReaderLockWhenTimeout(cp, finite, false, finite);
-  TestWaitTimeout(cp, finite, false, finite);
-}
-
-TEST(Mutex, Timeouts) {
-  auto tp = CreateDefaultPool();
-  for (bool use_deadline : {false, true}) {
-    Cond cond(use_deadline);
-    TestNegativeTimeouts(tp.get(), &cond);
-    TestInfiniteTimeouts(tp.get(), &cond);
-    TestFiniteTimeouts(tp.get(), &cond);
+    if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
+      EXPECT_EQ(params.expected_result, result);
+      break;
+    }
   }
 }
 
diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel
index fe55fe1f9bf6..e793da87d10e 100644
--- a/absl/time/BUILD.bazel
+++ b/absl/time/BUILD.bazel
@@ -44,6 +44,7 @@ cc_library(
         "//absl/base",
         "//absl/base:core_headers",
         "//absl/numeric:int128",
+        "//absl/strings",
         "//absl/time/internal/cctz:civil_time",
         "//absl/time/internal/cctz:time_zone",
     ],
@@ -80,9 +81,6 @@ cc_test(
         "time_zone_test.cc",
     ],
     copts = ABSL_TEST_COPTS,
-    tags = [
-        "no_test_loonix",
-    ],
     deps = [
         ":test_util",
         ":time",
diff --git a/absl/time/CMakeLists.txt b/absl/time/CMakeLists.txt
index 72bb4d25b5a5..06272364f14e 100644
--- a/absl/time/CMakeLists.txt
+++ b/absl/time/CMakeLists.txt
@@ -53,7 +53,7 @@ list(APPEND TIME_SRC
   ${TIME_PUBLIC_HEADERS}
   ${TIME_INTERNAL_HEADERS}
 )
-set(TIME_PUBLIC_LIBRARIES absl::base absl::stacktrace absl::int128)
+set(TIME_PUBLIC_LIBRARIES absl::base absl::stacktrace absl::int128 absl::strings)
 
 absl_library(
   TARGET
diff --git a/absl/time/clock_test.cc b/absl/time/clock_test.cc
index f143c0360b0d..707166d0c289 100644
--- a/absl/time/clock_test.cc
+++ b/absl/time/clock_test.cc
@@ -35,36 +35,84 @@ TEST(Time, Now) {
   EXPECT_GE(after, now);
 }
 
-TEST(SleepForTest, BasicSanity) {
-  absl::Duration sleep_time = absl::Milliseconds(2500);
-  absl::Time start = absl::Now();
-  absl::SleepFor(sleep_time);
-  absl::Time end = absl::Now();
-  EXPECT_LE(sleep_time - absl::Milliseconds(100), end - start);
-  EXPECT_GE(sleep_time + absl::Milliseconds(200), end - start);
-}
+enum class AlarmPolicy { kWithoutAlarm, kWithAlarm };
 
-#ifdef ABSL_HAVE_ALARM
-// Helper for test SleepFor.
+#if defined(ABSL_HAVE_ALARM)
 bool alarm_handler_invoked = false;
+
 void AlarmHandler(int signo) {
   ASSERT_EQ(signo, SIGALRM);
   alarm_handler_invoked = true;
 }
+#endif
+
+// Does SleepFor(d) take between lower_bound and upper_bound at least
+// once between now and (now + timeout)?  If requested (and supported),
+// add an alarm for the middle of the sleep period and expect it to fire.
+bool SleepForBounded(absl::Duration d, absl::Duration lower_bound,
+                     absl::Duration upper_bound, absl::Duration timeout,
+                     AlarmPolicy alarm_policy, int* attempts) {
+  const absl::Time deadline = absl::Now() + timeout;
+  while (absl::Now() < deadline) {
+#if defined(ABSL_HAVE_ALARM)
+    sig_t old_alarm = SIG_DFL;
+    if (alarm_policy == AlarmPolicy::kWithAlarm) {
+      alarm_handler_invoked = false;
+      old_alarm = signal(SIGALRM, AlarmHandler);
+      alarm(absl::ToInt64Seconds(d / 2));
+    }
+#else
+    EXPECT_EQ(alarm_policy, AlarmPolicy::kWithoutAlarm);
+#endif
+    ++*attempts;
+    absl::Time start = absl::Now();
+    absl::SleepFor(d);
+    absl::Duration actual = absl::Now() - start;
+#if defined(ABSL_HAVE_ALARM)
+    if (alarm_policy == AlarmPolicy::kWithAlarm) {
+      signal(SIGALRM, old_alarm);
+      if (!alarm_handler_invoked) continue;
+    }
+#endif
+    if (lower_bound <= actual && actual <= upper_bound) {
+      return true;  // yes, the SleepFor() was correctly bounded
+    }
+  }
+  return false;
+}
 
-TEST(SleepForTest, AlarmSupport) {
-  alarm_handler_invoked = false;
-  sig_t old_alarm = signal(SIGALRM, AlarmHandler);
-  alarm(2);
-  absl::Duration sleep_time = absl::Milliseconds(3500);
-  absl::Time start = absl::Now();
-  absl::SleepFor(sleep_time);
-  absl::Time end = absl::Now();
-  EXPECT_TRUE(alarm_handler_invoked);
-  EXPECT_LE(sleep_time - absl::Milliseconds(100), end - start);
-  EXPECT_GE(sleep_time + absl::Milliseconds(200), end - start);
-  signal(SIGALRM, old_alarm);
+testing::AssertionResult AssertSleepForBounded(absl::Duration d,
+                                               absl::Duration early,
+                                               absl::Duration late,
+                                               absl::Duration timeout,
+                                               AlarmPolicy alarm_policy) {
+  const absl::Duration lower_bound = d - early;
+  const absl::Duration upper_bound = d + late;
+  int attempts = 0;
+  if (SleepForBounded(d, lower_bound, upper_bound, timeout, alarm_policy,
+                      &attempts)) {
+    return testing::AssertionSuccess();
+  }
+  return testing::AssertionFailure()
+         << "SleepFor(" << d << ") did not return within [" << lower_bound
+         << ":" << upper_bound << "] in " << attempts << " attempt"
+         << (attempts == 1 ? "" : "s") << " over " << timeout
+         << (alarm_policy == AlarmPolicy::kWithAlarm ? " with" : " without")
+         << " an alarm";
+}
+
+// Tests that SleepFor() returns neither too early nor too late.
+TEST(SleepFor, Bounded) {
+  const absl::Duration d = absl::Milliseconds(2500);
+  const absl::Duration early = absl::Milliseconds(100);
+  const absl::Duration late = absl::Milliseconds(300);
+  const absl::Duration timeout = 48 * d;
+  EXPECT_TRUE(AssertSleepForBounded(d, early, late, timeout,
+                                    AlarmPolicy::kWithoutAlarm));
+#if defined(ABSL_HAVE_ALARM)
+  EXPECT_TRUE(AssertSleepForBounded(d, early, late, timeout,
+                                    AlarmPolicy::kWithAlarm));
+#endif
 }
-#endif  // ABSL_HAVE_ALARM
 
 }  // namespace
diff --git a/absl/time/duration.cc b/absl/time/duration.cc
index 82b4d9892c71..c13fa79b7fa3 100644
--- a/absl/time/duration.cc
+++ b/absl/time/duration.cc
@@ -896,8 +896,7 @@ bool ParseDuration(const std::string& dur_string, Duration* d) {
   return true;
 }
 
-// TODO(absl-team): Remove once dependencies are removed.
-bool ParseFlag(const std::string& text, Duration* dst, std::string* /* err */) {
+bool ParseFlag(const std::string& text, Duration* dst, std::string* ) {
   return ParseDuration(text, dst);
 }
 
diff --git a/absl/time/format.cc b/absl/time/format.cc
index 5dc01bda1553..e98e60a372bf 100644
--- a/absl/time/format.cc
+++ b/absl/time/format.cc
@@ -34,15 +34,13 @@ namespace {
 const char kInfiniteFutureStr[] = "infinite-future";
 const char kInfinitePastStr[] = "infinite-past";
 
-using cctz_sec = cctz::time_point<cctz::sys_seconds>;
-using cctz_fem = cctz::detail::femtoseconds;
 struct cctz_parts {
-  cctz_sec sec;
-  cctz_fem fem;
+  cctz::time_point<cctz::seconds> sec;
+  cctz::detail::femtoseconds fem;
 };
 
-inline cctz_sec unix_epoch() {
-  return std::chrono::time_point_cast<cctz::sys_seconds>(
+inline cctz::time_point<cctz::seconds> unix_epoch() {
+  return std::chrono::time_point_cast<cctz::seconds>(
       std::chrono::system_clock::from_time_t(0));
 }
 
@@ -53,8 +51,8 @@ cctz_parts Split(absl::Time t) {
   const auto d = time_internal::ToUnixDuration(t);
   const int64_t rep_hi = time_internal::GetRepHi(d);
   const int64_t rep_lo = time_internal::GetRepLo(d);
-  const auto sec = unix_epoch() + cctz::sys_seconds(rep_hi);
-  const auto fem = cctz_fem(rep_lo * (1000 * 1000 / 4));
+  const auto sec = unix_epoch() + cctz::seconds(rep_hi);
+  const auto fem = cctz::detail::femtoseconds(rep_lo * (1000 * 1000 / 4));
   return {sec, fem};
 }
 
@@ -129,7 +127,6 @@ bool ParseTime(const std::string& format, const std::string& input, absl::TimeZo
   return b;
 }
 
-// TODO(absl-team): Remove once dependencies are removed.
 // Functions required to support absl::Time flags.
 bool ParseFlag(const std::string& text, absl::Time* t, std::string* error) {
   return absl::ParseTime(RFC3339_full, text, absl::UTCTimeZone(), t, error);
diff --git a/absl/time/internal/cctz/include/cctz/civil_time_detail.h b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
index d52eddcdba06..2362a4f4fbc9 100644
--- a/absl/time/internal/cctz/include/cctz/civil_time_detail.h
+++ b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
@@ -403,20 +403,16 @@ class civil_time {
   }
 
   // Binary arithmetic operators.
-  inline friend CONSTEXPR_M civil_time operator+(civil_time a,
-                                                 diff_t n) noexcept {
+  friend CONSTEXPR_F civil_time operator+(civil_time a, diff_t n) noexcept {
     return a += n;
   }
-  inline friend CONSTEXPR_M civil_time operator+(diff_t n,
-                                                 civil_time a) noexcept {
+  friend CONSTEXPR_F civil_time operator+(diff_t n, civil_time a) noexcept {
     return a += n;
   }
-  inline friend CONSTEXPR_M civil_time operator-(civil_time a,
-                                                 diff_t n) noexcept {
+  friend CONSTEXPR_F civil_time operator-(civil_time a, diff_t n) noexcept {
     return a -= n;
   }
-  inline friend CONSTEXPR_M diff_t operator-(const civil_time& lhs,
-                                             const civil_time& rhs) noexcept {
+  friend CONSTEXPR_F diff_t operator-(civil_time lhs, civil_time rhs) noexcept {
     return difference(T{}, lhs.f_, rhs.f_);
   }
 
@@ -434,8 +430,8 @@ class civil_time {
 
 // Disallows difference between differently aligned types.
 // auto n = civil_day(...) - civil_hour(...);  // would be confusing.
-template <typename Tag1, typename Tag2>
-CONSTEXPR_F diff_t operator-(civil_time<Tag1>, civil_time<Tag2>) = delete;
+template <typename T, typename U>
+CONSTEXPR_F diff_t operator-(civil_time<T>, civil_time<U>) = delete;
 
 using civil_year = civil_time<year_tag>;
 using civil_month = civil_time<month_tag>;
diff --git a/absl/time/internal/cctz/include/cctz/time_zone.h b/absl/time/internal/cctz/include/cctz/time_zone.h
index 31abc2c4bbdf..0b9764ea72a9 100644
--- a/absl/time/internal/cctz/include/cctz/time_zone.h
+++ b/absl/time/internal/cctz/include/cctz/time_zone.h
@@ -34,23 +34,24 @@ namespace cctz {
 // Convenience aliases. Not intended as public API points.
 template <typename D>
 using time_point = std::chrono::time_point<std::chrono::system_clock, D>;
-using sys_seconds = std::chrono::duration<std::int_fast64_t>;
+using seconds = std::chrono::duration<std::int_fast64_t>;
+using sys_seconds = seconds;  // Deprecated.  Use cctz::seconds instead.
 
 namespace detail {
 template <typename D>
-inline std::pair<time_point<sys_seconds>, D>
+inline std::pair<time_point<seconds>, D>
 split_seconds(const time_point<D>& tp) {
-  auto sec = std::chrono::time_point_cast<sys_seconds>(tp);
+  auto sec = std::chrono::time_point_cast<seconds>(tp);
   auto sub = tp - sec;
   if (sub.count() < 0) {
-    sec -= sys_seconds(1);
-    sub += sys_seconds(1);
+    sec -= seconds(1);
+    sub += seconds(1);
   }
   return {sec, std::chrono::duration_cast<D>(sub)};
 }
-inline std::pair<time_point<sys_seconds>, sys_seconds>
-split_seconds(const time_point<sys_seconds>& tp) {
-  return {tp, sys_seconds(0)};
+inline std::pair<time_point<seconds>, seconds>
+split_seconds(const time_point<seconds>& tp) {
+  return {tp, seconds::zero()};
 }
 }  // namespace detail
 
@@ -99,7 +100,7 @@ class time_zone {
     bool is_dst;       // is offset non-standard?
     const char* abbr;  // time-zone abbreviation (e.g., "PST")
   };
-  absolute_lookup lookup(const time_point<sys_seconds>& tp) const;
+  absolute_lookup lookup(const time_point<seconds>& tp) const;
   template <typename D>
   absolute_lookup lookup(const time_point<D>& tp) const {
     return lookup(detail::split_seconds(tp).first);
@@ -118,9 +119,9 @@ class time_zone {
   // of the given civil-time argument, and the pre, trans, and post
   // members will give the absolute time answers using the pre-transition
   // offset, the transition point itself, and the post-transition offset,
-  // respectively (all three times are equal if kind == UNIQUE).  If any
+  // respectively (all three times are equal if kind == UNIQUE). If any
   // of these three absolute times is outside the representable range of a
-  // time_point<sys_seconds> the field is set to its maximum/minimum value.
+  // time_point<seconds> the field is set to its maximum/minimum value.
   //
   // Example:
   //   cctz::time_zone lax;
@@ -152,23 +153,85 @@ class time_zone {
       SKIPPED,   // the civil time did not exist (pre >= trans > post)
       REPEATED,  // the civil time was ambiguous (pre < trans <= post)
     } kind;
-    time_point<sys_seconds> pre;    // uses the pre-transition offset
-    time_point<sys_seconds> trans;  // instant of civil-offset change
-    time_point<sys_seconds> post;   // uses the post-transition offset
+    time_point<seconds> pre;    // uses the pre-transition offset
+    time_point<seconds> trans;  // instant of civil-offset change
+    time_point<seconds> post;   // uses the post-transition offset
   };
   civil_lookup lookup(const civil_second& cs) const;
 
+  // Finds the time of the next/previous offset change in this time zone.
+  //
+  // By definition, next_transition(tp, &trans) returns false when tp has
+  // its maximum value, and prev_transition(tp, &trans) returns false
+  // when tp has its minimum value. If the zone has no transitions, the
+  // result will also be false no matter what the argument.
+  //
+  // Otherwise, when tp has its minimum value, next_transition(tp, &trans)
+  // returns true and sets trans to the first recorded transition. Chains
+  // of calls to next_transition()/prev_transition() will eventually return
+  // false, but it is unspecified exactly when next_transition(tp, &trans)
+  // jumps to false, or what time is set by prev_transition(tp, &trans) for
+  // a very distant tp.
+  //
+  // Note: Enumeration of time-zone transitions is for informational purposes
+  // only. Modern time-related code should not care about when offset changes
+  // occur.
+  //
+  // Example:
+  //   cctz::time_zone nyc;
+  //   if (!cctz::load_time_zone("America/New_York", &nyc)) { ... }
+  //   const auto now = std::chrono::system_clock::now();
+  //   auto tp = cctz::time_point<cctz::seconds>::min();
+  //   cctz::time_zone::civil_transition trans;
+  //   while (tp <= now && nyc.next_transition(tp, &trans)) {
+  //     // transition: trans.from -> trans.to
+  //     tp = nyc.lookup(trans.to).trans;
+  //   }
+  struct civil_transition {
+    civil_second from;  // the civil time we jump from
+    civil_second to;    // the civil time we jump to
+  };
+  bool next_transition(const time_point<seconds>& tp,
+                       civil_transition* trans) const;
+  template <typename D>
+  bool next_transition(const time_point<D>& tp,
+                       civil_transition* trans) const {
+    return next_transition(detail::split_seconds(tp).first, trans);
+  }
+  bool prev_transition(const time_point<seconds>& tp,
+                       civil_transition* trans) const;
+  template <typename D>
+  bool prev_transition(const time_point<D>& tp,
+                       civil_transition* trans) const {
+    return prev_transition(detail::split_seconds(tp).first, trans);
+  }
+
+  // version() and description() provide additional information about the
+  // time zone. The content of each of the returned strings is unspecified,
+  // however, when the IANA Time Zone Database is the underlying data source
+  // the version() std::string will be in the familar form (e.g, "2018e") or
+  // empty when unavailable.
+  //
+  // Note: These functions are for informational or testing purposes only.
+  std::string version() const;  // empty when unknown
+  std::string description() const;
+
+  // Relational operators.
+  friend bool operator==(time_zone lhs, time_zone rhs) {
+    return &lhs.effective_impl() == &rhs.effective_impl();
+  }
+  friend bool operator!=(time_zone lhs, time_zone rhs) {
+    return !(lhs == rhs);
+  }
+
   class Impl;
 
  private:
   explicit time_zone(const Impl* impl) : impl_(impl) {}
+  const Impl& effective_impl() const;  // handles implicit UTC
   const Impl* impl_;
 };
 
-// Relational operators.
-bool operator==(time_zone lhs, time_zone rhs);
-inline bool operator!=(time_zone lhs, time_zone rhs) { return !(lhs == rhs); }
-
 // Loads the named time zone. May perform I/O on the initial load.
 // If the name is invalid, or some other kind of error occurs, returns
 // false and "*tz" is set to the UTC time zone.
@@ -180,9 +243,10 @@ time_zone utc_time_zone();
 // Returns a time zone that is a fixed offset (seconds east) from UTC.
 // Note: If the absolute value of the offset is greater than 24 hours
 // you'll get UTC (i.e., zero offset) instead.
-time_zone fixed_time_zone(const sys_seconds& offset);
+time_zone fixed_time_zone(const seconds& offset);
 
 // Returns a time zone representing the local time zone. Falls back to UTC.
+// Note: local_time_zone.name() may only be something like "localtime".
 time_zone local_time_zone();
 
 // Returns the civil time (cctz::civil_second) within the given time zone at
@@ -199,8 +263,8 @@ inline civil_second convert(const time_point<D>& tp, const time_zone& tz) {
 // it was either repeated or non-existent), then the returned time_point is
 // the best estimate that preserves relative order. That is, this function
 // guarantees that if cs1 < cs2, then convert(cs1, tz) <= convert(cs2, tz).
-inline time_point<sys_seconds> convert(const civil_second& cs,
-                                       const time_zone& tz) {
+inline time_point<seconds> convert(const civil_second& cs,
+                                   const time_zone& tz) {
   const time_zone::civil_lookup cl = tz.lookup(cs);
   if (cl.kind == time_zone::civil_lookup::SKIPPED) return cl.trans;
   return cl.pre;
@@ -208,10 +272,10 @@ inline time_point<sys_seconds> convert(const civil_second& cs,
 
 namespace detail {
 using femtoseconds = std::chrono::duration<std::int_fast64_t, std::femto>;
-std::string format(const std::string&, const time_point<sys_seconds>&,
+std::string format(const std::string&, const time_point<seconds>&,
                    const femtoseconds&, const time_zone&);
 bool parse(const std::string&, const std::string&, const time_zone&,
-           time_point<sys_seconds>*, femtoseconds*, std::string* err = nullptr);
+           time_point<seconds>*, femtoseconds*, std::string* err = nullptr);
 }  // namespace detail
 
 // Formats the given time_point in the given cctz::time_zone according to
@@ -226,7 +290,7 @@ bool parse(const std::string&, const std::string&, const time_zone&,
 //   - %E*f - Fractional seconds with full precision (a literal '*')
 //   - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
 //
-// Note that %E0S behaves like %S, and %E0f produces no characters.  In
+// Note that %E0S behaves like %S, and %E0f produces no characters. In
 // contrast %E*f always produces at least one digit, which may be '0'.
 //
 // Note that %Y produces as many characters as it takes to fully render the
@@ -253,7 +317,7 @@ inline std::string format(const std::string& fmt, const time_point<D>& tp,
 // Parses an input std::string according to the provided format std::string and
 // returns the corresponding time_point. Uses strftime()-like formatting
 // options, with the same extensions as cctz::format(), but with the
-// exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f.  %Ez
+// exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez
 // and %E*z also accept the same inputs.
 //
 // %Y consumes as many numeric characters as it can, so the matching data
@@ -298,7 +362,7 @@ inline std::string format(const std::string& fmt, const time_point<D>& tp,
 template <typename D>
 inline bool parse(const std::string& fmt, const std::string& input,
                   const time_zone& tz, time_point<D>* tpp) {
-  time_point<sys_seconds> sec;
+  time_point<seconds> sec;
   detail::femtoseconds fs;
   const bool b = detail::parse(fmt, input, tz, &sec, &fs);
   if (b) {
diff --git a/absl/time/internal/cctz/include/cctz/zone_info_source.h b/absl/time/internal/cctz/include/cctz/zone_info_source.h
index 4d9d8f875ed8..20a76979370f 100644
--- a/absl/time/internal/cctz/include/cctz/zone_info_source.h
+++ b/absl/time/internal/cctz/include/cctz/zone_info_source.h
@@ -31,6 +31,11 @@ class ZoneInfoSource {
 
   virtual std::size_t Read(void* ptr, std::size_t size) = 0;  // like fread()
   virtual int Skip(std::size_t offset) = 0;  // like fseek()
+
+  // Until the zoneinfo data supports versioning information, we provide
+  // a way for a ZoneInfoSource to indicate it out-of-band.  The default
+  // implementation returns an empty std::string.
+  virtual std::string Version() const;
 };
 
 }  // namespace cctz
diff --git a/absl/time/internal/cctz/src/cctz_benchmark.cc b/absl/time/internal/cctz/src/cctz_benchmark.cc
index f13cb4ee6b5f..c97df78c09c8 100644
--- a/absl/time/internal/cctz/src/cctz_benchmark.cc
+++ b/absl/time/internal/cctz/src/cctz_benchmark.cc
@@ -754,23 +754,21 @@ void BM_Zone_LoadAllTimeZonesCached(benchmark::State& state) {
 }
 BENCHMARK(BM_Zone_LoadAllTimeZonesCached);
 
-void BM_Zone_TimeZoneImplGetImplicit(benchmark::State& state) {
+void BM_Zone_TimeZoneEqualityImplicit(benchmark::State& state) {
   cctz::time_zone tz;  // implicit UTC
-  cctz::time_zone::Impl::get(tz);
   while (state.KeepRunning()) {
-    cctz::time_zone::Impl::get(tz);
+    benchmark::DoNotOptimize(tz == tz);
   }
 }
-BENCHMARK(BM_Zone_TimeZoneImplGetImplicit);
+BENCHMARK(BM_Zone_TimeZoneEqualityImplicit);
 
-void BM_Zone_TimeZoneImplGetExplicit(benchmark::State& state) {
+void BM_Zone_TimeZoneEqualityExplicit(benchmark::State& state) {
   cctz::time_zone tz = cctz::utc_time_zone();  // explicit UTC
-  cctz::time_zone::Impl::get(tz);
   while (state.KeepRunning()) {
-    cctz::time_zone::Impl::get(tz);
+    benchmark::DoNotOptimize(tz == tz);
   }
 }
-BENCHMARK(BM_Zone_TimeZoneImplGetExplicit);
+BENCHMARK(BM_Zone_TimeZoneEqualityExplicit);
 
 void BM_Zone_UTCTimeZone(benchmark::State& state) {
   cctz::time_zone tz;
diff --git a/absl/time/internal/cctz/src/time_zone_fixed.cc b/absl/time/internal/cctz/src/time_zone_fixed.cc
index 65eba3569d97..598b08fde422 100644
--- a/absl/time/internal/cctz/src/time_zone_fixed.cc
+++ b/absl/time/internal/cctz/src/time_zone_fixed.cc
@@ -42,9 +42,9 @@ int Parse02d(const char* p) {
 
 }  // namespace
 
-bool FixedOffsetFromName(const std::string& name, sys_seconds* offset) {
+bool FixedOffsetFromName(const std::string& name, seconds* offset) {
   if (name.compare(0, std::string::npos, "UTC", 3) == 0) {
-    *offset = sys_seconds::zero();
+    *offset = seconds::zero();
     return true;
   }
 
@@ -69,12 +69,12 @@ bool FixedOffsetFromName(const std::string& name, sys_seconds* offset) {
 
   secs += ((hours * 60) + mins) * 60;
   if (secs > 24 * 60 * 60) return false;  // outside supported offset range
-  *offset = sys_seconds(secs * (np[0] == '-' ? -1 : 1));  // "-" means west
+  *offset = seconds(secs * (np[0] == '-' ? -1 : 1));  // "-" means west
   return true;
 }
 
-std::string FixedOffsetToName(const sys_seconds& offset) {
-  if (offset == sys_seconds::zero()) return "UTC";
+std::string FixedOffsetToName(const seconds& offset) {
+  if (offset == seconds::zero()) return "UTC";
   if (offset < std::chrono::hours(-24) || offset > std::chrono::hours(24)) {
     // We don't support fixed-offset zones more than 24 hours
     // away from UTC to avoid complications in rendering such
@@ -101,7 +101,7 @@ std::string FixedOffsetToName(const sys_seconds& offset) {
   return buf;
 }
 
-std::string FixedOffsetToAbbr(const sys_seconds& offset) {
+std::string FixedOffsetToAbbr(const seconds& offset) {
   std::string abbr = FixedOffsetToName(offset);
   const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1;
   if (abbr.size() == prefix_len + 9) {         // <prefix>+99:99:99
diff --git a/absl/time/internal/cctz/src/time_zone_fixed.h b/absl/time/internal/cctz/src/time_zone_fixed.h
index 7c9d11db9c5b..489b857d5df3 100644
--- a/absl/time/internal/cctz/src/time_zone_fixed.h
+++ b/absl/time/internal/cctz/src/time_zone_fixed.h
@@ -38,9 +38,9 @@ namespace cctz {
 // Note: FixedOffsetFromName() fails on syntax errors or when the parsed
 // offset exceeds 24 hours.  FixedOffsetToName() and FixedOffsetToAbbr()
 // both produce "UTC" when the argument offset exceeds 24 hours.
-bool FixedOffsetFromName(const std::string& name, sys_seconds* offset);
-std::string FixedOffsetToName(const sys_seconds& offset);
-std::string FixedOffsetToAbbr(const sys_seconds& offset);
+bool FixedOffsetFromName(const std::string& name, seconds* offset);
+std::string FixedOffsetToName(const seconds& offset);
+std::string FixedOffsetToAbbr(const seconds& offset);
 
 }  // namespace cctz
 }  // namespace time_internal
diff --git a/absl/time/internal/cctz/src/time_zone_format.cc b/absl/time/internal/cctz/src/time_zone_format.cc
index 6d5ccba1ce49..1b023848efa1 100644
--- a/absl/time/internal/cctz/src/time_zone_format.cc
+++ b/absl/time/internal/cctz/src/time_zone_format.cc
@@ -141,6 +141,9 @@ char* Format02d(char* ep, int v) {
 
 // Formats a UTC offset, like +00:00.
 char* FormatOffset(char* ep, int offset, const char* mode) {
+  // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
+  // generate a "negative zero" when we're formatting a zero offset
+  // as the result of a failed load_time_zone().
   char sign = '+';
   if (offset < 0) {
     offset = -offset;  // bounded by 24h so no overflow
@@ -277,7 +280,7 @@ const std::int_fast64_t kExp10[kDigits10_64 + 1] = {
 // not support the tm_gmtoff and tm_zone extensions to std::tm.
 //
 // Requires that zero() <= fs < seconds(1).
-std::string format(const std::string& format, const time_point<sys_seconds>& tp,
+std::string format(const std::string& format, const time_point<seconds>& tp,
                    const detail::femtoseconds& fs, const time_zone& tz) {
   std::string result;
   result.reserve(format.size());  // A reasonable guess for the result size.
@@ -555,7 +558,7 @@ const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) {
 // We also handle the %z specifier to accommodate platforms that do not
 // support the tm_gmtoff extension to std::tm.  %Z is parsed but ignored.
 bool parse(const std::string& format, const std::string& input,
-           const time_zone& tz, time_point<sys_seconds>* sec,
+           const time_zone& tz, time_point<seconds>* sec,
            detail::femtoseconds* fs, std::string* err) {
   // The unparsed input.
   const char* data = input.c_str();  // NUL terminated
@@ -822,15 +825,15 @@ bool parse(const std::string& format, const std::string& input,
 
   const auto tp = ptz.lookup(cs).pre;
   // Checks for overflow/underflow and returns an error as necessary.
-  if (tp == time_point<sys_seconds>::max()) {
-    const auto al = ptz.lookup(time_point<sys_seconds>::max());
+  if (tp == time_point<seconds>::max()) {
+    const auto al = ptz.lookup(time_point<seconds>::max());
     if (cs > al.cs) {
       if (err != nullptr) *err = "Out-of-range field";
       return false;
     }
   }
-  if (tp == time_point<sys_seconds>::min()) {
-    const auto al = ptz.lookup(time_point<sys_seconds>::min());
+  if (tp == time_point<seconds>::min()) {
+    const auto al = ptz.lookup(time_point<seconds>::min());
     if (cs < al.cs) {
       if (err != nullptr) *err = "Out-of-range field";
       return false;
diff --git a/absl/time/internal/cctz/src/time_zone_format_test.cc b/absl/time/internal/cctz/src/time_zone_format_test.cc
index 7d5b02ad3a68..a90dda7603a8 100644
--- a/absl/time/internal/cctz/src/time_zone_format_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_format_test.cc
@@ -23,15 +23,7 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
-using std::chrono::time_point_cast;
-using std::chrono::system_clock;
-using std::chrono::nanoseconds;
-using std::chrono::microseconds;
-using std::chrono::milliseconds;
-using std::chrono::seconds;
-using std::chrono::minutes;
-using std::chrono::hours;
-using testing::HasSubstr;
+namespace chrono = std::chrono;
 
 namespace absl {
 namespace time_internal {
@@ -72,6 +64,17 @@ void TestFormatSpecifier(time_point<D> tp, time_zone tz, const std::string& fmt,
   EXPECT_EQ("xxx " + ans + " yyy", format("xxx " + fmt + " yyy", tp, tz));
 }
 
+// These tests sometimes run on platforms that have zoneinfo data so old
+// that the transition we are attempting to check does not exist, most
+// notably Android emulators.  Fortunately, AndroidZoneInfoSource supports
+// time_zone::version() so, in cases where we've learned that it matters,
+// we can make the check conditionally.
+int VersionCmp(time_zone tz, const std::string& target) {
+  std::string version = tz.version();
+  if (version.empty() && !target.empty()) return 1;  // unknown > known
+  return version.compare(target);
+}
+
 }  // namespace
 
 //
@@ -81,33 +84,36 @@ void TestFormatSpecifier(time_point<D> tp, time_zone tz, const std::string& fmt,
 TEST(Format, TimePointResolution) {
   const char kFmt[] = "%H:%M:%E*S";
   const time_zone utc = utc_time_zone();
-  const time_point<nanoseconds> t0 = system_clock::from_time_t(1420167845) +
-                                     milliseconds(123) + microseconds(456) +
-                                     nanoseconds(789);
-  EXPECT_EQ("03:04:05.123456789",
-            format(kFmt, time_point_cast<nanoseconds>(t0), utc));
-  EXPECT_EQ("03:04:05.123456",
-            format(kFmt, time_point_cast<microseconds>(t0), utc));
-  EXPECT_EQ("03:04:05.123",
-            format(kFmt, time_point_cast<milliseconds>(t0), utc));
+  const time_point<chrono::nanoseconds> t0 =
+      chrono::system_clock::from_time_t(1420167845) +
+      chrono::milliseconds(123) + chrono::microseconds(456) +
+      chrono::nanoseconds(789);
+  EXPECT_EQ(
+      "03:04:05.123456789",
+      format(kFmt, chrono::time_point_cast<chrono::nanoseconds>(t0), utc));
+  EXPECT_EQ(
+      "03:04:05.123456",
+      format(kFmt, chrono::time_point_cast<chrono::microseconds>(t0), utc));
+  EXPECT_EQ(
+      "03:04:05.123",
+      format(kFmt, chrono::time_point_cast<chrono::milliseconds>(t0), utc));
   EXPECT_EQ("03:04:05",
-            format(kFmt, time_point_cast<seconds>(t0), utc));
+            format(kFmt, chrono::time_point_cast<chrono::seconds>(t0), utc));
   EXPECT_EQ("03:04:05",
-            format(kFmt, time_point_cast<sys_seconds>(t0), utc));
+            format(kFmt, chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0), utc));
   EXPECT_EQ("03:04:00",
-            format(kFmt, time_point_cast<minutes>(t0), utc));
+            format(kFmt, chrono::time_point_cast<chrono::minutes>(t0), utc));
   EXPECT_EQ("03:00:00",
-            format(kFmt, time_point_cast<hours>(t0), utc));
+            format(kFmt, chrono::time_point_cast<chrono::hours>(t0), utc));
 }
 
 TEST(Format, TimePointExtendedResolution) {
   const char kFmt[] = "%H:%M:%E*S";
   const time_zone utc = utc_time_zone();
-  const time_point<sys_seconds> tp =
-      std::chrono::time_point_cast<sys_seconds>(
-          std::chrono::system_clock::from_time_t(0)) +
-      std::chrono::hours(12) + std::chrono::minutes(34) +
-      std::chrono::seconds(56);
+  const time_point<absl::time_internal::cctz::seconds> tp =
+      chrono::time_point_cast<absl::time_internal::cctz::seconds>(
+          chrono::system_clock::from_time_t(0)) +
+      chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56);
 
   EXPECT_EQ(
       "12:34:56.123456789012345",
@@ -132,7 +138,7 @@ TEST(Format, TimePointExtendedResolution) {
 
 TEST(Format, Basics) {
   time_zone tz = utc_time_zone();
-  time_point<nanoseconds> tp = system_clock::from_time_t(0);
+  time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0);
 
   // Starts with a couple basic edge cases.
   EXPECT_EQ("", format("", tp, tz));
@@ -145,8 +151,9 @@ TEST(Format, Basics) {
   std::string bigger(100000, 'x');
   EXPECT_EQ(bigger, format(bigger, tp, tz));
 
-  tp += hours(13) + minutes(4) + seconds(5);
-  tp += milliseconds(6) + microseconds(7) + nanoseconds(8);
+  tp += chrono::hours(13) + chrono::minutes(4) + chrono::seconds(5);
+  tp += chrono::milliseconds(6) + chrono::microseconds(7) +
+        chrono::nanoseconds(8);
   EXPECT_EQ("1970-01-01", format("%Y-%m-%d", tp, tz));
   EXPECT_EQ("13:04:05", format("%H:%M:%S", tp, tz));
   EXPECT_EQ("13:04:05.006", format("%H:%M:%E3S", tp, tz));
@@ -156,7 +163,7 @@ TEST(Format, Basics) {
 
 TEST(Format, PosixConversions) {
   const time_zone tz = utc_time_zone();
-  auto tp = system_clock::from_time_t(0);
+  auto tp = chrono::system_clock::from_time_t(0);
 
   TestFormatSpecifier(tp, tz, "%d", "01");
   TestFormatSpecifier(tp, tz, "%e", " 1");  // extension but internal support
@@ -196,7 +203,7 @@ TEST(Format, PosixConversions) {
 
 TEST(Format, LocaleSpecific) {
   const time_zone tz = utc_time_zone();
-  auto tp = system_clock::from_time_t(0);
+  auto tp = chrono::system_clock::from_time_t(0);
 
   TestFormatSpecifier(tp, tz, "%a", "Thu");
   TestFormatSpecifier(tp, tz, "%A", "Thursday");
@@ -205,8 +212,8 @@ TEST(Format, LocaleSpecific) {
 
   // %c should at least produce the numeric year and time-of-day.
   const std::string s = format("%c", tp, utc_time_zone());
-  EXPECT_THAT(s, HasSubstr("1970"));
-  EXPECT_THAT(s, HasSubstr("00:00:00"));
+  EXPECT_THAT(s, testing::HasSubstr("1970"));
+  EXPECT_THAT(s, testing::HasSubstr("00:00:00"));
 
   TestFormatSpecifier(tp, tz, "%p", "AM");
   TestFormatSpecifier(tp, tz, "%x", "01/01/70");
@@ -245,7 +252,7 @@ TEST(Format, LocaleSpecific) {
 
 TEST(Format, Escaping) {
   const time_zone tz = utc_time_zone();
-  auto tp = system_clock::from_time_t(0);
+  auto tp = chrono::system_clock::from_time_t(0);
 
   TestFormatSpecifier(tp, tz, "%%", "%");
   TestFormatSpecifier(tp, tz, "%%a", "%a");
@@ -266,8 +273,8 @@ TEST(Format, ExtendedSeconds) {
   const time_zone tz = utc_time_zone();
 
   // No subseconds.
-  time_point<nanoseconds> tp = system_clock::from_time_t(0);
-  tp += seconds(5);
+  time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0);
+  tp += chrono::seconds(5);
   EXPECT_EQ("05", format("%E*S", tp, tz));
   EXPECT_EQ("05", format("%E0S", tp, tz));
   EXPECT_EQ("05.0", format("%E1S", tp, tz));
@@ -287,7 +294,8 @@ TEST(Format, ExtendedSeconds) {
   EXPECT_EQ("05.000000000000000", format("%E15S", tp, tz));
 
   // With subseconds.
-  tp += milliseconds(6) + microseconds(7) + nanoseconds(8);
+  tp += chrono::milliseconds(6) + chrono::microseconds(7) +
+        chrono::nanoseconds(8);
   EXPECT_EQ("05.006007008", format("%E*S", tp, tz));
   EXPECT_EQ("05", format("%E0S", tp, tz));
   EXPECT_EQ("05.0", format("%E1S", tp, tz));
@@ -307,17 +315,18 @@ TEST(Format, ExtendedSeconds) {
   EXPECT_EQ("05.006007008000000", format("%E15S", tp, tz));
 
   // Times before the Unix epoch.
-  tp = system_clock::from_time_t(0) + microseconds(-1);
+  tp = chrono::system_clock::from_time_t(0) + chrono::microseconds(-1);
   EXPECT_EQ("1969-12-31 23:59:59.999999",
             format("%Y-%m-%d %H:%M:%E*S", tp, tz));
 
   // Here is a "%E*S" case we got wrong for a while.  While the first
   // instant below is correctly rendered as "...:07.333304", the second
   // one used to appear as "...:07.33330499999999999".
-  tp = system_clock::from_time_t(0) + microseconds(1395024427333304);
+  tp = chrono::system_clock::from_time_t(0) +
+       chrono::microseconds(1395024427333304);
   EXPECT_EQ("2014-03-17 02:47:07.333304",
             format("%Y-%m-%d %H:%M:%E*S", tp, tz));
-  tp += microseconds(1);
+  tp += chrono::microseconds(1);
   EXPECT_EQ("2014-03-17 02:47:07.333305",
             format("%Y-%m-%d %H:%M:%E*S", tp, tz));
 }
@@ -326,8 +335,8 @@ TEST(Format, ExtendedSubeconds) {
   const time_zone tz = utc_time_zone();
 
   // No subseconds.
-  time_point<nanoseconds> tp = system_clock::from_time_t(0);
-  tp += seconds(5);
+  time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0);
+  tp += chrono::seconds(5);
   EXPECT_EQ("0", format("%E*f", tp, tz));
   EXPECT_EQ("", format("%E0f", tp, tz));
   EXPECT_EQ("0", format("%E1f", tp, tz));
@@ -347,7 +356,8 @@ TEST(Format, ExtendedSubeconds) {
   EXPECT_EQ("000000000000000", format("%E15f", tp, tz));
 
   // With subseconds.
-  tp += milliseconds(6) + microseconds(7) + nanoseconds(8);
+  tp += chrono::milliseconds(6) + chrono::microseconds(7) +
+        chrono::nanoseconds(8);
   EXPECT_EQ("006007008", format("%E*f", tp, tz));
   EXPECT_EQ("", format("%E0f", tp, tz));
   EXPECT_EQ("0", format("%E1f", tp, tz));
@@ -367,17 +377,18 @@ TEST(Format, ExtendedSubeconds) {
   EXPECT_EQ("006007008000000", format("%E15f", tp, tz));
 
   // Times before the Unix epoch.
-  tp = system_clock::from_time_t(0) + microseconds(-1);
+  tp = chrono::system_clock::from_time_t(0) + chrono::microseconds(-1);
   EXPECT_EQ("1969-12-31 23:59:59.999999",
             format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz));
 
   // Here is a "%E*S" case we got wrong for a while.  While the first
   // instant below is correctly rendered as "...:07.333304", the second
   // one used to appear as "...:07.33330499999999999".
-  tp = system_clock::from_time_t(0) + microseconds(1395024427333304);
+  tp = chrono::system_clock::from_time_t(0) +
+       chrono::microseconds(1395024427333304);
   EXPECT_EQ("2014-03-17 02:47:07.333304",
             format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz));
-  tp += microseconds(1);
+  tp += chrono::microseconds(1);
   EXPECT_EQ("2014-03-17 02:47:07.333305",
             format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz));
 }
@@ -392,8 +403,8 @@ TEST(Format, CompareExtendSecondsVsSubseconds) {
   auto fmt_B = [](const std::string& prec) { return "%S.%E" + prec + "f"; };
 
   // No subseconds:
-  time_point<nanoseconds> tp = system_clock::from_time_t(0);
-  tp += seconds(5);
+  time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0);
+  tp += chrono::seconds(5);
   // ... %E*S and %S.%E*f are different.
   EXPECT_EQ("05", format(fmt_A("*"), tp, tz));
   EXPECT_EQ("05.0", format(fmt_B("*"), tp, tz));
@@ -409,7 +420,8 @@ TEST(Format, CompareExtendSecondsVsSubseconds) {
 
   // With subseconds:
   // ... %E*S and %S.%E*f are the same.
-  tp += milliseconds(6) + microseconds(7) + nanoseconds(8);
+  tp += chrono::milliseconds(6) + chrono::microseconds(7) +
+        chrono::nanoseconds(8);
   EXPECT_EQ("05.006007008", format(fmt_A("*"), tp, tz));
   EXPECT_EQ("05.006007008", format(fmt_B("*"), tp, tz));
   // ... %E0S and %S.%E0f are different.
@@ -424,7 +436,7 @@ TEST(Format, CompareExtendSecondsVsSubseconds) {
 }
 
 TEST(Format, ExtendedOffset) {
-  auto tp = system_clock::from_time_t(0);
+  auto tp = chrono::system_clock::from_time_t(0);
 
   time_zone tz = utc_time_zone();
   TestFormatSpecifier(tp, tz, "%Ez", "+00:00");
@@ -446,30 +458,28 @@ TEST(Format, ExtendedOffset) {
 
 TEST(Format, ExtendedSecondOffset) {
   const time_zone utc = utc_time_zone();
-  time_point<seconds> tp;
+  time_point<chrono::seconds> tp;
   time_zone tz;
 
   EXPECT_TRUE(load_time_zone("America/New_York", &tz));
   tp = convert(civil_second(1883, 11, 18, 16, 59, 59), utc);
   if (tz.lookup(tp).offset == -5 * 60 * 60) {
-    // We're likely dealing with zoneinfo that doesn't support really old
-    // timestamps, so America/New_York never looks to be on local mean time.
+    // It looks like the tzdata is only 32 bit (probably macOS),
+    // which bottoms out at 1901-12-13T20:45:52+00:00.
   } else {
     TestFormatSpecifier(tp, tz, "%E*z", "-04:56:02");
     TestFormatSpecifier(tp, tz, "%Ez", "-04:56");
   }
-  tp += seconds(1);
+  tp += chrono::seconds(1);
   TestFormatSpecifier(tp, tz, "%E*z", "-05:00:00");
 
   EXPECT_TRUE(load_time_zone("Europe/Moscow", &tz));
   tp = convert(civil_second(1919, 6, 30, 23, 59, 59), utc);
-#if defined(__ANDROID__) && __ANDROID_API__ < 25
-  // Only Android 'N'.1 and beyond have this tz2016g transition.
-#else
-  TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19");
-  TestFormatSpecifier(tp, tz, "%Ez", "+04:31");
-#endif
-  tp += seconds(1);
+  if (VersionCmp(tz, "2016g") >= 0) {
+    TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19");
+    TestFormatSpecifier(tp, tz, "%Ez", "+04:31");
+  }
+  tp += chrono::seconds(1);
   TestFormatSpecifier(tp, tz, "%E*z", "+04:00:00");
 }
 
@@ -510,44 +520,44 @@ TEST(Format, RFC3339Format) {
   time_zone tz;
   EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
 
-  time_point<nanoseconds> tp =
+  time_point<chrono::nanoseconds> tp =
       convert(civil_second(1977, 6, 28, 9, 8, 7), tz);
   EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_full, tp, tz));
   EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
 
-  tp += milliseconds(100);
+  tp += chrono::milliseconds(100);
   EXPECT_EQ("1977-06-28T09:08:07.1-07:00", format(RFC3339_full, tp, tz));
   EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
 
-  tp += milliseconds(20);
+  tp += chrono::milliseconds(20);
   EXPECT_EQ("1977-06-28T09:08:07.12-07:00", format(RFC3339_full, tp, tz));
   EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
 
-  tp += milliseconds(3);
+  tp += chrono::milliseconds(3);
   EXPECT_EQ("1977-06-28T09:08:07.123-07:00", format(RFC3339_full, tp, tz));
   EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
 
-  tp += microseconds(400);
+  tp += chrono::microseconds(400);
   EXPECT_EQ("1977-06-28T09:08:07.1234-07:00", format(RFC3339_full, tp, tz));
   EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
 
-  tp += microseconds(50);
+  tp += chrono::microseconds(50);
   EXPECT_EQ("1977-06-28T09:08:07.12345-07:00", format(RFC3339_full, tp, tz));
   EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
 
-  tp += microseconds(6);
+  tp += chrono::microseconds(6);
   EXPECT_EQ("1977-06-28T09:08:07.123456-07:00", format(RFC3339_full, tp, tz));
   EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
 
-  tp += nanoseconds(700);
+  tp += chrono::nanoseconds(700);
   EXPECT_EQ("1977-06-28T09:08:07.1234567-07:00", format(RFC3339_full, tp, tz));
   EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
 
-  tp += nanoseconds(80);
+  tp += chrono::nanoseconds(80);
   EXPECT_EQ("1977-06-28T09:08:07.12345678-07:00", format(RFC3339_full, tp, tz));
   EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
 
-  tp += nanoseconds(9);
+  tp += chrono::nanoseconds(9);
   EXPECT_EQ("1977-06-28T09:08:07.123456789-07:00",
             format(RFC3339_full, tp, tz));
   EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
@@ -570,13 +580,13 @@ TEST(Parse, TimePointResolution) {
   const char kFmt[] = "%H:%M:%E*S";
   const time_zone utc = utc_time_zone();
 
-  time_point<nanoseconds> tp_ns;
+  time_point<chrono::nanoseconds> tp_ns;
   EXPECT_TRUE(parse(kFmt, "03:04:05.123456789", utc, &tp_ns));
   EXPECT_EQ("03:04:05.123456789", format(kFmt, tp_ns, utc));
   EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_ns));
   EXPECT_EQ("03:04:05.123456", format(kFmt, tp_ns, utc));
 
-  time_point<microseconds> tp_us;
+  time_point<chrono::microseconds> tp_us;
   EXPECT_TRUE(parse(kFmt, "03:04:05.123456789", utc, &tp_us));
   EXPECT_EQ("03:04:05.123456", format(kFmt, tp_us, utc));
   EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_us));
@@ -584,7 +594,7 @@ TEST(Parse, TimePointResolution) {
   EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_us));
   EXPECT_EQ("03:04:05.123", format(kFmt, tp_us, utc));
 
-  time_point<milliseconds> tp_ms;
+  time_point<chrono::milliseconds> tp_ms;
   EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_ms));
   EXPECT_EQ("03:04:05.123", format(kFmt, tp_ms, utc));
   EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_ms));
@@ -592,17 +602,17 @@ TEST(Parse, TimePointResolution) {
   EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_ms));
   EXPECT_EQ("03:04:05", format(kFmt, tp_ms, utc));
 
-  time_point<seconds> tp_s;
+  time_point<chrono::seconds> tp_s;
   EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_s));
   EXPECT_EQ("03:04:05", format(kFmt, tp_s, utc));
   EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_s));
   EXPECT_EQ("03:04:05", format(kFmt, tp_s, utc));
 
-  time_point<minutes> tp_m;
+  time_point<chrono::minutes> tp_m;
   EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_m));
   EXPECT_EQ("03:04:00", format(kFmt, tp_m, utc));
 
-  time_point<hours> tp_h;
+  time_point<chrono::hours> tp_h;
   EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_h));
   EXPECT_EQ("03:00:00", format(kFmt, tp_h, utc));
 }
@@ -611,7 +621,7 @@ TEST(Parse, TimePointExtendedResolution) {
   const char kFmt[] = "%H:%M:%E*S";
   const time_zone utc = utc_time_zone();
 
-  time_point<sys_seconds> tp;
+  time_point<absl::time_internal::cctz::seconds> tp;
   detail::femtoseconds fs;
   EXPECT_TRUE(detail::parse(kFmt, "12:34:56.123456789012345", utc, &tp, &fs));
   EXPECT_EQ("12:34:56.123456789012345", detail::format(kFmt, tp, fs, utc));
@@ -629,11 +639,12 @@ TEST(Parse, TimePointExtendedResolution) {
 
 TEST(Parse, Basics) {
   time_zone tz = utc_time_zone();
-  time_point<nanoseconds> tp = system_clock::from_time_t(1234567890);
+  time_point<chrono::nanoseconds> tp =
+      chrono::system_clock::from_time_t(1234567890);
 
   // Simple edge cases.
   EXPECT_TRUE(parse("", "", tz, &tp));
-  EXPECT_EQ(system_clock::from_time_t(0), tp);  // everything defaulted
+  EXPECT_EQ(chrono::system_clock::from_time_t(0), tp);  // everything defaulted
   EXPECT_TRUE(parse(" ", " ", tz, &tp));
   EXPECT_TRUE(parse("  ", "  ", tz, &tp));
   EXPECT_TRUE(parse("x", "x", tz, &tp));
@@ -647,7 +658,7 @@ TEST(Parse, Basics) {
 TEST(Parse, WithTimeZone) {
   time_zone tz;
   EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
-  time_point<nanoseconds> tp;
+  time_point<chrono::nanoseconds> tp;
 
   // We can parse a std::string without a UTC offset if we supply a timezone.
   EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &tp));
@@ -672,7 +683,7 @@ TEST(Parse, WithTimeZone) {
 TEST(Parse, LeapSecond) {
   time_zone tz;
   EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
-  time_point<nanoseconds> tp;
+  time_point<chrono::nanoseconds> tp;
 
   // ":59" -> ":59"
   EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:59-08:00", tz, &tp));
@@ -696,7 +707,7 @@ TEST(Parse, LeapSecond) {
 
 TEST(Parse, ErrorCases) {
   const time_zone tz = utc_time_zone();
-  auto tp = system_clock::from_time_t(0);
+  auto tp = chrono::system_clock::from_time_t(0);
 
   // Illegal trailing data.
   EXPECT_FALSE(parse("%S", "123", tz, &tp));
@@ -739,7 +750,7 @@ TEST(Parse, ErrorCases) {
 
 TEST(Parse, PosixConversions) {
   time_zone tz = utc_time_zone();
-  auto tp = system_clock::from_time_t(0);
+  auto tp = chrono::system_clock::from_time_t(0);
   const auto reset = convert(civil_second(1977, 6, 28, 9, 8, 7), tz);
 
   tp = reset;
@@ -828,14 +839,14 @@ TEST(Parse, PosixConversions) {
 
   tp = reset;
   EXPECT_TRUE(parse("%s", "1234567890", tz, &tp));
-  EXPECT_EQ(system_clock::from_time_t(1234567890), tp);
+  EXPECT_EQ(chrono::system_clock::from_time_t(1234567890), tp);
 
   // %s conversion, like %z/%Ez, pays no heed to the optional zone.
   time_zone lax;
   EXPECT_TRUE(load_time_zone("America/Los_Angeles", &lax));
   tp = reset;
   EXPECT_TRUE(parse("%s", "1234567890", lax, &tp));
-  EXPECT_EQ(system_clock::from_time_t(1234567890), tp);
+  EXPECT_EQ(chrono::system_clock::from_time_t(1234567890), tp);
 
   // This is most important when the time has the same YMDhms
   // breakdown in the zone as some other time.  For example, ...
@@ -843,16 +854,16 @@ TEST(Parse, PosixConversions) {
   //  1414920600 in US/Pacific -> Sun Nov 2 01:30:00 2014 (PST)
   tp = reset;
   EXPECT_TRUE(parse("%s", "1414917000", lax, &tp));
-  EXPECT_EQ(system_clock::from_time_t(1414917000), tp);
+  EXPECT_EQ(chrono::system_clock::from_time_t(1414917000), tp);
   tp = reset;
   EXPECT_TRUE(parse("%s", "1414920600", lax, &tp));
-  EXPECT_EQ(system_clock::from_time_t(1414920600), tp);
+  EXPECT_EQ(chrono::system_clock::from_time_t(1414920600), tp);
 #endif
 }
 
 TEST(Parse, LocaleSpecific) {
   time_zone tz = utc_time_zone();
-  auto tp = system_clock::from_time_t(0);
+  auto tp = chrono::system_clock::from_time_t(0);
   const auto reset = convert(civil_second(1977, 6, 28, 9, 8, 7), tz);
 
   // %a is parsed but ignored.
@@ -983,7 +994,8 @@ TEST(Parse, LocaleSpecific) {
 
 TEST(Parse, ExtendedSeconds) {
   const time_zone tz = utc_time_zone();
-  const time_point<nanoseconds> unix_epoch = system_clock::from_time_t(0);
+  const time_point<chrono::nanoseconds> unix_epoch =
+      chrono::system_clock::from_time_t(0);
 
   // All %E<prec>S cases are treated the same as %E*S on input.
   auto precisions = {"*", "0", "1",  "2",  "3",  "4",  "5",  "6", "7",
@@ -991,47 +1003,47 @@ TEST(Parse, ExtendedSeconds) {
   for (const std::string& prec : precisions) {
     const std::string fmt = "%E" + prec + "S";
     SCOPED_TRACE(fmt);
-    time_point<nanoseconds> tp = unix_epoch;
+    time_point<chrono::nanoseconds> tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "5", tz, &tp));
-    EXPECT_EQ(unix_epoch + seconds(5), tp);
+    EXPECT_EQ(unix_epoch + chrono::seconds(5), tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "05", tz, &tp));
-    EXPECT_EQ(unix_epoch + seconds(5), tp);
+    EXPECT_EQ(unix_epoch + chrono::seconds(5), tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "05.0", tz, &tp));
-    EXPECT_EQ(unix_epoch + seconds(5), tp);
+    EXPECT_EQ(unix_epoch + chrono::seconds(5), tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "05.00", tz, &tp));
-    EXPECT_EQ(unix_epoch + seconds(5), tp);
+    EXPECT_EQ(unix_epoch + chrono::seconds(5), tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "05.6", tz, &tp));
-    EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(600), tp);
+    EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(600), tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "05.60", tz, &tp));
-    EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(600), tp);
+    EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(600), tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "05.600", tz, &tp));
-    EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(600), tp);
+    EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(600), tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "05.67", tz, &tp));
-    EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(670), tp);
+    EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(670), tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "05.670", tz, &tp));
-    EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(670), tp);
+    EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(670), tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "05.678", tz, &tp));
-    EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(678), tp);
+    EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(678), tp);
   }
 
   // Here is a "%E*S" case we got wrong for a while.  The fractional
   // part of the first instant is less than 2^31 and was correctly
   // parsed, while the second (and any subsecond field >=2^31) failed.
-  time_point<nanoseconds> tp = unix_epoch;
+  time_point<chrono::nanoseconds> tp = unix_epoch;
   EXPECT_TRUE(parse("%E*S", "0.2147483647", tz, &tp));
-  EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
+  EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
   tp = unix_epoch;
   EXPECT_TRUE(parse("%E*S", "0.2147483648", tz, &tp));
-  EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
+  EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
 
   // We should also be able to specify long strings of digits far
   // beyond the current resolution and have them convert the same way.
@@ -1039,18 +1051,18 @@ TEST(Parse, ExtendedSeconds) {
   EXPECT_TRUE(parse(
       "%E*S", "0.214748364801234567890123456789012345678901234567890123456789",
       tz, &tp));
-  EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
+  EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
 }
 
 TEST(Parse, ExtendedSecondsScan) {
   const time_zone tz = utc_time_zone();
-  time_point<nanoseconds> tp;
+  time_point<chrono::nanoseconds> tp;
   for (int ms = 0; ms < 1000; ms += 111) {
     for (int us = 0; us < 1000; us += 27) {
       const int micros = ms * 1000 + us;
       for (int ns = 0; ns < 1000; ns += 9) {
-        const auto expected =
-            system_clock::from_time_t(0) + nanoseconds(micros * 1000 + ns);
+        const auto expected = chrono::system_clock::from_time_t(0) +
+                              chrono::nanoseconds(micros * 1000 + ns);
         std::ostringstream oss;
         oss << "0." << std::setfill('0') << std::setw(3);
         oss << ms << std::setw(3) << us << std::setw(3) << ns;
@@ -1064,7 +1076,8 @@ TEST(Parse, ExtendedSecondsScan) {
 
 TEST(Parse, ExtendedSubeconds) {
   const time_zone tz = utc_time_zone();
-  const time_point<nanoseconds> unix_epoch = system_clock::from_time_t(0);
+  const time_point<chrono::nanoseconds> unix_epoch =
+      chrono::system_clock::from_time_t(0);
 
   // All %E<prec>f cases are treated the same as %E*f on input.
   auto precisions = {"*", "0", "1",  "2",  "3",  "4",  "5",  "6", "7",
@@ -1072,41 +1085,42 @@ TEST(Parse, ExtendedSubeconds) {
   for (const std::string& prec : precisions) {
     const std::string fmt = "%E" + prec + "f";
     SCOPED_TRACE(fmt);
-    time_point<nanoseconds> tp = unix_epoch - seconds(1);
+    time_point<chrono::nanoseconds> tp = unix_epoch - chrono::seconds(1);
     EXPECT_TRUE(parse(fmt, "", tz, &tp));
     EXPECT_EQ(unix_epoch, tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "6", tz, &tp));
-    EXPECT_EQ(unix_epoch + milliseconds(600), tp);
+    EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "60", tz, &tp));
-    EXPECT_EQ(unix_epoch + milliseconds(600), tp);
+    EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "600", tz, &tp));
-    EXPECT_EQ(unix_epoch + milliseconds(600), tp);
+    EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "67", tz, &tp));
-    EXPECT_EQ(unix_epoch + milliseconds(670), tp);
+    EXPECT_EQ(unix_epoch + chrono::milliseconds(670), tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "670", tz, &tp));
-    EXPECT_EQ(unix_epoch + milliseconds(670), tp);
+    EXPECT_EQ(unix_epoch + chrono::milliseconds(670), tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "678", tz, &tp));
-    EXPECT_EQ(unix_epoch + milliseconds(678), tp);
+    EXPECT_EQ(unix_epoch + chrono::milliseconds(678), tp);
     tp = unix_epoch;
     EXPECT_TRUE(parse(fmt, "6789", tz, &tp));
-    EXPECT_EQ(unix_epoch + milliseconds(678) + microseconds(900), tp);
+    EXPECT_EQ(
+        unix_epoch + chrono::milliseconds(678) + chrono::microseconds(900), tp);
   }
 
   // Here is a "%E*f" case we got wrong for a while.  The fractional
   // part of the first instant is less than 2^31 and was correctly
   // parsed, while the second (and any subsecond field >=2^31) failed.
-  time_point<nanoseconds> tp = unix_epoch;
+  time_point<chrono::nanoseconds> tp = unix_epoch;
   EXPECT_TRUE(parse("%E*f", "2147483647", tz, &tp));
-  EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
+  EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
   tp = unix_epoch;
   EXPECT_TRUE(parse("%E*f", "2147483648", tz, &tp));
-  EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
+  EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
 
   // We should also be able to specify long strings of digits far
   // beyond the current resolution and have them convert the same way.
@@ -1114,11 +1128,11 @@ TEST(Parse, ExtendedSubeconds) {
   EXPECT_TRUE(parse(
       "%E*f", "214748364801234567890123456789012345678901234567890123456789",
       tz, &tp));
-  EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
+  EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
 }
 
 TEST(Parse, ExtendedSubecondsScan) {
-  time_point<nanoseconds> tp;
+  time_point<chrono::nanoseconds> tp;
   const time_zone tz = utc_time_zone();
   for (int ms = 0; ms < 1000; ms += 111) {
     for (int us = 0; us < 1000; us += 27) {
@@ -1128,14 +1142,14 @@ TEST(Parse, ExtendedSubecondsScan) {
         oss << std::setfill('0') << std::setw(3) << ms;
         oss << std::setw(3) << us << std::setw(3) << ns;
         const std::string nanos = oss.str();
-        const auto expected =
-            system_clock::from_time_t(0) + nanoseconds(micros * 1000 + ns);
+        const auto expected = chrono::system_clock::from_time_t(0) +
+                              chrono::nanoseconds(micros * 1000 + ns);
         for (int ps = 0; ps < 1000; ps += 250) {
           std::ostringstream oss;
           oss << std::setfill('0') << std::setw(3) << ps;
           const std::string input = nanos + oss.str() + "999";
           EXPECT_TRUE(parse("%E*f", input, tz, &tp));
-          EXPECT_EQ(expected + nanoseconds(ps) / 1000, tp) << input;
+          EXPECT_EQ(expected + chrono::nanoseconds(ps) / 1000, tp) << input;
         }
       }
     }
@@ -1144,7 +1158,7 @@ TEST(Parse, ExtendedSubecondsScan) {
 
 TEST(Parse, ExtendedOffset) {
   const time_zone utc = utc_time_zone();
-  time_point<sys_seconds> tp;
+  time_point<absl::time_internal::cctz::seconds> tp;
 
   // %z against +-HHMM.
   EXPECT_TRUE(parse("%z", "+0000", utc, &tp));
@@ -1194,7 +1208,7 @@ TEST(Parse, ExtendedOffset) {
 
 TEST(Parse, ExtendedSecondOffset) {
   const time_zone utc = utc_time_zone();
-  time_point<sys_seconds> tp;
+  time_point<absl::time_internal::cctz::seconds> tp;
 
   // %Ez against +-HH:MM:SS.
   EXPECT_TRUE(parse("%Ez", "+00:00:00", utc, &tp));
@@ -1263,7 +1277,7 @@ TEST(Parse, ExtendedSecondOffset) {
 TEST(Parse, ExtendedYears) {
   const time_zone utc = utc_time_zone();
   const char e4y_fmt[] = "%E4Y%m%d";  // no separators
-  time_point<sys_seconds> tp;
+  time_point<absl::time_internal::cctz::seconds> tp;
 
   // %E4Y consumes exactly four chars, including any sign.
   EXPECT_TRUE(parse(e4y_fmt, "-9991127", utc, &tp));
@@ -1294,45 +1308,45 @@ TEST(Parse, ExtendedYears) {
 
 TEST(Parse, RFC3339Format) {
   const time_zone tz = utc_time_zone();
-  time_point<nanoseconds> tp;
+  time_point<chrono::nanoseconds> tp;
   EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00+00:00", tz, &tp));
   ExpectTime(tp, tz, 2014, 2, 12, 20, 21, 0, 0, false, "UTC");
 
   // Check that %Ez also accepts "Z" as a synonym for "+00:00".
-  time_point<nanoseconds> tp2;
+  time_point<chrono::nanoseconds> tp2;
   EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00Z", tz, &tp2));
   EXPECT_EQ(tp, tp2);
 }
 
 TEST(Parse, MaxRange) {
   const time_zone utc = utc_time_zone();
-  time_point<sys_seconds> tp;
+  time_point<absl::time_internal::cctz::seconds> tp;
 
   // tests the upper limit using +00:00 offset
   EXPECT_TRUE(
       parse(RFC3339_sec, "292277026596-12-04T15:30:07+00:00", utc, &tp));
-  EXPECT_EQ(tp, time_point<sys_seconds>::max());
+  EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::max());
   EXPECT_FALSE(
       parse(RFC3339_sec, "292277026596-12-04T15:30:08+00:00", utc, &tp));
 
   // tests the upper limit using -01:00 offset
   EXPECT_TRUE(
       parse(RFC3339_sec, "292277026596-12-04T14:30:07-01:00", utc, &tp));
-  EXPECT_EQ(tp, time_point<sys_seconds>::max());
+  EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::max());
   EXPECT_FALSE(
       parse(RFC3339_sec, "292277026596-12-04T15:30:07-01:00", utc, &tp));
 
   // tests the lower limit using +00:00 offset
   EXPECT_TRUE(
       parse(RFC3339_sec, "-292277022657-01-27T08:29:52+00:00", utc, &tp));
-  EXPECT_EQ(tp, time_point<sys_seconds>::min());
+  EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::min());
   EXPECT_FALSE(
       parse(RFC3339_sec, "-292277022657-01-27T08:29:51+00:00", utc, &tp));
 
   // tests the lower limit using +01:00 offset
   EXPECT_TRUE(
       parse(RFC3339_sec, "-292277022657-01-27T09:29:52+01:00", utc, &tp));
-  EXPECT_EQ(tp, time_point<sys_seconds>::min());
+  EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::min());
   EXPECT_FALSE(
       parse(RFC3339_sec, "-292277022657-01-27T08:29:51+01:00", utc, &tp));
 
@@ -1355,11 +1369,11 @@ TEST(FormatParse, RoundTrip) {
   time_zone lax;
   EXPECT_TRUE(load_time_zone("America/Los_Angeles", &lax));
   const auto in = convert(civil_second(1977, 6, 28, 9, 8, 7), lax);
-  const auto subseconds = nanoseconds(654321);
+  const auto subseconds = chrono::nanoseconds(654321);
 
   // RFC3339, which renders subseconds.
   {
-    time_point<nanoseconds> out;
+    time_point<chrono::nanoseconds> out;
     const std::string s = format(RFC3339_full, in + subseconds, lax);
     EXPECT_TRUE(parse(RFC3339_full, s, lax, &out)) << s;
     EXPECT_EQ(in + subseconds, out);  // RFC3339_full includes %Ez
@@ -1367,7 +1381,7 @@ TEST(FormatParse, RoundTrip) {
 
   // RFC1123, which only does whole seconds.
   {
-    time_point<nanoseconds> out;
+    time_point<chrono::nanoseconds> out;
     const std::string s = format(RFC1123_full, in, lax);
     EXPECT_TRUE(parse(RFC1123_full, s, lax, &out)) << s;
     EXPECT_EQ(in, out);  // RFC1123_full includes %z
@@ -1380,7 +1394,7 @@ TEST(FormatParse, RoundTrip) {
   // Even though we don't know what %c will produce, it should roundtrip,
   // but only in the 0-offset timezone.
   {
-    time_point<nanoseconds> out;
+    time_point<chrono::nanoseconds> out;
     time_zone utc = utc_time_zone();
     const std::string s = format("%c", in, utc);
     EXPECT_TRUE(parse("%c", s, utc, &out)) << s;
@@ -1391,18 +1405,18 @@ TEST(FormatParse, RoundTrip) {
 
 TEST(FormatParse, RoundTripDistantFuture) {
   const time_zone utc = utc_time_zone();
-  const time_point<sys_seconds> in = time_point<sys_seconds>::max();
+  const time_point<absl::time_internal::cctz::seconds> in = time_point<absl::time_internal::cctz::seconds>::max();
   const std::string s = format(RFC3339_full, in, utc);
-  time_point<sys_seconds> out;
+  time_point<absl::time_internal::cctz::seconds> out;
   EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s;
   EXPECT_EQ(in, out);
 }
 
 TEST(FormatParse, RoundTripDistantPast) {
   const time_zone utc = utc_time_zone();
-  const time_point<sys_seconds> in = time_point<sys_seconds>::min();
+  const time_point<absl::time_internal::cctz::seconds> in = time_point<absl::time_internal::cctz::seconds>::min();
   const std::string s = format(RFC3339_full, in, utc);
-  time_point<sys_seconds> out;
+  time_point<absl::time_internal::cctz::seconds> out;
   EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s;
   EXPECT_EQ(in, out);
 }
diff --git a/absl/time/internal/cctz/src/time_zone_if.h b/absl/time/internal/cctz/src/time_zone_if.h
index ce4da1b728b6..e4bd3866a87b 100644
--- a/absl/time/internal/cctz/src/time_zone_if.h
+++ b/absl/time/internal/cctz/src/time_zone_if.h
@@ -37,30 +37,32 @@ class TimeZoneIf {
   virtual ~TimeZoneIf();
 
   virtual time_zone::absolute_lookup BreakTime(
-      const time_point<sys_seconds>& tp) const = 0;
+      const time_point<seconds>& tp) const = 0;
   virtual time_zone::civil_lookup MakeTime(
       const civil_second& cs) const = 0;
 
+  virtual bool NextTransition(const time_point<seconds>& tp,
+                              time_zone::civil_transition* trans) const = 0;
+  virtual bool PrevTransition(const time_point<seconds>& tp,
+                              time_zone::civil_transition* trans) const = 0;
+
+  virtual std::string Version() const = 0;
   virtual std::string Description() const = 0;
-  virtual bool NextTransition(time_point<sys_seconds>* tp) const = 0;
-  virtual bool PrevTransition(time_point<sys_seconds>* tp) const = 0;
 
  protected:
   TimeZoneIf() {}
 };
 
-// Convert between time_point<sys_seconds> and a count of seconds since
-// the Unix epoch.  We assume that the std::chrono::system_clock and the
+// Convert between time_point<seconds> and a count of seconds since the
+// Unix epoch.  We assume that the std::chrono::system_clock and the
 // Unix clock are second aligned, but not that they share an epoch.
-inline std::int_fast64_t ToUnixSeconds(const time_point<sys_seconds>& tp) {
-  return (tp - std::chrono::time_point_cast<sys_seconds>(
-                   std::chrono::system_clock::from_time_t(0)))
-      .count();
+inline std::int_fast64_t ToUnixSeconds(const time_point<seconds>& tp) {
+  return (tp - std::chrono::time_point_cast<seconds>(
+                   std::chrono::system_clock::from_time_t(0))).count();
 }
-inline time_point<sys_seconds> FromUnixSeconds(std::int_fast64_t t) {
-  return std::chrono::time_point_cast<sys_seconds>(
-             std::chrono::system_clock::from_time_t(0)) +
-         sys_seconds(t);
+inline time_point<seconds> FromUnixSeconds(std::int_fast64_t t) {
+  return std::chrono::time_point_cast<seconds>(
+             std::chrono::system_clock::from_time_t(0)) + seconds(t);
 }
 
 }  // namespace cctz
diff --git a/absl/time/internal/cctz/src/time_zone_impl.cc b/absl/time/internal/cctz/src/time_zone_impl.cc
index b3f635f786ff..3062ccd3ceb0 100644
--- a/absl/time/internal/cctz/src/time_zone_impl.cc
+++ b/absl/time/internal/cctz/src/time_zone_impl.cc
@@ -45,8 +45,8 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
   const time_zone::Impl* const utc_impl = UTCImpl();
 
   // First check for UTC (which is never a key in time_zone_map).
-  auto offset = sys_seconds::zero();
-  if (FixedOffsetFromName(name, &offset) && offset == sys_seconds::zero()) {
+  auto offset = seconds::zero();
+  if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) {
     *tz = time_zone(utc_impl);
     return true;
   }
@@ -83,15 +83,6 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
   return impl != utc_impl;
 }
 
-const time_zone::Impl& time_zone::Impl::get(const time_zone& tz) {
-  if (tz.impl_ == nullptr) {
-    // Dereferencing an implicit-UTC time_zone is expected to be
-    // rare, so we don't mind paying a small synchronization cost.
-    return *UTCImpl();
-  }
-  return *tz.impl_;
-}
-
 void time_zone::Impl::ClearTimeZoneMapTestOnly() {
   std::lock_guard<std::mutex> lock(time_zone_mutex);
   if (time_zone_map != nullptr) {
diff --git a/absl/time/internal/cctz/src/time_zone_impl.h b/absl/time/internal/cctz/src/time_zone_impl.h
index 2c1c30b690e5..14965ef54bfb 100644
--- a/absl/time/internal/cctz/src/time_zone_impl.h
+++ b/absl/time/internal/cctz/src/time_zone_impl.h
@@ -37,19 +37,18 @@ class time_zone::Impl {
   // some other kind of error occurs. Note that loading "UTC" never fails.
   static bool LoadTimeZone(const std::string& name, time_zone* tz);
 
-  // Dereferences the time_zone to obtain its Impl.
-  static const time_zone::Impl& get(const time_zone& tz);
-
   // Clears the map of cached time zones.  Primarily for use in benchmarks
   // that gauge the performance of loading/parsing the time-zone data.
   static void ClearTimeZoneMapTestOnly();
 
   // The primary key is the time-zone ID (e.g., "America/New_York").
-  const std::string& name() const { return name_; }
+  const std::string& Name() const {
+    // TODO: It would nice if the zoneinfo data included the zone name.
+    return name_;
+  }
 
   // Breaks a time_point down to civil-time components in this time zone.
-  time_zone::absolute_lookup BreakTime(
-      const time_point<sys_seconds>& tp) const {
+  time_zone::absolute_lookup BreakTime(const time_point<seconds>& tp) const {
     return zone_->BreakTime(tp);
   }
 
@@ -60,28 +59,22 @@ class time_zone::Impl {
     return zone_->MakeTime(cs);
   }
 
-  // Returns an implementation-specific description of this time zone.
-  std::string Description() const { return zone_->Description(); }
-
   // Finds the time of the next/previous offset change in this time zone.
-  //
-  // By definition, NextTransition(&tp) returns false when tp has its
-  // maximum value, and PrevTransition(&tp) returns false when tp has its
-  // mimimum value.  If the zone has no transitions, the result will also
-  // be false no matter what the argument.
-  //
-  // Otherwise, when tp has its mimimum value, NextTransition(&tp) returns
-  // true and sets tp to the first recorded transition.  Chains of calls
-  // to NextTransition()/PrevTransition() will eventually return false,
-  // but it is unspecified exactly when NextTransition(&tp) jumps to false,
-  // or what time is set by PrevTransition(&tp) for a very distant tp.
-  bool NextTransition(time_point<sys_seconds>* tp) const {
-    return zone_->NextTransition(tp);
+  bool NextTransition(const time_point<seconds>& tp,
+                      time_zone::civil_transition* trans) const {
+    return zone_->NextTransition(tp, trans);
   }
-  bool PrevTransition(time_point<sys_seconds>* tp) const {
-    return zone_->PrevTransition(tp);
+  bool PrevTransition(const time_point<seconds>& tp,
+                      time_zone::civil_transition* trans) const {
+    return zone_->PrevTransition(tp, trans);
   }
 
+  // Returns an implementation-defined version std::string for this time zone.
+  std::string Version() const { return zone_->Version(); }
+
+  // Returns an implementation-defined description of this time zone.
+  std::string Description() const { return zone_->Description(); }
+
  private:
   explicit Impl(const std::string& name);
   static const Impl* UTCImpl();
diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc
index 20bba28b363b..bf73635d4c6a 100644
--- a/absl/time/internal/cctz/src/time_zone_info.cc
+++ b/absl/time/internal/cctz/src/time_zone_info.cc
@@ -140,7 +140,7 @@ std::int_fast64_t TransOffset(bool leap_year, int jan1_weekday,
   return (days * kSecsPerDay) + pt.time.offset;
 }
 
-inline time_zone::civil_lookup MakeUnique(const time_point<sys_seconds>& tp) {
+inline time_zone::civil_lookup MakeUnique(const time_point<seconds>& tp) {
   time_zone::civil_lookup cl;
   cl.kind = time_zone::civil_lookup::UNIQUE;
   cl.pre = cl.trans = cl.post = tp;
@@ -179,21 +179,20 @@ inline civil_second YearShift(const civil_second& cs, year_t shift) {
 }  // namespace
 
 // What (no leap-seconds) UTC+seconds zoneinfo would look like.
-bool TimeZoneInfo::ResetToBuiltinUTC(const sys_seconds& offset) {
+bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
   transition_types_.resize(1);
   TransitionType& tt(transition_types_.back());
   tt.utc_offset = static_cast<std::int_least32_t>(offset.count());
   tt.is_dst = false;
   tt.abbr_index = 0;
 
-  // We temporarily add some redundant, contemporary (2012 through 2021)
+  // We temporarily add some redundant, contemporary (2013 through 2023)
   // transitions for performance reasons.  See TimeZoneInfo::LocalTime().
   // TODO: Fix the performance issue and remove the extra transitions.
   transitions_.clear();
   transitions_.reserve(12);
   for (const std::int_fast64_t unix_time : {
            -(1LL << 59),  // BIG_BANG
-           1325376000LL,  // 2012-01-01T00:00:00+00:00
            1356998400LL,  // 2013-01-01T00:00:00+00:00
            1388534400LL,  // 2014-01-01T00:00:00+00:00
            1420070400LL,  // 2015-01-01T00:00:00+00:00
@@ -203,6 +202,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const sys_seconds& offset) {
            1546300800LL,  // 2019-01-01T00:00:00+00:00
            1577836800LL,  // 2020-01-01T00:00:00+00:00
            1609459200LL,  // 2021-01-01T00:00:00+00:00
+           1640995200LL,  // 2022-01-01T00:00:00+00:00
+           1672531200LL,  // 2023-01-01T00:00:00+00:00
            2147483647LL,  // 2^31 - 1
        }) {
     Transition& tr(*transitions_.emplace(transitions_.end()));
@@ -218,8 +219,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const sys_seconds& offset) {
   future_spec_.clear();  // never needed for a fixed-offset zone
   extended_ = false;
 
-  tt.civil_max = LocalTime(sys_seconds::max().count(), tt).cs;
-  tt.civil_min = LocalTime(sys_seconds::min().count(), tt).cs;
+  tt.civil_max = LocalTime(seconds::max().count(), tt).cs;
+  tt.civil_min = LocalTime(seconds::min().count(), tt).cs;
 
   transitions_.shrink_to_fit();
   return true;
@@ -519,6 +520,13 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
 
   // We don't check for EOF so that we're forwards compatible.
 
+  // If we did not find version information during the standard loading
+  // process (as of tzh_version '3' that is unsupported), then ask the
+  // ZoneInfoSource for any out-of-bound version std::string it may be privy to.
+  if (version_.empty()) {
+    version_ = zip->Version();
+  }
+
   // 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).
@@ -565,10 +573,10 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
   }
 
   // Compute the maximum/minimum civil times that can be converted to a
-  // time_point<sys_seconds> for each of the zone's transition types.
+  // time_point<seconds> for each of the zone's transition types.
   for (auto& tt : transition_types_) {
-    tt.civil_max = LocalTime(sys_seconds::max().count(), tt).cs;
-    tt.civil_min = LocalTime(sys_seconds::min().count(), tt).cs;
+    tt.civil_max = LocalTime(seconds::max().count(), tt).cs;
+    tt.civil_min = LocalTime(seconds::min().count(), tt).cs;
   }
 
   transitions_.shrink_to_fit();
@@ -605,6 +613,10 @@ class FileZoneInfoSource : public ZoneInfoSource {
     if (rc == 0) len_ -= offset;
     return rc;
   }
+  std::string Version() const override {
+    // TODO: It would nice if the zoneinfo data included the tzdb version.
+    return std::string();
+  }
 
  protected:
   explicit FileZoneInfoSource(
@@ -654,14 +666,15 @@ std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open(
   return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(fp, length));
 }
 
-#if defined(__ANDROID__)
 class AndroidZoneInfoSource : public FileZoneInfoSource {
  public:
   static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
+  std::string Version() const override { return version_; }
 
  private:
-  explicit AndroidZoneInfoSource(FILE* fp, std::size_t len)
-      : FileZoneInfoSource(fp, len) {}
+  explicit AndroidZoneInfoSource(FILE* fp, std::size_t len, const char* vers)
+      : FileZoneInfoSource(fp, len), version_(vers) {}
+  std::string version_;
 };
 
 std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
@@ -669,6 +682,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
   // Use of the "file:" prefix is intended for testing purposes only.
   if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5));
 
+#if defined(__ANDROID__)
   // See Android's libc/tzcode/bionic.cpp for additional information.
   for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata",
                              "/system/usr/share/zoneinfo/tzdata"}) {
@@ -678,6 +692,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
     char hbuf[24];  // covers header.zonetab_offset too
     if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue;
     if (strncmp(hbuf, "tzdata", 6) != 0) continue;
+    const char* vers = (hbuf[11] == '\0') ? hbuf + 6 : "";
     const std::int_fast32_t index_offset = Decode32(hbuf + 12);
     const std::int_fast32_t data_offset = Decode32(hbuf + 16);
     if (index_offset < 0 || data_offset < index_offset) continue;
@@ -698,13 +713,13 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
       if (strcmp(name.c_str(), ebuf) == 0) {
         if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break;
         return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource(
-            fp.release(), static_cast<std::size_t>(length)));
+            fp.release(), static_cast<std::size_t>(length), vers));
       }
     }
   }
+#endif  // __ANDROID__
   return nullptr;
 }
-#endif
 
 }  // namespace
 
@@ -713,7 +728,7 @@ bool TimeZoneInfo::Load(const std::string& name) {
   // zone never fails because the simple, fixed-offset state can be
   // internally generated. Note that this depends on our choice to not
   // accept leap-second encoded ("right") zoneinfo.
-  auto offset = sys_seconds::zero();
+  auto offset = seconds::zero();
   if (FixedOffsetFromName(name, &offset)) {
     return ResetToBuiltinUTC(offset);
   }
@@ -722,9 +737,7 @@ bool TimeZoneInfo::Load(const std::string& name) {
   auto zip = cctz_extension::zone_info_source_factory(
       name, [](const std::string& name) -> std::unique_ptr<ZoneInfoSource> {
         if (auto zip = FileZoneInfoSource::Open(name)) return zip;
-#if defined(__ANDROID__)
         if (auto zip = AndroidZoneInfoSource::Open(name)) return zip;
-#endif
         return nullptr;
       });
   return zip != nullptr && Load(name, zip.get());
@@ -755,14 +768,14 @@ time_zone::civil_lookup TimeZoneInfo::TimeLocal(const civil_second& cs,
                                                 year_t c4_shift) const {
   assert(last_year_ - 400 < cs.year() && cs.year() <= last_year_);
   time_zone::civil_lookup cl = MakeTime(cs);
-  if (c4_shift > sys_seconds::max().count() / kSecsPer400Years) {
-    cl.pre = cl.trans = cl.post = time_point<sys_seconds>::max();
+  if (c4_shift > seconds::max().count() / kSecsPer400Years) {
+    cl.pre = cl.trans = cl.post = time_point<seconds>::max();
   } else {
-    const auto offset = sys_seconds(c4_shift * kSecsPer400Years);
-    const auto limit = time_point<sys_seconds>::max() - offset;
+    const auto offset = seconds(c4_shift * kSecsPer400Years);
+    const auto limit = time_point<seconds>::max() - offset;
     for (auto* tp : {&cl.pre, &cl.trans, &cl.post}) {
       if (*tp > limit) {
-        *tp = time_point<sys_seconds>::max();
+        *tp = time_point<seconds>::max();
       } else {
         *tp += offset;
       }
@@ -772,7 +785,7 @@ time_zone::civil_lookup TimeZoneInfo::TimeLocal(const civil_second& cs,
 }
 
 time_zone::absolute_lookup TimeZoneInfo::BreakTime(
-    const time_point<sys_seconds>& tp) const {
+    const time_point<seconds>& tp) const {
   std::int_fast64_t unix_time = ToUnixSeconds(tp);
   const std::size_t timecnt = transitions_.size();
   assert(timecnt != 0);  // We always add a transition.
@@ -788,7 +801,7 @@ time_zone::absolute_lookup TimeZoneInfo::BreakTime(
       const std::int_fast64_t diff =
           unix_time - transitions_[timecnt - 1].unix_time;
       const year_t shift = diff / kSecsPer400Years + 1;
-      const auto d = sys_seconds(shift * kSecsPer400Years);
+      const auto d = seconds(shift * kSecsPer400Years);
       time_zone::absolute_lookup al = BreakTime(tp - d);
       al.cs = YearShift(al.cs, shift * 400);
       return al;
@@ -847,7 +860,7 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const {
     if (tr->prev_civil_sec >= cs) {
       // Before first transition, so use the default offset.
       const TransitionType& tt(transition_types_[default_transition_type_]);
-      if (cs < tt.civil_min) return MakeUnique(time_point<sys_seconds>::min());
+      if (cs < tt.civil_min) return MakeUnique(time_point<seconds>::min());
       return MakeUnique(cs - (civil_second() + tt.utc_offset));
     }
     // tr->prev_civil_sec < cs < tr->civil_sec
@@ -864,7 +877,7 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const {
         return TimeLocal(YearShift(cs, shift * -400), shift);
       }
       const TransitionType& tt(transition_types_[tr->type_index]);
-      if (cs > tt.civil_max) return MakeUnique(time_point<sys_seconds>::max());
+      if (cs > tt.civil_max) return MakeUnique(time_point<seconds>::max());
       return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
     }
     // tr->civil_sec <= cs <= tr->prev_civil_sec
@@ -885,17 +898,20 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const {
   return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
 }
 
+std::string TimeZoneInfo::Version() const {
+  return version_;
+}
+
 std::string TimeZoneInfo::Description() const {
   std::ostringstream oss;
-  // TODO: It would nice if the zoneinfo data included the zone name.
-  // TODO: It would nice if the zoneinfo data included the tzdb version.
   oss << "#trans=" << transitions_.size();
   oss << " #types=" << transition_types_.size();
   oss << " spec='" << future_spec_ << "'";
   return oss.str();
 }
 
-bool TimeZoneInfo::NextTransition(time_point<sys_seconds>* tp) const {
+bool TimeZoneInfo::NextTransition(const time_point<seconds>& tp,
+                                  time_zone::civil_transition* trans) const {
   if (transitions_.empty()) return false;
   const Transition* begin = &transitions_[0];
   const Transition* end = begin + transitions_.size();
@@ -904,22 +920,24 @@ bool TimeZoneInfo::NextTransition(time_point<sys_seconds>* tp) const {
     // really a sentinel, not a transition.  See tz/zic.c.
     ++begin;
   }
-  std::int_fast64_t unix_time = ToUnixSeconds(*tp);
+  std::int_fast64_t unix_time = ToUnixSeconds(tp);
   const Transition target = { unix_time };
   const Transition* tr = std::upper_bound(begin, end, target,
                                           Transition::ByUnixTime());
-  if (tr != begin) {  // skip no-op transitions
-    for (; tr != end; ++tr) {
-      if (!EquivTransitions(tr[-1].type_index, tr[0].type_index)) break;
-    }
+  for (; tr != end; ++tr) {  // skip no-op transitions
+    std::uint_fast8_t prev_type_index =
+        (tr == begin) ? default_transition_type_ : tr[-1].type_index;
+    if (!EquivTransitions(prev_type_index, tr[0].type_index)) break;
   }
   // When tr == end we return false, ignoring future_spec_.
   if (tr == end) return false;
-  *tp = FromUnixSeconds(tr->unix_time);
+  trans->from = tr->prev_civil_sec + 1;
+  trans->to = tr->civil_sec;
   return true;
 }
 
-bool TimeZoneInfo::PrevTransition(time_point<sys_seconds>* tp) const {
+bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp,
+                                  time_zone::civil_transition* trans) const {
   if (transitions_.empty()) return false;
   const Transition* begin = &transitions_[0];
   const Transition* end = begin + transitions_.size();
@@ -928,11 +946,12 @@ bool TimeZoneInfo::PrevTransition(time_point<sys_seconds>* tp) const {
     // really a sentinel, not a transition.  See tz/zic.c.
     ++begin;
   }
-  std::int_fast64_t unix_time = ToUnixSeconds(*tp);
-  if (FromUnixSeconds(unix_time) != *tp) {
+  std::int_fast64_t unix_time = ToUnixSeconds(tp);
+  if (FromUnixSeconds(unix_time) != tp) {
     if (unix_time == std::numeric_limits<std::int_fast64_t>::max()) {
       if (end == begin) return false;  // Ignore future_spec_.
-      *tp = FromUnixSeconds((--end)->unix_time);
+      trans->from = (--end)->prev_civil_sec + 1;
+      trans->to = end->civil_sec;
       return true;
     }
     unix_time += 1;  // ceils
@@ -940,14 +959,15 @@ bool TimeZoneInfo::PrevTransition(time_point<sys_seconds>* tp) const {
   const Transition target = { unix_time };
   const Transition* tr = std::lower_bound(begin, end, target,
                                           Transition::ByUnixTime());
-  if (tr != begin) {  // skip no-op transitions
-    for (; tr - 1 != begin; --tr) {
-      if (!EquivTransitions(tr[-2].type_index, tr[-1].type_index)) break;
-    }
+  for (; tr != begin; --tr) {  // skip no-op transitions
+    std::uint_fast8_t prev_type_index =
+        (tr - 1 == begin) ? default_transition_type_ : tr[-2].type_index;
+    if (!EquivTransitions(prev_type_index, tr[-1].type_index)) break;
   }
   // When tr == end we return the "last" transition, ignoring future_spec_.
   if (tr == begin) return false;
-  *tp = FromUnixSeconds((--tr)->unix_time);
+  trans->from = (--tr)->prev_civil_sec + 1;
+  trans->to = tr->civil_sec;
   return true;
 }
 
diff --git a/absl/time/internal/cctz/src/time_zone_info.h b/absl/time/internal/cctz/src/time_zone_info.h
index b4d1696bf61b..958e9b6bd744 100644
--- a/absl/time/internal/cctz/src/time_zone_info.h
+++ b/absl/time/internal/cctz/src/time_zone_info.h
@@ -71,12 +71,15 @@ class TimeZoneInfo : public TimeZoneIf {
 
   // TimeZoneIf implementations.
   time_zone::absolute_lookup BreakTime(
-      const time_point<sys_seconds>& tp) const override;
+      const time_point<seconds>& tp) const override;
   time_zone::civil_lookup MakeTime(
       const civil_second& cs) const override;
+  bool NextTransition(const time_point<seconds>& tp,
+                      time_zone::civil_transition* trans) const override;
+  bool PrevTransition(const time_point<seconds>& tp,
+                      time_zone::civil_transition* trans) const override;
+  std::string Version() const override;
   std::string Description() const override;
-  bool NextTransition(time_point<sys_seconds>* tp) const override;
-  bool PrevTransition(time_point<sys_seconds>* tp) const override;
 
  private:
   struct Header {  // counts of:
@@ -98,7 +101,7 @@ class TimeZoneInfo : public TimeZoneIf {
                         std::uint_fast8_t tt2_index) const;
   void ExtendTransitions(const std::string& name, const Header& hdr);
 
-  bool ResetToBuiltinUTC(const sys_seconds& offset);
+  bool ResetToBuiltinUTC(const seconds& offset);
   bool Load(const std::string& name, ZoneInfoSource* zip);
 
   // Helpers for BreakTime() and MakeTime().
@@ -114,6 +117,7 @@ class TimeZoneInfo : public TimeZoneIf {
   std::uint_fast8_t default_transition_type_;  // for before first transition
   std::string abbreviations_;  // all the NUL-terminated abbreviations
 
+  std::string version_;      // the tzdata version if available
   std::string future_spec_;  // for after the last zic transition
   bool extended_;            // future_spec_ was used to generate transitions
   year_t last_year_;         // the final year of the generated transitions
diff --git a/absl/time/internal/cctz/src/time_zone_libc.cc b/absl/time/internal/cctz/src/time_zone_libc.cc
index b0b56a522332..074c8d0a4a40 100644
--- a/absl/time/internal/cctz/src/time_zone_libc.cc
+++ b/absl/time/internal/cctz/src/time_zone_libc.cc
@@ -91,7 +91,7 @@ TimeZoneLibC::TimeZoneLibC(const std::string& name)
     : local_(name == "localtime") {}
 
 time_zone::absolute_lookup TimeZoneLibC::BreakTime(
-    const time_point<sys_seconds>& tp) const {
+    const time_point<seconds>& tp) const {
   time_zone::absolute_lookup al;
   std::time_t t = ToUnixSeconds(tp);
   std::tm tm;
@@ -139,16 +139,22 @@ time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
   return cl;
 }
 
-std::string TimeZoneLibC::Description() const {
-  return local_ ? "localtime" : "UTC";
+bool TimeZoneLibC::NextTransition(const time_point<seconds>& tp,
+                                  time_zone::civil_transition* trans) const {
+  return false;
 }
 
-bool TimeZoneLibC::NextTransition(time_point<sys_seconds>* tp) const {
+bool TimeZoneLibC::PrevTransition(const time_point<seconds>& tp,
+                                  time_zone::civil_transition* trans) const {
   return false;
 }
 
-bool TimeZoneLibC::PrevTransition(time_point<sys_seconds>* tp) const {
-  return false;
+std::string TimeZoneLibC::Version() const {
+  return std::string();  // unknown
+}
+
+std::string TimeZoneLibC::Description() const {
+  return local_ ? "localtime" : "UTC";
 }
 
 }  // namespace cctz
diff --git a/absl/time/internal/cctz/src/time_zone_libc.h b/absl/time/internal/cctz/src/time_zone_libc.h
index 41f7dde2b06a..4e40c61ab243 100644
--- a/absl/time/internal/cctz/src/time_zone_libc.h
+++ b/absl/time/internal/cctz/src/time_zone_libc.h
@@ -32,12 +32,15 @@ class TimeZoneLibC : public TimeZoneIf {
 
   // TimeZoneIf implementations.
   time_zone::absolute_lookup BreakTime(
-      const time_point<sys_seconds>& tp) const override;
+      const time_point<seconds>& tp) const override;
   time_zone::civil_lookup MakeTime(
       const civil_second& cs) const override;
+  bool NextTransition(const time_point<seconds>& tp,
+                      time_zone::civil_transition* trans) const override;
+  bool PrevTransition(const time_point<seconds>& tp,
+                      time_zone::civil_transition* trans) const override;
+  std::string Version() const override;
   std::string Description() const override;
-  bool NextTransition(time_point<sys_seconds>* tp) const override;
-  bool PrevTransition(time_point<sys_seconds>* tp) const override;
 
  private:
   const bool local_;  // localtime or UTC
diff --git a/absl/time/internal/cctz/src/time_zone_lookup.cc b/absl/time/internal/cctz/src/time_zone_lookup.cc
index d549d862a769..f2d151e4d5e5 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup.cc
@@ -61,20 +61,43 @@ int __system_property_get(const char* name, char* value) {
 #endif
 
 std::string time_zone::name() const {
-  return time_zone::Impl::get(*this).name();
+  return effective_impl().Name();
 }
 
 time_zone::absolute_lookup time_zone::lookup(
-    const time_point<sys_seconds>& tp) const {
-  return time_zone::Impl::get(*this).BreakTime(tp);
+    const time_point<seconds>& tp) const {
+  return effective_impl().BreakTime(tp);
 }
 
 time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const {
-  return time_zone::Impl::get(*this).MakeTime(cs);
+  return effective_impl().MakeTime(cs);
 }
 
-bool operator==(time_zone lhs, time_zone rhs) {
-  return &time_zone::Impl::get(lhs) == &time_zone::Impl::get(rhs);
+bool time_zone::next_transition(const time_point<seconds>& tp,
+                                civil_transition* trans) const {
+  return effective_impl().NextTransition(tp, trans);
+}
+
+bool time_zone::prev_transition(const time_point<seconds>& tp,
+                                civil_transition* trans) const {
+  return effective_impl().PrevTransition(tp, trans);
+}
+
+std::string time_zone::version() const {
+  return effective_impl().Version();
+}
+
+std::string time_zone::description() const {
+  return effective_impl().Description();
+}
+
+const time_zone::Impl& time_zone::effective_impl() const {
+  if (impl_ == nullptr) {
+    // Dereferencing an implicit-UTC time_zone is expected to be
+    // rare, so we don't mind paying a small synchronization cost.
+    return *time_zone::Impl::UTC().impl_;
+  }
+  return *impl_;
 }
 
 bool load_time_zone(const std::string& name, time_zone* tz) {
@@ -85,7 +108,7 @@ time_zone utc_time_zone() {
   return time_zone::Impl::UTC();  // avoid name lookup
 }
 
-time_zone fixed_time_zone(const sys_seconds& offset) {
+time_zone fixed_time_zone(const seconds& offset) {
   time_zone tz;
   load_time_zone(FixedOffsetToName(offset), &tz);
   return tz;
diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
index 06b172a80323..551292fb55e0 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -24,14 +24,7 @@
 #include "absl/time/internal/cctz/include/cctz/civil_time.h"
 #include "gtest/gtest.h"
 
-using std::chrono::time_point_cast;
-using std::chrono::system_clock;
-using std::chrono::nanoseconds;
-using std::chrono::microseconds;
-using std::chrono::milliseconds;
-using std::chrono::seconds;
-using std::chrono::minutes;
-using std::chrono::hours;
+namespace chrono = std::chrono;
 
 namespace absl {
 namespace time_internal {
@@ -658,6 +651,17 @@ time_zone LoadZone(const std::string& name) {
     /* EXPECT_STREQ(zone, al.abbr); */                            \
   } while (0)
 
+// These tests sometimes run on platforms that have zoneinfo data so old
+// that the transition we are attempting to check does not exist, most
+// notably Android emulators.  Fortunately, AndroidZoneInfoSource supports
+// time_zone::version() so, in cases where we've learned that it matters,
+// we can make the check conditionally.
+int VersionCmp(time_zone tz, const std::string& target) {
+  std::string version = tz.version();
+  if (version.empty() && !target.empty()) return 1;  // unknown > known
+  return version.compare(target);
+}
+
 }  // namespace
 
 TEST(TimeZones, LoadZonesConcurrently) {
@@ -715,13 +719,13 @@ TEST(TimeZone, NamedTimeZones) {
   EXPECT_EQ("America/New_York", nyc.name());
   const time_zone syd = LoadZone("Australia/Sydney");
   EXPECT_EQ("Australia/Sydney", syd.name());
-  const time_zone fixed0 = fixed_time_zone(sys_seconds::zero());
+  const time_zone fixed0 = fixed_time_zone(absl::time_internal::cctz::seconds::zero());
   EXPECT_EQ("UTC", fixed0.name());
-  const time_zone fixed_pos =
-      fixed_time_zone(hours(3) + minutes(25) + seconds(45));
+  const time_zone fixed_pos = fixed_time_zone(
+      chrono::hours(3) + chrono::minutes(25) + chrono::seconds(45));
   EXPECT_EQ("Fixed/UTC+03:25:45", fixed_pos.name());
-  const time_zone fixed_neg =
-      fixed_time_zone(-(hours(12) + minutes(34) + seconds(56)));
+  const time_zone fixed_neg = fixed_time_zone(
+      -(chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56)));
   EXPECT_EQ("Fixed/UTC-12:34:56", fixed_neg.name());
 }
 
@@ -731,19 +735,19 @@ TEST(TimeZone, Failures) {
 
   tz = LoadZone("America/Los_Angeles");
   EXPECT_FALSE(load_time_zone("Invalid/TimeZone", &tz));
-  EXPECT_EQ(system_clock::from_time_t(0),
+  EXPECT_EQ(chrono::system_clock::from_time_t(0),
             convert(civil_second(1970, 1, 1, 0, 0, 0), tz));  // UTC
 
   // Ensures that the load still fails on a subsequent attempt.
   tz = LoadZone("America/Los_Angeles");
   EXPECT_FALSE(load_time_zone("Invalid/TimeZone", &tz));
-  EXPECT_EQ(system_clock::from_time_t(0),
+  EXPECT_EQ(chrono::system_clock::from_time_t(0),
             convert(civil_second(1970, 1, 1, 0, 0, 0), tz));  // UTC
 
   // Loading an empty std::string timezone should fail.
   tz = LoadZone("America/Los_Angeles");
   EXPECT_FALSE(load_time_zone("", &tz));
-  EXPECT_EQ(system_clock::from_time_t(0),
+  EXPECT_EQ(chrono::system_clock::from_time_t(0),
             convert(civil_second(1970, 1, 1, 0, 0, 0), tz));  // UTC
 }
 
@@ -758,7 +762,7 @@ TEST(TimeZone, Equality) {
   EXPECT_EQ(implicit_utc, explicit_utc);
   EXPECT_EQ(implicit_utc.name(), explicit_utc.name());
 
-  const time_zone fixed_zero = fixed_time_zone(sys_seconds::zero());
+  const time_zone fixed_zero = fixed_time_zone(absl::time_internal::cctz::seconds::zero());
   EXPECT_EQ(fixed_zero, LoadZone(fixed_zero.name()));
   EXPECT_EQ(fixed_zero, explicit_utc);
 
@@ -766,23 +770,25 @@ TEST(TimeZone, Equality) {
   EXPECT_EQ(fixed_utc, LoadZone(fixed_utc.name()));
   EXPECT_EQ(fixed_utc, explicit_utc);
 
-  const time_zone fixed_pos =
-      fixed_time_zone(hours(3) + minutes(25) + seconds(45));
+  const time_zone fixed_pos = fixed_time_zone(
+      chrono::hours(3) + chrono::minutes(25) + chrono::seconds(45));
   EXPECT_EQ(fixed_pos, LoadZone(fixed_pos.name()));
   EXPECT_NE(fixed_pos, explicit_utc);
-  const time_zone fixed_neg =
-      fixed_time_zone(-(hours(12) + minutes(34) + seconds(56)));
+  const time_zone fixed_neg = fixed_time_zone(
+      -(chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56)));
   EXPECT_EQ(fixed_neg, LoadZone(fixed_neg.name()));
   EXPECT_NE(fixed_neg, explicit_utc);
 
-  const time_zone fixed_lim = fixed_time_zone(hours(24));
+  const time_zone fixed_lim = fixed_time_zone(chrono::hours(24));
   EXPECT_EQ(fixed_lim, LoadZone(fixed_lim.name()));
   EXPECT_NE(fixed_lim, explicit_utc);
-  const time_zone fixed_ovfl = fixed_time_zone(hours(24) + seconds(1));
+  const time_zone fixed_ovfl =
+      fixed_time_zone(chrono::hours(24) + chrono::seconds(1));
   EXPECT_EQ(fixed_ovfl, LoadZone(fixed_ovfl.name()));
   EXPECT_EQ(fixed_ovfl, explicit_utc);
 
-  EXPECT_EQ(fixed_time_zone(seconds(1)), fixed_time_zone(seconds(1)));
+  EXPECT_EQ(fixed_time_zone(chrono::seconds(1)),
+            fixed_time_zone(chrono::seconds(1)));
 
   const time_zone local = local_time_zone();
   EXPECT_EQ(local, LoadZone(local.name()));
@@ -795,40 +801,43 @@ TEST(TimeZone, Equality) {
 TEST(StdChronoTimePoint, TimeTAlignment) {
   // Ensures that the Unix epoch and the system clock epoch are an integral
   // number of seconds apart. This simplifies conversions to/from time_t.
-  auto diff = system_clock::time_point() - system_clock::from_time_t(0);
-  EXPECT_EQ(system_clock::time_point::duration::zero(), diff % seconds(1));
+  auto diff = chrono::system_clock::time_point() -
+              chrono::system_clock::from_time_t(0);
+  EXPECT_EQ(chrono::system_clock::time_point::duration::zero(),
+            diff % chrono::seconds(1));
 }
 
 TEST(BreakTime, TimePointResolution) {
   const time_zone utc = utc_time_zone();
-  const auto t0 = system_clock::from_time_t(0);
+  const auto t0 = chrono::system_clock::from_time_t(0);
 
-  ExpectTime(time_point_cast<nanoseconds>(t0), utc,
+  ExpectTime(chrono::time_point_cast<chrono::nanoseconds>(t0), utc,
              1970, 1, 1, 0, 0, 0, 0, false, "UTC");
-  ExpectTime(time_point_cast<microseconds>(t0), utc,
+  ExpectTime(chrono::time_point_cast<chrono::microseconds>(t0), utc,
              1970, 1, 1, 0, 0, 0, 0, false, "UTC");
-  ExpectTime(time_point_cast<milliseconds>(t0), utc,
+  ExpectTime(chrono::time_point_cast<chrono::milliseconds>(t0), utc,
              1970, 1, 1, 0, 0, 0, 0, false, "UTC");
-  ExpectTime(time_point_cast<seconds>(t0), utc,
+  ExpectTime(chrono::time_point_cast<chrono::seconds>(t0), utc,
              1970, 1, 1, 0, 0, 0, 0, false, "UTC");
-  ExpectTime(time_point_cast<sys_seconds>(t0), utc,
+  ExpectTime(chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0), utc,
              1970, 1, 1, 0, 0, 0, 0, false, "UTC");
-  ExpectTime(time_point_cast<minutes>(t0), utc,
+  ExpectTime(chrono::time_point_cast<chrono::minutes>(t0), utc,
              1970, 1, 1, 0, 0, 0, 0, false, "UTC");
-  ExpectTime(time_point_cast<hours>(t0), utc,
+  ExpectTime(chrono::time_point_cast<chrono::hours>(t0), utc,
              1970, 1, 1, 0, 0, 0, 0, false, "UTC");
 }
 
 TEST(BreakTime, LocalTimeInUTC) {
   const time_zone tz = utc_time_zone();
-  const auto tp = system_clock::from_time_t(0);
+  const auto tp = chrono::system_clock::from_time_t(0);
   ExpectTime(tp, tz, 1970, 1, 1, 0, 0, 0, 0, false, "UTC");
   EXPECT_EQ(weekday::thursday, get_weekday(civil_day(convert(tp, tz))));
 }
 
 TEST(BreakTime, LocalTimeInUTCUnaligned) {
   const time_zone tz = utc_time_zone();
-  const auto tp = system_clock::from_time_t(0) - milliseconds(500);
+  const auto tp =
+      chrono::system_clock::from_time_t(0) - chrono::milliseconds(500);
   ExpectTime(tp, tz, 1969, 12, 31, 23, 59, 59, 0, false, "UTC");
   EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
 }
@@ -836,15 +845,16 @@ TEST(BreakTime, LocalTimeInUTCUnaligned) {
 TEST(BreakTime, LocalTimePosix) {
   // See IEEE Std 1003.1-1988 B.2.3 General Terms, Epoch.
   const time_zone tz = utc_time_zone();
-  const auto tp = system_clock::from_time_t(536457599);
+  const auto tp = chrono::system_clock::from_time_t(536457599);
   ExpectTime(tp, tz, 1986, 12, 31, 23, 59, 59, 0, false, "UTC");
   EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
 }
 
 TEST(TimeZoneImpl, LocalTimeInFixed) {
-  const sys_seconds offset = -(hours(8) + minutes(33) + seconds(47));
+  const absl::time_internal::cctz::seconds offset =
+      -(chrono::hours(8) + chrono::minutes(33) + chrono::seconds(47));
   const time_zone tz = fixed_time_zone(offset);
-  const auto tp = system_clock::from_time_t(0);
+  const auto tp = chrono::system_clock::from_time_t(0);
   ExpectTime(tp, tz, 1969, 12, 31, 15, 26, 13, offset.count(), false,
              "-083347");
   EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
@@ -852,52 +862,52 @@ TEST(TimeZoneImpl, LocalTimeInFixed) {
 
 TEST(BreakTime, LocalTimeInNewYork) {
   const time_zone tz = LoadZone("America/New_York");
-  const auto tp = system_clock::from_time_t(45);
+  const auto tp = chrono::system_clock::from_time_t(45);
   ExpectTime(tp, tz, 1969, 12, 31, 19, 0, 45, -5 * 60 * 60, false, "EST");
   EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
 }
 
 TEST(BreakTime, LocalTimeInMTV) {
   const time_zone tz = LoadZone("America/Los_Angeles");
-  const auto tp = system_clock::from_time_t(1380855729);
+  const auto tp = chrono::system_clock::from_time_t(1380855729);
   ExpectTime(tp, tz, 2013, 10, 3, 20, 2, 9, -7 * 60 * 60, true, "PDT");
   EXPECT_EQ(weekday::thursday, get_weekday(civil_day(convert(tp, tz))));
 }
 
 TEST(BreakTime, LocalTimeInSydney) {
   const time_zone tz = LoadZone("Australia/Sydney");
-  const auto tp = system_clock::from_time_t(90);
+  const auto tp = chrono::system_clock::from_time_t(90);
   ExpectTime(tp, tz, 1970, 1, 1, 10, 1, 30, 10 * 60 * 60, false, "AEST");
   EXPECT_EQ(weekday::thursday, get_weekday(civil_day(convert(tp, tz))));
 }
 
 TEST(MakeTime, TimePointResolution) {
   const time_zone utc = utc_time_zone();
-  const time_point<nanoseconds> tp_ns =
+  const time_point<chrono::nanoseconds> tp_ns =
       convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
   EXPECT_EQ("04:05", format("%M:%E*S", tp_ns, utc));
-  const time_point<microseconds> tp_us =
+  const time_point<chrono::microseconds> tp_us =
       convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
   EXPECT_EQ("04:05", format("%M:%E*S", tp_us, utc));
-  const time_point<milliseconds> tp_ms =
+  const time_point<chrono::milliseconds> tp_ms =
       convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
   EXPECT_EQ("04:05", format("%M:%E*S", tp_ms, utc));
-  const time_point<seconds> tp_s =
+  const time_point<chrono::seconds> tp_s =
       convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
   EXPECT_EQ("04:05", format("%M:%E*S", tp_s, utc));
-  const time_point<sys_seconds> tp_s64 =
+  const time_point<absl::time_internal::cctz::seconds> tp_s64 =
       convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
   EXPECT_EQ("04:05", format("%M:%E*S", tp_s64, utc));
 
-  // These next two require time_point_cast because the conversion from a
-  // resolution of seconds (the return value of convert()) to a coarser
-  // resolution requires an explicit cast.
-  const time_point<minutes> tp_m =
-      time_point_cast<minutes>(
+  // These next two require chrono::time_point_cast because the conversion
+  // from a resolution of seconds (the return value of convert()) to a
+  // coarser resolution requires an explicit cast.
+  const time_point<chrono::minutes> tp_m =
+      chrono::time_point_cast<chrono::minutes>(
           convert(civil_second(2015, 1, 2, 3, 4, 5), utc));
   EXPECT_EQ("04:00", format("%M:%E*S", tp_m, utc));
-  const time_point<hours> tp_h =
-      time_point_cast<hours>(
+  const time_point<chrono::hours> tp_h =
+      chrono::time_point_cast<chrono::hours>(
           convert(civil_second(2015, 1, 2, 3, 4, 5), utc));
   EXPECT_EQ("00:00", format("%M:%E*S", tp_h, utc));
 }
@@ -905,7 +915,7 @@ TEST(MakeTime, TimePointResolution) {
 TEST(MakeTime, Normalization) {
   const time_zone tz = LoadZone("America/New_York");
   const auto tp = convert(civil_second(2009, 2, 13, 18, 31, 30), tz);
-  EXPECT_EQ(system_clock::from_time_t(1234567890), tp);
+  EXPECT_EQ(chrono::system_clock::from_time_t(1234567890), tp);
 
   // Now requests for the same time_point but with out-of-range fields.
   EXPECT_EQ(tp, convert(civil_second(2008, 14, 13, 18, 31, 30), tz));  // month
@@ -919,67 +929,130 @@ TEST(MakeTime, Normalization) {
 TEST(MakeTime, SysSecondsLimits) {
   const char RFC3339[] =  "%Y-%m-%dT%H:%M:%S%Ez";
   const time_zone utc = utc_time_zone();
-  const time_zone east = fixed_time_zone(hours(14));
-  const time_zone west = fixed_time_zone(-hours(14));
-  time_point<sys_seconds> tp;
+  const time_zone east = fixed_time_zone(chrono::hours(14));
+  const time_zone west = fixed_time_zone(-chrono::hours(14));
+  time_point<absl::time_internal::cctz::seconds> tp;
 
-  // Approach the maximal time_point<sys_seconds> value from below.
+  // Approach the maximal time_point<cctz::seconds> value from below.
   tp = convert(civil_second(292277026596, 12, 4, 15, 30, 6), utc);
   EXPECT_EQ("292277026596-12-04T15:30:06+00:00", format(RFC3339, tp, utc));
   tp = convert(civil_second(292277026596, 12, 4, 15, 30, 7), utc);
   EXPECT_EQ("292277026596-12-04T15:30:07+00:00", format(RFC3339, tp, utc));
-  EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
   tp = convert(civil_second(292277026596, 12, 4, 15, 30, 8), utc);
-  EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
   tp = convert(civil_second::max(), utc);
-  EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
 
   // Checks that we can also get the maximal value for a far-east zone.
   tp = convert(civil_second(292277026596, 12, 5, 5, 30, 7), east);
   EXPECT_EQ("292277026596-12-05T05:30:07+14:00", format(RFC3339, tp, east));
-  EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
   tp = convert(civil_second(292277026596, 12, 5, 5, 30, 8), east);
-  EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
   tp = convert(civil_second::max(), east);
-  EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
 
   // Checks that we can also get the maximal value for a far-west zone.
   tp = convert(civil_second(292277026596, 12, 4, 1, 30, 7), west);
   EXPECT_EQ("292277026596-12-04T01:30:07-14:00", format(RFC3339, tp, west));
-  EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
   tp = convert(civil_second(292277026596, 12, 4, 7, 30, 8), west);
-  EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
   tp = convert(civil_second::max(), west);
-  EXPECT_EQ(time_point<sys_seconds>::max(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
 
-  // Approach the minimal time_point<sys_seconds> value from above.
+  // Approach the minimal time_point<cctz::seconds> value from above.
   tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 53), utc);
   EXPECT_EQ("-292277022657-01-27T08:29:53+00:00", format(RFC3339, tp, utc));
   tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 52), utc);
   EXPECT_EQ("-292277022657-01-27T08:29:52+00:00", format(RFC3339, tp, utc));
-  EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
   tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 51), utc);
-  EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
   tp = convert(civil_second::min(), utc);
-  EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
 
   // Checks that we can also get the minimal value for a far-east zone.
   tp = convert(civil_second(-292277022657, 1, 27, 22, 29, 52), east);
   EXPECT_EQ("-292277022657-01-27T22:29:52+14:00", format(RFC3339, tp, east));
-  EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
   tp = convert(civil_second(-292277022657, 1, 27, 22, 29, 51), east);
-  EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
   tp = convert(civil_second::min(), east);
-  EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
 
   // Checks that we can also get the minimal value for a far-west zone.
   tp = convert(civil_second(-292277022657, 1, 26, 18, 29, 52), west);
   EXPECT_EQ("-292277022657-01-26T18:29:52-14:00", format(RFC3339, tp, west));
-  EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
   tp = convert(civil_second(-292277022657, 1, 26, 18, 29, 51), west);
-  EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
   tp = convert(civil_second::min(), west);
-  EXPECT_EQ(time_point<sys_seconds>::min(), tp);
+  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
+}
+
+TEST(NextTransition, UTC) {
+  const auto tz = utc_time_zone();
+  time_zone::civil_transition trans;
+
+  auto tp = time_point<absl::time_internal::cctz::seconds>::min();
+  EXPECT_FALSE(tz.next_transition(tp, &trans));
+
+  tp = time_point<absl::time_internal::cctz::seconds>::max();
+  EXPECT_FALSE(tz.next_transition(tp, &trans));
+}
+
+TEST(PrevTransition, UTC) {
+  const auto tz = utc_time_zone();
+  time_zone::civil_transition trans;
+
+  auto tp = time_point<absl::time_internal::cctz::seconds>::max();
+  EXPECT_FALSE(tz.prev_transition(tp, &trans));
+
+  tp = time_point<absl::time_internal::cctz::seconds>::min();
+  EXPECT_FALSE(tz.prev_transition(tp, &trans));
+}
+
+TEST(NextTransition, AmericaNewYork) {
+  const auto tz = LoadZone("America/New_York");
+  time_zone::civil_transition trans;
+
+  auto tp = convert(civil_second(2018, 6, 30, 0, 0, 0), tz);
+  EXPECT_TRUE(tz.next_transition(tp, &trans));
+  EXPECT_EQ(civil_second(2018, 11, 4, 2, 0, 0), trans.from);
+  EXPECT_EQ(civil_second(2018, 11, 4, 1, 0, 0), trans.to);
+
+  tp = time_point<absl::time_internal::cctz::seconds>::max();
+  EXPECT_FALSE(tz.next_transition(tp, &trans));
+
+  tp = time_point<absl::time_internal::cctz::seconds>::min();
+  EXPECT_TRUE(tz.next_transition(tp, &trans));
+  if (trans.from == civil_second(1918, 3, 31, 2, 0, 0)) {
+    // It looks like the tzdata is only 32 bit (probably macOS),
+    // which bottoms out at 1901-12-13T20:45:52+00:00.
+    EXPECT_EQ(civil_second(1918, 3, 31, 3, 0, 0), trans.to);
+  } else {
+    EXPECT_EQ(civil_second(1883, 11, 18, 12, 3, 58), trans.from);
+    EXPECT_EQ(civil_second(1883, 11, 18, 12, 0, 0), trans.to);
+  }
+}
+
+TEST(PrevTransition, AmericaNewYork) {
+  const auto tz = LoadZone("America/New_York");
+  time_zone::civil_transition trans;
+
+  auto tp = convert(civil_second(2018, 6, 30, 0, 0, 0), tz);
+  EXPECT_TRUE(tz.prev_transition(tp, &trans));
+  EXPECT_EQ(civil_second(2018, 3, 11, 2, 0, 0), trans.from);
+  EXPECT_EQ(civil_second(2018, 3, 11, 3, 0, 0), trans.to);
+
+  tp = time_point<absl::time_internal::cctz::seconds>::min();
+  EXPECT_FALSE(tz.prev_transition(tp, &trans));
+
+  tp = time_point<absl::time_internal::cctz::seconds>::max();
+  EXPECT_TRUE(tz.prev_transition(tp, &trans));
+  // We have a transition but we don't know which one.
 }
 
 TEST(TimeZoneEdgeCase, AmericaNewYork) {
@@ -988,13 +1061,13 @@ TEST(TimeZoneEdgeCase, AmericaNewYork) {
   // Spring 1:59:59 -> 3:00:00
   auto tp = convert(civil_second(2013, 3, 10, 1, 59, 59), tz);
   ExpectTime(tp, tz, 2013, 3, 10, 1, 59, 59, -5 * 3600, false, "EST");
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 2013, 3, 10, 3, 0, 0, -4 * 3600, true, "EDT");
 
   // Fall 1:59:59 -> 1:00:00
   tp = convert(civil_second(2013, 11, 3, 1, 59, 59), tz);
   ExpectTime(tp, tz, 2013, 11, 3, 1, 59, 59, -4 * 3600, true, "EDT");
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 2013, 11, 3, 1, 0, 0, -5 * 3600, false, "EST");
 }
 
@@ -1004,13 +1077,13 @@ TEST(TimeZoneEdgeCase, AmericaLosAngeles) {
   // Spring 1:59:59 -> 3:00:00
   auto tp = convert(civil_second(2013, 3, 10, 1, 59, 59), tz);
   ExpectTime(tp, tz, 2013, 3, 10, 1, 59, 59, -8 * 3600, false, "PST");
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 2013, 3, 10, 3, 0, 0, -7 * 3600, true, "PDT");
 
   // Fall 1:59:59 -> 1:00:00
   tp = convert(civil_second(2013, 11, 3, 1, 59, 59), tz);
   ExpectTime(tp, tz, 2013, 11, 3, 1, 59, 59, -7 * 3600, true, "PDT");
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 2013, 11, 3, 1, 0, 0, -8 * 3600, false, "PST");
 }
 
@@ -1020,13 +1093,13 @@ TEST(TimeZoneEdgeCase, ArizonaNoTransition) {
   // No transition in Spring.
   auto tp = convert(civil_second(2013, 3, 10, 1, 59, 59), tz);
   ExpectTime(tp, tz, 2013, 3, 10, 1, 59, 59, -7 * 3600, false, "MST");
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 2013, 3, 10, 2, 0, 0, -7 * 3600, false, "MST");
 
   // No transition in Fall.
   tp = convert(civil_second(2013, 11, 3, 1, 59, 59), tz);
   ExpectTime(tp, tz, 2013, 11, 3, 1, 59, 59, -7 * 3600, false, "MST");
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 2013, 11, 3, 2, 0, 0, -7 * 3600, false, "MST");
 }
 
@@ -1039,7 +1112,7 @@ TEST(TimeZoneEdgeCase, AsiaKathmandu) {
   //   504901800 == Wed,  1 Jan 1986 00:15:00 +0545 (+0545)
   auto tp = convert(civil_second(1985, 12, 31, 23, 59, 59), tz);
   ExpectTime(tp, tz, 1985, 12, 31, 23, 59, 59, 5.5 * 3600, false, "+0530");
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 1986, 1, 1, 0, 15, 0, 5.75 * 3600, false, "+0545");
 }
 
@@ -1052,14 +1125,14 @@ TEST(TimeZoneEdgeCase, PacificChatham) {
   //   1365256800 == Sun,  7 Apr 2013 02:45:00 +1245 (+1245)
   auto tp = convert(civil_second(2013, 4, 7, 3, 44, 59), tz);
   ExpectTime(tp, tz, 2013, 4, 7, 3, 44, 59, 13.75 * 3600, true, "+1345");
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 2013, 4, 7, 2, 45, 0, 12.75 * 3600, false, "+1245");
 
   //   1380376799 == Sun, 29 Sep 2013 02:44:59 +1245 (+1245)
   //   1380376800 == Sun, 29 Sep 2013 03:45:00 +1345 (+1345)
   tp = convert(civil_second(2013, 9, 29, 2, 44, 59), tz);
   ExpectTime(tp, tz, 2013, 9, 29, 2, 44, 59, 12.75 * 3600, false, "+1245");
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 2013, 9, 29, 3, 45, 0, 13.75 * 3600, true, "+1345");
 }
 
@@ -1072,14 +1145,14 @@ TEST(TimeZoneEdgeCase, AustraliaLordHowe) {
   //   1365260400 == Sun,  7 Apr 2013 01:30:00 +1030 (+1030)
   auto tp = convert(civil_second(2013, 4, 7, 1, 59, 59), tz);
   ExpectTime(tp, tz, 2013, 4, 7, 1, 59, 59, 11 * 3600, true, "+11");
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 2013, 4, 7, 1, 30, 0, 10.5 * 3600, false, "+1030");
 
   //   1380986999 == Sun,  6 Oct 2013 01:59:59 +1030 (+1030)
   //   1380987000 == Sun,  6 Oct 2013 02:30:00 +1100 (+11)
   tp = convert(civil_second(2013, 10, 6, 1, 59, 59), tz);
   ExpectTime(tp, tz, 2013, 10, 6, 1, 59, 59, 10.5 * 3600, false, "+1030");
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 2013, 10, 6, 2, 30, 0, 11 * 3600, true, "+11");
 }
 
@@ -1097,7 +1170,7 @@ TEST(TimeZoneEdgeCase, PacificApia) {
   auto tp = convert(civil_second(2011, 12, 29, 23, 59, 59), tz);
   ExpectTime(tp, tz, 2011, 12, 29, 23, 59, 59, -10 * 3600, true, "-10");
   EXPECT_EQ(363, get_yearday(civil_day(convert(tp, tz))));
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 2011, 12, 31, 0, 0, 0, 14 * 3600, true, "+14");
   EXPECT_EQ(365, get_yearday(civil_day(convert(tp, tz))));
 }
@@ -1105,35 +1178,31 @@ TEST(TimeZoneEdgeCase, PacificApia) {
 TEST(TimeZoneEdgeCase, AfricaCairo) {
   const time_zone tz = LoadZone("Africa/Cairo");
 
-#if defined(__ANDROID__) && __ANDROID_API__ < 21
-  // Only Android 'L' and beyond have this tz2014c transition.
-#else
-  // An interesting case of midnight not existing.
-  //
-  //   1400191199 == Thu, 15 May 2014 23:59:59 +0200 (EET)
-  //   1400191200 == Fri, 16 May 2014 01:00:00 +0300 (EEST)
-  auto tp = convert(civil_second(2014, 5, 15, 23, 59, 59), tz);
-  ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET");
-  tp += seconds(1);
-  ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST");
-#endif
+  if (VersionCmp(tz, "2014c") >= 0) {
+    // An interesting case of midnight not existing.
+    //
+    //   1400191199 == Thu, 15 May 2014 23:59:59 +0200 (EET)
+    //   1400191200 == Fri, 16 May 2014 01:00:00 +0300 (EEST)
+    auto tp = convert(civil_second(2014, 5, 15, 23, 59, 59), tz);
+    ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET");
+    tp += absl::time_internal::cctz::seconds(1);
+    ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST");
+  }
 }
 
 TEST(TimeZoneEdgeCase, AfricaMonrovia) {
   const time_zone tz = LoadZone("Africa/Monrovia");
 
-#if defined(__ANDROID__) && __ANDROID_API__ < 26
-  // Only Android 'O' and beyond have this tz2017b transition.
-#else
-  // Strange offset change -00:44:30 -> +00:00:00 (non-DST)
-  //
-  //   63593069 == Thu,  6 Jan 1972 23:59:59 -0044 (MMT)
-  //   63593070 == Fri,  7 Jan 1972 00:44:30 +0000 (GMT)
-  auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz);
-  ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT");
-  tp += seconds(1);
-  ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT");
-#endif
+  if (VersionCmp(tz, "2017b") >= 0) {
+    // Strange offset change -00:44:30 -> +00:00:00 (non-DST)
+    //
+    //   63593069 == Thu,  6 Jan 1972 23:59:59 -0044 (MMT)
+    //   63593070 == Fri,  7 Jan 1972 00:44:30 +0000 (GMT)
+    auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz);
+    ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT");
+    tp += absl::time_internal::cctz::seconds(1);
+    ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT");
+  }
 }
 
 TEST(TimeZoneEdgeCase, AmericaJamaica) {
@@ -1145,30 +1214,31 @@ TEST(TimeZoneEdgeCase, AmericaJamaica) {
   const time_zone tz = LoadZone("America/Jamaica");
 
   // Before the first transition.
-  auto tp = convert(civil_second(1889, 12, 31, 0, 0, 0), tz);
-#if AMERICA_JAMAICA_PRE_1913_OFFSET_FIX
-  // Commit 907241e: Fix off-by-1 error for Jamaica and T&C before 1913.
-  // Until that commit has made its way into a full release we avoid the
-  // expectations on the -18430 offset below.  TODO: Uncomment these.
-  ExpectTime(tp, tz, 1889, 12, 31, 0, 0, 0, -18430, false,
-             tz.lookup(tp).abbr);
-
-  // Over the first (abbreviation-change only) transition.
-  //   -2524503170 == Tue, 31 Dec 1889 23:59:59 -0507 (LMT)
-  //   -2524503169 == Wed,  1 Jan 1890 00:00:00 -0507 (KMT)
-  tp = convert(civil_second(1889, 12, 31, 23, 59, 59), tz);
-  ExpectTime(tp, tz, 1889, 12, 31, 23, 59, 59, -18430, false,
-             tz.lookup(tp).abbr);
-  tp += seconds(1);
-  ExpectTime(tp, tz, 1890, 1, 1, 0, 0, 0, -18430, false, "KMT");
-#endif
+  if (!tz.version().empty() && VersionCmp(tz, "2018d") >= 0) {
+    // We avoid the expectations on the -18430 offset below unless we are
+    // certain we have commit 907241e (Fix off-by-1 error for Jamaica and
+    // T&C before 1913) from 2018d.  TODO: Remove the "version() not empty"
+    // part when 2018d is generally available from /usr/share/zoneinfo.
+    auto tp = convert(civil_second(1889, 12, 31, 0, 0, 0), tz);
+    ExpectTime(tp, tz, 1889, 12, 31, 0, 0, 0, -18430, false,
+               tz.lookup(tp).abbr);
+
+    // Over the first (abbreviation-change only) transition.
+    //   -2524503170 == Tue, 31 Dec 1889 23:59:59 -0507 (LMT)
+    //   -2524503169 == Wed,  1 Jan 1890 00:00:00 -0507 (KMT)
+    tp = convert(civil_second(1889, 12, 31, 23, 59, 59), tz);
+    ExpectTime(tp, tz, 1889, 12, 31, 23, 59, 59, -18430, false,
+               tz.lookup(tp).abbr);
+    tp += absl::time_internal::cctz::seconds(1);
+    ExpectTime(tp, tz, 1890, 1, 1, 0, 0, 0, -18430, false, "KMT");
+  }
 
   // Over the last (DST) transition.
   //     436341599 == Sun, 30 Oct 1983 01:59:59 -0400 (EDT)
   //     436341600 == Sun, 30 Oct 1983 01:00:00 -0500 (EST)
-  tp = convert(civil_second(1983, 10, 30, 1, 59, 59), tz);
+  auto tp = convert(civil_second(1983, 10, 30, 1, 59, 59), tz);
   ExpectTime(tp, tz, 1983, 10, 30, 1, 59, 59, -4 * 3600, true, "EDT");
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 1983, 10, 30, 1, 0, 0, -5 * 3600, false, "EST");
 
   // After the last transition.
@@ -1189,7 +1259,7 @@ TEST(TimeZoneEdgeCase, WET) {
   //     228877200 == Sun,  3 Apr 1977 02:00:00 +0100 (WEST)
   tp = convert(civil_second(1977, 4, 3, 0, 59, 59), tz);
   ExpectTime(tp, tz, 1977, 4, 3, 0, 59, 59, 0, false, "WET");
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 1977, 4, 3, 2, 0, 0, 1 * 3600, true, "WEST");
 
   // A non-existent time within the first transition.
@@ -1211,12 +1281,12 @@ TEST(TimeZoneEdgeCase, FixedOffsets) {
   const time_zone gmtm5 = LoadZone("Etc/GMT+5");  // -0500
   auto tp = convert(civil_second(1970, 1, 1, 0, 0, 0), gmtm5);
   ExpectTime(tp, gmtm5, 1970, 1, 1, 0, 0, 0, -5 * 3600, false, "-05");
-  EXPECT_EQ(system_clock::from_time_t(5 * 3600), tp);
+  EXPECT_EQ(chrono::system_clock::from_time_t(5 * 3600), tp);
 
   const time_zone gmtp5 = LoadZone("Etc/GMT-5");  // +0500
   tp = convert(civil_second(1970, 1, 1, 0, 0, 0), gmtp5);
   ExpectTime(tp, gmtp5, 1970, 1, 1, 0, 0, 0, 5 * 3600, false, "+05");
-  EXPECT_EQ(system_clock::from_time_t(-5 * 3600), tp);
+  EXPECT_EQ(chrono::system_clock::from_time_t(-5 * 3600), tp);
 }
 
 TEST(TimeZoneEdgeCase, NegativeYear) {
@@ -1225,7 +1295,7 @@ TEST(TimeZoneEdgeCase, NegativeYear) {
   auto tp = convert(civil_second(0, 1, 1, 0, 0, 0), tz);
   ExpectTime(tp, tz, 0, 1, 1, 0, 0, 0, 0 * 3600, false, "UTC");
   EXPECT_EQ(weekday::saturday, get_weekday(civil_day(convert(tp, tz))));
-  tp -= seconds(1);
+  tp -= absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, -1, 12, 31, 23, 59, 59, 0 * 3600, false, "UTC");
   EXPECT_EQ(weekday::friday, get_weekday(civil_day(convert(tp, tz))));
 }
@@ -1239,7 +1309,7 @@ TEST(TimeZoneEdgeCase, UTC32bitLimit) {
   //   2147483648 == Tue, 19 Jan 2038 03:14:08 +0000 (UTC)
   auto tp = convert(civil_second(2038, 1, 19, 3, 14, 7), tz);
   ExpectTime(tp, tz, 2038, 1, 19, 3, 14, 7, 0 * 3600, false, "UTC");
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 2038, 1, 19, 3, 14, 8, 0 * 3600, false, "UTC");
 }
 
@@ -1252,7 +1322,7 @@ TEST(TimeZoneEdgeCase, UTC5DigitYear) {
   //   253402300800 == Sat,  1 Jan 1000 00:00:00 +0000 (UTC)
   auto tp = convert(civil_second(9999, 12, 31, 23, 59, 59), tz);
   ExpectTime(tp, tz, 9999, 12, 31, 23, 59, 59, 0 * 3600, false, "UTC");
-  tp += seconds(1);
+  tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 10000, 1, 1, 0, 0, 0, 0 * 3600, false, "UTC");
 }
 
diff --git a/absl/time/internal/cctz/src/zone_info_source.cc b/absl/time/internal/cctz/src/zone_info_source.cc
index b77c0a585a12..bf2d2d2d2b53 100644
--- a/absl/time/internal/cctz/src/zone_info_source.cc
+++ b/absl/time/internal/cctz/src/zone_info_source.cc
@@ -20,6 +20,7 @@ namespace cctz {
 
 // Defined out-of-line to avoid emitting a weak vtable in all TUs.
 ZoneInfoSource::~ZoneInfoSource() {}
+std::string ZoneInfoSource::Version() const { return std::string(); }
 
 }  // namespace cctz
 }  // namespace time_internal
@@ -60,9 +61,17 @@ ZoneInfoSourceFactory default_factory = DefaultFactory;
 #else
 #error Unsupported MSVC platform
 #endif
-#else
+#else  // _MSC_VER
+#if !defined(__has_attribute)
+#define __has_attribute(x) 0
+#endif
+#if __has_attribute(weak) || defined(__GNUC__)
 ZoneInfoSourceFactory zone_info_source_factory
     __attribute__((weak)) = DefaultFactory;
+#else
+// Make it a "strong" definition if we have no other choice.
+ZoneInfoSourceFactory zone_info_source_factory = DefaultFactory;
+#endif
 #endif  // _MSC_VER
 
 }  // namespace cctz_extension
diff --git a/absl/time/time.cc b/absl/time/time.cc
index 03720f62b007..71fd8ee6e743 100644
--- a/absl/time/time.cc
+++ b/absl/time/time.cc
@@ -44,8 +44,8 @@ namespace absl {
 
 namespace {
 
-inline cctz::time_point<cctz::sys_seconds> unix_epoch() {
-  return std::chrono::time_point_cast<cctz::sys_seconds>(
+inline cctz::time_point<cctz::seconds> unix_epoch() {
+  return std::chrono::time_point_cast<cctz::seconds>(
       std::chrono::system_clock::from_time_t(0));
 }
 
@@ -110,12 +110,12 @@ inline TimeConversion InfinitePastTimeConversion() {
 
 // Makes a Time from sec, overflowing to InfiniteFuture/InfinitePast as
 // necessary. If sec is min/max, then consult cs+tz to check for overlow.
-Time MakeTimeWithOverflow(const cctz::time_point<cctz::sys_seconds>& sec,
+Time MakeTimeWithOverflow(const cctz::time_point<cctz::seconds>& sec,
                           const cctz::civil_second& cs,
                           const cctz::time_zone& tz,
                           bool* normalized = nullptr) {
-  const auto max = cctz::time_point<cctz::sys_seconds>::max();
-  const auto min = cctz::time_point<cctz::sys_seconds>::min();
+  const auto max = cctz::time_point<cctz::seconds>::max();
+  const auto min = cctz::time_point<cctz::seconds>::min();
   if (sec == max) {
     const auto al = tz.lookup(max);
     if (cs > al.cs) {
@@ -174,8 +174,7 @@ absl::Time::Breakdown Time::In(absl::TimeZone tz) const {
   if (*this == absl::InfiniteFuture()) return absl::InfiniteFutureBreakdown();
   if (*this == absl::InfinitePast()) return absl::InfinitePastBreakdown();
 
-  const auto tp =
-      unix_epoch() + cctz::sys_seconds(time_internal::GetRepHi(rep_));
+  const auto tp = unix_epoch() + cctz::seconds(time_internal::GetRepHi(rep_));
   const auto al = cctz::time_zone(tz).lookup(tp);
   const auto cs = al.cs;
   const auto cd = cctz::civil_day(cs);
diff --git a/absl/time/time.h b/absl/time/time.h
index 99c12bbd87ed..880fc783ae4a 100644
--- a/absl/time/time.h
+++ b/absl/time/time.h
@@ -50,7 +50,7 @@
 #ifndef ABSL_TIME_TIME_H_
 #define ABSL_TIME_TIME_H_
 
-#if !defined(_WIN32)
+#if !defined(_MSC_VER)
 #include <sys/time.h>
 #else
 #include <winsock2.h>
@@ -64,6 +64,7 @@
 #include <utility>
 
 #include "absl/base/port.h"  // Needed for string vs std::string
+#include "absl/strings/string_view.h"
 #include "absl/time/internal/cctz/include/cctz/time_zone.h"
 
 namespace absl {
@@ -491,9 +492,6 @@ inline std::ostream& operator<<(std::ostream& os, Duration d) {
 // `ZeroDuration()`.  Parses "inf" and "-inf" as +/- `InfiniteDuration()`.
 bool ParseDuration(const std::string& dur_string, Duration* d);
 
-// Flag Support
-// TODO(absl-team): Remove once dependencies are removed.
-
 // ParseFlag()
 //
 bool ParseFlag(const std::string& text, Duration* dst, std::string* error);
@@ -993,8 +991,6 @@ bool ParseTime(const std::string& format, const std::string& input, Time* time,
 bool ParseTime(const std::string& format, const std::string& input, TimeZone tz,
                Time* time, std::string* err);
 
-// TODO(absl-team): Remove once dependencies are removed.
-
 // ParseFlag()
 // UnparseFlag()
 //
diff --git a/absl/time/time_zone_test.cc b/absl/time/time_zone_test.cc
index 7138560ac2d1..43d919040445 100644
--- a/absl/time/time_zone_test.cc
+++ b/absl/time/time_zone_test.cc
@@ -59,7 +59,7 @@ TEST(TimeZone, DefaultTimeZones) {
 
 TEST(TimeZone, FixedTimeZone) {
   const absl::TimeZone tz = absl::FixedTimeZone(123);
-  const cctz::time_zone cz = cctz::fixed_time_zone(cctz::sys_seconds(123));
+  const cctz::time_zone cz = cctz::fixed_time_zone(cctz::seconds(123));
   EXPECT_EQ(tz, absl::TimeZone(cz));
 }
 
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel
index 1fc4c098e9bd..096c119e638c 100644
--- a/absl/types/BUILD.bazel
+++ b/absl/types/BUILD.bazel
@@ -42,7 +42,10 @@ cc_library(
     name = "bad_any_cast",
     hdrs = ["bad_any_cast.h"],
     copts = ABSL_DEFAULT_COPTS,
-    deps = [":bad_any_cast_impl"],
+    deps = [
+        ":bad_any_cast_impl",
+        "//absl/base:config",
+    ],
 )
 
 cc_library(
@@ -248,6 +251,20 @@ cc_test(
 )
 
 cc_test(
+    name = "variant_benchmark",
+    srcs = [
+        "variant_benchmark.cc",
+    ],
+    copts = ABSL_TEST_COPTS,
+    tags = ["benchmark"],
+    deps = [
+        ":variant",
+        "//absl/utility",
+        "@com_github_google_benchmark//:benchmark_main",
+    ],
+)
+
+cc_test(
     name = "variant_exception_safety_test",
     size = "small",
     srcs = [
diff --git a/absl/types/bad_any_cast.cc b/absl/types/bad_any_cast.cc
index c9b73300690f..2e2fd29a3378 100644
--- a/absl/types/bad_any_cast.cc
+++ b/absl/types/bad_any_cast.cc
@@ -14,6 +14,8 @@
 
 #include "absl/types/bad_any_cast.h"
 
+#ifndef ABSL_HAVE_STD_ANY
+
 #include <cstdlib>
 
 #include "absl/base/config.h"
@@ -38,3 +40,5 @@ void ThrowBadAnyCast() {
 
 }  // namespace any_internal
 }  // namespace absl
+
+#endif  // ABSL_HAVE_STD_ANY
diff --git a/absl/types/bad_any_cast.h b/absl/types/bad_any_cast.h
index 3b963077d417..603901324afc 100644
--- a/absl/types/bad_any_cast.h
+++ b/absl/types/bad_any_cast.h
@@ -23,6 +23,18 @@
 
 #include <typeinfo>
 
+#include "absl/base/config.h"
+
+#ifdef ABSL_HAVE_STD_ANY
+
+#include <any>
+
+namespace absl {
+using std::bad_any_cast;
+}  // namespace absl
+
+#else  // ABSL_HAVE_STD_ANY
+
 namespace absl {
 
 // -----------------------------------------------------------------------------
@@ -54,4 +66,6 @@ namespace any_internal {
 }  // namespace any_internal
 }  // namespace absl
 
+#endif  // ABSL_HAVE_STD_ANY
+
 #endif  // ABSL_TYPES_BAD_ANY_CAST_H_
diff --git a/absl/types/bad_optional_access.cc b/absl/types/bad_optional_access.cc
index 6bc67df77c9a..558707760e9d 100644
--- a/absl/types/bad_optional_access.cc
+++ b/absl/types/bad_optional_access.cc
@@ -14,6 +14,8 @@
 
 #include "absl/types/bad_optional_access.h"
 
+#ifndef ABSL_HAVE_STD_OPTIONAL
+
 #include <cstdlib>
 
 #include "absl/base/config.h"
@@ -40,3 +42,5 @@ void throw_bad_optional_access() {
 
 }  // namespace optional_internal
 }  // namespace absl
+
+#endif  // ABSL_HAVE_STD_OPTIONAL
diff --git a/absl/types/bad_optional_access.h b/absl/types/bad_optional_access.h
index e9aa8b83eda1..c6c27460a76d 100644
--- a/absl/types/bad_optional_access.h
+++ b/absl/types/bad_optional_access.h
@@ -23,6 +23,18 @@
 
 #include <stdexcept>
 
+#include "absl/base/config.h"
+
+#ifdef ABSL_HAVE_STD_OPTIONAL
+
+#include <optional>
+
+namespace absl {
+using std::bad_optional_access;
+}  // namespace absl
+
+#else  // ABSL_HAVE_STD_OPTIONAL
+
 namespace absl {
 
 // -----------------------------------------------------------------------------
@@ -57,4 +69,6 @@ namespace optional_internal {
 }  // namespace optional_internal
 }  // namespace absl
 
+#endif  // ABSL_HAVE_STD_OPTIONAL
+
 #endif  // ABSL_TYPES_BAD_OPTIONAL_ACCESS_H_
diff --git a/absl/types/bad_variant_access.cc b/absl/types/bad_variant_access.cc
index 817fd78909a5..d27d77561791 100644
--- a/absl/types/bad_variant_access.cc
+++ b/absl/types/bad_variant_access.cc
@@ -14,6 +14,8 @@
 
 #include "absl/types/bad_variant_access.h"
 
+#ifndef ABSL_HAVE_STD_VARIANT
+
 #include <cstdlib>
 #include <stdexcept>
 
@@ -56,3 +58,5 @@ void Rethrow() {
 
 }  // namespace variant_internal
 }  // namespace absl
+
+#endif  // ABSL_HAVE_STD_VARIANT
diff --git a/absl/types/bad_variant_access.h b/absl/types/bad_variant_access.h
index 67abe7136b7b..e7355a5a74c8 100644
--- a/absl/types/bad_variant_access.h
+++ b/absl/types/bad_variant_access.h
@@ -23,6 +23,18 @@
 
 #include <stdexcept>
 
+#include "absl/base/config.h"
+
+#ifdef ABSL_HAVE_STD_VARIANT
+
+#include <variant>
+
+namespace absl {
+using std::bad_variant_access;
+}  // namespace absl
+
+#else  // ABSL_HAVE_STD_VARIANT
+
 namespace absl {
 
 // -----------------------------------------------------------------------------
@@ -61,4 +73,6 @@ namespace variant_internal {
 }  // namespace variant_internal
 }  // namespace absl
 
+#endif  // ABSL_HAVE_STD_VARIANT
+
 #endif  // ABSL_TYPES_BAD_VARIANT_ACCESS_H_
diff --git a/absl/types/internal/variant.h b/absl/types/internal/variant.h
index 61c56ddf4e8f..7db5e0535f68 100644
--- a/absl/types/internal/variant.h
+++ b/absl/types/internal/variant.h
@@ -19,15 +19,19 @@
 #ifndef ABSL_TYPES_variant_internal_H_
 #define ABSL_TYPES_variant_internal_H_
 
+#include <cassert>
 #include <cstddef>
+#include <cstdlib>
 #include <memory>
 #include <stdexcept>
 #include <tuple>
 #include <type_traits>
 
+#include "absl/base/config.h"
 #include "absl/base/internal/identity.h"
 #include "absl/base/internal/inline_variable.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/types/bad_variant_access.h"
@@ -119,6 +123,8 @@ using GiveQualsToT = typename GiveQualsTo<T, U>::type;
 template <std::size_t I>
 using SizeT = std::integral_constant<std::size_t, I>;
 
+using NPos = SizeT<variant_npos>;
+
 template <class Variant, class T, class = void>
 struct IndexOfConstructedType {};
 
@@ -248,19 +254,270 @@ struct MakeVisitationMatrix<ReturnType, FunctionObject,
           ReturnType, FunctionObject, index_sequence<TailEndIndices...>,
           absl::make_index_sequence<HeadEndIndex>, BoundIndices...> {};
 
-template <std::size_t... EndIndices, class Op, class... SizeT>
-VisitIndicesResultT<Op, SizeT...> visit_indices(Op&& op, SizeT... indices) {
-  return AccessSimpleArray(
-      MakeVisitationMatrix<VisitIndicesResultT<Op, SizeT...>, Op,
-                           index_sequence<(EndIndices + 1)...>>::Run(),
-      (indices + 1)...)(absl::forward<Op>(op));
-}
+struct UnreachableSwitchCase {
+  template <class Op>
+  [[noreturn]] static VisitIndicesResultT<Op, std::size_t> Run(
+      Op&& /*ignored*/) {
+#if ABSL_HAVE_BUILTIN(__builtin_unreachable) || \
+    (defined(__GNUC__) && !defined(__clang__))
+    __builtin_unreachable();
+#elif defined(_MSC_VER)
+    __assume(false);
+#else
+    // Try to use assert of false being identified as an unreachable intrinsic.
+    // NOTE: We use assert directly to increase chances of exploiting an assume
+    //       intrinsic.
+    assert(false);  // NOLINT
+
+    // Hack to silence potential no return warning -- cause an infinite loop.
+    return Run(absl::forward<Op>(op));
+#endif  // Checks for __builtin_unreachable
+  }
+};
+
+template <class Op, std::size_t I>
+struct ReachableSwitchCase {
+  static VisitIndicesResultT<Op, std::size_t> Run(Op&& op) {
+    return absl::base_internal::Invoke(absl::forward<Op>(op), SizeT<I>());
+  }
+};
+
+// The number 33 is just a guess at a reasonable maximum to our switch. It is
+// not based on any analysis. The reason it is a power of 2 plus 1 instead of a
+// power of 2 is because the number was picked to correspond to a power of 2
+// amount of "normal" alternatives, plus one for the possibility of the user
+// providing "monostate" in addition to the more natural alternatives.
+ABSL_INTERNAL_INLINE_CONSTEXPR(std::size_t, MaxUnrolledVisitCases, 33);
+
+// Note: The default-definition is for unreachable cases.
+template <bool IsReachable>
+struct PickCaseImpl {
+  template <class Op, std::size_t I>
+  using Apply = UnreachableSwitchCase;
+};
+
+template <>
+struct PickCaseImpl</*IsReachable =*/true> {
+  template <class Op, std::size_t I>
+  using Apply = ReachableSwitchCase<Op, I>;
+};
+
+// Note: This form of dance with template aliases is to make sure that we
+//       instantiate a number of templates proportional to the number of variant
+//       alternatives rather than a number of templates proportional to our
+//       maximum unrolled amount of visitation cases (aliases are effectively
+//       "free" whereas other template instantiations are costly).
+template <class Op, std::size_t I, std::size_t EndIndex>
+using PickCase = typename PickCaseImpl<(I < EndIndex)>::template Apply<Op, I>;
 
 template <class ReturnType>
 [[noreturn]] ReturnType TypedThrowBadVariantAccess() {
   absl::variant_internal::ThrowBadVariantAccess();
 }
 
+// Given N variant sizes, determine the number of cases there would need to be
+// in a single switch-statement that would cover every possibility in the
+// corresponding N-ary visit operation.
+template <std::size_t... NumAlternatives>
+struct NumCasesOfSwitch;
+
+template <std::size_t HeadNumAlternatives, std::size_t... TailNumAlternatives>
+struct NumCasesOfSwitch<HeadNumAlternatives, TailNumAlternatives...> {
+  static constexpr std::size_t value =
+      (HeadNumAlternatives + 1) *
+      NumCasesOfSwitch<TailNumAlternatives...>::value;
+};
+
+template <>
+struct NumCasesOfSwitch<> {
+  static constexpr std::size_t value = 1;
+};
+
+// A switch statement optimizes better than the table of function pointers.
+template <std::size_t EndIndex>
+struct VisitIndicesSwitch {
+  static_assert(EndIndex <= MaxUnrolledVisitCases,
+                "Maximum unrolled switch size exceeded.");
+
+  template <class Op>
+  static VisitIndicesResultT<Op, std::size_t> Run(Op&& op, std::size_t i) {
+    switch (i) {
+      case 0:
+        return PickCase<Op, 0, EndIndex>::Run(absl::forward<Op>(op));
+      case 1:
+        return PickCase<Op, 1, EndIndex>::Run(absl::forward<Op>(op));
+      case 2:
+        return PickCase<Op, 2, EndIndex>::Run(absl::forward<Op>(op));
+      case 3:
+        return PickCase<Op, 3, EndIndex>::Run(absl::forward<Op>(op));
+      case 4:
+        return PickCase<Op, 4, EndIndex>::Run(absl::forward<Op>(op));
+      case 5:
+        return PickCase<Op, 5, EndIndex>::Run(absl::forward<Op>(op));
+      case 6:
+        return PickCase<Op, 6, EndIndex>::Run(absl::forward<Op>(op));
+      case 7:
+        return PickCase<Op, 7, EndIndex>::Run(absl::forward<Op>(op));
+      case 8:
+        return PickCase<Op, 8, EndIndex>::Run(absl::forward<Op>(op));
+      case 9:
+        return PickCase<Op, 9, EndIndex>::Run(absl::forward<Op>(op));
+      case 10:
+        return PickCase<Op, 10, EndIndex>::Run(absl::forward<Op>(op));
+      case 11:
+        return PickCase<Op, 11, EndIndex>::Run(absl::forward<Op>(op));
+      case 12:
+        return PickCase<Op, 12, EndIndex>::Run(absl::forward<Op>(op));
+      case 13:
+        return PickCase<Op, 13, EndIndex>::Run(absl::forward<Op>(op));
+      case 14:
+        return PickCase<Op, 14, EndIndex>::Run(absl::forward<Op>(op));
+      case 15:
+        return PickCase<Op, 15, EndIndex>::Run(absl::forward<Op>(op));
+      case 16:
+        return PickCase<Op, 16, EndIndex>::Run(absl::forward<Op>(op));
+      case 17:
+        return PickCase<Op, 17, EndIndex>::Run(absl::forward<Op>(op));
+      case 18:
+        return PickCase<Op, 18, EndIndex>::Run(absl::forward<Op>(op));
+      case 19:
+        return PickCase<Op, 19, EndIndex>::Run(absl::forward<Op>(op));
+      case 20:
+        return PickCase<Op, 20, EndIndex>::Run(absl::forward<Op>(op));
+      case 21:
+        return PickCase<Op, 21, EndIndex>::Run(absl::forward<Op>(op));
+      case 22:
+        return PickCase<Op, 22, EndIndex>::Run(absl::forward<Op>(op));
+      case 23:
+        return PickCase<Op, 23, EndIndex>::Run(absl::forward<Op>(op));
+      case 24:
+        return PickCase<Op, 24, EndIndex>::Run(absl::forward<Op>(op));
+      case 25:
+        return PickCase<Op, 25, EndIndex>::Run(absl::forward<Op>(op));
+      case 26:
+        return PickCase<Op, 26, EndIndex>::Run(absl::forward<Op>(op));
+      case 27:
+        return PickCase<Op, 27, EndIndex>::Run(absl::forward<Op>(op));
+      case 28:
+        return PickCase<Op, 28, EndIndex>::Run(absl::forward<Op>(op));
+      case 29:
+        return PickCase<Op, 29, EndIndex>::Run(absl::forward<Op>(op));
+      case 30:
+        return PickCase<Op, 30, EndIndex>::Run(absl::forward<Op>(op));
+      case 31:
+        return PickCase<Op, 31, EndIndex>::Run(absl::forward<Op>(op));
+      case 32:
+        return PickCase<Op, 32, EndIndex>::Run(absl::forward<Op>(op));
+      default:
+        ABSL_ASSERT(i == variant_npos);
+        return absl::base_internal::Invoke(absl::forward<Op>(op), NPos());
+    }
+  }
+};
+
+template <std::size_t... EndIndices>
+struct VisitIndicesFallback {
+  template <class Op, class... SizeT>
+  static VisitIndicesResultT<Op, SizeT...> Run(Op&& op, SizeT... indices) {
+    return AccessSimpleArray(
+        MakeVisitationMatrix<VisitIndicesResultT<Op, SizeT...>, Op,
+                             index_sequence<(EndIndices + 1)...>>::Run(),
+        (indices + 1)...)(absl::forward<Op>(op));
+  }
+};
+
+// Take an N-dimensional series of indices and convert them into a single index
+// without loss of information. The purpose of this is to be able to convert an
+// N-ary visit operation into a single switch statement.
+template <std::size_t...>
+struct FlattenIndices;
+
+template <std::size_t HeadSize, std::size_t... TailSize>
+struct FlattenIndices<HeadSize, TailSize...> {
+  template<class... SizeType>
+  static constexpr std::size_t Run(std::size_t head, SizeType... tail) {
+    return head + HeadSize * FlattenIndices<TailSize...>::Run(tail...);
+  }
+};
+
+template <>
+struct FlattenIndices<> {
+  static constexpr std::size_t Run() { return 0; }
+};
+
+// Take a single "flattened" index (flattened by FlattenIndices) and determine
+// the value of the index of one of the logically represented dimensions.
+template <std::size_t I, std::size_t IndexToGet, std::size_t HeadSize,
+          std::size_t... TailSize>
+struct UnflattenIndex {
+  static constexpr std::size_t value =
+      UnflattenIndex<I / HeadSize, IndexToGet - 1, TailSize...>::value;
+};
+
+template <std::size_t I, std::size_t HeadSize, std::size_t... TailSize>
+struct UnflattenIndex<I, 0, HeadSize, TailSize...> {
+  static constexpr std::size_t value = (I % HeadSize);
+};
+
+// The backend for converting an N-ary visit operation into a unary visit.
+template <class IndexSequence, std::size_t... EndIndices>
+struct VisitIndicesVariadicImpl;
+
+template <std::size_t... N, std::size_t... EndIndices>
+struct VisitIndicesVariadicImpl<absl::index_sequence<N...>, EndIndices...> {
+  // A type that can take an N-ary function object and converts it to a unary
+  // function object that takes a single, flattened index, and "unflattens" it
+  // into its individual dimensions when forwarding to the wrapped object.
+  template <class Op>
+  struct FlattenedOp {
+    template <std::size_t I>
+    VisitIndicesResultT<Op, decltype(EndIndices)...> operator()(
+        SizeT<I> /*index*/) && {
+      return base_internal::Invoke(
+          absl::forward<Op>(op),
+          SizeT<UnflattenIndex<I, N, (EndIndices + 1)...>::value -
+                std::size_t{1}>()...);
+    }
+
+    Op&& op;
+  };
+
+  template <class Op, class... SizeType>
+  static VisitIndicesResultT<Op, decltype(EndIndices)...> Run(
+      Op&& op, SizeType... i) {
+    return VisitIndicesSwitch<NumCasesOfSwitch<EndIndices...>::value>::Run(
+        FlattenedOp<Op>{absl::forward<Op>(op)},
+        FlattenIndices<(EndIndices + std::size_t{1})...>::Run(
+            (i + std::size_t{1})...));
+  }
+};
+
+template <std::size_t... EndIndices>
+struct VisitIndicesVariadic
+    : VisitIndicesVariadicImpl<absl::make_index_sequence<sizeof...(EndIndices)>,
+                               EndIndices...> {};
+
+// This implementation will flatten N-ary visit operations into a single switch
+// statement when the number of cases would be less than our maximum specified
+// switch-statement size.
+// TODO(calabrese)
+//   Based on benchmarks, determine whether the function table approach actually
+//   does optimize better than a chain of switch statements and possibly update
+//   the implementation accordingly. Also consider increasing the maximum switch
+//   size.
+template <std::size_t... EndIndices>
+struct VisitIndices
+    : absl::conditional_t<(NumCasesOfSwitch<EndIndices...>::value <=
+                           MaxUnrolledVisitCases),
+                          VisitIndicesVariadic<EndIndices...>,
+                          VisitIndicesFallback<EndIndices...>> {};
+
+template <std::size_t EndIndex>
+struct VisitIndices<EndIndex>
+    : absl::conditional_t<(EndIndex <= MaxUnrolledVisitCases),
+                          VisitIndicesSwitch<EndIndex>,
+                          VisitIndicesFallback<EndIndex>> {};
+
 // Suppress bogus warning on MSVC: MSVC complains that the `reinterpret_cast`
 // below is returning the address of a temporary or local object.
 #ifdef _MSC_VER
@@ -270,8 +527,10 @@ template <class ReturnType>
 
 // TODO(calabrese) std::launder
 // TODO(calabrese) constexpr
+// NOTE: DO NOT REMOVE the `inline` keyword as it is necessary to work around a
+// MSVC bug. See https://github.com/abseil/abseil-cpp/issues/129 for details.
 template <class Self, std::size_t I>
-VariantAccessResult<I, Self> AccessUnion(Self&& self, SizeT<I> /*i*/) {
+inline VariantAccessResult<I, Self> AccessUnion(Self&& self, SizeT<I> /*i*/) {
   return reinterpret_cast<VariantAccessResult<I, Self>>(self);
 }
 
@@ -313,19 +572,16 @@ struct VariantCoreAccess {
 
   template <class Variant>
   static void InitFrom(Variant& self, Variant&& other) {  // NOLINT
-    variant_internal::visit_indices<absl::variant_size<Variant>::value>(
+    VisitIndices<absl::variant_size<Variant>::value>::Run(
         InitFromVisitor<Variant, Variant&&>{&self,
                                             std::forward<Variant>(other)},
         other.index());
     self.index_ = other.index();
   }
 
+  // Access a variant alternative, assuming the index is correct.
   template <std::size_t I, class Variant>
   static VariantAccessResult<I, Variant> Access(Variant&& self) {
-    if (ABSL_PREDICT_FALSE(self.index_ != I)) {
-      TypedThrowBadVariantAccess<VariantAccessResult<I, Variant>>();
-    }
-
     // This cast instead of invocation of AccessUnion with an rvalue is a
     // workaround for msvc. Without this there is a runtime failure when dealing
     // with rvalues.
@@ -334,6 +590,16 @@ struct VariantCoreAccess {
         variant_internal::AccessUnion(self.state_, SizeT<I>()));
   }
 
+  // Access a variant alternative, throwing if the index is incorrect.
+  template <std::size_t I, class Variant>
+  static VariantAccessResult<I, Variant> CheckedAccess(Variant&& self) {
+    if (ABSL_PREDICT_FALSE(self.index_ != I)) {
+      TypedThrowBadVariantAccess<VariantAccessResult<I, Variant>>();
+    }
+
+    return Access<I>(absl::forward<Variant>(self));
+  }
+
   // The implementation of the move-assignment operation for a variant.
   template <class VType>
   struct MoveAssignVisitor {
@@ -841,49 +1107,40 @@ using EqualResult = decltype(std::declval<T>() == std::declval<T>());
 template <class T>
 using NotEqualResult = decltype(std::declval<T>() != std::declval<T>());
 
-template <class T>
-using HasLessThan = is_detected_convertible<bool, LessThanResult, T>;
-
-template <class T>
-using HasGreaterThan = is_detected_convertible<bool, GreaterThanResult, T>;
-
-template <class T>
-using HasLessThanOrEqual =
-    is_detected_convertible<bool, LessThanOrEqualResult, T>;
-
-template <class T>
-using HasGreaterThanOrEqual =
-    is_detected_convertible<bool, GreaterThanOrEqualResult, T>;
-
-template <class T>
-using HasEqual = is_detected_convertible<bool, EqualResult, T>;
-
-template <class T>
-using HasNotEqual = is_detected_convertible<bool, NotEqualResult, T>;
-
 template <class... T>
-using RequireAllHaveEqualT =
-    absl::enable_if_t<absl::conjunction<HasEqual<T>...>::value, bool>;
+using RequireAllHaveEqualT = absl::enable_if_t<
+    absl::conjunction<is_detected_convertible<bool, EqualResult, T>...>::value,
+    bool>;
 
 template <class... T>
 using RequireAllHaveNotEqualT =
-    absl::enable_if_t<absl::conjunction<HasEqual<T>...>::value, bool>;
+    absl::enable_if_t<absl::conjunction<is_detected_convertible<
+                          bool, NotEqualResult, T>...>::value,
+                      bool>;
 
 template <class... T>
 using RequireAllHaveLessThanT =
-    absl::enable_if_t<absl::conjunction<HasLessThan<T>...>::value, bool>;
+    absl::enable_if_t<absl::conjunction<is_detected_convertible<
+                          bool, LessThanResult, T>...>::value,
+                      bool>;
 
 template <class... T>
 using RequireAllHaveLessThanOrEqualT =
-    absl::enable_if_t<absl::conjunction<HasLessThan<T>...>::value, bool>;
+    absl::enable_if_t<absl::conjunction<is_detected_convertible<
+                          bool, LessThanOrEqualResult, T>...>::value,
+                      bool>;
 
 template <class... T>
 using RequireAllHaveGreaterThanOrEqualT =
-    absl::enable_if_t<absl::conjunction<HasLessThan<T>...>::value, bool>;
+    absl::enable_if_t<absl::conjunction<is_detected_convertible<
+                          bool, GreaterThanOrEqualResult, T>...>::value,
+                      bool>;
 
 template <class... T>
 using RequireAllHaveGreaterThanT =
-    absl::enable_if_t<absl::conjunction<HasLessThan<T>...>::value, bool>;
+    absl::enable_if_t<absl::conjunction<is_detected_convertible<
+                          bool, GreaterThanResult, T>...>::value,
+                      bool>;
 
 // Helper template containing implementations details of variant that can't go
 // in the private section. For convenience, this takes the variant type as a
@@ -1049,9 +1306,7 @@ class VariantStateBaseDestructorNontrivial : protected VariantStateBase<T...> {
     VariantStateBaseDestructorNontrivial* self;
   };
 
-  void destroy() {
-    variant_internal::visit_indices<sizeof...(T)>(Destroyer{this}, index_);
-  }
+  void destroy() { VisitIndices<sizeof...(T)>::Run(Destroyer{this}, index_); }
 
   ~VariantStateBaseDestructorNontrivial() { destroy(); }
 
@@ -1087,8 +1342,7 @@ class VariantMoveBaseNontrivial : protected VariantStateBaseDestructor<T...> {
   VariantMoveBaseNontrivial(VariantMoveBaseNontrivial&& other) noexcept(
       absl::conjunction<std::is_nothrow_move_constructible<T>...>::value)
       : Base(NoopConstructorTag()) {
-    variant_internal::visit_indices<sizeof...(T)>(Construct{this, &other},
-                                                  other.index_);
+    VisitIndices<sizeof...(T)>::Run(Construct{this, &other}, other.index_);
     index_ = other.index_;
   }
 
@@ -1131,8 +1385,7 @@ class VariantCopyBaseNontrivial : protected VariantMoveBase<T...> {
 
   VariantCopyBaseNontrivial(VariantCopyBaseNontrivial const& other)
       : Base(NoopConstructorTag()) {
-    variant_internal::visit_indices<sizeof...(T)>(Construct{this, &other},
-                                                  other.index_);
+    VisitIndices<sizeof...(T)>::Run(Construct{this, &other}, other.index_);
     index_ = other.index_;
   }
 
@@ -1166,7 +1419,7 @@ class VariantMoveAssignBaseNontrivial : protected VariantCopyBase<T...> {
     operator=(VariantMoveAssignBaseNontrivial&& other) noexcept(
         absl::conjunction<std::is_nothrow_move_constructible<T>...,
                           std::is_nothrow_move_assignable<T>...>::value) {
-      variant_internal::visit_indices<sizeof...(T)>(
+      VisitIndices<sizeof...(T)>::Run(
           VariantCoreAccess::MakeMoveAssignVisitor(this, &other), other.index_);
       return *this;
     }
@@ -1195,7 +1448,7 @@ class VariantCopyAssignBaseNontrivial : protected VariantMoveAssignBase<T...> {
 
     VariantCopyAssignBaseNontrivial& operator=(
         const VariantCopyAssignBaseNontrivial& other) {
-      variant_internal::visit_indices<sizeof...(T)>(
+      VisitIndices<sizeof...(T)>::Run(
           VariantCoreAccess::MakeCopyAssignVisitor(this, other), other.index_);
       return *this;
     }
@@ -1336,7 +1589,7 @@ struct Swap {
   template <std::size_t Wi>
   void operator()(SizeT<Wi> /*w_i*/) {
     if (v->index() == Wi) {
-      visit_indices<sizeof...(Types)>(SwapSameIndex<Types...>{v, w}, Wi);
+      VisitIndices<sizeof...(Types)>::Run(SwapSameIndex<Types...>{v, w}, Wi);
     } else {
       generic_swap();
     }
@@ -1370,11 +1623,10 @@ struct VariantHashBase<Variant,
     if (var.valueless_by_exception()) {
       return 239799884;
     }
-    size_t result =
-        variant_internal::visit_indices<variant_size<Variant>::value>(
-            PerformVisitation<VariantHashVisitor, const Variant&>{
-                std::forward_as_tuple(var), VariantHashVisitor{}},
-            var.index());
+    size_t result = VisitIndices<variant_size<Variant>::value>::Run(
+        PerformVisitation<VariantHashVisitor, const Variant&>{
+            std::forward_as_tuple(var), VariantHashVisitor{}},
+        var.index());
     // Combine the index and the hash result in order to distinguish
     // std::variant<int, int> holding the same value as different alternative.
     return result ^ var.index();
diff --git a/absl/types/optional.h b/absl/types/optional.h
index 80a2d149fc87..c837cddeef48 100644
--- a/absl/types/optional.h
+++ b/absl/types/optional.h
@@ -48,7 +48,7 @@ using std::optional;
 using std::make_optional;
 using std::nullopt_t;
 using std::nullopt;
-}
+}  // namespace absl
 
 #else  // ABSL_HAVE_STD_OPTIONAL
 
diff --git a/absl/types/variant.h b/absl/types/variant.h
index 7ae65abe0e6d..17e0634de037 100644
--- a/absl/types/variant.h
+++ b/absl/types/variant.h
@@ -248,7 +248,7 @@ using variant_alternative_t = typename variant_alternative<I, T>::type;
 //
 // Example:
 //
-//   absl::variant<int, std::string> bar = 42;
+//   absl::variant<int, std::string> foo = 42;
 //   if (absl::holds_alternative<int>(foo)) {
 //       std::cout << "The variant holds an integer";
 //   }
@@ -290,7 +290,7 @@ constexpr bool holds_alternative(const variant<Types...>& v) noexcept {
 // Overload for getting a variant's lvalue by type.
 template <class T, class... Types>
 constexpr T& get(variant<Types...>& v) {  // NOLINT
-  return variant_internal::VariantCoreAccess::Access<
+  return variant_internal::VariantCoreAccess::CheckedAccess<
       variant_internal::IndexOf<T, Types...>::value>(v);
 }
 
@@ -298,14 +298,14 @@ constexpr T& get(variant<Types...>& v) {  // NOLINT
 // Note: `absl::move()` is required to allow use of constexpr in C++11.
 template <class T, class... Types>
 constexpr T&& get(variant<Types...>&& v) {
-  return variant_internal::VariantCoreAccess::Access<
+  return variant_internal::VariantCoreAccess::CheckedAccess<
       variant_internal::IndexOf<T, Types...>::value>(absl::move(v));
 }
 
 // Overload for getting a variant's const lvalue by type.
 template <class T, class... Types>
 constexpr const T& get(const variant<Types...>& v) {
-  return variant_internal::VariantCoreAccess::Access<
+  return variant_internal::VariantCoreAccess::CheckedAccess<
       variant_internal::IndexOf<T, Types...>::value>(v);
 }
 
@@ -313,7 +313,7 @@ constexpr const T& get(const variant<Types...>& v) {
 // Note: `absl::move()` is required to allow use of constexpr in C++11.
 template <class T, class... Types>
 constexpr const T&& get(const variant<Types...>&& v) {
-  return variant_internal::VariantCoreAccess::Access<
+  return variant_internal::VariantCoreAccess::CheckedAccess<
       variant_internal::IndexOf<T, Types...>::value>(absl::move(v));
 }
 
@@ -321,7 +321,7 @@ constexpr const T&& get(const variant<Types...>&& v) {
 template <std::size_t I, class... Types>
 constexpr variant_alternative_t<I, variant<Types...>>& get(
     variant<Types...>& v) {  // NOLINT
-  return variant_internal::VariantCoreAccess::Access<I>(v);
+  return variant_internal::VariantCoreAccess::CheckedAccess<I>(v);
 }
 
 // Overload for getting a variant's rvalue by index.
@@ -329,14 +329,14 @@ constexpr variant_alternative_t<I, variant<Types...>>& get(
 template <std::size_t I, class... Types>
 constexpr variant_alternative_t<I, variant<Types...>>&& get(
     variant<Types...>&& v) {
-  return variant_internal::VariantCoreAccess::Access<I>(absl::move(v));
+  return variant_internal::VariantCoreAccess::CheckedAccess<I>(absl::move(v));
 }
 
 // Overload for getting a variant's const lvalue by index.
 template <std::size_t I, class... Types>
 constexpr const variant_alternative_t<I, variant<Types...>>& get(
     const variant<Types...>& v) {
-  return variant_internal::VariantCoreAccess::Access<I>(v);
+  return variant_internal::VariantCoreAccess::CheckedAccess<I>(v);
 }
 
 // Overload for getting a variant's const rvalue by index.
@@ -344,7 +344,7 @@ constexpr const variant_alternative_t<I, variant<Types...>>& get(
 template <std::size_t I, class... Types>
 constexpr const variant_alternative_t<I, variant<Types...>>&& get(
     const variant<Types...>&& v) {
-  return variant_internal::VariantCoreAccess::Access<I>(absl::move(v));
+  return variant_internal::VariantCoreAccess::CheckedAccess<I>(absl::move(v));
 }
 
 // get_if()
@@ -362,8 +362,10 @@ constexpr const variant_alternative_t<I, variant<Types...>>&& get(
 template <std::size_t I, class... Types>
 constexpr absl::add_pointer_t<variant_alternative_t<I, variant<Types...>>>
 get_if(variant<Types...>* v) noexcept {
-  return (v != nullptr && v->index() == I) ? std::addressof(absl::get<I>(*v))
-                                           : nullptr;
+  return (v != nullptr && v->index() == I)
+             ? std::addressof(
+                   variant_internal::VariantCoreAccess::Access<I>(*v))
+             : nullptr;
 }
 
 // Overload for getting a pointer to the const value stored in the given
@@ -371,8 +373,10 @@ get_if(variant<Types...>* v) noexcept {
 template <std::size_t I, class... Types>
 constexpr absl::add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
 get_if(const variant<Types...>* v) noexcept {
-  return (v != nullptr && v->index() == I) ? std::addressof(absl::get<I>(*v))
-                                           : nullptr;
+  return (v != nullptr && v->index() == I)
+             ? std::addressof(
+                   variant_internal::VariantCoreAccess::Access<I>(*v))
+             : nullptr;
 }
 
 // Overload for getting a pointer to the value stored in the given variant by
@@ -416,12 +420,12 @@ constexpr absl::add_pointer_t<const T> get_if(
 template <typename Visitor, typename... Variants>
 variant_internal::VisitResult<Visitor, Variants...> visit(Visitor&& vis,
                                                           Variants&&... vars) {
-  return variant_internal::visit_indices<
-      variant_size<absl::decay_t<Variants>>::value...>(
-      variant_internal::PerformVisitation<Visitor, Variants...>{
-          std::forward_as_tuple(absl::forward<Variants>(vars)...),
-          absl::forward<Visitor>(vis)},
-      vars.index()...);
+  return variant_internal::
+      VisitIndices<variant_size<absl::decay_t<Variants> >::value...>::Run(
+          variant_internal::PerformVisitation<Visitor, Variants...>{
+              std::forward_as_tuple(absl::forward<Variants>(vars)...),
+              absl::forward<Visitor>(vis)},
+          vars.index()...);
 }
 
 // monostate
@@ -445,12 +449,19 @@ constexpr bool operator!=(monostate, monostate) noexcept { return false; }
 //------------------------------------------------------------------------------
 template <typename T0, typename... Tn>
 class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> {
-  static_assert(absl::conjunction<std::is_object<T0>, std::is_object<Tn>...,
-                                  absl::negation<std::is_array<T0>>,
-                                  absl::negation<std::is_array<Tn>>...,
-                                  std::is_nothrow_destructible<T0>,
+  static_assert(absl::conjunction<std::is_object<T0>,
+                                  std::is_object<Tn>...>::value,
+                "Attempted to instantiate a variant containing a non-object "
+                "type.");
+  // Intentionally not qualifing `negation` with `absl::` to work around a bug
+  // in MSVC 2015 with inline namespace and variadic template.
+  static_assert(absl::conjunction<negation<std::is_array<T0> >,
+                                  negation<std::is_array<Tn> >...>::value,
+                "Attempted to instantiate a variant containing an array type.");
+  static_assert(absl::conjunction<std::is_nothrow_destructible<T0>,
                                   std::is_nothrow_destructible<Tn>...>::value,
-                "Attempted to instantiate a variant with an unsupported type.");
+                "Attempted to instantiate a variant containing a non-nothrow "
+                "destructible type.");
 
   friend struct variant_internal::VariantCoreAccess;
 
@@ -573,7 +584,7 @@ class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> {
   variant& operator=(T&& t) noexcept(
       std::is_nothrow_assignable<Tj&, T>::value&&
           std::is_nothrow_constructible<Tj, T>::value) {
-    variant_internal::visit_indices<sizeof...(Tn) + 1>(
+    variant_internal::VisitIndices<sizeof...(Tn) + 1>::Run(
         variant_internal::VariantCoreAccess::MakeConversionAssignVisitor(
             this, absl::forward<T>(t)),
         index());
@@ -682,7 +693,7 @@ class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> {
   //   true and `is_nothrow_swappable()` is same as `std::is_trivial()`.
   void swap(variant& rhs) noexcept(
       absl::conjunction<std::is_trivial<T0>, std::is_trivial<Tn>...>::value) {
-    return variant_internal::visit_indices<sizeof...(Tn) + 1>(
+    return variant_internal::VisitIndices<sizeof...(Tn) + 1>::Run(
         variant_internal::Swap<T0, Tn...>{this, &rhs}, rhs.index());
   }
 };
@@ -722,7 +733,7 @@ template <typename... Types>
 constexpr variant_internal::RequireAllHaveEqualT<Types...> operator==(
     const variant<Types...>& a, const variant<Types...>& b) {
   return (a.index() == b.index()) &&
-         variant_internal::visit_indices<sizeof...(Types)>(
+         variant_internal::VisitIndices<sizeof...(Types)>::Run(
              variant_internal::EqualsOp<Types...>{&a, &b}, a.index());
 }
 
@@ -731,7 +742,7 @@ template <typename... Types>
 constexpr variant_internal::RequireAllHaveNotEqualT<Types...> operator!=(
     const variant<Types...>& a, const variant<Types...>& b) {
   return (a.index() != b.index()) ||
-         variant_internal::visit_indices<sizeof...(Types)>(
+         variant_internal::VisitIndices<sizeof...(Types)>::Run(
              variant_internal::NotEqualsOp<Types...>{&a, &b}, a.index());
 }
 
@@ -741,7 +752,7 @@ constexpr variant_internal::RequireAllHaveLessThanT<Types...> operator<(
     const variant<Types...>& a, const variant<Types...>& b) {
   return (a.index() != b.index())
              ? (a.index() + 1) < (b.index() + 1)
-             : variant_internal::visit_indices<sizeof...(Types)>(
+             : variant_internal::VisitIndices<sizeof...(Types)>::Run(
                    variant_internal::LessThanOp<Types...>{&a, &b}, a.index());
 }
 
@@ -751,7 +762,7 @@ constexpr variant_internal::RequireAllHaveGreaterThanT<Types...> operator>(
     const variant<Types...>& a, const variant<Types...>& b) {
   return (a.index() != b.index())
              ? (a.index() + 1) > (b.index() + 1)
-             : variant_internal::visit_indices<sizeof...(Types)>(
+             : variant_internal::VisitIndices<sizeof...(Types)>::Run(
                    variant_internal::GreaterThanOp<Types...>{&a, &b},
                    a.index());
 }
@@ -762,7 +773,7 @@ constexpr variant_internal::RequireAllHaveLessThanOrEqualT<Types...> operator<=(
     const variant<Types...>& a, const variant<Types...>& b) {
   return (a.index() != b.index())
              ? (a.index() + 1) < (b.index() + 1)
-             : variant_internal::visit_indices<sizeof...(Types)>(
+             : variant_internal::VisitIndices<sizeof...(Types)>::Run(
                    variant_internal::LessThanOrEqualsOp<Types...>{&a, &b},
                    a.index());
 }
@@ -773,7 +784,7 @@ constexpr variant_internal::RequireAllHaveGreaterThanOrEqualT<Types...>
 operator>=(const variant<Types...>& a, const variant<Types...>& b) {
   return (a.index() != b.index())
              ? (a.index() + 1) > (b.index() + 1)
-             : variant_internal::visit_indices<sizeof...(Types)>(
+             : variant_internal::VisitIndices<sizeof...(Types)>::Run(
                    variant_internal::GreaterThanOrEqualsOp<Types...>{&a, &b},
                    a.index());
 }
diff --git a/absl/types/variant_benchmark.cc b/absl/types/variant_benchmark.cc
new file mode 100644
index 000000000000..99658ac70c46
--- /dev/null
+++ b/absl/types/variant_benchmark.cc
@@ -0,0 +1,220 @@
+// 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
+//
+//      http://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.
+
+// Unit tests for the variant template. The 'is' and 'IsEmpty' methods
+// of variant are not explicitly tested because they are used repeatedly
+// in building other tests. All other public variant methods should have
+// explicit tests.
+
+#include "absl/types/variant.h"
+
+#include <cstddef>
+#include <cstdlib>
+#include <string>
+#include <tuple>
+
+#include "benchmark/benchmark.h"
+#include "absl/utility/utility.h"
+
+namespace absl {
+namespace {
+
+template <std::size_t I>
+struct VariantAlternative {
+  char member;
+};
+
+template <class Indices>
+struct VariantOfAlternativesImpl;
+
+template <std::size_t... Indices>
+struct VariantOfAlternativesImpl<absl::index_sequence<Indices...>> {
+  using type = absl::variant<VariantAlternative<Indices>...>;
+};
+
+template <std::size_t NumAlternatives>
+using VariantOfAlternatives = typename VariantOfAlternativesImpl<
+    absl::make_index_sequence<NumAlternatives>>::type;
+
+struct Empty {};
+
+template <class... T>
+void Ignore(T...) noexcept {}
+
+template <class T>
+Empty DoNotOptimizeAndReturnEmpty(T&& arg) noexcept {
+  benchmark::DoNotOptimize(arg);
+  return {};
+}
+
+struct VisitorApplier {
+  struct Visitor {
+    template <class... T>
+    void operator()(T&&... args) const noexcept {
+      Ignore(DoNotOptimizeAndReturnEmpty(args)...);
+    }
+  };
+
+  template <class... Vars>
+  void operator()(const Vars&... vars) const noexcept {
+    absl::visit(Visitor(), vars...);
+  }
+};
+
+template <std::size_t NumIndices, std::size_t CurrIndex = NumIndices - 1>
+struct MakeWithIndex {
+  using Variant = VariantOfAlternatives<NumIndices>;
+
+  static Variant Run(std::size_t index) {
+    return index == CurrIndex
+               ? Variant(absl::in_place_index_t<CurrIndex>())
+               : MakeWithIndex<NumIndices, CurrIndex - 1>::Run(index);
+  }
+};
+
+template <std::size_t NumIndices>
+struct MakeWithIndex<NumIndices, 0> {
+  using Variant = VariantOfAlternatives<NumIndices>;
+
+  static Variant Run(std::size_t /*index*/) { return Variant(); }
+};
+
+template <std::size_t NumIndices, class Dimensions>
+struct MakeVariantTuple;
+
+template <class T, std::size_t /*I*/>
+using always_t = T;
+
+template <std::size_t NumIndices>
+VariantOfAlternatives<NumIndices> MakeVariant(std::size_t dimension,
+                                              std::size_t index) {
+  return dimension == 0
+             ? MakeWithIndex<NumIndices>::Run(index % NumIndices)
+             : MakeVariant<NumIndices>(dimension - 1, index / NumIndices);
+}
+
+template <std::size_t NumIndices, std::size_t... Dimensions>
+struct MakeVariantTuple<NumIndices, absl::index_sequence<Dimensions...>> {
+  using VariantTuple =
+      std::tuple<always_t<VariantOfAlternatives<NumIndices>, Dimensions>...>;
+
+  static VariantTuple Run(int index) {
+    return std::make_tuple(MakeVariant<NumIndices>(Dimensions, index)...);
+  }
+};
+
+constexpr std::size_t integral_pow(std::size_t base, std::size_t power) {
+  return power == 0 ? 1 : base * integral_pow(base, power - 1);
+}
+
+template <std::size_t End, std::size_t I = 0>
+struct VisitTestBody {
+  template <class Vars, class State>
+  static bool Run(Vars& vars, State& state) {
+    if (state.KeepRunning()) {
+      absl::apply(VisitorApplier(), vars[I]);
+      return VisitTestBody<End, I + 1>::Run(vars, state);
+    }
+    return false;
+  }
+};
+
+template <std::size_t End>
+struct VisitTestBody<End, End> {
+  template <class Vars, class State>
+  static bool Run(Vars& /*vars*/, State& /*state*/) {
+    return true;
+  }
+};
+
+// Visit operations where branch prediction is likely to give a boost.
+template <std::size_t NumIndices, std::size_t NumDimensions = 1>
+void BM_RedundantVisit(benchmark::State& state) {
+  auto vars =
+      MakeVariantTuple<NumIndices, absl::make_index_sequence<NumDimensions>>::
+          Run(static_cast<std::size_t>(state.range(0)));
+
+  for (auto _ : state) {  // NOLINT
+    benchmark::DoNotOptimize(vars);
+    absl::apply(VisitorApplier(), vars);
+  }
+}
+
+// Visit operations where branch prediction is unlikely to give a boost.
+template <std::size_t NumIndices, std::size_t NumDimensions = 1>
+void BM_Visit(benchmark::State& state) {
+  constexpr std::size_t num_possibilities =
+      integral_pow(NumIndices, NumDimensions);
+
+  using VariantTupleMaker =
+      MakeVariantTuple<NumIndices, absl::make_index_sequence<NumDimensions>>;
+  using Tuple = typename VariantTupleMaker::VariantTuple;
+
+  Tuple vars[num_possibilities];
+  for (std::size_t i = 0; i < num_possibilities; ++i)
+    vars[i] = VariantTupleMaker::Run(i);
+
+  while (VisitTestBody<num_possibilities>::Run(vars, state)) {
+  }
+}
+
+// Visitation
+//   Each visit is on a different variant with a different active alternative)
+
+// Unary visit
+BENCHMARK_TEMPLATE(BM_Visit, 1);
+BENCHMARK_TEMPLATE(BM_Visit, 2);
+BENCHMARK_TEMPLATE(BM_Visit, 3);
+BENCHMARK_TEMPLATE(BM_Visit, 4);
+BENCHMARK_TEMPLATE(BM_Visit, 5);
+BENCHMARK_TEMPLATE(BM_Visit, 6);
+BENCHMARK_TEMPLATE(BM_Visit, 7);
+BENCHMARK_TEMPLATE(BM_Visit, 8);
+BENCHMARK_TEMPLATE(BM_Visit, 16);
+BENCHMARK_TEMPLATE(BM_Visit, 32);
+BENCHMARK_TEMPLATE(BM_Visit, 64);
+
+// Binary visit
+BENCHMARK_TEMPLATE(BM_Visit, 1, 2);
+BENCHMARK_TEMPLATE(BM_Visit, 2, 2);
+BENCHMARK_TEMPLATE(BM_Visit, 3, 2);
+BENCHMARK_TEMPLATE(BM_Visit, 4, 2);
+BENCHMARK_TEMPLATE(BM_Visit, 5, 2);
+
+// Ternary visit
+BENCHMARK_TEMPLATE(BM_Visit, 1, 3);
+BENCHMARK_TEMPLATE(BM_Visit, 2, 3);
+BENCHMARK_TEMPLATE(BM_Visit, 3, 3);
+
+// Quaternary visit
+BENCHMARK_TEMPLATE(BM_Visit, 1, 4);
+BENCHMARK_TEMPLATE(BM_Visit, 2, 4);
+
+// Redundant Visitation
+//   Each visit consistently has the same alternative active
+
+// Unary visit
+BENCHMARK_TEMPLATE(BM_RedundantVisit, 1)->Arg(0);
+BENCHMARK_TEMPLATE(BM_RedundantVisit, 2)->DenseRange(0, 1);
+BENCHMARK_TEMPLATE(BM_RedundantVisit, 8)->DenseRange(0, 7);
+
+// Binary visit
+BENCHMARK_TEMPLATE(BM_RedundantVisit, 1, 2)->Arg(0);
+BENCHMARK_TEMPLATE(BM_RedundantVisit, 2, 2)
+    ->DenseRange(0, integral_pow(2, 2) - 1);
+BENCHMARK_TEMPLATE(BM_RedundantVisit, 4, 2)
+    ->DenseRange(0, integral_pow(4, 2) - 1);
+
+}  // namespace
+}  // namespace absl