Commit 1918ad2a by Abseil Team Committed by Andy Getz

Export of internal Abseil changes

--
0bfa836596a9c787a2f0bdc283011dd1f6810c6e by Benjamin Barenblat <bbaren@google.com>:

Ignore missing CPU frequency on more architectures

Linux on MIPS, PA-RISC, RISC-V, and SystemZ doesn’t expose the nominal
CPU frequency via /sys, so don’t worry if `NominalCPUFrequency` returns
1.0 on those platforms.

Some POWER machines expose the CPU frequency; others do not. Since we
can’t predict which type of machine the tests will run on, simply
disable testing for `NominalCPUFrequency` on POWER.

PiperOrigin-RevId: 347079873

--
492b6834ed4a07cbc3abccd846f7e37d8c556ee5 by Benjamin Barenblat <bbaren@google.com>:

Use ABSL_HAVE_THREAD_LOCAL macro instead of copying code

Reduce code duplication by checking the ABSL_HAVE_THREAD_LOCAL macro
instead of copying code from base/config.h.

PiperOrigin-RevId: 347079561

--
8d656efce4da9cb032094377e58493d98427a536 by Abseil Team <absl-team@google.com>:

Rollback

PiperOrigin-RevId: 347078779

--
221bc69ec6dd7e2777ffcff6942584f979ef6382 by Abseil Team <absl-team@google.com>:

Add flag for 'shallow subcord' feature for experimental ring buffer rollout

There is a potential trade-off of CPU cost vs over-sharing cord data for subcord of large cords. This flag allows making subcords shallow for ringbuffers (with a potential larger waste of referenced source cords), which allows us to make subcord fast for this apps that do no persist (unmodified / plain copied) sub cords.

This change also introduces constants for the default settings, intended to keep the internal cord settings concistent with external flags.

PiperOrigin-RevId: 347053271

--
00a56c24293566734009f6bf2169a83fb37a35ba by Abseil Team <absl-team@google.com>:

Revert the usage of variant<> in Cord iterator and reader.

The introduction of the variant may lead to some missed compiler optimizations.

PiperOrigin-RevId: 347053041

--
c7b7b5ed7e3ab46b1e75b80f1a7de0bda26c8f70 by Chris Kennelly <ckennelly@google.com>:

Release library for integer power-of-2 functions and bit counting.

PiperOrigin-RevId: 347035065

--
5a035c0d9840b251967f9e7039fc6a4e01dd52f3 by Abseil Team <absl-team@google.com>:

Restructure Cord::ChunkIterator for future ring buffer support.

