landing xpcom patch for bug 235499 "re-enable string buffer sharing between C++ and JS in xpconnect" r=dbaron sr=jst

git-svn-id: svn://10.0.0.236/trunk@168722 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
darin%meer.net
2005-02-02 22:18:37 +00:00
parent 6b042de066
commit ba67c97eb4
6 changed files with 400 additions and 106 deletions

View File

@@ -47,6 +47,7 @@
#include <stdlib.h>
#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"

View File

@@ -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)
{