зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
f7590852c8
Коммит
824e7d22ca
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче