Modernize and streamline HTMLTokenizer
[WebKit-https.git] / Source / WebCore / platform / text / SegmentedString.h
1 /*
2     Copyright (C) 2004-2008, 2015 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 #ifndef SegmentedString_h
21 #define SegmentedString_h
22
23 #include <wtf/Deque.h>
24 #include <wtf/text/StringBuilder.h>
25
26 namespace WebCore {
27
28 class SegmentedString;
29
30 class SegmentedSubstring {
31 public:
32     SegmentedSubstring()
33         : m_length(0)
34         , m_doNotExcludeLineNumbers(true)
35         , m_is8Bit(false)
36     {
37         m_data.string16Ptr = 0;
38     }
39
40     SegmentedSubstring(const String& str)
41         : m_length(str.length())
42         , m_doNotExcludeLineNumbers(true)
43         , m_string(str)
44     {
45         if (m_length) {
46             if (m_string.is8Bit()) {
47                 m_is8Bit = true;
48                 m_data.string8Ptr = m_string.characters8();
49             } else {
50                 m_is8Bit = false;
51                 m_data.string16Ptr = m_string.characters16();
52             }
53         } else
54             m_is8Bit = false;
55     }
56
57     void clear() { m_length = 0; m_data.string16Ptr = 0; m_is8Bit = false;}
58     
59     bool is8Bit() { return m_is8Bit; }
60     
61     bool excludeLineNumbers() const { return !m_doNotExcludeLineNumbers; }
62     bool doNotExcludeLineNumbers() const { return m_doNotExcludeLineNumbers; }
63
64     void setExcludeLineNumbers() { m_doNotExcludeLineNumbers = false; }
65
66     int numberOfCharactersConsumed() const { return m_string.length() - m_length; }
67
68     void appendTo(StringBuilder& builder) const
69     {
70         int offset = m_string.length() - m_length;
71
72         if (!offset) {
73             if (m_length)
74                 builder.append(m_string);
75         } else
76             builder.append(m_string.substring(offset, m_length));
77     }
78
79     UChar getCurrentChar8()
80     {
81         return *m_data.string8Ptr;
82     }
83
84     UChar getCurrentChar16()
85     {
86         return m_data.string16Ptr ? *m_data.string16Ptr : 0;
87     }
88
89     UChar incrementAndGetCurrentChar8()
90     {
91         ASSERT(m_data.string8Ptr);
92         return *++m_data.string8Ptr;
93     }
94
95     UChar incrementAndGetCurrentChar16()
96     {
97         ASSERT(m_data.string16Ptr);
98         return *++m_data.string16Ptr;
99     }
100
101     String currentSubString(unsigned length)
102     {
103         int offset = m_string.length() - m_length;
104         return m_string.substring(offset, length);
105     }
106
107     ALWAYS_INLINE UChar getCurrentChar()
108     {
109         ASSERT(m_length);
110         if (is8Bit())
111             return getCurrentChar8();
112         return getCurrentChar16();
113     }
114     
115     ALWAYS_INLINE UChar incrementAndGetCurrentChar()
116     {
117         ASSERT(m_length);
118         if (is8Bit())
119             return incrementAndGetCurrentChar8();
120         return incrementAndGetCurrentChar16();
121     }
122
123 public:
124     union {
125         const LChar* string8Ptr;
126         const UChar* string16Ptr;
127     } m_data;
128     int m_length;
129
130 private:
131     bool m_doNotExcludeLineNumbers;
132     bool m_is8Bit;
133     String m_string;
134 };
135
136 class SegmentedString {
137 public:
138     SegmentedString()
139         : m_pushedChar1(0)
140         , m_pushedChar2(0)
141         , m_currentChar(0)
142         , m_numberOfCharactersConsumedPriorToCurrentString(0)
143         , m_numberOfCharactersConsumedPriorToCurrentLine(0)
144         , m_currentLine(0)
145         , m_closed(false)
146         , m_empty(true)
147         , m_fastPathFlags(NoFastPath)
148         , m_advanceFunc(&SegmentedString::advanceEmpty)
149         , m_advanceAndUpdateLineNumberFunc(&SegmentedString::advanceEmpty)
150     {
151     }
152
153     SegmentedString(const String& str)
154         : m_pushedChar1(0)
155         , m_pushedChar2(0)
156         , m_currentString(str)
157         , m_currentChar(0)
158         , m_numberOfCharactersConsumedPriorToCurrentString(0)
159         , m_numberOfCharactersConsumedPriorToCurrentLine(0)
160         , m_currentLine(0)
161         , m_closed(false)
162         , m_empty(!str.length())
163         , m_fastPathFlags(NoFastPath)
164     {
165         if (m_currentString.m_length)
166             m_currentChar = m_currentString.getCurrentChar();
167         updateAdvanceFunctionPointers();
168     }
169
170     SegmentedString(const SegmentedString&);
171     SegmentedString& operator=(const SegmentedString&);
172
173     void clear();
174     void close();
175
176     void append(const SegmentedString&);
177     void pushBack(const SegmentedString&);
178
179     void setExcludeLineNumbers();
180
181     void push(UChar c)
182     {
183         if (!m_pushedChar1) {
184             m_pushedChar1 = c;
185             m_currentChar = m_pushedChar1 ? m_pushedChar1 : m_currentString.getCurrentChar();
186             updateSlowCaseFunctionPointers();
187         } else {
188             ASSERT(!m_pushedChar2);
189             m_pushedChar2 = c;
190         }
191     }
192
193     bool isEmpty() const { return m_empty; }
194     unsigned length() const;
195
196     bool isClosed() const { return m_closed; }
197
198     enum AdvancePastResult { DidNotMatch, DidMatch, NotEnoughCharacters };
199     template<unsigned length> AdvancePastResult advancePast(const char (&literal)[length]) { return advancePast(literal, length - 1, true); }
200     template<unsigned length> AdvancePastResult advancePastIgnoringCase(const char (&literal)[length]) { return advancePast(literal, length - 1, false); }
201
202     void advance()
203     {
204         if (m_fastPathFlags & Use8BitAdvance) {
205             ASSERT(!m_pushedChar1);
206             bool haveOneCharacterLeft = (--m_currentString.m_length == 1);
207             m_currentChar = m_currentString.incrementAndGetCurrentChar8();
208
209             if (!haveOneCharacterLeft)
210                 return;
211
212             updateSlowCaseFunctionPointers();
213
214             return;
215         }
216
217         (this->*m_advanceFunc)();
218     }
219
220     void advanceAndUpdateLineNumber()
221     {
222         if (m_fastPathFlags & Use8BitAdvance) {
223             ASSERT(!m_pushedChar1);
224
225             bool haveNewLine = (m_currentChar == '\n') & !!(m_fastPathFlags & Use8BitAdvanceAndUpdateLineNumbers);
226             bool haveOneCharacterLeft = (--m_currentString.m_length == 1);
227
228             m_currentChar = m_currentString.incrementAndGetCurrentChar8();
229
230             if (!(haveNewLine | haveOneCharacterLeft))
231                 return;
232
233             if (haveNewLine) {
234                 ++m_currentLine;
235                 m_numberOfCharactersConsumedPriorToCurrentLine =  m_numberOfCharactersConsumedPriorToCurrentString + m_currentString.numberOfCharactersConsumed();
236             }
237
238             if (haveOneCharacterLeft)
239                 updateSlowCaseFunctionPointers();
240
241             return;
242         }
243
244         (this->*m_advanceAndUpdateLineNumberFunc)();
245     }
246
247     void advancePastNonNewline()
248     {
249         ASSERT(currentChar() != '\n');
250         advance();
251     }
252
253     void advancePastNewlineAndUpdateLineNumber()
254     {
255         ASSERT(currentChar() == '\n');
256         if (!m_pushedChar1 && m_currentString.m_length > 1) {
257             int newLineFlag = m_currentString.doNotExcludeLineNumbers();
258             m_currentLine += newLineFlag;
259             if (newLineFlag)
260                 m_numberOfCharactersConsumedPriorToCurrentLine = numberOfCharactersConsumed() + 1;
261             decrementAndCheckLength();
262             m_currentChar = m_currentString.incrementAndGetCurrentChar();
263             return;
264         }
265         advanceAndUpdateLineNumberSlowCase();
266     }
267
268     int numberOfCharactersConsumed() const
269     {
270         int numberOfPushedCharacters = 0;
271         if (m_pushedChar1) {
272             ++numberOfPushedCharacters;
273             if (m_pushedChar2)
274                 ++numberOfPushedCharacters;
275         }
276         return m_numberOfCharactersConsumedPriorToCurrentString + m_currentString.numberOfCharactersConsumed() - numberOfPushedCharacters;
277     }
278
279     String toString() const;
280
281     UChar currentChar() const { return m_currentChar; }    
282
283     OrdinalNumber currentColumn() const;
284     OrdinalNumber currentLine() const;
285
286     // Sets value of line/column variables. Column is specified indirectly by a parameter columnAfterProlog
287     // which is a value of column that we should get after a prolog (first prologLength characters) has been consumed.
288     void setCurrentPosition(OrdinalNumber line, OrdinalNumber columnAfterProlog, int prologLength);
289
290 private:
291     enum FastPathFlags {
292         NoFastPath = 0,
293         Use8BitAdvanceAndUpdateLineNumbers = 1 << 0,
294         Use8BitAdvance = 1 << 1,
295     };
296
297     void append(const SegmentedSubstring&);
298     void pushBack(const SegmentedSubstring&);
299
300     void advance8();
301     void advance16();
302     void advanceAndUpdateLineNumber8();
303     void advanceAndUpdateLineNumber16();
304     void advanceSlowCase();
305     void advanceAndUpdateLineNumberSlowCase();
306     void advanceEmpty();
307     void advanceSubstring();
308     
309     void updateSlowCaseFunctionPointers();
310
311     void decrementAndCheckLength()
312     {
313         ASSERT(m_currentString.m_length > 1);
314         if (--m_currentString.m_length == 1)
315             updateSlowCaseFunctionPointers();
316     }
317
318     void updateAdvanceFunctionPointers()
319     {
320         if ((m_currentString.m_length > 1) && !m_pushedChar1) {
321             if (m_currentString.is8Bit()) {
322                 m_advanceFunc = &SegmentedString::advance8;
323                 m_fastPathFlags = Use8BitAdvance;
324                 if (m_currentString.doNotExcludeLineNumbers()) {
325                     m_advanceAndUpdateLineNumberFunc = &SegmentedString::advanceAndUpdateLineNumber8;
326                     m_fastPathFlags |= Use8BitAdvanceAndUpdateLineNumbers;
327                 } else
328                     m_advanceAndUpdateLineNumberFunc = &SegmentedString::advance8;
329                 return;
330             }
331
332             m_advanceFunc = &SegmentedString::advance16;
333             m_fastPathFlags = NoFastPath;
334             if (m_currentString.doNotExcludeLineNumbers())
335                 m_advanceAndUpdateLineNumberFunc = &SegmentedString::advanceAndUpdateLineNumber16;
336             else
337                 m_advanceAndUpdateLineNumberFunc = &SegmentedString::advance16;
338             return;
339         }
340
341         if (!m_currentString.m_length && !isComposite()) {
342             m_advanceFunc = &SegmentedString::advanceEmpty;
343             m_fastPathFlags = NoFastPath;
344             m_advanceAndUpdateLineNumberFunc = &SegmentedString::advanceEmpty;
345         }
346
347         updateSlowCaseFunctionPointers();
348     }
349
350     // Writes consumed characters into consumedCharacters, which must have space for at least |count| characters.
351     void advancePastNonNewlines(unsigned count);
352     void advancePastNonNewlines(unsigned count, UChar* consumedCharacters);
353
354     AdvancePastResult advancePast(const char* literal, unsigned length, bool caseSensitive);
355     AdvancePastResult advancePastSlowCase(const char* literal, bool caseSensitive);
356
357     bool isComposite() const { return !m_substrings.isEmpty(); }
358
359     UChar m_pushedChar1;
360     UChar m_pushedChar2;
361     SegmentedSubstring m_currentString;
362     UChar m_currentChar;
363     int m_numberOfCharactersConsumedPriorToCurrentString;
364     int m_numberOfCharactersConsumedPriorToCurrentLine;
365     int m_currentLine;
366     Deque<SegmentedSubstring> m_substrings;
367     bool m_closed;
368     bool m_empty;
369     unsigned char m_fastPathFlags;
370     void (SegmentedString::*m_advanceFunc)();
371     void (SegmentedString::*m_advanceAndUpdateLineNumberFunc)();
372 };
373
374 inline void SegmentedString::advancePastNonNewlines(unsigned count)
375 {
376     for (unsigned i = 0; i < count; ++i)
377         advancePastNonNewline();
378 }
379
380 inline SegmentedString::AdvancePastResult SegmentedString::advancePast(const char* literal, unsigned length, bool caseSensitive)
381 {
382     ASSERT(strlen(literal) == length);
383     ASSERT(!strchr(literal, '\n'));
384     if (!m_pushedChar1) {
385         if (length <= static_cast<unsigned>(m_currentString.m_length)) {
386             if (!m_currentString.currentSubString(length).startsWith(literal, caseSensitive))
387                 return DidNotMatch;
388             advancePastNonNewlines(length);
389             return DidMatch;
390         }
391     }
392     return advancePastSlowCase(literal, caseSensitive);
393 }
394
395 }
396
397 #endif