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

This commit is contained in:
dbaron%fas.harvard.edu 2001-11-28 05:22:57 +00:00
Родитель f7590852c8
Коммит 824e7d22ca
35 изменённых файлов: 1368 добавлений и 425 удалений

Просмотреть файл

@ -421,7 +421,7 @@ nsFingerChannel::SendRequest(nsITransport* aTransport) {
requestBuffer.Append(CRLF);
mRequest.Adopt(ToNewCString(requestBuffer));
mRequest.Assign(requestBuffer);
rv = NS_NewCharInputStream(getter_AddRefs(result), mRequest);
if (NS_FAILED(rv)) return rv;

Просмотреть файл

@ -2301,6 +2301,7 @@ protected:
NS_REINTERPRET_CAST(PRUnichar *,
JS_GetStringChars(str) +
JS_GetStringLength(str)),
JS_GetStringLength(str) + 1,
mAllocator),
mAllocator(str)
{ }

Просмотреть файл

@ -299,7 +299,7 @@ NS_IMETHODIMP nsMsgSendReport::DisplayReport(nsIPrompt *prompt, PRBool showError
nsMsgBuildErrorMessageByID(currError, errorMsg);
if (! errorMsg.IsEmpty())
currMessage.Adopt(ToNewUnicode(errorMsg));
currMessage.Assign(errorMsg);
break;
}
}
@ -365,7 +365,7 @@ NS_IMETHODIMP nsMsgSendReport::DisplayReport(nsIPrompt *prompt, PRBool showError
if (! dialogMessage.IsEmpty())
temp.Append(NS_LITERAL_STRING("\n"));
temp.Append(currMessage);
dialogMessage.Adopt(ToNewUnicode(temp));
dialogMessage.Assign(temp);
}
}
@ -378,7 +378,7 @@ NS_IMETHODIMP nsMsgSendReport::DisplayReport(nsIPrompt *prompt, PRBool showError
if (! dialogMessage.IsEmpty())
temp.Append(NS_LITERAL_STRING("\n"));
temp.Append(text1);
dialogMessage.Adopt(ToNewUnicode(temp));
dialogMessage.Assign(temp);
nsMsgAskBooleanQuestionByString(prompt, dialogMessage, &oopsGiveMeBackTheComposeWindow, dialogTitle);
if (!oopsGiveMeBackTheComposeWindow)
*_retval = NS_OK;
@ -431,7 +431,7 @@ NS_IMETHODIMP nsMsgSendReport::DisplayReport(nsIPrompt *prompt, PRBool showError
if (! dialogMessage.IsEmpty())
temp.Append(NS_LITERAL_STRING("\n"));
temp.Append(currMessage);
dialogMessage.Adopt(ToNewUnicode(temp));
dialogMessage.Assign(temp);
}
nsMsgDisplayMessageByString(prompt, dialogMessage, dialogTitle);

Просмотреть файл

@ -357,7 +357,7 @@ nsMimeHtmlDisplayEmitter::StartAttachment(const char *name, const char *contentT
if (NS_FAILED(rv))
{
unicodeHeaderValue.Adopt(ToNewUnicode(NS_ConvertUTF8toUCS2(name)));
unicodeHeaderValue.Assign(NS_ConvertUTF8toUCS2(name));
// but it's not really a failure if we didn't have a converter in the first place
if ( !mUnicodeConverter )

Просмотреть файл

@ -337,7 +337,7 @@ NS_IMETHODIMP nsIconChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports
nsCAutoString formattedFileExt;
formattedFileExt = ".";
formattedFileExt.Append(fileExt.get());
filePath.Adopt(ToNewCString(formattedFileExt));
filePath.Assign(formattedFileExt);
}
}

Просмотреть файл

@ -231,7 +231,7 @@ nsDirectoryIndexStream::Init(nsIFile* aDir)
if (NS_FAILED(rv)) return rv;
mFSCharset.Adopt(ToNewCString(tmp));
#else
mFSCharset.Adopt(nsCRT::strdup("ISO-8859-1"));
mFSCharset.Assign(NS_LITERAL_CSTRING("ISO-8859-1"));
#endif
}

Просмотреть файл

@ -421,7 +421,7 @@ nsFingerChannel::SendRequest(nsITransport* aTransport) {
requestBuffer.Append(CRLF);
mRequest.Adopt(ToNewCString(requestBuffer));
mRequest.Assign(requestBuffer);
rv = NS_NewCharInputStream(getter_AddRefs(result), mRequest);
if (NS_FAILED(rv)) return rv;

Просмотреть файл

@ -1015,7 +1015,7 @@ nsHttpHandler::InitUserAgentComponents()
buf = (char*)name.sysname;
buf += ' ';
buf += (char*)name.machine;
mOscpu.Adopt(ToNewCString(buf));
mOscpu.Assign(buf);
}
#elif defined (XP_MAC)
long version;

Просмотреть файл

@ -44,16 +44,29 @@ class NS_COM nsAFlatString
: public nsASingleFragmentString
{
public:
// don't really want this to be virtual, and won't after |obsolete_nsString| is really dead
virtual const char_type* get() const { const char_type* temp; return BeginReading(temp); }
// don't really want this to be virtual, and won't after
// |obsolete_nsString| is really dead and |nsXPIDLString| works
// differently
virtual const char_type* get() const
{
const char_type* temp;
return BeginReading(temp);
}
};
class NS_COM nsAFlatCString
: public nsASingleFragmentCString
{
public:
// don't really want this to be virtual, and won't after |obsolete_nsCString| is really dead
virtual const char_type* get() const { const char_type* temp; return BeginReading(temp); }
// don't really want this to be virtual, and won't after
// |obsolete_nsCString| is really dead and |nsXPIDLCString|
// works differently
virtual const char_type* get() const
{
const char_type* temp;
return BeginReading(temp);
}
};
#endif /* !defined(nsAFlatString_h___) */

Просмотреть файл

@ -112,9 +112,19 @@ class NS_COM nsAString
virtual void SetIsVoid( PRBool );
/**
* |CharAt|, |operator[]|, |First()|, and |Last()| are not guaranteed to be constant-time operations.
* These signatures should be pushed down into interfaces that guarantee flat allocation.
* Clients at _this_ level should always use iterators.
* |CharAt|, |operator[]|, |First()|, and |Last()| are not
* guaranteed to be constant-time operations. These signatures
* should be pushed down into interfaces that guarantee flat
* allocation. (Right now |First| and |Last| are here but
* |CharAt| and |operator[]| are on |nsASingleFragmentString|.)
*
* Clients at _this_ level should always use iterators. For
* example, to see if the n-th character is a '-':
*
* nsAString::const_iterator iter;
* if ( *myString.BeginReading(iter).advance(n) == PRUnichar('-') )
* // do something...
*
*/
char_type First() const;
char_type Last() const;
@ -151,8 +161,8 @@ class NS_COM nsAString
// RFindCharInSet( ... ) const;
/**
* |SetCapacity| is not required to do anything; however, it can be used
* as a hint to the implementation to reduce allocations.
* |SetCapacity| is not required to do anything; however, it can be
* used as a hint to the implementation to reduce allocations.
* |SetCapacity(0)| is a suggestion to discard all associated storage.
*/
virtual void SetCapacity( size_type ) { }
@ -162,13 +172,20 @@ class NS_COM nsAString
* 1) to |Cut| a suffix of the string;
* 2) to prepare to |Append| or move characters around.
*
* External callers are not allowed to use |SetLength| is this latter capacity.
* Should this really be a public operation?
* Additionally, your implementation of |SetLength| need not satisfy (2) if and only if you
* override the |do_...| routines to not need this facility.
* External callers are not allowed to use |SetLength| is this
* latter capacity, and should prefer |Truncate| for the former.
* In other words, |SetLength| is deprecated for all use outside
* of the string library and the internal use may at some point
* be replaced as well.
*
* This distinction makes me think the two different uses should be split into
* two distinct functions.
* Should this really be a public operation?
*
* Additionally, your implementation of |SetLength| need not
* satisfy (2) if and only if you override the |do_...| routines
* to not need this facility.
*
* This distinction makes me think the two different uses should
* be split into two distinct functions.
*/
virtual void SetLength( size_type ) { }
@ -176,10 +193,10 @@ class NS_COM nsAString
void
Truncate( size_type aNewLength=0 )
{
NS_ASSERTION(aNewLength<=this->Length(), "Can't use |Truncate()| to make a string longer.");
NS_ASSERTION(aNewLength <= this->Length(),
"Can't use |Truncate()| to make a string longer.");
if ( aNewLength < this->Length() )
SetLength(aNewLength);
SetLength(aNewLength);
}
@ -201,9 +218,13 @@ class NS_COM nsAString
//
// |Assign()|, |operator=()|
//
/**
* |Assign()| and |operator=()| make |this| equivalent to the
* string or buffer given as an argument. If possible, they do
* this by sharing a refcounted buffer (see
* |nsSharableC?String|, |nsXPIDLC?String|. If not, they copy
* the buffer into their own buffer.
*/
void Assign( const self_type& aReadable ) { AssignFromReadable(aReadable); }
inline void Assign( const promise_type& aReadable );
@ -366,9 +387,19 @@ class NS_COM nsACString
virtual void SetIsVoid( PRBool );
/**
* |CharAt|, |operator[]|, |First()|, and |Last()| are not guaranteed to be constant-time operations.
* These signatures should be pushed down into interfaces that guarantee flat allocation.
* Clients at _this_ level should always use iterators.
* |CharAt|, |operator[]|, |First()|, and |Last()| are not
* guaranteed to be constant-time operations. These signatures
* should be pushed down into interfaces that guarantee flat
* allocation. (Right now |First| and |Last| are here but
* |CharAt| and |operator[]| are on |nsASingleFragmentString|.)
*
* Clients at _this_ level should always use iterators. For
* example, to see if the n-th character is a '-':
*
* nsACString::const_iterator iter;
* if ( *myString.BeginReading(iter).advance(n) == '-' )
* // do something...
*
*/
char_type First() const;
char_type Last() const;

Просмотреть файл

