Commit b19ba967 by Abseil Team Committed by Andy Soffer

Export of internal Abseil changes

--
a3e58c1870a9626039f4d178d2d599319bd9f8a8 by Matt Kulukundis <kfm@google.com>:

Allow MakeCordFromExternal to take a zero arg releaser.

PiperOrigin-RevId: 298650274

--
01897c4a9bb99f3dc329a794019498ad345ddebd by Samuel Benzaquen <sbenza@google.com>:

Reduce library bloat for absl::Flag by moving the definition of base virtual functions to a .cc file.
This removes the duplicate symbols in user translation units and  has the side effect of moving the vtable definition too (re key function)

PiperOrigin-RevId: 298617920

--
190f0d3782c63aed01046886d7fbc1be5bca2de9 by Derek Mauro <dmauro@google.com>:

Import GitHub #596: Unbreak stacktrace code for UWP apps

PiperOrigin-RevId: 298600834

--
cd5cf6f8c87b35b85a9584e94da2a99057345b73 by Gennadiy Rozental <rogeeff@google.com>:

Use union of heap allocated pointer, one word atomic and two word atomic to represent flags value.

Any type T, which is trivially copy-able and with with sizeof(T) <= 8, will be stored in atomic int64_t.
Any type T, which is trivially copy-able and with with 8 < sizeof(T) <= 16, will be stored in atomic AlignedTwoWords.

We also introducing value storage type to distinguish these cases.

PiperOrigin-RevId: 298497200

--
f8fe7bd53bfed601f002f521e34ab4bc083fc28b by Matthew Brown <matthewbr@google.com>:

Ensure a deep copy and proper equality on absl::Status::ErasePayload

PiperOrigin-RevId: 298482742

--
a5c9ccddf4b04f444e3f7e27dbc14faf1fcb5373 by Gennadiy Rozental <rogeeff@google.com>:

Change ChunkIterator implementation to use fixed capacity collection of CordRep*. We can now assume that depth never exceeds 91. That makes comparison operator exception safe.

I've tested that with this CL we do not observe an overhead of chunk_end. Compiler optimized this iterator completely.

PiperOrigin-RevId: 298458472

--
327ea5e8910bc388b03389c730763f9823abfce5 by Abseil Team <absl-team@google.com>:

Minor cleanups in b-tree code:
- Rename some variables: fix issues of different param names between definition/declaration, move away from `x` as a default meaningless variable name.
- Make init_leaf/init_internal be non-static methods (they already take the node as the first parameter).
- In internal_emplace/try_shrink, update root/rightmost the same way as in insert_unique/insert_multi.
- Replace a TODO with a comment.

PiperOrigin-RevId: 298432836

--
8020ce9ec8558ee712d9733ae3d660ac1d3ffe1a by Abseil Team <absl-team@google.com>:

Guard against unnecessary copy in case the buffer is empty. This is important in cases were the user is explicitly tuning their chunks to match PiecewiseChunkSize().

PiperOrigin-RevId: 298366044

--
89324441d1c0c697c90ba7d8fc63639805fcaa9d by Abseil Team <absl-team@google.com>:

Internal change

