about summary refs log tree commit diff
path: root/absl
diff options
context:
space:
mode:
Diffstat (limited to 'absl')
-rw-r--r--absl/base/call_once.h5
-rw-r--r--absl/meta/type_traits.h4
-rw-r--r--absl/strings/str_cat.cc31
-rw-r--r--absl/strings/str_cat.h35
-rw-r--r--absl/strings/str_cat_test.cc83
5 files changed, 140 insertions, 18 deletions
diff --git a/absl/base/call_once.h b/absl/base/call_once.h
index 5d823a11a013..25c783e5f3e0 100644
--- a/absl/base/call_once.h
+++ b/absl/base/call_once.h
@@ -134,7 +134,10 @@ enum {
   kOnceInit = 0,
   kOnceRunning = 0x65C2937B,
   kOnceWaiter = 0x05A308D2,
-  kOnceDone = 0x3F2D8AB0,
+  // A very small constant is chosen for kOnceDone so that it fit in a single
+  // compare with immediate instruction for most common ISAs.  This is verified
+  // for x86, POWER and ARM.
+  kOnceDone = 221,    // Random Number
 };
 
 template <typename Callable, typename... Args>
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h
index f36a59aa7777..ac5d8e1ca7f8 100644
--- a/absl/meta/type_traits.h
+++ b/absl/meta/type_traits.h
@@ -150,6 +150,7 @@ struct is_trivially_destructible
     : std::integral_constant<bool, __has_trivial_destructor(T) &&
                                    std::is_destructible<T>::value> {
 #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
+ private:
   static constexpr bool compliant = std::is_trivially_destructible<T>::value ==
                                     is_trivially_destructible::value;
   static_assert(compliant || std::is_trivially_destructible<T>::value,
@@ -199,6 +200,7 @@ struct is_trivially_default_constructible
                                    std::is_default_constructible<T>::value &&
                                    is_trivially_destructible<T>::value> {
 #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+ private:
   static constexpr bool compliant =
       std::is_trivially_default_constructible<T>::value ==
       is_trivially_default_constructible::value;
@@ -230,6 +232,7 @@ struct is_trivially_copy_constructible
                                    std::is_copy_constructible<T>::value &&
                                    is_trivially_destructible<T>::value> {
 #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+ private:
   static constexpr bool compliant =
       std::is_trivially_copy_constructible<T>::value ==
       is_trivially_copy_constructible::value;
@@ -262,6 +265,7 @@ struct is_trivially_copy_assignable
     : std::integral_constant<bool, __has_trivial_assign(T) &&
                                    std::is_copy_assignable<T>::value> {
 #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
+ private:
   static constexpr bool compliant =
       std::is_trivially_copy_assignable<T>::value ==
       is_trivially_copy_assignable::value;
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc
index 99eb28908c19..3fe8c95eca9e 100644
--- a/absl/strings/str_cat.cc
+++ b/absl/strings/str_cat.cc
@@ -45,6 +45,37 @@ AlphaNum::AlphaNum(Hex hex) {
   piece_ = absl::string_view(beg, end - beg);
 }
 
+AlphaNum::AlphaNum(Dec dec) {
+  assert(dec.width <= numbers_internal::kFastToBufferSize);
+  char* const end = &digits_[numbers_internal::kFastToBufferSize];
+  char* const minfill = end - dec.width;
+  char* writer = end;
+  uint64_t value = dec.value;
+  bool neg = dec.neg;
+  while (value > 9) {
+    *--writer = '0' + (value % 10);
+    value /= 10;
+  }
+  *--writer = '0' + value;
+  if (neg) *--writer = '-';
+
+  ptrdiff_t fillers = writer - minfill;
+  if (fillers > 0) {
+    // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
+    // But...: if the fill character is '0', then it's <+/-><fill><digits>
+    bool add_sign_again = false;
+    if (neg && dec.fill == '0') {  // If filling with '0',
+      ++writer;                    // ignore the sign we just added
+      add_sign_again = true;       // and re-add the sign later.
+    }
+    writer -= fillers;
+    std::fill_n(writer, fillers, dec.fill);
+    if (add_sign_again) *--writer = '-';
+  }
+
+  piece_ = absl::string_view(writer, end - writer);
+}
+
 // ----------------------------------------------------------------------
 // StrCat()
 //    This merges the given strings or integers, with no delimiter.  This
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h
index 1cf7b11fec71..0c33b6ccd243 100644
--- a/absl/strings/str_cat.h
+++ b/absl/strings/str_cat.h
@@ -76,10 +76,10 @@ struct AlphaNumBuffer {
 
 }  // namespace strings_internal
 
-// Enum that specifies the number of significant digits to return in a `Hex`
-// conversion and fill character to use. A `kZeroPad2` value, for example, would
-// produce hexadecimal strings such as "0A","0F" and 'kSpacePad5' value would
-// produce hexadecimal strings such as "    A","    F".
+// Enum that specifies the number of significant digits to return in a `Hex` or
+// `Dec` conversion and fill character to use. A `kZeroPad2` value, for example,
+// would produce hexadecimal strings such as "0A","0F" and a 'kSpacePad5' value
+// would produce hexadecimal strings such as "    A","    F".
 enum PadSpec {
   kNoPad = 1,
   kZeroPad2,
@@ -154,6 +154,32 @@ 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
+// integer conversion, so use it only if you need padding.
+struct Dec {
+  uint64_t value;
+  uint8_t width;
+  char fill;
+  bool neg;
+
+  template <typename Int>
+  explicit Dec(Int v, PadSpec spec = absl::kNoPad,
+               typename std::enable_if<(sizeof(Int) <= 8)>::type* = nullptr)
+      : value(v >= 0 ? static_cast<uint64_t>(v)
+                     : uint64_t{0} - static_cast<uint64_t>(v)),
+        width(spec == absl::kNoPad
+                  ? 1
+                  : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
+                                             : spec - absl::kZeroPad2 + 2),
+        fill(spec >= absl::kSpacePad2 ? ' ' : '0'),
+        neg(v < 0) {}
+};
+
+// -----------------------------------------------------------------------------
 // AlphaNum
 // -----------------------------------------------------------------------------
 //
@@ -191,6 +217,7 @@ class AlphaNum {
       : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
 
   AlphaNum(Hex hex);  // NOLINT(runtime/explicit)
+  AlphaNum(Dec dec);  // NOLINT(runtime/explicit)
 
   template <size_t size>
   AlphaNum(  // NOLINT(runtime/explicit)
diff --git a/absl/strings/str_cat_test.cc b/absl/strings/str_cat_test.cc
index dd063b015b63..710113f83b9f 100644
--- a/absl/strings/str_cat_test.cc
+++ b/absl/strings/str_cat_test.cc
@@ -431,28 +431,85 @@ void CheckHex(IntType v, const char* nopad_format, const char* zeropad_format,
   }
 }
 
-void CheckHex64(uint64_t v) {
-  unsigned long long llv = v;  // NOLINT(runtime/int)
+template <typename IntType>
+void CheckDec(IntType v, const char* nopad_format, const char* zeropad_format,
+              const char* spacepad_format) {
+  char expected[256];
+
+  std::string actual = absl::StrCat(absl::Dec(v, absl::kNoPad));
+  snprintf(expected, sizeof(expected), nopad_format, v);
+  EXPECT_EQ(expected, actual) << " decimal value " << v;
+
+  for (int spec = absl::kZeroPad2; spec <= absl::kZeroPad16; ++spec) {
+    std::string actual =
+        absl::StrCat(absl::Dec(v, static_cast<absl::PadSpec>(spec)));
+    snprintf(expected, sizeof(expected), zeropad_format,
+             spec - absl::kZeroPad2 + 2, v);
+    EXPECT_EQ(expected, actual)
+        << " decimal value " << v << " format '" << zeropad_format
+        << "' digits " << (spec - absl::kZeroPad2 + 2);
+  }
+
+  for (int spec = absl::kSpacePad2; spec <= absl::kSpacePad16; ++spec) {
+    std::string actual =
+        absl::StrCat(absl::Dec(v, static_cast<absl::PadSpec>(spec)));
+    snprintf(expected, sizeof(expected), spacepad_format,
+             spec - absl::kSpacePad2 + 2, v);
+    EXPECT_EQ(expected, actual)
+        << " decimal value " << v << " format '" << spacepad_format
+        << "' digits " << (spec - absl::kSpacePad2 + 2);
+  }
+}
+
+void CheckHexDec64(uint64_t v) {
+  unsigned long long ullv = v;  // NOLINT(runtime/int)
+
+  CheckHex(ullv, "%llx", "%0*llx", "%*llx");
+  CheckDec(ullv, "%llu", "%0*llu", "%*llu");
+
+  long long llv = static_cast<long long>(ullv);  // NOLINT(runtime/int)
+  CheckDec(llv, "%lld", "%0*lld", "%*lld");
+}
 
-  CheckHex(llv, "%llx", "%0*llx", "%*llx");
+void CheckHexDec32(uint32_t uv) {
+  CheckHex(uv, "%x", "%0*x", "%*x");
+  CheckDec(uv, "%u", "%0*u", "%*u");
+  int32_t v = static_cast<int32_t>(uv);
+  CheckDec(v, "%d", "%0*d", "%*d");
 }
 
-template <typename Int32Type>
-void CheckHex32(Int32Type v) {
-  CheckHex(v, "%x", "%0*x", "%*x");
+void CheckAll(uint64_t v) {
+  CheckHexDec64(v);
+  CheckHexDec32(static_cast<uint32_t>(v));
 }
 
 void TestFastPrints() {
-  // Test min int to make sure that works
+  // Test all small ints; there aren't many and they're common.
   for (int i = 0; i < 10000; i++) {
-    CheckHex64(i);
-    CheckHex32(static_cast<uint32_t>(i));
-    CheckHex32(i);
-    CheckHex32(-i);
+    CheckAll(i);
   }
 
-  CheckHex64(uint64_t{0x123456789abcdef0});
-  CheckHex32(0x12345678U);
+  CheckAll(std::numeric_limits<uint64_t>::max());
+  CheckAll(std::numeric_limits<uint64_t>::max() - 1);
+  CheckAll(std::numeric_limits<int64_t>::min());
+  CheckAll(std::numeric_limits<int64_t>::min() + 1);
+  CheckAll(std::numeric_limits<uint32_t>::max());
+  CheckAll(std::numeric_limits<uint32_t>::max() - 1);
+  CheckAll(std::numeric_limits<int32_t>::min());
+  CheckAll(std::numeric_limits<int32_t>::min() + 1);
+  CheckAll(999999999);              // fits in 32 bits
+  CheckAll(1000000000);             // fits in 32 bits
+  CheckAll(9999999999);             // doesn't fit in 32 bits
+  CheckAll(10000000000);            // doesn't fit in 32 bits
+  CheckAll(999999999999999999);     // fits in signed 64-bit
+  CheckAll(9999999999999999999u);   // fits in unsigned 64-bit, but not signed.
+  CheckAll(1000000000000000000);    // fits in signed 64-bit
+  CheckAll(10000000000000000000u);  // fits in unsigned 64-bit, but not signed.
+
+  CheckAll(999999999876543210);    // check all decimal digits, signed
+  CheckAll(9999999999876543210u);  // check all decimal digits, unsigned.
+  CheckAll(0x123456789abcdef0);    // check all hex digits
+  CheckAll(0x12345678);
 
   int8_t minus_one_8bit = -1;
   EXPECT_EQ("ff", absl::StrCat(absl::Hex(minus_one_8bit)));