@ -31,9 +31,6 @@
#include "nsStringDefines.h"
#endif
#include <stddef.h>
// for |ptrdiff_t|
#include "prtypes.h"
// for |PRBool|
@ -49,32 +46,39 @@
#pragma warning( disable: 4251 )
#endif
/**
The classes in this file are collectively called `buffer handles'.
All buffer handles begin with a pointer-tuple that delimits the useful content of a
hunk of string. A buffer handle that points to a sharable hunk of string data
additionally has a field which multiplexes some flags and a reference count.
ns[Const]BufferHandle nsSharedBufferHandle mFlexBufferHandle
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| mDataStart | | mDataStart | | mDataStart |
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| mDataEnd | | mDataEnd | | mDataEnd |
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| mFlags | | mFlags |
+-----+-----+-----+-----+ +-----+-----+-----+-----+
. mAllocator . | mStorageStart |
......................... +-----+-----+-----+-----+
| mStorageEnd |
+-----+-----+-----+-----+
. mAllocator .
.........................
Given only a |ns[Const]BufferHandle|, there is no legal way to tell if it is sharable.
In all cases, the data might immediately follow the handle in the same allocated block.
From the |mFlags| field, you can tell exactly what configuration of a handle you
actually have.
/*
* The classes in this file are collectively called `buffer handles'.
* All buffer handles begin with a pointer-tuple that delimits the
* useful content of a hunk of string. A buffer handle that points to
* a sharable hunk of string data additionally has a field which
* multiplexes some flags and a reference count.
*
*
* ns[Const]BufferHandle nsSharedBufferHandle
* +-----+-----+-----+-----+ +-----+-----+-----+-----+
* | mDataStart | | mDataStart |
* +-----+-----+-----+-----+ +-----+-----+-----+-----+
* | mDataEnd | | mDataEnd |
* +-----+-----+-----+-----+ +-----+-----+-----+-----+
* | mFlags |
* +-----+-----+-----+-----+
* | mStorageLength |
* +-----+-----+-----+-----+
* . mAllocator .
* .........................
*
* Given only a |ns[Const]BufferHandle|, there is no legal way to tell
* if it is sharable. In all cases, the data might immediately follow
* the handle in the same allocated block. From the |mFlags| field,
* you can tell exactly what configuration of a handle you actually
* have.
*
* An |nsSharedBufferHandle| has the limitation that its |mDataStart|
* must also be the beginning of the allocated storage. However,
* allowing otherwise would introduce significant additional
* complexity for a feature that would better be handled by allowing
* an owninng substring string class that owned a reference to the
* buffer handle.
*/
@ -85,6 +89,8 @@ template <class CharT>
class nsBufferHandle
{
public:
typedef PRUint32 size_type;
nsBufferHandle() { }
nsBufferHandle( CharT* aDataStart, CharT* aDataEnd ) : mDataStart(aDataStart), mDataEnd(aDataEnd) { }
@ -96,7 +102,7 @@ class nsBufferHandle
CharT* DataEnd() { return mDataEnd; }
const CharT* DataEnd() const { return mDataEnd; }
ptrdiff_t DataLength() const { return mDataEnd - mDataStart; }
size_type DataLength() const { return mDataEnd - mDataStart; }
protected:
CharT* mDataStart;
@ -107,6 +113,8 @@ template <class CharT>
class nsConstBufferHandle
{
public:
typedef PRUint32 size_type;
nsConstBufferHandle() { }
nsConstBufferHandle( const CharT* aDataStart, const CharT* aDataEnd ) : mDataStart(aDataStart), mDataEnd(aDataEnd) { }
@ -116,7 +124,7 @@ class nsConstBufferHandle
void DataEnd( const CharT* aNewDataEnd ) { mDataEnd = aNewDataEnd; }
const CharT* DataEnd() const { return mDataEnd; }
ptrdiff_t DataLength() const { return mDataEnd - mDataStart; }
size_type DataLength() const { return mDataEnd - mDataStart; }
protected:
const CharT* mDataStart;
@ -175,13 +183,15 @@ template <class CharT>
class nsSharedBufferHandle
: public nsBufferHandle<CharT>
{
public:
typedef PRUint32 size_type;
protected:
enum
{
kIsShared = 0x08000000, // one reason _not_ to set this is for a stack based handle that wants to express `NULL'-ness et al
kIsSingleAllocationWithBuffer = 0x04000000, // handle and buffer are one piece, no separate deallocation is possible for the buffer
kIsStorageDefinedSeparately = 0x02000000, // i.e., we're using the ``flex'' structure defined below
kIsUserAllocator = 0x01000000, // can't |delete|, call a hook instead
kIsShared = 0x01000000, // one reason _not_ to set this is for a stack based handle that wants to express `NULL'-ness et al
kIsSingleAllocationWithBuffer = 0x02000000, // handle and buffer are one piece, no separate deallocation is possible for the buffer
kIsUserAllocator = 0x04000000, // can't |delete|, call a hook instead
// the following flags are opaque to the string library itself
kIsNULL = 0x80000000, // the most common request of external clients is a scheme by which they can express `NULL'-ness
@ -192,14 +202,9 @@ class nsSharedBufferHandle
};
public:
nsSharedBufferHandle( CharT* aDataStart, CharT* aDataEnd )
: nsBufferHandle<CharT>(aDataStart, aDataEnd)
{
mFlags = kIsShared;
}
nsSharedBufferHandle( CharT* aDataStart, CharT* aDataEnd, CharT*, CharT*, PRBool isSingleAllocation )
: nsBufferHandle<CharT>(aDataStart, aDataEnd)
nsSharedBufferHandle( CharT* aDataStart, CharT* aDataEnd, size_type aStorageLength, PRBool isSingleAllocation )
: nsBufferHandle<CharT>(aDataStart, aDataEnd),
mStorageLength(aStorageLength)
{
mFlags = kIsShared;
if ( isSingleAllocation )
@ -230,6 +235,23 @@ class nsSharedBufferHandle
return get_refcount() != 0;
}
PRBool
IsShared() const
{
return get_refcount() > 1;
}
void StorageLength( size_type aNewStorageLength )
{
mStorageLength = aNewStorageLength;
}
size_type
StorageLength() const
{
return mStorageLength;
}
PRUint32
GetImplementationFlags() const
{
@ -244,6 +266,7 @@ class nsSharedBufferHandle
protected:
PRUint32 mFlags;
size_type mStorageLength;
PRUint32
get_refcount() const
@ -264,61 +287,17 @@ class nsSharedBufferHandle
};
template <class CharT>
class nsFlexBufferHandle
: public nsSharedBufferHandle<CharT>
{
public:
nsFlexBufferHandle( CharT* aDataStart, CharT* aDataEnd, CharT* aStorageStart, CharT* aStorageEnd )
: nsSharedBufferHandle<CharT>(aDataStart, aDataEnd),
mStorageStart(aStorageStart),
mStorageEnd(aStorageEnd)
{
this->mFlags |= this->kIsStorageDefinedSeparately;
}
void StorageStart( CharT* aNewStorageStart ) { mStorageStart = aNewStorageStart; }
CharT* StorageStart() { return mStorageStart; }
const CharT* StorageStart() const { return mStorageStart; }
void StorageEnd( CharT* aNewStorageEnd ) { mStorageEnd = aNewStorageEnd; }
CharT* StorageEnd() { return mStorageEnd; }
const CharT* StorageEnd() const { return mStorageEnd; }
ptrdiff_t StorageLength() const { return mStorageEnd - mStorageStart; }
protected:
CharT* mStorageStart;
CharT* mStorageEnd;
};
template <class CharT>
class nsSharedBufferHandleWithAllocator
: public nsSharedBufferHandle<CharT>
{
public:
nsSharedBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd, nsStringAllocator<CharT>& aAllocator )
: nsSharedBufferHandle<CharT>(aDataStart, aDataEnd),
mAllocator(aAllocator)
{
this->mFlags |= this->kIsUserAllocator;
}
// why is this needed again?
typedef PRUint32 size_type;
nsStringAllocator<CharT>& get_allocator() const { return mAllocator; }
protected:
nsStringAllocator<CharT>& mAllocator;
};
template <class CharT>
class nsFlexBufferHandleWithAllocator
: public nsFlexBufferHandle<CharT>
{
public:
nsFlexBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd,
CharT* aStorageStart, CharT* aStorageEnd,
nsStringAllocator<CharT>& aAllocator )
: nsFlexBufferHandle<CharT>(aDataStart, aDataEnd, aStorageStart, aStorageEnd),
nsSharedBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd, size_type aStorageLength, nsStringAllocator<CharT>& aAllocator )
: nsSharedBufferHandle<CharT>(aDataStart, aDataEnd,
aStorageLength, PR_FALSE),
mAllocator(aAllocator)
{
this->mFlags |= this->kIsUserAllocator;
@ -338,10 +317,7 @@ nsSharedBufferHandle<CharT>::get_allocator() const
{
if ( mFlags & kIsUserAllocator )
{
if ( mFlags & kIsStorageDefinedSeparately )
return NS_REINTERPRET_CAST(const nsFlexBufferHandleWithAllocator<CharT>*, this)->get_allocator();
else
return NS_REINTERPRET_CAST(const nsSharedBufferHandleWithAllocator<CharT>*, this)->get_allocator();
return NS_REINTERPRET_CAST(const nsSharedBufferHandleWithAllocator<CharT>*, this)->get_allocator();
}
return nsStringAllocatorTraits<CharT>::global_string_allocator();
@ -357,9 +333,6 @@ nsSharedBufferHandle<CharT>::~nsSharedBufferHandle()
if ( !(mFlags & kIsSingleAllocationWithBuffer) )
{
CharT* string_storage = this->mDataStart;
if ( mFlags & kIsStorageDefinedSeparately )
string_storage = NS_REINTERPRET_CAST(nsFlexBufferHandle<CharT>*, this)->StorageStart();
get_allocator().Deallocate(string_storage);
}
}

Просмотреть файл

@ -169,7 +169,7 @@ NS_AllocateContiguousHandleWithData( const HandleT* aDummyHandlePtr, PRUint32 aA
if ( aAdditionalCapacity > 0 )
*toBegin = char_type(0);
result = new (handle_ptr) HandleT(data_start_ptr, data_end_ptr, data_start_ptr, buffer_end_ptr, PR_TRUE);
result = new (handle_ptr) HandleT(data_start_ptr, data_end_ptr, buffer_end_ptr - data_start_ptr, PR_TRUE);
}
return result;

Просмотреть файл

@ -35,10 +35,13 @@
#include "nsBufferHandleUtils.h"
#endif
//-------1---------2---------3---------4---------5---------6---------7---------8
/**
* Not yet ready for non-|const| access
* |nsSharableC?String| is the basic copy-on-write string class. It
* implements |nsAFlatC?String|, so it always has a single,
* null-terminated, buffer. It can accept assignment in two ways:
* 1. |Assign| (which is equivalent to |operator=|), which shares
* the buffer if possible and copies the buffer otherwise, and
* 2. |Adopt|, which takes over ownership of a raw buffer.
*/
class NS_COM nsSharableString
@ -48,22 +51,53 @@ class NS_COM nsSharableString
typedef nsSharableString self_type;
public:
nsSharableString() { }
nsSharableString() : mBuffer(GetSharedEmptyBufferHandle()) { }
nsSharableString( const self_type& aOther ) : mBuffer(aOther.mBuffer) { }
explicit nsSharableString( const abstract_string_type& aReadable ) { assign(aReadable); }
explicit nsSharableString( const shared_buffer_handle_type* aHandle ) : mBuffer(aHandle) { }
self_type&
operator=( const abstract_string_type& aReadable )
explicit nsSharableString( const abstract_string_type& aReadable )
{
assign(aReadable);
// can call |do_AssignFromReadable| directly since we know
// |&aReadable != this|.
do_AssignFromReadable(aReadable);
}
explicit nsSharableString( const shared_buffer_handle_type* aHandle )
: mBuffer(aHandle)
{
NS_ASSERTION(aHandle, "null handle");
}
// |operator=| does not inherit, so we must provide it again
self_type& operator=( const abstract_string_type& aReadable )
{
Assign(aReadable);
return *this;
}
/**
* The |Adopt| method assigns a raw, null-terminated, character
* buffer to this string object by transferring ownership of that
* buffer to the string. No copying occurs.
*
* XXX Add a second |Adopt| method that takes a length for clients
* that already know the length.
*/
void Adopt( char_type* aNewValue );
/**
* These overrides of |SetCapacity|, |SetLength|,
* |do_AssignFromReadable|, and |GetSharedBufferHandle|, all
* virtual methods on |nsAString|, allow |nsSharableString| to do
* copy-on-write buffer sharing.
*/
public:
virtual void SetCapacity( size_type aNewCapacity );
virtual void SetLength( size_type aNewLength );
protected:
void assign( const abstract_string_type& );
virtual void do_AssignFromReadable( const abstract_string_type& aReadable );
virtual const shared_buffer_handle_type* GetSharedBufferHandle() const;
protected:
static shared_buffer_handle_type* GetSharedEmptyBufferHandle();
protected:
nsAutoBufferHandle<char_type> mBuffer;
};
@ -73,25 +107,56 @@ class NS_COM nsSharableCString
: public nsAFlatCString
{
public:
typedef nsSharableCString self_type;
typedef nsSharableCString self_type;
public:
nsSharableCString() { }
nsSharableCString() : mBuffer(GetSharedEmptyBufferHandle()) { }
nsSharableCString( const self_type& aOther ) : mBuffer(aOther.mBuffer) { }
explicit nsSharableCString( const abstract_string_type& aReadable ) { assign(aReadable); }
explicit nsSharableCString( const shared_buffer_handle_type* aHandle ) : mBuffer(aHandle) { }
self_type&
operator=( const abstract_string_type& aReadable )
explicit nsSharableCString( const abstract_string_type& aReadable )
{
assign(aReadable);
// can call |do_AssignFromReadable| directly since we know
// |&aReadable != this|.
do_AssignFromReadable(aReadable);
}
explicit nsSharableCString( const shared_buffer_handle_type* aHandle )
: mBuffer(aHandle)
{
NS_ASSERTION(aHandle, "null handle");
}
// |operator=| does not inherit, so we must provide it again
self_type& operator=( const abstract_string_type& aReadable )
{
Assign(aReadable);
return *this;
}
/**
* The |Adopt| method assigns a raw, null-terminated, character
* buffer to this string object by transferring ownership of that
* buffer to the string. No copying occurs.
*
* XXX Add a second |Adopt| method that takes a length for clients
* that already know the length.
*/
void Adopt( char_type* aNewValue );
/**
* These overrides of |SetCapacity|, |SetLength|,
* |do_AssignFromReadable|, and |GetSharedBufferHandle|, all
* virtual methods on |nsAString|, allow |nsSharableCString| to do
* copy-on-write buffer sharing.
*/
public:
virtual void SetCapacity( size_type aNewCapacity );
virtual void SetLength( size_type aNewLength );
protected:
void assign( const abstract_string_type& );
virtual void do_AssignFromReadable( const abstract_string_type& aReadable );
virtual const shared_buffer_handle_type* GetSharedBufferHandle() const;
protected:
static shared_buffer_handle_type* GetSharedEmptyBufferHandle();
protected:
nsAutoBufferHandle<char_type> mBuffer;
};

Просмотреть файл

@ -62,14 +62,12 @@ class NS_COM nsSharedBufferList
public:
class Buffer
: public nsFlexBufferHandle<PRUnichar>
: public nsSharedBufferHandle<PRUnichar>
{
public:
Buffer( PRUnichar* aDataStart, PRUnichar* aDataEnd, PRUnichar* aStorageStart, PRUnichar* aStorageEnd, PRBool aIsSingleAllocation=PR_FALSE )
: nsFlexBufferHandle<PRUnichar>(aDataStart, aDataEnd, aStorageStart, aStorageEnd)
Buffer( PRUnichar* aDataStart, PRUnichar* aDataEnd, size_type aStorageLength, PRBool aIsSingleAllocation=PR_FALSE )
: nsSharedBufferHandle<PRUnichar>(aDataStart, aDataEnd, aStorageLength, aIsSingleAllocation)
{
if ( aIsSingleAllocation )
this->mFlags |= this->kIsSingleAllocationWithBuffer;
}
/**
@ -198,7 +196,7 @@ class NS_COM nsSharedBufferList
Buffer*
NewWrappingBuffer( PRUnichar* aDataStart, PRUnichar* aDataEnd, PRUnichar* aStorageEnd )
{
return new Buffer(aDataStart, aDataEnd, aDataStart, aStorageEnd);
return new Buffer(aDataStart, aDataEnd, aStorageEnd - aDataStart);
}
void DiscardSuffix( PRUint32 );

Просмотреть файл

@ -32,9 +32,66 @@
#include <stdio.h>
#endif
/**
* Interface documentation:
*
* |nsXPIDLC?String| differs from |nsSharableC?String| in that one can
* assign into it from an XPCOM getter that fills in a |char_type**|
* with a pointer to a buffer whose ownership is to be transferred to
* the caller.
*
* Consider the following interface:
*
* interface nsIFoo {
* attribute string Bar;
* };
*
* This will generate the following C++ header file:
*
* class nsIFoo {
* NS_IMETHOD SetBar(const PRUnichar* aValue);
* NS_IMETHOD GetBar(PRUnichar* *aValue);
* };
*
* The GetBar() method will allocate a copy of the nsIFoo object's
* "bar" attribute, and leave you to deal with freeing it:
*
* nsIFoo* aFoo; // assume we get this somehow
* PRUnichar* bar;
* aFoo->GetFoo(&bar);
* do_something_with_buffer(bar);
* nsMemory::Free(bar);
*
* This makes your life harder, because you need to convolute your
* code to ensure that you don't leak `bar'.
*
* Enter nsXPIDLString, which manages the ownership of the allocated
* string, and automatically destroys it when the nsXPIDLString goes
* out of scope:
*
* nsIFoo* aFoo; // assume we get this somehow
* nsXPIDLString bar;
* aFoo->GetFoo( getter_Copies(bar) );
* do_something_with_buffer(bar.get());
* // no need to remember to nsMemory::Free().
*
* Like nsCOMPtr, nsXPIDLString uses some syntactic sugar to make it
* painfully clear exactly what the code expects. You need to wrap an
* nsXPIDLString object with `getter_Copies()' before passing it to a
* getter: these tell the nsXPIDLString how ownership is being
* handled.
*
* In the case of `getter_Copies()', the callee is allocating a copy
* (which is usually the case). In the case where the callee is
* returning a const reference to `the real deal' (this can be done
* using the [shared] attribute in XPIDL) you can just use a
* |const char*| or |const PRUnichar*|.
*/
/**
* Implementation notes:
*
* |nsXPIDLC?String| extends |nsSharableC?String| with the ability
* to defer calculation of its length. This is crucial to allowing
* the |getter_Copies| assignment behavior.
@ -45,7 +102,7 @@
* the operation on which all other flat string operations are based.
* A valid handle will either have all |NULL| or all non-|NULL|
* pointers. After use as an `out' string pointer parameter, an
* |nsXPIDLC?String|'s handle will have a non-|NULL| storage start, but
* |nsXPIDLC?String|'s handle will have a non-|NULL| data start, but
* all other members will be |NULL|. This is the signal that the
* length needs to be recalculated. |GetSharedBufferHandle| detects
* this situation and repairs it.
@ -54,7 +111,8 @@
* the string before it's destruction. In this case, because the start of
* storage is known, storage can still be freed in the usual way.
*
* An |nsXPIDLC?String| is now a sharable object, just like |nsSharableC?String|.
* An |nsXPIDLC?String| is now a sharable object, just like
* |nsSharableC?String|.
* This simple implementation always allocates an intermediary handle
* object. This cost might turn out to be a burden, it's something we'll
* want to measure. A couple of optimizations spring to mind if allocation
@ -75,6 +133,7 @@ class NS_COM nsXPIDLString
public:
nsXPIDLString()
: nsSharableString(GetSharedEmptyBufferHandle())
{
#if DEBUG_STRING_STATS
++sCreatedCount;
@ -84,7 +143,7 @@ class NS_COM nsXPIDLString
}
nsXPIDLString( const self_type& aString )
: nsSharableString(aString.GetSharedBufferHandle())
: nsSharableString(aString.mBuffer.get())
// copy-constructor required (or else C++ generates one for us)
{
#if DEBUG_STRING_STATS
@ -94,6 +153,16 @@ class NS_COM nsXPIDLString
#endif
}
explicit nsXPIDLString( const abstract_string_type& aReadable )
: nsSharableString(aReadable)
{
#if DEBUG_STRING_STATS
++sCreatedCount;
if ( ++sAliveCount > sHighWaterCount )
sHighWaterCount = sAliveCount;
#endif
}
#if DEBUG_STRING_STATS
~nsXPIDLString()
{
@ -102,22 +171,26 @@ class NS_COM nsXPIDLString
#endif
self_type&
operator=( const self_type& rhs )
// copy-assignment operator required (or else C++ generates one for us)
operator=( const abstract_string_type& aReadable )
{
// self-assignment is handled by the underlying |nsAutoBufferHandle|
mBuffer = rhs.GetSharedBufferHandle();
Assign(aReadable);
return *this;
}
/**
* This is an override of a non-virtual function on
* |nsSharableString|. This override is not necessary, but it can
* improve performance in the case where we know we have an
* |nsXPIDLString|.
*/
void Adopt( char_type* aNewValue ) { *PrepareForUseAsOutParam() = aNewValue; }
// overridden to make getter_Copies mechanism work
const char_type* get() const
virtual const char_type* get() const
{
const buffer_handle_type* handle = GetBufferHandle();
// NS_ASSERTION(handle, "handle is null!");
return handle ? handle->DataStart() : 0;
return (mBuffer.get() != GetSharedEmptyBufferHandle())
? mBuffer->DataStart()
: 0;
}
// deprecated, to be eliminated
@ -150,6 +223,8 @@ class NS_COM nsXPIDLString
char_type** PrepareForUseAsOutParam();
static shared_buffer_handle_type* GetSharedEmptyBufferHandle();
#if DEBUG_STRING_STATS
static size_t sCreatedCount; // total number of |nsXPIDLString|s ever created
static size_t sAliveCount; // total number of |nsXPIDLStrings|s alive right now
@ -180,6 +255,7 @@ class NS_COM nsXPIDLCString
public:
nsXPIDLCString()
: nsSharableCString(GetSharedEmptyBufferHandle())
{
#if DEBUG_STRING_STATS
++sCreatedCount;
@ -189,7 +265,7 @@ class NS_COM nsXPIDLCString
}
nsXPIDLCString( const self_type& aString )
: nsSharableCString(aString.GetSharedBufferHandle())
: nsSharableCString(aString.mBuffer.get())
// copy-constructor required (or else C++ generates one for us)
{
#if DEBUG_STRING_STATS
@ -207,22 +283,26 @@ class NS_COM nsXPIDLCString
#endif
self_type&
operator=( const self_type& rhs )
// copy-assignment operator required (or else C++ generates one for us)
operator=( const abstract_string_type& aReadable )
{
// self-assignment is handled by the underlying |nsAutoBufferHandle|
mBuffer = rhs.GetSharedBufferHandle();
Assign(aReadable);
return *this;
}
/**
* This is an override of a non-virtual function on
* |nsSharableCString|. This override is not necessary, but it can
* improve performance in the case where we know we have an
* |nsXPIDLCString|.
*/
void Adopt( char_type* aNewValue ) { *PrepareForUseAsOutParam() = aNewValue; }
// overridden to make getter_Copies mechanism work
const char_type* get() const
virtual const char_type* get() const
{
const buffer_handle_type* handle = GetBufferHandle();
// NS_ASSERTION(handle, "handle is null!");
return handle ? handle->DataStart() : 0;
return (mBuffer.get() != GetSharedEmptyBufferHandle())
? mBuffer->DataStart()
: 0;
}
// deprecated, to be eliminated
@ -255,6 +335,8 @@ class NS_COM nsXPIDLCString
char_type** PrepareForUseAsOutParam();
static shared_buffer_handle_type* GetSharedEmptyBufferHandle();
#if DEBUG_STRING_STATS
static size_t sCreatedCount; // total number of |nsXPIDLCString|s ever created
static size_t sAliveCount; // total number of |nsXPIDLCStrings|s alive right now

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -98,15 +98,20 @@ nsSharedBufferList::SplitBuffer( const Position& aSplitPosition, SplitDispositio
NS_ASSERTION(bufferToSplit, "bufferToSplit");
ptrdiff_t splitOffset = aSplitPosition.mPosInBuffer - bufferToSplit->DataStart();
Buffer::size_type splitOffset =
aSplitPosition.mPosInBuffer - bufferToSplit->DataStart();
NS_ASSERTION(0 <= splitOffset && splitOffset <= bufferToSplit->DataLength(), "|splitOffset| within buffer");
NS_ASSERTION(aSplitPosition.mPosInBuffer >= bufferToSplit->DataStart() &&
splitOffset <= bufferToSplit->DataLength(),
"|splitOffset| within buffer");
// if the caller specifically asked to split off the right side of the buffer-to-be-split
// or else if they asked for the minimum amount of work, and that turned out to be the right side...
// if the caller specifically asked to split off the right side of
// the buffer-to-be-split or else if they asked for the minimum
// amount of work, and that turned out to be the right side...
ptrdiff_t savedLength = mTotalDataLength;
if ( aSplitDirection==kSplitCopyRightData ||
( aSplitDirection==kSplitCopyLeastData && ((bufferToSplit->DataLength() >> 1) <= splitOffset) ) )
( aSplitDirection==kSplitCopyLeastData &&
((bufferToSplit->DataLength() >> 1) <= splitOffset) ) )
{
// ...then allocate a new buffer initializing it by copying all the data _after_ the split in the source buffer (i.e., `split right')
Buffer* new_buffer = NewSingleAllocationBuffer(bufferToSplit->DataStart()+splitOffset, PRUint32(bufferToSplit->DataLength()-splitOffset));

Просмотреть файл

@ -125,7 +125,7 @@ AllocateContiguousHandleWithData( Buffer_ptr aDummyHandlePtr, const nsAString& a
nsAString::const_iterator fromBegin, fromEnd;
PRUnichar* toBegin = string_start_ptr;
copy_string(aDataSource.BeginReading(fromBegin), aDataSource.EndReading(fromEnd), toBegin);
result = new (handle_ptr) nsSharedBufferList::Buffer(string_start_ptr, string_end_ptr, string_start_ptr, string_end_ptr+1, PR_TRUE);
result = new (handle_ptr) nsSharedBufferList::Buffer(string_start_ptr, string_end_ptr, string_end_ptr-string_start_ptr+1, PR_TRUE);
}
return result;
@ -282,7 +282,8 @@ nsSlidingString::nsSlidingString( PRUnichar* aStorageStart, PRUnichar* aDataEnd,
void
nsSlidingString::AppendBuffer( PRUnichar* aStorageStart, PRUnichar* aDataEnd, PRUnichar* aStorageEnd )
{
Buffer* new_buffer = new Buffer(aStorageStart, aDataEnd, aStorageStart, aStorageEnd);
Buffer* new_buffer =
new Buffer(aStorageStart, aDataEnd, aStorageEnd - aStorageStart);
Buffer* old_last_buffer = mBufferList->GetLastBuffer();
mBufferList->LinkBuffer(old_last_buffer, new_buffer, 0);
mLength += new_buffer->DataLength();

Просмотреть файл

@ -21,6 +21,16 @@
* Scott Collins <scc@mozilla.org> (original author)
*/
// XXX TODO:
//
// nsSharableString will need to be careful to use GetSharedBufferHandle
// where necessary so that an nsXPIDLString can be passed as an
// nsSharableString&. We must be careful to ensure that an
// nsXPIDLString can be used as an nsSharableString& and that an
// nsXPIDLString and an nsSharableString can share buffers created by
// the other (and buffers created by nsSharableString::Adopt rather than
// its other assignment methods).
#include "nsXPIDLString.h"
#if DEBUG_STRING_STATS
@ -40,12 +50,12 @@ size_t nsXPIDLCString::sShareCount = 0;
template <class CharT>
class nsImportedStringHandle
: public nsFlexBufferHandle<CharT>
: public nsSharedBufferHandle<CharT>
{
public:
nsImportedStringHandle() : nsFlexBufferHandle<CharT>(0, 0, 0, 0) { }
nsImportedStringHandle() : nsSharedBufferHandle<CharT>(0, 0, 0, PR_FALSE) { }
CharT** AddressOfStorageStart() { return &(this->mStorageStart); }
CharT** AddressOfStorageStart() { return &(this->mDataStart); }
void RecalculateBoundaries() const;
};
@ -55,19 +65,17 @@ void
nsImportedStringHandle<CharT>::RecalculateBoundaries() const
{
size_t data_length = 0;
size_t storage_length = 0;
CharT* storage_start = NS_CONST_CAST(CharT*, this->StorageStart());
CharT* storage_start = NS_CONST_CAST(CharT*, this->DataStart());
if ( storage_start )
{
data_length = nsCharTraits<CharT>::length(storage_start);
storage_length = data_length + 1;
}
nsImportedStringHandle<CharT>* mutable_this = NS_CONST_CAST(nsImportedStringHandle<CharT>*, this);
mutable_this->DataStart(storage_start);
mutable_this->DataEnd(storage_start+data_length);
mutable_this->StorageEnd(storage_start+storage_length);
mutable_this->DataEnd(storage_start + data_length);
mutable_this->StorageLength(data_length + 1);
}
@ -100,15 +108,21 @@ nsXPIDLString::DebugPrintStats( FILE* aOutFile )
const nsXPIDLString::shared_buffer_handle_type*
nsXPIDLString::GetSharedBufferHandle() const
{
const nsImportedStringHandle<char_type>* answer = NS_STATIC_CAST(const nsImportedStringHandle<char_type>*, mBuffer.get());
if ( answer && !answer->DataEnd() && answer->StorageStart() )
answer->RecalculateBoundaries();
self_type* mutable_this = NS_CONST_CAST(self_type*, this);
if ( !mBuffer->DataStart() )
// XXXldb This isn't any good. What if we just called
// PrepareForUseAsOutParam and it hasn't been filled in yet?
mutable_this->mBuffer = GetSharedEmptyBufferHandle();
else if ( !mBuffer->DataEnd() )
// Our handle may not be an nsImportedStringHandle. However, if it
// is not, this cast will still be safe since no other handle will
// be in this state.
NS_STATIC_CAST(const nsImportedStringHandle<char_type>*, mBuffer.get())->RecalculateBoundaries();
#if DEBUG_STRING_STATS
++sShareCount;
#endif
return answer;
return mBuffer.get();
}
@ -125,6 +139,20 @@ nsXPIDLString::PrepareForUseAsOutParam()
return handle->AddressOfStorageStart();
}
/* static */
nsXPIDLString::shared_buffer_handle_type*
nsXPIDLString::GetSharedEmptyBufferHandle()
{
static shared_buffer_handle_type* sBufferHandle = nsnull;
if (!sBufferHandle) {
sBufferHandle = NS_AllocateContiguousHandleWithData(sBufferHandle,
PRUint32(1), (self_type*)nsnull);
sBufferHandle->AcquireReference();
}
return sBufferHandle;
}
#if DEBUG_STRING_STATS
const nsXPIDLCString::buffer_handle_type*
@ -155,15 +183,21 @@ nsXPIDLCString::DebugPrintStats( FILE* aOutFile )
const nsXPIDLCString::shared_buffer_handle_type*
nsXPIDLCString::GetSharedBufferHandle() const
{
const nsImportedStringHandle<char_type>* answer = NS_STATIC_CAST(const nsImportedStringHandle<char_type>*, mBuffer.get());
if ( answer && !answer->DataEnd() && answer->StorageStart() )
answer->RecalculateBoundaries();
self_type* mutable_this = NS_CONST_CAST(self_type*, this);
if ( !mBuffer->DataStart() )
// XXXldb This isn't any good. What if we just called
// PrepareForUseAsOutParam and it hasn't been filled in yet?
mutable_this->mBuffer = GetSharedEmptyBufferHandle();
else if ( !mBuffer->DataEnd() )
// Our handle may not be an nsImportedStringHandle. However, if it
// is not, this cast will still be safe since no other handle will
// be in this state.
NS_STATIC_CAST(const nsImportedStringHandle<char_type>*, mBuffer.get())->RecalculateBoundaries();
#if DEBUG_STRING_STATS
++sShareCount;
#endif
return answer;
return mBuffer.get();
}
@ -179,3 +213,17 @@ nsXPIDLCString::PrepareForUseAsOutParam()
#endif
return handle->AddressOfStorageStart();
}
/* static */
nsXPIDLCString::shared_buffer_handle_type*
nsXPIDLCString::GetSharedEmptyBufferHandle()
{
static shared_buffer_handle_type* sBufferHandle = nsnull;
if (!sBufferHandle) {
sBufferHandle = NS_AllocateContiguousHandleWithData(sBufferHandle,
PRUint32(1), (self_type*)nsnull);
sBufferHandle->AcquireReference();
}
return sBufferHandle;
}

Просмотреть файл

@ -265,8 +265,7 @@ nsLocalFile::InitWithPath(const char *filePath)
ssize_t len = strlen(filePath);
while (filePath[len-1] == '/' && len > 1)
--len;
// XXXldb change to |Assign| rather than |Adopt(ToNewCString|
mPath.Adopt(ToNewCString(Substring(filePath, filePath+len)));
mPath.Assign(Substring(filePath, filePath+len));
InvalidateCache();
return NS_OK;
@ -439,8 +438,8 @@ nsLocalFile::AppendRelativePath(const char *fragment)
if (*fragment == '/')
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
mPath.Adopt(ToNewCString(mPath + NS_LITERAL_CSTRING("/") +
nsDependentCString(fragment)));
mPath.Assign(mPath + NS_LITERAL_CSTRING("/") +
nsDependentCString(fragment));
if (!mPath.get())
return NS_ERROR_OUT_OF_MEMORY;

Просмотреть файл

@ -44,16 +44,29 @@ class NS_COM nsAFlatString
: public nsASingleFragmentString
{
public:
// don't really want this to be virtual, and won't after |obsolete_nsString| is really dead
virtual const char_type* get() const { const char_type* temp; return BeginReading(temp); }
// don't really want this to be virtual, and won't after
// |obsolete_nsString| is really dead and |nsXPIDLString| works
// differently
virtual const char_type* get() const
{
const char_type* temp;
return BeginReading(temp);
}
};
class NS_COM nsAFlatCString
: public nsASingleFragmentCString
{
public:
// don't really want this to be virtual, and won't after |obsolete_nsCString| is really dead
virtual const char_type* get() const { const char_type* temp; return BeginReading(temp); }
// don't really want this to be virtual, and won't after
// |obsolete_nsCString| is really dead and |nsXPIDLCString|
// works differently
virtual const char_type* get() const
{
const char_type* temp;
return BeginReading(temp);
}
};
#endif /* !defined(nsAFlatString_h___) */

Просмотреть файл

@ -112,9 +112,19 @@ class NS_COM nsAString
virtual void SetIsVoid( PRBool );
/**
* |CharAt|, |operator[]|, |First()|, and |Last()| are not guaranteed to be constant-time operations.
* These signatures should be pushed down into interfaces that guarantee flat allocation.
* Clients at _this_ level should always use iterators.
* |CharAt|, |operator[]|, |First()|, and |Last()| are not
* guaranteed to be constant-time operations. These signatures
* should be pushed down into interfaces that guarantee flat
* allocation. (Right now |First| and |Last| are here but
* |CharAt| and |operator[]| are on |nsASingleFragmentString|.)
*
* Clients at _this_ level should always use iterators. For
* example, to see if the n-th character is a '-':
*
* nsAString::const_iterator iter;
* if ( *myString.BeginReading(iter).advance(n) == PRUnichar('-') )
* // do something...
*
*/
char_type First() const;
char_type Last() const;
@ -151,8 +161,8 @@ class NS_COM nsAString
// RFindCharInSet( ... ) const;
/**
* |SetCapacity| is not required to do anything; however, it can be used
* as a hint to the implementation to reduce allocations.
* |SetCapacity| is not required to do anything; however, it can be
* used as a hint to the implementation to reduce allocations.
* |SetCapacity(0)| is a suggestion to discard all associated storage.
*/
virtual void SetCapacity( size_type ) { }
@ -162,13 +172,20 @@ class NS_COM nsAString
* 1) to |Cut| a suffix of the string;
* 2) to prepare to |Append| or move characters around.
*
* External callers are not allowed to use |SetLength| is this latter capacity.
* Should this really be a public operation?
* Additionally, your implementation of |SetLength| need not satisfy (2) if and only if you
* override the |do_...| routines to not need this facility.
* External callers are not allowed to use |SetLength| is this
* latter capacity, and should prefer |Truncate| for the former.
* In other words, |SetLength| is deprecated for all use outside
* of the string library and the internal use may at some point
* be replaced as well.
*
* This distinction makes me think the two different uses should be split into
* two distinct functions.
* Should this really be a public operation?
*
* Additionally, your implementation of |SetLength| need not
* satisfy (2) if and only if you override the |do_...| routines
* to not need this facility.
*
* This distinction makes me think the two different uses should
* be split into two distinct functions.
*/
virtual void SetLength( size_type ) { }
@ -176,10 +193,10 @@ class NS_COM nsAString
void
Truncate( size_type aNewLength=0 )
{
NS_ASSERTION(aNewLength<=this->Length(), "Can't use |Truncate()| to make a string longer.");
NS_ASSERTION(aNewLength <= this->Length(),
"Can't use |Truncate()| to make a string longer.");
if ( aNewLength < this->Length() )
SetLength(aNewLength);
SetLength(aNewLength);
}
@ -201,9 +218,13 @@ class NS_COM nsAString
//
// |Assign()|, |operator=()|
//
/**
* |Assign()| and |operator=()| make |this| equivalent to the
* string or buffer given as an argument. If possible, they do
* this by sharing a refcounted buffer (see
* |nsSharableC?String|, |nsXPIDLC?String|. If not, they copy
* the buffer into their own buffer.
*/
void Assign( const self_type& aReadable ) { AssignFromReadable(aReadable); }
inline void Assign( const promise_type& aReadable );
@ -366,9 +387,19 @@ class NS_COM nsACString
virtual void SetIsVoid( PRBool );
/**
* |CharAt|, |operator[]|, |First()|, and |Last()| are not guaranteed to be constant-time operations.
* These signatures should be pushed down into interfaces that guarantee flat allocation.
* Clients at _this_ level should always use iterators.
* |CharAt|, |operator[]|, |First()|, and |Last()| are not
* guaranteed to be constant-time operations. These signatures
* should be pushed down into interfaces that guarantee flat
* allocation. (Right now |First| and |Last| are here but
* |CharAt| and |operator[]| are on |nsASingleFragmentString|.)
*
* Clients at _this_ level should always use iterators. For
* example, to see if the n-th character is a '-':
*
* nsACString::const_iterator iter;
* if ( *myString.BeginReading(iter).advance(n) == '-' )
* // do something...
*
*/
char_type First() const;
char_type Last() const;

Просмотреть файл

@ -31,9 +31,6 @@
#include "nsStringDefines.h"
#endif
#include <stddef.h>
// for |ptrdiff_t|
#include "prtypes.h"
// for |PRBool|
@ -49,32 +46,39 @@
#pragma warning( disable: 4251 )
#endif
/**
The classes in this file are collectively called `buffer handles'.
All buffer handles begin with a pointer-tuple that delimits the useful content of a
hunk of string. A buffer handle that points to a sharable hunk of string data
additionally has a field which multiplexes some flags and a reference count.
ns[Const]BufferHandle nsSharedBufferHandle mFlexBufferHandle
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| mDataStart | | mDataStart | | mDataStart |
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| mDataEnd | | mDataEnd | | mDataEnd |
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| mFlags | | mFlags |
+-----+-----+-----+-----+ +-----+-----+-----+-----+
. mAllocator . | mStorageStart |
......................... +-----+-----+-----+-----+
| mStorageEnd |
+-----+-----+-----+-----+
. mAllocator .
.........................
Given only a |ns[Const]BufferHandle|, there is no legal way to tell if it is sharable.
In all cases, the data might immediately follow the handle in the same allocated block.
From the |mFlags| field, you can tell exactly what configuration of a handle you
actually have.
/*
* The classes in this file are collectively called `buffer handles'.
* All buffer handles begin with a pointer-tuple that delimits the
* useful content of a hunk of string. A buffer handle that points to
* a sharable hunk of string data additionally has a field which
* multiplexes some flags and a reference count.
*
*
* ns[Const]BufferHandle nsSharedBufferHandle
* +-----+-----+-----+-----+ +-----+-----+-----+-----+
* | mDataStart | | mDataStart |
* +-----+-----+-----+-----+ +-----+-----+-----+-----+
* | mDataEnd | | mDataEnd |
* +-----+-----+-----+-----+ +-----+-----+-----+-----+
* | mFlags |
* +-----+-----+-----+-----+
* | mStorageLength |
* +-----+-----+-----+-----+
* . mAllocator .
* .........................
*
* Given only a |ns[Const]BufferHandle|, there is no legal way to tell
* if it is sharable. In all cases, the data might immediately follow
* the handle in the same allocated block. From the |mFlags| field,
* you can tell exactly what configuration of a handle you actually
* have.
*
* An |nsSharedBufferHandle| has the limitation that its |mDataStart|
* must also be the beginning of the allocated storage. However,
* allowing otherwise would introduce significant additional
* complexity for a feature that would better be handled by allowing
* an owninng substring string class that owned a reference to the
* buffer handle.
*/
@ -85,6 +89,8 @@ template <class CharT>
class nsBufferHandle
{
public:
typedef PRUint32 size_type;
nsBufferHandle() { }
nsBufferHandle( CharT* aDataStart, CharT* aDataEnd ) : mDataStart(aDataStart), mDataEnd(aDataEnd) { }
@ -96,7 +102,7 @@ class nsBufferHandle
CharT* DataEnd() { return mDataEnd; }
const CharT* DataEnd() const { return mDataEnd; }
ptrdiff_t DataLength() const { return mDataEnd - mDataStart; }
size_type DataLength() const { return mDataEnd - mDataStart; }
protected:
CharT* mDataStart;
@ -107,6 +113,8 @@ template <class CharT>
class nsConstBufferHandle
{
public:
typedef PRUint32 size_type;
nsConstBufferHandle() { }
nsConstBufferHandle( const CharT* aDataStart, const CharT* aDataEnd ) : mDataStart(aDataStart), mDataEnd(aDataEnd) { }
@ -116,7 +124,7 @@ class nsConstBufferHandle
void DataEnd( const CharT* aNewDataEnd ) { mDataEnd = aNewDataEnd; }
const CharT* DataEnd() const { return mDataEnd; }
ptrdiff_t DataLength() const { return mDataEnd - mDataStart; }
size_type DataLength() const { return mDataEnd - mDataStart; }
protected:
const CharT* mDataStart;
@ -175,13 +183,15 @@ template <class CharT>
class nsSharedBufferHandle
: public nsBufferHandle<CharT>
{
public:
typedef PRUint32 size_type;
protected:
enum
{
kIsShared = 0x08000000, // one reason _not_ to set this is for a stack based handle that wants to express `NULL'-ness et al
kIsSingleAllocationWithBuffer = 0x04000000, // handle and buffer are one piece, no separate deallocation is possible for the buffer
kIsStorageDefinedSeparately = 0x02000000, // i.e., we're using the ``flex'' structure defined below
kIsUserAllocator = 0x01000000, // can't |delete|, call a hook instead
kIsShared = 0x01000000, // one reason _not_ to set this is for a stack based handle that wants to express `NULL'-ness et al
kIsSingleAllocationWithBuffer = 0x02000000, // handle and buffer are one piece, no separate deallocation is possible for the buffer
kIsUserAllocator = 0x04000000, // can't |delete|, call a hook instead
// the following flags are opaque to the string library itself
kIsNULL = 0x80000000, // the most common request of external clients is a scheme by which they can express `NULL'-ness
@ -192,14 +202,9 @@ class nsSharedBufferHandle
};
public:
nsSharedBufferHandle( CharT* aDataStart, CharT* aDataEnd )
: nsBufferHandle<CharT>(aDataStart, aDataEnd)
{
mFlags = kIsShared;
}
nsSharedBufferHandle( CharT* aDataStart, CharT* aDataEnd, CharT*, CharT*, PRBool isSingleAllocation )
: nsBufferHandle<CharT>(aDataStart, aDataEnd)
nsSharedBufferHandle( CharT* aDataStart, CharT* aDataEnd, size_type aStorageLength, PRBool isSingleAllocation )
: nsBufferHandle<CharT>(aDataStart, aDataEnd),
mStorageLength(aStorageLength)
{
mFlags = kIsShared;
if ( isSingleAllocation )
@ -230,6 +235,23 @@ class nsSharedBufferHandle
return get_refcount() != 0;
}
PRBool
IsShared() const
{
return get_refcount() > 1;
}
void StorageLength( size_type aNewStorageLength )
{
mStorageLength = aNewStorageLength;
}
size_type
StorageLength() const
{
return mStorageLength;
}
PRUint32
GetImplementationFlags() const
{
@ -244,6 +266,7 @@ class nsSharedBufferHandle
protected:
PRUint32 mFlags;
size_type mStorageLength;
PRUint32
get_refcount() const
@ -264,61 +287,17 @@ class nsSharedBufferHandle
};
template <class CharT>
class nsFlexBufferHandle
: public nsSharedBufferHandle<CharT>
{
public:
nsFlexBufferHandle( CharT* aDataStart, CharT* aDataEnd, CharT* aStorageStart, CharT* aStorageEnd )
: nsSharedBufferHandle<CharT>(aDataStart, aDataEnd),
mStorageStart(aStorageStart),
mStorageEnd(aStorageEnd)
{
this->mFlags |= this->kIsStorageDefinedSeparately;
}
void StorageStart( CharT* aNewStorageStart ) { mStorageStart = aNewStorageStart; }
CharT* StorageStart() { return mStorageStart; }
const CharT* StorageStart() const { return mStorageStart; }
void StorageEnd( CharT* aNewStorageEnd ) { mStorageEnd = aNewStorageEnd; }
CharT* StorageEnd() { return mStorageEnd; }
const CharT* StorageEnd() const { return mStorageEnd; }
ptrdiff_t StorageLength() const { return mStorageEnd - mStorageStart; }
protected:
CharT* mStorageStart;
CharT* mStorageEnd;
};
template <class CharT>
class nsSharedBufferHandleWithAllocator
: public nsSharedBufferHandle<CharT>
{
public:
nsSharedBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd, nsStringAllocator<CharT>& aAllocator )
: nsSharedBufferHandle<CharT>(aDataStart, aDataEnd),
mAllocator(aAllocator)
{
this->mFlags |= this->kIsUserAllocator;
}
// why is this needed again?
typedef PRUint32 size_type;
nsStringAllocator<CharT>& get_allocator() const { return mAllocator; }
protected:
nsStringAllocator<CharT>& mAllocator;
};
template <class CharT>
class nsFlexBufferHandleWithAllocator
: public nsFlexBufferHandle<CharT>
{
public:
nsFlexBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd,
CharT* aStorageStart, CharT* aStorageEnd,
nsStringAllocator<CharT>& aAllocator )
: nsFlexBufferHandle<CharT>(aDataStart, aDataEnd, aStorageStart, aStorageEnd),
nsSharedBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd, size_type aStorageLength, nsStringAllocator<CharT>& aAllocator )
: nsSharedBufferHandle<CharT>(aDataStart, aDataEnd,
aStorageLength, PR_FALSE),
mAllocator(aAllocator)
{
this->mFlags |= this->kIsUserAllocator;
@ -338,10 +317,7 @@ nsSharedBufferHandle<CharT>::get_allocator() const
{
if ( mFlags & kIsUserAllocator )
{
if ( mFlags & kIsStorageDefinedSeparately )
return NS_REINTERPRET_CAST(const nsFlexBufferHandleWithAllocator<CharT>*, this)->get_allocator();
else
return NS_REINTERPRET_CAST(const nsSharedBufferHandleWithAllocator<CharT>*, this)->get_allocator();
return NS_REINTERPRET_CAST(const nsSharedBufferHandleWithAllocator<CharT>*, this)->get_allocator();
}
return nsStringAllocatorTraits<CharT>::global_string_allocator();
@ -357,9 +333,6 @@ nsSharedBufferHandle<CharT>::~nsSharedBufferHandle()
if ( !(mFlags & kIsSingleAllocationWithBuffer) )
{
CharT* string_storage = this->mDataStart;
if ( mFlags & kIsStorageDefinedSeparately )
string_storage = NS_REINTERPRET_CAST(nsFlexBufferHandle<CharT>*, this)->StorageStart();
get_allocator().Deallocate(string_storage);
}
}

Просмотреть файл

@ -169,7 +169,7 @@ NS_AllocateContiguousHandleWithData( const HandleT* aDummyHandlePtr, PRUint32 aA
if ( aAdditionalCapacity > 0 )
*toBegin = char_type(0);
result = new (handle_ptr) HandleT(data_start_ptr, data_end_ptr, data_start_ptr, buffer_end_ptr, PR_TRUE);
result = new (handle_ptr) HandleT(data_start_ptr, data_end_ptr, buffer_end_ptr - data_start_ptr, PR_TRUE);
}
return result;

Просмотреть файл

@ -35,10 +35,13 @@
#include "nsBufferHandleUtils.h"
#endif
//-------1---------2---------3---------4---------5---------6---------7---------8
/**
* Not yet ready for non-|const| access
* |nsSharableC?String| is the basic copy-on-write string class. It
* implements |nsAFlatC?String|, so it always has a single,
* null-terminated, buffer. It can accept assignment in two ways:
* 1. |Assign| (which is equivalent to |operator=|), which shares
* the buffer if possible and copies the buffer otherwise, and
* 2. |Adopt|, which takes over ownership of a raw buffer.
*/
class NS_COM nsSharableString
@ -48,22 +51,53 @@ class NS_COM nsSharableString
typedef nsSharableString self_type;
public:
nsSharableString() { }
nsSharableString() : mBuffer(GetSharedEmptyBufferHandle()) { }
nsSharableString( const self_type& aOther ) : mBuffer(aOther.mBuffer) { }
explicit nsSharableString( const abstract_string_type& aReadable ) { assign(aReadable); }
explicit nsSharableString( const shared_buffer_handle_type* aHandle ) : mBuffer(aHandle) { }
self_type&
operator=( const abstract_string_type& aReadable )
explicit nsSharableString( const abstract_string_type& aReadable )
{
assign(aReadable);
// can call |do_AssignFromReadable| directly since we know
// |&aReadable != this|.
do_AssignFromReadable(aReadable);
}
explicit nsSharableString( const shared_buffer_handle_type* aHandle )
: mBuffer(aHandle)
{
NS_ASSERTION(aHandle, "null handle");
}
// |operator=| does not inherit, so we must provide it again
self_type& operator=( const abstract_string_type& aReadable )
{
Assign(aReadable);
return *this;
}
/**
* The |Adopt| method assigns a raw, null-terminated, character
* buffer to this string object by transferring ownership of that
* buffer to the string. No copying occurs.
*
* XXX Add a second |Adopt| method that takes a length for clients
* that already know the length.
*/
void Adopt( char_type* aNewValue );
/**
* These overrides of |SetCapacity|, |SetLength|,
* |do_AssignFromReadable|, and |GetSharedBufferHandle|, all
* virtual methods on |nsAString|, allow |nsSharableString| to do
* copy-on-write buffer sharing.
*/
public:
virtual void SetCapacity( size_type aNewCapacity );
virtual void SetLength( size_type aNewLength );
protected:
void assign( const abstract_string_type& );
virtual void do_AssignFromReadable( const abstract_string_type& aReadable );
virtual const shared_buffer_handle_type* GetSharedBufferHandle() const;
protected:
static shared_buffer_handle_type* GetSharedEmptyBufferHandle();
protected:
nsAutoBufferHandle<char_type> mBuffer;
};
@ -73,25 +107,56 @@ class NS_COM nsSharableCString
: public nsAFlatCString
{
public:
typedef nsSharableCString self_type;
typedef nsSharableCString self_type;
public:
nsSharableCString() { }
nsSharableCString() : mBuffer(GetSharedEmptyBufferHandle()) { }
nsSharableCString( const self_type& aOther ) : mBuffer(aOther.mBuffer) { }
explicit nsSharableCString( const abstract_string_type& aReadable ) { assign(aReadable); }
explicit nsSharableCString( const shared_buffer_handle_type* aHandle ) : mBuffer(aHandle) { }
self_type&
operator=( const abstract_string_type& aReadable )
explicit nsSharableCString( const abstract_string_type& aReadable )
{
assign(aReadable);
// can call |do_AssignFromReadable| directly since we know
// |&aReadable != this|.
do_AssignFromReadable(aReadable);
}
explicit nsSharableCString( const shared_buffer_handle_type* aHandle )
: mBuffer(aHandle)
{
NS_ASSERTION(aHandle, "null handle");
}
// |operator=| does not inherit, so we must provide it again
self_type& operator=( const abstract_string_type& aReadable )
{
Assign(aReadable);
return *this;
}
/**
* The |Adopt| method assigns a raw, null-terminated, character
* buffer to this string object by transferring ownership of that
* buffer to the string. No copying occurs.
*
* XXX Add a second |Adopt| method that takes a length for clients
* that already know the length.
*/
void Adopt( char_type* aNewValue );
/**
* These overrides of |SetCapacity|, |SetLength|,
* |do_AssignFromReadable|, and |GetSharedBufferHandle|, all
* virtual methods on |nsAString|, allow |nsSharableCString| to do
* copy-on-write buffer sharing.
*/
public:
virtual void SetCapacity( size_type aNewCapacity );
virtual void SetLength( size_type aNewLength );
protected:
void assign( const abstract_string_type& );
virtual void do_AssignFromReadable( const abstract_string_type& aReadable );
virtual const shared_buffer_handle_type* GetSharedBufferHandle() const;
protected:
static shared_buffer_handle_type* GetSharedEmptyBufferHandle();
protected:
nsAutoBufferHandle<char_type> mBuffer;
};

Просмотреть файл

@ -62,14 +62,12 @@ class NS_COM nsSharedBufferList
public:
class Buffer
: public nsFlexBufferHandle<PRUnichar>
: public nsSharedBufferHandle<PRUnichar>
{
public:
Buffer( PRUnichar* aDataStart, PRUnichar* aDataEnd, PRUnichar* aStorageStart, PRUnichar* aStorageEnd, PRBool aIsSingleAllocation=PR_FALSE )
: nsFlexBufferHandle<PRUnichar>(aDataStart, aDataEnd, aStorageStart, aStorageEnd)
Buffer( PRUnichar* aDataStart, PRUnichar* aDataEnd, size_type aStorageLength, PRBool aIsSingleAllocation=PR_FALSE )
: nsSharedBufferHandle<PRUnichar>(aDataStart, aDataEnd, aStorageLength, aIsSingleAllocation)
{
if ( aIsSingleAllocation )
this->mFlags |= this->kIsSingleAllocationWithBuffer;
}
/**
@ -198,7 +196,7 @@ class NS_COM nsSharedBufferList
Buffer*
NewWrappingBuffer( PRUnichar* aDataStart, PRUnichar* aDataEnd, PRUnichar* aStorageEnd )
{
return new Buffer(aDataStart, aDataEnd, aDataStart, aStorageEnd);
return new Buffer(aDataStart, aDataEnd, aStorageEnd - aDataStart);
}
void DiscardSuffix( PRUint32 );

Просмотреть файл

@ -32,9 +32,66 @@
#include <stdio.h>
#endif
/**
* Interface documentation:
*
* |nsXPIDLC?String| differs from |nsSharableC?String| in that one can
* assign into it from an XPCOM getter that fills in a |char_type**|
* with a pointer to a buffer whose ownership is to be transferred to
* the caller.
*
* Consider the following interface:
*
* interface nsIFoo {
* attribute string Bar;
* };
*
* This will generate the following C++ header file:
*
* class nsIFoo {
* NS_IMETHOD SetBar(const PRUnichar* aValue);
* NS_IMETHOD GetBar(PRUnichar* *aValue);
* };
*
* The GetBar() method will allocate a copy of the nsIFoo object's
* "bar" attribute, and leave you to deal with freeing it:
*
* nsIFoo* aFoo; // assume we get this somehow
* PRUnichar* bar;
* aFoo->GetFoo(&bar);
* do_something_with_buffer(bar);
* nsMemory::Free(bar);
*
* This makes your life harder, because you need to convolute your
* code to ensure that you don't leak `bar'.
*
* Enter nsXPIDLString, which manages the ownership of the allocated
* string, and automatically destroys it when the nsXPIDLString goes
* out of scope:
*
* nsIFoo* aFoo; // assume we get this somehow
* nsXPIDLString bar;
* aFoo->GetFoo( getter_Copies(bar) );
* do_something_with_buffer(bar.get());
* // no need to remember to nsMemory::Free().
*
* Like nsCOMPtr, nsXPIDLString uses some syntactic sugar to make it
* painfully clear exactly what the code expects. You need to wrap an
* nsXPIDLString object with `getter_Copies()' before passing it to a
* getter: these tell the nsXPIDLString how ownership is being
* handled.
*
* In the case of `getter_Copies()', the callee is allocating a copy
* (which is usually the case). In the case where the callee is
* returning a const reference to `the real deal' (this can be done
* using the [shared] attribute in XPIDL) you can just use a
* |const char*| or |const PRUnichar*|.
*/
/**
* Implementation notes:
*
* |nsXPIDLC?String| extends |nsSharableC?String| with the ability
* to defer calculation of its length. This is crucial to allowing
* the |getter_Copies| assignment behavior.
@ -45,7 +102,7 @@
* the operation on which all other flat string operations are based.
* A valid handle will either have all |NULL| or all non-|NULL|
* pointers. After use as an `out' string pointer parameter, an
* |nsXPIDLC?String|'s handle will have a non-|NULL| storage start, but
* |nsXPIDLC?String|'s handle will have a non-|NULL| data start, but
* all other members will be |NULL|. This is the signal that the
* length needs to be recalculated. |GetSharedBufferHandle| detects
* this situation and repairs it.
@ -54,7 +111,8 @@
* the string before it's destruction. In this case, because the start of
* storage is known, storage can still be freed in the usual way.
*
* An |nsXPIDLC?String| is now a sharable object, just like |nsSharableC?String|.
* An |nsXPIDLC?String| is now a sharable object, just like
* |nsSharableC?String|.
* This simple implementation always allocates an intermediary handle
* object. This cost might turn out to be a burden, it's something we'll
* want to measure. A couple of optimizations spring to mind if allocation
@ -75,6 +133,7 @@ class NS_COM nsXPIDLString
public:
nsXPIDLString()
: nsSharableString(GetSharedEmptyBufferHandle())
{
#if DEBUG_STRING_STATS
++sCreatedCount;
@ -84,7 +143,7 @@ class NS_COM nsXPIDLString
}
nsXPIDLString( const self_type& aString )
: nsSharableString(aString.GetSharedBufferHandle())
: nsSharableString(aString.mBuffer.get())
// copy-constructor required (or else C++ generates one for us)
{
#if DEBUG_STRING_STATS
@ -94,6 +153,16 @@ class NS_COM nsXPIDLString
#endif
}
explicit nsXPIDLString( const abstract_string_type& aReadable )
: nsSharableString(aReadable)
{
#if DEBUG_STRING_STATS
++sCreatedCount;
if ( ++sAliveCount > sHighWaterCount )
sHighWaterCount = sAliveCount;
#endif
}
#if DEBUG_STRING_STATS
~nsXPIDLString()
{
@ -102,22 +171,26 @@ class NS_COM nsXPIDLString
#endif
self_type&
operator=( const self_type& rhs )
// copy-assignment operator required (or else C++ generates one for us)
operator=( const abstract_string_type& aReadable )
{
// self-assignment is handled by the underlying |nsAutoBufferHandle|
mBuffer = rhs.GetSharedBufferHandle();
Assign(aReadable);
return *this;
}
/**
* This is an override of a non-virtual function on
* |nsSharableString|. This override is not necessary, but it can
* improve performance in the case where we know we have an
* |nsXPIDLString|.
*/
void Adopt( char_type* aNewValue ) { *PrepareForUseAsOutParam() = aNewValue; }
// overridden to make getter_Copies mechanism work
const char_type* get() const
virtual const char_type* get() const
{
const buffer_handle_type* handle = GetBufferHandle();
// NS_ASSERTION(handle, "handle is null!");
return handle ? handle->DataStart() : 0;
return (mBuffer.get() != GetSharedEmptyBufferHandle())
? mBuffer->DataStart()
: 0;
}
// deprecated, to be eliminated
@ -150,6 +223,8 @@ class NS_COM nsXPIDLString
char_type** PrepareForUseAsOutParam();
static shared_buffer_handle_type* GetSharedEmptyBufferHandle();
#if DEBUG_STRING_STATS
static size_t sCreatedCount; // total number of |nsXPIDLString|s ever created
static size_t sAliveCount; // total number of |nsXPIDLStrings|s alive right now
@ -180,6 +255,7 @@ class NS_COM nsXPIDLCString
public:
nsXPIDLCString()
: nsSharableCString(GetSharedEmptyBufferHandle())
{
#if DEBUG_STRING_STATS
++sCreatedCount;
@ -189,7 +265,7 @@ class NS_COM nsXPIDLCString
}
nsXPIDLCString( const self_type& aString )
: nsSharableCString(aString.GetSharedBufferHandle())
: nsSharableCString(aString.mBuffer.get())
// copy-constructor required (or else C++ generates one for us)
{
#if DEBUG_STRING_STATS
@ -207,22 +283,26 @@ class NS_COM nsXPIDLCString
#endif
self_type&
operator=( const self_type& rhs )
// copy-assignment operator required (or else C++ generates one for us)
operator=( const abstract_string_type& aReadable )
{
// self-assignment is handled by the underlying |nsAutoBufferHandle|
mBuffer = rhs.GetSharedBufferHandle();
Assign(aReadable);
return *this;
}
/**
* This is an override of a non-virtual function on
* |nsSharableCString|. This override is not necessary, but it can
* improve performance in the case where we know we have an
* |nsXPIDLCString|.
*/
void Adopt( char_type* aNewValue ) { *PrepareForUseAsOutParam() = aNewValue; }
// overridden to make getter_Copies mechanism work
const char_type* get() const
virtual const char_type* get() const
{
const buffer_handle_type* handle = GetBufferHandle();
// NS_ASSERTION(handle, "handle is null!");
return handle ? handle->DataStart() : 0;
return (mBuffer.get() != GetSharedEmptyBufferHandle())
? mBuffer->DataStart()
: 0;
}
// deprecated, to be eliminated
@ -255,6 +335,8 @@ class NS_COM nsXPIDLCString
char_type** PrepareForUseAsOutParam();
static shared_buffer_handle_type* GetSharedEmptyBufferHandle();
#if DEBUG_STRING_STATS
static size_t sCreatedCount; // total number of |nsXPIDLCString|s ever created
static size_t sAliveCount; // total number of |nsXPIDLCStrings|s alive right now

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -98,15 +98,20 @@ nsSharedBufferList::SplitBuffer( const Position& aSplitPosition, SplitDispositio
NS_ASSERTION(bufferToSplit, "bufferToSplit");
ptrdiff_t splitOffset = aSplitPosition.mPosInBuffer - bufferToSplit->DataStart();
Buffer::size_type splitOffset =
aSplitPosition.mPosInBuffer - bufferToSplit->DataStart();
NS_ASSERTION(0 <= splitOffset && splitOffset <= bufferToSplit->DataLength(), "|splitOffset| within buffer");
NS_ASSERTION(aSplitPosition.mPosInBuffer >= bufferToSplit->DataStart() &&
splitOffset <= bufferToSplit->DataLength(),
"|splitOffset| within buffer");
// if the caller specifically asked to split off the right side of the buffer-to-be-split
// or else if they asked for the minimum amount of work, and that turned out to be the right side...
// if the caller specifically asked to split off the right side of
// the buffer-to-be-split or else if they asked for the minimum
// amount of work, and that turned out to be the right side...
ptrdiff_t savedLength = mTotalDataLength;
if ( aSplitDirection==kSplitCopyRightData ||
( aSplitDirection==kSplitCopyLeastData && ((bufferToSplit->DataLength() >> 1) <= splitOffset) ) )
( aSplitDirection==kSplitCopyLeastData &&
((bufferToSplit->DataLength() >> 1) <= splitOffset) ) )
{
// ...then allocate a new buffer initializing it by copying all the data _after_ the split in the source buffer (i.e., `split right')
Buffer* new_buffer = NewSingleAllocationBuffer(bufferToSplit->DataStart()+splitOffset, PRUint32(bufferToSplit->DataLength()-splitOffset));

Просмотреть файл

@ -125,7 +125,7 @@ AllocateContiguousHandleWithData( Buffer_ptr aDummyHandlePtr, const nsAString& a
nsAString::const_iterator fromBegin, fromEnd;
PRUnichar* toBegin = string_start_ptr;
copy_string(aDataSource.BeginReading(fromBegin), aDataSource.EndReading(fromEnd), toBegin);
result = new (handle_ptr) nsSharedBufferList::Buffer(string_start_ptr, string_end_ptr, string_start_ptr, string_end_ptr+1, PR_TRUE);
result = new (handle_ptr) nsSharedBufferList::Buffer(string_start_ptr, string_end_ptr, string_end_ptr-string_start_ptr+1, PR_TRUE);
}
return result;
@ -282,7 +282,8 @@ nsSlidingString::nsSlidingString( PRUnichar* aStorageStart, PRUnichar* aDataEnd,
void
nsSlidingString::AppendBuffer( PRUnichar* aStorageStart, PRUnichar* aDataEnd, PRUnichar* aStorageEnd )
{
Buffer* new_buffer = new Buffer(aStorageStart, aDataEnd, aStorageStart, aStorageEnd);
Buffer* new_buffer =
new Buffer(aStorageStart, aDataEnd, aStorageEnd - aStorageStart);
Buffer* old_last_buffer = mBufferList->GetLastBuffer();
mBufferList->LinkBuffer(old_last_buffer, new_buffer, 0);
mLength += new_buffer->DataLength();

Просмотреть файл

@ -21,6 +21,16 @@
* Scott Collins <scc@mozilla.org> (original author)
*/
// XXX TODO:
//
// nsSharableString will need to be careful to use GetSharedBufferHandle
// where necessary so that an nsXPIDLString can be passed as an
// nsSharableString&. We must be careful to ensure that an
// nsXPIDLString can be used as an nsSharableString& and that an
// nsXPIDLString and an nsSharableString can share buffers created by
// the other (and buffers created by nsSharableString::Adopt rather than
// its other assignment methods).
#include "nsXPIDLString.h"
#if DEBUG_STRING_STATS
@ -40,12 +50,12 @@ size_t nsXPIDLCString::sShareCount = 0;
template <class CharT>
class nsImportedStringHandle
: public nsFlexBufferHandle<CharT>
: public nsSharedBufferHandle<CharT>
{
public:
nsImportedStringHandle() : nsFlexBufferHandle<CharT>(0, 0, 0, 0) { }
nsImportedStringHandle() : nsSharedBufferHandle<CharT>(0, 0, 0, PR_FALSE) { }
CharT** AddressOfStorageStart() { return &(this->mStorageStart); }
CharT** AddressOfStorageStart() { return &(this->mDataStart); }
void RecalculateBoundaries() const;
};
@ -55,19 +65,17 @@ void
nsImportedStringHandle<CharT>::RecalculateBoundaries() const
{
size_t data_length = 0;
size_t storage_length = 0;
CharT* storage_start = NS_CONST_CAST(CharT*, this->StorageStart());
CharT* storage_start = NS_CONST_CAST(CharT*, this->DataStart());
if ( storage_start )
{
data_length = nsCharTraits<CharT>::length(storage_start);
storage_length = data_length + 1;
}
nsImportedStringHandle<CharT>* mutable_this = NS_CONST_CAST(nsImportedStringHandle<CharT>*, this);
mutable_this->DataStart(storage_start);
mutable_this->DataEnd(storage_start+data_length);
mutable_this->StorageEnd(storage_start+storage_length);
mutable_this->DataEnd(storage_start + data_length);
mutable_this->StorageLength(data_length + 1);
}
@ -100,15 +108,21 @@ nsXPIDLString::DebugPrintStats( FILE* aOutFile )
const nsXPIDLString::shared_buffer_handle_type*
nsXPIDLString::GetSharedBufferHandle() const
{
const nsImportedStringHandle<char_type>* answer = NS_STATIC_CAST(const nsImportedStringHandle<char_type>*, mBuffer.get());
if ( answer && !answer->DataEnd() && answer->StorageStart() )
answer->RecalculateBoundaries();
self_type* mutable_this = NS_CONST_CAST(self_type*, this);
if ( !mBuffer->DataStart() )
// XXXldb This isn't any good. What if we just called
// PrepareForUseAsOutParam and it hasn't been filled in yet?
mutable_this->mBuffer = GetSharedEmptyBufferHandle();
else if ( !mBuffer->DataEnd() )
// Our handle may not be an nsImportedStringHandle. However, if it
// is not, this cast will still be safe since no other handle will
// be in this state.
NS_STATIC_CAST(const nsImportedStringHandle<char_type>*, mBuffer.get())->RecalculateBoundaries();
#if DEBUG_STRING_STATS
++sShareCount;
#endif
return answer;
return mBuffer.get();
}
@ -125,6 +139,20 @@ nsXPIDLString::PrepareForUseAsOutParam()
return handle->AddressOfStorageStart();
}
/* static */
nsXPIDLString::shared_buffer_handle_type*
nsXPIDLString::GetSharedEmptyBufferHandle()
{
static shared_buffer_handle_type* sBufferHandle = nsnull;
if (!sBufferHandle) {
sBufferHandle = NS_AllocateContiguousHandleWithData(sBufferHandle,
PRUint32(1), (self_type*)nsnull);
sBufferHandle->AcquireReference();
}
return sBufferHandle;
}
#if DEBUG_STRING_STATS
const nsXPIDLCString::buffer_handle_type*
@ -155,15 +183,21 @@ nsXPIDLCString::DebugPrintStats( FILE* aOutFile )
const nsXPIDLCString::shared_buffer_handle_type*
nsXPIDLCString::GetSharedBufferHandle() const
{
const nsImportedStringHandle<char_type>* answer = NS_STATIC_CAST(const nsImportedStringHandle<char_type>*, mBuffer.get());
if ( answer && !answer->DataEnd() && answer->StorageStart() )
answer->RecalculateBoundaries();
self_type* mutable_this = NS_CONST_CAST(self_type*, this);
if ( !mBuffer->DataStart() )
// XXXldb This isn't any good. What if we just called
// PrepareForUseAsOutParam and it hasn't been filled in yet?
mutable_this->mBuffer = GetSharedEmptyBufferHandle();
else if ( !mBuffer->DataEnd() )
// Our handle may not be an nsImportedStringHandle. However, if it
// is not, this cast will still be safe since no other handle will
// be in this state.
NS_STATIC_CAST(const nsImportedStringHandle<char_type>*, mBuffer.get())->RecalculateBoundaries();
#if DEBUG_STRING_STATS
++sShareCount;
#endif
return answer;
return mBuffer.get();
}
@ -179,3 +213,17 @@ nsXPIDLCString::PrepareForUseAsOutParam()
#endif
return handle->AddressOfStorageStart();
}
/* static */
nsXPIDLCString::shared_buffer_handle_type*
nsXPIDLCString::GetSharedEmptyBufferHandle()
{
static shared_buffer_handle_type* sBufferHandle = nsnull;
if (!sBufferHandle) {
sBufferHandle = NS_AllocateContiguousHandleWithData(sBufferHandle,
PRUint32(1), (self_type*)nsnull);
sBufferHandle->AcquireReference();
}
return sBufferHandle;
}

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
@ -293,19 +293,14 @@ nsSHistory::PrintHistory()
if (uri)
uri->GetSpec(getter_Copies(url));
#if 0
char * titleCStr=nsnull;
nsString titlestr(title);
titleCStr = ToNewCString(titlestr);
#if 0
printf("**** SH Transaction #%d, Entry = %x\n", index, entry.get());
printf("\t\t URL = %s\n", url);
printf("\t\t Title = %s\n", titleCStr);
printf("\t\t Title = %s\n", NS_LossyConvertUCS2toASCII(title).get());
printf("\t\t layout History Data = %x\n", layoutHistoryState);
Recycle(titleCStr);
#endif
#endif
Recycle(title);
nsMemory::Free(title);
nsCOMPtr<nsISHTransaction> next;

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file