2004-02-19 02:44:03 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is Mozilla.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is IBM Corporation.
|
|
|
|
* Portions created by IBM Corporation are Copyright (C) 2003
|
|
|
|
* IBM Corporation. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Darin Fisher <darin@meer.net>
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
2010-08-07 00:46:51 +00:00
|
|
|
#ifdef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
|
2006-11-10 20:05:04 +00:00
|
|
|
nsTSubstring_CharT::nsTSubstring_CharT( char_type *data, size_type length,
|
|
|
|
PRUint32 flags)
|
|
|
|
: mData(data),
|
|
|
|
mLength(length),
|
|
|
|
mFlags(flags)
|
|
|
|
{
|
|
|
|
if (flags & F_OWNED) {
|
|
|
|
STRING_STAT_INCREMENT(Adopt);
|
|
|
|
#ifdef NS_BUILD_REFCNT_LOGGING
|
|
|
|
NS_LogCtor(mData, "StringAdopt", 1);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
2010-08-07 00:46:51 +00:00
|
|
|
#endif /* XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE */
|
2008-08-18 15:40:49 +00:00
|
|
|
|
2004-02-20 01:53:23 +00:00
|
|
|
/**
|
|
|
|
* helper function for down-casting a nsTSubstring to a nsTFixedString.
|
|
|
|
*/
|
|
|
|
inline const nsTFixedString_CharT*
|
|
|
|
AsFixedString( const nsTSubstring_CharT* s )
|
|
|
|
{
|
2007-07-08 07:08:04 +00:00
|
|
|
return static_cast<const nsTFixedString_CharT*>(s);
|
2004-02-20 01:53:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-02-19 02:44:03 +00:00
|
|
|
/**
|
|
|
|
* this function is called to prepare mData for writing. the given capacity
|
|
|
|
* indicates the required minimum storage size for mData, in sizeof(char_type)
|
|
|
|
* increments. this function returns true if the operation succeeds. it also
|
|
|
|
* returns the old data and old flags members if mData is newly allocated.
|
|
|
|
* the old data must be released by the caller.
|
|
|
|
*/
|
|
|
|
PRBool
|
|
|
|
nsTSubstring_CharT::MutatePrep( size_type capacity, char_type** oldData, PRUint32* oldFlags )
|
|
|
|
{
|
|
|
|
// initialize to no old data
|
|
|
|
*oldData = nsnull;
|
|
|
|
*oldFlags = 0;
|
|
|
|
|
|
|
|
size_type curCapacity = Capacity();
|
|
|
|
|
2004-10-19 21:46:45 +00:00
|
|
|
// If |capacity > size_type(-1)/2|, then our doubling algorithm may not be
|
|
|
|
// able to allocate it. Just bail out in cases like that. We don't want
|
|
|
|
// to be allocating 2GB+ strings anyway.
|
|
|
|
if (capacity > size_type(-1)/2) {
|
2009-11-19 01:14:29 +00:00
|
|
|
// Also assert for |capacity| equal to |size_type(-1)|, since we used to
|
|
|
|
// use that value to flag immutability.
|
2004-10-19 21:46:45 +00:00
|
|
|
NS_ASSERTION(capacity != size_type(-1), "Bogus capacity");
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2009-11-19 01:14:29 +00:00
|
|
|
// |curCapacity == 0| means that the buffer is immutable or 0-sized, so we
|
|
|
|
// need to allocate a new buffer. We cannot use the existing buffer even
|
2004-02-19 02:44:03 +00:00
|
|
|
// though it might be large enough.
|
|
|
|
|
2009-11-19 01:14:29 +00:00
|
|
|
if (curCapacity != 0)
|
2004-02-19 02:44:03 +00:00
|
|
|
{
|
2006-02-04 00:42:18 +00:00
|
|
|
if (capacity <= curCapacity) {
|
|
|
|
mFlags &= ~F_VOIDED; // mutation clears voided flag
|
2004-02-19 02:44:03 +00:00
|
|
|
return PR_TRUE;
|
2006-02-04 00:42:18 +00:00
|
|
|
}
|
2004-02-19 02:44:03 +00:00
|
|
|
|
|
|
|
if (curCapacity > 0)
|
|
|
|
{
|
2004-10-19 21:46:45 +00:00
|
|
|
// use doubling algorithm when forced to increase available
|
|
|
|
// capacity.
|
2004-02-19 02:44:03 +00:00
|
|
|
PRUint32 temp = curCapacity;
|
|
|
|
while (temp < capacity)
|
|
|
|
temp <<= 1;
|
|
|
|
capacity = temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// several cases:
|
|
|
|
//
|
|
|
|
// (1) we have a shared buffer (mFlags & F_SHARED)
|
|
|
|
// (2) we have an owned buffer (mFlags & F_OWNED)
|
|
|
|
// (3) we have a fixed buffer (mFlags & F_FIXED)
|
|
|
|
// (4) we have a readonly buffer
|
|
|
|
//
|
|
|
|
// requiring that we in some cases preserve the data before creating
|
|
|
|
// a new buffer complicates things just a bit ;-)
|
|
|
|
//
|
|
|
|
|
|
|
|
size_type storageSize = (capacity + 1) * sizeof(char_type);
|
|
|
|
|
|
|
|
// case #1
|
|
|
|
if (mFlags & F_SHARED)
|
|
|
|
{
|
2005-02-02 22:18:37 +00:00
|
|
|
nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
|
2004-02-19 02:44:03 +00:00
|
|
|
if (!hdr->IsReadonly())
|
|
|
|
{
|
2005-02-02 22:18:37 +00:00
|
|
|
nsStringBuffer *newHdr = nsStringBuffer::Realloc(hdr, storageSize);
|
2005-02-16 07:20:20 +00:00
|
|
|
if (!newHdr)
|
|
|
|
return PR_FALSE; // out-of-memory (original header left intact)
|
|
|
|
|
|
|
|
hdr = newHdr;
|
|
|
|
mData = (char_type*) hdr->Data();
|
2006-02-04 00:42:18 +00:00
|
|
|
mFlags &= ~F_VOIDED; // mutation clears voided flag
|
2005-02-16 07:20:20 +00:00
|
|
|
return PR_TRUE;
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-02-20 01:53:23 +00:00
|
|
|
char_type* newData;
|
|
|
|
PRUint32 newDataFlags;
|
2004-02-19 02:44:03 +00:00
|
|
|
|
2004-02-20 01:53:23 +00:00
|
|
|
// if we have a fixed buffer of sufficient size, then use it. this helps
|
|
|
|
// avoid heap allocations.
|
|
|
|
if ((mFlags & F_CLASS_FIXED) && (capacity < AsFixedString(this)->mFixedCapacity))
|
|
|
|
{
|
|
|
|
newData = AsFixedString(this)->mFixedBuf;
|
|
|
|
newDataFlags = F_TERMINATED | F_FIXED;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// if we reach here then, we must allocate a new buffer. we cannot
|
|
|
|
// make use of our F_OWNED or F_FIXED buffers because they are not
|
|
|
|
// large enough.
|
|
|
|
|
2005-02-02 22:18:37 +00:00
|
|
|
nsStringBuffer* newHdr = nsStringBuffer::Alloc(storageSize);
|
2004-02-20 01:53:23 +00:00
|
|
|
if (!newHdr)
|
|
|
|
return PR_FALSE; // we are still in a consistent state
|
2004-02-19 02:44:03 +00:00
|
|
|
|
2004-02-20 01:53:23 +00:00
|
|
|
newData = (char_type*) newHdr->Data();
|
|
|
|
newDataFlags = F_TERMINATED | F_SHARED;
|
|
|
|
}
|
2004-02-19 02:44:03 +00:00
|
|
|
|
|
|
|
// save old data and flags
|
|
|
|
*oldData = mData;
|
|
|
|
*oldFlags = mFlags;
|
|
|
|
|
|
|
|
mData = newData;
|
2004-02-20 01:53:23 +00:00
|
|
|
SetDataFlags(newDataFlags);
|
2004-02-19 02:44:03 +00:00
|
|
|
|
|
|
|
// mLength does not change
|
|
|
|
|
|
|
|
// though we are not necessarily terminated at the moment, now is probably
|
|
|
|
// still the best time to set F_TERMINATED.
|
|
|
|
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsTSubstring_CharT::Finalize()
|
|
|
|
{
|
|
|
|
::ReleaseData(mData, mFlags);
|
|
|
|
// mData, mLength, and mFlags are purposefully left dangling
|
|
|
|
}
|
|
|
|
|
2005-02-16 07:20:20 +00:00
|
|
|
PRBool
|
2004-02-19 02:44:03 +00:00
|
|
|
nsTSubstring_CharT::ReplacePrep( index_type cutStart, size_type cutLen, size_type fragLen )
|
|
|
|
{
|
|
|
|
// bound cut length
|
|
|
|
cutLen = NS_MIN(cutLen, mLength - cutStart);
|
|
|
|
|
|
|
|
PRUint32 newLen = mLength - cutLen + fragLen;
|
|
|
|
|
|
|
|
char_type* oldData;
|
|
|
|
PRUint32 oldFlags;
|
|
|
|
if (!MutatePrep(newLen, &oldData, &oldFlags))
|
2005-02-16 07:20:20 +00:00
|
|
|
return PR_FALSE; // out-of-memory
|
2004-02-19 02:44:03 +00:00
|
|
|
|
|
|
|
if (oldData)
|
|
|
|
{
|
|
|
|
// determine whether or not we need to copy part of the old string
|
|
|
|
// over to the new string.
|
|
|
|
|
|
|
|
if (cutStart > 0)
|
|
|
|
{
|
|
|
|
// copy prefix from old string
|
|
|
|
char_traits::copy(mData, oldData, cutStart);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cutStart + cutLen < mLength)
|
|
|
|
{
|
|
|
|
// copy suffix from old string to new offset
|
|
|
|
size_type from = cutStart + cutLen;
|
|
|
|
size_type fromLen = mLength - from;
|
|
|
|
PRUint32 to = cutStart + fragLen;
|
|
|
|
char_traits::copy(mData + to, oldData + from, fromLen);
|
|
|
|
}
|
|
|
|
|
|
|
|
::ReleaseData(oldData, oldFlags);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// original data remains intact
|
|
|
|
|
|
|
|
// determine whether or not we need to move part of the existing string
|
|
|
|
// to make room for the requested hole.
|
|
|
|
if (fragLen != cutLen && cutStart + cutLen < mLength)
|
|
|
|
{
|
|
|
|
PRUint32 from = cutStart + cutLen;
|
|
|
|
PRUint32 fromLen = mLength - from;
|
|
|
|
PRUint32 to = cutStart + fragLen;
|
|
|
|
char_traits::move(mData + to, mData + from, fromLen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add null terminator (mutable mData always has room for the null-
|
|
|
|
// terminator).
|
|
|
|
mData[newLen] = char_type(0);
|
|
|
|
mLength = newLen;
|
2005-02-16 07:20:20 +00:00
|
|
|
|
|
|
|
return PR_TRUE;
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsTSubstring_CharT::size_type
|
|
|
|
nsTSubstring_CharT::Capacity() const
|
|
|
|
{
|
2009-11-19 01:14:29 +00:00
|
|
|
// return 0 to indicate an immutable or 0-sized buffer
|
2004-02-19 02:44:03 +00:00
|
|
|
|
|
|
|
size_type capacity;
|
|
|
|
if (mFlags & F_SHARED)
|
|
|
|
{
|
|
|
|
// if the string is readonly, then we pretend that it has no capacity.
|
2005-02-02 22:18:37 +00:00
|
|
|
nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
|
2004-02-19 02:44:03 +00:00
|
|
|
if (hdr->IsReadonly())
|
2009-11-19 01:14:29 +00:00
|
|
|
capacity = 0;
|
|
|
|
else {
|
2004-02-19 02:44:03 +00:00
|
|
|
capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
|
2009-11-19 01:14:29 +00:00
|
|
|
}
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
else if (mFlags & F_FIXED)
|
|
|
|
{
|
2004-02-20 01:53:23 +00:00
|
|
|
capacity = AsFixedString(this)->mFixedCapacity;
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
else if (mFlags & F_OWNED)
|
|
|
|
{
|
|
|
|
// we don't store the capacity of an adopted buffer because that would
|
|
|
|
// require an additional member field. the best we can do is base the
|
|
|
|
// capacity on our length. remains to be seen if this is the right
|
|
|
|
// trade-off.
|
|
|
|
capacity = mLength;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-11-19 01:14:29 +00:00
|
|
|
capacity = 0;
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return capacity;
|
|
|
|
}
|
|
|
|
|
2006-06-28 22:11:39 +00:00
|
|
|
PRBool
|
|
|
|
nsTSubstring_CharT::EnsureMutable( size_type newLen )
|
2004-02-19 02:44:03 +00:00
|
|
|
{
|
2006-06-28 22:11:39 +00:00
|
|
|
if (newLen == size_type(-1) || newLen == mLength)
|
|
|
|
{
|
|
|
|
if (mFlags & (F_FIXED | F_OWNED))
|
|
|
|
return PR_TRUE;
|
|
|
|
if ((mFlags & F_SHARED) && !nsStringBuffer::FromData(mData)->IsReadonly())
|
|
|
|
return PR_TRUE;
|
2004-02-19 02:44:03 +00:00
|
|
|
|
2006-06-28 22:11:39 +00:00
|
|
|
// promote to a shared string buffer
|
|
|
|
char_type* prevData = mData;
|
2009-08-14 11:03:05 +00:00
|
|
|
Assign(mData, mLength);
|
2006-06-28 22:11:39 +00:00
|
|
|
return mData != prevData;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SetLength(newLen);
|
|
|
|
return mLength == newLen;
|
|
|
|
}
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
2005-10-17 16:28:21 +00:00
|
|
|
// This version of Assign is optimized for single-character assignment.
|
|
|
|
void
|
|
|
|
nsTSubstring_CharT::Assign( char_type c )
|
|
|
|
{
|
|
|
|
if (ReplacePrep(0, mLength, 1))
|
|
|
|
*mData = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-02-19 02:44:03 +00:00
|
|
|
void
|
|
|
|
nsTSubstring_CharT::Assign( const char_type* data, size_type length )
|
|
|
|
{
|
|
|
|
// unfortunately, some callers pass null :-(
|
2006-03-01 00:38:15 +00:00
|
|
|
if (!data)
|
2004-02-19 02:44:03 +00:00
|
|
|
{
|
|
|
|
Truncate();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (length == size_type(-1))
|
|
|
|
length = char_traits::length(data);
|
|
|
|
|
|
|
|
if (IsDependentOn(data, data + length))
|
|
|
|
{
|
|
|
|
// take advantage of sharing here...
|
|
|
|
Assign(string_type(data, length));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-02-16 07:20:20 +00:00
|
|
|
if (ReplacePrep(0, mLength, length))
|
|
|
|
char_traits::copy(mData, data, length);
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
|
2004-06-06 02:17:00 +00:00
|
|
|
void
|
|
|
|
nsTSubstring_CharT::AssignASCII( const char* data, size_type length )
|
|
|
|
{
|
|
|
|
// A Unicode string can't depend on an ASCII string buffer,
|
|
|
|
// so this dependence check only applies to CStrings.
|
|
|
|
#ifdef CharT_is_char
|
|
|
|
if (IsDependentOn(data, data + length))
|
|
|
|
{
|
|
|
|
// take advantage of sharing here...
|
|
|
|
Assign(string_type(data, length));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-02-16 07:20:20 +00:00
|
|
|
if (ReplacePrep(0, mLength, length))
|
|
|
|
char_traits::copyASCII(mData, data, length);
|
2004-06-06 02:17:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsTSubstring_CharT::AssignASCII( const char* data )
|
|
|
|
{
|
|
|
|
AssignASCII(data, strlen(data));
|
|
|
|
}
|
|
|
|
|
2004-02-19 02:44:03 +00:00
|
|
|
void
|
|
|
|
nsTSubstring_CharT::Assign( const self_type& str )
|
|
|
|
{
|
|
|
|
// |str| could be sharable. we need to check its flags to know how to
|
|
|
|
// deal with it.
|
|
|
|
|
|
|
|
if (&str == this)
|
|
|
|
return;
|
|
|
|
|
2009-08-14 11:03:05 +00:00
|
|
|
if (!str.mLength)
|
|
|
|
{
|
|
|
|
Truncate();
|
|
|
|
mFlags |= str.mFlags & F_VOIDED;
|
|
|
|
}
|
|
|
|
else if (str.mFlags & F_SHARED)
|
2004-02-19 02:44:03 +00:00
|
|
|
{
|
|
|
|
// nice! we can avoid a string copy :-)
|
|
|
|
|
|
|
|
// |str| should be null-terminated
|
|
|
|
NS_ASSERTION(str.mFlags & F_TERMINATED, "shared, but not terminated");
|
|
|
|
|
|
|
|
::ReleaseData(mData, mFlags);
|
|
|
|
|
|
|
|
mData = str.mData;
|
|
|
|
mLength = str.mLength;
|
2004-02-20 01:53:23 +00:00
|
|
|
SetDataFlags(F_TERMINATED | F_SHARED);
|
2004-02-19 02:44:03 +00:00
|
|
|
|
|
|
|
// get an owning reference to the mData
|
2005-02-02 22:18:37 +00:00
|
|
|
nsStringBuffer::FromData(mData)->AddRef();
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// else, treat this like an ordinary assignment.
|
|
|
|
Assign(str.Data(), str.Length());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsTSubstring_CharT::Assign( const substring_tuple_type& tuple )
|
|
|
|
{
|
|
|
|
if (tuple.IsDependentOn(mData, mData + mLength))
|
|
|
|
{
|
|
|
|
// take advantage of sharing here...
|
|
|
|
Assign(string_type(tuple));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_type length = tuple.Length();
|
|
|
|
|
2009-03-04 09:50:22 +00:00
|
|
|
// don't use ReplacePrep here because it changes the length
|
|
|
|
char_type* oldData;
|
|
|
|
PRUint32 oldFlags;
|
|
|
|
if (MutatePrep(length, &oldData, &oldFlags)) {
|
|
|
|
if (oldData)
|
|
|
|
::ReleaseData(oldData, oldFlags);
|
|
|
|
|
2004-02-19 02:44:03 +00:00
|
|
|
tuple.WriteTo(mData, length);
|
2009-03-04 09:50:22 +00:00
|
|
|
mData[length] = 0;
|
|
|
|
mLength = length;
|
|
|
|
}
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsTSubstring_CharT::Adopt( char_type* data, size_type length )
|
|
|
|
{
|
|
|
|
if (data)
|
|
|
|
{
|
|
|
|
::ReleaseData(mData, mFlags);
|
|
|
|
|
|
|
|
if (length == size_type(-1))
|
|
|
|
length = char_traits::length(data);
|
|
|
|
|
|
|
|
mData = data;
|
|
|
|
mLength = length;
|
2004-02-20 01:53:23 +00:00
|
|
|
SetDataFlags(F_TERMINATED | F_OWNED);
|
2004-02-19 02:44:03 +00:00
|
|
|
|
|
|
|
STRING_STAT_INCREMENT(Adopt);
|
2006-05-12 03:36:37 +00:00
|
|
|
#ifdef NS_BUILD_REFCNT_LOGGING
|
|
|
|
// Treat this as construction of a "StringAdopt" object for leak
|
|
|
|
// tracking purposes.
|
|
|
|
NS_LogCtor(mData, "StringAdopt", 1);
|
|
|
|
#endif // NS_BUILD_REFCNT_LOGGING
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SetIsVoid(PR_TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-10-17 16:28:21 +00:00
|
|
|
// This version of Replace is optimized for single-character replacement.
|
|
|
|
void
|
|
|
|
nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, char_type c )
|
|
|
|
{
|
|
|
|
cutStart = PR_MIN(cutStart, Length());
|
|
|
|
|
|
|
|
if (ReplacePrep(cutStart, cutLength, 1))
|
|
|
|
mData[cutStart] = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-02-19 02:44:03 +00:00
|
|
|
void
|
|
|
|
nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const char_type* data, size_type length )
|
|
|
|
{
|
|
|
|
// unfortunately, some callers pass null :-(
|
|
|
|
if (!data)
|
|
|
|
{
|
|
|
|
length = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (length == size_type(-1))
|
|
|
|
length = char_traits::length(data);
|
|
|
|
|
|
|
|
if (IsDependentOn(data, data + length))
|
|
|
|
{
|
|
|
|
nsTAutoString_CharT temp(data, length);
|
|
|
|
Replace(cutStart, cutLength, temp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-06-08 22:30:11 +00:00
|
|
|
cutStart = PR_MIN(cutStart, Length());
|
|
|
|
|
2005-02-16 07:20:20 +00:00
|
|
|
if (ReplacePrep(cutStart, cutLength, length) && length > 0)
|
2004-02-19 02:44:03 +00:00
|
|
|
char_traits::copy(mData + cutStart, data, length);
|
|
|
|
}
|
|
|
|
|
2004-06-06 02:17:00 +00:00
|
|
|
void
|
|
|
|
nsTSubstring_CharT::ReplaceASCII( index_type cutStart, size_type cutLength, const char* data, size_type length )
|
|
|
|
{
|
|
|
|
if (length == size_type(-1))
|
|
|
|
length = strlen(data);
|
|
|
|
|
|
|
|
// A Unicode string can't depend on an ASCII string buffer,
|
|
|
|
// so this dependence check only applies to CStrings.
|
|
|
|
#ifdef CharT_is_char
|
|
|
|
if (IsDependentOn(data, data + length))
|
|
|
|
{
|
|
|
|
nsTAutoString_CharT temp(data, length);
|
|
|
|
Replace(cutStart, cutLength, temp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-06-08 22:30:11 +00:00
|
|
|
cutStart = PR_MIN(cutStart, Length());
|
|
|
|
|
2005-02-16 07:20:20 +00:00
|
|
|
if (ReplacePrep(cutStart, cutLength, length) && length > 0)
|
2004-06-06 02:17:00 +00:00
|
|
|
char_traits::copyASCII(mData + cutStart, data, length);
|
|
|
|
}
|
|
|
|
|
2004-02-19 02:44:03 +00:00
|
|
|
void
|
|
|
|
nsTSubstring_CharT::Replace( index_type cutStart, size_type cutLength, const substring_tuple_type& tuple )
|
|
|
|
{
|
|
|
|
if (tuple.IsDependentOn(mData, mData + mLength))
|
|
|
|
{
|
|
|
|
nsTAutoString_CharT temp(tuple);
|
|
|
|
Replace(cutStart, cutLength, temp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_type length = tuple.Length();
|
|
|
|
|
2004-06-08 22:30:11 +00:00
|
|
|
cutStart = PR_MIN(cutStart, Length());
|
|
|
|
|
2005-02-16 07:20:20 +00:00
|
|
|
if (ReplacePrep(cutStart, cutLength, length) && length > 0)
|
2004-02-19 02:44:03 +00:00
|
|
|
tuple.WriteTo(mData + cutStart, length);
|
|
|
|
}
|
|
|
|
|
2009-11-19 01:14:29 +00:00
|
|
|
PRBool
|
2004-02-19 02:44:03 +00:00
|
|
|
nsTSubstring_CharT::SetCapacity( size_type capacity )
|
|
|
|
{
|
|
|
|
// capacity does not include room for the terminating null char
|
|
|
|
|
|
|
|
// if our capacity is reduced to zero, then free our buffer.
|
|
|
|
if (capacity == 0)
|
|
|
|
{
|
|
|
|
::ReleaseData(mData, mFlags);
|
2009-07-20 20:04:44 +00:00
|
|
|
mData = char_traits::sEmptyBuffer;
|
2004-02-19 02:44:03 +00:00
|
|
|
mLength = 0;
|
2004-02-20 01:53:23 +00:00
|
|
|
SetDataFlags(F_TERMINATED);
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char_type* oldData;
|
|
|
|
PRUint32 oldFlags;
|
|
|
|
if (!MutatePrep(capacity, &oldData, &oldFlags))
|
2009-11-19 01:14:29 +00:00
|
|
|
return PR_FALSE; // out-of-memory
|
2004-02-19 02:44:03 +00:00
|
|
|
|
|
|
|
// compute new string length
|
|
|
|
size_type newLen = NS_MIN(mLength, capacity);
|
|
|
|
|
|
|
|
if (oldData)
|
|
|
|
{
|
|
|
|
// preserve old data
|
|
|
|
if (mLength > 0)
|
|
|
|
char_traits::copy(mData, oldData, newLen);
|
|
|
|
|
|
|
|
::ReleaseData(oldData, oldFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
// adjust mLength if our buffer shrunk down in size
|
|
|
|
if (newLen < mLength)
|
|
|
|
mLength = newLen;
|
|
|
|
|
|
|
|
// always null-terminate here, even if the buffer got longer. this is
|
|
|
|
// for backwards compat with the old string implementation.
|
|
|
|
mData[capacity] = char_type(0);
|
|
|
|
}
|
2009-11-19 01:14:29 +00:00
|
|
|
|
|
|
|
return PR_TRUE;
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsTSubstring_CharT::SetLength( size_type length )
|
|
|
|
{
|
2009-11-19 01:14:29 +00:00
|
|
|
if (SetCapacity(length))
|
2005-02-16 07:20:20 +00:00
|
|
|
mLength = length;
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsTSubstring_CharT::SetIsVoid( PRBool val )
|
|
|
|
{
|
|
|
|
if (val)
|
|
|
|
{
|
|
|
|
Truncate();
|
|
|
|
mFlags |= F_VOIDED;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mFlags &= ~F_VOIDED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsTSubstring_CharT::Equals( const self_type& str ) const
|
|
|
|
{
|
|
|
|
return mLength == str.mLength && char_traits::compare(mData, str.mData, mLength) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsTSubstring_CharT::Equals( const self_type& str, const comparator_type& comp ) const
|
|
|
|
{
|
2010-09-01 01:03:40 +00:00
|
|
|
return mLength == str.mLength && comp(mData, str.mData, mLength, str.mLength) == 0;
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsTSubstring_CharT::Equals( const char_type* data ) const
|
|
|
|
{
|
2004-03-09 06:23:35 +00:00
|
|
|
// unfortunately, some callers pass null :-(
|
|
|
|
if (!data)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("null data pointer");
|
|
|
|
return mLength == 0;
|
|
|
|
}
|
|
|
|
|
2004-02-25 02:08:34 +00:00
|
|
|
// XXX avoid length calculation?
|
|
|
|
size_type length = char_traits::length(data);
|
|
|
|
return mLength == length && char_traits::compare(mData, data, mLength) == 0;
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsTSubstring_CharT::Equals( const char_type* data, const comparator_type& comp ) const
|
|
|
|
{
|
2004-03-09 06:23:35 +00:00
|
|
|
// unfortunately, some callers pass null :-(
|
|
|
|
if (!data)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("null data pointer");
|
|
|
|
return mLength == 0;
|
|
|
|
}
|
|
|
|
|
2004-02-25 02:08:34 +00:00
|
|
|
// XXX avoid length calculation?
|
|
|
|
size_type length = char_traits::length(data);
|
2010-09-01 01:03:40 +00:00
|
|
|
return mLength == length && comp(mData, data, mLength, length) == 0;
|
2004-02-19 02:44:03 +00:00
|
|
|
}
|
|
|
|
|
2004-04-24 22:02:22 +00:00
|
|
|
PRBool
|
|
|
|
nsTSubstring_CharT::EqualsASCII( const char* data, size_type len ) const
|
|
|
|
{
|
|
|
|
return mLength == len && char_traits::compareASCII(mData, data, len) == 0;
|
|
|
|
}
|
|
|
|
|
2004-04-30 12:05:14 +00:00
|
|
|
PRBool
|
|
|
|
nsTSubstring_CharT::EqualsASCII( const char* data ) const
|
|
|
|
{
|
|
|
|
return char_traits::compareASCIINullTerminated(mData, mLength, data) == 0;
|
|
|
|
}
|
|
|
|
|
2004-06-06 02:17:00 +00:00
|
|
|
PRBool
|
|
|
|
nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data, size_type len ) const
|
|
|
|
{
|
|
|
|
return mLength == len && char_traits::compareLowerCaseToASCII(mData, data, len) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsTSubstring_CharT::LowerCaseEqualsASCII( const char* data ) const
|
|
|
|
{
|
|
|
|
return char_traits::compareLowerCaseToASCIINullTerminated(mData, mLength, data) == 0;
|
|
|
|
}
|
|
|
|
|
2004-02-19 02:44:03 +00:00
|
|
|
nsTSubstring_CharT::size_type
|
|
|
|
nsTSubstring_CharT::CountChar( char_type c ) const
|
|
|
|
{
|
|
|
|
const char_type *start = mData;
|
|
|
|
const char_type *end = mData + mLength;
|
|
|
|
|
|
|
|
return NS_COUNT(start, end, c);
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32
|
|
|
|
nsTSubstring_CharT::FindChar( char_type c, index_type offset ) const
|
|
|
|
{
|
|
|
|
if (offset < mLength)
|
|
|
|
{
|
|
|
|
const char_type* result = char_traits::find(mData + offset, mLength - offset, c);
|
|
|
|
if (result)
|
|
|
|
return result - mData;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
2004-11-25 07:03:20 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
nsTSubstring_CharT::StripChar( char_type aChar, PRInt32 aOffset )
|
|
|
|
{
|
|
|
|
if (mLength == 0 || aOffset >= PRInt32(mLength))
|
|
|
|
return;
|
|
|
|
|
|
|
|
EnsureMutable(); // XXX do this lazily?
|
|
|
|
|
2005-02-16 07:20:20 +00:00
|
|
|
// XXX(darin): this code should defer writing until necessary.
|
2004-11-25 07:03:20 +00:00
|
|
|
|
|
|
|
char_type* to = mData + aOffset;
|
|
|
|
char_type* from = mData + aOffset;
|
|
|
|
char_type* end = mData + mLength;
|
|
|
|
|
|
|
|
while (from < end)
|
|
|
|
{
|
|
|
|
char_type theChar = *from++;
|
|
|
|
if (aChar != theChar)
|
|
|
|
*to++ = theChar;
|
|
|
|
}
|
|
|
|
*to = char_type(0); // add the null
|
|
|
|
mLength = to - mData;
|
|
|
|
}
|
2009-11-04 21:33:23 +00:00
|
|
|
|
2010-07-21 00:11:19 +00:00
|
|
|
void
|
|
|
|
nsTSubstring_CharT::StripChars( const char_type* aChars, PRUint32 aOffset )
|
|
|
|
{
|
|
|
|
if (aOffset >= PRUint32(mLength))
|
|
|
|
return;
|
|
|
|
|
|
|
|
EnsureMutable(); // XXX do this lazily?
|
|
|
|
|
|
|
|
// XXX(darin): this code should defer writing until necessary.
|
|
|
|
|
|
|
|
char_type* to = mData + aOffset;
|
|
|
|
char_type* from = mData + aOffset;
|
|
|
|
char_type* end = mData + mLength;
|
|
|
|
|
|
|
|
while (from < end)
|
|
|
|
{
|
|
|
|
char_type theChar = *from++;
|
|
|
|
const char_type* test = aChars;
|
|
|
|
|
|
|
|
for (; *test && *test != theChar; ++test);
|
|
|
|
|
|
|
|
if (!*test) {
|
|
|
|
// Not stripped, copy this char.
|
|
|
|
*to++ = theChar;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*to = char_type(0); // add the null
|
|
|
|
mLength = to - mData;
|
|
|
|
}
|
|
|
|
|
2009-11-04 21:33:23 +00:00
|
|
|
void nsTSubstring_CharT::AppendPrintf( const char* format, ...)
|
|
|
|
{
|
|
|
|
char buf[32];
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
PRUint32 len = PR_vsnprintf(buf, sizeof(buf), format, ap);
|
|
|
|
AppendASCII(buf, len);
|
|
|
|
va_end(ap);
|
|
|
|
}
|