Commit 22771d47 by Abseil Team Committed by vslashg

Export of internal Abseil changes

--
642ab296a2c9629c44f3f2ce6911cd2488bcf416 by Derek Mauro <dmauro@google.com>:

Remove an obsolete check in CMakeLists.txt

PiperOrigin-RevId: 352852564

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

Clarify that the calling *thread* must have locked the mutex in order to unlock
it.

PiperOrigin-RevId: 352801804

--
24e1f5f72756046f5265abf618e951c341f09b8d by Derek Mauro <dmauro@google.com>:

Fixes failing CMake string comparisons
https://cmake.org/cmake/help/latest/policy/CMP0054.html

Fixes #791

PiperOrigin-RevId: 352791054

--
0ac10bc3f4dca2c4c4b51d7b8196a2eaee9537a1 by Abseil Team <absl-team@google.com>:

Introduce CordRepRing class

This change introduces the CordRepRing class that implements all the lower level / internal implementation for upcoming CordRepRing ring buffer support in cord.

PiperOrigin-RevId: 352771994

--
4bd36dda61760785844f0f29f26d90cc18046f75 by Abseil Team <absl-team@google.com>:

Optimize InlineData representation for cord sampling (cordz)

This CL changes InlineData to allow us to store a (future) Cordz Info pointer directly into the inline representation:

- make InlineData a class that provides a public API to set the active union members (tree or chars) and safely access that data.
- change 'tree' and 'profiled' bits to be the 2 least significant bits, allowing us 62 continquous bits for storing a Cordz Info pointer.

PiperOrigin-RevId: 352642411

--
dc55ba71bbce0e6a83e05a453990c51ac3d68426 by Mark Barolak <mbar@google.com>:

Add unit test coverage for the mutating overload of absl::AsciiStrToLower.

