diff options
author | Abseil Team <absl-team@google.com> | 2020-07-30 13:58:50 -0700 |
---|---|---|
committer | Mark Barolak <mbar@google.com> | 2020-07-31 11:30:59 -0400 |
commit | 184cf2524101310a0ba315c743e82cf45fccccf8 (patch) | |
tree | 61f5cb2de5da31efd024ce2a49f2d304352b33ba /absl/strings | |
parent | 82302f1e05d6daed93bdd77e8951b529b0ebfcf6 (diff) |
Export of internal Abseil changes
--
587e6db882749fa7faa12815e614afab04d218b9 by Derek Mauro <dmauro@google.com>:
Use attribute detection for other sanitizer related attributes
PiperOrigin-RevId: 324077073
--
3ee55e4935b4235516b1fcac3c55945e510f7afc by Evan Brown <ezb@google.com>:
Simplify CordRepExternal allocation/deallocation.
I think this can save some memory when `Releaser` is empty and when on platforms where alignof(CordRepExternal) < (default `::operator new` alignment).
We no longer need the API requirement that alignof(Releaser) <= (default `::operator new` alignment).
Also remove another static_assert from a TODO in cord_internal.h and fix some warnings about calling std::move on a forwarding reference.
PiperOrigin-RevId: 324053720
--
9fc78436565eb3b204d4aa425ee3773354392f45 by Derek Mauro <dmauro@google.com>:
Use auto-detected sanitizer attributes for ASAN, MSAN, and TSAN builds
PiperOrigin-RevId: 323831461
GitOrigin-RevId: 587e6db882749fa7faa12815e614afab04d218b9
Change-Id: Ie0e4a2846d7f66988a2d81a5e50721b62fdb3d6d
Diffstat (limited to 'absl/strings')
-rw-r--r-- | absl/strings/BUILD.bazel | 3 | ||||
-rw-r--r-- | absl/strings/CMakeLists.txt | 1 | ||||
-rw-r--r-- | absl/strings/cord.cc | 74 | ||||
-rw-r--r-- | absl/strings/cord.h | 57 | ||||
-rw-r--r-- | absl/strings/internal/cord_internal.h | 65 | ||||
-rw-r--r-- | absl/strings/string_view_test.cc | 4 |
6 files changed, 66 insertions, 138 deletions
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index ef412639..64a13cef 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -258,6 +258,8 @@ cc_library( visibility = ["//visibility:private"], deps = [ ":strings", + "//absl/base:base_internal", + "//absl/container:compressed_tuple", "//absl/meta:type_traits", ], ) @@ -277,7 +279,6 @@ cc_library( ":str_format", ":strings", "//absl/base", - "//absl/base:base_internal", "//absl/base:core_headers", "//absl/base:endian", "//absl/base:raw_logging_internal", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index d7237231..d6c2126d 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -548,6 +548,7 @@ absl_cc_library( DEPS absl::base absl::base_internal + absl::compressed_tuple absl::core_headers absl::endian absl::fixed_array diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 920dcc67..763dcc45 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -61,48 +61,6 @@ enum CordRepKind { FLAT = 3, }; -namespace { - -// Type used with std::allocator for allocating and deallocating -// `CordRepExternal`. std::allocator is used because it opaquely handles the -// different new / delete overloads available on a given platform. -struct alignas(absl::cord_internal::ExternalRepAlignment()) ExternalAllocType { - unsigned char value[absl::cord_internal::ExternalRepAlignment()]; -}; - -// Returns the number of objects to pass in to std::allocator<ExternalAllocType> -// allocate() and deallocate() to create enough room for `CordRepExternal` with -// `releaser_size` bytes on the end. -constexpr size_t GetExternalAllocNumObjects(size_t releaser_size) { - // Be sure to round up since `releaser_size` could be smaller than - // `sizeof(ExternalAllocType)`. - return (sizeof(CordRepExternal) + releaser_size + sizeof(ExternalAllocType) - - 1) / - sizeof(ExternalAllocType); -} - -// Allocates enough memory for `CordRepExternal` and a releaser with size -// `releaser_size` bytes. -void* AllocateExternal(size_t releaser_size) { - return std::allocator<ExternalAllocType>().allocate( - GetExternalAllocNumObjects(releaser_size)); -} - -// Deallocates the memory for a `CordRepExternal` assuming it was allocated with -// a releaser of given size and alignment. -void DeallocateExternal(CordRepExternal* p, size_t releaser_size) { - std::allocator<ExternalAllocType>().deallocate( - reinterpret_cast<ExternalAllocType*>(p), - GetExternalAllocNumObjects(releaser_size)); -} - -// Returns a pointer to the type erased releaser for the given CordRepExternal. -void* GetExternalReleaser(CordRepExternal* rep) { - return rep + 1; -} - -} // namespace - namespace cord_internal { inline CordRepConcat* CordRep::concat() { @@ -304,11 +262,7 @@ static void UnrefInternal(CordRep* rep) { } } else if (rep->tag == EXTERNAL) { CordRepExternal* rep_external = rep->external(); - absl::string_view data(rep_external->base, rep->length); - void* releaser = GetExternalReleaser(rep_external); - size_t releaser_size = rep_external->releaser_invoker(releaser, data); - rep_external->~CordRepExternal(); - DeallocateExternal(rep_external, releaser_size); + rep_external->releaser_invoker(rep_external); rep = nullptr; } else if (rep->tag == SUBSTRING) { CordRepSubstring* rep_substring = rep->substring(); @@ -458,18 +412,12 @@ static CordRep* NewTree(const char* data, namespace cord_internal { -ExternalRepReleaserPair NewExternalWithUninitializedReleaser( - absl::string_view data, ExternalReleaserInvoker invoker, - size_t releaser_size) { +void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep) { assert(!data.empty()); - - void* raw_rep = AllocateExternal(releaser_size); - auto* rep = new (raw_rep) CordRepExternal(); rep->length = data.size(); rep->tag = EXTERNAL; rep->base = data.data(); - rep->releaser_invoker = invoker; - return {VerifyTree(rep), GetExternalReleaser(rep)}; + VerifyTree(rep); } } // namespace cord_internal @@ -721,12 +669,12 @@ Cord::Cord(T&& src) { std::string data; }; const absl::string_view original_data = src; - CordRepExternal* rep = - static_cast<CordRepExternal*>(absl::cord_internal::NewExternalRep( - original_data, StringReleaser{std::move(src)})); + auto* rep = static_cast< + ::absl::cord_internal::CordRepExternalImpl<StringReleaser>*>( + absl::cord_internal::NewExternalRep( + original_data, StringReleaser{std::forward<T>(src)})); // Moving src may have invalidated its data pointer, so adjust it. - rep->base = - static_cast<StringReleaser*>(GetExternalReleaser(rep))->data.data(); + rep->base = rep->template get<0>().data.data(); contents_.set_tree(rep); } } @@ -775,7 +723,7 @@ Cord& Cord::operator=(T&& src) { if (src.size() <= kMaxBytesToCopy) { *this = absl::string_view(src); } else { - *this = Cord(std::move(src)); + *this = Cord(std::forward<T>(src)); } return *this; } @@ -898,7 +846,7 @@ void Cord::Append(T&& src) { if (src.size() <= kMaxBytesToCopy) { Append(absl::string_view(src)); } else { - Append(Cord(std::move(src))); + Append(Cord(std::forward<T>(src))); } } @@ -938,7 +886,7 @@ inline void Cord::Prepend(T&& src) { if (src.size() <= kMaxBytesToCopy) { Prepend(absl::string_view(src)); } else { - Prepend(Cord(std::move(src))); + Prepend(Cord(std::forward<T>(src))); } } diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 8580d803..a0db2797 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -71,7 +71,6 @@ #include <type_traits> #include "absl/base/internal/endian.h" -#include "absl/base/internal/invoke.h" #include "absl/base/internal/per_thread_tls.h" #include "absl/base/macros.h" #include "absl/base/port.h" @@ -173,10 +172,6 @@ class Cord { // // * be move constructible // * support `void operator()(absl::string_view) const` or `void operator()` - // * not have alignment requirement greater than what is guaranteed by - // `::operator new`. This alignment is dictated by - // `alignof(std::max_align_t)` (pre-C++17 code) or - // `__STDCPP_DEFAULT_NEW_ALIGNMENT__` (C++17 code). // // Example: // @@ -842,47 +837,15 @@ inline void SmallMemmove(char* dst, const char* src, size_t n, } } -struct ExternalRepReleaserPair { - CordRep* rep; - void* releaser_address; -}; - -// Allocates a new external `CordRep` and returns a pointer to it and a pointer -// to `releaser_size` bytes where the desired releaser can be constructed. +// Does non-template-specific `CordRepExternal` initialization. // Expects `data` to be non-empty. -ExternalRepReleaserPair NewExternalWithUninitializedReleaser( - absl::string_view data, ExternalReleaserInvoker invoker, - size_t releaser_size); - -struct Rank1 {}; -struct Rank0 : Rank1 {}; - -template <typename Releaser, typename = ::absl::base_internal::invoke_result_t< - 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::invoke_result_t<Releaser>> -void InvokeReleaser(Rank1, Releaser&& releaser, absl::string_view) { - ::absl::base_internal::invoke(std::forward<Releaser>(releaser)); -} +void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep); // Creates a new `CordRep` that owns `data` and `releaser` and returns a pointer // to it, or `nullptr` if `data` was empty. template <typename Releaser> // NOLINTNEXTLINE - suppress clang-tidy raw pointer return. CordRep* NewExternalRep(absl::string_view data, Releaser&& releaser) { - static_assert( -#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) - alignof(Releaser) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__, -#else - alignof(Releaser) <= alignof(max_align_t), -#endif - "Releasers with alignment requirement greater than what is returned by " - "default `::operator new()` are not supported."); - using ReleaserType = absl::decay_t<Releaser>; if (data.empty()) { // Never create empty external nodes. @@ -891,18 +854,10 @@ CordRep* NewExternalRep(absl::string_view data, Releaser&& releaser) { return nullptr; } - auto releaser_invoker = [](void* type_erased_releaser, absl::string_view d) { - auto* my_releaser = static_cast<ReleaserType*>(type_erased_releaser); - InvokeReleaser(Rank0{}, std::move(*my_releaser), d); - my_releaser->~ReleaserType(); - return sizeof(Releaser); - }; - - ExternalRepReleaserPair external = NewExternalWithUninitializedReleaser( - data, releaser_invoker, sizeof(releaser)); - ::new (external.releaser_address) - ReleaserType(std::forward<Releaser>(releaser)); - return external.rep; + CordRepExternal* rep = new CordRepExternalImpl<ReleaserType>( + std::forward<Releaser>(releaser), 0); + InitializeCordRepExternal(data, rep); + return rep; } // Overload for function reference types that dispatches using a function diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 830ceaf4..d456eef8 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -21,6 +21,8 @@ #include <cstdint> #include <type_traits> +#include "absl/base/internal/invoke.h" +#include "absl/container/internal/compressed_tuple.h" #include "absl/meta/type_traits.h" #include "absl/strings/string_view.h" @@ -114,35 +116,56 @@ struct CordRepSubstring : public CordRep { CordRep* child; }; -// TODO(strel): replace the following logic (and related functions in cord.cc) -// with container_internal::Layout. - -// Alignment requirement for CordRepExternal so that the type erased releaser -// will be stored at a suitably aligned address. -constexpr size_t ExternalRepAlignment() { -#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) - return __STDCPP_DEFAULT_NEW_ALIGNMENT__; -#else - return alignof(max_align_t); -#endif -} - -// Type for function pointer that will invoke and destroy the type-erased -// releaser function object. Accepts a pointer to the releaser and the -// `string_view` that were passed in to `NewExternalRep` below. The return value -// is the size of the `Releaser` type. -using ExternalReleaserInvoker = size_t (*)(void*, absl::string_view); +// Type for function pointer that will invoke the releaser function and also +// delete the `CordRepExternalImpl` corresponding to the passed in +// `CordRepExternal`. +using ExternalReleaserInvoker = void (*)(CordRepExternal*); // External CordReps are allocated together with a type erased releaser. The // releaser is stored in the memory directly following the CordRepExternal. -struct alignas(ExternalRepAlignment()) CordRepExternal : public CordRep { +struct CordRepExternal : public CordRep { const char* base; // Pointer to function that knows how to call and destroy the releaser. ExternalReleaserInvoker releaser_invoker; }; -// TODO(strel): look into removing, it doesn't seem like anything relies on this -static_assert(sizeof(CordRepConcat) == sizeof(CordRepSubstring), ""); +struct Rank1 {}; +struct Rank0 : Rank1 {}; + +template <typename Releaser, typename = ::absl::base_internal::invoke_result_t< + 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::invoke_result_t<Releaser>> +void InvokeReleaser(Rank1, Releaser&& releaser, absl::string_view) { + ::absl::base_internal::invoke(std::forward<Releaser>(releaser)); +} + +// We use CompressedTuple so that we can benefit from EBCO. +template <typename Releaser> +struct CordRepExternalImpl + : public CordRepExternal, + public ::absl::container_internal::CompressedTuple<Releaser> { + // The extra int arg is so that we can avoid interfering with copy/move + // constructors while still benefitting from perfect forwarding. + template <typename T> + CordRepExternalImpl(T&& releaser, int) + : CordRepExternalImpl::CompressedTuple(std::forward<T>(releaser)) { + this->releaser_invoker = &Release; + } + + ~CordRepExternalImpl() { + InvokeReleaser(Rank0{}, std::move(this->template get<0>()), + absl::string_view(base, length)); + } + + static void Release(CordRepExternal* rep) { + delete static_cast<CordRepExternalImpl*>(rep); + } +}; } // namespace cord_internal ABSL_NAMESPACE_END diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc index 41dbc97b..dcebb150 100644 --- a/absl/strings/string_view_test.cc +++ b/absl/strings/string_view_test.cc @@ -1177,7 +1177,7 @@ TEST(FindOneCharTest, EdgeCases) { EXPECT_EQ(absl::string_view::npos, a.rfind('x')); } -#ifndef THREAD_SANITIZER // Allocates too much memory for tsan. +#ifndef ABSL_HAVE_THREAD_SANITIZER // Allocates too much memory for tsan. TEST(HugeStringView, TwoPointTwoGB) { if (sizeof(size_t) <= 4) return; @@ -1191,7 +1191,7 @@ TEST(HugeStringView, TwoPointTwoGB) { sp.remove_suffix(2); EXPECT_EQ(size - 1 - 2, sp.length()); } -#endif // THREAD_SANITIZER +#endif // ABSL_HAVE_THREAD_SANITIZER #if !defined(NDEBUG) && !defined(ABSL_USES_STD_STRING_VIEW) TEST(NonNegativeLenTest, NonNegativeLen) { |