123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "nsTArray.h"
- /**
- * nsTString::Find
- *
- * aOffset specifies starting index
- * aCount specifies number of string compares (iterations)
- */
- int32_t
- nsTString_CharT::Find( const nsCString& aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
- {
- // this method changes the meaning of aOffset and aCount:
- Find_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount);
- int32_t result = FindSubstring(mData + aOffset, aCount, aString.get(), aString.Length(), aIgnoreCase);
- if (result != kNotFound)
- result += aOffset;
- return result;
- }
- int32_t
- nsTString_CharT::Find( const char* aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
- {
- return Find(nsDependentCString(aString), aIgnoreCase, aOffset, aCount);
- }
- /**
- * nsTString::RFind
- *
- * aOffset specifies starting index
- * aCount specifies number of string compares (iterations)
- */
- int32_t
- nsTString_CharT::RFind( const nsCString& aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
- {
- // this method changes the meaning of aOffset and aCount:
- RFind_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount);
- int32_t result = RFindSubstring(mData + aOffset, aCount, aString.get(), aString.Length(), aIgnoreCase);
- if (result != kNotFound)
- result += aOffset;
- return result;
- }
- int32_t
- nsTString_CharT::RFind( const char* aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
- {
- return RFind(nsDependentCString(aString), aIgnoreCase, aOffset, aCount);
- }
- /**
- * nsTString::RFindChar
- */
- int32_t
- nsTString_CharT::RFindChar( char16_t aChar, int32_t aOffset, int32_t aCount) const
- {
- return nsBufferRoutines<CharT>::rfind_char(mData, mLength, aOffset, aChar, aCount);
- }
- /**
- * nsTString::FindCharInSet
- */
- int32_t
- nsTString_CharT::FindCharInSet( const char* aSet, int32_t aOffset ) const
- {
- if (aOffset < 0)
- aOffset = 0;
- else if (aOffset >= int32_t(mLength))
- return kNotFound;
- int32_t result = ::FindCharInSet(mData + aOffset, mLength - aOffset, aSet);
- if (result != kNotFound)
- result += aOffset;
- return result;
- }
- /**
- * nsTString::RFindCharInSet
- */
- int32_t
- nsTString_CharT::RFindCharInSet( const CharT* aSet, int32_t aOffset ) const
- {
- // We want to pass a "data length" to ::RFindCharInSet
- if (aOffset < 0 || aOffset > int32_t(mLength))
- aOffset = mLength;
- else
- ++aOffset;
- return ::RFindCharInSet(mData, aOffset, aSet);
- }
- // it's a shame to replicate this code. it was done this way in the past
- // to help performance. this function also gets to keep the rickg style
- // indentation :-/
- int32_t
- nsTString_CharT::ToInteger( nsresult* aErrorCode, uint32_t aRadix ) const
- {
- CharT* cp=mData;
- int32_t theRadix=10; // base 10 unless base 16 detected, or overriden (aRadix != kAutoDetect)
- int32_t result=0;
- bool negate=false;
- CharT theChar=0;
- //initial value, override if we find an integer
- *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
- if(cp) {
- //begin by skipping over leading chars that shouldn't be part of the number...
- CharT* endcp=cp+mLength;
- bool done=false;
- while((cp<endcp) && (!done)){
- switch(*cp++) {
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- theRadix=16;
- done=true;
- break;
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- done=true;
- break;
- case '-':
- negate=true; //fall through...
- break;
- case 'X': case 'x':
- theRadix=16;
- break;
- default:
- break;
- } //switch
- }
- if (done) {
- //integer found
- *aErrorCode = NS_OK;
- if (aRadix!=kAutoDetect) theRadix = aRadix; // override
- //now iterate the numeric chars and build our result
- CharT* first=--cp; //in case we have to back up.
- bool haveValue = false;
- while(cp<endcp){
- int32_t oldresult = result;
- theChar=*cp++;
- if(('0'<=theChar) && (theChar<='9')){
- result = (theRadix * result) + (theChar-'0');
- haveValue = true;
- }
- else if((theChar>='A') && (theChar<='F')) {
- if(10==theRadix) {
- if(kAutoDetect==aRadix){
- theRadix=16;
- cp=first; //backup
- result=0;
- haveValue = false;
- }
- else {
- *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
- result=0;
- break;
- }
- }
- else {
- result = (theRadix * result) + ((theChar-'A')+10);
- haveValue = true;
- }
- }
- else if((theChar>='a') && (theChar<='f')) {
- if(10==theRadix) {
- if(kAutoDetect==aRadix){
- theRadix=16;
- cp=first; //backup
- result=0;
- haveValue = false;
- }
- else {
- *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
- result=0;
- break;
- }
- }
- else {
- result = (theRadix * result) + ((theChar-'a')+10);
- haveValue = true;
- }
- }
- else if((('X'==theChar) || ('x'==theChar)) && (!haveValue || result == 0)) {
- continue;
- }
- else if((('#'==theChar) || ('+'==theChar)) && !haveValue) {
- continue;
- }
- else {
- //we've encountered a char that's not a legal number or sign
- break;
- }
- if (result < oldresult) {
- // overflow!
- *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
- result = 0;
- break;
- }
- } //while
- if(negate)
- result=-result;
- } //if
- }
- return result;
- }
- /**
- * nsTString::ToInteger64
- */
- int64_t
- nsTString_CharT::ToInteger64( nsresult* aErrorCode, uint32_t aRadix ) const
- {
- CharT* cp=mData;
- int32_t theRadix=10; // base 10 unless base 16 detected, or overriden (aRadix != kAutoDetect)
- int64_t result=0;
- bool negate=false;
- CharT theChar=0;
- //initial value, override if we find an integer
- *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
- if(cp) {
- //begin by skipping over leading chars that shouldn't be part of the number...
- CharT* endcp=cp+mLength;
- bool done=false;
- while((cp<endcp) && (!done)){
- switch(*cp++) {
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- theRadix=16;
- done=true;
- break;
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- done=true;
- break;
- case '-':
- negate=true; //fall through...
- break;
- case 'X': case 'x':
- theRadix=16;
- break;
- default:
- break;
- } //switch
- }
- if (done) {
- //integer found
- *aErrorCode = NS_OK;
- if (aRadix!=kAutoDetect) theRadix = aRadix; // override
- //now iterate the numeric chars and build our result
- CharT* first=--cp; //in case we have to back up.
- bool haveValue = false;
- while(cp<endcp){
- int64_t oldresult = result;
- theChar=*cp++;
- if(('0'<=theChar) && (theChar<='9')){
- result = (theRadix * result) + (theChar-'0');
- haveValue = true;
- }
- else if((theChar>='A') && (theChar<='F')) {
- if(10==theRadix) {
- if(kAutoDetect==aRadix){
- theRadix=16;
- cp=first; //backup
- result=0;
- haveValue = false;
- }
- else {
- *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
- result=0;
- break;
- }
- }
- else {
- result = (theRadix * result) + ((theChar-'A')+10);
- haveValue = true;
- }
- }
- else if((theChar>='a') && (theChar<='f')) {
- if(10==theRadix) {
- if(kAutoDetect==aRadix){
- theRadix=16;
- cp=first; //backup
- result=0;
- haveValue = false;
- }
- else {
- *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
- result=0;
- break;
- }
- }
- else {
- result = (theRadix * result) + ((theChar-'a')+10);
- haveValue = true;
- }
- }
- else if((('X'==theChar) || ('x'==theChar)) && (!haveValue || result == 0)) {
- continue;
- }
- else if((('#'==theChar) || ('+'==theChar)) && !haveValue) {
- continue;
- }
- else {
- //we've encountered a char that's not a legal number or sign
- break;
- }
- if (result < oldresult) {
- // overflow!
- *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
- result = 0;
- break;
- }
- } //while
- if(negate)
- result=-result;
- } //if
- }
- return result;
- }
- /**
- * nsTString::Mid
- */
- uint32_t
- nsTString_CharT::Mid( self_type& aResult, index_type aStartPos, size_type aLengthToCopy ) const
- {
- if (aStartPos == 0 && aLengthToCopy >= mLength)
- aResult = *this;
- else
- aResult = Substring(*this, aStartPos, aLengthToCopy);
- return aResult.mLength;
- }
- /**
- * nsTString::SetCharAt
- */
- bool
- nsTString_CharT::SetCharAt( char16_t aChar, uint32_t aIndex )
- {
- if (aIndex >= mLength)
- return false;
- if (!EnsureMutable())
- AllocFailed(mLength);
- mData[aIndex] = CharT(aChar);
- return true;
- }
- /**
- * nsTString::StripChars,StripChar,StripWhitespace
- */
- void
- nsTString_CharT::StripChars( const char* aSet )
- {
- if (!EnsureMutable())
- AllocFailed(mLength);
- mLength = nsBufferRoutines<CharT>::strip_chars(mData, mLength, aSet);
- }
- bool
- nsTString_CharT::StripChars( const char* aSet, const fallible_t& )
- {
- if (!EnsureMutable()) {
- return false;
- }
- mLength = nsBufferRoutines<CharT>::strip_chars(mData, mLength, aSet);
- return true;
- }
- void
- nsTString_CharT::StripWhitespace()
- {
- StripChars(kWhitespace);
- }
- bool
- nsTString_CharT::StripWhitespace(const fallible_t& aFallible)
- {
- return StripChars(kWhitespace, aFallible);
- }
- /**
- * nsTString::ReplaceChar,ReplaceSubstring
- */
- void
- nsTString_CharT::ReplaceChar( char_type aOldChar, char_type aNewChar )
- {
- if (!EnsureMutable()) // XXX do this lazily?
- AllocFailed(mLength);
- for (uint32_t i=0; i<mLength; ++i)
- {
- if (mData[i] == aOldChar)
- mData[i] = aNewChar;
- }
- }
- void
- nsTString_CharT::ReplaceChar( const char* aSet, char_type aNewChar )
- {
- if (!EnsureMutable()) // XXX do this lazily?
- AllocFailed(mLength);
- char_type* data = mData;
- uint32_t lenRemaining = mLength;
- while (lenRemaining)
- {
- int32_t i = ::FindCharInSet(data, lenRemaining, aSet);
- if (i == kNotFound)
- break;
- data[i++] = aNewChar;
- data += i;
- lenRemaining -= i;
- }
- }
- void ReleaseData(void* aData, uint32_t aFlags);
- void
- nsTString_CharT::ReplaceSubstring(const char_type* aTarget,
- const char_type* aNewValue)
- {
- ReplaceSubstring(nsTDependentString_CharT(aTarget),
- nsTDependentString_CharT(aNewValue));
- }
- bool
- nsTString_CharT::ReplaceSubstring(const char_type* aTarget,
- const char_type* aNewValue,
- const fallible_t& aFallible)
- {
- return ReplaceSubstring(nsTDependentString_CharT(aTarget),
- nsTDependentString_CharT(aNewValue),
- aFallible);
- }
- void
- nsTString_CharT::ReplaceSubstring(const self_type& aTarget,
- const self_type& aNewValue)
- {
- if (!ReplaceSubstring(aTarget, aNewValue, mozilla::fallible)) {
- // Note that this may wildly underestimate the allocation that failed, as
- // we could have been replacing multiple copies of aTarget.
- AllocFailed(mLength + (aNewValue.Length() - aTarget.Length()));
- }
- }
- bool
- nsTString_CharT::ReplaceSubstring(const self_type& aTarget,
- const self_type& aNewValue,
- const fallible_t&)
- {
- if (aTarget.Length() == 0)
- return true;
- // Remember all of the non-matching parts.
- AutoTArray<Segment, 16> nonMatching;
- uint32_t i = 0;
- uint32_t newLength = 0;
- while (true)
- {
- int32_t r = FindSubstring(mData + i, mLength - i, static_cast<const char_type*>(aTarget.Data()), aTarget.Length(), false);
- int32_t until = (r == kNotFound) ? mLength - i : r;
- nonMatching.AppendElement(Segment(i, until));
- newLength += until;
- if (r == kNotFound) {
- break;
- }
- newLength += aNewValue.Length();
- i += r + aTarget.Length();
- if (i >= mLength) {
- // Add an auxiliary entry at the end of the list to help as an edge case
- // for the algorithms below.
- nonMatching.AppendElement(Segment(mLength, 0));
- break;
- }
- }
- // If there's only one non-matching segment, then the target string was not
- // found, and there's nothing to do.
- if (nonMatching.Length() == 1) {
- MOZ_ASSERT(nonMatching[0].mBegin == 0 && nonMatching[0].mLength == mLength,
- "We should have the correct non-matching segment.");
- return true;
- }
- // Make sure that we can mutate our buffer.
- // Note that we always allocate at least an mLength sized buffer, because the
- // rest of the algorithm relies on having access to all of the original
- // string. In other words, we over-allocate in the shrinking case.
- char_type* oldData;
- uint32_t oldFlags;
- if (!MutatePrep(XPCOM_MAX(mLength, newLength), &oldData, &oldFlags))
- return false;
- if (oldData) {
- // Copy all of the old data to the new buffer.
- char_traits::copy(mData, oldData, mLength);
- ::ReleaseData(oldData, oldFlags);
- }
- if (aTarget.Length() >= aNewValue.Length()) {
- // In the shrinking case, start filling the buffer from the beginning.
- const uint32_t delta = (aTarget.Length() - aNewValue.Length());
- for (i = 1; i < nonMatching.Length(); ++i) {
- // When we move the i'th non-matching segment into position, we need to
- // account for the characters deleted by the previous |i| replacements by
- // subtracting |i * delta|.
- const char_type* sourceSegmentPtr = mData + nonMatching[i].mBegin;
- char_type* destinationSegmentPtr = mData + nonMatching[i].mBegin - i * delta;
- // Write the i'th replacement immediately before the new i'th non-matching
- // segment.
- char_traits::copy(destinationSegmentPtr - aNewValue.Length(),
- aNewValue.Data(), aNewValue.Length());
- char_traits::move(destinationSegmentPtr, sourceSegmentPtr,
- nonMatching[i].mLength);
- }
- } else {
- // In the growing case, start filling the buffer from the end.
- const uint32_t delta = (aNewValue.Length() - aTarget.Length());
- for (i = nonMatching.Length() - 1; i > 0; --i) {
- // When we move the i'th non-matching segment into position, we need to
- // account for the characters added by the previous |i| replacements by
- // adding |i * delta|.
- const char_type* sourceSegmentPtr = mData + nonMatching[i].mBegin;
- char_type* destinationSegmentPtr = mData + nonMatching[i].mBegin + i * delta;
- char_traits::move(destinationSegmentPtr, sourceSegmentPtr,
- nonMatching[i].mLength);
- // Write the i'th replacement immediately before the new i'th non-matching
- // segment.
- char_traits::copy(destinationSegmentPtr - aNewValue.Length(),
- aNewValue.Data(), aNewValue.Length());
- }
- }
- // Adjust the length and make sure the string is null terminated.
- mLength = newLength;
- mData[mLength] = char_type(0);
- return true;
- }
- /**
- * nsTString::Trim
- */
- void
- nsTString_CharT::Trim( const char* aSet, bool aTrimLeading, bool aTrimTrailing, bool aIgnoreQuotes )
- {
- // the old implementation worried about aSet being null :-/
- if (!aSet)
- return;
- char_type* start = mData;
- char_type* end = mData + mLength;
- // skip over quotes if requested
- if (aIgnoreQuotes && mLength > 2 && mData[0] == mData[mLength - 1] &&
- (mData[0] == '\'' || mData[0] == '"'))
- {
- ++start;
- --end;
- }
- uint32_t setLen = nsCharTraits<char>::length(aSet);
- if (aTrimLeading)
- {
- uint32_t cutStart = start - mData;
- uint32_t cutLength = 0;
- // walk forward from start to end
- for (; start != end; ++start, ++cutLength)
- {
- int32_t pos = FindChar1(aSet, setLen, 0, *start, setLen);
- if (pos == kNotFound)
- break;
- }
- if (cutLength)
- {
- Cut(cutStart, cutLength);
- // reset iterators
- start = mData + cutStart;
- end = mData + mLength - cutStart;
- }
- }
- if (aTrimTrailing)
- {
- uint32_t cutEnd = end - mData;
- uint32_t cutLength = 0;
- // walk backward from end to start
- --end;
- for (; end >= start; --end, ++cutLength)
- {
- int32_t pos = FindChar1(aSet, setLen, 0, *end, setLen);
- if (pos == kNotFound)
- break;
- }
- if (cutLength)
- Cut(cutEnd - cutLength, cutLength);
- }
- }
- /**
- * nsTString::CompressWhitespace
- */
- void
- nsTString_CharT::CompressWhitespace( bool aTrimLeading, bool aTrimTrailing )
- {
- const char* set = kWhitespace;
- ReplaceChar(set, ' ');
- Trim(set, aTrimLeading, aTrimTrailing);
- // this one does some questionable fu... just copying the old code!
- mLength = nsBufferRoutines<char_type>::compress_chars(mData, mLength, set);
- }
- /**
- * nsTString::AssignWithConversion
- */
- void
- nsTString_CharT::AssignWithConversion( const incompatible_char_type* aData, int32_t aLength )
- {
- // for compatibility with the old string implementation, we need to allow
- // for a nullptr input buffer :-(
- if (!aData)
- {
- Truncate();
- }
- else
- {
- if (aLength < 0)
- aLength = nsCharTraits<incompatible_char_type>::length(aData);
- AssignWithConversion(Substring(aData, aLength));
- }
- }
|