Title: [176528] trunk
Revision
176528
Author
[email protected]
Date
2014-11-24 16:15:46 -0800 (Mon, 24 Nov 2014)

Log Message

Avoid String concatenation with line break iterator
https://bugs.webkit.org/show_bug.cgi?id=139034

Reviewed by Zalan Bujtas.

Source/WebCore:

Test: fast/text/simple-lines-multiple-renderers-break.html

* rendering/SimpleLineLayoutFlowContents.cpp:
(WebCore::SimpleLineLayout::initializeSegments):

    Include String too so it doesn't need to be fetched from the renderer.

(WebCore::SimpleLineLayout::FlowContents::FlowContents):
(WebCore::SimpleLineLayout::FlowContents::findNextBreakablePosition):

    Make this iterative instead of recursive.
    Uset setPriorContext to provide previous characters instead of concatenating
    the string from all the previous segments.

(WebCore::SimpleLineLayout::findNextNonWhitespace):
(WebCore::SimpleLineLayout::FlowContents::findNextNonWhitespacePosition):

    Search using segments instead of the concatenated string.

(WebCore::SimpleLineLayout::FlowContents::textWidth):
(WebCore::SimpleLineLayout::FlowContents::segmentIndexForPositionSlow):
(WebCore::SimpleLineLayout::FlowContents::runWidth):
(WebCore::SimpleLineLayout::FlowContents::segmentForPositionSlow): Deleted.
(WebCore::SimpleLineLayout::FlowContents::appendNextRendererContentIfNeeded): Deleted.
(WebCore::SimpleLineLayout::FlowContents::nextNonWhitespacePosition): Deleted.
* rendering/SimpleLineLayoutFlowContents.h:
(WebCore::SimpleLineLayout::FlowContents::characterAt):
(WebCore::SimpleLineLayout::FlowContents::isNewlineCharacter):
(WebCore::SimpleLineLayout::FlowContents::segmentIndexForPosition):
(WebCore::SimpleLineLayout::FlowContents::segmentForPosition):

LayoutTests:

Test that there is no implicit break point between Text nodes.

* fast/text/simple-lines-multiple-renderers-break-expected.html: Added.
* fast/text/simple-lines-multiple-renderers-break.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (176527 => 176528)


--- trunk/LayoutTests/ChangeLog	2014-11-25 00:10:17 UTC (rev 176527)
+++ trunk/LayoutTests/ChangeLog	2014-11-25 00:15:46 UTC (rev 176528)
@@ -1,3 +1,15 @@
+2014-11-24  Antti Koivisto  <[email protected]>
+
+        Avoid String concatenation with line break iterator
+        https://bugs.webkit.org/show_bug.cgi?id=139034
+
+        Reviewed by Zalan Bujtas.
+
+        Test that there is no implicit break point between Text nodes.
+
+        * fast/text/simple-lines-multiple-renderers-break-expected.html: Added.
+        * fast/text/simple-lines-multiple-renderers-break.html: Added.
+
 2014-11-24  Zalan Bujtas  <[email protected]>
 
         SimpleLineLayout::canUseFor() should iterate through RenderTexts to check if their content is eligible for simple line layout.

Added: trunk/LayoutTests/fast/text/simple-lines-multiple-renderers-break-expected.html (0 => 176528)


--- trunk/LayoutTests/fast/text/simple-lines-multiple-renderers-break-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/text/simple-lines-multiple-renderers-break-expected.html	2014-11-25 00:15:46 UTC (rev 176528)
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+</script>
+</head>
+<body>
+<div style="width:300px; border: 2px solid green">
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+</div>
+</body>
+</html>

Added: trunk/LayoutTests/fast/text/simple-lines-multiple-renderers-break.html (0 => 176528)


