Commit d84a9754 authored by Koji Ishii's avatar Koji Ishii Committed by Commit Bot

[FragmentItem] Make NGFragmentItems flexible array

This patch changes |NGFragmentItems| to flexible array,
saving the overhead of |Vector| and possible unused capacity.

Bug: 982194
Change-Id: I824ab7cfba8e707a2e8d41e2d54cbd85dee6f111
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2142888Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarKent Tamura <tkent@chromium.org>
Reviewed-by: default avatarIan Kilpatrick <ikilpatrick@chromium.org>
Commit-Queue: Koji Ishii <kojii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#758461}
parent b38768ab
...@@ -18,29 +18,49 @@ inline bool ShouldAssociateWithLayoutObject(const NGFragmentItem& item) { ...@@ -18,29 +18,49 @@ inline bool ShouldAssociateWithLayoutObject(const NGFragmentItem& item) {
} // namespace } // namespace
NGFragmentItems::NGFragmentItems(NGFragmentItemsBuilder* builder) NGFragmentItems::NGFragmentItems(NGFragmentItemsBuilder* builder)
: items_(std::move(builder->items_)), : text_content_(std::move(builder->text_content_)),
text_content_(std::move(builder->text_content_)), first_line_text_content_(std::move(builder->first_line_text_content_)),
first_line_text_content_(std::move(builder->first_line_text_content_)) {} size_(builder->items_.size()) {
Vector<scoped_refptr<const NGFragmentItem>>& source_items = builder->items_;
for (unsigned i = 0; i < size_; ++i) {
// Call the move constructor to move without |AddRef|. Items in
// |NGFragmentItemsBuilder| are not used after |this| was constructed.
DCHECK(source_items[i]);
new (&items_[i])
scoped_refptr<const NGFragmentItem>(std::move(source_items[i]));
DCHECK(!source_items[i]); // Ensure the source was moved.
}
}
NGFragmentItems::~NGFragmentItems() {
for (unsigned i = 0; i < size_; ++i)
items_[i]->Release();
}
bool NGFragmentItems::IsSubSpan(const Span& span) const {
return span.empty() ||
(span.data() >= ItemsData() && &span.back() < ItemsData() + Size());
}
void NGFragmentItems::AssociateWithLayoutObject() const { void NGFragmentItems::AssociateWithLayoutObject() const {
const Vector<scoped_refptr<const NGFragmentItem>>* items = &items_; DCHECK(std::all_of(Items().begin(), Items().end(), [](const auto& item) {
DCHECK(std::all_of(items->begin(), items->end(), [](const auto& item) {
return !item->DeltaToNextForSameLayoutObject(); return !item->DeltaToNextForSameLayoutObject();
})); }));
// items_[0] can be: // items_[0] can be:
// - kBox for list marker, e.g. <li>abc</li> // - kBox for list marker, e.g. <li>abc</li>
// - kLine for line, e.g. <div>abc</div> // - kLine for line, e.g. <div>abc</div>
// Calling get() is necessary below because operator<< in std::unique_ptr is DCHECK(Items().empty() || Items()[0]->IsContainer());
// a C++20 feature. if (Size() <= 1)
// TODO(https://crbug.com/980914): Drop .get() once we move to C++20. return;
DCHECK(items->IsEmpty() || (*items)[0]->IsContainer()) << (*items)[0].get();
HashMap<const LayoutObject*, wtf_size_t> last_fragment_map; HashMap<const LayoutObject*, wtf_size_t> last_fragment_map;
for (wtf_size_t index = 1u; index < items->size(); ++index) { const Span items = Items();
const NGFragmentItem& item = *(*items)[index]; wtf_size_t index = 0;
if (!ShouldAssociateWithLayoutObject(item)) for (const scoped_refptr<const NGFragmentItem>& item : items.subspan(1)) {
++index;
if (!ShouldAssociateWithLayoutObject(*item))
continue; continue;
LayoutObject* const layout_object = item.GetMutableLayoutObject(); LayoutObject* const layout_object = item->GetMutableLayoutObject();
DCHECK(layout_object->IsInLayoutNGInlineFormattingContext()) << item; DCHECK(layout_object->IsInLayoutNGInlineFormattingContext()) << *item;
auto insert_result = last_fragment_map.insert(layout_object, index); auto insert_result = last_fragment_map.insert(layout_object, index);
if (insert_result.is_new_entry) { if (insert_result.is_new_entry) {
DCHECK_EQ(layout_object->FirstInlineFragmentItemIndex(), 0u); DCHECK_EQ(layout_object->FirstInlineFragmentItemIndex(), 0u);
...@@ -49,20 +69,20 @@ void NGFragmentItems::AssociateWithLayoutObject() const { ...@@ -49,20 +69,20 @@ void NGFragmentItems::AssociateWithLayoutObject() const {
} }
const wtf_size_t last_index = insert_result.stored_value->value; const wtf_size_t last_index = insert_result.stored_value->value;
insert_result.stored_value->value = index; insert_result.stored_value->value = index;
DCHECK_GT(last_index, 0u) << item; DCHECK_GT(last_index, 0u) << *item;
DCHECK_LT(last_index, items->size()); DCHECK_LT(last_index, items.size());
DCHECK_LT(last_index, index); DCHECK_LT(last_index, index);
DCHECK_EQ((*items)[last_index]->DeltaToNextForSameLayoutObject(), 0u); DCHECK_EQ(items[last_index]->DeltaToNextForSameLayoutObject(), 0u);
(*items)[last_index]->SetDeltaToNextForSameLayoutObject(index - last_index); items[last_index]->SetDeltaToNextForSameLayoutObject(index - last_index);
} }
} }
void NGFragmentItems::ClearAssociatedFragments() const { void NGFragmentItems::ClearAssociatedFragments() const {
DCHECK(items_.IsEmpty() || items_[0]->IsContainer()); DCHECK(Items().empty() || Items()[0]->IsContainer());
if (items_.size() <= 1) if (Size() <= 1)
return; return;
LayoutObject* last_object = nullptr; LayoutObject* last_object = nullptr;
for (const auto& item : base::make_span(items_.begin() + 1, items_.end())) { for (const scoped_refptr<const NGFragmentItem>& item : Items().subspan(1)) {
if (!ShouldAssociateWithLayoutObject(*item)) { if (!ShouldAssociateWithLayoutObject(*item)) {
// These items are not associated and that no need to clear. // These items are not associated and that no need to clear.
DCHECK_EQ(item->DeltaToNextForSameLayoutObject(), 0u); DCHECK_EQ(item->DeltaToNextForSameLayoutObject(), 0u);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_FRAGMENT_ITEMS_H_ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_FRAGMENT_ITEMS_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_FRAGMENT_ITEMS_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_FRAGMENT_ITEMS_H_
#include "base/containers/span.h"
#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h"
...@@ -18,11 +19,14 @@ class NGFragmentItemsBuilder; ...@@ -18,11 +19,14 @@ class NGFragmentItemsBuilder;
// transformed to a flat list of |NGFragmentItem| and stored in this class. // transformed to a flat list of |NGFragmentItem| and stored in this class.
class CORE_EXPORT NGFragmentItems { class CORE_EXPORT NGFragmentItems {
public: public:
NGFragmentItems(NGFragmentItemsBuilder* builder); explicit NGFragmentItems(NGFragmentItemsBuilder* builder);
~NGFragmentItems();
const Vector<scoped_refptr<const NGFragmentItem>>& Items() const { wtf_size_t Size() const { return size_; }
return items_;
} using Span = base::span<const scoped_refptr<const NGFragmentItem>>;
Span Items() const { return base::make_span(ItemsData(), size_); }
bool IsSubSpan(const Span& span) const;
const String& Text(bool first_line) const { const String& Text(bool first_line) const {
return UNLIKELY(first_line) ? first_line_text_content_ : text_content_; return UNLIKELY(first_line) ? first_line_text_content_ : text_content_;
...@@ -36,11 +40,30 @@ class CORE_EXPORT NGFragmentItems { ...@@ -36,11 +40,30 @@ class CORE_EXPORT NGFragmentItems {
static void LayoutObjectWillBeDestroyed(const LayoutObject& layout_object); static void LayoutObjectWillBeDestroyed(const LayoutObject& layout_object);
static void LayoutObjectWillBeMoved(const LayoutObject& layout_object); static void LayoutObjectWillBeMoved(const LayoutObject& layout_object);
// The byte size of this instance.
constexpr static wtf_size_t ByteSizeFor(wtf_size_t count) {
return sizeof(NGFragmentItems) + count * sizeof(items_[0]);
}
wtf_size_t ByteSize() const { return ByteSizeFor(Size()); }
private: private:
// TODO(kojii): inline capacity TBD. const scoped_refptr<const NGFragmentItem>* ItemsData() const {
Vector<scoped_refptr<const NGFragmentItem>> items_; return reinterpret_cast<const scoped_refptr<const NGFragmentItem>*>(items_);
}
String text_content_; String text_content_;
String first_line_text_content_; String first_line_text_content_;
wtf_size_t size_;
// Semantically, |items_| is a flexible array of |scoped_refptr<const
// NGFragmentItem>|, but |scoped_refptr| has non-trivial destruction which
// causes an error in clang. Declare as a flexible array of |NGFragmentItem*|
// instead. Please see |ItemsData()|.
static_assert(
sizeof(NGFragmentItem*) == sizeof(scoped_refptr<const NGFragmentItem>),
"scoped_refptr must be the size of a pointer for |ItemsData()| to work");
NGFragmentItem* items_[];
}; };
} // namespace blink } // namespace blink
......
...@@ -154,7 +154,7 @@ void NGFragmentItemsBuilder::AddItems(const NGFragmentItems& items, ...@@ -154,7 +154,7 @@ void NGFragmentItemsBuilder::AddItems(const NGFragmentItems& items,
TextDirection direction, TextDirection direction,
const PhysicalSize& container_size) { const PhysicalSize& container_size) {
DCHECK(items_.IsEmpty()); DCHECK(items_.IsEmpty());
items_.AppendVector(items.Items()); items_.Append(items.Items().data(), items.Size());
// Convert offsets to logical. The logic is opposite to |ConvertToPhysical|. // Convert offsets to logical. The logic is opposite to |ConvertToPhysical|.
// This is needed because the container size may be different, in that case, // This is needed because the container size may be different, in that case,
......
...@@ -25,6 +25,8 @@ class CORE_EXPORT NGFragmentItemsBuilder { ...@@ -25,6 +25,8 @@ class CORE_EXPORT NGFragmentItemsBuilder {
public: public:
NGFragmentItemsBuilder(NGBoxFragmentBuilder* box_builder) {} NGFragmentItemsBuilder(NGBoxFragmentBuilder* box_builder) {}
wtf_size_t Size() const { return items_.size(); }
// Returns true if we have any floating descendants which need to be // Returns true if we have any floating descendants which need to be
// traversed during the float paint phase. // traversed during the float paint phase.
bool HasFloatingDescendantsForPaint() const { bool HasFloatingDescendantsForPaint() const {
......
...@@ -28,8 +28,7 @@ void NGInlineCursor::SetRoot(const NGFragmentItems& fragment_items, ...@@ -28,8 +28,7 @@ void NGInlineCursor::SetRoot(const NGFragmentItems& fragment_items,
DCHECK(!HasRoot()); DCHECK(!HasRoot());
fragment_items_ = &fragment_items; fragment_items_ = &fragment_items;
items_ = items; items_ = items;
DCHECK(items_.empty() || (items_.data() >= fragment_items_->Items().data() && DCHECK(fragment_items_->IsSubSpan(items_));
items_.data() < fragment_items_->Items().end()));
MoveToItem(items_.begin()); MoveToItem(items_.begin());
} }
...@@ -872,8 +871,8 @@ void NGInlineCursor::MoveTo(const NGInlineCursorPosition& position) { ...@@ -872,8 +871,8 @@ void NGInlineCursor::MoveTo(const NGInlineCursorPosition& position) {
inline unsigned NGInlineCursor::SpanIndexFromItemIndex(unsigned index) const { inline unsigned NGInlineCursor::SpanIndexFromItemIndex(unsigned index) const {
DCHECK(IsItemCursor()); DCHECK(IsItemCursor());
DCHECK_GE(items_.data(), fragment_items_->Items().data()); DCHECK(!items_.empty());
DCHECK_LT(items_.data(), fragment_items_->Items().end()); DCHECK(fragment_items_->IsSubSpan(items_));
if (items_.data() == fragment_items_->Items().data()) if (items_.data() == fragment_items_->Items().data())
return index; return index;
unsigned span_index = fragment_items_->Items().data() - items_.data() + index; unsigned span_index = fragment_items_->Items().data() - items_.data() + index;
......
...@@ -78,7 +78,7 @@ void GatherInlineContainerFragmentsFromLinebox( ...@@ -78,7 +78,7 @@ void GatherInlineContainerFragmentsFromLinebox(
} }
void GatherInlineContainerFragmentsFromItems( void GatherInlineContainerFragmentsFromItems(
const Vector<scoped_refptr<const NGFragmentItem>>& items, const NGFragmentItems::Span& items,
const PhysicalOffset& box_offset, const PhysicalOffset& box_offset,
NGBoxFragmentBuilder::InlineContainingBlockMap* inline_containing_block_map, NGBoxFragmentBuilder::InlineContainingBlockMap* inline_containing_block_map,
HashMap<const LayoutObject*, LineBoxPair>* containing_linebox_map) { HashMap<const LayoutObject*, LineBoxPair>* containing_linebox_map) {
......
...@@ -56,8 +56,8 @@ scoped_refptr<const NGPhysicalBoxFragment> NGPhysicalBoxFragment::Create( ...@@ -56,8 +56,8 @@ scoped_refptr<const NGPhysicalBoxFragment> NGPhysicalBoxFragment::Create(
sizeof(NGLink) * builder->children_.size() + sizeof(NGLink) * builder->children_.size() +
(borders.IsZero() ? 0 : sizeof(borders)) + (borders.IsZero() ? 0 : sizeof(borders)) +
(padding.IsZero() ? 0 : sizeof(padding)); (padding.IsZero() ? 0 : sizeof(padding));
if (builder->ItemsBuilder()) if (const NGFragmentItemsBuilder* items_builder = builder->ItemsBuilder())
byte_size += sizeof(NGFragmentItems); byte_size += NGFragmentItems::ByteSizeFor(items_builder->Size());
// We store the children list inline in the fragment as a flexible // We store the children list inline in the fragment as a flexible
// array. Therefore, we need to make sure to allocate enough space for // array. Therefore, we need to make sure to allocate enough space for
// that array here, which requires a manual allocation + placement new. // that array here, which requires a manual allocation + placement new.
......
...@@ -159,9 +159,10 @@ class CORE_EXPORT NGPhysicalBoxFragment final ...@@ -159,9 +159,10 @@ class CORE_EXPORT NGPhysicalBoxFragment final
const NGPhysicalBoxStrut* ComputeBordersAddress() const { const NGPhysicalBoxStrut* ComputeBordersAddress() const {
DCHECK(has_borders_ || has_padding_); DCHECK(has_borders_ || has_padding_);
const NGFragmentItems* items = ComputeItemsAddress(); const NGFragmentItems* items = ComputeItemsAddress();
if (has_fragment_items_) if (!has_fragment_items_)
++items; return reinterpret_cast<const NGPhysicalBoxStrut*>(items);
return reinterpret_cast<const NGPhysicalBoxStrut*>(items); return reinterpret_cast<const NGPhysicalBoxStrut*>(
reinterpret_cast<const uint8_t*>(items) + items->ByteSize());
} }
const NGPhysicalBoxStrut* ComputePaddingAddress() const { const NGPhysicalBoxStrut* ComputePaddingAddress() const {
......
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