diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 90c9f1f..7646c15 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake
@@ -19,6 +19,7 @@ "base/internal/errno_saver.h" "base/internal/exponential_biased.cc" "base/internal/exponential_biased.h" + "base/internal/fast_type_id.h" "base/internal/hide_ptr.h" "base/internal/identity.h" "base/internal/invoke.h" @@ -35,6 +36,8 @@ "base/internal/scheduling_mode.h" "base/internal/scoped_set_env.cc" "base/internal/scoped_set_env.h" + "base/internal/strerror.h" + "base/internal/strerror.cc" "base/internal/spinlock.cc" "base/internal/spinlock.h" "base/internal/spinlock_wait.cc" @@ -128,7 +131,6 @@ "random/bit_gen_ref.h" "random/discrete_distribution.cc" "random/discrete_distribution.h" - "random/distribution_format_traits.h" "random/distributions.h" "random/exponential_distribution.h" "random/gaussian_distribution.cc"
diff --git a/CMake/Googletest/DownloadGTest.cmake b/CMake/Googletest/DownloadGTest.cmake index 6552e1d..9d071c9 100644 --- a/CMake/Googletest/DownloadGTest.cmake +++ b/CMake/Googletest/DownloadGTest.cmake
@@ -38,6 +38,4 @@ # Add googletest directly to our build. This defines the gtest and gtest_main # targets. -add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src - ${CMAKE_BINARY_DIR}/googletest-build - EXCLUDE_FROM_ALL) +add_subdirectory(${absl_gtest_src_dir} ${absl_gtest_build_dir} EXCLUDE_FROM_ALL)
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index bae7942..1af9e45 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel
@@ -307,6 +307,7 @@ linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":errno_saver", + ":strerror", "@com_google_googletest//:gtest_main", ], ) @@ -451,6 +452,7 @@ testonly = 1, copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], visibility = ["//visibility:private"], deps = [ ":spinlock_benchmark_common", @@ -705,3 +707,71 @@ "@com_google_googletest//:gtest_main", ], ) + +cc_library( + name = "strerror", + srcs = ["internal/strerror.cc"], + hdrs = ["internal/strerror.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + ":config", + ":core_headers", + ":errno_saver", + ], +) + +cc_test( + name = "strerror_test", + size = "small", + srcs = ["internal/strerror_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":strerror", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_binary( + name = "strerror_benchmark", + testonly = 1, + srcs = ["internal/strerror_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":strerror", + "@com_github_google_benchmark//:benchmark_main", + ], +) + +cc_library( + name = "fast_type_id", + hdrs = ["internal/fast_type_id.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + ":config", + ], +) + +cc_test( + name = "fast_type_id_test", + size = "small", + srcs = ["internal/fast_type_id_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":fast_type_id", + "@com_google_googletest//:gtest_main", + ], +)
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 14c52ea..5454992 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt
@@ -326,6 +326,7 @@ ${ABSL_TEST_COPTS} DEPS absl::errno_saver + absl::strerror gmock gtest_main ) @@ -642,3 +643,59 @@ gmock gtest_main ) + +absl_cc_library( + NAME + strerror + SRCS + "internal/strerror.cc" + HDRS + "internal/strerror.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config + absl::core_headers + absl::errno_saver +) + +absl_cc_test( + NAME + strerror_test + SRCS + "internal/strerror_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strerror + absl::strings + gmock + gtest_main +) + +absl_cc_library( + NAME + fast_type_id + HDRS + "internal/fast_type_id.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config +) + +absl_cc_test( + NAME + fast_type_id_test + SRCS + "internal/fast_type_id_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::fast_type_id + gtest_main +)
diff --git a/absl/base/config.h b/absl/base/config.h index ee99f94..f54466d 100644 --- a/absl/base/config.h +++ b/absl/base/config.h
@@ -262,13 +262,6 @@ #endif #endif // defined(__ANDROID__) && defined(__clang__) -// Emscripten doesn't yet support `thread_local` or `__thread`. -// https://github.com/emscripten-core/emscripten/issues/3502 -#if defined(__EMSCRIPTEN__) -#undef ABSL_HAVE_TLS -#undef ABSL_HAVE_THREAD_LOCAL -#endif // defined(__EMSCRIPTEN__) - // ABSL_HAVE_INTRINSIC_INT128 // // Checks whether the __int128 compiler extension for a 128-bit integral type is
diff --git a/absl/base/internal/bits.h b/absl/base/internal/bits.h index 8b03453..14c51d8 100644 --- a/absl/base/internal/bits.h +++ b/absl/base/internal/bits.h
@@ -24,7 +24,7 @@ // Clang on Windows has __builtin_clzll; otherwise we need to use the // windows intrinsic functions. -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) #include <intrin.h> #if defined(_M_X64) #pragma intrinsic(_BitScanReverse64) @@ -36,7 +36,7 @@ #include "absl/base/attributes.h" -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) // We can achieve something similar to attribute((always_inline)) with MSVC by // using the __forceinline keyword, however this is not perfect. MSVC is // much less aggressive about inlining, and even with the __forceinline keyword. @@ -73,14 +73,14 @@ } ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) { -#if defined(_MSC_VER) && defined(_M_X64) +#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_X64) // MSVC does not have __buitin_clzll. Use _BitScanReverse64. unsigned long result = 0; // NOLINT(runtime/int) if (_BitScanReverse64(&result, n)) { return 63 - result; } return 64; -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) && !defined(__clang__) // MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse unsigned long result = 0; // NOLINT(runtime/int) if ((n >> 32) && _BitScanReverse(&result, n >> 32)) { @@ -90,7 +90,7 @@ return 63 - result; } return 64; -#elif defined(__GNUC__) +#elif defined(__GNUC__) || defined(__clang__) // Use __builtin_clzll, which uses the following instructions: // x86: bsr // ARM64: clz @@ -126,13 +126,13 @@ } ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) { -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) unsigned long result = 0; // NOLINT(runtime/int) if (_BitScanReverse(&result, n)) { return 31 - result; } return 32; -#elif defined(__GNUC__) +#elif defined(__GNUC__) || defined(__clang__) // Use __builtin_clz, which uses the following instructions: // x86: bsr // ARM64: clz @@ -163,11 +163,11 @@ } ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) { -#if defined(_MSC_VER) && defined(_M_X64) +#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_X64) unsigned long result = 0; // NOLINT(runtime/int) _BitScanForward64(&result, n); return result; -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) && !defined(__clang__) unsigned long result = 0; // NOLINT(runtime/int) if (static_cast<uint32_t>(n) == 0) { _BitScanForward(&result, n >> 32); @@ -175,7 +175,7 @@ } _BitScanForward(&result, n); return result; -#elif defined(__GNUC__) +#elif defined(__GNUC__) || defined(__clang__) static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int) "__builtin_ctzll does not take 64-bit arg"); return __builtin_ctzll(n); @@ -196,11 +196,11 @@ } ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) { -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) unsigned long result = 0; // NOLINT(runtime/int) _BitScanForward(&result, n); return result; -#elif defined(__GNUC__) +#elif defined(__GNUC__) || defined(__clang__) static_assert(sizeof(int) == sizeof(n), "__builtin_ctz does not take 32-bit arg"); return __builtin_ctz(n);
diff --git a/absl/base/internal/errno_saver_test.cc b/absl/base/internal/errno_saver_test.cc index b845e2d..e9b742c 100644 --- a/absl/base/internal/errno_saver_test.cc +++ b/absl/base/internal/errno_saver_test.cc
@@ -18,6 +18,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/internal/strerror.h" namespace { using ::testing::Eq; @@ -26,7 +27,7 @@ int no; }; std::ostream &operator<<(std::ostream &os, ErrnoPrinter ep) { - return os << strerror(ep.no) << " [" << ep.no << "]"; + return os << absl::base_internal::StrError(ep.no) << " [" << ep.no << "]"; } bool operator==(ErrnoPrinter one, ErrnoPrinter two) { return one.no == two.no; }
diff --git a/absl/base/internal/fast_type_id.h b/absl/base/internal/fast_type_id.h new file mode 100644 index 0000000..3db59e8 --- /dev/null +++ b/absl/base/internal/fast_type_id.h
@@ -0,0 +1,48 @@ +// +// Copyright 2020 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. +// + +#ifndef ABSL_BASE_INTERNAL_FAST_TYPE_ID_H_ +#define ABSL_BASE_INTERNAL_FAST_TYPE_ID_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +template <typename Type> +struct FastTypeTag { + constexpr static char dummy_var = 0; +}; + +template <typename Type> +constexpr char FastTypeTag<Type>::dummy_var; + +// FastTypeId<Type>() evaluates at compile/link-time to a unique pointer for the +// passed-in type. These are meant to be good match for keys into maps or +// straight up comparisons. +using FastTypeIdType = const void*; + +template <typename Type> +constexpr inline FastTypeIdType FastTypeId() { + return &FastTypeTag<Type>::dummy_var; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_FAST_TYPE_ID_H_
diff --git a/absl/base/internal/fast_type_id_test.cc b/absl/base/internal/fast_type_id_test.cc new file mode 100644 index 0000000..16f3c14 --- /dev/null +++ b/absl/base/internal/fast_type_id_test.cc
@@ -0,0 +1,123 @@ +// Copyright 2020 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 "absl/base/internal/fast_type_id.h" + +#include <cstdint> +#include <map> +#include <vector> + +#include "gtest/gtest.h" + +namespace { +namespace bi = absl::base_internal; + +// NOLINTNEXTLINE +#define PRIM_TYPES(A) \ + A(bool) \ + A(short) \ + A(unsigned short) \ + A(int) \ + A(unsigned int) \ + A(long) \ + A(unsigned long) \ + A(long long) \ + A(unsigned long long) \ + A(float) \ + A(double) \ + A(long double) + +TEST(FastTypeIdTest, PrimitiveTypes) { + bi::FastTypeIdType type_ids[] = { +#define A(T) bi::FastTypeId<T>(), + PRIM_TYPES(A) +#undef A +#define A(T) bi::FastTypeId<const T>(), + PRIM_TYPES(A) +#undef A +#define A(T) bi::FastTypeId<volatile T>(), + PRIM_TYPES(A) +#undef A +#define A(T) bi::FastTypeId<const volatile T>(), + PRIM_TYPES(A) +#undef A + }; + size_t total_type_ids = sizeof(type_ids) / sizeof(bi::FastTypeIdType); + + for (int i = 0; i < total_type_ids; ++i) { + EXPECT_EQ(type_ids[i], type_ids[i]); + for (int j = 0; j < i; ++j) { + EXPECT_NE(type_ids[i], type_ids[j]); + } + } +} + +#define FIXED_WIDTH_TYPES(A) \ + A(int8_t) \ + A(uint8_t) \ + A(int16_t) \ + A(uint16_t) \ + A(int32_t) \ + A(uint32_t) \ + A(int64_t) \ + A(uint64_t) + +TEST(FastTypeIdTest, FixedWidthTypes) { + bi::FastTypeIdType type_ids[] = { +#define A(T) bi::FastTypeId<T>(), + FIXED_WIDTH_TYPES(A) +#undef A +#define A(T) bi::FastTypeId<const T>(), + FIXED_WIDTH_TYPES(A) +#undef A +#define A(T) bi::FastTypeId<volatile T>(), + FIXED_WIDTH_TYPES(A) +#undef A +#define A(T) bi::FastTypeId<const volatile T>(), + FIXED_WIDTH_TYPES(A) +#undef A + }; + size_t total_type_ids = sizeof(type_ids) / sizeof(bi::FastTypeIdType); + + for (int i = 0; i < total_type_ids; ++i) { + EXPECT_EQ(type_ids[i], type_ids[i]); + for (int j = 0; j < i; ++j) { + EXPECT_NE(type_ids[i], type_ids[j]); + } + } +} + +TEST(FastTypeIdTest, AliasTypes) { + using int_alias = int; + EXPECT_EQ(bi::FastTypeId<int_alias>(), bi::FastTypeId<int>()); +} + +TEST(FastTypeIdTest, TemplateSpecializations) { + EXPECT_NE(bi::FastTypeId<std::vector<int>>(), + bi::FastTypeId<std::vector<long>>()); + + EXPECT_NE((bi::FastTypeId<std::map<int, float>>()), + (bi::FastTypeId<std::map<int, double>>())); +} + +struct Base {}; +struct Derived : Base {}; +struct PDerived : private Base {}; + +TEST(FastTypeIdTest, Inheritance) { + EXPECT_NE(bi::FastTypeId<Base>(), bi::FastTypeId<Derived>()); + EXPECT_NE(bi::FastTypeId<Base>(), bi::FastTypeId<PDerived>()); +} + +} // namespace
diff --git a/absl/base/internal/spinlock.cc b/absl/base/internal/spinlock.cc index 830d472..fd0c733 100644 --- a/absl/base/internal/spinlock.cc +++ b/absl/base/internal/spinlock.cc
@@ -190,30 +190,32 @@ // We use the upper 29 bits of the lock word to store the time spent waiting to // acquire this lock. This is reported by contentionz profiling. Since the // lower bits of the cycle counter wrap very quickly on high-frequency -// processors we divide to reduce the granularity to 2^PROFILE_TIMESTAMP_SHIFT +// processors we divide to reduce the granularity to 2^kProfileTimestampShift // sized units. On a 4Ghz machine this will lose track of wait times greater // than (2^29/4 Ghz)*128 =~ 17.2 seconds. Such waits should be extremely rare. -enum { PROFILE_TIMESTAMP_SHIFT = 7 }; -enum { LOCKWORD_RESERVED_SHIFT = 3 }; // We currently reserve the lower 3 bits. +static constexpr int kProfileTimestampShift = 7; + +// We currently reserve the lower 3 bits. +static constexpr int kLockwordReservedShift = 3; uint32_t SpinLock::EncodeWaitCycles(int64_t wait_start_time, int64_t wait_end_time) { static const int64_t kMaxWaitTime = - std::numeric_limits<uint32_t>::max() >> LOCKWORD_RESERVED_SHIFT; + std::numeric_limits<uint32_t>::max() >> kLockwordReservedShift; int64_t scaled_wait_time = - (wait_end_time - wait_start_time) >> PROFILE_TIMESTAMP_SHIFT; + (wait_end_time - wait_start_time) >> kProfileTimestampShift; // Return a representation of the time spent waiting that can be stored in // the lock word's upper bits. uint32_t clamped = static_cast<uint32_t>( - std::min(scaled_wait_time, kMaxWaitTime) << LOCKWORD_RESERVED_SHIFT); + std::min(scaled_wait_time, kMaxWaitTime) << kLockwordReservedShift); if (clamped == 0) { return kSpinLockSleeper; // Just wake waiters, but don't record contention. } // Bump up value if necessary to avoid returning kSpinLockSleeper. const uint32_t kMinWaitTime = - kSpinLockSleeper + (1 << LOCKWORD_RESERVED_SHIFT); + kSpinLockSleeper + (1 << kLockwordReservedShift); if (clamped == kSpinLockSleeper) { return kMinWaitTime; } @@ -224,8 +226,7 @@ // Cast to uint32_t first to ensure bits [63:32] are cleared. const uint64_t scaled_wait_time = static_cast<uint32_t>(lock_value & kWaitTimeMask); - return scaled_wait_time - << (PROFILE_TIMESTAMP_SHIFT - LOCKWORD_RESERVED_SHIFT); + return scaled_wait_time << (kProfileTimestampShift - kLockwordReservedShift); } } // namespace base_internal
diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h index 24e2e9a..89e93aa 100644 --- a/absl/base/internal/spinlock.h +++ b/absl/base/internal/spinlock.h
@@ -148,12 +148,13 @@ // bit[1] encodes whether a lock uses cooperative scheduling. // bit[2] encodes whether a lock disables scheduling. // bit[3:31] encodes time a lock spent on waiting as a 29-bit unsigned int. - enum { kSpinLockHeld = 1 }; - enum { kSpinLockCooperative = 2 }; - enum { kSpinLockDisabledScheduling = 4 }; - enum { kSpinLockSleeper = 8 }; - enum { kWaitTimeMask = // Includes kSpinLockSleeper. - ~(kSpinLockHeld | kSpinLockCooperative | kSpinLockDisabledScheduling) }; + static constexpr uint32_t kSpinLockHeld = 1; + static constexpr uint32_t kSpinLockCooperative = 2; + static constexpr uint32_t kSpinLockDisabledScheduling = 4; + static constexpr uint32_t kSpinLockSleeper = 8; + // Includes kSpinLockSleeper. + static constexpr uint32_t kWaitTimeMask = + ~(kSpinLockHeld | kSpinLockCooperative | kSpinLockDisabledScheduling); // Returns true if the provided scheduling mode is cooperative. static constexpr bool IsCooperative(
diff --git a/absl/base/internal/strerror.cc b/absl/base/internal/strerror.cc new file mode 100644 index 0000000..af18151 --- /dev/null +++ b/absl/base/internal/strerror.cc
@@ -0,0 +1,75 @@ +// Copyright 2020 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 "absl/base/internal/strerror.h" + +#include <cerrno> +#include <cstddef> +#include <cstdio> +#include <cstring> +#include <string> +#include <type_traits> + +#include "absl/base/attributes.h" +#include "absl/base/internal/errno_saver.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { +namespace { +const char* StrErrorAdaptor(int errnum, char* buf, size_t buflen) { +#if defined(_WIN32) + int rc = strerror_s(buf, buflen, errnum); + buf[buflen - 1] = '\0'; // guarantee NUL termination + if (rc == 0 && strncmp(buf, "Unknown error", buflen) == 0) *buf = '\0'; + return buf; +#else +#if defined(__GLIBC__) || defined(__APPLE__) + // Use the BSD sys_errlist API provided by GNU glibc and others to + // avoid any need to copy the message into the local buffer first. + if (0 <= errnum && errnum < sys_nerr) { + if (const char* p = sys_errlist[errnum]) { + return p; + } + } +#endif + // The type of `ret` is platform-specific; both of these branches must compile + // either way but only one will execute on any given platform: + auto ret = strerror_r(errnum, buf, buflen); + if (std::is_same<decltype(ret), int>::value) { + // XSI `strerror_r`; `ret` is `int`: + if (ret) *buf = '\0'; + return buf; + } else { + // GNU `strerror_r`; `ret` is `char *`: + return reinterpret_cast<const char*>(ret); + } +#endif +} +} // namespace + +std::string StrError(int errnum) { + absl::base_internal::ErrnoSaver errno_saver; + char buf[100]; + const char* str = StrErrorAdaptor(errnum, buf, sizeof buf); + if (*str == '\0') { + snprintf(buf, sizeof buf, "Unknown error %d", errnum); + str = buf; + } + return str; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl
diff --git a/absl/base/internal/strerror.h b/absl/base/internal/strerror.h new file mode 100644 index 0000000..3500973 --- /dev/null +++ b/absl/base/internal/strerror.h
@@ -0,0 +1,39 @@ +// Copyright 2020 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. + +#ifndef ABSL_BASE_INTERNAL_STRERROR_H_ +#define ABSL_BASE_INTERNAL_STRERROR_H_ + +#include <string> + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// A portable and thread-safe alternative to C89's `strerror`. +// +// The C89 specification of `strerror` is not suitable for use in a +// multi-threaded application as the returned string may be changed by calls to +// `strerror` from another thread. The many non-stdlib alternatives differ +// enough in their names, availability, and semantics to justify this wrapper +// around them. `errno` will not be modified by a call to `absl::StrError`. +std::string StrError(int errnum); + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_STRERROR_H_
diff --git a/absl/base/internal/strerror_benchmark.cc b/absl/base/internal/strerror_benchmark.cc new file mode 100644 index 0000000..d8ca86b --- /dev/null +++ b/absl/base/internal/strerror_benchmark.cc
@@ -0,0 +1,38 @@ +// Copyright 2020 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 <cerrno> +#include <cstdio> +#include <string> + +#include "absl/base/internal/strerror.h" +#include "benchmark/benchmark.h" + +namespace { +#if defined(__GLIBC__) || defined(__APPLE__) +void BM_SysErrList(benchmark::State& state) { + for (auto _ : state) { + benchmark::DoNotOptimize(std::string(sys_errlist[ERANGE])); + } +} +BENCHMARK(BM_SysErrList); +#endif + +void BM_AbslStrError(benchmark::State& state) { + for (auto _ : state) { + benchmark::DoNotOptimize(absl::base_internal::StrError(ERANGE)); + } +} +BENCHMARK(BM_AbslStrError); +} // namespace
diff --git a/absl/base/internal/strerror_test.cc b/absl/base/internal/strerror_test.cc new file mode 100644 index 0000000..a53da97 --- /dev/null +++ b/absl/base/internal/strerror_test.cc
@@ -0,0 +1,86 @@ +// Copyright 2020 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 "absl/base/internal/strerror.h" + +#include <atomic> +#include <cerrno> +#include <cstdio> +#include <cstring> +#include <string> +#include <thread> // NOLINT(build/c++11) +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/match.h" + +namespace { +using ::testing::AnyOf; +using ::testing::Eq; + +TEST(StrErrorTest, ValidErrorCode) { + errno = ERANGE; + EXPECT_THAT(absl::base_internal::StrError(EDOM), Eq(strerror(EDOM))); + EXPECT_THAT(errno, Eq(ERANGE)); +} + +TEST(StrErrorTest, InvalidErrorCode) { + errno = ERANGE; + EXPECT_THAT(absl::base_internal::StrError(-1), + AnyOf(Eq("No error information"), Eq("Unknown error -1"))); + EXPECT_THAT(errno, Eq(ERANGE)); +} + +TEST(StrErrorTest, MultipleThreads) { + // In this test, we will start up 2 threads and have each one call + // StrError 1000 times, each time with a different errnum. We + // expect that StrError(errnum) will return a string equal to the + // one returned by strerror(errnum), if the code is known. Since + // strerror is known to be thread-hostile, collect all the expected + // strings up front. + const int kNumCodes = 1000; + std::vector<std::string> expected_strings(kNumCodes); + for (int i = 0; i < kNumCodes; ++i) { + expected_strings[i] = strerror(i); + } + + std::atomic_int counter(0); + auto thread_fun = [&]() { + for (int i = 0; i < kNumCodes; ++i) { + ++counter; + errno = ERANGE; + const std::string value = absl::base_internal::StrError(i); + // Only the GNU implementation is guaranteed to provide the + // string "Unknown error nnn". POSIX doesn't say anything. + if (!absl::StartsWith(value, "Unknown error ")) { + EXPECT_THAT(absl::base_internal::StrError(i), Eq(expected_strings[i])); + } + EXPECT_THAT(errno, Eq(ERANGE)); + } + }; + + const int kNumThreads = 100; + std::vector<std::thread> threads; + for (int i = 0; i < kNumThreads; ++i) { + threads.push_back(std::thread(thread_fun)); + } + for (auto& thread : threads) { + thread.join(); + } + + EXPECT_THAT(counter, Eq(kNumThreads * kNumCodes)); +} + +} // namespace
diff --git a/absl/base/macros.h b/absl/base/macros.h index 547f93b..2c4e357 100644 --- a/absl/base/macros.h +++ b/absl/base/macros.h
@@ -32,6 +32,7 @@ #include <cstddef> #include "absl/base/attributes.h" +#include "absl/base/config.h" #include "absl/base/optimization.h" #include "absl/base/port.h" @@ -207,6 +208,41 @@ : [] { assert(false && #expr); }()) // NOLINT #endif +// `ABSL_INTERNAL_HARDENING_ABORT()` controls how `ABSL_HARDENING_ASSERT()` +// aborts the program in release mode (when NDEBUG is defined). The +// implementation should abort the program as quickly as possible and ideally it +// should not be possible to ignore the abort request. +#if (ABSL_HAVE_BUILTIN(__builtin_trap) && \ + ABSL_HAVE_BUILTIN(__builtin_unreachable)) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_INTERNAL_HARDENING_ABORT() \ + do { \ + __builtin_trap(); \ + __builtin_unreachable(); \ + } while (false) +#else +#define ABSL_INTERNAL_HARDENING_ABORT() abort() +#endif + +// ABSL_HARDENING_ASSERT() +// +// `ABSL_HARDENING_ASSERT()` is like `ABSL_ASSERT()`, but used to implement +// runtime assertions that should be enabled in hardened builds even when +// `NDEBUG` is defined. +// +// When `NDEBUG` is not defined, `ABSL_HARDENING_ASSERT()` is identical to +// `ABSL_ASSERT()`. +// +// See `ABSL_OPTION_HARDENED` in `absl/base/options.h` for more information on +// hardened mode. +#if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG) +#define ABSL_HARDENING_ASSERT(expr) \ + (ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) \ + : [] { ABSL_INTERNAL_HARDENING_ABORT(); }()) +#else +#define ABSL_HARDENING_ASSERT(expr) ABSL_ASSERT(expr) +#endif + #ifdef ABSL_HAVE_EXCEPTIONS #define ABSL_INTERNAL_TRY try #define ABSL_INTERNAL_CATCH_ANY catch (...)
diff --git a/absl/base/options.h b/absl/base/options.h index 234137c..230bf1e 100644 --- a/absl/base/options.h +++ b/absl/base/options.h
@@ -1,6 +1,3 @@ -#ifndef ABSL_BASE_OPTIONS_H_ -#define ABSL_BASE_OPTIONS_H_ - // Copyright 2019 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -67,6 +64,9 @@ // proper Abseil implementation at compile-time, which will not be sufficient // to guarantee ABI stability to package managers. +#ifndef ABSL_BASE_OPTIONS_H_ +#define ABSL_BASE_OPTIONS_H_ + // Include a standard library header to allow configuration based on the // standard library in use. #ifdef __cplusplus @@ -208,4 +208,31 @@ #define ABSL_OPTION_USE_INLINE_NAMESPACE 0 #define ABSL_OPTION_INLINE_NAMESPACE_NAME head +// ABSL_OPTION_HARDENED +// +// This option enables a "hardened" build in release mode (in this context, +// release mode is defined as a build where the `NDEBUG` macro is defined). +// +// A value of 0 means that "hardened" mode is not enabled. +// +// A value of 1 means that "hardened" mode is enabled. +// +// Hardened builds have additional security checks enabled when `NDEBUG` is +// defined. Defining `NDEBUG` is normally used to turn `assert()` macro into a +// no-op, as well as disabling other bespoke program consistency checks. By +// defining ABSL_OPTION_HARDENED to 1, a select set of checks remain enabled in +// release mode. These checks guard against programming errors that may lead to +// security vulnerabilities. In release mode, when one of these programming +// errors is encountered, the program will immediately abort, possibly without +// any attempt at logging. +// +// The checks enabled by this option are not free; they do incur runtime cost. +// +// The checks enabled by this option are always active when `NDEBUG` is not +// defined, even in the case when ABSL_OPTION_HARDENED is defined to 0. The +// checks enabled by this option may abort the program in a different way and +// log additional information when `NDEBUG` is not defined. + +#define ABSL_OPTION_HARDENED 0 + #endif // ABSL_BASE_OPTIONS_H_
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index 7098564..1b0710b 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel
@@ -14,7 +14,7 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -74,6 +74,7 @@ linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":fixed_array", + "//absl/base:config", "//absl/base:exception_testing", "//absl/hash:hash_testing", "//absl/memory", @@ -153,6 +154,7 @@ ":counting_allocator", ":inlined_vector", ":test_instance_tracker", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:exception_testing", "//absl/base:raw_logging_internal", @@ -364,6 +366,7 @@ linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/memory", + "//absl/meta:type_traits", "//absl/utility", ], ) @@ -376,6 +379,7 @@ tags = NOTEST_TAGS_NONMOBILE, deps = [ ":container_memory", + ":test_instance_tracker", "//absl/strings", "@com_google_googletest//:gtest_main", ],
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 99a8e9c..d79fa12 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt
@@ -147,6 +147,7 @@ ${ABSL_TEST_COPTS} DEPS absl::fixed_array + absl::config absl::exception_testing absl::hash_testing absl::memory @@ -221,6 +222,7 @@ absl::counting_allocator absl::inlined_vector absl::test_instance_tracker + absl::config absl::core_headers absl::exception_testing absl::hash_testing @@ -419,6 +421,7 @@ ${ABSL_DEFAULT_COPTS} DEPS absl::memory + absl::type_traits absl::utility PUBLIC ) @@ -433,6 +436,7 @@ DEPS absl::container_memory absl::strings + absl::test_instance_tracker gmock_main )
diff --git a/absl/container/btree_benchmark.cc b/absl/container/btree_benchmark.cc index ca4d575..4679867 100644 --- a/absl/container/btree_benchmark.cc +++ b/absl/container/btree_benchmark.cc
@@ -134,6 +134,27 @@ } } +// Benchmark inserting the first few elements in a container. In b-tree, this is +// when the root node grows. +template <typename T> +void BM_InsertSmall(benchmark::State& state) { + using V = typename remove_pair_const<typename T::value_type>::type; + + const int kSize = 8; + std::vector<V> values = GenerateValues<V>(kSize); + T container; + + while (state.KeepRunningBatch(kSize)) { + for (int i = 0; i < kSize; ++i) { + benchmark::DoNotOptimize(container.insert(values[i])); + } + state.PauseTiming(); + // Do not measure the time it takes to clear the container. + container.clear(); + state.ResumeTiming(); + } +} + template <typename T> void BM_LookupImpl(benchmark::State& state, bool sorted) { using V = typename remove_pair_const<typename T::value_type>::type; @@ -493,6 +514,7 @@ MY_BENCHMARK4(type, Insert); \ MY_BENCHMARK4(type, InsertSorted); \ MY_BENCHMARK4(type, InsertEnd); \ + MY_BENCHMARK4(type, InsertSmall); \ MY_BENCHMARK4(type, Lookup); \ MY_BENCHMARK4(type, FullLookup); \ MY_BENCHMARK4(type, Delete); \
diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h index a9ce99b..796dd62 100644 --- a/absl/container/fixed_array.h +++ b/absl/container/fixed_array.h
@@ -217,7 +217,7 @@ // Returns a reference the ith element of the fixed array. // REQUIRES: 0 <= i < size() reference operator[](size_type i) { - assert(i < size()); + ABSL_HARDENING_ASSERT(i < size()); return data()[i]; } @@ -225,7 +225,7 @@ // ith element of the fixed array. // REQUIRES: 0 <= i < size() const_reference operator[](size_type i) const { - assert(i < size()); + ABSL_HARDENING_ASSERT(i < size()); return data()[i]; } @@ -252,20 +252,32 @@ // FixedArray::front() // // Returns a reference to the first element of the fixed array. - reference front() { return *begin(); } + reference front() { + ABSL_HARDENING_ASSERT(!empty()); + return data()[0]; + } // Overload of FixedArray::front() to return a reference to the first element // of a fixed array of const values. - const_reference front() const { return *begin(); } + const_reference front() const { + ABSL_HARDENING_ASSERT(!empty()); + return data()[0]; + } // FixedArray::back() // // Returns a reference to the last element of the fixed array. - reference back() { return *(end() - 1); } + reference back() { + ABSL_HARDENING_ASSERT(!empty()); + return data()[size() - 1]; + } // Overload of FixedArray::back() to return a reference to the last element // of a fixed array of const values. - const_reference back() const { return *(end() - 1); } + const_reference back() const { + ABSL_HARDENING_ASSERT(!empty()); + return data()[size() - 1]; + } // FixedArray::begin() //
diff --git a/absl/container/fixed_array_test.cc b/absl/container/fixed_array_test.cc index c960fe5..9b1c224 100644 --- a/absl/container/fixed_array_test.cc +++ b/absl/container/fixed_array_test.cc
@@ -28,6 +28,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/internal/exception_testing.h" +#include "absl/base/options.h" #include "absl/hash/hash_testing.h" #include "absl/memory/memory.h" @@ -188,6 +189,21 @@ "failed bounds check"); } +TEST(FixedArrayTest, Hardened) { +#if !defined(NDEBUG) || ABSL_OPTION_HARDENED + absl::FixedArray<int> a = {1, 2, 3}; + EXPECT_EQ(a[2], 3); + EXPECT_DEATH_IF_SUPPORTED(a[3], ""); + EXPECT_DEATH_IF_SUPPORTED(a[-1], ""); + + absl::FixedArray<int> empty(0); + EXPECT_DEATH_IF_SUPPORTED(empty[0], ""); + EXPECT_DEATH_IF_SUPPORTED(empty[-1], ""); + EXPECT_DEATH_IF_SUPPORTED(empty.front(), ""); + EXPECT_DEATH_IF_SUPPORTED(empty.back(), ""); +#endif +} + TEST(FixedArrayRelationalsTest, EqualArrays) { for (int i = 0; i < 10; ++i) { absl::FixedArray<int, 5> a1(i);
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index 2388d47..5f6f615 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h
@@ -48,6 +48,7 @@ #include "absl/algorithm/algorithm.h" #include "absl/base/internal/throw_delegate.h" +#include "absl/base/macros.h" #include "absl/base/optimization.h" #include "absl/base/port.h" #include "absl/container/internal/inlined_vector.h" @@ -307,16 +308,14 @@ // // Returns a `reference` to the `i`th element of the inlined vector. reference operator[](size_type i) { - assert(i < size()); - + ABSL_HARDENING_ASSERT(i < size()); return data()[i]; } // Overload of `InlinedVector::operator[](...)` that returns a // `const_reference` to the `i`th element of the inlined vector. const_reference operator[](size_type i) const { - assert(i < size()); - + ABSL_HARDENING_ASSERT(i < size()); return data()[i]; } @@ -331,7 +330,6 @@ base_internal::ThrowStdOutOfRange( "`InlinedVector::at(size_type)` failed bounds check"); } - return data()[i]; } @@ -345,7 +343,6 @@ base_internal::ThrowStdOutOfRange( "`InlinedVector::at(size_type) const` failed bounds check"); } - return data()[i]; } @@ -353,16 +350,14 @@ // // Returns a `reference` to the first element of the inlined vector. reference front() { - assert(!empty()); - + ABSL_HARDENING_ASSERT(!empty()); return at(0); } // Overload of `InlinedVector::front()` that returns a `const_reference` to // the first element of the inlined vector. const_reference front() const { - assert(!empty()); - + ABSL_HARDENING_ASSERT(!empty()); return at(0); } @@ -370,16 +365,14 @@ // // Returns a `reference` to the last element of the inlined vector. reference back() { - assert(!empty()); - + ABSL_HARDENING_ASSERT(!empty()); return at(size() - 1); } // Overload of `InlinedVector::back()` that returns a `const_reference` to the // last element of the inlined vector. const_reference back() const { - assert(!empty()); - + ABSL_HARDENING_ASSERT(!empty()); return at(size() - 1); } @@ -573,8 +566,8 @@ // of `v` starting at `pos`, returning an `iterator` pointing to the first of // the newly inserted elements. iterator insert(const_iterator pos, size_type n, const_reference v) { - assert(pos >= begin()); - assert(pos <= end()); + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); if (ABSL_PREDICT_TRUE(n != 0)) { value_type dealias = v; @@ -600,8 +593,8 @@ EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr> iterator insert(const_iterator pos, ForwardIterator first, ForwardIterator last) { - assert(pos >= begin()); - assert(pos <= end()); + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); if (ABSL_PREDICT_TRUE(first != last)) { return storage_.Insert(pos, IteratorValueAdapter<ForwardIterator>(first), @@ -619,8 +612,8 @@ template <typename InputIterator, DisableIfAtLeastForwardIterator<InputIterator>* = nullptr> iterator insert(const_iterator pos, InputIterator first, InputIterator last) { - assert(pos >= begin()); - assert(pos <= end()); + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); size_type index = std::distance(cbegin(), pos); for (size_type i = index; first != last; ++i, static_cast<void>(++first)) { @@ -636,8 +629,8 @@ // `pos`, returning an `iterator` pointing to the newly emplaced element. template <typename... Args> iterator emplace(const_iterator pos, Args&&... args) { - assert(pos >= begin()); - assert(pos <= end()); + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); value_type dealias(std::forward<Args>(args)...); return storage_.Insert(pos, @@ -670,7 +663,7 @@ // // Destroys the element at `back()`, reducing the size by `1`. void pop_back() noexcept { - assert(!empty()); + ABSL_HARDENING_ASSERT(!empty()); AllocatorTraits::destroy(*storage_.GetAllocPtr(), data() + (size() - 1)); storage_.SubtractSize(1); @@ -683,8 +676,8 @@ // // NOTE: may return `end()`, which is not dereferencable. iterator erase(const_iterator pos) { - assert(pos >= begin()); - assert(pos < end()); + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos < end()); return storage_.Erase(pos, pos + 1); } @@ -695,9 +688,9 @@ // // NOTE: may return `end()`, which is not dereferencable. iterator erase(const_iterator from, const_iterator to) { - assert(from >= begin()); - assert(from <= to); - assert(to <= end()); + ABSL_HARDENING_ASSERT(from >= begin()); + ABSL_HARDENING_ASSERT(from <= to); + ABSL_HARDENING_ASSERT(to <= end()); if (ABSL_PREDICT_TRUE(from != to)) { return storage_.Erase(from, to);
diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc index 5965eac..415c60d 100644 --- a/absl/container/inlined_vector_test.cc +++ b/absl/container/inlined_vector_test.cc
@@ -30,6 +30,7 @@ #include "absl/base/internal/exception_testing.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" +#include "absl/base/options.h" #include "absl/container/internal/counting_allocator.h" #include "absl/container/internal/test_instance_tracker.h" #include "absl/hash/hash_testing.h" @@ -247,6 +248,16 @@ } } +TEST(IntVec, Hardened) { + IntVec v; + Fill(&v, 10); + EXPECT_EQ(v[9], 9); +#if !defined(NDEBUG) || ABSL_OPTION_HARDENED + EXPECT_DEATH_IF_SUPPORTED(v[10], ""); + EXPECT_DEATH_IF_SUPPORTED(v[-1], ""); +#endif +} + // At the end of this test loop, the elements between [erase_begin, erase_end) // should have reference counts == 0, and all others elements should have // reference counts == 1.
diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index d986f81..4504e9c 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h
@@ -776,9 +776,6 @@ // delimiting key in the parent node onto itself. void merge(btree_node *src, allocator_type *alloc); - // Swaps the contents of `this` and `other`. - void swap(btree_node *other, allocator_type *alloc); - // Node allocation/deletion routines. void init_leaf(btree_node *parent, int max_count) { set_parent(parent); @@ -820,6 +817,14 @@ absl::container_internal::SanitizerPoisonObject(slot(i)); } + // Transfers value from slot `src_i` in `src` to slot `dest_i` in `this`. + void transfer(const size_type dest_i, const size_type src_i, btree_node *src, + allocator_type *alloc) { + absl::container_internal::SanitizerUnpoisonObject(slot(dest_i)); + params_type::transfer(alloc, slot(dest_i), src->slot(src_i)); + absl::container_internal::SanitizerPoisonObject(src->slot(src_i)); + } + // Move n values starting at value i in this node into the values starting at // value j in dest_node. void uninitialized_move_n(const size_type n, const size_type i, @@ -932,8 +937,13 @@ } // Accessors for the key/value the iterator is pointing at. - reference operator*() const { return node->value(position); } - pointer operator->() const { return &node->value(position); } + reference operator*() const { + ABSL_HARDENING_ASSERT(node != nullptr); + ABSL_HARDENING_ASSERT(node->start() <= position); + ABSL_HARDENING_ASSERT(node->finish() > position); + return node->value(position); + } + pointer operator->() const { return &operator*(); } btree_iterator &operator++() { increment(); @@ -1752,54 +1762,6 @@ parent()->remove_value(position(), alloc); } -template <typename P> -void btree_node<P>::swap(btree_node *other, allocator_type *alloc) { - using std::swap; - assert(leaf() == other->leaf()); - - // Determine which is the smaller/larger node. - btree_node *smaller = this, *larger = other; - if (smaller->count() > larger->count()) { - swap(smaller, larger); - } - - // Swap the values. - for (slot_type *a = smaller->start_slot(), *b = larger->start_slot(), - *end = smaller->finish_slot(); - a != end; ++a, ++b) { - params_type::swap(alloc, a, b); - } - - // Move values that can't be swapped. - const size_type to_move = larger->count() - smaller->count(); - larger->uninitialized_move_n(to_move, smaller->finish(), smaller->finish(), - smaller, alloc); - larger->value_destroy_n(smaller->finish(), to_move, alloc); - - if (!leaf()) { - // Swap the child pointers. - std::swap_ranges(&smaller->mutable_child(smaller->start()), - &smaller->mutable_child(smaller->finish() + 1), - &larger->mutable_child(larger->start())); - // Update swapped children's parent pointers. - int i = smaller->start(); - int j = larger->start(); - for (; i <= smaller->finish(); ++i, ++j) { - smaller->child(i)->set_parent(smaller); - larger->child(j)->set_parent(larger); - } - // Move the child pointers that couldn't be swapped. - for (; j <= larger->finish(); ++i, ++j) { - smaller->init_child(i, larger->child(j)); - larger->clear_child(j); - } - } - - // Swap the `finish`s. - // TODO(ezb): with floating storage, will also need to swap starts. - swap(mutable_finish(), other->mutable_finish()); -} - //// // btree_iterator methods template <typename N, typename R, typename P> @@ -1812,6 +1774,7 @@ position = node->position(); node = node->parent(); } + // TODO(ezb): assert we aren't incrementing end() instead of handling. if (position == node->finish()) { *this = save; } @@ -1835,6 +1798,7 @@ position = node->position() - 1; node = node->parent(); } + // TODO(ezb): assert we aren't decrementing begin() instead of handling. if (position < node->start()) { *this = save; } @@ -2492,6 +2456,7 @@ ++iter.position; } const int max_count = iter.node->max_count(); + allocator_type *alloc = mutable_allocator(); if (iter.node->count() == max_count) { // Make room in the leaf for the new item. if (max_count < kNodeValues) { @@ -2500,15 +2465,21 @@ assert(iter.node == root()); iter.node = new_leaf_root_node((std::min<int>)(kNodeValues, 2 * max_count)); - iter.node->swap(root(), mutable_allocator()); - delete_leaf_node(root()); - mutable_root() = rightmost_ = iter.node; + // Transfer the values from the old root to the new root. + node_type *old_root = root(); + node_type *new_root = iter.node; + for (int i = old_root->start(), f = old_root->finish(); i < f; ++i) { + new_root->transfer(i, i, old_root, alloc); + } + new_root->set_finish(old_root->finish()); + old_root->set_finish(old_root->start()); + delete_leaf_node(old_root); + mutable_root() = rightmost_ = new_root; } else { rebalance_or_split(&iter); } } - iter.node->emplace_value(iter.position, mutable_allocator(), - std::forward<Args>(args)...); + iter.node->emplace_value(iter.position, alloc, std::forward<Args>(args)...); ++size_; return iter; }
diff --git a/absl/container/internal/common.h b/absl/container/internal/common.h index 5037d80..8990f29 100644 --- a/absl/container/internal/common.h +++ b/absl/container/internal/common.h
@@ -138,6 +138,7 @@ absl::void_t<typename Policy::mapped_type>> : public node_handle_base<PolicyTraits, Alloc> { using Base = node_handle_base<PolicyTraits, Alloc>; + using slot_type = typename PolicyTraits::slot_type; public: using key_type = typename Policy::key_type; @@ -145,7 +146,7 @@ constexpr node_handle() {} - auto key() const -> decltype(PolicyTraits::key(this->slot())) { + auto key() const -> decltype(PolicyTraits::key(std::declval<slot_type*>())) { return PolicyTraits::key(this->slot()); }
diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h index d24b0f8..3487ac1 100644 --- a/absl/container/internal/container_memory.h +++ b/absl/container/internal/container_memory.h
@@ -31,12 +31,16 @@ #include <utility> #include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" #include "absl/utility/utility.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { +template <size_t Alignment> +struct alignas(Alignment) AlignedType {}; + // Allocates at least n bytes aligned to the specified alignment. // Alignment must be a power of 2. It must be positive. // @@ -48,7 +52,7 @@ void* Allocate(Alloc* alloc, size_t n) { static_assert(Alignment > 0, ""); assert(n && "n must be positive"); - struct alignas(Alignment) M {}; + using M = AlignedType<Alignment>; using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>; using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; A mem_alloc(*alloc); @@ -64,7 +68,7 @@ void Deallocate(Alloc* alloc, void* p, size_t n) { static_assert(Alignment > 0, ""); assert(n && "n must be positive"); - struct alignas(Alignment) M {}; + using M = AlignedType<Alignment>; using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>; using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; A mem_alloc(*alloc); @@ -316,11 +320,12 @@ map_slot_type() {} ~map_slot_type() = delete; using value_type = std::pair<const K, V>; - using mutable_value_type = std::pair<K, V>; + using mutable_value_type = + std::pair<absl::remove_const_t<K>, absl::remove_const_t<V>>; value_type value; mutable_value_type mutable_value; - K key; + absl::remove_const_t<K> key; }; template <class K, class V>
diff --git a/absl/container/internal/container_memory_test.cc b/absl/container/internal/container_memory_test.cc index 7942c7b..6a7fcd2 100644 --- a/absl/container/internal/container_memory_test.cc +++ b/absl/container/internal/container_memory_test.cc
@@ -16,10 +16,13 @@ #include <cstdint> #include <tuple> +#include <typeindex> +#include <typeinfo> #include <utility> #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/container/internal/test_instance_tracker.h" #include "absl/strings/string_view.h" namespace absl { @@ -27,6 +30,11 @@ namespace container_internal { namespace { +using ::absl::test_internal::CopyableMovableInstance; +using ::absl::test_internal::InstanceTracker; +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::Gt; using ::testing::Pair; TEST(Memory, AlignmentLargerThanBase) { @@ -45,6 +53,39 @@ Deallocate<2>(&alloc, mem, 3); } +std::map<std::type_index, int>& AllocationMap() { + static auto* map = new std::map<std::type_index, int>; + return *map; +} + +template <typename T> +struct TypeCountingAllocator { + TypeCountingAllocator() = default; + template <typename U> + TypeCountingAllocator(const TypeCountingAllocator<U>&) {} // NOLINT + + using value_type = T; + + T* allocate(size_t n, const void* = nullptr) { + AllocationMap()[typeid(T)] += n; + return std::allocator<T>().allocate(n); + } + void deallocate(T* p, std::size_t n) { + AllocationMap()[typeid(T)] -= n; + return std::allocator<T>().deallocate(p, n); + } +}; + +TEST(Memory, AllocateDeallocateMatchType) { + TypeCountingAllocator<int> alloc; + void* mem = Allocate<1>(&alloc, 1); + // Verify that it was allocated + EXPECT_THAT(AllocationMap(), ElementsAre(Pair(_, Gt(0)))); + Deallocate<1>(&alloc, mem, 1); + // Verify that the deallocation matched. + EXPECT_THAT(AllocationMap(), ElementsAre(Pair(_, 0))); +} + class Fixture : public ::testing::Test { using Alloc = std::allocator<std::string>; @@ -184,6 +225,31 @@ std::make_tuple(0.5))); } +TEST(MapSlotPolicy, ConstKeyAndValue) { + using slot_policy = map_slot_policy<const CopyableMovableInstance, + const CopyableMovableInstance>; + using slot_type = typename slot_policy::slot_type; + + union Slots { + Slots() {} + ~Slots() {} + slot_type slots[100]; + } slots; + + std::allocator< + std::pair<const CopyableMovableInstance, const CopyableMovableInstance>> + alloc; + InstanceTracker tracker; + slot_policy::construct(&alloc, &slots.slots[0], CopyableMovableInstance(1), + CopyableMovableInstance(1)); + for (int i = 0; i < 99; ++i) { + slot_policy::transfer(&alloc, &slots.slots[i + 1], &slots.slots[i]); + } + slot_policy::destroy(&alloc, &slots.slots[99]); + + EXPECT_EQ(tracker.copies(), 0); +} + } // namespace } // namespace container_internal ABSL_NAMESPACE_END
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index fb47f62..e47e1fe 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h
@@ -104,6 +104,7 @@ #include "absl/base/internal/bits.h" #include "absl/base/internal/endian.h" +#include "absl/base/macros.h" #include "absl/base/port.h" #include "absl/container/internal/common.h" #include "absl/container/internal/compressed_tuple.h" @@ -651,9 +652,9 @@ iterator(ctrl_t* ctrl) : ctrl_(ctrl) {} // for end() iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) {} - void assert_is_full() const { assert(IsFull(*ctrl_)); } + void assert_is_full() const { ABSL_HARDENING_ASSERT(IsFull(*ctrl_)); } void assert_is_valid() const { - assert(!ctrl_ || IsFull(*ctrl_) || *ctrl_ == kSentinel); + ABSL_HARDENING_ASSERT(!ctrl_ || IsFull(*ctrl_) || *ctrl_ == kSentinel); } void skip_empty_or_deleted() {
diff --git a/absl/copts/AbseilConfigureCopts.cmake b/absl/copts/AbseilConfigureCopts.cmake index 390a07a..9557e36 100644 --- a/absl/copts/AbseilConfigureCopts.cmake +++ b/absl/copts/AbseilConfigureCopts.cmake
@@ -63,3 +63,5 @@ set(ABSL_DEFAULT_COPTS "") set(ABSL_TEST_COPTS "") endif() + +set(ABSL_CXX_STANDARD "${CMAKE_CXX_STANDARD}")
diff --git a/absl/copts/configure_copts.bzl b/absl/copts/configure_copts.bzl index 9dd6bd0..ff9a5ea 100644 --- a/absl/copts/configure_copts.bzl +++ b/absl/copts/configure_copts.bzl
@@ -48,7 +48,7 @@ ":cpu_darwin": ABSL_RANDOM_HWAES_X64_FLAGS, ":cpu_x64_windows_msvc": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS, ":cpu_x64_windows": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS, - ":cpu_haswell": ABSL_RANDOM_HWAES_X64_FLAGS, + ":cpu_k8": ABSL_RANDOM_HWAES_X64_FLAGS, ":cpu_ppc": ["-mcrypto"], # Supported by default or unsupported. @@ -65,7 +65,7 @@ # These configs have consistent flags to enable HWAES intsructions. cpu_configs = [ "ppc", - "haswell", + "k8", "darwin_x86_64", "darwin", "x64_windows_msvc",
diff --git a/absl/debugging/internal/stacktrace_aarch64-inl.inc b/absl/debugging/internal/stacktrace_aarch64-inl.inc index 411ea30..14a76f1 100644 --- a/absl/debugging/internal/stacktrace_aarch64-inl.inc +++ b/absl/debugging/internal/stacktrace_aarch64-inl.inc
@@ -74,6 +74,8 @@ // checks (the strictness of which is controlled by the boolean parameter // "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. template<bool STRICT_UNWINDING, bool WITH_CONTEXT> +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. static void **NextStackFrame(void **old_frame_pointer, const void *uc) { void **new_frame_pointer = reinterpret_cast<void**>(*old_frame_pointer); bool check_frame_size = true; @@ -123,6 +125,8 @@ } template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT> +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, const void *ucp, int *min_dropped_frames) { #ifdef __GNUC__
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 9166f74..4b51d9d 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel
@@ -14,7 +14,7 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -147,6 +147,7 @@ ":marshalling", "//absl/base:config", "//absl/base:core_headers", + "//absl/base:fast_type_id", "//absl/strings", "//absl/types:optional", ],
diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index 01cf09b..2204b0f 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt
@@ -128,6 +128,7 @@ ${ABSL_DEFAULT_LINKOPTS} DEPS absl::config + absl::fast_type_id absl::flags_config absl::flags_marshalling absl::core_headers
diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index 1e01b49..377e3b2 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc
@@ -91,30 +91,30 @@ }; TEST_F(FlagTest, Traits) { - EXPECT_EQ(flags::FlagValue::Kind<int>(), + EXPECT_EQ(flags::StorageKind<int>(), flags::FlagValueStorageKind::kOneWordAtomic); - EXPECT_EQ(flags::FlagValue::Kind<bool>(), + EXPECT_EQ(flags::StorageKind<bool>(), flags::FlagValueStorageKind::kOneWordAtomic); - EXPECT_EQ(flags::FlagValue::Kind<double>(), + EXPECT_EQ(flags::StorageKind<double>(), flags::FlagValueStorageKind::kOneWordAtomic); - EXPECT_EQ(flags::FlagValue::Kind<int64_t>(), + EXPECT_EQ(flags::StorageKind<int64_t>(), flags::FlagValueStorageKind::kOneWordAtomic); #if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) - EXPECT_EQ(flags::FlagValue::Kind<S1>(), + EXPECT_EQ(flags::StorageKind<S1>(), flags::FlagValueStorageKind::kTwoWordsAtomic); - EXPECT_EQ(flags::FlagValue::Kind<S2>(), + EXPECT_EQ(flags::StorageKind<S2>(), flags::FlagValueStorageKind::kTwoWordsAtomic); #else - EXPECT_EQ(flags::FlagValue::Kind<S1>(), + EXPECT_EQ(flags::StorageKind<S1>(), flags::FlagValueStorageKind::kHeapAllocated); - EXPECT_EQ(flags::FlagValue::Kind<S2>(), + EXPECT_EQ(flags::StorageKind<S2>(), flags::FlagValueStorageKind::kHeapAllocated); #endif - EXPECT_EQ(flags::FlagValue::Kind<std::string>(), + EXPECT_EQ(flags::StorageKind<std::string>(), flags::FlagValueStorageKind::kHeapAllocated); - EXPECT_EQ(flags::FlagValue::Kind<std::vector<std::string>>(), + EXPECT_EQ(flags::StorageKind<std::vector<std::string>>(), flags::FlagValueStorageKind::kHeapAllocated); } @@ -293,6 +293,18 @@ // -------------------------------------------------------------------- TEST_F(FlagTest, TestDefault) { + EXPECT_EQ(FLAGS_test_flag_01.DefaultValue(), "true"); + EXPECT_EQ(FLAGS_test_flag_02.DefaultValue(), "1234"); + EXPECT_EQ(FLAGS_test_flag_03.DefaultValue(), "-34"); + EXPECT_EQ(FLAGS_test_flag_04.DefaultValue(), "189"); + EXPECT_EQ(FLAGS_test_flag_05.DefaultValue(), "10765"); + EXPECT_EQ(FLAGS_test_flag_06.DefaultValue(), "40000"); + EXPECT_EQ(FLAGS_test_flag_07.DefaultValue(), "-1234567"); + EXPECT_EQ(FLAGS_test_flag_08.DefaultValue(), "9876543"); + EXPECT_EQ(FLAGS_test_flag_09.DefaultValue(), "-9.876e-50"); + EXPECT_EQ(FLAGS_test_flag_10.DefaultValue(), "1.234e+12"); + EXPECT_EQ(FLAGS_test_flag_11.DefaultValue(), ""); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), -34); @@ -308,6 +320,61 @@ // -------------------------------------------------------------------- +struct NonTriviallyCopyableAggregate { + NonTriviallyCopyableAggregate() = default; + NonTriviallyCopyableAggregate(const NonTriviallyCopyableAggregate& rhs) + : value(rhs.value) {} + NonTriviallyCopyableAggregate& operator=( + const NonTriviallyCopyableAggregate& rhs) { + value = rhs.value; + return *this; + } + + int value; +}; +bool AbslParseFlag(absl::string_view src, NonTriviallyCopyableAggregate* f, + std::string* e) { + return absl::ParseFlag(src, &f->value, e); +} +std::string AbslUnparseFlag(const NonTriviallyCopyableAggregate& ntc) { + return absl::StrCat(ntc.value); +} + +bool operator==(const NonTriviallyCopyableAggregate& ntc1, + const NonTriviallyCopyableAggregate& ntc2) { + return ntc1.value == ntc2.value; +} + +} // namespace + +ABSL_FLAG(bool, test_flag_eb_01, {}, ""); +ABSL_FLAG(int32_t, test_flag_eb_02, {}, ""); +ABSL_FLAG(int64_t, test_flag_eb_03, {}, ""); +ABSL_FLAG(double, test_flag_eb_04, {}, ""); +ABSL_FLAG(std::string, test_flag_eb_05, {}, ""); +ABSL_FLAG(NonTriviallyCopyableAggregate, test_flag_eb_06, {}, ""); + +namespace { + +TEST_F(FlagTest, TestEmptyBracesDefault) { + EXPECT_EQ(FLAGS_test_flag_eb_01.DefaultValue(), "false"); + EXPECT_EQ(FLAGS_test_flag_eb_02.DefaultValue(), "0"); + EXPECT_EQ(FLAGS_test_flag_eb_03.DefaultValue(), "0"); + EXPECT_EQ(FLAGS_test_flag_eb_04.DefaultValue(), "0"); + EXPECT_EQ(FLAGS_test_flag_eb_05.DefaultValue(), ""); + EXPECT_EQ(FLAGS_test_flag_eb_06.DefaultValue(), "0"); + + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_01), false); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_02), 0); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_03), 0); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_04), 0.0); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_05), ""); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_06), + NonTriviallyCopyableAggregate{}); +} + +// -------------------------------------------------------------------- + TEST_F(FlagTest, TestGetSet) { absl::SetFlag(&FLAGS_test_flag_01, false); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), false); @@ -557,10 +624,10 @@ EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 25); } -// -------------------------------------------------------------------- - } // namespace +// -------------------------------------------------------------------- + ABSL_RETIRED_FLAG(bool, old_bool_flag, true, "old descr"); ABSL_RETIRED_FLAG(int, old_int_flag, (int)std::sqrt(10), "old descr"); ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr"));
diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h index 9a740d5..ef992f7 100644 --- a/absl/flags/internal/commandlineflag.h +++ b/absl/flags/internal/commandlineflag.h
@@ -24,6 +24,7 @@ #include <typeinfo> #include "absl/base/config.h" +#include "absl/base/internal/fast_type_id.h" #include "absl/base/macros.h" #include "absl/flags/config.h" #include "absl/flags/marshalling.h" @@ -34,23 +35,12 @@ ABSL_NAMESPACE_BEGIN namespace flags_internal { -// An alias for flag static type id. Values of type identify the flag value type -// simialarly to typeid(T), but without relying on RTTI being available. In most +// An alias for flag fast type id. This value identifies the flag value type +// simialarly to typeid(T), without relying on RTTI being available. In most // cases this id is enough to uniquely identify the flag's value type. In a few // cases we'll have to resort to using actual RTTI implementation if it is // available. -using FlagStaticTypeId = void* (*)(); - -// Address of this function template is used in current implementation as a flag -// static type id. -template <typename T> -void* FlagStaticTypeIdGen() { -#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI) - return const_cast<std::type_info*>(&typeid(T)); -#else - return nullptr; -#endif -} +using FlagFastTypeId = base_internal::FastTypeIdType; // Options that control SetCommandLineOptionWithMode. enum FlagSettingMode { @@ -65,7 +55,7 @@ SET_FLAGS_DEFAULT }; -// Options that control SetFromString: Source of a value. +// Options that control ParseFrom: Source of a value. enum ValueSource { // Flag is being set by value specified on a command line. kCommandLine, @@ -97,7 +87,7 @@ // Return true iff flag has type T. template <typename T> inline bool IsOfType() const { - return TypeId() == &flags_internal::FlagStaticTypeIdGen<T>; + return TypeId() == base_internal::FastTypeId<T>(); } // Attempts to retrieve the flag value. Returns value on success, @@ -150,7 +140,7 @@ // Returns true iff this is a handle to an Abseil Flag. virtual bool IsAbseilFlag() const; // Returns id of the flag's value type. - virtual FlagStaticTypeId TypeId() const = 0; + virtual FlagFastTypeId TypeId() const = 0; virtual bool IsModified() const = 0; virtual bool IsSpecifiedOnCommandLine() const = 0; virtual std::string DefaultValue() const = 0; @@ -171,10 +161,10 @@ // * Update the flag's default value // * Update the current flag value if it was never set before // The mode is selected based on `set_mode` parameter. - virtual bool SetFromString(absl::string_view value, - flags_internal::FlagSettingMode set_mode, - flags_internal::ValueSource source, - std::string* error) = 0; + virtual bool ParseFrom(absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, + std::string* error) = 0; // Checks that flags default value can be converted to string and back to the // flag's value type.
diff --git a/absl/flags/internal/commandlineflag_test.cc b/absl/flags/internal/commandlineflag_test.cc index 0e8bc31..c1142b7 100644 --- a/absl/flags/internal/commandlineflag_test.cc +++ b/absl/flags/internal/commandlineflag_test.cc
@@ -119,99 +119,99 @@ // -------------------------------------------------------------------- -TEST_F(CommandLineFlagTest, TestSetFromStringCurrentValue) { +TEST_F(CommandLineFlagTest, TestParseFromCurrentValue) { std::string err; auto* flag_01 = flags::FindCommandLineFlag("int_flag"); EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - EXPECT_TRUE(flag_01->SetFromString("11", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flag_01->ParseFrom("11", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11); EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - EXPECT_TRUE(flag_01->SetFromString("-123", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flag_01->ParseFrom("-123", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - EXPECT_TRUE(!flag_01->SetFromString("xyz", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(!flag_01->ParseFrom("xyz", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); EXPECT_EQ(err, "Illegal value 'xyz' specified for flag 'int_flag'"); EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - EXPECT_TRUE(!flag_01->SetFromString("A1", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(!flag_01->ParseFrom("A1", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); EXPECT_EQ(err, "Illegal value 'A1' specified for flag 'int_flag'"); EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - EXPECT_TRUE(flag_01->SetFromString("0x10", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flag_01->ParseFrom("0x10", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 16); EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - EXPECT_TRUE(flag_01->SetFromString("011", flags::SET_FLAGS_VALUE, - flags::kCommandLine, &err)); + EXPECT_TRUE(flag_01->ParseFrom("011", flags::SET_FLAGS_VALUE, + flags::kCommandLine, &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11); EXPECT_TRUE(flag_01->IsSpecifiedOnCommandLine()); - EXPECT_TRUE(!flag_01->SetFromString("", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(!flag_01->ParseFrom("", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); EXPECT_EQ(err, "Illegal value '' specified for flag 'int_flag'"); auto* flag_02 = flags::FindCommandLineFlag("string_flag"); - EXPECT_TRUE(flag_02->SetFromString("xyz", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flag_02->ParseFrom("xyz", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "xyz"); - EXPECT_TRUE(flag_02->SetFromString("", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flag_02->ParseFrom("", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), ""); } // -------------------------------------------------------------------- -TEST_F(CommandLineFlagTest, TestSetFromStringDefaultValue) { +TEST_F(CommandLineFlagTest, TestParseFromDefaultValue) { std::string err; auto* flag_01 = flags::FindCommandLineFlag("int_flag"); - EXPECT_TRUE(flag_01->SetFromString("111", flags::SET_FLAGS_DEFAULT, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flag_01->ParseFrom("111", flags::SET_FLAGS_DEFAULT, + flags::kProgrammaticChange, &err)); EXPECT_EQ(flag_01->DefaultValue(), "111"); auto* flag_02 = flags::FindCommandLineFlag("string_flag"); - EXPECT_TRUE(flag_02->SetFromString("abc", flags::SET_FLAGS_DEFAULT, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flag_02->ParseFrom("abc", flags::SET_FLAGS_DEFAULT, + flags::kProgrammaticChange, &err)); EXPECT_EQ(flag_02->DefaultValue(), "abc"); } // -------------------------------------------------------------------- -TEST_F(CommandLineFlagTest, TestSetFromStringIfDefault) { +TEST_F(CommandLineFlagTest, TestParseFromIfDefault) { std::string err; auto* flag_01 = flags::FindCommandLineFlag("int_flag"); - EXPECT_TRUE(flag_01->SetFromString("22", flags::SET_FLAG_IF_DEFAULT, - flags::kProgrammaticChange, &err)) + EXPECT_TRUE(flag_01->ParseFrom("22", flags::SET_FLAG_IF_DEFAULT, + flags::kProgrammaticChange, &err)) << err; EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22); - EXPECT_TRUE(flag_01->SetFromString("33", flags::SET_FLAG_IF_DEFAULT, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flag_01->ParseFrom("33", flags::SET_FLAG_IF_DEFAULT, + flags::kProgrammaticChange, &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22); // EXPECT_EQ(err, "ERROR: int_flag is already set to 22"); // Reset back to default value - EXPECT_TRUE(flag_01->SetFromString("201", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flag_01->ParseFrom("201", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); - EXPECT_TRUE(flag_01->SetFromString("33", flags::SET_FLAG_IF_DEFAULT, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flag_01->ParseFrom("33", flags::SET_FLAG_IF_DEFAULT, + flags::kProgrammaticChange, &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 201); // EXPECT_EQ(err, "ERROR: int_flag is already set to 201"); }
diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc index a12fe7c..f3c424a 100644 --- a/absl/flags/internal/flag.cc +++ b/absl/flags/internal/flag.cc
@@ -25,6 +25,7 @@ #include <vector> #include "absl/base/attributes.h" +#include "absl/base/casts.h" #include "absl/base/config.h" #include "absl/base/const_init.h" #include "absl/base/optimization.h" @@ -47,9 +48,9 @@ namespace { // Currently we only validate flag values for user-defined flag types. -bool ShouldValidateFlagValue(FlagStaticTypeId flag_type_id) { +bool ShouldValidateFlagValue(FlagFastTypeId flag_type_id) { #define DONT_VALIDATE(T) \ - if (flag_type_id == &FlagStaticTypeIdGen<T>) return false; + if (flag_type_id == base_internal::FastTypeId<T>()) return false; ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(DONT_VALIDATE) #undef DONT_VALIDATE @@ -74,6 +75,59 @@ } // namespace +/////////////////////////////////////////////////////////////////////////////// +// Persistent state of the flag data. + +class FlagImpl; + +class FlagState : public flags_internal::FlagStateInterface { + public: + template <typename V> + FlagState(FlagImpl* flag_impl, const V& v, bool modified, + bool on_command_line, int64_t counter) + : flag_impl_(flag_impl), + value_(v), + modified_(modified), + on_command_line_(on_command_line), + counter_(counter) {} + + ~FlagState() override { + if (flag_impl_->ValueStorageKind() != FlagValueStorageKind::kHeapAllocated) + return; + flags_internal::Delete(flag_impl_->op_, value_.dynamic); + } + + private: + friend class FlagImpl; + + // Restores the flag to the saved state. + void Restore() const override { + if (!flag_impl_->RestoreState(*this)) return; + + ABSL_INTERNAL_LOG( + INFO, absl::StrCat("Restore saved value of ", flag_impl_->Name(), + " to: ", flag_impl_->CurrentValue())); + } + + // Flag and saved flag data. + FlagImpl* flag_impl_; + union SavedValue { + explicit SavedValue(void* v) : dynamic(v) {} + explicit SavedValue(int64_t v) : one_word(v) {} + explicit SavedValue(flags_internal::AlignedTwoWords v) : two_words(v) {} + + void* dynamic; + int64_t one_word; + flags_internal::AlignedTwoWords two_words; + } value_; + bool modified_; + bool on_command_line_; + int64_t counter_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Flag implementation, which does not depend on flag value type. + void FlagImpl::Init() { new (&data_guard_) absl::Mutex; @@ -82,18 +136,18 @@ (*default_value_.gen_func)(), DynValueDeleter{op_}); switch (ValueStorageKind()) { case FlagValueStorageKind::kHeapAllocated: - value_.dynamic = init_value.release(); + HeapAllocatedValue() = init_value.release(); break; case FlagValueStorageKind::kOneWordAtomic: { int64_t atomic_value; std::memcpy(&atomic_value, init_value.get(), Sizeof(op_)); - value_.one_word_atomic.store(atomic_value, std::memory_order_release); + OneWordValue().store(atomic_value, std::memory_order_release); break; } case FlagValueStorageKind::kTwoWordsAtomic: { AlignedTwoWords atomic_value{0, 0}; std::memcpy(&atomic_value, init_value.get(), Sizeof(op_)); - value_.two_words_atomic.store(atomic_value, std::memory_order_release); + TwoWordsValue().store(atomic_value, std::memory_order_release); break; } } @@ -107,24 +161,24 @@ return reinterpret_cast<absl::Mutex*>(&data_guard_); } -void FlagImpl::AssertValidType(FlagStaticTypeId type_id) const { - FlagStaticTypeId this_type_id = flags_internal::StaticTypeId(op_); +void FlagImpl::AssertValidType(FlagFastTypeId rhs_type_id, + const std::type_info* (*gen_rtti)()) const { + FlagFastTypeId lhs_type_id = flags_internal::FastTypeId(op_); - // `type_id` is the type id corresponding to the declaration visibile at the - // call site. `this_type_id` is the type id corresponding to the type stored - // during flag definition. They must match for this operation to be - // well-defined. - if (ABSL_PREDICT_TRUE(type_id == this_type_id)) return; + // `rhs_type_id` is the fast type id corresponding to the declaration + // visibile at the call site. `lhs_type_id` is the fast type id + // corresponding to the type specified in flag definition. They must match + // for this operation to be well-defined. + if (ABSL_PREDICT_TRUE(lhs_type_id == rhs_type_id)) return; - void* lhs_runtime_type_id = type_id(); - void* rhs_runtime_type_id = this_type_id(); + const std::type_info* lhs_runtime_type_id = + flags_internal::RuntimeTypeId(op_); + const std::type_info* rhs_runtime_type_id = (*gen_rtti)(); if (lhs_runtime_type_id == rhs_runtime_type_id) return; #if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI) - if (*reinterpret_cast<std::type_info*>(lhs_runtime_type_id) == - *reinterpret_cast<std::type_info*>(rhs_runtime_type_id)) - return; + if (*lhs_runtime_type_id == *rhs_runtime_type_id) return; #endif ABSL_INTERNAL_LOG( @@ -145,18 +199,18 @@ void FlagImpl::StoreValue(const void* src) { switch (ValueStorageKind()) { case FlagValueStorageKind::kHeapAllocated: - Copy(op_, src, value_.dynamic); + Copy(op_, src, HeapAllocatedValue()); break; case FlagValueStorageKind::kOneWordAtomic: { - int64_t one_word_val; + int64_t one_word_val = 0; std::memcpy(&one_word_val, src, Sizeof(op_)); - value_.one_word_atomic.store(one_word_val, std::memory_order_release); + OneWordValue().store(one_word_val, std::memory_order_release); break; } case FlagValueStorageKind::kTwoWordsAtomic: { AlignedTwoWords two_words_val{0, 0}; std::memcpy(&two_words_val, src, Sizeof(op_)); - value_.two_words_atomic.store(two_words_val, std::memory_order_release); + TwoWordsValue().store(two_words_val, std::memory_order_release); break; } } @@ -172,11 +226,17 @@ return flags_internal::GetUsageConfig().normalize_filename(filename_); } +absl::string_view FlagImpl::Typename() const { return ""; } + std::string FlagImpl::Help() const { return HelpSourceKind() == FlagHelpKind::kLiteral ? help_.literal : help_.gen_func(); } +FlagFastTypeId FlagImpl::TypeId() const { + return flags_internal::FastTypeId(op_); +} + bool FlagImpl::IsModified() const { absl::MutexLock l(DataGuard()); return modified_; @@ -195,21 +255,23 @@ } std::string FlagImpl::CurrentValue() const { - DataGuard(); // Make sure flag initialized + auto* guard = DataGuard(); // Make sure flag initialized switch (ValueStorageKind()) { case FlagValueStorageKind::kHeapAllocated: { - absl::MutexLock l(DataGuard()); - return flags_internal::Unparse(op_, value_.dynamic); + absl::MutexLock l(guard); + return flags_internal::Unparse(op_, HeapAllocatedValue()); } case FlagValueStorageKind::kOneWordAtomic: { const auto one_word_val = - value_.one_word_atomic.load(std::memory_order_acquire); - return flags_internal::Unparse(op_, &one_word_val); + absl::bit_cast<std::array<char, sizeof(int64_t)>>( + OneWordValue().load(std::memory_order_acquire)); + return flags_internal::Unparse(op_, one_word_val.data()); } case FlagValueStorageKind::kTwoWordsAtomic: { const auto two_words_val = - value_.two_words_atomic.load(std::memory_order_acquire); - return flags_internal::Unparse(op_, &two_words_val); + absl::bit_cast<std::array<char, sizeof(AlignedTwoWords)>>( + TwoWordsValue().load(std::memory_order_acquire)); + return flags_internal::Unparse(op_, two_words_val.data()); } } @@ -250,26 +312,78 @@ cb(); } -bool FlagImpl::RestoreState(const void* value, bool modified, - bool on_command_line, int64_t counter) { - { - absl::MutexLock l(DataGuard()); +std::unique_ptr<FlagStateInterface> FlagImpl::SaveState() { + absl::MutexLock l(DataGuard()); - if (counter_ == counter) return false; + bool modified = modified_; + bool on_command_line = on_command_line_; + switch (ValueStorageKind()) { + case FlagValueStorageKind::kHeapAllocated: { + return absl::make_unique<FlagState>( + this, flags_internal::Clone(op_, HeapAllocatedValue()), modified, + on_command_line, counter_); + } + case FlagValueStorageKind::kOneWordAtomic: { + return absl::make_unique<FlagState>( + this, OneWordValue().load(std::memory_order_acquire), modified, + on_command_line, counter_); + } + case FlagValueStorageKind::kTwoWordsAtomic: { + return absl::make_unique<FlagState>( + this, TwoWordsValue().load(std::memory_order_acquire), modified, + on_command_line, counter_); + } + } + return nullptr; +} + +bool FlagImpl::RestoreState(const FlagState& flag_state) { + absl::MutexLock l(DataGuard()); + + if (flag_state.counter_ == counter_) { + return false; } - Write(value); - - { - absl::MutexLock l(DataGuard()); - - modified_ = modified; - on_command_line_ = on_command_line; + switch (ValueStorageKind()) { + case FlagValueStorageKind::kHeapAllocated: + StoreValue(flag_state.value_.dynamic); + break; + case FlagValueStorageKind::kOneWordAtomic: + StoreValue(&flag_state.value_.one_word); + break; + case FlagValueStorageKind::kTwoWordsAtomic: + StoreValue(&flag_state.value_.two_words); + break; } + modified_ = flag_state.modified_; + on_command_line_ = flag_state.on_command_line_; + return true; } +template <typename StorageT> +typename StorageT::value_type& FlagImpl::OffsetValue() const { + char* p = reinterpret_cast<char*>(const_cast<FlagImpl*>(this)); + // The offset is deduced via Flag value type specific op_. + size_t offset = flags_internal::ValueOffset(op_); + + return reinterpret_cast<StorageT*>(p + offset)->value; +} + +void*& FlagImpl::HeapAllocatedValue() const { + assert(ValueStorageKind() == FlagValueStorageKind::kHeapAllocated); + return OffsetValue<FlagHeapAllocatedValue>(); +} +std::atomic<int64_t>& FlagImpl::OneWordValue() const { + assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic); + return OffsetValue<FlagOneWordValue>(); +} +std::atomic<AlignedTwoWords>& FlagImpl::TwoWordsValue() const { + assert(ValueStorageKind() == FlagValueStorageKind::kTwoWordsAtomic); + return OffsetValue<FlagTwoWordsValue>(); +} + // Attempts to parse supplied `value` string using parsing routine in the `flag` // argument. If parsing successful, this function replaces the dst with newly // parsed value. In case if any error is encountered in either step, the error @@ -290,23 +404,22 @@ } void FlagImpl::Read(void* dst) const { - DataGuard(); // Make sure flag initialized + auto* guard = DataGuard(); // Make sure flag initialized switch (ValueStorageKind()) { case FlagValueStorageKind::kHeapAllocated: { - absl::MutexLock l(DataGuard()); - - flags_internal::CopyConstruct(op_, value_.dynamic, dst); + absl::MutexLock l(guard); + flags_internal::CopyConstruct(op_, HeapAllocatedValue(), dst); break; } case FlagValueStorageKind::kOneWordAtomic: { - const auto one_word_val = - value_.one_word_atomic.load(std::memory_order_acquire); + const int64_t one_word_val = + OneWordValue().load(std::memory_order_acquire); std::memcpy(dst, &one_word_val, Sizeof(op_)); break; } case FlagValueStorageKind::kTwoWordsAtomic: { - const auto two_words_val = - value_.two_words_atomic.load(std::memory_order_acquire); + const AlignedTwoWords two_words_val = + TwoWordsValue().load(std::memory_order_acquire); std::memcpy(dst, &two_words_val, Sizeof(op_)); break; } @@ -316,7 +429,7 @@ void FlagImpl::Write(const void* src) { absl::MutexLock l(DataGuard()); - if (ShouldValidateFlagValue(flags_internal::StaticTypeId(op_))) { + if (ShouldValidateFlagValue(flags_internal::FastTypeId(op_))) { std::unique_ptr<void, DynValueDeleter> obj{flags_internal::Clone(op_, src), DynValueDeleter{op_}}; std::string ignored_error; @@ -338,8 +451,8 @@ // * Update the flag's default value // * Update the current flag value if it was never set before // The mode is selected based on 'set_mode' parameter. -bool FlagImpl::SetFromString(absl::string_view value, FlagSettingMode set_mode, - ValueSource source, std::string* err) { +bool FlagImpl::ParseFrom(absl::string_view value, FlagSettingMode set_mode, + ValueSource source, std::string* err) { absl::MutexLock l(DataGuard()); switch (set_mode) {
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index 344e31f..c1bf865 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h
@@ -23,6 +23,7 @@ #include <memory> #include <string> #include <type_traits> +#include <typeinfo> #include "absl/base/call_once.h" #include "absl/base/config.h" @@ -40,9 +41,6 @@ ABSL_NAMESPACE_BEGIN namespace flags_internal { -template <typename T> -class Flag; - /////////////////////////////////////////////////////////////////////////////// // Flag value type operations, eg., parsing, copying, etc. are provided // by function specific to that type with a signature matching FlagOpFn. @@ -53,50 +51,17 @@ kCopy, kCopyConstruct, kSizeof, - kStaticTypeId, + kFastTypeId, + kRuntimeTypeId, kParse, kUnparse, + kValueOffset, }; using FlagOpFn = void* (*)(FlagOp, const void*, void*, void*); -// Flag value specific operations routine. +// Forward declaration for Flag value specific operations. template <typename T> -void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) { - switch (op) { - case FlagOp::kDelete: - delete static_cast<const T*>(v1); - return nullptr; - case FlagOp::kClone: - return new T(*static_cast<const T*>(v1)); - case FlagOp::kCopy: - *static_cast<T*>(v2) = *static_cast<const T*>(v1); - return nullptr; - case FlagOp::kCopyConstruct: - new (v2) T(*static_cast<const T*>(v1)); - return nullptr; - case FlagOp::kSizeof: - return reinterpret_cast<void*>(sizeof(T)); - case FlagOp::kStaticTypeId: - return reinterpret_cast<void*>(&FlagStaticTypeIdGen<T>); - case FlagOp::kParse: { - // Initialize the temporary instance of type T based on current value in - // destination (which is going to be flag's default value). - T temp(*static_cast<T*>(v2)); - if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp, - static_cast<std::string*>(v3))) { - return nullptr; - } - *static_cast<T*>(v2) = std::move(temp); - return v2; - } - case FlagOp::kUnparse: - *static_cast<std::string*>(v2) = - absl::UnparseFlag<T>(*static_cast<const T*>(v1)); - return nullptr; - default: - return nullptr; - } -} +void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3); // Deletes memory interpreting obj as flag value type pointer. inline void Delete(FlagOpFn op, const void* obj) { @@ -133,41 +98,36 @@ return static_cast<size_t>(reinterpret_cast<intptr_t>( op(FlagOp::kSizeof, nullptr, nullptr, nullptr))); } -// Returns static type id coresponding to the value type. -inline FlagStaticTypeId StaticTypeId(FlagOpFn op) { - return reinterpret_cast<FlagStaticTypeId>( - op(FlagOp::kStaticTypeId, nullptr, nullptr, nullptr)); +// Returns fast type id coresponding to the value type. +inline FlagFastTypeId FastTypeId(FlagOpFn op) { + return reinterpret_cast<FlagFastTypeId>( + op(FlagOp::kFastTypeId, nullptr, nullptr, nullptr)); +} +// Returns fast type id coresponding to the value type. +inline const std::type_info* RuntimeTypeId(FlagOpFn op) { + return reinterpret_cast<const std::type_info*>( + op(FlagOp::kRuntimeTypeId, nullptr, nullptr, nullptr)); +} +// Returns offset of the field value_ from the field impl_ inside of +// absl::Flag<T> data. Given FlagImpl pointer p you can get the +// location of the corresponding value as: +// reinterpret_cast<char*>(p) + ValueOffset(). +inline ptrdiff_t ValueOffset(FlagOpFn op) { + // This sequence of casts reverses the sequence from + // `flags_internal::FlagOps()` + return static_cast<ptrdiff_t>(reinterpret_cast<intptr_t>( + op(FlagOp::kValueOffset, nullptr, nullptr, nullptr))); } -/////////////////////////////////////////////////////////////////////////////// -// Persistent state of the flag data. - +// Returns an address of RTTI's typeid(T). template <typename T> -class FlagState : public flags_internal::FlagStateInterface { - public: - FlagState(Flag<T>* flag, T&& cur, bool modified, bool on_command_line, - int64_t counter) - : flag_(flag), - cur_value_(std::move(cur)), - modified_(modified), - on_command_line_(on_command_line), - counter_(counter) {} - - ~FlagState() override = default; - - private: - friend class Flag<T>; - - // Restores the flag to the saved state. - void Restore() const override; - - // Flag and saved flag data. - Flag<T>* flag_; - T cur_value_; - bool modified_; - bool on_command_line_; - int64_t counter_; -}; +inline const std::type_info* GenRuntimeTypeId() { +#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI) + return &typeid(T); +#else + return nullptr; +#endif +} /////////////////////////////////////////////////////////////////////////////// // Flag help auxiliary structs. @@ -263,6 +223,10 @@ struct alignas(16) AlignedTwoWords { int64_t first; int64_t second; + + bool IsInitialized() const { + return first != flags_internal::UninitializedFlagValue(); + } }; template <typename T> @@ -272,8 +236,14 @@ #else // This is actually unused and only here to avoid ifdefs in other palces. struct AlignedTwoWords { - constexpr AlignedTwoWords() = default; - constexpr AlignedTwoWords(int64_t, int64_t) {} + constexpr AlignedTwoWords() noexcept : dummy() {} + constexpr AlignedTwoWords(int64_t, int64_t) noexcept : dummy() {} + char dummy; + + bool IsInitialized() const { + std::abort(); + return true; + } }; // This trait should be type dependent, otherwise SFINAE below will fail @@ -293,23 +263,70 @@ kTwoWordsAtomic = 2 }; -union FlagValue { - constexpr explicit FlagValue(int64_t v) : one_word_atomic(v) {} +template <typename T> +static constexpr FlagValueStorageKind StorageKind() { + return FlagUseHeapStorage<T>::value + ? FlagValueStorageKind::kHeapAllocated + : FlagUseOneWordStorage<T>::value + ? FlagValueStorageKind::kOneWordAtomic + : FlagUseTwoWordsStorage<T>::value + ? FlagValueStorageKind::kTwoWordsAtomic + : FlagValueStorageKind::kHeapAllocated; +} - template <typename T> - static constexpr FlagValueStorageKind Kind() { - return FlagUseHeapStorage<T>::value - ? FlagValueStorageKind::kHeapAllocated - : FlagUseOneWordStorage<T>::value - ? FlagValueStorageKind::kOneWordAtomic - : FlagUseTwoWordsStorage<T>::value - ? FlagValueStorageKind::kTwoWordsAtomic - : FlagValueStorageKind::kHeapAllocated; +struct FlagHeapAllocatedValue { + using value_type = void*; + + value_type value; +}; + +struct FlagOneWordValue { + using value_type = std::atomic<int64_t>; + constexpr FlagOneWordValue() : value(UninitializedFlagValue()) {} + + value_type value; +}; + +struct FlagTwoWordsValue { + using value_type = std::atomic<AlignedTwoWords>; + constexpr FlagTwoWordsValue() + : value(AlignedTwoWords{UninitializedFlagValue(), 0}) {} + + value_type value; +}; + +template <typename T, + FlagValueStorageKind Kind = flags_internal::StorageKind<T>()> +struct FlagValue; + +template <typename T> +struct FlagValue<T, FlagValueStorageKind::kHeapAllocated> + : FlagHeapAllocatedValue { + bool Get(T*) const { return false; } +}; + +template <typename T> +struct FlagValue<T, FlagValueStorageKind::kOneWordAtomic> : FlagOneWordValue { + bool Get(T* dst) const { + int64_t one_word_val = value.load(std::memory_order_acquire); + if (ABSL_PREDICT_FALSE(one_word_val == UninitializedFlagValue())) { + return false; + } + std::memcpy(dst, static_cast<const void*>(&one_word_val), sizeof(T)); + return true; } +}; - void* dynamic; - std::atomic<int64_t> one_word_atomic; - std::atomic<flags_internal::AlignedTwoWords> two_words_atomic; +template <typename T> +struct FlagValue<T, FlagValueStorageKind::kTwoWordsAtomic> : FlagTwoWordsValue { + bool Get(T* dst) const { + AlignedTwoWords two_words_val = value.load(std::memory_order_acquire); + if (ABSL_PREDICT_FALSE(!two_words_val.IsInitialized())) { + return false; + } + std::memcpy(dst, static_cast<const void*>(&two_words_val), sizeof(T)); + return true; + } }; /////////////////////////////////////////////////////////////////////////////// @@ -332,13 +349,15 @@ struct DynValueDeleter { explicit DynValueDeleter(FlagOpFn op_arg = nullptr) : op(op_arg) {} void operator()(void* ptr) const { - if (op != nullptr) Delete(op, ptr); + if (op != nullptr) flags_internal::Delete(op, ptr); } FlagOpFn op; }; -class FlagImpl { +class FlagState; + +class FlagImpl final : public flags_internal::CommandLineFlag { public: constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op, FlagHelpArg help, FlagValueStorageKind value_kind, @@ -355,83 +374,33 @@ counter_(0), callback_(nullptr), default_value_(default_value_gen), - value_(flags_internal::UninitializedFlagValue()), data_guard_{} {} // Constant access methods - absl::string_view Name() const; - std::string Filename() const; - std::string Help() const; - bool IsModified() const ABSL_LOCKS_EXCLUDED(*DataGuard()); - bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(*DataGuard()); - std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(*DataGuard()); - std::string CurrentValue() const ABSL_LOCKS_EXCLUDED(*DataGuard()); - void Read(void* dst) const ABSL_LOCKS_EXCLUDED(*DataGuard()); - - template <typename T, typename std::enable_if<FlagUseHeapStorage<T>::value, - int>::type = 0> - void Get(T* dst) const { - Read(dst); - } - template <typename T, typename std::enable_if<FlagUseOneWordStorage<T>::value, - int>::type = 0> - void Get(T* dst) const { - int64_t one_word_val = - value_.one_word_atomic.load(std::memory_order_acquire); - if (ABSL_PREDICT_FALSE(one_word_val == UninitializedFlagValue())) { - DataGuard(); // Make sure flag initialized - one_word_val = value_.one_word_atomic.load(std::memory_order_acquire); - } - std::memcpy(dst, static_cast<const void*>(&one_word_val), sizeof(T)); - } - template <typename T, typename std::enable_if< - FlagUseTwoWordsStorage<T>::value, int>::type = 0> - void Get(T* dst) const { - DataGuard(); // Make sure flag initialized - const auto two_words_val = - value_.two_words_atomic.load(std::memory_order_acquire); - std::memcpy(dst, &two_words_val, sizeof(T)); - } + void Read(void* dst) const override ABSL_LOCKS_EXCLUDED(*DataGuard()); // Mutating access methods void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard()); - bool SetFromString(absl::string_view value, FlagSettingMode set_mode, - ValueSource source, std::string* err) - ABSL_LOCKS_EXCLUDED(*DataGuard()); // Interfaces to operate on callbacks. void SetCallback(const FlagCallbackFunc mutation_callback) ABSL_LOCKS_EXCLUDED(*DataGuard()); void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); - // Interfaces to save/restore mutable flag data - template <typename T> - std::unique_ptr<FlagStateInterface> SaveState(Flag<T>* flag) const - ABSL_LOCKS_EXCLUDED(*DataGuard()) { - T&& cur_value = flag->Get(); - absl::MutexLock l(DataGuard()); - - return absl::make_unique<FlagState<T>>( - flag, std::move(cur_value), modified_, on_command_line_, counter_); - } - bool RestoreState(const void* value, bool modified, bool on_command_line, - int64_t counter) ABSL_LOCKS_EXCLUDED(*DataGuard()); - - // Value validation interfaces. - void CheckDefaultValueParsingRoundtrip() const - ABSL_LOCKS_EXCLUDED(*DataGuard()); - bool ValidateInputValue(absl::string_view value) const - ABSL_LOCKS_EXCLUDED(*DataGuard()); - // Used in read/write operations to validate source/target has correct type. // For example if flag is declared as absl::Flag<int> FLAGS_foo, a call to // absl::GetFlag(FLAGS_foo) validates that the type of FLAGS_foo is indeed // int. To do that we pass the "assumed" type id (which is deduced from type - // int) as an argument `op`, which is in turn is validated against the type id - // stored in flag object by flag definition statement. - void AssertValidType(FlagStaticTypeId type_id) const; + // int) as an argument `type_id`, which is in turn is validated against the + // type id stored in flag object by flag definition statement. + void AssertValidType(FlagFastTypeId type_id, + const std::type_info* (*gen_rtti)()) const; private: + template <typename T> + friend class Flag; + friend class FlagState; + // Ensures that `data_guard_` is initialized and returns it. absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED((absl::Mutex*)&data_guard_); // Returns heap allocated value of type T initialized with default value. @@ -439,6 +408,25 @@ ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); // Flag initialization called via absl::call_once. void Init(); + + // Offset value access methods. One per storage kind. These methods to not + // respect const correctness, so be very carefull using them. + + // This is a shared helper routine which encapsulates most of the magic. Since + // it is only used inside the three routines below, which are defined in + // flag.cc, we can define it in that file as well. + template <typename StorageT> + typename StorageT::value_type& OffsetValue() const; + // This is an accessor for a value stored in heap allocated storage. + // Returns a mutable reference to a pointer to allow vlaue mutation. + void*& HeapAllocatedValue() const; + // This is an accessor for a value stored as one word atomic. Returns a + // mutable reference to an atomic value. + std::atomic<int64_t>& OneWordValue() const; + // This is an accessor for a value stored as two words atomic. Returns a + // mutable reference to an atomic value. + std::atomic<AlignedTwoWords>& TwoWordsValue() const; + // Attempts to parse supplied `value` string. If parsing is successful, // returns new value. Otherwise returns nullptr. std::unique_ptr<void, DynValueDeleter> TryParse(absl::string_view value, @@ -458,6 +446,37 @@ return static_cast<FlagDefaultKind>(def_kind_); } + // CommandLineFlag interface implementation + absl::string_view Name() const override; + std::string Filename() const override; + absl::string_view Typename() const override; + std::string Help() const override; + FlagFastTypeId TypeId() const override; + bool IsModified() const override ABSL_LOCKS_EXCLUDED(*DataGuard()); + bool IsSpecifiedOnCommandLine() const override + ABSL_LOCKS_EXCLUDED(*DataGuard()); + std::string DefaultValue() const override ABSL_LOCKS_EXCLUDED(*DataGuard()); + std::string CurrentValue() const override ABSL_LOCKS_EXCLUDED(*DataGuard()); + bool ValidateInputValue(absl::string_view value) const override + ABSL_LOCKS_EXCLUDED(*DataGuard()); + void CheckDefaultValueParsingRoundtrip() const override + ABSL_LOCKS_EXCLUDED(*DataGuard()); + + // Interfaces to save and restore flags to/from persistent state. + // Returns current flag state or nullptr if flag does not support + // saving and restoring a state. + std::unique_ptr<FlagStateInterface> SaveState() override + ABSL_LOCKS_EXCLUDED(*DataGuard()); + + // Restores the flag state to the supplied state object. If there is + // nothing to restore returns false. Otherwise returns true. + bool RestoreState(const FlagState& flag_state) + ABSL_LOCKS_EXCLUDED(*DataGuard()); + + bool ParseFrom(absl::string_view value, FlagSettingMode set_mode, + ValueSource source, std::string* error) override + ABSL_LOCKS_EXCLUDED(*DataGuard()); + // Immutable flag's state. // Flags name passed to ABSL_FLAG as second arg. @@ -505,13 +524,6 @@ // these two cases. FlagDefaultSrc default_value_; - // Atomically mutable flag's state - - // Flag's value. This can be either the atomically stored small value or - // pointer to the heap allocated dynamic value. value_storage_kind_ is used - // to distinguish these cases. - FlagValue value_; - // This is reserved space for an absl::Mutex to guard flag data. It will be // initialized in FlagImpl::Init via placement new. // We can't use "absl::Mutex data_guard_", since this class is not literal. @@ -527,12 +539,13 @@ // flag reflection handle interface. template <typename T> -class Flag final : public flags_internal::CommandLineFlag { +class Flag { public: constexpr Flag(const char* name, const char* filename, const FlagHelpArg help, const FlagDfltGenFunc default_value_gen) - : impl_(name, filename, &FlagOps<T>, help, FlagValue::Kind<T>(), - default_value_gen) {} + : impl_(name, filename, &FlagOps<T>, help, + flags_internal::StorageKind<T>(), default_value_gen), + value_() {} T Get() const { // See implementation notes in CommandLineFlag::Get(). @@ -544,14 +557,14 @@ U u; #if !defined(NDEBUG) - impl_.AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>); + impl_.AssertValidType(base_internal::FastTypeId<T>(), &GenRuntimeTypeId<T>); #endif - impl_.Get(&u.value); + if (!value_.Get(&u.value)) impl_.Read(&u.value); return std::move(u.value); } void Set(const T& v) { - impl_.AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>); + impl_.AssertValidType(base_internal::FastTypeId<T>(), &GenRuntimeTypeId<T>); impl_.Write(&v); } void SetCallback(const FlagCallbackFunc mutation_callback) { @@ -559,60 +572,79 @@ } // CommandLineFlag interface - absl::string_view Name() const override { return impl_.Name(); } - std::string Filename() const override { return impl_.Filename(); } - absl::string_view Typename() const override { return ""; } - std::string Help() const override { return impl_.Help(); } - bool IsModified() const override { return impl_.IsModified(); } - bool IsSpecifiedOnCommandLine() const override { + absl::string_view Name() const { return impl_.Name(); } + std::string Filename() const { return impl_.Filename(); } + absl::string_view Typename() const { return ""; } + std::string Help() const { return impl_.Help(); } + bool IsModified() const { return impl_.IsModified(); } + bool IsSpecifiedOnCommandLine() const { return impl_.IsSpecifiedOnCommandLine(); } - std::string DefaultValue() const override { return impl_.DefaultValue(); } - std::string CurrentValue() const override { return impl_.CurrentValue(); } - bool ValidateInputValue(absl::string_view value) const override { - return impl_.ValidateInputValue(value); - } - - // Interfaces to save and restore flags to/from persistent state. - // Returns current flag state or nullptr if flag does not support - // saving and restoring a state. - std::unique_ptr<FlagStateInterface> SaveState() override { - return impl_.SaveState(this); - } - - // Restores the flag state to the supplied state object. If there is - // nothing to restore returns false. Otherwise returns true. - bool RestoreState(const FlagState<T>& flag_state) { - return impl_.RestoreState(&flag_state.cur_value_, flag_state.modified_, - flag_state.on_command_line_, flag_state.counter_); - } - bool SetFromString(absl::string_view value, FlagSettingMode set_mode, - ValueSource source, std::string* error) override { - return impl_.SetFromString(value, set_mode, source, error); - } - void CheckDefaultValueParsingRoundtrip() const override { - impl_.CheckDefaultValueParsingRoundtrip(); - } + std::string DefaultValue() const { return impl_.DefaultValue(); } + std::string CurrentValue() const { return impl_.CurrentValue(); } private: - friend class FlagState<T>; - - void Read(void* dst) const override { impl_.Read(dst); } - FlagStaticTypeId TypeId() const override { return &FlagStaticTypeIdGen<T>; } + template <typename U, bool do_register> + friend class FlagRegistrar; // Flag's data + // The implementation depends on value_ field to be placed exactly after the + // impl_ field, so that impl_ can figure out the offset to the value and + // access it. FlagImpl impl_; + FlagValue<T> value_; }; +/////////////////////////////////////////////////////////////////////////////// +// Implementation of Flag value specific operations routine. template <typename T> -void FlagState<T>::Restore() const { - if (flag_->RestoreState(*this)) { - ABSL_INTERNAL_LOG(INFO, - absl::StrCat("Restore saved value of ", flag_->Name(), - " to: ", flag_->CurrentValue())); +void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) { + switch (op) { + case FlagOp::kDelete: + delete static_cast<const T*>(v1); + return nullptr; + case FlagOp::kClone: + return new T(*static_cast<const T*>(v1)); + case FlagOp::kCopy: + *static_cast<T*>(v2) = *static_cast<const T*>(v1); + return nullptr; + case FlagOp::kCopyConstruct: + new (v2) T(*static_cast<const T*>(v1)); + return nullptr; + case FlagOp::kSizeof: + return reinterpret_cast<void*>(static_cast<uintptr_t>(sizeof(T))); + case FlagOp::kFastTypeId: + return const_cast<void*>(base_internal::FastTypeId<T>()); + case FlagOp::kRuntimeTypeId: + return const_cast<std::type_info*>(GenRuntimeTypeId<T>()); + case FlagOp::kParse: { + // Initialize the temporary instance of type T based on current value in + // destination (which is going to be flag's default value). + T temp(*static_cast<T*>(v2)); + if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp, + static_cast<std::string*>(v3))) { + return nullptr; + } + *static_cast<T*>(v2) = std::move(temp); + return v2; + } + case FlagOp::kUnparse: + *static_cast<std::string*>(v2) = + absl::UnparseFlag<T>(*static_cast<const T*>(v1)); + return nullptr; + case FlagOp::kValueOffset: { + // Round sizeof(FlagImp) to a multiple of alignof(FlagValue<T>) to get the + // offset of the data. + ptrdiff_t round_to = alignof(FlagValue<T>); + ptrdiff_t offset = + (sizeof(FlagImpl) + round_to - 1) / round_to * round_to; + return reinterpret_cast<void*>(offset); + } } + return nullptr; } +/////////////////////////////////////////////////////////////////////////////// // This class facilitates Flag object registration and tail expression-based // flag definition, for example: // ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher); @@ -620,7 +652,7 @@ class FlagRegistrar { public: explicit FlagRegistrar(Flag<T>* flag) : flag_(flag) { - if (do_register) flags_internal::RegisterCommandLineFlag(flag_); + if (do_register) flags_internal::RegisterCommandLineFlag(&flag_->impl_); } FlagRegistrar& OnUpdate(FlagCallbackFunc cb) && { @@ -647,7 +679,7 @@ template <typename T> T* MakeFromDefaultValue(EmptyBraces) { - return new T; + return new T{}; } } // namespace flags_internal
diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc index e434a85..eb619c7 100644 --- a/absl/flags/internal/registry.cc +++ b/absl/flags/internal/registry.cc
@@ -284,14 +284,14 @@ class RetiredFlagObj final : public flags_internal::CommandLineFlag { public: - constexpr RetiredFlagObj(const char* name, FlagStaticTypeId type_id) + constexpr RetiredFlagObj(const char* name, FlagFastTypeId type_id) : name_(name), type_id_(type_id) {} private: absl::string_view Name() const override { return name_; } std::string Filename() const override { return "RETIRED"; } absl::string_view Typename() const override { return ""; } - FlagStaticTypeId TypeId() const override { return type_id_; } + FlagFastTypeId TypeId() const override { return type_id_; } std::string Help() const override { return ""; } bool IsRetired() const override { return true; } bool IsModified() const override { return false; } @@ -306,8 +306,8 @@ return nullptr; } - bool SetFromString(absl::string_view, flags_internal::FlagSettingMode, - flags_internal::ValueSource, std::string*) override { + bool ParseFrom(absl::string_view, flags_internal::FlagSettingMode, + flags_internal::ValueSource, std::string*) override { return false; } @@ -317,7 +317,7 @@ // Data members const char* const name_; - const FlagStaticTypeId type_id_; + const FlagFastTypeId type_id_; }; void DestroyRetiredFlag(flags_internal::CommandLineFlag* flag) { @@ -327,7 +327,7 @@ } // namespace -bool Retire(const char* name, FlagStaticTypeId type_id) { +bool Retire(const char* name, FlagFastTypeId type_id) { auto* flag = new flags_internal::RetiredFlagObj(name, type_id); FlagRegistry::GlobalRegistry()->RegisterFlag(flag); return true;
diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h index 69ff889..af8ed6b 100644 --- a/absl/flags/internal/registry.h +++ b/absl/flags/internal/registry.h
@@ -79,12 +79,12 @@ // // Retire flag with name "name" and type indicated by ops. -bool Retire(const char* name, FlagStaticTypeId type_id); +bool Retire(const char* name, FlagFastTypeId type_id); // Registered a retired flag with name 'flag_name' and type 'T'. template <typename T> inline bool RetiredFlag(const char* flag_name) { - return flags_internal::Retire(flag_name, &FlagStaticTypeIdGen<T>); + return flags_internal::Retire(flag_name, base_internal::FastTypeId<T>()); } // If the flag is retired, returns true and indicates in |*type_is_bool|
diff --git a/absl/flags/internal/type_erased.cc b/absl/flags/internal/type_erased.cc index 490bc4e..75b4cdf 100644 --- a/absl/flags/internal/type_erased.cc +++ b/absl/flags/internal/type_erased.cc
@@ -56,7 +56,7 @@ if (!flag || flag->IsRetired()) return false; std::string error; - if (!flag->SetFromString(value, set_mode, kProgrammaticChange, &error)) { + if (!flag->ParseFrom(value, set_mode, kProgrammaticChange, &error)) { // Errors here are all of the form: the provided name was a recognized // flag, but the value was invalid (bad type, or validation failed). flags_internal::ReportUsageError(error, false);
diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc index af5fb12..b60b36f 100644 --- a/absl/flags/parse.cc +++ b/absl/flags/parse.cc
@@ -696,7 +696,7 @@ if (flag->IsRetired()) continue; std::string error; - if (!flag->SetFromString(value, SET_FLAGS_VALUE, kCommandLine, &error)) { + if (!flag->ParseFrom(value, SET_FLAGS_VALUE, kCommandLine, &error)) { flags_internal::ReportUsageError(error, true); success = false; }
diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel index f78fbc7..4d94e1b 100644 --- a/absl/random/BUILD.bazel +++ b/absl/random/BUILD.bazel
@@ -53,7 +53,6 @@ "bernoulli_distribution.h", "beta_distribution.h", "discrete_distribution.h", - "distribution_format_traits.h", "distributions.h", "exponential_distribution.h", "gaussian_distribution.h", @@ -141,16 +140,12 @@ cc_library( name = "mocking_bit_gen", testonly = 1, - srcs = [ - "mocking_bit_gen.cc", - ], hdrs = [ "mocking_bit_gen.h", ], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":distributions", - "//absl/base:raw_logging_internal", "//absl/container:flat_hash_map", "//absl/meta:type_traits", "//absl/random/internal:distribution_caller",
diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt index efa55d8..69bedbd 100644 --- a/absl/random/CMakeLists.txt +++ b/absl/random/CMakeLists.txt
@@ -102,8 +102,6 @@ HDRS "mock_distributions.h" "mocking_bit_gen.h" - SRCS - "mocking_bit_gen.cc" COPTS ${ABSL_DEFAULT_COPTS} LINKOPTS @@ -168,7 +166,6 @@ "bernoulli_distribution.h" "beta_distribution.h" "discrete_distribution.h" - "distribution_format_traits.h" "distributions.h" "exponential_distribution.h" "gaussian_distribution.h"
diff --git a/absl/random/bit_gen_ref.h b/absl/random/bit_gen_ref.h index e877116..59591a4 100644 --- a/absl/random/bit_gen_ref.h +++ b/absl/random/bit_gen_ref.h
@@ -132,7 +132,7 @@ template <> struct DistributionCaller<absl::BitGenRef> { - template <typename DistrT, typename FormatT, typename... Args> + template <typename DistrT, typename... Args> static typename DistrT::result_type Call(absl::BitGenRef* gen_ref, Args&&... args) { auto* mock_ptr = gen_ref->mocked_gen_ptr_; @@ -140,8 +140,7 @@ DistrT dist(std::forward<Args>(args)...); return dist(*gen_ref); } else { - return mock_ptr->template Call<DistrT, FormatT>( - std::forward<Args>(args)...); + return mock_ptr->template Call<DistrT>(std::forward<Args>(args)...); } } };
diff --git a/absl/random/distribution_format_traits.h b/absl/random/distribution_format_traits.h deleted file mode 100644 index 22b358c..0000000 --- a/absl/random/distribution_format_traits.h +++ /dev/null
@@ -1,278 +0,0 @@ -// -// Copyright 2018 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// 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. -// -#ifndef ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_ -#define ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_ - -#include <string> -#include <tuple> -#include <typeinfo> - -#include "absl/meta/type_traits.h" -#include "absl/random/bernoulli_distribution.h" -#include "absl/random/beta_distribution.h" -#include "absl/random/exponential_distribution.h" -#include "absl/random/gaussian_distribution.h" -#include "absl/random/log_uniform_int_distribution.h" -#include "absl/random/poisson_distribution.h" -#include "absl/random/uniform_int_distribution.h" -#include "absl/random/uniform_real_distribution.h" -#include "absl/random/zipf_distribution.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_join.h" -#include "absl/strings/string_view.h" -#include "absl/types/span.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN - -struct IntervalClosedClosedTag; -struct IntervalClosedOpenTag; -struct IntervalOpenClosedTag; -struct IntervalOpenOpenTag; - -namespace random_internal { - -// ScalarTypeName defines a preferred hierarchy of preferred type names for -// scalars, and is evaluated at compile time for the specific type -// specialization. -template <typename T> -constexpr const char* ScalarTypeName() { - static_assert(std::is_integral<T>() || std::is_floating_point<T>(), ""); - // clang-format off - return - std::is_same<T, float>::value ? "float" : - std::is_same<T, double>::value ? "double" : - std::is_same<T, long double>::value ? "long double" : - std::is_same<T, bool>::value ? "bool" : - std::is_signed<T>::value && sizeof(T) == 1 ? "int8_t" : - std::is_signed<T>::value && sizeof(T) == 2 ? "int16_t" : - std::is_signed<T>::value && sizeof(T) == 4 ? "int32_t" : - std::is_signed<T>::value && sizeof(T) == 8 ? "int64_t" : - std::is_unsigned<T>::value && sizeof(T) == 1 ? "uint8_t" : - std::is_unsigned<T>::value && sizeof(T) == 2 ? "uint16_t" : - std::is_unsigned<T>::value && sizeof(T) == 4 ? "uint32_t" : - std::is_unsigned<T>::value && sizeof(T) == 8 ? "uint64_t" : - "undefined"; - // clang-format on - - // NOTE: It would be nice to use typeid(T).name(), but that's an - // implementation-defined attribute which does not necessarily - // correspond to a name. We could potentially demangle it - // using, e.g. abi::__cxa_demangle. -} - -// Distribution traits used by DistributionCaller and internal implementation -// details of the mocking framework. -/* -struct DistributionFormatTraits { - // Returns the parameterized name of the distribution function. - static constexpr const char* FunctionName() - // Format DistrT parameters. - static std::string FormatArgs(DistrT& dist); - // Format DistrT::result_type results. - static std::string FormatResults(DistrT& dist); -}; -*/ -template <typename DistrT> -struct DistributionFormatTraits; - -template <typename R> -struct DistributionFormatTraits<absl::uniform_int_distribution<R>> { - using distribution_t = absl::uniform_int_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Uniform"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat("absl::IntervalClosedClosed, ", (d.min)(), ", ", - (d.max)()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename R> -struct DistributionFormatTraits<absl::uniform_real_distribution<R>> { - using distribution_t = absl::uniform_real_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Uniform"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat((d.min)(), ", ", (d.max)()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename R> -struct DistributionFormatTraits<absl::exponential_distribution<R>> { - using distribution_t = absl::exponential_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Exponential"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat(d.lambda()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename R> -struct DistributionFormatTraits<absl::poisson_distribution<R>> { - using distribution_t = absl::poisson_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Poisson"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat(d.mean()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <> -struct DistributionFormatTraits<absl::bernoulli_distribution> { - using distribution_t = absl::bernoulli_distribution; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Bernoulli"; } - - static constexpr const char* FunctionName() { return Name(); } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat(d.p()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename R> -struct DistributionFormatTraits<absl::beta_distribution<R>> { - using distribution_t = absl::beta_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Beta"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat(d.alpha(), ", ", d.beta()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename R> -struct DistributionFormatTraits<absl::zipf_distribution<R>> { - using distribution_t = absl::zipf_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Zipf"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat(d.k(), ", ", d.v(), ", ", d.q()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename R> -struct DistributionFormatTraits<absl::gaussian_distribution<R>> { - using distribution_t = absl::gaussian_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Gaussian"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrJoin(std::make_tuple(d.mean(), d.stddev()), ", "); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename R> -struct DistributionFormatTraits<absl::log_uniform_int_distribution<R>> { - using distribution_t = absl::log_uniform_int_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "LogUniform"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrJoin(std::make_tuple((d.min)(), (d.max)(), d.base()), ", "); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename NumType> -struct UniformDistributionWrapper; - -template <typename NumType> -struct DistributionFormatTraits<UniformDistributionWrapper<NumType>> { - using distribution_t = UniformDistributionWrapper<NumType>; - using result_t = NumType; - - static constexpr const char* Name() { return "Uniform"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<NumType>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat((d.min)(), ", ", (d.max)()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -} // namespace random_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_
diff --git a/absl/random/distributions.h b/absl/random/distributions.h index d026d92..8680f6a 100644 --- a/absl/random/distributions.h +++ b/absl/random/distributions.h
@@ -55,7 +55,6 @@ #include "absl/base/internal/inline_variable.h" #include "absl/random/bernoulli_distribution.h" #include "absl/random/beta_distribution.h" -#include "absl/random/distribution_format_traits.h" #include "absl/random/exponential_distribution.h" #include "absl/random/gaussian_distribution.h" #include "absl/random/internal/distributions.h" // IWYU pragma: export @@ -126,14 +125,13 @@ R lo, R hi) { using gen_t = absl::decay_t<URBG>; using distribution_t = random_internal::UniformDistributionWrapper<R>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; auto a = random_internal::uniform_lower_bound(tag, lo, hi); auto b = random_internal::uniform_upper_bound(tag, lo, hi); if (a > b) return a; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, tag, lo, hi); + distribution_t>(&urbg, tag, lo, hi); } // absl::Uniform<T>(bitgen, lo, hi) @@ -146,7 +144,6 @@ R lo, R hi) { using gen_t = absl::decay_t<URBG>; using distribution_t = random_internal::UniformDistributionWrapper<R>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; constexpr auto tag = absl::IntervalClosedOpen; auto a = random_internal::uniform_lower_bound(tag, lo, hi); @@ -154,7 +151,7 @@ if (a > b) return a; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, lo, hi); + distribution_t>(&urbg, lo, hi); } // absl::Uniform(tag, bitgen, lo, hi) @@ -172,14 +169,13 @@ using gen_t = absl::decay_t<URBG>; using return_t = typename random_internal::uniform_inferred_return_t<A, B>; using distribution_t = random_internal::UniformDistributionWrapper<return_t>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi); auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi); if (a > b) return a; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, tag, static_cast<return_t>(lo), + distribution_t>(&urbg, tag, static_cast<return_t>(lo), static_cast<return_t>(hi)); } @@ -196,7 +192,6 @@ using gen_t = absl::decay_t<URBG>; using return_t = typename random_internal::uniform_inferred_return_t<A, B>; using distribution_t = random_internal::UniformDistributionWrapper<return_t>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; constexpr auto tag = absl::IntervalClosedOpen; auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi); @@ -204,7 +199,7 @@ if (a > b) return a; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, static_cast<return_t>(lo), + distribution_t>(&urbg, static_cast<return_t>(lo), static_cast<return_t>(hi)); } @@ -217,10 +212,9 @@ Uniform(URBG&& urbg) { // NOLINT(runtime/references) using gen_t = absl::decay_t<URBG>; using distribution_t = random_internal::UniformDistributionWrapper<R>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg); + distribution_t>(&urbg); } // ----------------------------------------------------------------------------- @@ -248,10 +242,9 @@ double p) { using gen_t = absl::decay_t<URBG>; using distribution_t = absl::bernoulli_distribution; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, p); + distribution_t>(&urbg, p); } // ----------------------------------------------------------------------------- @@ -281,10 +274,9 @@ using gen_t = absl::decay_t<URBG>; using distribution_t = typename absl::beta_distribution<RealType>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, alpha, beta); + distribution_t>(&urbg, alpha, beta); } // ----------------------------------------------------------------------------- @@ -314,10 +306,9 @@ using gen_t = absl::decay_t<URBG>; using distribution_t = typename absl::exponential_distribution<RealType>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, lambda); + distribution_t>(&urbg, lambda); } // ----------------------------------------------------------------------------- @@ -346,10 +337,9 @@ using gen_t = absl::decay_t<URBG>; using distribution_t = typename absl::gaussian_distribution<RealType>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, mean, stddev); + distribution_t>(&urbg, mean, stddev); } // ----------------------------------------------------------------------------- @@ -389,10 +379,9 @@ using gen_t = absl::decay_t<URBG>; using distribution_t = typename absl::log_uniform_int_distribution<IntType>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, lo, hi, base); + distribution_t>(&urbg, lo, hi, base); } // ----------------------------------------------------------------------------- @@ -420,10 +409,9 @@ using gen_t = absl::decay_t<URBG>; using distribution_t = typename absl::poisson_distribution<IntType>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, mean); + distribution_t>(&urbg, mean); } // ----------------------------------------------------------------------------- @@ -453,10 +441,9 @@ using gen_t = absl::decay_t<URBG>; using distribution_t = typename absl::zipf_distribution<IntType>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, hi, q, v); + distribution_t>(&urbg, hi, q, v); } ABSL_NAMESPACE_END
diff --git a/absl/random/internal/distribution_caller.h b/absl/random/internal/distribution_caller.h index 02603cf..4e07244 100644 --- a/absl/random/internal/distribution_caller.h +++ b/absl/random/internal/distribution_caller.h
@@ -33,20 +33,7 @@ // Call the provided distribution type. The parameters are expected // to be explicitly specified. // DistrT is the distribution type. - // FormatT is the formatter type: - // - // struct FormatT { - // using result_type = distribution_t::result_type; - // static std::string FormatCall( - // const distribution_t& distr, - // absl::Span<const result_type>); - // - // static std::string FormatExpectation( - // absl::string_view match_args, - // absl::Span<const result_t> results); - // } - // - template <typename DistrT, typename FormatT, typename... Args> + template <typename DistrT, typename... Args> static typename DistrT::result_type Call(URBG* urbg, Args&&... args) { DistrT dist(std::forward<Args>(args)...); return dist(*urbg);
diff --git a/absl/random/internal/mocking_bit_gen_base.h b/absl/random/internal/mocking_bit_gen_base.h index eeeae9d..23ecaf6 100644 --- a/absl/random/internal/mocking_bit_gen_base.h +++ b/absl/random/internal/mocking_bit_gen_base.h
@@ -16,8 +16,6 @@ #ifndef ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ #define ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ -#include <atomic> -#include <deque> #include <string> #include <typeinfo> @@ -28,27 +26,6 @@ ABSL_NAMESPACE_BEGIN namespace random_internal { -// MockingBitGenExpectationFormatter is invoked to format unsatisfied mocks -// and remaining results into a description string. -template <typename DistrT, typename FormatT> -struct MockingBitGenExpectationFormatter { - std::string operator()(absl::string_view args) { - return absl::StrCat(FormatT::FunctionName(), "(", args, ")"); - } -}; - -// MockingBitGenCallFormatter is invoked to format each distribution call -// into a description string for the mock log. -template <typename DistrT, typename FormatT> -struct MockingBitGenCallFormatter { - std::string operator()(const DistrT& dist, - const typename DistrT::result_type& result) { - return absl::StrCat( - FormatT::FunctionName(), "(", FormatT::FormatArgs(dist), ") => {", - FormatT::FormatResults(absl::MakeSpan(&result, 1)), "}"); - } -}; - class MockingBitGenBase { template <typename> friend struct DistributionCaller; @@ -61,14 +38,9 @@ static constexpr result_type(max)() { return (generator_type::max)(); } result_type operator()() { return gen_(); } - MockingBitGenBase() : gen_(), observed_call_log_() {} virtual ~MockingBitGenBase() = default; protected: - const std::deque<std::string>& observed_call_log() { - return observed_call_log_; - } - // CallImpl is the type-erased virtual dispatch. // The type of dist is always distribution<T>, // The type of result is always distribution<T>::result_type. @@ -81,10 +53,9 @@ } // Call the generating distribution function. - // Invoked by DistributionCaller<>::Call<DistT, FormatT>. + // Invoked by DistributionCaller<>::Call<DistT>. // DistT is the distribution type. - // FormatT is the distribution formatter traits type. - template <typename DistrT, typename FormatT, typename... Args> + template <typename DistrT, typename... Args> typename DistrT::result_type Call(Args&&... args) { using distr_result_type = typename DistrT::result_type; using ArgTupleT = std::tuple<absl::decay_t<Args>...>; @@ -100,17 +71,11 @@ result = dist(gen_); } - // TODO(asoffer): Forwarding the args through means we no longer need to - // extract them from the from the distribution in formatter traits. We can - // just StrJoin them. - observed_call_log_.push_back( - MockingBitGenCallFormatter<DistrT, FormatT>{}(dist, result)); return result; } private: generator_type gen_; - std::deque<std::string> observed_call_log_; }; // namespace random_internal } // namespace random_internal
diff --git a/absl/random/mocking_bit_gen.cc b/absl/random/mocking_bit_gen.cc deleted file mode 100644 index 6bb1e41..0000000 --- a/absl/random/mocking_bit_gen.cc +++ /dev/null
@@ -1,30 +0,0 @@ -// -// Copyright 2018 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// 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 "absl/random/mocking_bit_gen.h" - -#include <string> - -namespace absl { -ABSL_NAMESPACE_BEGIN -MockingBitGen::~MockingBitGen() { - - for (const auto& del : deleters_) { - del(); - } -} - -ABSL_NAMESPACE_END -} // namespace absl
diff --git a/absl/random/mocking_bit_gen.h b/absl/random/mocking_bit_gen.h index 36cef91..3d8a979 100644 --- a/absl/random/mocking_bit_gen.h +++ b/absl/random/mocking_bit_gen.h
@@ -100,7 +100,9 @@ public: MockingBitGen() {} - ~MockingBitGen() override; + ~MockingBitGen() override { + for (const auto& del : deleters_) del(); + } private: template <typename DistrT, typename... Args> @@ -182,10 +184,10 @@ template <> struct DistributionCaller<absl::MockingBitGen> { - template <typename DistrT, typename FormatT, typename... Args> + template <typename DistrT, typename... Args> static typename DistrT::result_type Call(absl::MockingBitGen* gen, Args&&... args) { - return gen->template Call<DistrT, FormatT>(std::forward<Args>(args)...); + return gen->template Call<DistrT>(std::forward<Args>(args)...); } };
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index e72db82..3890112 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel
@@ -313,6 +313,7 @@ ":strings", "//absl/base", "//absl/base:config", + "//absl/base:core_headers", "//absl/base:endian", "//absl/base:raw_logging_internal", "//absl/container:fixed_array", @@ -485,6 +486,7 @@ copts = ABSL_TEST_COPTS, visibility = ["//visibility:private"], deps = [ + ":internal", ":pow10_helper", ":strings", "//absl/base:config",
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 668d722..d3a8bd7 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt
@@ -284,6 +284,7 @@ absl::raw_logging_internal absl::random_random absl::random_distributions + absl::strings_internal gmock_main ) @@ -577,6 +578,7 @@ absl::strings absl::base absl::config + absl::core_headers absl::endian absl::raw_logging_internal absl::fixed_array
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 415b239..7de7766 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc
@@ -28,6 +28,7 @@ #include "absl/base/casts.h" #include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" #include "absl/base/port.h" #include "absl/container/fixed_array.h" #include "absl/strings/escaping.h" @@ -713,6 +714,14 @@ memset(data_, 0, sizeof(data_)); } +inline Cord::InternalChunkIterator Cord::internal_chunk_begin() const { + return InternalChunkIterator(this); +} + +inline Cord::InternalChunkRange Cord::InternalChunks() const { + return InternalChunkRange(this); +} + // -------------------------------------------------------------------- // Constructors and destructors @@ -1087,7 +1096,7 @@ } else if (new_size == 0) { // We want to return empty subcord, so nothing to do. } else if (new_size <= InlineRep::kMaxInline) { - Cord::ChunkIterator it = chunk_begin(); + Cord::InternalChunkIterator it = internal_chunk_begin(); it.AdvanceBytes(pos); char* dest = sub_cord.contents_.data_; size_t remaining_size = new_size; @@ -1325,7 +1334,7 @@ inline int Cord::CompareSlowPath(absl::string_view rhs, size_t compared_size, size_t size_to_compare) const { - auto advance = [](Cord::ChunkIterator* it, absl::string_view* chunk) { + auto advance = [](Cord::InternalChunkIterator* it, absl::string_view* chunk) { if (!chunk->empty()) return true; ++*it; if (it->bytes_remaining_ == 0) return false; @@ -1333,7 +1342,7 @@ return true; }; - Cord::ChunkIterator lhs_it = chunk_begin(); + Cord::InternalChunkIterator lhs_it = internal_chunk_begin(); // compared_size is inside first chunk. absl::string_view lhs_chunk = @@ -1355,7 +1364,7 @@ inline int Cord::CompareSlowPath(const Cord& rhs, size_t compared_size, size_t size_to_compare) const { - auto advance = [](Cord::ChunkIterator* it, absl::string_view* chunk) { + auto advance = [](Cord::InternalChunkIterator* it, absl::string_view* chunk) { if (!chunk->empty()) return true; ++*it; if (it->bytes_remaining_ == 0) return false; @@ -1363,8 +1372,8 @@ return true; }; - Cord::ChunkIterator lhs_it = chunk_begin(); - Cord::ChunkIterator rhs_it = rhs.chunk_begin(); + Cord::InternalChunkIterator lhs_it = internal_chunk_begin(); + Cord::InternalChunkIterator rhs_it = rhs.internal_chunk_begin(); // compared_size is inside both first chunks. absl::string_view lhs_chunk = @@ -1498,8 +1507,11 @@ } } -Cord::ChunkIterator& Cord::ChunkIterator::operator++() { - assert(bytes_remaining_ > 0 && "Attempted to iterate past `end()`"); +template <typename StorageType> +Cord::GenericChunkIterator<StorageType>& +Cord::GenericChunkIterator<StorageType>::operator++() { + ABSL_HARDENING_ASSERT(bytes_remaining_ > 0 && + "Attempted to iterate past `end()`"); assert(bytes_remaining_ >= current_chunk_.size()); bytes_remaining_ -= current_chunk_.size(); @@ -1537,8 +1549,10 @@ return *this; } -Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { - assert(bytes_remaining_ >= n && "Attempted to iterate past `end()`"); +template <typename StorageType> +Cord Cord::GenericChunkIterator<StorageType>::AdvanceAndReadBytes(size_t n) { + ABSL_HARDENING_ASSERT(bytes_remaining_ >= n && + "Attempted to iterate past `end()`"); Cord subcord; if (n <= InlineRep::kMaxInline) { @@ -1650,7 +1664,8 @@ return subcord; } -void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) { +template <typename StorageType> +void Cord::GenericChunkIterator<StorageType>::AdvanceBytesSlowPath(size_t n) { assert(bytes_remaining_ >= n && "Attempted to iterate past `end()`"); assert(n >= current_chunk_.size()); // This should only be called when // iterating to a new node. @@ -1709,7 +1724,7 @@ } char Cord::operator[](size_t i) const { - assert(i < size()); + ABSL_HARDENING_ASSERT(i < size()); size_t offset = i; const CordRep* rep = contents_.tree(); if (rep == nullptr) { @@ -1990,6 +2005,9 @@ return out; } +template class Cord::GenericChunkIterator<cord_internal::CordTreeMutablePath>; +template class Cord::GenericChunkIterator<cord_internal::CordTreeDynamicPath>; + namespace strings_internal { size_t CordTestAccess::FlatOverhead() { return kFlatOverhead; } size_t CordTestAccess::MaxFlatLength() { return kMaxFlatLength; }
diff --git a/absl/strings/cord.h b/absl/strings/cord.h index eb236e5..3ab3cb8 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h
@@ -11,25 +11,52 @@ // 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. - -// A Cord is a sequence of characters with some unusual access propreties. -// A Cord supports efficient insertions and deletions at the start and end of -// the byte sequence, but random access reads are slower, and random access -// modifications are not supported by the API. Cord also provides cheap copies -// (using a copy-on-write strategy) and cheap substring operations. // -// Thread safety -// ------------- +// ----------------------------------------------------------------------------- +// File: cord.h +// ----------------------------------------------------------------------------- +// +// This file defines the `absl::Cord` data structure and operations on that data +// structure. A Cord is a string-like sequence of characters optimized for +// specific use cases. Unlike a `std::string`, which stores an array of +// contiguous characters, Cord data is stored in a structure consisting of +// separate, reference-counted "chunks." (Currently, this implementation is a +// tree structure, though that implementation may change.) +// +// Because a Cord consists of these chunks, data can be added to or removed from +// a Cord during its lifetime. Chunks may also be shared between Cords. Unlike a +// `std::string`, a Cord can therefore accomodate data that changes over its +// lifetime, though it's not quite "mutable"; it can change only in the +// attachment, detachment, or rearrangement of chunks of its constituent data. +// +// A Cord provides some benefit over `std::string` under the following (albeit +// narrow) circumstances: +// +// * Cord data is designed to grow and shrink over a Cord's lifetime. Cord +// provides efficient insertions and deletions at the start and end of the +// character sequences, avoiding copies in those cases. Static data should +// generally be stored as strings. +// * External memory consisting of string-like data can be directly added to +// a Cord without requiring copies or allocations. +// * Cord data may be shared and copied cheaply. Cord provides a copy-on-write +// implementation and cheap sub-Cord operations. Copying a Cord is an O(1) +// operation. +// +// As a consequence to the above, Cord data is generally large. Small data +// should generally use strings, as construction of a Cord requires some +// overhead. Small Cords (<= 15 bytes) are represented inline, but most small +// Cords are expected to grow over their lifetimes. +// +// Note that because a Cord is made up of separate chunked data, random access +// to character data within a Cord is slower than within a `std::string`. +// +// Thread Safety +// // Cord has the same thread-safety properties as many other types like // std::string, std::vector<>, int, etc -- it is thread-compatible. In -// particular, if no thread may call a non-const method, then it is safe to -// concurrently call const methods. Copying a Cord produces a new instance that -// can be used concurrently with the original in arbitrary ways. -// -// Implementation is similar to the "Ropes" described in: -// Ropes: An alternative to strings -// Hans J. Boehm, Russ Atkinson, Michael Plass -// Software Practice and Experience, December 1995 +// particular, if threads do not call non-const methods, then it is safe to call +// const methods without synchronization. Copying a Cord produces a new instance +// that can be used concurrently with the original in arbitrary ways. #ifndef ABSL_STRINGS_CORD_H_ #define ABSL_STRINGS_CORD_H_ @@ -48,6 +75,7 @@ #include "absl/base/internal/per_thread_tls.h" #include "absl/base/macros.h" #include "absl/base/port.h" +#include "absl/container/inlined_vector.h" #include "absl/functional/function_ref.h" #include "absl/meta/type_traits.h" #include "absl/strings/internal/cord_internal.h" @@ -67,11 +95,40 @@ H HashFragmentedCord(H, const Cord&); } +// Cord +// +// A Cord is a sequence of characters, designed to be more efficient than a +// `std::string` in certain circumstances: namely, large string data that needs +// to change over its lifetime or shared, especially when such data is shared +// across API boundaries. +// +// A Cord stores its character data in a structure that allows efficient prepend +// and append operations. This makes a Cord useful for large string data sent +// over in a wire format that may need to be prepended or appended at some point +// during the data exchange (e.g. HTTP, protocol buffers). For example, a +// Cord is useful for storing an HTTP request, and prepending an HTTP header to +// such a request. +// +// Cords should not be used for storing general string data, however. They +// require overhead to construct and are slower than strings for random access. +// +// The Cord API provides the following common API operations: +// +// * Create or assign Cords out of existing string data, memory, or other Cords +// * Append and prepend data to an existing Cord +// * Create new Sub-Cords from existing Cord data +// * Swap Cord data and compare Cord equality +// * Write out Cord data by constructing a `std::string` +// +// Additionally, the API provides iterator utilities to iterate through Cord +// data via chunks or character bytes. +// + namespace cord_internal { -// It's expensive to keep a tree perfectly balanced, so instead we keep trees -// approximately balanced. A tree node N of depth D(N) that contains a string -// of L(N) characters is considered balanced if L >= Fibonacci(D + 2). +// It's expensive to keep a Cord's tree perfectly balanced, so instead we keep +// trees approximately balanced. A tree node N of depth D(N) that contains a +// string of L(N) characters is considered balanced if L >= Fibonacci(D + 2). // The "+ 2" is used to ensure that every balanced leaf node contains at least // one character. Here we presume that // Fibonacci(0) = 0 @@ -113,7 +170,13 @@ size_t size_ = 0; }; +// Fixed length container for mutable "path" in cord tree, which can hold any +// possible valid path in cord tree. using CordTreeMutablePath = CordTreePath<CordRep*, MaxCordDepth()>; +// Variable length container for mutable "path" in cord tree. It starts with +// capacity for 15 elements and grow if necessary. +using CordTreeDynamicPath = + absl::InlinedVector<absl::cord_internal::CordRep*, 15>; } // namespace cord_internal // A Cord is a sequence of characters. @@ -123,175 +186,14 @@ using EnableIfString = absl::enable_if_t<std::is_same<T, std::string>::value, int>; - public: - // -------------------------------------------------------------------- - // Constructors, destructors and helper factories - - // Create an empty cord - constexpr Cord() noexcept; - - // Cord is copyable and efficiently movable. - // The moved-from state is valid but unspecified. - Cord(const Cord& src); - Cord(Cord&& src) noexcept; - Cord& operator=(const Cord& x); - Cord& operator=(Cord&& x) noexcept; - - // Create a cord out of "src". This constructor is explicit on - // purpose so that people do not get automatic type conversions. - explicit Cord(absl::string_view src); - Cord& operator=(absl::string_view src); - - // These are templated to avoid ambiguities for types that are convertible to - // both `absl::string_view` and `std::string`, such as `const char*`. + //---------------------------------------------------------------------------- + // Cord::GenericChunkIterator + //---------------------------------------------------------------------------- // - // Note that these functions reserve the right to reuse the `string&&`'s - // memory and that they will do so in the future. - template <typename T, EnableIfString<T> = 0> - explicit Cord(T&& src) : Cord(absl::string_view(src)) {} - template <typename T, EnableIfString<T> = 0> - Cord& operator=(T&& src); - - // Destroy the cord - ~Cord() { - if (contents_.is_tree()) DestroyCordSlow(); - } - - // Creates a Cord that takes ownership of external memory. The contents of - // `data` are not copied. - // - // This function takes a callable that is invoked when all Cords are - // finished with `data`. The data must remain live and unchanging until the - // releaser is called. The requirements for the releaser are that it: - // * is move constructible, - // * supports `void operator()(absl::string_view) const` or - // `void operator()() const`, - // * does not have alignment requirement greater than what is guaranteed by - // ::operator new. This is dictated by alignof(std::max_align_t) before - // C++17 and __STDCPP_DEFAULT_NEW_ALIGNMENT__ if compiling with C++17 or - // it is supported by the implementation. - // - // Example: - // - // Cord MakeCord(BlockPool* pool) { - // Block* block = pool->NewBlock(); - // FillBlock(block); - // return absl::MakeCordFromExternal( - // block->ToStringView(), - // [pool, block](absl::string_view v) { - // pool->FreeBlock(block, v); - // }); - // } - // - // WARNING: It's likely a bug if your releaser doesn't do anything. - // For example, consider the following: - // - // void Foo(const char* buffer, int len) { - // auto c = absl::MakeCordFromExternal(absl::string_view(buffer, len), - // [](absl::string_view) {}); - // - // // BUG: If Bar() copies its cord for any reason, including keeping a - // // substring of it, the lifetime of buffer might be extended beyond - // // when Foo() returns. - // Bar(c); - // } - template <typename Releaser> - friend Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser); - - // -------------------------------------------------------------------- - // Mutations - - void Clear(); - - void Append(const Cord& src); - void Append(Cord&& src); - void Append(absl::string_view src); - template <typename T, EnableIfString<T> = 0> - void Append(T&& src); - - void Prepend(const Cord& src); - void Prepend(absl::string_view src); - template <typename T, EnableIfString<T> = 0> - void Prepend(T&& src); - - void RemovePrefix(size_t n); - void RemoveSuffix(size_t n); - - // Returns a new cord representing the subrange [pos, pos + new_size) of - // *this. If pos >= size(), the result is empty(). If - // (pos + new_size) >= size(), the result is the subrange [pos, size()). - Cord Subcord(size_t pos, size_t new_size) const; - - friend void swap(Cord& x, Cord& y) noexcept; - - // -------------------------------------------------------------------- - // Accessors - - size_t size() const; - bool empty() const; - - // Returns the approximate number of bytes pinned by this Cord. Note that - // Cords that share memory could each be "charged" independently for the same - // shared memory. - size_t EstimatedMemoryUsage() const; - - // -------------------------------------------------------------------- - // Comparators - - // Compares 'this' Cord with rhs. This function and its relatives - // treat Cords as sequences of unsigned bytes. The comparison is a - // straightforward lexicographic comparison. Return value: - // -1 'this' Cord is smaller - // 0 two Cords are equal - // 1 'this' Cord is larger - int Compare(absl::string_view rhs) const; - int Compare(const Cord& rhs) const; - - // Does 'this' cord start/end with rhs - bool StartsWith(const Cord& rhs) const; - bool StartsWith(absl::string_view rhs) const; - bool EndsWith(absl::string_view rhs) const; - bool EndsWith(const Cord& rhs) const; - - // -------------------------------------------------------------------- - // Conversion to other types - - explicit operator std::string() const; - - // Copies the contents from `src` to `*dst`. - // - // This function optimizes the case of reusing the destination string since it - // can reuse previously allocated capacity. However, this function does not - // guarantee that pointers previously returned by `dst->data()` remain valid - // even if `*dst` had enough capacity to hold `src`. If `*dst` is a new - // object, prefer to simply use the conversion operator to `std::string`. - friend void CopyCordToString(const Cord& src, std::string* dst); - - // -------------------------------------------------------------------- - // Iteration - - class CharIterator; - - // Type for iterating over the chunks of a `Cord`. See comments for - // `Cord::chunk_begin()`, `Cord::chunk_end()` and `Cord::Chunks()` below for - // preferred usage. - // - // Additional notes: - // * The `string_view` returned by dereferencing a valid, non-`end()` - // iterator is guaranteed to be non-empty. - // * A `ChunkIterator` object is invalidated after any non-const - // operation on the `Cord` object over which it iterates. - // * Two `ChunkIterator` objects can be equality compared if and only if - // they remain valid and iterate over the same `Cord`. - // * This is a proxy iterator. This means the `string_view` returned by the - // iterator does not live inside the Cord, and its lifetime is limited to - // the lifetime of the iterator itself. To help prevent issues, - // `ChunkIterator::reference` is not a true reference type and is - // equivalent to `value_type`. - // * The iterator keeps state that can grow for `Cord`s that contain many - // nodes and are imbalanced due to sharing. Prefer to pass this type by - // const reference instead of by value. - class ChunkIterator { + // A `Cord::GenericChunkIterator` provides an interface for the standard + // `Cord::ChunkIterator` as well as some private implementations. + template <typename StorageType> + class GenericChunkIterator { public: using iterator_category = std::input_iterator_tag; using value_type = absl::string_view; @@ -299,12 +201,12 @@ using pointer = const value_type*; using reference = value_type; - ChunkIterator() = default; + GenericChunkIterator() = default; - ChunkIterator& operator++(); - ChunkIterator operator++(int); - bool operator==(const ChunkIterator& other) const; - bool operator!=(const ChunkIterator& other) const; + GenericChunkIterator& operator++(); + GenericChunkIterator operator++(int); + bool operator==(const GenericChunkIterator& other) const; + bool operator!=(const GenericChunkIterator& other) const; reference operator*() const; pointer operator->() const; @@ -313,7 +215,7 @@ private: // Constructs a `begin()` iterator from `cord`. - explicit ChunkIterator(const Cord* cord); + explicit GenericChunkIterator(const Cord* cord); // Removes `n` bytes from `current_chunk_`. Expects `n` to be smaller than // `current_chunk_.size()`. @@ -330,16 +232,248 @@ // The current leaf, or `nullptr` if the iterator points to short data. // If the current chunk is a substring node, current_leaf_ points to the // underlying flat or external node. - absl::cord_internal::CordRep* current_leaf_ = nullptr; + cord_internal::CordRep* current_leaf_ = nullptr; // The number of bytes left in the `Cord` over which we are iterating. size_t bytes_remaining_ = 0; - absl::cord_internal::CordTreeMutablePath stack_of_right_children_; + StorageType stack_of_right_children_; + }; + template <typename IteratorType> + class GenericChunkRange { + public: + explicit GenericChunkRange(const Cord* cord) : cord_(cord) {} + + IteratorType begin() const { return IteratorType(cord_); } + IteratorType end() const { return IteratorType(); } + + private: + const Cord* cord_; }; + public: + // Cord::Cord() Constructors + + // Creates an empty Cord + constexpr Cord() noexcept; + + // Creates a Cord from an existing Cord. Cord is copyable and efficiently + // movable. The moved-from state is valid but unspecified. + Cord(const Cord& src); + Cord(Cord&& src) noexcept; + Cord& operator=(const Cord& x); + Cord& operator=(Cord&& x) noexcept; + + // Creates a Cord from a `src` string. This constructor is marked explicit to + // prevent implicit Cord constructions from arguments convertible to an + // `absl::string_view`. + explicit Cord(absl::string_view src); + Cord& operator=(absl::string_view src); + + // Creates a Cord from a `std::string&&` rvalue. These constructors are + // templated to avoid ambiguities for types that are convertible to both + // `absl::string_view` and `std::string`, such as `const char*`. + // + // Note that these functions reserve the right to use the `string&&`'s + // memory and that they will do so in the future. + template <typename T, EnableIfString<T> = 0> + explicit Cord(T&& src) : Cord(absl::string_view(src)) {} + template <typename T, EnableIfString<T> = 0> + Cord& operator=(T&& src); + + // Cord::~Cord() + // + // Destructs the Cord + ~Cord() { + if (contents_.is_tree()) DestroyCordSlow(); + } + + // Cord::MakeCordFromExternal(data, callable) + // + // Creates a Cord that takes ownership of external string memory. The + // contents of `data` are not copied to the Cord; instead, the external + // memory is added to the Cord and reference-counted. This data may not be + // changed for the life of the Cord, though it may be prepended or appended + // to. + // + // `MakeCordFromExternal()` takes a callable "releaser" that is invoked when + // the reference count for `data` reaches zero. As noted above, this data must + // remain live until the releaser is invoked. The callable releaser also must: + // + // * be move constructible + // * support `void operator()(absl::string_view) const` or `void operator()` + // * not have alignment requirement greater than what is guaranteed by + // `::operator new`. This alignment is dictated by + // `alignof(std::max_align_t)` (pre-C++17 code) or + // `__STDCPP_DEFAULT_NEW_ALIGNMENT__` (C++17 code). + // + // Example: + // + // Cord MakeCord(BlockPool* pool) { + // Block* block = pool->NewBlock(); + // FillBlock(block); + // return absl::MakeCordFromExternal( + // block->ToStringView(), + // [pool, block](absl::string_view v) { + // pool->FreeBlock(block, v); + // }); + // } + // + // WARNING: Because a Cord can be reference-counted, it's likely a bug if your + // releaser doesn't do anything. For example, consider the following: + // + // void Foo(const char* buffer, int len) { + // auto c = absl::MakeCordFromExternal(absl::string_view(buffer, len), + // [](absl::string_view) {}); + // + // // BUG: If Bar() copies its cord for any reason, including keeping a + // // substring of it, the lifetime of buffer might be extended beyond + // // when Foo() returns. + // Bar(c); + // } + template <typename Releaser> + friend Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser); + + // Cord::Clear() + // + // Releases the Cord data. Any nodes that share data with other Cords, if + // applicable, will have their reference counts reduced by 1. + void Clear(); + + // Cord::Append() + // + // Appends data to the Cord, which may come from another Cord or other string + // data. + void Append(const Cord& src); + void Append(Cord&& src); + void Append(absl::string_view src); + template <typename T, EnableIfString<T> = 0> + void Append(T&& src); + + // Cord::Prepend() + // + // Prepends data to the Cord, which may come from another Cord or other string + // data. + void Prepend(const Cord& src); + void Prepend(absl::string_view src); + template <typename T, EnableIfString<T> = 0> + void Prepend(T&& src); + + // Cord::RemovePrefix() + // + // Removes the first `n` bytes of a Cord. + void RemovePrefix(size_t n); + void RemoveSuffix(size_t n); + + // Cord::Subcord() + // + // Returns a new Cord representing the subrange [pos, pos + new_size) of + // *this. If pos >= size(), the result is empty(). If + // (pos + new_size) >= size(), the result is the subrange [pos, size()). + Cord Subcord(size_t pos, size_t new_size) const; + + // swap() + // + // Swaps the data of Cord `x` with Cord `y`. + friend void swap(Cord& x, Cord& y) noexcept; + + // Cord::size() + // + // Returns the size of the Cord. + size_t size() const; + + // Cord::empty() + // + // Determines whether the given Cord is empty, returning `true` is so. + bool empty() const; + + // Cord:EstimatedMemoryUsage() + // + // Returns the *approximate* number of bytes held in full or in part by this + // Cord (which may not remain the same between invocations). Note that Cords + // that share memory could each be "charged" independently for the same shared + // memory. + size_t EstimatedMemoryUsage() const; + + // Cord::Compare() + // + // Compares 'this' Cord with rhs. This function and its relatives treat Cords + // as sequences of unsigned bytes. The comparison is a straightforward + // lexicographic comparison. `Cord::Compare()` returns values as follows: + // + // -1 'this' Cord is smaller + // 0 two Cords are equal + // 1 'this' Cord is larger + int Compare(absl::string_view rhs) const; + int Compare(const Cord& rhs) const; + + // Cord::StartsWith() + // + // Determines whether the Cord starts with the passed string data `rhs`. + bool StartsWith(const Cord& rhs) const; + bool StartsWith(absl::string_view rhs) const; + + // Cord::EndsWidth() + // + // Determines whether the Cord ends with the passed string data `rhs`. + bool EndsWith(absl::string_view rhs) const; + bool EndsWith(const Cord& rhs) const; + + // Cord::operator std::string() + // + // Converts a Cord into a `std::string()`. This operator is marked explicit to + // prevent unintended Cord usage in functions that take a string. + explicit operator std::string() const; + + // CopyCordToString() + // + // Copies the contents of a `src` Cord into a `*dst` string. + // + // This function optimizes the case of reusing the destination string since it + // can reuse previously allocated capacity. However, this function does not + // guarantee that pointers previously returned by `dst->data()` remain valid + // even if `*dst` had enough capacity to hold `src`. If `*dst` is a new + // object, prefer to simply use the conversion operator to `std::string`. + friend void CopyCordToString(const Cord& src, std::string* dst); + + class CharIterator; + + //---------------------------------------------------------------------------- + // Cord::ChunkIterator + //---------------------------------------------------------------------------- + // + // A `Cord::ChunkIterator` allows iteration over the constituent chunks of its + // Cord. Such iteration allows you to perform non-const operatons on the data + // of a Cord without modifying it. + // + // Generally, you do not instantiate a `Cord::ChunkIterator` directly; + // instead, you create one implicitly through use of the `Cord::Chunks()` + // member function. + // + // The `Cord::ChunkIterator` has the following properties: + // + // * The iterator is invalidated after any non-const operation on the + // Cord object over which it iterates. + // * The `string_view` returned by dereferencing a valid, non-`end()` + // iterator is guaranteed to be non-empty. + // * Two `ChunkIterator` objects can be compared equal if and only if they + // remain valid and iterate over the same Cord. + // * The iterator in this case is a proxy iterator; the `string_view` + // returned by the iterator does not live inside the Cord, and its + // lifetime is limited to the lifetime of the iterator itself. To help + // prevent lifetime issues, `ChunkIterator::reference` is not a true + // reference type and is equivalent to `value_type`. + // * The iterator keeps state that can grow for Cords that contain many + // nodes and are imbalanced due to sharing. Prefer to pass this type by + // const reference instead of by value. + using ChunkIterator = + GenericChunkIterator<cord_internal::CordTreeDynamicPath>; + + // Cord::ChunkIterator::chunk_begin() + // // Returns an iterator to the first chunk of the `Cord`. // - // This is useful for getting a `ChunkIterator` outside the context of a - // range-based for-loop (in which case see `Cord::Chunks()` below). + // Generally, prefer using `Cord::Chunks()` within a range-based for loop for + // iterating over the chunks of a Cord. This method may be useful for getting + // a `ChunkIterator` where range-based for-loops are not useful. // // Example: // @@ -348,26 +482,35 @@ // return std::find(c.chunk_begin(), c.chunk_end(), s); // } ChunkIterator chunk_begin() const; + + // Cord::ChunkItertator::chunk_end() + // // Returns an iterator one increment past the last chunk of the `Cord`. + // + // Generally, prefer using `Cord::Chunks()` within a range-based for loop for + // iterating over the chunks of a Cord. This method may be useful for getting + // a `ChunkIterator` where range-based for-loops may not be available. ChunkIterator chunk_end() const; - // Convenience wrapper over `Cord::chunk_begin()` and `Cord::chunk_end()` to - // enable range-based for-loop iteration over `Cord` chunks. + //---------------------------------------------------------------------------- + // Cord::ChunkIterator::ChunkRange + //---------------------------------------------------------------------------- // - // Prefer to use `Cord::Chunks()` below instead of constructing this directly. - class ChunkRange { - public: - explicit ChunkRange(const Cord* cord) : cord_(cord) {} + // `ChunkRange` is a helper class for iterating over the chunks of the `Cord`, + // producing an iterator which can be used within a range-based for loop. + // Construction of a `ChunkRange` will return an iterator pointing to the + // first chunk of the Cord. Generally, do not construct a `ChunkRange` + // directly; instead, prefer to use the `Cord::Chunks()` method. + // + // Implementation note: `ChunkRange` is simply a convenience wrapper over + // `Cord::chunk_begin()` and `Cord::chunk_end()`. + using ChunkRange = GenericChunkRange<ChunkIterator>; - ChunkIterator begin() const; - ChunkIterator end() const; - - private: - const Cord* cord_; - }; - - // Returns a range for iterating over the chunks of a `Cord` with a - // range-based for-loop. + // Cord::Chunks() + // + // Returns a `Cord::ChunkIterator::ChunkRange` for iterating over the chunks + // of a `Cord` with a range-based for-loop. For most iteration tasks on a + // Cord, use `Cord::Chunks()` to retrieve this iterator. // // Example: // @@ -384,22 +527,30 @@ // } ChunkRange Chunks() const; - // Type for iterating over the characters of a `Cord`. See comments for - // `Cord::char_begin()`, `Cord::char_end()` and `Cord::Chars()` below for - // preferred usage. + //---------------------------------------------------------------------------- + // Cord::CharIterator + //---------------------------------------------------------------------------- // - // Additional notes: - // * A `CharIterator` object is invalidated after any non-const - // operation on the `Cord` object over which it iterates. - // * Two `CharIterator` objects can be equality compared if and only if - // they remain valid and iterate over the same `Cord`. - // * The iterator keeps state that can grow for `Cord`s that contain many + // A `Cord::CharIterator` allows iteration over the constituent characters of + // a `Cord`. + // + // Generally, you do not instantiate a `Cord::CharIterator` directly; instead, + // you create one implicitly through use of the `Cord::Chars()` member + // function. + // + // A `Cord::CharIterator` has the following properties: + // + // * The iterator is invalidated after any non-const operation on the + // Cord object over which it iterates. + // * Two `CharIterator` objects can be compared equal if and only if they + // remain valid and iterate over the same Cord. + // * The iterator keeps state that can grow for Cords that contain many // nodes and are imbalanced due to sharing. Prefer to pass this type by // const reference instead of by value. - // * This type cannot be a forward iterator because a `Cord` can reuse - // sections of memory. This violates the requirement that if dereferencing - // two iterators returns the same object, the iterators must compare - // equal. + // * This type cannot act as a forward iterator because a `Cord` can reuse + // sections of memory. This fact violates the requirement for forward + // iterators to compare equal if dereferencing them returns the same + // object. class CharIterator { public: using iterator_category = std::input_iterator_tag; @@ -425,34 +576,56 @@ ChunkIterator chunk_iterator_; }; - // Advances `*it` by `n_bytes` and returns the bytes passed as a `Cord`. + // Cord::CharIterator::AdvanceAndRead() // - // `n_bytes` must be less than or equal to the number of bytes remaining for - // iteration. Otherwise the behavior is undefined. It is valid to pass - // `char_end()` and 0. + // Advances the `Cord::CharIterator` by `n_bytes` and returns the bytes + // advanced as a separate `Cord`. `n_bytes` must be less than or equal to the + // number of bytes within the Cord; otherwise, behavior is undefined. It is + // valid to pass `char_end()` and `0`. static Cord AdvanceAndRead(CharIterator* it, size_t n_bytes); - // Advances `*it` by `n_bytes`. + // Cord::CharIterator::Advance() // - // `n_bytes` must be less than or equal to the number of bytes remaining for - // iteration. Otherwise the behavior is undefined. It is valid to pass - // `char_end()` and 0. + // Advances the `Cord::CharIterator` by `n_bytes`. `n_bytes` must be less than + // or equal to the number of bytes remaining within the Cord; otherwise, + // behavior is undefined. It is valid to pass `char_end()` and `0`. static void Advance(CharIterator* it, size_t n_bytes); + // Cord::CharIterator::ChunkRemaining() + // // Returns the longest contiguous view starting at the iterator's position. // // `it` must be dereferenceable. static absl::string_view ChunkRemaining(const CharIterator& it); + // Cord::CharIterator::char_begin() + // // Returns an iterator to the first character of the `Cord`. + // + // Generally, prefer using `Cord::Chars()` within a range-based for loop for + // iterating over the chunks of a Cord. This method may be useful for getting + // a `CharIterator` where range-based for-loops may not be available. CharIterator char_begin() const; + + // Cord::CharIterator::char_end() + // // Returns an iterator to one past the last character of the `Cord`. + // + // Generally, prefer using `Cord::Chars()` within a range-based for loop for + // iterating over the chunks of a Cord. This method may be useful for getting + // a `CharIterator` where range-based for-loops are not useful. CharIterator char_end() const; - // Convenience wrapper over `Cord::char_begin()` and `Cord::char_end()` to - // enable range-based for-loop iterator over the characters of a `Cord`. + // Cord::CharIterator::CharRange // - // Prefer to use `Cord::Chars()` below instead of constructing this directly. + // `CharRange` is a helper class for iterating over the characters of a + // producing an iterator which can be used within a range-based for loop. + // Construction of a `CharRange` will return an iterator pointing to the first + // character of the Cord. Generally, do not construct a `CharRange` directly; + // instead, prefer to use the `Cord::Chars()` method show below. + // + // Implementation note: `CharRange` is simply a convenience wrapper over + // `Cord::char_begin()` and `Cord::char_end()`. class CharRange { public: explicit CharRange(const Cord* cord) : cord_(cord) {} @@ -464,8 +637,11 @@ const Cord* cord_; }; - // Returns a range for iterating over the characters of a `Cord` with a - // range-based for-loop. + // Cord::CharIterator::Chars() + // + // Returns a `Cord::CharIterator` for iterating over the characters of a + // `Cord` with a range-based for-loop. For most character-based iteration + // tasks on a Cord, use `Cord::Chars()` to retrieve this iterator. // // Example: // @@ -482,23 +658,26 @@ // } CharRange Chars() const; - // -------------------------------------------------------------------- - // Miscellaneous - - // Get the "i"th character of 'this' and return it. - // NOTE: This routine is reasonably efficient. It is roughly - // logarithmic in the number of nodes that make up the cord. Still, - // if you need to iterate over the contents of a cord, you should - // use a CharIterator/CordIterator rather than call operator[] or Get() - // repeatedly in a loop. + // Cord::operator[] // - // REQUIRES: 0 <= i < size() + // Get the "i"th character of the Cord and returns it, provided that + // 0 <= i < Cord.size(). + // + // NOTE: This routine is reasonably efficient. It is roughly + // logarithmic based on the number of chunks that make up the cord. Still, + // if you need to iterate over the contents of a cord, you should + // use a CharIterator/ChunkIterator rather than call operator[] or Get() + // repeatedly in a loop. char operator[](size_t i) const; + // Cord::TryFlat() + // // If this cord's representation is a single flat array, return a // string_view referencing that array. Otherwise return nullopt. absl::optional<absl::string_view> TryFlat() const; + // Cord::Flatten() + // // Flattens the cord into a single array and returns a view of the data. // // If the cord was already flat, the contents are not modified. @@ -621,6 +800,14 @@ static bool GetFlatAux(absl::cord_internal::CordRep* rep, absl::string_view* fragment); + // Iterators for use inside Cord implementation + using InternalChunkIterator = + GenericChunkIterator<cord_internal::CordTreeMutablePath>; + using InternalChunkRange = GenericChunkRange<InternalChunkIterator>; + + InternalChunkIterator internal_chunk_begin() const; + InternalChunkRange InternalChunks() const; + // Helper for ForEachChunk() static void ForEachChunkAux( absl::cord_internal::CordRep* rep, @@ -655,6 +842,11 @@ void AppendImpl(C&& src); }; +extern template class Cord::GenericChunkIterator< + cord_internal::CordTreeMutablePath>; +extern template class Cord::GenericChunkIterator< + cord_internal::CordTreeDynamicPath>; + ABSL_NAMESPACE_END } // namespace absl @@ -994,7 +1186,9 @@ return EqualsImpl(rhs, rhs_size); } -inline Cord::ChunkIterator::ChunkIterator(const Cord* cord) +template <typename StorageType> +inline Cord::GenericChunkIterator<StorageType>::GenericChunkIterator( + const Cord* cord) : bytes_remaining_(cord->size()) { if (cord->empty()) return; if (cord->contents_.is_tree()) { @@ -1005,37 +1199,50 @@ } } -inline Cord::ChunkIterator Cord::ChunkIterator::operator++(int) { - ChunkIterator tmp(*this); +template <typename StorageType> +inline Cord::GenericChunkIterator<StorageType> +Cord::GenericChunkIterator<StorageType>::operator++(int) { + GenericChunkIterator tmp(*this); operator++(); return tmp; } -inline bool Cord::ChunkIterator::operator==(const ChunkIterator& other) const { +template <typename StorageType> +inline bool Cord::GenericChunkIterator<StorageType>::operator==( + const GenericChunkIterator<StorageType>& other) const { return bytes_remaining_ == other.bytes_remaining_; } -inline bool Cord::ChunkIterator::operator!=(const ChunkIterator& other) const { +template <typename StorageType> +inline bool Cord::GenericChunkIterator<StorageType>::operator!=( + const GenericChunkIterator<StorageType>& other) const { return !(*this == other); } -inline Cord::ChunkIterator::reference Cord::ChunkIterator::operator*() const { - assert(bytes_remaining_ != 0); +template <typename StorageType> +inline typename Cord::GenericChunkIterator<StorageType>::reference +Cord::GenericChunkIterator<StorageType>::operator*() const { + ABSL_HARDENING_ASSERT(bytes_remaining_ != 0); return current_chunk_; } -inline Cord::ChunkIterator::pointer Cord::ChunkIterator::operator->() const { - assert(bytes_remaining_ != 0); +template <typename StorageType> +inline typename Cord::GenericChunkIterator<StorageType>::pointer +Cord::GenericChunkIterator<StorageType>::operator->() const { + ABSL_HARDENING_ASSERT(bytes_remaining_ != 0); return ¤t_chunk_; } -inline void Cord::ChunkIterator::RemoveChunkPrefix(size_t n) { +template <typename StorageType> +inline void Cord::GenericChunkIterator<StorageType>::RemoveChunkPrefix( + size_t n) { assert(n < current_chunk_.size()); current_chunk_.remove_prefix(n); bytes_remaining_ -= n; } -inline void Cord::ChunkIterator::AdvanceBytes(size_t n) { +template <typename StorageType> +inline void Cord::GenericChunkIterator<StorageType>::AdvanceBytes(size_t n) { if (ABSL_PREDICT_TRUE(n < current_chunk_.size())) { RemoveChunkPrefix(n); } else if (n != 0) { @@ -1049,14 +1256,6 @@ inline Cord::ChunkIterator Cord::chunk_end() const { return ChunkIterator(); } -inline Cord::ChunkIterator Cord::ChunkRange::begin() const { - return cord_->chunk_begin(); -} - -inline Cord::ChunkIterator Cord::ChunkRange::end() const { - return cord_->chunk_end(); -} - inline Cord::ChunkRange Cord::Chunks() const { return ChunkRange(this); } inline Cord::CharIterator& Cord::CharIterator::operator++() {
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index f2d81d4..0ec93b4 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc
@@ -18,6 +18,7 @@ #include "absl/base/config.h" #include "absl/base/internal/endian.h" #include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" #include "absl/container/fixed_array.h" #include "absl/strings/cord_test_helpers.h" #include "absl/strings/str_cat.h" @@ -1627,3 +1628,23 @@ } } } + +TEST(CordDeathTest, Hardening) { + absl::Cord cord("hello"); + // These statement should abort the program in all builds modes. + EXPECT_DEATH_IF_SUPPORTED(cord.RemovePrefix(6), ""); + EXPECT_DEATH_IF_SUPPORTED(cord.RemoveSuffix(6), ""); + + bool test_hardening = false; + ABSL_HARDENING_ASSERT([&]() { + // This only runs when ABSL_HARDENING_ASSERT is active. + test_hardening = true; + return true; + }()); + if (!test_hardening) return; + + EXPECT_DEATH_IF_SUPPORTED(cord[5], ""); + EXPECT_DEATH_IF_SUPPORTED(*cord.chunk_end(), ""); + EXPECT_DEATH_IF_SUPPORTED(static_cast<void>(cord.chunk_end()->empty()), ""); + EXPECT_DEATH_IF_SUPPORTED(++cord.chunk_end(), ""); +}
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc index 4d0604e..3aa0296 100644 --- a/absl/strings/internal/str_format/arg.cc +++ b/absl/strings/internal/str_format/arg.cc
@@ -120,24 +120,25 @@ // the '#' flag is specified to modify the precision for 'o' conversions. string_view BaseIndicator(const ConvertedIntInfo &info, const ConversionSpec conv) { - bool alt = conv.flags().alt; - int radix = FormatConversionCharRadix(conv.conv()); - if (conv.conv() == ConversionChar::p) alt = true; // always show 0x for %p. + bool alt = conv.has_alt_flag(); + int radix = FormatConversionCharRadix(conv.conversion_char()); + if (conv.conversion_char() == ConversionChar::p) + alt = true; // always show 0x for %p. // From the POSIX description of '#' flag: // "For x or X conversion specifiers, a non-zero result shall have // 0x (or 0X) prefixed to it." if (alt && radix == 16 && !info.digits().empty()) { - if (FormatConversionCharIsUpper(conv.conv())) return "0X"; + if (FormatConversionCharIsUpper(conv.conversion_char())) return "0X"; return "0x"; } return {}; } string_view SignColumn(bool neg, const ConversionSpec conv) { - if (FormatConversionCharIsSigned(conv.conv())) { + if (FormatConversionCharIsSigned(conv.conversion_char())) { if (neg) return "-"; - if (conv.flags().show_pos) return "+"; - if (conv.flags().sign_col) return " "; + if (conv.has_show_pos_flag()) return "+"; + if (conv.has_sign_col_flag()) return " "; } return {}; } @@ -147,9 +148,9 @@ size_t fill = 0; if (conv.width() >= 0) fill = conv.width(); ReducePadding(1, &fill); - if (!conv.flags().left) sink->Append(fill, ' '); + if (!conv.has_left_flag()) sink->Append(fill, ' '); sink->Append(1, v); - if (conv.flags().left) sink->Append(fill, ' '); + if (conv.has_left_flag()) sink->Append(fill, ' '); return true; } @@ -174,7 +175,7 @@ if (!precision_specified) precision = 1; - if (conv.flags().alt && conv.conv() == ConversionChar::o) { + if (conv.has_alt_flag() && conv.conversion_char() == ConversionChar::o) { // From POSIX description of the '#' (alt) flag: // "For o conversion, it increases the precision (if necessary) to // force the first digit of the result to be zero." @@ -187,13 +188,13 @@ size_t num_zeroes = Excess(formatted.size(), precision); ReducePadding(num_zeroes, &fill); - size_t num_left_spaces = !conv.flags().left ? fill : 0; - size_t num_right_spaces = conv.flags().left ? fill : 0; + size_t num_left_spaces = !conv.has_left_flag() ? fill : 0; + size_t num_right_spaces = conv.has_left_flag() ? fill : 0; // From POSIX description of the '0' (zero) flag: // "For d, i, o, u, x, and X conversion specifiers, if a precision // is specified, the '0' flag is ignored." - if (!precision_specified && conv.flags().zero) { + if (!precision_specified && conv.has_zero_flag()) { num_zeroes += num_left_spaces; num_left_spaces = 0; } @@ -209,8 +210,8 @@ template <typename T> bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) { - ConvertedIntInfo info(v, conv.conv()); - if (conv.flags().basic && (conv.conv() != ConversionChar::p)) { + ConvertedIntInfo info(v, conv.conversion_char()); + if (conv.is_basic() && (conv.conversion_char() != ConversionChar::p)) { if (info.is_neg()) sink->Append(1, '-'); if (info.digits().empty()) { sink->Append(1, '0'); @@ -224,13 +225,14 @@ template <typename T> bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { - if (FormatConversionCharIsFloat(conv.conv())) { + if (FormatConversionCharIsFloat(conv.conversion_char())) { return FormatConvertImpl(static_cast<double>(v), conv, sink).value; } - if (conv.conv() == ConversionChar::c) + if (conv.conversion_char() == ConversionChar::c) return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink); - if (!FormatConversionCharIsIntegral(conv.conv())) return false; - if (!FormatConversionCharIsSigned(conv.conv()) && IsSigned<T>::value) { + if (!FormatConversionCharIsIntegral(conv.conversion_char())) return false; + if (!FormatConversionCharIsSigned(conv.conversion_char()) && + IsSigned<T>::value) { using U = typename MakeUnsigned<T>::type; return FormatConvertImpl(static_cast<U>(v), conv, sink).value; } @@ -239,19 +241,19 @@ template <typename T> bool ConvertFloatArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { - return FormatConversionCharIsFloat(conv.conv()) && + return FormatConversionCharIsFloat(conv.conversion_char()) && ConvertFloatImpl(v, conv, sink); } inline bool ConvertStringArg(string_view v, const ConversionSpec conv, FormatSinkImpl *sink) { - if (conv.conv() != ConversionChar::s) return false; - if (conv.flags().basic) { + if (conv.conversion_char() != ConversionChar::s) return false; + if (conv.is_basic()) { sink->Append(v); return true; } return sink->PutPaddedString(v, conv.width(), conv.precision(), - conv.flags().left); + conv.has_left_flag()); } } // namespace @@ -272,7 +274,7 @@ ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v, const ConversionSpec conv, FormatSinkImpl *sink) { - if (conv.conv() == ConversionChar::p) + if (conv.conversion_char() == ConversionChar::p) return {FormatConvertImpl(VoidPtr(v), conv, sink).value}; size_t len; if (v == nullptr) { @@ -289,7 +291,7 @@ // ==================== Raw pointers ==================== ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv, FormatSinkImpl *sink) { - if (conv.conv() != ConversionChar::p) return {false}; + if (conv.conversion_char() != ConversionChar::p) return {false}; if (!v.value) { sink->Append("(nil)"); return {true};
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h index 7a93756..1c36e30 100644 --- a/absl/strings/internal/str_format/arg.h +++ b/absl/strings/internal/str_format/arg.h
@@ -70,9 +70,11 @@ ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value, ConversionSpec conv, FormatSinkImpl* sink) { - if (conv.conv() != ConversionChar::s) return {false}; + if (conv.conversion_char() != ConversionChar::s) { + return {false}; + } - bool is_left = conv.flags().left; + bool is_left = conv.has_left_flag(); size_t space_remaining = 0; int width = conv.width(); @@ -106,8 +108,8 @@ } using IntegralConvertResult = - ConvertResult<Conv::c | Conv::numeric | Conv::star>; -using FloatingConvertResult = ConvertResult<Conv::floating>; + ConvertResult<Conv::c | Conv::kNumeric | Conv::kStar>; +using FloatingConvertResult = ConvertResult<Conv::kFloating>; // Floats. FloatingConvertResult FormatConvertImpl(float v, ConversionSpec conv, @@ -185,7 +187,9 @@ FormatSinkImpl* sink) { const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v; - if (conv.conv() != str_format_internal::ConversionChar::n) return {false}; + if (conv.conversion_char() != str_format_internal::ConversionChar::n) { + return {false}; + } *v2.p_ = static_cast<int>(sink->size()); return {true}; } @@ -377,7 +381,7 @@ 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() == ConversionChar::none)) { + if (ABSL_PREDICT_FALSE(spec.conversion_char() == ConversionChar::kNone)) { return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(), std::is_enum<T>()); }
diff --git a/absl/strings/internal/str_format/bind.cc b/absl/strings/internal/str_format/bind.cc index 27522fd..6980ed1 100644 --- a/absl/strings/internal/str_format/bind.cc +++ b/absl/strings/internal/str_format/bind.cc
@@ -147,7 +147,7 @@ << FormatConversionSpecImplFriend::FlagsToString(bound); if (bound.width() >= 0) ss << bound.width(); if (bound.precision() >= 0) ss << "." << bound.precision(); - ss << bound.conv() << "}"; + ss << bound.conversion_char() << "}"; Append(ss.str()); return true; }
diff --git a/absl/strings/internal/str_format/checker_test.cc b/absl/strings/internal/str_format/checker_test.cc index ea2a768..49a24b4 100644 --- a/absl/strings/internal/str_format/checker_test.cc +++ b/absl/strings/internal/str_format/checker_test.cc
@@ -9,13 +9,17 @@ namespace str_format_internal { namespace { -std::string ConvToString(Conv conv) { +std::string ConvToString(FormatConversionCharSet conv) { std::string out; #define CONV_SET_CASE(c) \ - if (Contains(conv, Conv::c)) out += #c; + if (Contains(conv, FormatConversionCharSet::c)) { \ + out += #c; \ + } ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, ) #undef CONV_SET_CASE - if (Contains(conv, Conv::star)) out += "*"; + if (Contains(conv, FormatConversionCharSet::kStar)) { + out += "*"; + } return out; }
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h index d166575..bae2c07 100644 --- a/absl/strings/internal/str_format/extension.h +++ b/absl/strings/internal/str_format/extension.h
@@ -24,6 +24,7 @@ #include "absl/base/config.h" #include "absl/base/port.h" +#include "absl/meta/type_traits.h" #include "absl/strings/internal/str_format/output.h" #include "absl/strings/string_view.h" @@ -154,8 +155,7 @@ d, i, o, u, x, X, // int f, F, e, E, g, G, a, A, // float n, p, // misc - kNone, - none = kNone + kNone }; // clang-format on @@ -287,11 +287,6 @@ // negative value. int precision() const { return precision_; } - // Deprecated (use has_x_flag() instead). - Flags flags() const { return flags_; } - // Deprecated - FormatConversionChar conv() const { return conversion_char(); } - private: friend struct str_format_internal::FormatConversionSpecImplFriend; FormatConversionChar conv_ = FormatConversionChar::kNone; @@ -343,15 +338,7 @@ kFloating = a | e | f | g | A | E | F | G, kNumeric = kIntegral | kFloating, kString = s, - kPointer = p, - - // The following are deprecated - star = kStar, - integral = kIntegral, - floating = kFloating, - numeric = kNumeric, - string = kString, - pointer = kPointer + kPointer = p }; // Type safe OR operator. @@ -365,11 +352,22 @@ static_cast<uint64_t>(b)); } +// Overloaded conversion functions to support absl::ParsedFormat. // Get a conversion with a single character in it. -constexpr FormatConversionCharSet ConversionCharToConv(char c) { - return FormatConversionCharSet(FormatConversionCharToConvValue(c)); +constexpr FormatConversionCharSet ToFormatConversionCharSet(char c) { + return static_cast<FormatConversionCharSet>( + FormatConversionCharToConvValue(c)); } +// Get a conversion with a single character in it. +constexpr FormatConversionCharSet ToFormatConversionCharSet( + FormatConversionCharSet c) { + return c; +} + +template <typename T> +void ToFormatConversionCharSet(T) = delete; + // Checks whether `c` exists in `set`. constexpr bool Contains(FormatConversionCharSet set, char c) { return (static_cast<uint64_t>(set) & FormatConversionCharToConvValue(c)) != 0;
diff --git a/absl/strings/internal/str_format/float_conversion.cc b/absl/strings/internal/str_format/float_conversion.cc index d4c647c..d5a1ee4 100644 --- a/absl/strings/internal/str_format/float_conversion.cc +++ b/absl/strings/internal/str_format/float_conversion.cc
@@ -33,7 +33,7 @@ if (std::is_same<long double, Float>()) { *fp++ = 'L'; } - *fp++ = FormatConversionCharToChar(conv.conv()); + *fp++ = FormatConversionCharToChar(conv.conversion_char()); *fp = 0; assert(fp < fmt + sizeof(fmt)); } @@ -100,17 +100,19 @@ char text[4], *ptr = text; if (sign_char) *ptr++ = sign_char; if (std::isnan(v)) { - ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "NAN" : "nan", - 3, ptr); + ptr = std::copy_n( + FormatConversionCharIsUpper(conv.conversion_char()) ? "NAN" : "nan", 3, + ptr); } else if (std::isinf(v)) { - ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "INF" : "inf", - 3, ptr); + ptr = std::copy_n( + FormatConversionCharIsUpper(conv.conversion_char()) ? "INF" : "inf", 3, + ptr); } else { return false; } return sink->PutPaddedString(string_view(text, ptr - text), conv.width(), -1, - conv.flags().left); + conv.has_left_flag()); } // Round up the last digit of the value. @@ -358,9 +360,9 @@ static_cast<int>(sign_char != 0), 0) : 0; - if (conv.flags().left) { + if (conv.has_left_flag()) { right_spaces = missing_chars; - } else if (conv.flags().zero) { + } else if (conv.has_zero_flag()) { zeros = missing_chars; } else { left_spaces = missing_chars; @@ -382,9 +384,9 @@ if (std::signbit(abs_v)) { sign_char = '-'; abs_v = -abs_v; - } else if (conv.flags().show_pos) { + } else if (conv.has_show_pos_flag()) { sign_char = '+'; - } else if (conv.flags().sign_col) { + } else if (conv.has_sign_col_flag()) { sign_char = ' '; } @@ -401,14 +403,14 @@ Buffer buffer; - switch (conv.conv()) { + switch (conv.conversion_char()) { case ConversionChar::f: case ConversionChar::F: if (!FloatToBuffer<FormatStyle::Fixed>(decomposed, precision, &buffer, nullptr)) { return FallbackToSnprintf(v, conv, sink); } - if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back(); + if (!conv.has_alt_flag() && buffer.back() == '.') buffer.pop_back(); break; case ConversionChar::e: @@ -417,9 +419,10 @@ &exp)) { return FallbackToSnprintf(v, conv, sink); } - if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back(); - PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e', - &buffer); + if (!conv.has_alt_flag() && buffer.back() == '.') buffer.pop_back(); + PrintExponent( + exp, FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', + &buffer); break; case ConversionChar::g: @@ -446,13 +449,15 @@ } exp = 0; } - if (!conv.flags().alt) { + if (!conv.has_alt_flag()) { while (buffer.back() == '0') buffer.pop_back(); if (buffer.back() == '.') buffer.pop_back(); } if (exp) { - PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e', - &buffer); + PrintExponent( + exp, + FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', + &buffer); } break;
diff --git a/absl/strings/internal/str_format/parser_test.cc b/absl/strings/internal/str_format/parser_test.cc index 1b1ee03..51eb53f 100644 --- a/absl/strings/internal/str_format/parser_test.cc +++ b/absl/strings/internal/str_format/parser_test.cc
@@ -52,7 +52,7 @@ X(f), X(F), X(e), X(E), X(g), X(G), X(a), X(A), // float X(n), X(p), // misc #undef X - {ConversionChar::none, '\0'}, + {ConversionChar::kNone, '\0'}, }; // clang-format on for (auto e : kExpect) {
diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc index bd4e116..7db85e7 100644 --- a/absl/strings/numbers_test.cc +++ b/absl/strings/numbers_test.cc
@@ -40,6 +40,7 @@ #include "absl/random/distributions.h" #include "absl/random/random.h" #include "absl/strings/internal/numbers_test_common.h" +#include "absl/strings/internal/ostringstream.h" #include "absl/strings/internal/pow10_helper.h" #include "absl/strings/str_cat.h"
diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h index 2f9b4b2..d40fca1 100644 --- a/absl/strings/str_format.h +++ b/absl/strings/str_format.h
@@ -285,7 +285,7 @@ // } template <char... Conv> using ParsedFormat = str_format_internal::ExtendedParsedFormat< - str_format_internal::ConversionCharToConv(Conv)...>; + absl::str_format_internal::ToFormatConversionCharSet(Conv)...>; // StrFormat() //
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc index 554dca7..f0d1f0a 100644 --- a/absl/strings/str_format_test.cc +++ b/absl/strings/str_format_test.cc
@@ -540,19 +540,19 @@ EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f)); std::string format = "%sFFF%dZZZ%f"; - auto f2 = - ExtendedParsedFormat<Conv::string, Conv::d, Conv::floating>::New(format); + auto f2 = ExtendedParsedFormat<Conv::kString, Conv::d, Conv::kFloating>::New( + format); ASSERT_TRUE(f2); EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2)); - f2 = ExtendedParsedFormat<Conv::string, Conv::d, Conv::floating>::New( + f2 = ExtendedParsedFormat<Conv::kString, Conv::d, Conv::kFloating>::New( "%s %d %f"); ASSERT_TRUE(f2); EXPECT_EQ("{s:1$s}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2)); - auto star = ExtendedParsedFormat<Conv::star, Conv::d>::New("%*d"); + auto star = ExtendedParsedFormat<Conv::kStar, Conv::d>::New("%*d"); ASSERT_TRUE(star); EXPECT_EQ("{*d:2$1$*d}", SummarizeParsedFormat(*star));
diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h index b314bc3..8a9db8c 100644 --- a/absl/strings/string_view.h +++ b/absl/strings/string_view.h
@@ -48,7 +48,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN -using std::string_view; +using string_view = std::string_view; ABSL_NAMESPACE_END } // namespace absl @@ -283,7 +283,7 @@ // Returns the ith element of the `string_view` using the array operator. // Note that this operator does not perform any bounds checking. constexpr const_reference operator[](size_type i) const { - return ABSL_ASSERT(i < size()), ptr_[i]; + return ABSL_HARDENING_ASSERT(i < size()), ptr_[i]; } // string_view::at() @@ -303,14 +303,14 @@ // // Returns the first element of a `string_view`. constexpr const_reference front() const { - return ABSL_ASSERT(!empty()), ptr_[0]; + return ABSL_HARDENING_ASSERT(!empty()), ptr_[0]; } // string_view::back() // // Returns the last element of a `string_view`. constexpr const_reference back() const { - return ABSL_ASSERT(!empty()), ptr_[size() - 1]; + return ABSL_HARDENING_ASSERT(!empty()), ptr_[size() - 1]; } // string_view::data() @@ -329,7 +329,7 @@ // Removes the first `n` characters from the `string_view`. Note that the // underlying string is not changed, only the view. void remove_prefix(size_type n) { - assert(n <= length_); + ABSL_HARDENING_ASSERT(n <= length_); ptr_ += n; length_ -= n; } @@ -339,7 +339,7 @@ // Removes the last `n` characters from the `string_view`. Note that the // underlying string is not changed, only the view. void remove_suffix(size_type n) { - assert(n <= length_); + ABSL_HARDENING_ASSERT(n <= length_); length_ -= n; } @@ -520,7 +520,7 @@ (std::numeric_limits<difference_type>::max)(); static constexpr size_type CheckLengthInternal(size_type len) { - return (void)ABSL_ASSERT(len <= kMaxSize), len; + return ABSL_HARDENING_ASSERT(len <= kMaxSize), len; } static constexpr size_type StrlenInternal(const char* str) {
diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc index 6ba0614..ff31b51 100644 --- a/absl/strings/string_view_test.cc +++ b/absl/strings/string_view_test.cc
@@ -28,6 +28,7 @@ #include "gtest/gtest.h" #include "absl/base/config.h" #include "absl/base/dynamic_annotations.h" +#include "absl/base/options.h" #if defined(ABSL_HAVE_STD_STRING_VIEW) || defined(__ANDROID__) // We don't control the death messaging when using std::string_view. @@ -820,7 +821,7 @@ TEST(StringViewTest, FrontBackEmpty) { #ifndef ABSL_USES_STD_STRING_VIEW -#ifndef NDEBUG +#if !defined(NDEBUG) || ABSL_OPTION_HARDENED // Abseil's string_view implementation has debug assertions that check that // front() and back() are not called on an empty string_view. absl::string_view sv; @@ -1130,7 +1131,7 @@ TEST(StringViewTest, BoundsCheck) { #ifndef ABSL_USES_STD_STRING_VIEW -#ifndef NDEBUG +#if !defined(NDEBUG) || ABSL_OPTION_HARDENED // Abseil's string_view implementation has bounds-checking in debug mode. absl::string_view h = "hello"; ABSL_EXPECT_DEATH_IF_SUPPORTED(h[5], "");
diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc index 426558e..6ee5f23 100644 --- a/absl/synchronization/mutex.cc +++ b/absl/synchronization/mutex.cc
@@ -1751,7 +1751,8 @@ }; // Internal version of LockWhen(). See LockSlowWithDeadline() -void Mutex::LockSlow(MuHow how, const Condition *cond, int flags) { +ABSL_ATTRIBUTE_NOINLINE void Mutex::LockSlow(MuHow how, const Condition *cond, + int flags) { ABSL_RAW_CHECK( this->LockSlowWithDeadline(how, cond, KernelTimeout::Never(), flags), "condition untrue on return from LockSlow"); @@ -2017,7 +2018,7 @@ // which holds the lock but is not runnable because its condition is false // or it is in the process of blocking on a condition variable; it must requeue // itself on the mutex/condvar to wait for its condition to become true. -void Mutex::UnlockSlow(SynchWaitParams *waitp) { +ABSL_ATTRIBUTE_NOINLINE void Mutex::UnlockSlow(SynchWaitParams *waitp) { intptr_t v = mu_.load(std::memory_order_relaxed); this->AssertReaderHeld(); CheckForMutexCorruption(v, "Unlock");
diff --git a/absl/types/optional.h b/absl/types/optional.h index 01d747d..61540cf 100644 --- a/absl/types/optional.h +++ b/absl/types/optional.h
@@ -136,10 +136,10 @@ constexpr optional(nullopt_t) noexcept {} // NOLINT(runtime/explicit) // Copy constructor, standard semantics - optional(const optional& src) = default; + optional(const optional&) = default; // Move constructor, standard semantics - optional(optional&& src) = default; + optional(optional&&) = default; // Constructs a non-empty `optional` direct-initialized value of type `T` from // the arguments `std::forward<Args>(args)...` within the `optional`. @@ -412,11 +412,11 @@ // // If you need myOpt->foo in constexpr, use (*myOpt).foo instead. const T* operator->() const { - assert(this->engaged_); + ABSL_HARDENING_ASSERT(this->engaged_); return std::addressof(this->data_); } T* operator->() { - assert(this->engaged_); + ABSL_HARDENING_ASSERT(this->engaged_); return std::addressof(this->data_); } @@ -425,17 +425,17 @@ // Accesses the underlying `T` value of an `optional`. If the `optional` is // empty, behavior is undefined. constexpr const T& operator*() const& { - return ABSL_ASSERT(this->engaged_), reference(); + return ABSL_HARDENING_ASSERT(this->engaged_), reference(); } T& operator*() & { - assert(this->engaged_); + ABSL_HARDENING_ASSERT(this->engaged_); return reference(); } constexpr const T&& operator*() const && { - return absl::move(reference()); + return ABSL_HARDENING_ASSERT(this->engaged_), absl::move(reference()); } T&& operator*() && { - assert(this->engaged_); + ABSL_HARDENING_ASSERT(this->engaged_); return std::move(reference()); }
diff --git a/absl/types/optional_test.cc b/absl/types/optional_test.cc index 47d5c8a..874334e 100644 --- a/absl/types/optional_test.cc +++ b/absl/types/optional_test.cc
@@ -1057,8 +1057,7 @@ // test constexpr value() constexpr absl::optional<int> o1(1); static_assert(1 == o1.value(), ""); // const & -#if !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG) && \ - !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) +#if !defined(_MSC_VER) && !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) using COI = const absl::optional<int>; static_assert(2 == COI(2).value(), ""); // const && #endif @@ -1098,8 +1097,7 @@ constexpr absl::optional<int> opt1(1); static_assert(*opt1 == 1, ""); -#if !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG) && \ - !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) +#if !defined(_MSC_VER) && !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) using COI = const absl::optional<int>; static_assert(*COI(2) == 2, ""); #endif
diff --git a/absl/types/span.h b/absl/types/span.h index 21cda34..734db69 100644 --- a/absl/types/span.h +++ b/absl/types/span.h
@@ -276,7 +276,7 @@ // Returns a reference to the i'th element of this span. constexpr reference operator[](size_type i) const noexcept { // MSVC 2015 accepts this as constexpr, but not ptr_[i] - return ABSL_ASSERT(i < size()), *(data() + i); + return ABSL_HARDENING_ASSERT(i < size()), *(data() + i); } // Span::at() @@ -295,7 +295,7 @@ // Returns a reference to the first element of this span. The span must not // be empty. constexpr reference front() const noexcept { - return ABSL_ASSERT(size() > 0), *data(); + return ABSL_HARDENING_ASSERT(size() > 0), *data(); } // Span::back() @@ -303,7 +303,7 @@ // Returns a reference to the last element of this span. The span must not // be empty. constexpr reference back() const noexcept { - return ABSL_ASSERT(size() > 0), *(data() + size() - 1); + return ABSL_HARDENING_ASSERT(size() > 0), *(data() + size() - 1); } // Span::begin() @@ -368,7 +368,7 @@ // // Removes the first `n` elements from the span. void remove_prefix(size_type n) noexcept { - assert(size() >= n); + ABSL_HARDENING_ASSERT(size() >= n); ptr_ += n; len_ -= n; } @@ -377,7 +377,7 @@ // // Removes the last `n` elements from the span. void remove_suffix(size_type n) noexcept { - assert(size() >= n); + ABSL_HARDENING_ASSERT(size() >= n); len_ -= n; } @@ -665,7 +665,7 @@ template <int&... ExplicitArgumentBarrier, typename T> Span<T> MakeSpan(T* begin, T* end) noexcept { - return ABSL_ASSERT(begin <= end), Span<T>(begin, end - begin); + return ABSL_HARDENING_ASSERT(begin <= end), Span<T>(begin, end - begin); } template <int&... ExplicitArgumentBarrier, typename C> @@ -710,7 +710,7 @@ template <int&... ExplicitArgumentBarrier, typename T> Span<const T> MakeConstSpan(T* begin, T* end) noexcept { - return ABSL_ASSERT(begin <= end), Span<const T>(begin, end - begin); + return ABSL_HARDENING_ASSERT(begin <= end), Span<const T>(begin, end - begin); } template <int&... ExplicitArgumentBarrier, typename C>
diff --git a/absl/types/span_test.cc b/absl/types/span_test.cc index 6ac234d..2584339 100644 --- a/absl/types/span_test.cc +++ b/absl/types/span_test.cc
@@ -27,6 +27,7 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/exception_testing.h" +#include "absl/base/options.h" #include "absl/container/fixed_array.h" #include "absl/container/inlined_vector.h" #include "absl/hash/hash_testing.h" @@ -233,7 +234,7 @@ EXPECT_EQ(s.front(), s[0]); EXPECT_EQ(s.back(), s[9]); -#ifndef NDEBUG +#if !defined(NDEBUG) || ABSL_OPTION_HARDENED EXPECT_DEATH_IF_SUPPORTED(s[-1], ""); EXPECT_DEATH_IF_SUPPORTED(s[10], ""); #endif @@ -273,6 +274,13 @@ EXPECT_EQ(s.size(), 0); EXPECT_EQ(v, MakeRamp(20, 1)); + +#if !defined(NDEBUG) || ABSL_OPTION_HARDENED + absl::Span<int> prefix_death(v); + EXPECT_DEATH_IF_SUPPORTED(prefix_death.remove_prefix(21), ""); + absl::Span<int> suffix_death(v); + EXPECT_DEATH_IF_SUPPORTED(suffix_death.remove_suffix(21), ""); +#endif } TEST(IntSpan, Subspan) {
diff --git a/ci/absl_alternate_options.h b/ci/absl_alternate_options.h index f0c21fe..29b020d 100644 --- a/ci/absl_alternate_options.h +++ b/ci/absl_alternate_options.h
@@ -1,6 +1,3 @@ -#ifndef ABSL_BASE_OPTIONS_H_ -#define ABSL_BASE_OPTIONS_H_ - // Copyright 2019 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,11 +15,15 @@ // Alternate options.h file, used in continuous integration testing to exercise // option settings not used by default. +#ifndef ABSL_BASE_OPTIONS_H_ +#define ABSL_BASE_OPTIONS_H_ + #define ABSL_OPTION_USE_STD_ANY 0 #define ABSL_OPTION_USE_STD_OPTIONAL 0 #define ABSL_OPTION_USE_STD_STRING_VIEW 0 #define ABSL_OPTION_USE_STD_VARIANT 0 #define ABSL_OPTION_USE_INLINE_NAMESPACE 1 #define ABSL_OPTION_INLINE_NAMESPACE_NAME ns +#define ABSL_OPTION_HARDENED 1 #endif // ABSL_BASE_OPTIONS_H_
diff --git a/ci/cmake_install_test.sh b/ci/cmake_install_test.sh index 55fb4f1..4c748eb 100755 --- a/ci/cmake_install_test.sh +++ b/ci/cmake_install_test.sh
@@ -20,6 +20,9 @@ ABSEIL_ROOT="$(realpath $(dirname ${0})/..)" fi +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_GCC_LATEST_CONTAINER} + time docker run \ --volume="${ABSEIL_ROOT}:/abseil-cpp:ro" \ --workdir=/abseil-cpp \ @@ -28,5 +31,5 @@ --rm \ -e CFLAGS="-Werror" \ -e CXXFLAGS="-Werror" \ - gcr.io/google.com/absl-177019/linux_gcc-latest:20200106 \ + ${DOCKER_CONTAINER} \ /bin/bash CMake/install_test_project/test.sh $@
diff --git a/ci/linux_clang-latest_libcxx_asan_bazel.sh b/ci/linux_clang-latest_libcxx_asan_bazel.sh index 24efe3b..03463e2 100755 --- a/ci/linux_clang-latest_libcxx_asan_bazel.sh +++ b/ci/linux_clang-latest_libcxx_asan_bazel.sh
@@ -36,7 +36,8 @@ EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20200102" +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_CLANG_LATEST_CONTAINER} # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. @@ -50,6 +51,14 @@ BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}" fi +# Avoid depending on external sites like GitHub by checking --distdir for +# external dependencies first. +# https://docs.bazel.build/versions/master/guide.html#distdir +if [ -z ${KOKORO_GFILE_DIR:-} && -d "${KOKORO_GFILE_DIR}/distdir" ]; then + DOCKER_EXTRA_ARGS="--volume=${KOKORO_GFILE_DIR}/distdir:/distdir:ro ${DOCKER_EXTRA_ARGS:-}" + BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}" +fi + for std in ${STD}; do for compilation_mode in ${COMPILATION_MODE}; do for exceptions_mode in ${EXCEPTIONS_MODE}; do
diff --git a/ci/linux_clang-latest_libcxx_bazel.sh b/ci/linux_clang-latest_libcxx_bazel.sh index 127e7bc..050a98c 100755 --- a/ci/linux_clang-latest_libcxx_bazel.sh +++ b/ci/linux_clang-latest_libcxx_bazel.sh
@@ -36,7 +36,8 @@ EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20200102" +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_CLANG_LATEST_CONTAINER} # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. @@ -50,6 +51,14 @@ BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}" fi +# Avoid depending on external sites like GitHub by checking --distdir for +# external dependencies first. +# https://docs.bazel.build/versions/master/guide.html#distdir +if [ -z ${KOKORO_GFILE_DIR:-} && -d "${KOKORO_GFILE_DIR}/distdir" ]; then + DOCKER_EXTRA_ARGS="--volume=${KOKORO_GFILE_DIR}/distdir:/distdir:ro ${DOCKER_EXTRA_ARGS:-}" + BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}" +fi + for std in ${STD}; do for compilation_mode in ${COMPILATION_MODE}; do for exceptions_mode in ${EXCEPTIONS_MODE}; do
diff --git a/ci/linux_clang-latest_libcxx_tsan_bazel.sh b/ci/linux_clang-latest_libcxx_tsan_bazel.sh index 00257b3..2f5fb12 100755 --- a/ci/linux_clang-latest_libcxx_tsan_bazel.sh +++ b/ci/linux_clang-latest_libcxx_tsan_bazel.sh
@@ -36,7 +36,8 @@ EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20200102" +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_CLANG_LATEST_CONTAINER} # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. @@ -50,6 +51,14 @@ BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}" fi +# Avoid depending on external sites like GitHub by checking --distdir for +# external dependencies first. +# https://docs.bazel.build/versions/master/guide.html#distdir +if [ -z ${KOKORO_GFILE_DIR:-} && -d "${KOKORO_GFILE_DIR}/distdir" ]; then + DOCKER_EXTRA_ARGS="--volume=${KOKORO_GFILE_DIR}/distdir:/distdir:ro ${DOCKER_EXTRA_ARGS:-}" + BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}" +fi + for std in ${STD}; do for compilation_mode in ${COMPILATION_MODE}; do for exceptions_mode in ${EXCEPTIONS_MODE}; do
diff --git a/ci/linux_clang-latest_libstdcxx_bazel.sh b/ci/linux_clang-latest_libstdcxx_bazel.sh index 9fe71d3..087f59b 100755 --- a/ci/linux_clang-latest_libstdcxx_bazel.sh +++ b/ci/linux_clang-latest_libstdcxx_bazel.sh
@@ -36,7 +36,8 @@ EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20200102" +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_CLANG_LATEST_CONTAINER} # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. @@ -50,6 +51,14 @@ BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}" fi +# Avoid depending on external sites like GitHub by checking --distdir for +# external dependencies first. +# https://docs.bazel.build/versions/master/guide.html#distdir +if [ -z ${KOKORO_GFILE_DIR:-} && -d "${KOKORO_GFILE_DIR}/distdir" ]; then + DOCKER_EXTRA_ARGS="--volume=${KOKORO_GFILE_DIR}/distdir:/distdir:ro ${DOCKER_EXTRA_ARGS:-}" + BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}" +fi + for std in ${STD}; do for compilation_mode in ${COMPILATION_MODE}; do for exceptions_mode in ${EXCEPTIONS_MODE}; do
diff --git a/ci/linux_docker_containers.sh b/ci/linux_docker_containers.sh new file mode 100644 index 0000000..cf056b3 --- /dev/null +++ b/ci/linux_docker_containers.sh
@@ -0,0 +1,21 @@ +# Copyright 2019 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. + +# The file contains Docker container identifiers currently used by test scripts. +# Test scripts should source this file to get the identifiers. + +readonly LINUX_ALPINE_CONTAINER="gcr.io/google.com/absl-177019/alpine:20191016" +readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20200319" +readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-latest:20200319" +readonly LINUX_GCC_49_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-4.9:20191018"
diff --git a/ci/linux_gcc-4.9_libstdcxx_bazel.sh b/ci/linux_gcc-4.9_libstdcxx_bazel.sh index f8102cc..622ea84 100755 --- a/ci/linux_gcc-4.9_libstdcxx_bazel.sh +++ b/ci/linux_gcc-4.9_libstdcxx_bazel.sh
@@ -36,7 +36,8 @@ EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-4.9:20191018" +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_GCC_49_CONTAINER} # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. @@ -50,6 +51,14 @@ BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}" fi +# Avoid depending on external sites like GitHub by checking --distdir for +# external dependencies first. +# https://docs.bazel.build/versions/master/guide.html#distdir +if [ -z ${KOKORO_GFILE_DIR:-} && -d "${KOKORO_GFILE_DIR}/distdir" ]; then + DOCKER_EXTRA_ARGS="--volume=${KOKORO_GFILE_DIR}/distdir:/distdir:ro ${DOCKER_EXTRA_ARGS:-}" + BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}" +fi + for std in ${STD}; do for compilation_mode in ${COMPILATION_MODE}; do for exceptions_mode in ${EXCEPTIONS_MODE}; do
diff --git a/ci/linux_gcc-latest_libstdcxx_bazel.sh b/ci/linux_gcc-latest_libstdcxx_bazel.sh index 5964703..f7dbd19 100755 --- a/ci/linux_gcc-latest_libstdcxx_bazel.sh +++ b/ci/linux_gcc-latest_libstdcxx_bazel.sh
@@ -36,7 +36,8 @@ EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-latest:20200106" +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_GCC_LATEST_CONTAINER} # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. @@ -50,6 +51,14 @@ BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}" fi +# Avoid depending on external sites like GitHub by checking --distdir for +# external dependencies first. +# https://docs.bazel.build/versions/master/guide.html#distdir +if [ -z ${KOKORO_GFILE_DIR:-} && -d "${KOKORO_GFILE_DIR}/distdir" ]; then + DOCKER_EXTRA_ARGS="--volume=${KOKORO_GFILE_DIR}/distdir:/distdir:ro ${DOCKER_EXTRA_ARGS:-}" + BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}" +fi + for std in ${STD}; do for compilation_mode in ${COMPILATION_MODE}; do for exceptions_mode in ${EXCEPTIONS_MODE}; do
diff --git a/ci/linux_gcc-latest_libstdcxx_cmake.sh b/ci/linux_gcc-latest_libstdcxx_cmake.sh index 38ad99f..b501544 100755 --- a/ci/linux_gcc-latest_libstdcxx_cmake.sh +++ b/ci/linux_gcc-latest_libstdcxx_cmake.sh
@@ -34,6 +34,9 @@ ABSL_CMAKE_BUILD_TYPES="Debug Release" fi +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_GCC_LATEST_CONTAINER} + for std in ${ABSL_CMAKE_CXX_STANDARDS}; do for compilation_mode in ${ABSL_CMAKE_BUILD_TYPES}; do echo "--------------------------------------------------------------------" @@ -47,7 +50,7 @@ --rm \ -e CFLAGS="-Werror" \ -e CXXFLAGS="-Werror" \ - gcr.io/google.com/absl-177019/linux_gcc-latest:20200106 \ + ${DOCKER_CONTAINER} \ /bin/bash -c " cd /buildfs && \ cmake /abseil-cpp \
diff --git a/ci/linux_gcc_alpine_cmake.sh b/ci/linux_gcc_alpine_cmake.sh index 830a136..4496f8d 100755 --- a/ci/linux_gcc_alpine_cmake.sh +++ b/ci/linux_gcc_alpine_cmake.sh
@@ -34,7 +34,8 @@ ABSL_CMAKE_BUILD_TYPES="Debug Release" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/alpine:20191016" +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_ALPINE_CONTAINER} for std in ${ABSL_CMAKE_CXX_STANDARDS}; do for compilation_mode in ${ABSL_CMAKE_BUILD_TYPES}; do
diff --git a/ci/macos_xcode_cmake.sh b/ci/macos_xcode_cmake.sh index aa9ee15..a1f4a85 100755 --- a/ci/macos_xcode_cmake.sh +++ b/ci/macos_xcode_cmake.sh
@@ -36,7 +36,7 @@ time cmake ${ABSEIL_ROOT} \ -GXcode \ -DCMAKE_BUILD_TYPE=${compilation_mode} \ - -DCMAKE_CXX_FLAGS=-std=c++14 \ + -DCMAKE_CXX_STANDARD=11 \ -DABSL_USE_GOOGLETEST_HEAD=ON \ -DABSL_RUN_TESTS=ON time cmake --build .