--- trunk/LayoutTests/fast/text/simple-lines-multiple-renderers-break.html	                        (rev 0)
+++ trunk/LayoutTests/fast/text/simple-lines-multiple-renderers-break.html	2014-11-25 00:15:46 UTC (rev 176528)
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test that there is no break point between simple line layout segments.</title>
+</head>
+<body>
+<div id=container style="width:300px; border: 2px solid green">
+</div>
+<script>
+var container = document.getElementById("container");
+container.appendChild(document.createTextNode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
+container.appendChild(document.createTextNode("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"));
+</script>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (176527 => 176528)


--- trunk/Source/WebCore/ChangeLog	2014-11-25 00:10:17 UTC (rev 176527)
+++ trunk/Source/WebCore/ChangeLog	2014-11-25 00:15:46 UTC (rev 176528)
@@ -1,3 +1,41 @@
+2014-11-24  Antti Koivisto  <[email protected]>
+
+        Avoid String concatenation with line break iterator
+        https://bugs.webkit.org/show_bug.cgi?id=139034
+
+        Reviewed by Zalan Bujtas.
+
+        Test: fast/text/simple-lines-multiple-renderers-break.html
+
+        * rendering/SimpleLineLayoutFlowContents.cpp:
+        (WebCore::SimpleLineLayout::initializeSegments):
+
+            Include String too so it doesn't need to be fetched from the renderer.
+
+        (WebCore::SimpleLineLayout::FlowContents::FlowContents):
+        (WebCore::SimpleLineLayout::FlowContents::findNextBreakablePosition):
+
+            Make this iterative instead of recursive.
+            Uset setPriorContext to provide previous characters instead of concatenating
+            the string from all the previous segments.
+
+        (WebCore::SimpleLineLayout::findNextNonWhitespace):
+        (WebCore::SimpleLineLayout::FlowContents::findNextNonWhitespacePosition):
+
+            Search using segments instead of the concatenated string.
+
+        (WebCore::SimpleLineLayout::FlowContents::textWidth):
+        (WebCore::SimpleLineLayout::FlowContents::segmentIndexForPositionSlow):
+        (WebCore::SimpleLineLayout::FlowContents::runWidth):
+        (WebCore::SimpleLineLayout::FlowContents::segmentForPositionSlow): Deleted.
+        (WebCore::SimpleLineLayout::FlowContents::appendNextRendererContentIfNeeded): Deleted.
+        (WebCore::SimpleLineLayout::FlowContents::nextNonWhitespacePosition): Deleted.
+        * rendering/SimpleLineLayoutFlowContents.h:
+        (WebCore::SimpleLineLayout::FlowContents::characterAt):
+        (WebCore::SimpleLineLayout::FlowContents::isNewlineCharacter):
+        (WebCore::SimpleLineLayout::FlowContents::segmentIndexForPosition):
+        (WebCore::SimpleLineLayout::FlowContents::segmentForPosition):
+
 2014-11-24  Zalan Bujtas  <[email protected]>
 
         SimpleLineLayout::canUseFor() should iterate through RenderTexts to check if their content is eligible for simple line layout.

Modified: trunk/Source/WebCore/rendering/SimpleLineLayoutFlowContents.cpp (176527 => 176528)


--- trunk/Source/WebCore/rendering/SimpleLineLayoutFlowContents.cpp	2014-11-25 00:10:17 UTC (rev 176527)
+++ trunk/Source/WebCore/rendering/SimpleLineLayoutFlowContents.cpp	2014-11-25 00:15:46 UTC (rev 176528)
@@ -52,7 +52,7 @@
     unsigned startPosition = 0;
     for (auto& textChild : childrenOfType<RenderText>(flow)) {
         unsigned textLength = textChild.text()->length();
-        segments.append(FlowContents::Segment { startPosition, startPosition + textLength, textChild });
+        segments.append(FlowContents::Segment { startPosition, startPosition + textLength, textChild.text(), textChild });
         startPosition += textLength;
     }
     return segments;
@@ -61,28 +61,55 @@
 FlowContents::FlowContents(const RenderBlockFlow& flow)
     : m_style(flow.style())
     , m_segments(initializeSegments(flow))
-    , m_lineBreakIterator(downcast<RenderText>(*flow.firstChild()).text(), flow.style().locale())
+    , m_lineBreakIterator(m_segments[0].text, flow.style().locale())
     , m_lastSegmentIndex(0)
 {
 }
 
 unsigned FlowContents::findNextBreakablePosition(unsigned position) const
 {
-    String string = m_lineBreakIterator.string();
-    unsigned breakablePosition = nextBreakablePositionNonLoosely<LChar, NBSPBehavior::IgnoreNBSP>(m_lineBreakIterator, string.characters8(), string.length(), position);
-    if (appendNextRendererContentIfNeeded(breakablePosition))
-        return findNextBreakablePosition(position);
-    ASSERT(breakablePosition >= position);
-    return breakablePosition;
+    while (!isEnd(position)) {
+        auto& segment = segmentForPosition(position);
+        if (segment.text.impl() != m_lineBreakIterator.string().impl()) {
+            UChar lastCharacter = segment.start > 0 ? characterAt(segment.start - 1) : 0;
+            UChar secondToLastCharacter = segment.start > 1 ? characterAt(segment.start - 2) : 0;
+            m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter);
+            m_lineBreakIterator.resetStringAndReleaseIterator(segment.text, m_style.locale, LineBreakIteratorModeUAX14);
+        }
+
+        auto* characters = segment.text.characters8();
+        unsigned segmentLength = segment.end - segment.start;
+        unsigned segmentPosition = position - segment.start;
+        unsigned breakable = nextBreakablePositionNonLoosely<LChar, NBSPBehavior::IgnoreNBSP>(m_lineBreakIterator, characters, segmentLength, segmentPosition);
+        position = segment.start + breakable;
+        if (position < segment.end)
+            break;
+    }
+    return position;
 }
 
