xpcom/string/src/nsTSubstring.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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 #include "mozilla/MemoryReporting.h"
     7 #include "double-conversion.h"
     9 using double_conversion::DoubleToStringConverter;
    11 #ifdef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
    12 nsTSubstring_CharT::nsTSubstring_CharT( char_type *data, size_type length,
    13                                         uint32_t flags)
    14   : mData(data),
    15     mLength(length),
    16     mFlags(flags)
    17   {
    18     if (flags & F_OWNED) {
    19       STRING_STAT_INCREMENT(Adopt);
    20 #ifdef NS_BUILD_REFCNT_LOGGING
    21       NS_LogCtor(mData, "StringAdopt", 1);
    22 #endif
    23     }
    24   }
    25 #endif /* XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE */
    27   /**
    28    * helper function for down-casting a nsTSubstring to a nsTFixedString.
    29    */
    30 inline const nsTFixedString_CharT*
    31 AsFixedString( const nsTSubstring_CharT* s )
    32   {
    33     return static_cast<const nsTFixedString_CharT*>(s);
    34   }
    37   /**
    38    * this function is called to prepare mData for writing.  the given capacity
    39    * indicates the required minimum storage size for mData, in sizeof(char_type)
    40    * increments.  this function returns true if the operation succeeds.  it also
    41    * returns the old data and old flags members if mData is newly allocated.
    42    * the old data must be released by the caller.
    43    */
    44 bool
    45 nsTSubstring_CharT::MutatePrep( size_type capacity, char_type** oldData, uint32_t* oldFlags )
    46   {
    47     // initialize to no old data
    48     *oldData = nullptr;
    49     *oldFlags = 0;
    51     size_type curCapacity = Capacity();
    53     // If |capacity > kMaxCapacity|, then our doubling algorithm may not be
    54     // able to allocate it.  Just bail out in cases like that.  We don't want
    55     // to be allocating 2GB+ strings anyway.
    56     PR_STATIC_ASSERT((sizeof(nsStringBuffer) & 0x1) == 0);
    57     const size_type kMaxCapacity =
    58       (size_type(-1)/2 - sizeof(nsStringBuffer)) / sizeof(char_type) - 2;
    59     if (capacity > kMaxCapacity) {
    60       // Also assert for |capacity| equal to |size_type(-1)|, since we used to
    61       // use that value to flag immutability.
    62       NS_ASSERTION(capacity != size_type(-1), "Bogus capacity");
    63       return false;
    64     }
    66     // |curCapacity == 0| means that the buffer is immutable or 0-sized, so we
    67     // need to allocate a new buffer. We cannot use the existing buffer even
    68     // though it might be large enough.
    70     if (curCapacity != 0)
    71       {
    72         if (capacity <= curCapacity) {
    73           mFlags &= ~F_VOIDED;  // mutation clears voided flag
    74           return true;
    75         }
    77         // Use doubling algorithm when forced to increase available capacity.
    78         size_type temp = curCapacity;
    79         while (temp < capacity)
    80           temp <<= 1;
    81         NS_ASSERTION(XPCOM_MIN(temp, kMaxCapacity) >= capacity,
    82                      "should have hit the early return at the top");
    83         capacity = XPCOM_MIN(temp, kMaxCapacity);
    84       }
    86     //
    87     // several cases:
    88     //
    89     //  (1) we have a shared buffer (mFlags & F_SHARED)
    90     //  (2) we have an owned buffer (mFlags & F_OWNED)
    91     //  (3) we have a fixed buffer (mFlags & F_FIXED)
    92     //  (4) we have a readonly buffer
    93     //
    94     // requiring that we in some cases preserve the data before creating
    95     // a new buffer complicates things just a bit ;-)
    96     //
    98     size_type storageSize = (capacity + 1) * sizeof(char_type);
   100     // case #1
   101     if (mFlags & F_SHARED)
   102       {
   103         nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
   104         if (!hdr->IsReadonly())
   105           {
   106             nsStringBuffer *newHdr = nsStringBuffer::Realloc(hdr, storageSize);
   107             if (!newHdr)
   108               return false; // out-of-memory (original header left intact)
   110             hdr = newHdr;
   111             mData = (char_type*) hdr->Data();
   112             mFlags &= ~F_VOIDED;  // mutation clears voided flag
   113             return true;
   114           }
   115       }
   117     char_type* newData;
   118     uint32_t newDataFlags;
   120       // if we have a fixed buffer of sufficient size, then use it.  this helps
   121       // avoid heap allocations.
   122     if ((mFlags & F_CLASS_FIXED) && (capacity < AsFixedString(this)->mFixedCapacity))
   123       {
   124         newData = AsFixedString(this)->mFixedBuf;
   125         newDataFlags = F_TERMINATED | F_FIXED;
   126       }
   127     else
   128       {
   129         // if we reach here then, we must allocate a new buffer.  we cannot
   130         // make use of our F_OWNED or F_FIXED buffers because they are not
   131         // large enough.
   133         nsStringBuffer* newHdr =
   134           nsStringBuffer::Alloc(storageSize).take();
   135         if (!newHdr)
   136           return false; // we are still in a consistent state
   138         newData = (char_type*) newHdr->Data();
   139         newDataFlags = F_TERMINATED | F_SHARED;
   140       }
   142     // save old data and flags
   143     *oldData = mData;
   144     *oldFlags = mFlags;
   146     mData = newData;
   147     SetDataFlags(newDataFlags);
   149     // mLength does not change
   151     // though we are not necessarily terminated at the moment, now is probably
   152     // still the best time to set F_TERMINATED.
   154     return true;
   155   }
   157 void
   158 nsTSubstring_CharT::Finalize()
   159   {
   160     ::ReleaseData(mData, mFlags);
   161     // mData, mLength, and mFlags are purposefully left dangling
   162   }
   164 bool
   165 nsTSubstring_CharT::ReplacePrepInternal(index_type cutStart, size_type cutLen,
   166                                         size_type fragLen, size_type newLen)
   167   {
   168     char_type* oldData;
   169     uint32_t oldFlags;
   170     if (!MutatePrep(newLen, &oldData, &oldFlags))
   171       return false; // out-of-memory
   173     if (oldData)
   174       {
   175         // determine whether or not we need to copy part of the old string
   176         // over to the new string.
   178         if (cutStart > 0)
   179           {
   180             // copy prefix from old string
   181             char_traits::copy(mData, oldData, cutStart);
   182           }
   184         if (cutStart + cutLen < mLength)
   185           {
   186             // copy suffix from old string to new offset
   187             size_type from = cutStart + cutLen;
   188             size_type fromLen = mLength - from;
   189             uint32_t to = cutStart + fragLen;
   190             char_traits::copy(mData + to, oldData + from, fromLen);
   191           }
   193         ::ReleaseData(oldData, oldFlags);
   194       }
   195     else
   196       {
   197         // original data remains intact
   199         // determine whether or not we need to move part of the existing string
   200         // to make room for the requested hole.
   201         if (fragLen != cutLen && cutStart + cutLen < mLength)
   202           {
   203             uint32_t from = cutStart + cutLen;
   204             uint32_t fromLen = mLength - from;
   205             uint32_t to = cutStart + fragLen;
   206             char_traits::move(mData + to, mData + from, fromLen);
   207           }
   208       }
   210     // add null terminator (mutable mData always has room for the null-
   211     // terminator).
   212     mData[newLen] = char_type(0);
   213     mLength = newLen;
   215     return true;
   216   }
   218 nsTSubstring_CharT::size_type
   219 nsTSubstring_CharT::Capacity() const
   220   {
   221     // return 0 to indicate an immutable or 0-sized buffer
   223     size_type capacity;
   224     if (mFlags & F_SHARED)
   225       {
   226         // if the string is readonly, then we pretend that it has no capacity.
   227         nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
   228         if (hdr->IsReadonly())
   229           capacity = 0;
   230         else {
   231           capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
   232         }
   233       }
   234     else if (mFlags & F_FIXED)
   235       {
   236         capacity = AsFixedString(this)->mFixedCapacity;
   237       }
   238     else if (mFlags & F_OWNED)
   239       {
   240         // we don't store the capacity of an adopted buffer because that would
   241         // require an additional member field.  the best we can do is base the
   242         // capacity on our length.  remains to be seen if this is the right
   243         // trade-off.
   244         capacity = mLength;
   245       }
   246     else
   247       {
   248         capacity = 0;
   249       }
   251     return capacity;
   252   }
   254 bool
   255 nsTSubstring_CharT::EnsureMutable( size_type newLen )
   256   {
   257     if (newLen == size_type(-1) || newLen == mLength)
   258       {
   259         if (mFlags & (F_FIXED | F_OWNED))
   260           return true;
   261         if ((mFlags & F_SHARED) && !nsStringBuffer::FromData(mData)->IsReadonly())
   262           return true;
   264         newLen = mLength;
   265       }
   266     return SetLength(newLen, fallible_t());
   267   }
   269 // ---------------------------------------------------------------------------
   271   // This version of Assign is optimized for single-character assignment.
   272 void
   273 nsTSubstring_CharT::Assign( char_type c )
   274   {
   275     if (!ReplacePrep(0, mLength, 1))
   276       NS_ABORT_OOM(mLength);
   278     *mData = c;
   279   }
   281 bool
   282 nsTSubstring_CharT::Assign( char_type c, const fallible_t& )
   283   {
   284     if (!ReplacePrep(0, mLength, 1))
   285       return false;
   287     *mData = c;
   288     return true;
   289   }
   291 void
   292 nsTSubstring_CharT::Assign( const char_type* data )
   293   {
   294     if (!Assign(data, size_type(-1), fallible_t()))
   295       NS_ABORT_OOM(char_traits::length(data));
   296   }
   298 void
   299 nsTSubstring_CharT::Assign( const char_type* data, size_type length )
   300   {
   301     if (!Assign(data, length, fallible_t()))
   302       NS_ABORT_OOM(length);
   303   }
   305 bool
   306 nsTSubstring_CharT::Assign( const char_type* data, size_type length, const fallible_t& )
   307   {
   308     if (!data || length == 0)
   309       {
   310         Truncate();
   311         return true;
   312       }
   314     if (length == size_type(-1))
   315       length = char_traits::length(data);
   317     if (IsDependentOn(data, data + length))
   318       {
   319         return Assign(string_type(data, length), fallible_t());
   320       }
   322     if (!ReplacePrep(0, mLength, length))
   323       return false;
   325     char_traits::copy(mData, data, length);
   326     return true;
   327   }
   329 void
   330 nsTSubstring_CharT::AssignASCII( const char* data, size_type length )
   331   {
   332     if (!AssignASCII(data, length, fallible_t()))
   333       NS_ABORT_OOM(length);
   334   }
   336 bool
   337 nsTSubstring_CharT::AssignASCII( const char* data, size_type length, const fallible_t& )
   338   {
   339     // A Unicode string can't depend on an ASCII string buffer,
   340     // so this dependence check only applies to CStrings.
   341 #ifdef CharT_is_char
   342     if (IsDependentOn(data, data + length))
   343       {
   344         return Assign(string_type(data, length), fallible_t());
   345       }
   346 #endif
   348     if (!ReplacePrep(0, mLength, length))
   349       return false;
   351     char_traits::copyASCII(mData, data, length);
   352     return true;
   353   }
   355 void
   356 nsTSubstring_CharT::AssignLiteral( const char_type* data, size_type length )
   357   {
   358     ::ReleaseData(mData, mFlags);
   359     mData = const_cast<char_type*>(data);
   360     mLength = length;
   361     SetDataFlags(F_TERMINATED | F_LITERAL);
   362   }
   364 void
   365 nsTSubstring_CharT::Assign( const self_type& str )
   366 {
   367   if (!Assign(str, fallible_t()))
   368     NS_ABORT_OOM(str.Length());
   369 }
   371 bool
   372 nsTSubstring_CharT::Assign( const self_type& str, const fallible_t& )
   373   {
   374     // |str| could be sharable.  we need to check its flags to know how to
   375     // deal with it.
   377     if (&str == this)
   378       return true;
   380     if (!str.mLength)
   381       {
   382         Truncate();
   383         mFlags |= str.mFlags & F_VOIDED;
   384         return true;
   385       }
   387     if (str.mFlags & F_SHARED)
   388       {
   389         // nice! we can avoid a string copy :-)
   391         // |str| should be null-terminated
   392         NS_ASSERTION(str.mFlags & F_TERMINATED, "shared, but not terminated");
   394         ::ReleaseData(mData, mFlags);
   396         mData = str.mData;
   397         mLength = str.mLength;
   398         SetDataFlags(F_TERMINATED | F_SHARED);
   400         // get an owning reference to the mData
   401         nsStringBuffer::FromData(mData)->AddRef();
   402         return true;
   403       }
   404     else if (str.mFlags & F_LITERAL)
   405       {
   406         NS_ABORT_IF_FALSE(str.mFlags & F_TERMINATED, "Unterminated literal");
   408         AssignLiteral(str.mData, str.mLength);
   409         return true;
   410       }
   412     // else, treat this like an ordinary assignment.
   413     return Assign(str.Data(), str.Length(), fallible_t());
   414   }
   416 void
   417 nsTSubstring_CharT::Assign( const substring_tuple_type& tuple )
   418   {
   419     if (!Assign(tuple, fallible_t()))
   420       NS_ABORT_OOM(tuple.Length());
   421   }
   423 bool
   424 nsTSubstring_CharT::Assign( const substring_tuple_type& tuple, const fallible_t& )
   425   {
   426     if (tuple.IsDependentOn(mData, mData + mLength))
   427       {
   428         // take advantage of sharing here...
   429         return Assign(string_type(tuple), fallible_t());
   430       }
   432     size_type length = tuple.Length();
   434     // don't use ReplacePrep here because it changes the length
   435     char_type* oldData;
   436     uint32_t oldFlags;
   437     if (!MutatePrep(length, &oldData, &oldFlags))
   438       return false;
   440     if (oldData)
   441       ::ReleaseData(oldData, oldFlags);
   443     tuple.WriteTo(mData, length);
   444     mData[length] = 0;
   445     mLength = length;
   446     return true;
   447   }
   449 void
   450 nsTSubstring_CharT::Adopt( char_type* data, size_type length )
   451   {
   452     if (data)
   453       {
   454         ::ReleaseData(mData, mFlags);
   456         if (length == size_type(-1))
   457           length = char_traits::length(data);
   459         mData = data;
   460         mLength = length;
   461         SetDataFlags(F_TERMINATED | F_OWNED);
   463         STRING_STAT_INCREMENT(Adopt);
   464 #ifdef NS_BUILD_REFCNT_LOGGING
   465         // Treat this as construction of a "StringAdopt" object for leak
   466         // tracking purposes.        
   467         NS_LogCtor(mData, "StringAdopt", 1);
   468 #endif // NS_BUILD_REFCNT_LOGGING
   469       }
   470     else
   471       {
   472         SetIsVoid(true);
   473       }
   474   }
   477   // This version of Replace is optimized for single-character replacement.
   478 void
   479 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, char_type c )
   480   {
   481     cutStart = XPCOM_MIN(cutStart, Length());
   483     if (ReplacePrep(cutStart, cutLength, 1))
   484       mData[cutStart] = c;
   485   }
   487 bool
   488 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, char_type c, const mozilla::fallible_t& )
   489   {
   490     cutStart = XPCOM_MIN(cutStart, Length());
   492     if (!ReplacePrep(cutStart, cutLength, 1))
   493       return false;
   495     mData[cutStart] = c;
   497     return true;
   498   }
   500 void
   501 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length )
   502   {
   503     if (!Replace(cutStart, cutLength, data, length, mozilla::fallible_t()))
   504       {
   505         NS_ABORT_OOM(Length() - cutLength + 1);
   506       }
   507   }
   509 bool
   510 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length, const mozilla::fallible_t& )
   511   {
   512       // unfortunately, some callers pass null :-(
   513     if (!data)
   514       {
   515         length = 0;
   516       }
   517     else
   518       {
   519         if (length == size_type(-1))
   520           length = char_traits::length(data);
   522         if (IsDependentOn(data, data + length))
   523           {
   524             nsTAutoString_CharT temp(data, length);
   525             return Replace(cutStart, cutLength, temp, mozilla::fallible_t());
   526           }
   527       }
   529     cutStart = XPCOM_MIN(cutStart, Length());
   531     bool ok = ReplacePrep(cutStart, cutLength, length);
   532     if (!ok)
   533       return false;
   535     if (length > 0)
   536       char_traits::copy(mData + cutStart, data, length);
   538     return true;
   539   }
   541 void
   542 nsTSubstring_CharT::ReplaceASCII( index_type cutStart, size_type cutLength, const char* data, size_type length )
   543   {
   544     if (length == size_type(-1))
   545       length = strlen(data);
   547     // A Unicode string can't depend on an ASCII string buffer,
   548     // so this dependence check only applies to CStrings.
   549 #ifdef CharT_is_char
   550     if (IsDependentOn(data, data + length))
   551       {
   552         nsTAutoString_CharT temp(data, length);
   553         Replace(cutStart, cutLength, temp);
   554         return;
   555       }
   556 #endif
   558     cutStart = XPCOM_MIN(cutStart, Length());
   560     if (ReplacePrep(cutStart, cutLength, length) && length > 0)
   561       char_traits::copyASCII(mData + cutStart, data, length);
   562   }
   564 void
   565 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const substring_tuple_type& tuple )
   566   {
   567     if (tuple.IsDependentOn(mData, mData + mLength))
   568       {
   569         nsTAutoString_CharT temp(tuple);
   570         Replace(cutStart, cutLength, temp);
   571         return;
   572       }
   574     size_type length = tuple.Length();
   576     cutStart = XPCOM_MIN(cutStart, Length());
   578     if (ReplacePrep(cutStart, cutLength, length) && length > 0)
   579       tuple.WriteTo(mData + cutStart, length);
   580   }
   582 void
   583 nsTSubstring_CharT::ReplaceLiteral( index_type cutStart, size_type cutLength, const char_type* data, size_type length )
   584   {
   585     cutStart = XPCOM_MIN(cutStart, Length());
   587     if (!cutStart && cutLength == Length())
   588       AssignLiteral(data, length);
   589     else if (ReplacePrep(cutStart, cutLength, length) && length > 0)
   590       char_traits::copy(mData + cutStart, data, length);
   591   }
   593 void
   594 nsTSubstring_CharT::SetCapacity( size_type capacity )
   595   {
   596     if (!SetCapacity(capacity, fallible_t()))
   597       NS_ABORT_OOM(capacity);
   598   }
   600 bool
   601 nsTSubstring_CharT::SetCapacity( size_type capacity, const fallible_t& )
   602   {
   603     // capacity does not include room for the terminating null char
   605     // if our capacity is reduced to zero, then free our buffer.
   606     if (capacity == 0)
   607       {
   608         ::ReleaseData(mData, mFlags);
   609         mData = char_traits::sEmptyBuffer;
   610         mLength = 0;
   611         SetDataFlags(F_TERMINATED);
   612         return true;
   613       }
   615     char_type* oldData;
   616     uint32_t oldFlags;
   617     if (!MutatePrep(capacity, &oldData, &oldFlags))
   618       return false; // out-of-memory
   620     // compute new string length
   621     size_type newLen = XPCOM_MIN(mLength, capacity);
   623     if (oldData)
   624       {
   625         // preserve old data
   626         if (mLength > 0)
   627           char_traits::copy(mData, oldData, newLen);
   629         ::ReleaseData(oldData, oldFlags);
   630       }
   632     // adjust mLength if our buffer shrunk down in size
   633     if (newLen < mLength)
   634       mLength = newLen;
   636     // always null-terminate here, even if the buffer got longer.  this is
   637     // for backwards compat with the old string implementation.
   638     mData[capacity] = char_type(0);
   640     return true;
   641   }
   643 void
   644 nsTSubstring_CharT::SetLength( size_type length )
   645   {
   646     SetCapacity(length);
   647     mLength = length;
   648   }
   650 bool
   651 nsTSubstring_CharT::SetLength( size_type length, const fallible_t& )
   652   {
   653     if (!SetCapacity(length, fallible_t()))
   654       return false;
   656     mLength = length;
   657     return true;
   658   }
   660 void
   661 nsTSubstring_CharT::SetIsVoid( bool val )
   662   {
   663     if (val)
   664       {
   665         Truncate();
   666         mFlags |= F_VOIDED;
   667       }
   668     else
   669       {
   670         mFlags &= ~F_VOIDED;
   671       }
   672   }
   674 bool
   675 nsTSubstring_CharT::Equals( const self_type& str ) const
   676   {
   677     return mLength == str.mLength && char_traits::compare(mData, str.mData, mLength) == 0;
   678   }
   680 bool
   681 nsTSubstring_CharT::Equals( const self_type& str, const comparator_type& comp ) const
   682   {
   683     return mLength == str.mLength && comp(mData, str.mData, mLength, str.mLength) == 0;
   684   }
   686 bool
   687 nsTSubstring_CharT::Equals( const char_type* data ) const
   688   {
   689     // unfortunately, some callers pass null :-(
   690     if (!data)
   691       {
   692         NS_NOTREACHED("null data pointer");
   693         return mLength == 0;
   694       }
   696     // XXX avoid length calculation?
   697     size_type length = char_traits::length(data);
   698     return mLength == length && char_traits::compare(mData, data, mLength) == 0;
   699   }
   701 bool
   702 nsTSubstring_CharT::Equals( const char_type* data, const comparator_type& comp ) const
   703   {
   704     // unfortunately, some callers pass null :-(
   705     if (!data)
   706       {
   707         NS_NOTREACHED("null data pointer");
   708         return mLength == 0;
   709       }
   711     // XXX avoid length calculation?
   712     size_type length = char_traits::length(data);
   713     return mLength == length && comp(mData, data, mLength, length) == 0;
   714   }
   716 bool
   717 nsTSubstring_CharT::EqualsASCII( const char* data, size_type len ) const
   718   {
   719     return mLength == len && char_traits::compareASCII(mData, data, len) == 0;
   720   }
   722 bool
   723 nsTSubstring_CharT::EqualsASCII( const char* data ) const
   724   {
   725     return char_traits::compareASCIINullTerminated(mData, mLength, data) == 0;
   726   }
   728 bool
   729 nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data, size_type len ) const
   730   {
   731     return mLength == len && char_traits::compareLowerCaseToASCII(mData, data, len) == 0;
   732   }
   734 bool
   735 nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data ) const
   736   {
   737     return char_traits::compareLowerCaseToASCIINullTerminated(mData, mLength, data) == 0;
   738   }
   740 nsTSubstring_CharT::size_type
   741 nsTSubstring_CharT::CountChar( char_type c ) const
   742   {
   743     const char_type *start = mData;
   744     const char_type *end   = mData + mLength;
   746     return NS_COUNT(start, end, c);
   747   }
   749 int32_t
   750 nsTSubstring_CharT::FindChar( char_type c, index_type offset ) const
   751   {
   752     if (offset < mLength)
   753       {
   754         const char_type* result = char_traits::find(mData + offset, mLength - offset, c);
   755         if (result)
   756           return result - mData;
   757       }
   758     return -1;
   759   }
   761 void
   762 nsTSubstring_CharT::StripChar( char_type aChar, int32_t aOffset )
   763   {
   764     if (mLength == 0 || aOffset >= int32_t(mLength))
   765       return;
   767     if (!EnsureMutable()) // XXX do this lazily?
   768       NS_ABORT_OOM(mLength);
   770     // XXX(darin): this code should defer writing until necessary.
   772     char_type* to   = mData + aOffset;
   773     char_type* from = mData + aOffset;
   774     char_type* end  = mData + mLength;
   776     while (from < end)
   777       {
   778         char_type theChar = *from++;
   779         if (aChar != theChar)
   780           *to++ = theChar;
   781       }
   782     *to = char_type(0); // add the null
   783     mLength = to - mData;
   784   }
   786 void
   787 nsTSubstring_CharT::StripChars( const char_type* aChars, uint32_t aOffset )
   788   {
   789     if (aOffset >= uint32_t(mLength))
   790       return;
   792     if (!EnsureMutable()) // XXX do this lazily?
   793       NS_ABORT_OOM(mLength);
   795     // XXX(darin): this code should defer writing until necessary.
   797     char_type* to   = mData + aOffset;
   798     char_type* from = mData + aOffset;
   799     char_type* end  = mData + mLength;
   801     while (from < end)
   802       {
   803         char_type theChar = *from++;
   804         const char_type* test = aChars;
   806         for (; *test && *test != theChar; ++test);
   808         if (!*test) {
   809           // Not stripped, copy this char.
   810           *to++ = theChar;
   811         }
   812       }
   813     *to = char_type(0); // add the null
   814     mLength = to - mData;
   815   }
   817 int
   818 nsTSubstring_CharT::AppendFunc(void* arg, const char* s, uint32_t len)
   819   {
   820     self_type* self = static_cast<self_type*>(arg);
   822     // NSPR sends us the final null terminator even though we don't want it
   823     if (len && s[len - 1] == '\0') {
   824       --len;
   825     }
   827     self->AppendASCII(s, len);
   829     return len;
   830   }
   832 void nsTSubstring_CharT::AppendPrintf( const char* format, ...)
   833   {
   834     va_list ap;
   835     va_start(ap, format);
   836     uint32_t r = PR_vsxprintf(AppendFunc, this, format, ap);
   837     if (r == (uint32_t) -1)
   838       NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf");
   839     va_end(ap);
   840   }
   842 void nsTSubstring_CharT::AppendPrintf( const char* format, va_list ap )
   843   {
   844     uint32_t r = PR_vsxprintf(AppendFunc, this, format, ap);
   845     if (r == (uint32_t) -1)
   846       NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf");
   847   }
   849 /* hack to make sure we define FormatWithoutTrailingZeros only once */
   850 #ifdef CharT_is_PRUnichar
   851 // Returns the length of the formatted aDouble in buf.
   852 static int
   853 FormatWithoutTrailingZeros(char (& buf)[40], double aDouble,
   854                            int precision)
   855 {
   856   static const DoubleToStringConverter converter(DoubleToStringConverter::UNIQUE_ZERO |
   857                                                  DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
   858                                                  "Infinity",
   859                                                  "NaN",
   860                                                  'e',
   861                                                  -6, 21,
   862                                                  6, 1);
   863   double_conversion::StringBuilder builder(buf, sizeof(buf));
   864   bool exponential_notation = false;
   865   converter.ToPrecision(aDouble, precision, &exponential_notation, &builder);
   866   int length = builder.position();
   867   char* formattedDouble = builder.Finalize();
   869   // If we have a shorter string than precision, it means we have a special
   870   // value (NaN or Infinity).  All other numbers will be formatted with at
   871   // least precision digits.
   872   if (length <= precision) {
   873     return length;
   874   }
   876   char* end = formattedDouble + length;
   877   char* decimalPoint = strchr(buf, '.');
   878   // No trailing zeros to remove.
   879   if (decimalPoint == nullptr) {
   880     return length;
   881   }
   883   if (MOZ_UNLIKELY(exponential_notation)) {
   884     // We need to check for cases like 1.00000e-10 (yes, this is
   885     // disgusting).
   886     char* exponent = end - 1;
   887     for ( ; ; --exponent) {
   888       if (*exponent == 'e') {
   889         break;
   890       }
   891     }
   892     char* zerosBeforeExponent = exponent - 1;
   893     for ( ; zerosBeforeExponent != decimalPoint; --zerosBeforeExponent) {
   894       if (*zerosBeforeExponent != '0') {
   895         break;
   896       }
   897     }
   898     if (zerosBeforeExponent == decimalPoint) {
   899       --zerosBeforeExponent;
   900     }
   901     // Slide the exponent to the left over the trailing zeros.  Don't
   902     // worry about copying the trailing NUL character.
   903     size_t exponentSize = end - exponent;
   904     memmove(zerosBeforeExponent + 1, exponent, exponentSize);
   905     length -= exponent - (zerosBeforeExponent + 1);
   906   } else {
   907     char* trailingZeros = end - 1;
   908     for ( ; trailingZeros != decimalPoint; --trailingZeros) {
   909       if (*trailingZeros != '0') {
   910         break;
   911       }
   912     }
   913     if (trailingZeros == decimalPoint) {
   914       --trailingZeros;
   915     }
   916     length -= end - (trailingZeros + 1);
   917   }
   919   return length;
   920 }
   921 #endif /* CharT_is_PRUnichar */
   923 void
   924 nsTSubstring_CharT::AppendFloat( float aFloat )
   925 {
   926   char buf[40];
   927   int length = FormatWithoutTrailingZeros(buf, aFloat, 6);
   928   AppendASCII(buf, length);
   929 }
   931 void
   932 nsTSubstring_CharT::AppendFloat( double aFloat )
   933 {
   934   char buf[40];
   935   int length = FormatWithoutTrailingZeros(buf, aFloat, 15);
   936   AppendASCII(buf, length);
   937 }
   939 size_t
   940 nsTSubstring_CharT::SizeOfExcludingThisMustBeUnshared(
   941     mozilla::MallocSizeOf mallocSizeOf) const
   942 {
   943   if (mFlags & F_SHARED) {
   944     return nsStringBuffer::FromData(mData)->
   945              SizeOfIncludingThisMustBeUnshared(mallocSizeOf);
   946   }
   947   if (mFlags & F_OWNED) {
   948     return mallocSizeOf(mData);
   949   }
   951   // If we reach here, exactly one of the following must be true:
   952   // - F_VOIDED is set, and mData points to sEmptyBuffer;
   953   // - F_FIXED is set, and mData points to a buffer within a string
   954   //   object (e.g. nsAutoString);
   955   // - None of F_SHARED, F_OWNED, F_FIXED is set, and mData points to a buffer
   956   //   owned by something else.
   957   //
   958   // In all three cases, we don't measure it.
   959   return 0;
   960 }
   962 size_t
   963 nsTSubstring_CharT::SizeOfExcludingThisIfUnshared(
   964     mozilla::MallocSizeOf mallocSizeOf) const
   965 {
   966   // This is identical to SizeOfExcludingThisMustBeUnshared except for the
   967   // F_SHARED case.
   968   if (mFlags & F_SHARED) {
   969     return nsStringBuffer::FromData(mData)->
   970              SizeOfIncludingThisIfUnshared(mallocSizeOf);
   971   }
   972   if (mFlags & F_OWNED) {
   973     return mallocSizeOf(mData);
   974   }
   975   return 0;
   976 }
   978 size_t
   979 nsTSubstring_CharT::SizeOfExcludingThisEvenIfShared(
   980     mozilla::MallocSizeOf mallocSizeOf) const
   981 {
   982   // This is identical to SizeOfExcludingThisMustBeUnshared except for the
   983   // F_SHARED case.
   984   if (mFlags & F_SHARED) {
   985     return nsStringBuffer::FromData(mData)->
   986              SizeOfIncludingThisEvenIfShared(mallocSizeOf);
   987   }
   988   if (mFlags & F_OWNED) {
   989     return mallocSizeOf(mData);
   990   }
   991   return 0;
   992 }
   994 size_t
   995 nsTSubstring_CharT::SizeOfIncludingThisMustBeUnshared(
   996     mozilla::MallocSizeOf mallocSizeOf) const
   997 {
   998   return mallocSizeOf(this) + SizeOfExcludingThisMustBeUnshared(mallocSizeOf);
   999 }
  1001 size_t
  1002 nsTSubstring_CharT::SizeOfIncludingThisIfUnshared(
  1003     mozilla::MallocSizeOf mallocSizeOf) const
  1005   return mallocSizeOf(this) + SizeOfExcludingThisIfUnshared(mallocSizeOf);
  1008 size_t
  1009 nsTSubstring_CharT::SizeOfIncludingThisEvenIfShared(
  1010     mozilla::MallocSizeOf mallocSizeOf) const
  1012   return mallocSizeOf(this) + SizeOfExcludingThisEvenIfShared(mallocSizeOf);

mercurial