/src/mozilla-central/parser/htmlparser/nsScannerString.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include <stdlib.h> |
8 | | #include "nsScannerString.h" |
9 | | #include "mozilla/CheckedInt.h" |
10 | | |
11 | | |
12 | | /** |
13 | | * nsScannerBufferList |
14 | | */ |
15 | | |
16 | 0 | #define MAX_CAPACITY ((UINT32_MAX / sizeof(char16_t)) - \ |
17 | 0 | (sizeof(Buffer) + sizeof(char16_t))) |
18 | | |
19 | | nsScannerBufferList::Buffer* |
20 | | nsScannerBufferList::AllocBufferFromString( const nsAString& aString ) |
21 | 0 | { |
22 | 0 | uint32_t len = aString.Length(); |
23 | 0 | Buffer* buf = AllocBuffer(len); |
24 | 0 |
|
25 | 0 | if (buf) |
26 | 0 | { |
27 | 0 | nsAString::const_iterator source; |
28 | 0 | aString.BeginReading(source); |
29 | 0 | nsCharTraits<char16_t>::copy(buf->DataStart(), source.get(), len); |
30 | 0 | } |
31 | 0 | return buf; |
32 | 0 | } |
33 | | |
34 | | nsScannerBufferList::Buffer* |
35 | | nsScannerBufferList::AllocBuffer( uint32_t capacity ) |
36 | 0 | { |
37 | 0 | if (capacity > MAX_CAPACITY) |
38 | 0 | return nullptr; |
39 | 0 | |
40 | 0 | void* ptr = malloc(sizeof(Buffer) + (capacity + 1) * sizeof(char16_t)); |
41 | 0 | if (!ptr) |
42 | 0 | return nullptr; |
43 | 0 | |
44 | 0 | Buffer* buf = new (ptr) Buffer(); |
45 | 0 |
|
46 | 0 | buf->mUsageCount = 0; |
47 | 0 | buf->mDataEnd = buf->DataStart() + capacity; |
48 | 0 |
|
49 | 0 | // XXX null terminate. this shouldn't be required, but we do it because |
50 | 0 | // nsScanner erroneously thinks it can dereference DataEnd :-( |
51 | 0 | *buf->mDataEnd = char16_t(0); |
52 | 0 | return buf; |
53 | 0 | } |
54 | | |
55 | | void |
56 | | nsScannerBufferList::ReleaseAll() |
57 | 0 | { |
58 | 0 | while (!mBuffers.isEmpty()) |
59 | 0 | { |
60 | 0 | Buffer* node = mBuffers.popFirst(); |
61 | 0 | //printf(">>> freeing buffer @%p\n", node); |
62 | 0 | free(node); |
63 | 0 | } |
64 | 0 | } |
65 | | |
66 | | void |
67 | | nsScannerBufferList::SplitBuffer( const Position& pos ) |
68 | 0 | { |
69 | 0 | // splitting to the right keeps the work string and any extant token |
70 | 0 | // pointing to and holding a reference count on the same buffer. |
71 | 0 |
|
72 | 0 | Buffer* bufferToSplit = pos.mBuffer; |
73 | 0 | NS_ASSERTION(bufferToSplit, "null pointer"); |
74 | 0 |
|
75 | 0 | uint32_t splitOffset = pos.mPosition - bufferToSplit->DataStart(); |
76 | 0 | NS_ASSERTION(pos.mPosition >= bufferToSplit->DataStart() && |
77 | 0 | splitOffset <= bufferToSplit->DataLength(), |
78 | 0 | "split offset is outside buffer"); |
79 | 0 | |
80 | 0 | uint32_t len = bufferToSplit->DataLength() - splitOffset; |
81 | 0 | Buffer* new_buffer = AllocBuffer(len); |
82 | 0 | if (new_buffer) |
83 | 0 | { |
84 | 0 | nsCharTraits<char16_t>::copy(new_buffer->DataStart(), |
85 | 0 | bufferToSplit->DataStart() + splitOffset, |
86 | 0 | len); |
87 | 0 | InsertAfter(new_buffer, bufferToSplit); |
88 | 0 | bufferToSplit->SetDataLength(splitOffset); |
89 | 0 | } |
90 | 0 | } |
91 | | |
92 | | void |
93 | | nsScannerBufferList::DiscardUnreferencedPrefix( Buffer* aBuf ) |
94 | 0 | { |
95 | 0 | if (aBuf == Head()) |
96 | 0 | { |
97 | 0 | while (!mBuffers.isEmpty() && !Head()->IsInUse()) |
98 | 0 | { |
99 | 0 | Buffer* buffer = Head(); |
100 | 0 | buffer->remove(); |
101 | 0 | free(buffer); |
102 | 0 | } |
103 | 0 | } |
104 | 0 | } |
105 | | |
106 | | size_t |
107 | | nsScannerBufferList::Position::Distance( const Position& aStart, const Position& aEnd ) |
108 | 0 | { |
109 | 0 | size_t result = 0; |
110 | 0 | if (aStart.mBuffer == aEnd.mBuffer) |
111 | 0 | { |
112 | 0 | result = aEnd.mPosition - aStart.mPosition; |
113 | 0 | } |
114 | 0 | else |
115 | 0 | { |
116 | 0 | result = aStart.mBuffer->DataEnd() - aStart.mPosition; |
117 | 0 | for (Buffer* b = aStart.mBuffer->Next(); b != aEnd.mBuffer; b = b->Next()) |
118 | 0 | result += b->DataLength(); |
119 | 0 | result += aEnd.mPosition - aEnd.mBuffer->DataStart(); |
120 | 0 | } |
121 | 0 | return result; |
122 | 0 | } |
123 | | |
124 | | |
125 | | /** |
126 | | * nsScannerSubstring |
127 | | */ |
128 | | |
129 | | nsScannerSubstring::nsScannerSubstring() |
130 | | : mStart(nullptr, nullptr) |
131 | | , mEnd(nullptr, nullptr) |
132 | | , mBufferList(nullptr) |
133 | | , mLength(0) |
134 | | , mIsDirty(true) |
135 | 0 | { |
136 | 0 | } |
137 | | |
138 | | nsScannerSubstring::nsScannerSubstring( const nsAString& s ) |
139 | | : mBufferList(nullptr) |
140 | | , mIsDirty(true) |
141 | 0 | { |
142 | 0 | Rebind(s); |
143 | 0 | } |
144 | | |
145 | | nsScannerSubstring::~nsScannerSubstring() |
146 | 0 | { |
147 | 0 | release_ownership_of_buffer_list(); |
148 | 0 | } |
149 | | |
150 | | int32_t |
151 | | nsScannerSubstring::CountChar( char16_t c ) const |
152 | 0 | { |
153 | 0 | /* |
154 | 0 | re-write this to use a counting sink |
155 | 0 | */ |
156 | 0 |
|
157 | 0 | size_type result = 0; |
158 | 0 | size_type lengthToExamine = Length(); |
159 | 0 |
|
160 | 0 | nsScannerIterator iter; |
161 | 0 | for ( BeginReading(iter); ; ) |
162 | 0 | { |
163 | 0 | int32_t lengthToExamineInThisFragment = iter.size_forward(); |
164 | 0 | const char16_t* fromBegin = iter.get(); |
165 | 0 | result += size_type(NS_COUNT(fromBegin, fromBegin+lengthToExamineInThisFragment, c)); |
166 | 0 | if ( !(lengthToExamine -= lengthToExamineInThisFragment) ) |
167 | 0 | return result; |
168 | 0 | iter.advance(lengthToExamineInThisFragment); |
169 | 0 | } |
170 | 0 | // never reached; quiets warnings |
171 | 0 | return 0; |
172 | 0 | } |
173 | | |
174 | | void |
175 | | nsScannerSubstring::Rebind( const nsScannerSubstring& aString, |
176 | | const nsScannerIterator& aStart, |
177 | | const nsScannerIterator& aEnd ) |
178 | 0 | { |
179 | 0 | // allow for the case where &aString == this |
180 | 0 |
|
181 | 0 | aString.acquire_ownership_of_buffer_list(); |
182 | 0 | release_ownership_of_buffer_list(); |
183 | 0 |
|
184 | 0 | mStart = aStart; |
185 | 0 | mEnd = aEnd; |
186 | 0 | mBufferList = aString.mBufferList; |
187 | 0 | mLength = Distance(aStart, aEnd); |
188 | 0 | mIsDirty = true; |
189 | 0 | } |
190 | | |
191 | | void |
192 | | nsScannerSubstring::Rebind( const nsAString& aString ) |
193 | 0 | { |
194 | 0 | release_ownership_of_buffer_list(); |
195 | 0 |
|
196 | 0 | mBufferList = new nsScannerBufferList(AllocBufferFromString(aString)); |
197 | 0 | mIsDirty = true; |
198 | 0 |
|
199 | 0 | init_range_from_buffer_list(); |
200 | 0 | acquire_ownership_of_buffer_list(); |
201 | 0 | } |
202 | | |
203 | | const nsAString& |
204 | | nsScannerSubstring::AsString() const |
205 | 0 | { |
206 | 0 | if (mIsDirty) |
207 | 0 | { |
208 | 0 | nsScannerSubstring* mutable_this = const_cast<nsScannerSubstring*>(this); |
209 | 0 |
|
210 | 0 | if (mStart.mBuffer == mEnd.mBuffer) { |
211 | 0 | // We only have a single fragment to deal with, so just return it |
212 | 0 | // as a substring. |
213 | 0 | mutable_this->mFlattenedRep.Rebind(mStart.mPosition, mEnd.mPosition); |
214 | 0 | } else { |
215 | 0 | // Otherwise, we need to copy the data into a flattened buffer. |
216 | 0 | nsScannerIterator start, end; |
217 | 0 | CopyUnicodeTo(BeginReading(start), EndReading(end), mutable_this->mFlattenedRep); |
218 | 0 | } |
219 | 0 |
|
220 | 0 | mutable_this->mIsDirty = false; |
221 | 0 | } |
222 | 0 |
|
223 | 0 | return mFlattenedRep; |
224 | 0 | } |
225 | | |
226 | | nsScannerIterator& |
227 | | nsScannerSubstring::BeginReading( nsScannerIterator& iter ) const |
228 | 0 | { |
229 | 0 | iter.mOwner = this; |
230 | 0 |
|
231 | 0 | iter.mFragment.mBuffer = mStart.mBuffer; |
232 | 0 | iter.mFragment.mFragmentStart = mStart.mPosition; |
233 | 0 | if (mStart.mBuffer == mEnd.mBuffer) |
234 | 0 | iter.mFragment.mFragmentEnd = mEnd.mPosition; |
235 | 0 | else |
236 | 0 | iter.mFragment.mFragmentEnd = mStart.mBuffer->DataEnd(); |
237 | 0 |
|
238 | 0 | iter.mPosition = mStart.mPosition; |
239 | 0 | iter.normalize_forward(); |
240 | 0 | return iter; |
241 | 0 | } |
242 | | |
243 | | nsScannerIterator& |
244 | | nsScannerSubstring::EndReading( nsScannerIterator& iter ) const |
245 | 0 | { |
246 | 0 | iter.mOwner = this; |
247 | 0 |
|
248 | 0 | iter.mFragment.mBuffer = mEnd.mBuffer; |
249 | 0 | iter.mFragment.mFragmentEnd = mEnd.mPosition; |
250 | 0 | if (mStart.mBuffer == mEnd.mBuffer) |
251 | 0 | iter.mFragment.mFragmentStart = mStart.mPosition; |
252 | 0 | else |
253 | 0 | iter.mFragment.mFragmentStart = mEnd.mBuffer->DataStart(); |
254 | 0 |
|
255 | 0 | iter.mPosition = mEnd.mPosition; |
256 | 0 | // must not |normalize_backward| as that would likely invalidate tests like |while ( first != last )| |
257 | 0 | return iter; |
258 | 0 | } |
259 | | |
260 | | bool |
261 | | nsScannerSubstring::GetNextFragment( nsScannerFragment& frag ) const |
262 | 0 | { |
263 | 0 | // check to see if we are at the end of the buffer list |
264 | 0 | if (frag.mBuffer == mEnd.mBuffer) |
265 | 0 | return false; |
266 | 0 | |
267 | 0 | frag.mBuffer = frag.mBuffer->getNext(); |
268 | 0 |
|
269 | 0 | if (frag.mBuffer == mStart.mBuffer) |
270 | 0 | frag.mFragmentStart = mStart.mPosition; |
271 | 0 | else |
272 | 0 | frag.mFragmentStart = frag.mBuffer->DataStart(); |
273 | 0 |
|
274 | 0 | if (frag.mBuffer == mEnd.mBuffer) |
275 | 0 | frag.mFragmentEnd = mEnd.mPosition; |
276 | 0 | else |
277 | 0 | frag.mFragmentEnd = frag.mBuffer->DataEnd(); |
278 | 0 |
|
279 | 0 | return true; |
280 | 0 | } |
281 | | |
282 | | bool |
283 | | nsScannerSubstring::GetPrevFragment( nsScannerFragment& frag ) const |
284 | 0 | { |
285 | 0 | // check to see if we are at the beginning of the buffer list |
286 | 0 | if (frag.mBuffer == mStart.mBuffer) |
287 | 0 | return false; |
288 | 0 | |
289 | 0 | frag.mBuffer = frag.mBuffer->getPrevious(); |
290 | 0 |
|
291 | 0 | if (frag.mBuffer == mStart.mBuffer) |
292 | 0 | frag.mFragmentStart = mStart.mPosition; |
293 | 0 | else |
294 | 0 | frag.mFragmentStart = frag.mBuffer->DataStart(); |
295 | 0 |
|
296 | 0 | if (frag.mBuffer == mEnd.mBuffer) |
297 | 0 | frag.mFragmentEnd = mEnd.mPosition; |
298 | 0 | else |
299 | 0 | frag.mFragmentEnd = frag.mBuffer->DataEnd(); |
300 | 0 |
|
301 | 0 | return true; |
302 | 0 | } |
303 | | |
304 | | |
305 | | /** |
306 | | * nsScannerString |
307 | | */ |
308 | | |
309 | | nsScannerString::nsScannerString( Buffer* aBuf ) |
310 | 0 | { |
311 | 0 | mBufferList = new nsScannerBufferList(aBuf); |
312 | 0 |
|
313 | 0 | init_range_from_buffer_list(); |
314 | 0 | acquire_ownership_of_buffer_list(); |
315 | 0 | } |
316 | | |
317 | | void |
318 | | nsScannerString::AppendBuffer( Buffer* aBuf ) |
319 | 0 | { |
320 | 0 | mBufferList->Append(aBuf); |
321 | 0 | mLength += aBuf->DataLength(); |
322 | 0 |
|
323 | 0 | mEnd.mBuffer = aBuf; |
324 | 0 | mEnd.mPosition = aBuf->DataEnd(); |
325 | 0 |
|
326 | 0 | mIsDirty = true; |
327 | 0 | } |
328 | | |
329 | | void |
330 | | nsScannerString::DiscardPrefix( const nsScannerIterator& aIter ) |
331 | 0 | { |
332 | 0 | Position old_start(mStart); |
333 | 0 | mStart = aIter; |
334 | 0 | mLength -= Position::Distance(old_start, mStart); |
335 | 0 | |
336 | 0 | mStart.mBuffer->IncrementUsageCount(); |
337 | 0 | old_start.mBuffer->DecrementUsageCount(); |
338 | 0 |
|
339 | 0 | mBufferList->DiscardUnreferencedPrefix(old_start.mBuffer); |
340 | 0 |
|
341 | 0 | mIsDirty = true; |
342 | 0 | } |
343 | | |
344 | | void |
345 | | nsScannerString::UngetReadable( const nsAString& aReadable, const nsScannerIterator& aInsertPoint ) |
346 | | /* |
347 | | * Warning: this routine manipulates the shared buffer list in an unexpected way. |
348 | | * The original design did not really allow for insertions, but this call promises |
349 | | * that if called for a point after the end of all extant token strings, that no token string |
350 | | * or the work string will be invalidated. |
351 | | * |
352 | | * This routine is protected because it is the responsibility of the derived class to keep those promises. |
353 | | */ |
354 | 0 | { |
355 | 0 | Position insertPos(aInsertPoint); |
356 | 0 |
|
357 | 0 | mBufferList->SplitBuffer(insertPos); |
358 | 0 | // splitting to the right keeps the work string and any extant token pointing to and |
359 | 0 | // holding a reference count on the same buffer |
360 | 0 |
|
361 | 0 | Buffer* new_buffer = AllocBufferFromString(aReadable); |
362 | 0 | // make a new buffer with all the data to insert... |
363 | 0 | // BULLSHIT ALERT: we may have empty space to re-use in the split buffer, measure the cost |
364 | 0 | // of this and decide if we should do the work to fill it |
365 | 0 |
|
366 | 0 | Buffer* buffer_to_split = insertPos.mBuffer; |
367 | 0 | mBufferList->InsertAfter(new_buffer, buffer_to_split); |
368 | 0 | mLength += aReadable.Length(); |
369 | 0 |
|
370 | 0 | mEnd.mBuffer = mBufferList->Tail(); |
371 | 0 | mEnd.mPosition = mEnd.mBuffer->DataEnd(); |
372 | 0 |
|
373 | 0 | mIsDirty = true; |
374 | 0 | } |
375 | | |
376 | | /** |
377 | | * nsScannerSharedSubstring |
378 | | */ |
379 | | |
380 | | void |
381 | | nsScannerSharedSubstring::Rebind(const nsScannerIterator &aStart, |
382 | | const nsScannerIterator &aEnd) |
383 | 0 | { |
384 | 0 | // If the start and end positions are inside the same buffer, we must |
385 | 0 | // acquire ownership of the buffer. If not, we can optimize by not holding |
386 | 0 | // onto it. |
387 | 0 |
|
388 | 0 | Buffer *buffer = const_cast<Buffer*>(aStart.buffer()); |
389 | 0 | bool sameBuffer = buffer == aEnd.buffer(); |
390 | 0 |
|
391 | 0 | nsScannerBufferList *bufferList; |
392 | 0 |
|
393 | 0 | if (sameBuffer) { |
394 | 0 | bufferList = aStart.mOwner->mBufferList; |
395 | 0 | bufferList->AddRef(); |
396 | 0 | buffer->IncrementUsageCount(); |
397 | 0 | } |
398 | 0 |
|
399 | 0 | if (mBufferList) |
400 | 0 | ReleaseBuffer(); |
401 | 0 |
|
402 | 0 | if (sameBuffer) { |
403 | 0 | mBuffer = buffer; |
404 | 0 | mBufferList = bufferList; |
405 | 0 | mString.Rebind(aStart.mPosition, aEnd.mPosition); |
406 | 0 | } else { |
407 | 0 | mBuffer = nullptr; |
408 | 0 | mBufferList = nullptr; |
409 | 0 | CopyUnicodeTo(aStart, aEnd, mString); |
410 | 0 | } |
411 | 0 | } |
412 | | |
413 | | void |
414 | | nsScannerSharedSubstring::ReleaseBuffer() |
415 | 0 | { |
416 | 0 | NS_ASSERTION(mBufferList, "Should only be called with non-null mBufferList"); |
417 | 0 | mBuffer->DecrementUsageCount(); |
418 | 0 | mBufferList->DiscardUnreferencedPrefix(mBuffer); |
419 | 0 | mBufferList->Release(); |
420 | 0 | } |
421 | | |
422 | | void |
423 | | nsScannerSharedSubstring::MakeMutable() |
424 | 0 | { |
425 | 0 | nsString temp(mString); // this will force a copy of the data |
426 | 0 | mString.Assign(temp); // mString will now share the just-allocated buffer |
427 | 0 |
|
428 | 0 | ReleaseBuffer(); |
429 | 0 |
|
430 | 0 | mBuffer = nullptr; |
431 | 0 | mBufferList = nullptr; |
432 | 0 | } |
433 | | |
434 | | /** |
435 | | * utils -- based on code from nsReadableUtils.cpp |
436 | | */ |
437 | | |
438 | | // private helper function |
439 | | static inline |
440 | | nsAString::iterator& |
441 | | copy_multifragment_string( nsScannerIterator& first, const nsScannerIterator& last, nsAString::iterator& result ) |
442 | 0 | { |
443 | 0 | typedef nsCharSourceTraits<nsScannerIterator> source_traits; |
444 | 0 | typedef nsCharSinkTraits<nsAString::iterator> sink_traits; |
445 | 0 |
|
446 | 0 | while ( first != last ) |
447 | 0 | { |
448 | 0 | uint32_t distance = source_traits::readable_distance(first, last); |
449 | 0 | sink_traits::write(result, source_traits::read(first), distance); |
450 | 0 | NS_ASSERTION(distance > 0, "|copy_multifragment_string| will never terminate"); |
451 | 0 | source_traits::advance(first, distance); |
452 | 0 | } |
453 | 0 |
|
454 | 0 | return result; |
455 | 0 | } |
456 | | |
457 | | bool |
458 | | CopyUnicodeTo( const nsScannerIterator& aSrcStart, |
459 | | const nsScannerIterator& aSrcEnd, |
460 | | nsAString& aDest ) |
461 | 0 | { |
462 | 0 | mozilla::CheckedInt<nsAString::size_type> distance(Distance(aSrcStart, aSrcEnd)); |
463 | 0 | if (!distance.isValid()) { |
464 | 0 | return false; // overflow detected |
465 | 0 | } |
466 | 0 | |
467 | 0 | if (!aDest.SetLength(distance.value(), mozilla::fallible)) { |
468 | 0 | aDest.Truncate(); |
469 | 0 | return false; // out of memory |
470 | 0 | } |
471 | 0 | auto writer = aDest.BeginWriting(); |
472 | 0 | nsScannerIterator fromBegin(aSrcStart); |
473 | 0 |
|
474 | 0 | copy_multifragment_string(fromBegin, aSrcEnd, writer); |
475 | 0 | return true; |
476 | 0 | } |
477 | | |
478 | | bool |
479 | | AppendUnicodeTo( const nsScannerIterator& aSrcStart, |
480 | | const nsScannerIterator& aSrcEnd, |
481 | | nsScannerSharedSubstring& aDest ) |
482 | 0 | { |
483 | 0 | // Check whether we can just create a dependent string. |
484 | 0 | if (aDest.str().IsEmpty()) { |
485 | 0 | // We can just make |aDest| point to the buffer. |
486 | 0 | // This will take care of copying if the buffer spans fragments. |
487 | 0 | aDest.Rebind(aSrcStart, aSrcEnd); |
488 | 0 | return true; |
489 | 0 | } |
490 | 0 | // The dest string is not empty, so it can't be a dependent substring. |
491 | 0 | return AppendUnicodeTo(aSrcStart, aSrcEnd, aDest.writable()); |
492 | 0 | } |
493 | | |
494 | | bool |
495 | | AppendUnicodeTo( const nsScannerIterator& aSrcStart, |
496 | | const nsScannerIterator& aSrcEnd, |
497 | | nsAString& aDest ) |
498 | 0 | { |
499 | 0 | const nsAString::size_type oldLength = aDest.Length(); |
500 | 0 | CheckedInt<nsAString::size_type> newLen(Distance(aSrcStart, aSrcEnd)); |
501 | 0 | newLen += oldLength; |
502 | 0 | if (!newLen.isValid()) { |
503 | 0 | return false; // overflow detected |
504 | 0 | } |
505 | 0 | |
506 | 0 | if (!aDest.SetLength(newLen.value(), mozilla::fallible)) |
507 | 0 | return false; // out of memory |
508 | 0 | auto writer = aDest.BeginWriting(); |
509 | 0 | std::advance(writer, oldLength); |
510 | 0 | nsScannerIterator fromBegin(aSrcStart); |
511 | 0 |
|
512 | 0 | copy_multifragment_string(fromBegin, aSrcEnd, writer); |
513 | 0 | return true; |
514 | 0 | } |
515 | | |
516 | | bool |
517 | | FindCharInReadable( char16_t aChar, |
518 | | nsScannerIterator& aSearchStart, |
519 | | const nsScannerIterator& aSearchEnd ) |
520 | 0 | { |
521 | 0 | while ( aSearchStart != aSearchEnd ) |
522 | 0 | { |
523 | 0 | int32_t fragmentLength; |
524 | 0 | if ( SameFragment(aSearchStart, aSearchEnd) ) |
525 | 0 | fragmentLength = aSearchEnd.get() - aSearchStart.get(); |
526 | 0 | else |
527 | 0 | fragmentLength = aSearchStart.size_forward(); |
528 | 0 |
|
529 | 0 | const char16_t* charFoundAt = nsCharTraits<char16_t>::find(aSearchStart.get(), fragmentLength, aChar); |
530 | 0 | if ( charFoundAt ) { |
531 | 0 | aSearchStart.advance( charFoundAt - aSearchStart.get() ); |
532 | 0 | return true; |
533 | 0 | } |
534 | 0 | |
535 | 0 | aSearchStart.advance(fragmentLength); |
536 | 0 | } |
537 | 0 |
|
538 | 0 | return false; |
539 | 0 | } |
540 | | |
541 | | bool |
542 | | FindInReadable( const nsAString& aPattern, |
543 | | nsScannerIterator& aSearchStart, |
544 | | nsScannerIterator& aSearchEnd, |
545 | | const nsStringComparator& compare ) |
546 | 0 | { |
547 | 0 | bool found_it = false; |
548 | 0 |
|
549 | 0 | // only bother searching at all if we're given a non-empty range to search |
550 | 0 | if ( aSearchStart != aSearchEnd ) |
551 | 0 | { |
552 | 0 | nsAString::const_iterator aPatternStart, aPatternEnd; |
553 | 0 | aPattern.BeginReading(aPatternStart); |
554 | 0 | aPattern.EndReading(aPatternEnd); |
555 | 0 |
|
556 | 0 | // outer loop keeps searching till we find it or run out of string to search |
557 | 0 | while ( !found_it ) |
558 | 0 | { |
559 | 0 | // fast inner loop (that's what it's called, not what it is) looks for a potential match |
560 | 0 | while ( aSearchStart != aSearchEnd && |
561 | 0 | compare(aPatternStart.get(), aSearchStart.get(), 1, 1) ) |
562 | 0 | ++aSearchStart; |
563 | 0 |
|
564 | 0 | // if we broke out of the `fast' loop because we're out of string ... we're done: no match |
565 | 0 | if ( aSearchStart == aSearchEnd ) |
566 | 0 | break; |
567 | 0 | |
568 | 0 | // otherwise, we're at a potential match, let's see if we really hit one |
569 | 0 | nsAString::const_iterator testPattern(aPatternStart); |
570 | 0 | nsScannerIterator testSearch(aSearchStart); |
571 | 0 |
|
572 | 0 | // slow inner loop verifies the potential match (found by the `fast' loop) at the current position |
573 | 0 | for(;;) |
574 | 0 | { |
575 | 0 | // we already compared the first character in the outer loop, |
576 | 0 | // so we'll advance before the next comparison |
577 | 0 | ++testPattern; |
578 | 0 | ++testSearch; |
579 | 0 |
|
580 | 0 | // if we verified all the way to the end of the pattern, then we found it! |
581 | 0 | if ( testPattern == aPatternEnd ) |
582 | 0 | { |
583 | 0 | found_it = true; |
584 | 0 | aSearchEnd = testSearch; // return the exact found range through the parameters |
585 | 0 | break; |
586 | 0 | } |
587 | 0 | |
588 | 0 | // if we got to end of the string we're searching before we hit the end of the |
589 | 0 | // pattern, we'll never find what we're looking for |
590 | 0 | if ( testSearch == aSearchEnd ) |
591 | 0 | { |
592 | 0 | aSearchStart = aSearchEnd; |
593 | 0 | break; |
594 | 0 | } |
595 | 0 | |
596 | 0 | // else if we mismatched ... it's time to advance to the next search position |
597 | 0 | // and get back into the `fast' loop |
598 | 0 | if ( compare(testPattern.get(), testSearch.get(), 1, 1) ) |
599 | 0 | { |
600 | 0 | ++aSearchStart; |
601 | 0 | break; |
602 | 0 | } |
603 | 0 | } |
604 | 0 | } |
605 | 0 | } |
606 | 0 |
|
607 | 0 | return found_it; |
608 | 0 | } |
609 | | |
610 | | /** |
611 | | * This implementation is simple, but does too much work. |
612 | | * It searches the entire string from left to right, and returns the last match found, if any. |
613 | | * This implementation will be replaced when I get |reverse_iterator|s working. |
614 | | */ |
615 | | bool |
616 | | RFindInReadable( const nsAString& aPattern, |
617 | | nsScannerIterator& aSearchStart, |
618 | | nsScannerIterator& aSearchEnd, |
619 | | const nsStringComparator& aComparator ) |
620 | 0 | { |
621 | 0 | bool found_it = false; |
622 | 0 |
|
623 | 0 | nsScannerIterator savedSearchEnd(aSearchEnd); |
624 | 0 | nsScannerIterator searchStart(aSearchStart), searchEnd(aSearchEnd); |
625 | 0 |
|
626 | 0 | while ( searchStart != searchEnd ) |
627 | 0 | { |
628 | 0 | if ( FindInReadable(aPattern, searchStart, searchEnd, aComparator) ) |
629 | 0 | { |
630 | 0 | found_it = true; |
631 | 0 |
|
632 | 0 | // this is the best match so far, so remember it |
633 | 0 | aSearchStart = searchStart; |
634 | 0 | aSearchEnd = searchEnd; |
635 | 0 |
|
636 | 0 | // ...and get ready to search some more |
637 | 0 | // (it's tempting to set |searchStart=searchEnd| ... but that misses overlapping patterns) |
638 | 0 | ++searchStart; |
639 | 0 | searchEnd = savedSearchEnd; |
640 | 0 | } |
641 | 0 | } |
642 | 0 |
|
643 | 0 | // if we never found it, return an empty range |
644 | 0 | if ( !found_it ) |
645 | 0 | aSearchStart = aSearchEnd; |
646 | 0 |
|
647 | 0 | return found_it; |
648 | 0 | } |