Bug 1547623 [wpt PR 16386] - [Animation Worklet] Support effects with no target,...
[gecko.git] / parser / htmlparser / nsScannerString.cpp
blob60a0bb0fb68435a17a95e4ef702f7fc7829ff74d
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/. */
7 #include <stdlib.h>
8 #include "nsScannerString.h"
9 #include "mozilla/CheckedInt.h"
11 /**
12 * nsScannerBufferList
15 #define MAX_CAPACITY \
16 ((UINT32_MAX / sizeof(char16_t)) - (sizeof(Buffer) + sizeof(char16_t)))
18 nsScannerBufferList::Buffer* nsScannerBufferList::AllocBufferFromString(
19 const nsAString& aString) {
20 uint32_t len = aString.Length();
21 Buffer* buf = AllocBuffer(len);
23 if (buf) {
24 nsAString::const_iterator source;
25 aString.BeginReading(source);
26 nsCharTraits<char16_t>::copy(buf->DataStart(), source.get(), len);
28 return buf;
31 nsScannerBufferList::Buffer* nsScannerBufferList::AllocBuffer(
32 uint32_t capacity) {
33 if (capacity > MAX_CAPACITY) return nullptr;
35 void* ptr = malloc(sizeof(Buffer) + (capacity + 1) * sizeof(char16_t));
36 if (!ptr) return nullptr;
38 Buffer* buf = new (ptr) Buffer();
40 buf->mUsageCount = 0;
41 buf->mDataEnd = buf->DataStart() + capacity;
43 // XXX null terminate. this shouldn't be required, but we do it because
44 // nsScanner erroneously thinks it can dereference DataEnd :-(
45 *buf->mDataEnd = char16_t(0);
46 return buf;
49 void nsScannerBufferList::ReleaseAll() {
50 while (!mBuffers.isEmpty()) {
51 Buffer* node = mBuffers.popFirst();
52 // printf(">>> freeing buffer @%p\n", node);
53 free(node);
57 void nsScannerBufferList::SplitBuffer(const Position& pos) {
58 // splitting to the right keeps the work string and any extant token
59 // pointing to and holding a reference count on the same buffer.
61 Buffer* bufferToSplit = pos.mBuffer;
62 NS_ASSERTION(bufferToSplit, "null pointer");
64 uint32_t splitOffset = pos.mPosition - bufferToSplit->DataStart();
65 NS_ASSERTION(pos.mPosition >= bufferToSplit->DataStart() &&
66 splitOffset <= bufferToSplit->DataLength(),
67 "split offset is outside buffer");
69 uint32_t len = bufferToSplit->DataLength() - splitOffset;
70 Buffer* new_buffer = AllocBuffer(len);
71 if (new_buffer) {
72 nsCharTraits<char16_t>::copy(new_buffer->DataStart(),
73 bufferToSplit->DataStart() + splitOffset, len);
74 InsertAfter(new_buffer, bufferToSplit);
75 bufferToSplit->SetDataLength(splitOffset);
79 void nsScannerBufferList::DiscardUnreferencedPrefix(Buffer* aBuf) {
80 if (aBuf == Head()) {
81 while (!mBuffers.isEmpty() && !Head()->IsInUse()) {
82 Buffer* buffer = Head();
83 buffer->remove();
84 free(buffer);
89 size_t nsScannerBufferList::Position::Distance(const Position& aStart,
90 const Position& aEnd) {
91 size_t result = 0;
92 if (aStart.mBuffer == aEnd.mBuffer) {
93 result = aEnd.mPosition - aStart.mPosition;
94 } else {
95 result = aStart.mBuffer->DataEnd() - aStart.mPosition;
96 for (Buffer* b = aStart.mBuffer->Next(); b != aEnd.mBuffer; b = b->Next())
97 result += b->DataLength();
98 result += aEnd.mPosition - aEnd.mBuffer->DataStart();
100 return result;
104 * nsScannerSubstring
107 nsScannerSubstring::nsScannerSubstring()
108 : mStart(nullptr, nullptr),
109 mEnd(nullptr, nullptr),
110 mBufferList(nullptr),
111 mLength(0),
112 mIsDirty(true) {}
114 nsScannerSubstring::nsScannerSubstring(const nsAString& s)
115 : mBufferList(nullptr), mIsDirty(true) {
116 Rebind(s);
119 nsScannerSubstring::~nsScannerSubstring() {
120 release_ownership_of_buffer_list();
123 int32_t nsScannerSubstring::CountChar(char16_t c) const {
125 re-write this to use a counting sink
128 size_type result = 0;
129 size_type lengthToExamine = Length();
131 nsScannerIterator iter;
132 for (BeginReading(iter);;) {
133 int32_t lengthToExamineInThisFragment = iter.size_forward();
134 const char16_t* fromBegin = iter.get();
135 result += size_type(
136 NS_COUNT(fromBegin, fromBegin + lengthToExamineInThisFragment, c));
137 if (!(lengthToExamine -= lengthToExamineInThisFragment)) return result;
138 iter.advance(lengthToExamineInThisFragment);
140 // never reached; quiets warnings
141 return 0;
144 void nsScannerSubstring::Rebind(const nsScannerSubstring& aString,
145 const nsScannerIterator& aStart,
146 const nsScannerIterator& aEnd) {
147 // allow for the case where &aString == this
149 aString.acquire_ownership_of_buffer_list();
150 release_ownership_of_buffer_list();
152 mStart = aStart;
153 mEnd = aEnd;
154 mBufferList = aString.mBufferList;
155 mLength = Distance(aStart, aEnd);
156 mIsDirty = true;
159 void nsScannerSubstring::Rebind(const nsAString& aString) {
160 release_ownership_of_buffer_list();
162 mBufferList = new nsScannerBufferList(AllocBufferFromString(aString));
163 mIsDirty = true;
165 init_range_from_buffer_list();
166 acquire_ownership_of_buffer_list();
169 const nsAString& nsScannerSubstring::AsString() const {
170 if (mIsDirty) {
171 nsScannerSubstring* mutable_this = const_cast<nsScannerSubstring*>(this);
173 if (mStart.mBuffer == mEnd.mBuffer) {
174 // We only have a single fragment to deal with, so just return it
175 // as a substring.
176 mutable_this->mFlattenedRep.Rebind(mStart.mPosition, mEnd.mPosition);
177 } else {
178 // Otherwise, we need to copy the data into a flattened buffer.
179 nsScannerIterator start, end;
180 CopyUnicodeTo(BeginReading(start), EndReading(end),
181 mutable_this->mFlattenedRep);
184 mutable_this->mIsDirty = false;
187 return mFlattenedRep;
190 nsScannerIterator& nsScannerSubstring::BeginReading(
191 nsScannerIterator& iter) const {
192 iter.mOwner = this;
194 iter.mFragment.mBuffer = mStart.mBuffer;
195 iter.mFragment.mFragmentStart = mStart.mPosition;
196 if (mStart.mBuffer == mEnd.mBuffer)
197 iter.mFragment.mFragmentEnd = mEnd.mPosition;
198 else
199 iter.mFragment.mFragmentEnd = mStart.mBuffer->DataEnd();
201 iter.mPosition = mStart.mPosition;
202 iter.normalize_forward();
203 return iter;
206 nsScannerIterator& nsScannerSubstring::EndReading(
207 nsScannerIterator& iter) const {
208 iter.mOwner = this;
210 iter.mFragment.mBuffer = mEnd.mBuffer;
211 iter.mFragment.mFragmentEnd = mEnd.mPosition;
212 if (mStart.mBuffer == mEnd.mBuffer)
213 iter.mFragment.mFragmentStart = mStart.mPosition;
214 else
215 iter.mFragment.mFragmentStart = mEnd.mBuffer->DataStart();
217 iter.mPosition = mEnd.mPosition;
218 // must not |normalize_backward| as that would likely invalidate tests like
219 // |while ( first != last )|
220 return iter;
223 bool nsScannerSubstring::GetNextFragment(nsScannerFragment& frag) const {
224 // check to see if we are at the end of the buffer list
225 if (frag.mBuffer == mEnd.mBuffer) return false;
227 frag.mBuffer = frag.mBuffer->getNext();
229 if (frag.mBuffer == mStart.mBuffer)
230 frag.mFragmentStart = mStart.mPosition;
231 else
232 frag.mFragmentStart = frag.mBuffer->DataStart();
234 if (frag.mBuffer == mEnd.mBuffer)
235 frag.mFragmentEnd = mEnd.mPosition;
236 else
237 frag.mFragmentEnd = frag.mBuffer->DataEnd();
239 return true;
242 bool nsScannerSubstring::GetPrevFragment(nsScannerFragment& frag) const {
243 // check to see if we are at the beginning of the buffer list
244 if (frag.mBuffer == mStart.mBuffer) return false;
246 frag.mBuffer = frag.mBuffer->getPrevious();
248 if (frag.mBuffer == mStart.mBuffer)
249 frag.mFragmentStart = mStart.mPosition;
250 else
251 frag.mFragmentStart = frag.mBuffer->DataStart();
253 if (frag.mBuffer == mEnd.mBuffer)
254 frag.mFragmentEnd = mEnd.mPosition;
255 else
256 frag.mFragmentEnd = frag.mBuffer->DataEnd();
258 return true;
262 * nsScannerString
265 nsScannerString::nsScannerString(Buffer* aBuf) {
266 mBufferList = new nsScannerBufferList(aBuf);
268 init_range_from_buffer_list();
269 acquire_ownership_of_buffer_list();
272 void nsScannerString::AppendBuffer(Buffer* aBuf) {
273 mBufferList->Append(aBuf);
274 mLength += aBuf->DataLength();
276 mEnd.mBuffer = aBuf;
277 mEnd.mPosition = aBuf->DataEnd();
279 mIsDirty = true;
282 void nsScannerString::DiscardPrefix(const nsScannerIterator& aIter) {
283 Position old_start(mStart);
284 mStart = aIter;
285 mLength -= Position::Distance(old_start, mStart);
287 mStart.mBuffer->IncrementUsageCount();
288 old_start.mBuffer->DecrementUsageCount();
290 mBufferList->DiscardUnreferencedPrefix(old_start.mBuffer);
292 mIsDirty = true;
295 void nsScannerString::UngetReadable(const nsAString& aReadable,
296 const nsScannerIterator& aInsertPoint)
298 * Warning: this routine manipulates the shared buffer list in an
299 * unexpected way. The original design did not really allow for
300 * insertions, but this call promises that if called for a point after the
301 * end of all extant token strings, that no token string or the work string
302 * will be invalidated.
304 * This routine is protected because it is the responsibility of the
305 * derived class to keep those promises.
308 Position insertPos(aInsertPoint);
310 mBufferList->SplitBuffer(insertPos);
311 // splitting to the right keeps the work string and any extant token
312 // pointing to and holding a reference count on the same buffer
314 Buffer* new_buffer = AllocBufferFromString(aReadable);
315 // make a new buffer with all the data to insert...
316 // BULLSHIT ALERT: we may have empty space to re-use in the split buffer,
317 // measure the cost of this and decide if we should do the work to fill
318 // it
320 Buffer* buffer_to_split = insertPos.mBuffer;
321 mBufferList->InsertAfter(new_buffer, buffer_to_split);
322 mLength += aReadable.Length();
324 mEnd.mBuffer = mBufferList->Tail();
325 mEnd.mPosition = mEnd.mBuffer->DataEnd();
327 mIsDirty = true;
331 * nsScannerSharedSubstring
334 void nsScannerSharedSubstring::Rebind(const nsScannerIterator& aStart,
335 const nsScannerIterator& aEnd) {
336 // If the start and end positions are inside the same buffer, we must
337 // acquire ownership of the buffer. If not, we can optimize by not holding
338 // onto it.
340 Buffer* buffer = const_cast<Buffer*>(aStart.buffer());
341 bool sameBuffer = buffer == aEnd.buffer();
343 nsScannerBufferList* bufferList;
345 if (sameBuffer) {
346 bufferList = aStart.mOwner->mBufferList;
347 bufferList->AddRef();
348 buffer->IncrementUsageCount();
351 if (mBufferList) ReleaseBuffer();
353 if (sameBuffer) {
354 mBuffer = buffer;
355 mBufferList = bufferList;
356 mString.Rebind(aStart.mPosition, aEnd.mPosition);
357 } else {
358 mBuffer = nullptr;
359 mBufferList = nullptr;
360 CopyUnicodeTo(aStart, aEnd, mString);
364 void nsScannerSharedSubstring::ReleaseBuffer() {
365 NS_ASSERTION(mBufferList, "Should only be called with non-null mBufferList");
366 mBuffer->DecrementUsageCount();
367 mBufferList->DiscardUnreferencedPrefix(mBuffer);
368 mBufferList->Release();
371 void nsScannerSharedSubstring::MakeMutable() {
372 nsString temp(mString); // this will force a copy of the data
373 mString.Assign(temp); // mString will now share the just-allocated buffer
375 ReleaseBuffer();
377 mBuffer = nullptr;
378 mBufferList = nullptr;
382 * utils -- based on code from nsReadableUtils.cpp
385 // private helper function
386 static inline nsAString::iterator& copy_multifragment_string(
387 nsScannerIterator& first, const nsScannerIterator& last,
388 nsAString::iterator& result) {
389 typedef nsCharSourceTraits<nsScannerIterator> source_traits;
390 typedef nsCharSinkTraits<nsAString::iterator> sink_traits;
392 while (first != last) {
393 uint32_t distance = source_traits::readable_distance(first, last);
394 sink_traits::write(result, source_traits::read(first), distance);
395 NS_ASSERTION(distance > 0,
396 "|copy_multifragment_string| will never terminate");
397 source_traits::advance(first, distance);
400 return result;
403 bool CopyUnicodeTo(const nsScannerIterator& aSrcStart,
404 const nsScannerIterator& aSrcEnd, nsAString& aDest) {
405 mozilla::CheckedInt<nsAString::size_type> distance(
406 Distance(aSrcStart, aSrcEnd));
407 if (!distance.isValid()) {
408 return false; // overflow detected
411 if (!aDest.SetLength(distance.value(), mozilla::fallible)) {
412 aDest.Truncate();
413 return false; // out of memory
415 auto writer = aDest.BeginWriting();
416 nsScannerIterator fromBegin(aSrcStart);
418 copy_multifragment_string(fromBegin, aSrcEnd, writer);
419 return true;
422 bool AppendUnicodeTo(const nsScannerIterator& aSrcStart,
423 const nsScannerIterator& aSrcEnd,
424 nsScannerSharedSubstring& aDest) {
425 // Check whether we can just create a dependent string.
426 if (aDest.str().IsEmpty()) {
427 // We can just make |aDest| point to the buffer.
428 // This will take care of copying if the buffer spans fragments.
429 aDest.Rebind(aSrcStart, aSrcEnd);
430 return true;
432 // The dest string is not empty, so it can't be a dependent substring.
433 return AppendUnicodeTo(aSrcStart, aSrcEnd, aDest.writable());
436 bool AppendUnicodeTo(const nsScannerIterator& aSrcStart,
437 const nsScannerIterator& aSrcEnd, nsAString& aDest) {
438 const nsAString::size_type oldLength = aDest.Length();
439 CheckedInt<nsAString::size_type> newLen(Distance(aSrcStart, aSrcEnd));
440 newLen += oldLength;
441 if (!newLen.isValid()) {
442 return false; // overflow detected
445 if (!aDest.SetLength(newLen.value(), mozilla::fallible))
446 return false; // out of memory
447 auto writer = aDest.BeginWriting();
448 std::advance(writer, oldLength);
449 nsScannerIterator fromBegin(aSrcStart);
451 copy_multifragment_string(fromBegin, aSrcEnd, writer);
452 return true;
455 bool FindCharInReadable(char16_t aChar, nsScannerIterator& aSearchStart,
456 const nsScannerIterator& aSearchEnd) {
457 while (aSearchStart != aSearchEnd) {
458 int32_t fragmentLength;
459 if (SameFragment(aSearchStart, aSearchEnd))
460 fragmentLength = aSearchEnd.get() - aSearchStart.get();
461 else
462 fragmentLength = aSearchStart.size_forward();
464 const char16_t* charFoundAt =
465 nsCharTraits<char16_t>::find(aSearchStart.get(), fragmentLength, aChar);
466 if (charFoundAt) {
467 aSearchStart.advance(charFoundAt - aSearchStart.get());
468 return true;
471 aSearchStart.advance(fragmentLength);
474 return false;
477 bool FindInReadable(const nsAString& aPattern, nsScannerIterator& aSearchStart,
478 nsScannerIterator& aSearchEnd,
479 const nsStringComparator& compare) {
480 bool found_it = false;
482 // only bother searching at all if we're given a non-empty range to search
483 if (aSearchStart != aSearchEnd) {
484 nsAString::const_iterator aPatternStart, aPatternEnd;
485 aPattern.BeginReading(aPatternStart);
486 aPattern.EndReading(aPatternEnd);
488 // outer loop keeps searching till we find it or run out of string to search
489 while (!found_it) {
490 // fast inner loop (that's what it's called, not what it is) looks for a
491 // potential match
492 while (aSearchStart != aSearchEnd &&
493 compare(aPatternStart.get(), aSearchStart.get(), 1, 1))
494 ++aSearchStart;
496 // if we broke out of the `fast' loop because we're out of string ...
497 // we're done: no match
498 if (aSearchStart == aSearchEnd) break;
500 // otherwise, we're at a potential match, let's see if we really hit one
501 nsAString::const_iterator testPattern(aPatternStart);
502 nsScannerIterator testSearch(aSearchStart);
504 // slow inner loop verifies the potential match (found by the `fast' loop)
505 // at the current position
506 for (;;) {
507 // we already compared the first character in the outer loop,
508 // so we'll advance before the next comparison
509 ++testPattern;
510 ++testSearch;
512 // if we verified all the way to the end of the pattern, then we found
513 // it!
514 if (testPattern == aPatternEnd) {
515 found_it = true;
516 aSearchEnd = testSearch; // return the exact found range through the
517 // parameters
518 break;
521 // if we got to end of the string we're searching before we hit the end
522 // of the
523 // pattern, we'll never find what we're looking for
524 if (testSearch == aSearchEnd) {
525 aSearchStart = aSearchEnd;
526 break;
529 // else if we mismatched ... it's time to advance to the next search
530 // position
531 // and get back into the `fast' loop
532 if (compare(testPattern.get(), testSearch.get(), 1, 1)) {
533 ++aSearchStart;
534 break;
540 return found_it;
544 * This implementation is simple, but does too much work.
545 * It searches the entire string from left to right, and returns the last match
546 * found, if any. This implementation will be replaced when I get
547 * |reverse_iterator|s working.
549 bool RFindInReadable(const nsAString& aPattern, nsScannerIterator& aSearchStart,
550 nsScannerIterator& aSearchEnd,
551 const nsStringComparator& aComparator) {
552 bool found_it = false;
554 nsScannerIterator savedSearchEnd(aSearchEnd);
555 nsScannerIterator searchStart(aSearchStart), searchEnd(aSearchEnd);
557 while (searchStart != searchEnd) {
558 if (FindInReadable(aPattern, searchStart, searchEnd, aComparator)) {
559 found_it = true;
561 // this is the best match so far, so remember it
562 aSearchStart = searchStart;
563 aSearchEnd = searchEnd;
565 // ...and get ready to search some more
566 // (it's tempting to set |searchStart=searchEnd| ... but that misses
567 // overlapping patterns)
568 ++searchStart;
569 searchEnd = savedSearchEnd;
573 // if we never found it, return an empty range
574 if (!found_it) aSearchStart = aSearchEnd;
576 return found_it;