+static bool findNextNonWhitespace(const FlowContents::Segment& segment, const FlowContents::Style& style, unsigned& position, unsigned& spaceCount)
+{
+    const LChar* text = segment.text.characters8();
+    for (; position < segment.end; ++position) {
+        auto character = text[position - segment.start];
+        bool isSpace = character == ' ';
+        bool isWhitespace = isSpace || character == '\t' || (!style.preserveNewline && character == '\n');
+        if (!isWhitespace)
+            return true;
+        if (isSpace)
+            ++spaceCount;
+    }
+    return false;
+}
+
 unsigned FlowContents::findNextNonWhitespacePosition(unsigned position, unsigned& spaceCount) const
 {
-    unsigned nonWhitespacePosition = nextNonWhitespacePosition(position, spaceCount);
-    if (appendNextRendererContentIfNeeded(nonWhitespacePosition))
-        return findNextNonWhitespacePosition(position, spaceCount);
-    ASSERT(nonWhitespacePosition >= position);
-    return nonWhitespacePosition;
+    for (unsigned i = segmentIndexForPosition(position); i < m_segments.size(); ++i) {
+        if (findNextNonWhitespace(m_segments[i], m_style, position, spaceCount))
+            break;
+    }
+    return position;
 }
 
 float FlowContents::textWidth(unsigned from, unsigned to, float xPosition) const
@@ -97,7 +124,7 @@
     unsigned fragmentEnd = 0;
     while (true) {
         fragmentEnd = std::min(to, segment->end);
-        textWidth += runWidth(segment->renderer, from - segment->start, fragmentEnd - segment->start, xPosition + textWidth);
+        textWidth += runWidth(segment->text, from - segment->start, fragmentEnd - segment->start, xPosition + textWidth);
         if (fragmentEnd == to)
             break;
         from = fragmentEnd;
@@ -107,14 +134,15 @@
     return textWidth;
 }
 
-const FlowContents::Segment& FlowContents::segmentForPositionSlow(unsigned position) const
+unsigned FlowContents::segmentIndexForPositionSlow(unsigned position) const
 {
     auto it = std::lower_bound(m_segments.begin(), m_segments.end(), position, [](const Segment& segment, unsigned position) {
         return segment.end <= position;
     });
     ASSERT(it != m_segments.end());
-    m_lastSegmentIndex = it - m_segments.begin();
-    return *it;
+    auto index = it - m_segments.begin();
+    m_lastSegmentIndex = index;
+    return index;
 }
 
 const FlowContents::Segment& FlowContents::segmentForRenderer(const RenderText& renderer) const
@@ -127,45 +155,13 @@
     return m_segments.last();
 }
 
