summaryrefslogtreecommitdiff
path: root/absl/strings
diff options
context:
space:
mode:
Diffstat (limited to 'absl/strings')
-rw-r--r--absl/strings/BUILD.bazel196
-rw-r--r--absl/strings/CMakeLists.txt146
-rw-r--r--absl/strings/ascii.cc4
-rw-r--r--absl/strings/ascii.h18
-rw-r--r--absl/strings/ascii_benchmark.cc120
-rw-r--r--absl/strings/charconv.cc12
-rw-r--r--absl/strings/charconv.h10
-rw-r--r--absl/strings/charconv_test.cc20
-rw-r--r--absl/strings/escaping.cc24
-rw-r--r--absl/strings/escaping.h37
-rw-r--r--absl/strings/escaping_test.cc17
-rw-r--r--absl/strings/internal/bits.h55
-rw-r--r--absl/strings/internal/char_map.h4
-rw-r--r--absl/strings/internal/charconv_bigint.cc4
-rw-r--r--absl/strings/internal/charconv_bigint.h19
-rw-r--r--absl/strings/internal/charconv_bigint_test.cc4
-rw-r--r--absl/strings/internal/charconv_parse.cc6
-rw-r--r--absl/strings/internal/charconv_parse.h4
-rw-r--r--absl/strings/internal/charconv_parse_test.cc16
-rw-r--r--absl/strings/internal/escaping_test_common.h4
-rw-r--r--absl/strings/internal/memutil.cc4
-rw-r--r--absl/strings/internal/memutil.h12
-rw-r--r--absl/strings/internal/numbers_test_common.h27
-rw-r--r--absl/strings/internal/ostringstream.cc4
-rw-r--r--absl/strings/internal/ostringstream.h16
-rw-r--r--absl/strings/internal/pow10_helper.cc122
-rw-r--r--absl/strings/internal/pow10_helper.h38
-rw-r--r--absl/strings/internal/pow10_helper_test.cc122
-rw-r--r--absl/strings/internal/resize_uninitialized.h10
-rw-r--r--absl/strings/internal/stl_type_traits.h4
-rw-r--r--absl/strings/internal/str_format/arg.cc379
-rw-r--r--absl/strings/internal/str_format/arg.h422
-rw-r--r--absl/strings/internal/str_format/arg_test.cc113
-rw-r--r--absl/strings/internal/str_format/bind.cc231
-rw-r--r--absl/strings/internal/str_format/bind.h199
-rw-r--r--absl/strings/internal/str_format/bind_test.cc133
-rw-r--r--absl/strings/internal/str_format/checker.h327
-rw-r--r--absl/strings/internal/str_format/checker_test.cc152
-rw-r--r--absl/strings/internal/str_format/convert_test.cc577
-rw-r--r--absl/strings/internal/str_format/extension.cc86
-rw-r--r--absl/strings/internal/str_format/extension.h414
-rw-r--r--absl/strings/internal/str_format/extension_test.cc65
-rw-r--r--absl/strings/internal/str_format/float_conversion.cc485
-rw-r--r--absl/strings/internal/str_format/float_conversion.h23
-rw-r--r--absl/strings/internal/str_format/output.cc72
-rw-r--r--absl/strings/internal/str_format/output.h103
-rw-r--r--absl/strings/internal/str_format/output_test.cc80
-rw-r--r--absl/strings/internal/str_format/parser.cc311
-rw-r--r--absl/strings/internal/str_format/parser.h294
-rw-r--r--absl/strings/internal/str_format/parser_test.cc391
-rw-r--r--absl/strings/internal/str_join_internal.h16
-rw-r--r--absl/strings/internal/str_split_internal.h36
-rw-r--r--absl/strings/internal/utf8.cc4
-rw-r--r--absl/strings/internal/utf8.h4
-rw-r--r--absl/strings/match.cc11
-rw-r--r--absl/strings/match.h26
-rw-r--r--absl/strings/match_test.cc11
-rw-r--r--absl/strings/numbers.cc16
-rw-r--r--absl/strings/numbers.h28
-rw-r--r--absl/strings/numbers_benchmark.cc263
-rw-r--r--absl/strings/numbers_test.cc17
-rw-r--r--absl/strings/str_cat.cc12
-rw-r--r--absl/strings/str_cat.h38
-rw-r--r--absl/strings/str_cat_test.cc11
-rw-r--r--absl/strings/str_format.h514
-rw-r--r--absl/strings/str_format_test.cc628
-rw-r--r--absl/strings/str_join.h61
-rw-r--r--absl/strings/str_replace.cc4
-rw-r--r--absl/strings/str_replace.h34
-rw-r--r--absl/strings/str_replace_benchmark.cc8
-rw-r--r--absl/strings/str_split.cc17
-rw-r--r--absl/strings/str_split.h77
-rw-r--r--absl/strings/str_split_benchmark.cc8
-rw-r--r--absl/strings/str_split_test.cc34
-rw-r--r--absl/strings/string_view.cc4
-rw-r--r--absl/strings/string_view.h65
-rw-r--r--absl/strings/string_view_benchmark.cc10
-rw-r--r--absl/strings/string_view_test.cc20
-rw-r--r--absl/strings/strip.h22
-rw-r--r--absl/strings/strip_test.cc4
-rw-r--r--absl/strings/substitute.cc4
-rw-r--r--absl/strings/substitute.h40
82 files changed, 7500 insertions, 483 deletions
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index 328f52f3..3b85f1b4 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -19,6 +19,7 @@ load(
"ABSL_DEFAULT_COPTS",
"ABSL_TEST_COPTS",
"ABSL_EXCEPTIONS_FLAG",
+ "ABSL_EXCEPTIONS_FLAG_LINKOPTS",
)
package(
@@ -69,6 +70,7 @@ cc_library(
deps = [
":internal",
"//absl/base",
+ "//absl/base:bits",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:endian",
@@ -86,7 +88,6 @@ cc_library(
"internal/utf8.cc",
],
hdrs = [
- "internal/bits.h",
"internal/char_map.h",
"internal/ostringstream.h",
"internal/resize_uninitialized.h",
@@ -159,6 +160,18 @@ cc_test(
)
cc_test(
+ name = "ascii_benchmark",
+ srcs = ["ascii_benchmark.cc"],
+ copts = ABSL_TEST_COPTS,
+ tags = ["benchmark"],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":strings",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+)
+
+cc_test(
name = "memutil_benchmark",
srcs = [
"internal/memutil.h",
@@ -200,7 +213,6 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":internal",
- ":strings",
"//absl/base:core_headers",
"@com_google_googletest//:gtest_main",
],
@@ -225,6 +237,7 @@ cc_test(
size = "small",
srcs = ["string_view_test.cc"],
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
visibility = ["//visibility:private"],
deps = [
":strings",
@@ -361,7 +374,6 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":strings",
- "//absl/memory",
"@com_github_google_benchmark//:benchmark_main",
],
)
@@ -401,6 +413,7 @@ cc_test(
copts = ABSL_TEST_COPTS,
visibility = ["//visibility:private"],
deps = [
+ ":pow10_helper",
":strings",
"//absl/base",
"//absl/base:core_headers",
@@ -409,6 +422,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"],
@@ -446,6 +472,8 @@ cc_test(
srcs = ["charconv_test.cc"],
copts = ABSL_TEST_COPTS,
deps = [
+ ":pow10_helper",
+ ":str_format",
":strings",
"//absl/base",
"@com_google_googletest//:gtest_main",
@@ -486,9 +514,171 @@ cc_test(
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",
+ ],
+)
+
+cc_library(
+ name = "pow10_helper",
+ testonly = True,
+ srcs = ["internal/pow10_helper.cc"],
+ hdrs = ["internal/pow10_helper.h"],
+ visibility = ["//visibility:private"],
+)
+
+cc_test(
+ name = "pow10_helper_test",
+ srcs = ["internal/pow10_helper_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":pow10_helper",
+ ":str_format",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index cab2c456..5b877ad1 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -32,7 +32,6 @@ 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"
@@ -68,7 +67,7 @@ list(APPEND STRINGS_SRC
${STRINGS_PUBLIC_HEADERS}
${STRINGS_INTERNAL_HEADERS}
)
-set(STRINGS_PUBLIC_LIBRARIES absl::base absl_throw_delegate)
+set(STRINGS_PUBLIC_LIBRARIES absl::base absl_internal_throw_delegate)
absl_library(
TARGET
@@ -81,6 +80,65 @@ 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::inlined_vector
+ 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
+)
+
+# pow10_helper
+absl_library(
+ TARGET
+ pow10_helper
+ SOURCES
+ "internal/pow10_helper.cc"
+ "internal/pow10_helper.h"
+)
#
## TESTS
@@ -158,7 +216,7 @@ absl_test(
# test string_view_test
set(STRING_VIEW_TEST_SRC "string_view_test.cc")
-set(STRING_VIEW_TEST_PUBLIC_LIBRARIES absl::strings absl_throw_delegate absl::base)
+set(STRING_VIEW_TEST_PUBLIC_LIBRARIES absl::strings absl_internal_throw_delegate absl::base)
absl_test(
TARGET
@@ -186,7 +244,7 @@ absl_test(
# test str_replace_test
set(STR_REPLACE_TEST_SRC "str_replace_test.cc")
-set(STR_REPLACE_TEST_PUBLIC_LIBRARIES absl::strings absl::base absl_throw_delegate)
+set(STR_REPLACE_TEST_PUBLIC_LIBRARIES absl::strings absl::base absl_internal_throw_delegate)
absl_test(
TARGET
@@ -200,7 +258,7 @@ absl_test(
# test str_split_test
set(STR_SPLIT_TEST_SRC "str_split_test.cc")
-set(STR_SPLIT_TEST_PUBLIC_LIBRARIES absl::strings absl::base absl_throw_delegate)
+set(STR_SPLIT_TEST_PUBLIC_LIBRARIES absl::strings absl::base absl_internal_throw_delegate)
absl_test(
TARGET
@@ -267,7 +325,7 @@ absl_test(
# test numbers_test
set(NUMBERS_TEST_SRC "numbers_test.cc")
-set(NUMBERS_TEST_PUBLIC_LIBRARIES absl::strings)
+set(NUMBERS_TEST_PUBLIC_LIBRARIES absl::strings pow10_helper)
absl_test(
TARGET
@@ -309,7 +367,7 @@ absl_test(
# test charconv_test
set(CHARCONV_TEST_SRC "charconv_test.cc")
-set(CHARCONV_TEST_PUBLIC_LIBRARIES absl::strings)
+set(CHARCONV_TEST_PUBLIC_LIBRARIES absl::strings absl::str_format pow10_helper)
absl_test(
TARGET
@@ -347,3 +405,77 @@ absl_test(
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
+)
+
+# test pow10_helper_test
+absl_test(
+ TARGET
+ pow10_helper_test
+ SOURCES
+ "internal/pow10_helper_test.cc"
+ PUBLIC_LIBRARIES
+ pow10_helper
+ absl::str_format
+)
diff --git a/absl/strings/ascii.cc b/absl/strings/ascii.cc
index 5c94acc7..5d08e816 100644
--- a/absl/strings/ascii.cc
+++ b/absl/strings/ascii.cc
@@ -15,7 +15,7 @@
#include "absl/strings/ascii.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace ascii_internal {
// # Table generated by this Python code (bit 0x02 is currently unused):
@@ -196,5 +196,5 @@ void RemoveExtraAsciiWhitespace(std::string* str) {
str->erase(output_it - &(*str)[0]);
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/ascii.h b/absl/strings/ascii.h
index 507fad4b..98418fd2 100644
--- a/absl/strings/ascii.h
+++ b/absl/strings/ascii.h
@@ -59,7 +59,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace ascii_internal {
// Declaration for an array of bitfields holding character information.
@@ -166,7 +166,7 @@ inline char ascii_tolower(unsigned char c) {
// Converts the characters in `s` to lowercase, changing the contents of `s`.
void AsciiStrToLower(std::string* s);
-// Creates a lowercase std::string from a given absl::string_view.
+// Creates a lowercase string from a given absl::string_view.
ABSL_MUST_USE_RESULT inline std::string AsciiStrToLower(absl::string_view s) {
std::string result(s);
absl::AsciiStrToLower(&result);
@@ -184,7 +184,7 @@ inline char ascii_toupper(unsigned char c) {
// Converts the characters in `s` to uppercase, changing the contents of `s`.
void AsciiStrToUpper(std::string* s);
-// Creates an uppercase std::string from a given absl::string_view.
+// Creates an uppercase string from a given absl::string_view.
ABSL_MUST_USE_RESULT inline std::string AsciiStrToUpper(absl::string_view s) {
std::string result(s);
absl::AsciiStrToUpper(&result);
@@ -196,10 +196,10 @@ ABSL_MUST_USE_RESULT inline std::string AsciiStrToUpper(absl::string_view s) {
ABSL_MUST_USE_RESULT inline absl::string_view StripLeadingAsciiWhitespace(
absl::string_view str) {
auto it = std::find_if_not(str.begin(), str.end(), absl::ascii_isspace);
- return absl::string_view(it, str.end() - it);
+ return str.substr(it - str.begin());
}
-// Strips in place whitespace from the beginning of the given std::string.
+// Strips in place whitespace from the beginning of the given string.
inline void StripLeadingAsciiWhitespace(std::string* str) {
auto it = std::find_if_not(str->begin(), str->end(), absl::ascii_isspace);
str->erase(str->begin(), it);
@@ -210,10 +210,10 @@ inline void StripLeadingAsciiWhitespace(std::string* str) {
ABSL_MUST_USE_RESULT inline absl::string_view StripTrailingAsciiWhitespace(
absl::string_view str) {
auto it = std::find_if_not(str.rbegin(), str.rend(), absl::ascii_isspace);
- return absl::string_view(str.begin(), str.rend() - it);
+ return str.substr(0, str.rend() - it);
}
-// Strips in place whitespace from the end of the given std::string
+// Strips in place whitespace from the end of the given string
inline void StripTrailingAsciiWhitespace(std::string* str) {
auto it = std::find_if_not(str->rbegin(), str->rend(), absl::ascii_isspace);
str->erase(str->rend() - it);
@@ -226,7 +226,7 @@ ABSL_MUST_USE_RESULT inline absl::string_view StripAsciiWhitespace(
return StripTrailingAsciiWhitespace(StripLeadingAsciiWhitespace(str));
}
-// Strips in place whitespace from both ends of the given std::string
+// Strips in place whitespace from both ends of the given string
inline void StripAsciiWhitespace(std::string* str) {
StripTrailingAsciiWhitespace(str);
StripLeadingAsciiWhitespace(str);
@@ -235,7 +235,7 @@ inline void StripAsciiWhitespace(std::string* str) {
// Removes leading, trailing, and consecutive internal whitespace.
void RemoveExtraAsciiWhitespace(std::string*);
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_ASCII_H_
diff --git a/absl/strings/ascii_benchmark.cc b/absl/strings/ascii_benchmark.cc
new file mode 100644
index 00000000..8dea4b8c
--- /dev/null
+++ b/absl/strings/ascii_benchmark.cc
@@ -0,0 +1,120 @@
+// 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/ascii.h"
+
+#include <cctype>
+#include <string>
+#include <array>
+#include <random>
+
+#include "benchmark/benchmark.h"
+
+namespace {
+
+std::array<unsigned char, 256> MakeShuffledBytes() {
+ std::array<unsigned char, 256> bytes;
+ for (size_t i = 0; i < 256; ++i) bytes[i] = static_cast<unsigned char>(i);
+ std::random_device rd;
+ std::seed_seq seed({rd(), rd(), rd(), rd(), rd(), rd(), rd(), rd()});
+ std::mt19937 g(seed);
+ std::shuffle(bytes.begin(), bytes.end(), g);
+ return bytes;
+}
+
+template <typename Function>
+void AsciiBenchmark(benchmark::State& state, Function f) {
+ std::array<unsigned char, 256> bytes = MakeShuffledBytes();
+ size_t sum = 0;
+ for (auto _ : state) {
+ for (unsigned char b : bytes) sum += f(b) ? 1 : 0;
+ }
+ // Make a copy of `sum` before calling `DoNotOptimize` to make sure that `sum`
+ // can be put in a CPU register and not degrade performance in the loop above.
+ size_t sum2 = sum;
+ benchmark::DoNotOptimize(sum2);
+ state.SetBytesProcessed(state.iterations() * bytes.size());
+}
+
+using StdAsciiFunction = int (*)(int);
+template <StdAsciiFunction f>
+void BM_Ascii(benchmark::State& state) {
+ AsciiBenchmark(state, f);
+}
+
+using AbslAsciiIsFunction = bool (*)(unsigned char);
+template <AbslAsciiIsFunction f>
+void BM_Ascii(benchmark::State& state) {
+ AsciiBenchmark(state, f);
+}
+
+using AbslAsciiToFunction = char (*)(unsigned char);
+template <AbslAsciiToFunction f>
+void BM_Ascii(benchmark::State& state) {
+ AsciiBenchmark(state, f);
+}
+
+inline char Noop(unsigned char b) { return static_cast<char>(b); }
+
+BENCHMARK_TEMPLATE(BM_Ascii, Noop);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isalpha);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isalpha);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isdigit);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isdigit);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isalnum);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isalnum);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isspace);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isspace);
+BENCHMARK_TEMPLATE(BM_Ascii, std::ispunct);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_ispunct);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isblank);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isblank);
+BENCHMARK_TEMPLATE(BM_Ascii, std::iscntrl);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_iscntrl);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isxdigit);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isxdigit);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isprint);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isprint);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isgraph);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isgraph);
+BENCHMARK_TEMPLATE(BM_Ascii, std::isupper);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isupper);
+BENCHMARK_TEMPLATE(BM_Ascii, std::islower);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_islower);
+BENCHMARK_TEMPLATE(BM_Ascii, isascii);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isascii);
+BENCHMARK_TEMPLATE(BM_Ascii, std::tolower);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_tolower);
+BENCHMARK_TEMPLATE(BM_Ascii, std::toupper);
+BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_toupper);
+
+static void BM_StrToLower(benchmark::State& state) {
+ const int size = state.range(0);
+ std::string s(size, 'X');
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(absl::AsciiStrToLower(s));
+ }
+}
+BENCHMARK(BM_StrToLower)->Range(1, 1 << 20);
+
+static void BM_StrToUpper(benchmark::State& state) {
+ const int size = state.range(0);
+ std::string s(size, 'x');
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(absl::AsciiStrToUpper(s));
+ }
+}
+BENCHMARK(BM_StrToUpper)->Range(1, 1 << 20);
+
+} // namespace
diff --git a/absl/strings/charconv.cc b/absl/strings/charconv.cc
index ab75ab49..21ea17b1 100644
--- a/absl/strings/charconv.cc
+++ b/absl/strings/charconv.cc
@@ -20,8 +20,8 @@
#include <cstring>
#include "absl/base/casts.h"
+#include "absl/base/internal/bits.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"
@@ -57,7 +57,7 @@
// narrower mantissas.
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
template <typename FloatType>
@@ -244,9 +244,9 @@ struct CalculatedFloat {
// minus the number of leading zero bits.)
int BitWidth(uint128 value) {
if (Uint128High64(value) == 0) {
- return 64 - strings_internal::CountLeadingZeros64(Uint128Low64(value));
+ return 64 - base_internal::CountLeadingZeros64(Uint128Low64(value));
}
- return 128 - strings_internal::CountLeadingZeros64(Uint128High64(value));
+ return 128 - base_internal::CountLeadingZeros64(Uint128High64(value));
}
// Calculates how far to the right a mantissa needs to be shifted to create a
@@ -519,7 +519,7 @@ 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);
+ int mantissa_width = 64 - base_internal::CountLeadingZeros64(mantissa);
const int shift = NormalizedShiftSize<FloatType>(mantissa_width, exponent);
bool result_exact;
exponent += shift;
@@ -980,5 +980,5 @@ const int16_t kPower10ExponentTable[] = {
};
} // namespace
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/charconv.h b/absl/strings/charconv.h
index fe2dee37..160306e6 100644
--- a/absl/strings/charconv.h
+++ b/absl/strings/charconv.h
@@ -18,12 +18,12 @@
#include <system_error> // NOLINT(build/c++11)
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// 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.
+// configure the string-to-float conversion.
enum class chars_format {
scientific = 1,
fixed = 2,
@@ -31,7 +31,7 @@ enum class chars_format {
general = fixed | scientific,
};
-// The return result of a std::string-to-number conversion.
+// The return result of a 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
@@ -68,7 +68,7 @@ struct from_chars_result {
// 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
+// then an exponent will never match. (For example, the 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
@@ -111,7 +111,7 @@ inline chars_format& operator^=(chars_format& lhs, chars_format rhs) {
return lhs;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_CHARCONV_H_
diff --git a/absl/strings/charconv_test.cc b/absl/strings/charconv_test.cc
index f8d71cc6..d07537eb 100644
--- a/absl/strings/charconv_test.cc
+++ b/absl/strings/charconv_test.cc
@@ -19,7 +19,9 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/strings/internal/pow10_helper.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
#ifdef _MSC_FULL_VER
#define ABSL_COMPILER_DOES_EXACT_ROUNDING 0
@@ -31,9 +33,11 @@
namespace {
+using absl::strings_internal::Pow10;
+
#if ABSL_COMPILER_DOES_EXACT_ROUNDING
-// Tests that the given std::string is accepted by absl::from_chars, and that it
+// Tests that the given 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);
@@ -250,7 +254,7 @@ TEST(FromChars, NearRoundingCasesExplicit) {
EXPECT_EQ(ToFloat("459926601011.e15"), ldexpf(12466336, 65));
}
-// Common test logic for converting a std::string which lies exactly halfway between
+// Common test logic for converting a string which lies exactly halfway between
// two target floats.
//
// mantissa and exponent represent the precise value between two floating point
@@ -655,7 +659,7 @@ int NextStep(int step) {
// 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.
+// input_generator maps from an integer index to a 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).
@@ -678,7 +682,8 @@ void TestOverflowAndUnderflow(
auto result =
absl::from_chars(input.data(), input.data() + input.size(), actual);
EXPECT_EQ(result.ec, std::errc());
- EXPECT_EQ(expected, actual);
+ EXPECT_EQ(expected, actual)
+ << absl::StrFormat("%a vs %a", expected, actual);
}
// test legal values near upper_bound
for (index = upper_bound, step = 1; index > lower_bound;
@@ -690,7 +695,8 @@ void TestOverflowAndUnderflow(
auto result =
absl::from_chars(input.data(), input.data() + input.size(), actual);
EXPECT_EQ(result.ec, std::errc());
- EXPECT_EQ(expected, actual);
+ EXPECT_EQ(expected, actual)
+ << absl::StrFormat("%a vs %a", expected, actual);
}
// Test underflow values below lower_bound
for (index = lower_bound - 1, step = 1; index > -1000000;
@@ -747,7 +753,7 @@ TEST(FromChars, HexdecimalFloatLimits) {
// 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); };
+ auto expected_gen = [](int index) { return Pow10(index); };
TestOverflowAndUnderflow<double>(input_gen, expected_gen, -323, 308);
}
@@ -759,7 +765,7 @@ TEST(FromChars, DecimalDoubleLimits) {
// 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); };
+ auto expected_gen = [](int index) { return Pow10(index); };
TestOverflowAndUnderflow<float>(input_gen, expected_gen, -45, 38);
}
diff --git a/absl/strings/escaping.cc b/absl/strings/escaping.cc
index 9874f492..69053c19 100644
--- a/absl/strings/escaping.cc
+++ b/absl/strings/escaping.cc
@@ -33,7 +33,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
// Digit conversion.
@@ -90,7 +90,7 @@ inline bool IsSurrogate(char32_t c, absl::string_view src, std::string* error) {
//
// Unescapes C escape sequences and is the reverse of CEscape().
//
-// If 'source' is valid, stores the unescaped std::string and its size in
+// If 'source' is valid, stores the unescaped string and its size in
// 'dest' and 'dest_len' respectively, and returns true. Otherwise
// returns false and optionally stores the error description in
// 'error'. Set 'error' to nullptr to disable error reporting.
@@ -105,7 +105,7 @@ bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped,
char* dest, ptrdiff_t* dest_len, std::string* error) {
char* d = dest;
const char* p = source.data();
- const char* end = source.end();
+ const char* end = p + source.size();
const char* last_byte = end - 1;
// Small optimization for case where source = dest and there's no escaping
@@ -295,7 +295,7 @@ bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped,
// ----------------------------------------------------------------------
// CUnescapeInternal()
//
-// Same as above but uses a C++ std::string for output. 'source' and 'dest'
+// Same as above but uses a C++ string for output. 'source' and 'dest'
// may be the same.
// ----------------------------------------------------------------------
bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped,
@@ -305,7 +305,7 @@ bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped,
ptrdiff_t dest_size;
if (!CUnescapeInternal(source,
leave_nulls_escaped,
- const_cast<char*>(dest->data()),
+ &(*dest)[0],
&dest_size,
error)) {
return false;
@@ -685,7 +685,7 @@ bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest,
// The arrays below were generated by the following code
// #include <sys/time.h>
// #include <stdlib.h>
-// #include <std::string.h>
+// #include <string.h>
// main()
// {
// static const char Base64[] =
@@ -940,7 +940,8 @@ constexpr char kBase64Chars[] =
constexpr char kWebSafeBase64Chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
-void Base64EscapeInternal(const unsigned char* src, size_t szsrc, std::string* dest,
+template <typename String>
+void Base64EscapeInternal(const unsigned char* src, size_t szsrc, String* dest,
bool do_padding, const char* base64_chars) {
const size_t calc_escaped_size =
CalculateBase64EscapedLenInternal(szsrc, do_padding);
@@ -952,7 +953,8 @@ void Base64EscapeInternal(const unsigned char* src, size_t szsrc, std::string* d
dest->erase(escaped_len);
}
-bool Base64UnescapeInternal(const char* src, size_t slen, std::string* dest,
+template <typename String>
+bool Base64UnescapeInternal(const char* src, size_t slen, String* dest,
const signed char* unbase64) {
// Determine the size of the output std::string. Base64 encodes every 3 bytes into
// 4 characters. any leftover chars are added directly for good measure.
@@ -1000,7 +1002,7 @@ constexpr char kHexValue[256] = {
/* clang-format on */
// This is a templated function so that T can be either a char*
-// or a std::string. This works because we use the [] operator to access
+// or a string. This works because we use the [] operator to access
// individual characters at a time.
template <typename T>
void HexStringToBytesInternal(const char* from, T to, ptrdiff_t num) {
@@ -1010,7 +1012,7 @@ void HexStringToBytesInternal(const char* from, T to, ptrdiff_t num) {
}
}
-// This is a templated function so that T can be either a char* or a std::string.
+// This is a templated function so that T can be either a char* or a string.
template <typename T>
void BytesToHexStringInternal(const unsigned char* src, T dest, ptrdiff_t num) {
auto dest_ptr = &dest[0];
@@ -1107,5 +1109,5 @@ std::string BytesToHexString(absl::string_view from) {
return result;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/escaping.h b/absl/strings/escaping.h
index 16326a9c..a31fb374 100644
--- a/absl/strings/escaping.h
+++ b/absl/strings/escaping.h
@@ -17,7 +17,7 @@
// File: escaping.h
// -----------------------------------------------------------------------------
//
-// This header file contains std::string utilities involved in escaping and
+// This header file contains string utilities involved in escaping and
// unescaping strings in various ways.
//
@@ -34,11 +34,11 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// CUnescape()
//
-// Unescapes a `source` std::string and copies it into `dest`, rewriting C-style
+// Unescapes a `source` string and copies it into `dest`, rewriting C-style
// escape sequences (http://en.cppreference.com/w/cpp/language/escape) into
// their proper code point equivalents, returning `true` if successful.
//
@@ -58,9 +58,10 @@ inline namespace lts_2018_06_20 {
// 0x99).
//
//
-// If any errors are encountered, this function returns `false` and stores the
-// first encountered error in `error`. To disable error reporting, set `error`
-// to `nullptr` or use the overload with no error reporting below.
+// If any errors are encountered, this function returns `false`, leaving the
+// `dest` output parameter in an unspecified state, and stores the first
+// encountered error in `error`. To disable error reporting, set `error` to
+// `nullptr` or use the overload with no error reporting below.
//
// Example:
//
@@ -79,7 +80,7 @@ inline bool CUnescape(absl::string_view source, std::string* dest) {
// CEscape()
//
-// Escapes a 'src' std::string using C-style escapes sequences
+// Escapes a 'src' string using C-style escapes sequences
// (http://en.cppreference.com/w/cpp/language/escape), escaping other
// non-printable/non-whitespace bytes as octal sequences (e.g. "\377").
//
@@ -92,7 +93,7 @@ std::string CEscape(absl::string_view src);
// CHexEscape()
//
-// Escapes a 'src' std::string using C-style escape sequences, escaping
+// Escapes a 'src' string using C-style escape sequences, escaping
// other non-printable/non-whitespace bytes as hexadecimal sequences (e.g.
// "\xFF").
//
@@ -105,7 +106,7 @@ std::string CHexEscape(absl::string_view src);
// Utf8SafeCEscape()
//
-// Escapes a 'src' std::string using C-style escape sequences, escaping bytes as
+// Escapes a 'src' string using C-style escape sequences, escaping bytes as
// octal sequences, and passing through UTF-8 characters without conversion.
// I.e., when encountering any bytes with their high bit set, this function
// will not escape those values, whether or not they are valid UTF-8.
@@ -113,51 +114,51 @@ std::string Utf8SafeCEscape(absl::string_view src);
// Utf8SafeCHexEscape()
//
-// Escapes a 'src' std::string using C-style escape sequences, escaping bytes as
+// Escapes a 'src' string using C-style escape sequences, escaping bytes as
// hexadecimal sequences, and passing through UTF-8 characters without
// conversion.
std::string Utf8SafeCHexEscape(absl::string_view src);
// Base64Unescape()
//
-// Converts a `src` std::string encoded in Base64 to its binary equivalent, writing
+// Converts a `src` string encoded in Base64 to its binary equivalent, writing
// it to a `dest` buffer, returning `true` on success. If `src` contains invalid
// characters, `dest` is cleared and returns `false`.
bool Base64Unescape(absl::string_view src, std::string* dest);
-// WebSafeBase64Unescape(absl::string_view, std::string*)
+// WebSafeBase64Unescape()
//
-// Converts a `src` std::string encoded in Base64 to its binary equivalent, writing
+// Converts a `src` string encoded in Base64 to its binary equivalent, writing
// it to a `dest` buffer, but using '-' instead of '+', and '_' instead of '/'.
// If `src` contains invalid characters, `dest` is cleared and returns `false`.
bool WebSafeBase64Unescape(absl::string_view src, std::string* dest);
// Base64Escape()
//
-// Encodes a `src` std::string into a `dest` buffer using base64 encoding, with
+// Encodes a `src` string into a `dest` buffer using base64 encoding, with
// padding characters. This function conforms with RFC 4648 section 4 (base64).
void Base64Escape(absl::string_view src, std::string* dest);
// WebSafeBase64Escape()
//
-// Encodes a `src` std::string into a `dest` buffer using uses '-' instead of '+' and
+// Encodes a `src` string into a `dest` buffer using '-' instead of '+' and
// '_' instead of '/', and without padding. This function conforms with RFC 4648
// section 5 (base64url).
void WebSafeBase64Escape(absl::string_view src, std::string* dest);
// HexStringToBytes()
//
-// Converts an ASCII hex std::string into bytes, returning binary data of length
+// Converts an ASCII hex string into bytes, returning binary data of length
// `from.size()/2`.
std::string HexStringToBytes(absl::string_view from);
// BytesToHexString()
//
-// Converts binary data into an ASCII text std::string, returning a std::string of size
+// Converts binary data into an ASCII text string, returning a string of size
// `2*from.size()`.
std::string BytesToHexString(absl::string_view from);
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_ESCAPING_H_
diff --git a/absl/strings/escaping_test.cc b/absl/strings/escaping_test.cc
index 3f65ec10..9dc27f3f 100644
--- a/absl/strings/escaping_test.cc
+++ b/absl/strings/escaping_test.cc
@@ -543,18 +543,19 @@ static struct {
{"abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo="},
};
-TEST(Base64, EscapeAndUnescape) {
+template <typename StringType>
+void TestEscapeAndUnescape() {
// Check the short strings; this tests the math (and boundaries)
for (const auto& tc : base64_tests) {
- std::string encoded("this junk should be ignored");
+ StringType encoded("this junk should be ignored");
absl::Base64Escape(tc.plaintext, &encoded);
EXPECT_EQ(encoded, tc.cyphertext);
- std::string decoded("this junk should be ignored");
+ StringType decoded("this junk should be ignored");
EXPECT_TRUE(absl::Base64Unescape(encoded, &decoded));
EXPECT_EQ(decoded, tc.plaintext);
- std::string websafe(tc.cyphertext);
+ StringType websafe(tc.cyphertext);
for (int c = 0; c < websafe.size(); ++c) {
if ('+' == websafe[c]) websafe[c] = '-';
if ('/' == websafe[c]) websafe[c] = '_';
@@ -576,7 +577,7 @@ TEST(Base64, EscapeAndUnescape) {
// Now try the long strings, this tests the streaming
for (const auto& tc : absl::strings_internal::base64_strings()) {
- std::string buffer;
+ StringType buffer;
absl::WebSafeBase64Escape(tc.plaintext, &buffer);
EXPECT_EQ(tc.cyphertext, buffer);
}
@@ -586,7 +587,7 @@ TEST(Base64, EscapeAndUnescape) {
absl::string_view data_set[] = {"ab-/", absl::string_view("\0bcd", 4),
absl::string_view("abc.\0", 5)};
for (absl::string_view bad_data : data_set) {
- std::string buf;
+ StringType buf;
EXPECT_FALSE(absl::Base64Unescape(bad_data, &buf));
EXPECT_FALSE(absl::WebSafeBase64Unescape(bad_data, &buf));
EXPECT_TRUE(buf.empty());
@@ -594,6 +595,10 @@ TEST(Base64, EscapeAndUnescape) {
}
}
+TEST(Base64, EscapeAndUnescape) {
+ TestEscapeAndUnescape<std::string>();
+}
+
TEST(Base64, DISABLED_HugeData) {
const size_t kSize = size_t(3) * 1000 * 1000 * 1000;
static_assert(kSize % 3 == 0, "kSize must be divisible by 3");
diff --git a/absl/strings/internal/bits.h b/absl/strings/internal/bits.h
deleted file mode 100644
index a7d0ee5b..00000000
--- a/absl/strings/internal/bits.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// 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_BITS_H_
-#define ABSL_STRINGS_INTERNAL_BITS_H_
-
-#include <cstdint>
-
-#if defined(_MSC_VER) && defined(_M_X64)
-#include <intrin.h>
-#pragma intrinsic(_BitScanReverse64)
-#endif
-
-namespace absl {
-inline namespace lts_2018_06_20 {
-namespace strings_internal {
-
-// Returns the number of leading 0 bits in a 64-bit value.
-inline int CountLeadingZeros64(uint64_t n) {
-#if defined(__GNUC__)
- static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
- "__builtin_clzll does not take 64bit arg");
- return n == 0 ? 64 : __builtin_clzll(n);
-#elif defined(_MSC_VER) && defined(_M_X64)
- unsigned long result; // NOLINT(runtime/int)
- if (_BitScanReverse64(&result, n)) {
- return 63 - result;
- }
- return 64;
-#else
- int zeroes = 60;
- if (n >> 32) zeroes -= 32, n >>= 32;
- if (n >> 16) zeroes -= 16, n >>= 16;
- if (n >> 8) zeroes -= 8, n >>= 8;
- if (n >> 4) zeroes -= 4, n >>= 4;
- return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0\0"[n] + zeroes;
-#endif
-}
-
-} // namespace strings_internal
-} // inline namespace lts_2018_06_20
-} // namespace absl
-
-#endif // ABSL_STRINGS_INTERNAL_BITS_H_
diff --git a/absl/strings/internal/char_map.h b/absl/strings/internal/char_map.h
index e1280b03..10b7d007 100644
--- a/absl/strings/internal/char_map.h
+++ b/absl/strings/internal/char_map.h
@@ -28,7 +28,7 @@
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
class Charmap {
@@ -150,7 +150,7 @@ constexpr Charmap GraphCharmap() { return PrintCharmap() & ~SpaceCharmap(); }
constexpr Charmap PunctCharmap() { return GraphCharmap() & ~AlnumCharmap(); }
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_CHAR_MAP_H_
diff --git a/absl/strings/internal/charconv_bigint.cc b/absl/strings/internal/charconv_bigint.cc
index eb2e2976..dac907e2 100644
--- a/absl/strings/internal/charconv_bigint.cc
+++ b/absl/strings/internal/charconv_bigint.cc
@@ -19,7 +19,7 @@
#include <string>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
namespace {
@@ -355,5 +355,5 @@ template class BigUnsigned<4>;
template class BigUnsigned<84>;
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/internal/charconv_bigint.h b/absl/strings/internal/charconv_bigint.h
index 9827b56e..ffafc11c 100644
--- a/absl/strings/internal/charconv_bigint.h
+++ b/absl/strings/internal/charconv_bigint.h
@@ -25,7 +25,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
// The largest power that 5 that can be raised to, and still fit in a uint32_t.
@@ -58,17 +58,10 @@ class BigUnsigned {
"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;
- }
- }
+ explicit constexpr BigUnsigned(uint64_t v)
+ : size_((v >> 32) ? 2 : v ? 1 : 0),
+ words_{static_cast<uint32_t>(v & 0xffffffffu),
+ static_cast<uint32_t>(v >> 32)} {}
// 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
@@ -422,7 +415,7 @@ extern template class BigUnsigned<4>;
extern template class BigUnsigned<84>;
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // 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
index 118b0dcb..dbab3208 100644
--- a/absl/strings/internal/charconv_bigint_test.cc
+++ b/absl/strings/internal/charconv_bigint_test.cc
@@ -19,7 +19,7 @@
#include "gtest/gtest.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
TEST(BigUnsigned, ShiftLeft) {
@@ -201,5 +201,5 @@ TEST(BigUnsigned, TenToTheNth) {
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/internal/charconv_parse.cc b/absl/strings/internal/charconv_parse.cc
index 37d75635..68d65a8a 100644
--- a/absl/strings/internal/charconv_parse.cc
+++ b/absl/strings/internal/charconv_parse.cc
@@ -22,7 +22,7 @@
#include "absl/strings/internal/memutil.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
// ParseFloat<10> will read the first 19 significant digits of the mantissa.
@@ -92,7 +92,7 @@ static_assert(std::numeric_limits<int>::digits10 >= kDecimalExponentDigitsMax,
// 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
+// digits we will accept. The implementation refuses to match a string with
// more consecutive significant mantissa digits than this.
constexpr int kDecimalDigitLimit = 50000000;
@@ -494,5 +494,5 @@ template ParsedFloat ParseFloat<16>(const char* begin, const char* end,
chars_format format_flags);
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/internal/charconv_parse.h b/absl/strings/internal/charconv_parse.h
index 41f4f40d..17d5a8f8 100644
--- a/absl/strings/internal/charconv_parse.h
+++ b/absl/strings/internal/charconv_parse.h
@@ -20,7 +20,7 @@
#include "absl/strings/charconv.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
// Enum indicating whether a parsed float is a number or special value.
@@ -93,6 +93,6 @@ extern template ParsedFloat ParseFloat<16>(const char* begin, const char* end,
absl::chars_format format_flags);
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // 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
index 1ff86004..f48b9aee 100644
--- a/absl/strings/internal/charconv_parse_test.cc
+++ b/absl/strings/internal/charconv_parse_test.cc
@@ -29,16 +29,16 @@ using absl::strings_internal::ParseFloat;
namespace {
-// Check that a given std::string input is parsed to the expected mantissa and
+// Check that a given string input is parsed to the expected mantissa and
// exponent.
//
-// Input std::string `s` must contain a '$' character. It marks the end of the
+// Input 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
+// If input 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
+// the location of the extended NaN 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,
@@ -92,10 +92,10 @@ void ExpectParsedFloat(std::string s, absl::chars_format format_flags,
EXPECT_EQ(characters_matched, expected_characters_matched);
}
-// Check that a given std::string input is parsed to the expected mantissa and
+// Check that a given string input is parsed to the expected mantissa and
// exponent.
//
-// Input std::string `s` must contain a '$' character. It marks the end of the
+// Input 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,
@@ -106,7 +106,7 @@ void ExpectNumber(std::string s, absl::chars_format format_flags,
expected_literal_exponent);
}
-// Check that a given std::string input is parsed to the given special value.
+// Check that a given 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.
@@ -116,7 +116,7 @@ void ExpectSpecial(const std::string& s, absl::chars_format format_flags,
ExpectParsedFloat<16>(s, format_flags, type, 0, 0);
}
-// Check that a given input std::string is not matched by Float.
+// Check that a given input string is not matched by Float.
template <int base>
void ExpectFailedParse(absl::string_view s, absl::chars_format format_flags) {
ParsedFloat parsed =
diff --git a/absl/strings/internal/escaping_test_common.h b/absl/strings/internal/escaping_test_common.h
index 478e0582..50ef595f 100644
--- a/absl/strings/internal/escaping_test_common.h
+++ b/absl/strings/internal/escaping_test_common.h
@@ -22,7 +22,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
struct base64_testcase {
@@ -127,7 +127,7 @@ inline const std::array<base64_testcase, 5>& base64_strings() {
}
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_ESCAPING_TEST_COMMON_H_
diff --git a/absl/strings/internal/memutil.cc b/absl/strings/internal/memutil.cc
index 13ad686c..1d6cfa36 100644
--- a/absl/strings/internal/memutil.cc
+++ b/absl/strings/internal/memutil.cc
@@ -17,7 +17,7 @@
#include <cstdlib>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
int memcasecmp(const char* s1, const char* s2, size_t len) {
@@ -108,5 +108,5 @@ const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle,
}
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/internal/memutil.h b/absl/strings/internal/memutil.h
index 828b01d5..dcc5c9a3 100644
--- a/absl/strings/internal/memutil.h
+++ b/absl/strings/internal/memutil.h
@@ -14,7 +14,7 @@
// limitations under the License.
//
-// These routines provide mem versions of standard C std::string routines,
+// These routines provide mem versions of standard C string routines,
// such as strpbrk. They function exactly the same as the str versions,
// so if you wonder what they are, replace the word "mem" by
// "str" and check out the man page. I could return void*, as the
@@ -22,14 +22,14 @@
// since this is by far the most common way these functions are called.
//
// The difference between the mem and str versions is the mem version
-// takes a pointer and a length, rather than a '\0'-terminated std::string.
+// takes a pointer and a length, rather than a '\0'-terminated string.
// The memcase* routines defined here assume the locale is "C"
// (they use absl::ascii_tolower instead of tolower).
//
// These routines are based on the BSD library.
//
-// Here's a list of routines from std::string.h, and their mem analogues.
-// Functions in lowercase are defined in std::string.h; those in UPPERCASE
+// Here's a list of routines from string.h, and their mem analogues.
+// Functions in lowercase are defined in string.h; those in UPPERCASE
// are defined here:
//
// strlen --
@@ -69,7 +69,7 @@
#include "absl/strings/ascii.h" // for absl::ascii_tolower
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
inline char* memcat(char* dest, size_t destlen, const char* src,
@@ -142,7 +142,7 @@ const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle,
size_t neelen);
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_MEMUTIL_H_
diff --git a/absl/strings/internal/numbers_test_common.h b/absl/strings/internal/numbers_test_common.h
index a511dcf7..32aa0bfa 100644
--- a/absl/strings/internal/numbers_test_common.h
+++ b/absl/strings/internal/numbers_test_common.h
@@ -24,7 +24,7 @@
#include <string>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
template <typename IntType>
@@ -65,11 +65,11 @@ struct uint32_test_case {
inline const std::array<uint32_test_case, 27>& strtouint32_test_cases() {
static const std::array<uint32_test_case, 27> test_cases{{
- {"0xffffffff", true, 16, std::numeric_limits<uint32_t>::max()},
+ {"0xffffffff", true, 16, (std::numeric_limits<uint32_t>::max)()},
{"0x34234324", true, 16, 0x34234324},
{"34234324", true, 16, 0x34234324},
{"0", true, 16, 0},
- {" \t\n 0xffffffff", true, 16, std::numeric_limits<uint32_t>::max()},
+ {" \t\n 0xffffffff", true, 16, (std::numeric_limits<uint32_t>::max)()},
{" \f\v 46", true, 10, 46}, // must accept weird whitespace
{" \t\n 72717222", true, 8, 072717222},
{" \t\n 072717222", true, 8, 072717222},
@@ -78,7 +78,7 @@ inline const std::array<uint32_test_case, 27>& strtouint32_test_cases() {
// Base-10 version.
{"34234324", true, 0, 34234324},
- {"4294967295", true, 0, std::numeric_limits<uint32_t>::max()},
+ {"4294967295", true, 0, (std::numeric_limits<uint32_t>::max)()},
{"34234324 \n\t", true, 10, 34234324},
// Unusual base
@@ -97,8 +97,8 @@ inline const std::array<uint32_test_case, 27>& strtouint32_test_cases() {
{" \t\n -123", false, 0, 0},
// Out of bounds.
- {"4294967296", false, 0, std::numeric_limits<uint32_t>::max()},
- {"0x100000000", false, 0, std::numeric_limits<uint32_t>::max()},
+ {"4294967296", false, 0, (std::numeric_limits<uint32_t>::max)()},
+ {"0x100000000", false, 0, (std::numeric_limits<uint32_t>::max)()},
{nullptr, false, 0, 0},
}};
return test_cases;
@@ -120,7 +120,7 @@ inline const std::array<uint64_test_case, 34>& strtouint64_test_cases() {
{"000", true, 0, 0},
{"0", true, 0, 0},
{" \t\n 0xffffffffffffffff", true, 16,
- std::numeric_limits<uint64_t>::max()},
+ (std::numeric_limits<uint64_t>::max)()},
{"012345670123456701234", true, 8, int64_t{012345670123456701234}},
{"12345670123456701234", true, 8, int64_t{012345670123456701234}},
@@ -131,7 +131,7 @@ inline const std::array<uint64_test_case, 34>& strtouint64_test_cases() {
{"34234324487834466", true, 0, int64_t{34234324487834466}},
{" \t\n 18446744073709551615", true, 0,
- std::numeric_limits<uint64_t>::max()},
+ (std::numeric_limits<uint64_t>::max)()},
{"34234324487834466 \n\t ", true, 0, int64_t{34234324487834466}},
@@ -157,12 +157,13 @@ inline const std::array<uint64_test_case, 34>& strtouint64_test_cases() {
// Out of bounds.
{"18446744073709551616", false, 10, 0},
{"18446744073709551616", false, 0, 0},
- {"0x10000000000000000", false, 16, std::numeric_limits<uint64_t>::max()},
+ {"0x10000000000000000", false, 16,
+ (std::numeric_limits<uint64_t>::max)()},
{"0X10000000000000000", false, 16,
- std::numeric_limits<uint64_t>::max()}, // 0X versus 0x.
- {"0x10000000000000000", false, 0, std::numeric_limits<uint64_t>::max()},
+ (std::numeric_limits<uint64_t>::max)()}, // 0X versus 0x.
+ {"0x10000000000000000", false, 0, (std::numeric_limits<uint64_t>::max)()},
{"0X10000000000000000", false, 0,
- std::numeric_limits<uint64_t>::max()}, // 0X versus 0x.
+ (std::numeric_limits<uint64_t>::max)()}, // 0X versus 0x.
{"0x1234", true, 16, 0x1234},
@@ -174,7 +175,7 @@ inline const std::array<uint64_test_case, 34>& strtouint64_test_cases() {
}
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_NUMBERS_TEST_COMMON_H_
diff --git a/absl/strings/internal/ostringstream.cc b/absl/strings/internal/ostringstream.cc
index 9fe5b3c5..77f4b0b3 100644
--- a/absl/strings/internal/ostringstream.cc
+++ b/absl/strings/internal/ostringstream.cc
@@ -15,7 +15,7 @@
#include "absl/strings/internal/ostringstream.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
OStringStream::Buf::int_type OStringStream::overflow(int c) {
@@ -32,5 +32,5 @@ std::streamsize OStringStream::xsputn(const char* s, std::streamsize n) {
}
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/internal/ostringstream.h b/absl/strings/internal/ostringstream.h
index 66ae3c29..908e170c 100644
--- a/absl/strings/internal/ostringstream.h
+++ b/absl/strings/internal/ostringstream.h
@@ -23,21 +23,21 @@
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
-// The same as std::ostringstream but appends to a user-specified std::string,
+// The same as std::ostringstream but appends to a user-specified string,
// and is faster. It is ~70% faster to create, ~50% faster to write to, and
-// completely free to extract the result std::string.
+// completely free to extract the result string.
//
-// std::string s;
+// string s;
// OStringStream strm(&s);
// strm << 42 << ' ' << 3.14; // appends to `s`
//
// The stream object doesn't have to be named. Starting from C++11 operator<<
// works with rvalues of std::ostream.
//
-// std::string s;
+// string s;
// OStringStream(&s) << 42 << ' ' << 3.14; // appends to `s`
//
// OStringStream is faster to create than std::ostringstream but it's still
@@ -46,14 +46,14 @@ namespace strings_internal {
//
// Creates unnecessary instances of OStringStream: slow.
//
-// std::string s;
+// string s;
// OStringStream(&s) << 42;
// OStringStream(&s) << ' ';
// OStringStream(&s) << 3.14;
//
// Creates a single instance of OStringStream and reuses it: fast.
//
-// std::string s;
+// string s;
// OStringStream strm(&s);
// strm << 42;
// strm << ' ';
@@ -83,7 +83,7 @@ class OStringStream : private std::basic_streambuf<char>, public std::ostream {
};
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_OSTRINGSTREAM_H_
diff --git a/absl/strings/internal/pow10_helper.cc b/absl/strings/internal/pow10_helper.cc
new file mode 100644
index 00000000..c7f4875a
--- /dev/null
+++ b/absl/strings/internal/pow10_helper.cc
@@ -0,0 +1,122 @@
+// 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/pow10_helper.h"
+
+#include <cmath>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace strings_internal {
+
+namespace {
+
+// The exact value of 1e23 falls precisely halfway between two representable
+// doubles. Furthermore, the rounding rules we prefer (break ties by rounding
+// to the nearest even) dictate in this case that the number should be rounded
+// down, but this is not completely specified for floating-point literals in
+// C++. (It just says to use the default rounding mode of the standard
+// library.) We ensure the result we want by using a number that has an
+// unambiguous correctly rounded answer.
+constexpr double k1e23 = 9999999999999999e7;
+
+constexpr double kPowersOfTen[] = {
+ 0.0, 1e-323, 1e-322, 1e-321, 1e-320, 1e-319, 1e-318, 1e-317, 1e-316,
+ 1e-315, 1e-314, 1e-313, 1e-312, 1e-311, 1e-310, 1e-309, 1e-308, 1e-307,
+ 1e-306, 1e-305, 1e-304, 1e-303, 1e-302, 1e-301, 1e-300, 1e-299, 1e-298,
+ 1e-297, 1e-296, 1e-295, 1e-294, 1e-293, 1e-292, 1e-291, 1e-290, 1e-289,
+ 1e-288, 1e-287, 1e-286, 1e-285, 1e-284, 1e-283, 1e-282, 1e-281, 1e-280,
+ 1e-279, 1e-278, 1e-277, 1e-276, 1e-275, 1e-274, 1e-273, 1e-272, 1e-271,
+ 1e-270, 1e-269, 1e-268, 1e-267, 1e-266, 1e-265, 1e-264, 1e-263, 1e-262,
+ 1e-261, 1e-260, 1e-259, 1e-258, 1e-257, 1e-256, 1e-255, 1e-254, 1e-253,
+ 1e-252, 1e-251, 1e-250, 1e-249, 1e-248, 1e-247, 1e-246, 1e-245, 1e-244,
+ 1e-243, 1e-242, 1e-241, 1e-240, 1e-239, 1e-238, 1e-237, 1e-236, 1e-235,
+ 1e-234, 1e-233, 1e-232, 1e-231, 1e-230, 1e-229, 1e-228, 1e-227, 1e-226,
+ 1e-225, 1e-224, 1e-223, 1e-222, 1e-221, 1e-220, 1e-219, 1e-218, 1e-217,
+ 1e-216, 1e-215, 1e-214, 1e-213, 1e-212, 1e-211, 1e-210, 1e-209, 1e-208,
+ 1e-207, 1e-206, 1e-205, 1e-204, 1e-203, 1e-202, 1e-201, 1e-200, 1e-199,
+ 1e-198, 1e-197, 1e-196, 1e-195, 1e-194, 1e-193, 1e-192, 1e-191, 1e-190,
+ 1e-189, 1e-188, 1e-187, 1e-186, 1e-185, 1e-184, 1e-183, 1e-182, 1e-181,
+ 1e-180, 1e-179, 1e-178, 1e-177, 1e-176, 1e-175, 1e-174, 1e-173, 1e-172,
+ 1e-171, 1e-170, 1e-169, 1e-168, 1e-167, 1e-166, 1e-165, 1e-164, 1e-163,
+ 1e-162, 1e-161, 1e-160, 1e-159, 1e-158, 1e-157, 1e-156, 1e-155, 1e-154,
+ 1e-153, 1e-152, 1e-151, 1e-150, 1e-149, 1e-148, 1e-147, 1e-146, 1e-145,
+ 1e-144, 1e-143, 1e-142, 1e-141, 1e-140, 1e-139, 1e-138, 1e-137, 1e-136,
+ 1e-135, 1e-134, 1e-133, 1e-132, 1e-131, 1e-130, 1e-129, 1e-128, 1e-127,
+ 1e-126, 1e-125, 1e-124, 1e-123, 1e-122, 1e-121, 1e-120, 1e-119, 1e-118,
+ 1e-117, 1e-116, 1e-115, 1e-114, 1e-113, 1e-112, 1e-111, 1e-110, 1e-109,
+ 1e-108, 1e-107, 1e-106, 1e-105, 1e-104, 1e-103, 1e-102, 1e-101, 1e-100,
+ 1e-99, 1e-98, 1e-97, 1e-96, 1e-95, 1e-94, 1e-93, 1e-92, 1e-91,
+ 1e-90, 1e-89, 1e-88, 1e-87, 1e-86, 1e-85, 1e-84, 1e-83, 1e-82,
+ 1e-81, 1e-80, 1e-79, 1e-78, 1e-77, 1e-76, 1e-75, 1e-74, 1e-73,
+ 1e-72, 1e-71, 1e-70, 1e-69, 1e-68, 1e-67, 1e-66, 1e-65, 1e-64,
+ 1e-63, 1e-62, 1e-61, 1e-60, 1e-59, 1e-58, 1e-57, 1e-56, 1e-55,
+ 1e-54, 1e-53, 1e-52, 1e-51, 1e-50, 1e-49, 1e-48, 1e-47, 1e-46,
+ 1e-45, 1e-44, 1e-43, 1e-42, 1e-41, 1e-40, 1e-39, 1e-38, 1e-37,
+ 1e-36, 1e-35, 1e-34, 1e-33, 1e-32, 1e-31, 1e-30, 1e-29, 1e-28,
+ 1e-27, 1e-26, 1e-25, 1e-24, 1e-23, 1e-22, 1e-21, 1e-20, 1e-19,
+ 1e-18, 1e-17, 1e-16, 1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10,
+ 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1,
+ 1e+0, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8,
+ 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17,
+ 1e+18, 1e+19, 1e+20, 1e+21, 1e+22, k1e23, 1e+24, 1e+25, 1e+26,
+ 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35,
+ 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, 1e+41, 1e+42, 1e+43, 1e+44,
+ 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53,
+ 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, 1e+61, 1e+62,
+ 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71,
+ 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80,
+ 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89,
+ 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98,
+ 1e+99, 1e+100, 1e+101, 1e+102, 1e+103, 1e+104, 1e+105, 1e+106, 1e+107,
+ 1e+108, 1e+109, 1e+110, 1e+111, 1e+112, 1e+113, 1e+114, 1e+115, 1e+116,
+ 1e+117, 1e+118, 1e+119, 1e+120, 1e+121, 1e+122, 1e+123, 1e+124, 1e+125,
+ 1e+126, 1e+127, 1e+128, 1e+129, 1e+130, 1e+131, 1e+132, 1e+133, 1e+134,
+ 1e+135, 1e+136, 1e+137, 1e+138, 1e+139, 1e+140, 1e+141, 1e+142, 1e+143,
+ 1e+144, 1e+145, 1e+146, 1e+147, 1e+148, 1e+149, 1e+150, 1e+151, 1e+152,
+ 1e+153, 1e+154, 1e+155, 1e+156, 1e+157, 1e+158, 1e+159, 1e+160, 1e+161,
+ 1e+162, 1e+163, 1e+164, 1e+165, 1e+166, 1e+167, 1e+168, 1e+169, 1e+170,
+ 1e+171, 1e+172, 1e+173, 1e+174, 1e+175, 1e+176, 1e+177, 1e+178, 1e+179,
+ 1e+180, 1e+181, 1e+182, 1e+183, 1e+184, 1e+185, 1e+186, 1e+187, 1e+188,
+ 1e+189, 1e+190, 1e+191, 1e+192, 1e+193, 1e+194, 1e+195, 1e+196, 1e+197,
+ 1e+198, 1e+199, 1e+200, 1e+201, 1e+202, 1e+203, 1e+204, 1e+205, 1e+206,
+ 1e+207, 1e+208, 1e+209, 1e+210, 1e+211, 1e+212, 1e+213, 1e+214, 1e+215,
+ 1e+216, 1e+217, 1e+218, 1e+219, 1e+220, 1e+221, 1e+222, 1e+223, 1e+224,
+ 1e+225, 1e+226, 1e+227, 1e+228, 1e+229, 1e+230, 1e+231, 1e+232, 1e+233,
+ 1e+234, 1e+235, 1e+236, 1e+237, 1e+238, 1e+239, 1e+240, 1e+241, 1e+242,
+ 1e+243, 1e+244, 1e+245, 1e+246, 1e+247, 1e+248, 1e+249, 1e+250, 1e+251,
+ 1e+252, 1e+253, 1e+254, 1e+255, 1e+256, 1e+257, 1e+258, 1e+259, 1e+260,
+ 1e+261, 1e+262, 1e+263, 1e+264, 1e+265, 1e+266, 1e+267, 1e+268, 1e+269,
+ 1e+270, 1e+271, 1e+272, 1e+273, 1e+274, 1e+275, 1e+276, 1e+277, 1e+278,
+ 1e+279, 1e+280, 1e+281, 1e+282, 1e+283, 1e+284, 1e+285, 1e+286, 1e+287,
+ 1e+288, 1e+289, 1e+290, 1e+291, 1e+292, 1e+293, 1e+294, 1e+295, 1e+296,
+ 1e+297, 1e+298, 1e+299, 1e+300, 1e+301, 1e+302, 1e+303, 1e+304, 1e+305,
+ 1e+306, 1e+307, 1e+308,
+};
+
+} // namespace
+
+double Pow10(int exp) {
+ if (exp < -324) {
+ return 0.0;
+ } else if (exp > 308) {
+ return INFINITY;
+ } else {
+ return kPowersOfTen[exp + 324];
+ }
+}
+
+} // namespace strings_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/pow10_helper.h b/absl/strings/internal/pow10_helper.h
new file mode 100644
index 00000000..750051bd
--- /dev/null
+++ b/absl/strings/internal/pow10_helper.h
@@ -0,0 +1,38 @@
+//
+// 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.
+//
+// This test helper library contains a table of powers of 10, to guarantee
+// precise values are computed across the full range of doubles. We can't rely
+// on the pow() function, because not all standard libraries ship a version
+// that is precise.
+#ifndef ABSL_STRINGS_POW10_HELPER_H_
+#define ABSL_STRINGS_POW10_HELPER_H_
+
+#include <vector>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace strings_internal {
+
+// Computes the precise value of 10^exp. (I.e. the nearest representable
+// double to the exact value, rounding to nearest-even in the (single) case of
+// being exactly halfway between.)
+double Pow10(int exp);
+
+} // namespace strings_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_STRINGS_POW10_HELPER_H_
diff --git a/absl/strings/internal/pow10_helper_test.cc b/absl/strings/internal/pow10_helper_test.cc
new file mode 100644
index 00000000..371fe122
--- /dev/null
+++ b/absl/strings/internal/pow10_helper_test.cc
@@ -0,0 +1,122 @@
+// 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/pow10_helper.h"
+
+#include <cmath>
+
+#include "gtest/gtest.h"
+#include "absl/strings/str_format.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+namespace strings_internal {
+
+namespace {
+
+struct TestCase {
+ int power; // Testing Pow10(power)
+ uint64_t significand; // Raw bits of the expected value
+ int radix; // significand is adjusted by 2^radix
+};
+
+TEST(Pow10HelperTest, Works) {
+ // The logic in pow10_helper.cc is so simple that theoretically we don't even
+ // need a test. However, we're paranoid and believe that there may be
+ // compilers that don't round floating-point literals correctly, even though
+ // it is specified by the standard. We check various edge cases, just to be
+ // sure.
+ constexpr TestCase kTestCases[] = {
+ // Subnormals
+ {-323, 0x2, -1074},
+ {-322, 0x14, -1074},
+ {-321, 0xca, -1074},
+ {-320, 0x7e8, -1074},
+ {-319, 0x4f10, -1074},
+ {-318, 0x316a2, -1074},
+ {-317, 0x1ee257, -1074},
+ {-316, 0x134d761, -1074},
+ {-315, 0xc1069cd, -1074},
+ {-314, 0x78a42205, -1074},
+ {-313, 0x4b6695433, -1074},
+ {-312, 0x2f201d49fb, -1074},
+ {-311, 0x1d74124e3d1, -1074},
+ {-310, 0x12688b70e62b, -1074},
+ {-309, 0xb8157268fdaf, -1074},
+ {-308, 0x730d67819e8d2, -1074},
+ // Values that are very close to rounding the other way.
+ // Comment shows difference of significand from the true value.
+ {-307, 0x11fa182c40c60d, -1072}, // -.4588
+ {-290, 0x18f2b061aea072, -1016}, // .4854
+ {-276, 0x11BA03F5B21000, -969}, // .4709
+ {-259, 0x1899C2F6732210, -913}, // .4830
+ {-252, 0x1D53844EE47DD1, -890}, // -.4743
+ {-227, 0x1E5297287C2F45, -807}, // -.4708
+ {-198, 0x1322E220A5B17E, -710}, // -.4714
+ {-195, 0x12B010D3E1CF56, -700}, // .4928
+ {-192, 0x123FF06EEA847A, -690}, // .4968
+ {-163, 0x1708D0F84D3DE7, -594}, // -.4977
+ {-145, 0x13FAAC3E3FA1F3, -534}, // -.4785
+ {-111, 0x133D4032C2C7F5, -421}, // .4774
+ {-106, 0x1D5B561574765B, -405}, // -.4869
+ {-104, 0x16EF5B40C2FC77, -398}, // -.4741
+ {-88, 0x197683DF2F268D, -345}, // -.4738
+ {-86, 0x13E497065CD61F, -338}, // .4736
+ {-76, 0x17288E1271F513, -305}, // -.4761
+ {-63, 0x1A53FC9631D10D, -262}, // .4929
+ {-30, 0x14484BFEEBC2A0, -152}, // .4758
+ {-21, 0x12E3B40A0E9B4F, -122}, // -.4916
+ {-5, 0x14F8B588E368F1, -69}, // .4829
+ {23, 0x152D02C7E14AF6, 24}, // -.5000 (exactly, round-to-even)
+ {29, 0x1431E0FAE6D721, 44}, // -.4870
+ {34, 0x1ED09BEAD87C03, 60}, // -.4721
+ {70, 0x172EBAD6DDC73D, 180}, // .4733
+ {105, 0x1BE7ABD3781ECA, 296}, // -.4850
+ {126, 0x17A2ECC414A03F, 366}, // -.4999
+ {130, 0x1CDA62055B2D9E, 379}, // .4855
+ {165, 0x115D847AD00087, 496}, // -.4913
+ {172, 0x14B378469B6732, 519}, // .4818
+ {187, 0x1262DFEEBBB0F9, 569}, // -.4805
+ {210, 0x18557F31326BBB, 645}, // -.4992
+ {212, 0x1302CB5E6F642A, 652}, // -.4838
+ {215, 0x1290BA9A38C7D1, 662}, // -.4881
+ {236, 0x1F736F9B3494E9, 731}, // .4707
+ {244, 0x176EC98994F489, 758}, // .4924
+ {250, 0x1658E3AB795204, 778}, // -.4963
+ {252, 0x117571DDF6C814, 785}, // .4873
+ {254, 0x1B4781EAD1989E, 791}, // -.4887
+ {260, 0x1A03FDE214CAF1, 811}, // .4784
+ {284, 0x1585041B2C477F, 891}, // .4798
+ {304, 0x1D2A1BE4048F90, 957}, // -.4987
+ // Out-of-range values
+ {-324, 0x0, 0},
+ {-325, 0x0, 0},
+ {-326, 0x0, 0},
+ {309, 1, 2000},
+ {310, 1, 2000},
+ {311, 1, 2000},
+ };
+ for (const TestCase& test_case : kTestCases) {
+ EXPECT_EQ(Pow10(test_case.power),
+ std::ldexp(test_case.significand, test_case.radix))
+ << absl::StrFormat("Failure for Pow10(%d): %a vs %a", test_case.power,
+ Pow10(test_case.power),
+ std::ldexp(test_case.significand, test_case.radix));
+ }
+}
+
+} // namespace
+} // namespace strings_internal
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/resize_uninitialized.h b/absl/strings/internal/resize_uninitialized.h
index b3690332..2951bf84 100644
--- a/absl/strings/internal/resize_uninitialized.h
+++ b/absl/strings/internal/resize_uninitialized.h
@@ -24,7 +24,7 @@
#include "absl/meta/type_traits.h" // for void_t
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
// Is a subclass of true_type or false_type, depending on whether or not
@@ -45,8 +45,8 @@ void ResizeUninit(string_type* s, size_t new_size, std::false_type) {
s->resize(new_size);
}
-// Returns true if the std::string implementation supports a resize where
-// the new characters added to the std::string are left untouched.
+// Returns true if the string implementation supports a resize where
+// the new characters added to the string are left untouched.
//
// (A better name might be "STLStringSupportsUninitializedResize", alluding to
// the previous function.)
@@ -58,14 +58,14 @@ inline constexpr bool STLStringSupportsNontrashingResize(string_type*) {
// Like str->resize(new_size), except any new characters added to "*str" as a
// result of resizing may be left uninitialized, rather than being filled with
// '0' bytes. Typically used when code is then going to overwrite the backing
-// store of the std::string with known data. Uses a Google extension to std::string.
+// store of the string with known data. Uses a Google extension to ::string.
template <typename string_type, typename = void>
inline void STLStringResizeUninitialized(string_type* s, size_t new_size) {
ResizeUninit(s, new_size, HasResizeUninitialized<string_type>());
}
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_
diff --git a/absl/strings/internal/stl_type_traits.h b/absl/strings/internal/stl_type_traits.h
index 7fc56a3c..fed7bf7c 100644
--- a/absl/strings/internal/stl_type_traits.h
+++ b/absl/strings/internal/stl_type_traits.h
@@ -40,7 +40,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
template <typename C, template <typename...> class T>
@@ -243,6 +243,6 @@ struct IsStrictlyBaseOfAndConvertibleToSTLContainer
IsConvertibleToSTLContainer<C>> {};
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
new file mode 100644
index 00000000..e5e1eee5
--- /dev/null
+++ b/absl/strings/internal/str_format/arg.cc
@@ -0,0 +1,379 @@
+//
+// 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 {
+inline namespace lts_2018_12_18 {
+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)};
+}
+
+ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_();
+
+
+} // namespace str_format_internal
+
+} // inline namespace lts_2018_12_18
+} // 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 00000000..0af4c839
--- /dev/null
+++ b/absl/strings/internal/str_format/arg.h
@@ -0,0 +1,422 @@
+#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 {
+inline namespace lts_2018_12_18 {
+
+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<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, ConversionSpec conv,
+ FormatSinkImpl* sink);
+
+// Strings.
+ConvertResult<Conv::s> FormatConvertImpl(const std::string& v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+ConvertResult<Conv::s> FormatConvertImpl(string_view v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char* v,
+ 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,
+ 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, ConversionSpec conv,
+ FormatSinkImpl* sink);
+FloatingConvertResult FormatConvertImpl(double v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+FloatingConvertResult FormatConvertImpl(long double v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+
+// Chars.
+IntegralConvertResult FormatConvertImpl(char v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(signed char v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned char v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+
+// Ints.
+IntegralConvertResult FormatConvertImpl(short v, // NOLINT
+ ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT
+ ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(int v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(long v, // NOLINT
+ ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT
+ ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(long long v, // NOLINT
+ ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT
+ ConversionSpec conv,
+ FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(uint128 v, ConversionSpec conv,
+ FormatSinkImpl* sink);
+template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0>
+IntegralConvertResult FormatConvertImpl(T v, 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, ConversionSpec conv, FormatSinkImpl* sink);
+
+template <typename T>
+ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<T>& v,
+ 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,
+ 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,
+ 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) {
+ // A value initialized ConversionSpec has a `none` conv, which tells the
+ // dispatcher to run the `int` conversion.
+ return arg.dispatcher_(arg.data_, {}, out);
+ }
+
+ template <typename Arg>
+ static bool Convert(Arg arg, str_format_internal::ConversionSpec conv,
+ FormatSinkImpl* out) {
+ return arg.dispatcher_(arg.data_, conv, out);
+ }
+
+ template <typename Arg>
+ static typename Arg::Dispatcher GetVTablePtrForTest(Arg arg) {
+ return arg.dispatcher_;
+ }
+};
+
+// 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];
+ };
+
+ using Dispatcher = bool (*)(Data, ConversionSpec, void* out);
+
+ 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))> {
+ };
+
+ // 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) {
+ data_ = Manager<T>::SetValue(value);
+ dispatcher_ = &Dispatch<T>;
+ }
+
+ 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);
+ }
+
+ template <typename T>
+ static bool ToInt(Data arg, int* out, std::true_type /* is_integral */,
+ std::false_type) {
+ *out = ToIntVal(Manager<T>::Value(arg));
+ return true;
+ }
+
+ template <typename T>
+ static bool ToInt(Data arg, int* out, std::false_type,
+ std::true_type /* is_enum */) {
+ *out = ToIntVal(static_cast<typename std::underlying_type<T>::type>(
+ Manager<T>::Value(arg)));
+ return true;
+ }
+
+ template <typename T>
+ static bool ToInt(Data, int*, std::false_type, std::false_type) {
+ return false;
+ }
+
+ template <typename T>
+ static bool Dispatch(Data arg, ConversionSpec spec, void* out) {
+ // A `none` conv indicates that we want the `int` conversion.
+ if (ABSL_PREDICT_FALSE(spec.conv().id() == ConversionChar::none)) {
+ return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(),
+ std::is_enum<T>());
+ }
+
+ return str_format_internal::FormatConvertImpl(
+ Manager<T>::Value(arg), spec, static_cast<FormatSinkImpl*>(out))
+ .value;
+ }
+
+ Data data_;
+ Dispatcher dispatcher_;
+};
+
+#define ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(T, E) \
+ E template bool FormatArgImpl::Dispatch<T>(Data, ConversionSpec, void*)
+
+#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(str_format_internal::VoidPtr, \
+ __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(bool, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(char, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(signed char, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned char, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(short, __VA_ARGS__); /* NOLINT */ \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned short, /* NOLINT */ \
+ __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(int, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned int, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long, __VA_ARGS__); /* NOLINT */ \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long, /* NOLINT */ \
+ __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long long, /* NOLINT */ \
+ __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long long, /* NOLINT */ \
+ __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(uint128, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(float, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(double, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long double, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const char*, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::string, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(string_view, __VA_ARGS__)
+
+ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern);
+
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // 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 00000000..9cb9559c
--- /dev/null
+++ b/absl/strings/internal/str_format/arg_test.cc
@@ -0,0 +1,113 @@
+// 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 {
+inline namespace lts_2018_12_18 {
+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
+} // inline namespace lts_2018_12_18
+} // 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 00000000..5cf026b6
--- /dev/null
+++ b/absl/strings/internal/str_format/bind.cc
@@ -0,0 +1,231 @@
+#include "absl/strings/internal/str_format/bind.h"
+
+#include <cerrno>
+#include <limits>
+#include <sstream>
+#include <string>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+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, Converter converter) {
+ if (format.has_parsed_conversion()) {
+ return format.parsed_conversion()->ProcessFormat(
+ ConverterConsumer<Converter>(converter, args));
+ } else {
+ 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))) {
+ return "";
+ }
+ }
+ return out;
+}
+
+bool FormatUntyped(FormatRawSinkImpl raw_sink,
+ const UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args) {
+ FormatSinkImpl sink(raw_sink);
+ using Converter = DefaultConverter;
+ return ConvertAll(format, args, Converter(&sink));
+}
+
+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 (ABSL_PREDICT_FALSE(!FormatUntyped(out, format, args))) {
+ out->erase(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
+} // inline namespace lts_2018_12_18
+} // 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 00000000..df5562f6
--- /dev/null
+++ b/absl/strings/internal/str_format/bind.h
@@ -0,0 +1,199 @@
+#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 {
+inline namespace lts_2018_12_18 {
+
+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)
+ : data_(s.data()), size_(s.size()) {}
+ explicit UntypedFormatSpecImpl(
+ const str_format_internal::ParsedFormatBase* pc)
+ : data_(pc), size_(~size_t{}) {}
+
+ bool has_parsed_conversion() const { return size_ == ~size_t{}; }
+
+ string_view str() const {
+ assert(!has_parsed_conversion());
+ return string_view(static_cast<const char*>(data_), size_);
+ }
+ const str_format_internal::ParsedFormatBase* parsed_conversion() const {
+ assert(has_parsed_conversion());
+ return static_cast<const str_format_internal::ParsedFormatBase*>(data_);
+ }
+
+ template <typename T>
+ static const UntypedFormatSpecImpl& Extract(const T& s) {
+ return s.spec_;
+ }
+
+ private:
+ const void* data_;
+ size_t size_;
+};
+
+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(UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args);
+bool BindWithPack(const UnboundConversion* props,
+ absl::Span<const FormatArgImpl> pack, BoundConversion* bound);
+
+bool FormatUntyped(FormatRawSinkImpl raw_sink,
+ UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args);
+
+std::string& AppendPack(std::string* out, 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, UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args);
+int SnprintF(char* output, size_t size, UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args);
+
+// Returned by Streamed(v). Converts via '%s' to the 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,
+ ConversionSpec conv,
+ FormatSinkImpl* out);
+ const T& v_;
+};
+
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // 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 00000000..58d9e072
--- /dev/null
+++ b/absl/strings/internal/str_format/bind_test.cc
@@ -0,0 +1,133 @@
+#include "absl/strings/internal/str_format/bind.h"
+
+#include <string.h>
+
+#include "gtest/gtest.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+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
+} // inline namespace lts_2018_12_18
+} // 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 00000000..d0191968
--- /dev/null
+++ b/absl/strings/internal/str_format/checker.h
@@ -0,0 +1,327 @@
+#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 {
+inline namespace lts_2018_12_18 {
+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
+} // inline namespace lts_2018_12_18
+} // 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 00000000..b4f38979
--- /dev/null
+++ b/absl/strings/internal/str_format/checker_test.cc
@@ -0,0 +1,152 @@
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/strings/str_format.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+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
+} // inline namespace lts_2018_12_18
+} // 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 00000000..95d57b67
--- /dev/null
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -0,0 +1,577 @@
+#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 {
+inline namespace lts_2018_12_18 {
+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
+} // inline namespace lts_2018_12_18
+} // 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 00000000..e3b41c82
--- /dev/null
+++ b/absl/strings/internal/str_format/extension.cc
@@ -0,0 +1,86 @@
+//
+// 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 {
+inline namespace lts_2018_12_18 {
+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
+} // inline namespace lts_2018_12_18
+} // 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 00000000..d401b4ed
--- /dev/null
+++ b/absl/strings/internal/str_format/extension.h
@@ -0,0 +1,414 @@
+//
+// 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 <cstddef>
+#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 {
+inline namespace lts_2018_12_18 {
+
+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 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 {
+ // Keep this field first in the struct . It generates better code when
+ // accessing it when ConversionSpec is passed by value in registers.
+ static_assert(offsetof(ConversionSpec, conv_) == 0, "");
+ 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:
+ ConversionChar conv_;
+ Flags flags_;
+ LengthMod length_mod_;
+ 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
+
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+#endif // ABSL_STRINGS_INTERNAL_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 00000000..224fc923
--- /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 00000000..7b617689
--- /dev/null
+++ b/absl/strings/internal/str_format/float_conversion.cc
@@ -0,0 +1,485 @@
+#include "absl/strings/internal/str_format/float_conversion.h"
+
+#include <string.h>
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <string>
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+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
+#if defined(__clang__) && !defined(__SSE3__)
+ // Workaround for clang bug: https://bugs.llvm.org/show_bug.cgi?id=38289
+ // Casting from long double to uint64_t is miscompiled and drops bits.
+ (!std::is_same<Float, long double>::value ||
+ !std::is_same<Int, uint64_t>::value) &&
+#endif
+ 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
+} // inline namespace lts_2018_12_18
+} // 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 00000000..280c471a
--- /dev/null
+++ b/absl/strings/internal/str_format/float_conversion.h
@@ -0,0 +1,23 @@
+#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 {
+inline namespace lts_2018_12_18 {
+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
+} // inline namespace lts_2018_12_18
+} // 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 00000000..010bf341
--- /dev/null
+++ b/absl/strings/internal/str_format/output.cc
@@ -0,0 +1,72 @@
+// 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 {
+inline namespace lts_2018_12_18 {
+namespace str_format_internal {
+
+namespace {
+struct ClearErrnoGuard {
+ ClearErrnoGuard() : old_value(errno) { errno = 0; }
+ ~ClearErrnoGuard() {
+ if (!errno) errno = old_value;
+ }
+ int old_value;
+};
+} // namespace
+
+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_) {
+ // Reset errno to zero in case the libc implementation doesn't set errno
+ // when a failure occurs.
+ ClearErrnoGuard guard;
+
+ if (size_t result = std::fwrite(v.data(), 1, v.size(), output_)) {
+ // Some progress was made.
+ count_ += result;
+ v.remove_prefix(result);
+ } else {
+ if (errno == EINTR) {
+ continue;
+ } else if (errno) {
+ error_ = errno;
+ } else if (std::ferror(output_)) {
+ // Non-POSIX compliant libc implementations may not set errno, so we
+ // have check the streams error indicator.
+ error_ = EBADF;
+ } else {
+ // We're likely on a non-POSIX system that encountered EINTR but had no
+ // way of reporting it.
+ continue;
+ }
+ }
+ }
+}
+
+} // namespace str_format_internal
+} // inline namespace lts_2018_12_18
+} // 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 00000000..0f3ab349
--- /dev/null
+++ b/absl/strings/internal/str_format/output.h
@@ -0,0 +1,103 @@
+// 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 {
+inline namespace lts_2018_12_18 {
+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.data(), s.size());
+}
+inline void AbslFormatFlush(std::ostream* out, string_view s) {
+ out->write(s.data(), 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
+} // inline namespace lts_2018_12_18
+} // 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 00000000..0a014cac
--- /dev/null
+++ b/absl/strings/internal/str_format/output_test.cc
@@ -0,0 +1,80 @@
+// 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 {
+inline namespace lts_2018_12_18 {
+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
+} // inline namespace lts_2018_12_18
+} // 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 00000000..119a711e
--- /dev/null
+++ b/absl/strings/internal/str_format/parser.cc
@@ -0,0 +1,311 @@
+#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 {
+inline namespace lts_2018_12_18 {
+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->data();
+ const char *const end = pos + src->size();
+ char c;
+ // Read the next char into `c` and update `pos`. Returns false if there are
+ // no more chars to read.
+#define ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR() \
+ do { \
+ if (ABSL_PREDICT_FALSE(pos == end)) return false; \
+ c = *pos++; \
+ } while (0)
+
+ const auto parse_digits = [&] {
+ int digits = c - '0';
+ // We do not want to overflow `digits` so we consume at most digits10
+ // digits. If there are more digits the parsing will fail later on when the
+ // digit doesn't match the expected characters.
+ int num_digits = std::numeric_limits<int>::digits10;
+ for (;;) {
+ if (ABSL_PREDICT_FALSE(pos == end || !num_digits)) break;
+ c = *pos++;
+ if (!std::isdigit(c)) break;
+ --num_digits;
+ digits = 10 * digits + c - '0';
+ }
+ return digits;
+ };
+
+ if (is_positional) {
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return false;
+ conv->arg_position = parse_digits();
+ assert(conv->arg_position > 0);
+ if (ABSL_PREDICT_FALSE(c != '$')) return false;
+ }
+
+ ABSL_FORMAT_PARSER_INTERNAL_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';) {
+ // FIXME: We might be able to speed this up reusing the kIds lookup table
+ // from above.
+ // It might require changing Flags to be a plain integer where we can |= a
+ // value.
+ switch (c) {
+ case '-':
+ conv->flags.left = true;
+ break;
+ case '+':
+ conv->flags.show_pos = true;
+ break;
+ case ' ':
+ conv->flags.sign_col = true;
+ break;
+ case '#':
+ conv->flags.alt = true;
+ break;
+ case '0':
+ conv->flags.zero = true;
+ break;
+ default:
+ goto flags_done;
+ }
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ }
+flags_done:
+
+ if (c <= '9') {
+ if (c >= '0') {
+ int maybe_width = parse_digits();
+ if (!is_positional && c == '$') {
+ if (ABSL_PREDICT_FALSE(*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 == '*') {
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ if (is_positional) {
+ if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return false;
+ conv->width.set_from_arg(parse_digits());
+ if (ABSL_PREDICT_FALSE(c != '$')) return false;
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ } else {
+ conv->width.set_from_arg(++*next_arg);
+ }
+ }
+ }
+
+ if (c == '.') {
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ if (std::isdigit(c)) {
+ conv->precision.set_value(parse_digits());
+ } else if (c == '*') {
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ if (is_positional) {
+ if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return false;
+ conv->precision.set_from_arg(parse_digits());
+ if (c != '$') return false;
+ ABSL_FORMAT_PARSER_INTERNAL_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 (ABSL_PREDICT_FALSE(id == none)) return false;
+
+ // It is a length modifier.
+ using str_format_internal::LengthMod;
+ LengthMod length_mod = LengthMod::FromId(static_cast<LM>(~id));
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ if (c == 'h' && length_mod.id() == LengthMod::h) {
+ conv->length_mod = LengthMod::FromId(LengthMod::hh);
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ } else if (c == 'l' && length_mod.id() == LengthMod::l) {
+ conv->length_mod = LengthMod::FromId(LengthMod::ll);
+ ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
+ } else {
+ conv->length_mod = length_mod;
+ }
+ id = kIds[static_cast<unsigned char>(c)];
+ if (ABSL_PREDICT_FALSE(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
+} // inline namespace lts_2018_12_18
+} // 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 00000000..9842c117
--- /dev/null
+++ b/absl/strings/internal/str_format/parser.h
@@ -0,0 +1,294 @@
+#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 {
+inline namespace lts_2018_12_18 {
+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 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 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.data(), '%', 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.data() + src.size()) 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_) {
+ const char* const end = text.data() + text.size();
+ text = string_view(end, (base + item.text_end) - 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
+// 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
+} // inline namespace lts_2018_12_18
+} // 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 00000000..14d90344
--- /dev/null
+++ b/absl/strings/internal/str_format/parser_test.cc
@@ -0,0 +1,391 @@
+#include "absl/strings/internal/str_format/parser.h"
+
+#include <string.h>
+#include "gtest/gtest.h"
+#include "absl/base/macros.h"
+
+namespace absl {
+inline namespace lts_2018_12_18 {
+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->data();
+ o = UnboundConversion(); // refresh
+ ConsumeUnboundConversion(src, &o, &next);
+ return {prev_begin, static_cast<size_t>(src->data() - 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;
+ string_view src;
+ string_view out;
+ string_view 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";
+
+ // Large values
+ EXPECT_TRUE(Run("999999999.999999999d"));
+ EXPECT_FALSE(o.width.is_from_arg());
+ EXPECT_EQ(999999999, o.width.value());
+ EXPECT_FALSE(o.precision.is_from_arg());
+ EXPECT_EQ(999999999, o.precision.value());
+
+ EXPECT_FALSE(Run("1000000000.999999999d"));
+ EXPECT_FALSE(Run("999999999.1000000000d"));
+}
+
+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
+} // inline namespace lts_2018_12_18
+} // namespace absl
diff --git a/absl/strings/internal/str_join_internal.h b/absl/strings/internal/str_join_internal.h
index 5834403e..90b06d6f 100644
--- a/absl/strings/internal/str_join_internal.h
+++ b/absl/strings/internal/str_join_internal.h
@@ -43,7 +43,7 @@
#include "absl/strings/str_cat.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
//
@@ -190,7 +190,7 @@ struct DefaultFormatter<std::unique_ptr<ValueType>>
//
// The main joining algorithm. This simply joins the elements in the given
-// iterator range, each separated by the given separator, into an output std::string,
+// iterator range, each separated by the given separator, into an output string,
// and formats each element using the provided Formatter object.
template <typename Iterator, typename Formatter>
std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s,
@@ -206,20 +206,20 @@ std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s,
}
// A joining algorithm that's optimized for a forward iterator range of
-// std::string-like objects that do not need any additional formatting. This is to
-// optimize the common case of joining, say, a std::vector<std::string> or a
+// string-like objects that do not need any additional formatting. This is to
+// optimize the common case of joining, say, a std::vector<string> or a
// std::vector<absl::string_view>.
//
// This is an overload of the previous JoinAlgorithm() function. Here the
// Formatter argument is of type NoFormatter. Since NoFormatter is an internal
// type, this overload is only invoked when strings::Join() is called with a
-// range of std::string-like objects (e.g., std::string, absl::string_view), and an
+// range of string-like objects (e.g., string, absl::string_view), and an
// explicit Formatter argument was NOT specified.
//
// The optimization is that the needed space will be reserved in the output
-// std::string to avoid the need to resize while appending. To do this, the iterator
+// string to avoid the need to resize while appending. To do this, the iterator
// range will be traversed twice: once to calculate the total needed size, and
-// then again to copy the elements and delimiters to the output std::string.
+// then again to copy the elements and delimiters to the output string.
template <typename Iterator,
typename = typename std::enable_if<std::is_convertible<
typename std::iterator_traits<Iterator>::iterator_category,
@@ -307,7 +307,7 @@ std::string JoinRange(const Range& range, absl::string_view separator) {
}
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_JOIN_INTERNAL_H_
diff --git a/absl/strings/internal/str_split_internal.h b/absl/strings/internal/str_split_internal.h
index e2d2c6b0..2300193a 100644
--- a/absl/strings/internal/str_split_internal.h
+++ b/absl/strings/internal/str_split_internal.h
@@ -47,12 +47,12 @@
#endif // _GLIBCXX_DEBUG
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
// This class is implicitly constructible from everything that absl::string_view
// is implicitly constructible from. If it's constructed from a temporary
-// std::string, the data is moved into a data member so its lifetime matches that of
+// string, the data is moved into a data member so its lifetime matches that of
// the ConvertibleToStringView instance.
class ConvertibleToStringView {
public:
@@ -103,7 +103,7 @@ ConvertibleToStringView(std::string&& s) // NOLINT(runtime/explicit)
absl::string_view value_;
};
-// An iterator that enumerates the parts of a std::string from a Splitter. The text
+// An iterator that enumerates the parts of a string from a Splitter. The text
// to be split, the Delimiter, and the Predicate are all taken from the given
// Splitter object. Iterators may only be compared if they refer to the same
// Splitter instance.
@@ -160,7 +160,7 @@ class SplitIterator {
}
const absl::string_view text = splitter_->text();
const absl::string_view d = delimiter_.Find(text, pos_);
- if (d.data() == text.end()) state_ = kLastState;
+ if (d.data() == text.data() + text.size()) state_ = kLastState;
curr_ = text.substr(pos_, d.data() - (text.data() + pos_));
pos_ += curr_.size() + d.size();
} while (!predicate_(curr_));
@@ -229,14 +229,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
@@ -282,7 +299,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);
@@ -431,7 +449,7 @@ class Splitter {
};
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_SPLIT_INTERNAL_H_
diff --git a/absl/strings/internal/utf8.cc b/absl/strings/internal/utf8.cc
index 81b6241b..c6ab0d52 100644
--- a/absl/strings/internal/utf8.cc
+++ b/absl/strings/internal/utf8.cc
@@ -17,7 +17,7 @@
#include "absl/strings/internal/utf8.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
size_t EncodeUTF8Char(char *buffer, char32_t utf8_char) {
@@ -49,5 +49,5 @@ size_t EncodeUTF8Char(char *buffer, char32_t utf8_char) {
}
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/internal/utf8.h b/absl/strings/internal/utf8.h
index 5a94ae6f..59c4f5b4 100644
--- a/absl/strings/internal/utf8.h
+++ b/absl/strings/internal/utf8.h
@@ -22,7 +22,7 @@
#include <cstdint>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
// For Unicode code points 0 through 0x10FFFF, EncodeUTF8Char writes
@@ -43,7 +43,7 @@ enum { kMaxEncodedUTF8Size = 4 };
size_t EncodeUTF8Char(char *buffer, char32_t utf8_char);
} // namespace strings_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_UTF8_H_
diff --git a/absl/strings/match.cc b/absl/strings/match.cc
index 8f33c72c..12ba8edf 100644
--- a/absl/strings/match.cc
+++ b/absl/strings/match.cc
@@ -17,7 +17,7 @@
#include "absl/strings/internal/memutil.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
bool CaseEqual(absl::string_view piece1, absl::string_view piece2) {
@@ -28,6 +28,13 @@ bool CaseEqual(absl::string_view piece1, absl::string_view piece2) {
}
} // namespace
+bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2) {
+ return (piece1.size() == piece2.size() &&
+ 0 == absl::strings_internal::memcasecmp(piece1.data(), piece2.data(),
+ piece1.size()));
+ // memcasecmp uses absl::ascii_tolower().
+}
+
bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix) {
return (text.size() >= prefix.size()) &&
CaseEqual(text.substr(0, prefix.size()), prefix);
@@ -38,5 +45,5 @@ bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix) {
CaseEqual(text.substr(text.size() - suffix.size()), suffix);
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/match.h b/absl/strings/match.h
index 908f051f..5805207c 100644
--- a/absl/strings/match.h
+++ b/absl/strings/match.h
@@ -17,7 +17,7 @@
// File: match.h
// -----------------------------------------------------------------------------
//
-// This file contains simple utilities for performing std::string matching checks.
+// This file contains simple utilities for performing string matching checks.
// All of these function parameters are specified as `absl::string_view`,
// meaning that these functions can accept `std::string`, `absl::string_view` or
// nul-terminated C-style strings.
@@ -38,18 +38,18 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// StrContains()
//
-// Returns whether a given std::string `haystack` contains the substring `needle`.
+// Returns whether a given string `haystack` contains the substring `needle`.
inline bool StrContains(absl::string_view haystack, absl::string_view needle) {
return haystack.find(needle, 0) != haystack.npos;
}
// StartsWith()
//
-// Returns whether a given std::string `text` begins with `prefix`.
+// Returns whether a given string `text` begins with `prefix`.
inline bool StartsWith(absl::string_view text, absl::string_view prefix) {
return prefix.empty() ||
(text.size() >= prefix.size() &&
@@ -58,7 +58,7 @@ inline bool StartsWith(absl::string_view text, absl::string_view prefix) {
// EndsWith()
//
-// Returns whether a given std::string `text` ends with `suffix`.
+// Returns whether a given string `text` ends with `suffix`.
inline bool EndsWith(absl::string_view text, absl::string_view suffix) {
return suffix.empty() ||
(text.size() >= suffix.size() &&
@@ -67,19 +67,25 @@ inline bool EndsWith(absl::string_view text, absl::string_view suffix) {
);
}
-// StartsWithIgnoreCase()
+// EqualsIgnoreCase()
//
-// Returns whether a given std::string `text` starts with `starts_with`, ignoring
+// Returns whether given ASCII strings `piece1` and `piece2` are equal, ignoring
// case in the comparison.
+bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2);
+
+// StartsWithIgnoreCase()
+//
+// Returns whether a given ASCII string `text` starts with `starts_with`,
+// ignoring case in the comparison.
bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix);
// EndsWithIgnoreCase()
//
-// Returns whether a given std::string `text` ends with `ends_with`, ignoring case
-// in the comparison.
+// Returns whether a given ASCII string `text` ends with `ends_with`, ignoring
+// case in the comparison.
bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix);
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_MATCH_H_
diff --git a/absl/strings/match_test.cc b/absl/strings/match_test.cc
index d194f0e6..c21e00bf 100644
--- a/absl/strings/match_test.cc
+++ b/absl/strings/match_test.cc
@@ -80,6 +80,17 @@ TEST(MatchTest, ContainsNull) {
EXPECT_FALSE(absl::StrContains(cs, sv2));
}
+TEST(MatchTest, EqualsIgnoreCase) {
+ std::string text = "the";
+ absl::string_view data(text);
+
+ EXPECT_TRUE(absl::EqualsIgnoreCase(data, "The"));
+ EXPECT_TRUE(absl::EqualsIgnoreCase(data, "THE"));
+ EXPECT_TRUE(absl::EqualsIgnoreCase(data, "the"));
+ EXPECT_FALSE(absl::EqualsIgnoreCase(data, "Quick"));
+ EXPECT_FALSE(absl::EqualsIgnoreCase(data, "then"));
+}
+
TEST(MatchTest, StartsWithIgnoreCase) {
EXPECT_TRUE(absl::StartsWithIgnoreCase("foo", "foo"));
EXPECT_TRUE(absl::StartsWithIgnoreCase("foo", "Fo"));
diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc
index 48dca919..4c3ddb34 100644
--- a/absl/strings/numbers.cc
+++ b/absl/strings/numbers.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// This file contains std::string processing functions related to
+// This file contains string processing functions related to
// numeric values.
#include "absl/strings/numbers.h"
@@ -30,15 +30,15 @@
#include <memory>
#include <utility>
+#include "absl/base/internal/bits.h"
#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"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
bool SimpleAtof(absl::string_view str, float* value) {
*value = 0.0;
@@ -162,8 +162,8 @@ bool SimpleAtob(absl::string_view str, bool* value) {
// their output to the beginning of the buffer. The caller is responsible
// for ensuring that the buffer has enough space to hold the output.
//
-// Returns a pointer to the end of the std::string (i.e. the null character
-// terminating the std::string).
+// Returns a pointer to the end of the string (i.e. the null character
+// terminating the string).
// ----------------------------------------------------------------------
namespace {
@@ -345,7 +345,7 @@ static std::pair<uint64_t, uint64_t> Mul32(std::pair<uint64_t, uint64_t> num,
uint64_t bits128_up = (bits96_127 >> 32) + (bits64_127 < bits64_95);
if (bits128_up == 0) return {bits64_127, bits0_63};
- int shift = 64 - strings_internal::CountLeadingZeros64(bits128_up);
+ int shift = 64 - base_internal::CountLeadingZeros64(bits128_up);
uint64_t lo = (bits0_63 >> shift) + (bits64_127 << (64 - shift));
uint64_t hi = (bits64_127 >> shift) + (bits128_up << (64 - shift));
return {hi, lo};
@@ -376,7 +376,7 @@ static std::pair<uint64_t, uint64_t> PowFive(uint64_t num, int expfive) {
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5};
result = Mul32(result, powers_of_five[expfive & 15]);
- int shift = strings_internal::CountLeadingZeros64(result.first);
+ int shift = base_internal::CountLeadingZeros64(result.first);
if (shift != 0) {
result.first = (result.first << shift) + (result.second >> (64 - shift));
result.second = (result.second << shift);
@@ -910,5 +910,5 @@ bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base) {
}
} // namespace numbers_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h
index 8efdb200..250d2603 100644
--- a/absl/strings/numbers.h
+++ b/absl/strings/numbers.h
@@ -38,48 +38,48 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// SimpleAtoi()
//
-// Converts the given std::string into an integer value, returning `true` if
-// successful. The std::string must reflect a base-10 integer (optionally followed or
+// Converts the given string into an integer value, returning `true` if
+// successful. The string must reflect a base-10 integer (optionally followed or
// preceded by ASCII whitespace) whose value falls within the range of the
-// integer type,
+// integer type.
template <typename int_type>
ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view s, int_type* out);
// SimpleAtof()
//
-// Converts the given std::string (optionally followed or preceded by ASCII
+// Converts the given string (optionally followed or preceded by ASCII
// whitespace) into a float, which may be rounded on overflow or underflow.
-// See http://en.cppreference.com/w/c/std::string/byte/strtof for details about the
+// See http://en.cppreference.com/w/c/string/byte/strtof for details about the
// allowed formats for `str`.
ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str, float* value);
// SimpleAtod()
//
-// Converts the given std::string (optionally followed or preceded by ASCII
+// Converts the given string (optionally followed or preceded by ASCII
// whitespace) into a double, which may be rounded on overflow or underflow.
-// See http://en.cppreference.com/w/c/std::string/byte/strtof for details about the
+// See http://en.cppreference.com/w/c/string/byte/strtof for details about the
// allowed formats for `str`.
ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* value);
// SimpleAtob()
//
-// Converts the given std::string into a boolean, returning `true` if successful.
+// Converts the given string into a boolean, returning `true` if successful.
// The following case-insensitive strings are interpreted as boolean `true`:
// "true", "t", "yes", "y", "1". The following case-insensitive strings
// are interpreted as boolean `false`: "false", "f", "no", "n", "0".
ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* value);
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
// End of public API. Implementation details follow.
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace numbers_internal {
// safe_strto?() functions for implementing SimpleAtoi()
@@ -172,9 +172,9 @@ ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out,
// SimpleAtoi()
//
-// Converts a std::string to an integer, using `safe_strto?()` functions for actual
+// Converts a string to an integer, using `safe_strto?()` functions for actual
// parsing, returning `true` if successful. The `safe_strto?()` functions apply
-// strict checking; the std::string must be a base-10 integer, optionally followed or
+// strict checking; the string must be a base-10 integer, optionally followed or
// preceded by ASCII whitespace, with a value in the range of the corresponding
// integer type.
template <typename int_type>
@@ -182,7 +182,7 @@ ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view s, int_type* out) {
return numbers_internal::safe_strtoi_base(s, out, 10);
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_NUMBERS_H_
diff --git a/absl/strings/numbers_benchmark.cc b/absl/strings/numbers_benchmark.cc
new file mode 100644
index 00000000..0570b758
--- /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 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/numbers_test.cc b/absl/strings/numbers_test.cc
index 24e7138c..099326c2 100644
--- a/absl/strings/numbers_test.cc
+++ b/absl/strings/numbers_test.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// This file tests std::string processing functions related to numeric values.
+// This file tests string processing functions related to numeric values.
#include "absl/strings/numbers.h"
@@ -39,6 +39,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/internal/numbers_test_common.h"
+#include "absl/strings/internal/pow10_helper.h"
namespace {
@@ -56,16 +57,11 @@ using testing::Eq;
using testing::MatchesRegex;
// Number of floats to test with.
-// 10,000,000 is a reasonable default for a test that only takes a few seconds.
+// 5,000,000 is a reasonable default for a test that only takes a few seconds.
// 1,000,000,000+ triggers checking for all possible mantissa values for
// double-precision tests. 2,000,000,000+ triggers checking for every possible
// single-precision float.
-#ifdef _MSC_VER
-// Use a smaller number on MSVC to avoid test time out (1 min)
const int kFloatNumCases = 5000000;
-#else
-const int kFloatNumCases = 10000000;
-#endif
// This is a slow, brute-force routine to compute the exact base-10
// representation of a double-precision floating-point number. It
@@ -716,8 +712,9 @@ TEST(stringtest, safe_strtou64_base_length_delimited) {
}
}
-// feenableexcept() and fedisableexcept() are missing on Mac OS X, MSVC.
-#if defined(_MSC_VER) || defined(__APPLE__)
+// feenableexcept() and fedisableexcept() are missing on Mac OS X, MSVC,
+// and WebAssembly.
+#if defined(_MSC_VER) || defined(__APPLE__) || defined(__EMSCRIPTEN__)
#define ABSL_MISSING_FEENABLEEXCEPT 1
#define ABSL_MISSING_FEDISABLEEXCEPT 1
#endif
@@ -875,7 +872,7 @@ TEST_F(SimpleDtoaTest, ExhaustiveDoubleToSixDigits) {
}
for (int exponent = -324; exponent <= 308; ++exponent) {
- double powten = pow(10.0, exponent);
+ double powten = absl::strings_internal::Pow10(exponent);
if (powten == 0) powten = 5e-324;
if (kFloatNumCases >= 1e9) {
// The exhaustive test takes a very long time, so log progress.
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc
index 499f3f41..2f2e5315 100644
--- a/absl/strings/str_cat.cc
+++ b/absl/strings/str_cat.cc
@@ -23,7 +23,7 @@
#include "absl/strings/internal/resize_uninitialized.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
AlphaNum::AlphaNum(Hex hex) {
char* const end = &digits_[numbers_internal::kFastToBufferSize];
@@ -80,8 +80,8 @@ AlphaNum::AlphaNum(Dec dec) {
// ----------------------------------------------------------------------
// StrCat()
// This merges the given strings or integers, with no delimiter. This
-// is designed to be the fastest possible way to construct a std::string out
-// of a mix of raw C strings, StringPieces, strings, and integer values.
+// is designed to be the fastest possible way to construct a string out
+// of a mix of raw C strings, string_views, strings, and integer values.
// ----------------------------------------------------------------------
// Append is merely a version of memcpy that returns the address of the byte
@@ -155,10 +155,10 @@ std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
}
// It's possible to call StrAppend with an absl::string_view that is itself a
-// fragment of the std::string we're appending to. However the results of this are
+// fragment of the string we're appending to. However the results of this are
// random. Therefore, check for this in debug mode. Use unsigned math so we
// only have to do one comparison. Note, there's an exception case: appending an
-// empty std::string is always allowed.
+// empty string is always allowed.
#define ASSERT_NO_OVERLAP(dest, src) \
assert(((src).size() == 0) || \
(uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size())))
@@ -237,5 +237,5 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
assert(out == begin + dest->size());
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h
index c694cbc1..edda40ad 100644
--- a/absl/strings/str_cat.h
+++ b/absl/strings/str_cat.h
@@ -23,7 +23,7 @@
// designed to be used as a parameter type that efficiently manages conversion
// to strings and avoids copies in the above operations.
//
-// Any routine accepting either a std::string or a number may accept `AlphaNum`.
+// Any routine accepting either a string or a number may accept `AlphaNum`.
// The basic idea is that by accepting a `const AlphaNum &` as an argument
// to your function, your callers will automagically convert bools, integers,
// and floating point values to strings for you.
@@ -63,10 +63,10 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
-// AlphaNumBuffer allows a way to pass a std::string to StrCat without having to do
+// AlphaNumBuffer allows a way to pass a string to StrCat without having to do
// memory allocation. It is simply a pair of a fixed-size character array, and
// a size. Please don't use outside of absl, yet.
template <size_t max_size>
@@ -98,6 +98,10 @@ enum PadSpec : uint8_t {
kZeroPad14,
kZeroPad15,
kZeroPad16,
+ kZeroPad17,
+ kZeroPad18,
+ kZeroPad19,
+ kZeroPad20,
kSpacePad2 = kZeroPad2 + 64,
kSpacePad3,
@@ -114,14 +118,18 @@ enum PadSpec : uint8_t {
kSpacePad14,
kSpacePad15,
kSpacePad16,
+ kSpacePad17,
+ kSpacePad18,
+ kSpacePad19,
+ kSpacePad20,
};
// -----------------------------------------------------------------------------
// Hex
// -----------------------------------------------------------------------------
//
-// `Hex` stores a set of hexadecimal std::string conversion parameters for use
-// within `AlphaNum` std::string conversions.
+// `Hex` stores a set of hexadecimal string conversion parameters for use
+// within `AlphaNum` string conversions.
struct Hex {
uint64_t value;
uint8_t width;
@@ -169,8 +177,8 @@ struct Hex {
// Dec
// -----------------------------------------------------------------------------
//
-// `Dec` stores a set of decimal std::string conversion parameters for use
-// within `AlphaNum` std::string conversions. Dec is slower than the default
+// `Dec` stores a set of decimal string conversion parameters for use
+// within `AlphaNum` string conversions. Dec is slower than the default
// integer conversion, so use it only if you need padding.
struct Dec {
uint64_t value;
@@ -272,7 +280,7 @@ class AlphaNum {
//
// Merges given strings or numbers, using no delimiter(s).
//
-// `StrCat()` is designed to be the fastest possible way to construct a std::string
+// `StrCat()` is designed to be the fastest possible way to construct a string
// out of a mix of raw C strings, string_views, strings, bool values,
// and numeric values.
//
@@ -280,7 +288,7 @@ class AlphaNum {
// works poorly on strings built up out of fragments.
//
// For clarity and performance, don't use `StrCat()` when appending to a
-// std::string. Use `StrAppend()` instead. In particular, avoid using any of these
+// string. Use `StrAppend()` instead. In particular, avoid using any of these
// (anti-)patterns:
//
// str.append(StrCat(...))
@@ -329,26 +337,26 @@ ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a, const AlphaNum
// StrAppend()
// -----------------------------------------------------------------------------
//
-// Appends a std::string or set of strings to an existing std::string, in a similar
+// Appends a string or set of strings to an existing string, in a similar
// fashion to `StrCat()`.
//
// WARNING: `StrAppend(&str, a, b, c, ...)` requires that none of the
// a, b, c, parameters be a reference into str. For speed, `StrAppend()` does
// not try to check each of its input arguments to be sure that they are not
-// a subset of the std::string being appended to. That is, while this will work:
+// a subset of the string being appended to. That is, while this will work:
//
-// std::string s = "foo";
+// string s = "foo";
// s += s;
//
// This output is undefined:
//
-// std::string s = "foo";
+// string s = "foo";
// StrAppend(&s, s);
//
// This output is undefined as well, since `absl::string_view` does not own its
// data:
//
-// std::string s = "foobar";
+// string s = "foobar";
// absl::string_view p = s;
// StrAppend(&s, p);
@@ -381,7 +389,7 @@ SixDigits(double d) {
return result;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_STR_CAT_H_
diff --git a/absl/strings/str_cat_test.cc b/absl/strings/str_cat_test.cc
index e9d67690..07141072 100644
--- a/absl/strings/str_cat_test.cc
+++ b/absl/strings/str_cat_test.cc
@@ -28,7 +28,8 @@
#define ABSL_EXPECT_DEBUG_DEATH(statement, regex) \
EXPECT_DEBUG_DEATH(statement, ".*")
#else
-#define ABSL_EXPECT_DEBUG_DEATH EXPECT_DEBUG_DEATH
+#define ABSL_EXPECT_DEBUG_DEATH(statement, regex) \
+ EXPECT_DEBUG_DEATH(statement, regex)
#endif
namespace {
@@ -426,7 +427,7 @@ void CheckHex(IntType v, const char* nopad_format, const char* zeropad_format,
snprintf(expected, sizeof(expected), nopad_format, v);
EXPECT_EQ(expected, actual) << " decimal value " << v;
- for (int spec = absl::kZeroPad2; spec <= absl::kZeroPad16; ++spec) {
+ for (int spec = absl::kZeroPad2; spec <= absl::kZeroPad20; ++spec) {
std::string actual =
absl::StrCat(absl::Hex(v, static_cast<absl::PadSpec>(spec)));
snprintf(expected, sizeof(expected), zeropad_format,
@@ -434,7 +435,7 @@ void CheckHex(IntType v, const char* nopad_format, const char* zeropad_format,
EXPECT_EQ(expected, actual) << " decimal value " << v;
}
- for (int spec = absl::kSpacePad2; spec <= absl::kSpacePad16; ++spec) {
+ for (int spec = absl::kSpacePad2; spec <= absl::kSpacePad20; ++spec) {
std::string actual =
absl::StrCat(absl::Hex(v, static_cast<absl::PadSpec>(spec)));
snprintf(expected, sizeof(expected), spacepad_format,
@@ -452,7 +453,7 @@ void CheckDec(IntType v, const char* nopad_format, const char* zeropad_format,
snprintf(expected, sizeof(expected), nopad_format, v);
EXPECT_EQ(expected, actual) << " decimal value " << v;
- for (int spec = absl::kZeroPad2; spec <= absl::kZeroPad16; ++spec) {
+ for (int spec = absl::kZeroPad2; spec <= absl::kZeroPad20; ++spec) {
std::string actual =
absl::StrCat(absl::Dec(v, static_cast<absl::PadSpec>(spec)));
snprintf(expected, sizeof(expected), zeropad_format,
@@ -462,7 +463,7 @@ void CheckDec(IntType v, const char* nopad_format, const char* zeropad_format,
<< "' digits " << (spec - absl::kZeroPad2 + 2);
}
- for (int spec = absl::kSpacePad2; spec <= absl::kSpacePad16; ++spec) {
+ for (int spec = absl::kSpacePad2; spec <= absl::kSpacePad20; ++spec) {
std::string actual =
absl::StrCat(absl::Dec(v, static_cast<absl::PadSpec>(spec)));
snprintf(expected, sizeof(expected), spacepad_format,
diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h
new file mode 100644
index 00000000..7b19d411
--- /dev/null
+++ b/absl/strings/str_format.h
@@ -0,0 +1,514 @@
+//
+// 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()` 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:
+//
+// 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 string to a `string` value.
+// * `absl::StrAppendFormat()` to append a format string to a `string`
+// * `absl::StreamFormat()` to more efficiently write a format string to a
+// stream, such as`std::cout`.
+// * `absl::PrintF()`, `absl::FPrintF()` and `absl::SNPrintF()` as
+// replacements for `std::printf()`, `std::fprintf()` and `std::snprintf()`.
+//
+// Note: a version of `std::sprintf()` is not supported as it is
+// generally unsafe due to buffer overflows.
+//
+// Additionally, you can provide a format string (and its associated arguments)
+// using one of the following abstractions:
+//
+// * A `FormatSpec` class template fully encapsulates a format 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 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 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 {
+inline namespace lts_2018_12_18 {
+
+// 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");
+// 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;
+// 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 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 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 string is
+// valid before use. (See below.)
+//
+// Example:
+//
+// // Provided as a 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 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;
+// 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 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 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 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 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 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 string.
+//
+// Example:
+//
+// string s = absl::StrFormat(
+// "Welcome to %s, Number %d!", "The Village", 6);
+// EXPECT_EQ("Welcome to The Village, Number 6!", s);
+//
+// Returns an empty 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` string given a format 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:
+//
+// 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 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 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 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(stdout, "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 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 string to an arbitrary sink object (implementing the
+// `absl::FormatRawSink` interface), using a format 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 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 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
+// 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<string> FormatDynamic(const string& in_format,
+// const vector<string>& in_args) {
+// 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);
+}
+
+} // inline namespace lts_2018_12_18
+} // 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 00000000..77b8647f
--- /dev/null
+++ b/absl/strings/str_format_test.cc
@@ -0,0 +1,628 @@
+
+#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 {
+inline namespace lts_2018_12_18 {
+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_);
+ EXPECT_GT(size, 0);
+ 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 __GLIBC__
+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 // __GLIBC__
+
+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 = 0x7ffdeb4;
+ 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
+} // inline namespace lts_2018_12_18
+} // namespace absl
+
+// Some codegen thunks that we can use to easily dump the generated assembly for
+// different StrFormat calls.
+
+std::string CodegenAbslStrFormatInt(int i) { // NOLINT
+ return absl::StrFormat("%d", i);
+}
+
+std::string CodegenAbslStrFormatIntStringInt64(int i, const std::string& s,
+ int64_t i64) { // NOLINT
+ return absl::StrFormat("%d %s %d", i, s, i64);
+}
+
+void CodegenAbslStrAppendFormatInt(std::string* out, int i) { // NOLINT
+ absl::StrAppendFormat(out, "%d", i);
+}
+
+void CodegenAbslStrAppendFormatIntStringInt64(std::string* out, int i,
+ const std::string& s,
+ int64_t i64) { // NOLINT
+ absl::StrAppendFormat(out, "%d %s %d", i, s, i64);
+}
diff --git a/absl/strings/str_join.h b/absl/strings/str_join.h
index bce2cd66..dc476a22 100644
--- a/absl/strings/str_join.h
+++ b/absl/strings/str_join.h
@@ -18,10 +18,10 @@
// -----------------------------------------------------------------------------
//
// This header file contains functions for joining a range of elements and
-// returning the result as a std::string. StrJoin operations are specified by passing
-// a range, a separator std::string to use between the elements joined, and an
+// returning the result as a string. StrJoin operations are specified by passing
+// a range, a separator string to use between the elements joined, and an
// optional Formatter responsible for converting each argument in the range to a
-// std::string. If omitted, a default `AlphaNumFormatter()` is called on the elements
+// string. If omitted, a default `AlphaNumFormatter()` is called on the elements
// to be joined, using the same formatting that `absl::StrCat()` uses. This
// package defines a number of default formatters, and you can define your own
// implementations.
@@ -29,7 +29,7 @@
// Ranges are specified by passing a container with `std::begin()` and
// `std::end()` iterators, container-specific `begin()` and `end()` iterators, a
// brace-initialized `std::initializer_list`, or a `std::tuple` of heterogeneous
-// objects. The separator std::string is specified as an `absl::string_view`.
+// objects. The separator string is specified as an `absl::string_view`.
//
// Because the default formatter uses the `absl::AlphaNum` class,
// `absl::StrJoin()`, like `absl::StrCat()`, will work out-of-the-box on
@@ -37,8 +37,8 @@
//
// Example:
//
-// std::vector<std::string> v = {"foo", "bar", "baz"};
-// std::string s = absl::StrJoin(v, "-");
+// std::vector<string> v = {"foo", "bar", "baz"};
+// string s = absl::StrJoin(v, "-");
// EXPECT_EQ("foo-bar-baz", s);
//
// See comments on the `absl::StrJoin()` function for more examples.
@@ -52,6 +52,7 @@
#include <iterator>
#include <string>
#include <tuple>
+#include <type_traits>
#include <utility>
#include "absl/base/macros.h"
@@ -59,23 +60,23 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// -----------------------------------------------------------------------------
// Concept: Formatter
// -----------------------------------------------------------------------------
//
// A Formatter is a function object that is responsible for formatting its
-// argument as a std::string and appending it to a given output std::string. Formatters
+// argument as a string and appending it to a given output string. Formatters
// may be implemented as function objects, lambdas, or normal functions. You may
// provide your own Formatter to enable `absl::StrJoin()` to work with arbitrary
// types.
//
// The following is an example of a custom Formatter that simply uses
-// `std::to_string()` to format an integer as a std::string.
+// `std::to_string()` to format an integer as a string.
//
// struct MyFormatter {
-// void operator()(std::string* out, int i) const {
+// void operator()(string* out, int i) const {
// out->append(std::to_string(i));
// }
// };
@@ -84,7 +85,7 @@ inline namespace lts_2018_06_20 {
// argument to `absl::StrJoin()`:
//
// std::vector<int> v = {1, 2, 3, 4};
-// std::string s = absl::StrJoin(v, "-", MyFormatter());
+// string s = absl::StrJoin(v, "-", MyFormatter());
// EXPECT_EQ("1-2-3-4", s);
//
// The following standard formatters are provided within this file:
@@ -156,10 +157,10 @@ DereferenceFormatter() {
// StrJoin()
// -----------------------------------------------------------------------------
//
-// Joins a range of elements and returns the result as a std::string.
-// `absl::StrJoin()` takes a range, a separator std::string to use between the
+// Joins a range of elements and returns the result as a string.
+// `absl::StrJoin()` takes a range, a separator string to use between the
// elements joined, and an optional Formatter responsible for converting each
-// argument in the range to a std::string.
+// argument in the range to a string.
//
// If omitted, the default `AlphaNumFormatter()` is called on the elements to be
// joined.
@@ -167,22 +168,22 @@ DereferenceFormatter() {
// Example 1:
// // Joins a collection of strings. This pattern also works with a collection
// // of `absl::string_view` or even `const char*`.
-// std::vector<std::string> v = {"foo", "bar", "baz"};
-// std::string s = absl::StrJoin(v, "-");
+// std::vector<string> v = {"foo", "bar", "baz"};
+// string s = absl::StrJoin(v, "-");
// EXPECT_EQ("foo-bar-baz", s);
//
// Example 2:
// // Joins the values in the given `std::initializer_list<>` specified using
// // brace initialization. This pattern also works with an initializer_list
// // of ints or `absl::string_view` -- any `AlphaNum`-compatible type.
-// std::string s = absl::StrJoin({"foo", "bar", "baz"}, "-");
+// string s = absl::StrJoin({"foo", "bar", "baz"}, "-");
// EXPECT_EQ("foo-bar-baz", s);
//
// Example 3:
// // Joins a collection of ints. This pattern also works with floats,
// // doubles, int64s -- any `StrCat()`-compatible type.
// std::vector<int> v = {1, 2, 3, -4};
-// std::string s = absl::StrJoin(v, "-");
+// string s = absl::StrJoin(v, "-");
// EXPECT_EQ("1-2-3--4", s);
//
// Example 4:
@@ -193,7 +194,7 @@ DereferenceFormatter() {
// // `std::vector<int*>`.
// int x = 1, y = 2, z = 3;
// std::vector<int*> v = {&x, &y, &z};
-// std::string s = absl::StrJoin(v, "-");
+// string s = absl::StrJoin(v, "-");
// EXPECT_EQ("1-2-3", s);
//
// Example 5:
@@ -202,42 +203,42 @@ DereferenceFormatter() {
// v.emplace_back(new int(1));
// v.emplace_back(new int(2));
// v.emplace_back(new int(3));
-// std::string s = absl::StrJoin(v, "-");
+// string s = absl::StrJoin(v, "-");
// EXPECT_EQ("1-2-3", s);
//
// Example 6:
// // Joins a `std::map`, with each key-value pair separated by an equals
// // sign. This pattern would also work with, say, a
// // `std::vector<std::pair<>>`.
-// std::map<std::string, int> m = {
+// std::map<string, int> m = {
// std::make_pair("a", 1),
// std::make_pair("b", 2),
// std::make_pair("c", 3)};
-// std::string s = absl::StrJoin(m, ",", absl::PairFormatter("="));
+// string s = absl::StrJoin(m, ",", absl::PairFormatter("="));
// EXPECT_EQ("a=1,b=2,c=3", s);
//
// Example 7:
// // These examples show how `absl::StrJoin()` handles a few common edge
// // cases:
-// std::vector<std::string> v_empty;
+// std::vector<string> v_empty;
// EXPECT_EQ("", absl::StrJoin(v_empty, "-"));
//
-// std::vector<std::string> v_one_item = {"foo"};
+// std::vector<string> v_one_item = {"foo"};
// EXPECT_EQ("foo", absl::StrJoin(v_one_item, "-"));
//
-// std::vector<std::string> v_empty_string = {""};
+// std::vector<string> v_empty_string = {""};
// EXPECT_EQ("", absl::StrJoin(v_empty_string, "-"));
//
-// std::vector<std::string> v_one_item_empty_string = {"a", ""};
+// std::vector<string> v_one_item_empty_string = {"a", ""};
// EXPECT_EQ("a-", absl::StrJoin(v_one_item_empty_string, "-"));
//
-// std::vector<std::string> v_two_empty_string = {"", ""};
+// std::vector<string> v_two_empty_string = {"", ""};
// EXPECT_EQ("-", absl::StrJoin(v_two_empty_string, "-"));
//
// Example 8:
// // Joins a `std::tuple<T...>` of heterogeneous types, converting each to
-// // a std::string using the `absl::AlphaNum` class.
-// std::string s = absl::StrJoin(std::make_tuple(123, "abc", 0.456), "-");
+// // a string using the `absl::AlphaNum` class.
+// string s = absl::StrJoin(std::make_tuple(123, "abc", 0.456), "-");
// EXPECT_EQ("123-abc-0.456", s);
template <typename Iterator, typename Formatter>
@@ -284,7 +285,7 @@ std::string StrJoin(const std::tuple<T...>& value, absl::string_view separator)
return strings_internal::JoinAlgorithm(value, separator, AlphaNumFormatter());
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_STR_JOIN_H_
diff --git a/absl/strings/str_replace.cc b/absl/strings/str_replace.cc
index c50d314a..72a0b584 100644
--- a/absl/strings/str_replace.cc
+++ b/absl/strings/str_replace.cc
@@ -17,7 +17,7 @@
#include "absl/strings/str_cat.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace strings_internal {
using FixedMapping =
@@ -77,5 +77,5 @@ int StrReplaceAll(strings_internal::FixedMapping replacements, std::string* targ
return StrReplaceAll<strings_internal::FixedMapping>(replacements, target);
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/str_replace.h b/absl/strings/str_replace.h
index 7ba43946..a963f91e 100644
--- a/absl/strings/str_replace.h
+++ b/absl/strings/str_replace.h
@@ -17,19 +17,19 @@
// File: str_replace.h
// -----------------------------------------------------------------------------
//
-// This file defines `absl::StrReplaceAll()`, a general-purpose std::string
+// This file defines `absl::StrReplaceAll()`, a general-purpose string
// replacement function designed for large, arbitrary text substitutions,
// especially on strings which you are receiving from some other system for
// further processing (e.g. processing regular expressions, escaping HTML
-// entities, etc. `StrReplaceAll` is designed to be efficient even when only
+// entities, etc.). `StrReplaceAll` is designed to be efficient even when only
// one substitution is being performed, or when substitution is rare.
//
-// If the std::string being modified is known at compile-time, and the substitutions
+// If the string being modified is known at compile-time, and the substitutions
// vary, `absl::Substitute()` may be a better choice.
//
// Example:
//
-// std::string html_escaped = absl::StrReplaceAll(user_input, {
+// string html_escaped = absl::StrReplaceAll(user_input, {
// {"&", "&amp;"},
// {"<", "&lt;"},
// {">", "&gt;"},
@@ -46,20 +46,20 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// StrReplaceAll()
//
-// Replaces character sequences within a given std::string with replacements provided
+// Replaces character sequences within a given string with replacements provided
// within an initializer list of key/value pairs. Candidate replacements are
-// considered in order as they occur within the std::string, with earlier matches
+// considered in order as they occur within the string, with earlier matches
// taking precedence, and longer matches taking precedence for candidates
-// starting at the same position in the std::string. Once a substitution is made, the
+// starting at the same position in the string. Once a substitution is made, the
// replaced text is not considered for any further substitutions.
//
// Example:
//
-// std::string s = absl::StrReplaceAll("$who bought $count #Noun. Thanks $who!",
+// string s = absl::StrReplaceAll("$who bought $count #Noun. Thanks $who!",
// {{"$count", absl::StrCat(5)},
// {"$who", "Bob"},
// {"#Noun", "Apples"}});
@@ -79,28 +79,28 @@ ABSL_MUST_USE_RESULT std::string StrReplaceAll(
// replacements["$who"] = "Bob";
// replacements["$count"] = "5";
// replacements["#Noun"] = "Apples";
-// std::string s = absl::StrReplaceAll("$who bought $count #Noun. Thanks $who!",
+// string s = absl::StrReplaceAll("$who bought $count #Noun. Thanks $who!",
// replacements);
// EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
//
// // A std::vector of std::pair elements can be more efficient.
-// std::vector<std::pair<const absl::string_view, std::string>> replacements;
+// std::vector<std::pair<const absl::string_view, string>> replacements;
// replacements.push_back({"&", "&amp;"});
// replacements.push_back({"<", "&lt;"});
// replacements.push_back({">", "&gt;"});
-// std::string s = absl::StrReplaceAll("if (ptr < &foo)",
+// string s = absl::StrReplaceAll("if (ptr < &foo)",
// replacements);
// EXPECT_EQ("if (ptr &lt; &amp;foo)", s);
template <typename StrToStrMapping>
std::string StrReplaceAll(absl::string_view s, const StrToStrMapping& replacements);
// Overload of `StrReplaceAll()` to replace character sequences within a given
-// output std::string *in place* with replacements provided within an initializer
+// output string *in place* with replacements provided within an initializer
// list of key/value pairs, returning the number of substitutions that occurred.
//
// Example:
//
-// std::string s = std::string("$who bought $count #Noun. Thanks $who!");
+// string s = std::string("$who bought $count #Noun. Thanks $who!");
// int count;
// count = absl::StrReplaceAll({{"$count", absl::StrCat(5)},
// {"$who", "Bob"},
@@ -113,12 +113,12 @@ int StrReplaceAll(
std::string* target);
// Overload of `StrReplaceAll()` to replace patterns within a given output
-// std::string *in place* with replacements provided within a container of key/value
+// string *in place* with replacements provided within a container of key/value
// pairs.
//
// Example:
//
-// std::string s = std::string("if (ptr < &foo)");
+// string s = std::string("if (ptr < &foo)");
// int count = absl::StrReplaceAll({{"&", "&amp;"},
// {"<", "&lt;"},
// {">", "&gt;"}}, &s);
@@ -209,7 +209,7 @@ int StrReplaceAll(const StrToStrMapping& replacements, std::string* target) {
return substitutions;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_STR_REPLACE_H_
diff --git a/absl/strings/str_replace_benchmark.cc b/absl/strings/str_replace_benchmark.cc
index e608de8d..8386f2e6 100644
--- a/absl/strings/str_replace_benchmark.cc
+++ b/absl/strings/str_replace_benchmark.cc
@@ -38,16 +38,16 @@ struct Replacement {
{"liquor", "shakes"}, //
};
-// Here, we set up a std::string for use in global-replace benchmarks.
+// Here, we set up a string for use in global-replace benchmarks.
// We started with a million blanks, and then deterministically insert
-// 10,000 copies each of two pangrams. The result is a std::string that is
+// 10,000 copies each of two pangrams. The result is a string that is
// 40% blank space and 60% these words. 'the' occurs 18,247 times and
// all the substitutions together occur 49,004 times.
//
-// We then create "after_replacing_the" to be a std::string that is a result of
+// We then create "after_replacing_the" to be a string that is a result of
// replacing "the" with "box" in big_string.
//
-// And then we create "after_replacing_many" to be a std::string that is result
+// And then we create "after_replacing_many" to be a string that is result
// of preferring several substitutions.
void SetUpStrings() {
if (big_string == nullptr) {
diff --git a/absl/strings/str_split.cc b/absl/strings/str_split.cc
index 2a31e09e..cd90425f 100644
--- a/absl/strings/str_split.cc
+++ b/absl/strings/str_split.cc
@@ -27,7 +27,7 @@
#include "absl/strings/ascii.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
@@ -44,10 +44,11 @@ absl::string_view GenericFind(absl::string_view text,
if (delimiter.empty() && text.length() > 0) {
// Special case for empty std::string delimiters: always return a zero-length
// absl::string_view referring to the item at position 1 past pos.
- return absl::string_view(text.begin() + pos + 1, 0);
+ return absl::string_view(text.data() + pos + 1, 0);
}
size_t found_pos = absl::string_view::npos;
- absl::string_view found(text.end(), 0); // By default, not found
+ absl::string_view found(text.data() + text.size(),
+ 0); // By default, not found
found_pos = find_policy.Find(text, delimiter, pos);
if (found_pos != absl::string_view::npos) {
found = absl::string_view(text.data() + found_pos,
@@ -88,7 +89,7 @@ absl::string_view ByString::Find(absl::string_view text, size_t pos) const {
// absl::string_view.
size_t found_pos = text.find(delimiter_[0], pos);
if (found_pos == absl::string_view::npos)
- return absl::string_view(text.end(), 0);
+ return absl::string_view(text.data() + text.size(), 0);
return text.substr(found_pos, 1);
}
return GenericFind(text, delimiter_, pos, LiteralPolicy());
@@ -101,7 +102,7 @@ absl::string_view ByString::Find(absl::string_view text, size_t pos) const {
absl::string_view ByChar::Find(absl::string_view text, size_t pos) const {
size_t found_pos = text.find(c_, pos);
if (found_pos == absl::string_view::npos)
- return absl::string_view(text.end(), 0);
+ return absl::string_view(text.data() + text.size(), 0);
return text.substr(found_pos, 1);
}
@@ -129,10 +130,10 @@ absl::string_view ByLength::Find(absl::string_view text,
// If the std::string is shorter than the chunk size we say we
// "can't find the delimiter" so this will be the last chunk.
if (substr.length() <= static_cast<size_t>(length_))
- return absl::string_view(text.end(), 0);
+ return absl::string_view(text.data() + text.size(), 0);
- return absl::string_view(substr.begin() + length_, 0);
+ return absl::string_view(substr.data() + length_, 0);
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/str_split.h b/absl/strings/str_split.h
index b9f586b9..9483b30e 100644
--- a/absl/strings/str_split.h
+++ b/absl/strings/str_split.h
@@ -19,13 +19,13 @@
//
// This file contains functions for splitting strings. It defines the main
// `StrSplit()` function, several delimiters for determining the boundaries on
-// which to split the std::string, and predicates for filtering delimited results.
+// which to split the string, and predicates for filtering delimited results.
// `StrSplit()` adapts the returned collection to the type specified by the
// caller.
//
// Example:
//
-// // Splits the given std::string on commas. Returns the results in a
+// // Splits the given string on commas. Returns the results in a
// // vector of strings.
// std::vector<std::string> v = absl::StrSplit("a,b,c", ',');
// // Can also use ","
@@ -49,14 +49,14 @@
#include "absl/strings/strip.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
//------------------------------------------------------------------------------
// Delimiters
//------------------------------------------------------------------------------
//
// `StrSplit()` uses delimiters to define the boundaries between elements in the
-// provided input. Several `Delimiter` types are defined below. If a std::string
+// provided input. Several `Delimiter` types are defined below. If a string
// (`const char*`, `std::string`, or `absl::string_view`) is passed in place of
// an explicit `Delimiter` object, `StrSplit()` treats it the same way as if it
// were passed a `ByString` delimiter.
@@ -66,7 +66,7 @@ inline namespace lts_2018_06_20 {
//
// The following `Delimiter` types are available for use within `StrSplit()`:
//
-// - `ByString` (default for std::string arguments)
+// - `ByString` (default for string arguments)
// - `ByChar` (default for a char argument)
// - `ByAnyChar`
// - `ByLength`
@@ -77,15 +77,15 @@ inline namespace lts_2018_06_20 {
// be split and the position to begin searching for the next delimiter in the
// input text. The returned absl::string_view should refer to the next
// occurrence (after pos) of the represented delimiter; this returned
-// absl::string_view represents the next location where the input std::string should
+// absl::string_view represents the next location where the input string should
// be broken. The returned absl::string_view may be zero-length if the Delimiter
-// does not represent a part of the std::string (e.g., a fixed-length delimiter). If
+// does not represent a part of the string (e.g., a fixed-length delimiter). If
// no delimiter is found in the given text, a zero-length absl::string_view
// referring to text.end() should be returned (e.g.,
// absl::string_view(text.end(), 0)). It is important that the returned
// absl::string_view always be within the bounds of input text given as an
-// argument--it must not refer to a std::string that is physically located outside of
-// the given std::string.
+// argument--it must not refer to a string that is physically located outside of
+// the given string.
//
// The following example is a simple Delimiter object that is created with a
// single char and will look for that char in the text passed to the Find()
@@ -105,13 +105,13 @@ inline namespace lts_2018_06_20 {
// ByString
//
-// A sub-std::string delimiter. If `StrSplit()` is passed a std::string in place of a
-// `Delimiter` object, the std::string will be implicitly converted into a
+// A sub-string delimiter. If `StrSplit()` is passed a string in place of a
+// `Delimiter` object, the string will be implicitly converted into a
// `ByString` delimiter.
//
// Example:
//
-// // Because a std::string literal is converted to an `absl::ByString`,
+// // Because a string literal is converted to an `absl::ByString`,
// // the following two splits are equivalent.
//
// std::vector<std::string> v1 = absl::StrSplit("a, b, c", ", ");
@@ -132,7 +132,7 @@ class ByString {
// ByChar
//
// A single character delimiter. `ByChar` is functionally equivalent to a
-// 1-char std::string within a `ByString` delimiter, but slightly more
+// 1-char string within a `ByString` delimiter, but slightly more
// efficient.
//
// Example:
@@ -165,9 +165,9 @@ class ByChar {
// ByAnyChar
//
// A delimiter that will match any of the given byte-sized characters within
-// its provided std::string.
+// its provided string.
//
-// Note: this delimiter works with single-byte std::string data, but does not work
+// Note: this delimiter works with single-byte string data, but does not work
// with variable-width encodings, such as UTF-8.
//
// Example:
@@ -176,8 +176,8 @@ class ByChar {
// std::vector<std::string> v = absl::StrSplit("a,b=c", ByAnyChar(",="));
// // v[0] == "a", v[1] == "b", v[2] == "c"
//
-// If `ByAnyChar` is given the empty std::string, it behaves exactly like
-// `ByString` and matches each individual character in the input std::string.
+// If `ByAnyChar` is given the empty string, it behaves exactly like
+// `ByString` and matches each individual character in the input string.
//
class ByAnyChar {
public:
@@ -193,7 +193,7 @@ class ByAnyChar {
// A delimiter for splitting into equal-length strings. The length argument to
// the constructor must be greater than 0.
//
-// Note: this delimiter works with single-byte std::string data, but does not work
+// Note: this delimiter works with single-byte string data, but does not work
// with variable-width encodings, such as UTF-8.
//
// Example:
@@ -203,7 +203,7 @@ class ByAnyChar {
// // v[0] == "123", v[1] == "456", v[2] == "789"
//
-// Note that the std::string does not have to be a multiple of the fixed split
+// Note that the string does not have to be a multiple of the fixed split
// length. In such a case, the last substring will be shorter.
//
// using absl::ByLength;
@@ -224,9 +224,9 @@ namespace strings_internal {
// A traits-like metafunction for selecting the default Delimiter object type
// for a particular Delimiter type. The base case simply exposes type Delimiter
// itself as the delimiter's Type. However, there are specializations for
-// std::string-like objects that map them to the ByString delimiter object.
+// string-like objects that map them to the ByString delimiter object.
// This allows functions like absl::StrSplit() and absl::MaxSplits() to accept
-// std::string-like objects (e.g., ',') as delimiter arguments but they will be
+// string-like objects (e.g., ',') as delimiter arguments but they will be
// treated as if a ByString delimiter was given.
template <typename Delimiter>
struct SelectDelimiter {
@@ -262,7 +262,8 @@ class MaxSplitsImpl {
: delimiter_(delimiter), limit_(limit), count_(0) {}
absl::string_view Find(absl::string_view text, size_t pos) {
if (count_++ == limit_) {
- return absl::string_view(text.end(), 0); // No more matches.
+ return absl::string_view(text.data() + text.size(),
+ 0); // No more matches.
}
return delimiter_.Find(text, pos);
}
@@ -332,7 +333,7 @@ struct AllowEmpty {
// SkipEmpty()
//
// Returns `false` if the given `absl::string_view` is empty, indicating that
-// `StrSplit()` should omit the empty std::string.
+// `StrSplit()` should omit the empty string.
//
// Example:
//
@@ -340,7 +341,7 @@ struct AllowEmpty {
//
// // v[0] == "a", v[1] == "b"
//
-// Note: `SkipEmpty()` does not consider a std::string containing only whitespace
+// Note: `SkipEmpty()` does not consider a string containing only whitespace
// to be empty. To skip such whitespace as well, use the `SkipWhitespace()`
// predicate.
struct SkipEmpty {
@@ -350,7 +351,7 @@ struct SkipEmpty {
// SkipWhitespace()
//
// Returns `false` if the given `absl::string_view` is empty *or* contains only
-// whitespace, indicating that `StrSplit()` should omit the std::string.
+// whitespace, indicating that `StrSplit()` should omit the string.
//
// Example:
//
@@ -374,11 +375,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 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:
//
@@ -413,7 +414,7 @@ struct SkipWhitespace {
//
// The `StrSplit()` function adapts the returned collection to the collection
// specified by the caller (e.g. `std::vector` above). The returned collections
-// may contain `string`, `absl::string_view` (in which case the original std::string
+// may contain `string`, `absl::string_view` (in which case the original string
// being split must ensure that it outlives the collection), or any object that
// can be explicitly created from an `absl::string_view`. This behavior works
// for:
@@ -425,7 +426,7 @@ struct SkipWhitespace {
// Example:
//
// // The results are returned as `absl::string_view` objects. Note that we
-// // have to ensure that the input std::string outlives any results.
+// // have to ensure that the input string outlives any results.
// std::vector<absl::string_view> v = absl::StrSplit("a,b,c", ',');
//
// // Stores results in a std::set<std::string>, which also performs
@@ -445,7 +446,7 @@ struct SkipWhitespace {
// // is provided as a series of key/value pairs. For example, the 0th element
// // resulting from the split will be stored as a key to the 1st element. If
// // an odd number of elements are resolved, the last element is paired with
-// // a default-constructed value (e.g., empty std::string).
+// // a default-constructed value (e.g., empty string).
// std::map<std::string, std::string> m = absl::StrSplit("a,b,c", ',');
// // m["a"] == "b", m["c"] == "" // last component value equals ""
//
@@ -453,14 +454,14 @@ struct SkipWhitespace {
// elements and is not a collection type. When splitting to a `std::pair` the
// first two split strings become the `std::pair` `.first` and `.second`
// members, respectively. The remaining split substrings are discarded. If there
-// are less than two split substrings, the empty std::string is used for the
+// are less than two split substrings, the empty string is used for the
// corresponding
// `std::pair` member.
//
// Example:
//
// // Stores first two split strings as the members in a std::pair.
-// std::pair<std::string, std::string> p = absl::StrSplit("a,b,c", ',');
+// std::pair<string, string> p = absl::StrSplit("a,b,c", ',');
// // p.first == "a", p.second == "b" // "c" is omitted.
//
// The `StrSplit()` function can be used multiple times to perform more
@@ -468,9 +469,9 @@ struct SkipWhitespace {
//
// Example:
//
-// // The input std::string "a=b=c,d=e,f=,g" becomes
+// // The input string "a=b=c,d=e,f=,g" becomes
// // { "a" => "b=c", "d" => "e", "f" => "", "g" => "" }
-// std::map<std::string, std::string> m;
+// std::map<string, string> m;
// for (absl::string_view sp : absl::StrSplit("a=b=c,d=e,f=,g", ',')) {
// m.insert(absl::StrSplit(sp, absl::MaxSplits('=', 1)));
// }
@@ -507,7 +508,7 @@ StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d,
std::move(text), DelimiterType(d), std::move(p));
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_STR_SPLIT_H_
diff --git a/absl/strings/str_split_benchmark.cc b/absl/strings/str_split_benchmark.cc
index 326ff744..0ac297c8 100644
--- a/absl/strings/str_split_benchmark.cc
+++ b/absl/strings/str_split_benchmark.cc
@@ -35,16 +35,16 @@ std::string MakeTestString(int desired_length) {
return test;
}
-void BM_Split2StringPiece(benchmark::State& state) {
+void BM_Split2StringView(benchmark::State& state) {
std::string test = MakeTestString(state.range(0));
for (auto _ : state) {
std::vector<absl::string_view> result = absl::StrSplit(test, ';');
benchmark::DoNotOptimize(result);
}
}
-BENCHMARK_RANGE(BM_Split2StringPiece, 0, 1 << 20);
+BENCHMARK_RANGE(BM_Split2StringView, 0, 1 << 20);
-void BM_Split2StringPieceLifted(benchmark::State& state) {
+void BM_Split2StringViewLifted(benchmark::State& state) {
std::string test = MakeTestString(state.range(0));
std::vector<absl::string_view> result;
for (auto _ : state) {
@@ -52,7 +52,7 @@ void BM_Split2StringPieceLifted(benchmark::State& state) {
}
benchmark::DoNotOptimize(result);
}
-BENCHMARK_RANGE(BM_Split2StringPieceLifted, 0, 1 << 20);
+BENCHMARK_RANGE(BM_Split2StringViewLifted, 0, 1 << 20);
void BM_Split2String(benchmark::State& state) {
std::string test = MakeTestString(state.range(0));
diff --git a/absl/strings/str_split_test.cc b/absl/strings/str_split_test.cc
index c172a762..caa88277 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
@@ -248,7 +276,7 @@ TEST(SplitIterator, Basics) {
EXPECT_EQ(it, end);
}
-// Simple Predicate to skip a particular std::string.
+// Simple Predicate to skip a particular string.
class Skip {
public:
explicit Skip(const std::string& s) : s_(s) {}
@@ -735,12 +763,12 @@ template <typename Delimiter>
static bool IsFoundAtStartingPos(absl::string_view text, Delimiter d,
size_t starting_pos, int expected_pos) {
absl::string_view found = d.Find(text, starting_pos);
- return found.data() != text.end() &&
+ return found.data() != text.data() + text.size() &&
expected_pos == found.data() - text.data();
}
// Helper function for testing Delimiter objects. Returns true if the given
-// Delimiter is found in the given std::string at the given position. This function
+// Delimiter is found in the given string at the given position. This function
// tests two cases:
// 1. The actual text given, staring at position 0
// 2. The text given with leading padding that should be ignored
diff --git a/absl/strings/string_view.cc b/absl/strings/string_view.cc
index 92197a75..3620ff44 100644
--- a/absl/strings/string_view.cc
+++ b/absl/strings/string_view.cc
@@ -24,7 +24,7 @@
#include "absl/strings/internal/memutil.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace {
void WritePadding(std::ostream& o, size_t pad) {
@@ -241,7 +241,7 @@ constexpr string_view::size_type string_view::npos;
ABSL_STRING_VIEW_SELECTANY
constexpr string_view::size_type string_view::kMaxSize;
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_HAVE_STD_STRING_VIEW
diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h
index 4a5e4d8a..df6f1ae4 100644
--- a/absl/strings/string_view.h
+++ b/absl/strings/string_view.h
@@ -19,7 +19,7 @@
//
// This file contains the definition of the `absl::string_view` class. A
// `string_view` points to a contiguous span of characters, often part or all of
-// another `std::string`, double-quoted std::string literal, character array, or even
+// another `std::string`, double-quoted string literal, character array, or even
// another `string_view`.
//
// This `absl::string_view` abstraction is designed to be a drop-in
@@ -35,9 +35,9 @@
#include <string_view>
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
using std::string_view;
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#else // ABSL_HAVE_STD_STRING_VIEW
@@ -55,19 +55,19 @@ using std::string_view;
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// absl::string_view
//
-// A `string_view` provides a lightweight view into the std::string data provided by
-// a `std::string`, double-quoted std::string literal, character array, or even
-// another `string_view`. A `string_view` does *not* own the std::string to which it
+// A `string_view` provides a lightweight view into the string data provided by
+// a `std::string`, double-quoted string literal, character array, or even
+// another `string_view`. A `string_view` does *not* own the string to which it
// points, and that data cannot be modified through the view.
//
// You can use `string_view` as a function or method parameter anywhere a
-// parameter can receive a double-quoted std::string literal, `const char*`,
+// parameter can receive a double-quoted string literal, `const char*`,
// `std::string`, or another `absl::string_view` argument with no need to copy
-// the std::string data. Systematic use of `string_view` within function arguments
+// the string data. Systematic use of `string_view` within function arguments
// reduces data copies and `strlen()` calls.
//
// Because of its small size, prefer passing `string_view` by value:
@@ -100,8 +100,8 @@ inline namespace lts_2018_06_20 {
// `string_view` this way, it is your responsibility to ensure that the object
// pointed to by the `string_view` outlives the `string_view`.
//
-// A `string_view` may represent a whole std::string or just part of a std::string. For
-// example, when splitting a std::string, `std::vector<absl::string_view>` is a
+// A `string_view` may represent a whole string or just part of a string. For
+// example, when splitting a string, `std::vector<absl::string_view>` is a
// natural data type for the output.
//
//
@@ -144,7 +144,7 @@ inline namespace lts_2018_06_20 {
// All empty `string_view` objects whether null or not, are equal:
//
// absl::string_view() == absl::string_view("", 0)
-// absl::string_view(nullptr, 0) == absl:: string_view("abcdef"+6, 0)
+// absl::string_view(nullptr, 0) == absl::string_view("abcdef"+6, 0)
class string_view {
public:
using traits_type = std::char_traits<char>;
@@ -176,8 +176,19 @@ class string_view {
// Implicit constructor of a `string_view` from nul-terminated `str`. When
// accepting possibly null strings, use `absl::NullSafeStringView(str)`
// instead (see below).
+#if ABSL_HAVE_BUILTIN(__builtin_strlen) || \
+ (defined(__GNUC__) && !defined(__clang__))
+ // GCC has __builtin_strlen according to
+ // https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Other-Builtins.html, but
+ // ABSL_HAVE_BUILTIN doesn't detect that, so we use the extra checks above.
+ // __builtin_strlen is constexpr.
+ constexpr string_view(const char* str) // NOLINT(runtime/explicit)
+ : ptr_(str),
+ length_(CheckLengthInternal(str ? __builtin_strlen(str) : 0)) {}
+#else
constexpr string_view(const char* str) // NOLINT(runtime/explicit)
- : ptr_(str), length_(CheckLengthInternal(StrLenInternal(str))) {}
+ : ptr_(str), length_(CheckLengthInternal(str ? strlen(str) : 0)) {}
+#endif
// Implicit constructor of a `string_view` from a `const char*` and length.
constexpr string_view(const char* data, size_type len)
@@ -343,7 +354,7 @@ class string_view {
//
// Returns a "substring" of the `string_view` (at offset `pos` and length
// `n`) as another string_view. This function throws `std::out_of_bounds` if
- // `pos > size'.
+ // `pos > size`.
string_view substr(size_type pos, size_type n = npos) const {
if (ABSL_PREDICT_FALSE(pos > length_))
base_internal::ThrowStdOutOfRange("absl::string_view::substr");
@@ -354,7 +365,7 @@ class string_view {
// string_view::compare()
//
// Performs a lexicographical comparison between the `string_view` and
- // another `absl::string_view), returning -1 if `this` is less than, 0 if
+ // another `absl::string_view`, returning -1 if `this` is less than, 0 if
// `this` is equal to, and 1 if `this` is greater than the passed std::string
// view. Note that in the case of data equality, a further comparison is made
// on the respective sizes of the two `string_view`s to determine which is
@@ -482,23 +493,7 @@ class string_view {
private:
static constexpr size_type kMaxSize =
- std::numeric_limits<difference_type>::max();
-
- // check whether __builtin_strlen is provided by the compiler.
- // GCC doesn't have __has_builtin()
- // (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66970),
- // but has __builtin_strlen according to
- // https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Other-Builtins.html.
-#if ABSL_HAVE_BUILTIN(__builtin_strlen) || \
- (defined(__GNUC__) && !defined(__clang__))
- static constexpr size_type StrLenInternal(const char* str) {
- return str ? __builtin_strlen(str) : 0;
- }
-#else
- static constexpr size_type StrLenInternal(const char* str) {
- return str ? strlen(str) : 0;
- }
-#endif
+ (std::numeric_limits<difference_type>::max)();
static constexpr size_type CheckLengthInternal(size_type len) {
return ABSL_ASSERT(len <= kMaxSize), len;
@@ -543,13 +538,13 @@ inline bool operator>=(string_view x, string_view y) noexcept {
// IO Insertion Operator
std::ostream& operator<<(std::ostream& o, string_view piece);
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_HAVE_STD_STRING_VIEW
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// ClippedSubstr()
//
@@ -570,7 +565,7 @@ inline string_view NullSafeStringView(const char* p) {
return p ? string_view(p) : string_view();
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_STRING_VIEW_H_
diff --git a/absl/strings/string_view_benchmark.cc b/absl/strings/string_view_benchmark.cc
index fb46db18..f4420320 100644
--- a/absl/strings/string_view_benchmark.cc
+++ b/absl/strings/string_view_benchmark.cc
@@ -151,7 +151,7 @@ void BM_find_string_view_len_one(benchmark::State& state) {
std::string haystack(state.range(0), '0');
absl::string_view s(haystack);
for (auto _ : state) {
- s.find("x"); // not present; length 1
+ benchmark::DoNotOptimize(s.find("x")); // not present; length 1
}
}
BENCHMARK(BM_find_string_view_len_one)->Range(1, 1 << 20);
@@ -160,7 +160,7 @@ void BM_find_string_view_len_two(benchmark::State& state) {
std::string haystack(state.range(0), '0');
absl::string_view s(haystack);
for (auto _ : state) {
- s.find("xx"); // not present; length 2
+ benchmark::DoNotOptimize(s.find("xx")); // not present; length 2
}
}
BENCHMARK(BM_find_string_view_len_two)->Range(1, 1 << 20);
@@ -169,7 +169,7 @@ void BM_find_one_char(benchmark::State& state) {
std::string haystack(state.range(0), '0');
absl::string_view s(haystack);
for (auto _ : state) {
- s.find('x'); // not present
+ benchmark::DoNotOptimize(s.find('x')); // not present
}
}
BENCHMARK(BM_find_one_char)->Range(1, 1 << 20);
@@ -178,7 +178,7 @@ void BM_rfind_one_char(benchmark::State& state) {
std::string haystack(state.range(0), '0');
absl::string_view s(haystack);
for (auto _ : state) {
- s.rfind('x'); // not present
+ benchmark::DoNotOptimize(s.rfind('x')); // not present
}
}
BENCHMARK(BM_rfind_one_char)->Range(1, 1 << 20);
@@ -193,7 +193,7 @@ void BM_worst_case_find_first_of(benchmark::State& state, int haystack_len) {
absl::string_view s(haystack);
for (auto _ : state) {
- s.find_first_of(needle);
+ benchmark::DoNotOptimize(s.find_first_of(needle));
}
}
diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc
index fffa7b99..ed34ed83 100644
--- a/absl/strings/string_view_test.cc
+++ b/absl/strings/string_view_test.cc
@@ -35,7 +35,8 @@
#define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
EXPECT_DEATH_IF_SUPPORTED(statement, ".*")
#else
-#define ABSL_EXPECT_DEATH_IF_SUPPORTED EXPECT_DEATH_IF_SUPPORTED
+#define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
+ EXPECT_DEATH_IF_SUPPORTED(statement, regex)
#endif
namespace {
@@ -283,7 +284,7 @@ TEST(StringViewTest, ComparisonOperatorsByCharacterPosition) {
}
#undef COMPARE
-// Sadly, our users often confuse std::string::npos with absl::string_view::npos;
+// Sadly, our users often confuse string::npos with absl::string_view::npos;
// So much so that we test here that they are the same. They need to
// both be unsigned, and both be the maximum-valued integer of their type.
@@ -677,9 +678,9 @@ TEST(StringViewTest, STL2Substr) {
EXPECT_EQ(a.substr(23, absl::string_view::npos), c);
// throw exception
#ifdef ABSL_HAVE_EXCEPTIONS
- EXPECT_THROW(a.substr(99, 2), std::out_of_range);
+ EXPECT_THROW((void)a.substr(99, 2), std::out_of_range);
#else
- EXPECT_DEATH(a.substr(99, 2), "absl::string_view::substr");
+ EXPECT_DEATH((void)a.substr(99, 2), "absl::string_view::substr");
#endif
}
@@ -811,15 +812,18 @@ TEST(StringViewTest, FrontBackSingleChar) {
}
// `std::string_view::string_view(const char*)` calls
-// `std::char_traits<char>::length(const char*)` to get the std::string length. In
+// `std::char_traits<char>::length(const char*)` to get the string length. In
// libc++, it doesn't allow `nullptr` in the constexpr context, with the error
// "read of dereferenced null pointer is not allowed in a constant expression".
// At run time, the behavior of `std::char_traits::length()` on `nullptr` is
-// undefined by the standard and usually results in crash with libc++. This
-// conforms to the standard, but `absl::string_view` implements a different
+// undefined by the standard and usually results in crash with libc++.
+// In MSVC, creating a constexpr string_view from nullptr also triggers an
+// "unevaluable pointer value" error. This compiler implementation conforms
+// to the standard, but `absl::string_view` implements a different
// behavior for historical reasons. We work around tests that construct
// `string_view` from `nullptr` when using libc++.
-#if !defined(ABSL_HAVE_STD_STRING_VIEW) || !defined(_LIBCPP_VERSION)
+#if !defined(ABSL_HAVE_STD_STRING_VIEW) || \
+ (!defined(_LIBCPP_VERSION) && !defined(_MSC_VER))
#define ABSL_HAVE_STRING_VIEW_FROM_NULLPTR 1
#endif // !defined(ABSL_HAVE_STD_STRING_VIEW) || !defined(_LIBCPP_VERSION)
diff --git a/absl/strings/strip.h b/absl/strings/strip.h
index ea64c334..059f57b7 100644
--- a/absl/strings/strip.h
+++ b/absl/strings/strip.h
@@ -17,7 +17,7 @@
// File: strip.h
// -----------------------------------------------------------------------------
//
-// This file contains various functions for stripping substrings from a std::string.
+// This file contains various functions for stripping substrings from a string.
#ifndef ABSL_STRINGS_STRIP_H_
#define ABSL_STRINGS_STRIP_H_
@@ -30,11 +30,11 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
// ConsumePrefix()
//
-// Strips the `expected` prefix from the start of the given std::string, returning
+// Strips the `expected` prefix from the start of the given string, returning
// `true` if the strip operation succeeded or false otherwise.
//
// Example:
@@ -49,7 +49,7 @@ inline bool ConsumePrefix(absl::string_view* str, absl::string_view expected) {
}
// ConsumeSuffix()
//
-// Strips the `expected` suffix from the end of the given std::string, returning
+// Strips the `expected` suffix from the end of the given string, returning
// `true` if the strip operation succeeded or false otherwise.
//
// Example:
@@ -65,9 +65,9 @@ inline bool ConsumeSuffix(absl::string_view* str, absl::string_view expected) {
// StripPrefix()
//
-// Returns a view into the input std::string 'str' with the given 'prefix' removed,
-// but leaving the original std::string intact. If the prefix does not match at the
-// start of the std::string, returns the original std::string instead.
+// Returns a view into the input string 'str' with the given 'prefix' removed,
+// but leaving the original string intact. If the prefix does not match at the
+// start of the string, returns the original string instead.
ABSL_MUST_USE_RESULT inline absl::string_view StripPrefix(
absl::string_view str, absl::string_view prefix) {
if (absl::StartsWith(str, prefix)) str.remove_prefix(prefix.size());
@@ -76,16 +76,16 @@ ABSL_MUST_USE_RESULT inline absl::string_view StripPrefix(
// StripSuffix()
//
-// Returns a view into the input std::string 'str' with the given 'suffix' removed,
-// but leaving the original std::string intact. If the suffix does not match at the
-// end of the std::string, returns the original std::string instead.
+// Returns a view into the input string 'str' with the given 'suffix' removed,
+// but leaving the original string intact. If the suffix does not match at the
+// end of the string, returns the original string instead.
ABSL_MUST_USE_RESULT inline absl::string_view StripSuffix(
absl::string_view str, absl::string_view suffix) {
if (absl::EndsWith(str, suffix)) str.remove_suffix(suffix.size());
return str;
}
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_STRIP_H_
diff --git a/absl/strings/strip_test.cc b/absl/strings/strip_test.cc
index 205c160c..40c4c607 100644
--- a/absl/strings/strip_test.cc
+++ b/absl/strings/strip_test.cc
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// This file contains functions that remove a defined part from the std::string,
-// i.e., strip the std::string.
+// This file contains functions that remove a defined part from the string,
+// i.e., strip the string.
#include "absl/strings/strip.h"
diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc
index 849443b9..b70e70e8 100644
--- a/absl/strings/substitute.cc
+++ b/absl/strings/substitute.cc
@@ -23,7 +23,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace substitute_internal {
void SubstituteAndAppendArray(std::string* output, absl::string_view format,
@@ -168,5 +168,5 @@ Arg::Arg(Dec dec) {
}
} // namespace substitute_internal
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h
index def79be2..43d73ad7 100644
--- a/absl/strings/substitute.h
+++ b/absl/strings/substitute.h
@@ -17,46 +17,46 @@
// File: substitute.h
// -----------------------------------------------------------------------------
//
-// This package contains functions for efficiently performing std::string
-// substitutions using a format std::string with positional notation:
+// This package contains functions for efficiently performing string
+// substitutions using a format string with positional notation:
// `Substitute()` and `SubstituteAndAppend()`.
//
// Unlike printf-style format specifiers, `Substitute()` functions do not need
// to specify the type of the substitution arguments. Supported arguments
-// following the format std::string, such as strings, string_views, ints,
+// following the format string, such as strings, string_views, ints,
// floats, and bools, are automatically converted to strings during the
// substitution process. (See below for a full list of supported types.)
//
// `Substitute()` does not allow you to specify *how* to format a value, beyond
-// the default conversion to std::string. For example, you cannot format an integer
+// the default conversion to string. For example, you cannot format an integer
// in hex.
//
-// The format std::string uses positional identifiers indicated by a dollar sign ($)
+// The format string uses positional identifiers indicated by a dollar sign ($)
// and single digit positional ids to indicate which substitution arguments to
-// use at that location within the format std::string.
+// use at that location within the format string.
//
// Example 1:
-// std::string s = Substitute("$1 purchased $0 $2. Thanks $1!",
+// string s = Substitute("$1 purchased $0 $2. Thanks $1!",
// 5, "Bob", "Apples");
// EXPECT_EQ("Bob purchased 5 Apples. Thanks Bob!", s);
//
// Example 2:
-// std::string s = "Hi. ";
+// string s = "Hi. ";
// SubstituteAndAppend(&s, "My name is $0 and I am $1 years old.", "Bob", 5);
// EXPECT_EQ("Hi. My name is Bob and I am 5 years old.", s);
//
//
// Supported types:
-// * absl::string_view, std::string, const char* (null is equivalent to "")
+// * absl::string_view, string, const char* (null is equivalent to "")
// * int32_t, int64_t, uint32_t, uint64
// * float, double
// * bool (Printed as "true" or "false")
-// * pointer types other than char* (Printed as "0x<lower case hex std::string>",
+// * pointer types other than char* (Printed as "0x<lower case hex string>",
// except that null is printed as "NULL")
//
-// If an invalid format std::string is provided, Substitute returns an empty std::string
-// and SubstituteAndAppend does not change the provided output std::string.
-// A format std::string is invalid if it:
+// If an invalid format string is provided, Substitute returns an empty string
+// and SubstituteAndAppend does not change the provided output string.
+// A format string is invalid if it:
// * ends in an unescaped $ character,
// e.g. "Hello $", or
// * calls for a position argument which is not provided,
@@ -82,14 +82,14 @@
#include "absl/strings/strip.h"
namespace absl {
-inline namespace lts_2018_06_20 {
+inline namespace lts_2018_12_18 {
namespace substitute_internal {
// Arg
//
// This class provides an argument type for `absl::Substitute()` and
// `absl::SubstituteAndAppend()`. `Arg` handles implicit conversion of various
-// types to a std::string. (`Arg` is very similar to the `AlphaNum` class in
+// types to a string. (`Arg` is very similar to the `AlphaNum` class in
// `StrCat()`.)
//
// This class has implicit constructors.
@@ -198,8 +198,8 @@ constexpr int PlaceholderBitmask(const char* format) {
// SubstituteAndAppend()
//
-// Substitutes variables into a given format std::string and appends to a given
-// output std::string. See file comments above for usage.
+// Substitutes variables into a given format string and appends to a given
+// output string. See file comments above for usage.
//
// The declarations of `SubstituteAndAppend()` below consist of overloads
// for passing 0 to 10 arguments, respectively.
@@ -445,7 +445,7 @@ void SubstituteAndAppend(
// Substitute()
//
-// Substitutes variables into a given format std::string. See file comments above
+// Substitutes variables into a given format string. See file comments above
// for usage.
//
// The declarations of `Substitute()` below consist of overloads for passing 0
@@ -457,7 +457,7 @@ void SubstituteAndAppend(
// Example:
// template <typename... Args>
// void VarMsg(absl::string_view format, const Args&... args) {
-// std::string s = absl::Substitute(format, args...);
+// string s = absl::Substitute(format, args...);
ABSL_MUST_USE_RESULT inline std::string Substitute(absl::string_view format) {
std::string result;
@@ -667,7 +667,7 @@ std::string Substitute(
"format std::string doesn't contain all of $0 through $9");
#endif // ABSL_BAD_CALL_IF
-} // inline namespace lts_2018_06_20
+} // inline namespace lts_2018_12_18
} // namespace absl
#endif // ABSL_STRINGS_SUBSTITUTE_H_