Roll abseil_revision f70eadadd7..3b22e57740
Change Log:
https://chromium.googlesource.com/external/github.com/abseil/abseil-cpp/+log/f70eadadd7..3b22e57740
Full diff:
https://chromium.googlesource.com/external/github.com/abseil/abseil-cpp/+/f70eadadd7..3b22e57740
Bug: None
Change-Id: I28427deea4e0e9e3df63bc8000aa93771529d3f5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3257270
Commit-Queue: Mirko Bonadei <mbonadei@chromium.org>
Reviewed-by: Danil Chapovalov <danilchap@chromium.org>
Cr-Commit-Position: refs/heads/main@{#937927}
NOKEYCHECK=True
GitOrigin-RevId: 1becef9e78101e4187ca04aa1b9ead6e83a02538
diff --git a/BUILD.gn b/BUILD.gn
index 6645b52..bf18d56 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -180,6 +180,7 @@
"absl/base:config_test",
"absl/cleanup:cleanup_test",
"absl/container:inlined_vector_test",
+ "absl/container:sample_element_size_test",
"absl/hash:low_level_hash_test",
"absl/memory:memory_test",
"absl/meta:type_traits_test",
@@ -187,6 +188,8 @@
"absl/profiling:periodic_sampler_test",
"absl/status:statusor_test",
"absl/strings:ascii_test",
+ "absl/strings:cord_internal_test",
+ "absl/strings:cord_test",
"absl/strings:cord_rep_btree_navigator_test",
"absl/strings:cord_rep_btree_reader_test",
"absl/strings:cord_rep_btree_test",
diff --git a/README.chromium b/README.chromium
index 8dabbd5..155b761 100644
--- a/README.chromium
+++ b/README.chromium
@@ -4,7 +4,7 @@
License: Apache 2.0
License File: LICENSE
Version: 0
-Revision: f70eadadd7767c3a97774b63c4c23981fa89af9f
+Revision: 3b22e57740b8aec4920c4cfd76b78b3a4fcb2bb5
Security Critical: yes
Description:
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index d1df524..ffaee19 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -876,6 +876,22 @@
],
)
+cc_test(
+ name = "sample_element_size_test",
+ srcs = ["sample_element_size_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ tags = NOTEST_TAGS_NONMOBILE,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":flat_hash_map",
+ ":flat_hash_set",
+ ":node_hash_map",
+ ":node_hash_set",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
cc_library(
name = "btree",
srcs = [
diff --git a/absl/container/BUILD.gn b/absl/container/BUILD.gn
index d57367e..54cf84a 100644
--- a/absl/container/BUILD.gn
+++ b/absl/container/BUILD.gn
@@ -326,6 +326,18 @@
]
}
+absl_source_set("sample_element_size_test") {
+ testonly = true
+ public = [ "sample_element_size_test.cc" ]
+ deps = [
+ ":flat_hash_map",
+ ":flat_hash_set",
+ ":node_hash_map",
+ ":node_hash_set",
+ "//third_party/googletest:gtest",
+ ]
+}
+
absl_source_set("btree") {
sources = [
"internal/btree.h",
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt
index 9b8a750..78584d2 100644
--- a/absl/container/CMakeLists.txt
+++ b/absl/container/CMakeLists.txt
@@ -894,3 +894,18 @@
absl::unordered_map_modifiers_test
GTest::gmock_main
)
+
+absl_cc_test(
+ NAME
+ sample_element_size_test
+ SRCS
+ "sample_element_size_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::flat_hash_map
+ absl::flat_hash_set
+ absl::node_hash_map
+ absl::node_hash_set
+ GTest::gmock_main
+)
diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc
index 7070912..40cce04 100644
--- a/absl/container/internal/hashtablez_sampler.cc
+++ b/absl/container/internal/hashtablez_sampler.cc
@@ -55,6 +55,9 @@
return *sampler;
}
+// TODO(bradleybear): The comments at this constructors declaration say that the
+// fields are not initialized, but this definition does initialize the fields.
+// Something needs to be cleaned up.
HashtablezInfo::HashtablezInfo() { PrepareForSampling(); }
HashtablezInfo::~HashtablezInfo() = default;
@@ -98,10 +101,12 @@
return state == kForce;
}
-HashtablezInfo* SampleSlow(int64_t* next_sample) {
+HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size) {
if (ABSL_PREDICT_FALSE(ShouldForceSampling())) {
*next_sample = 1;
- return GlobalHashtablezSampler().Register();
+ HashtablezInfo* result = GlobalHashtablezSampler().Register();
+ result->inline_element_size = inline_element_size;
+ return result;
}
#if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
@@ -123,10 +128,12 @@
// that case.
if (first) {
if (ABSL_PREDICT_TRUE(--*next_sample > 0)) return nullptr;
- return SampleSlow(next_sample);
+ return SampleSlow(next_sample, inline_element_size);
}
- return GlobalHashtablezSampler().Register();
+ HashtablezInfo* result = GlobalHashtablezSampler().Register();
+ result->inline_element_size = inline_element_size;
+ return result;
#endif
}
diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h
index 812118e..91fcdb3 100644
--- a/absl/container/internal/hashtablez_sampler.h
+++ b/absl/container/internal/hashtablez_sampler.h
@@ -91,6 +91,7 @@
absl::Time create_time;
int32_t depth;
void* stack[kMaxStackDepth];
+ size_t inline_element_size;
};
inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) {
@@ -143,7 +144,7 @@
std::memory_order_relaxed);
}
-HashtablezInfo* SampleSlow(int64_t* next_sample);
+HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size);
void UnsampleSlow(HashtablezInfo* info);
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
@@ -238,12 +239,14 @@
// Returns an RAII sampling handle that manages registration and unregistation
// with the global sampler.
-inline HashtablezInfoHandle Sample() {
+inline HashtablezInfoHandle Sample(
+ size_t inline_element_size ABSL_ATTRIBUTE_UNUSED) {
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) {
return HashtablezInfoHandle(nullptr);
}
- return HashtablezInfoHandle(SampleSlow(&global_next_sample));
+ return HashtablezInfoHandle(
+ SampleSlow(&global_next_sample, inline_element_size));
#else
return HashtablezInfoHandle(nullptr);
#endif // !ABSL_PER_THREAD_TLS
diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc
index f053c19..449619a 100644
--- a/absl/container/internal/hashtablez_sampler_test.cc
+++ b/absl/container/internal/hashtablez_sampler_test.cc
@@ -78,10 +78,12 @@
TEST(HashtablezInfoTest, PrepareForSampling) {
absl::Time test_start = absl::Now();
+ const size_t test_element_size = 17;
HashtablezInfo info;
absl::MutexLock l(&info.init_mu);
info.PrepareForSampling();
+ info.inline_element_size = test_element_size;
EXPECT_EQ(info.capacity.load(), 0);
EXPECT_EQ(info.size.load(), 0);
EXPECT_EQ(info.num_erases.load(), 0);
@@ -93,6 +95,7 @@
EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
EXPECT_EQ(info.max_reserve.load(), 0);
EXPECT_GE(info.create_time, test_start);
+ EXPECT_EQ(info.inline_element_size, test_element_size);
info.capacity.store(1, std::memory_order_relaxed);
info.size.store(1, std::memory_order_relaxed);
@@ -116,6 +119,7 @@
EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
EXPECT_EQ(info.max_reserve.load(), 0);
+ EXPECT_EQ(info.inline_element_size, test_element_size);
EXPECT_GE(info.create_time, test_start);
}
@@ -154,9 +158,11 @@
}
TEST(HashtablezInfoTest, RecordErase) {
+ const size_t test_element_size = 29;
HashtablezInfo info;
absl::MutexLock l(&info.init_mu);
info.PrepareForSampling();
+ info.inline_element_size = test_element_size;
EXPECT_EQ(info.num_erases.load(), 0);
EXPECT_EQ(info.size.load(), 0);
RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength);
@@ -164,12 +170,15 @@
RecordEraseSlow(&info);
EXPECT_EQ(info.size.load(), 0);
EXPECT_EQ(info.num_erases.load(), 1);
+ EXPECT_EQ(info.inline_element_size, test_element_size);
}
TEST(HashtablezInfoTest, RecordRehash) {
+ const size_t test_element_size = 31;
HashtablezInfo info;
absl::MutexLock l(&info.init_mu);
info.PrepareForSampling();
+ info.inline_element_size = test_element_size;
RecordInsertSlow(&info, 0x1, 0);
RecordInsertSlow(&info, 0x2, kProbeLength);
RecordInsertSlow(&info, 0x4, kProbeLength);
@@ -188,6 +197,7 @@
EXPECT_EQ(info.total_probe_length.load(), 3);
EXPECT_EQ(info.num_erases.load(), 0);
EXPECT_EQ(info.num_rehashes.load(), 1);
+ EXPECT_EQ(info.inline_element_size, test_element_size);
}
TEST(HashtablezInfoTest, RecordReservation) {
@@ -208,12 +218,13 @@
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
TEST(HashtablezSamplerTest, SmallSampleParameter) {
+ const size_t test_element_size = 31;
SetHashtablezEnabled(true);
SetHashtablezSampleParameter(100);
for (int i = 0; i < 1000; ++i) {
int64_t next_sample = 0;
- HashtablezInfo* sample = SampleSlow(&next_sample);
+ HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size);
EXPECT_GT(next_sample, 0);
EXPECT_NE(sample, nullptr);
UnsampleSlow(sample);
@@ -221,12 +232,13 @@
}
TEST(HashtablezSamplerTest, LargeSampleParameter) {
+ const size_t test_element_size = 31;
SetHashtablezEnabled(true);
SetHashtablezSampleParameter(std::numeric_limits<int32_t>::max());
for (int i = 0; i < 1000; ++i) {
int64_t next_sample = 0;
- HashtablezInfo* sample = SampleSlow(&next_sample);
+ HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size);
EXPECT_GT(next_sample, 0);
EXPECT_NE(sample, nullptr);
UnsampleSlow(sample);
@@ -234,13 +246,14 @@
}
TEST(HashtablezSamplerTest, Sample) {
+ const size_t test_element_size = 31;
SetHashtablezEnabled(true);
SetHashtablezSampleParameter(100);
int64_t num_sampled = 0;
int64_t total = 0;
double sample_rate = 0.0;
for (int i = 0; i < 1000000; ++i) {
- HashtablezInfoHandle h = Sample();
+ HashtablezInfoHandle h = Sample(test_element_size);
++total;
if (HashtablezInfoHandlePeer::IsSampled(h)) {
++num_sampled;
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h
index 47e5a22..12682b3 100644
--- a/absl/container/internal/raw_hash_set.h
+++ b/absl/container/internal/raw_hash_set.h
@@ -1643,7 +1643,7 @@
// bound more carefully.
if (std::is_same<SlotAlloc, std::allocator<slot_type>>::value &&
slots_ == nullptr) {
- infoz() = Sample();
+ infoz() = Sample(sizeof(slot_type));
}
char* mem = static_cast<char*>(Allocate<alignof(slot_type)>(
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc
index b46c492..362b3ca 100644
--- a/absl/container/internal/raw_hash_set_test.cc
+++ b/absl/container/internal/raw_hash_set_test.cc
@@ -2075,6 +2075,7 @@
std::memory_order_relaxed)]++;
reservations[info.max_reserve.load(std::memory_order_relaxed)]++;
}
+ EXPECT_EQ(info.inline_element_size, sizeof(int64_t));
++end_size;
});
diff --git a/absl/container/sample_element_size_test.cc b/absl/container/sample_element_size_test.cc
new file mode 100644
index 0000000..b23626b
--- /dev/null
+++ b/absl/container/sample_element_size_test.cc
@@ -0,0 +1,114 @@
+// 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 "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/container/flat_hash_map.h"
+#include "absl/container/flat_hash_set.h"
+#include "absl/container/node_hash_map.h"
+#include "absl/container/node_hash_set.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace container_internal {
+namespace {
+
+#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
+// Create some tables of type `Table`, then look at all the new
+// `HashtablezInfo`s to make sure that the `inline_element_size ==
+// expected_element_size`. The `inline_element_size` is the amount of memory
+// allocated for each slot of a hash table, that is `sizeof(slot_type)`. Add
+// the new `HashtablezInfo`s to `preexisting_info`. Store all the new tables
+// into `tables`.
+template <class Table>
+void TestInlineElementSize(
+ HashtablezSampler& sampler,
+ // clang-tidy gives a false positive on this declaration. This unordered
+ // set cannot be flat_hash_set, however, since that would introduce a mutex
+ // deadlock.
+ std::unordered_set<const HashtablezInfo*>& preexisting_info, // NOLINT
+ std::vector<Table>& tables, const typename Table::value_type& elt,
+ size_t expected_element_size) {
+ for (int i = 0; i < 10; ++i) {
+ // We create a new table and must store it somewhere so that when we store
+ // a pointer to the resulting `HashtablezInfo` into `preexisting_info`
+ // that we aren't storing a dangling pointer.
+ tables.emplace_back();
+ // We must insert an element to get a hashtablez to instantiate.
+ tables.back().insert(elt);
+ }
+ size_t new_count = 0;
+ sampler.Iterate([&](const HashtablezInfo& info) {
+ if (preexisting_info.insert(&info).second) {
+ EXPECT_EQ(info.inline_element_size, expected_element_size);
+ ++new_count;
+ }
+ });
+ // Make sure we actually did get a new hashtablez.
+ EXPECT_GT(new_count, 0);
+}
+
+struct bigstruct {
+ char a[1000];
+ friend bool operator==(const bigstruct& x, const bigstruct& y) {
+ return memcmp(x.a, y.a, sizeof(x.a)) == 0;
+ }
+ template <typename H>
+ friend H AbslHashValue(H h, const bigstruct& c) {
+ return H::combine_contiguous(std::move(h), c.a, sizeof(c.a));
+ }
+};
+#endif
+
+TEST(FlatHashMap, SampleElementSize) {
+#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
+ // Enable sampling even if the prod default is off.
+ SetHashtablezEnabled(true);
+ SetHashtablezSampleParameter(1);
+
+ auto& sampler = GlobalHashtablezSampler();
+ std::vector<flat_hash_map<int, bigstruct>> flat_map_tables;
+ std::vector<flat_hash_set<bigstruct>> flat_set_tables;
+ std::vector<node_hash_map<int, bigstruct>> node_map_tables;
+ std::vector<node_hash_set<bigstruct>> node_set_tables;
+
+ // It takes thousands of new tables after changing the sampling parameters
+ // before you actually get some instrumentation. And if you must actually
+ // put something into those tables.
+ for (int i = 0; i < 10000; ++i) {
+ flat_map_tables.emplace_back();
+ flat_map_tables.back()[i] = bigstruct{};
+ }
+
+ // clang-tidy gives a false positive on this declaration. This unordered set
+ // cannot be a flat_hash_set, however, since that would introduce a mutex
+ // deadlock.
+ std::unordered_set<const HashtablezInfo*> preexisting_info; // NOLINT
+ sampler.Iterate(
+ [&](const HashtablezInfo& info) { preexisting_info.insert(&info); });
+ TestInlineElementSize(sampler, preexisting_info, flat_map_tables,
+ {0, bigstruct{}}, sizeof(int) + sizeof(bigstruct));
+ TestInlineElementSize(sampler, preexisting_info, node_map_tables,
+ {0, bigstruct{}}, sizeof(void*));
+ TestInlineElementSize(sampler, preexisting_info, flat_set_tables, //
+ bigstruct{}, sizeof(bigstruct));
+ TestInlineElementSize(sampler, preexisting_info, node_set_tables, //
+ bigstruct{}, sizeof(void*));
+#endif
+}
+
+} // namespace
+} // namespace container_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/flags/flag.h b/absl/flags/flag.h
index 1a7e4d4..a724ccc 100644
--- a/absl/flags/flag.h
+++ b/absl/flags/flag.h
@@ -241,8 +241,8 @@
/* default value argument. That keeps temporaries alive */ \
/* long enough for NonConst to work correctly. */ \
static constexpr absl::string_view Value( \
- absl::string_view v = ABSL_FLAG_IMPL_FLAGHELP(txt)) { \
- return v; \
+ absl::string_view absl_flag_help = ABSL_FLAG_IMPL_FLAGHELP(txt)) { \
+ return absl_flag_help; \
} \
static std::string NonConst() { return std::string(Value()); } \
}; \
@@ -254,8 +254,8 @@
#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \
struct AbslFlagDefaultGenFor##name { \
Type value = absl::flags_internal::InitDefaultValue<Type>(default_value); \
- static void Gen(void* p) { \
- new (p) Type(AbslFlagDefaultGenFor##name{}.value); \
+ static void Gen(void* absl_flag_default_loc) { \
+ new (absl_flag_default_loc) Type(AbslFlagDefaultGenFor##name{}.value); \
} \
};
diff --git a/absl/random/internal/explicit_seed_seq.h b/absl/random/internal/explicit_seed_seq.h
index e3aa31a..25f7915 100644
--- a/absl/random/internal/explicit_seed_seq.h
+++ b/absl/random/internal/explicit_seed_seq.h
@@ -74,7 +74,7 @@
template <typename OutIterator>
void generate(OutIterator begin, OutIterator end) {
for (size_t index = 0; begin != end; begin++) {
- *begin = state_.empty() ? 0 : little_endian::FromHost32(state_[index++]);
+ *begin = state_.empty() ? 0 : state_[index++];
if (index >= state_.size()) {
index = 0;
}
diff --git a/absl/random/internal/randen_engine.h b/absl/random/internal/randen_engine.h
index 92bb890..372c3ac 100644
--- a/absl/random/internal/randen_engine.h
+++ b/absl/random/internal/randen_engine.h
@@ -121,6 +121,13 @@
const size_t requested_entropy = (entropy_size == 0) ? 8u : entropy_size;
std::fill(std::begin(buffer) + requested_entropy, std::end(buffer), 0);
seq.generate(std::begin(buffer), std::begin(buffer) + requested_entropy);
+#ifdef ABSL_IS_BIG_ENDIAN
+ // Randen expects the seed buffer to be in Little Endian; reverse it on
+ // Big Endian platforms.
+ for (sequence_result_type& e : buffer) {
+ e = absl::little_endian::FromHost(e);
+ }
+#endif
// The Randen paper suggests preferentially initializing even-numbered
// 128-bit vectors of the randen state (there are 16 such vectors).
// The seed data is merged into the state offset by 128-bits, which
diff --git a/absl/random/internal/randen_slow.cc b/absl/random/internal/randen_slow.cc
index d5c9347..9bfd2a4 100644
--- a/absl/random/internal/randen_slow.cc
+++ b/absl/random/internal/randen_slow.cc
@@ -395,6 +395,23 @@
}
}
+// Enables native loads in the round loop by pre-swapping.
+inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void SwapEndian(
+ absl::uint128* state) {
+#ifdef ABSL_IS_BIG_ENDIAN
+ for (uint32_t block = 0; block < RandenTraits::kFeistelBlocks; ++block) {
+ uint64_t new_lo = absl::little_endian::ToHost64(
+ static_cast<uint64_t>(state[block] >> 64));
+ uint64_t new_hi = absl::little_endian::ToHost64(
+ static_cast<uint64_t>((state[block] << 64) >> 64));
+ state[block] = (static_cast<absl::uint128>(new_hi) << 64) | new_lo;
+ }
+#else
+ // Avoid warning about unused variable.
+ (void)state;
+#endif
+}
+
} // namespace
namespace absl {
@@ -439,8 +456,12 @@
const absl::uint128 prev_inner = state[0];
+ SwapEndian(state);
+
Permute(state, keys);
+ SwapEndian(state);
+
// Ensure backtracking resistance.
*state ^= prev_inner;
}
diff --git a/absl/random/internal/randen_test.cc b/absl/random/internal/randen_test.cc
index c186fe0..92773b8 100644
--- a/absl/random/internal/randen_test.cc
+++ b/absl/random/internal/randen_test.cc
@@ -23,9 +23,6 @@
using absl::random_internal::Randen;
-// Local state parameters.
-constexpr size_t kStateSizeT = Randen::kStateBytes / sizeof(uint64_t);
-
TEST(RandenTest, CopyAndMove) {
static_assert(std::is_copy_constructible<Randen>::value,
"Randen must be copy constructible");
@@ -41,30 +38,38 @@
}
TEST(RandenTest, Default) {
- constexpr uint64_t kGolden[] = {
- 0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977,
- 0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912,
- 0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6,
- 0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a,
- 0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37,
- 0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556,
- 0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42,
- 0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc,
- 0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f,
- 0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3,
- 0x026ff374c101da7e, 0x811ef0821c3de851,
+ constexpr uint8_t kGolden[] = {
+ 0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d,
+ 0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3,
+ 0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f,
+ 0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0,
+ 0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff,
+ 0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2,
+ 0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5,
+ 0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c,
+ 0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56,
+ 0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4,
+ 0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91,
+ 0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5,
+ 0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d,
+ 0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58,
+ 0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e,
+ 0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49,
+ 0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0,
+ 0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6,
+ 0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61,
+ 0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35,
+ 0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c,
+ 0x82, 0xf0, 0x1e, 0x81,
};
- alignas(16) uint64_t state[kStateSizeT];
+ alignas(16) uint8_t state[Randen::kStateBytes];
std::memset(state, 0, sizeof(state));
Randen r;
r.Generate(state);
- auto id = std::begin(state);
- for (const auto& elem : kGolden) {
- EXPECT_EQ(elem, *id++);
- }
+ EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state)));
}
} // namespace
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index c046366..090fc58 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -306,6 +306,17 @@
)
cc_test(
+ name = "cord_internal_test",
+ srcs = ["internal/cord_internal_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":cord_internal",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
name = "cord_rep_btree_test",
size = "medium",
srcs = ["internal/cord_rep_btree_test.cc"],
@@ -684,6 +695,7 @@
"//absl/base:endian",
"//absl/base:raw_logging_internal",
"//absl/container:fixed_array",
+ "//absl/random",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/strings/BUILD.gn b/absl/strings/BUILD.gn
index 69fc521..55249c5 100644
--- a/absl/strings/BUILD.gn
+++ b/absl/strings/BUILD.gn
@@ -154,6 +154,16 @@
]
}
+absl_source_set("cord_internal_test") {
+ testonly = true
+ sources = [ "internal/cord_internal_test.cc" ]
+ deps = [
+ ":cord_internal",
+ "//third_party/googletest:gmock",
+ "//third_party/googletest:gtest",
+ ]
+}
+
absl_source_set("cord_rep_btree_test") {
testonly = true
sources = [ "internal/cord_rep_btree_test.cc" ]
@@ -450,6 +460,27 @@
]
}
+absl_source_set("cord_test") {
+ testonly = true
+ public = [ "cord_test.cc" ]
+ deps = [
+ ":cord",
+ ":cord_test_helpers",
+ ":cordz_functions",
+ ":cordz_test_helpers",
+ ":str_format",
+ ":strings",
+ "//third_party/abseil-cpp/absl/base",
+ "//third_party/abseil-cpp/absl/base:config",
+ "//third_party/abseil-cpp/absl/base:core_headers",
+ "//third_party/abseil-cpp/absl/base:endian",
+ "//third_party/abseil-cpp/absl/base:raw_logging_internal",
+ "//third_party/abseil-cpp/absl/container:fixed_array",
+ "//third_party/abseil-cpp/absl/random",
+ "//third_party/googletest:gtest",
+ ]
+}
+
absl_source_set("cordz_test") {
testonly = true
sources = [ "cordz_test.cc" ]
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index 8ad5f9c..d6801fe 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -920,6 +920,7 @@
absl::cordz_test_helpers
absl::core_headers
absl::endian
+ absl::random_random
absl::raw_logging_internal
absl::fixed_array
GTest::gmock_main
@@ -945,6 +946,18 @@
absl_cc_test(
NAME
+ cord_internal_test
+ SRCS
+ "internal/cord_internal_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::cord_internal
+ GTest::gmock_main
+)
+
+absl_cc_test(
+ NAME
cord_rep_btree_test
SRCS
"internal/cord_rep_btree_test.cc"
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc
index 29af978..854047c 100644
--- a/absl/strings/cord.cc
+++ b/absl/strings/cord.cc
@@ -419,7 +419,7 @@
// written to region and the actual size increase will be written to size.
static inline bool PrepareAppendRegion(CordRep* root, char** region,
size_t* size, size_t max_length) {
- if (root->IsBtree() && root->refcount.IsOne()) {
+ if (root->IsBtree() && root->refcount.IsMutable()) {
Span<char> span = root->btree()->GetAppendBuffer(max_length);
if (!span.empty()) {
*region = span.data();
@@ -430,11 +430,11 @@
// Search down the right-hand path for a non-full FLAT node.
CordRep* dst = root;
- while (dst->IsConcat() && dst->refcount.IsOne()) {
+ while (dst->IsConcat() && dst->refcount.IsMutable()) {
dst = dst->concat()->right;
}
- if (!dst->IsFlat() || !dst->refcount.IsOne()) {
+ if (!dst->IsFlat() || !dst->refcount.IsMutable()) {
*region = nullptr;
*size = 0;
return false;
@@ -649,7 +649,7 @@
if (tree != nullptr) {
CordzUpdateScope scope(contents_.cordz_info(), method);
if (tree->IsFlat() && tree->flat()->Capacity() >= length &&
- tree->refcount.IsOne()) {
+ tree->refcount.IsMutable()) {
// Copy in place if the existing FLAT node is reusable.
memmove(tree->flat()->Data(), data, length);
tree->length = length;
@@ -819,7 +819,7 @@
return Prepend(src_contents);
}
-void Cord::Prepend(absl::string_view src) {
+void Cord::PrependArray(absl::string_view src, MethodIdentifier method) {
if (src.empty()) return; // memcpy(_, nullptr, 0) is undefined.
if (!contents_.is_tree()) {
size_t cur_size = contents_.inline_size();
@@ -834,7 +834,7 @@
}
}
CordRep* rep = NewTree(src.data(), src.size(), 0);
- contents_.PrependTree(rep, CordzUpdateTracker::kPrependString);
+ contents_.PrependTree(rep, method);
}
template <typename T, Cord::EnableIfString<T>>
@@ -894,7 +894,7 @@
if (n >= node->length) return nullptr;
if (n == 0) return CordRep::Ref(node);
absl::InlinedVector<CordRep*, kInlinedVectorSize> lhs_stack;
- bool inplace_ok = node->refcount.IsOne();
+ bool inplace_ok = node->refcount.IsMutable();
while (node->IsConcat()) {
assert(n <= node->length);
@@ -907,7 +907,7 @@
n -= node->concat()->right->length;
node = node->concat()->left;
}
- inplace_ok = inplace_ok && node->refcount.IsOne();
+ inplace_ok = inplace_ok && node->refcount.IsMutable();
}
assert(n <= node->length);
@@ -968,9 +968,7 @@
auto constexpr method = CordzUpdateTracker::kRemoveSuffix;
CordzUpdateScope scope(contents_.cordz_info(), method);
if (tree->IsBtree()) {
- CordRep* old = tree;
- tree = tree->btree()->SubTree(0, tree->length - n);
- CordRep::Unref(old);
+ tree = CordRepBtree::RemoveSuffix(tree->btree(), n);
} else {
CordRep* newrep = RemoveSuffixFrom(tree, n);
CordRep::Unref(tree);
diff --git a/absl/strings/cord.h b/absl/strings/cord.h
index 175d7b9..f0a1991 100644
--- a/absl/strings/cord.h
+++ b/absl/strings/cord.h
@@ -460,6 +460,16 @@
// `Cord::chunk_begin()` and `Cord::chunk_end()`.
class ChunkRange {
public:
+ // Fulfill minimum c++ container requirements [container.requirements]
+ // Theses (partial) container type definitions allow ChunkRange to be used
+ // in various utilities expecting a subset of [container.requirements].
+ // For example, the below enables using `::testing::ElementsAre(...)`
+ using value_type = absl::string_view;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using iterator = ChunkIterator;
+ using const_iterator = ChunkIterator;
+
explicit ChunkRange(const Cord* cord) : cord_(cord) {}
ChunkIterator begin() const;
@@ -591,6 +601,16 @@
// `Cord::char_begin()` and `Cord::char_end()`.
class CharRange {
public:
+ // Fulfill minimum c++ container requirements [container.requirements]
+ // Theses (partial) container type definitions allow CharRange to be used
+ // in various utilities expecting a subset of [container.requirements].
+ // For example, the below enables using `::testing::ElementsAre(...)`
+ using value_type = char;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using iterator = CharIterator;
+ using const_iterator = CharIterator;
+
explicit CharRange(const Cord* cord) : cord_(cord) {}
CharIterator begin() const;
@@ -881,6 +901,10 @@
template <typename C>
void AppendImpl(C&& src);
+ // Prepends the provided data to this instance. `method` contains the public
+ // API method for this action which is tracked for Cordz sampling purposes.
+ void PrependArray(absl::string_view src, MethodIdentifier method);
+
// Assigns the value in 'src' to this instance, 'stealing' its contents.
// Requires src.length() > kMaxBytesToCopy.
Cord& AssignLargeString(std::string&& src);
@@ -1220,6 +1244,10 @@
contents_.AppendArray(src, CordzUpdateTracker::kAppendString);
}
+inline void Cord::Prepend(absl::string_view src) {
+ PrependArray(src, CordzUpdateTracker::kPrependString);
+}
+
extern template void Cord::Append(std::string&& src);
extern template void Cord::Prepend(std::string&& src);
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc
index 50079b7..cced9bb 100644
--- a/absl/strings/cord_test.cc
+++ b/absl/strings/cord_test.cc
@@ -34,6 +34,7 @@
#include "absl/base/internal/raw_logging.h"
#include "absl/base/macros.h"
#include "absl/container/fixed_array.h"
+#include "absl/random/random.h"
#include "absl/strings/cord_test_helpers.h"
#include "absl/strings/cordz_test_helpers.h"
#include "absl/strings/str_cat.h"
@@ -1833,6 +1834,48 @@
EXPECT_DEATH_IF_SUPPORTED(++cord.chunk_end(), "");
}
+// This test mimics a specific (and rare) application repeatedly splitting a
+// cord, inserting (overwriting) a string value, and composing a new cord from
+// the three pieces. This is hostile towards a Btree implementation: A split of
+// a node at any level is likely to have the right-most edge of the left split,
+// and the left-most edge of the right split shared. For example, splitting a
+// leaf node with 6 edges will result likely in a 1-6, 2-5, 3-4, etc. split,
+// sharing the 'split node'. When recomposing such nodes, we 'injected' an edge
+// in that node. As this happens with some probability on each level of the
+// tree, this will quickly grow the tree until it reaches maximum height.
+TEST_P(CordTest, BtreeHostileSplitInsertJoin) {
+ absl::BitGen bitgen;
+
+ // Start with about 1GB of data
+ std::string data(1 << 10, 'x');
+ absl::Cord buffer(data);
+ absl::Cord cord;
+ for (int i = 0; i < 1000000; ++i) {
+ cord.Append(buffer);
+ }
+
+ for (int j = 0; j < 1000; ++j) {
+ size_t offset = absl::Uniform(bitgen, 0u, cord.size());
+ size_t length = absl::Uniform(bitgen, 100u, data.size());
+ if (cord.size() == offset) {
+ cord.Append(absl::string_view(data.data(), length));
+ } else {
+ absl::Cord suffix;
+ if (offset + length < cord.size()) {
+ suffix = cord;
+ suffix.RemovePrefix(offset + length);
+ }
+ if (cord.size() > offset) {
+ cord.RemoveSuffix(cord.size() - offset);
+ }
+ cord.Append(absl::string_view(data.data(), length));
+ if (!suffix.empty()) {
+ cord.Append(suffix);
+ }
+ }
+ }
+}
+
class AfterExitCordTester {
public:
bool Set(absl::Cord* cord, absl::string_view expected) {
diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h
index 0300ac4..bfe5564 100644
--- a/absl/strings/internal/cord_internal.h
+++ b/absl/strings/internal/cord_internal.h
@@ -87,6 +87,9 @@
constexpr RefcountAndFlags() : count_{kRefIncrement} {}
struct Immortal {};
explicit constexpr RefcountAndFlags(Immortal) : count_(kImmortalFlag) {}
+ struct WithCrc {};
+ explicit constexpr RefcountAndFlags(WithCrc)
+ : count_(kCrcFlag | kRefIncrement) {}
// Increments the reference count. Imposes no memory ordering.
inline void Increment() {
@@ -122,14 +125,32 @@
return count_.load(std::memory_order_acquire) >> kNumFlags;
}
- // Returns whether the atomic integer is 1.
- // If the reference count is used in the conventional way, a
- // reference count of 1 implies that the current thread owns the
- // reference and no other thread shares it.
- // This call performs the test for a reference count of one, and
- // performs the memory barrier needed for the owning thread
- // to act on the object, knowing that it has exclusive access to the
- // object. Always returns false when the immortal bit is set.
+ // Returns true if the referenced object carries a CRC value.
+ bool HasCrc() const {
+ return (count_.load(std::memory_order_relaxed) & kCrcFlag) != 0;
+ }
+
+ // Returns true iff the atomic integer is 1 and this node does not store
+ // a CRC. When both these conditions are met, the current thread owns
+ // the reference and no other thread shares it, so its contents may be
+ // safely mutated.
+ //
+ // If the referenced item is shared, carries a CRC, or is immortal,
+ // it should not be modified in-place, and this function returns false.
+ //
+ // This call performs the memory barrier needed for the owning thread
+ // to act on the object, so that if it returns true, it may safely
+ // assume exclusive access to the object.
+ inline bool IsMutable() {
+ return (count_.load(std::memory_order_acquire)) == kRefIncrement;
+ }
+
+ // Returns whether the atomic integer is 1. Similar to IsMutable(),
+ // but does not check for a stored CRC. (An unshared node with a CRC is not
+ // mutable, because changing its data would invalidate the CRC.)
+ //
+ // When this returns true, there are no other references, and data sinks
+ // may safely adopt the children of the CordRep.
inline bool IsOne() {
return (count_.load(std::memory_order_acquire) & kRefcountMask) ==
kRefIncrement;
@@ -149,14 +170,14 @@
kNumFlags = 2,
kImmortalFlag = 0x1,
- kReservedFlag = 0x2,
+ kCrcFlag = 0x2,
kRefIncrement = (1 << kNumFlags),
// Bitmask to use when checking refcount by equality. This masks out
// all flags except kImmortalFlag, which is part of the refcount for
// purposes of equality. (A refcount of 0 or 1 does not count as 0 or 1
// if the immortal bit is set.)
- kRefcountMask = ~kReservedFlag,
+ kRefcountMask = ~kCrcFlag,
};
std::atomic<int32_t> count_;
diff --git a/absl/strings/internal/cord_internal_test.cc b/absl/strings/internal/cord_internal_test.cc
new file mode 100644
index 0000000..0758dfe
--- /dev/null
+++ b/absl/strings/internal/cord_internal_test.cc
@@ -0,0 +1,116 @@
+// Copyright 2021 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/strings/internal/cord_internal.h"
+
+#include "gmock/gmock.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+TEST(RefcountAndFlags, NormalRefcount) {
+ for (bool expect_high_refcount : {false, true}) {
+ SCOPED_TRACE(expect_high_refcount);
+ RefcountAndFlags refcount;
+ // count = 1
+
+ EXPECT_FALSE(refcount.HasCrc());
+ EXPECT_TRUE(refcount.IsMutable());
+ EXPECT_TRUE(refcount.IsOne());
+
+ refcount.Increment();
+ // count = 2
+
+ EXPECT_FALSE(refcount.HasCrc());
+ EXPECT_FALSE(refcount.IsMutable());
+ EXPECT_FALSE(refcount.IsOne());
+
+ // Decrementing should return true, since a reference is outstanding.
+ if (expect_high_refcount) {
+ EXPECT_TRUE(refcount.DecrementExpectHighRefcount());
+ } else {
+ EXPECT_TRUE(refcount.Decrement());
+ }
+ // count = 1
+
+ EXPECT_FALSE(refcount.HasCrc());
+ EXPECT_TRUE(refcount.IsMutable());
+ EXPECT_TRUE(refcount.IsOne());
+
+ // One more decremnt will return false, as no references remain.
+ if (expect_high_refcount) {
+ EXPECT_FALSE(refcount.DecrementExpectHighRefcount());
+ } else {
+ EXPECT_FALSE(refcount.Decrement());
+ }
+ }
+}
+
+TEST(RefcountAndFlags, CrcRefcount) {
+ for (bool expect_high_refcount : {false, true}) {
+ SCOPED_TRACE(expect_high_refcount);
+ RefcountAndFlags refcount(RefcountAndFlags::WithCrc{});
+ // count = 1
+
+ // A CRC-carrying node is never mutable, but can be unshared
+ EXPECT_TRUE(refcount.HasCrc());
+ EXPECT_FALSE(refcount.IsMutable());
+ EXPECT_TRUE(refcount.IsOne());
+
+ refcount.Increment();
+ // count = 2
+
+ EXPECT_TRUE(refcount.HasCrc());
+ EXPECT_FALSE(refcount.IsMutable());
+ EXPECT_FALSE(refcount.IsOne());
+
+ // Decrementing should return true, since a reference is outstanding.
+ if (expect_high_refcount) {
+ EXPECT_TRUE(refcount.DecrementExpectHighRefcount());
+ } else {
+ EXPECT_TRUE(refcount.Decrement());
+ }
+ // count = 1
+
+ EXPECT_TRUE(refcount.HasCrc());
+ EXPECT_FALSE(refcount.IsMutable());
+ EXPECT_TRUE(refcount.IsOne());
+
+ // One more decremnt will return false, as no references remain.
+ if (expect_high_refcount) {
+ EXPECT_FALSE(refcount.DecrementExpectHighRefcount());
+ } else {
+ EXPECT_FALSE(refcount.Decrement());
+ }
+ }
+}
+
+TEST(RefcountAndFlags, ImmortalRefcount) {
+ RefcountAndFlags immortal_refcount(RefcountAndFlags::Immortal{});
+
+ for (int i = 0; i < 100; ++i) {
+ // An immortal refcount is never unshared, and decrementing never causes
+ // a collection.
+ EXPECT_FALSE(immortal_refcount.HasCrc());
+ EXPECT_FALSE(immortal_refcount.IsMutable());
+ EXPECT_FALSE(immortal_refcount.IsOne());
+ EXPECT_TRUE(immortal_refcount.Decrement());
+ EXPECT_TRUE(immortal_refcount.DecrementExpectHighRefcount());
+ }
+}
+
+} // namespace cord_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc
index 6d53ab6..4404f33 100644
--- a/absl/strings/internal/cord_rep_btree.cc
+++ b/absl/strings/internal/cord_rep_btree.cc
@@ -140,6 +140,26 @@
return CreateSubstring(rep, offset, rep->length - offset);
}
+// Resizes `edge` to the provided `length`. Adopts a reference on `edge`.
+// This method directly returns `edge` if `length` equals `edge->length`.
+// If `is_mutable` is set to true, this function may return `edge` with
+// `edge->length` set to the new length depending on the type and size of
+// `edge`. Otherwise, this function returns a new CordRepSubstring value.
+// Requires `length > 0 && length <= edge->length`.
+CordRep* ResizeEdge(CordRep* edge, size_t length, bool is_mutable) {
+ assert(length > 0);
+ assert(length <= edge->length);
+ assert(CordRepBtree::IsDataEdge(edge));
+ if (length >= edge->length) return edge;
+
+ if (is_mutable && (edge->tag >= FLAT || edge->tag == SUBSTRING)) {
+ edge->length = length;
+ return edge;
+ }
+
+ return CreateSubstring(edge, 0, length);
+}
+
template <EdgeType edge_type>
inline absl::string_view Consume(absl::string_view s, size_t n) {
return edge_type == kBack ? s.substr(n) : s.substr(0, s.size() - n);
@@ -196,8 +216,8 @@
// propagate node changes up the stack.
template <EdgeType edge_type>
struct StackOperations {
- // Returns true if the node at 'depth' is not shared, i.e. has a refcount
- // of one and all of its parent nodes have a refcount of one.
+ // Returns true if the node at 'depth' is mutable, i.e. has a refcount
+ // of one, carries no CRC, and all of its parent nodes have a refcount of one.
inline bool owned(int depth) const { return depth < share_depth; }
// Returns the node at 'depth'.
@@ -208,11 +228,11 @@
inline CordRepBtree* BuildStack(CordRepBtree* tree, int depth) {
assert(depth <= tree->height());
int current_depth = 0;
- while (current_depth < depth && tree->refcount.IsOne()) {
+ while (current_depth < depth && tree->refcount.IsMutable()) {
stack[current_depth++] = tree;
tree = tree->Edge(edge_type)->btree();
}
- share_depth = current_depth + (tree->refcount.IsOne() ? 1 : 0);
+ share_depth = current_depth + (tree->refcount.IsMutable() ? 1 : 0);
while (current_depth < depth) {
stack[current_depth++] = tree;
tree = tree->Edge(edge_type)->btree();
@@ -221,17 +241,17 @@
}
// Builds a stack with the invariant that all nodes are private owned / not
- // shared. This is used in iterative updates where a previous propagation
- // guaranteed all nodes are owned / private.
+ // shared and carry no CRC data. This is used in iterative updates where a
+ // previous propagation guaranteed all nodes have this property.
inline void BuildOwnedStack(CordRepBtree* tree, int height) {
assert(height <= CordRepBtree::kMaxHeight);
int depth = 0;
while (depth < height) {
- assert(tree->refcount.IsOne());
+ assert(tree->refcount.IsMutable());
stack[depth++] = tree;
tree = tree->Edge(edge_type)->btree();
}
- assert(tree->refcount.IsOne());
+ assert(tree->refcount.IsMutable());
share_depth = depth + 1;
}
@@ -240,11 +260,14 @@
static inline CordRepBtree* Finalize(CordRepBtree* tree, OpResult result) {
switch (result.action) {
case CordRepBtree::kPopped:
- if (ABSL_PREDICT_FALSE(tree->height() >= CordRepBtree::kMaxHeight)) {
- ABSL_RAW_LOG(FATAL, "Max height exceeded");
- }
- return edge_type == kBack ? CordRepBtree::New(tree, result.tree)
+ tree = edge_type == kBack ? CordRepBtree::New(tree, result.tree)
: CordRepBtree::New(result.tree, tree);
+ if (ABSL_PREDICT_FALSE(tree->height() > CordRepBtree::kMaxHeight)) {
+ tree = CordRepBtree::Rebuild(tree);
+ ABSL_RAW_CHECK(tree->height() <= CordRepBtree::kMaxHeight,
+ "Max height exceeded");
+ }
+ return tree;
case CordRepBtree::kCopied:
CordRep::Unref(tree);
ABSL_FALLTHROUGH_INTENDED;
@@ -313,12 +336,12 @@
return Unwind</*propagate=*/true>(tree, depth, length, result);
}
- // `share_depth` contains the depth at which the nodes in the stack become
- // shared. I.e., if the top most level is shared (i.e.: `!refcount.IsOne()`),
- // then `share_depth` is 0. If the 2nd node is shared (and implicitly all
- // nodes below that) then `share_depth` is 1, etc. A `share_depth` greater
- // than the depth of the stack indicates that none of the nodes in the stack
- // are shared.
+ // `share_depth` contains the depth at which the nodes in the stack cannot
+ // be mutated. I.e., if the top most level is shared (i.e.:
+ // `!refcount.IsMutable()`), then `share_depth` is 0. If the 2nd node
+ // is shared (and implicitly all nodes below that) then `share_depth` is 1,
+ // etc. A `share_depth` greater than the depth of the stack indicates that
+ // none of the nodes in the stack are shared.
int share_depth;
NodeStack stack;
@@ -692,7 +715,7 @@
return result;
}
-CopyResult CordRepBtree::CopyPrefix(size_t n) {
+CopyResult CordRepBtree::CopyPrefix(size_t n, bool allow_folding) {
assert(n > 0);
assert(n <= this->length);
@@ -704,10 +727,12 @@
int height = this->height();
CordRepBtree* node = this;
CordRep* front = node->Edge(kFront);
- while (front->length >= n) {
- if (--height < 0) return {MakeSubstring(CordRep::Ref(front), 0, n), -1};
- node = front->btree();
- front = node->Edge(kFront);
+ if (allow_folding) {
+ while (front->length >= n) {
+ if (--height < 0) return {MakeSubstring(CordRep::Ref(front), 0, n), -1};
+ node = front->btree();
+ front = node->Edge(kFront);
+ }
}
if (node->length == n) return {CordRep::Ref(node), height};
@@ -746,6 +771,97 @@
return result;
}
+CordRep* CordRepBtree::ExtractFront(CordRepBtree* tree) {
+ CordRep* front = tree->Edge(tree->begin());
+ if (tree->refcount.IsMutable()) {
+ Unref(tree->Edges(tree->begin() + 1, tree->end()));
+ CordRepBtree::Delete(tree);
+ } else {
+ CordRep::Ref(front);
+ CordRep::Unref(tree);
+ }
+ return front;
+}
+
+CordRepBtree* CordRepBtree::ConsumeBeginTo(CordRepBtree* tree, size_t end,
+ size_t new_length) {
+ assert(end <= tree->end());
+ if (tree->refcount.IsMutable()) {
+ Unref(tree->Edges(end, tree->end()));
+ tree->set_end(end);
+ tree->length = new_length;
+ } else {
+ CordRepBtree* old = tree;
+ tree = tree->CopyBeginTo(end, new_length);
+ CordRep::Unref(old);
+ }
+ return tree;
+}
+
+CordRep* CordRepBtree::RemoveSuffix(CordRepBtree* tree, size_t n) {
+ // Check input and deal with trivial cases 'Remove all/none'
+ assert(tree != nullptr);
+ assert(n <= tree->length);
+ const size_t len = tree->length;
+ if (ABSL_PREDICT_FALSE(n == 0)) {
+ return tree;
+ }
+ if (ABSL_PREDICT_FALSE(n >= len)) {
+ CordRepBtree::Unref(tree);
+ return nullptr;
+ }
+
+ size_t length = len - n;
+ int height = tree->height();
+ bool is_mutable = tree->refcount.IsMutable();
+
+ // Extract all top nodes which are reduced to size = 1
+ Position pos = tree->IndexOfLength(length);
+ while (pos.index == tree->begin()) {
+ CordRep* edge = ExtractFront(tree);
+ is_mutable &= edge->refcount.IsMutable();
+ if (height-- == 0) return ResizeEdge(edge, length, is_mutable);
+ tree = edge->btree();
+ pos = tree->IndexOfLength(length);
+ }
+
+ // Repeat the following sequence traversing down the tree:
+ // - Crop the top node to the 'last remaining edge' adjusting length.
+ // - Set the length for down edges to the partial length in that last edge.
+ // - Repeat this until the last edge is 'included in full'
+ // - If we hit the data edge level, resize and return the last data edge
+ CordRepBtree* top = tree = ConsumeBeginTo(tree, pos.index + 1, length);
+ CordRep* edge = tree->Edge(pos.index);
+ length = pos.n;
+ while (length != edge->length) {
+ // ConsumeBeginTo guarantees `tree` is a clean, privately owned copy.
+ assert(tree->refcount.IsMutable());
+ const bool edge_is_mutable = edge->refcount.IsMutable();
+
+ if (height-- == 0) {
+ tree->edges_[pos.index] = ResizeEdge(edge, length, edge_is_mutable);
+ return AssertValid(top);
+ }
+
+ if (!edge_is_mutable) {
+ // We can't 'in place' remove any suffixes down this edge.
+ // Replace this edge with a prefix copy instead.
+ tree->edges_[pos.index] = edge->btree()->CopyPrefix(length, false).edge;
+ CordRep::Unref(edge);
+ return AssertValid(top);
+ }
+
+ // Move down one level, rinse repeat.
+ tree = edge->btree();
+ pos = tree->IndexOfLength(length);
+ tree = ConsumeBeginTo(edge->btree(), pos.index + 1, length);
+ edge = tree->Edge(pos.index);
+ length = pos.n;
+ }
+
+ return AssertValid(top);
+}
+
CordRep* CordRepBtree::SubTree(size_t offset, size_t n) {
assert(n <= this->length);
assert(offset <= this->length - n);
@@ -857,7 +973,7 @@
Span<char> CordRepBtree::GetAppendBufferSlow(size_t size) {
// The inlined version in `GetAppendBuffer()` deals with all heights <= 3.
assert(height() >= 4);
- assert(refcount.IsOne());
+ assert(refcount.IsMutable());
// Build a stack of nodes we may potentially need to update if we find a
// non-shared FLAT with capacity at the leaf level.
@@ -866,13 +982,13 @@
CordRepBtree* stack[kMaxDepth];
for (int i = 0; i < depth; ++i) {
node = node->Edge(kBack)->btree();
- if (!node->refcount.IsOne()) return {};
+ if (!node->refcount.IsMutable()) return {};
stack[i] = node;
}
- // Must be a privately owned flat.
+ // Must be a privately owned, mutable flat.
CordRep* const edge = node->Edge(kBack);
- if (!edge->refcount.IsOne() || edge->tag < FLAT) return {};
+ if (!edge->refcount.IsMutable() || edge->tag < FLAT) return {};
// Must have capacity.
const size_t avail = edge->flat()->Capacity() - edge->length;
@@ -950,6 +1066,63 @@
absl::string_view data,
size_t extra);
+void CordRepBtree::Rebuild(CordRepBtree** stack, CordRepBtree* tree,
+ bool consume) {
+ bool owned = consume && tree->refcount.IsOne();
+ if (tree->height() == 0) {
+ for (CordRep* edge : tree->Edges()) {
+ if (!owned) edge = CordRep::Ref(edge);
+ size_t height = 0;
+ size_t length = edge->length;
+ CordRepBtree* node = stack[0];
+ OpResult result = node->AddEdge<kBack>(true, edge, length);
+ while (result.action == CordRepBtree::kPopped) {
+ stack[height] = result.tree;
+ if (stack[++height] == nullptr) {
+ result.action = CordRepBtree::kSelf;
+ stack[height] = CordRepBtree::New(node, result.tree);
+ } else {
+ node = stack[height];
+ result = node->AddEdge<kBack>(true, result.tree, length);
+ }
+ }
+ while (stack[++height] != nullptr) {
+ stack[height]->length += length;
+ }
+ }
+ } else {
+ for (CordRep* rep : tree->Edges()) {
+ Rebuild(stack, rep->btree(), owned);
+ }
+ }
+ if (consume) {
+ if (owned) {
+ CordRepBtree::Delete(tree);
+ } else {
+ CordRepBtree::Unref(tree);
+ }
+ }
+}
+
+CordRepBtree* CordRepBtree::Rebuild(CordRepBtree* tree) {
+ // Set up initial stack with empty leaf node.
+ CordRepBtree* node = CordRepBtree::New();
+ CordRepBtree* stack[CordRepBtree::kMaxDepth + 1] = {node};
+
+ // Recursively build the tree, consuming the input tree.
+ Rebuild(stack, tree, /* consume reference */ true);
+
+ // Return top most node
+ for (CordRepBtree* parent : stack) {
+ if (parent == nullptr) return node;
+ node = parent;
+ }
+
+ // Unreachable
+ assert(false);
+ return nullptr;
+}
+
} // namespace cord_internal
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h
index bbaa793..bb38f0c 100644
--- a/absl/strings/internal/cord_rep_btree.h
+++ b/absl/strings/internal/cord_rep_btree.h
@@ -163,6 +163,12 @@
// typically after a ref_count.Decrement() on the last reference count.
static void Destroy(CordRepBtree* tree);
+ // Use CordRep::Unref() as we overload for absl::Span<CordRep* const>.
+ using CordRep::Unref;
+
+ // Unrefs all edges in `edges` which are assumed to be 'likely one'.
+ static void Unref(absl::Span<CordRep* const> edges);
+
// Appends / Prepends an existing CordRep instance to this tree.
// The below methods accept three types of input:
// 1) `rep` is a data node (See `IsDataNode` for valid data edges).
@@ -198,6 +204,19 @@
// Requires `offset + n <= length`. Returns `nullptr` if `n` is zero.
CordRep* SubTree(size_t offset, size_t n);
+ // Removes `n` trailing bytes from `tree`, and returns the resulting tree
+ // or data edge. Returns `tree` if n is zero, and nullptr if n == length.
+ // This function is logically identical to:
+ // result = tree->SubTree(0, tree->length - n);
+ // Unref(tree);
+ // return result;
+ // However, the actual implementation will as much as possible perform 'in
+ // place' modifications on the tree on all nodes and edges that are mutable.
+ // For example, in a fully privately owned tree with the last edge being a
+ // flat of length 12, RemoveSuffix(1) will simply set the length of that data
+ // edge to 11, and reduce the length of all nodes on the edge path by 1.
+ static CordRep* RemoveSuffix(CordRepBtree* tree, size_t n);
+
// Returns the character at the given offset.
char GetCharacter(size_t offset) const;
@@ -221,9 +240,9 @@
// length of the flat node and involved tree nodes have been increased by
// `span.length()`. The caller is responsible for immediately assigning values
// to all uninitialized data reference by the returned span.
- // Requires `this->refcount.IsOne()`: this function forces the caller to do
- // this fast path check on the top level node, as this is the most commonly
- // shared node of a cord tree.
+ // Requires `this->refcount.IsMutable()`: this function forces the
+ // caller to do this fast path check on the top level node, as this is the
+ // most commonly shared node of a cord tree.
Span<char> GetAppendBuffer(size_t size);
// Returns the `height` of the tree. The height of a tree is limited to
@@ -324,6 +343,11 @@
// `front.height() + 1`. Requires `back.height() == front.height()`.
static CordRepBtree* New(CordRepBtree* front, CordRepBtree* back);
+ // Creates a fully balanced tree from the provided tree by rebuilding a new
+ // tree from all data edges in the input. This function is automatically
+ // invoked internally when the tree exceeds the maximum height.
+ static CordRepBtree* Rebuild(CordRepBtree* tree);
+
private:
CordRepBtree() = default;
~CordRepBtree() = default;
@@ -368,6 +392,12 @@
// Requires 0 < `offset` <= length.
Position IndexBefore(size_t offset) const;
+ // Returns the index of the edge ending at (or on) length `length`, and the
+ // number of bytes inside that edge up to `length`. For example, if we have a
+ // Node with 2 edges, one of 10 and one of 20 long, then IndexOfLength(27)
+ // will return {1, 17}, and IndexOfLength(10) will return {0, 10}.
+ Position IndexOfLength(size_t n) const;
+
// Identical to the above function except starting from the position `front`.
// This function is equivalent to `IndexBefore(front.n + offset)`, with
// the difference that this function is optimized to start at `front.index`.
@@ -406,11 +436,28 @@
// created copy to `new_length`.
CordRepBtree* CopyBeginTo(size_t end, size_t new_length) const;
+ // Returns a tree containing the edges [tree->begin(), end) and length
+ // of `new_length`. This method consumes a reference on the provided
+ // tree, and logically performs the following operation:
+ // result = tree->CopyBeginTo(end, new_length);
+ // CordRep::Unref(tree);
+ // return result;
+ static CordRepBtree* ConsumeBeginTo(CordRepBtree* tree, size_t end,
+ size_t new_length);
+
// Creates a partial copy of this Btree node, copying all edges starting at
// `begin`, adding a reference on each copied edge, and sets the length of
// the newly created copy to `new_length`.
CordRepBtree* CopyToEndFrom(size_t begin, size_t new_length) const;
+ // Extracts and returns the front edge from the provided tree.
+ // This method consumes a reference on the provided tree, and logically
+ // performs the following operation:
+ // edge = CordRep::Ref(tree->Edge(kFront));
+ // CordRep::Unref(tree);
+ // return edge;
+ static CordRep* ExtractFront(CordRepBtree* tree);
+
// Returns a tree containing the result of appending `right` to `left`.
static CordRepBtree* MergeTrees(CordRepBtree* left, CordRepBtree* right);
@@ -420,6 +467,12 @@
static CordRepBtree* AppendSlow(CordRepBtree*, CordRep* rep);
static CordRepBtree* PrependSlow(CordRepBtree*, CordRep* rep);
+ // Recursively rebuilds `tree` into `stack`. If 'consume` is set to true, the
+ // function will consume a reference on `tree`. `stack` is a null terminated
+ // array containing the new tree's state, with the current leaf node at
+ // stack[0], and parent nodes above that, or null for 'top of tree'.
+ static void Rebuild(CordRepBtree** stack, CordRepBtree* tree, bool consume);
+
// Aligns existing edges to start at index 0, to allow for a new edge to be
// added to the back of the current edges.
inline void AlignBegin();
@@ -459,11 +512,11 @@
// Returns a partial copy of the current tree containing the first `n` bytes
// of data. `CopyResult` contains both the resulting edge and its height. The
// resulting tree may be less high than the current tree, or even be a single
- // matching data edge. For example, if `n == 1`, then the result will be the
- // single data edge, and height will be set to -1 (one below the owning leaf
- // node). If n == 0, this function returns null.
- // Requires `n <= length`
- CopyResult CopyPrefix(size_t n);
+ // matching data edge if `allow_folding` is set to true.
+ // For example, if `n == 1`, then the result will be the single data edge, and
+ // height will be set to -1 (one below the owning leaf node). If n == 0, this
+ // function returns null. Requires `n <= length`
+ CopyResult CopyPrefix(size_t n, bool allow_folding = true);
// Returns a partial copy of the current tree containing all data starting
// after `offset`. `CopyResult` contains both the resulting edge and its
@@ -619,6 +672,14 @@
DestroyTree(tree, tree->begin(), tree->end());
}
+inline void CordRepBtree::Unref(absl::Span<CordRep* const> edges) {
+ for (CordRep* edge : edges) {
+ if (ABSL_PREDICT_FALSE(!edge->refcount.Decrement())) {
+ CordRep::Destroy(edge);
+ }
+ }
+}
+
inline CordRepBtree* CordRepBtree::CopyRaw() const {
auto* tree = static_cast<CordRepBtree*>(::operator new(sizeof(CordRepBtree)));
memcpy(static_cast<void*>(tree), this, sizeof(CordRepBtree));
@@ -762,6 +823,14 @@
return {index, offset};
}
+inline CordRepBtree::Position CordRepBtree::IndexOfLength(size_t n) const {
+ assert(n <= length);
+ size_t index = back();
+ size_t strip = length - n;
+ while (strip >= edges_[index]->length) strip -= edges_[index--]->length;
+ return {index, edges_[index]->length - strip};
+}
+
inline CordRepBtree::Position CordRepBtree::IndexBeyond(
const size_t offset) const {
// We need to find the edge which `starting offset` is beyond (>=)`offset`.
@@ -780,7 +849,7 @@
}
inline Span<char> CordRepBtree::GetAppendBuffer(size_t size) {
- assert(refcount.IsOne());
+ assert(refcount.IsMutable());
CordRepBtree* tree = this;
const int height = this->height();
CordRepBtree* n1 = tree;
@@ -789,21 +858,21 @@
switch (height) {
case 3:
tree = tree->Edge(kBack)->btree();
- if (!tree->refcount.IsOne()) return {};
+ if (!tree->refcount.IsMutable()) return {};
n2 = tree;
ABSL_FALLTHROUGH_INTENDED;
case 2:
tree = tree->Edge(kBack)->btree();
- if (!tree->refcount.IsOne()) return {};
+ if (!tree->refcount.IsMutable()) return {};
n1 = tree;
ABSL_FALLTHROUGH_INTENDED;
case 1:
tree = tree->Edge(kBack)->btree();
- if (!tree->refcount.IsOne()) return {};
+ if (!tree->refcount.IsMutable()) return {};
ABSL_FALLTHROUGH_INTENDED;
case 0:
CordRep* edge = tree->Edge(kBack);
- if (!edge->refcount.IsOne()) return {};
+ if (!edge->refcount.IsMutable()) return {};
if (edge->tag < FLAT) return {};
size_t avail = edge->flat()->Capacity() - edge->length;
if (avail == 0) return {};
diff --git a/absl/strings/internal/cord_rep_btree_test.cc b/absl/strings/internal/cord_rep_btree_test.cc
index 073a7d4..be9473d 100644
--- a/absl/strings/internal/cord_rep_btree_test.cc
+++ b/absl/strings/internal/cord_rep_btree_test.cc
@@ -47,7 +47,9 @@
namespace {
using ::absl::cordrep_testing::AutoUnref;
+using ::absl::cordrep_testing::CordCollectRepsIf;
using ::absl::cordrep_testing::CordToString;
+using ::absl::cordrep_testing::CordVisitReps;
using ::absl::cordrep_testing::CreateFlatsFromString;
using ::absl::cordrep_testing::CreateRandomString;
using ::absl::cordrep_testing::MakeConcat;
@@ -62,6 +64,7 @@
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::HasSubstr;
+using ::testing::Le;
using ::testing::Ne;
using ::testing::Not;
using ::testing::SizeIs;
@@ -205,14 +208,17 @@
return tree;
}
-CordRepBtree* CreateTree(absl::string_view data, size_t chunk_size) {
- std::vector<CordRep*> flats = CreateFlatsFromString(data, chunk_size);
- auto it = flats.begin();
+CordRepBtree* CreateTree(absl::Span<CordRep* const> reps) {
+ auto it = reps.begin();
CordRepBtree* tree = CordRepBtree::Create(*it);
- while (++it != flats.end()) tree = CordRepBtree::Append(tree, *it);
+ while (++it != reps.end()) tree = CordRepBtree::Append(tree, *it);
return tree;
}
+CordRepBtree* CreateTree(absl::string_view data, size_t chunk_size) {
+ return CreateTree(CreateFlatsFromString(data, chunk_size));
+}
+
CordRepBtree* CreateTreeReverse(absl::string_view data, size_t chunk_size) {
std::vector<CordRep*> flats = CreateFlatsFromString(data, chunk_size);
auto rit = flats.rbegin();
@@ -759,6 +765,63 @@
}
}
+TEST_P(CordRepBtreeTest, RemoveSuffix) {
+ // Create tree of 1, 2 and 3 levels high
+ constexpr size_t max_cap = CordRepBtree::kMaxCapacity;
+ for (size_t cap : {max_cap - 1, max_cap * 2, max_cap * max_cap * 2}) {
+ const std::string data = CreateRandomString(cap * 512);
+
+ {
+ // Verify RemoveSuffix(<all>)
+ AutoUnref refs;
+ CordRepBtree* node = refs.RefIf(shared(), CreateTree(data, 512));
+ EXPECT_THAT(CordRepBtree::RemoveSuffix(node, data.length()), Eq(nullptr));
+
+ // Verify RemoveSuffix(<none>)
+ node = refs.RefIf(shared(), CreateTree(data, 512));
+ EXPECT_THAT(CordRepBtree::RemoveSuffix(node, 0), Eq(node));
+ CordRep::Unref(node);
+ }
+
+ for (int n = 1; n < data.length(); ++n) {
+ AutoUnref refs;
+ auto flats = CreateFlatsFromString(data, 512);
+ CordRepBtree* node = refs.RefIf(shared(), CreateTree(flats));
+ CordRep* rep = refs.Add(CordRepBtree::RemoveSuffix(node, n));
+ EXPECT_THAT(CordToString(rep), Eq(data.substr(0, data.length() - n)));
+
+ // Collect all flats
+ auto is_flat = [](CordRep* rep) { return rep->tag >= FLAT; };
+ std::vector<CordRep*> edges = CordCollectRepsIf(is_flat, rep);
+ ASSERT_THAT(edges.size(), Le(flats.size()));
+
+ // Isolate last edge
+ CordRep* last_edge = edges.back();
+ edges.pop_back();
+ const size_t last_length = rep->length - edges.size() * 512;
+
+ // All flats except the last edge must be kept or copied 'as is'
+ int index = 0;
+ for (CordRep* edge : edges) {
+ ASSERT_THAT(edge, Eq(flats[index++]));
+ ASSERT_THAT(edge->length, Eq(512));
+ }
+
+ // CordRepBtree may optimize small substrings to avoid waste, so only
+ // check for flat sharing / updates where the code should always do this.
+ if (last_length >= 500) {
+ EXPECT_THAT(last_edge, Eq(flats[index++]));
+ if (shared()) {
+ EXPECT_THAT(last_edge->length, Eq(512));
+ } else {
+ EXPECT_TRUE(last_edge->refcount.IsOne());
+ EXPECT_THAT(last_edge->length, Eq(last_length));
+ }
+ }
+ }
+ }
+}
+
TEST(CordRepBtreeTest, SubTree) {
// Create tree of at least 2 levels high
constexpr size_t max_cap = CordRepBtree::kMaxCapacity;
@@ -986,23 +1049,6 @@
CordRep::Unref(result);
}
-TEST_P(CordRepBtreeTest, ExceedMaxHeight) {
- AutoUnref refs;
- CordRepBtree* tree = MakeLeaf();
- for (int h = 1; h <= CordRepBtree::kMaxHeight; ++h) {
- CordRepBtree* newtree = CordRepBtree::New(tree);
- for (size_t i = 1; i < CordRepBtree::kMaxCapacity; ++i) {
- newtree = CordRepBtree::Append(newtree, CordRep::Ref(tree));
- }
- tree = newtree;
- }
- refs.RefIf(shared(), tree);
-#if defined(GTEST_HAS_DEATH_TEST)
- EXPECT_DEATH(tree = CordRepBtree::Append(tree, MakeFlat("Boom")), ".*");
-#endif
- CordRep::Unref(tree);
-}
-
TEST(CordRepBtreeTest, GetCharacter) {
size_t n = CordRepBtree::kMaxCapacity * CordRepBtree::kMaxCapacity + 2;
std::string data = CreateRandomString(n * 3);
@@ -1389,6 +1435,54 @@
CordRep::Unref(tree);
}
+TEST_P(CordRepBtreeTest, Rebuild) {
+ for (size_t size : {3, 8, 100, 10000, 1000000}) {
+ SCOPED_TRACE(absl::StrCat("Rebuild @", size));
+
+ std::vector<CordRepFlat*> flats;
+ for (int i = 0; i < size; ++i) {
+ flats.push_back(CordRepFlat::New(2));
+ flats.back()->Data()[0] = 'x';
+ flats.back()->length = 1;
+ }
+
+ // Build the tree into 'right', and each so many 'split_limit' edges,
+ // combine 'left' + 'right' into a new 'left', and start a new 'right'.
+ // This guarantees we get a reasonable amount of chaos in the tree.
+ size_t split_count = 0;
+ size_t split_limit = 3;
+ auto it = flats.begin();
+ CordRepBtree* left = nullptr;
+ CordRepBtree* right = CordRepBtree::New(*it);
+ while (++it != flats.end()) {
+ if (++split_count >= split_limit) {
+ split_limit += split_limit / 16;
+ left = left ? CordRepBtree::Append(left, right) : right;
+ right = CordRepBtree::New(*it);
+ } else {
+ right = CordRepBtree::Append(right, *it);
+ }
+ }
+
+ // Finalize tree
+ left = left ? CordRepBtree::Append(left, right) : right;
+
+ // Rebuild
+ AutoUnref ref;
+ left = ref.Add(CordRepBtree::Rebuild(ref.RefIf(shared(), left)));
+ ASSERT_TRUE(CordRepBtree::IsValid(left));
+
+ // Verify we have the exact same edges in the exact same order.
+ bool ok = true;
+ it = flats.begin();
+ CordVisitReps(left, [&](CordRep* edge) {
+ if (edge->tag < FLAT) return;
+ ok = ok && (it != flats.end() && *it++ == edge);
+ });
+ EXPECT_TRUE(ok && it == flats.end()) << "Rebuild edges mismatch";
+ }
+}
+
} // namespace
} // namespace cord_internal
ABSL_NAMESPACE_END
diff --git a/absl/strings/internal/cord_rep_ring.cc b/absl/strings/internal/cord_rep_ring.cc
index db1f63f..07c77eb 100644
--- a/absl/strings/internal/cord_rep_ring.cc
+++ b/absl/strings/internal/cord_rep_ring.cc
@@ -277,7 +277,7 @@
// Get current number of entries, and check for max capacity.
size_t entries = rep->entries();
- if (!rep->refcount.IsOne()) {
+ if (!rep->refcount.IsMutable()) {
return Copy(rep, rep->head(), rep->tail(), extra);
} else if (entries + extra > rep->capacity()) {
const size_t min_grow = rep->capacity() + rep->capacity() / 2;
@@ -292,10 +292,10 @@
}
Span<char> CordRepRing::GetAppendBuffer(size_t size) {
- assert(refcount.IsOne());
+ assert(refcount.IsMutable());
index_type back = retreat(tail_);
CordRep* child = entry_child(back);
- if (child->tag >= FLAT && child->refcount.IsOne()) {
+ if (child->tag >= FLAT && child->refcount.IsMutable()) {
size_t capacity = child->flat()->Capacity();
pos_type end_pos = entry_end_pos(back);
size_t data_offset = entry_data_offset(back);
@@ -312,10 +312,10 @@
}
Span<char> CordRepRing::GetPrependBuffer(size_t size) {
- assert(refcount.IsOne());
+ assert(refcount.IsMutable());
CordRep* child = entry_child(head_);
size_t data_offset = entry_data_offset(head_);
- if (data_offset && child->refcount.IsOne() && child->tag >= FLAT) {
+ if (data_offset && child->refcount.IsMutable() && child->tag >= FLAT) {
size_t n = (std::min)(data_offset, size);
this->length += n;
begin_pos_ -= n;
@@ -504,7 +504,7 @@
CordRepRing* CordRepRing::Append(CordRepRing* rep, absl::string_view data,
size_t extra) {
- if (rep->refcount.IsOne()) {
+ if (rep->refcount.IsMutable()) {
Span<char> avail = rep->GetAppendBuffer(data.length());
if (!avail.empty()) {
memcpy(avail.data(), data.data(), avail.length());
@@ -538,7 +538,7 @@
CordRepRing* CordRepRing::Prepend(CordRepRing* rep, absl::string_view data,
size_t extra) {
- if (rep->refcount.IsOne()) {
+ if (rep->refcount.IsMutable()) {
Span<char> avail = rep->GetPrependBuffer(data.length());
if (!avail.empty()) {
const char* tail = data.data() + data.length() - avail.length();
@@ -678,7 +678,7 @@
Position tail = rep->FindTail(head.index, offset + len);
const size_t new_entries = rep->entries(head.index, tail.index);
- if (rep->refcount.IsOne() && extra <= (rep->capacity() - new_entries)) {
+ if (rep->refcount.IsMutable() && extra <= (rep->capacity() - new_entries)) {
// We adopt a privately owned rep and no extra entries needed.
if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index);
if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_);
@@ -715,7 +715,7 @@
}
Position head = rep->Find(len);
- if (rep->refcount.IsOne()) {
+ if (rep->refcount.IsMutable()) {
if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index);
rep->head_ = head.index;
} else {
@@ -745,7 +745,7 @@
}
Position tail = rep->FindTail(rep->length - len);
- if (rep->refcount.IsOne()) {
+ if (rep->refcount.IsMutable()) {
// We adopt a privately owned rep, scrub.
if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_);
rep->tail_ = tail.index;
diff --git a/absl/strings/internal/cord_rep_ring.h b/absl/strings/internal/cord_rep_ring.h
index 44db849..2000e21 100644
--- a/absl/strings/internal/cord_rep_ring.h
+++ b/absl/strings/internal/cord_rep_ring.h
@@ -383,8 +383,8 @@
// Destroys the provided ring buffer, decrementing the reference count of all
// contained child CordReps. The provided 1\`rep` should have a ref count of
- // one (pre decrement destroy call observing `refcount.IsOne()`) or zero (post
- // decrement destroy call observing `!refcount.Decrement()`).
+ // one (pre decrement destroy call observing `refcount.IsOne()`) or zero
+ // (post decrement destroy call observing `!refcount.Decrement()`).
static void Destroy(CordRepRing* rep);
// Returns a mutable reference to the logical end position array.
diff --git a/absl/strings/internal/cord_rep_test_util.h b/absl/strings/internal/cord_rep_test_util.h
index bc50006..ad828af 100644
--- a/absl/strings/internal/cord_rep_test_util.h
+++ b/absl/strings/internal/cord_rep_test_util.h
@@ -115,6 +115,41 @@
return node;
}
+template <typename Fn>
+inline void CordVisitReps(cord_internal::CordRep* rep, Fn&& fn) {
+ fn(rep);
+ while (rep->tag == cord_internal::SUBSTRING) {
+ rep = rep->substring()->child;
+ fn(rep);
+ }
+ if (rep->tag == cord_internal::BTREE) {
+ for (cord_internal::CordRep* edge : rep->btree()->Edges()) {
+ CordVisitReps(edge, fn);
+ }
+ } else if (rep->tag == cord_internal::CONCAT) {
+ CordVisitReps(rep->concat()->left, fn);
+ CordVisitReps(rep->concat()->right, fn);
+ }
+}
+
+template <typename Predicate>
+inline std::vector<cord_internal::CordRep*> CordCollectRepsIf(
+ Predicate&& predicate, cord_internal::CordRep* rep) {
+ std::vector<cord_internal::CordRep*> reps;
+ CordVisitReps(rep, [&reps, &predicate](cord_internal::CordRep* rep) {
+ if (predicate(rep)) reps.push_back(rep);
+ });
+ return reps;
+}
+
+inline std::vector<cord_internal::CordRep*> CordCollectReps(
+ cord_internal::CordRep* rep) {
+ std::vector<cord_internal::CordRep*> reps;
+ auto fn = [&reps](cord_internal::CordRep* rep) { reps.push_back(rep); };
+ CordVisitReps(rep, fn);
+ return reps;
+}
+
inline void CordToString(cord_internal::CordRep* rep, std::string& s) {
size_t offset = 0;
size_t length = rep->length;
diff --git a/absl/strings/internal/cordz_update_tracker.h b/absl/strings/internal/cordz_update_tracker.h
index 02efcc3..1f76448 100644
--- a/absl/strings/internal/cordz_update_tracker.h
+++ b/absl/strings/internal/cordz_update_tracker.h
@@ -39,6 +39,7 @@
// Tracked update methods.
enum MethodIdentifier {
kUnknown,
+ kAppendBuffer,
kAppendCord,
kAppendExternalMemory,
kAppendString,
@@ -54,6 +55,7 @@
kMoveAppendCord,
kMoveAssignCord,
kMovePrependCord,
+ kPrependBuffer,
kPrependCord,
kPrependString,
kRemovePrefix,
diff --git a/absl/strings/internal/cordz_update_tracker_test.cc b/absl/strings/internal/cordz_update_tracker_test.cc
index fcd17df..2348a17 100644
--- a/absl/strings/internal/cordz_update_tracker_test.cc
+++ b/absl/strings/internal/cordz_update_tracker_test.cc
@@ -37,6 +37,7 @@
// Returns an array of all methods defined in `MethodIdentifier`
Methods AllMethods() {
return Methods{Method::kUnknown,
+ Method::kAppendBuffer,
Method::kAppendCord,
Method::kAppendExternalMemory,
Method::kAppendString,
@@ -52,6 +53,7 @@
Method::kMoveAppendCord,
Method::kMoveAssignCord,
Method::kMovePrependCord,
+ Method::kPrependBuffer,
Method::kPrependCord,
Method::kPrependString,
Method::kRemovePrefix,
diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h
index 1780bb4..4ae07c2 100644
--- a/absl/strings/numbers.h
+++ b/absl/strings/numbers.h
@@ -96,6 +96,25 @@
// unspecified state.
ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* out);
+// SimpleHexAtoi()
+//
+// Converts a hexadecimal string (optionally followed or preceded by ASCII
+// whitespace) to an integer, returning `true` if successful. Only valid base-16
+// hexadecimal integers whose value falls within the range of the integer type
+// (optionally preceded by a `+` or `-`) can be converted. A valid hexadecimal
+// value may include both upper and lowercase character symbols, and may
+// optionally include a leading "0x" (or "0X") number prefix, which is ignored
+// by this function. If any errors are encountered, this function returns
+// `false`, leaving `out` in an unspecified state.
+template <typename int_type>
+ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, int_type* out);
+
+// Overloads of SimpleHexAtoi() for 128 bit integers.
+ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str,
+ absl::int128* out);
+ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str,
+ absl::uint128* out);
+
ABSL_NAMESPACE_END
} // namespace absl
@@ -260,6 +279,21 @@
return numbers_internal::safe_strtou128_base(str, out, 10);
}
+template <typename int_type>
+ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, int_type* out) {
+ return numbers_internal::safe_strtoi_base(str, out, 16);
+}
+
+ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str,
+ absl::int128* out) {
+ return numbers_internal::safe_strto128_base(str, out, 16);
+}
+
+ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str,
+ absl::uint128* out) {
+ return numbers_internal::safe_strtou128_base(str, out, 16);
+}
+
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc
index f310310..498c210 100644
--- a/absl/strings/numbers_test.cc
+++ b/absl/strings/numbers_test.cc
@@ -47,6 +47,7 @@
namespace {
using absl::SimpleAtoi;
+using absl::SimpleHexAtoi;
using absl::numbers_internal::kSixDigitsToBufferSize;
using absl::numbers_internal::safe_strto32_base;
using absl::numbers_internal::safe_strto64_base;
@@ -468,6 +469,148 @@
VerifySimpleAtoiGood<E_biguint>(E_biguint_max32, E_biguint_max32);
}
+template <typename int_type, typename in_val_type>
+void VerifySimpleHexAtoiGood(in_val_type in_value, int_type exp_value) {
+ std::string s;
+ // uint128 can be streamed but not StrCat'd
+ absl::strings_internal::OStringStream strm(&s);
+ if (in_value >= 0) {
+ strm << std::hex << in_value;
+ } else {
+ // Inefficient for small integers, but works with all integral types.
+ strm << "-" << std::hex << -absl::uint128(in_value);
+ }
+ int_type x = static_cast<int_type>(~exp_value);
+ EXPECT_TRUE(SimpleHexAtoi(s, &x))
+ << "in_value=" << std::hex << in_value << " s=" << s << " x=" << x;
+ EXPECT_EQ(exp_value, x);
+ x = static_cast<int_type>(~exp_value);
+ EXPECT_TRUE(SimpleHexAtoi(
+ s.c_str(), &x)); // NOLINT: readability-redundant-string-conversions
+ EXPECT_EQ(exp_value, x);
+}
+
+template <typename int_type, typename in_val_type>
+void VerifySimpleHexAtoiBad(in_val_type in_value) {
+ std::string s;
+ // uint128 can be streamed but not StrCat'd
+ absl::strings_internal::OStringStream strm(&s);
+ if (in_value >= 0) {
+ strm << std::hex << in_value;
+ } else {
+ // Inefficient for small integers, but works with all integral types.
+ strm << "-" << std::hex << -absl::uint128(in_value);
+ }
+ int_type x;
+ EXPECT_FALSE(SimpleHexAtoi(s, &x));
+ EXPECT_FALSE(SimpleHexAtoi(
+ s.c_str(), &x)); // NOLINT: readability-redundant-string-conversions
+}
+
+TEST(NumbersTest, HexAtoi) {
+ // SimpleHexAtoi(absl::string_view, int32_t)
+ VerifySimpleHexAtoiGood<int32_t>(0, 0);
+ VerifySimpleHexAtoiGood<int32_t>(0x42, 0x42);
+ VerifySimpleHexAtoiGood<int32_t>(-0x42, -0x42);
+
+ VerifySimpleHexAtoiGood<int32_t>(std::numeric_limits<int32_t>::min(),
+ std::numeric_limits<int32_t>::min());
+ VerifySimpleHexAtoiGood<int32_t>(std::numeric_limits<int32_t>::max(),
+ std::numeric_limits<int32_t>::max());
+
+ // SimpleHexAtoi(absl::string_view, uint32_t)
+ VerifySimpleHexAtoiGood<uint32_t>(0, 0);
+ VerifySimpleHexAtoiGood<uint32_t>(0x42, 0x42);
+ VerifySimpleHexAtoiBad<uint32_t>(-0x42);
+
+ VerifySimpleHexAtoiBad<uint32_t>(std::numeric_limits<int32_t>::min());
+ VerifySimpleHexAtoiGood<uint32_t>(std::numeric_limits<int32_t>::max(),
+ std::numeric_limits<int32_t>::max());
+ VerifySimpleHexAtoiGood<uint32_t>(std::numeric_limits<uint32_t>::max(),
+ std::numeric_limits<uint32_t>::max());
+ VerifySimpleHexAtoiBad<uint32_t>(std::numeric_limits<int64_t>::min());
+ VerifySimpleHexAtoiBad<uint32_t>(std::numeric_limits<int64_t>::max());
+ VerifySimpleHexAtoiBad<uint32_t>(std::numeric_limits<uint64_t>::max());
+
+ // SimpleHexAtoi(absl::string_view, int64_t)
+ VerifySimpleHexAtoiGood<int64_t>(0, 0);
+ VerifySimpleHexAtoiGood<int64_t>(0x42, 0x42);
+ VerifySimpleHexAtoiGood<int64_t>(-0x42, -0x42);
+
+ VerifySimpleHexAtoiGood<int64_t>(std::numeric_limits<int32_t>::min(),
+ std::numeric_limits<int32_t>::min());
+ VerifySimpleHexAtoiGood<int64_t>(std::numeric_limits<int32_t>::max(),
+ std::numeric_limits<int32_t>::max());
+ VerifySimpleHexAtoiGood<int64_t>(std::numeric_limits<uint32_t>::max(),
+ std::numeric_limits<uint32_t>::max());
+ VerifySimpleHexAtoiGood<int64_t>(std::numeric_limits<int64_t>::min(),
+ std::numeric_limits<int64_t>::min());
+ VerifySimpleHexAtoiGood<int64_t>(std::numeric_limits<int64_t>::max(),
+ std::numeric_limits<int64_t>::max());
+ VerifySimpleHexAtoiBad<int64_t>(std::numeric_limits<uint64_t>::max());
+
+ // SimpleHexAtoi(absl::string_view, uint64_t)
+ VerifySimpleHexAtoiGood<uint64_t>(0, 0);
+ VerifySimpleHexAtoiGood<uint64_t>(0x42, 0x42);
+ VerifySimpleHexAtoiBad<uint64_t>(-0x42);
+
+ VerifySimpleHexAtoiBad<uint64_t>(std::numeric_limits<int32_t>::min());
+ VerifySimpleHexAtoiGood<uint64_t>(std::numeric_limits<int32_t>::max(),
+ std::numeric_limits<int32_t>::max());
+ VerifySimpleHexAtoiGood<uint64_t>(std::numeric_limits<uint32_t>::max(),
+ std::numeric_limits<uint32_t>::max());
+ VerifySimpleHexAtoiBad<uint64_t>(std::numeric_limits<int64_t>::min());
+ VerifySimpleHexAtoiGood<uint64_t>(std::numeric_limits<int64_t>::max(),
+ std::numeric_limits<int64_t>::max());
+ VerifySimpleHexAtoiGood<uint64_t>(std::numeric_limits<uint64_t>::max(),
+ std::numeric_limits<uint64_t>::max());
+
+ // SimpleHexAtoi(absl::string_view, absl::uint128)
+ VerifySimpleHexAtoiGood<absl::uint128>(0, 0);
+ VerifySimpleHexAtoiGood<absl::uint128>(0x42, 0x42);
+ VerifySimpleHexAtoiBad<absl::uint128>(-0x42);
+
+ VerifySimpleHexAtoiBad<absl::uint128>(std::numeric_limits<int32_t>::min());
+ VerifySimpleHexAtoiGood<absl::uint128>(std::numeric_limits<int32_t>::max(),
+ std::numeric_limits<int32_t>::max());
+ VerifySimpleHexAtoiGood<absl::uint128>(std::numeric_limits<uint32_t>::max(),
+ std::numeric_limits<uint32_t>::max());
+ VerifySimpleHexAtoiBad<absl::uint128>(std::numeric_limits<int64_t>::min());
+ VerifySimpleHexAtoiGood<absl::uint128>(std::numeric_limits<int64_t>::max(),
+ std::numeric_limits<int64_t>::max());
+ VerifySimpleHexAtoiGood<absl::uint128>(std::numeric_limits<uint64_t>::max(),
+ std::numeric_limits<uint64_t>::max());
+ VerifySimpleHexAtoiGood<absl::uint128>(
+ std::numeric_limits<absl::uint128>::max(),
+ std::numeric_limits<absl::uint128>::max());
+
+ // Some other types
+ VerifySimpleHexAtoiGood<int>(-0x42, -0x42);
+ VerifySimpleHexAtoiGood<int32_t>(-0x42, -0x42);
+ VerifySimpleHexAtoiGood<uint32_t>(0x42, 0x42);
+ VerifySimpleHexAtoiGood<unsigned int>(0x42, 0x42);
+ VerifySimpleHexAtoiGood<int64_t>(-0x42, -0x42);
+ VerifySimpleHexAtoiGood<long>(-0x42, -0x42); // NOLINT: runtime-int
+ VerifySimpleHexAtoiGood<uint64_t>(0x42, 0x42);
+ VerifySimpleHexAtoiGood<size_t>(0x42, 0x42);
+ VerifySimpleHexAtoiGood<std::string::size_type>(0x42, 0x42);
+
+ // Number prefix
+ int32_t value;
+ EXPECT_TRUE(safe_strto32_base("0x34234324", &value, 16));
+ EXPECT_EQ(0x34234324, value);
+
+ EXPECT_TRUE(safe_strto32_base("0X34234324", &value, 16));
+ EXPECT_EQ(0x34234324, value);
+
+ // ASCII whitespace
+ EXPECT_TRUE(safe_strto32_base(" \t\n 34234324", &value, 16));
+ EXPECT_EQ(0x34234324, value);
+
+ EXPECT_TRUE(safe_strto32_base("34234324 \t\n ", &value, 16));
+ EXPECT_EQ(0x34234324, value);
+}
+
TEST(stringtest, safe_strto32_base) {
int32_t value;
EXPECT_TRUE(safe_strto32_base("0x34234324", &value, 16));
diff --git a/absl/strings/str_split_test.cc b/absl/strings/str_split_test.cc
index f472f9e..1b4427b 100644
--- a/absl/strings/str_split_test.cc
+++ b/absl/strings/str_split_test.cc
@@ -943,8 +943,14 @@
}
TEST(Split, WorksWithLargeStrings) {
+#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+ defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER)
+ constexpr size_t kSize = (uint32_t{1} << 26) + 1; // 64M + 1 byte
+#else
+ constexpr size_t kSize = (uint32_t{1} << 31) + 1; // 2G + 1 byte
+#endif
if (sizeof(size_t) > 4) {
- std::string s((uint32_t{1} << 31) + 1, 'x'); // 2G + 1 byte
+ std::string s(kSize, 'x');
s.back() = '-';
std::vector<absl::string_view> v = absl::StrSplit(s, '-');
EXPECT_EQ(2, v.size());
diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc
index 1f3c740..8980b19 100644
--- a/absl/strings/substitute.cc
+++ b/absl/strings/substitute.cc
@@ -75,7 +75,8 @@
// Build the string.
size_t original_size = output->size();
- strings_internal::STLStringResizeUninitialized(output, original_size + size);
+ strings_internal::STLStringResizeUninitializedAmortized(output,
+ original_size + size);
char* target = &(*output)[original_size];
for (size_t i = 0; i < format.size(); i++) {
if (format[i] == '$') {
diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h
index f49e0c8..38338f2 100644
--- a/absl/synchronization/mutex.h
+++ b/absl/synchronization/mutex.h
@@ -778,9 +778,9 @@
//
// Usage to wake T is:
// mu.Lock();
-// // process data, possibly establishing C
-// if (C) { cv->Signal(); }
-// mu.Unlock();
+// // process data, possibly establishing C
+// if (C) { cv->Signal(); }
+// mu.Unlock();
//
// If C may be useful to more than one waiter, use `SignalAll()` instead of
// `Signal()`.
diff --git a/absl/time/internal/cctz/BUILD.gn b/absl/time/internal/cctz/BUILD.gn
index b32f565..1c22b49 100644
--- a/absl/time/internal/cctz/BUILD.gn
+++ b/absl/time/internal/cctz/BUILD.gn
@@ -43,4 +43,13 @@
":civil_time",
"//third_party/abseil-cpp/absl/base:config",
]
+ if (is_fuchsia) {
+ deps += [
+ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.intl",
+ "//third_party/fuchsia-sdk/sdk/pkg/async",
+ "//third_party/fuchsia-sdk/sdk/pkg/async-loop-cpp",
+ "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
+ "//third_party/fuchsia-sdk/sdk/pkg/zx",
+ ]
+ }
}
diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc
index f2777d9..4f175d9 100644
--- a/absl/time/internal/cctz/src/time_zone_info.cc
+++ b/absl/time/internal/cctz/src/time_zone_info.cc
@@ -39,6 +39,7 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
+#include <fstream>
#include <functional>
#include <memory>
#include <sstream>
@@ -649,7 +650,7 @@
// Open the zoneinfo file.
auto fp = FOpen(path.c_str(), "rb");
- if (fp.get() == nullptr) return nullptr;
+ if (fp == nullptr) return nullptr;
return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(std::move(fp)));
}
@@ -674,7 +675,7 @@
for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata",
"/system/usr/share/zoneinfo/tzdata"}) {
auto fp = FOpen(tzdata, "rb");
- if (fp.get() == nullptr) continue;
+ if (fp == nullptr) continue;
char hbuf[24]; // covers header.zonetab_offset too
if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue;
@@ -708,6 +709,69 @@
return nullptr;
}
+// A zoneinfo source for use inside Fuchsia components. This attempts to
+// read zoneinfo files from one of several known paths in a component's
+// incoming namespace. [Config data][1] is preferred, but package-specific
+// resources are also supported.
+//
+// Fuchsia's implementation supports `FileZoneInfoSource::Version()`.
+//
+// [1]:
+// https://fuchsia.dev/fuchsia-src/development/components/data#using_config_data_in_your_component
+class FuchsiaZoneInfoSource : public FileZoneInfoSource {
+ public:
+ static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
+ std::string Version() const override { return version_; }
+
+ private:
+ explicit FuchsiaZoneInfoSource(FilePtr fp, std::string version)
+ : FileZoneInfoSource(std::move(fp)), version_(std::move(version)) {}
+ std::string version_;
+};
+
+std::unique_ptr<ZoneInfoSource> FuchsiaZoneInfoSource::Open(
+ const std::string& name) {
+ // Use of the "file:" prefix is intended for testing purposes only.
+ const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
+
+ // Prefixes where a Fuchsia component might find zoneinfo files,
+ // in descending order of preference.
+ const auto kTzdataPrefixes = {
+ "/config/data/tzdata/",
+ "/pkg/data/tzdata/",
+ "/data/tzdata/",
+ };
+ const auto kEmptyPrefix = {""};
+ const bool name_absolute = (pos != name.size() && name[pos] == '/');
+ const auto prefixes = name_absolute ? kEmptyPrefix : kTzdataPrefixes;
+
+ // Fuchsia builds place zoneinfo files at "<prefix><format><name>".
+ for (const std::string prefix : prefixes) {
+ std::string path = prefix;
+ if (!prefix.empty()) path += "zoneinfo/tzif2/"; // format
+ path.append(name, pos, std::string::npos);
+
+ auto fp = FOpen(path.c_str(), "rb");
+ if (fp == nullptr) continue;
+
+ std::string version;
+ if (!prefix.empty()) {
+ // Fuchsia builds place the version in "<prefix>revision.txt".
+ std::ifstream version_stream(prefix + "revision.txt");
+ if (version_stream.is_open()) {
+ // revision.txt should contain no newlines, but to be
+ // defensive we read just the first line.
+ std::getline(version_stream, version);
+ }
+ }
+
+ return std::unique_ptr<ZoneInfoSource>(
+ new FuchsiaZoneInfoSource(std::move(fp), std::move(version)));
+ }
+
+ return nullptr;
+}
+
} // namespace
bool TimeZoneInfo::Load(const std::string& name) {
@@ -725,6 +789,7 @@
name, [](const std::string& n) -> std::unique_ptr<ZoneInfoSource> {
if (auto z = FileZoneInfoSource::Open(n)) return z;
if (auto z = AndroidZoneInfoSource::Open(n)) return z;
+ if (auto z = FuchsiaZoneInfoSource::Open(n)) return z;
return nullptr;
});
return zip != nullptr && Load(zip.get());
diff --git a/absl/time/internal/cctz/src/time_zone_lookup.cc b/absl/time/internal/cctz/src/time_zone_lookup.cc
index efdea64..898d04c 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup.cc
@@ -28,6 +28,13 @@
#include <vector>
#endif
+#if defined(__Fuchsia__)
+#include <fuchsia/intl/cpp/fidl.h>
+#include <lib/async-loop/cpp/loop.h>
+#include <lib/sys/cpp/component_context.h>
+#include <zircon/types.h>
+#endif
+
#include <cstdlib>
#include <cstring>
#include <string>
@@ -140,6 +147,48 @@
}
CFRelease(tz_default);
#endif
+#if defined(__Fuchsia__)
+ std::string primary_tz;
+ [&]() {
+ // Note: We can't use the synchronous FIDL API here because it doesn't
+ // allow timeouts; if the FIDL call failed, local_time_zone() would never
+ // return.
+
+ const zx::duration kTimeout = zx::msec(500);
+
+ // Don't attach to the thread because otherwise the thread's dispatcher
+ // would be set to null when the loop is destroyed, causing any other FIDL
+ // code running on the same thread to crash.
+ async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
+ std::unique_ptr<sys::ComponentContext> context =
+ sys::ComponentContext::Create();
+
+ fuchsia::intl::PropertyProviderHandle handle;
+ zx_status_t status = context->svc()->Connect(handle.NewRequest());
+ if (status != ZX_OK) {
+ return;
+ }
+
+ fuchsia::intl::PropertyProviderPtr intl_provider;
+ status = intl_provider.Bind(std::move(handle), loop.dispatcher());
+ if (status != ZX_OK) {
+ return;
+ }
+
+ intl_provider->GetProfile(
+ [&loop, &primary_tz](fuchsia::intl::Profile profile) {
+ if (!profile.time_zones().empty()) {
+ primary_tz = profile.time_zones()[0].id;
+ }
+ loop.Quit();
+ });
+ loop.Run(zx::deadline_after(kTimeout));
+ }();
+
+ if (!primary_tz.empty()) {
+ zone = primary_tz.c_str();
+ }
+#endif
// Allow ${TZ} to override to default zone.
char* tz_env = nullptr;
diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
index 8751b34..0226ab7 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -1027,8 +1027,12 @@
#endif
const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900;
tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut);
+#if defined(__Fuchsia__)
+ // Fuchsia's gmtime_r() fails on extreme negative values (fxbug.dev/78527).
+#else
EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, cut));
#endif
+#endif
}
}
diff --git a/absl/time/internal/cctz/src/zone_info_source.cc b/absl/time/internal/cctz/src/zone_info_source.cc
index 7209533..5ab5a59 100644
--- a/absl/time/internal/cctz/src/zone_info_source.cc
+++ b/absl/time/internal/cctz/src/zone_info_source.cc
@@ -65,7 +65,7 @@
extern ZoneInfoSourceFactory zone_info_source_factory;
extern ZoneInfoSourceFactory default_factory;
ZoneInfoSourceFactory default_factory = DefaultFactory;
-#if defined(_M_IX86)
+#if defined(_M_IX86) || defined(_M_ARM)
#pragma comment( \
linker, \
"/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \
@@ -83,8 +83,7 @@
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
"@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
"@@ZA")
-#elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM) || \
- defined(_M_ARM64)
+#elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM64)
#pragma comment( \
linker, \
"/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \
diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version
index 51191b5..8ee898b 100644
--- a/absl/time/internal/cctz/testdata/version
+++ b/absl/time/internal/cctz/testdata/version
@@ -1 +1 @@
-2021c
+2021e
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
index 58e9fdf..ccc57c9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
index aeda06b..906d8d5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
index e3934e4..8b2dd52 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
Binary files differ
diff --git a/symbols_arm64_dbg.def b/symbols_arm64_dbg.def
index b198b2a..1e4760f 100644
--- a/symbols_arm64_dbg.def
+++ b/symbols_arm64_dbg.def
@@ -1659,6 +1659,7 @@
?ConstructNext@?$IteratorValueAdapter@V?$allocator@UPayload@status_internal@absl@@@__1@std@@V?$move_iterator@PEAUPayload@status_internal@absl@@@23@@inlined_vector_internal@absl@@QEAAXAEAV?$allocator@UPayload@status_internal@absl@@@__1@std@@PEAUPayload@status_internal@3@@Z
?ConstructNext@?$IteratorValueAdapter@V?$allocator@USubRange@absl@@@__1@std@@V?$move_iterator@PEAUSubRange@absl@@@23@@inlined_vector_internal@absl@@QEAAXAEAV?$allocator@USubRange@absl@@@__1@std@@PEAUSubRange@3@@Z
?Consume@cord_internal@absl@@YAXPEAUCordRep@12@V?$FunctionRef@$$A6AXPEAUCordRep@cord_internal@absl@@_K1@Z@2@@Z
+ ?ConsumeBeginTo@CordRepBtree@cord_internal@absl@@CAPEAV123@PEAV123@_K1@Z
?ConsumePrefix@absl@@YA_NPEAVstring_view@1@V21@@Z
?ConsumeUnboundConversion@str_format_internal@absl@@YAPEBDPEBD0PEAUUnboundConversion@12@PEAH@Z
?Contains@str_format_internal@absl@@YA_NW4FormatConversionCharSet@2@D@Z
@@ -1675,7 +1676,7 @@
?Copy@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@II_K@Z
?CopyBeginTo@CordRepBtree@cord_internal@absl@@AEBAPEAV123@_K0@Z
?CopyCordToString@absl@@YAXAEBVCord@1@PEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
- ?CopyPrefix@CordRepBtree@cord_internal@absl@@AEAA?AUCopyResult@123@_K@Z
+ ?CopyPrefix@CordRepBtree@cord_internal@absl@@AEAA?AUCopyResult@123@_K_N@Z
?CopyRaw@CordRepBtree@cord_internal@absl@@AEBAPEAV123@XZ
?CopySuffix@CordRepBtree@cord_internal@absl@@AEAA?AUCopyResult@123@_K@Z
?CopyTo@InlineRep@Cord@absl@@QEBAXPEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
@@ -1806,6 +1807,7 @@
?Eval@Condition@absl@@QEBA_NXZ
?Excess@str_format_internal@absl@@YA_K_K0@Z
?ExtendTransitions@TimeZoneInfo@cctz@time_internal@absl@@AEAA_NXZ
+ ?ExtractFront@CordRepBtree@cord_internal@absl@@CAPEAUCordRep@23@PEAV123@@Z
?FDivDuration@absl@@YANVDuration@1@0@Z
?FailedPreconditionError@absl@@YA?AVStatus@1@Vstring_view@1@@Z
?FailureSignalToString@debugging_internal@absl@@YAPEBDH@Z
@@ -2035,6 +2037,7 @@
?IndexBefore@CordRepBtree@cord_internal@absl@@AEBA?AUPosition@123@U4123@_K@Z
?IndexBeyond@CordRepBtree@cord_internal@absl@@AEBA?AUPosition@123@_K@Z
?IndexOf@CordRepBtree@cord_internal@absl@@AEBA?AUPosition@123@_K@Z
+ ?IndexOfLength@CordRepBtree@cord_internal@absl@@AEBA?AUPosition@123@_K@Z
?InfiniteDuration@absl@@YA?AVDuration@1@XZ
?InfiniteFuture@absl@@YA?AVTime@1@XZ
?InfinitePast@absl@@YA?AVTime@1@XZ
@@ -2082,6 +2085,7 @@
?IsInternal@absl@@YA_NAEBVStatus@1@@Z
?IsInvalidArgument@absl@@YA_NAEBVStatus@1@@Z
?IsMovedFrom@Status@absl@@CA_N_K@Z
+ ?IsMutable@RefcountAndFlags@cord_internal@absl@@QEAA_NXZ
?IsNotFound@absl@@YA_NAEBVStatus@1@@Z
?IsOne@RefcountAndFlags@cord_internal@absl@@QEAA_NXZ
?IsOutOfRange@absl@@YA_NAEBVStatus@1@@Z
@@ -2242,6 +2246,7 @@
?Prepend@CordRepBtree@cord_internal@absl@@SAPEAV123@PEAV123@Vstring_view@3@_K@Z
?Prepend@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@PEAUCordRep@23@@Z
?Prepend@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@Vstring_view@3@_K@Z
+ ?PrependArray@Cord@absl@@AEAAXVstring_view@2@W4MethodIdentifier@CordzUpdateTracker@cord_internal@2@@Z
?PrependLeaf@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@PEAUCordRep@23@_K2@Z
?PrependNode@CordForest@absl@@AEAAPEAUCordRep@cord_internal@2@PEAU342@0@Z
?PrependSlow@CordRepBtree@cord_internal@absl@@CAPEAV123@PEAV123@PEAUCordRep@23@@Z
@@ -2277,6 +2282,8 @@
?ReaderLockWhenWithTimeout@Mutex@absl@@QEAA_NAEBVCondition@2@VDuration@2@@Z
?ReaderTryLock@Mutex@absl@@QEAA_NXZ
?ReaderUnlock@Mutex@absl@@QEAAXXZ
+ ?Rebuild@CordRepBtree@cord_internal@absl@@CAXPEAPEAV123@PEAV123@_N@Z
+ ?Rebuild@CordRepBtree@cord_internal@absl@@SAPEAV123@PEAV123@@Z
?ReclaimThreadIdentity@synchronization_internal@absl@@YAXPEAX@Z
?RecordInsertSlow@container_internal@absl@@YAXPEAUHashtablezInfo@12@_K1@Z
?Ref@CordRep@cord_internal@absl@@SAPEAU123@PEAU123@@Z
@@ -2306,6 +2313,7 @@
?RemovePrefix@Cord@absl@@QEAAX_K@Z
?RemovePrefix@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@_K1@Z
?RemoveSuffix@Cord@absl@@QEAAX_K@Z
+ ?RemoveSuffix@CordRepBtree@cord_internal@absl@@SAPEAUCordRep@23@PEAV123@_K@Z
?RemoveSuffix@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@_K1@Z
?RepToPointer@Status@absl@@CAPEAUStatusRep@status_internal@2@_K@Z
?Reset@?$AllocationTransaction@V?$allocator@H@__1@std@@@inlined_vector_internal@absl@@AEAAXXZ
@@ -2323,7 +2331,7 @@
?RoundUpForTag@cord_internal@absl@@YA_K_K@Z
?SafeToDelete@CordzHandle@cord_internal@absl@@QEBA_NXZ
?SafeWriteToStderr@raw_logging_internal@absl@@YAXPEBD_K@Z
- ?SampleSlow@container_internal@absl@@YAPEAUHashtablezInfo@12@PEA_J@Z
+ ?SampleSlow@container_internal@absl@@YAPEAUHashtablezInfo@12@PEA_J_K@Z
?Seek@CordRepBtreeNavigator@cord_internal@absl@@QEAA?AUPosition@123@_K@Z
?Seek@CordRepBtreeReader@cord_internal@absl@@QEAA?AVstring_view@3@_K@Z
?SetAllocation@?$Storage@H$0CP@V?$allocator@H@__1@std@@@inlined_vector_internal@absl@@QEAAXU?$Allocation@V?$allocator@H@__1@std@@@23@@Z
@@ -2537,6 +2545,7 @@
?UnparseFlag@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@VDuration@1@@Z
?UnparseFlag@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@VTime@1@@Z
?Unref@CordRep@cord_internal@absl@@SAXPEAU123@@Z
+ ?Unref@CordRepBtree@cord_internal@absl@@SAXV?$Span@QEAUCordRep@cord_internal@absl@@@3@@Z
?Unref@Status@absl@@CAX_K@Z
?UnrefNonInlined@Status@absl@@CAX_K@Z
?UnrefTree@InlineRep@Cord@absl@@AEAAXXZ
diff --git a/symbols_arm64_rel.def b/symbols_arm64_rel.def
index 81e91ed..05fe073 100644
--- a/symbols_arm64_rel.def
+++ b/symbols_arm64_rel.def
@@ -291,6 +291,7 @@
?CompareSlowPath@Cord@absl@@AEBAHVstring_view@2@_K1@Z
?ConcatNodes@CordForest@absl@@QEAAPEAUCordRep@cord_internal@2@XZ
?Consume@cord_internal@absl@@YAXPEAUCordRep@12@V?$FunctionRef@$$A6AXPEAUCordRep@cord_internal@absl@@_K1@Z@2@@Z
+ ?ConsumeBeginTo@CordRepBtree@cord_internal@absl@@CAPEAV123@PEAV123@_K1@Z
?ConsumeUnboundConversion@str_format_internal@absl@@YAPEBDPEBD0PEAUUnboundConversion@12@PEAH@Z
?ConvertDateTime@absl@@YA?AUTimeConversion@1@_JHHHHHVTimeZone@1@@Z
?ConvertDeletedToEmptyAndFullToDeleted@container_internal@absl@@YAXPEAW4ctrl_t@12@_K@Z
@@ -300,7 +301,7 @@
?ConvertOne@ParsedFormatConsumer@ParsedFormatBase@str_format_internal@absl@@QEAA_NAEBUUnboundConversion@34@Vstring_view@4@@Z
?Copy@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@II_K@Z
?CopyCordToString@absl@@YAXAEBVCord@1@PEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
- ?CopyPrefix@CordRepBtree@cord_internal@absl@@AEAA?AUCopyResult@123@_K@Z
+ ?CopyPrefix@CordRepBtree@cord_internal@absl@@AEAA?AUCopyResult@123@_K_N@Z
?CopySuffix@CordRepBtree@cord_internal@absl@@AEAA?AUCopyResult@123@_K@Z
?CopyTo@InlineRep@Cord@absl@@QEBAXPEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
?CopyToArraySlowPath@Cord@absl@@AEBAXPEAD@Z
@@ -369,6 +370,7 @@
?ErasePayload@Status@absl@@QEAA_NVstring_view@2@@Z
?Eval@Condition@absl@@QEBA_NXZ
?ExtendTransitions@TimeZoneInfo@cctz@time_internal@absl@@AEAA_NXZ
+ ?ExtractFront@CordRepBtree@cord_internal@absl@@CAPEAUCordRep@23@PEAV123@@Z
?FDivDuration@absl@@YANVDuration@1@0@Z
?FailedPreconditionError@absl@@YA?AVStatus@1@Vstring_view@1@@Z
?FailureSignalToString@debugging_internal@absl@@YAPEBDH@Z
@@ -596,10 +598,10 @@
?PrepareForSampling@HashtablezInfo@container_internal@absl@@QEAAXXZ
?PrepareToModify@Status@absl@@AEAAXXZ
?Prepend@Cord@absl@@QEAAXAEBV12@@Z
- ?Prepend@Cord@absl@@QEAAXVstring_view@2@@Z
?Prepend@CordRepBtree@cord_internal@absl@@SAPEAV123@PEAV123@Vstring_view@3@_K@Z
?Prepend@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@PEAUCordRep@23@@Z
?Prepend@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@Vstring_view@3@_K@Z
+ ?PrependArray@Cord@absl@@AEAAXVstring_view@2@W4MethodIdentifier@CordzUpdateTracker@cord_internal@2@@Z
?PrependLeaf@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@PEAUCordRep@23@_K2@Z
?PrependSlow@CordRepBtree@cord_internal@absl@@CAPEAV123@PEAV123@PEAUCordRep@23@@Z
?PrependSlow@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@PEAUCordRep@23@@Z
@@ -627,6 +629,8 @@
?ReaderLockWhenWithTimeout@Mutex@absl@@QEAA_NAEBVCondition@2@VDuration@2@@Z
?ReaderTryLock@Mutex@absl@@QEAA_NXZ
?ReaderUnlock@Mutex@absl@@QEAAXXZ
+ ?Rebuild@CordRepBtree@cord_internal@absl@@CAXPEAPEAV123@PEAV123@_N@Z
+ ?Rebuild@CordRepBtree@cord_internal@absl@@SAPEAV123@PEAV123@@Z
?ReclaimThreadIdentity@synchronization_internal@absl@@YAXPEAX@Z
?RecordInsertSlow@container_internal@absl@@YAXPEAUHashtablezInfo@12@_K1@Z
?Register@?$SampleRecorder@UHashtablezInfo@container_internal@absl@@@profiling_internal@absl@@QEAAPEAUHashtablezInfo@container_internal@3@XZ
@@ -647,6 +651,7 @@
?RemovePrefix@Cord@absl@@QEAAX_K@Z
?RemovePrefix@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@_K1@Z
?RemoveSuffix@Cord@absl@@QEAAX_K@Z
+ ?RemoveSuffix@CordRepBtree@cord_internal@absl@@SAPEAUCordRep@23@PEAV123@_K@Z
?RemoveSuffix@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@_K1@Z
?ResetToBuiltinUTC@TimeZoneInfo@cctz@time_internal@absl@@AEAA_NAEBV?$duration@_JV?$ratio@$00$00@__1@std@@@chrono@__1@std@@@Z
?ResourceExhaustedError@absl@@YA?AVStatus@1@Vstring_view@1@@Z
@@ -654,7 +659,7 @@
?ReverseConsume@cord_internal@absl@@YAXPEAUCordRep@12@V?$FunctionRef@$$A6AXPEAUCordRep@cord_internal@absl@@_K1@Z@2@@Z
?SafeToDelete@CordzHandle@cord_internal@absl@@QEBA_NXZ
?SafeWriteToStderr@raw_logging_internal@absl@@YAXPEBD_K@Z
- ?SampleSlow@container_internal@absl@@YAPEAUHashtablezInfo@12@PEA_J@Z
+ ?SampleSlow@container_internal@absl@@YAPEAUHashtablezInfo@12@PEA_J_K@Z
?Seek@CordRepBtreeReader@cord_internal@absl@@QEAA?AVstring_view@3@_K@Z
?SetCapacityForTesting@CordRepRing@cord_internal@absl@@QEAAX_K@Z
?SetCurrentThreadIdentity@base_internal@absl@@YAXPEAUThreadIdentity@12@P6AXPEAX@Z@Z
diff --git a/symbols_x64_dbg.def b/symbols_x64_dbg.def
index b048f6c..1b2ccf3 100644
--- a/symbols_x64_dbg.def
+++ b/symbols_x64_dbg.def
@@ -1663,6 +1663,7 @@
?ConstructNext@?$IteratorValueAdapter@V?$allocator@UPayload@status_internal@absl@@@__1@std@@V?$move_iterator@PEAUPayload@status_internal@absl@@@23@@inlined_vector_internal@absl@@QEAAXAEAV?$allocator@UPayload@status_internal@absl@@@__1@std@@PEAUPayload@status_internal@3@@Z
?ConstructNext@?$IteratorValueAdapter@V?$allocator@USubRange@absl@@@__1@std@@V?$move_iterator@PEAUSubRange@absl@@@23@@inlined_vector_internal@absl@@QEAAXAEAV?$allocator@USubRange@absl@@@__1@std@@PEAUSubRange@3@@Z
?Consume@cord_internal@absl@@YAXPEAUCordRep@12@V?$FunctionRef@$$A6AXPEAUCordRep@cord_internal@absl@@_K1@Z@2@@Z
+ ?ConsumeBeginTo@CordRepBtree@cord_internal@absl@@CAPEAV123@PEAV123@_K1@Z
?ConsumePrefix@absl@@YA_NPEAVstring_view@1@V21@@Z
?ConsumeUnboundConversion@str_format_internal@absl@@YAPEBDPEBD0PEAUUnboundConversion@12@PEAH@Z
?Contains@str_format_internal@absl@@YA_NW4FormatConversionCharSet@2@D@Z
@@ -1679,7 +1680,7 @@
?Copy@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@II_K@Z
?CopyBeginTo@CordRepBtree@cord_internal@absl@@AEBAPEAV123@_K0@Z
?CopyCordToString@absl@@YAXAEBVCord@1@PEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
- ?CopyPrefix@CordRepBtree@cord_internal@absl@@AEAA?AUCopyResult@123@_K@Z
+ ?CopyPrefix@CordRepBtree@cord_internal@absl@@AEAA?AUCopyResult@123@_K_N@Z
?CopyRaw@CordRepBtree@cord_internal@absl@@AEBAPEAV123@XZ
?CopySuffix@CordRepBtree@cord_internal@absl@@AEAA?AUCopyResult@123@_K@Z
?CopyTo@InlineRep@Cord@absl@@QEBAXPEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
@@ -1810,6 +1811,7 @@
?Eval@Condition@absl@@QEBA_NXZ
?Excess@str_format_internal@absl@@YA_K_K0@Z
?ExtendTransitions@TimeZoneInfo@cctz@time_internal@absl@@AEAA_NXZ
+ ?ExtractFront@CordRepBtree@cord_internal@absl@@CAPEAUCordRep@23@PEAV123@@Z
?FDivDuration@absl@@YANVDuration@1@0@Z
?FailedPreconditionError@absl@@YA?AVStatus@1@Vstring_view@1@@Z
?FailureSignalToString@debugging_internal@absl@@YAPEBDH@Z
@@ -2038,6 +2040,7 @@
?IndexBefore@CordRepBtree@cord_internal@absl@@AEBA?AUPosition@123@U4123@_K@Z
?IndexBeyond@CordRepBtree@cord_internal@absl@@AEBA?AUPosition@123@_K@Z
?IndexOf@CordRepBtree@cord_internal@absl@@AEBA?AUPosition@123@_K@Z
+ ?IndexOfLength@CordRepBtree@cord_internal@absl@@AEBA?AUPosition@123@_K@Z
?InfiniteDuration@absl@@YA?AVDuration@1@XZ
?InfiniteFuture@absl@@YA?AVTime@1@XZ
?InfinitePast@absl@@YA?AVTime@1@XZ
@@ -2085,6 +2088,7 @@
?IsInternal@absl@@YA_NAEBVStatus@1@@Z
?IsInvalidArgument@absl@@YA_NAEBVStatus@1@@Z
?IsMovedFrom@Status@absl@@CA_N_K@Z
+ ?IsMutable@RefcountAndFlags@cord_internal@absl@@QEAA_NXZ
?IsNotFound@absl@@YA_NAEBVStatus@1@@Z
?IsOne@RefcountAndFlags@cord_internal@absl@@QEAA_NXZ
?IsOutOfRange@absl@@YA_NAEBVStatus@1@@Z
@@ -2244,6 +2248,7 @@
?Prepend@CordRepBtree@cord_internal@absl@@SAPEAV123@PEAV123@Vstring_view@3@_K@Z
?Prepend@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@PEAUCordRep@23@@Z
?Prepend@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@Vstring_view@3@_K@Z
+ ?PrependArray@Cord@absl@@AEAAXVstring_view@2@W4MethodIdentifier@CordzUpdateTracker@cord_internal@2@@Z
?PrependLeaf@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@PEAUCordRep@23@_K2@Z
?PrependNode@CordForest@absl@@AEAAPEAUCordRep@cord_internal@2@PEAU342@0@Z
?PrependSlow@CordRepBtree@cord_internal@absl@@CAPEAV123@PEAV123@PEAUCordRep@23@@Z
@@ -2279,6 +2284,8 @@
?ReaderLockWhenWithTimeout@Mutex@absl@@QEAA_NAEBVCondition@2@VDuration@2@@Z
?ReaderTryLock@Mutex@absl@@QEAA_NXZ
?ReaderUnlock@Mutex@absl@@QEAAXXZ
+ ?Rebuild@CordRepBtree@cord_internal@absl@@CAXPEAPEAV123@PEAV123@_N@Z
+ ?Rebuild@CordRepBtree@cord_internal@absl@@SAPEAV123@PEAV123@@Z
?ReclaimThreadIdentity@synchronization_internal@absl@@YAXPEAX@Z
?RecordInsertSlow@container_internal@absl@@YAXPEAUHashtablezInfo@12@_K1@Z
?Ref@CordRep@cord_internal@absl@@SAPEAU123@PEAU123@@Z
@@ -2308,6 +2315,7 @@
?RemovePrefix@Cord@absl@@QEAAX_K@Z
?RemovePrefix@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@_K1@Z
?RemoveSuffix@Cord@absl@@QEAAX_K@Z
+ ?RemoveSuffix@CordRepBtree@cord_internal@absl@@SAPEAUCordRep@23@PEAV123@_K@Z
?RemoveSuffix@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@_K1@Z
?RepToPointer@Status@absl@@CAPEAUStatusRep@status_internal@2@_K@Z
?Reset@?$AllocationTransaction@V?$allocator@H@__1@std@@@inlined_vector_internal@absl@@AEAAXXZ
@@ -2325,7 +2333,7 @@
?RoundUpForTag@cord_internal@absl@@YA_K_K@Z
?SafeToDelete@CordzHandle@cord_internal@absl@@QEBA_NXZ
?SafeWriteToStderr@raw_logging_internal@absl@@YAXPEBD_K@Z
- ?SampleSlow@container_internal@absl@@YAPEAUHashtablezInfo@12@PEA_J@Z
+ ?SampleSlow@container_internal@absl@@YAPEAUHashtablezInfo@12@PEA_J_K@Z
?Seek@CordRepBtreeNavigator@cord_internal@absl@@QEAA?AUPosition@123@_K@Z
?Seek@CordRepBtreeReader@cord_internal@absl@@QEAA?AVstring_view@3@_K@Z
?SetAllocation@?$Storage@H$0CP@V?$allocator@H@__1@std@@@inlined_vector_internal@absl@@QEAAXU?$Allocation@V?$allocator@H@__1@std@@@23@@Z
@@ -2536,6 +2544,7 @@
?UnparseFlag@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@VDuration@1@@Z
?UnparseFlag@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@VTime@1@@Z
?Unref@CordRep@cord_internal@absl@@SAXPEAU123@@Z
+ ?Unref@CordRepBtree@cord_internal@absl@@SAXV?$Span@QEAUCordRep@cord_internal@absl@@@3@@Z
?Unref@Status@absl@@CAX_K@Z
?UnrefNonInlined@Status@absl@@CAX_K@Z
?UnrefTree@InlineRep@Cord@absl@@AEAAXXZ
diff --git a/symbols_x64_rel.def b/symbols_x64_rel.def
index 71bf5b3..bbcd7bc 100644
--- a/symbols_x64_rel.def
+++ b/symbols_x64_rel.def
@@ -292,6 +292,7 @@
?CompareSlowPath@Cord@absl@@AEBAHVstring_view@2@_K1@Z
?ConcatNodes@CordForest@absl@@QEAAPEAUCordRep@cord_internal@2@XZ
?Consume@cord_internal@absl@@YAXPEAUCordRep@12@V?$FunctionRef@$$A6AXPEAUCordRep@cord_internal@absl@@_K1@Z@2@@Z
+ ?ConsumeBeginTo@CordRepBtree@cord_internal@absl@@CAPEAV123@PEAV123@_K1@Z
?ConsumeUnboundConversion@str_format_internal@absl@@YAPEBDPEBD0PEAUUnboundConversion@12@PEAH@Z
?ConvertDateTime@absl@@YA?AUTimeConversion@1@_JHHHHHVTimeZone@1@@Z
?ConvertDeletedToEmptyAndFullToDeleted@container_internal@absl@@YAXPEAW4ctrl_t@12@_K@Z
@@ -301,7 +302,7 @@
?ConvertOne@ParsedFormatConsumer@ParsedFormatBase@str_format_internal@absl@@QEAA_NAEBUUnboundConversion@34@Vstring_view@4@@Z
?Copy@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@II_K@Z
?CopyCordToString@absl@@YAXAEBVCord@1@PEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
- ?CopyPrefix@CordRepBtree@cord_internal@absl@@AEAA?AUCopyResult@123@_K@Z
+ ?CopyPrefix@CordRepBtree@cord_internal@absl@@AEAA?AUCopyResult@123@_K_N@Z
?CopySuffix@CordRepBtree@cord_internal@absl@@AEAA?AUCopyResult@123@_K@Z
?CopyTo@InlineRep@Cord@absl@@QEBAXPEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
?CopyToArraySlowPath@Cord@absl@@AEBAXPEAD@Z
@@ -370,6 +371,7 @@
?ErasePayload@Status@absl@@QEAA_NVstring_view@2@@Z
?Eval@Condition@absl@@QEBA_NXZ
?ExtendTransitions@TimeZoneInfo@cctz@time_internal@absl@@AEAA_NXZ
+ ?ExtractFront@CordRepBtree@cord_internal@absl@@CAPEAUCordRep@23@PEAV123@@Z
?FDivDuration@absl@@YANVDuration@1@0@Z
?FailedPreconditionError@absl@@YA?AVStatus@1@Vstring_view@1@@Z
?FailureSignalToString@debugging_internal@absl@@YAPEBDH@Z
@@ -597,10 +599,10 @@
?PrepareForSampling@HashtablezInfo@container_internal@absl@@QEAAXXZ
?PrepareToModify@Status@absl@@AEAAXXZ
?Prepend@Cord@absl@@QEAAXAEBV12@@Z
- ?Prepend@Cord@absl@@QEAAXVstring_view@2@@Z
?Prepend@CordRepBtree@cord_internal@absl@@SAPEAV123@PEAV123@Vstring_view@3@_K@Z
?Prepend@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@PEAUCordRep@23@@Z
?Prepend@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@Vstring_view@3@_K@Z
+ ?PrependArray@Cord@absl@@AEAAXVstring_view@2@W4MethodIdentifier@CordzUpdateTracker@cord_internal@2@@Z
?PrependLeaf@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@PEAUCordRep@23@_K2@Z
?PrependSlow@CordRepBtree@cord_internal@absl@@CAPEAV123@PEAV123@PEAUCordRep@23@@Z
?PrependSlow@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@PEAUCordRep@23@@Z
@@ -628,6 +630,8 @@
?ReaderLockWhenWithTimeout@Mutex@absl@@QEAA_NAEBVCondition@2@VDuration@2@@Z
?ReaderTryLock@Mutex@absl@@QEAA_NXZ
?ReaderUnlock@Mutex@absl@@QEAAXXZ
+ ?Rebuild@CordRepBtree@cord_internal@absl@@CAXPEAPEAV123@PEAV123@_N@Z
+ ?Rebuild@CordRepBtree@cord_internal@absl@@SAPEAV123@PEAV123@@Z
?ReclaimThreadIdentity@synchronization_internal@absl@@YAXPEAX@Z
?RecordInsertSlow@container_internal@absl@@YAXPEAUHashtablezInfo@12@_K1@Z
?Register@?$SampleRecorder@UHashtablezInfo@container_internal@absl@@@profiling_internal@absl@@QEAAPEAUHashtablezInfo@container_internal@3@XZ
@@ -648,6 +652,7 @@
?RemovePrefix@Cord@absl@@QEAAX_K@Z
?RemovePrefix@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@_K1@Z
?RemoveSuffix@Cord@absl@@QEAAX_K@Z
+ ?RemoveSuffix@CordRepBtree@cord_internal@absl@@SAPEAUCordRep@23@PEAV123@_K@Z
?RemoveSuffix@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@_K1@Z
?ResetToBuiltinUTC@TimeZoneInfo@cctz@time_internal@absl@@AEAA_NAEBV?$duration@_JV?$ratio@$00$00@__1@std@@@chrono@__1@std@@@Z
?ResourceExhaustedError@absl@@YA?AVStatus@1@Vstring_view@1@@Z
@@ -655,7 +660,7 @@
?ReverseConsume@cord_internal@absl@@YAXPEAUCordRep@12@V?$FunctionRef@$$A6AXPEAUCordRep@cord_internal@absl@@_K1@Z@2@@Z
?SafeToDelete@CordzHandle@cord_internal@absl@@QEBA_NXZ
?SafeWriteToStderr@raw_logging_internal@absl@@YAXPEBD_K@Z
- ?SampleSlow@container_internal@absl@@YAPEAUHashtablezInfo@12@PEA_J@Z
+ ?SampleSlow@container_internal@absl@@YAPEAUHashtablezInfo@12@PEA_J_K@Z
?Seek@CordRepBtreeReader@cord_internal@absl@@QEAA?AVstring_view@3@_K@Z
?SetCapacityForTesting@CordRepRing@cord_internal@absl@@QEAAX_K@Z
?SetCurrentThreadIdentity@base_internal@absl@@YAXPEAUThreadIdentity@12@P6AXPEAX@Z@Z
diff --git a/symbols_x64_rel_asan.def b/symbols_x64_rel_asan.def
index a05e93f..63cccc0 100644
--- a/symbols_x64_rel_asan.def
+++ b/symbols_x64_rel_asan.def
@@ -304,6 +304,7 @@
?CompareSlowPath@Cord@absl@@AEBAHVstring_view@2@_K1@Z
?ConcatNodes@CordForest@absl@@QEAAPEAUCordRep@cord_internal@2@XZ
?Consume@cord_internal@absl@@YAXPEAUCordRep@12@V?$FunctionRef@$$A6AXPEAUCordRep@cord_internal@absl@@_K1@Z@2@@Z
+ ?ConsumeBeginTo@CordRepBtree@cord_internal@absl@@CAPEAV123@PEAV123@_K1@Z
?ConsumeUnboundConversion@str_format_internal@absl@@YAPEBDPEBD0PEAUUnboundConversion@12@PEAH@Z
?ConvertDateTime@absl@@YA?AUTimeConversion@1@_JHHHHHVTimeZone@1@@Z
?ConvertDeletedToEmptyAndFullToDeleted@container_internal@absl@@YAXPEAW4ctrl_t@12@_K@Z
@@ -312,7 +313,7 @@
?ConvertFloatImpl@str_format_internal@absl@@YA_NOAEBVFormatConversionSpecImpl@12@PEAVFormatSinkImpl@12@@Z
?Copy@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@II_K@Z
?CopyCordToString@absl@@YAXAEBVCord@1@PEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
- ?CopyPrefix@CordRepBtree@cord_internal@absl@@AEAA?AUCopyResult@123@_K@Z
+ ?CopyPrefix@CordRepBtree@cord_internal@absl@@AEAA?AUCopyResult@123@_K_N@Z
?CopySuffix@CordRepBtree@cord_internal@absl@@AEAA?AUCopyResult@123@_K@Z
?CopyTo@InlineRep@Cord@absl@@QEBAXPEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
?CopyToArraySlowPath@Cord@absl@@AEBAXPEAD@Z
@@ -381,6 +382,7 @@
?ErasePayload@Status@absl@@QEAA_NVstring_view@2@@Z
?Eval@Condition@absl@@QEBA_NXZ
?ExtendTransitions@TimeZoneInfo@cctz@time_internal@absl@@AEAA_NXZ
+ ?ExtractFront@CordRepBtree@cord_internal@absl@@CAPEAUCordRep@23@PEAV123@@Z
?FDivDuration@absl@@YANVDuration@1@0@Z
?FailedPreconditionError@absl@@YA?AVStatus@1@Vstring_view@1@@Z
?FailureSignalToString@debugging_internal@absl@@YAPEBDH@Z
@@ -610,10 +612,10 @@
?PrepareForSampling@HashtablezInfo@container_internal@absl@@QEAAXXZ
?PrepareToModify@Status@absl@@AEAAXXZ
?Prepend@Cord@absl@@QEAAXAEBV12@@Z
- ?Prepend@Cord@absl@@QEAAXVstring_view@2@@Z
?Prepend@CordRepBtree@cord_internal@absl@@SAPEAV123@PEAV123@Vstring_view@3@_K@Z
?Prepend@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@PEAUCordRep@23@@Z
?Prepend@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@Vstring_view@3@_K@Z
+ ?PrependArray@Cord@absl@@AEAAXVstring_view@2@W4MethodIdentifier@CordzUpdateTracker@cord_internal@2@@Z
?PrependLeaf@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@PEAUCordRep@23@_K2@Z
?PrependSlow@CordRepBtree@cord_internal@absl@@CAPEAV123@PEAV123@PEAUCordRep@23@@Z
?PrependSlow@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@PEAUCordRep@23@@Z
@@ -641,6 +643,8 @@
?ReaderLockWhenWithTimeout@Mutex@absl@@QEAA_NAEBVCondition@2@VDuration@2@@Z
?ReaderTryLock@Mutex@absl@@QEAA_NXZ
?ReaderUnlock@Mutex@absl@@QEAAXXZ
+ ?Rebuild@CordRepBtree@cord_internal@absl@@CAXPEAPEAV123@PEAV123@_N@Z
+ ?Rebuild@CordRepBtree@cord_internal@absl@@SAPEAV123@PEAV123@@Z
?ReclaimThreadIdentity@synchronization_internal@absl@@YAXPEAX@Z
?RecordInsertSlow@container_internal@absl@@YAXPEAUHashtablezInfo@12@_K1@Z
?Register@?$SampleRecorder@UHashtablezInfo@container_internal@absl@@@profiling_internal@absl@@QEAAPEAUHashtablezInfo@container_internal@3@XZ
@@ -661,6 +665,7 @@
?RemovePrefix@Cord@absl@@QEAAX_K@Z
?RemovePrefix@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@_K1@Z
?RemoveSuffix@Cord@absl@@QEAAX_K@Z
+ ?RemoveSuffix@CordRepBtree@cord_internal@absl@@SAPEAUCordRep@23@PEAV123@_K@Z
?RemoveSuffix@CordRepRing@cord_internal@absl@@SAPEAV123@PEAV123@_K1@Z
?ResetToBuiltinUTC@TimeZoneInfo@cctz@time_internal@absl@@AEAA_NAEBV?$duration@_JV?$ratio@$00$00@__1@std@@@chrono@__1@std@@@Z
?ResourceExhaustedError@absl@@YA?AVStatus@1@Vstring_view@1@@Z
@@ -668,7 +673,7 @@
?ReverseConsume@cord_internal@absl@@YAXPEAUCordRep@12@V?$FunctionRef@$$A6AXPEAUCordRep@cord_internal@absl@@_K1@Z@2@@Z
?SafeToDelete@CordzHandle@cord_internal@absl@@QEBA_NXZ
?SafeWriteToStderr@raw_logging_internal@absl@@YAXPEBD_K@Z
- ?SampleSlow@container_internal@absl@@YAPEAUHashtablezInfo@12@PEA_J@Z
+ ?SampleSlow@container_internal@absl@@YAPEAUHashtablezInfo@12@PEA_J_K@Z
?Seek@CordRepBtreeReader@cord_internal@absl@@QEAA?AVstring_view@3@_K@Z
?SetCapacityForTesting@CordRepRing@cord_internal@absl@@QEAAX_K@Z
?SetCurrentThreadIdentity@base_internal@absl@@YAXPEAUThreadIdentity@12@P6AXPEAX@Z@Z
diff --git a/symbols_x86_dbg.def b/symbols_x86_dbg.def
index 10fd43a..f715699 100644
--- a/symbols_x86_dbg.def
+++ b/symbols_x86_dbg.def
@@ -1657,6 +1657,7 @@
?ConstructNext@?$IteratorValueAdapter@V?$allocator@UPayload@status_internal@absl@@@__1@std@@V?$move_iterator@PAUPayload@status_internal@absl@@@23@@inlined_vector_internal@absl@@QAEXAAV?$allocator@UPayload@status_internal@absl@@@__1@std@@PAUPayload@status_internal@3@@Z
?ConstructNext@?$IteratorValueAdapter@V?$allocator@USubRange@absl@@@__1@std@@V?$move_iterator@PAUSubRange@absl@@@23@@inlined_vector_internal@absl@@QAEXAAV?$allocator@USubRange@absl@@@__1@std@@PAUSubRange@3@@Z
?Consume@cord_internal@absl@@YAXPAUCordRep@12@V?$FunctionRef@$$A6AXPAUCordRep@cord_internal@absl@@II@Z@2@@Z
+ ?ConsumeBeginTo@CordRepBtree@cord_internal@absl@@CAPAV123@PAV123@II@Z
?ConsumePrefix@absl@@YA_NPAVstring_view@1@V21@@Z
?ConsumeUnboundConversion@str_format_internal@absl@@YAPBDPBD0PAUUnboundConversion@12@PAH@Z
?Contains@str_format_internal@absl@@YA_NW4FormatConversionCharSet@2@D@Z
@@ -1673,7 +1674,7 @@
?Copy@CordRepRing@cord_internal@absl@@CAPAV123@PAV123@III@Z
?CopyBeginTo@CordRepBtree@cord_internal@absl@@ABEPAV123@II@Z
?CopyCordToString@absl@@YAXABVCord@1@PAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
- ?CopyPrefix@CordRepBtree@cord_internal@absl@@AAE?AUCopyResult@123@I@Z
+ ?CopyPrefix@CordRepBtree@cord_internal@absl@@AAE?AUCopyResult@123@I_N@Z
?CopyRaw@CordRepBtree@cord_internal@absl@@ABEPAV123@XZ
?CopySuffix@CordRepBtree@cord_internal@absl@@AAE?AUCopyResult@123@I@Z
?CopyTo@InlineRep@Cord@absl@@QBEXPAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
@@ -1804,6 +1805,7 @@
?Eval@Condition@absl@@QBE_NXZ
?Excess@str_format_internal@absl@@YAIII@Z
?ExtendTransitions@TimeZoneInfo@cctz@time_internal@absl@@AAE_NXZ
+ ?ExtractFront@CordRepBtree@cord_internal@absl@@CAPAUCordRep@23@PAV123@@Z
?FDivDuration@absl@@YANVDuration@1@0@Z
?FailedPreconditionError@absl@@YA?AVStatus@1@Vstring_view@1@@Z
?FailureSignalToString@debugging_internal@absl@@YAPBDH@Z
@@ -2032,6 +2034,7 @@
?IndexBefore@CordRepBtree@cord_internal@absl@@ABE?AUPosition@123@U4123@I@Z
?IndexBeyond@CordRepBtree@cord_internal@absl@@ABE?AUPosition@123@I@Z
?IndexOf@CordRepBtree@cord_internal@absl@@ABE?AUPosition@123@I@Z
+ ?IndexOfLength@CordRepBtree@cord_internal@absl@@ABE?AUPosition@123@I@Z
?InfiniteDuration@absl@@YA?AVDuration@1@XZ
?InfiniteFuture@absl@@YA?AVTime@1@XZ
?InfinitePast@absl@@YA?AVTime@1@XZ
@@ -2079,6 +2082,7 @@
?IsInternal@absl@@YA_NABVStatus@1@@Z
?IsInvalidArgument@absl@@YA_NABVStatus@1@@Z
?IsMovedFrom@Status@absl@@CA_NI@Z
+ ?IsMutable@RefcountAndFlags@cord_internal@absl@@QAE_NXZ
?IsNotFound@absl@@YA_NABVStatus@1@@Z
?IsOne@RefcountAndFlags@cord_internal@absl@@QAE_NXZ
?IsOutOfRange@absl@@YA_NABVStatus@1@@Z
@@ -2238,6 +2242,7 @@
?Prepend@CordRepBtree@cord_internal@absl@@SAPAV123@PAV123@Vstring_view@3@I@Z
?Prepend@CordRepRing@cord_internal@absl@@SAPAV123@PAV123@PAUCordRep@23@@Z
?Prepend@CordRepRing@cord_internal@absl@@SAPAV123@PAV123@Vstring_view@3@I@Z
+ ?PrependArray@Cord@absl@@AAEXVstring_view@2@W4MethodIdentifier@CordzUpdateTracker@cord_internal@2@@Z
?PrependLeaf@CordRepRing@cord_internal@absl@@CAPAV123@PAV123@PAUCordRep@23@II@Z
?PrependNode@CordForest@absl@@AAEPAUCordRep@cord_internal@2@PAU342@0@Z
?PrependSlow@CordRepBtree@cord_internal@absl@@CAPAV123@PAV123@PAUCordRep@23@@Z
@@ -2273,6 +2278,8 @@
?ReaderLockWhenWithTimeout@Mutex@absl@@QAE_NABVCondition@2@VDuration@2@@Z
?ReaderTryLock@Mutex@absl@@QAE_NXZ
?ReaderUnlock@Mutex@absl@@QAEXXZ
+ ?Rebuild@CordRepBtree@cord_internal@absl@@CAXPAPAV123@PAV123@_N@Z
+ ?Rebuild@CordRepBtree@cord_internal@absl@@SAPAV123@PAV123@@Z
?ReclaimThreadIdentity@synchronization_internal@absl@@YAXPAX@Z
?RecordInsertSlow@container_internal@absl@@YAXPAUHashtablezInfo@12@II@Z
?Ref@CordRep@cord_internal@absl@@SAPAU123@PAU123@@Z
@@ -2302,6 +2309,7 @@
?RemovePrefix@Cord@absl@@QAEXI@Z
?RemovePrefix@CordRepRing@cord_internal@absl@@SAPAV123@PAV123@II@Z
?RemoveSuffix@Cord@absl@@QAEXI@Z
+ ?RemoveSuffix@CordRepBtree@cord_internal@absl@@SAPAUCordRep@23@PAV123@I@Z
?RemoveSuffix@CordRepRing@cord_internal@absl@@SAPAV123@PAV123@II@Z
?RepToPointer@Status@absl@@CAPAUStatusRep@status_internal@2@I@Z
?Reset@?$AllocationTransaction@V?$allocator@H@__1@std@@@inlined_vector_internal@absl@@AAEXXZ
@@ -2319,7 +2327,7 @@
?RoundUpForTag@cord_internal@absl@@YAII@Z
?SafeToDelete@CordzHandle@cord_internal@absl@@QBE_NXZ
?SafeWriteToStderr@raw_logging_internal@absl@@YAXPBDI@Z
- ?SampleSlow@container_internal@absl@@YAPAUHashtablezInfo@12@PA_J@Z
+ ?SampleSlow@container_internal@absl@@YAPAUHashtablezInfo@12@PA_JI@Z
?Seek@CordRepBtreeNavigator@cord_internal@absl@@QAE?AUPosition@123@I@Z
?Seek@CordRepBtreeReader@cord_internal@absl@@QAE?AVstring_view@3@I@Z
?SetAllocation@?$Storage@H$0CP@V?$allocator@H@__1@std@@@inlined_vector_internal@absl@@QAEXU?$Allocation@V?$allocator@H@__1@std@@@23@@Z
@@ -2530,6 +2538,7 @@
?UnparseFlag@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@VDuration@1@@Z
?UnparseFlag@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@VTime@1@@Z
?Unref@CordRep@cord_internal@absl@@SAXPAU123@@Z
+ ?Unref@CordRepBtree@cord_internal@absl@@SAXV?$Span@QAUCordRep@cord_internal@absl@@@3@@Z
?Unref@Status@absl@@CAXI@Z
?UnrefNonInlined@Status@absl@@CAXI@Z
?UnrefTree@InlineRep@Cord@absl@@AAEXXZ
diff --git a/symbols_x86_rel.def b/symbols_x86_rel.def
index 73e2b2d..580672e 100644
--- a/symbols_x86_rel.def
+++ b/symbols_x86_rel.def
@@ -292,6 +292,7 @@
?CompareSlowPath@Cord@absl@@ABEHVstring_view@2@II@Z
?ConcatNodes@CordForest@absl@@QAEPAUCordRep@cord_internal@2@XZ
?Consume@cord_internal@absl@@YAXPAUCordRep@12@V?$FunctionRef@$$A6AXPAUCordRep@cord_internal@absl@@II@Z@2@@Z
+ ?ConsumeBeginTo@CordRepBtree@cord_internal@absl@@CAPAV123@PAV123@II@Z
?ConsumeUnboundConversion@str_format_internal@absl@@YAPBDPBD0PAUUnboundConversion@12@PAH@Z
?ConvertDateTime@absl@@YA?AUTimeConversion@1@_JHHHHHVTimeZone@1@@Z
?ConvertDeletedToEmptyAndFullToDeleted@container_internal@absl@@YAXPAW4ctrl_t@12@I@Z
@@ -301,7 +302,7 @@
?ConvertOne@ParsedFormatConsumer@ParsedFormatBase@str_format_internal@absl@@QAE_NABUUnboundConversion@34@Vstring_view@4@@Z
?Copy@CordRepRing@cord_internal@absl@@CAPAV123@PAV123@III@Z
?CopyCordToString@absl@@YAXABVCord@1@PAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
- ?CopyPrefix@CordRepBtree@cord_internal@absl@@AAE?AUCopyResult@123@I@Z
+ ?CopyPrefix@CordRepBtree@cord_internal@absl@@AAE?AUCopyResult@123@I_N@Z
?CopySuffix@CordRepBtree@cord_internal@absl@@AAE?AUCopyResult@123@I@Z
?CopyTo@InlineRep@Cord@absl@@QBEXPAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
?CopyToArraySlowPath@Cord@absl@@ABEXPAD@Z
@@ -370,6 +371,7 @@
?ErasePayload@Status@absl@@QAE_NVstring_view@2@@Z
?Eval@Condition@absl@@QBE_NXZ
?ExtendTransitions@TimeZoneInfo@cctz@time_internal@absl@@AAE_NXZ
+ ?ExtractFront@CordRepBtree@cord_internal@absl@@CAPAUCordRep@23@PAV123@@Z
?FDivDuration@absl@@YANVDuration@1@0@Z
?FailedPreconditionError@absl@@YA?AVStatus@1@Vstring_view@1@@Z
?FailureSignalToString@debugging_internal@absl@@YAPBDH@Z
@@ -597,10 +599,10 @@
?PrepareForSampling@HashtablezInfo@container_internal@absl@@QAEXXZ
?PrepareToModify@Status@absl@@AAEXXZ
?Prepend@Cord@absl@@QAEXABV12@@Z
- ?Prepend@Cord@absl@@QAEXVstring_view@2@@Z
?Prepend@CordRepBtree@cord_internal@absl@@SAPAV123@PAV123@Vstring_view@3@I@Z
?Prepend@CordRepRing@cord_internal@absl@@SAPAV123@PAV123@PAUCordRep@23@@Z
?Prepend@CordRepRing@cord_internal@absl@@SAPAV123@PAV123@Vstring_view@3@I@Z
+ ?PrependArray@Cord@absl@@AAEXVstring_view@2@W4MethodIdentifier@CordzUpdateTracker@cord_internal@2@@Z
?PrependLeaf@CordRepRing@cord_internal@absl@@CAPAV123@PAV123@PAUCordRep@23@II@Z
?PrependSlow@CordRepBtree@cord_internal@absl@@CAPAV123@PAV123@PAUCordRep@23@@Z
?PrependSlow@CordRepRing@cord_internal@absl@@CAPAV123@PAV123@PAUCordRep@23@@Z
@@ -628,6 +630,8 @@
?ReaderLockWhenWithTimeout@Mutex@absl@@QAE_NABVCondition@2@VDuration@2@@Z
?ReaderTryLock@Mutex@absl@@QAE_NXZ
?ReaderUnlock@Mutex@absl@@QAEXXZ
+ ?Rebuild@CordRepBtree@cord_internal@absl@@CAXPAPAV123@PAV123@_N@Z
+ ?Rebuild@CordRepBtree@cord_internal@absl@@SAPAV123@PAV123@@Z
?ReclaimThreadIdentity@synchronization_internal@absl@@YAXPAX@Z
?RecordInsertSlow@container_internal@absl@@YAXPAUHashtablezInfo@12@II@Z
?Register@?$SampleRecorder@UHashtablezInfo@container_internal@absl@@@profiling_internal@absl@@QAEPAUHashtablezInfo@container_internal@3@XZ
@@ -648,6 +652,7 @@
?RemovePrefix@Cord@absl@@QAEXI@Z
?RemovePrefix@CordRepRing@cord_internal@absl@@SAPAV123@PAV123@II@Z
?RemoveSuffix@Cord@absl@@QAEXI@Z
+ ?RemoveSuffix@CordRepBtree@cord_internal@absl@@SAPAUCordRep@23@PAV123@I@Z
?RemoveSuffix@CordRepRing@cord_internal@absl@@SAPAV123@PAV123@II@Z
?ResetToBuiltinUTC@TimeZoneInfo@cctz@time_internal@absl@@AAE_NABV?$duration@_JV?$ratio@$00$00@__1@std@@@chrono@__1@std@@@Z
?ResourceExhaustedError@absl@@YA?AVStatus@1@Vstring_view@1@@Z
@@ -655,7 +660,7 @@
?ReverseConsume@cord_internal@absl@@YAXPAUCordRep@12@V?$FunctionRef@$$A6AXPAUCordRep@cord_internal@absl@@II@Z@2@@Z
?SafeToDelete@CordzHandle@cord_internal@absl@@QBE_NXZ
?SafeWriteToStderr@raw_logging_internal@absl@@YAXPBDI@Z
- ?SampleSlow@container_internal@absl@@YAPAUHashtablezInfo@12@PA_J@Z
+ ?SampleSlow@container_internal@absl@@YAPAUHashtablezInfo@12@PA_JI@Z
?Seek@CordRepBtreeReader@cord_internal@absl@@QAE?AVstring_view@3@I@Z
?SetCapacityForTesting@CordRepRing@cord_internal@absl@@QAEXI@Z
?SetCurrentThreadIdentity@base_internal@absl@@YAXPAUThreadIdentity@12@P6AXPAX@Z@Z