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

This commit is contained in:
darin%meer.net 2005-02-02 22:18:37 +00:00
parent 79cb60be4a
commit dd78f625f5
6 changed files with 400 additions and 106 deletions

View File

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

View File

@ -59,6 +59,7 @@ EXPORTS = \
nsPromiseFlatString.h \
nsReadableUtils.h \
nsString.h \
nsStringBuffer.h \
nsStringFwd.h \
nsStringIterator.h \
nsSubstring.h \

View File

@ -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 <darin@meer.net>
*
* 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__ */

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

View File

@ -37,6 +37,7 @@
#include <stdio.h>
#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 }
};