Commit 5169f3a4 by Martijn Vels Committed by Copybara-Service

Refactor InlineData to allow for memory sanitizer changes step 1

PiperOrigin-RevId: 500765473
Change-Id: Iaa3f9fdee6c9f4322bc8995b0d381cf1c8cb1349
parent 708873b4
...@@ -815,7 +815,7 @@ class Cord { ...@@ -815,7 +815,7 @@ class Cord {
InlineRep& operator=(const InlineRep& src); InlineRep& operator=(const InlineRep& src);
InlineRep& operator=(InlineRep&& src) noexcept; InlineRep& operator=(InlineRep&& src) noexcept;
explicit constexpr InlineRep(cord_internal::InlineData data); explicit constexpr InlineRep(absl::string_view sv, CordRep* rep);
void Swap(InlineRep* rhs); void Swap(InlineRep* rhs);
bool empty() const; bool empty() const;
...@@ -1106,8 +1106,8 @@ Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser) { ...@@ -1106,8 +1106,8 @@ Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser) {
return cord; return cord;
} }
constexpr Cord::InlineRep::InlineRep(cord_internal::InlineData data) constexpr Cord::InlineRep::InlineRep(absl::string_view sv, CordRep* rep)
: data_(data) {} : data_(sv, rep) {}
inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src)
: data_(InlineData::kDefaultInit) { : data_(InlineData::kDefaultInit) {
...@@ -1267,13 +1267,12 @@ inline Cord::Cord(absl::string_view src) ...@@ -1267,13 +1267,12 @@ inline Cord::Cord(absl::string_view src)
template <typename T> template <typename T>
constexpr Cord::Cord(strings_internal::StringConstant<T>) constexpr Cord::Cord(strings_internal::StringConstant<T>)
: contents_(strings_internal::StringConstant<T>::value.size() <= : contents_(strings_internal::StringConstant<T>::value,
strings_internal::StringConstant<T>::value.size() <=
cord_internal::kMaxInline cord_internal::kMaxInline
? cord_internal::InlineData( ? nullptr
strings_internal::StringConstant<T>::value) : &cord_internal::ConstInitExternalStorage<
: cord_internal::InlineData( strings_internal::StringConstant<T>>::value) {}
&cord_internal::ConstInitExternalStorage<
strings_internal::StringConstant<T>>::value)) {}
inline Cord& Cord::operator=(const Cord& x) { inline Cord& Cord::operator=(const Cord& x) {
contents_ = x.contents_; contents_ = x.contents_;
......
...@@ -430,12 +430,12 @@ constexpr char GetOrNull(absl::string_view data, size_t pos) { ...@@ -430,12 +430,12 @@ constexpr char GetOrNull(absl::string_view data, size_t pos) {
// We store cordz_info as 64 bit pointer value in little endian format. This // We store cordz_info as 64 bit pointer value in little endian format. This
// guarantees that the least significant byte of cordz_info matches the first // guarantees that the least significant byte of cordz_info matches the first
// byte of the inline data representation in as_chars_, which holds the inlined // byte of the inline data representation in `data`, which holds the inlined
// size or the 'is_tree' bit. // size or the 'is_tree' bit.
using cordz_info_t = int64_t; using cordz_info_t = int64_t;
// Assert that the `cordz_info` pointer value perfectly overlaps the last half // Assert that the `cordz_info` pointer value perfectly overlaps the last half
// of `as_chars_` and can hold a pointer value. // of `data` and can hold a pointer value.
static_assert(sizeof(cordz_info_t) * 2 == kMaxInline + 1, ""); static_assert(sizeof(cordz_info_t) * 2 == kMaxInline + 1, "");
static_assert(sizeof(cordz_info_t) >= sizeof(intptr_t), ""); static_assert(sizeof(cordz_info_t) >= sizeof(intptr_t), "");
...@@ -466,39 +466,31 @@ class InlineData { ...@@ -466,39 +466,31 @@ class InlineData {
// is actively inspected and used by gdb pretty printing code. // is actively inspected and used by gdb pretty printing code.
static constexpr size_t kTagOffset = 0; static constexpr size_t kTagOffset = 0;
constexpr InlineData() : as_chars_{0} {} constexpr InlineData() = default;
explicit InlineData(DefaultInitType) {} explicit InlineData(DefaultInitType) : rep_(kDefaultInit) {}
explicit constexpr InlineData(CordRep* rep) : as_tree_(rep) {} explicit InlineData(CordRep* rep) : rep_(rep) {}
explicit constexpr InlineData(absl::string_view chars)
: as_chars_{static_cast<char>((chars.size() << 1)), // Explicit constexpr constructor to create a constexpr InlineData
GetOrNull(chars, 0), // value. Creates an inlined SSO value if `rep` is null, otherwise
GetOrNull(chars, 1), // creates a tree instance value.
GetOrNull(chars, 2), constexpr InlineData(absl::string_view sv, CordRep* rep)
GetOrNull(chars, 3), : rep_(rep ? Rep(rep) : Rep(sv)) {}
GetOrNull(chars, 4),
GetOrNull(chars, 5), constexpr InlineData(const InlineData& rhs) = default;
GetOrNull(chars, 6), InlineData& operator=(const InlineData& rhs) = default;
GetOrNull(chars, 7),
GetOrNull(chars, 8),
GetOrNull(chars, 9),
GetOrNull(chars, 10),
GetOrNull(chars, 11),
GetOrNull(chars, 12),
GetOrNull(chars, 13),
GetOrNull(chars, 14)} {}
// Returns true if the current instance is empty. // Returns true if the current instance is empty.
// The 'empty value' is an inlined data value of zero length. // The 'empty value' is an inlined data value of zero length.
bool is_empty() const { return tag() == 0; } bool is_empty() const { return rep_.tag() == 0; }
// Returns true if the current instance holds a tree value. // Returns true if the current instance holds a tree value.
bool is_tree() const { return (tag() & 1) != 0; } bool is_tree() const { return (rep_.tag() & 1) != 0; }
// Returns true if the current instance holds a cordz_info value. // Returns true if the current instance holds a cordz_info value.
// Requires the current instance to hold a tree value. // Requires the current instance to hold a tree value.
bool is_profiled() const { bool is_profiled() const {
assert(is_tree()); assert(is_tree());
return as_tree_.cordz_info != kNullCordzInfo; return rep_.cordz_info() != kNullCordzInfo;
} }
// Returns true if either of the provided instances hold a cordz_info value. // Returns true if either of the provided instances hold a cordz_info value.
...@@ -507,7 +499,7 @@ class InlineData { ...@@ -507,7 +499,7 @@ class InlineData {
static bool is_either_profiled(const InlineData& data1, static bool is_either_profiled(const InlineData& data1,
const InlineData& data2) { const InlineData& data2) {
assert(data1.is_tree() && data2.is_tree()); assert(data1.is_tree() && data2.is_tree());
return (data1.as_tree_.cordz_info | data2.as_tree_.cordz_info) != return (data1.rep_.cordz_info() | data2.rep_.cordz_info()) !=
kNullCordzInfo; kNullCordzInfo;
} }
...@@ -517,7 +509,7 @@ class InlineData { ...@@ -517,7 +509,7 @@ class InlineData {
CordzInfo* cordz_info() const { CordzInfo* cordz_info() const {
assert(is_tree()); assert(is_tree());
intptr_t info = static_cast<intptr_t>(absl::little_endian::ToHost64( intptr_t info = static_cast<intptr_t>(absl::little_endian::ToHost64(
static_cast<uint64_t>(as_tree_.cordz_info))); static_cast<uint64_t>(rep_.cordz_info())));
assert(info & 1); assert(info & 1);
return reinterpret_cast<CordzInfo*>(info - 1); return reinterpret_cast<CordzInfo*>(info - 1);
} }
...@@ -528,21 +520,21 @@ class InlineData { ...@@ -528,21 +520,21 @@ class InlineData {
void set_cordz_info(CordzInfo* cordz_info) { void set_cordz_info(CordzInfo* cordz_info) {
assert(is_tree()); assert(is_tree());
uintptr_t info = reinterpret_cast<uintptr_t>(cordz_info) | 1; uintptr_t info = reinterpret_cast<uintptr_t>(cordz_info) | 1;
as_tree_.cordz_info = rep_.set_cordz_info(
static_cast<cordz_info_t>(absl::little_endian::FromHost64(info)); static_cast<cordz_info_t>(absl::little_endian::FromHost64(info)));
} }
// Resets the current cordz_info to null / empty. // Resets the current cordz_info to null / empty.
void clear_cordz_info() { void clear_cordz_info() {
assert(is_tree()); assert(is_tree());
as_tree_.cordz_info = kNullCordzInfo; rep_.set_cordz_info(kNullCordzInfo);
} }
// Returns a read only pointer to the character data inside this instance. // Returns a read only pointer to the character data inside this instance.
// Requires the current instance to hold inline data. // Requires the current instance to hold inline data.
const char* as_chars() const { const char* as_chars() const {
assert(!is_tree()); assert(!is_tree());
return &as_chars_[1]; return rep_.as_chars();
} }
// Returns a mutable pointer to the character data inside this instance. // Returns a mutable pointer to the character data inside this instance.
...@@ -560,35 +552,32 @@ class InlineData { ...@@ -560,35 +552,32 @@ class InlineData {
// //
// It's an error to read from the returned pointer without a preceding write // 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. // if the current instance does not hold inline data, i.e.: is_tree() == true.
char* as_chars() { return &as_chars_[1]; } char* as_chars() { return rep_.as_chars(); }
// Returns the tree value of this value. // Returns the tree value of this value.
// Requires the current instance to hold a tree value. // Requires the current instance to hold a tree value.
CordRep* as_tree() const { CordRep* as_tree() const {
assert(is_tree()); assert(is_tree());
return as_tree_.rep; return rep_.tree();
} }
// Initialize this instance to holding the tree value `rep`, // Initialize this instance to holding the tree value `rep`,
// initializing the cordz_info to null, i.e.: 'not profiled'. // initializing the cordz_info to null, i.e.: 'not profiled'.
void make_tree(CordRep* rep) { void make_tree(CordRep* rep) { rep_.make_tree(rep); }
as_tree_.rep = rep;
as_tree_.cordz_info = kNullCordzInfo;
}
// Set the tree value of this instance to 'rep`. // Set the tree value of this instance to 'rep`.
// Requires the current instance to already hold a tree value. // Requires the current instance to already hold a tree value.
// Does not affect the value of cordz_info. // Does not affect the value of cordz_info.
void set_tree(CordRep* rep) { void set_tree(CordRep* rep) {
assert(is_tree()); assert(is_tree());
as_tree_.rep = rep; rep_.set_tree(rep);
} }
// Returns the size of the inlined character data inside this instance. // Returns the size of the inlined character data inside this instance.
// Requires the current instance to hold inline data. // Requires the current instance to hold inline data.
size_t inline_size() const { size_t inline_size() const {
assert(!is_tree()); assert(!is_tree());
return static_cast<size_t>(tag()) >> 1; return static_cast<size_t>(rep_.tag()) >> 1;
} }
// Sets the size of the inlined character data inside this instance. // Sets the size of the inlined character data inside this instance.
...@@ -596,7 +585,7 @@ class InlineData { ...@@ -596,7 +585,7 @@ class InlineData {
// See the documentation on 'as_chars()' for more information and examples. // See the documentation on 'as_chars()' for more information and examples.
void set_inline_size(size_t size) { void set_inline_size(size_t size) {
ABSL_ASSERT(size <= kMaxInline); ABSL_ASSERT(size <= kMaxInline);
tag() = static_cast<int8_t>(size << 1); rep_.set_tag(static_cast<int8_t>(size << 1));
} }
// Compares 'this' inlined data with rhs. The comparison is a straightforward // Compares 'this' inlined data with rhs. The comparison is a straightforward
...@@ -623,24 +612,68 @@ class InlineData { ...@@ -623,24 +612,68 @@ class InlineData {
} }
private: private:
// See cordz_info_t for forced alignment and size of `cordz_info` details. struct Rep {
struct AsTree { // See cordz_info_t for forced alignment and size of `cordz_info` details.
explicit constexpr AsTree(absl::cord_internal::CordRep* tree) : rep(tree) {} struct AsTree {
cordz_info_t cordz_info = kNullCordzInfo; explicit constexpr AsTree(absl::cord_internal::CordRep* tree)
absl::cord_internal::CordRep* rep; : rep(tree) {}
}; cordz_info_t cordz_info = kNullCordzInfo;
absl::cord_internal::CordRep* rep;
int8_t& tag() { return reinterpret_cast<int8_t*>(this)[0]; } };
int8_t tag() const { return reinterpret_cast<const int8_t*>(this)[0]; }
explicit Rep(DefaultInitType) {}
constexpr Rep() : data{0} {}
constexpr Rep(const Rep&) = default;
constexpr Rep& operator=(const Rep&) = default;
explicit constexpr Rep(CordRep* rep) : as_tree(rep) {}
explicit constexpr Rep(absl::string_view chars)
: data{static_cast<char>((chars.size() << 1)),
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)} {}
int8_t tag() const { return reinterpret_cast<const int8_t*>(this)[0]; }
void set_tag(int8_t rhs) { reinterpret_cast<int8_t*>(this)[0] = rhs; }
char* as_chars() { return data + 1; }
const char* as_chars() const { return data + 1; }
CordRep* tree() const { return as_tree.rep; }
void set_tree(CordRep* rhs) { as_tree.rep = rhs; }
cordz_info_t cordz_info() const { return as_tree.cordz_info; }
void set_cordz_info(cordz_info_t rhs) { as_tree.cordz_info = rhs; }
void make_tree(CordRep* tree) {
as_tree.rep = tree;
as_tree.cordz_info = kNullCordzInfo;
}
// If the data has length <= kMaxInline, we store it in `as_chars_`, and // If the data has length <= kMaxInline, we store it in `data`, and
// store the size in the last char of `as_chars_` shifted left + 1. // store the size in the first char of `data` shifted left + 1.
// Else we store it in a tree and store a pointer to that tree in // 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`. // `as_tree.rep` with a tagged pointer to make `tag() & 1` non zero.
union { union {
char as_chars_[kMaxInline + 1]; char data[kMaxInline + 1];
AsTree as_tree_; AsTree as_tree;
};
}; };
Rep rep_;
}; };
static_assert(sizeof(InlineData) == kMaxInline + 1, ""); static_assert(sizeof(InlineData) == kMaxInline + 1, "");
......
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