-bool FlowContents::appendNextRendererContentIfNeeded(unsigned position) const
+float FlowContents::runWidth(const String& text, unsigned from, unsigned to, float xPosition) const
 {
-    if (isEnd(position))
-        return false;
-    String string = m_lineBreakIterator.string();
-    if (position < string.length())
-        return false;
-
-    // Content needs to be requested sequentially.
-    ASSERT(position == string.length());
-    auto& segment = segmentForPosition(position);
-
-    m_lineBreakIterator.resetStringAndReleaseIterator(string + String(segment.renderer.text()), m_style.locale, LineBreakIteratorModeUAX14);
-    return true;
-}
-
-unsigned FlowContents::nextNonWhitespacePosition(unsigned position, unsigned& spaceCount) const
-{
-    String string = m_lineBreakIterator.string();
-    unsigned length = string.length();
-    const LChar* text = string.characters8();
-    for (; position < length; ++position) {
-        bool isSpace = text[position] == ' ';
-        if (!(isSpace || text[position] == '\t' || (!m_style.preserveNewline && text[position] == '\n')))
-            return position;
-        if (isSpace)
-            ++spaceCount;
-    }
-    return length;
-}
-
-float FlowContents::runWidth(const RenderText& renderer, unsigned from, unsigned to, float xPosition) const
-{
     ASSERT(from < to);
-    String string = renderer.text();
-    bool measureWithEndSpace = m_style.collapseWhitespace && to < string.length() && string[to] == ' ';
+    bool measureWithEndSpace = m_style.collapseWhitespace && to < text.length() && text[to] == ' ';
     if (measureWithEndSpace)
         ++to;
-    TextRun run(string.characters8() + from, to - from);
+    TextRun run(text.characters8() + from, to - from);
     run.setXPos(xPosition);
     run.setTabSize(!!m_style.tabWidth, m_style.tabWidth);
     float width = m_style.font.width(run);

Modified: trunk/Source/WebCore/rendering/SimpleLineLayoutFlowContents.h (176527 => 176528)


--- trunk/Source/WebCore/rendering/SimpleLineLayoutFlowContents.h	2014-11-25 00:10:17 UTC (rev 176527)
+++ trunk/Source/WebCore/rendering/SimpleLineLayoutFlowContents.h	2014-11-25 00:15:46 UTC (rev 176528)
@@ -51,6 +51,7 @@
     struct Segment {
         unsigned start;
         unsigned end;
+        String text;
         const RenderText& renderer;
     };
     const Segment& segmentForPosition(unsigned) const;
@@ -75,11 +76,12 @@
     const Style& style() const { return m_style; }
 
 private:
-    const Segment& segmentForPositionSlow(unsigned) const;
-    bool appendNextRendererContentIfNeeded(unsigned position) const;
-    unsigned nextNonWhitespacePosition(unsigned position, unsigned& spaceCount) const;
-    float runWidth(const RenderText&, unsigned from, unsigned to, float xPosition) const;
+    unsigned segmentIndexForPosition(unsigned position) const;
+    unsigned segmentIndexForPositionSlow(unsigned position) const;
 
+    UChar characterAt(unsigned position) const;
+    float runWidth(const String&, unsigned from, unsigned to, float xPosition) const;
+
     const Style m_style;
     const Vector<Segment, 8> m_segments;
 
@@ -87,11 +89,15 @@
     mutable unsigned m_lastSegmentIndex;
 };
 
+inline UChar FlowContents::characterAt(unsigned position) const
+{
+    auto& segment = segmentForPosition(position);
+    return segment.text[position - segment.start];
+}
+
 inline bool FlowContents::isNewlineCharacter(unsigned position) const
 {
-    appendNextRendererContentIfNeeded(position);
-    ASSERT(position < m_lineBreakIterator.string().length());
-    return m_lineBreakIterator.string().at(position) == '\n';
+    return characterAt(position) == '\n';
 }
 
 inline bool FlowContents::isEnd(unsigned position) const
@@ -99,17 +105,21 @@
     return position >= length();
 }
 
-inline const FlowContents::Segment& FlowContents::segmentForPosition(unsigned position) const
+inline unsigned FlowContents::segmentIndexForPosition(unsigned position) const
 {
     ASSERT(!isEnd(position));
-
     auto& lastSegment = m_segments[m_lastSegmentIndex];
     if (lastSegment.start <= position && position < lastSegment.end)
-        return lastSegment;
-    return segmentForPositionSlow(position);
+        return m_lastSegmentIndex;
+    return segmentIndexForPositionSlow(position);
 }
 
+inline const FlowContents::Segment& FlowContents::segmentForPosition(unsigned position) const
+{
+    return m_segments[segmentIndexForPosition(position)];
 }
+
 }
+}
 
 #endif
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to