SegmentedString.cpp revision ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddb
1/*
2    Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public
6    License as published by the Free Software Foundation; either
7    version 2 of the License, or (at your option) any later version.
8
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public License
15    along with this library; see the file COPYING.LIB.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17    Boston, MA 02110-1301, USA.
18*/
19
20#include "config.h"
21#include "SegmentedString.h"
22
23namespace WebCore {
24
25SegmentedString::SegmentedString(const SegmentedString &other)
26    : m_pushedChar1(other.m_pushedChar1)
27    , m_pushedChar2(other.m_pushedChar2)
28    , m_currentString(other.m_currentString)
29    , m_substrings(other.m_substrings)
30    , m_composite(other.m_composite)
31    , m_closed(other.m_closed)
32{
33    if (other.m_currentChar == &other.m_pushedChar1)
34        m_currentChar = &m_pushedChar1;
35    else if (other.m_currentChar == &other.m_pushedChar2)
36        m_currentChar = &m_pushedChar2;
37    else
38        m_currentChar = other.m_currentChar;
39}
40
41const SegmentedString& SegmentedString::operator=(const SegmentedString &other)
42{
43    m_pushedChar1 = other.m_pushedChar1;
44    m_pushedChar2 = other.m_pushedChar2;
45    m_currentString = other.m_currentString;
46    m_substrings = other.m_substrings;
47    m_composite = other.m_composite;
48    if (other.m_currentChar == &other.m_pushedChar1)
49        m_currentChar = &m_pushedChar1;
50    else if (other.m_currentChar == &other.m_pushedChar2)
51        m_currentChar = &m_pushedChar2;
52    else
53        m_currentChar = other.m_currentChar;
54    m_closed = other.m_closed;
55    m_numberOfCharactersConsumedPriorToCurrentString = other.m_numberOfCharactersConsumedPriorToCurrentString;
56    m_numberOfCharactersConsumedPriorToCurrentLine = other.m_numberOfCharactersConsumedPriorToCurrentLine;
57    m_currentLine = other.m_currentLine;
58
59    return *this;
60}
61
62unsigned SegmentedString::length() const
63{
64    unsigned length = m_currentString.m_length;
65    if (m_pushedChar1) {
66        ++length;
67        if (m_pushedChar2)
68            ++length;
69    }
70    if (m_composite) {
71        Deque<SegmentedSubstring>::const_iterator it = m_substrings.begin();
72        Deque<SegmentedSubstring>::const_iterator e = m_substrings.end();
73        for (; it != e; ++it)
74            length += it->m_length;
75    }
76    return length;
77}
78
79void SegmentedString::setExcludeLineNumbers()
80{
81    m_currentString.setExcludeLineNumbers();
82    if (m_composite) {
83        Deque<SegmentedSubstring>::iterator it = m_substrings.begin();
84        Deque<SegmentedSubstring>::iterator e = m_substrings.end();
85        for (; it != e; ++it)
86            it->setExcludeLineNumbers();
87    }
88}
89
90void SegmentedString::clear()
91{
92    m_pushedChar1 = 0;
93    m_pushedChar2 = 0;
94    m_currentChar = 0;
95    m_currentString.clear();
96    m_substrings.clear();
97    m_composite = false;
98    m_closed = false;
99}
100
101void SegmentedString::append(const SegmentedSubstring &s)
102{
103    ASSERT(!m_closed);
104    if (s.m_length) {
105        if (!m_currentString.m_length) {
106            m_numberOfCharactersConsumedPriorToCurrentString += m_currentString.numberOfCharactersConsumed();
107            m_currentString = s;
108        } else {
109            m_substrings.append(s);
110            m_composite = true;
111        }
112    }
113}
114
115void SegmentedString::prepend(const SegmentedSubstring &s)
116{
117    ASSERT(!escaped());
118    ASSERT(!s.numberOfCharactersConsumed());
119    if (s.m_length) {
120        // FIXME: We're assuming that the prepend were originally consumed by
121        //        this SegmentedString.  We're also ASSERTing that s is a fresh
122        //        SegmentedSubstring.  These assumptions are sufficient for our
123        //        current use, but we might need to handle the more elaborate
124        //        cases in the future.
125        m_numberOfCharactersConsumedPriorToCurrentString += m_currentString.numberOfCharactersConsumed();
126        m_numberOfCharactersConsumedPriorToCurrentString -= s.m_length;
127        if (!m_currentString.m_length)
128            m_currentString = s;
129        else {
130            // Shift our m_currentString into our list.
131            m_substrings.prepend(m_currentString);
132            m_currentString = s;
133            m_composite = true;
134        }
135    }
136}
137
138void SegmentedString::close()
139{
140    // Closing a stream twice is likely a coding mistake.
141    ASSERT(!m_closed);
142    m_closed = true;
143}
144
145void SegmentedString::append(const SegmentedString &s)
146{
147    ASSERT(!m_closed);
148    ASSERT(!s.escaped());
149    append(s.m_currentString);
150    if (s.m_composite) {
151        Deque<SegmentedSubstring>::const_iterator it = s.m_substrings.begin();
152        Deque<SegmentedSubstring>::const_iterator e = s.m_substrings.end();
153        for (; it != e; ++it)
154            append(*it);
155    }
156    m_currentChar = m_pushedChar1 ? &m_pushedChar1 : m_currentString.m_current;
157}
158
159void SegmentedString::prepend(const SegmentedString &s)
160{
161    ASSERT(!escaped());
162    ASSERT(!s.escaped());
163    if (s.m_composite) {
164        Deque<SegmentedSubstring>::const_reverse_iterator it = s.m_substrings.rbegin();
165        Deque<SegmentedSubstring>::const_reverse_iterator e = s.m_substrings.rend();
166        for (; it != e; ++it)
167            prepend(*it);
168    }
169    prepend(s.m_currentString);
170    m_currentChar = m_pushedChar1 ? &m_pushedChar1 : m_currentString.m_current;
171}
172
173void SegmentedString::advanceSubstring()
174{
175    if (m_composite) {
176        m_numberOfCharactersConsumedPriorToCurrentString += m_currentString.numberOfCharactersConsumed();
177        m_currentString = m_substrings.takeFirst();
178        // If we've previously consumed some characters of the non-current
179        // string, we now account for those characters as part of the current
180        // string, not as part of "prior to current string."
181        m_numberOfCharactersConsumedPriorToCurrentString -= m_currentString.numberOfCharactersConsumed();
182        if (m_substrings.isEmpty())
183            m_composite = false;
184    } else {
185        m_currentString.clear();
186    }
187}
188
189int SegmentedString::numberOfCharactersConsumedSlow() const
190{
191    int result = m_numberOfCharactersConsumedPriorToCurrentString + m_currentString.numberOfCharactersConsumed();
192    if (m_pushedChar1) {
193        --result;
194        if (m_pushedChar2)
195            --result;
196    }
197    return result;
198}
199
200String SegmentedString::toString() const
201{
202    String result;
203    if (m_pushedChar1) {
204        result.append(m_pushedChar1);
205        if (m_pushedChar2)
206            result.append(m_pushedChar2);
207    }
208    m_currentString.appendTo(result);
209    if (m_composite) {
210        Deque<SegmentedSubstring>::const_iterator it = m_substrings.begin();
211        Deque<SegmentedSubstring>::const_iterator e = m_substrings.end();
212        for (; it != e; ++it)
213            it->appendTo(result);
214    }
215    return result;
216}
217
218void SegmentedString::advance(unsigned count, UChar* consumedCharacters)
219{
220    ASSERT(count <= length());
221    for (unsigned i = 0; i < count; ++i) {
222        consumedCharacters[i] = *current();
223        advance();
224    }
225}
226
227void SegmentedString::advanceSlowCase()
228{
229    if (m_pushedChar1) {
230        m_pushedChar1 = m_pushedChar2;
231        m_pushedChar2 = 0;
232    } else if (m_currentString.m_current) {
233        ++m_currentString.m_current;
234        if (--m_currentString.m_length == 0)
235            advanceSubstring();
236    }
237    m_currentChar = m_pushedChar1 ? &m_pushedChar1 : m_currentString.m_current;
238}
239
240void SegmentedString::advanceSlowCase(int& lineNumber)
241{
242    if (m_pushedChar1) {
243        m_pushedChar1 = m_pushedChar2;
244        m_pushedChar2 = 0;
245    } else if (m_currentString.m_current) {
246        if (*m_currentString.m_current++ == '\n' && m_currentString.doNotExcludeLineNumbers()) {
247            ++lineNumber;
248            ++m_currentLine;
249            // Plus 1 because numberOfCharactersConsumed value hasn't incremented yet; it does with m_length decrement below.
250            m_numberOfCharactersConsumedPriorToCurrentLine = numberOfCharactersConsumed() + 1;
251        }
252        if (--m_currentString.m_length == 0)
253            advanceSubstring();
254    }
255    m_currentChar = m_pushedChar1 ? &m_pushedChar1 : m_currentString.m_current;
256}
257
258WTF::ZeroBasedNumber SegmentedString::currentLine() const
259{
260    return WTF::ZeroBasedNumber::fromZeroBasedInt(m_currentLine);
261}
262
263WTF::ZeroBasedNumber SegmentedString::currentColumn() const
264{
265    int zeroBasedColumn = numberOfCharactersConsumedSlow() - m_numberOfCharactersConsumedPriorToCurrentLine;
266    return WTF::ZeroBasedNumber::fromZeroBasedInt(zeroBasedColumn);
267}
268
269void SegmentedString::setCurrentPosition(WTF::ZeroBasedNumber line, WTF::ZeroBasedNumber columnAftreProlog, int prologLength)
270{
271    m_currentLine = line.zeroBasedInt();
272    m_numberOfCharactersConsumedPriorToCurrentLine = numberOfCharactersConsumedSlow() + prologLength - columnAftreProlog.zeroBasedInt();
273}
274
275}
276