PiperOrigin-RevId: 346890054
GitOrigin-RevId: 0bfa836596a9c787a2f0bdc283011dd1f6810c6e
Change-Id: I3a58e2a44cb4c6f2116c43e2a4ccbc319d3ccecf
parent 938fd0f4
...@@ -126,8 +126,10 @@ set(ABSL_INTERNAL_DLL_FILES ...@@ -126,8 +126,10 @@ set(ABSL_INTERNAL_DLL_FILES
"hash/internal/wyhash.cc" "hash/internal/wyhash.cc"
"memory/memory.h" "memory/memory.h"
"meta/type_traits.h" "meta/type_traits.h"
"numeric/bits.h"
"numeric/int128.cc" "numeric/int128.cc"
"numeric/int128.h" "numeric/int128.h"
"numeric/internal/bits.h"
"random/bernoulli_distribution.h" "random/bernoulli_distribution.h"
"random/beta_distribution.h" "random/beta_distribution.h"
"random/bit_gen_ref.h" "random/bit_gen_ref.h"
......
...@@ -520,7 +520,7 @@ absl_cc_test( ...@@ -520,7 +520,7 @@ absl_cc_test(
absl_cc_library( absl_cc_library(
NAME NAME
bits internal_bits
HDRS HDRS
"internal/bits.h" "internal/bits.h"
COPTS COPTS
...@@ -532,13 +532,13 @@ absl_cc_library( ...@@ -532,13 +532,13 @@ absl_cc_library(
absl_cc_test( absl_cc_test(
NAME NAME
bits_test internal_bits_test
SRCS SRCS
"internal/bits_test.cc" "internal/bits_test.cc"
COPTS COPTS
${ABSL_TEST_COPTS} ${ABSL_TEST_COPTS}
DEPS DEPS
absl::bits absl::internal_bits
gtest_main gtest_main
) )
......
...@@ -37,17 +37,28 @@ TEST(SysinfoTest, NumCPUs) { ...@@ -37,17 +37,28 @@ TEST(SysinfoTest, NumCPUs) {
<< "NumCPUs() should not have the default value of 0"; << "NumCPUs() should not have the default value of 0";
} }
// Ensure that NominalCPUFrequency returns a reasonable value, or 1.00 on
// platforms where the CPU frequency is not available through sysfs.
//
// POWER is particularly problematic here; some Linux kernels expose the CPU
// frequency, while others do not. Since we can't predict a priori what a given
// machine is going to do, just disable this test on POWER on Linux.
#if !(defined(__linux) && (defined(__ppc64__) || defined(__PPC64__)))
TEST(SysinfoTest, NominalCPUFrequency) { TEST(SysinfoTest, NominalCPUFrequency) {
#if !(defined(__aarch64__) && defined(__linux__)) && !defined(__EMSCRIPTEN__) // Linux only exposes the CPU frequency on certain architectures, and
EXPECT_GE(NominalCPUFrequency(), 1000.0) // Emscripten doesn't expose it at all.
<< "NominalCPUFrequency() did not return a reasonable value"; #if defined(__linux__) && \
#else (defined(__aarch64__) || defined(__hppa__) || defined(__mips__) || \
// Aarch64 cannot read the CPU frequency from sysfs, so we get back 1.0. defined(__riscv) || defined(__s390x__)) || \
// Emscripten does not have a sysfs to read from at all. defined(__EMSCRIPTEN__)
EXPECT_EQ(NominalCPUFrequency(), 1.0) EXPECT_EQ(NominalCPUFrequency(), 1.0)
<< "CPU frequency detection was fixed! Please update unittest."; << "CPU frequency detection was fixed! Please update unittest.";
#else
EXPECT_GE(NominalCPUFrequency(), 1000.0)
<< "NominalCPUFrequency() did not return a reasonable value";
#endif #endif
} }
#endif
TEST(SysinfoTest, GetTID) { TEST(SysinfoTest, GetTID) {
EXPECT_EQ(GetTID(), GetTID()); // Basic compile and equality test. EXPECT_EQ(GetTID(), GetTID()); // Basic compile and equality test.
......
...@@ -665,7 +665,7 @@ absl_cc_library( ...@@ -665,7 +665,7 @@ absl_cc_library(
COPTS COPTS
${ABSL_DEFAULT_COPTS} ${ABSL_DEFAULT_COPTS}
DEPS DEPS
absl::bits absl::internal_bits
absl::compressed_tuple absl::compressed_tuple
absl::config absl::config
absl::container_common absl::container_common
......
...@@ -33,6 +33,12 @@ namespace absl { ...@@ -33,6 +33,12 @@ namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
namespace inlined_vector_internal { namespace inlined_vector_internal {
// GCC does not deal very well with the below code
#if !defined(__clang__) && defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
template <typename Iterator> template <typename Iterator>
using IsAtLeastForwardIterator = std::is_convertible< using IsAtLeastForwardIterator = std::is_convertible<
typename std::iterator_traits<Iterator>::iterator_category, typename std::iterator_traits<Iterator>::iterator_category,
...@@ -889,6 +895,11 @@ auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void { ...@@ -889,6 +895,11 @@ auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void {
swap(*GetAllocPtr(), *other_storage_ptr->GetAllocPtr()); swap(*GetAllocPtr(), *other_storage_ptr->GetAllocPtr());
} }
// End ignore "maybe-uninitialized"
#if !defined(__clang__) && defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
} // namespace inlined_vector_internal } // namespace inlined_vector_internal
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl
......
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_ #ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_ #define ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
#include "absl/base/config.h"
#if defined(ABSL_STACKTRACE_INL_HEADER) #if defined(ABSL_STACKTRACE_INL_HEADER)
#error ABSL_STACKTRACE_INL_HEADER cannot be directly set #error ABSL_STACKTRACE_INL_HEADER cannot be directly set
...@@ -29,19 +31,8 @@ ...@@ -29,19 +31,8 @@
"absl/debugging/internal/stacktrace_win32-inl.inc" "absl/debugging/internal/stacktrace_win32-inl.inc"
#elif defined(__APPLE__) #elif defined(__APPLE__)
#ifdef ABSL_HAVE_THREAD_LOCAL
// Thread local support required for UnwindImpl. // Thread local support required for UnwindImpl.
// Notes:
// * Xcode's clang did not support `thread_local` until version 8, and
// even then not for all iOS < 9.0.
// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator
// targeting iOS 9.x.
// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time
// making __has_feature unreliable there.
//
// Otherwise, `__has_feature` is only supported by Clang so it has be inside
// `defined(__APPLE__)` check.
#if __has_feature(cxx_thread_local) && \
!(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
#define ABSL_STACKTRACE_INL_HEADER \ #define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_generic-inl.inc" "absl/debugging/internal/stacktrace_generic-inl.inc"
#endif #endif
......
...@@ -25,6 +25,35 @@ package(default_visibility = ["//visibility:public"]) ...@@ -25,6 +25,35 @@ package(default_visibility = ["//visibility:public"])
licenses(["notice"]) licenses(["notice"])
cc_library( cc_library(
name = "bits",
hdrs = [
"bits.h",
"internal/bits.h",
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
"//absl/base:config",
"//absl/base:core_headers",
],
)
cc_test(
name = "bits_test",
size = "small",
srcs = [
"bits_test.cc",
],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":bits",
"//absl/random",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "int128", name = "int128",
srcs = [ srcs = [
"int128.cc", "int128.cc",
......
...@@ -16,6 +16,33 @@ ...@@ -16,6 +16,33 @@
absl_cc_library( absl_cc_library(
NAME NAME
bits
HDRS
"bits.h"
"internal/bits.h"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
absl::core_headers
PUBLIC
)
absl_cc_test(
NAME
bits_test
SRCS
"bits_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::bits
absl::core_headers
absl::random_random
gmock_main
)
absl_cc_library(
NAME
int128 int128
HDRS HDRS
"int128.h" "int128.h"
...@@ -26,9 +53,9 @@ absl_cc_library( ...@@ -26,9 +53,9 @@ absl_cc_library(
COPTS COPTS
${ABSL_DEFAULT_COPTS} ${ABSL_DEFAULT_COPTS}
DEPS DEPS
absl::bits
absl::config absl::config
absl::core_headers absl::core_headers
absl::internal_bits
PUBLIC PUBLIC
) )
......
// 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.
//
// -----------------------------------------------------------------------------
// File: bits.h
// -----------------------------------------------------------------------------
//
// This file contains implementations of C++20's bitwise math functions, as
// defined by:
//
// P0553R4:
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0553r4.html
// P0556R3:
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0556r3.html
// P1355R2:
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1355r2.html
// P1956R1:
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1956r1.pdf
//
// When using a standard library that implements these functions, we use the
// standard library's implementation.
#ifndef ABSL_NUMERIC_BITS_H_
#define ABSL_NUMERIC_BITS_H_
#include <cstdint>
#include <limits>
#include <type_traits>
#if (defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L) || \
(defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L)
#include <bit>
#endif
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/numeric/internal/bits.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
#if !(defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L)
// rotating
template <class T>
ABSL_MUST_USE_RESULT constexpr
typename std::enable_if<std::is_unsigned<T>::value, T>::type
rotl(T x, int s) noexcept {
return numeric_internal::RotateLeft(x, s);
}
template <class T>
ABSL_MUST_USE_RESULT constexpr
typename std::enable_if<std::is_unsigned<T>::value, T>::type
rotr(T x, int s) noexcept {
return numeric_internal::RotateRight(x, s);
}
// Counting functions
//
// While these functions are typically constexpr, on some platforms, they may
// not be marked as constexpr due to constraints of the compiler/available
// intrinsics.
template <class T>
ABSL_INTERNAL_CONSTEXPR_CLZ inline
typename std::enable_if<std::is_unsigned<T>::value, int>::type
countl_zero(T x) noexcept {
return numeric_internal::CountLeadingZeroes(x);
}
template <class T>
ABSL_INTERNAL_CONSTEXPR_CLZ inline
typename std::enable_if<std::is_unsigned<T>::value, int>::type
countl_one(T x) noexcept {
// Avoid integer promotion to a wider type
return countl_zero(static_cast<T>(~x));
}
template <class T>
ABSL_INTERNAL_CONSTEXPR_CTZ inline
typename std::enable_if<std::is_unsigned<T>::value, int>::type
countr_zero(T x) noexcept {
return numeric_internal::CountTrailingZeroes(x);
}
template <class T>
ABSL_INTERNAL_CONSTEXPR_CTZ inline
typename std::enable_if<std::is_unsigned<T>::value, int>::type
countr_one(T x) noexcept {
// Avoid integer promotion to a wider type
return countr_zero(static_cast<T>(~x));
}
template <class T>
ABSL_INTERNAL_CONSTEXPR_POPCOUNT inline
typename std::enable_if<std::is_unsigned<T>::value, int>::type
popcount(T x) noexcept {
return numeric_internal::Popcount(x);
}
#else // defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L
using std::countl_one;
using std::countl_zero;
using std::countr_one;
using std::countr_zero;
using std::popcount;
using std::rotl;
using std::rotr;
#endif
#if !(defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L)
// Returns: true if x is an integral power of two; false otherwise.
template <class T>
constexpr inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type
has_single_bit(T x) noexcept {
return x != 0 && (x & (x - 1)) == 0;
}
// Returns: If x == 0, 0; otherwise one plus the base-2 logarithm of x, with any
// fractional part discarded.
template <class T>
ABSL_INTERNAL_CONSTEXPR_CLZ inline
typename std::enable_if<std::is_unsigned<T>::value, T>::type
bit_width(T x) noexcept {
return std::numeric_limits<T>::digits - countl_zero(x);
}
// Returns: If x == 0, 0; otherwise the maximal value y such that
// has_single_bit(y) is true and y <= x.
template <class T>
ABSL_INTERNAL_CONSTEXPR_CLZ inline
typename std::enable_if<std::is_unsigned<T>::value, T>::type
bit_floor(T x) noexcept {
return x == 0 ? 0 : T{1} << (bit_width(x) - 1);
}
// Returns: N, where N is the smallest power of 2 greater than or equal to x.
//
// Preconditions: N is representable as a value of type T.
template <class T>
ABSL_INTERNAL_CONSTEXPR_CLZ inline
typename std::enable_if<std::is_unsigned<T>::value, T>::type
bit_ceil(T x) {
// If T is narrower than unsigned, T{1} << bit_width will be promoted. We
// want to force it to wraparound so that bit_ceil of an invalid value are not
// core constant expressions.
//
// BitCeilNonPowerOf2 triggers an overflow in constexpr contexts if we would
// undergo promotion to unsigned but not fit the result into T without
// truncation.
return has_single_bit(x) ? T{1} << (bit_width(x) - 1)
: numeric_internal::BitCeilNonPowerOf2(x);
}
#else // defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L
using std::bit_ceil;
using std::bit_floor;
using std::bit_width;
using std::has_single_bit;
#endif
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_NUMERIC_BITS_H_
...@@ -673,7 +673,7 @@ absl_cc_library( ...@@ -673,7 +673,7 @@ absl_cc_library(
LINKOPTS LINKOPTS
${ABSL_DEFAULT_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS}
DEPS DEPS
absl::bits absl::internal_bits
absl::random_internal_fastmath absl::random_internal_fastmath
absl::random_internal_traits absl::random_internal_traits
absl::type_traits absl::type_traits
...@@ -690,7 +690,7 @@ absl_cc_library( ...@@ -690,7 +690,7 @@ absl_cc_library(
LINKOPTS LINKOPTS
${ABSL_DEFAULT_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS}
DEPS DEPS
absl::bits absl::internal_bits
absl::config absl::config
absl::int128 absl::int128
) )
...@@ -706,7 +706,7 @@ absl_cc_library( ...@@ -706,7 +706,7 @@ absl_cc_library(
LINKOPTS LINKOPTS
${ABSL_DEFAULT_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS}
DEPS DEPS
absl::bits absl::internal_bits
) )
# Internal-only target, do not depend on directly. # Internal-only target, do not depend on directly.
...@@ -902,7 +902,7 @@ absl_cc_test( ...@@ -902,7 +902,7 @@ absl_cc_test(
LINKOPTS LINKOPTS
${ABSL_DEFAULT_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS}
DEPS DEPS
absl::bits absl::internal_bits
absl::flags absl::flags
absl::random_internal_generate_real absl::random_internal_generate_real
gtest_main gtest_main
...@@ -1201,7 +1201,7 @@ absl_cc_test( ...@@ -1201,7 +1201,7 @@ absl_cc_test(
${ABSL_DEFAULT_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS}
DEPS DEPS
absl::random_internal_wide_multiply absl::random_internal_wide_multiply
absl::bits absl::internal_bits
absl::int128 absl::int128
gtest_main gtest_main
) )
...@@ -56,7 +56,7 @@ absl_cc_library( ...@@ -56,7 +56,7 @@ absl_cc_library(
DEPS DEPS
absl::strings_internal absl::strings_internal
absl::base absl::base
absl::bits absl::internal_bits
absl::config absl::config
absl::core_headers absl::core_headers
absl::endian absl::endian
...@@ -406,7 +406,7 @@ absl_cc_library( ...@@ -406,7 +406,7 @@ absl_cc_library(
COPTS COPTS
${ABSL_DEFAULT_COPTS} ${ABSL_DEFAULT_COPTS}
DEPS DEPS
absl::bits absl::internal_bits
absl::strings absl::strings
absl::config absl::config
absl::core_headers absl::core_headers
......
...@@ -1298,26 +1298,23 @@ void Cord::CopyToArraySlowPath(char* dst) const { ...@@ -1298,26 +1298,23 @@ void Cord::CopyToArraySlowPath(char* dst) const {
} }
} }
Cord::ChunkIterator& Cord::ChunkIterator::operator++() { Cord::ChunkIterator& Cord::ChunkIterator::AdvanceStack() {
ABSL_HARDENING_ASSERT(bytes_remaining_ > 0 && assert(absl::holds_alternative<Stack>(context_));
"Attempted to iterate past `end()`"); auto& stack_of_right_children = absl::get<Stack>(context_);
assert(bytes_remaining_ >= current_chunk_.size()); if (stack_of_right_children.empty()) {
bytes_remaining_ -= current_chunk_.size();
if (stack_of_right_children_.empty()) {
assert(!current_chunk_.empty()); // Called on invalid iterator. assert(!current_chunk_.empty()); // Called on invalid iterator.
// We have reached the end of the Cord. // We have reached the end of the Cord.
return *this; return *this;
} }
// Process the next node on the stack. // Process the next node on the stack.
CordRep* node = stack_of_right_children_.back(); CordRep* node = stack_of_right_children.back();
stack_of_right_children_.pop_back(); stack_of_right_children.pop_back();
// Walk down the left branches until we hit a non-CONCAT node. Save the // Walk down the left branches until we hit a non-CONCAT node. Save the
// right children to the stack for subsequent traversal. // right children to the stack for subsequent traversal.
while (node->tag == CONCAT) { while (node->tag == CONCAT) {
stack_of_right_children_.push_back(node->concat()->right); stack_of_right_children.push_back(node->concat()->right);
node = node->concat()->left; node = node->concat()->left;
} }
...@@ -1360,6 +1357,8 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { ...@@ -1360,6 +1357,8 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) {
} }
return subcord; return subcord;
} }
assert(absl::holds_alternative<Stack>(context_));
auto& stack_of_right_children = absl::get<Stack>(context_);
if (n < current_chunk_.size()) { if (n < current_chunk_.size()) {
// Range to read is a proper subrange of the current chunk. // Range to read is a proper subrange of the current chunk.
assert(current_leaf_ != nullptr); assert(current_leaf_ != nullptr);
...@@ -1388,13 +1387,13 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { ...@@ -1388,13 +1387,13 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) {
// Process the next node(s) on the stack, reading whole subtrees depending on // Process the next node(s) on the stack, reading whole subtrees depending on
// their length and how many bytes we are advancing. // their length and how many bytes we are advancing.
CordRep* node = nullptr; CordRep* node = nullptr;
while (!stack_of_right_children_.empty()) { while (!stack_of_right_children.empty()) {
node = stack_of_right_children_.back(); node = stack_of_right_children.back();
stack_of_right_children_.pop_back(); stack_of_right_children.pop_back();
if (node->length > n) break; if (node->length > n) break;
// TODO(qrczak): This might unnecessarily recreate existing concat nodes. // TODO(qrczak): This might unnecessarily recreate existing concat nodes.
// Avoiding that would need pretty complicated logic (instead of // Avoiding that would need pretty complicated logic (instead of
// current_leaf_, keep current_subtree_ which points to the highest node // current_leaf, keep current_subtree_ which points to the highest node
// such that the current leaf can be found on the path of left children // such that the current leaf can be found on the path of left children
// starting from current_subtree_; delay creating subnode while node is // starting from current_subtree_; delay creating subnode while node is
// below current_subtree_; find the proper node along the path of left // below current_subtree_; find the proper node along the path of left
...@@ -1419,7 +1418,7 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { ...@@ -1419,7 +1418,7 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) {
while (node->tag == CONCAT) { while (node->tag == CONCAT) {
if (node->concat()->left->length > n) { if (node->concat()->left->length > n) {
// Push right, descend left. // Push right, descend left.
stack_of_right_children_.push_back(node->concat()->right); stack_of_right_children.push_back(node->concat()->right);
node = node->concat()->left; node = node->concat()->left;
} else { } else {
// Read left, descend right. // Read left, descend right.
...@@ -1462,12 +1461,20 @@ void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) { ...@@ -1462,12 +1461,20 @@ void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) {
n -= current_chunk_.size(); n -= current_chunk_.size();
bytes_remaining_ -= current_chunk_.size(); bytes_remaining_ -= current_chunk_.size();
if (!absl::holds_alternative<Stack>(context_)) {
// We have reached the end of the Cord.
assert(bytes_remaining_ == 0);
return;
}
// Process the next node(s) on the stack, skipping whole subtrees depending on // Process the next node(s) on the stack, skipping whole subtrees depending on
// their length and how many bytes we are advancing. // their length and how many bytes we are advancing.
CordRep* node = nullptr; CordRep* node = nullptr;
while (!stack_of_right_children_.empty()) { assert(absl::holds_alternative<Stack>(context_));
node = stack_of_right_children_.back(); auto& stack_of_right_children = absl::get<Stack>(context_);
stack_of_right_children_.pop_back(); while (!stack_of_right_children.empty()) {
node = stack_of_right_children.back();
stack_of_right_children.pop_back();
if (node->length > n) break; if (node->length > n) break;
n -= node->length; n -= node->length;
bytes_remaining_ -= node->length; bytes_remaining_ -= node->length;
...@@ -1485,7 +1492,7 @@ void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) { ...@@ -1485,7 +1492,7 @@ void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) {
while (node->tag == CONCAT) { while (node->tag == CONCAT) {
if (node->concat()->left->length > n) { if (node->concat()->left->length > n) {
// Push right, descend left. // Push right, descend left.
stack_of_right_children_.push_back(node->concat()->right); stack_of_right_children.push_back(node->concat()->right);
node = node->concat()->left; node = node->concat()->left;
} else { } else {
// Skip left, descend right. // Skip left, descend right.
......
...@@ -362,6 +362,8 @@ class Cord { ...@@ -362,6 +362,8 @@ class Cord {
friend class CharIterator; friend class CharIterator;
private: private:
using Stack = absl::InlinedVector<absl::cord_internal::CordRep*, 4>;
// Constructs a `begin()` iterator from `cord`. // Constructs a `begin()` iterator from `cord`.
explicit ChunkIterator(const Cord* cord); explicit ChunkIterator(const Cord* cord);
...@@ -370,6 +372,10 @@ class Cord { ...@@ -370,6 +372,10 @@ class Cord {
void RemoveChunkPrefix(size_t n); void RemoveChunkPrefix(size_t n);
Cord AdvanceAndReadBytes(size_t n); Cord AdvanceAndReadBytes(size_t n);
void AdvanceBytes(size_t n); void AdvanceBytes(size_t n);
// Stack specific operator++
ChunkIterator& AdvanceStack();
// Iterates `n` bytes, where `n` is expected to be greater than or equal to // Iterates `n` bytes, where `n` is expected to be greater than or equal to
// `current_chunk_.size()`. // `current_chunk_.size()`.
void AdvanceBytesSlowPath(size_t n); void AdvanceBytesSlowPath(size_t n);
...@@ -383,8 +389,10 @@ class Cord { ...@@ -383,8 +389,10 @@ 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> // Context of this chunk iterator, can be one of:
stack_of_right_children_; // - monostate: iterator holds only one chunk or is empty.
// - Stack : iterator holds a concat / substring tree
absl::variant<absl::monostate, Stack> context_;
}; };
// Cord::ChunkIterator::chunk_begin() // Cord::ChunkIterator::chunk_begin()
...@@ -1100,13 +1108,29 @@ inline Cord::ChunkIterator::ChunkIterator(const Cord* cord) ...@@ -1100,13 +1108,29 @@ inline Cord::ChunkIterator::ChunkIterator(const Cord* cord)
: bytes_remaining_(cord->size()) { : bytes_remaining_(cord->size()) {
if (cord->empty()) return; if (cord->empty()) return;
if (cord->contents_.is_tree()) { if (cord->contents_.is_tree()) {
stack_of_right_children_.push_back(cord->contents_.tree()); Stack& stack_of_right_children = context_.emplace<Stack>();
stack_of_right_children.push_back(cord->contents_.tree());
operator++(); operator++();
} else { } else {
current_chunk_ = absl::string_view(cord->contents_.data(), cord->size()); current_chunk_ = absl::string_view(cord->contents_.data(), cord->size());
} }
} }
inline Cord::ChunkIterator& Cord::ChunkIterator::operator++() {
ABSL_HARDENING_ASSERT(bytes_remaining_ > 0 &&
"Attempted to iterate past `end()`");
assert(bytes_remaining_ >= current_chunk_.size());
bytes_remaining_ -= current_chunk_.size();
if (bytes_remaining_ > 0) {
if (absl::holds_alternative<Stack>(context_)) {
return AdvanceStack();
}
} else {
current_chunk_ = {};
}
return *this;
}
inline Cord::ChunkIterator Cord::ChunkIterator::operator++(int) { inline Cord::ChunkIterator Cord::ChunkIterator::operator++(int) {
ChunkIterator tmp(*this); ChunkIterator tmp(*this);
operator++(); operator++();
......
...@@ -24,7 +24,10 @@ namespace absl { ...@@ -24,7 +24,10 @@ namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
namespace cord_internal { namespace cord_internal {
ABSL_CONST_INIT std::atomic<bool> cord_ring_buffer_enabled(false); ABSL_CONST_INIT std::atomic<bool> cord_ring_buffer_enabled(
kCordEnableRingBufferDefault);
ABSL_CONST_INIT std::atomic<bool> shallow_subcords_enabled(
kCordShallowSubcordsDefault);
void CordRep::Destroy(CordRep* rep) { void CordRep::Destroy(CordRep* rep) {
assert(rep != nullptr); assert(rep != nullptr);
......
...@@ -31,12 +31,23 @@ namespace absl { ...@@ -31,12 +31,23 @@ namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
namespace cord_internal { namespace cord_internal {
// Default feature enable states for cord ring buffers
enum CordFeatureDefaults {
kCordEnableRingBufferDefault = false,
kCordShallowSubcordsDefault = false
};
extern std::atomic<bool> cord_ring_buffer_enabled; extern std::atomic<bool> cord_ring_buffer_enabled;
extern std::atomic<bool> shallow_subcords_enabled;
inline void enable_cord_ring_buffer(bool enable) { inline void enable_cord_ring_buffer(bool enable) {
cord_ring_buffer_enabled.store(enable, std::memory_order_relaxed); cord_ring_buffer_enabled.store(enable, std::memory_order_relaxed);
} }
inline void enable_shallow_subcords(bool enable) {
shallow_subcords_enabled.store(enable, std::memory_order_relaxed);
}
enum Constants { enum Constants {
// The inlined size to use with absl::InlinedVector. // The inlined size to use with absl::InlinedVector.
// //
......
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