Commit 5bee2940 authored by eric@webkit.org's avatar eric@webkit.org

2011-04-04 Eric Seidel <eric@webkit.org>

        Reviewed by Ryosuke Niwa.

        Split run storage out from BidiResolver into a new BidiRunList class
        https://bugs.webkit.org/show_bug.cgi?id=57764

        Part of what makes BidiResolver and InlineIterator so difficult to understand
        (and bug 50912 so difficult to fix) is that BidiResolver is both a state machine
        for the Unicode Bidi Algorithm (UBA) as well as storage for the resulting
        BidiRuns from the algorithm.  This patch breaks the storage aspect off
        into its own class BidiRunList.

        This patch is only a start.  It does not actually fix the problematic ownership
        relationship, but it does make it possible to fix such in a second patch.

        The run pointers and addRun/prependRun, etc. were already a tightly coupled
        logical subset of the BidiResolver class, so moving them into their own class
        was both obvious and easy.  The only piece of logic I had to move was that
        deleteRuns() had a side-effect of setting the m_emptyRun bit on the resolver.

        I believe this deleteRuns side-effect was only ever used for one place
        (right after findNextLineBreak) and that it's only needed because
        findNextLineBreak can sometimes create bidi runs.  Run creation appears to be
        an unintentional side-effect of how InlineIterator calls through to BidiResolver
        as part of bidiNext and not a desired effect of the code.  I have added the call
        to markCurrentRunEmpty to both places deleteRuns was called (where the resolver
        could get re-used) as a safety precaution.  We could replace both with ASSERTs
        in a later patch.

        I suspect there may be a small performance win from further refactoring so that
        findNextLineBreak does not need to create BidiRuns.

        As I commented in the code, callers should own their own BidiRunList which they
        pass to BidiResolver::createBidiRunsForLine.  I attempted to implement that in
        an earlier version of this patch, but it was too complicated with the current
        twisted dependencies between InlineIterator/bidiNext and InlineBidiResolver.
        raise/lowerExplicitEmbeddingLevel are called unconditionally
        from commitExplicitEmbedding (which is called by bidiNext) and expect to have
        access to a runs list even in cases where we don't want any runs (findNextLineBreak).

        I also cleaned up some of the callers to pass around BidiRunList objects instead
        of InlineBidiResolvers now that they're separate objects.

        * GNUmakefile.am:
        * WebCore.gypi:
        * WebCore.pro:
        * WebCore.vcproj/WebCore.vcproj:
        * WebCore.xcodeproj/project.pbxproj:
        * platform/graphics/GraphicsContext.cpp:
        (WebCore::GraphicsContext::drawBidiText):
        * platform/text/BidiResolver.h:
        (WebCore::BidiResolver::BidiResolver):
        (WebCore::BidiResolver::runs):
        (WebCore::BidiResolver::markCurrentRunEmpty):
        (WebCore::::appendRun):
        (WebCore::::lowerExplicitEmbeddingLevel):
        (WebCore::::raiseExplicitEmbeddingLevel):
        (WebCore::::reorderRunsFromLevels):
        (WebCore::::createBidiRunsForLine):
        * rendering/InlineIterator.h:
        (WebCore::InlineBidiResolver::appendRun):
        * rendering/RenderBlock.h:
        * rendering/RenderBlockLineLayout.cpp:
        (WebCore::createRun):
        (WebCore::RenderBlock::appendRunsForObject):
        (WebCore::reachedEndOfTextRenderer):
        (WebCore::RenderBlock::handleTrailingSpaces):
        (WebCore::RenderBlock::layoutInlineChildren):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@83240 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 90e26174