PiperOrigin-RevId: 298219363
GitOrigin-RevId: a3e58c1870a9626039f4d178d2d599319bd9f8a8
Change-Id: I28dffc684b6fd0292b94807b88ec6664d5d0e183
parent 06f0e767
...@@ -53,7 +53,7 @@ TEST(StreamTest, Works) { ...@@ -53,7 +53,7 @@ TEST(StreamTest, Works) {
} }
static_assert( static_assert(
absl::flags_internal::IsAtomicFlagTypeTrait<absl::LogSeverity>::value, absl::flags_internal::FlagUseOneWordStorage<absl::LogSeverity>::value,
"Flags of type absl::LogSeverity ought to be lock-free."); "Flags of type absl::LogSeverity ought to be lock-free.");
using ParseFlagFromOutOfRangeIntegerTest = TestWithParam<int64_t>; using ParseFlagFromOutOfRangeIntegerTest = TestWithParam<int64_t>;
......
...@@ -538,19 +538,19 @@ struct BigType { ...@@ -538,19 +538,19 @@ struct BigType {
BigType() : BigType(0) {} BigType() : BigType(0) {}
explicit BigType(int x) { std::iota(values.begin(), values.end(), x); } explicit BigType(int x) { std::iota(values.begin(), values.end(), x); }
void Copy(const BigType& x) { void Copy(const BigType& other) {
for (int i = 0; i < Size && i < Copies; ++i) values[i] = x.values[i]; for (int i = 0; i < Size && i < Copies; ++i) values[i] = other.values[i];
// If Copies > Size, do extra copies. // If Copies > Size, do extra copies.
for (int i = Size, idx = 0; i < Copies; ++i) { for (int i = Size, idx = 0; i < Copies; ++i) {
int64_t tmp = x.values[idx]; int64_t tmp = other.values[idx];
benchmark::DoNotOptimize(tmp); benchmark::DoNotOptimize(tmp);
idx = idx + 1 == Size ? 0 : idx + 1; idx = idx + 1 == Size ? 0 : idx + 1;
} }
} }
BigType(const BigType& x) { Copy(x); } BigType(const BigType& other) { Copy(other); }
BigType& operator=(const BigType& x) { BigType& operator=(const BigType& other) {
Copy(x); Copy(other);
return *this; return *this;
} }
...@@ -641,14 +641,14 @@ struct BigTypePtr { ...@@ -641,14 +641,14 @@ struct BigTypePtr {
explicit BigTypePtr(int x) { explicit BigTypePtr(int x) {
ptr = absl::make_unique<BigType<Size, Size>>(x); ptr = absl::make_unique<BigType<Size, Size>>(x);
} }
BigTypePtr(const BigTypePtr& x) { BigTypePtr(const BigTypePtr& other) {
ptr = absl::make_unique<BigType<Size, Size>>(*x.ptr); ptr = absl::make_unique<BigType<Size, Size>>(*other.ptr);
} }
BigTypePtr(BigTypePtr&& x) noexcept = default; BigTypePtr(BigTypePtr&& other) noexcept = default;
BigTypePtr& operator=(const BigTypePtr& x) { BigTypePtr& operator=(const BigTypePtr& other) {
ptr = absl::make_unique<BigType<Size, Size>>(*x.ptr); ptr = absl::make_unique<BigType<Size, Size>>(*other.ptr);
} }
BigTypePtr& operator=(BigTypePtr&& x) noexcept = default; BigTypePtr& operator=(BigTypePtr&& other) noexcept = default;
bool operator<(const BigTypePtr& other) const { return *ptr < *other.ptr; } bool operator<(const BigTypePtr& other) const { return *ptr < *other.ptr; }
bool operator==(const BigTypePtr& other) const { return *ptr == *other.ptr; } bool operator==(const BigTypePtr& other) const { return *ptr == *other.ptr; }
......
...@@ -318,7 +318,7 @@ class btree_map ...@@ -318,7 +318,7 @@ class btree_map
// Extracts the element at the indicated position and returns a node handle // Extracts the element at the indicated position and returns a node handle
// owning that extracted data. // owning that extracted data.
// //
// template <typename K> node_type extract(const K& x): // template <typename K> node_type extract(const K& k):
// //
// Extracts the element with the key matching the passed key value and // Extracts the element with the key matching the passed key value and
// returns a node handle owning that extracted data. If the `btree_map` // returns a node handle owning that extracted data. If the `btree_map`
...@@ -645,7 +645,7 @@ class btree_multimap ...@@ -645,7 +645,7 @@ class btree_multimap
// Extracts the element at the indicated position and returns a node handle // Extracts the element at the indicated position and returns a node handle
// owning that extracted data. // owning that extracted data.
// //
// template <typename K> node_type extract(const K& x): // template <typename K> node_type extract(const K& k):
// //
// Extracts the element with the key matching the passed key value and // Extracts the element with the key matching the passed key value and
// returns a node handle owning that extracted data. If the `btree_multimap` // returns a node handle owning that extracted data. If the `btree_multimap`
......
...@@ -263,7 +263,7 @@ class btree_set ...@@ -263,7 +263,7 @@ class btree_set
// Extracts the element at the indicated position and returns a node handle // Extracts the element at the indicated position and returns a node handle
// owning that extracted data. // owning that extracted data.
// //
// template <typename K> node_type extract(const K& x): // template <typename K> node_type extract(const K& k):
// //
// Extracts the element with the key matching the passed key value and // Extracts the element with the key matching the passed key value and
// returns a node handle owning that extracted data. If the `btree_set` // returns a node handle owning that extracted data. If the `btree_set`
...@@ -567,7 +567,7 @@ class btree_multiset ...@@ -567,7 +567,7 @@ class btree_multiset
// Extracts the element at the indicated position and returns a node handle // Extracts the element at the indicated position and returns a node handle
// owning that extracted data. // owning that extracted data.
// //
// template <typename K> node_type extract(const K& x): // template <typename K> node_type extract(const K& k):
// //
// Extracts the element with the key matching the passed key value and // Extracts the element with the key matching the passed key value and
// returns a node handle owning that extracted data. If the `btree_multiset` // returns a node handle owning that extracted data. If the `btree_multiset`
......
...@@ -89,8 +89,8 @@ class base_checker { ...@@ -89,8 +89,8 @@ class base_checker {
public: public:
base_checker() : const_tree_(tree_) {} base_checker() : const_tree_(tree_) {}
base_checker(const base_checker &x) base_checker(const base_checker &other)
: tree_(x.tree_), const_tree_(tree_), checker_(x.checker_) {} : tree_(other.tree_), const_tree_(tree_), checker_(other.checker_) {}
template <typename InputIterator> template <typename InputIterator>
base_checker(InputIterator b, InputIterator e) base_checker(InputIterator b, InputIterator e)
: tree_(b, e), const_tree_(tree_), checker_(b, e) {} : tree_(b, e), const_tree_(tree_), checker_(b, e) {}
...@@ -124,11 +124,11 @@ class base_checker { ...@@ -124,11 +124,11 @@ class base_checker {
} }
return tree_iter; return tree_iter;
} }
void value_check(const value_type &x) { void value_check(const value_type &v) {
typename KeyOfValue<typename TreeType::key_type, typename KeyOfValue<typename TreeType::key_type,
typename TreeType::value_type>::type key_of_value; typename TreeType::value_type>::type key_of_value;
const key_type &key = key_of_value(x); const key_type &key = key_of_value(v);
CheckPairEquals(*find(key), x); CheckPairEquals(*find(key), v);
lower_bound(key); lower_bound(key);
upper_bound(key); upper_bound(key);
equal_range(key); equal_range(key);
...@@ -187,9 +187,9 @@ class base_checker { ...@@ -187,9 +187,9 @@ class base_checker {
return res; return res;
} }
base_checker &operator=(const base_checker &x) { base_checker &operator=(const base_checker &other) {
tree_ = x.tree_; tree_ = other.tree_;
checker_ = x.checker_; checker_ = other.checker_;
return *this; return *this;
} }
...@@ -250,9 +250,9 @@ class base_checker { ...@@ -250,9 +250,9 @@ class base_checker {
tree_.clear(); tree_.clear();
checker_.clear(); checker_.clear();
} }
void swap(base_checker &x) { void swap(base_checker &other) {
tree_.swap(x.tree_); tree_.swap(other.tree_);
checker_.swap(x.checker_); checker_.swap(other.checker_);
} }
void verify() const { void verify() const {
...@@ -323,28 +323,28 @@ class unique_checker : public base_checker<TreeType, CheckerType> { ...@@ -323,28 +323,28 @@ class unique_checker : public base_checker<TreeType, CheckerType> {
public: public:
unique_checker() : super_type() {} unique_checker() : super_type() {}
unique_checker(const unique_checker &x) : super_type(x) {} unique_checker(const unique_checker &other) : super_type(other) {}
template <class InputIterator> template <class InputIterator>
unique_checker(InputIterator b, InputIterator e) : super_type(b, e) {} unique_checker(InputIterator b, InputIterator e) : super_type(b, e) {}
unique_checker &operator=(const unique_checker &) = default; unique_checker &operator=(const unique_checker &) = default;
// Insertion routines. // Insertion routines.
std::pair<iterator, bool> insert(const value_type &x) { std::pair<iterator, bool> insert(const value_type &v) {
int size = this->tree_.size(); int size = this->tree_.size();
std::pair<typename CheckerType::iterator, bool> checker_res = std::pair<typename CheckerType::iterator, bool> checker_res =
this->checker_.insert(x); this->checker_.insert(v);
std::pair<iterator, bool> tree_res = this->tree_.insert(x); std::pair<iterator, bool> tree_res = this->tree_.insert(v);
CheckPairEquals(*tree_res.first, *checker_res.first); CheckPairEquals(*tree_res.first, *checker_res.first);
EXPECT_EQ(tree_res.second, checker_res.second); EXPECT_EQ(tree_res.second, checker_res.second);
EXPECT_EQ(this->tree_.size(), this->checker_.size()); EXPECT_EQ(this->tree_.size(), this->checker_.size());
EXPECT_EQ(this->tree_.size(), size + tree_res.second); EXPECT_EQ(this->tree_.size(), size + tree_res.second);
return tree_res; return tree_res;
} }
iterator insert(iterator position, const value_type &x) { iterator insert(iterator position, const value_type &v) {
int size = this->tree_.size(); int size = this->tree_.size();
std::pair<typename CheckerType::iterator, bool> checker_res = std::pair<typename CheckerType::iterator, bool> checker_res =
this->checker_.insert(x); this->checker_.insert(v);
iterator tree_res = this->tree_.insert(position, x); iterator tree_res = this->tree_.insert(position, v);
CheckPairEquals(*tree_res, *checker_res.first); CheckPairEquals(*tree_res, *checker_res.first);
EXPECT_EQ(this->tree_.size(), this->checker_.size()); EXPECT_EQ(this->tree_.size(), this->checker_.size());
EXPECT_EQ(this->tree_.size(), size + checker_res.second); EXPECT_EQ(this->tree_.size(), size + checker_res.second);
...@@ -371,25 +371,25 @@ class multi_checker : public base_checker<TreeType, CheckerType> { ...@@ -371,25 +371,25 @@ class multi_checker : public base_checker<TreeType, CheckerType> {
public: public:
multi_checker() : super_type() {} multi_checker() : super_type() {}
multi_checker(const multi_checker &x) : super_type(x) {} multi_checker(const multi_checker &other) : super_type(other) {}
template <class InputIterator> template <class InputIterator>
multi_checker(InputIterator b, InputIterator e) : super_type(b, e) {} multi_checker(InputIterator b, InputIterator e) : super_type(b, e) {}
multi_checker &operator=(const multi_checker &) = default; multi_checker &operator=(const multi_checker &) = default;
// Insertion routines. // Insertion routines.
iterator insert(const value_type &x) { iterator insert(const value_type &v) {
int size = this->tree_.size(); int size = this->tree_.size();
auto checker_res = this->checker_.insert(x); auto checker_res = this->checker_.insert(v);
iterator tree_res = this->tree_.insert(x); iterator tree_res = this->tree_.insert(v);
CheckPairEquals(*tree_res, *checker_res); CheckPairEquals(*tree_res, *checker_res);
EXPECT_EQ(this->tree_.size(), this->checker_.size()); EXPECT_EQ(this->tree_.size(), this->checker_.size());
EXPECT_EQ(this->tree_.size(), size + 1); EXPECT_EQ(this->tree_.size(), size + 1);
return tree_res; return tree_res;
} }
iterator insert(iterator position, const value_type &x) { iterator insert(iterator position, const value_type &v) {
int size = this->tree_.size(); int size = this->tree_.size();
auto checker_res = this->checker_.insert(x); auto checker_res = this->checker_.insert(v);
iterator tree_res = this->tree_.insert(position, x); iterator tree_res = this->tree_.insert(position, v);
CheckPairEquals(*tree_res, *checker_res); CheckPairEquals(*tree_res, *checker_res);
EXPECT_EQ(this->tree_.size(), this->checker_.size()); EXPECT_EQ(this->tree_.size(), this->checker_.size());
EXPECT_EQ(this->tree_.size(), size + 1); EXPECT_EQ(this->tree_.size(), size + 1);
......
...@@ -68,10 +68,10 @@ class btree_container { ...@@ -68,10 +68,10 @@ class btree_container {
explicit btree_container(const key_compare &comp, explicit btree_container(const key_compare &comp,
const allocator_type &alloc = allocator_type()) const allocator_type &alloc = allocator_type())
: tree_(comp, alloc) {} : tree_(comp, alloc) {}
btree_container(const btree_container &x) = default; btree_container(const btree_container &other) = default;
btree_container(btree_container &&x) noexcept = default; btree_container(btree_container &&other) noexcept = default;
btree_container &operator=(const btree_container &x) = default; btree_container &operator=(const btree_container &other) = default;
btree_container &operator=(btree_container &&x) noexcept( btree_container &operator=(btree_container &&other) noexcept(
std::is_nothrow_move_assignable<Tree>::value) = default; std::is_nothrow_move_assignable<Tree>::value) = default;
// Iterator routines. // Iterator routines.
...@@ -154,7 +154,7 @@ class btree_container { ...@@ -154,7 +154,7 @@ class btree_container {
public: public:
// Utility routines. // Utility routines.
void clear() { tree_.clear(); } void clear() { tree_.clear(); }
void swap(btree_container &x) { tree_.swap(x.tree_); } void swap(btree_container &other) { tree_.swap(other.tree_); }
void verify() const { tree_.verify(); } void verify() const { tree_.verify(); }
// Size routines. // Size routines.
...@@ -257,26 +257,26 @@ class btree_set_container : public btree_container<Tree> { ...@@ -257,26 +257,26 @@ class btree_set_container : public btree_container<Tree> {
} }
// Insertion routines. // Insertion routines.
std::pair<iterator, bool> insert(const value_type &x) { std::pair<iterator, bool> insert(const value_type &v) {
return this->tree_.insert_unique(params_type::key(x), x); return this->tree_.insert_unique(params_type::key(v), v);
} }
std::pair<iterator, bool> insert(value_type &&x) { std::pair<iterator, bool> insert(value_type &&v) {
return this->tree_.insert_unique(params_type::key(x), std::move(x)); return this->tree_.insert_unique(params_type::key(v), std::move(v));
} }
template <typename... Args> template <typename... Args>
std::pair<iterator, bool> emplace(Args &&... args) { std::pair<iterator, bool> emplace(Args &&... args) {
init_type v(std::forward<Args>(args)...); init_type v(std::forward<Args>(args)...);
return this->tree_.insert_unique(params_type::key(v), std::move(v)); return this->tree_.insert_unique(params_type::key(v), std::move(v));
} }
iterator insert(const_iterator position, const value_type &x) { iterator insert(const_iterator position, const value_type &v) {
return this->tree_ return this->tree_
.insert_hint_unique(iterator(position), params_type::key(x), x) .insert_hint_unique(iterator(position), params_type::key(v), v)
.first; .first;
} }
iterator insert(const_iterator position, value_type &&x) { iterator insert(const_iterator position, value_type &&v) {
return this->tree_ return this->tree_
.insert_hint_unique(iterator(position), params_type::key(x), .insert_hint_unique(iterator(position), params_type::key(v),
std::move(x)) std::move(v))
.first; .first;
} }
template <typename... Args> template <typename... Args>
...@@ -562,15 +562,15 @@ class btree_multiset_container : public btree_container<Tree> { ...@@ -562,15 +562,15 @@ class btree_multiset_container : public btree_container<Tree> {
} }
// Insertion routines. // Insertion routines.
iterator insert(const value_type &x) { return this->tree_.insert_multi(x); } iterator insert(const value_type &v) { return this->tree_.insert_multi(v); }
iterator insert(value_type &&x) { iterator insert(value_type &&v) {
return this->tree_.insert_multi(std::move(x)); return this->tree_.insert_multi(std::move(v));
} }
iterator insert(const_iterator position, const value_type &x) { iterator insert(const_iterator position, const value_type &v) {
return this->tree_.insert_hint_multi(iterator(position), x); return this->tree_.insert_hint_multi(iterator(position), v);
} }
iterator insert(const_iterator position, value_type &&x) { iterator insert(const_iterator position, value_type &&v) {
return this->tree_.insert_hint_multi(iterator(position), std::move(x)); return this->tree_.insert_hint_multi(iterator(position), std::move(v));
} }
template <typename InputIterator> template <typename InputIterator>
void insert(InputIterator b, InputIterator e) { void insert(InputIterator b, InputIterator e) {
......
...@@ -56,8 +56,8 @@ static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn = ...@@ -56,8 +56,8 @@ static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn =
// Load the function we need at static init time, where we don't have // Load the function we need at static init time, where we don't have
// to worry about someone else holding the loader's lock. // to worry about someone else holding the loader's lock.
static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn = static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn =
(RtlCaptureStackBackTrace_Function*) (RtlCaptureStackBackTrace_Function*)GetProcAddress(
GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace"); GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace");
#endif // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP #endif // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT> template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
......
...@@ -45,6 +45,7 @@ cc_library( ...@@ -45,6 +45,7 @@ cc_library(
"//absl/base:config", "//absl/base:config",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/memory", "//absl/memory",
"//absl/meta:type_traits",
"//absl/strings", "//absl/strings",
"//absl/synchronization", "//absl/synchronization",
], ],
...@@ -130,6 +131,9 @@ cc_library( ...@@ -130,6 +131,9 @@ cc_library(
cc_library( cc_library(
name = "handle", name = "handle",
srcs = [
"internal/commandlineflag.cc",
],
hdrs = [ hdrs = [
"internal/commandlineflag.h", "internal/commandlineflag.h",
], ],
......
...@@ -33,6 +33,7 @@ absl_cc_library( ...@@ -33,6 +33,7 @@ absl_cc_library(
absl::flags_handle absl::flags_handle
absl::flags_registry absl::flags_registry
absl::synchronization absl::synchronization
absl::meta
PUBLIC PUBLIC
) )
...@@ -117,6 +118,8 @@ absl_cc_library( ...@@ -117,6 +118,8 @@ absl_cc_library(
absl_cc_library( absl_cc_library(
NAME NAME
flags_handle flags_handle
SRCS
"internal/commandlineflag.cc"
HDRS HDRS
"internal/commandlineflag.h" "internal/commandlineflag.h"
COPTS COPTS
......
...@@ -148,7 +148,6 @@ class Flag { ...@@ -148,7 +148,6 @@ class Flag {
return GetImpl()->template IsOfType<U>(); return GetImpl()->template IsOfType<U>();
} }
T Get() const { return GetImpl()->Get(); } T Get() const { return GetImpl()->Get(); }
bool AtomicGet(T* v) const { return GetImpl()->AtomicGet(v); }
void Set(const T& v) { GetImpl()->Set(v); } void Set(const T& v) { GetImpl()->Set(v); }
void SetCallback(const flags_internal::FlagCallbackFunc mutation_callback) { void SetCallback(const flags_internal::FlagCallbackFunc mutation_callback) {
GetImpl()->SetCallback(mutation_callback); GetImpl()->SetCallback(mutation_callback);
......
...@@ -109,3 +109,11 @@ namespace { ...@@ -109,3 +109,11 @@ namespace {
BENCHMARKED_TYPES(BM_GetFlag) BENCHMARKED_TYPES(BM_GetFlag)
} // namespace } // namespace
#define InvokeGetFlag(T) \
T AbslInvokeGetFlag##T() { return absl::GetFlag(FLAGS_##T##_flag); } \
int odr##T = (benchmark::DoNotOptimize(AbslInvokeGetFlag##T), 1);
BENCHMARKED_TYPES(InvokeGetFlag)
// To veiw disassembly use: gdb ${BINARY} -batch -ex "disassemble /s $FUNC"
...@@ -49,28 +49,6 @@ void* TestMakeDflt() { ...@@ -49,28 +49,6 @@ void* TestMakeDflt() {
} }
void TestCallback() {} void TestCallback() {}
template <typename T>
bool TestConstructionFor() {
constexpr flags::FlagHelpArg help_arg{flags::FlagHelpMsg("literal help"),
flags::FlagHelpKind::kLiteral};
constexpr flags::Flag<T> f1("f1", "file", help_arg, &TestMakeDflt<T>);
EXPECT_EQ(f1.Name(), "f1");
EXPECT_EQ(f1.Help(), "literal help");
EXPECT_EQ(f1.Filename(), "file");
ABSL_CONST_INIT static flags::Flag<T> f2(
"f2", "file",
{flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc},
&TestMakeDflt<T>);
flags::FlagRegistrar<T, false>(&f2).OnUpdate(TestCallback);
EXPECT_EQ(f2.Name(), "f2");
EXPECT_EQ(f2.Help(), "dynamic help");
EXPECT_EQ(f2.Filename(), "file");
return true;
}
struct UDT { struct UDT {
UDT() = default; UDT() = default;
UDT(const UDT&) = default; UDT(const UDT&) = default;
...@@ -98,19 +76,103 @@ class FlagTest : public testing::Test { ...@@ -98,19 +76,103 @@ class FlagTest : public testing::Test {
} }
}; };
struct S1 {
S1() = default;
S1(const S1&) = default;
int32_t f1;
int64_t f2;
};
struct S2 {
S2() = default;
S2(const S2&) = default;
int64_t f1;
double f2;
};
TEST_F(FlagTest, Traits) {
EXPECT_EQ(flags::FlagValue::Kind<int>(),
flags::FlagValueStorageKind::kOneWordAtomic);
EXPECT_EQ(flags::FlagValue::Kind<bool>(),
flags::FlagValueStorageKind::kOneWordAtomic);
EXPECT_EQ(flags::FlagValue::Kind<double>(),
flags::FlagValueStorageKind::kOneWordAtomic);
EXPECT_EQ(flags::FlagValue::Kind<int64_t>(),
flags::FlagValueStorageKind::kOneWordAtomic);
#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
EXPECT_EQ(flags::FlagValue::Kind<S1>(),
flags::FlagValueStorageKind::kTwoWordsAtomic);
EXPECT_EQ(flags::FlagValue::Kind<S2>(),
flags::FlagValueStorageKind::kTwoWordsAtomic);
#else
EXPECT_EQ(flags::FlagValue::Kind<S1>(),
flags::FlagValueStorageKind::kHeapAllocated);
EXPECT_EQ(flags::FlagValue::Kind<S2>(),
flags::FlagValueStorageKind::kHeapAllocated);
#endif
EXPECT_EQ(flags::FlagValue::Kind<std::string>(),
flags::FlagValueStorageKind::kHeapAllocated);
EXPECT_EQ(flags::FlagValue::Kind<std::vector<std::string>>(),
flags::FlagValueStorageKind::kHeapAllocated);
}
// --------------------------------------------------------------------
constexpr flags::FlagHelpArg help_arg{flags::FlagHelpMsg("literal help"),
flags::FlagHelpKind::kLiteral};
using String = std::string;
#define DEFINE_CONSTRUCTED_FLAG(T) \
constexpr flags::Flag<T> f1##T("f1", "file", help_arg, &TestMakeDflt<T>); \
ABSL_CONST_INIT flags::Flag<T> f2##T( \
"f2", "file", \
{flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc}, \
&TestMakeDflt<T>)
#define TEST_CONSTRUCTED_FLAG(T) TestConstructionFor(f1##T, &f2##T);
DEFINE_CONSTRUCTED_FLAG(bool);
DEFINE_CONSTRUCTED_FLAG(int16_t);
DEFINE_CONSTRUCTED_FLAG(uint16_t);
DEFINE_CONSTRUCTED_FLAG(int32_t);
DEFINE_CONSTRUCTED_FLAG(uint32_t);
DEFINE_CONSTRUCTED_FLAG(int64_t);
DEFINE_CONSTRUCTED_FLAG(uint64_t);
DEFINE_CONSTRUCTED_FLAG(float);
DEFINE_CONSTRUCTED_FLAG(double);
DEFINE_CONSTRUCTED_FLAG(String);
DEFINE_CONSTRUCTED_FLAG(UDT);
template <typename T>
bool TestConstructionFor(const flags::Flag<T>& f1, flags::Flag<T>* f2) {
EXPECT_EQ(f1.Name(), "f1");
EXPECT_EQ(f1.Help(), "literal help");
EXPECT_EQ(f1.Filename(), "file");
flags::FlagRegistrar<T, false>(f2).OnUpdate(TestCallback);
EXPECT_EQ(f2->Name(), "f2");
EXPECT_EQ(f2->Help(), "dynamic help");
EXPECT_EQ(f2->Filename(), "file");
return true;
}
TEST_F(FlagTest, TestConstruction) { TEST_F(FlagTest, TestConstruction) {
TestConstructionFor<bool>(); TEST_CONSTRUCTED_FLAG(bool);
TestConstructionFor<int16_t>(); TEST_CONSTRUCTED_FLAG(int16_t);
TestConstructionFor<uint16_t>(); TEST_CONSTRUCTED_FLAG(uint16_t);
TestConstructionFor<int32_t>(); TEST_CONSTRUCTED_FLAG(int32_t);
TestConstructionFor<uint32_t>(); TEST_CONSTRUCTED_FLAG(uint32_t);
TestConstructionFor<int64_t>(); TEST_CONSTRUCTED_FLAG(int64_t);
TestConstructionFor<uint64_t>(); TEST_CONSTRUCTED_FLAG(uint64_t);
TestConstructionFor<double>(); TEST_CONSTRUCTED_FLAG(float);
TestConstructionFor<float>(); TEST_CONSTRUCTED_FLAG(double);
TestConstructionFor<std::string>(); TEST_CONSTRUCTED_FLAG(String);
TEST_CONSTRUCTED_FLAG(UDT);
TestConstructionFor<UDT>();
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -391,17 +453,18 @@ TEST_F(FlagTest, TestCustomUDT) { ...@@ -391,17 +453,18 @@ TEST_F(FlagTest, TestCustomUDT) {
using FlagDeathTest = FlagTest; using FlagDeathTest = FlagTest;
TEST_F(FlagDeathTest, TestTypeMismatchValidations) { TEST_F(FlagDeathTest, TestTypeMismatchValidations) {
EXPECT_DEBUG_DEATH( #if !defined(NDEBUG)
static_cast<void>(absl::GetFlag(FLAGS_mistyped_int_flag)), EXPECT_DEATH(static_cast<void>(absl::GetFlag(FLAGS_mistyped_int_flag)),
"Flag 'mistyped_int_flag' is defined as one type and declared " "Flag 'mistyped_int_flag' is defined as one type and declared "
"as another"); "as another");
EXPECT_DEATH(absl::SetFlag(&FLAGS_mistyped_int_flag, 1),
"Flag 'mistyped_int_flag' is defined as one type and declared "
"as another");
EXPECT_DEATH(static_cast<void>(absl::GetFlag(FLAGS_mistyped_string_flag)), EXPECT_DEATH(static_cast<void>(absl::GetFlag(FLAGS_mistyped_string_flag)),
"Flag 'mistyped_string_flag' is defined as one type and " "Flag 'mistyped_string_flag' is defined as one type and "
"declared as another"); "declared as another");
#endif
EXPECT_DEATH(absl::SetFlag(&FLAGS_mistyped_int_flag, 1),
"Flag 'mistyped_int_flag' is defined as one type and declared "
"as another");
EXPECT_DEATH( EXPECT_DEATH(
absl::SetFlag(&FLAGS_mistyped_string_flag, std::vector<std::string>{}), absl::SetFlag(&FLAGS_mistyped_string_flag, std::vector<std::string>{}),
"Flag 'mistyped_string_flag' is defined as one type and declared as " "Flag 'mistyped_string_flag' is defined as one type and declared as "
......
//
// Copyright 2020 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/flags/internal/commandlineflag.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace flags_internal {
FlagStateInterface::~FlagStateInterface() {}
bool CommandLineFlag::IsRetired() const { return false; }
bool CommandLineFlag::IsAbseilFlag() const { return true; }
} // namespace flags_internal
ABSL_NAMESPACE_END
} // namespace absl
...@@ -77,7 +77,7 @@ enum ValueSource { ...@@ -77,7 +77,7 @@ enum ValueSource {
// of a flag produced this flag state from method CommandLineFlag::SaveState(). // of a flag produced this flag state from method CommandLineFlag::SaveState().
class FlagStateInterface { class FlagStateInterface {
public: public:
virtual ~FlagStateInterface() {} virtual ~FlagStateInterface();
// Restores the flag originated this object to the saved state. // Restores the flag originated this object to the saved state.
virtual void Restore() const = 0; virtual void Restore() const = 0;
...@@ -146,9 +146,9 @@ class CommandLineFlag { ...@@ -146,9 +146,9 @@ class CommandLineFlag {
// Returns help message associated with this flag. // Returns help message associated with this flag.
virtual std::string Help() const = 0; virtual std::string Help() const = 0;
// Returns true iff this object corresponds to retired flag. // Returns true iff this object corresponds to retired flag.
virtual bool IsRetired() const { return false; } virtual bool IsRetired() const;
// Returns true iff this is a handle to an Abseil Flag. // Returns true iff this is a handle to an Abseil Flag.
virtual bool IsAbseilFlag() const { return true; } virtual bool IsAbseilFlag() const;
// Returns id of the flag's value type. // Returns id of the flag's value type.
virtual FlagStaticTypeId TypeId() const = 0; virtual FlagStaticTypeId TypeId() const = 0;
virtual bool IsModified() const = 0; virtual bool IsModified() const = 0;
......
...@@ -77,19 +77,33 @@ class MutexRelock { ...@@ -77,19 +77,33 @@ class MutexRelock {
void FlagImpl::Init() { void FlagImpl::Init() {
new (&data_guard_) absl::Mutex; new (&data_guard_) absl::Mutex;
absl::MutexLock lock(reinterpret_cast<absl::Mutex*>(&data_guard_)); // At this point the default_value_ always points to gen_func.
std::unique_ptr<void, DynValueDeleter> init_value(
value_.dynamic = MakeInitValue().release(); (*default_value_.gen_func)(), DynValueDeleter{op_});
StoreAtomic(); switch (ValueStorageKind()) {
case FlagValueStorageKind::kHeapAllocated:
value_.dynamic = init_value.release();
break;
case FlagValueStorageKind::kOneWordAtomic: {
int64_t atomic_value;
std::memcpy(&atomic_value, init_value.get(), Sizeof(op_));
value_.one_word_atomic.store(atomic_value, std::memory_order_release);
break;
}
case FlagValueStorageKind::kTwoWordsAtomic: {
AlignedTwoWords atomic_value{0, 0};
std::memcpy(&atomic_value, init_value.get(), Sizeof(op_));
value_.two_words_atomic.store(atomic_value, std::memory_order_release);
break;
}
}
} }
// Ensures that the lazily initialized data is initialized,
// and returns pointer to the mutex guarding flags data.
absl::Mutex* FlagImpl::DataGuard() const { absl::Mutex* FlagImpl::DataGuard() const {
absl::call_once(const_cast<FlagImpl*>(this)->init_control_, &FlagImpl::Init, absl::call_once(const_cast<FlagImpl*>(this)->init_control_, &FlagImpl::Init,
const_cast<FlagImpl*>(this)); const_cast<FlagImpl*>(this));
// data_guard_ is initialized. // data_guard_ is initialized inside Init.
return reinterpret_cast<absl::Mutex*>(&data_guard_); return reinterpret_cast<absl::Mutex*>(&data_guard_);
} }
...@@ -129,8 +143,24 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const { ...@@ -129,8 +143,24 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const {
} }
void FlagImpl::StoreValue(const void* src) { void FlagImpl::StoreValue(const void* src) {
flags_internal::Copy(op_, src, value_.dynamic); switch (ValueStorageKind()) {
StoreAtomic(); case FlagValueStorageKind::kHeapAllocated:
Copy(op_, src, value_.dynamic);
break;
case FlagValueStorageKind::kOneWordAtomic: {
int64_t one_word_val;
std::memcpy(&one_word_val, src, Sizeof(op_));
value_.one_word_atomic.store(one_word_val, std::memory_order_release);
break;
}
case FlagValueStorageKind::kTwoWordsAtomic: {
AlignedTwoWords two_words_val{0, 0};
std::memcpy(&two_words_val, src, Sizeof(op_));
value_.two_words_atomic.store(two_words_val, std::memory_order_release);
break;
}
}
modified_ = true; modified_ = true;
++counter_; ++counter_;
InvokeCallback(); InvokeCallback();
...@@ -165,9 +195,25 @@ std::string FlagImpl::DefaultValue() const { ...@@ -165,9 +195,25 @@ std::string FlagImpl::DefaultValue() const {
} }
std::string FlagImpl::CurrentValue() const { std::string FlagImpl::CurrentValue() const {
DataGuard(); // Make sure flag initialized
switch (ValueStorageKind()) {
case FlagValueStorageKind::kHeapAllocated: {
absl::MutexLock l(DataGuard()); absl::MutexLock l(DataGuard());
return flags_internal::Unparse(op_, value_.dynamic); return flags_internal::Unparse(op_, value_.dynamic);
}
case FlagValueStorageKind::kOneWordAtomic: {
const auto one_word_val =
value_.one_word_atomic.load(std::memory_order_acquire);
return flags_internal::Unparse(op_, &one_word_val);
}
case FlagValueStorageKind::kTwoWordsAtomic: {
const auto two_words_val =
value_.two_words_atomic.load(std::memory_order_acquire);
return flags_internal::Unparse(op_, &two_words_val);
}
}
return "";
} }
void FlagImpl::SetCallback(const FlagCallbackFunc mutation_callback) { void FlagImpl::SetCallback(const FlagCallbackFunc mutation_callback) {
...@@ -244,26 +290,27 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse( ...@@ -244,26 +290,27 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse(
} }
void FlagImpl::Read(void* dst) const { void FlagImpl::Read(void* dst) const {
absl::ReaderMutexLock l(DataGuard()); DataGuard(); // Make sure flag initialized
switch (ValueStorageKind()) {
case FlagValueStorageKind::kHeapAllocated: {
absl::MutexLock l(DataGuard());
flags_internal::CopyConstruct(op_, value_.dynamic, dst); flags_internal::CopyConstruct(op_, value_.dynamic, dst);
} break;
}
void FlagImpl::StoreAtomic() { case FlagValueStorageKind::kOneWordAtomic: {
size_t data_size = flags_internal::Sizeof(op_); const auto one_word_val =
value_.one_word_atomic.load(std::memory_order_acquire);
if (data_size <= sizeof(int64_t)) { std::memcpy(dst, &one_word_val, Sizeof(op_));
int64_t t = 0; break;
std::memcpy(&t, value_.dynamic, data_size); }
value_.atomics.small_atomic.store(t, std::memory_order_release); case FlagValueStorageKind::kTwoWordsAtomic: {
const auto two_words_val =
value_.two_words_atomic.load(std::memory_order_acquire);
std::memcpy(dst, &two_words_val, Sizeof(op_));
break;
} }
#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
else if (data_size <= sizeof(FlagsInternalTwoWordsType)) {
FlagsInternalTwoWordsType t{0, 0};
std::memcpy(&t, value_.dynamic, data_size);
value_.atomics.big_atomic.store(t, std::memory_order_release);
} }
#endif
} }
void FlagImpl::Write(const void* src) { void FlagImpl::Write(const void* src) {
...@@ -339,7 +386,7 @@ bool FlagImpl::SetFromString(absl::string_view value, FlagSettingMode set_mode, ...@@ -339,7 +386,7 @@ bool FlagImpl::SetFromString(absl::string_view value, FlagSettingMode set_mode,
} }
if (!modified_) { if (!modified_) {
// Need to set both default value *and* current, in this case // Need to set both default value *and* current, in this case.
StoreValue(default_value_.dynamic_value); StoreValue(default_value_.dynamic_value);
modified_ = false; modified_ = false;
} }
......
...@@ -955,12 +955,15 @@ H PiecewiseCombiner::add_buffer(H state, const unsigned char* data, ...@@ -955,12 +955,15 @@ H PiecewiseCombiner::add_buffer(H state, const unsigned char* data,
return state; return state;
} }
// Complete the buffer and hash it // If the buffer is partially filled we need to complete the buffer
// and hash it.
if (position_ != 0) {
const size_t bytes_needed = PiecewiseChunkSize() - position_; const size_t bytes_needed = PiecewiseChunkSize() - position_;
memcpy(buf_ + position_, data, bytes_needed); memcpy(buf_ + position_, data, bytes_needed);
state = H::combine_contiguous(std::move(state), buf_, PiecewiseChunkSize()); state = H::combine_contiguous(std::move(state), buf_, PiecewiseChunkSize());
data += bytes_needed; data += bytes_needed;
size -= bytes_needed; size -= bytes_needed;
}
// Hash whatever chunks we can without copying // Hash whatever chunks we can without copying
while (size >= PiecewiseChunkSize()) { while (size >= PiecewiseChunkSize()) {
......
...@@ -705,6 +705,7 @@ cc_test( ...@@ -705,6 +705,7 @@ cc_test(
cc_test( cc_test(
name = "randen_benchmarks", name = "randen_benchmarks",
size = "medium", size = "medium",
timeout = "long",
srcs = ["randen_benchmarks.cc"], srcs = ["randen_benchmarks.cc"],
copts = ABSL_TEST_COPTS + ABSL_RANDOM_RANDEN_COPTS, copts = ABSL_TEST_COPTS + ABSL_RANDOM_RANDEN_COPTS,
flaky = 1, flaky = 1,
......
...@@ -147,7 +147,15 @@ void Status::SetPayload(absl::string_view type_url, absl::Cord payload) { ...@@ -147,7 +147,15 @@ void Status::SetPayload(absl::string_view type_url, absl::Cord payload) {
bool Status::ErasePayload(absl::string_view type_url) { bool Status::ErasePayload(absl::string_view type_url) {
int index = status_internal::FindPayloadIndexByUrl(GetPayloads(), type_url); int index = status_internal::FindPayloadIndexByUrl(GetPayloads(), type_url);
if (index != -1) { if (index != -1) {
PrepareToModify();
GetPayloads()->erase(GetPayloads()->begin() + index); GetPayloads()->erase(GetPayloads()->begin() + index);
if (GetPayloads()->empty() && message().empty()) {
// Special case: If this can be represented inlined, it MUST be
// inlined (EqualsSlow depends on this behavior).
StatusCode c = static_cast<StatusCode>(raw_code());
Unref(rep_);
rep_ = CodeToInlinedRep(c);
}
return true; return true;
} }
......
...@@ -204,6 +204,25 @@ TEST(Status, TestComparePayloads) { ...@@ -204,6 +204,25 @@ TEST(Status, TestComparePayloads) {
EXPECT_EQ(bad_status1, bad_status2); EXPECT_EQ(bad_status1, bad_status2);
} }
TEST(Status, TestComparePayloadsAfterErase) {
absl::Status payload_status(absl::StatusCode::kInternal, "");
payload_status.SetPayload(kUrl1, absl::Cord(kPayload1));
payload_status.SetPayload(kUrl2, absl::Cord(kPayload2));
absl::Status empty_status(absl::StatusCode::kInternal, "");
// Different payloads, not equal
EXPECT_NE(payload_status, empty_status);
EXPECT_TRUE(payload_status.ErasePayload(kUrl1));
// Still Different payloads, still not equal.
EXPECT_NE(payload_status, empty_status);
EXPECT_TRUE(payload_status.ErasePayload(kUrl2));
// Both empty payloads, should be equal
EXPECT_EQ(payload_status, empty_status);
}
PayloadsVec AllVisitedPayloads(const absl::Status& s) { PayloadsVec AllVisitedPayloads(const absl::Status& s) {
PayloadsVec result; PayloadsVec result;
...@@ -261,6 +280,36 @@ TEST(Status, ToString) { ...@@ -261,6 +280,36 @@ TEST(Status, ToString) {
HasSubstr("[bar='\\xff']"))); HasSubstr("[bar='\\xff']")));
} }
absl::Status EraseAndReturn(const absl::Status& base) {
absl::Status copy = base;
EXPECT_TRUE(copy.ErasePayload(kUrl1));
return copy;
}
TEST(Status, CopyOnWriteForErasePayload) {
{
absl::Status base(absl::StatusCode::kInvalidArgument, "fail");
base.SetPayload(kUrl1, absl::Cord(kPayload1));
EXPECT_TRUE(base.GetPayload(kUrl1).has_value());
absl::Status copy = EraseAndReturn(base);
EXPECT_TRUE(base.GetPayload(kUrl1).has_value());
EXPECT_FALSE(copy.GetPayload(kUrl1).has_value());
}
{
absl::Status base(absl::StatusCode::kInvalidArgument, "fail");
base.SetPayload(kUrl1, absl::Cord(kPayload1));
absl::Status copy = base;
EXPECT_TRUE(base.GetPayload(kUrl1).has_value());
EXPECT_TRUE(copy.GetPayload(kUrl1).has_value());
EXPECT_TRUE(base.ErasePayload(kUrl1));
EXPECT_FALSE(base.GetPayload(kUrl1).has_value());
EXPECT_TRUE(copy.GetPayload(kUrl1).has_value());
}
}
TEST(Status, CopyConstructor) { TEST(Status, CopyConstructor) {
{ {
absl::Status status; absl::Status status;
...@@ -300,6 +349,14 @@ TEST(Status, CopyAssignment) { ...@@ -300,6 +349,14 @@ TEST(Status, CopyAssignment) {
} }
} }
TEST(Status, CopyAssignmentIsNotRef) {
const absl::Status status_orig(absl::StatusCode::kInvalidArgument, "message");
absl::Status status_copy = status_orig;
EXPECT_EQ(status_orig, status_copy);
status_copy.SetPayload(kUrl1, absl::Cord(kPayload1));
EXPECT_NE(status_orig, status_copy);
}
TEST(Status, MoveConstructor) { TEST(Status, MoveConstructor) {
{ {
absl::Status status; absl::Status status;
......
...@@ -41,13 +41,13 @@ ...@@ -41,13 +41,13 @@
#include <iostream> #include <iostream>
#include <iterator> #include <iterator>
#include <string> #include <string>
#include <type_traits>
#include "absl/base/internal/endian.h" #include "absl/base/internal/endian.h"
#include "absl/base/internal/invoke.h" #include "absl/base/internal/invoke.h"
#include "absl/base/internal/per_thread_tls.h" #include "absl/base/internal/per_thread_tls.h"
#include "absl/base/macros.h" #include "absl/base/macros.h"
#include "absl/base/port.h" #include "absl/base/port.h"
#include "absl/container/inlined_vector.h"
#include "absl/functional/function_ref.h" #include "absl/functional/function_ref.h"
#include "absl/meta/type_traits.h" #include "absl/meta/type_traits.h"
#include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_internal.h"
...@@ -66,6 +66,73 @@ template <typename H> ...@@ -66,6 +66,73 @@ template <typename H>
H HashFragmentedCord(H, const Cord&); H HashFragmentedCord(H, const Cord&);
} }
namespace cord_internal {
// It's expensive to keep a tree perfectly balanced, so instead we keep trees
// approximately balanced. A tree node N of depth D(N) that contains a string
// of L(N) characters is considered balanced if L >= Fibonacci(D + 2).
// The "+ 2" is used to ensure that every leaf node contains at least one
// character. Here we presume that
// Fibonacci(0) = 0
// Fibonacci(1) = 1
// Fibonacci(2) = 1
// Fibonacci(3) = 2
// ...
//
// Fibonacci numbers are convenient because it means when two balanced trees of
// the same depth are made the children of a new node, the resulting tree is
// guaranteed to also be balanced:
//
//
// L(left) >= Fibonacci(D(left) + 2)
// L(right) >= Fibonacci(D(right) + 2)
//
// L(left) + L(right) >= Fibonacci(D(left) + 2) + Fibonacci(D(right) + 2)
// L(left) + L(right) == L(new_tree)
//
// L(new_tree) >= 2 * Fibonacci(D(child) + 2)
// D(child) == D(new_tree) - 1
//
// L(new_tree) >= 2 * Fibonacci(D(new_tree) + 1)
// 2 * Fibonacci(N) >= Fibonacci(N + 1)
//
// L(new_tree) >= Fibonacci(D(new_tree) + 2)
//
//
// The 93rd Fibonacci number is the largest Fibonacci number that can be
// represented in 64 bits, so the size of a balanced Cord of depth 92 is too big
// for an unsigned 64 bit integer to hold. Therefore we can safely assume that
// the maximum depth of a Cord is 91.
constexpr size_t MaxCordDepth() { return 91; }
// This class models fixed max size stack of CordRep pointers.
// The elements are being pushed back and popped from the back.
template <typename CordRepPtr, size_t N>
class CordTreePath {
public:
CordTreePath() {}
explicit CordTreePath(CordRepPtr root) { push_back(root); }
bool empty() const { return size_ == 0; }
size_t size() const { return size_; }
void clear() { size_ = 0; }
CordRepPtr back() { return data_[size_ - 1]; }
void pop_back() {
--size_;
assert(size_ < N);
}
void push_back(CordRepPtr elem) { data_[size_++] = elem; }
private:
CordRepPtr data_[N];
size_t size_ = 0;
};
using CordTreeMutablePath = CordTreePath<CordRep*, MaxCordDepth()>;
} // namespace cord_internal
// A Cord is a sequence of characters. // A Cord is a sequence of characters.
class Cord { class Cord {
private: private:
...@@ -114,7 +181,8 @@ class Cord { ...@@ -114,7 +181,8 @@ class Cord {
// finished with `data`. The data must remain live and unchanging until the // finished with `data`. The data must remain live and unchanging until the
// releaser is called. The requirements for the releaser are that it: // releaser is called. The requirements for the releaser are that it:
// * is move constructible, // * is move constructible,
// * supports `void operator()(absl::string_view) const`, // * supports `void operator()(absl::string_view) const` or
// `void operator()() const`,
// * does not have alignment requirement greater than what is guaranteed by // * does not have alignment requirement greater than what is guaranteed by
// ::operator new. This is dictated by alignof(std::max_align_t) before // ::operator new. This is dictated by alignof(std::max_align_t) before
// C++17 and __STDCPP_DEFAULT_NEW_ALIGNMENT__ if compiling with C++17 or // C++17 and __STDCPP_DEFAULT_NEW_ALIGNMENT__ if compiling with C++17 or
...@@ -127,8 +195,8 @@ class Cord { ...@@ -127,8 +195,8 @@ class Cord {
// FillBlock(block); // FillBlock(block);
// return absl::MakeCordFromExternal( // return absl::MakeCordFromExternal(
// block->ToStringView(), // block->ToStringView(),
// [pool, block](absl::string_view /*ignored*/) { // [pool, block](absl::string_view v) {
// pool->FreeBlock(block); // pool->FreeBlock(block, v);
// }); // });
// } // }
// //
...@@ -282,8 +350,7 @@ class Cord { ...@@ -282,8 +350,7 @@ class Cord {
absl::cord_internal::CordRep* current_leaf_ = nullptr; absl::cord_internal::CordRep* current_leaf_ = nullptr;
// The number of bytes left in the `Cord` over which we are iterating. // The number of bytes left in the `Cord` over which we are iterating.
size_t bytes_remaining_ = 0; size_t bytes_remaining_ = 0;
absl::InlinedVector<absl::cord_internal::CordRep*, 4> absl::cord_internal::CordTreeMutablePath stack_of_right_children_;
stack_of_right_children_;
}; };
// Returns an iterator to the first chunk of the `Cord`. // Returns an iterator to the first chunk of the `Cord`.
...@@ -667,6 +734,21 @@ ExternalRepReleaserPair NewExternalWithUninitializedReleaser( ...@@ -667,6 +734,21 @@ ExternalRepReleaserPair NewExternalWithUninitializedReleaser(
absl::string_view data, ExternalReleaserInvoker invoker, absl::string_view data, ExternalReleaserInvoker invoker,
size_t releaser_size); size_t releaser_size);
struct Rank1 {};
struct Rank0 : Rank1 {};
template <typename Releaser, typename = ::absl::base_internal::InvokeT<
Releaser, absl::string_view>>
void InvokeReleaser(Rank0, Releaser&& releaser, absl::string_view data) {
::absl::base_internal::Invoke(std::forward<Releaser>(releaser), data);
}
template <typename Releaser,
typename = ::absl::base_internal::InvokeT<Releaser>>
void InvokeReleaser(Rank1, Releaser&& releaser, absl::string_view) {
::absl::base_internal::Invoke(std::forward<Releaser>(releaser));
}
// Creates a new `CordRep` that owns `data` and `releaser` and returns a pointer // Creates a new `CordRep` that owns `data` and `releaser` and returns a pointer
// to it, or `nullptr` if `data` was empty. // to it, or `nullptr` if `data` was empty.
template <typename Releaser> template <typename Releaser>
...@@ -684,14 +766,14 @@ CordRep* NewExternalRep(absl::string_view data, Releaser&& releaser) { ...@@ -684,14 +766,14 @@ CordRep* NewExternalRep(absl::string_view data, Releaser&& releaser) {
using ReleaserType = absl::decay_t<Releaser>; using ReleaserType = absl::decay_t<Releaser>;
if (data.empty()) { if (data.empty()) {
// Never create empty external nodes. // Never create empty external nodes.
::absl::base_internal::Invoke( InvokeReleaser(Rank0{}, ReleaserType(std::forward<Releaser>(releaser)),
ReleaserType(std::forward<Releaser>(releaser)), data); data);
return nullptr; return nullptr;
} }
auto releaser_invoker = [](void* type_erased_releaser, absl::string_view d) { auto releaser_invoker = [](void* type_erased_releaser, absl::string_view d) {
auto* my_releaser = static_cast<ReleaserType*>(type_erased_releaser); auto* my_releaser = static_cast<ReleaserType*>(type_erased_releaser);
::absl::base_internal::Invoke(std::move(*my_releaser), d); InvokeReleaser(Rank0{}, std::move(*my_releaser), d);
my_releaser->~ReleaserType(); my_releaser->~ReleaserType();
return sizeof(Releaser); return sizeof(Releaser);
}; };
......
...@@ -1032,6 +1032,19 @@ TEST(ConstructFromExternal, MoveOnlyReleaser) { ...@@ -1032,6 +1032,19 @@ TEST(ConstructFromExternal, MoveOnlyReleaser) {
EXPECT_TRUE(invoked); EXPECT_TRUE(invoked);
} }
TEST(ConstructFromExternal, NoArgLambda) {
bool invoked = false;
(void)absl::MakeCordFromExternal("dummy", [&invoked]() { invoked = true; });
EXPECT_TRUE(invoked);
}
TEST(ConstructFromExternal, StringViewArgLambda) {
bool invoked = false;
(void)absl::MakeCordFromExternal(
"dummy", [&invoked](absl::string_view) { invoked = true; });
EXPECT_TRUE(invoked);
}
TEST(ConstructFromExternal, NonTrivialReleaserDestructor) { TEST(ConstructFromExternal, NonTrivialReleaserDestructor) {
struct Releaser { struct Releaser {
explicit Releaser(bool* destroyed) : destroyed(destroyed) {} explicit Releaser(bool* destroyed) : destroyed(destroyed) {}
...@@ -1346,6 +1359,49 @@ TEST(CordChunkIterator, Operations) { ...@@ -1346,6 +1359,49 @@ TEST(CordChunkIterator, Operations) {
VerifyChunkIterator(subcords, 128); VerifyChunkIterator(subcords, 128);
} }
TEST(CordChunkIterator, MaxLengthFullTree) {
absl::Cord cord;
size_t size = 1;
AddExternalMemory("x", &cord);
EXPECT_EQ(cord.size(), size);
for (int i = 0; i < 63; ++i) {
cord.Prepend(absl::Cord(cord));
size <<= 1;
EXPECT_EQ(cord.size(), size);
auto chunk_it = cord.chunk_begin();
EXPECT_EQ(*chunk_it, "x");
}
EXPECT_DEATH_IF_SUPPORTED(
(cord.Prepend(absl::Cord(cord)), *cord.chunk_begin()),
"Cord is too long");
}
TEST(CordChunkIterator, MaxDepth) {
// By reusing nodes, it's possible in pathological cases to build a Cord that
// exceeds both the maximum permissible length and depth. In this case, the
// violation of the maximum depth is reported.
absl::Cord left_child;
AddExternalMemory("x", &left_child);
absl::Cord root = left_child;
for (int i = 0; i < 91; ++i) {
size_t new_size = left_child.size() + root.size();
root.Prepend(left_child);
EXPECT_EQ(root.size(), new_size);
auto chunk_it = root.chunk_begin();
EXPECT_EQ(*chunk_it, "x");
std::swap(left_child, root);
}
EXPECT_DEATH_IF_SUPPORTED(root.Prepend(left_child), "Cord depth exceeds max");
}
TEST(CordCharIterator, Traits) { TEST(CordCharIterator, Traits) {
static_assert(std::is_copy_constructible<absl::Cord::CharIterator>::value, static_assert(std::is_copy_constructible<absl::Cord::CharIterator>::value,
""); "");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment