void nsTString_CharT::ReplaceSubstring(const self_type& aTarget, const self_type& aNewValue) { if (!ReplaceSubstring(aTarget, aNewValue, mozilla::fallible)) { // Note that this may wildly underestimate the allocation that failed, as // we could have been replacing multiple copies of aTarget. AllocFailed(mLength + (aNewValue.Length() - aTarget.Length())); } }
void nsTSubstring_CharT::Assign(const self_type& aStr) { if (!Assign(aStr, fallible_t())) { NS_ABORT_OOM(aStr.Length()); } }
void nsTSubstring_CharT::Assign(const self_type& aStr) { if (!Assign(aStr, fallible_t())) { AllocFailed(aStr.Length()); } }
void nsTString_CharT::ReplaceSubstring( const self_type& aTarget, const self_type& aNewValue ) { if (aTarget.Length() == 0) return; uint32_t i = 0; while (i < mLength) { int32_t r = FindSubstring(mData + i, mLength - i, aTarget.Data(), aTarget.Length(), false); if (r == kNotFound) break; Replace(i + r, aTarget.Length(), aNewValue); i += r + aNewValue.Length(); } }
bool nsTSubstring_CharT::Assign( const self_type& str, const fallible_t& ) { // |str| could be sharable. we need to check its flags to know how to // deal with it. if (&str == this) return true; if (!str.mLength) { Truncate(); mFlags |= str.mFlags & F_VOIDED; return true; } if (str.mFlags & F_SHARED) { // nice! we can avoid a string copy :-) // |str| should be null-terminated NS_ASSERTION(str.mFlags & F_TERMINATED, "shared, but not terminated"); ::ReleaseData(mData, mFlags); mData = str.mData; mLength = str.mLength; SetDataFlags(F_TERMINATED | F_SHARED); // get an owning reference to the mData nsStringBuffer::FromData(mData)->AddRef(); return true; } else if (str.mFlags & F_LITERAL) { NS_ABORT_IF_FALSE(str.mFlags & F_TERMINATED, "Unterminated literal"); AssignLiteral(str.mData, str.mLength); return true; } // else, treat this like an ordinary assignment. return Assign(str.Data(), str.Length(), fallible_t()); }
bool nsTString_CharT::ReplaceSubstring(const self_type& aTarget, const self_type& aNewValue, const fallible_t&) { if (aTarget.Length() == 0) return true; // Remember all of the non-matching parts. AutoTArray<Segment, 16> nonMatching; uint32_t i = 0; uint32_t newLength = 0; while (true) { int32_t r = FindSubstring(mData + i, mLength - i, static_cast<const char_type*>(aTarget.Data()), aTarget.Length(), false); int32_t until = (r == kNotFound) ? mLength - i : r; nonMatching.AppendElement(Segment(i, until)); newLength += until; if (r == kNotFound) { break; } newLength += aNewValue.Length(); i += r + aTarget.Length(); if (i >= mLength) { // Add an auxiliary entry at the end of the list to help as an edge case // for the algorithms below. nonMatching.AppendElement(Segment(mLength, 0)); break; } } // If there's only one non-matching segment, then the target string was not // found, and there's nothing to do. if (nonMatching.Length() == 1) { MOZ_ASSERT(nonMatching[0].mBegin == 0 && nonMatching[0].mLength == mLength, "We should have the correct non-matching segment."); return true; } // Make sure that we can mutate our buffer. // Note that we always allocate at least an mLength sized buffer, because the // rest of the algorithm relies on having access to all of the original // string. In other words, we over-allocate in the shrinking case. char_type* oldData; uint32_t oldFlags; if (!MutatePrep(XPCOM_MAX(mLength, newLength), &oldData, &oldFlags)) return false; if (oldData) { // Copy all of the old data to the new buffer. char_traits::copy(mData, oldData, mLength); ::ReleaseData(oldData, oldFlags); } if (aTarget.Length() >= aNewValue.Length()) { // In the shrinking case, start filling the buffer from the beginning. const uint32_t delta = (aTarget.Length() - aNewValue.Length()); for (i = 1; i < nonMatching.Length(); ++i) { // When we move the i'th non-matching segment into position, we need to // account for the characters deleted by the previous |i| replacements by // subtracting |i * delta|. const char_type* sourceSegmentPtr = mData + nonMatching[i].mBegin; char_type* destinationSegmentPtr = mData + nonMatching[i].mBegin - i * delta; // Write the i'th replacement immediately before the new i'th non-matching // segment. char_traits::copy(destinationSegmentPtr - aNewValue.Length(), aNewValue.Data(), aNewValue.Length()); char_traits::move(destinationSegmentPtr, sourceSegmentPtr, nonMatching[i].mLength); } } else { // In the growing case, start filling the buffer from the end. const uint32_t delta = (aNewValue.Length() - aTarget.Length()); for (i = nonMatching.Length() - 1; i > 0; --i) { // When we move the i'th non-matching segment into position, we need to // account for the characters added by the previous |i| replacements by // adding |i * delta|. const char_type* sourceSegmentPtr = mData + nonMatching[i].mBegin; char_type* destinationSegmentPtr = mData + nonMatching[i].mBegin + i * delta; char_traits::move(destinationSegmentPtr, sourceSegmentPtr, nonMatching[i].mLength); // Write the i'th replacement immediately before the new i'th non-matching // segment. char_traits::copy(destinationSegmentPtr - aNewValue.Length(), aNewValue.Data(), aNewValue.Length()); } } // Adjust the length and make sure the string is null terminated. mLength = newLength; mData[mLength] = char_type(0); return true; }