PiperOrigin-RevId: 352626006
GitOrigin-RevId: 642ab296a2c9629c44f3f2ce6911cd2488bcf416
Change-Id: I6c5929dd830d3c630e14e7fd5387fc3e25a69100
parent b2dcbba1
......@@ -196,6 +196,8 @@ set(ABSL_INTERNAL_DLL_FILES
"strings/internal/cord_internal.cc"
"strings/internal/cord_internal.h"
"strings/internal/cord_rep_flat.h"
"strings/internal/cord_rep_ring.cc"
"strings/internal/cord_rep_ring.h"
"strings/internal/charconv_bigint.cc"
"strings/internal/charconv_bigint.h"
"strings/internal/charconv_parse.cc"
......
......@@ -104,7 +104,7 @@ function(absl_cc_library)
endif()
endforeach()
if("${ABSL_CC_SRCS}" STREQUAL "")
if(ABSL_CC_SRCS STREQUAL "")
set(ABSL_CC_LIB_IS_INTERFACE 1)
else()
set(ABSL_CC_LIB_IS_INTERFACE 0)
......@@ -142,7 +142,7 @@ function(absl_cc_library)
endif()
# Generate a pkg-config file for every library:
if(${_build_type} STREQUAL "static" OR ${_build_type} STREQUAL "shared")
if(_build_type STREQUAL "static" OR _build_type STREQUAL "shared")
if(NOT ABSL_CC_LIB_TESTONLY)
if(absl_VERSION)
set(PC_VERSION "${absl_VERSION}")
......@@ -183,7 +183,7 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n")
endif()
if(NOT ABSL_CC_LIB_IS_INTERFACE)
if(${_build_type} STREQUAL "dll_dep")
if(_build_type STREQUAL "dll_dep")
# This target depends on the DLL. When adding dependencies to this target,
# any depended-on-target which is contained inside the DLL is replaced
# with a dependency on the DLL.
......@@ -212,7 +212,7 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n")
"${_gtest_link_define}"
)
elseif(${_build_type} STREQUAL "static" OR ${_build_type} STREQUAL "shared")
elseif(_build_type STREQUAL "static" OR _build_type STREQUAL "shared")
add_library(${_NAME} "")
target_sources(${_NAME} PRIVATE ${ABSL_CC_LIB_SRCS} ${ABSL_CC_LIB_HDRS})
target_link_libraries(${_NAME}
......@@ -273,7 +273,7 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n")
$<INSTALL_INTERFACE:${ABSL_INSTALL_INCLUDEDIR}>
)
if (${_build_type} STREQUAL "dll")
if (_build_type STREQUAL "dll")
set(ABSL_CC_LIB_DEPS abseil_dll)
endif()
......
......@@ -51,7 +51,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# when absl is included as subproject (i.e. using add_subdirectory(abseil-cpp))
# in the source tree of a project that uses it, install rules are disabled.
if(NOT "^${CMAKE_SOURCE_DIR}$" STREQUAL "^${PROJECT_SOURCE_DIR}$")
if(NOT CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
option(ABSL_ENABLE_INSTALL "Enable install rule" OFF)
else()
option(ABSL_ENABLE_INSTALL "Enable install rule" ON)
......
......@@ -12,16 +12,16 @@ else()
set(ABSL_BUILD_DLL FALSE)
endif()
if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "x86_64|amd64|AMD64")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
if (MSVC)
set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_MSVC_X64_FLAGS}")
else()
set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_X64_FLAGS}")
endif()
elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm.*|aarch64")
if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*|aarch64")
if (CMAKE_SIZEOF_VOID_P STREQUAL "8")
set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM64_FLAGS}")
elseif("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
elseif(CMAKE_SIZEOF_VOID_P STREQUAL "4")
set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM32_FLAGS}")
else()
message(WARNING "Value of CMAKE_SIZEOF_VOID_P (${CMAKE_SIZEOF_VOID_P}) is not supported.")
......@@ -32,10 +32,10 @@ else()
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(ABSL_DEFAULT_COPTS "${ABSL_GCC_FLAGS}")
set(ABSL_TEST_COPTS "${ABSL_GCC_FLAGS};${ABSL_GCC_TEST_FLAGS}")
elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# MATCHES so we get both Clang and AppleClang
if(MSVC)
# clang-cl is half MSVC, half LLVM
......@@ -45,7 +45,7 @@ elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
else()
set(ABSL_DEFAULT_COPTS "${ABSL_LLVM_FLAGS}")
set(ABSL_TEST_COPTS "${ABSL_LLVM_FLAGS};${ABSL_LLVM_TEST_FLAGS}")
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# AppleClang doesn't have lsan
# https://developer.apple.com/documentation/code_diagnostics
if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5)
......@@ -54,7 +54,7 @@ elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
endif()
endif()
endif()
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(ABSL_DEFAULT_COPTS "${ABSL_MSVC_FLAGS}")
set(ABSL_TEST_COPTS "${ABSL_MSVC_FLAGS};${ABSL_MSVC_TEST_FLAGS}")
set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}")
......
......@@ -269,10 +269,12 @@ cc_library(
name = "cord_internal",
srcs = [
"internal/cord_internal.cc",
"internal/cord_rep_ring.cc",
],
hdrs = [
"internal/cord_internal.h",
"internal/cord_rep_flat.h",
"internal/cord_rep_ring.h",
],
copts = ABSL_DEFAULT_COPTS,
visibility = [
......@@ -281,9 +283,13 @@ cc_library(
deps = [
":strings",
"//absl/base:base_internal",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:raw_logging_internal",
"//absl/base:throw_delegate",
"//absl/container:compressed_tuple",
"//absl/container:inlined_vector",
"//absl/container:layout",
"//absl/meta:type_traits",
],
)
......@@ -348,6 +354,23 @@ cc_test(
)
cc_test(
name = "cord_ring_test",
size = "medium",
srcs = ["cord_ring_test.cc"],
copts = ABSL_TEST_COPTS,
visibility = ["//visibility:private"],
deps = [
":cord_internal",
":strings",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:raw_logging_internal",
"//absl/debugging:leak_check",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "substitute_test",
size = "small",
srcs = ["substitute_test.cc"],
......
......@@ -558,6 +558,8 @@ absl_cc_library(
"cord.cc"
"internal/cord_internal.cc"
"internal/cord_internal.h"
"internal/cord_rep_ring.h"
"internal/cord_rep_ring.cc"
"internal/cord_rep_flat.h"
COPTS
${ABSL_DEFAULT_COPTS}
......@@ -565,6 +567,7 @@ absl_cc_library(
absl::base
absl::base_internal
absl::compressed_tuple
absl::config
absl::core_headers
absl::endian
absl::fixed_array
......@@ -574,6 +577,7 @@ absl_cc_library(
absl::raw_logging_internal
absl::strings
absl::strings_internal
absl::throw_delegate
absl::type_traits
PUBLIC
)
......@@ -609,3 +613,20 @@ absl_cc_test(
absl::fixed_array
gmock_main
)
absl_cc_test(
NAME
cord_ring_test
SRCS
"cord_ring_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::config
absl::cord
absl::strings
absl::base
absl::core_headers
absl::raw_logging_internal
gmock_main
)
......@@ -197,11 +197,15 @@ TEST(AsciiStrTo, Lower) {
const std::string str("GHIJKL");
const std::string str2("MNOPQR");
const absl::string_view sp(str2);
std::string mutable_str("STUVWX");
EXPECT_EQ("abcdef", absl::AsciiStrToLower(buf));
EXPECT_EQ("ghijkl", absl::AsciiStrToLower(str));
EXPECT_EQ("mnopqr", absl::AsciiStrToLower(sp));
absl::AsciiStrToLower(&mutable_str);
EXPECT_EQ("stuvwx", mutable_str);
char mutable_buf[] = "Mutable";
std::transform(mutable_buf, mutable_buf + strlen(mutable_buf),
mutable_buf, absl::ascii_tolower);
......
......@@ -255,50 +255,49 @@ inline void Cord::InlineRep::set_data(const char* data, size_t n,
bool nullify_tail) {
static_assert(kMaxInline == 15, "set_data is hard-coded for a length of 15");
cord_internal::SmallMemmove(data_.as_chars, data, n, nullify_tail);
set_tagged_size(static_cast<char>(n));
cord_internal::SmallMemmove(data_.as_chars(), data, n, nullify_tail);
set_inline_size(n);
}
inline char* Cord::InlineRep::set_data(size_t n) {
assert(n <= kMaxInline);
ResetToEmpty();
set_tagged_size(static_cast<char>(n));
return data_.as_chars;
set_inline_size(n);
return data_.as_chars();
}
inline CordRep* Cord::InlineRep::force_tree(size_t extra_hint) {
size_t len = tagged_size();
if (len > kMaxInline) {
return data_.as_tree.rep;
if (data_.is_tree()) {
return data_.as_tree();
}
size_t len = inline_size();
CordRepFlat* result = CordRepFlat::New(len + extra_hint);
result->length = len;
static_assert(kMinFlatLength >= sizeof(data_.as_chars), "");
memcpy(result->Data(), data_.as_chars, sizeof(data_.as_chars));
static_assert(kMinFlatLength >= sizeof(data_), "");
memcpy(result->Data(), data_.as_chars(), sizeof(data_));
set_tree(result);
return result;
}
inline void Cord::InlineRep::reduce_size(size_t n) {
size_t tag = tagged_size();
size_t tag = inline_size();
assert(tag <= kMaxInline);
assert(tag >= n);
tag -= n;
memset(data_.as_chars + tag, 0, n);
set_tagged_size(static_cast<char>(tag));
memset(data_.as_chars() + tag, 0, n);
set_inline_size(static_cast<char>(tag));
}
inline void Cord::InlineRep::remove_prefix(size_t n) {
cord_internal::SmallMemmove(data_.as_chars, data_.as_chars + n,
tagged_size() - n);
cord_internal::SmallMemmove(data_.as_chars(), data_.as_chars() + n,
inline_size() - n);
reduce_size(n);
}
void Cord::InlineRep::AppendTree(CordRep* tree) {
if (tree == nullptr) return;
size_t len = tagged_size();
if (len == 0) {
if (data_.is_empty()) {
set_tree(tree);
} else {
set_tree(Concat(force_tree(0), tree));
......@@ -307,8 +306,7 @@ void Cord::InlineRep::AppendTree(CordRep* tree) {
void Cord::InlineRep::PrependTree(CordRep* tree) {
assert(tree != nullptr);
size_t len = tagged_size();
if (len == 0) {
if (data_.is_empty()) {
set_tree(tree);
} else {
set_tree(Concat(tree, force_tree(0)));
......@@ -363,13 +361,15 @@ void Cord::InlineRep::GetAppendRegion(char** region, size_t* size,
}
// Try to fit in the inline buffer if possible.
size_t inline_length = tagged_size();
if (inline_length < kMaxInline && max_length <= kMaxInline - inline_length) {
*region = data_.as_chars + inline_length;
if (!is_tree()) {
size_t inline_length = inline_size();
if (max_length <= kMaxInline - inline_length) {
*region = data_.as_chars() + inline_length;
*size = max_length;
set_tagged_size(static_cast<char>(inline_length + max_length));
set_inline_size(inline_length + max_length);
return;
}
}
CordRep* root = force_tree(max_length);
......@@ -390,13 +390,15 @@ void Cord::InlineRep::GetAppendRegion(char** region, size_t* size) {
const size_t max_length = std::numeric_limits<size_t>::max();
// Try to fit in the inline buffer if possible.
size_t inline_length = tagged_size();
if (!data_.is_tree()) {
size_t inline_length = inline_size();
if (inline_length < kMaxInline) {
*region = data_.as_chars + inline_length;
*region = data_.as_chars() + inline_length;
*size = kMaxInline - inline_length;
set_tagged_size(kMaxInline);
set_inline_size(kMaxInline);
return;
}
}
CordRep* root = force_tree(max_length);
......@@ -549,24 +551,25 @@ template Cord& Cord::operator=(std::string&& src);
// we keep it here to make diffs easier.
void Cord::InlineRep::AppendArray(const char* src_data, size_t src_size) {
if (src_size == 0) return; // memcpy(_, nullptr, 0) is undefined.
// Try to fit in the inline buffer if possible.
size_t inline_length = tagged_size();
if (inline_length < kMaxInline && src_size <= kMaxInline - inline_length) {
// Append new data to embedded array
set_tagged_size(static_cast<char>(inline_length + src_size));
memcpy(data_.as_chars + inline_length, src_data, src_size);
return;
}
CordRep* root = tree();
size_t appended = 0;
if (root) {
CordRep* root = nullptr;
if (is_tree()) {
root = data_.as_tree();
char* region;
if (PrepareAppendRegion(root, &region, &appended, src_size)) {
memcpy(region, src_data, appended);
}
} else {
// Try to fit in the inline buffer if possible.
size_t inline_length = inline_size();
if (src_size <= kMaxInline - inline_length) {
// Append new data to embedded array
memcpy(data_.as_chars() + inline_length, src_data, src_size);
set_inline_size(inline_length + src_size);
return;
}
// It is possible that src_data == data_, but when we transition from an
// InlineRep to a tree we need to assign data_ = root via set_tree. To
// avoid corrupting the source data before we copy it, delay calling
......@@ -578,7 +581,7 @@ void Cord::InlineRep::AppendArray(const char* src_data, size_t src_size) {
root = CordRepFlat::New(std::max<size_t>(size1, size2));
appended = std::min(
src_size, root->flat()->Capacity() - inline_length);
memcpy(root->flat()->Data(), data_.as_chars, inline_length);
memcpy(root->flat()->Data(), data_.as_chars(), inline_length);
memcpy(root->flat()->Data() + inline_length, src_data, appended);
root->length = inline_length + appended;
set_tree(root);
......@@ -684,18 +687,19 @@ void Cord::Prepend(const Cord& src) {
void Cord::Prepend(absl::string_view src) {
if (src.empty()) return; // memcpy(_, nullptr, 0) is undefined.
size_t cur_size = contents_.size();
if (!contents_.is_tree() && cur_size + src.size() <= InlineRep::kMaxInline) {
if (!contents_.is_tree()) {
size_t cur_size = contents_.inline_size();
if (cur_size + src.size() <= InlineRep::kMaxInline) {
// Use embedded storage.
char data[InlineRep::kMaxInline + 1] = {0};
data[InlineRep::kMaxInline] = cur_size + src.size(); // set size
memcpy(data, src.data(), src.size());
memcpy(data + src.size(), contents_.data(), cur_size);
memcpy(reinterpret_cast<void*>(&contents_), data,
InlineRep::kMaxInline + 1);
} else {
contents_.PrependTree(NewTree(src.data(), src.size(), 0));
memcpy(contents_.data_.as_chars(), data, InlineRep::kMaxInline + 1);
contents_.set_inline_size(cur_size + src.size());
return;
}
}
contents_.PrependTree(NewTree(src.data(), src.size(), 0));
}
template <typename T, Cord::EnableIfString<T>>
......@@ -888,7 +892,7 @@ Cord Cord::Subcord(size_t pos, size_t new_size) const {
} else if (new_size <= InlineRep::kMaxInline) {
Cord::ChunkIterator it = chunk_begin();
it.AdvanceBytes(pos);
char* dest = sub_cord.contents_.data_.as_chars;
char* dest = sub_cord.contents_.data_.as_chars();
size_t remaining_size = new_size;
while (remaining_size > it->size()) {
cord_internal::SmallMemmove(dest, it->data(), it->size());
......@@ -897,7 +901,7 @@ Cord Cord::Subcord(size_t pos, size_t new_size) const {
++it;
}
cord_internal::SmallMemmove(dest, it->data(), remaining_size);
sub_cord.contents_.set_tagged_size(new_size);
sub_cord.contents_.set_inline_size(new_size);
} else {
sub_cord.contents_.set_tree(NewSubRange(tree, pos, new_size));
}
......@@ -1086,9 +1090,8 @@ bool ComputeCompareResult<bool>(int memcmp_res) {
// Helper routine. Locates the first flat chunk of the Cord without
// initializing the iterator.
inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const {
size_t n = tagged_size();
if (n <= kMaxInline) {
return absl::string_view(data_.as_chars, n);
if (!is_tree()) {
return absl::string_view(data_.as_chars(), data_.inline_size());
}
CordRep* node = tree();
......
......@@ -665,8 +665,6 @@ class Cord {
public:
static constexpr unsigned char kMaxInline = cord_internal::kMaxInline;
static_assert(kMaxInline >= sizeof(absl::cord_internal::CordRep*), "");
static constexpr unsigned char kTreeFlag = cord_internal::kTreeFlag;
static constexpr unsigned char kProfiledFlag = cord_internal::kProfiledFlag;
constexpr InlineRep() : data_() {}
InlineRep(const InlineRep& src);
......@@ -685,6 +683,7 @@ class Cord {
char* set_data(size_t n); // Write data to the result
// Returns nullptr if holding bytes
absl::cord_internal::CordRep* tree() const;
absl::cord_internal::CordRep* as_tree() const;
// Discards old pointer, if any
void set_tree(absl::cord_internal::CordRep* rep);
// Replaces a tree with a new root. This is faster than set_tree, but it
......@@ -728,13 +727,13 @@ class Cord {
memcpy(&(*dst)[0], &data_, sizeof(data_) - 1);
// erase is faster than resize because the logic for memory allocation is
// not needed.
dst->erase(tagged_size());
dst->erase(inline_size());
}
// Copies the inline contents into `dst`. Assumes the cord is not empty.
void CopyToArray(char* dst) const;
bool is_tree() const { return tagged_size() > kMaxInline; }
bool is_tree() const { return data_.is_tree(); }
private:
friend class Cord;
......@@ -745,14 +744,8 @@ class Cord {
void ResetToEmpty() { data_ = {}; }
// This uses reinterpret_cast instead of the union to avoid accessing the
// inactive union element. The tagged size is not a common prefix.
void set_tagged_size(char new_tag) {
reinterpret_cast<char*>(&data_)[kMaxInline] = new_tag;
}
char tagged_size() const {
return reinterpret_cast<const char*>(&data_)[kMaxInline];
}
void set_inline_size(size_t size) { data_.set_inline_size(size); }
size_t inline_size() const { return data_.inline_size(); }
cord_internal::InlineData data_;
};
......@@ -948,35 +941,39 @@ inline void Cord::InlineRep::Swap(Cord::InlineRep* rhs) {
}
inline const char* Cord::InlineRep::data() const {
return is_tree() ? nullptr : data_.as_chars;
return is_tree() ? nullptr : data_.as_chars();
}
inline absl::cord_internal::CordRep* Cord::InlineRep::as_tree() const {
assert(data_.is_tree());
return data_.as_tree();
}
inline absl::cord_internal::CordRep* Cord::InlineRep::tree() const {
if (is_tree()) {
return data_.as_tree.rep;
return as_tree();
} else {
return nullptr;
}
}
inline bool Cord::InlineRep::empty() const { return tagged_size() == 0; }
inline bool Cord::InlineRep::empty() const { return data_.is_empty(); }
inline size_t Cord::InlineRep::size() const {
const char tag = tagged_size();
if (tag <= kMaxInline) return tag;
return static_cast<size_t>(tree()->length);
return is_tree() ? as_tree()->length : inline_size();
}
inline void Cord::InlineRep::set_tree(absl::cord_internal::CordRep* rep) {
if (rep == nullptr) {
ResetToEmpty();
} else {
bool was_tree = is_tree();
data_.as_tree = {rep, {}, tagged_size()};
if (!was_tree) {
// If we were not a tree already, set the tag.
// Otherwise, leave it alone because it might have the profile bit on.
set_tagged_size(kTreeFlag);
if (data_.is_tree()) {
// `data_` already holds a 'tree' value and an optional cordz_info value.
// Replace the tree value only, leaving the cordz_info value unchanged.
data_.set_tree(rep);
} else {
// `data_` contains inlined data: initialize data_ to tree value `rep`.
data_.make_tree(rep);
}
}
}
......@@ -987,7 +984,7 @@ inline void Cord::InlineRep::replace_tree(absl::cord_internal::CordRep* rep) {
set_tree(rep);
return;
}
data_.as_tree = {rep, {}, tagged_size()};
data_.set_tree(rep);
}
inline absl::cord_internal::CordRep* Cord::InlineRep::clear() {
......@@ -998,9 +995,9 @@ inline absl::cord_internal::CordRep* Cord::InlineRep::clear() {
inline void Cord::InlineRep::CopyToArray(char* dst) const {
assert(!is_tree());
size_t n = tagged_size();
size_t n = inline_size();
assert(n != 0);
cord_internal::SmallMemmove(dst, data_.as_chars, n);
cord_internal::SmallMemmove(dst, data_.as_chars(), n);
}
constexpr inline Cord::Cord() noexcept {}
......@@ -1011,11 +1008,9 @@ constexpr Cord::Cord(strings_internal::StringConstant<T>)
cord_internal::kMaxInline
? cord_internal::InlineData(
strings_internal::StringConstant<T>::value)
: cord_internal::InlineData(cord_internal::AsTree{
: cord_internal::InlineData(
&cord_internal::ConstInitExternalStorage<
strings_internal::StringConstant<T>>::value,
{},
cord_internal::kTreeFlag})) {}
strings_internal::StringConstant<T>>::value)) {}
inline Cord& Cord::operator=(const Cord& x) {
contents_ = x.contents_;
......@@ -1107,12 +1102,12 @@ inline bool Cord::StartsWith(absl::string_view rhs) const {
inline Cord::ChunkIterator::ChunkIterator(const Cord* cord)
: bytes_remaining_(cord->size()) {
if (cord->empty()) return;
if (cord->contents_.is_tree()) {
stack_of_right_children_.push_back(cord->contents_.tree());
stack_of_right_children_.push_back(cord->contents_.as_tree());
operator++();
} else {
current_chunk_ = absl::string_view(cord->contents_.data(), cord->size());
current_chunk_ =
absl::string_view(cord->contents_.data(), bytes_remaining_);
}
}
......
......@@ -19,6 +19,7 @@
#include "absl/container/inlined_vector.h"
#include "absl/strings/internal/cord_rep_flat.h"
#include "absl/strings/internal/cord_rep_ring.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
......@@ -48,6 +49,9 @@ void CordRep::Destroy(CordRep* rep) {
rep = left;
continue;
}
} else if (rep->tag == RING) {
CordRepRing::Destroy(rep->ring());
rep = nullptr;
} else if (rep->tag == EXTERNAL) {
CordRepExternal::Delete(rep);
rep = nullptr;
......
// Copyright 2020 The Abseil Authors.
// 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.
......@@ -21,6 +21,7 @@
#include <cstdint>
#include <type_traits>
#include "absl/base/config.h"
#include "absl/base/internal/invoke.h"
#include "absl/base/optimization.h"
#include "absl/container/internal/compressed_tuple.h"
......@@ -145,6 +146,7 @@ struct CordRepConcat;
struct CordRepExternal;
struct CordRepFlat;
struct CordRepSubstring;
class CordRepRing;
// Various representations that we allow
enum CordRepKind {
......@@ -160,7 +162,7 @@ enum CordRepKind {
// as the Tag <---> Size logic so that FLAT stil represents the minimum flat
// allocation size. (32 bytes as of now).
FLAT = 4,
MAX_FLAT_TAG = 224,
MAX_FLAT_TAG = 224
};
struct CordRep {
......@@ -177,6 +179,8 @@ struct CordRep {
uint8_t tag;
char storage[1]; // Starting point for flat array: MUST BE LAST FIELD
inline CordRepRing* ring();
inline const CordRepRing* ring() const;
inline CordRepConcat* concat();
inline const CordRepConcat* concat() const;
inline CordRepSubstring* substring();
......@@ -306,45 +310,165 @@ CordRepExternal ConstInitExternalStorage<Str>::value(Str::value);
enum {
kMaxInline = 15,
// Tag byte & kMaxInline means we are storing a pointer.
kTreeFlag = 1 << 4,
// Tag byte & kProfiledFlag means we are profiling the Cord.
kProfiledFlag = 1 << 5
};
// If the data has length <= kMaxInline, we store it in `as_chars`, and
// store the size in `tagged_size`.
// Else we store it in a tree and store a pointer to that tree in
// `as_tree.rep` and store a tag in `tagged_size`.
struct AsTree {
absl::cord_internal::CordRep* rep;
char padding[kMaxInline + 1 - sizeof(absl::cord_internal::CordRep*) - 1];
char tagged_size;
};
constexpr char GetOrNull(absl::string_view data, size_t pos) {
return pos < data.size() ? data[pos] : '\0';
}
union InlineData {
constexpr InlineData() : as_chars{} {}
explicit constexpr InlineData(AsTree tree) : as_tree(tree) {}
// We store cordz_info as 64 bit pointer value in big endian format. This
// guarantees that the least significant byte of cordz_info matches the last
// byte of the inline data representation in as_chars_, which holds the inlined
// size or the 'is_tree' bit.
using cordz_info_t = int64_t;
// Assert that the `cordz_info` pointer value perfectly overlaps the last half
// of `as_chars_` and can hold a pointer value.
static_assert(sizeof(cordz_info_t) * 2 == kMaxInline + 1, "");
static_assert(sizeof(cordz_info_t) >= sizeof(intptr_t), "");
// BigEndianByte() creates a big endian representation of 'value', i.e.: a big
// endian value where the last byte in the host's representation holds 'value`,
// with all other bytes being 0.
static constexpr cordz_info_t BigEndianByte(unsigned char value) {
#if defined(ABSL_IS_BIG_ENDIAN)
return value;
#else
return static_cast<cordz_info_t>(value) << ((sizeof(cordz_info_t) - 1) * 8);
#endif
}
class InlineData {
public:
// kNullCordzInfo holds the big endian representation of intptr_t(1)
// This is the 'null' / initial value of 'cordz_info'. The null value
// is specifically big endian 1 as with 64-bit pointers, the last
// byte of cordz_info overlaps with the last byte holding the tag.
static constexpr cordz_info_t kNullCordzInfo = BigEndianByte(1);
// kFakeCordzInfo holds a 'fake', non-null cordz-info value we use to
// emulate the previous 'kProfiled' tag logic in 'set_profiled' until
// cord code is changed to store cordz_info values in InlineData.
static constexpr cordz_info_t kFakeCordzInfo = BigEndianByte(9);
constexpr InlineData() : as_chars_{0} {}
explicit constexpr InlineData(CordRep* rep) : as_tree_(rep) {}
explicit constexpr InlineData(absl::string_view chars)
: as_chars{GetOrNull(chars, 0), GetOrNull(chars, 1),
: as_chars_{
GetOrNull(chars, 0), GetOrNull(chars, 1),
GetOrNull(chars, 2), GetOrNull(chars, 3),
GetOrNull(chars, 4), GetOrNull(chars, 5),
GetOrNull(chars, 6), GetOrNull(chars, 7),
GetOrNull(chars, 8), GetOrNull(chars, 9),
GetOrNull(chars, 10), GetOrNull(chars, 11),
GetOrNull(chars, 12), GetOrNull(chars, 13),
GetOrNull(chars, 14), static_cast<char>(chars.size())} {}
GetOrNull(chars, 14), static_cast<char>((chars.size() << 1))} {}
// Returns true if the current instance is empty.
// The 'empty value' is an inlined data value of zero length.
bool is_empty() const { return tag() == 0; }
// Returns true if the current instance holds a tree value.
bool is_tree() const { return (tag() & 1) != 0; }
// Returns true if the current instance holds a cordz_info value.
// Requires the current instance to hold a tree value.
bool is_profiled() const {
assert(is_tree());
return as_tree_.cordz_info != kNullCordzInfo;
}
// Returns a read only pointer to the character data inside this instance.
// Requires the current instance to hold inline data.
const char* as_chars() const {
assert(!is_tree());
return as_chars_;
}
// Returns a mutable pointer to the character data inside this instance.
// Should be used for 'write only' operations setting an inlined value.
// Applications can set the value of inlined data either before or after
// setting the inlined size, i.e., both of the below are valid:
//
// // Set inlined data and inline size
// memcpy(data_.as_chars(), data, size);
// data_.set_inline_size(size);
//
// // Set inlined size and inline data
// data_.set_inline_size(size);
// memcpy(data_.as_chars(), data, size);
//
// It's an error to read from the returned pointer without a preceding write
// if the current instance does not hold inline data, i.e.: is_tree() == true.
char* as_chars() { return as_chars_; }
// Returns the tree value of this value.
// Requires the current instance to hold a tree value.
CordRep* as_tree() const {
assert(is_tree());
return as_tree_.rep;
}
// Initialize this instance to holding the tree value `rep`,
// initializing the cordz_info to null, i.e.: 'not profiled'.
void make_tree(CordRep* rep) {
as_tree_.rep = rep;
as_tree_.cordz_info = kNullCordzInfo;
}
AsTree as_tree;
char as_chars[kMaxInline + 1];
// Set the tree value of this instance to 'rep`.
// Requires the current instance to already hold a tree value.
// Does not affect the value of cordz_info.
void set_tree(CordRep* rep) {
assert(is_tree());
as_tree_.rep = rep;
}
// Returns the size of the inlined character data inside this instance.
// Requires the current instance to hold inline data.
size_t inline_size() const {
assert(!is_tree());
return tag() >> 1;
}
// Sets the size of the inlined character data inside this instance.
// Requires `size` to be <= kMaxInline.
// See the documentation on 'as_chars()' for more information and examples.
void set_inline_size(size_t size) {
ABSL_ASSERT(size <= kMaxInline);
tag() = static_cast<char>(size << 1);
}
// Sets or unsets the 'is_profiled' state of this instance.
// Requires the current instance to hold a tree value.
void set_profiled(bool profiled) {
assert(is_tree());
as_tree_.cordz_info = profiled ? kFakeCordzInfo : kNullCordzInfo;
}
private:
// See cordz_info_t for forced alignment and size of `cordz_info` details.
struct AsTree {
explicit constexpr AsTree(absl::cord_internal::CordRep* tree)
: rep(tree), cordz_info(kNullCordzInfo) {}
absl::cord_internal::CordRep* rep;
alignas(sizeof(cordz_info_t)) cordz_info_t cordz_info;
};
char& tag() { return reinterpret_cast<char*>(this)[kMaxInline]; }
char tag() const { return reinterpret_cast<const char*>(this)[kMaxInline]; }
// If the data has length <= kMaxInline, we store it in `as_chars_`, and
// store the size in the last char of `as_chars_` shifted left + 1.
// Else we store it in a tree and store a pointer to that tree in
// `as_tree_.rep` and store a tag in `tagged_size`.
union {
char as_chars_[kMaxInline + 1];
AsTree as_tree_;
};
};
static_assert(sizeof(InlineData) == kMaxInline + 1, "");
static_assert(sizeof(AsTree) == sizeof(InlineData), "");
static_assert(offsetof(AsTree, tagged_size) == kMaxInline, "");
inline CordRepConcat* CordRep::concat() {
assert(tag == CONCAT);
......@@ -386,6 +510,16 @@ inline const CordRepFlat* CordRep::flat() const {
return reinterpret_cast<const CordRepFlat*>(this);
}
inline CordRepRing* CordRep::ring() {
assert(tag == RING);
return reinterpret_cast<CordRepRing*>(this);
}
inline const CordRepRing* CordRep::ring() const {
assert(tag == RING);
return reinterpret_cast<const CordRepRing*>(this);
}
inline CordRep* CordRep::Ref(CordRep* rep) {
assert(rep != nullptr);
rep->refcount.Increment();
......
......@@ -104,7 +104,8 @@ struct CordRepFlat : public CordRep {
// Flat CordReps are allocated and constructed with raw ::operator new and
// placement new, and must be destructed and deallocated accordingly.
static void Delete(CordRep*rep) {
assert(rep->tag >= FLAT);
assert(rep->tag >= FLAT && rep->tag <= MAX_FLAT_TAG);
#if defined(__cpp_sized_deallocation)
size_t size = TagToAllocatedSize(rep->tag);
rep->~CordRep();
......@@ -115,6 +116,7 @@ struct CordRepFlat : public CordRep {
#endif
}
// Returns a pointer to the data inside this flat rep.
char* Data() { return storage; }
const char* Data() const { return storage; }
......
......@@ -162,7 +162,7 @@ class ABSL_LOCKABLE Mutex {
// Mutex::Unlock()
//
// Releases this `Mutex` and returns it from the exclusive/write state to the
// free state. Caller must hold the `Mutex` exclusively.
// free state. Calling thread must hold the `Mutex` exclusively.
void Unlock() ABSL_UNLOCK_FUNCTION();
// Mutex::TryLock()
......
......@@ -353,9 +353,6 @@ absl_cc_test(
gmock_main
)
# TODO(cohenjon,zhangxy) Figure out why this test is failing on gcc 4.8
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
else()
absl_cc_test(
NAME
variant_exception_safety_test
......@@ -370,4 +367,3 @@ absl_cc_test(
absl::memory
gmock_main
)
endif()
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