summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2018-09-12 11:03:25 -0700
committerGravatar Gennadiy Civil <misterg@google.com>2018-09-13 13:24:44 -0400
commit8ff1374008259719b54a8cb128ef951c02da164c (patch)
treed6768eeb981f8e3a14668c329336faee20291346
parent02451914b9ad5320f81f56a89f3eef1f8683227c (diff)
Export of internal Abseil changes.
-- 821196cfb2a3b943ffdc4c9e75daec92d7ffb28b by Abseil Team <absl-team@google.com>: Performance improvements PiperOrigin-RevId: 212668992 -- 704858e2e767016bad27d53eec01d9d48e546b23 by Abseil Team <absl-team@google.com>: Low-level Portability enchancements for Abseil Mutex on WebAssembly. Emscripten Pthreads do not use signals, so remove use of pthread_sigmask or other async-signal-safe related handling code. PiperOrigin-RevId: 212527958 -- be3e38cb4d493b755132d20c8c2d1a51e45d5449 by Jon Cohen <cohenjon@google.com>: Internal change. PiperOrigin-RevId: 212523797 GitOrigin-RevId: 821196cfb2a3b943ffdc4c9e75daec92d7ffb28b Change-Id: I5694e23e4e09364a15dd6fc4e2e3f15e38835687
-rw-r--r--absl/base/internal/low_level_alloc.cc14
-rw-r--r--absl/base/internal/low_level_alloc.h7
-rw-r--r--absl/base/internal/thread_identity.cc10
-rw-r--r--absl/memory/memory_test.cc1
-rw-r--r--absl/strings/internal/str_format/arg.cc80
-rw-r--r--absl/strings/internal/str_format/arg.h210
-rw-r--r--absl/strings/internal/str_format/bind.h2
-rw-r--r--absl/strings/internal/str_format/extension.h10
-rw-r--r--absl/strings/str_format_test.cc36
9 files changed, 199 insertions, 171 deletions
diff --git a/absl/base/internal/low_level_alloc.cc b/absl/base/internal/low_level_alloc.cc
index 0a6f3070..6e636a02 100644
--- a/absl/base/internal/low_level_alloc.cc
+++ b/absl/base/internal/low_level_alloc.cc
@@ -401,16 +401,20 @@ bool LowLevelAlloc::DeleteArena(Arena *arena) {
ABSL_RAW_CHECK(munmap_result != 0,
"LowLevelAlloc::DeleteArena: VitualFree failed");
#else
+#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) == 0) {
munmap_result = munmap(region, size);
} else {
munmap_result = base_internal::DirectMunmap(region, size);
}
+#else
+ munmap_result = munmap(region, size);
+#endif // ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
if (munmap_result != 0) {
ABSL_RAW_LOG(FATAL, "LowLevelAlloc::DeleteArena: munmap failed: %d",
errno);
}
-#endif
+#endif // _WIN32
}
section.Leave();
arena->~Arena();
@@ -545,6 +549,7 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) {
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
ABSL_RAW_CHECK(new_pages != nullptr, "VirtualAlloc failed");
#else
+#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) != 0) {
new_pages = base_internal::DirectMmap(nullptr, new_pages_size,
PROT_WRITE|PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
@@ -552,10 +557,15 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) {
new_pages = mmap(nullptr, new_pages_size, PROT_WRITE | PROT_READ,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
}
+#else
+ new_pages = mmap(nullptr, new_pages_size, PROT_WRITE | PROT_READ,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+#endif // ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
if (new_pages == MAP_FAILED) {
ABSL_RAW_LOG(FATAL, "mmap error: %d", errno);
}
-#endif
+
+#endif // _WIN32
arena->mu.Lock();
s = reinterpret_cast<AllocList *>(new_pages);
s->header.size = new_pages_size;
diff --git a/absl/base/internal/low_level_alloc.h b/absl/base/internal/low_level_alloc.h
index 3c15605b..fba9466a 100644
--- a/absl/base/internal/low_level_alloc.h
+++ b/absl/base/internal/low_level_alloc.h
@@ -39,10 +39,13 @@
#define ABSL_LOW_LEVEL_ALLOC_MISSING 1
#endif
-// Using LowLevelAlloc with kAsyncSignalSafe isn't supported on Windows.
+// Using LowLevelAlloc with kAsyncSignalSafe isn't supported on Windows or
+// asm.js / WebAssembly.
+// See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html
+// for more information.
#ifdef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
#error ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING cannot be directly set
-#elif defined(_WIN32)
+#elif defined(_WIN32) || defined(__asmjs__) || defined(__wasm__)
#define ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING 1
#endif
diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc
index 678e8568..cff9c1b4 100644
--- a/absl/base/internal/thread_identity.cc
+++ b/absl/base/internal/thread_identity.cc
@@ -68,6 +68,14 @@ void SetCurrentThreadIdentity(
// NOTE: Not async-safe. But can be open-coded.
absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey,
reclaimer);
+
+#ifdef __EMSCRIPTEN__
+ // Emscripten PThread implementation does not support signals.
+ // See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html
+ // for more information.
+ pthread_setspecific(thread_identity_pthread_key,
+ reinterpret_cast<void*>(identity));
+#else
// We must mask signals around the call to setspecific as with current glibc,
// a concurrent getspecific (needed for GetCurrentThreadIdentityIfPresent())
// may zero our value.
@@ -81,6 +89,8 @@ void SetCurrentThreadIdentity(
pthread_setspecific(thread_identity_pthread_key,
reinterpret_cast<void*>(identity));
pthread_sigmask(SIG_SETMASK, &curr_signals, nullptr);
+#endif // !__EMSCRIPTEN__
+
#elif ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS
// NOTE: Not async-safe. But can be open-coded.
absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey,
diff --git a/absl/memory/memory_test.cc b/absl/memory/memory_test.cc
index dee9b486..2e453e22 100644
--- a/absl/memory/memory_test.cc
+++ b/absl/memory/memory_test.cc
@@ -149,7 +149,6 @@ TEST(Make_UniqueTest, NotAmbiguousWithStdMakeUnique) {
}
#if 0
-// TODO(billydonahue): Make a proper NC test.
// These tests shouldn't compile.
TEST(MakeUniqueTestNC, AcceptMoveOnlyLvalue) {
auto m = MoveOnly();
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
index eafb068f..b40be8ff 100644
--- a/absl/strings/internal/str_format/arg.cc
+++ b/absl/strings/internal/str_format/arg.cc
@@ -112,7 +112,7 @@ class ConvertedIntInfo {
// 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) {
+ const ConversionSpec conv) {
bool alt = conv.flags().alt;
int radix = conv.conv().radix();
if (conv.conv().id() == ConversionChar::p)
@@ -127,7 +127,7 @@ string_view BaseIndicator(const ConvertedIntInfo &info,
return {};
}
-string_view SignColumn(bool neg, const ConversionSpec &conv) {
+string_view SignColumn(bool neg, const ConversionSpec conv) {
if (conv.conv().is_signed()) {
if (neg) return "-";
if (conv.flags().show_pos) return "+";
@@ -136,7 +136,7 @@ string_view SignColumn(bool neg, const ConversionSpec &conv) {
return {};
}
-bool ConvertCharImpl(unsigned char v, const ConversionSpec &conv,
+bool ConvertCharImpl(unsigned char v, const ConversionSpec conv,
FormatSinkImpl *sink) {
size_t fill = 0;
if (conv.width() >= 0) fill = conv.width();
@@ -148,7 +148,7 @@ bool ConvertCharImpl(unsigned char v, const ConversionSpec &conv,
}
bool ConvertIntImplInner(const ConvertedIntInfo &info,
- const ConversionSpec &conv, FormatSinkImpl *sink) {
+ const ConversionSpec conv, FormatSinkImpl *sink) {
// Print as a sequence of Substrings:
// [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces]
size_t fill = 0;
@@ -202,8 +202,7 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info,
}
template <typename T>
-bool ConvertIntImplInner(T v, const ConversionSpec &conv,
- FormatSinkImpl *sink) {
+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, '-');
@@ -218,7 +217,7 @@ bool ConvertIntImplInner(T v, const ConversionSpec &conv,
}
template <typename T>
-bool ConvertIntArg(T v, const ConversionSpec &conv, FormatSinkImpl *sink) {
+bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
if (conv.conv().is_float()) {
return FormatConvertImpl(static_cast<double>(v), conv, sink).value;
}
@@ -234,11 +233,11 @@ bool ConvertIntArg(T v, const ConversionSpec &conv, FormatSinkImpl *sink) {
}
template <typename T>
-bool ConvertFloatArg(T v, const ConversionSpec &conv, FormatSinkImpl *sink) {
+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,
+inline bool ConvertStringArg(string_view v, const ConversionSpec conv,
FormatSinkImpl *sink) {
if (conv.conv().id() != ConversionChar::s)
return false;
@@ -254,19 +253,19 @@ inline bool ConvertStringArg(string_view v, const ConversionSpec &conv,
// ==================== Strings ====================
ConvertResult<Conv::s> FormatConvertImpl(const std::string &v,
- const ConversionSpec &conv,
+ const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertStringArg(v, conv, sink)};
}
ConvertResult<Conv::s> FormatConvertImpl(string_view v,
- const ConversionSpec &conv,
+ const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertStringArg(v, conv, sink)};
}
ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v,
- const ConversionSpec &conv,
+ const ConversionSpec conv,
FormatSinkImpl *sink) {
if (conv.conv().id() == ConversionChar::p)
return {FormatConvertImpl(VoidPtr(v), conv, sink).value};
@@ -283,7 +282,7 @@ ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v,
}
// ==================== Raw pointers ====================
-ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec &conv,
+ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv,
FormatSinkImpl *sink) {
if (conv.conv().id() != ConversionChar::p)
return {false};
@@ -295,104 +294,83 @@ ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec &conv,
}
// ==================== Floats ====================
-FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec &conv,
+FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertFloatArg(v, conv, sink)};
}
-FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec &conv,
+FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertFloatArg(v, conv, sink)};
}
FloatingConvertResult FormatConvertImpl(long double v,
- const ConversionSpec &conv,
+ const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertFloatArg(v, conv, sink)};
}
// ==================== Chars ====================
-IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec &conv,
+IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
IntegralConvertResult FormatConvertImpl(signed char v,
- const ConversionSpec &conv,
+ const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
IntegralConvertResult FormatConvertImpl(unsigned char v,
- const ConversionSpec &conv,
+ const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
// ==================== Ints ====================
IntegralConvertResult FormatConvertImpl(short v, // NOLINT
- const ConversionSpec &conv,
+ const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT
- const ConversionSpec &conv,
+ const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
-IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec &conv,
+IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
-IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec &conv,
+IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
IntegralConvertResult FormatConvertImpl(long v, // NOLINT
- const ConversionSpec &conv,
+ const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT
- const ConversionSpec &conv,
+ const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
IntegralConvertResult FormatConvertImpl(long long v, // NOLINT
- const ConversionSpec &conv,
+ const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT
- const ConversionSpec &conv,
+ const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
IntegralConvertResult FormatConvertImpl(absl::uint128 v,
- const ConversionSpec &conv,
+ const ConversionSpec conv,
FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
-template struct FormatArgImpl::TypedVTable<str_format_internal::VoidPtr>;
-
-template struct FormatArgImpl::TypedVTable<bool>;
-template struct FormatArgImpl::TypedVTable<char>;
-template struct FormatArgImpl::TypedVTable<signed char>;
-template struct FormatArgImpl::TypedVTable<unsigned char>;
-template struct FormatArgImpl::TypedVTable<short>; // NOLINT
-template struct FormatArgImpl::TypedVTable<unsigned short>; // NOLINT
-template struct FormatArgImpl::TypedVTable<int>;
-template struct FormatArgImpl::TypedVTable<unsigned>;
-template struct FormatArgImpl::TypedVTable<long>; // NOLINT
-template struct FormatArgImpl::TypedVTable<unsigned long>; // NOLINT
-template struct FormatArgImpl::TypedVTable<long long>; // NOLINT
-template struct FormatArgImpl::TypedVTable<unsigned long long>; // NOLINT
-template struct FormatArgImpl::TypedVTable<absl::uint128>;
-
-template struct FormatArgImpl::TypedVTable<float>;
-template struct FormatArgImpl::TypedVTable<double>;
-template struct FormatArgImpl::TypedVTable<long double>;
-
-template struct FormatArgImpl::TypedVTable<const char *>;
-template struct FormatArgImpl::TypedVTable<std::string>;
-template struct FormatArgImpl::TypedVTable<string_view>;
+ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_();
+
} // namespace str_format_internal
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
index a9562188..3376d48a 100644
--- a/absl/strings/internal/str_format/arg.h
+++ b/absl/strings/internal/str_format/arg.h
@@ -33,7 +33,7 @@ struct HasUserDefinedConvert : std::false_type {};
template <typename T>
struct HasUserDefinedConvert<
T, void_t<decltype(AbslFormatConvert(
- std::declval<const T&>(), std::declval<const ConversionSpec&>(),
+ std::declval<const T&>(), std::declval<ConversionSpec>(),
std::declval<FormatSink*>()))>> : std::true_type {};
template <typename T>
class StreamedWrapper;
@@ -50,25 +50,23 @@ struct VoidPtr {
: value(ptr ? reinterpret_cast<uintptr_t>(ptr) : 0) {}
uintptr_t value;
};
-ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec& conv,
+ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, ConversionSpec conv,
FormatSinkImpl* sink);
// Strings.
-ConvertResult<Conv::s> FormatConvertImpl(const std::string& v,
- const ConversionSpec& conv,
+ConvertResult<Conv::s> FormatConvertImpl(const std::string& v, ConversionSpec conv,
FormatSinkImpl* sink);
-ConvertResult<Conv::s> FormatConvertImpl(string_view v,
- const ConversionSpec& conv,
+ConvertResult<Conv::s> FormatConvertImpl(string_view v, ConversionSpec conv,
FormatSinkImpl* sink);
ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char* v,
- const ConversionSpec& conv,
+ ConversionSpec conv,
FormatSinkImpl* sink);
template <class AbslCord,
typename std::enable_if<
std::is_same<AbslCord, ::Cord>::value>::type* = nullptr,
class AbslCordReader = ::CordReader>
ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value,
- const ConversionSpec& conv,
+ ConversionSpec conv,
FormatSinkImpl* sink) {
if (conv.conv().id() != ConversionChar::s) return {false};
@@ -104,51 +102,48 @@ using IntegralConvertResult =
using FloatingConvertResult = ConvertResult<Conv::floating>;
// Floats.
-FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec& conv,
+FloatingConvertResult FormatConvertImpl(float v, ConversionSpec conv,
FormatSinkImpl* sink);
-FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec& conv,
+FloatingConvertResult FormatConvertImpl(double v, ConversionSpec conv,
FormatSinkImpl* sink);
-FloatingConvertResult FormatConvertImpl(long double v,
- const ConversionSpec& conv,
+FloatingConvertResult FormatConvertImpl(long double v, ConversionSpec conv,
FormatSinkImpl* sink);
// Chars.
-IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(char v, ConversionSpec conv,
FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(signed char v,
- const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(signed char v, ConversionSpec conv,
FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(unsigned char v,
- const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(unsigned char v, ConversionSpec conv,
FormatSinkImpl* sink);
// Ints.
IntegralConvertResult FormatConvertImpl(short v, // NOLINT
- const ConversionSpec& conv,
+ ConversionSpec conv,
FormatSinkImpl* sink);
IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT
- const ConversionSpec& conv,
+ ConversionSpec conv,
FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(int v, ConversionSpec conv,
FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(unsigned v, ConversionSpec conv,
FormatSinkImpl* sink);
IntegralConvertResult FormatConvertImpl(long v, // NOLINT
- const ConversionSpec& conv,
+ ConversionSpec conv,
FormatSinkImpl* sink);
IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT
- const ConversionSpec& conv,
+ ConversionSpec conv,
FormatSinkImpl* sink);
IntegralConvertResult FormatConvertImpl(long long v, // NOLINT
- const ConversionSpec& conv,
+ ConversionSpec conv,
FormatSinkImpl* sink);
IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT
- const ConversionSpec& conv,
+ ConversionSpec conv,
FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(uint128 v, const ConversionSpec& conv,
+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, const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(T v, ConversionSpec conv,
FormatSinkImpl* sink) {
return FormatConvertImpl(static_cast<int>(v), conv, sink);
}
@@ -159,11 +154,11 @@ template <typename T>
typename std::enable_if<std::is_enum<T>::value &&
!HasUserDefinedConvert<T>::value,
IntegralConvertResult>::type
-FormatConvertImpl(T v, const ConversionSpec& conv, FormatSinkImpl* sink);
+FormatConvertImpl(T v, ConversionSpec conv, FormatSinkImpl* sink);
template <typename T>
ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<T>& v,
- const ConversionSpec& conv,
+ ConversionSpec conv,
FormatSinkImpl* out) {
std::ostringstream oss;
oss << v.v_;
@@ -176,7 +171,7 @@ ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<T>& v,
struct FormatCountCaptureHelper {
template <class T = int>
static ConvertResult<Conv::n> ConvertHelper(const FormatCountCapture& v,
- const ConversionSpec& conv,
+ ConversionSpec conv,
FormatSinkImpl* sink) {
const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v;
@@ -189,7 +184,7 @@ struct FormatCountCaptureHelper {
template <class T = int>
ConvertResult<Conv::n> FormatConvertImpl(const FormatCountCapture& v,
- const ConversionSpec& conv,
+ ConversionSpec conv,
FormatSinkImpl* sink) {
return FormatCountCaptureHelper::ConvertHelper(v, conv, sink);
}
@@ -199,20 +194,20 @@ ConvertResult<Conv::n> FormatConvertImpl(const FormatCountCapture& v,
struct FormatArgImplFriend {
template <typename Arg>
static bool ToInt(Arg arg, int* out) {
- if (!arg.vtbl_->to_int) return false;
- *out = arg.vtbl_->to_int(arg.data_);
- return true;
+ // 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, const str_format_internal::ConversionSpec& conv,
+ static bool Convert(Arg arg, str_format_internal::ConversionSpec conv,
FormatSinkImpl* out) {
- return arg.vtbl_->convert(arg.data_, conv, out);
+ return arg.dispatcher_(arg.data_, conv, out);
}
template <typename Arg>
- static const void* GetVTablePtrForTest(Arg arg) {
- return arg.vtbl_;
+ static typename Arg::Dispatcher GetVTablePtrForTest(Arg arg) {
+ return arg.dispatcher_;
}
};
@@ -229,11 +224,7 @@ class FormatArgImpl {
char buf[kInlinedSpace];
};
- struct VTable {
- bool (*convert)(Data, const str_format_internal::ConversionSpec& conv,
- FormatSinkImpl* out);
- int (*to_int)(Data);
- };
+ using Dispatcher = bool (*)(Data, ConversionSpec, void* out);
template <typename T>
struct store_by_value
@@ -253,10 +244,6 @@ class FormatArgImpl {
: ByPointer))> {
};
- // An instance of an FormatArgImpl::VTable suitable for 'T'.
- template <typename T>
- struct TypedVTable;
-
// To reduce the number of vtables we will decay values before hand.
// Anything with a user-defined Convert will get its own vtable.
// For everything else:
@@ -338,7 +325,10 @@ class FormatArgImpl {
};
template <typename T>
- void Init(const T& value);
+ void Init(const T& value) {
+ data_ = Manager<T>::SetValue(value);
+ dispatcher_ = &Dispatch<T>;
+ }
template <typename T>
static int ToIntVal(const T& val) {
@@ -355,79 +345,75 @@ class FormatArgImpl {
return static_cast<int>(val);
}
- Data data_;
- const VTable* vtbl_;
-};
-
-template <typename T>
-struct FormatArgImpl::TypedVTable {
- private:
- static bool ConvertImpl(Data arg,
- const str_format_internal::ConversionSpec& conv,
- FormatSinkImpl* out) {
- return str_format_internal::FormatConvertImpl(Manager<T>::Value(arg), conv,
- out)
- .value;
+ template <typename 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 U = T, typename = void>
- struct ToIntImpl {
- static constexpr int (*value)(Data) = nullptr;
- };
+ 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 U>
- struct ToIntImpl<U,
- typename std::enable_if<std::is_integral<U>::value>::type> {
- static int Invoke(Data arg) { return ToIntVal(Manager<T>::Value(arg)); }
- static constexpr int (*value)(Data) = &Invoke;
- };
+ template <typename T>
+ static bool ToInt(Data, int*, std::false_type, std::false_type) {
+ return false;
+ }
- template <typename U>
- struct ToIntImpl<U, typename std::enable_if<std::is_enum<U>::value>::type> {
- static int Invoke(Data arg) {
- return ToIntVal(static_cast<typename std::underlying_type<T>::type>(
- Manager<T>::Value(arg)));
+ 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>());
}
- static constexpr int (*value)(Data) = &Invoke;
- };
- public:
- static constexpr VTable value{&ConvertImpl, ToIntImpl<>::value};
-};
+ return str_format_internal::FormatConvertImpl(
+ Manager<T>::Value(arg), spec, static_cast<FormatSinkImpl*>(out))
+ .value;
+ }
-template <typename T>
-constexpr FormatArgImpl::VTable FormatArgImpl::TypedVTable<T>::value;
+ Data data_;
+ Dispatcher dispatcher_;
+};
-template <typename T>
-void FormatArgImpl::Init(const T& value) {
- data_ = Manager<T>::SetValue(value);
- vtbl_ = &TypedVTable<T>::value;
-}
+#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);
-extern template struct FormatArgImpl::TypedVTable<str_format_internal::VoidPtr>;
-
-extern template struct FormatArgImpl::TypedVTable<bool>;
-extern template struct FormatArgImpl::TypedVTable<char>;
-extern template struct FormatArgImpl::TypedVTable<signed char>;
-extern template struct FormatArgImpl::TypedVTable<unsigned char>;
-extern template struct FormatArgImpl::TypedVTable<short>; // NOLINT
-extern template struct FormatArgImpl::TypedVTable<unsigned short>; // NOLINT
-extern template struct FormatArgImpl::TypedVTable<int>;
-extern template struct FormatArgImpl::TypedVTable<unsigned>;
-extern template struct FormatArgImpl::TypedVTable<long>; // NOLINT
-extern template struct FormatArgImpl::TypedVTable<unsigned long>; // NOLINT
-extern template struct FormatArgImpl::TypedVTable<long long>; // NOLINT
-extern template struct FormatArgImpl::TypedVTable<
- unsigned long long>; // NOLINT
-extern template struct FormatArgImpl::TypedVTable<uint128>;
-
-extern template struct FormatArgImpl::TypedVTable<float>;
-extern template struct FormatArgImpl::TypedVTable<double>;
-extern template struct FormatArgImpl::TypedVTable<long double>;
-
-extern template struct FormatArgImpl::TypedVTable<const char*>;
-extern template struct FormatArgImpl::TypedVTable<std::string>;
-extern template struct FormatArgImpl::TypedVTable<string_view>;
} // namespace str_format_internal
} // namespace absl
diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h
index a503b19b..1b52df9c 100644
--- a/absl/strings/internal/str_format/bind.h
+++ b/absl/strings/internal/str_format/bind.h
@@ -186,7 +186,7 @@ class StreamedWrapper {
private:
template <typename S>
friend ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<S>& v,
- const ConversionSpec& conv,
+ ConversionSpec conv,
FormatSinkImpl* out);
const T& v_;
};
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h
index f43195c1..11b996ae 100644
--- a/absl/strings/internal/str_format/extension.h
+++ b/absl/strings/internal/str_format/extension.h
@@ -18,6 +18,7 @@
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
#include <limits.h>
+#include <cstddef>
#include <cstring>
#include <ostream>
@@ -307,7 +308,12 @@ class ConversionSpec {
public:
Flags flags() const { return flags_; }
LengthMod length_mod() const { return length_mod_; }
- ConversionChar conv() const { return conv_; }
+ 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.
@@ -324,9 +330,9 @@ class ConversionSpec {
void set_left(bool b) { flags_.left = b; }
private:
+ ConversionChar conv_;
Flags flags_;
LengthMod length_mod_;
- ConversionChar conv_;
int width_;
int precision_;
};
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc
index 9a6576dc..aa14e211 100644
--- a/absl/strings/str_format_test.cc
+++ b/absl/strings/str_format_test.cc
@@ -601,3 +601,39 @@ TEST_F(ParsedFormatTest, RegressionMixPositional) {
} // namespace
} // namespace absl
+
+// Some codegen thunks that we can use to easily dump the generated assembly for
+// different StrFormat calls.
+
+inline std::string CodegenAbslStrFormatInt(int i) {
+ return absl::StrFormat("%d", i);
+}
+
+inline std::string CodegenAbslStrFormatIntStringInt64(int i, const std::string& s,
+ int64_t i64) {
+ return absl::StrFormat("%d %s %d", i, s, i64);
+}
+
+inline void CodegenAbslStrAppendFormatInt(std::string* out, int i) {
+ absl::StrAppendFormat(out, "%d", i);
+}
+
+inline void CodegenAbslStrAppendFormatIntStringInt64(std::string* out, int i,
+ const std::string& s,
+ int64_t i64) {
+ absl::StrAppendFormat(out, "%d %s %d", i, s, i64);
+}
+
+auto absl_internal_str_format_force_codegen_funcs = std::make_tuple(
+ CodegenAbslStrFormatInt, CodegenAbslStrFormatIntStringInt64,
+ CodegenAbslStrAppendFormatInt, CodegenAbslStrAppendFormatIntStringInt64);
+
+bool absl_internal_str_format_force_codegen_always_false;
+// Force the compiler to generate the functions by making it look like we
+// escape the function pointers.
+// It can't statically know that
+// absl_internal_str_format_force_codegen_always_false is not changed by someone
+// else.
+bool absl_internal_str_format_force_codegen =
+ absl_internal_str_format_force_codegen_always_false &&
+ printf("%p", &absl_internal_str_format_force_codegen_funcs) == 0;