diff --git a/mozilla/xpcom/build/dlldeps.cpp b/mozilla/xpcom/build/dlldeps.cpp index 08ad653f312..dcf0ef22cc8 100644 --- a/mozilla/xpcom/build/dlldeps.cpp +++ b/mozilla/xpcom/build/dlldeps.cpp @@ -91,6 +91,7 @@ #include "nsInterfaceRequestorAgg.h" #include "nsHashPropertyBag.h" #include "nsStringAPI.h" +#include "nsStringBuffer.h" void XXXNeverCalled() { @@ -220,4 +221,19 @@ void XXXNeverCalled() NS_StringCloneData(str1); NS_UTF16ToCString(str1, NS_CSTRING_ENCODING_ASCII, str2); NS_CStringToUTF16(str2, NS_CSTRING_ENCODING_ASCII, str1); + + // nsStringBuffer.h + { + nsString x; + nsCString y; + nsStringBuffer b; + b.AddRef(); + b.Release(); + nsStringBuffer::Alloc(0); + nsStringBuffer::Realloc(nsnull, 0); + nsStringBuffer::FromString(x); + nsStringBuffer::FromString(y); + b.ToString(0, x); + b.ToString(0, y); + } } diff --git a/mozilla/xpcom/string/public/Makefile.in b/mozilla/xpcom/string/public/Makefile.in index d3582bcc558..c706f2e5d41 100644 --- a/mozilla/xpcom/string/public/Makefile.in +++ b/mozilla/xpcom/string/public/Makefile.in @@ -59,6 +59,7 @@ EXPORTS = \ nsPromiseFlatString.h \ nsReadableUtils.h \ nsString.h \ + nsStringBuffer.h \ nsStringFwd.h \ nsStringIterator.h \ nsSubstring.h \ diff --git a/mozilla/xpcom/string/public/nsStringBuffer.h b/mozilla/xpcom/string/public/nsStringBuffer.h new file mode 100644 index 00000000000..acad897d497 --- /dev/null +++ b/mozilla/xpcom/string/public/nsStringBuffer.h @@ -0,0 +1,170 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is IBM Corporation. + * Portions created by IBM Corporation are Copyright (C) 2003 + * IBM Corporation. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsStringBuffer_h__ +#define nsStringBuffer_h__ + +/** + * This structure precedes the string buffers "we" allocate. It may be the + * case that nsTAString::mData does not point to one of these special + * buffers. The mFlags member variable distinguishes the buffer type. + * + * When this header is in use, it enables reference counting, and capacity + * tracking. NOTE: A string buffer can be modified only if its reference + * count is 1. + */ +class nsStringBuffer + { + private: + + PRInt32 mRefCount; + PRUint32 mStorageSize; + + public: + + /** + * Allocates a new string buffer, with given size in bytes and a + * reference count of one. When the string buffer is no longer needed, + * it should be released via Release. + * + * It is up to the caller to set the bytes corresponding to the string + * buffer by calling the Data method to fetch the raw data pointer. Care + * must be taken to properly null terminate the character array. The + * storage size can be greater than the length of the actual string + * (i.e., it is not required that the null terminator appear in the last + * storage unit of the string buffer's data). + * + * @return new string buffer or null if out of memory. + */ + NS_COM static nsStringBuffer* Alloc(size_t storageSize); + + /** + * Resizes the given string buffer to the specified storage size. This + * method must not be called on a readonly string buffer. Use this API + * carefully!! + * + * This method behaves like the ANSI-C realloc function. (i.e., If the + * allocation fails, null will be returned and the given string buffer + * will remain unmodified.) + * + * @see IsReadonly + */ + NS_COM static nsStringBuffer* Realloc(nsStringBuffer* buf, size_t storageSize); + + /** + * Increment the reference count on this string buffer. + */ + NS_COM void NS_FASTCALL AddRef(); + + /** + * Decrement the reference count on this string buffer. The string + * buffer will be destroyed when its reference count reaches zero. + */ + NS_COM void NS_FASTCALL Release(); + + /** + * This method returns the string buffer corresponding to the given data + * pointer. The data pointer must have been returned previously by a + * call to the nsStringBuffer::Data method. + */ + static nsStringBuffer* FromData(void* data) + { + return (nsStringBuffer*) ( ((char*) data) - sizeof(nsStringBuffer) ); + } + + /** + * This method returns the data pointer for this string buffer. + */ + void* Data() const + { + return (void*) ( ((char*) this) + sizeof(nsStringBuffer) ); + } + + /** + * This function returns the storage size of a string buffer in bytes. + * This value is the same value that was originally passed to Alloc (or + * Realloc). + */ + PRUint32 StorageSize() const + { + return mStorageSize; + } + + /** + * If this method returns false, then the caller can be sure that their + * reference to the string buffer is the only reference to the string + * buffer, and therefore it has exclusive access to the string buffer and + * associated data. However, if this function returns true, then other + * consumers may rely on the data in this buffer being immutable and + * other threads may access this buffer simultaneously. + */ + PRBool IsReadonly() const + { + return mRefCount > 1; + } + + /** + * The FromString methods return a string buffer for the given string + * object or null if the string object does not have a string buffer. + * The reference count of the string buffer is NOT incremented by these + * methods. If the caller wishes to hold onto the returned value, then + * the returned string buffer must have its reference count incremented + * via a call to the AddRef method. + */ + NS_COM static nsStringBuffer* FromString(const nsAString &str); + NS_COM static nsStringBuffer* FromString(const nsACString &str); + + /** + * The ToString methods assign this string buffer to a given string + * object. If the string object does not support sharable string + * buffers, then its value will be set to a copy of the given string + * buffer. Otherwise, these methods increment the reference count of the + * given string buffer. It is important to specify the length (in + * storage units) of the string contained in the string buffer since the + * length of the string may be less than its storage size. The string + * must have a null terminator at the offset specified by |len|. + * + * NOTE: storage size is measured in bytes even for wide strings; + * however, string length is always measured in storage units + * (2-byte units for wide strings). + */ + NS_COM void ToString(PRUint32 len, nsAString &str); + NS_COM void ToString(PRUint32 len, nsACString &str); + }; + +#endif /* !defined(nsStringBuffer_h__ */ diff --git a/mozilla/xpcom/string/src/nsSubstring.cpp b/mozilla/xpcom/string/src/nsSubstring.cpp index dc791c5e430..1d1068e5c8c 100644 --- a/mozilla/xpcom/string/src/nsSubstring.cpp +++ b/mozilla/xpcom/string/src/nsSubstring.cpp @@ -47,6 +47,7 @@ #include #include "nsSubstring.h" #include "nsString.h" +#include "nsStringBuffer.h" #include "nsDependentString.h" #include "nsMemory.h" #include "pratom.h" @@ -105,105 +106,6 @@ static nsStringStats gStringStats; #define STRING_STAT_INCREMENT(_s) #endif -// --------------------------------------------------------------------------- - - /** - * This structure precedes the string buffers "we" allocate. It may be the - * case that nsTSubstring::mData does not point to one of these special - * buffers. The mFlags member variable distinguishes the buffer type. - * - * When this header is in use, it enables reference counting, and capacity - * tracking. NOTE: A string buffer can be modified only if its reference - * count is 1. - */ -class nsStringHeader - { - private: - - PRInt32 mRefCount; - PRUint32 mStorageSize; - - public: - - void AddRef() - { - PR_AtomicIncrement(&mRefCount); - STRING_STAT_INCREMENT(Share); - } - - void Release() - { - if (PR_AtomicDecrement(&mRefCount) == 0) - { - STRING_STAT_INCREMENT(Free); - free(this); // we were allocated with |malloc| - } - } - - /** - * Alloc returns a pointer to a new string header with set capacity. - */ - static nsStringHeader* Alloc(size_t size) - { - STRING_STAT_INCREMENT(Alloc); - - NS_ASSERTION(size != 0, "zero capacity allocation not allowed"); - - nsStringHeader *hdr = - (nsStringHeader *) malloc(sizeof(nsStringHeader) + size); - if (hdr) - { - hdr->mRefCount = 1; - hdr->mStorageSize = size; - } - return hdr; - } - - static nsStringHeader* Realloc(nsStringHeader* hdr, size_t size) - { - STRING_STAT_INCREMENT(Realloc); - - NS_ASSERTION(size != 0, "zero capacity allocation not allowed"); - - // no point in trying to save ourselves if we hit this assertion - NS_ASSERTION(!hdr->IsReadonly(), "|Realloc| attempted on readonly string"); - - hdr = (nsStringHeader*) realloc(hdr, sizeof(nsStringHeader) + size); - if (hdr) - hdr->mStorageSize = size; - - return hdr; - } - - static nsStringHeader* FromData(void* data) - { - return (nsStringHeader*) ( ((char*) data) - sizeof(nsStringHeader) ); - } - - void* Data() const - { - return (void*) ( ((char*) this) + sizeof(nsStringHeader) ); - } - - PRUint32 StorageSize() const - { - return mStorageSize; - } - - /** - * Because nsTSubstring allows only single threaded access, if this - * method returns FALSE, then the caller can be sure that it has - * exclusive access to the nsStringHeader and associated data. - * However, if this function returns TRUE, then other strings may - * rely on the data in this buffer being constant and other threads - * may access this buffer simultaneously. - */ - PRBool IsReadonly() const - { - return mRefCount > 1; - } - }; - // --------------------------------------------------------------------------- inline void @@ -211,7 +113,7 @@ ReleaseData( void* data, PRUint32 flags ) { if (flags & nsSubstring::F_SHARED) { - nsStringHeader::FromData(data)->Release(); + nsStringBuffer::FromData(data)->Release(); } else if (flags & nsSubstring::F_OWNED) { @@ -221,6 +123,180 @@ ReleaseData( void* data, PRUint32 flags ) // otherwise, nothing to do. } +// --------------------------------------------------------------------------- + +// XXX or we could make nsStringBuffer be a friend of nsTAString + +class nsAStringAccessor : public nsAString + { + private: + nsAStringAccessor(); // NOT IMPLEMENTED + + public: + const void *vtable() const { return mVTable; } + char_type *data() const { return mData; } + size_type length() const { return mLength; } + PRUint32 flags() const { return mFlags; } + + void set(char_type *data, size_type len, PRUint32 flags) + { + ReleaseData(mData, mFlags); + mData = data; + mLength = len; + mFlags = flags; + } + }; + +class nsACStringAccessor : public nsACString + { + private: + nsACStringAccessor(); // NOT IMPLEMENTED + + public: + const void *vtable() const { return mVTable; } + char_type *data() const { return mData; } + size_type length() const { return mLength; } + PRUint32 flags() const { return mFlags; } + + void set(char_type *data, size_type len, PRUint32 flags) + { + ReleaseData(mData, mFlags); + mData = data; + mLength = len; + mFlags = flags; + } + }; + +// --------------------------------------------------------------------------- + +void +nsStringBuffer::AddRef() + { + PR_AtomicIncrement(&mRefCount); + STRING_STAT_INCREMENT(Share); + } + +void +nsStringBuffer::Release() + { + if (PR_AtomicDecrement(&mRefCount) == 0) + { + STRING_STAT_INCREMENT(Free); + free(this); // we were allocated with |malloc| + } + } + + /** + * Alloc returns a pointer to a new string header with set capacity. + */ +nsStringBuffer* +nsStringBuffer::Alloc(size_t size) + { + STRING_STAT_INCREMENT(Alloc); + + NS_ASSERTION(size != 0, "zero capacity allocation not allowed"); + + nsStringBuffer *hdr = + (nsStringBuffer *) malloc(sizeof(nsStringBuffer) + size); + if (hdr) + { + hdr->mRefCount = 1; + hdr->mStorageSize = size; + } + return hdr; + } + +nsStringBuffer* +nsStringBuffer::Realloc(nsStringBuffer* hdr, size_t size) + { + STRING_STAT_INCREMENT(Realloc); + + NS_ASSERTION(size != 0, "zero capacity allocation not allowed"); + + // no point in trying to save ourselves if we hit this assertion + NS_ASSERTION(!hdr->IsReadonly(), "|Realloc| attempted on readonly string"); + + hdr = (nsStringBuffer*) realloc(hdr, sizeof(nsStringBuffer) + size); + if (hdr) + hdr->mStorageSize = size; + + return hdr; + } + +nsStringBuffer* +nsStringBuffer::FromString(const nsAString& str) + { + const nsAStringAccessor* accessor = + NS_STATIC_CAST(const nsAStringAccessor*, &str); + + if (accessor->vtable() != nsObsoleteAString::sCanonicalVTable || + !(accessor->flags() & nsSubstring::F_SHARED)) + return nsnull; + + return FromData(accessor->data()); + } + +nsStringBuffer* +nsStringBuffer::FromString(const nsACString& str) + { + const nsACStringAccessor* accessor = + NS_STATIC_CAST(const nsACStringAccessor*, &str); + + if (accessor->vtable() != nsObsoleteACString::sCanonicalVTable || + !(accessor->flags() & nsCSubstring::F_SHARED)) + return nsnull; + + return FromData(accessor->data()); + } + +void +nsStringBuffer::ToString(PRUint32 len, nsAString &str) + { + PRUnichar* data = NS_STATIC_CAST(PRUnichar*, Data()); + + nsAStringAccessor* accessor = NS_STATIC_CAST(nsAStringAccessor*, &str); + if (accessor->vtable() != nsObsoleteAString::sCanonicalVTable) + { + str.Assign(data, len); + } + else + { + NS_ASSERTION(data[len] == PRUnichar(0), "data should be null terminated"); + + // preserve class flags + PRUint32 flags = accessor->flags(); + flags = (flags & 0xFFFF0000) | nsSubstring::F_SHARED | nsSubstring::F_TERMINATED; + + AddRef(); + accessor->set(data, len, flags); + } + } + +void +nsStringBuffer::ToString(PRUint32 len, nsACString &str) + { + char* data = NS_STATIC_CAST(char*, Data()); + + nsACStringAccessor* accessor = NS_STATIC_CAST(nsACStringAccessor*, &str); + if (accessor->vtable() != nsObsoleteACString::sCanonicalVTable) + { + str.Assign(data, len); + } + else + { + NS_ASSERTION(data[len] == char(0), "data should be null terminated"); + + // preserve class flags + PRUint32 flags = accessor->flags(); + flags = (flags & 0xFFFF0000) | nsCSubstring::F_SHARED | nsCSubstring::F_TERMINATED; + + AddRef(); + accessor->set(data, len, flags); + } + } + +// --------------------------------------------------------------------------- + // define nsSubstring #include "string-template-def-unichar.h" diff --git a/mozilla/xpcom/string/src/nsTSubstring.cpp b/mozilla/xpcom/string/src/nsTSubstring.cpp index eace2675687..f7bff361572 100644 --- a/mozilla/xpcom/string/src/nsTSubstring.cpp +++ b/mozilla/xpcom/string/src/nsTSubstring.cpp @@ -110,10 +110,10 @@ nsTSubstring_CharT::MutatePrep( size_type capacity, char_type** oldData, PRUint3 // case #1 if (mFlags & F_SHARED) { - nsStringHeader* hdr = nsStringHeader::FromData(mData); + nsStringBuffer* hdr = nsStringBuffer::FromData(mData); if (!hdr->IsReadonly()) { - nsStringHeader *newHdr = nsStringHeader::Realloc(hdr, storageSize); + nsStringBuffer *newHdr = nsStringBuffer::Realloc(hdr, storageSize); if (newHdr) { hdr = newHdr; @@ -145,7 +145,7 @@ nsTSubstring_CharT::MutatePrep( size_type capacity, char_type** oldData, PRUint3 // make use of our F_OWNED or F_FIXED buffers because they are not // large enough. - nsStringHeader* newHdr = nsStringHeader::Alloc(storageSize); + nsStringBuffer* newHdr = nsStringBuffer::Alloc(storageSize); if (!newHdr) return PR_FALSE; // we are still in a consistent state @@ -247,7 +247,7 @@ nsTSubstring_CharT::Capacity() const if (mFlags & F_SHARED) { // if the string is readonly, then we pretend that it has no capacity. - nsStringHeader* hdr = nsStringHeader::FromData(mData); + nsStringBuffer* hdr = nsStringBuffer::FromData(mData); if (hdr->IsReadonly()) capacity = size_type(-1); else @@ -278,7 +278,7 @@ nsTSubstring_CharT::EnsureMutable() { if (mFlags & (F_FIXED | F_OWNED)) return; - if ((mFlags & F_SHARED) && !nsStringHeader::FromData(mData)->IsReadonly()) + if ((mFlags & F_SHARED) && !nsStringBuffer::FromData(mData)->IsReadonly()) return; // promote to a shared string buffer @@ -358,7 +358,7 @@ nsTSubstring_CharT::Assign( const self_type& str ) SetDataFlags(F_TERMINATED | F_SHARED); // get an owning reference to the mData - nsStringHeader::FromData(mData)->AddRef(); + nsStringBuffer::FromData(mData)->AddRef(); } else if (str.mFlags & F_VOIDED) { diff --git a/mozilla/xpcom/tests/TestStrings.cpp b/mozilla/xpcom/tests/TestStrings.cpp index 71e8aa067ec..86e8caaabf7 100644 --- a/mozilla/xpcom/tests/TestStrings.cpp +++ b/mozilla/xpcom/tests/TestStrings.cpp @@ -37,6 +37,7 @@ #include #include "nsString.h" +#include "nsStringBuffer.h" #include "nsReadableUtils.h" #include "nsCRT.h" @@ -583,6 +584,35 @@ PRBool test_rfindcharinset() return PR_TRUE; } +PRBool test_stringbuffer() + { + const char kData[] = "hello world"; + + nsStringBuffer *buf; + + buf = nsStringBuffer::Alloc(sizeof(kData)); + if (!buf) + return PR_FALSE; + buf->Release(); + + buf = nsStringBuffer::Alloc(sizeof(kData)); + if (!buf) + return PR_FALSE; + char *data = (char *) buf->Data(); + memcpy(data, kData, sizeof(kData)); + + nsCString str; + buf->ToString(sizeof(kData)-1, str); + + nsStringBuffer *buf2; + buf2 = nsStringBuffer::FromString(str); + + PRBool rv = (buf == buf2); + + buf->Release(); + return rv; + } + //---- typedef PRBool (*TestFunc)(); @@ -620,6 +650,7 @@ tests[] = { "test_appendint64", test_appendint64 }, { "test_findcharinset", test_findcharinset }, { "test_rfindcharinset", test_rfindcharinset }, + { "test_stringbuffer", test_stringbuffer }, { nsnull, nsnull } };