Back to index

lightning-sunbird  0.9+nobinonly
nsTSubstring.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* vim:set ts=2 sw=2 sts=2 et cindent: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla.
00017  *
00018  * The Initial Developer of the Original Code is IBM Corporation.
00019  * Portions created by IBM Corporation are Copyright (C) 2003
00020  * IBM Corporation. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Darin Fisher <darin@meer.net>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 
00043 inline const nsTFixedString_CharT*
00044 AsFixedString( const nsTSubstring_CharT* s )
00045   {
00046     return NS_STATIC_CAST(const nsTFixedString_CharT*, s);
00047   }
00048 
00049 
00057 PRBool
00058 nsTSubstring_CharT::MutatePrep( size_type capacity, char_type** oldData, PRUint32* oldFlags )
00059   {
00060     // initialize to no old data
00061     *oldData = nsnull;
00062     *oldFlags = 0;
00063 
00064     size_type curCapacity = Capacity();
00065 
00066     // If |capacity > size_type(-1)/2|, then our doubling algorithm may not be
00067     // able to allocate it.  Just bail out in cases like that.  We don't want
00068     // to be allocating 2GB+ strings anyway.
00069     if (capacity > size_type(-1)/2) {
00070       // Also assert for |capacity| equal to |size_type(-1)|, since we use that value to
00071       // flag immutability.
00072       NS_ASSERTION(capacity != size_type(-1), "Bogus capacity");
00073       return PR_FALSE;
00074     }
00075 
00076     // |curCapacity == size_type(-1)| means that the buffer is immutable, so we
00077     // need to allocate a new buffer.  we cannot use the existing buffer even
00078     // though it might be large enough.
00079 
00080     if (curCapacity != size_type(-1))
00081       {
00082         if (capacity <= curCapacity)
00083           return PR_TRUE;
00084 
00085         if (curCapacity > 0)
00086           {
00087             // use doubling algorithm when forced to increase available
00088             // capacity.
00089             PRUint32 temp = curCapacity;
00090             while (temp < capacity)
00091               temp <<= 1;
00092             capacity = temp;
00093           }
00094       }
00095 
00096     //
00097     // several cases:
00098     //
00099     //  (1) we have a shared buffer (mFlags & F_SHARED)
00100     //  (2) we have an owned buffer (mFlags & F_OWNED)
00101     //  (3) we have a fixed buffer (mFlags & F_FIXED)
00102     //  (4) we have a readonly buffer
00103     //
00104     // requiring that we in some cases preserve the data before creating
00105     // a new buffer complicates things just a bit ;-)
00106     //
00107 
00108     size_type storageSize = (capacity + 1) * sizeof(char_type);
00109 
00110     // case #1
00111     if (mFlags & F_SHARED)
00112       {
00113         nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
00114         if (!hdr->IsReadonly())
00115           {
00116             nsStringBuffer *newHdr = nsStringBuffer::Realloc(hdr, storageSize);
00117             if (!newHdr)
00118               return PR_FALSE; // out-of-memory (original header left intact)
00119 
00120             hdr = newHdr;
00121             mData = (char_type*) hdr->Data();
00122             return PR_TRUE;
00123           }
00124       }
00125 
00126     char_type* newData;
00127     PRUint32 newDataFlags;
00128 
00129       // if we have a fixed buffer of sufficient size, then use it.  this helps
00130       // avoid heap allocations.
00131     if ((mFlags & F_CLASS_FIXED) && (capacity < AsFixedString(this)->mFixedCapacity))
00132       {
00133         newData = AsFixedString(this)->mFixedBuf;
00134         newDataFlags = F_TERMINATED | F_FIXED;
00135       }
00136     else
00137       {
00138         // if we reach here then, we must allocate a new buffer.  we cannot
00139         // make use of our F_OWNED or F_FIXED buffers because they are not
00140         // large enough.
00141 
00142         nsStringBuffer* newHdr = nsStringBuffer::Alloc(storageSize);
00143         if (!newHdr)
00144           return PR_FALSE; // we are still in a consistent state
00145 
00146         newData = (char_type*) newHdr->Data();
00147         newDataFlags = F_TERMINATED | F_SHARED;
00148       }
00149 
00150     // save old data and flags
00151     *oldData = mData;
00152     *oldFlags = mFlags;
00153 
00154     mData = newData;
00155     SetDataFlags(newDataFlags);
00156 
00157     // mLength does not change
00158 
00159     // though we are not necessarily terminated at the moment, now is probably
00160     // still the best time to set F_TERMINATED.
00161 
00162     return PR_TRUE;
00163   }
00164 
00165 void
00166 nsTSubstring_CharT::Finalize()
00167   {
00168     ::ReleaseData(mData, mFlags);
00169     // mData, mLength, and mFlags are purposefully left dangling
00170   }
00171 
00172 #ifndef MOZ_V1_STRING_ABI
00173 nsTSubstring_CharT::~nsTSubstring_CharT()
00174   {
00175     Finalize();
00176   }
00177 #endif
00178 
00179 PRBool
00180 nsTSubstring_CharT::ReplacePrep( index_type cutStart, size_type cutLen, size_type fragLen )
00181   {
00182     // bound cut length
00183     cutLen = NS_MIN(cutLen, mLength - cutStart);
00184 
00185     PRUint32 newLen = mLength - cutLen + fragLen;
00186 
00187     char_type* oldData;
00188     PRUint32 oldFlags;
00189     if (!MutatePrep(newLen, &oldData, &oldFlags))
00190       return PR_FALSE; // out-of-memory
00191 
00192     if (oldData)
00193       {
00194         // determine whether or not we need to copy part of the old string
00195         // over to the new string.
00196 
00197         if (cutStart > 0)
00198           {
00199             // copy prefix from old string
00200             char_traits::copy(mData, oldData, cutStart);
00201           }
00202 
00203         if (cutStart + cutLen < mLength)
00204           {
00205             // copy suffix from old string to new offset
00206             size_type from = cutStart + cutLen;
00207             size_type fromLen = mLength - from;
00208             PRUint32 to = cutStart + fragLen;
00209             char_traits::copy(mData + to, oldData + from, fromLen);
00210           }
00211 
00212         ::ReleaseData(oldData, oldFlags);
00213       }
00214     else
00215       {
00216         // original data remains intact
00217 
00218         // determine whether or not we need to move part of the existing string
00219         // to make room for the requested hole.
00220         if (fragLen != cutLen && cutStart + cutLen < mLength)
00221           {
00222             PRUint32 from = cutStart + cutLen;
00223             PRUint32 fromLen = mLength - from;
00224             PRUint32 to = cutStart + fragLen;
00225             char_traits::move(mData + to, mData + from, fromLen);
00226           }
00227       }
00228 
00229     // add null terminator (mutable mData always has room for the null-
00230     // terminator).
00231     mData[newLen] = char_type(0);
00232     mLength = newLen;
00233 
00234     return PR_TRUE;
00235   }
00236 
00237 nsTSubstring_CharT::size_type
00238 nsTSubstring_CharT::Capacity() const
00239   {
00240     // return size_type(-1) to indicate an immutable buffer
00241 
00242     size_type capacity;
00243     if (mFlags & F_SHARED)
00244       {
00245         // if the string is readonly, then we pretend that it has no capacity.
00246         nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
00247         if (hdr->IsReadonly())
00248           capacity = size_type(-1);
00249         else
00250           capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
00251       }
00252     else if (mFlags & F_FIXED)
00253       {
00254         capacity = AsFixedString(this)->mFixedCapacity;
00255       }
00256     else if (mFlags & F_OWNED)
00257       {
00258         // we don't store the capacity of an adopted buffer because that would
00259         // require an additional member field.  the best we can do is base the
00260         // capacity on our length.  remains to be seen if this is the right
00261         // trade-off.
00262         capacity = mLength;
00263       }
00264     else
00265       {
00266         capacity = size_type(-1);
00267       }
00268 
00269     return capacity;
00270   }
00271 
00272 void
00273 nsTSubstring_CharT::EnsureMutable()
00274   {
00275     if (mFlags & (F_FIXED | F_OWNED))
00276       return;
00277     if ((mFlags & F_SHARED) && !nsStringBuffer::FromData(mData)->IsReadonly())
00278       return;
00279 
00280     // promote to a shared string buffer
00281     Assign(string_type(mData, mLength));
00282   }
00283 
00284 // ---------------------------------------------------------------------------
00285 
00286 void
00287 nsTSubstring_CharT::Assign( const char_type* data, size_type length )
00288   {
00289       // unfortunately, some callers pass null :-(
00290     if (!data)
00291       {
00292         Truncate();
00293         return;
00294       }
00295 
00296     if (length == size_type(-1))
00297       length = char_traits::length(data);
00298 
00299     if (IsDependentOn(data, data + length))
00300       {
00301         // take advantage of sharing here...
00302         Assign(string_type(data, length));
00303         return;
00304       }
00305 
00306     if (ReplacePrep(0, mLength, length))
00307       char_traits::copy(mData, data, length);
00308   }
00309 
00310 void
00311 nsTSubstring_CharT::AssignASCII( const char* data, size_type length )
00312   {
00313     // A Unicode string can't depend on an ASCII string buffer,
00314     // so this dependence check only applies to CStrings.
00315 #ifdef CharT_is_char
00316     if (IsDependentOn(data, data + length))
00317       {
00318         // take advantage of sharing here...
00319         Assign(string_type(data, length));
00320         return;
00321       }
00322 #endif
00323 
00324     if (ReplacePrep(0, mLength, length))
00325       char_traits::copyASCII(mData, data, length);
00326   }
00327 
00328 void
00329 nsTSubstring_CharT::AssignASCII( const char* data )
00330   {
00331     AssignASCII(data, strlen(data));
00332   }
00333 
00334 void
00335 nsTSubstring_CharT::Assign( const self_type& str )
00336   {
00337     // |str| could be sharable.  we need to check its flags to know how to
00338     // deal with it.
00339 
00340     if (&str == this)
00341       return;
00342 
00343     if (str.mFlags & F_SHARED)
00344       {
00345         // nice! we can avoid a string copy :-)
00346 
00347         // |str| should be null-terminated
00348         NS_ASSERTION(str.mFlags & F_TERMINATED, "shared, but not terminated");
00349 
00350         ::ReleaseData(mData, mFlags);
00351 
00352         mData = str.mData;
00353         mLength = str.mLength;
00354         SetDataFlags(F_TERMINATED | F_SHARED);
00355 
00356         // get an owning reference to the mData
00357         nsStringBuffer::FromData(mData)->AddRef();
00358       }
00359     else if (str.mFlags & F_VOIDED)
00360       {
00361         // inherit the F_VOIDED attribute
00362         SetIsVoid(PR_TRUE);
00363       }
00364     else
00365       {
00366         // else, treat this like an ordinary assignment.
00367         Assign(str.Data(), str.Length());
00368       }
00369   }
00370 
00371 void
00372 nsTSubstring_CharT::Assign( const substring_tuple_type& tuple )
00373   {
00374     if (tuple.IsDependentOn(mData, mData + mLength))
00375       {
00376         // take advantage of sharing here...
00377         Assign(string_type(tuple));
00378         return;
00379       }
00380 
00381     size_type length = tuple.Length();
00382 
00383     if (ReplacePrep(0, mLength, length) && length)
00384       tuple.WriteTo(mData, length);
00385   }
00386 
00387   // this is non-inline to reduce codesize at the callsite
00388 #ifdef MOZ_V1_STRING_ABI
00389 void
00390 nsTSubstring_CharT::Assign( const abstract_string_type& readable )
00391   {
00392       // promote to string if possible to take advantage of sharing
00393     if (readable.mVTable == nsTObsoleteAString_CharT::sCanonicalVTable)
00394       Assign(*readable.AsSubstring());
00395     else
00396       Assign(readable.ToSubstring());
00397   }
00398 #endif
00399 
00400 
00401 void
00402 nsTSubstring_CharT::Adopt( char_type* data, size_type length )
00403   {
00404     if (data)
00405       {
00406         ::ReleaseData(mData, mFlags);
00407 
00408         if (length == size_type(-1))
00409           length = char_traits::length(data);
00410 
00411         mData = data;
00412         mLength = length;
00413         SetDataFlags(F_TERMINATED | F_OWNED);
00414 
00415         STRING_STAT_INCREMENT(Adopt);
00416       }
00417     else
00418       {
00419         SetIsVoid(PR_TRUE);
00420       }
00421   }
00422 
00423 
00424 void
00425 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length )
00426   {
00427       // unfortunately, some callers pass null :-(
00428     if (!data)
00429       {
00430         length = 0;
00431       }
00432     else
00433       {
00434         if (length == size_type(-1))
00435           length = char_traits::length(data);
00436 
00437         if (IsDependentOn(data, data + length))
00438           {
00439             nsTAutoString_CharT temp(data, length);
00440             Replace(cutStart, cutLength, temp);
00441             return;
00442           }
00443       }
00444 
00445     cutStart = PR_MIN(cutStart, Length());
00446 
00447     if (ReplacePrep(cutStart, cutLength, length) && length > 0)
00448       char_traits::copy(mData + cutStart, data, length);
00449   }
00450 
00451 void
00452 nsTSubstring_CharT::ReplaceASCII( index_type cutStart, size_type cutLength, const char* data, size_type length )
00453   {
00454     if (length == size_type(-1))
00455       length = strlen(data);
00456     
00457     // A Unicode string can't depend on an ASCII string buffer,
00458     // so this dependence check only applies to CStrings.
00459 #ifdef CharT_is_char
00460     if (IsDependentOn(data, data + length))
00461       {
00462         nsTAutoString_CharT temp(data, length);
00463         Replace(cutStart, cutLength, temp);
00464         return;
00465       }
00466 #endif
00467 
00468     cutStart = PR_MIN(cutStart, Length());
00469 
00470     if (ReplacePrep(cutStart, cutLength, length) && length > 0)
00471       char_traits::copyASCII(mData + cutStart, data, length);
00472   }
00473 
00474 void
00475 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const substring_tuple_type& tuple )
00476   {
00477     if (tuple.IsDependentOn(mData, mData + mLength))
00478       {
00479         nsTAutoString_CharT temp(tuple);
00480         Replace(cutStart, cutLength, temp);
00481         return;
00482       }
00483 
00484     size_type length = tuple.Length();
00485 
00486     cutStart = PR_MIN(cutStart, Length());
00487 
00488     if (ReplacePrep(cutStart, cutLength, length) && length > 0)
00489       tuple.WriteTo(mData + cutStart, length);
00490   }
00491 
00492 #ifdef MOZ_V1_STRING_ABI
00493 void
00494 nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const abstract_string_type& readable )
00495   {
00496     Replace(cutStart, cutLength, readable.ToSubstring());
00497   }
00498 #endif
00499 
00500 void
00501 nsTSubstring_CharT::SetCapacity( size_type capacity )
00502   {
00503     // capacity does not include room for the terminating null char
00504 
00505     // if our capacity is reduced to zero, then free our buffer.
00506     if (capacity == 0)
00507       {
00508         ::ReleaseData(mData, mFlags);
00509         mData = NS_CONST_CAST(char_type*, char_traits::sEmptyBuffer);
00510         mLength = 0;
00511         SetDataFlags(F_TERMINATED);
00512       }
00513     else
00514       {
00515         char_type* oldData;
00516         PRUint32 oldFlags;
00517         if (!MutatePrep(capacity, &oldData, &oldFlags))
00518           return; // out-of-memory
00519 
00520         // compute new string length
00521         size_type newLen = NS_MIN(mLength, capacity);
00522 
00523         if (oldData)
00524           {
00525             // preserve old data
00526             if (mLength > 0)
00527               char_traits::copy(mData, oldData, newLen);
00528 
00529             ::ReleaseData(oldData, oldFlags);
00530           }
00531 
00532         // adjust mLength if our buffer shrunk down in size
00533         if (newLen < mLength)
00534           mLength = newLen;
00535 
00536         // always null-terminate here, even if the buffer got longer.  this is
00537         // for backwards compat with the old string implementation.
00538         mData[capacity] = char_type(0);
00539       }
00540   }
00541 
00542 void
00543 nsTSubstring_CharT::SetLength( size_type length )
00544   {
00545     SetCapacity(length);
00546 
00547     // XXX(darin): SetCapacity may fail, but it doesn't give us a way to find
00548     // out.  We should improve that.  For now we just verify that the capacity
00549     // changed as expected as a means of error checking.
00550  
00551     if (Capacity() >= length)
00552       mLength = length;
00553   }
00554 
00555 void
00556 nsTSubstring_CharT::SetIsVoid( PRBool val )
00557   {
00558     if (val)
00559       {
00560         Truncate();
00561         mFlags |= F_VOIDED;
00562       }
00563     else
00564       {
00565         mFlags &= ~F_VOIDED;
00566       }
00567   }
00568 
00569 PRBool
00570 nsTSubstring_CharT::Equals( const self_type& str ) const
00571   {
00572     return mLength == str.mLength && char_traits::compare(mData, str.mData, mLength) == 0;
00573   }
00574 
00575 PRBool
00576 nsTSubstring_CharT::Equals( const self_type& str, const comparator_type& comp ) const
00577   {
00578     return mLength == str.mLength && comp(mData, str.mData, mLength) == 0;
00579   }
00580 
00581 #ifdef MOZ_V1_STRING_ABI
00582 PRBool
00583 nsTSubstring_CharT::Equals( const abstract_string_type& readable ) const
00584   {
00585     const char_type* data;
00586     size_type length = readable.GetReadableBuffer(&data);
00587 
00588     return mLength == length && char_traits::compare(mData, data, mLength) == 0;
00589   }
00590 
00591 PRBool
00592 nsTSubstring_CharT::Equals( const abstract_string_type& readable, const comparator_type& comp ) const
00593   {
00594     const char_type* data;
00595     size_type length = readable.GetReadableBuffer(&data);
00596 
00597     return mLength == length && comp(mData, data, mLength) == 0;
00598   }
00599 #endif
00600 
00601 PRBool
00602 nsTSubstring_CharT::Equals( const char_type* data ) const
00603   {
00604     // unfortunately, some callers pass null :-(
00605     if (!data)
00606       {
00607         NS_NOTREACHED("null data pointer");
00608         return mLength == 0;
00609       }
00610 
00611     // XXX avoid length calculation?
00612     size_type length = char_traits::length(data);
00613     return mLength == length && char_traits::compare(mData, data, mLength) == 0;
00614   }
00615 
00616 PRBool
00617 nsTSubstring_CharT::Equals( const char_type* data, const comparator_type& comp ) const
00618   {
00619     // unfortunately, some callers pass null :-(
00620     if (!data)
00621       {
00622         NS_NOTREACHED("null data pointer");
00623         return mLength == 0;
00624       }
00625 
00626     // XXX avoid length calculation?
00627     size_type length = char_traits::length(data);
00628     return mLength == length && comp(mData, data, mLength) == 0;
00629   }
00630 
00631 PRBool
00632 nsTSubstring_CharT::EqualsASCII( const char* data, size_type len ) const
00633   {
00634     return mLength == len && char_traits::compareASCII(mData, data, len) == 0;
00635   }
00636 
00637 PRBool
00638 nsTSubstring_CharT::EqualsASCII( const char* data ) const
00639   {
00640     return char_traits::compareASCIINullTerminated(mData, mLength, data) == 0;
00641   }
00642 
00643 PRBool
00644 nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data, size_type len ) const
00645   {
00646     return mLength == len && char_traits::compareLowerCaseToASCII(mData, data, len) == 0;
00647   }
00648 
00649 PRBool
00650 nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data ) const
00651   {
00652     return char_traits::compareLowerCaseToASCIINullTerminated(mData, mLength, data) == 0;
00653   }
00654 
00655 nsTSubstring_CharT::size_type
00656 nsTSubstring_CharT::CountChar( char_type c ) const
00657   {
00658     const char_type *start = mData;
00659     const char_type *end   = mData + mLength;
00660 
00661     return NS_COUNT(start, end, c);
00662   }
00663 
00664 PRInt32
00665 nsTSubstring_CharT::FindChar( char_type c, index_type offset ) const
00666   {
00667     if (offset < mLength)
00668       {
00669         const char_type* result = char_traits::find(mData + offset, mLength - offset, c);
00670         if (result)
00671           return result - mData;
00672       }
00673     return -1;
00674   }
00675 
00676 void
00677 nsTSubstring_CharT::StripChar( char_type aChar, PRInt32 aOffset )
00678   {
00679     if (mLength == 0 || aOffset >= PRInt32(mLength))
00680       return;
00681 
00682     EnsureMutable(); // XXX do this lazily?
00683 
00684     // XXX(darin): this code should defer writing until necessary.
00685 
00686     char_type* to   = mData + aOffset;
00687     char_type* from = mData + aOffset;
00688     char_type* end  = mData + mLength;
00689 
00690     while (from < end)
00691       {
00692         char_type theChar = *from++;
00693         if (aChar != theChar)
00694           *to++ = theChar;
00695       }
00696     *to = char_type(0); // add the null
00697     mLength = to - mData;
00698   }