Changeset 176528 in webkit


Ignore:
Timestamp:
Nov 24, 2014 4:15:46 PM (9 years ago)
Author:
Antti Koivisto
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.
Location:
trunk
Files:
2 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r176527 r176528  
     12014-11-24  Antti Koivisto  <antti@apple.com>
     2
     3        Avoid String concatenation with line break iterator
     4        https://bugs.webkit.org/show_bug.cgi?id=139034
     5
     6        Reviewed by Zalan Bujtas.
     7
     8        Test that there is no implicit break point between Text nodes.
     9
     10        * fast/text/simple-lines-multiple-renderers-break-expected.html: Added.
     11        * fast/text/simple-lines-multiple-renderers-break.html: Added.
     12
    1132014-11-24  Zalan Bujtas  <zalan@apple.com>
    214
  • trunk/Source/WebCore/ChangeLog

    r176527 r176528  
     12014-11-24  Antti Koivisto  <antti@apple.com>
     2
     3        Avoid String concatenation with line break iterator
     4        https://bugs.webkit.org/show_bug.cgi?id=139034
     5
     6        Reviewed by Zalan Bujtas.
     7
     8        Test: fast/text/simple-lines-multiple-renderers-break.html
     9
     10        * rendering/SimpleLineLayoutFlowContents.cpp:
     11        (WebCore::SimpleLineLayout::initializeSegments):
     12
     13            Include String too so it doesn't need to be fetched from the renderer.
     14
     15        (WebCore::SimpleLineLayout::FlowContents::FlowContents):
     16        (WebCore::SimpleLineLayout::FlowContents::findNextBreakablePosition):
     17
     18            Make this iterative instead of recursive.
     19            Uset setPriorContext to provide previous characters instead of concatenating
     20            the string from all the previous segments.
     21
     22        (WebCore::SimpleLineLayout::findNextNonWhitespace):
     23        (WebCore::SimpleLineLayout::FlowContents::findNextNonWhitespacePosition):
     24
     25            Search using segments instead of the concatenated string.
     26
     27        (WebCore::SimpleLineLayout::FlowContents::textWidth):
     28        (WebCore::SimpleLineLayout::FlowContents::segmentIndexForPositionSlow):
     29        (WebCore::SimpleLineLayout::FlowContents::runWidth):
     30        (WebCore::SimpleLineLayout::FlowContents::segmentForPositionSlow): Deleted.
     31        (WebCore::SimpleLineLayout::FlowContents::appendNextRendererContentIfNeeded): Deleted.
     32        (WebCore::SimpleLineLayout::FlowContents::nextNonWhitespacePosition): Deleted.
     33        * rendering/SimpleLineLayoutFlowContents.h:
     34        (WebCore::SimpleLineLayout::FlowContents::characterAt):
     35        (WebCore::SimpleLineLayout::FlowContents::isNewlineCharacter):
     36        (WebCore::SimpleLineLayout::FlowContents::segmentIndexForPosition):
     37        (WebCore::SimpleLineLayout::FlowContents::segmentForPosition):
     38
    1392014-11-24  Zalan Bujtas  <zalan@apple.com>
    240
  • trunk/Source/WebCore/rendering/SimpleLineLayoutFlowContents.cpp

    r176510 r176528  
    5353    for (auto& textChild : childrenOfType<RenderText>(flow)) {
    5454        unsigned textLength = textChild.text()->length();
    55         segments.append(FlowContents::Segment { startPosition, startPosition + textLength, textChild });
     55        segments.append(FlowContents::Segment { startPosition, startPosition + textLength, textChild.text(), textChild });
    5656        startPosition += textLength;
    5757    }
     
    6262    : m_style(flow.style())
    6363    , m_segments(initializeSegments(flow))
    64     , m_lineBreakIterator(downcast<RenderText>(*flow.firstChild()).text(), flow.style().locale())
     64    , m_lineBreakIterator(m_segments[0].text, flow.style().locale())
    6565    , m_lastSegmentIndex(0)
    6666{
     
    6969unsigned FlowContents::findNextBreakablePosition(unsigned position) const
    7070{
    71     String string = m_lineBreakIterator.string();
    72     unsigned breakablePosition = nextBreakablePositionNonLoosely<LChar, NBSPBehavior::IgnoreNBSP>(m_lineBreakIterator, string.characters8(), string.length(), position);
    73     if (appendNextRendererContentIfNeeded(breakablePosition))
    74         return findNextBreakablePosition(position);
    75     ASSERT(breakablePosition >= position);
    76     return breakablePosition;
     71    while (!isEnd(position)) {
     72        auto& segment = segmentForPosition(position);
     73        if (segment.text.impl() != m_lineBreakIterator.string().impl()) {
     74            UChar lastCharacter = segment.start > 0 ? characterAt(segment.start - 1) : 0;
     75            UChar secondToLastCharacter = segment.start > 1 ? characterAt(segment.start - 2) : 0;
     76            m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter);
     77            m_lineBreakIterator.resetStringAndReleaseIterator(segment.text, m_style.locale, LineBreakIteratorModeUAX14);
     78        }
     79
     80        auto* characters = segment.text.characters8();
     81        unsigned segmentLength = segment.end - segment.start;
     82        unsigned segmentPosition = position - segment.start;
     83        unsigned breakable = nextBreakablePositionNonLoosely<LChar, NBSPBehavior::IgnoreNBSP>(m_lineBreakIterator, characters, segmentLength, segmentPosition);
     84        position = segment.start + breakable;
     85        if (position < segment.end)
     86            break;
     87    }
     88    return position;
     89}
     90
     91static bool findNextNonWhitespace(const FlowContents::Segment& segment, const FlowContents::Style& style, unsigned& position, unsigned& spaceCount)
     92{
     93    const LChar* text = segment.text.characters8();
     94    for (; position < segment.end; ++position) {
     95        auto character = text[position - segment.start];
     96        bool isSpace = character == ' ';
     97        bool isWhitespace = isSpace || character == '\t' || (!style.preserveNewline && character == '\n');
     98        if (!isWhitespace)
     99            return true;
     100        if (isSpace)
     101            ++spaceCount;
     102    }
     103    return false;
    77104}
    78105
    79106unsigned FlowContents::findNextNonWhitespacePosition(unsigned position, unsigned& spaceCount) const
    80107{
    81     unsigned nonWhitespacePosition = nextNonWhitespacePosition(position, spaceCount);
    82     if (appendNextRendererContentIfNeeded(nonWhitespacePosition))
    83         return findNextNonWhitespacePosition(position, spaceCount);
    84     ASSERT(nonWhitespacePosition >= position);
    85     return nonWhitespacePosition;
     108    for (unsigned i = segmentIndexForPosition(position); i < m_segments.size(); ++i) {
     109        if (findNextNonWhitespace(m_segments[i], m_style, position, spaceCount))
     110            break;
     111    }
     112    return position;
    86113}
    87114
     
    98125    while (true) {
    99126        fragmentEnd = std::min(to, segment->end);
    100         textWidth += runWidth(segment->renderer, from - segment->start, fragmentEnd - segment->start, xPosition + textWidth);
     127        textWidth += runWidth(segment->text, from - segment->start, fragmentEnd - segment->start, xPosition + textWidth);
    101128        if (fragmentEnd == to)
    102129            break;
     
    108135}
    109136
    110 const FlowContents::Segment& FlowContents::segmentForPositionSlow(unsigned position) const
     137unsigned FlowContents::segmentIndexForPositionSlow(unsigned position) const
    111138{
    112139    auto it = std::lower_bound(m_segments.begin(), m_segments.end(), position, [](const Segment& segment, unsigned position) {
     
    114141    });
    115142    ASSERT(it != m_segments.end());
    116     m_lastSegmentIndex = it - m_segments.begin();
    117     return *it;
     143    auto index = it - m_segments.begin();
     144    m_lastSegmentIndex = index;
     145    return index;
    118146}
    119147
     
    128156}
    129157
    130 bool FlowContents::appendNextRendererContentIfNeeded(unsigned position) const
    131 {
    132     if (isEnd(position))
    133         return false;
    134     String string = m_lineBreakIterator.string();
    135     if (position < string.length())
    136         return false;
    137 
    138     // Content needs to be requested sequentially.
    139     ASSERT(position == string.length());
    140     auto& segment = segmentForPosition(position);
    141 
    142     m_lineBreakIterator.resetStringAndReleaseIterator(string + String(segment.renderer.text()), m_style.locale, LineBreakIteratorModeUAX14);
    143     return true;
    144 }
    145 
    146 unsigned FlowContents::nextNonWhitespacePosition(unsigned position, unsigned& spaceCount) const
    147 {
    148     String string = m_lineBreakIterator.string();
    149     unsigned length = string.length();
    150     const LChar* text = string.characters8();
    151     for (; position < length; ++position) {
    152         bool isSpace = text[position] == ' ';
    153         if (!(isSpace || text[position] == '\t' || (!m_style.preserveNewline && text[position] == '\n')))
    154             return position;
    155         if (isSpace)
    156             ++spaceCount;
    157     }
    158     return length;
    159 }
    160 
    161 float FlowContents::runWidth(const RenderText& renderer, unsigned from, unsigned to, float xPosition) const
     158float FlowContents::runWidth(const String& text, unsigned from, unsigned to, float xPosition) const
    162159{
    163160    ASSERT(from < to);
    164     String string = renderer.text();
    165     bool measureWithEndSpace = m_style.collapseWhitespace && to < string.length() && string[to] == ' ';
     161    bool measureWithEndSpace = m_style.collapseWhitespace && to < text.length() && text[to] == ' ';
    166162    if (measureWithEndSpace)
    167163        ++to;
    168     TextRun run(string.characters8() + from, to - from);
     164    TextRun run(text.characters8() + from, to - from);
    169165    run.setXPos(xPosition);
    170166    run.setTabSize(!!m_style.tabWidth, m_style.tabWidth);
  • trunk/Source/WebCore/rendering/SimpleLineLayoutFlowContents.h

    r176510 r176528  
    5252        unsigned start;
    5353        unsigned end;
     54        String text;
    5455        const RenderText& renderer;
    5556    };
     
    7677
    7778private:
    78     const Segment& segmentForPositionSlow(unsigned) const;
    79     bool appendNextRendererContentIfNeeded(unsigned position) const;
    80     unsigned nextNonWhitespacePosition(unsigned position, unsigned& spaceCount) const;
    81     float runWidth(const RenderText&, unsigned from, unsigned to, float xPosition) const;
     79    unsigned segmentIndexForPosition(unsigned position) const;
     80    unsigned segmentIndexForPositionSlow(unsigned position) const;
     81
     82    UChar characterAt(unsigned position) const;
     83    float runWidth(const String&, unsigned from, unsigned to, float xPosition) const;
    8284
    8385    const Style m_style;
     
    8890};
    8991
     92inline UChar FlowContents::characterAt(unsigned position) const
     93{
     94    auto& segment = segmentForPosition(position);
     95    return segment.text[position - segment.start];
     96}
     97
    9098inline bool FlowContents::isNewlineCharacter(unsigned position) const
    9199{
    92     appendNextRendererContentIfNeeded(position);
    93     ASSERT(position < m_lineBreakIterator.string().length());
    94     return m_lineBreakIterator.string().at(position) == '\n';
     100    return characterAt(position) == '\n';
    95101}
    96102
     
    100106}
    101107
     108inline unsigned FlowContents::segmentIndexForPosition(unsigned position) const
     109{
     110    ASSERT(!isEnd(position));
     111    auto& lastSegment = m_segments[m_lastSegmentIndex];
     112    if (lastSegment.start <= position && position < lastSegment.end)
     113        return m_lastSegmentIndex;
     114    return segmentIndexForPositionSlow(position);
     115}
     116
    102117inline const FlowContents::Segment& FlowContents::segmentForPosition(unsigned position) const
    103118{
    104     ASSERT(!isEnd(position));
    105 
    106     auto& lastSegment = m_segments[m_lastSegmentIndex];
    107     if (lastSegment.start <= position && position < lastSegment.end)
    108         return lastSegment;
    109     return segmentForPositionSlow(position);
     119    return m_segments[segmentIndexForPosition(position)];
    110120}
    111121
Note: See TracChangeset for help on using the changeset viewer.