diff options
36 files changed, 1040 insertions, 436 deletions
@@ -28,13 +28,11 @@ http_archive( ) # RE2 (the regular expression library used by GoogleTest) -# Note this must use a commit from the `abseil` branch of the RE2 project. -# https://github.com/google/re2/tree/abseil http_archive( name = "com_googlesource_code_re2", - sha256 = "0a890c2aa0bb05b2ce906a15efb520d0f5ad4c7d37b8db959c43772802991887", - strip_prefix = "re2-a427f10b9fb4622dd6d8643032600aa1b50fbd12", - urls = ["https://github.com/google/re2/archive/a427f10b9fb4622dd6d8643032600aa1b50fbd12.zip"], # 2022-06-09 + sha256 = "1726508efc93a50854c92e3f7ac66eb28f0e57652e413f11d7c1e28f97d997ba", + strip_prefix = "re2-03da4fc0857c285e3a26782f6bc8931c4c950df4", + urls = ["https://github.com/google/re2/archive/03da4fc0857c285e3a26782f6bc8931c4c950df4.zip"], # 2023-06-01 ) # Google benchmark. diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 34a35538..cb3f367f 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -695,7 +695,7 @@ // ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING // Baz ComputeBazFromFoo(Foo f); // ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING -#ifdef __GNUC__ +#if defined(__GNUC__) || defined(__clang__) // Clang also supports these GCC pragmas. #define ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING \ _Pragma("GCC diagnostic push") \ @@ -705,7 +705,7 @@ #else #define ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING #define ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING -#endif // __GNUC__ +#endif // defined(__GNUC__) || defined(__clang__) // ABSL_CONST_INIT // diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h index e8765254..3f852d31 100644 --- a/absl/base/internal/raw_logging.h +++ b/absl/base/internal/raw_logging.h @@ -129,7 +129,7 @@ void RawLog(absl::LogSeverity severity, const char* file, int line, const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5); // Writes the provided buffer directly to stderr, in a signal-safe, low-level -// manner. +// manner. Preserves errno. void AsyncSignalSafeWriteToStderr(const char* s, size_t len); // compile-time function to get the "base" filename, that is, the part of diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc index 8429fb90..7de8ead2 100644 --- a/absl/base/internal/sysinfo.cc +++ b/absl/base/internal/sysinfo.cc @@ -41,6 +41,7 @@ #include <string.h> #include <cassert> +#include <cerrno> #include <cstdint> #include <cstdio> #include <cstdlib> @@ -225,8 +226,8 @@ static int64_t ReadMonotonicClockNanos() { int rc = clock_gettime(CLOCK_MONOTONIC, &t); #endif if (rc != 0) { - perror("clock_gettime() failed"); - abort(); + ABSL_INTERNAL_LOG( + FATAL, "clock_gettime() failed: (" + std::to_string(errno) + ")"); } return int64_t{t.tv_sec} * 1000000000 + t.tv_nsec; } @@ -414,82 +415,24 @@ pid_t GetTID() { return tid; } -#else +#elif defined(__APPLE__) -// Fallback implementation of GetTID using pthread_getspecific. -ABSL_CONST_INIT static once_flag tid_once; -ABSL_CONST_INIT static pthread_key_t tid_key; -ABSL_CONST_INIT static absl::base_internal::SpinLock tid_lock( - absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); - -// We set a bit per thread in this array to indicate that an ID is in -// use. ID 0 is unused because it is the default value returned by -// pthread_getspecific(). -ABSL_CONST_INIT static std::vector<uint32_t> *tid_array - ABSL_GUARDED_BY(tid_lock) = nullptr; -static constexpr int kBitsPerWord = 32; // tid_array is uint32_t. - -// Returns the TID to tid_array. -static void FreeTID(void *v) { - intptr_t tid = reinterpret_cast<intptr_t>(v); - intptr_t word = tid / kBitsPerWord; - uint32_t mask = ~(1u << (tid % kBitsPerWord)); - absl::base_internal::SpinLockHolder lock(&tid_lock); - assert(0 <= word && static_cast<size_t>(word) < tid_array->size()); - (*tid_array)[static_cast<size_t>(word)] &= mask; +pid_t GetTID() { + uint64_t tid; + // `nullptr` here implies this thread. This only fails if the specified + // thread is invalid or the pointer-to-tid is null, so we needn't worry about + // it. + pthread_threadid_np(nullptr, &tid); + return static_cast<pid_t>(tid); } -static void InitGetTID() { - if (pthread_key_create(&tid_key, FreeTID) != 0) { - // The logging system calls GetTID() so it can't be used here. - perror("pthread_key_create failed"); - abort(); - } - - // Initialize tid_array. - absl::base_internal::SpinLockHolder lock(&tid_lock); - tid_array = new std::vector<uint32_t>(1); - (*tid_array)[0] = 1; // ID 0 is never-allocated. -} +#else -// Return a per-thread small integer ID from pthread's thread-specific data. +// Fallback implementation of `GetTID` using `pthread_self`. pid_t GetTID() { - absl::call_once(tid_once, InitGetTID); - - intptr_t tid = reinterpret_cast<intptr_t>(pthread_getspecific(tid_key)); - if (tid != 0) { - return static_cast<pid_t>(tid); - } - - int bit; // tid_array[word] = 1u << bit; - size_t word; - { - // Search for the first unused ID. - absl::base_internal::SpinLockHolder lock(&tid_lock); - // First search for a word in the array that is not all ones. - word = 0; - while (word < tid_array->size() && ~(*tid_array)[word] == 0) { - ++word; - } - if (word == tid_array->size()) { - tid_array->push_back(0); // No space left, add kBitsPerWord more IDs. - } - // Search for a zero bit in the word. - bit = 0; - while (bit < kBitsPerWord && (((*tid_array)[word] >> bit) & 1) != 0) { - ++bit; - } - tid = - static_cast<intptr_t>((word * kBitsPerWord) + static_cast<size_t>(bit)); - (*tid_array)[word] |= 1u << bit; // Mark the TID as allocated. - } - - if (pthread_setspecific(tid_key, reinterpret_cast<void *>(tid)) != 0) { - perror("pthread_setspecific failed"); - abort(); - } - - return static_cast<pid_t>(tid); + // `pthread_t` need not be arithmetic per POSIX; platforms where it isn't + // should be handled above. + return static_cast<pid_t>(pthread_self()); } #endif diff --git a/absl/base/internal/unscaledcycleclock.cc b/absl/base/internal/unscaledcycleclock.cc index 128dc52f..05e0e7ba 100644 --- a/absl/base/internal/unscaledcycleclock.cc +++ b/absl/base/internal/unscaledcycleclock.cc @@ -75,8 +75,8 @@ int64_t UnscaledCycleClock::Now() { "mftb %[lo32]\n" "mftbu %[tmp]\n" "cmpw %[tmp],%[hi32]\n" - "bne $-16\n" // retry on failure - : [ hi32 ] "=r"(tbu), [ lo32 ] "=r"(tbl), [ tmp ] "=r"(tmp)); + "bne $-16\n" // Retry on failure. + : [hi32] "=r"(tbu), [lo32] "=r"(tbl), [tmp] "=r"(tmp)); return (static_cast<int64_t>(tbu) << 32) | tbl; #endif #endif diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h index e99137a4..9f1c813d 100644 --- a/absl/container/fixed_array.h +++ b/absl/container/fixed_array.h @@ -117,14 +117,20 @@ class FixedArray { (N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type) : static_cast<size_type>(N)); - FixedArray( - const FixedArray& other, - const allocator_type& a = allocator_type()) noexcept(NoexceptCopyable()) + FixedArray(const FixedArray& other) noexcept(NoexceptCopyable()) + : FixedArray(other, + AllocatorTraits::select_on_container_copy_construction( + other.storage_.alloc())) {} + + FixedArray(const FixedArray& other, + const allocator_type& a) noexcept(NoexceptCopyable()) : FixedArray(other.begin(), other.end(), a) {} - FixedArray( - FixedArray&& other, - const allocator_type& a = allocator_type()) noexcept(NoexceptMovable()) + FixedArray(FixedArray&& other) noexcept(NoexceptMovable()) + : FixedArray(std::move(other), other.storage_.alloc()) {} + + FixedArray(FixedArray&& other, + const allocator_type& a) noexcept(NoexceptMovable()) : FixedArray(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end()), a) {} @@ -480,6 +486,9 @@ class FixedArray { StorageElement* begin() const { return data_; } StorageElement* end() const { return begin() + size(); } allocator_type& alloc() { return size_alloc_.template get<1>(); } + const allocator_type& alloc() const { + return size_alloc_.template get<1>(); + } private: static bool UsingInlinedStorage(size_type n) { diff --git a/absl/container/fixed_array_test.cc b/absl/container/fixed_array_test.cc index 49598e7a..9dbf2a84 100644 --- a/absl/container/fixed_array_test.cc +++ b/absl/container/fixed_array_test.cc @@ -768,6 +768,22 @@ TEST(AllocatorSupportTest, SizeValAllocConstructor) { } } +TEST(AllocatorSupportTest, PropagatesStatefulAllocator) { + constexpr size_t inlined_size = 4; + using Alloc = absl::container_internal::CountingAllocator<int>; + using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>; + + auto len = inlined_size * 2; + auto val = 0; + int64_t allocated = 0; + AllocFxdArr arr(len, val, Alloc(&allocated)); + + EXPECT_EQ(allocated, len * sizeof(int)); + + AllocFxdArr copy = arr; + EXPECT_EQ(allocated, len * sizeof(int) * 2); +} + #ifdef ABSL_HAVE_ADDRESS_SANITIZER TEST(FixedArrayTest, AddressSanitizerAnnotations1) { absl::FixedArray<int, 32> a(10); diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc index b91d5a47..1ccee1ed 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc @@ -107,21 +107,22 @@ FindInfo find_first_non_full_outofline(const CommonFields& common, return find_first_non_full(common, hash); } -// Return address of the ith slot in slots where each slot occupies slot_size. +// Returns the address of the ith slot in slots where each slot occupies +// slot_size. static inline void* SlotAddress(void* slot_array, size_t slot, size_t slot_size) { return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot_array) + (slot * slot_size)); } -// Return the address of the slot just after slot assuming each slot -// has the specified size. +// Returns the address of the slot just after slot assuming each slot has the +// specified size. static inline void* NextSlot(void* slot, size_t slot_size) { return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot) + slot_size); } -// Return the address of the slot just before slot assuming each slot -// has the specified size. +// Returns the address of the slot just before slot assuming each slot has the +// specified size. static inline void* PrevSlot(void* slot, size_t slot_size) { return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot) - slot_size); } diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index df7ff793..2880af70 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -376,12 +376,12 @@ class NonIterableBitMask { return static_cast<uint32_t>((bit_width(mask_) - 1) >> Shift); } - // Return the number of trailing zero *abstract* bits. + // Returns the number of trailing zero *abstract* bits. uint32_t TrailingZeros() const { return container_internal::TrailingZeros(mask_) >> Shift; } - // Return the number of leading zero *abstract* bits. + // Returns the number of leading zero *abstract* bits. uint32_t LeadingZeros() const { constexpr int total_significant_bits = SignificantBits << Shift; constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits; @@ -961,7 +961,7 @@ class CommonFields : public CommonFieldsGenerationInfo { compressed_tuple_{0u, HashtablezInfoHandle{}}; }; -// Returns he number of "cloned control bytes". +// Returns the number of "cloned control bytes". // // This is the number of control bytes that are present both at the beginning // of the control byte array and at the end, such that we can create a @@ -1361,7 +1361,7 @@ ABSL_ATTRIBUTE_NOINLINE void InitializeSlots(CommonFields& c, Alloc alloc) { struct PolicyFunctions { size_t slot_size; - // Return the hash of the pointed-to slot. + // Returns the hash of the pointed-to slot. size_t (*hash_slot)(void* set, void* slot); // Transfer the contents of src_slot to dst_slot. @@ -1644,9 +1644,9 @@ class raw_hash_set { // Note: can't use `= default` due to non-default noexcept (causes // problems for some compilers). NOLINTNEXTLINE raw_hash_set() noexcept( - std::is_nothrow_default_constructible<hasher>::value&& - std::is_nothrow_default_constructible<key_equal>::value&& - std::is_nothrow_default_constructible<allocator_type>::value) {} + std::is_nothrow_default_constructible<hasher>::value && + std::is_nothrow_default_constructible<key_equal>::value && + std::is_nothrow_default_constructible<allocator_type>::value) {} ABSL_ATTRIBUTE_NOINLINE explicit raw_hash_set( size_t bucket_count, const hasher& hash = hasher(), @@ -1772,9 +1772,9 @@ class raw_hash_set { } ABSL_ATTRIBUTE_NOINLINE raw_hash_set(raw_hash_set&& that) noexcept( - std::is_nothrow_copy_constructible<hasher>::value&& - std::is_nothrow_copy_constructible<key_equal>::value&& - std::is_nothrow_copy_constructible<allocator_type>::value) + std::is_nothrow_copy_constructible<hasher>::value && + std::is_nothrow_copy_constructible<key_equal>::value && + std::is_nothrow_copy_constructible<allocator_type>::value) : // Hash, equality and allocator are copied instead of moved because // `that` must be left valid. If Hash is std::function<Key>, moving it // would create a nullptr functor that cannot be called. @@ -1803,9 +1803,9 @@ class raw_hash_set { } raw_hash_set& operator=(raw_hash_set&& that) noexcept( - absl::allocator_traits<allocator_type>::is_always_equal::value&& - std::is_nothrow_move_assignable<hasher>::value&& - std::is_nothrow_move_assignable<key_equal>::value) { + absl::allocator_traits<allocator_type>::is_always_equal::value && + std::is_nothrow_move_assignable<hasher>::value && + std::is_nothrow_move_assignable<key_equal>::value) { // TODO(sbenza): We should only use the operations from the noexcept clause // to make sure we actually adhere to that contract. // NOLINTNEXTLINE: not returning *this for performance. diff --git a/absl/copts/AbseilConfigureCopts.cmake b/absl/copts/AbseilConfigureCopts.cmake index 8209b262..3f737c81 100644 --- a/absl/copts/AbseilConfigureCopts.cmake +++ b/absl/copts/AbseilConfigureCopts.cmake @@ -83,6 +83,16 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") # MATCHES so we get both Clang an set(ABSL_DEFAULT_COPTS "${ABSL_LLVM_FLAGS}") set(ABSL_TEST_COPTS "${ABSL_LLVM_TEST_FLAGS}") endif() +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM") + # IntelLLVM is similar to Clang, with some additional flags. + if(MSVC) + # clang-cl is half MSVC, half LLVM + set(ABSL_DEFAULT_COPTS "${ABSL_CLANG_CL_FLAGS}") + set(ABSL_TEST_COPTS "${ABSL_CLANG_CL_TEST_FLAGS}") + else() + set(ABSL_DEFAULT_COPTS "${ABSL_LLVM_FLAGS}") + set(ABSL_TEST_COPTS "${ABSL_LLVM_TEST_FLAGS}") + endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") set(ABSL_DEFAULT_COPTS "${ABSL_MSVC_FLAGS}") set(ABSL_TEST_COPTS "${ABSL_MSVC_TEST_FLAGS}") diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc index 9f399d02..0db2a896 100644 --- a/absl/debugging/failure_signal_handler.cc +++ b/absl/debugging/failure_signal_handler.cc @@ -236,10 +236,6 @@ static void InstallOneFailureHandler(FailureSignalData* data, #endif -static void WriteToStderr(const char* data) { - absl::raw_log_internal::AsyncSignalSafeWriteToStderr(data, strlen(data)); -} - static void WriteSignalMessage(int signo, int cpu, void (*writerfn)(const char*)) { char buf[96]; @@ -380,7 +376,11 @@ static void AbslFailureSignalHandler(int signo, siginfo_t*, void* ucontext) { #endif // First write to stderr. - WriteFailureInfo(signo, ucontext, my_cpu, WriteToStderr); + WriteFailureInfo( + signo, ucontext, my_cpu, +[](const char* data) { + absl::raw_log_internal::AsyncSignalSafeWriteToStderr(data, + strlen(data)); + }); // Riskier code (because it is less likely to be async-signal-safe) // goes after this point. diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 627f453a..583e6d94 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -99,6 +99,7 @@ cc_library( "//absl/base:config", "//absl/base:core_headers", "//absl/base:log_severity", + "//absl/numeric:int128", "//absl/strings", "//absl/strings:str_format", "//absl/types:optional", diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index b9d3b97b..6525eb2a 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt @@ -87,6 +87,7 @@ absl_cc_library( absl::config absl::core_headers absl::log_severity + absl::int128 absl::optional absl::strings absl::str_format diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc index 6a56fce9..13852e14 100644 --- a/absl/flags/internal/usage.cc +++ b/absl/flags/internal/usage.cc @@ -92,8 +92,16 @@ class XMLElement { case '>': out << ">"; break; + case '\n': + case '\v': + case '\f': + case '\t': + out << " "; + break; default: - out << c; + if (IsValidXmlCharacter(static_cast<unsigned char>(c))) { + out << c; + } break; } } @@ -102,6 +110,7 @@ class XMLElement { } private: + static bool IsValidXmlCharacter(unsigned char c) { return c >= 0x20; } absl::string_view tag_; absl::string_view txt_; }; diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc index c3ab4a42..6847386f 100644 --- a/absl/flags/internal/usage_test.cc +++ b/absl/flags/internal/usage_test.cc @@ -39,6 +39,8 @@ ABSL_FLAG(double, usage_reporting_test_flag_03, 1.03, "usage_reporting_test_flag_03 help message"); ABSL_FLAG(int64_t, usage_reporting_test_flag_04, 1000000000000004L, "usage_reporting_test_flag_04 help message"); +ABSL_FLAG(std::string, usage_reporting_test_flag_07, "\r\n\f\v\a\b\t ", + "usage_reporting_test_flag_07 help \r\n\f\v\a\b\t "); static const char kTestUsageMessage[] = "Custom usage message"; @@ -203,8 +205,12 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) { Some more help. Even more long long long long long long long long long long long long help - message.); default: ""; + message.); default: "";)" + + "\n --usage_reporting_test_flag_07 (usage_reporting_test_flag_07 " + "help\n\n \f\v\a\b ); default: \"\r\n\f\v\a\b\t \";\n" + R"( Try --helpfull to get a list of all flags or --help=substring shows help for flags which include specified substring in either in the name, or description or path. @@ -267,8 +273,9 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpshort) { std::stringstream test_buf; EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), flags::HelpMode::kShort); - EXPECT_EQ(test_buf.str(), - R"(usage_test: Custom usage message + EXPECT_EQ( + test_buf.str(), + R"(usage_test: Custom usage message Flags from absl/flags/internal/usage_test.cc: --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); @@ -285,8 +292,12 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpshort) { Some more help. Even more long long long long long long long long long long long long help - message.); default: ""; + message.); default: "";)" + + "\n --usage_reporting_test_flag_07 (usage_reporting_test_flag_07 " + "help\n\n \f\v\a\b ); default: \"\r\n\f\v\a\b\t \";\n" + R"( Try --helpfull to get a list of all flags or --help=substring shows help for flags which include specified substring in either in the name, or description or path. @@ -301,8 +312,9 @@ TEST_F(UsageReportingTest, TestUsageFlag_help_simple) { std::stringstream test_buf; EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), flags::HelpMode::kImportant); - EXPECT_EQ(test_buf.str(), - R"(usage_test: Custom usage message + EXPECT_EQ( + test_buf.str(), + R"(usage_test: Custom usage message Flags from absl/flags/internal/usage_test.cc: --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); @@ -319,8 +331,12 @@ TEST_F(UsageReportingTest, TestUsageFlag_help_simple) { Some more help. Even more long long long long long long long long long long long long help - message.); default: ""; + message.); default: "";)" + + "\n --usage_reporting_test_flag_07 (usage_reporting_test_flag_07 " + "help\n\n \f\v\a\b ); default: \"\r\n\f\v\a\b\t \";\n" + R"( Try --helpfull to get a list of all flags or --help=substring shows help for flags which include specified substring in either in the name, or description or path. @@ -361,8 +377,9 @@ TEST_F(UsageReportingTest, TestUsageFlag_help_multiple_flag) { std::stringstream test_buf; EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), flags::HelpMode::kMatch); - EXPECT_EQ(test_buf.str(), - R"(usage_test: Custom usage message + EXPECT_EQ( + test_buf.str(), + R"(usage_test: Custom usage message Flags from absl/flags/internal/usage_test.cc: --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); @@ -379,8 +396,12 @@ TEST_F(UsageReportingTest, TestUsageFlag_help_multiple_flag) { Some more help. Even more long long long long long long long long long long long long help - message.); default: ""; + message.); default: "";)" + + "\n --usage_reporting_test_flag_07 (usage_reporting_test_flag_07 " + "help\n\n \f\v\a\b ); default: \"\r\n\f\v\a\b\t \";\n" + R"( Try --helpfull to get a list of all flags or --help=substring shows help for flags which include specified substring in either in the name, or description or path. @@ -395,8 +416,9 @@ TEST_F(UsageReportingTest, TestUsageFlag_helppackage) { std::stringstream test_buf; EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), flags::HelpMode::kPackage); - EXPECT_EQ(test_buf.str(), - R"(usage_test: Custom usage message + EXPECT_EQ( + test_buf.str(), + R"(usage_test: Custom usage message Flags from absl/flags/internal/usage_test.cc: --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); @@ -413,8 +435,12 @@ TEST_F(UsageReportingTest, TestUsageFlag_helppackage) { Some more help. Even more long long long long long long long long long long long long help - message.); default: ""; + message.); default: "";)" + "\n --usage_reporting_test_flag_07 (usage_reporting_test_flag_07 " + "help\n\n \f\v\a\b ); default: \"\r\n\f\v\a\b\t \";\n" + + R"( Try --helpfull to get a list of all flags or --help=substring shows help for flags which include specified substring in either in the name, or description or path. @@ -471,8 +497,9 @@ path. std::stringstream test_buf_02; EXPECT_EQ(flags::HandleUsageFlags(test_buf_02, kTestUsageMessage), flags::HelpMode::kMatch); - EXPECT_EQ(test_buf_02.str(), - R"(usage_test: Custom usage message + EXPECT_EQ( + test_buf_02.str(), + R"(usage_test: Custom usage message Flags from absl/flags/internal/usage_test.cc: --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); @@ -489,8 +516,12 @@ path. Some more help. Even more long long long long long long long long long long long long help - message.); default: ""; + message.); default: "";)" + + "\n --usage_reporting_test_flag_07 (usage_reporting_test_flag_07 " + "help\n\n \f\v\a\b ); default: \"\r\n\f\v\a\b\t \";\n" + R"( Try --helpfull to get a list of all flags or --help=substring shows help for flags which include specified substring in either in the name, or description or path. diff --git a/absl/flags/marshalling.cc b/absl/flags/marshalling.cc index 81f9cebd..cf6312b1 100644 --- a/absl/flags/marshalling.cc +++ b/absl/flags/marshalling.cc @@ -26,6 +26,7 @@ #include "absl/base/config.h" #include "absl/base/log_severity.h" #include "absl/base/macros.h" +#include "absl/numeric/int128.h" #include "absl/strings/ascii.h" #include "absl/strings/match.h" #include "absl/strings/numbers.h" @@ -125,6 +126,32 @@ bool AbslParseFlag(absl::string_view text, unsigned long long* dst, return ParseFlagImpl(text, *dst); } +bool AbslParseFlag(absl::string_view text, absl::int128* dst, std::string*) { + text = absl::StripAsciiWhitespace(text); + + // check hex + int base = NumericBase(text); + if (!absl::numbers_internal::safe_strto128_base(text, dst, base)) { + return false; + } + + return base == 16 ? absl::SimpleHexAtoi(text, dst) + : absl::SimpleAtoi(text, dst); +} + +bool AbslParseFlag(absl::string_view text, absl::uint128* dst, std::string*) { + text = absl::StripAsciiWhitespace(text); + + // check hex + int base = NumericBase(text); + if (!absl::numbers_internal::safe_strtou128_base(text, dst, base)) { + return false; + } + + return base == 16 ? absl::SimpleHexAtoi(text, dst) + : absl::SimpleAtoi(text, dst); +} + // -------------------------------------------------------------------- // AbslParseFlag for floating point types. diff --git a/absl/flags/marshalling.h b/absl/flags/marshalling.h index 325e75e5..21d955d5 100644 --- a/absl/flags/marshalling.h +++ b/absl/flags/marshalling.h @@ -200,6 +200,7 @@ #define ABSL_FLAGS_MARSHALLING_H_ #include "absl/base/config.h" +#include "absl/numeric/int128.h" #if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL) #include <optional> @@ -233,6 +234,8 @@ bool AbslParseFlag(absl::string_view, unsigned long*, std::string*); // NOLINT bool AbslParseFlag(absl::string_view, long long*, std::string*); // NOLINT bool AbslParseFlag(absl::string_view, unsigned long long*, // NOLINT std::string*); +bool AbslParseFlag(absl::string_view, absl::int128*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, absl::uint128*, std::string*); // NOLINT bool AbslParseFlag(absl::string_view, float*, std::string*); bool AbslParseFlag(absl::string_view, double*, std::string*); bool AbslParseFlag(absl::string_view, std::string*, std::string*); diff --git a/absl/flags/marshalling_test.cc b/absl/flags/marshalling_test.cc index 7b6d2ad5..d996ca7f 100644 --- a/absl/flags/marshalling_test.cc +++ b/absl/flags/marshalling_test.cc @@ -455,6 +455,143 @@ TEST(MarshallingTest, TestUInt64Parsing) { // -------------------------------------------------------------------- +TEST(MarshallingTest, TestInt128Parsing) { + std::string err; + absl::int128 value; + + absl::int128 zero = 0; + absl::int128 one = 1; + absl::int128 neg_one = -1; + absl::int128 hundred = 100; + absl::int128 hundreds_val = 123; + absl::int128 neg_thousands_val = -98765; + absl::int128 pos_three = 3; + + // Decimal values. + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_EQ(value, zero); + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, one); + EXPECT_TRUE(absl::ParseFlag("-1", &value, &err)); + EXPECT_EQ(value, neg_one); + EXPECT_TRUE(absl::ParseFlag("123", &value, &err)); + EXPECT_EQ(value, hundreds_val); + EXPECT_TRUE(absl::ParseFlag("-98765", &value, &err)); + EXPECT_EQ(value, neg_thousands_val); + EXPECT_TRUE(absl::ParseFlag("+3", &value, &err)); + EXPECT_EQ(value, pos_three); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01", &value, &err)); + EXPECT_EQ(value, one); + EXPECT_TRUE(absl::ParseFlag("001", &value, &err)); + EXPECT_EQ(value, one); + EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err)); + EXPECT_EQ(value, hundred); + + absl::int128 sixteen = 16; + absl::int128 quintillion_val = 1152827684197027293; + absl::int128 quintillion_val2 = + absl::MakeInt128(0x000000000000fff, 0xFFFFFFFFFFFFFFF); + + // Hex values. + EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err)); + EXPECT_EQ(value, sixteen); + EXPECT_TRUE(absl::ParseFlag("0xFFFAAABBBCCCDDD", &value, &err)); + EXPECT_EQ(value, quintillion_val); + EXPECT_TRUE(absl::ParseFlag("0xFFF0FFFFFFFFFFFFFFF", &value, &err)); + EXPECT_EQ(value, quintillion_val2); + + // TODO(b/285183223): Add support for parsing negative hex representation + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("16 ", &value, &err)); + EXPECT_EQ(value, sixteen); + EXPECT_TRUE(absl::ParseFlag(" 16", &value, &err)); + EXPECT_EQ(value, sixteen); + EXPECT_TRUE(absl::ParseFlag(" 0100 ", &value, &err)); + EXPECT_EQ(value, hundred); + EXPECT_TRUE(absl::ParseFlag(" 0x7B ", &value, &err)); + EXPECT_EQ(value, hundreds_val); // =123 + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2U", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestUint128Parsing) { + std::string err; + absl::uint128 value; + + absl::uint128 zero = 0; + absl::uint128 one = 1; + absl::uint128 hundred = 100; + absl::uint128 hundreds_val = 123; + absl::uint128 pos_three = 3; + + // Decimal values. + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_EQ(value, zero); + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, one); + EXPECT_TRUE(absl::ParseFlag("123", &value, &err)); + EXPECT_EQ(value, hundreds_val); + EXPECT_TRUE(absl::ParseFlag("+3", &value, &err)); + EXPECT_EQ(value, pos_three); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01", &value, &err)); + EXPECT_EQ(value, one); + EXPECT_TRUE(absl::ParseFlag("001", &value, &err)); + EXPECT_EQ(value, one); + EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err)); + EXPECT_EQ(value, hundred); + + absl::uint128 sixteen = 16; + absl::uint128 quintillion_val = 1152827684197027293; + absl::uint128 quintillion_val2 = + absl::MakeInt128(0x000000000000fff, 0xFFFFFFFFFFFFFFF); + + // Hex values. + EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err)); + EXPECT_EQ(value, sixteen); + EXPECT_TRUE(absl::ParseFlag("0xFFFAAABBBCCCDDD", &value, &err)); + EXPECT_EQ(value, quintillion_val); + EXPECT_TRUE(absl::ParseFlag("0xFFF0FFFFFFFFFFFFFFF", &value, &err)); + EXPECT_EQ(value, quintillion_val2); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("16 ", &value, &err)); + EXPECT_EQ(value, sixteen); + EXPECT_TRUE(absl::ParseFlag(" 16", &value, &err)); + EXPECT_EQ(value, sixteen); + EXPECT_TRUE(absl::ParseFlag(" 0100 ", &value, &err)); + EXPECT_EQ(value, hundred); + EXPECT_TRUE(absl::ParseFlag(" 0x7B ", &value, &err)); + EXPECT_EQ(value, hundreds_val); // =123 + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("-1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2U", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err)); +} + +// -------------------------------------------------------------------- + TEST(MarshallingTest, TestFloatParsing) { std::string err; float value; diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel index a0db919b..7f964ae7 100644 --- a/absl/hash/BUILD.bazel +++ b/absl/hash/BUILD.bazel @@ -68,13 +68,17 @@ cc_library( cc_test( name = "hash_test", - srcs = ["hash_test.cc"], + srcs = [ + "hash_test.cc", + "internal/hash_test.h", + ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash", ":hash_testing", ":spy_hash_state", + "//absl/base:config", "//absl/base:core_headers", "//absl/container:btree", "//absl/container:flat_hash_map", @@ -88,6 +92,27 @@ cc_test( ], ) +cc_test( + name = "hash_instantiated_test", + srcs = [ + "hash_instantiated_test.cc", + "internal/hash_test.h", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":hash", + ":hash_testing", + "//absl/base:config", + "//absl/container:btree", + "//absl/container:flat_hash_map", + "//absl/container:flat_hash_set", + "//absl/container:node_hash_map", + "//absl/container:node_hash_set", + "@com_google_googletest//:gtest_main", + ], +) + cc_binary( name = "hash_benchmark", testonly = 1, diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt index f99f35bc..1adce617 100644 --- a/absl/hash/CMakeLists.txt +++ b/absl/hash/CMakeLists.txt @@ -64,6 +64,7 @@ absl_cc_test( hash_test SRCS "hash_test.cc" + "internal/hash_test.h" COPTS ${ABSL_TEST_COPTS} DEPS @@ -82,6 +83,26 @@ absl_cc_test( GTest::gmock_main ) +absl_cc_test( + NAME + hash_instantiated_test + SRCS + "hash_test.cc" + "internal/hash_test.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::hash + absl::hash_testing + absl::config + absl::btree + absl::flat_hash_map + absl::flat_hash_set + absl::node_hash_map + absl::node_hash_set + GTest::gtest_main +) + # Internal-only target, do not depend on directly. # # Note: Even though external code should not depend on this target diff --git a/absl/hash/hash_instantiated_test.cc b/absl/hash/hash_instantiated_test.cc new file mode 100644 index 00000000..e65de9ca --- /dev/null +++ b/absl/hash/hash_instantiated_test.cc @@ -0,0 +1,224 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains a few select absl::Hash tests that, due to their reliance +// on INSTANTIATE_TYPED_TEST_SUITE_P, require a large amount of memory to +// compile. Put new tests in hash_test.cc, not this file. + +#include "absl/hash/hash.h" + +#include <stddef.h> + +#include <algorithm> +#include <deque> +#include <forward_list> +#include <initializer_list> +#include <list> +#include <map> +#include <set> +#include <string> +#include <type_traits> +#include <unordered_map> +#include <unordered_set> +#include <utility> +#include <vector> + +#include "gtest/gtest.h" +#include "absl/container/btree_map.h" +#include "absl/container/btree_set.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/container/node_hash_map.h" +#include "absl/container/node_hash_set.h" +#include "absl/hash/hash_testing.h" +#include "absl/hash/internal/hash_test.h" + +namespace { + +using ::absl::hash_test_internal::is_hashable; +using ::absl::hash_test_internal::TypeErasedContainer; + +// Dummy type with unordered equality and hashing semantics. This preserves +// input order internally, and is used below to ensure we get test coverage +// for equal sequences with different iteraton orders. +template <typename T> +class UnorderedSequence { + public: + UnorderedSequence() = default; + template <typename TT> + UnorderedSequence(std::initializer_list<TT> l) + : values_(l.begin(), l.end()) {} + template <typename ForwardIterator, + typename std::enable_if<!std::is_integral<ForwardIterator>::value, + bool>::type = true> + UnorderedSequence(ForwardIterator begin, ForwardIterator end) + : values_(begin, end) {} + // one-argument constructor of value type T, to appease older toolchains that + // get confused by one-element initializer lists in some contexts + explicit UnorderedSequence(const T& v) : values_(&v, &v + 1) {} + + using value_type = T; + + size_t size() const { return values_.size(); } + typename std::vector<T>::const_iterator begin() const { + return values_.begin(); + } + typename std::vector<T>::const_iterator end() const { return values_.end(); } + + friend bool operator==(const UnorderedSequence& lhs, + const UnorderedSequence& rhs) { + return lhs.size() == rhs.size() && + std::is_permutation(lhs.begin(), lhs.end(), rhs.begin()); + } + friend bool operator!=(const UnorderedSequence& lhs, + const UnorderedSequence& rhs) { + return !(lhs == rhs); + } + template <typename H> + friend H AbslHashValue(H h, const UnorderedSequence& u) { + return H::combine(H::combine_unordered(std::move(h), u.begin(), u.end()), + u.size()); + } + + private: + std::vector<T> values_; +}; + +template <typename T> +class HashValueSequenceTest : public testing::Test {}; +TYPED_TEST_SUITE_P(HashValueSequenceTest); + +TYPED_TEST_P(HashValueSequenceTest, BasicUsage) { + EXPECT_TRUE((is_hashable<TypeParam>::value)); + + using IntType = typename TypeParam::value_type; + auto a = static_cast<IntType>(0); + auto b = static_cast<IntType>(23); + auto c = static_cast<IntType>(42); + + std::vector<TypeParam> exemplars = { + TypeParam(), TypeParam(), TypeParam{a, b, c}, + TypeParam{a, c, b}, TypeParam{c, a, b}, TypeParam{a}, + TypeParam{a, a}, TypeParam{a, a, a}, TypeParam{a, a, b}, + TypeParam{a, b, a}, TypeParam{b, a, a}, TypeParam{a, b}, + TypeParam{b, c}}; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); +} + +REGISTER_TYPED_TEST_SUITE_P(HashValueSequenceTest, BasicUsage); +using IntSequenceTypes = testing::Types< + std::deque<int>, std::forward_list<int>, std::list<int>, std::vector<int>, + std::vector<bool>, TypeErasedContainer<std::vector<int>>, std::set<int>, + std::multiset<int>, UnorderedSequence<int>, + TypeErasedContainer<UnorderedSequence<int>>, std::unordered_set<int>, + std::unordered_multiset<int>, absl::flat_hash_set<int>, + absl::node_hash_set<int>, absl::btree_set<int>>; +INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueSequenceTest, IntSequenceTypes); + +template <typename T> +class HashValueNestedSequenceTest : public testing::Test {}; +TYPED_TEST_SUITE_P(HashValueNestedSequenceTest); + +TYPED_TEST_P(HashValueNestedSequenceTest, BasicUsage) { + using T = TypeParam; + using V = typename T::value_type; + std::vector<T> exemplars = { + // empty case + T{}, + // sets of empty sets + T{V{}}, T{V{}, V{}}, T{V{}, V{}, V{}}, + // multisets of different values + T{V{1}}, T{V{1, 1}, V{1, 1}}, T{V{1, 1, 1}, V{1, 1, 1}, V{1, 1, 1}}, + // various orderings of same nested sets + T{V{}, V{1, 2}}, T{V{}, V{2, 1}}, T{V{1, 2}, V{}}, T{V{2, 1}, V{}}, + // various orderings of various nested sets, case 2 + T{V{1, 2}, V{3, 4}}, T{V{1, 2}, V{4, 3}}, T{V{1, 3}, V{2, 4}}, + T{V{1, 3}, V{4, 2}}, T{V{1, 4}, V{2, 3}}, T{V{1, 4}, V{3, 2}}, + T{V{2, 3}, V{1, 4}}, T{V{2, 3}, V{4, 1}}, T{V{2, 4}, V{1, 3}}, + T{V{2, 4}, V{3, 1}}, T{V{3, 4}, V{1, 2}}, T{V{3, 4}, V{2, 1}}}; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); +} + +REGISTER_TYPED_TEST_SUITE_P(HashValueNestedSequenceTest, BasicUsage); +template <typename T> +using TypeErasedSet = TypeErasedContainer<UnorderedSequence<T>>; + +using NestedIntSequenceTypes = testing::Types< + std::vector<std::vector<int>>, std::vector<UnorderedSequence<int>>, + std::vector<TypeErasedSet<int>>, UnorderedSequence<std::vector<int>>, + UnorderedSequence<UnorderedSequence<int>>, + UnorderedSequence<TypeErasedSet<int>>, TypeErasedSet<std::vector<int>>, + TypeErasedSet<UnorderedSequence<int>>, TypeErasedSet<TypeErasedSet<int>>>; +INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueNestedSequenceTest, + NestedIntSequenceTypes); + +template <typename T> +class HashValueAssociativeMapTest : public testing::Test {}; +TYPED_TEST_SUITE_P(HashValueAssociativeMapTest); + +TYPED_TEST_P(HashValueAssociativeMapTest, BasicUsage) { + using M = TypeParam; + using V = typename M::value_type; + std::vector<M> exemplars{M{}, + M{V{0, "foo"}}, + M{V{1, "foo"}}, + M{V{0, "bar"}}, + M{V{1, "bar"}}, + M{V{0, "foo"}, V{42, "bar"}}, + M{V{42, "bar"}, V{0, "foo"}}, + M{V{1, "foo"}, V{42, "bar"}}, + M{V{1, "foo"}, V{43, "bar"}}, + M{V{1, "foo"}, V{43, "baz"}}}; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); +} + +REGISTER_TYPED_TEST_SUITE_P(HashValueAssociativeMapTest, BasicUsage); +using AssociativeMapTypes = testing::Types< + std::map<int, std::string>, std::unordered_map<int, std::string>, + absl::flat_hash_map<int, std::string>, + absl::node_hash_map<int, std::string>, absl::btree_map<int, std::string>, + UnorderedSequence<std::pair<const int, std::string>>>; +INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueAssociativeMapTest, + AssociativeMapTypes); + +template <typename T> +class HashValueAssociativeMultimapTest : public testing::Test {}; +TYPED_TEST_SUITE_P(HashValueAssociativeMultimapTest); + +TYPED_TEST_P(HashValueAssociativeMultimapTest, BasicUsage) { + using MM = TypeParam; + using V = typename MM::value_type; + std::vector<MM> exemplars{MM{}, + MM{V{0, "foo"}}, + MM{V{1, "foo"}}, + MM{V{0, "bar"}}, + MM{V{1, "bar"}}, + MM{V{0, "foo"}, V{0, "bar"}}, + MM{V{0, "bar"}, V{0, "foo"}}, + MM{V{0, "foo"}, V{42, "bar"}}, + MM{V{1, "foo"}, V{42, "bar"}}, + MM{V{1, "foo"}, V{1, "foo"}, V{43, "bar"}}, + MM{V{1, "foo"}, V{43, "bar"}, V{1, "foo"}}, + MM{V{1, "foo"}, V{43, "baz"}}}; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); +} + +REGISTER_TYPED_TEST_SUITE_P(HashValueAssociativeMultimapTest, BasicUsage); +using AssociativeMultimapTypes = + testing::Types<std::multimap<int, std::string>, + std::unordered_multimap<int, std::string>>; +INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueAssociativeMultimapTest, + AssociativeMultimapTypes); + +} // namespace diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc index 6727dafa..a0e2e4a7 100644 --- a/absl/hash/hash_test.cc +++ b/absl/hash/hash_test.cc @@ -48,6 +48,7 @@ #include "absl/container/node_hash_map.h" #include "absl/container/node_hash_set.h" #include "absl/hash/hash_testing.h" +#include "absl/hash/internal/hash_test.h" #include "absl/hash/internal/spy_hash_state.h" #include "absl/meta/type_traits.h" #include "absl/numeric/int128.h" @@ -59,52 +60,9 @@ namespace { -// Utility wrapper of T for the purposes of testing the `AbslHash` type erasure -// mechanism. `TypeErasedValue<T>` can be constructed with a `T`, and can -// be compared and hashed. However, all hashing goes through the hashing -// type-erasure framework. -template <typename T> -class TypeErasedValue { - public: - TypeErasedValue() = default; - TypeErasedValue(const TypeErasedValue&) = default; - TypeErasedValue(TypeErasedValue&&) = default; - explicit TypeErasedValue(const T& n) : n_(n) {} - - template <typename H> - friend H AbslHashValue(H hash_state, const TypeErasedValue& v) { - v.HashValue(absl::HashState::Create(&hash_state)); - return hash_state; - } - - void HashValue(absl::HashState state) const { - absl::HashState::combine(std::move(state), n_); - } - - bool operator==(const TypeErasedValue& rhs) const { return n_ == rhs.n_; } - bool operator!=(const TypeErasedValue& rhs) const { return !(*this == rhs); } - - private: - T n_; -}; - -// A TypeErasedValue refinement, for containers. It exposes the wrapped -// `value_type` and is constructible from an initializer list. -template <typename T> -class TypeErasedContainer : public TypeErasedValue<T> { - public: - using value_type = typename T::value_type; - TypeErasedContainer() = default; - TypeErasedContainer(const TypeErasedContainer&) = default; - TypeErasedContainer(TypeErasedContainer&&) = default; - explicit TypeErasedContainer(const T& n) : TypeErasedValue<T>(n) {} - TypeErasedContainer(std::initializer_list<value_type> init_list) - : TypeErasedContainer(T(init_list.begin(), init_list.end())) {} - // one-argument constructor of value type T, to appease older toolchains that - // get confused by one-element initializer lists in some contexts - explicit TypeErasedContainer(const value_type& v) - : TypeErasedContainer(T(&v, &v + 1)) {} -}; +using ::absl::hash_test_internal::is_hashable; +using ::absl::hash_test_internal::TypeErasedContainer; +using ::absl::hash_test_internal::TypeErasedValue; template <typename T> using TypeErasedVector = TypeErasedContainer<std::vector<T>>; @@ -122,11 +80,6 @@ SpyHashState SpyHash(const T& value) { return SpyHashState::combine(SpyHashState(), value); } -// Helper trait to verify if T is hashable. We use absl::Hash's poison status to -// detect it. -template <typename T> -using is_hashable = std::is_default_constructible<absl::Hash<T>>; - TYPED_TEST_P(HashValueIntTest, BasicUsage) { EXPECT_TRUE((is_hashable<TypeParam>::value)); @@ -566,121 +519,6 @@ TEST(HashValueTest, StdBitset) { std::bitset<kNumBits>(bit_strings[5].c_str())})); } // namespace -// Dummy type with unordered equality and hashing semantics. This preserves -// input order internally, and is used below to ensure we get test coverage -// for equal sequences with different iteraton orders. -template <typename T> -class UnorderedSequence { - public: - UnorderedSequence() = default; - template <typename TT> - UnorderedSequence(std::initializer_list<TT> l) - : values_(l.begin(), l.end()) {} - template <typename ForwardIterator, - typename std::enable_if<!std::is_integral<ForwardIterator>::value, - bool>::type = true> - UnorderedSequence(ForwardIterator begin, ForwardIterator end) - : values_(begin, end) {} - // one-argument constructor of value type T, to appease older toolchains that - // get confused by one-element initializer lists in some contexts - explicit UnorderedSequence(const T& v) : values_(&v, &v + 1) {} - - using value_type = T; - - size_t size() const { return values_.size(); } - typename std::vector<T>::const_iterator begin() const { - return values_.begin(); - } - typename std::vector<T>::const_iterator end() const { return values_.end(); } - - friend bool operator==(const UnorderedSequence& lhs, - const UnorderedSequence& rhs) { - return lhs.size() == rhs.size() && - std::is_permutation(lhs.begin(), lhs.end(), rhs.begin()); - } - friend bool operator!=(const UnorderedSequence& lhs, - const UnorderedSequence& rhs) { - return !(lhs == rhs); - } - template <typename H> - friend H AbslHashValue(H h, const UnorderedSequence& u) { - return H::combine(H::combine_unordered(std::move(h), u.begin(), u.end()), - u.size()); - } - - private: - std::vector<T> values_; -}; - -template <typename T> -class HashValueSequenceTest : public testing::Test { -}; -TYPED_TEST_SUITE_P(HashValueSequenceTest); - -TYPED_TEST_P(HashValueSequenceTest, BasicUsage) { - EXPECT_TRUE((is_hashable<TypeParam>::value)); - - using IntType = typename TypeParam::value_type; - auto a = static_cast<IntType>(0); - auto b = static_cast<IntType>(23); - auto c = static_cast<IntType>(42); - - std::vector<TypeParam> exemplars = { - TypeParam(), TypeParam(), TypeParam{a, b, c}, - TypeParam{a, c, b}, TypeParam{c, a, b}, TypeParam{a}, - TypeParam{a, a}, TypeParam{a, a, a}, TypeParam{a, a, b}, - TypeParam{a, b, a}, TypeParam{b, a, a}, TypeParam{a, b}, - TypeParam{b, c}}; - EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); -} - -REGISTER_TYPED_TEST_SUITE_P(HashValueSequenceTest, BasicUsage); -using IntSequenceTypes = testing::Types< - std::deque<int>, std::forward_list<int>, std::list<int>, std::vector<int>, - std::vector<bool>, TypeErasedContainer<std::vector<int>>, std::set<int>, - std::multiset<int>, UnorderedSequence<int>, - TypeErasedContainer<UnorderedSequence<int>>, std::unordered_set<int>, - std::unordered_multiset<int>, absl::flat_hash_set<int>, - absl::node_hash_set<int>, absl::btree_set<int>>; -INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueSequenceTest, IntSequenceTypes); - -template <typename T> -class HashValueNestedSequenceTest : public testing::Test {}; -TYPED_TEST_SUITE_P(HashValueNestedSequenceTest); - -TYPED_TEST_P(HashValueNestedSequenceTest, BasicUsage) { - using T = TypeParam; - using V = typename T::value_type; - std::vector<T> exemplars = { - // empty case - T{}, - // sets of empty sets - T{V{}}, T{V{}, V{}}, T{V{}, V{}, V{}}, - // multisets of different values - T{V{1}}, T{V{1, 1}, V{1, 1}}, T{V{1, 1, 1}, V{1, 1, 1}, V{1, 1, 1}}, - // various orderings of same nested sets - T{V{}, V{1, 2}}, T{V{}, V{2, 1}}, T{V{1, 2}, V{}}, T{V{2, 1}, V{}}, - // various orderings of various nested sets, case 2 - T{V{1, 2}, V{3, 4}}, T{V{1, 2}, V{4, 3}}, T{V{1, 3}, V{2, 4}}, - T{V{1, 3}, V{4, 2}}, T{V{1, 4}, V{2, 3}}, T{V{1, 4}, V{3, 2}}, - T{V{2, 3}, V{1, 4}}, T{V{2, 3}, V{4, 1}}, T{V{2, 4}, V{1, 3}}, - T{V{2, 4}, V{3, 1}}, T{V{3, 4}, V{1, 2}}, T{V{3, 4}, V{2, 1}}}; - EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); -} - -REGISTER_TYPED_TEST_SUITE_P(HashValueNestedSequenceTest, BasicUsage); -template <typename T> -using TypeErasedSet = TypeErasedContainer<UnorderedSequence<T>>; - -using NestedIntSequenceTypes = testing::Types< - std::vector<std::vector<int>>, std::vector<UnorderedSequence<int>>, - std::vector<TypeErasedSet<int>>, UnorderedSequence<std::vector<int>>, - UnorderedSequence<UnorderedSequence<int>>, - UnorderedSequence<TypeErasedSet<int>>, TypeErasedSet<std::vector<int>>, - TypeErasedSet<UnorderedSequence<int>>, TypeErasedSet<TypeErasedSet<int>>>; -INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueNestedSequenceTest, - NestedIntSequenceTypes); - // Private type that only supports AbslHashValue to make sure our chosen hash // implementation is recursive within absl::Hash. // It uses std::abs() on the value to provide different bitwise representations @@ -839,64 +677,6 @@ TEST(HashValueTest, Variant) { #endif } -template <typename T> -class HashValueAssociativeMapTest : public testing::Test {}; -TYPED_TEST_SUITE_P(HashValueAssociativeMapTest); - -TYPED_TEST_P(HashValueAssociativeMapTest, BasicUsage) { - using M = TypeParam; - using V = typename M::value_type; - std::vector<M> exemplars{M{}, - M{V{0, "foo"}}, - M{V{1, "foo"}}, - M{V{0, "bar"}}, - M{V{1, "bar"}}, - M{V{0, "foo"}, V{42, "bar"}}, - M{V{42, "bar"}, V{0, "foo"}}, - M{V{1, "foo"}, V{42, "bar"}}, - M{V{1, "foo"}, V{43, "bar"}}, - M{V{1, "foo"}, V{43, "baz"}}}; - EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); -} - -REGISTER_TYPED_TEST_SUITE_P(HashValueAssociativeMapTest, BasicUsage); -using AssociativeMapTypes = testing::Types< - std::map<int, std::string>, std::unordered_map<int, std::string>, - absl::flat_hash_map<int, std::string>, - absl::node_hash_map<int, std::string>, absl::btree_map<int, std::string>, - UnorderedSequence<std::pair<const int, std::string>>>; -INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueAssociativeMapTest, - AssociativeMapTypes); - -template <typename T> -class HashValueAssociativeMultimapTest : public testing::Test {}; -TYPED_TEST_SUITE_P(HashValueAssociativeMultimapTest); - -TYPED_TEST_P(HashValueAssociativeMultimapTest, BasicUsage) { - using MM = TypeParam; - using V = typename MM::value_type; - std::vector<MM> exemplars{MM{}, - MM{V{0, "foo"}}, - MM{V{1, "foo"}}, - MM{V{0, "bar"}}, - MM{V{1, "bar"}}, - MM{V{0, "foo"}, V{0, "bar"}}, - MM{V{0, "bar"}, V{0, "foo"}}, - MM{V{0, "foo"}, V{42, "bar"}}, - MM{V{1, "foo"}, V{42, "bar"}}, - MM{V{1, "foo"}, V{1, "foo"}, V{43, "bar"}}, - MM{V{1, "foo"}, V{43, "bar"}, V{1, "foo"}}, - MM{V{1, "foo"}, V{43, "baz"}}}; - EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); -} - -REGISTER_TYPED_TEST_SUITE_P(HashValueAssociativeMultimapTest, BasicUsage); -using AssociativeMultimapTypes = - testing::Types<std::multimap<int, std::string>, - std::unordered_multimap<int, std::string>>; -INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueAssociativeMultimapTest, - AssociativeMultimapTypes); - TEST(HashValueTest, ReferenceWrapper) { EXPECT_TRUE(is_hashable<std::reference_wrapper<Private>>::value); diff --git a/absl/hash/internal/hash_test.h b/absl/hash/internal/hash_test.h new file mode 100644 index 00000000..9963dc0b --- /dev/null +++ b/absl/hash/internal/hash_test.h @@ -0,0 +1,87 @@ +// Copyright 2023 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Common code shared between absl/hash/hash_test.cc and +// absl/hash/hash_instantiated_test.cc. + +#ifndef ABSL_HASH_INTERNAL_HASH_TEST_H_ +#define ABSL_HASH_INTERNAL_HASH_TEST_H_ + +#include <type_traits> +#include <utility> + +#include "absl/base/config.h" +#include "absl/hash/hash.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace hash_test_internal { + +// Utility wrapper of T for the purposes of testing the `AbslHash` type erasure +// mechanism. `TypeErasedValue<T>` can be constructed with a `T`, and can +// be compared and hashed. However, all hashing goes through the hashing +// type-erasure framework. +template <typename T> +class TypeErasedValue { + public: + TypeErasedValue() = default; + TypeErasedValue(const TypeErasedValue&) = default; + TypeErasedValue(TypeErasedValue&&) = default; + explicit TypeErasedValue(const T& n) : n_(n) {} + + template <typename H> + friend H AbslHashValue(H hash_state, const TypeErasedValue& v) { + v.HashValue(absl::HashState::Create(&hash_state)); + return hash_state; + } + + void HashValue(absl::HashState state) const { + absl::HashState::combine(std::move(state), n_); + } + + bool operator==(const TypeErasedValue& rhs) const { return n_ == rhs.n_; } + bool operator!=(const TypeErasedValue& rhs) const { return !(*this == rhs); } + + private: + T n_; +}; + +// A TypeErasedValue refinement, for containers. It exposes the wrapped +// `value_type` and is constructible from an initializer list. +template <typename T> +class TypeErasedContainer : public TypeErasedValue<T> { + public: + using value_type = typename T::value_type; + TypeErasedContainer() = default; + TypeErasedContainer(const TypeErasedContainer&) = default; + TypeErasedContainer(TypeErasedContainer&&) = default; + explicit TypeErasedContainer(const T& n) : TypeErasedValue<T>(n) {} + TypeErasedContainer(std::initializer_list<value_type> init_list) + : TypeErasedContainer(T(init_list.begin(), init_list.end())) {} + // one-argument constructor of value type T, to appease older toolchains that + // get confused by one-element initializer lists in some contexts + explicit TypeErasedContainer(const value_type& v) + : TypeErasedContainer(T(&v, &v + 1)) {} +}; + +// Helper trait to verify if T is hashable. We use absl::Hash's poison status to +// detect it. +template <typename T> +using is_hashable = std::is_default_constructible<absl::Hash<T>>; + +} // namespace hash_test_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_HASH_INTERNAL_HASH_TEST_H_ diff --git a/absl/log/internal/globals.cc b/absl/log/internal/globals.cc index 863b047f..9ba997d5 100644 --- a/absl/log/internal/globals.cc +++ b/absl/log/internal/globals.cc @@ -16,6 +16,9 @@ #include <atomic> #include <cstdio> +#if defined(__EMSCRIPTEN__) +#include <emscripten/console.h> +#endif #include "absl/base/attributes.h" #include "absl/base/config.h" @@ -55,9 +58,21 @@ void SetInitialized() { } void WriteToStderr(absl::string_view message, absl::LogSeverity severity) { +#if defined(__EMSCRIPTEN__) + // In WebAssembly, bypass filesystem emulation via fwrite. + // TODO(b/282811932): Avoid this copy if these emscripten functions can + // be updated to accept size directly. + std::string null_terminated_message(message); + if (!null_terminated_message.empty() && + null_terminated_message.back() == '\n') { + null_terminated_message.pop_back(); + } + _emscripten_err(null_terminated_message.c_str()); +#else // Avoid using std::cerr from this module since we may get called during // exit code, and cerr may be partially or fully destroyed by then. std::fwrite(message.data(), message.size(), 1, stderr); +#endif #if defined(_WIN64) || defined(_WIN32) || defined(_WIN16) // C99 requires stderr to not be fully-buffered by default (7.19.3.7), but diff --git a/absl/status/internal/statusor_internal.h b/absl/status/internal/statusor_internal.h index eaac2c0b..49cead7a 100644 --- a/absl/status/internal/statusor_internal.h +++ b/absl/status/internal/statusor_internal.h @@ -69,11 +69,8 @@ using IsConstructibleOrConvertibleOrAssignableFromStatusOr = template <typename T, typename U> struct IsDirectInitializationAmbiguous : public absl::conditional_t< - std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, - U>::value, - std::false_type, - IsDirectInitializationAmbiguous< - T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {}; + std::is_same<absl::remove_cvref_t<U>, U>::value, std::false_type, + IsDirectInitializationAmbiguous<T, absl::remove_cvref_t<U>>> {}; template <typename T, typename V> struct IsDirectInitializationAmbiguous<T, absl::StatusOr<V>> @@ -84,14 +81,11 @@ struct IsDirectInitializationAmbiguous<T, absl::StatusOr<V>> template <typename T, typename U> using IsDirectInitializationValid = absl::disjunction< // Short circuits if T is basically U. - std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<T, absl::remove_cvref_t<U>>, absl::negation<absl::disjunction< - std::is_same<absl::StatusOr<T>, - absl::remove_cv_t<absl::remove_reference_t<U>>>, - std::is_same<absl::Status, - absl::remove_cv_t<absl::remove_reference_t<U>>>, - std::is_same<absl::in_place_t, - absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>, + std::is_same<absl::Status, absl::remove_cvref_t<U>>, + std::is_same<absl::in_place_t, absl::remove_cvref_t<U>>, IsDirectInitializationAmbiguous<T, U>>>>; // This trait detects whether `StatusOr<T>::operator=(U&&)` is ambiguous, which @@ -107,11 +101,8 @@ using IsDirectInitializationValid = absl::disjunction< template <typename T, typename U> struct IsForwardingAssignmentAmbiguous : public absl::conditional_t< - std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, - U>::value, - std::false_type, - IsForwardingAssignmentAmbiguous< - T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {}; + std::is_same<absl::remove_cvref_t<U>, U>::value, std::false_type, + IsForwardingAssignmentAmbiguous<T, absl::remove_cvref_t<U>>> {}; template <typename T, typename U> struct IsForwardingAssignmentAmbiguous<T, absl::StatusOr<U>> @@ -122,14 +113,11 @@ struct IsForwardingAssignmentAmbiguous<T, absl::StatusOr<U>> template <typename T, typename U> using IsForwardingAssignmentValid = absl::disjunction< // Short circuits if T is basically U. - std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<T, absl::remove_cvref_t<U>>, absl::negation<absl::disjunction< - std::is_same<absl::StatusOr<T>, - absl::remove_cv_t<absl::remove_reference_t<U>>>, - std::is_same<absl::Status, - absl::remove_cv_t<absl::remove_reference_t<U>>>, - std::is_same<absl::in_place_t, - absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>, + std::is_same<absl::Status, absl::remove_cvref_t<U>>, + std::is_same<absl::in_place_t, absl::remove_cvref_t<U>>, IsForwardingAssignmentAmbiguous<T, U>>>>; class Helper { diff --git a/absl/status/statusor.h b/absl/status/statusor.h index 935366d5..54c7ce02 100644 --- a/absl/status/statusor.h +++ b/absl/status/statusor.h @@ -411,7 +411,7 @@ class StatusOr : private internal_statusor::StatusOrData<T>, typename = typename std::enable_if<absl::conjunction< std::is_constructible<T, U&&>, std::is_assignable<T&, U&&>, absl::disjunction< - std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, T>, + std::is_same<absl::remove_cvref_t<U>, T>, absl::conjunction< absl::negation<std::is_convertible<U&&, absl::Status>>, absl::negation<internal_statusor:: @@ -444,8 +444,7 @@ class StatusOr : private internal_statusor::StatusOrData<T>, internal_statusor::IsDirectInitializationValid<T, U&&>, std::is_constructible<T, U&&>, std::is_convertible<U&&, T>, absl::disjunction< - std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, - T>, + std::is_same<absl::remove_cvref_t<U>, T>, absl::conjunction< absl::negation<std::is_convertible<U&&, absl::Status>>, absl::negation< @@ -461,8 +460,7 @@ class StatusOr : private internal_statusor::StatusOrData<T>, absl::conjunction< internal_statusor::IsDirectInitializationValid<T, U&&>, absl::disjunction< - std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, - T>, + std::is_same<absl::remove_cvref_t<U>, T>, absl::conjunction< absl::negation<std::is_constructible<absl::Status, U&&>>, absl::negation< diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index bd33c533..4a111b5a 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -1319,3 +1319,15 @@ cc_binary( "//absl/types:optional", ], ) + +cc_test( + name = "char_formatting_test", + srcs = [ + "char_formatting_test.cc", + ], + deps = [ + ":str_format", + ":strings", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 9aaa7932..2ad052a3 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -548,6 +548,20 @@ absl_cc_test( GTest::gmock_main ) +absl_cc_test( + NAME + char_formatting_test + SRCS + "char_formatting_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::str_format + absl::strings + GTest::gmock_main +) + # Internal-only target, do not depend on directly. absl_cc_library( NAME diff --git a/absl/strings/char_formatting_test.cc b/absl/strings/char_formatting_test.cc new file mode 100644 index 00000000..60416af3 --- /dev/null +++ b/absl/strings/char_formatting_test.cc @@ -0,0 +1,189 @@ +// Copyright 2023 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <cstddef> + +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/substitute.h" + +namespace { + +TEST(CharFormatting, Char) { + const char v = 'A'; + + // Desired behavior: does not compile: + // EXPECT_EQ(absl::StrCat(v, "B"), "AB"); + // EXPECT_EQ(absl::StrFormat("%vB", v), "AB"); + + // Legacy behavior: format as char: + EXPECT_EQ(absl::Substitute("$0B", v), "AB"); +} + +enum CharEnum : char {}; +TEST(CharFormatting, CharEnum) { + auto v = static_cast<CharEnum>('A'); + + // Desired behavior: format as decimal + // (No APIs do this today.) + + // BUG: internally fails + EXPECT_EQ(absl::StrFormat("%vB", v), ""); + + // Legacy behavior: does not compile: + // EXPECT_EQ(absl::StrCat(ch, "B"), "AB"); + + // Legacy behavior: format as character: + + // Some older versions of gcc behave differently in this one case. +#if !defined(__GNUC__) || defined(__clang__) + EXPECT_EQ(absl::Substitute("$0B", v), "AB"); +#endif +} + +enum class CharEnumClass: char {}; +TEST(CharFormatting, CharEnumClass) { + auto v = static_cast<CharEnumClass>('A'); + + // Desired behavior: format as decimal + // (No APIs do this today.) + + // BUG: internally fails + EXPECT_EQ(absl::StrFormat("%vB", v), ""); + + // Legacy behavior: does not compile: + // EXPECT_EQ(absl::StrCat(ch, "B"), "AB"); + + // Legacy behavior: format as character: + EXPECT_EQ(absl::Substitute("$0B", v), "AB"); +} + +TEST(CharFormatting, UnsignedChar) { + const unsigned char v = 'A'; + + // Desired behavior: format as decimal: + EXPECT_EQ(absl::StrCat(v, "B"), "65B"); + EXPECT_EQ(absl::Substitute("$0B", v), "65B"); + + // Legacy behavior: does not compile: + // EXPECT_EQ(absl::StrFormat("%vB", v), "65B"); + + // Signedness check + const unsigned char w = 255; + EXPECT_EQ(absl::StrCat(w, "B"), "255B"); + EXPECT_EQ(absl::Substitute("$0B", w), "255B"); + // EXPECT_EQ(absl::StrFormat("%vB", v), "255B"); +} + +TEST(CharFormatting, SignedChar) { + const signed char v = 'A'; + + // Desired behavior: format as decimal: + EXPECT_EQ(absl::StrCat(v, "B"), "65B"); + EXPECT_EQ(absl::Substitute("$0B", v), "65B"); + + // Legacy behavior: does not compile: + // EXPECT_EQ(absl::StrFormat("%vB", v), "AB"); + + // Signedness check + const signed char w = -128; + EXPECT_EQ(absl::StrCat(w, "B"), "-128B"); + EXPECT_EQ(absl::Substitute("$0B", w), "-128B"); +} + +enum UnsignedCharEnum : unsigned char {}; +TEST(CharFormatting, UnsignedCharEnum) { + auto v = static_cast<UnsignedCharEnum>('A'); + + // Desired behavior: format as decimal: + EXPECT_EQ(absl::StrCat(v, "B"), "65B"); + EXPECT_EQ(absl::Substitute("$0B", v), "65B"); + + // BUG: internally fails + EXPECT_EQ(absl::StrFormat("%vB", v), ""); + + // Signedness check + auto w = static_cast<UnsignedCharEnum>(255); + EXPECT_EQ(absl::StrCat(w, "B"), "255B"); + EXPECT_EQ(absl::Substitute("$0B", w), "255B"); +} + +enum SignedCharEnum : signed char {}; +TEST(CharFormatting, SignedCharEnum) { + auto v = static_cast<SignedCharEnum>('A'); + + // Desired behavior: format as decimal: + EXPECT_EQ(absl::StrCat(v, "B"), "65B"); + EXPECT_EQ(absl::Substitute("$0B", v), "65B"); + + // BUG: internally fails + EXPECT_EQ(absl::StrFormat("%vB", v), ""); + + // Signedness check + auto w = static_cast<SignedCharEnum>(-128); + EXPECT_EQ(absl::StrCat(w, "B"), "-128B"); + EXPECT_EQ(absl::Substitute("$0B", w), "-128B"); +} + +enum class UnsignedCharEnumClass : unsigned char {}; +TEST(CharFormatting, UnsignedCharEnumClass) { + auto v = static_cast<UnsignedCharEnumClass>('A'); + + // Desired behavior: format as decimal: + EXPECT_EQ(absl::StrCat(v, "B"), "65B"); + EXPECT_EQ(absl::Substitute("$0B", v), "65B"); + + // BUG: internally fails + EXPECT_EQ(absl::StrFormat("%vB", v), ""); + + // Signedness check + auto w = static_cast<UnsignedCharEnumClass>(255); + EXPECT_EQ(absl::StrCat(w, "B"), "255B"); + EXPECT_EQ(absl::Substitute("$0B", w), "255B"); +} + +enum SignedCharEnumClass : signed char {}; +TEST(CharFormatting, SignedCharEnumClass) { + auto v = static_cast<SignedCharEnumClass>('A'); + + // Desired behavior: format as decimal: + EXPECT_EQ(absl::StrCat(v, "B"), "65B"); + EXPECT_EQ(absl::Substitute("$0B", v), "65B"); + + // BUG: internally fails + EXPECT_EQ(absl::StrFormat("%vB", v), ""); + + // Signedness check + auto w = static_cast<SignedCharEnumClass>(-128); + EXPECT_EQ(absl::StrCat(w, "B"), "-128B"); + EXPECT_EQ(absl::Substitute("$0B", w), "-128B"); +} + +#ifdef __cpp_lib_byte +TEST(CharFormatting, StdByte) { + auto v = static_cast<std::byte>('A'); + // Desired behavior: format as 0xff + // (No APIs do this today.) + + // BUG: internally fails + EXPECT_EQ(absl::StrFormat("%vB", v), ""); + + // Legacy behavior: format as decimal: + EXPECT_EQ(absl::StrCat(v, "B"), "65B"); + EXPECT_EQ(absl::Substitute("$0B", v), "65B"); +} +#endif // _cpp_lib_byte + +} // namespace diff --git a/absl/strings/internal/str_split_internal.h b/absl/strings/internal/str_split_internal.h index 35edf3aa..081ad85a 100644 --- a/absl/strings/internal/str_split_internal.h +++ b/absl/strings/internal/str_split_internal.h @@ -235,6 +235,24 @@ struct SplitterIsConvertibleTo HasMappedType<C>::value> { }; +template <typename StringType, typename Container, typename = void> +struct ShouldUseLifetimeBound : std::false_type {}; + +template <typename StringType, typename Container> +struct ShouldUseLifetimeBound< + StringType, Container, + std::enable_if_t< + std::is_same<StringType, std::string>::value && + std::is_same<typename Container::value_type, absl::string_view>::value>> + : std::true_type {}; + +template <typename StringType, typename First, typename Second> +using ShouldUseLifetimeBoundForPair = std::integral_constant< + bool, std::is_same<StringType, std::string>::value && + (std::is_same<First, absl::string_view>::value || + std::is_same<Second, absl::string_view>::value)>; + + // This class implements the range that is returned by absl::StrSplit(). This // class has templated conversion operators that allow it to be implicitly // converted to a variety of types that the caller may have specified on the @@ -281,10 +299,24 @@ class Splitter { // An implicit conversion operator that is restricted to only those containers // that the splitter is convertible to. - template <typename Container, - typename = typename std::enable_if< - SplitterIsConvertibleTo<Container>::value>::type> - operator Container() const { // NOLINT(runtime/explicit) + template < + typename Container, + std::enable_if_t<ShouldUseLifetimeBound<StringType, Container>::value && + SplitterIsConvertibleTo<Container>::value, + std::nullptr_t> = nullptr> + // NOLINTNEXTLINE(google-explicit-constructor) + operator Container() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return ConvertToContainer<Container, typename Container::value_type, + HasMappedType<Container>::value>()(*this); + } + + template < + typename Container, + std::enable_if_t<!ShouldUseLifetimeBound<StringType, Container>::value && + SplitterIsConvertibleTo<Container>::value, + std::nullptr_t> = nullptr> + // NOLINTNEXTLINE(google-explicit-constructor) + operator Container() const { return ConvertToContainer<Container, typename Container::value_type, HasMappedType<Container>::value>()(*this); } @@ -293,8 +325,27 @@ class Splitter { // strings returned by the begin() iterator. Either/both of .first and .second // will be constructed with empty strings if the iterator doesn't have a // corresponding value. + template <typename First, typename Second, + std::enable_if_t< + ShouldUseLifetimeBoundForPair<StringType, First, Second>::value, + std::nullptr_t> = nullptr> + // NOLINTNEXTLINE(google-explicit-constructor) + operator std::pair<First, Second>() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return ConvertToPair<First, Second>(); + } + + template <typename First, typename Second, + std::enable_if_t<!ShouldUseLifetimeBoundForPair<StringType, First, + Second>::value, + std::nullptr_t> = nullptr> + // NOLINTNEXTLINE(google-explicit-constructor) + operator std::pair<First, Second>() const { + return ConvertToPair<First, Second>(); + } + + private: template <typename First, typename Second> - operator std::pair<First, Second>() const { // NOLINT(runtime/explicit) + std::pair<First, Second> ConvertToPair() const { absl::string_view first, second; auto it = begin(); if (it != end()) { @@ -306,7 +357,6 @@ class Splitter { return {First(first), Second(second)}; } - private: // ConvertToContainer is a functor converting a Splitter to the requested // Container of ValueType. It is specialized below to optimize splitting to // certain combinations of Container and ValueType. diff --git a/absl/time/CMakeLists.txt b/absl/time/CMakeLists.txt index b3124251..1c830c7a 100644 --- a/absl/time/CMakeLists.txt +++ b/absl/time/CMakeLists.txt @@ -84,6 +84,7 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + Threads::Threads $<$<PLATFORM_ID:Darwin>:${CoreFoundation}> ) diff --git a/absl/time/clock.cc b/absl/time/clock.cc index 2bf53d9c..aa74367b 100644 --- a/absl/time/clock.cc +++ b/absl/time/clock.cc @@ -48,17 +48,16 @@ Time Now() { ABSL_NAMESPACE_END } // namespace absl -// Decide if we should use the fast GetCurrentTimeNanos() algorithm -// based on the cyclecounter, otherwise just get the time directly -// from the OS on every call. This can be chosen at compile-time via +// Decide if we should use the fast GetCurrentTimeNanos() algorithm based on the +// cyclecounter, otherwise just get the time directly from the OS on every call. +// By default, the fast algorithm based on the cyclecount is disabled because in +// certain situations, for example, if the OS enters a "sleep" mode, it may +// produce incorrect values immediately upon waking. +// This can be chosen at compile-time via // -DABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS=[0|1] #ifndef ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS -#if ABSL_USE_UNSCALED_CYCLECLOCK -#define ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS 1 -#else #define ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS 0 #endif -#endif #if defined(__APPLE__) || defined(_WIN32) #include "absl/time/internal/get_current_time_chrono.inc" diff --git a/absl/time/internal/cctz/src/time_zone_if.cc b/absl/time/internal/cctz/src/time_zone_if.cc index 0319b2f9..1d44dde7 100644 --- a/absl/time/internal/cctz/src/time_zone_if.cc +++ b/absl/time/internal/cctz/src/time_zone_if.cc @@ -26,6 +26,8 @@ namespace cctz { std::unique_ptr<TimeZoneIf> TimeZoneIf::Load(const std::string& name) { // Support "libc:localtime" and "libc:*" to access the legacy // localtime and UTC support respectively from the C library. + // NOTE: The "libc:*" zones are internal, test-only interfaces, and + // are subject to change/removal without notice. Do not use them. if (name.compare(0, 5, "libc:") == 0) { return std::unique_ptr<TimeZoneIf>(new TimeZoneLibC(name.substr(5))); } diff --git a/absl/time/internal/cctz/src/time_zone_libc.cc b/absl/time/internal/cctz/src/time_zone_libc.cc index 887dd097..af9f063e 100644 --- a/absl/time/internal/cctz/src/time_zone_libc.cc +++ b/absl/time/internal/cctz/src/time_zone_libc.cc @@ -125,31 +125,30 @@ inline std::tm* local_time(const std::time_t* timep, std::tm* result) { #endif } -// Converts a civil second and "dst" flag into a time_t and UTC offset. +// Converts a civil second and "dst" flag into a time_t and a struct tm. // Returns false if time_t cannot represent the requested civil second. // Caller must have already checked that cs.year() will fit into a tm_year. -bool make_time(const civil_second& cs, int is_dst, std::time_t* t, int* off) { - std::tm tm; - tm.tm_year = static_cast<int>(cs.year() - year_t{1900}); - tm.tm_mon = cs.month() - 1; - tm.tm_mday = cs.day(); - tm.tm_hour = cs.hour(); - tm.tm_min = cs.minute(); - tm.tm_sec = cs.second(); - tm.tm_isdst = is_dst; - *t = std::mktime(&tm); +bool make_time(const civil_second& cs, int is_dst, std::time_t* t, + std::tm* tm) { + tm->tm_year = static_cast<int>(cs.year() - year_t{1900}); + tm->tm_mon = cs.month() - 1; + tm->tm_mday = cs.day(); + tm->tm_hour = cs.hour(); + tm->tm_min = cs.minute(); + tm->tm_sec = cs.second(); + tm->tm_isdst = is_dst; + *t = std::mktime(tm); if (*t == std::time_t{-1}) { std::tm tm2; const std::tm* tmp = local_time(t, &tm2); - if (tmp == nullptr || tmp->tm_year != tm.tm_year || - tmp->tm_mon != tm.tm_mon || tmp->tm_mday != tm.tm_mday || - tmp->tm_hour != tm.tm_hour || tmp->tm_min != tm.tm_min || - tmp->tm_sec != tm.tm_sec) { + if (tmp == nullptr || tmp->tm_year != tm->tm_year || + tmp->tm_mon != tm->tm_mon || tmp->tm_mday != tm->tm_mday || + tmp->tm_hour != tm->tm_hour || tmp->tm_min != tm->tm_min || + tmp->tm_sec != tm->tm_sec) { // A true error (not just one second before the epoch). return false; } } - *off = static_cast<int>(tm_gmtoff(tm)); return true; } @@ -254,33 +253,37 @@ time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { // We probe with "is_dst" values of 0 and 1 to try to distinguish unique // civil seconds from skipped or repeated ones. This is not always possible // however, as the "dst" flag does not change over some offset transitions. - // We are also subject to the vagaries of mktime() implementations. + // We are also subject to the vagaries of mktime() implementations. For + // example, some implementations treat "tm_isdst" as a demand (useless), + // and some as a disambiguator (useful). std::time_t t0, t1; - int offset0, offset1; - if (make_time(cs, 0, &t0, &offset0) && make_time(cs, 1, &t1, &offset1)) { - if (t0 == t1) { + std::tm tm0, tm1; + if (make_time(cs, 0, &t0, &tm0) && make_time(cs, 1, &t1, &tm1)) { + if (tm0.tm_isdst == tm1.tm_isdst) { // The civil time was singular (pre == trans == post). - const time_point<seconds> tp = FromUnixSeconds(t0); + const time_point<seconds> tp = FromUnixSeconds(tm0.tm_isdst ? t1 : t0); return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; } - if (t0 > t1) { + int offset = tm_gmtoff(tm0); + if (t0 < t1) { // negative DST std::swap(t0, t1); - std::swap(offset0, offset1); + offset = tm_gmtoff(tm1); } - const std::time_t tt = find_trans(t0, t1, offset1); + + const std::time_t tt = find_trans(t1, t0, offset); const time_point<seconds> trans = FromUnixSeconds(tt); - if (offset0 < offset1) { + if (tm0.tm_isdst) { // The civil time did not exist (pre >= trans > post). - const time_point<seconds> pre = FromUnixSeconds(t1); - const time_point<seconds> post = FromUnixSeconds(t0); + const time_point<seconds> pre = FromUnixSeconds(t0); + const time_point<seconds> post = FromUnixSeconds(t1); return {time_zone::civil_lookup::SKIPPED, pre, trans, post}; } // The civil time was ambiguous (pre < trans <= post). - const time_point<seconds> pre = FromUnixSeconds(t0); - const time_point<seconds> post = FromUnixSeconds(t1); + const time_point<seconds> pre = FromUnixSeconds(t1); + const time_point<seconds> post = FromUnixSeconds(t0); return {time_zone::civil_lookup::REPEATED, pre, trans, post}; } diff --git a/absl/time/internal/cctz/src/time_zone_lookup.cc b/absl/time/internal/cctz/src/time_zone_lookup.cc index 68084ce2..9a30ba5e 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup.cc @@ -148,11 +148,12 @@ std::string win32_local_time_zone(const HMODULE combase) { UINT32 wlen; const PCWSTR tz_wstr = windows_get_string_raw_buffer(tz_hstr, &wlen); if (tz_wstr) { - const int size = WideCharToMultiByte(CP_UTF8, 0, tz_wstr, wlen, nullptr, - 0, nullptr, nullptr); - result.resize(size); - WideCharToMultiByte(CP_UTF8, 0, tz_wstr, wlen, &result[0], size, nullptr, - nullptr); + const int size = + WideCharToMultiByte(CP_UTF8, 0, tz_wstr, static_cast<int>(wlen), + nullptr, 0, nullptr, nullptr); + result.resize(static_cast<size_t>(size)); + WideCharToMultiByte(CP_UTF8, 0, tz_wstr, static_cast<int>(wlen), + &result[0], size, nullptr, nullptr); } windows_delete_string(tz_hstr); } diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc index 4b093b37..38f10f48 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc @@ -1034,16 +1034,16 @@ TEST(MakeTime, SysSecondsLimits) { const time_zone cut = LoadZone("libc:UTC"); const year_t max_tm_year = year_t{std::numeric_limits<int>::max()} + 1900; tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), cut); -#if defined(__FreeBSD__) || defined(__OpenBSD__) - // The BSD gmtime_r() fails on extreme positive tm_year values. +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) + // Some gmtime_r() impls fail on extreme positive values. #else EXPECT_EQ("2147485547-12-31T23:59:59+00:00", absl::time_internal::cctz::format(RFC3339, tp, cut)); #endif const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900; tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut); -#if defined(__Fuchsia__) - // Fuchsia's gmtime_r() fails on extreme negative values (fxbug.dev/78527). +#if defined(__Fuchsia__) || defined(__EMSCRIPTEN__) + // Some gmtime_r() impls fail on extreme negative values (fxbug.dev/78527). #else EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", absl::time_internal::cctz::format(RFC3339, tp, cut)); @@ -1072,7 +1072,7 @@ TEST(MakeTime, LocalTimeLibC) { tp = zi.lookup(transition.to).trans) { const auto fcl = zi.lookup(transition.from); const auto tcl = zi.lookup(transition.to); - civil_second cs; // compare cs in zi and lc + civil_second cs, us; // compare cs and us in zi and lc if (fcl.kind == time_zone::civil_lookup::UNIQUE) { if (tcl.kind == time_zone::civil_lookup::UNIQUE) { // Both unique; must be an is_dst or abbr change. @@ -1088,12 +1088,14 @@ TEST(MakeTime, LocalTimeLibC) { } ASSERT_EQ(time_zone::civil_lookup::REPEATED, tcl.kind); cs = transition.to; + us = transition.from; } else { ASSERT_EQ(time_zone::civil_lookup::UNIQUE, tcl.kind); ASSERT_EQ(time_zone::civil_lookup::SKIPPED, fcl.kind); cs = transition.from; + us = transition.to; } - if (cs.year() > 2037) break; // limit test time (and to 32-bit time_t) + if (us.year() > 2037) break; // limit test time (and to 32-bit time_t) const auto cl_zi = zi.lookup(cs); if (zi.lookup(cl_zi.pre).is_dst == zi.lookup(cl_zi.post).is_dst) { // The "libc" implementation cannot correctly classify transitions @@ -1125,6 +1127,13 @@ TEST(MakeTime, LocalTimeLibC) { EXPECT_EQ(cl_zi.pre, cl_lc.pre); EXPECT_EQ(cl_zi.trans, cl_lc.trans); EXPECT_EQ(cl_zi.post, cl_lc.post); + const auto ucl_zi = zi.lookup(us); + const auto ucl_lc = lc.lookup(us); + SCOPED_TRACE(testing::Message() << "For " << us << " in " << *np); + EXPECT_EQ(ucl_zi.kind, ucl_lc.kind); + EXPECT_EQ(ucl_zi.pre, ucl_lc.pre); + EXPECT_EQ(ucl_zi.trans, ucl_lc.trans); + EXPECT_EQ(ucl_zi.post, ucl_lc.post); } } if (ep == nullptr) { |