diff --git a/extensions/finger/nsFingerChannel.cpp b/extensions/finger/nsFingerChannel.cpp index 702ed9f86258..029174c72af1 100644 --- a/extensions/finger/nsFingerChannel.cpp +++ b/extensions/finger/nsFingerChannel.cpp @@ -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; diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index c6e37ae4b9a7..26a38632ca4d 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -2301,6 +2301,7 @@ protected: NS_REINTERPRET_CAST(PRUnichar *, JS_GetStringChars(str) + JS_GetStringLength(str)), + JS_GetStringLength(str) + 1, mAllocator), mAllocator(str) { } diff --git a/mailnews/compose/src/nsMsgSendReport.cpp b/mailnews/compose/src/nsMsgSendReport.cpp index 7562017e1c62..c31879890d08 100644 --- a/mailnews/compose/src/nsMsgSendReport.cpp +++ b/mailnews/compose/src/nsMsgSendReport.cpp @@ -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); diff --git a/mailnews/mime/emitters/src/nsMimeHtmlEmitter.cpp b/mailnews/mime/emitters/src/nsMimeHtmlEmitter.cpp index d2f02156a331..efc41a80283b 100644 --- a/mailnews/mime/emitters/src/nsMimeHtmlEmitter.cpp +++ b/mailnews/mime/emitters/src/nsMimeHtmlEmitter.cpp @@ -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 ) diff --git a/modules/libpr0n/decoders/icon/win/nsIconChannel.cpp b/modules/libpr0n/decoders/icon/win/nsIconChannel.cpp index d55168696823..11adff16e0d2 100644 --- a/modules/libpr0n/decoders/icon/win/nsIconChannel.cpp +++ b/modules/libpr0n/decoders/icon/win/nsIconChannel.cpp @@ -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); } } diff --git a/netwerk/base/src/nsDirectoryIndexStream.cpp b/netwerk/base/src/nsDirectoryIndexStream.cpp index 5034dadc4da0..5db9ccd2e14b 100644 --- a/netwerk/base/src/nsDirectoryIndexStream.cpp +++ b/netwerk/base/src/nsDirectoryIndexStream.cpp @@ -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 } diff --git a/netwerk/protocol/finger/src/nsFingerChannel.cpp b/netwerk/protocol/finger/src/nsFingerChannel.cpp index 702ed9f86258..029174c72af1 100644 --- a/netwerk/protocol/finger/src/nsFingerChannel.cpp +++ b/netwerk/protocol/finger/src/nsFingerChannel.cpp @@ -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; diff --git a/netwerk/protocol/http/src/nsHttpHandler.cpp b/netwerk/protocol/http/src/nsHttpHandler.cpp index fa595f0b480a..10e492d8ca3b 100644 --- a/netwerk/protocol/http/src/nsHttpHandler.cpp +++ b/netwerk/protocol/http/src/nsHttpHandler.cpp @@ -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; diff --git a/string/public/nsAFlatString.h b/string/public/nsAFlatString.h index a79b3944a65a..0033762a33bb 100644 --- a/string/public/nsAFlatString.h +++ b/string/public/nsAFlatString.h @@ -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___) */ diff --git a/string/public/nsAString.h b/string/public/nsAString.h index 362039a55fc7..a36d632dadea 100644 --- a/string/public/nsAString.h +++ b/string/public/nsAString.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; diff --git a/string/public/nsBufferHandle.h b/string/public/nsBufferHandle.h index c12311762895..ee4566442736 100755 --- a/string/public/nsBufferHandle.h +++ b/string/public/nsBufferHandle.h @@ -31,9 +31,6 @@ #include "nsStringDefines.h" #endif -#include - // 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 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 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 nsSharedBufferHandle : public nsBufferHandle { + 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(aDataStart, aDataEnd) - { - mFlags = kIsShared; - } - - nsSharedBufferHandle( CharT* aDataStart, CharT* aDataEnd, CharT*, CharT*, PRBool isSingleAllocation ) - : nsBufferHandle(aDataStart, aDataEnd) + nsSharedBufferHandle( CharT* aDataStart, CharT* aDataEnd, size_type aStorageLength, PRBool isSingleAllocation ) + : nsBufferHandle(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 nsFlexBufferHandle - : public nsSharedBufferHandle - { - public: - nsFlexBufferHandle( CharT* aDataStart, CharT* aDataEnd, CharT* aStorageStart, CharT* aStorageEnd ) - : nsSharedBufferHandle(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 nsSharedBufferHandleWithAllocator : public nsSharedBufferHandle { public: - nsSharedBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd, nsStringAllocator& aAllocator ) - : nsSharedBufferHandle(aDataStart, aDataEnd), - mAllocator(aAllocator) - { - this->mFlags |= this->kIsUserAllocator; - } + // why is this needed again? + typedef PRUint32 size_type; - nsStringAllocator& get_allocator() const { return mAllocator; } - - protected: - nsStringAllocator& mAllocator; - }; - -template -class nsFlexBufferHandleWithAllocator - : public nsFlexBufferHandle - { - public: - nsFlexBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd, - CharT* aStorageStart, CharT* aStorageEnd, - nsStringAllocator& aAllocator ) - : nsFlexBufferHandle(aDataStart, aDataEnd, aStorageStart, aStorageEnd), + nsSharedBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd, size_type aStorageLength, nsStringAllocator& aAllocator ) + : nsSharedBufferHandle(aDataStart, aDataEnd, + aStorageLength, PR_FALSE), mAllocator(aAllocator) { this->mFlags |= this->kIsUserAllocator; @@ -338,10 +317,7 @@ nsSharedBufferHandle::get_allocator() const { if ( mFlags & kIsUserAllocator ) { - if ( mFlags & kIsStorageDefinedSeparately ) - return NS_REINTERPRET_CAST(const nsFlexBufferHandleWithAllocator*, this)->get_allocator(); - else - return NS_REINTERPRET_CAST(const nsSharedBufferHandleWithAllocator*, this)->get_allocator(); + return NS_REINTERPRET_CAST(const nsSharedBufferHandleWithAllocator*, this)->get_allocator(); } return nsStringAllocatorTraits::global_string_allocator(); @@ -357,9 +333,6 @@ nsSharedBufferHandle::~nsSharedBufferHandle() if ( !(mFlags & kIsSingleAllocationWithBuffer) ) { CharT* string_storage = this->mDataStart; - if ( mFlags & kIsStorageDefinedSeparately ) - string_storage = NS_REINTERPRET_CAST(nsFlexBufferHandle*, this)->StorageStart(); - get_allocator().Deallocate(string_storage); } } diff --git a/string/public/nsBufferHandleUtils.h b/string/public/nsBufferHandleUtils.h index afbf3d7bcc10..52d396327e0c 100644 --- a/string/public/nsBufferHandleUtils.h +++ b/string/public/nsBufferHandleUtils.h @@ -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; diff --git a/string/public/nsSharableString.h b/string/public/nsSharableString.h index c962b2d0ba3e..f84ee8b8ee65 100644 --- a/string/public/nsSharableString.h +++ b/string/public/nsSharableString.h @@ -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 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 mBuffer; }; diff --git a/string/public/nsSharedBufferList.h b/string/public/nsSharedBufferList.h index 43ae2da72f76..335d982cd4d9 100755 --- a/string/public/nsSharedBufferList.h +++ b/string/public/nsSharedBufferList.h @@ -62,14 +62,12 @@ class NS_COM nsSharedBufferList public: class Buffer - : public nsFlexBufferHandle + : public nsSharedBufferHandle { public: - Buffer( PRUnichar* aDataStart, PRUnichar* aDataEnd, PRUnichar* aStorageStart, PRUnichar* aStorageEnd, PRBool aIsSingleAllocation=PR_FALSE ) - : nsFlexBufferHandle(aDataStart, aDataEnd, aStorageStart, aStorageEnd) + Buffer( PRUnichar* aDataStart, PRUnichar* aDataEnd, size_type aStorageLength, PRBool aIsSingleAllocation=PR_FALSE ) + : nsSharedBufferHandle(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 ); diff --git a/string/public/nsXPIDLString.h b/string/public/nsXPIDLString.h index 5d8cb4935056..50995fb37835 100644 --- a/string/public/nsXPIDLString.h +++ b/string/public/nsXPIDLString.h @@ -32,9 +32,66 @@ #include #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 diff --git a/string/src/nsSharableString.cpp b/string/src/nsSharableString.cpp index 66a4eb865771..809b7c18f91a 100644 --- a/string/src/nsSharableString.cpp +++ b/string/src/nsSharableString.cpp @@ -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::length(aNewValue); + mBuffer = new nsSharedBufferHandle(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::length(aNewValue); + mBuffer = new nsSharedBufferHandle(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; + } + diff --git a/string/src/nsSharedBufferList.cpp b/string/src/nsSharedBufferList.cpp index f6f570fd88e4..684f75c36f2d 100755 --- a/string/src/nsSharedBufferList.cpp +++ b/string/src/nsSharedBufferList.cpp @@ -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)); diff --git a/string/src/nsSlidingString.cpp b/string/src/nsSlidingString.cpp index be8d01cb1f69..09e61c940d65 100755 --- a/string/src/nsSlidingString.cpp +++ b/string/src/nsSlidingString.cpp @@ -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(); diff --git a/string/src/nsXPIDLString.cpp b/string/src/nsXPIDLString.cpp index 2ece11117e22..daf12dcd9f74 100644 --- a/string/src/nsXPIDLString.cpp +++ b/string/src/nsXPIDLString.cpp @@ -21,6 +21,16 @@ * Scott Collins (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 nsImportedStringHandle - : public nsFlexBufferHandle + : public nsSharedBufferHandle { public: - nsImportedStringHandle() : nsFlexBufferHandle(0, 0, 0, 0) { } + nsImportedStringHandle() : nsSharedBufferHandle(0, 0, 0, PR_FALSE) { } - CharT** AddressOfStorageStart() { return &(this->mStorageStart); } + CharT** AddressOfStorageStart() { return &(this->mDataStart); } void RecalculateBoundaries() const; }; @@ -55,19 +65,17 @@ void nsImportedStringHandle::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::length(storage_start); - storage_length = data_length + 1; } nsImportedStringHandle* mutable_this = NS_CONST_CAST(nsImportedStringHandle*, 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* answer = NS_STATIC_CAST(const nsImportedStringHandle*, 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*, 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* answer = NS_STATIC_CAST(const nsImportedStringHandle*, 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*, 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; + } diff --git a/xpcom/io/nsLocalFileUnix.cpp b/xpcom/io/nsLocalFileUnix.cpp index d2ff519713b0..e1ce0a0ccc27 100644 --- a/xpcom/io/nsLocalFileUnix.cpp +++ b/xpcom/io/nsLocalFileUnix.cpp @@ -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; diff --git a/xpcom/string/public/nsAFlatString.h b/xpcom/string/public/nsAFlatString.h index a79b3944a65a..0033762a33bb 100644 --- a/xpcom/string/public/nsAFlatString.h +++ b/xpcom/string/public/nsAFlatString.h @@ -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___) */ diff --git a/xpcom/string/public/nsAString.h b/xpcom/string/public/nsAString.h index 362039a55fc7..a36d632dadea 100644 --- a/xpcom/string/public/nsAString.h +++ b/xpcom/string/public/nsAString.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; diff --git a/xpcom/string/public/nsBufferHandle.h b/xpcom/string/public/nsBufferHandle.h index c12311762895..ee4566442736 100755 --- a/xpcom/string/public/nsBufferHandle.h +++ b/xpcom/string/public/nsBufferHandle.h @@ -31,9 +31,6 @@ #include "nsStringDefines.h" #endif -#include - // 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 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 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 nsSharedBufferHandle : public nsBufferHandle { + 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(aDataStart, aDataEnd) - { - mFlags = kIsShared; - } - - nsSharedBufferHandle( CharT* aDataStart, CharT* aDataEnd, CharT*, CharT*, PRBool isSingleAllocation ) - : nsBufferHandle(aDataStart, aDataEnd) + nsSharedBufferHandle( CharT* aDataStart, CharT* aDataEnd, size_type aStorageLength, PRBool isSingleAllocation ) + : nsBufferHandle(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 nsFlexBufferHandle - : public nsSharedBufferHandle - { - public: - nsFlexBufferHandle( CharT* aDataStart, CharT* aDataEnd, CharT* aStorageStart, CharT* aStorageEnd ) - : nsSharedBufferHandle(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 nsSharedBufferHandleWithAllocator : public nsSharedBufferHandle { public: - nsSharedBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd, nsStringAllocator& aAllocator ) - : nsSharedBufferHandle(aDataStart, aDataEnd), - mAllocator(aAllocator) - { - this->mFlags |= this->kIsUserAllocator; - } + // why is this needed again? + typedef PRUint32 size_type; - nsStringAllocator& get_allocator() const { return mAllocator; } - - protected: - nsStringAllocator& mAllocator; - }; - -template -class nsFlexBufferHandleWithAllocator - : public nsFlexBufferHandle - { - public: - nsFlexBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd, - CharT* aStorageStart, CharT* aStorageEnd, - nsStringAllocator& aAllocator ) - : nsFlexBufferHandle(aDataStart, aDataEnd, aStorageStart, aStorageEnd), + nsSharedBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd, size_type aStorageLength, nsStringAllocator& aAllocator ) + : nsSharedBufferHandle(aDataStart, aDataEnd, + aStorageLength, PR_FALSE), mAllocator(aAllocator) { this->mFlags |= this->kIsUserAllocator; @@ -338,10 +317,7 @@ nsSharedBufferHandle::get_allocator() const { if ( mFlags & kIsUserAllocator ) { - if ( mFlags & kIsStorageDefinedSeparately ) - return NS_REINTERPRET_CAST(const nsFlexBufferHandleWithAllocator*, this)->get_allocator(); - else - return NS_REINTERPRET_CAST(const nsSharedBufferHandleWithAllocator*, this)->get_allocator(); + return NS_REINTERPRET_CAST(const nsSharedBufferHandleWithAllocator*, this)->get_allocator(); } return nsStringAllocatorTraits::global_string_allocator(); @@ -357,9 +333,6 @@ nsSharedBufferHandle::~nsSharedBufferHandle() if ( !(mFlags & kIsSingleAllocationWithBuffer) ) { CharT* string_storage = this->mDataStart; - if ( mFlags & kIsStorageDefinedSeparately ) - string_storage = NS_REINTERPRET_CAST(nsFlexBufferHandle*, this)->StorageStart(); - get_allocator().Deallocate(string_storage); } } diff --git a/xpcom/string/public/nsBufferHandleUtils.h b/xpcom/string/public/nsBufferHandleUtils.h index afbf3d7bcc10..52d396327e0c 100644 --- a/xpcom/string/public/nsBufferHandleUtils.h +++ b/xpcom/string/public/nsBufferHandleUtils.h @@ -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; diff --git a/xpcom/string/public/nsSharableString.h b/xpcom/string/public/nsSharableString.h index c962b2d0ba3e..f84ee8b8ee65 100644 --- a/xpcom/string/public/nsSharableString.h +++ b/xpcom/string/public/nsSharableString.h @@ -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 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 mBuffer; }; diff --git a/xpcom/string/public/nsSharedBufferList.h b/xpcom/string/public/nsSharedBufferList.h index 43ae2da72f76..335d982cd4d9 100755 --- a/xpcom/string/public/nsSharedBufferList.h +++ b/xpcom/string/public/nsSharedBufferList.h @@ -62,14 +62,12 @@ class NS_COM nsSharedBufferList public: class Buffer - : public nsFlexBufferHandle + : public nsSharedBufferHandle { public: - Buffer( PRUnichar* aDataStart, PRUnichar* aDataEnd, PRUnichar* aStorageStart, PRUnichar* aStorageEnd, PRBool aIsSingleAllocation=PR_FALSE ) - : nsFlexBufferHandle(aDataStart, aDataEnd, aStorageStart, aStorageEnd) + Buffer( PRUnichar* aDataStart, PRUnichar* aDataEnd, size_type aStorageLength, PRBool aIsSingleAllocation=PR_FALSE ) + : nsSharedBufferHandle(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 ); diff --git a/xpcom/string/public/nsXPIDLString.h b/xpcom/string/public/nsXPIDLString.h index 5d8cb4935056..50995fb37835 100644 --- a/xpcom/string/public/nsXPIDLString.h +++ b/xpcom/string/public/nsXPIDLString.h @@ -32,9 +32,66 @@ #include #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 diff --git a/xpcom/string/src/nsSharableString.cpp b/xpcom/string/src/nsSharableString.cpp index 66a4eb865771..809b7c18f91a 100644 --- a/xpcom/string/src/nsSharableString.cpp +++ b/xpcom/string/src/nsSharableString.cpp @@ -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::length(aNewValue); + mBuffer = new nsSharedBufferHandle(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::length(aNewValue); + mBuffer = new nsSharedBufferHandle(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; + } + diff --git a/xpcom/string/src/nsSharedBufferList.cpp b/xpcom/string/src/nsSharedBufferList.cpp index f6f570fd88e4..684f75c36f2d 100755 --- a/xpcom/string/src/nsSharedBufferList.cpp +++ b/xpcom/string/src/nsSharedBufferList.cpp @@ -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)); diff --git a/xpcom/string/src/nsSlidingString.cpp b/xpcom/string/src/nsSlidingString.cpp index be8d01cb1f69..09e61c940d65 100755 --- a/xpcom/string/src/nsSlidingString.cpp +++ b/xpcom/string/src/nsSlidingString.cpp @@ -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(); diff --git a/xpcom/string/src/nsXPIDLString.cpp b/xpcom/string/src/nsXPIDLString.cpp index 2ece11117e22..daf12dcd9f74 100644 --- a/xpcom/string/src/nsXPIDLString.cpp +++ b/xpcom/string/src/nsXPIDLString.cpp @@ -21,6 +21,16 @@ * Scott Collins (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 nsImportedStringHandle - : public nsFlexBufferHandle + : public nsSharedBufferHandle { public: - nsImportedStringHandle() : nsFlexBufferHandle(0, 0, 0, 0) { } + nsImportedStringHandle() : nsSharedBufferHandle(0, 0, 0, PR_FALSE) { } - CharT** AddressOfStorageStart() { return &(this->mStorageStart); } + CharT** AddressOfStorageStart() { return &(this->mDataStart); } void RecalculateBoundaries() const; }; @@ -55,19 +65,17 @@ void nsImportedStringHandle::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::length(storage_start); - storage_length = data_length + 1; } nsImportedStringHandle* mutable_this = NS_CONST_CAST(nsImportedStringHandle*, 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* answer = NS_STATIC_CAST(const nsImportedStringHandle*, 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*, 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* answer = NS_STATIC_CAST(const nsImportedStringHandle*, 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*, 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; + } diff --git a/xpfe/components/shistory/src/nsSHTransaction.cpp b/xpfe/components/shistory/src/nsSHTransaction.cpp index 110c774d55f6..d92a33991457 100644 --- a/xpfe/components/shistory/src/nsSHTransaction.cpp +++ b/xpfe/components/shistory/src/nsSHTransaction.cpp @@ -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 diff --git a/xpfe/components/shistory/src/nsSHTransaction.h b/xpfe/components/shistory/src/nsSHTransaction.h index 543761c41c6c..a3df8719c6e7 100644 --- a/xpfe/components/shistory/src/nsSHTransaction.h +++ b/xpfe/components/shistory/src/nsSHTransaction.h @@ -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 diff --git a/xpfe/components/shistory/src/nsSHistory.cpp b/xpfe/components/shistory/src/nsSHistory.cpp index f781631b855f..8f6cadef411f 100644 --- a/xpfe/components/shistory/src/nsSHistory.cpp +++ b/xpfe/components/shistory/src/nsSHistory.cpp @@ -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 next; diff --git a/xpfe/components/shistory/src/nsSHistory.h b/xpfe/components/shistory/src/nsSHistory.h index baaa40ae6b7c..53c82ff9f642 100644 --- a/xpfe/components/shistory/src/nsSHistory.h +++ b/xpfe/components/shistory/src/nsSHistory.h @@ -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