2011-04-04 Eric Seidel <eric@webkit.org>
Reviewed by Ryosuke Niwa.
Split run storage out from BidiResolver into a new BidiRunList class
https://bugs.webkit.org/show_bug.cgi?id=57764
Part of what makes BidiResolver and InlineIterator so difficult to understand
(and bug 50912 so difficult to fix) is that BidiResolver is both a state machine
for the Unicode Bidi Algorithm (UBA) as well as storage for the resulting
BidiRuns from the algorithm. This patch breaks the storage aspect off
into its own class BidiRunList.
This patch is only a start. It does not actually fix the problematic ownership
relationship, but it does make it possible to fix such in a second patch.
The run pointers and addRun/prependRun, etc. were already a tightly coupled
logical subset of the BidiResolver class, so moving them into their own class
was both obvious and easy. The only piece of logic I had to move was that
deleteRuns() had a side-effect of setting the m_emptyRun bit on the resolver.
I believe this deleteRuns side-effect was only ever used for one place
(right after findNextLineBreak) and that it's only needed because
findNextLineBreak can sometimes create bidi runs. Run creation appears to be
an unintentional side-effect of how InlineIterator calls through to BidiResolver
as part of bidiNext and not a desired effect of the code. I have added the call
to markCurrentRunEmpty to both places deleteRuns was called (where the resolver
could get re-used) as a safety precaution. We could replace both with ASSERTs
in a later patch.
I suspect there may be a small performance win from further refactoring so that
findNextLineBreak does not need to create BidiRuns.
As I commented in the code, callers should own their own BidiRunList which they
pass to BidiResolver::createBidiRunsForLine. I attempted to implement that in
an earlier version of this patch, but it was too complicated with the current
twisted dependencies between InlineIterator/bidiNext and InlineBidiResolver.
raise/lowerExplicitEmbeddingLevel are called unconditionally
from commitExplicitEmbedding (which is called by bidiNext) and expect to have
access to a runs list even in cases where we don't want any runs (findNextLineBreak).
I also cleaned up some of the callers to pass around BidiRunList objects instead
of InlineBidiResolvers now that they're separate objects.
* GNUmakefile.am:
* WebCore.gypi:
* WebCore.pro:
* WebCore.vcproj/WebCore.vcproj:
* WebCore.xcodeproj/project.pbxproj:
* platform/graphics/GraphicsContext.cpp:
(WebCore::GraphicsContext::drawBidiText):
* platform/text/BidiResolver.h:
(WebCore::BidiResolver::BidiResolver):
(WebCore::BidiResolver::runs):
(WebCore::BidiResolver::markCurrentRunEmpty):
(WebCore::::appendRun):
(WebCore::::lowerExplicitEmbeddingLevel):
(WebCore::::raiseExplicitEmbeddingLevel):
(WebCore::::reorderRunsFromLevels):
(WebCore::::createBidiRunsForLine):
* rendering/InlineIterator.h:
(WebCore::InlineBidiResolver::appendRun):
* rendering/RenderBlock.h:
* rendering/RenderBlockLineLayout.cpp:
(WebCore::createRun):
(WebCore::RenderBlock::appendRunsForObject):
(WebCore::reachedEndOfTextRenderer):
(WebCore::RenderBlock::handleTrailingSpaces):
(WebCore::RenderBlock::layoutInlineChildren):
2011-04-07 Naoki Takano <takano.naoki@gmail.com>
Reviewed by Adam Barth.
......@@ -2689,6 +2689,7 @@ webcore_sources += \
Source/WebCore/platform/text/BidiContext.cpp \
Source/WebCore/platform/text/BidiContext.h \
Source/WebCore/platform/text/BidiResolver.h \
Source/WebCore/platform/text/BidiRunList.h \
Source/WebCore/platform/text/Hyphenation.cpp \
Source/WebCore/platform/text/Hyphenation.h \
Source/WebCore/platform/text/LineEnding.cpp \
......
......@@ -900,6 +900,7 @@
'platform/sql/SQLiteDatabase.h',
'platform/sql/SQLiteTransaction.h',
'platform/text/Base64.h',
'platform/text/BidiRunList.h',
'platform/text/BidiContext.h',
'platform/text/BidiResolver.h',
'platform/text/LineEnding.h',
......
......@@ -1967,6 +1967,7 @@ HEADERS += \
platform/KillRing.h \
platform/KURL.h \
platform/Length.h \
platform/text/BidiRunList.h \
platform/text/LineEnding.h \
platform/text/TextCheckerClient.h \
platform/text/TextChecking.h \
......
......@@ -30424,6 +30424,10 @@
RelativePath="..\platform\text\BidiResolver.h"
>
</File>
<File
RelativePath="..\platform\text\BidiRunList.h"
>
</File>
<File
RelativePath="..\platform\text\Hyphenation.h"
>
......@@ -3639,6 +3639,7 @@
A8C2280E11D4A59700D5A7D3 /* DocumentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A8C2280D11D4A59700D5A7D3 /* DocumentParser.cpp */; };
A8C228A111D5722E00D5A7D3 /* DecodedDataDocumentParser.h in Headers */ = {isa = PBXBuildFile; fileRef = A8C2289F11D5722E00D5A7D3 /* DecodedDataDocumentParser.h */; };
A8C228A211D5722E00D5A7D3 /* DecodedDataDocumentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A8C228A011D5722E00D5A7D3 /* DecodedDataDocumentParser.cpp */; };
A8C402931348B2220063F1E5 /* BidiRunList.h in Headers */ = {isa = PBXBuildFile; fileRef = A8C402921348B2220063F1E5 /* BidiRunList.h */; };
A8C4A7FD09D563270003AC8D /* StyledElement.h in Headers */ = {isa = PBXBuildFile; fileRef = A8C4A7EB09D563270003AC8D /* StyledElement.h */; settings = {ATTRIBUTES = (Private, ); }; };
A8C4A7FE09D563270003AC8D /* StyledElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A8C4A7EC09D563270003AC8D /* StyledElement.cpp */; };
A8C4A80009D563270003AC8D /* Node.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A8C4A7EE09D563270003AC8D /* Node.cpp */; };
......@@ -10036,6 +10037,7 @@
A8C2280D11D4A59700D5A7D3 /* DocumentParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DocumentParser.cpp; sourceTree = "<group>"; };
A8C2289F11D5722E00D5A7D3 /* DecodedDataDocumentParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DecodedDataDocumentParser.h; sourceTree = "<group>"; };
A8C228A011D5722E00D5A7D3 /* DecodedDataDocumentParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DecodedDataDocumentParser.cpp; sourceTree = "<group>"; };
A8C402921348B2220063F1E5 /* BidiRunList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BidiRunList.h; sourceTree = "<group>"; };
A8C4A7EB09D563270003AC8D /* StyledElement.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = StyledElement.h; sourceTree = "<group>"; };
A8C4A7EC09D563270003AC8D /* StyledElement.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = StyledElement.cpp; sourceTree = "<group>"; };
A8C4A7EE09D563270003AC8D /* Node.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = Node.cpp; sourceTree = "<group>"; };
......@@ -17836,6 +17838,7 @@
B2C3D9F20D006C1D00EF6F26 /* BidiContext.cpp */,
B2C3D9F30D006C1D00EF6F26 /* BidiContext.h */,
B2C3D9F40D006C1D00EF6F26 /* BidiResolver.h */,
A8C402921348B2220063F1E5 /* BidiRunList.h */,
375CD231119D43C800A2A859 /* Hyphenation.h */,
89B5EA9F11E8003D00F2367E /* LineEnding.cpp */,
89B5EAA011E8003D00F2367E /* LineEnding.h */,
......@@ -19870,6 +19873,7 @@
B2C3DA240D006C1D00EF6F26 /* BidiContext.h in Headers */,
B2C3DA250D006C1D00EF6F26 /* BidiResolver.h in Headers */,
BCE789861120E7A60060ECE5 /* BidiRun.h in Headers */,
A8C402931348B2220063F1E5 /* BidiRunList.h in Headers */,
938192050F87E1EC00D5352A /* BinaryPropertyList.h in Headers */,
A622A8FB122C44A600A785B3 /* BindingSecurity.h in Headers */,
A622A8FD122C44A600A785B3 /* BindingSecurityBase.h in Headers */,
......@@ -397,7 +397,11 @@ void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const F
if (paintingDisabled())
return;
// FIXME: This ownership should be reversed. We should pass BidiRunList
// to BidiResolver in createBidiRunsForLine.
BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
WTF::Unicode::Direction paragraphDirection = run.ltr() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft;
bidiResolver.setStatus(BidiStatus(paragraphDirection, paragraphDirection, paragraphDirection, BidiContext::create(run.ltr() ? 0 : 1, paragraphDirection, run.directionalOverride())));
......@@ -405,11 +409,11 @@ void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const F
bidiResolver.setPosition(TextRunIterator(&run, 0));
bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
if (!bidiResolver.runCount())
if (!bidiRuns.runCount())
return;
FloatPoint currPoint = point;
BidiCharacterRun* bidiRun = bidiResolver.firstRun();
BidiCharacterRun* bidiRun = bidiRuns.firstRun();
while (bidiRun) {
TextRun subrun = run;
......@@ -425,7 +429,7 @@ void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const F
currPoint.move(font.width(subrun), 0);
}
bidiResolver.deleteRuns();
bidiRuns.deleteRuns();
}
void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const FloatPoint& point, int h, const Color& backgroundColor, ColorSpace colorSpace, int from, int to)
......
......@@ -23,6 +23,7 @@
#define BidiResolver_h
#include "BidiContext.h"
#include "BidiRunList.h"
#include <wtf/Noncopyable.h>
#include <wtf/PassRefPtr.h>
#include <wtf/Vector.h>
......@@ -147,6 +148,8 @@ enum VisualDirectionOverride {
VisualRightToLeftOverride
};
// BidiResolver is WebKit's implementation of the Unicode Bidi Algorithm
// http://unicode.org/reports/tr9
template <class Iterator, class Run> class BidiResolver {
WTF_MAKE_NONCOPYABLE(BidiResolver);
public:
......@@ -154,10 +157,6 @@ public:
: m_direction(WTF::Unicode::OtherNeutral)
, m_reachedEndOfLine(false)
, m_emptyRun(true)
, m_firstRun(0)
, m_lastRun(0)
, m_logicallyLastRun(0)
, m_runCount(0)
{
}
......@@ -186,22 +185,16 @@ public:
void createBidiRunsForLine(const Iterator& end, VisualDirectionOverride = NoVisualOverride, bool hardLineBreak = false);
Run* firstRun() const { return m_firstRun; }
Run* lastRun() const { return m_lastRun; }
Run* logicallyLastRun() const { return m_logicallyLastRun; }
unsigned runCount() const { return m_runCount; }
BidiRunList<Run>& runs() { return m_runs; }
void addRun(Run*);
void prependRun(Run*);
void moveRunToEnd(Run*);
void moveRunToBeginning(Run*);
void deleteRuns();
// FIXME: This used to be part of deleteRuns() but was a layering violation.
// It's unclear if this is still needed.
void markCurrentRunEmpty() { m_emptyRun = true; }
protected:
// FIXME: Instead of InlineBidiResolvers subclassing this method, we should
// pass in some sort of Traits object which knows how to create runs for appending.
void appendRun();
void reverseRuns(unsigned start, unsigned end);
Iterator m_current;
// sor and eor are "start of run" and "end of run" respectively and correpond
......@@ -216,10 +209,10 @@ protected:
Iterator m_lastBeforeET; // Before a EuropeanNumberTerminator
bool m_emptyRun;
Run* m_firstRun;
Run* m_lastRun;
Run* m_logicallyLastRun;
unsigned m_runCount;
// FIXME: This should not belong to the resolver, but rather be passed
// into createBidiRunsForLine by the caller.
BidiRunList<Run> m_runs;
MidpointState<Iterator> m_midpointState;
private:
......@@ -233,76 +226,6 @@ private:
Vector<BidiEmbedding, 8> m_currentExplicitEmbeddingSequence;
};
template <class Iterator, class Run>
inline void BidiResolver<Iterator, Run>::addRun(Run* run)
{
if (!m_firstRun)
m_firstRun = run;
else
m_lastRun->m_next = run;
m_lastRun = run;
m_runCount++;
}
template <class Iterator, class Run>
inline void BidiResolver<Iterator, Run>::prependRun(Run* run)
{
ASSERT(!run->m_next);
if (!m_lastRun)
m_lastRun = run;
else
run->m_next = m_firstRun;
m_firstRun = run;
m_runCount++;
}
template <class Iterator, class Run>
inline void BidiResolver<Iterator, Run>::moveRunToEnd(Run* run)
{
ASSERT(m_firstRun);
ASSERT(m_lastRun);
ASSERT(run->m_next);
Run* current = 0;
Run* next = m_firstRun;
while (next != run) {
current = next;
next = current->next();
}
if (!current)
m_firstRun = run->next();
else
current->m_next = run->m_next;
run->m_next = 0;
m_lastRun->m_next = run;
m_lastRun = run;
}
template <class Iterator, class Run>
inline void BidiResolver<Iterator, Run>::moveRunToBeginning(Run* run)
{
ASSERT(m_firstRun);
ASSERT(m_lastRun);
ASSERT(run != m_firstRun);
Run* current = m_firstRun;
Run* next = current->next();
while (next != run) {
current = next;
next = current->next();
}
current->m_next = run->m_next;
if (run == m_lastRun)
m_lastRun = current;
run->m_next = m_firstRun;
m_firstRun = run;
}
template <class Iterator, class Run>
void BidiResolver<Iterator, Run>::appendRun()
{
......@@ -316,7 +239,7 @@ void BidiResolver<Iterator, Run>::appendRun()
}
if (endOffset >= startOffset)
addRun(new Run(startOffset, endOffset + 1, context(), m_direction));
m_runs.addRun(new Run(startOffset, endOffset + 1, context(), m_direction));
m_eor.increment();
m_sor = m_eor;
......@@ -380,8 +303,10 @@ void BidiResolver<Iterator, Run>::lowerExplicitEmbeddingLevel(WTF::Unicode::Dire
}
m_eor = m_last;
}
appendRun();
m_emptyRun = true;
// sor for the new run is determined by the higher level (rule X10)
setLastDir(from);
setLastStrongDir(from);
......@@ -418,8 +343,10 @@ void BidiResolver<Iterator, Run>::raiseExplicitEmbeddingLevel(WTF::Unicode::Dire
}
m_eor = m_last;
}
appendRun();
m_emptyRun = true;
setLastDir(to);
setLastStrongDir(to);
m_eor = Iterator();
......@@ -465,75 +392,6 @@ bool BidiResolver<Iterator, Run>::commitExplicitEmbedding()
return fromLevel != toLevel;
}
template <class Iterator, class Run>
void BidiResolver<Iterator, Run>::deleteRuns()
{
m_emptyRun = true;
if (!m_firstRun)
return;
Run* curr = m_firstRun;
while (curr) {
Run* s = curr->next();
curr->destroy();
curr = s;
}
m_firstRun = 0;
m_lastRun = 0;
m_runCount = 0;
}
template <class Iterator, class Run>
void BidiResolver<Iterator, Run>::reverseRuns(unsigned start, unsigned end)
{
if (start >= end)
return;
ASSERT(end < m_runCount);
// Get the item before the start of the runs to reverse and put it in
// |beforeStart|. |curr| should point to the first run to reverse.
Run* curr = m_firstRun;
Run* beforeStart = 0;
unsigned i = 0;
while (i < start) {
i++;
beforeStart = curr;
curr = curr->next();
}
Run* startRun = curr;
while (i < end) {
i++;
curr = curr->next();
}
Run* endRun = curr;
Run* afterEnd = curr->next();
i = start;
curr = startRun;
Run* newNext = afterEnd;
while (i <= end) {
// Do the reversal.
Run* next = curr->next();
curr->m_next = newNext;
newNext = curr;
curr = next;
i++;
}
// Now hook up beforeStart and afterEnd to the startRun and endRun.
if (beforeStart)
beforeStart->m_next = endRun;
else
m_firstRun = endRun;
startRun->m_next = afterEnd;
if (!afterEnd)
m_lastRun = startRun;
}
template <class Iterator, class Run>
inline void BidiResolver<Iterator, Run>::updateStatusLastFromCurrentDirection(WTF::Unicode::Direction dirCurrent)
{
......@@ -581,7 +439,7 @@ inline void BidiResolver<Iterator, Run>::reorderRunsFromLevels()
{
unsigned char levelLow = 128;
unsigned char levelHigh = 0;
for (Run* run = firstRun(); run; run = run->next()) {
for (Run* run = m_runs.firstRun(); run; run = run->next()) {
levelHigh = std::max(run->level(), levelHigh);
levelLow = std::min(run->level(), levelLow);
}
......@@ -595,11 +453,11 @@ inline void BidiResolver<Iterator, Run>::reorderRunsFromLevels()
if (!(levelLow % 2))
levelLow++;
unsigned count = runCount() - 1;
unsigned count = m_runs.runCount() - 1;
while (levelHigh >= levelLow) {
unsigned i = 0;
Run* run = firstRun();
Run* run = m_runs.firstRun();
while (i < count) {
for (;i < count && run && run->level() < levelHigh; i++)
run = run->next();
......@@ -607,7 +465,7 @@ inline void BidiResolver<Iterator, Run>::reorderRunsFromLevels()
for (;i <= count && run && run->level() >= levelHigh; i++)
run = run->next();
unsigned end = i - 1;
reverseRuns(start, end);
m_runs.reverseRuns(start, end);
}
levelHigh--;
}
......@@ -630,9 +488,9 @@ void BidiResolver<Iterator, Run>::createBidiRunsForLine(const Iterator& end, Vis
}
m_direction = override == VisualLeftToRightOverride ? LeftToRight : RightToLeft;
appendRun();
m_logicallyLastRun = m_lastRun;
m_runs.setLogicallyLastRun(m_runs.lastRun());
if (override == VisualRightToLeftOverride)
reverseRuns(0, runCount() - 1);
m_runs.reverseRuns(0, m_runs.runCount() - 1);
return;
}
......@@ -997,7 +855,7 @@ void BidiResolver<Iterator, Run>::createBidiRunsForLine(const Iterator& end, Vis
}
}
m_logicallyLastRun = m_lastRun;
m_runs.setLogicallyLastRun(m_runs.lastRun());
reorderRunsFromLevels();
endOfLine = Iterator();
}
......
/*
* Copyright (C) 2000 Lars Knoll (knoll@kde.org)
* Copyright (C) 2003, 2004, 2006, 2007, 2008 Apple Inc. All right reserved.
* Copyright (C) 2011 Google, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef BidiRunList_h
#define BidiRunList_h
#include <wtf/Noncopyable.h>
namespace WebCore {
template <class Run>
class BidiRunList {
WTF_MAKE_NONCOPYABLE(BidiRunList);
public:
BidiRunList()
: m_firstRun(0)
, m_lastRun(0)
, m_logicallyLastRun(0)
, m_runCount(0)
{
}
// FIXME: Once BidiResolver no longer owns the BidiRunList,
// then ~BidiRunList should call deleteRuns() automatically.
Run* firstRun() const { return m_firstRun; }
Run* lastRun() const { return m_lastRun; }
Run* logicallyLastRun() const { return m_logicallyLastRun; }
unsigned runCount() const { return m_runCount; }
void addRun(Run*);
void prependRun(Run*);
void moveRunToEnd(Run*);
void moveRunToBeginning(Run*);
void deleteRuns();
void reverseRuns(unsigned start, unsigned end);
void reorderRunsFromLevels();
void setLogicallyLastRun(Run* run) { m_logicallyLastRun = run; }
private:
Run* m_firstRun;
Run* m_lastRun;
Run* m_logicallyLastRun;
unsigned m_runCount;
};
template <class Run>
inline void BidiRunList<Run>::addRun(Run* run)
{
if (!m_firstRun)
m_firstRun = run;
else
m_lastRun->m_next = run;
m_lastRun = run;
m_runCount++;
}
template <class Run>
inline void BidiRunList<Run>::prependRun(Run* run)
{
ASSERT(!run->m_next);
if (!m_lastRun)
m_lastRun = run;
else
run->m_next = m_firstRun;
m_firstRun = run;
m_runCount++;
}
template <class Run>
inline void BidiRunList<Run>::moveRunToEnd(Run* run)
{
ASSERT(m_firstRun);
ASSERT(m_lastRun);
ASSERT(run->m_next);
Run* current = 0;
Run* next = m_firstRun;
while (next != run) {
current = next;
next = current->next();
}
if (!current)
m_firstRun = run->next();
else
current->m_next = run->m_next;
run->m_next = 0;
m_lastRun->m_next = run;
m_lastRun = run;
}
template <class Run>
inline void BidiRunList<Run>::moveRunToBeginning(Run* run)
{
ASSERT(m_firstRun);
ASSERT(m_lastRun);
ASSERT(run != m_firstRun);
Run* current = m_firstRun;
Run* next = current->next();
while (next != run) {
current = next;
next = current->next();
}
current->m_next = run->m_next;
if (run == m_lastRun)
m_lastRun = current;
run->m_next = m_firstRun;
m_firstRun = run;
}
template <class Run>
void BidiRunList<Run>::deleteRuns()
{
if (!m_firstRun)
return;
Run* curr = m_firstRun;
while (curr) {
Run* s = curr->next();
curr->destroy();
curr = s;
}
m_firstRun = 0;
m_lastRun = 0;