diff options
-rw-r--r-- | absl/container/internal/hashtablez_sampler.cc | 37 | ||||
-rw-r--r-- | absl/container/internal/hashtablez_sampler.h | 9 | ||||
-rw-r--r-- | absl/profiling/internal/sample_recorder.h | 6 | ||||
-rw-r--r-- | absl/strings/BUILD.bazel | 1 | ||||
-rw-r--r-- | absl/strings/cord.h | 3 | ||||
-rw-r--r-- | absl/strings/cord_test.cc | 26 | ||||
-rw-r--r-- | absl/strings/internal/cord_rep_flat.h | 6 | ||||
-rw-r--r-- | absl/strings/internal/str_join_internal.h | 15 | ||||
-rw-r--r-- | absl/strings/str_join_test.cc | 134 |
9 files changed, 218 insertions, 19 deletions
diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index 40cce047..1a3ca7cc 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -38,12 +38,18 @@ ABSL_CONST_INIT std::atomic<bool> g_hashtablez_enabled{ false }; ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_sample_parameter{1 << 10}; +std::atomic<HashtablezConfigListener> g_hashtablez_config_listener{nullptr}; #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) ABSL_PER_THREAD_TLS_KEYWORD absl::profiling_internal::ExponentialBiased g_exponential_biased_generator; #endif +void TriggerHashtablezConfigListener() { + auto* listener = g_hashtablez_config_listener.load(std::memory_order_acquire); + if (listener != nullptr) listener(); +} + } // namespace #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) @@ -163,11 +169,33 @@ void RecordInsertSlow(HashtablezInfo* info, size_t hash, info->size.fetch_add(1, std::memory_order_relaxed); } +void SetHashtablezConfigListener(HashtablezConfigListener l) { + g_hashtablez_config_listener.store(l, std::memory_order_release); +} + +bool IsHashtablezEnabled() { + return g_hashtablez_enabled.load(std::memory_order_acquire); +} + void SetHashtablezEnabled(bool enabled) { + SetHashtablezEnabledInternal(enabled); + TriggerHashtablezConfigListener(); +} + +void SetHashtablezEnabledInternal(bool enabled) { g_hashtablez_enabled.store(enabled, std::memory_order_release); } +int32_t GetHashtablezSampleParameter() { + return g_hashtablez_sample_parameter.load(std::memory_order_acquire); +} + void SetHashtablezSampleParameter(int32_t rate) { + SetHashtablezSampleParameterInternal(rate); + TriggerHashtablezConfigListener(); +} + +void SetHashtablezSampleParameterInternal(int32_t rate) { if (rate > 0) { g_hashtablez_sample_parameter.store(rate, std::memory_order_release); } else { @@ -176,7 +204,16 @@ void SetHashtablezSampleParameter(int32_t rate) { } } +int32_t GetHashtablezMaxSamples() { + return GlobalHashtablezSampler().GetMaxSamples(); +} + void SetHashtablezMaxSamples(int32_t max) { + SetHashtablezMaxSamplesInternal(max); + TriggerHashtablezConfigListener(); +} + +void SetHashtablezMaxSamplesInternal(int32_t max) { if (max > 0) { GlobalHashtablezSampler().SetMaxSamples(max); } else { diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h index 91fcdb34..ee4d293e 100644 --- a/absl/container/internal/hashtablez_sampler.h +++ b/absl/container/internal/hashtablez_sampler.h @@ -258,14 +258,23 @@ using HashtablezSampler = // Returns a global Sampler. HashtablezSampler& GlobalHashtablezSampler(); +using HashtablezConfigListener = void (*)(); +void SetHashtablezConfigListener(HashtablezConfigListener l); + // Enables or disables sampling for Swiss tables. +bool IsHashtablezEnabled(); void SetHashtablezEnabled(bool enabled); +void SetHashtablezEnabledInternal(bool enabled); // Sets the rate at which Swiss tables will be sampled. +int32_t GetHashtablezSampleParameter(); void SetHashtablezSampleParameter(int32_t rate); +void SetHashtablezSampleParameterInternal(int32_t rate); // Sets a soft max for the number of samples that will be kept. +int32_t GetHashtablezMaxSamples(); void SetHashtablezMaxSamples(int32_t max); +void SetHashtablezMaxSamplesInternal(int32_t max); // Configuration override. // This allows process-wide sampling without depending on order of diff --git a/absl/profiling/internal/sample_recorder.h b/absl/profiling/internal/sample_recorder.h index 5e04a9cd..6c146210 100644 --- a/absl/profiling/internal/sample_recorder.h +++ b/absl/profiling/internal/sample_recorder.h @@ -75,6 +75,7 @@ class SampleRecorder { // samples that have been dropped. int64_t Iterate(const std::function<void(const T& stack)>& f); + int32_t GetMaxSamples() const; void SetMaxSamples(int32_t max); private: @@ -223,6 +224,11 @@ void SampleRecorder<T>::SetMaxSamples(int32_t max) { max_samples_.store(max, std::memory_order_release); } +template <typename T> +int32_t SampleRecorder<T>::GetMaxSamples() const { + return max_samples_.load(std::memory_order_acquire); +} + } // namespace profiling_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 4e37151e..5d433cce 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -436,6 +436,7 @@ cc_library( "//absl/container:inlined_vector", "//absl/functional:function_ref", "//absl/meta:type_traits", + "//absl/numeric:bits", "//absl/types:optional", "//absl/types:span", ], diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 662e889a..27d3475f 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -70,6 +70,7 @@ #include <string> #include <type_traits> +#include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/endian.h" #include "absl/base/internal/per_thread_tls.h" @@ -215,7 +216,7 @@ class Cord { // // Releases the Cord data. Any nodes that share data with other Cords, if // applicable, will have their reference counts reduced by 1. - void Clear(); + ABSL_ATTRIBUTE_REINITIALIZES void Clear(); // Cord::Append() // diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 8a311799..ea865cca 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -49,6 +49,11 @@ static constexpr auto MAX_FLAT_TAG = absl::cord_internal::MAX_FLAT_TAG; typedef std::mt19937_64 RandomEngine; +using absl::cord_internal::CordRep; +using absl::cord_internal::CordRepFlat; +using absl::cord_internal::kFlatOverhead; +using absl::cord_internal::kMaxFlatLength; + static std::string RandomLowercaseString(RandomEngine* rng); static std::string RandomLowercaseString(RandomEngine* rng, size_t length); @@ -266,10 +271,6 @@ INSTANTIATE_TEST_SUITE_P(WithParam, CordTest, testing::Values(0, 1, 2, 3), TEST(CordRepFlat, AllFlatCapacities) { - using absl::cord_internal::CordRep; - using absl::cord_internal::CordRepFlat; - using absl::cord_internal::kFlatOverhead; - // Explicitly and redundantly assert built-in min/max limits static_assert(absl::cord_internal::kFlatOverhead < 32, ""); static_assert(absl::cord_internal::kMinFlatSize == 32, ""); @@ -310,9 +311,6 @@ TEST(CordRepFlat, AllFlatCapacities) { } TEST(CordRepFlat, MaxFlatSize) { - using absl::cord_internal::CordRep; - using absl::cord_internal::CordRepFlat; - using absl::cord_internal::kMaxFlatLength; CordRepFlat* flat = CordRepFlat::New(kMaxFlatLength); EXPECT_EQ(flat->Capacity(), kMaxFlatLength); CordRep::Unref(flat); @@ -323,15 +321,23 @@ TEST(CordRepFlat, MaxFlatSize) { } TEST(CordRepFlat, MaxLargeFlatSize) { - using absl::cord_internal::CordRep; - using absl::cord_internal::CordRepFlat; - using absl::cord_internal::kFlatOverhead; const size_t size = 256 * 1024 - kFlatOverhead; CordRepFlat* flat = CordRepFlat::New(CordRepFlat::Large(), size); EXPECT_GE(flat->Capacity(), size); CordRep::Unref(flat); } +TEST(CordRepFlat, AllFlatSizes) { + const size_t kMaxSize = 256 * 1024; + for (size_t size = 32; size <= kMaxSize; size *=2) { + const size_t length = size - kFlatOverhead - 1; + CordRepFlat* flat = CordRepFlat::New(CordRepFlat::Large(), length); + EXPECT_GE(flat->Capacity(), length); + memset(flat->Data(), 0xCD, flat->Capacity()); + CordRep::Unref(flat); + } +} + TEST_P(CordTest, AllFlatSizes) { using absl::strings_internal::CordTestAccess; diff --git a/absl/strings/internal/cord_rep_flat.h b/absl/strings/internal/cord_rep_flat.h index a15c9acd..ae8b3a2a 100644 --- a/absl/strings/internal/cord_rep_flat.h +++ b/absl/strings/internal/cord_rep_flat.h @@ -20,6 +20,8 @@ #include <cstdint> #include <memory> +#include "absl/base/config.h" +#include "absl/base/macros.h" #include "absl/strings/internal/cord_internal.h" namespace absl { @@ -105,8 +107,8 @@ struct CordRepFlat : public CordRep { struct Large {}; // Creates a new flat node. - template <size_t max_flat_size> - static CordRepFlat* NewImpl(size_t len) { + template <size_t max_flat_size, typename... Args> + static CordRepFlat* NewImpl(size_t len, Args... args ABSL_ATTRIBUTE_UNUSED) { if (len <= kMinFlatLength) { len = kMinFlatLength; } else if (len > max_flat_size - kFlatOverhead) { diff --git a/absl/strings/internal/str_join_internal.h b/absl/strings/internal/str_join_internal.h index 31dbf672..d97d5033 100644 --- a/absl/strings/internal/str_join_internal.h +++ b/absl/strings/internal/str_join_internal.h @@ -229,10 +229,11 @@ std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s, std::string result; if (start != end) { // Sums size - size_t result_size = start->size(); + auto&& start_value = *start; + size_t result_size = start_value.size(); for (Iterator it = start; ++it != end;) { result_size += s.size(); - result_size += it->size(); + result_size += (*it).size(); } if (result_size > 0) { @@ -240,13 +241,15 @@ std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s, // Joins strings char* result_buf = &*result.begin(); - memcpy(result_buf, start->data(), start->size()); - result_buf += start->size(); + + memcpy(result_buf, start_value.data(), start_value.size()); + result_buf += start_value.size(); for (Iterator it = start; ++it != end;) { memcpy(result_buf, s.data(), s.size()); result_buf += s.size(); - memcpy(result_buf, it->data(), it->size()); - result_buf += it->size(); + auto&& value = *it; + memcpy(result_buf, value.data(), value.size()); + result_buf += value.size(); } } } diff --git a/absl/strings/str_join_test.cc b/absl/strings/str_join_test.cc index 2be6256e..c986e863 100644 --- a/absl/strings/str_join_test.cc +++ b/absl/strings/str_join_test.cc @@ -21,6 +21,7 @@ #include <cstdio> #include <functional> #include <initializer_list> +#include <iterator> #include <map> #include <memory> #include <ostream> @@ -33,6 +34,7 @@ #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" namespace { @@ -471,4 +473,136 @@ TEST(StrJoin, Tuple) { "-", absl::DereferenceFormatter(TestFormatter()))); } +// A minimal value type for `StrJoin` inputs. +// Used to ensure we do not excessively require more a specific type, such as a +// `string_view`. +// +// Anything that can be `data()` and `size()` is OK. +class TestValue { + public: + TestValue(const char* data, size_t size) : data_(data), size_(size) {} + const char* data() const { return data_; } + size_t size() const { return size_; } + + private: + const char* data_; + size_t size_; +}; + +// A minimal C++20 forward iterator, used to test that we do not impose +// excessive requirements on StrJoin inputs. +// +// The 2 main differences between pre-C++20 LegacyForwardIterator and the +// C++20 ForwardIterator are: +// 1. `operator->` is not required in C++20. +// 2. `operator*` result does not need to be an lvalue (a reference). +// +// The `operator->` requirement was removed on page 17 in: +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1037r0.pdf +// +// See the `[iterator.requirements]` section of the C++ standard. +// +// The value type is a template parameter so that we can test the behaviour +// of `StrJoin` specializations, e.g. the NoFormatter specialization for +// `string_view`. +template <typename ValueT> +class TestIterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = ValueT; + using pointer = void; + using reference = const value_type&; + using difference_type = int; + + // `data` must outlive the result. + static TestIterator begin(const std::vector<absl::string_view>& data) { + return TestIterator(&data, 0); + } + + static TestIterator end(const std::vector<absl::string_view>& data) { + return TestIterator(nullptr, data.size()); + } + + bool operator==(const TestIterator& other) const { + return pos_ == other.pos_; + } + bool operator!=(const TestIterator& other) const { + return pos_ != other.pos_; + } + + // This deliberately returns a `prvalue`. + // The requirement to return a reference was removed in C++20. + value_type operator*() const { + return ValueT((*data_)[pos_].data(), (*data_)[pos_].size()); + } + + // `operator->()` is deliberately omitted. + // The requirement to provide it was removed in C++20. + + TestIterator& operator++() { + ++pos_; + return *this; + } + + TestIterator operator++(int) { + TestIterator result = *this; + ++(*this); + return result; + } + + TestIterator& operator--() { + --pos_; + return *this; + } + + TestIterator operator--(int) { + TestIterator result = *this; + --(*this); + return result; + } + + private: + TestIterator(const std::vector<absl::string_view>* data, size_t pos) + : data_(data), pos_(pos) {} + + const std::vector<absl::string_view>* data_; + size_t pos_; +}; + +template <typename ValueT> +class TestIteratorRange { + public: + // `data` must be non-null and must outlive the result. + explicit TestIteratorRange(const std::vector<absl::string_view>& data) + : begin_(TestIterator<ValueT>::begin(data)), + end_(TestIterator<ValueT>::end(data)) {} + + const TestIterator<ValueT>& begin() const { return begin_; } + const TestIterator<ValueT>& end() const { return end_; } + + private: + TestIterator<ValueT> begin_; + TestIterator<ValueT> end_; +}; + +TEST(StrJoin, TestIteratorRequirementsNoFormatter) { + const std::vector<absl::string_view> a = {"a", "b", "c"}; + + // When the value type is string-like (`std::string` or `string_view`), + // the NoFormatter template specialization is used internally. + EXPECT_EQ("a-b-c", + absl::StrJoin(TestIteratorRange<absl::string_view>(a), "-")); +} + +TEST(StrJoin, TestIteratorRequirementsCustomFormatter) { + const std::vector<absl::string_view> a = {"a", "b", "c"}; + EXPECT_EQ("a-b-c", + absl::StrJoin(TestIteratorRange<TestValue>(a), "-", + [](std::string* out, const TestValue& value) { + absl::StrAppend( + out, + absl::string_view(value.data(), value.size())); + })); +} + } // namespace |