LCOV - code coverage report
Current view: top level - xpcom/string - nsTSubstring.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 383 526 72.8 %
Date: 2017-07-14 16:53:18 Functions: 106 137 77.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       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 "nsASCIIMask.h"
       8             : #include "mozilla/CheckedInt.h"
       9             : #include "mozilla/double-conversion.h"
      10             : #include "mozilla/MemoryReporting.h"
      11             : #include "mozilla/Printf.h"
      12             : 
      13             : using double_conversion::DoubleToStringConverter;
      14             : 
      15             : const nsTSubstring_CharT::size_type nsTSubstring_CharT::kMaxCapacity =
      16             :     (nsTSubstring_CharT::size_type(-1) /
      17             :         2 - sizeof(nsStringBuffer)) /
      18             :     sizeof(nsTSubstring_CharT::char_type) - 2;
      19             : 
      20             : #ifdef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
      21      367514 : nsTSubstring_CharT::nsTSubstring_CharT(char_type* aData, size_type aLength,
      22             :                                        DataFlags aDataFlags,
      23      367514 :                                        ClassFlags aClassFlags)
      24      367514 :   : nsTStringRepr_CharT(aData, aLength, aDataFlags, aClassFlags)
      25             : {
      26      367514 :   MOZ_RELEASE_ASSERT(CheckCapacity(aLength), "String is too large.");
      27             : 
      28      367514 :   if (aDataFlags & DataFlags::OWNED) {
      29           0 :     STRING_STAT_INCREMENT(Adopt);
      30           0 :     MOZ_LOG_CTOR(mData, "StringAdopt", 1);
      31             :   }
      32      367514 : }
      33             : #endif /* XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE */
      34             : 
      35             : /**
      36             :  * helper function for down-casting a nsTSubstring to a nsTFixedString.
      37             :  */
      38             : inline const nsTFixedString_CharT*
      39      281973 : AsFixedString(const nsTSubstring_CharT* aStr)
      40             : {
      41      281973 :   return static_cast<const nsTFixedString_CharT*>(aStr);
      42             : }
      43             : 
      44             : /**
      45             :  * this function is called to prepare mData for writing.  the given capacity
      46             :  * indicates the required minimum storage size for mData, in sizeof(char_type)
      47             :  * increments.  this function returns true if the operation succeeds.  it also
      48             :  * returns the old data and old flags members if mData is newly allocated.
      49             :  * the old data must be released by the caller.
      50             :  */
      51             : bool
      52      199877 : nsTSubstring_CharT::MutatePrep(size_type aCapacity, char_type** aOldData,
      53             :                                DataFlags* aOldDataFlags)
      54             : {
      55             :   // initialize to no old data
      56      199877 :   *aOldData = nullptr;
      57      199877 :   *aOldDataFlags = DataFlags(0);
      58             : 
      59      199877 :   size_type curCapacity = Capacity();
      60             : 
      61             :   // If |aCapacity > kMaxCapacity|, then our doubling algorithm may not be
      62             :   // able to allocate it.  Just bail out in cases like that.  We don't want
      63             :   // to be allocating 2GB+ strings anyway.
      64             :   static_assert((sizeof(nsStringBuffer) & 0x1) == 0,
      65             :                 "bad size for nsStringBuffer");
      66      199876 :   if (!CheckCapacity(aCapacity)) {
      67           0 :       return false;
      68             :   }
      69             : 
      70             :   // |curCapacity == 0| means that the buffer is immutable or 0-sized, so we
      71             :   // need to allocate a new buffer. We cannot use the existing buffer even
      72             :   // though it might be large enough.
      73             : 
      74      199876 :   if (curCapacity != 0) {
      75       61900 :     if (aCapacity <= curCapacity) {
      76       52203 :       mDataFlags &= ~DataFlags::VOIDED;  // mutation clears voided flag
      77       52203 :       return true;
      78             :     }
      79             :   }
      80             : 
      81      147673 :   if (curCapacity < aCapacity) {
      82             :     // We increase our capacity so that the allocated buffer grows
      83             :     // exponentially, which gives us amortized O(1) appending. Below the
      84             :     // threshold, we use powers-of-two. Above the threshold, we grow by at
      85             :     // least 1.125, rounding up to the nearest MiB.
      86      147357 :     const size_type slowGrowthThreshold = 8 * 1024 * 1024;
      87             : 
      88             :     // nsStringBuffer allocates sizeof(nsStringBuffer) + passed size, and
      89             :     // storageSize below wants extra 1 * sizeof(char_type).
      90             :     const size_type neededExtraSpace =
      91      147357 :       sizeof(nsStringBuffer) / sizeof(char_type) + 1;
      92             : 
      93             :     size_type temp;
      94      147357 :     if (aCapacity >= slowGrowthThreshold) {
      95           0 :       size_type minNewCapacity = curCapacity + (curCapacity >> 3); // multiply by 1.125
      96           0 :       temp = XPCOM_MAX(aCapacity, minNewCapacity) + neededExtraSpace;
      97             : 
      98             :       // Round up to the next multiple of MiB, but ensure the expected
      99             :       // capacity doesn't include the extra space required by nsStringBuffer
     100             :       // and null-termination.
     101           0 :       const size_t MiB = 1 << 20;
     102           0 :       temp = (MiB * ((temp + MiB - 1) / MiB)) - neededExtraSpace;
     103             :     } else {
     104             :       // Round up to the next power of two.
     105      147357 :       temp =
     106      147357 :         mozilla::RoundUpPow2(aCapacity + neededExtraSpace) - neededExtraSpace;
     107             :     }
     108             : 
     109      147357 :     MOZ_ASSERT(XPCOM_MIN(temp, kMaxCapacity) >= aCapacity,
     110             :                "should have hit the early return at the top");
     111      147357 :     aCapacity = XPCOM_MIN(temp, kMaxCapacity);
     112             :   }
     113             : 
     114             :   //
     115             :   // several cases:
     116             :   //
     117             :   //  (1) we have a shared buffer (mDataFlags & DataFlags::SHARED)
     118             :   //  (2) we have an owned buffer (mDataFlags & DataFlags::OWNED)
     119             :   //  (3) we have a fixed buffer (mDataFlags & DataFlags::FIXED)
     120             :   //  (4) we have a readonly buffer
     121             :   //
     122             :   // requiring that we in some cases preserve the data before creating
     123             :   // a new buffer complicates things just a bit ;-)
     124             :   //
     125             : 
     126      147674 :   size_type storageSize = (aCapacity + 1) * sizeof(char_type);
     127             : 
     128             :   // case #1
     129      147674 :   if (mDataFlags & DataFlags::SHARED) {
     130       10099 :     nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
     131       10099 :     if (!hdr->IsReadonly()) {
     132        2400 :       nsStringBuffer* newHdr = nsStringBuffer::Realloc(hdr, storageSize);
     133        2400 :       if (!newHdr) {
     134           0 :         return false;  // out-of-memory (original header left intact)
     135             :       }
     136             : 
     137        2400 :       hdr = newHdr;
     138        2400 :       mData = (char_type*)hdr->Data();
     139        2400 :       mDataFlags &= ~DataFlags::VOIDED;  // mutation clears voided flag
     140        2400 :       return true;
     141             :     }
     142             :   }
     143             : 
     144             :   char_type* newData;
     145             :   DataFlags newDataFlags;
     146             : 
     147             :   // if we have a fixed buffer of sufficient size, then use it.  this helps
     148             :   // avoid heap allocations.
     149      371204 :   if ((mClassFlags & ClassFlags::FIXED) &&
     150      225930 :       (aCapacity < AsFixedString(this)->mFixedCapacity)) {
     151       57732 :     newData = AsFixedString(this)->mFixedBuf;
     152       57732 :     newDataFlags = DataFlags::TERMINATED | DataFlags::FIXED;
     153             :   } else {
     154             :     // if we reach here then, we must allocate a new buffer.  we cannot
     155             :     // make use of our DataFlags::OWNED or DataFlags::FIXED buffers because they are not
     156             :     // large enough.
     157             : 
     158             :     nsStringBuffer* newHdr =
     159       87541 :       nsStringBuffer::Alloc(storageSize).take();
     160       87543 :     if (!newHdr) {
     161           0 :       return false;  // we are still in a consistent state
     162             :     }
     163             : 
     164       87543 :     newData = (char_type*)newHdr->Data();
     165       87543 :     newDataFlags = DataFlags::TERMINATED | DataFlags::SHARED;
     166             :   }
     167             : 
     168             :   // save old data and flags
     169      145274 :   *aOldData = mData;
     170      145274 :   *aOldDataFlags = mDataFlags;
     171             : 
     172      145274 :   mData = newData;
     173      145274 :   mDataFlags = newDataFlags;
     174             : 
     175             :   // mLength does not change
     176             : 
     177             :   // though we are not necessarily terminated at the moment, now is probably
     178             :   // still the best time to set DataFlags::TERMINATED.
     179             : 
     180      145274 :   return true;
     181             : }
     182             : 
     183             : void
     184      784294 : nsTSubstring_CharT::Finalize()
     185             : {
     186      784294 :   ::ReleaseData(mData, mDataFlags);
     187             :   // mData, mLength, and mDataFlags are purposefully left dangling
     188      784295 : }
     189             : 
     190             : bool
     191      227761 : nsTSubstring_CharT::ReplacePrep(index_type aCutStart,
     192             :                                 size_type aCutLength,
     193             :                                 size_type aNewLength)
     194             : {
     195      227761 :   aCutLength = XPCOM_MIN(aCutLength, mLength - aCutStart);
     196             : 
     197      227761 :   mozilla::CheckedInt<size_type> newTotalLen = mLength;
     198      227761 :   newTotalLen += aNewLength;
     199      227761 :   newTotalLen -= aCutLength;
     200      227761 :   if (!newTotalLen.isValid()) {
     201           0 :     return false;
     202             :   }
     203             : 
     204      227761 :   if (aCutStart == mLength && Capacity() > newTotalLen.value()) {
     205      113100 :     mDataFlags &= ~DataFlags::VOIDED;
     206      113100 :     mData[newTotalLen.value()] = char_type(0);
     207      113100 :     mLength = newTotalLen.value();
     208      113100 :     return true;
     209             :   }
     210             : 
     211      114661 :   return ReplacePrepInternal(aCutStart, aCutLength, aNewLength,
     212      114661 :                              newTotalLen.value());
     213             : }
     214             : 
     215             : bool
     216      114661 : nsTSubstring_CharT::ReplacePrepInternal(index_type aCutStart, size_type aCutLen,
     217             :                                         size_type aFragLen, size_type aNewLen)
     218             : {
     219             :   char_type* oldData;
     220             :   DataFlags oldFlags;
     221      114661 :   if (!MutatePrep(aNewLen, &oldData, &oldFlags)) {
     222           0 :     return false;  // out-of-memory
     223             :   }
     224             : 
     225      114660 :   if (oldData) {
     226             :     // determine whether or not we need to copy part of the old string
     227             :     // over to the new string.
     228             : 
     229      109823 :     if (aCutStart > 0) {
     230             :       // copy prefix from old string
     231        5059 :       char_traits::copy(mData, oldData, aCutStart);
     232             :     }
     233             : 
     234      109824 :     if (aCutStart + aCutLen < mLength) {
     235             :       // copy suffix from old string to new offset
     236          29 :       size_type from = aCutStart + aCutLen;
     237          29 :       size_type fromLen = mLength - from;
     238          29 :       uint32_t to = aCutStart + aFragLen;
     239          29 :       char_traits::copy(mData + to, oldData + from, fromLen);
     240             :     }
     241             : 
     242      109824 :     ::ReleaseData(oldData, oldFlags);
     243             :   } else {
     244             :     // original data remains intact
     245             : 
     246             :     // determine whether or not we need to move part of the existing string
     247             :     // to make room for the requested hole.
     248        4837 :     if (aFragLen != aCutLen && aCutStart + aCutLen < mLength) {
     249         956 :       uint32_t from = aCutStart + aCutLen;
     250         956 :       uint32_t fromLen = mLength - from;
     251         956 :       uint32_t to = aCutStart + aFragLen;
     252         956 :       char_traits::move(mData + to, mData + from, fromLen);
     253             :     }
     254             :   }
     255             : 
     256             :   // add null terminator (mutable mData always has room for the null-
     257             :   // terminator).
     258      114661 :   mData[aNewLen] = char_type(0);
     259      114661 :   mLength = aNewLen;
     260             : 
     261      114661 :   return true;
     262             : }
     263             : 
     264             : nsTSubstring_CharT::size_type
     265      424646 : nsTSubstring_CharT::Capacity() const
     266             : {
     267             :   // return 0 to indicate an immutable or 0-sized buffer
     268             : 
     269             :   size_type capacity;
     270      424646 :   if (mDataFlags & DataFlags::SHARED) {
     271             :     // if the string is readonly, then we pretend that it has no capacity.
     272       47452 :     nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
     273       47452 :     if (hdr->IsReadonly()) {
     274        9794 :       capacity = 0;
     275             :     } else {
     276       37652 :       capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
     277             :     }
     278      377198 :   } else if (mDataFlags & DataFlags::FIXED) {
     279      143584 :     capacity = AsFixedString(this)->mFixedCapacity;
     280      233614 :   } else if (mDataFlags & DataFlags::OWNED) {
     281             :     // we don't store the capacity of an adopted buffer because that would
     282             :     // require an additional member field.  the best we can do is base the
     283             :     // capacity on our length.  remains to be seen if this is the right
     284             :     // trade-off.
     285           0 :     capacity = mLength;
     286             :   } else {
     287      233614 :     capacity = 0;
     288             :   }
     289             : 
     290      424644 :   return capacity;
     291             : }
     292             : 
     293             : bool
     294      108572 : nsTSubstring_CharT::EnsureMutable(size_type aNewLen)
     295             : {
     296      108572 :   if (aNewLen == size_type(-1) || aNewLen == mLength) {
     297      108566 :     if (mDataFlags & (DataFlags::FIXED | DataFlags::OWNED)) {
     298       71376 :       return true;
     299             :     }
     300      109669 :     if ((mDataFlags & DataFlags::SHARED) &&
     301       72479 :         !nsStringBuffer::FromData(mData)->IsReadonly()) {
     302       34360 :       return true;
     303             :     }
     304             : 
     305        2830 :     aNewLen = mLength;
     306             :   }
     307        2836 :   return SetLength(aNewLen, mozilla::fallible);
     308             : }
     309             : 
     310             : // ---------------------------------------------------------------------------
     311             : 
     312             : // This version of Assign is optimized for single-character assignment.
     313             : void
     314         143 : nsTSubstring_CharT::Assign(char_type aChar)
     315             : {
     316         143 :   if (!ReplacePrep(0, mLength, 1)) {
     317           0 :     AllocFailed(mLength);
     318             :   }
     319             : 
     320         143 :   *mData = aChar;
     321         143 : }
     322             : 
     323             : bool
     324           0 : nsTSubstring_CharT::Assign(char_type aChar, const fallible_t&)
     325             : {
     326           0 :   if (!ReplacePrep(0, mLength, 1)) {
     327           0 :     return false;
     328             :   }
     329             : 
     330           0 :   *mData = aChar;
     331           0 :   return true;
     332             : }
     333             : 
     334             : void
     335        8938 : nsTSubstring_CharT::Assign(const char_type* aData)
     336             : {
     337        8938 :   if (!Assign(aData, mozilla::fallible)) {
     338           0 :     AllocFailed(char_traits::length(aData));
     339             :   }
     340        8938 : }
     341             : 
     342             : bool
     343        8944 : nsTSubstring_CharT::Assign(const char_type* aData, const fallible_t&)
     344             : {
     345        8944 :   return Assign(aData, size_type(-1), mozilla::fallible);
     346             : }
     347             : 
     348             : void
     349       43591 : nsTSubstring_CharT::Assign(const char_type* aData, size_type aLength)
     350             : {
     351       43591 :   if (!Assign(aData, aLength, mozilla::fallible)) {
     352           0 :     AllocFailed(aLength == size_type(-1) ? char_traits::length(aData)
     353           0 :                                          : aLength);
     354             :   }
     355       43591 : }
     356             : 
     357             : bool
     358      134682 : nsTSubstring_CharT::Assign(const char_type* aData, size_type aLength,
     359             :                            const fallible_t& aFallible)
     360             : {
     361      134682 :   if (!aData || aLength == 0) {
     362       10677 :     Truncate();
     363       10677 :     return true;
     364             :   }
     365             : 
     366      124005 :   if (aLength == size_type(-1)) {
     367       39792 :     aLength = char_traits::length(aData);
     368             :   }
     369             : 
     370      124005 :   if (IsDependentOn(aData, aData + aLength)) {
     371           2 :     return Assign(string_type(aData, aLength), aFallible);
     372             :   }
     373             : 
     374      124003 :   if (!ReplacePrep(0, mLength, aLength)) {
     375           0 :     return false;
     376             :   }
     377             : 
     378      124003 :   char_traits::copy(mData, aData, aLength);
     379      124003 :   return true;
     380             : }
     381             : 
     382             : void
     383        2057 : nsTSubstring_CharT::AssignASCII(const char* aData, size_type aLength)
     384             : {
     385        2057 :   if (!AssignASCII(aData, aLength, mozilla::fallible)) {
     386           0 :     AllocFailed(aLength);
     387             :   }
     388        2057 : }
     389             : 
     390             : bool
     391        2057 : nsTSubstring_CharT::AssignASCII(const char* aData, size_type aLength,
     392             :                                 const fallible_t& aFallible)
     393             : {
     394             :   // A Unicode string can't depend on an ASCII string buffer,
     395             :   // so this dependence check only applies to CStrings.
     396             : #ifdef CharT_is_char
     397        1117 :   if (IsDependentOn(aData, aData + aLength)) {
     398           0 :     return Assign(string_type(aData, aLength), aFallible);
     399             :   }
     400             : #endif
     401             : 
     402        2057 :   if (!ReplacePrep(0, mLength, aLength)) {
     403           0 :     return false;
     404             :   }
     405             : 
     406        2057 :   char_traits::copyASCII(mData, aData, aLength);
     407        2057 :   return true;
     408             : }
     409             : 
     410             : void
     411        6899 : nsTSubstring_CharT::AssignLiteral(const char_type* aData, size_type aLength)
     412             : {
     413        6899 :   ::ReleaseData(mData, mDataFlags);
     414        6899 :   mData = const_cast<char_type*>(aData);
     415        6899 :   mLength = aLength;
     416        6899 :   mDataFlags = DataFlags::TERMINATED | DataFlags::LITERAL;
     417        6899 : }
     418             : 
     419             : void
     420      192452 : nsTSubstring_CharT::Assign(const self_type& aStr)
     421             : {
     422      192452 :   if (!Assign(aStr, mozilla::fallible)) {
     423           0 :     AllocFailed(aStr.Length());
     424             :   }
     425      192453 : }
     426             : 
     427             : bool
     428      195257 : nsTSubstring_CharT::Assign(const self_type& aStr, const fallible_t& aFallible)
     429             : {
     430             :   // |aStr| could be sharable. We need to check its flags to know how to
     431             :   // deal with it.
     432             : 
     433      195257 :   if (&aStr == this) {
     434           0 :     return true;
     435             :   }
     436             : 
     437      195257 :   if (!aStr.mLength) {
     438       53069 :     Truncate();
     439       53069 :     mDataFlags |= aStr.mDataFlags & DataFlags::VOIDED;
     440       53069 :     return true;
     441             :   }
     442             : 
     443      142188 :   if (aStr.mDataFlags & DataFlags::SHARED) {
     444             :     // nice! we can avoid a string copy :-)
     445             : 
     446             :     // |aStr| should be null-terminated
     447       57095 :     NS_ASSERTION(aStr.mDataFlags & DataFlags::TERMINATED, "shared, but not terminated");
     448             : 
     449       57095 :     ::ReleaseData(mData, mDataFlags);
     450             : 
     451       57095 :     mData = aStr.mData;
     452       57095 :     mLength = aStr.mLength;
     453       57095 :     mDataFlags = DataFlags::TERMINATED | DataFlags::SHARED;
     454             : 
     455             :     // get an owning reference to the mData
     456       57095 :     nsStringBuffer::FromData(mData)->AddRef();
     457       57095 :     return true;
     458       85094 :   } else if (aStr.mDataFlags & DataFlags::LITERAL) {
     459        2947 :     MOZ_ASSERT(aStr.mDataFlags & DataFlags::TERMINATED, "Unterminated literal");
     460             : 
     461        2947 :     AssignLiteral(aStr.mData, aStr.mLength);
     462        2947 :     return true;
     463             :   }
     464             : 
     465             :   // else, treat this like an ordinary assignment.
     466       82147 :   return Assign(aStr.Data(), aStr.Length(), aFallible);
     467             : }
     468             : 
     469             : void
     470        3441 : nsTSubstring_CharT::Assign(const substring_tuple_type& aTuple)
     471             : {
     472        3441 :   if (!Assign(aTuple, mozilla::fallible)) {
     473           0 :     AllocFailed(aTuple.Length());
     474             :   }
     475        3441 : }
     476             : 
     477             : bool
     478        3441 : nsTSubstring_CharT::Assign(const substring_tuple_type& aTuple,
     479             :                            const fallible_t& aFallible)
     480             : {
     481        3441 :   if (aTuple.IsDependentOn(mData, mData + mLength)) {
     482             :     // take advantage of sharing here...
     483           0 :     return Assign(string_type(aTuple), aFallible);
     484             :   }
     485             : 
     486        3441 :   size_type length = aTuple.Length();
     487             : 
     488             :   // don't use ReplacePrep here because it changes the length
     489             :   char_type* oldData;
     490             :   DataFlags oldFlags;
     491        3441 :   if (!MutatePrep(length, &oldData, &oldFlags)) {
     492           0 :     return false;
     493             :   }
     494             : 
     495        3441 :   if (oldData) {
     496        2963 :     ::ReleaseData(oldData, oldFlags);
     497             :   }
     498             : 
     499        3441 :   aTuple.WriteTo(mData, length);
     500        3441 :   mData[length] = 0;
     501        3441 :   mLength = length;
     502        3441 :   return true;
     503             : }
     504             : 
     505             : void
     506        5757 : nsTSubstring_CharT::Adopt(char_type* aData, size_type aLength)
     507             : {
     508        5757 :   if (aData) {
     509        5362 :     ::ReleaseData(mData, mDataFlags);
     510             : 
     511        5362 :     if (aLength == size_type(-1)) {
     512        5286 :       aLength = char_traits::length(aData);
     513             :     }
     514             : 
     515        5362 :     MOZ_RELEASE_ASSERT(CheckCapacity(aLength), "adopting a too-long string");
     516             : 
     517        5362 :     mData = aData;
     518        5362 :     mLength = aLength;
     519        5362 :     mDataFlags = DataFlags::TERMINATED | DataFlags::OWNED;
     520             : 
     521        5362 :     STRING_STAT_INCREMENT(Adopt);
     522             :     // Treat this as construction of a "StringAdopt" object for leak
     523             :     // tracking purposes.
     524        5362 :     MOZ_LOG_CTOR(mData, "StringAdopt", 1);
     525             :   } else {
     526         395 :     SetIsVoid(true);
     527             :   }
     528        5757 : }
     529             : 
     530             : 
     531             : // This version of Replace is optimized for single-character replacement.
     532             : void
     533       13128 : nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
     534             :                             char_type aChar)
     535             : {
     536       13128 :   aCutStart = XPCOM_MIN(aCutStart, Length());
     537             : 
     538       13128 :   if (ReplacePrep(aCutStart, aCutLength, 1)) {
     539       13128 :     mData[aCutStart] = aChar;
     540             :   }
     541       13128 : }
     542             : 
     543             : bool
     544           0 : nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
     545             :                             char_type aChar,
     546             :                             const fallible_t&)
     547             : {
     548           0 :   aCutStart = XPCOM_MIN(aCutStart, Length());
     549             : 
     550           0 :   if (!ReplacePrep(aCutStart, aCutLength, 1)) {
     551           0 :     return false;
     552             :   }
     553             : 
     554           0 :   mData[aCutStart] = aChar;
     555             : 
     556           0 :   return true;
     557             : }
     558             : 
     559             : void
     560       71990 : nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
     561             :                             const char_type* aData, size_type aLength)
     562             : {
     563       71990 :   if (!Replace(aCutStart, aCutLength, aData, aLength,
     564             :                mozilla::fallible)) {
     565           0 :     AllocFailed(Length() - aCutLength + 1);
     566             :   }
     567       71990 : }
     568             : 
     569             : bool
     570       80697 : nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
     571             :                             const char_type* aData, size_type aLength,
     572             :                             const fallible_t& aFallible)
     573             : {
     574             :   // unfortunately, some callers pass null :-(
     575       80697 :   if (!aData) {
     576           0 :     aLength = 0;
     577             :   } else {
     578       80697 :     if (aLength == size_type(-1)) {
     579        8192 :       aLength = char_traits::length(aData);
     580             :     }
     581             : 
     582       80697 :     if (IsDependentOn(aData, aData + aLength)) {
     583           0 :       nsTAutoString_CharT temp(aData, aLength);
     584           0 :       return Replace(aCutStart, aCutLength, temp, aFallible);
     585             :     }
     586             :   }
     587             : 
     588       80697 :   aCutStart = XPCOM_MIN(aCutStart, Length());
     589             : 
     590       80697 :   bool ok = ReplacePrep(aCutStart, aCutLength, aLength);
     591       80697 :   if (!ok) {
     592           0 :     return false;
     593             :   }
     594             : 
     595       80697 :   if (aLength > 0) {
     596       78355 :     char_traits::copy(mData + aCutStart, aData, aLength);
     597             :   }
     598             : 
     599       80697 :   return true;
     600             : }
     601             : 
     602             : void
     603        6249 : nsTSubstring_CharT::ReplaceASCII(index_type aCutStart, size_type aCutLength,
     604             :                                  const char* aData, size_type aLength)
     605             : {
     606        6249 :   if (!ReplaceASCII(aCutStart, aCutLength, aData, aLength, mozilla::fallible)) {
     607           0 :     AllocFailed(Length() - aCutLength + 1);
     608             :   }
     609        6249 : }
     610             : 
     611             : bool
     612        6249 : nsTSubstring_CharT::ReplaceASCII(index_type aCutStart, size_type aCutLength,
     613             :                                  const char* aData, size_type aLength,
     614             :                                  const fallible_t& aFallible)
     615             : {
     616        6249 :   if (aLength == size_type(-1)) {
     617         489 :     aLength = strlen(aData);
     618             :   }
     619             : 
     620             :   // A Unicode string can't depend on an ASCII string buffer,
     621             :   // so this dependence check only applies to CStrings.
     622             : #ifdef CharT_is_char
     623        5336 :   if (IsDependentOn(aData, aData + aLength)) {
     624           0 :     nsTAutoString_CharT temp(aData, aLength);
     625           0 :     return Replace(aCutStart, aCutLength, temp, aFallible);
     626             :   }
     627             : #endif
     628             : 
     629        6249 :   aCutStart = XPCOM_MIN(aCutStart, Length());
     630             : 
     631        6249 :   bool ok = ReplacePrep(aCutStart, aCutLength, aLength);
     632        6249 :   if (!ok) {
     633           0 :     return false;
     634             :   }
     635             : 
     636        6249 :   if (aLength > 0) {
     637        6249 :     char_traits::copyASCII(mData + aCutStart, aData, aLength);
     638             :   }
     639             : 
     640        6249 :   return true;
     641             : }
     642             : 
     643             : void
     644           9 : nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
     645             :                             const substring_tuple_type& aTuple)
     646             : {
     647           9 :   if (aTuple.IsDependentOn(mData, mData + mLength)) {
     648           0 :     nsTAutoString_CharT temp(aTuple);
     649           0 :     Replace(aCutStart, aCutLength, temp);
     650           0 :     return;
     651             :   }
     652             : 
     653           9 :   size_type length = aTuple.Length();
     654             : 
     655           9 :   aCutStart = XPCOM_MIN(aCutStart, Length());
     656             : 
     657           9 :   if (ReplacePrep(aCutStart, aCutLength, length) && length > 0) {
     658           9 :     aTuple.WriteTo(mData + aCutStart, length);
     659             :   }
     660             : }
     661             : 
     662             : void
     663        1495 : nsTSubstring_CharT::ReplaceLiteral(index_type aCutStart, size_type aCutLength,
     664             :                                    const char_type* aData, size_type aLength)
     665             : {
     666        1495 :   aCutStart = XPCOM_MIN(aCutStart, Length());
     667             : 
     668        1495 :   if (!aCutStart && aCutLength == Length()) {
     669          20 :     AssignLiteral(aData, aLength);
     670        1475 :   } else if (ReplacePrep(aCutStart, aCutLength, aLength) && aLength > 0) {
     671        1475 :     char_traits::copy(mData + aCutStart, aData, aLength);
     672             :   }
     673        1495 : }
     674             : 
     675             : void
     676      312508 : nsTSubstring_CharT::SetCapacity(size_type aCapacity)
     677             : {
     678      312508 :   if (!SetCapacity(aCapacity, mozilla::fallible)) {
     679           0 :     AllocFailed(aCapacity);
     680             :   }
     681      312509 : }
     682             : 
     683             : bool
     684      353526 : nsTSubstring_CharT::SetCapacity(size_type aCapacity, const fallible_t&)
     685             : {
     686             :   // capacity does not include room for the terminating null char
     687             : 
     688             :   // if our capacity is reduced to zero, then free our buffer.
     689      353526 :   if (aCapacity == 0) {
     690      271751 :     ::ReleaseData(mData, mDataFlags);
     691      271751 :     mData = char_traits::sEmptyBuffer;
     692      271751 :     mLength = 0;
     693      271751 :     mDataFlags = DataFlags::TERMINATED;
     694      271751 :     return true;
     695             :   }
     696             : 
     697             :   char_type* oldData;
     698             :   DataFlags oldFlags;
     699       81775 :   if (!MutatePrep(aCapacity, &oldData, &oldFlags)) {
     700           0 :     return false;  // out-of-memory
     701             :   }
     702             : 
     703             :   // compute new string length
     704       81776 :   size_type newLen = XPCOM_MIN(mLength, aCapacity);
     705             : 
     706       81776 :   if (oldData) {
     707             :     // preserve old data
     708       32166 :     if (mLength > 0) {
     709        5554 :       char_traits::copy(mData, oldData, newLen);
     710             :     }
     711             : 
     712       32166 :     ::ReleaseData(oldData, oldFlags);
     713             :   }
     714             : 
     715             :   // adjust mLength if our buffer shrunk down in size
     716       81776 :   if (newLen < mLength) {
     717        8057 :     mLength = newLen;
     718             :   }
     719             : 
     720             :   // always null-terminate here, even if the buffer got longer.  this is
     721             :   // for backwards compat with the old string implementation.
     722       81776 :   mData[aCapacity] = char_type(0);
     723             : 
     724       81776 :   return true;
     725             : }
     726             : 
     727             : void
     728      309845 : nsTSubstring_CharT::SetLength(size_type aLength)
     729             : {
     730      309845 :   SetCapacity(aLength);
     731      309846 :   mLength = aLength;
     732      309846 : }
     733             : 
     734             : bool
     735       36639 : nsTSubstring_CharT::SetLength(size_type aLength, const fallible_t& aFallible)
     736             : {
     737       36639 :   if (!SetCapacity(aLength, aFallible)) {
     738           0 :     return false;
     739             :   }
     740             : 
     741       36639 :   mLength = aLength;
     742       36639 :   return true;
     743             : }
     744             : 
     745             : void
     746         861 : nsTSubstring_CharT::SetIsVoid(bool aVal)
     747             : {
     748         861 :   if (aVal) {
     749         861 :     Truncate();
     750         861 :     mDataFlags |= DataFlags::VOIDED;
     751             :   } else {
     752           0 :     mDataFlags &= ~DataFlags::VOIDED;
     753             :   }
     754         861 : }
     755             : 
     756             : namespace mozilla {
     757             : namespace detail {
     758             : 
     759             : nsTStringRepr_CharT::char_type
     760        8373 : nsTStringRepr_CharT::First() const
     761             : {
     762        8373 :   MOZ_RELEASE_ASSERT(mLength > 0, "|First()| called on an empty string");
     763        8373 :   return mData[0];
     764             : }
     765             : 
     766             : nsTStringRepr_CharT::char_type
     767        1426 : nsTStringRepr_CharT::Last() const
     768             : {
     769        1426 :   MOZ_RELEASE_ASSERT(mLength > 0, "|Last()| called on an empty string");
     770        1426 :   return mData[mLength - 1];
     771             : }
     772             : 
     773             : bool
     774       73027 : nsTStringRepr_CharT::Equals(const self_type& aStr) const
     775             : {
     776      144576 :   return mLength == aStr.mLength &&
     777      144576 :          char_traits::compare(mData, aStr.mData, mLength) == 0;
     778             : }
     779             : 
     780             : bool
     781         628 : nsTStringRepr_CharT::Equals(const self_type& aStr,
     782             :                             const comparator_type& aComp) const
     783             : {
     784         903 :   return mLength == aStr.mLength &&
     785         903 :          aComp(mData, aStr.mData, mLength, aStr.mLength) == 0;
     786             : }
     787             : 
     788             : bool
     789           0 : nsTStringRepr_CharT::Equals(const substring_tuple_type& aTuple) const
     790             : {
     791           0 :   return Equals(substring_type(aTuple));
     792             : }
     793             : 
     794             : bool
     795           0 : nsTStringRepr_CharT::Equals(const substring_tuple_type& aTuple,
     796             :                             const comparator_type& aComp) const
     797             : {
     798           0 :   return Equals(substring_type(aTuple), aComp);
     799             : }
     800             : 
     801             : bool
     802        6832 : nsTStringRepr_CharT::Equals(const char_type* aData) const
     803             : {
     804             :   // unfortunately, some callers pass null :-(
     805        6832 :   if (!aData) {
     806           0 :     NS_NOTREACHED("null data pointer");
     807           0 :     return mLength == 0;
     808             :   }
     809             : 
     810             :   // XXX avoid length calculation?
     811        6832 :   size_type length = char_traits::length(aData);
     812       10025 :   return mLength == length &&
     813       10025 :          char_traits::compare(mData, aData, mLength) == 0;
     814             : }
     815             : 
     816             : bool
     817           0 : nsTStringRepr_CharT::Equals(const char_type* aData,
     818             :                             const comparator_type& aComp) const
     819             : {
     820             :   // unfortunately, some callers pass null :-(
     821           0 :   if (!aData) {
     822           0 :     NS_NOTREACHED("null data pointer");
     823           0 :     return mLength == 0;
     824             :   }
     825             : 
     826             :   // XXX avoid length calculation?
     827           0 :   size_type length = char_traits::length(aData);
     828           0 :   return mLength == length && aComp(mData, aData, mLength, length) == 0;
     829             : }
     830             : 
     831             : bool
     832       27024 : nsTStringRepr_CharT::EqualsASCII(const char* aData, size_type aLen) const
     833             : {
     834       39749 :   return mLength == aLen &&
     835       39749 :          char_traits::compareASCII(mData, aData, aLen) == 0;
     836             : }
     837             : 
     838             : bool
     839         116 : nsTStringRepr_CharT::EqualsASCII(const char* aData) const
     840             : {
     841         116 :   return char_traits::compareASCIINullTerminated(mData, mLength, aData) == 0;
     842             : }
     843             : 
     844             : bool
     845       71002 : nsTStringRepr_CharT::LowerCaseEqualsASCII(const char* aData,
     846             :                                           size_type aLen) const
     847             : {
     848      105412 :   return mLength == aLen &&
     849      105412 :          char_traits::compareLowerCaseToASCII(mData, aData, aLen) == 0;
     850             : }
     851             : 
     852             : bool
     853       71670 : nsTStringRepr_CharT::LowerCaseEqualsASCII(const char* aData) const
     854             : {
     855       71670 :   return char_traits::compareLowerCaseToASCIINullTerminated(mData,
     856       71670 :                                                             mLength,
     857       71670 :                                                             aData) == 0;
     858             : }
     859             : 
     860             : nsTStringRepr_CharT::size_type
     861          14 : nsTStringRepr_CharT::CountChar(char_type aChar) const
     862             : {
     863          14 :   const char_type* start = mData;
     864          14 :   const char_type* end   = mData + mLength;
     865             : 
     866          14 :   return NS_COUNT(start, end, aChar);
     867             : }
     868             : 
     869             : int32_t
     870        9262 : nsTStringRepr_CharT::FindChar(char_type aChar, index_type aOffset) const
     871             : {
     872        9262 :   if (aOffset < mLength) {
     873       18122 :     const char_type* result = char_traits::find(mData + aOffset,
     874       18122 :                                                 mLength - aOffset, aChar);
     875        9061 :     if (result) {
     876        1048 :       return result - mData;
     877             :     }
     878             :   }
     879        8214 :   return -1;
     880             : }
     881             : 
     882             : } // namespace detail
     883             : } // namespace mozilla
     884             : 
     885             : void
     886           0 : nsTSubstring_CharT::StripChar(char_type aChar)
     887             : {
     888           0 :   if (mLength == 0) {
     889           0 :     return;
     890             :   }
     891             : 
     892           0 :   if (!EnsureMutable()) { // XXX do this lazily?
     893           0 :     AllocFailed(mLength);
     894             :   }
     895             : 
     896             :   // XXX(darin): this code should defer writing until necessary.
     897             : 
     898           0 :   char_type* to   = mData;
     899           0 :   char_type* from = mData;
     900           0 :   char_type* end  = mData + mLength;
     901             : 
     902           0 :   while (from < end) {
     903           0 :     char_type theChar = *from++;
     904           0 :     if (aChar != theChar) {
     905           0 :       *to++ = theChar;
     906             :     }
     907             :   }
     908           0 :   *to = char_type(0); // add the null
     909           0 :   mLength = to - mData;
     910             : }
     911             : 
     912             : void
     913           1 : nsTSubstring_CharT::StripChars(const char_type* aChars)
     914             : {
     915           1 :   if (mLength == 0) {
     916           0 :     return;
     917             :   }
     918             : 
     919           1 :   if (!EnsureMutable()) { // XXX do this lazily?
     920           0 :     AllocFailed(mLength);
     921             :   }
     922             : 
     923             :   // XXX(darin): this code should defer writing until necessary.
     924             : 
     925           1 :   char_type* to   = mData;
     926           1 :   char_type* from = mData;
     927           1 :   char_type* end  = mData + mLength;
     928             : 
     929         237 :   while (from < end) {
     930         118 :     char_type theChar = *from++;
     931         118 :     const char_type* test = aChars;
     932             : 
     933         351 :     for (; *test && *test != theChar; ++test);
     934             : 
     935         118 :     if (!*test) {
     936             :       // Not stripped, copy this char.
     937         116 :       *to++ = theChar;
     938             :     }
     939             :   }
     940           1 :   *to = char_type(0); // add the null
     941           1 :   mLength = to - mData;
     942             : }
     943             : 
     944             : void
     945        6445 : nsTSubstring_CharT::StripTaggedASCII(const ASCIIMaskArray& aToStrip)
     946             : {
     947        6445 :   if (mLength == 0) {
     948         165 :     return;
     949             :   }
     950             : 
     951        6280 :   if (!EnsureMutable()) {
     952           0 :     AllocFailed(mLength);
     953             :   }
     954             : 
     955        6280 :   char_type* to   = mData;
     956        6280 :   char_type* from = mData;
     957        6280 :   char_type* end  = mData + mLength;
     958             : 
     959       69684 :   while (from < end) {
     960       31702 :     uint32_t theChar = (uint32_t)*from++;
     961             :     // Replacing this with a call to ASCIIMask::IsMasked
     962             :     // regresses performance somewhat, so leaving it inlined.
     963       31702 :     if (!mozilla::ASCIIMask::IsMasked(aToStrip, theChar)) {
     964             :       // Not stripped, copy this char.
     965       31696 :       *to++ = (char_type)theChar;
     966             :     }
     967             :   }
     968        6280 :   *to = char_type(0); // add the null
     969        6280 :   mLength = to - mData;
     970             : }
     971             : 
     972             : void
     973          60 : nsTSubstring_CharT::StripCRLF()
     974             : {
     975             :   // Expanding this call to copy the code from StripTaggedASCII
     976             :   // instead of just calling it does somewhat help with performance
     977             :   // but it is not worth it given the duplicated code.
     978          60 :   StripTaggedASCII(mozilla::ASCIIMask::MaskCRLF());
     979          60 : }
     980             : 
     981         843 : struct MOZ_STACK_CLASS PrintfAppend_CharT : public mozilla::PrintfTarget
     982             : {
     983         843 :   explicit PrintfAppend_CharT(nsTSubstring_CharT* aString)
     984         843 :     : mString(aString)
     985             :   {
     986         843 :   }
     987             : 
     988        5743 :   bool append(const char* aStr, size_t aLen) override {
     989        5743 :     if (aLen == 0) {
     990           0 :       return true;
     991             :     }
     992             : 
     993        5743 :     mString->AppendASCII(aStr, aLen);
     994        5743 :     return true;
     995             :   }
     996             : 
     997             : private:
     998             : 
     999             :   nsTSubstring_CharT* mString;
    1000             : };
    1001             : 
    1002             : void
    1003         370 : nsTSubstring_CharT::AppendPrintf(const char* aFormat, ...)
    1004             : {
    1005         740 :   PrintfAppend_CharT appender(this);
    1006             :   va_list ap;
    1007         370 :   va_start(ap, aFormat);
    1008         370 :   bool r = appender.vprint(aFormat, ap);
    1009         370 :   if (!r) {
    1010           0 :     MOZ_CRASH("Allocation or other failure in PrintfTarget::print");
    1011             :   }
    1012         370 :   va_end(ap);
    1013         370 : }
    1014             : 
    1015             : void
    1016         473 : nsTSubstring_CharT::AppendPrintf(const char* aFormat, va_list aAp)
    1017             : {
    1018         946 :   PrintfAppend_CharT appender(this);
    1019         473 :   bool r = appender.vprint(aFormat, aAp);
    1020         473 :   if (!r) {
    1021           0 :     MOZ_CRASH("Allocation or other failure in PrintfTarget::print");
    1022             :   }
    1023         473 : }
    1024             : 
    1025             : /* hack to make sure we define FormatWithoutTrailingZeros only once */
    1026             : #ifdef CharT_is_PRUnichar
    1027             : // Returns the length of the formatted aDouble in aBuf.
    1028             : static int
    1029           0 : FormatWithoutTrailingZeros(char (&aBuf)[40], double aDouble,
    1030             :                            int aPrecision)
    1031             : {
    1032             :   static const DoubleToStringConverter converter(DoubleToStringConverter::UNIQUE_ZERO |
    1033             :                                                  DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
    1034             :                                                  "Infinity",
    1035             :                                                  "NaN",
    1036             :                                                  'e',
    1037             :                                                  -6, 21,
    1038           0 :                                                  6, 1);
    1039           0 :   double_conversion::StringBuilder builder(aBuf, sizeof(aBuf));
    1040           0 :   bool exponential_notation = false;
    1041           0 :   converter.ToPrecision(aDouble, aPrecision, &exponential_notation, &builder);
    1042           0 :   int length = builder.position();
    1043           0 :   char* formattedDouble = builder.Finalize();
    1044             : 
    1045             :   // If we have a shorter string than aPrecision, it means we have a special
    1046             :   // value (NaN or Infinity).  All other numbers will be formatted with at
    1047             :   // least aPrecision digits.
    1048           0 :   if (length <= aPrecision) {
    1049           0 :     return length;
    1050             :   }
    1051             : 
    1052           0 :   char* end = formattedDouble + length;
    1053           0 :   char* decimalPoint = strchr(aBuf, '.');
    1054             :   // No trailing zeros to remove.
    1055           0 :   if (!decimalPoint) {
    1056           0 :     return length;
    1057             :   }
    1058             : 
    1059           0 :   if (MOZ_UNLIKELY(exponential_notation)) {
    1060             :     // We need to check for cases like 1.00000e-10 (yes, this is
    1061             :     // disgusting).
    1062           0 :     char* exponent = end - 1;
    1063           0 :     for (; ; --exponent) {
    1064           0 :       if (*exponent == 'e') {
    1065           0 :         break;
    1066             :       }
    1067             :     }
    1068           0 :     char* zerosBeforeExponent = exponent - 1;
    1069           0 :     for (; zerosBeforeExponent != decimalPoint; --zerosBeforeExponent) {
    1070           0 :       if (*zerosBeforeExponent != '0') {
    1071           0 :         break;
    1072             :       }
    1073             :     }
    1074           0 :     if (zerosBeforeExponent == decimalPoint) {
    1075           0 :       --zerosBeforeExponent;
    1076             :     }
    1077             :     // Slide the exponent to the left over the trailing zeros.  Don't
    1078             :     // worry about copying the trailing NUL character.
    1079           0 :     size_t exponentSize = end - exponent;
    1080           0 :     memmove(zerosBeforeExponent + 1, exponent, exponentSize);
    1081           0 :     length -= exponent - (zerosBeforeExponent + 1);
    1082             :   } else {
    1083           0 :     char* trailingZeros = end - 1;
    1084           0 :     for (; trailingZeros != decimalPoint; --trailingZeros) {
    1085           0 :       if (*trailingZeros != '0') {
    1086           0 :         break;
    1087             :       }
    1088             :     }
    1089           0 :     if (trailingZeros == decimalPoint) {
    1090           0 :       --trailingZeros;
    1091             :     }
    1092           0 :     length -= end - (trailingZeros + 1);
    1093             :   }
    1094             : 
    1095           0 :   return length;
    1096             : }
    1097             : #endif /* CharT_is_PRUnichar */
    1098             : 
    1099             : void
    1100           0 : nsTSubstring_CharT::AppendFloat(float aFloat)
    1101             : {
    1102             :   char buf[40];
    1103           0 :   int length = FormatWithoutTrailingZeros(buf, aFloat, 6);
    1104           0 :   AppendASCII(buf, length);
    1105           0 : }
    1106             : 
    1107             : void
    1108           0 : nsTSubstring_CharT::AppendFloat(double aFloat)
    1109             : {
    1110             :   char buf[40];
    1111           0 :   int length = FormatWithoutTrailingZeros(buf, aFloat, 15);
    1112           0 :   AppendASCII(buf, length);
    1113           0 : }
    1114             : 
    1115             : size_t
    1116          31 : nsTSubstring_CharT::SizeOfExcludingThisIfUnshared(
    1117             :     mozilla::MallocSizeOf aMallocSizeOf) const
    1118             : {
    1119          31 :   if (mDataFlags & DataFlags::SHARED) {
    1120           0 :     return nsStringBuffer::FromData(mData)->
    1121           0 :       SizeOfIncludingThisIfUnshared(aMallocSizeOf);
    1122             :   }
    1123          31 :   if (mDataFlags & DataFlags::OWNED) {
    1124           0 :     return aMallocSizeOf(mData);
    1125             :   }
    1126             : 
    1127             :   // If we reach here, exactly one of the following must be true:
    1128             :   // - DataFlags::VOIDED is set, and mData points to sEmptyBuffer;
    1129             :   // - DataFlags::FIXED is set, and mData points to a buffer within a string
    1130             :   //   object (e.g. nsAutoString);
    1131             :   // - None of DataFlags::SHARED, DataFlags::OWNED, DataFlags::FIXED is set, and mData points to a buffer
    1132             :   //   owned by something else.
    1133             :   //
    1134             :   // In all three cases, we don't measure it.
    1135          31 :   return 0;
    1136             : }
    1137             : 
    1138             : size_t
    1139           0 : nsTSubstring_CharT::SizeOfExcludingThisEvenIfShared(
    1140             :     mozilla::MallocSizeOf aMallocSizeOf) const
    1141             : {
    1142             :   // This is identical to SizeOfExcludingThisIfUnshared except for the
    1143             :   // DataFlags::SHARED case.
    1144           0 :   if (mDataFlags & DataFlags::SHARED) {
    1145           0 :     return nsStringBuffer::FromData(mData)->
    1146           0 :       SizeOfIncludingThisEvenIfShared(aMallocSizeOf);
    1147             :   }
    1148           0 :   if (mDataFlags & DataFlags::OWNED) {
    1149           0 :     return aMallocSizeOf(mData);
    1150             :   }
    1151           0 :   return 0;
    1152             : }
    1153             : 
    1154             : size_t
    1155           0 : nsTSubstring_CharT::SizeOfIncludingThisIfUnshared(
    1156             :     mozilla::MallocSizeOf aMallocSizeOf) const
    1157             : {
    1158           0 :   return aMallocSizeOf(this) + SizeOfExcludingThisIfUnshared(aMallocSizeOf);
    1159             : }
    1160             : 
    1161             : size_t
    1162           0 : nsTSubstring_CharT::SizeOfIncludingThisEvenIfShared(
    1163             :     mozilla::MallocSizeOf aMallocSizeOf) const
    1164             : {
    1165           0 :   return aMallocSizeOf(this) + SizeOfExcludingThisEvenIfShared(aMallocSizeOf);
    1166             : }
    1167             : 
    1168             : inline
    1169           5 : nsTSubstringSplitter_CharT::nsTSubstringSplitter_CharT(
    1170           5 :     const nsTSubstring_CharT* aStr, char_type aDelim)
    1171             :   : mStr(aStr)
    1172             :   , mArray(nullptr)
    1173           5 :   , mDelim(aDelim)
    1174             : {
    1175           5 :   if (mStr->IsEmpty()) {
    1176           0 :     mArraySize = 0;
    1177           0 :     return;
    1178             :   }
    1179             : 
    1180           5 :   size_type delimCount = mStr->CountChar(aDelim);
    1181           5 :   mArraySize = delimCount + 1;
    1182          10 :   mArray.reset(new nsTDependentSubstring_CharT[mArraySize]);
    1183             : 
    1184           5 :   size_t seenParts = 0;
    1185           5 :   size_type start = 0;
    1186           7 :   do {
    1187          12 :     MOZ_ASSERT(seenParts < mArraySize);
    1188          12 :     int32_t offset = mStr->FindChar(aDelim, start);
    1189          12 :     if (offset != -1) {
    1190           7 :       size_type length = static_cast<size_type>(offset) - start;
    1191           7 :       mArray[seenParts++].Rebind(mStr->Data() + start, length);
    1192           7 :       start = static_cast<size_type>(offset) + 1;
    1193             :     } else {
    1194             :       // Get the remainder
    1195           5 :       mArray[seenParts++].Rebind(mStr->Data() + start, mStr->Length() - start);
    1196           5 :       break;
    1197             :     }
    1198           7 :   } while (start < mStr->Length());
    1199             : }
    1200             : 
    1201             : nsTSubstringSplitter_CharT
    1202           5 : nsTSubstring_CharT::Split(const char_type aChar) const
    1203             : {
    1204           5 :   return nsTSubstringSplitter_CharT(this, aChar);
    1205             : }
    1206             : 
    1207             : const nsTDependentSubstring_CharT&
    1208          12 : nsTSubstringSplitter_CharT::nsTSubstringSplit_Iter::operator* () const
    1209             : {
    1210          12 :    return mObj.Get(mPos);
    1211             : }

Generated by: LCOV version 1.13