Make nsSharable[C]String writable by overriding appropriate methods. Ensure that it doesn't store null buffer handle pointer internally, and override get() on nsXPIDL[C]String for backwards-compatibility. Collapse nsSharedBufferHandle and nsFlexBufferHandle into nsSharedBufferHandle which knows its length but can't have a storage start distinct from its data start. Convert string users who were working around broken nsSharableString::Assign back to using it. b=104663, 100751, 74726 r=jag sr=scc
git-svn-id: svn://10.0.0.236/trunk@109114 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -25,13 +25,120 @@
|
||||
|
||||
#include "nsSharableString.h"
|
||||
// #include "nsBufferHandleUtils.h"
|
||||
#include "nsDependentSubstring.h"
|
||||
|
||||
void
|
||||
nsSharableString::assign( const abstract_string_type& aReadable )
|
||||
nsSharableString::SetCapacity( size_type aNewCapacity )
|
||||
{
|
||||
// Note: Capacity numbers do not include room for a terminating
|
||||
// NULL. However, StorageLength numbers do, since this string type
|
||||
// requires a terminating null so we include it in the storage of
|
||||
// our buffer handle.
|
||||
|
||||
if ( aNewCapacity )
|
||||
{
|
||||
// SetCapacity wouldn't be called if the caller didn't intend to
|
||||
// mutate the string.
|
||||
//
|
||||
// If the buffer is shared, we want to allocate a new buffer
|
||||
// unconditionally. If we do not, and the caller plans to do an
|
||||
// assign and a series of appends, the assign will lead to a
|
||||
// small buffer that will then be grown in steps, defeating the
|
||||
// point of |SetCapacity|.
|
||||
//
|
||||
// Since the caller is planning to mutate the string, we don't
|
||||
// want to make the new buffer any larger than the requested
|
||||
// capacity since that would be a waste of space. This means
|
||||
// that, when the string is shared, we may want to give a string
|
||||
// a buffer shorter than its current length.
|
||||
//
|
||||
// Since sharing should be transparent to the caller, we should
|
||||
// therefore *unconditionally* truncate the current length of
|
||||
// the string to the requested capacity.
|
||||
|
||||
if ( mBuffer->IsShared() )
|
||||
{
|
||||
if ( aNewCapacity > Length() )
|
||||
mBuffer = NS_AllocateContiguousHandleWithData(mBuffer.get(),
|
||||
*this, PRUint32(aNewCapacity - Length() + 1));
|
||||
else
|
||||
mBuffer = NS_AllocateContiguousHandleWithData(mBuffer.get(),
|
||||
Substring(*this, 0, aNewCapacity), PRUint32(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( aNewCapacity >= mBuffer->StorageLength() )
|
||||
{
|
||||
// This is where we implement the "exact size on assign,
|
||||
// double on fault" allocation strategy. We don't do it
|
||||
// exactly since we don't double on a fault when the
|
||||
// buffer is shared, but we'll double soon enough.
|
||||
size_type doubledCapacity = (mBuffer->StorageLength() - 1) * 2;
|
||||
if ( doubledCapacity > aNewCapacity )
|
||||
aNewCapacity = doubledCapacity;
|
||||
|
||||
// XXX We should be able to use |realloc| under certain
|
||||
// conditions (contiguous buffer handle, kIsShared
|
||||
// (,etc.)?)
|
||||
mBuffer = NS_AllocateContiguousHandleWithData(mBuffer.get(),
|
||||
*this, PRUint32(aNewCapacity - Length() + 1));
|
||||
}
|
||||
else if ( aNewCapacity < mBuffer->DataLength() )
|
||||
{
|
||||
// Ensure we always have the same effect on the length
|
||||
// whether or not the buffer is shared, as mentioned
|
||||
// above.
|
||||
mBuffer->DataEnd(mBuffer->DataStart() + aNewCapacity);
|
||||
*mBuffer->DataEnd() = char_type('\0');
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
mBuffer = GetSharedEmptyBufferHandle();
|
||||
}
|
||||
|
||||
void
|
||||
nsSharableString::SetLength( size_type aNewLength )
|
||||
{
|
||||
if ( aNewLength > mBuffer->DataLength() )
|
||||
{
|
||||
SetCapacity(aNewLength);
|
||||
mBuffer->DataEnd( mBuffer->DataStart() + aNewLength );
|
||||
}
|
||||
else
|
||||
{
|
||||
mBuffer->DataEnd( mBuffer->DataStart() + aNewLength );
|
||||
*mBuffer->DataEnd() = char_type(0); // This is needed for |Truncate|
|
||||
// callers but perhaps not
|
||||
// for callers that are
|
||||
// manipulating the
|
||||
// internals of the string.
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsSharableString::do_AssignFromReadable( const abstract_string_type& aReadable )
|
||||
{
|
||||
const shared_buffer_handle_type* handle = aReadable.GetSharedBufferHandle();
|
||||
if ( !handle )
|
||||
handle = NS_AllocateContiguousHandleWithData(handle, aReadable, PRUint32(1));
|
||||
{
|
||||
// null-check |mBuffer.get()| here only for the constructor
|
||||
// taking |const abstract_string_type&|
|
||||
if ( mBuffer.get() && !mBuffer->IsShared() &&
|
||||
mBuffer->StorageLength() > aReadable.Length() &&
|
||||
!aReadable.IsDependentOn(*this) )
|
||||
{
|
||||
abstract_string_type::const_iterator fromBegin, fromEnd;
|
||||
char_type *storage_start = mBuffer->DataStart();
|
||||
*copy_string( aReadable.BeginReading(fromBegin),
|
||||
aReadable.EndReading(fromEnd),
|
||||
storage_start ) = char_type(0);
|
||||
return; // don't want to assign to |mBuffer| below
|
||||
}
|
||||
else
|
||||
handle = NS_AllocateContiguousHandleWithData(handle,
|
||||
aReadable, PRUint32(1));
|
||||
}
|
||||
mBuffer = handle;
|
||||
}
|
||||
|
||||
@@ -42,11 +149,140 @@ nsSharableString::GetSharedBufferHandle() const
|
||||
}
|
||||
|
||||
void
|
||||
nsSharableCString::assign( const abstract_string_type& aReadable )
|
||||
nsSharableString::Adopt( char_type* aNewValue )
|
||||
{
|
||||
size_type length = nsCharTraits<char_type>::length(aNewValue);
|
||||
mBuffer = new nsSharedBufferHandle<char_type>(aNewValue, aNewValue+length,
|
||||
length, PR_FALSE);
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsSharableString::shared_buffer_handle_type*
|
||||
nsSharableString::GetSharedEmptyBufferHandle()
|
||||
{
|
||||
static shared_buffer_handle_type* sBufferHandle = nsnull;
|
||||
|
||||
if (!sBufferHandle) {
|
||||
sBufferHandle = NS_AllocateContiguousHandleWithData(sBufferHandle,
|
||||
PRUint32(1), (self_type*)nsnull);
|
||||
sBufferHandle->AcquireReference();
|
||||
}
|
||||
return sBufferHandle;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsSharableCString::SetCapacity( size_type aNewCapacity )
|
||||
{
|
||||
// Note: Capacity numbers do not include room for a terminating
|
||||
// NULL. However, StorageLength numbers do, since this string type
|
||||
// requires a terminating null so we include it in the storage of
|
||||
// our buffer handle.
|
||||
|
||||
if ( aNewCapacity )
|
||||
{
|
||||
// SetCapacity wouldn't be called if the caller didn't intend to
|
||||
// mutate the string.
|
||||
//
|
||||
// If the buffer is shared, we want to allocate a new buffer
|
||||
// unconditionally. If we do not, and the caller plans to do an
|
||||
// assign and a series of appends, the assign will lead to a
|
||||
// small buffer that will then be grown in steps, defeating the
|
||||
// point of |SetCapacity|.
|
||||
//
|
||||
// Since the caller is planning to mutate the string, we don't
|
||||
// want to make the new buffer any larger than the requested
|
||||
// capacity since that would be a waste of space. This means
|
||||
// that, when the string is shared, we may want to give a string
|
||||
// a buffer shorter than its current length.
|
||||
//
|
||||
// Since sharing should be transparent to the caller, we should
|
||||
// therefore *unconditionally* truncate the current length of
|
||||
// the string to the requested capacity.
|
||||
|
||||
if ( mBuffer->IsShared() )
|
||||
{
|
||||
if ( aNewCapacity > Length() )
|
||||
mBuffer = NS_AllocateContiguousHandleWithData(mBuffer.get(),
|
||||
*this, PRUint32(aNewCapacity - Length() + 1));
|
||||
else
|
||||
mBuffer = NS_AllocateContiguousHandleWithData(mBuffer.get(),
|
||||
Substring(*this, 0, aNewCapacity), PRUint32(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( aNewCapacity >= mBuffer->StorageLength() )
|
||||
{
|
||||
// This is where we implement the "exact size on assign,
|
||||
// double on fault" allocation strategy. We don't do it
|
||||
// exactly since we don't double on a fault when the
|
||||
// buffer is shared, but we'll double soon enough.
|
||||
size_type doubledCapacity = (mBuffer->StorageLength() - 1) * 2;
|
||||
if ( doubledCapacity > aNewCapacity )
|
||||
aNewCapacity = doubledCapacity;
|
||||
|
||||
// XXX We should be able to use |realloc| under certain
|
||||
// conditions (contiguous buffer handle, kIsShared
|
||||
// (,etc.)?)
|
||||
mBuffer = NS_AllocateContiguousHandleWithData(mBuffer.get(),
|
||||
*this, PRUint32(aNewCapacity - Length() + 1));
|
||||
}
|
||||
else if ( aNewCapacity < mBuffer->DataLength() )
|
||||
{
|
||||
// Ensure we always have the same effect on the length
|
||||
// whether or not the buffer is shared, as mentioned
|
||||
// above.
|
||||
mBuffer->DataEnd(mBuffer->DataStart() + aNewCapacity);
|
||||
*mBuffer->DataEnd() = char_type('\0');
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
mBuffer = GetSharedEmptyBufferHandle();
|
||||
}
|
||||
|
||||
void
|
||||
nsSharableCString::SetLength( size_type aNewLength )
|
||||
{
|
||||
if ( aNewLength > mBuffer->DataLength() )
|
||||
{
|
||||
SetCapacity(aNewLength);
|
||||
mBuffer->DataEnd( mBuffer->DataStart() + aNewLength );
|
||||
}
|
||||
else
|
||||
{
|
||||
mBuffer->DataEnd( mBuffer->DataStart() + aNewLength );
|
||||
*mBuffer->DataEnd() = char_type(0); // This is needed for |Truncate|
|
||||
// callers but perhaps not
|
||||
// for callers that are
|
||||
// manipulating the
|
||||
// internals of the string.
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsSharableCString::do_AssignFromReadable( const abstract_string_type& aReadable )
|
||||
{
|
||||
const shared_buffer_handle_type* handle = aReadable.GetSharedBufferHandle();
|
||||
if ( !handle )
|
||||
handle = NS_AllocateContiguousHandleWithData(handle, aReadable, PRUint32(1));
|
||||
{
|
||||
// null-check |mBuffer.get()| here only for the constructor
|
||||
// taking |const abstract_string_type&|
|
||||
if ( mBuffer.get() && !mBuffer->IsShared() &&
|
||||
mBuffer->StorageLength() > aReadable.Length() &&
|
||||
!aReadable.IsDependentOn(*this) )
|
||||
{
|
||||
abstract_string_type::const_iterator fromBegin, fromEnd;
|
||||
char_type *storage_start = mBuffer->DataStart();
|
||||
*copy_string( aReadable.BeginReading(fromBegin),
|
||||
aReadable.EndReading(fromEnd),
|
||||
storage_start ) = char_type(0);
|
||||
return; // don't want to assign to |mBuffer| below
|
||||
}
|
||||
else
|
||||
handle = NS_AllocateContiguousHandleWithData(handle,
|
||||
aReadable, PRUint32(1));
|
||||
}
|
||||
mBuffer = handle;
|
||||
}
|
||||
|
||||
@@ -56,3 +292,25 @@ nsSharableCString::GetSharedBufferHandle() const
|
||||
return mBuffer.get();
|
||||
}
|
||||
|
||||
void
|
||||
nsSharableCString::Adopt( char_type* aNewValue )
|
||||
{
|
||||
size_type length = nsCharTraits<char_type>::length(aNewValue);
|
||||
mBuffer = new nsSharedBufferHandle<char_type>(aNewValue, aNewValue+length,
|
||||
length, PR_FALSE);
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsSharableCString::shared_buffer_handle_type*
|
||||
nsSharableCString::GetSharedEmptyBufferHandle()
|
||||
{
|
||||
static shared_buffer_handle_type* sBufferHandle = nsnull;
|
||||
|
||||
if (!sBufferHandle) {
|
||||
sBufferHandle = NS_AllocateContiguousHandleWithData(sBufferHandle,
|
||||
PRUint32(1), (self_type*)nsnull);
|
||||
sBufferHandle->AcquireReference();
|
||||
}
|
||||
return sBufferHandle;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user