gecko-dev/xpcom/ds/nsVoidArray.cpp

798 lines
19 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsVoidArray.h"
#include "nsCRT.h"
#include "nsISizeOfHandler.h"
#include "nsString.h"
static const PRInt32 kGrowArrayBy = 4;
static const PRInt32 kLinearThreshold = 16;
static const PRInt32 kGrowthFactor = 1;
inline PRInt32
nsVoidArray::GetArraySize() const
{
return mImpl ? PRInt32(mImpl->mBits & kArraySizeMask) : 0;
}
inline void
nsVoidArray::SetArraySize(PRInt32 aSize)
{
NS_PRECONDITION(mImpl, "can't set size");
mImpl->mBits &= ~kArraySizeMask;
mImpl->mBits |= PRUint32(aSize & kArraySizeMask);
}
inline PRBool
nsVoidArray::IsArrayOwner() const
{
return mImpl ? PRBool(mImpl->mBits & kArrayOwnerMask) : PR_FALSE;
}
inline void
nsVoidArray::SetArrayOwner(PRBool aOwner)
{
NS_PRECONDITION(mImpl, "can't set owner");
if (aOwner)
mImpl->mBits |= kArrayOwnerMask;
else
mImpl->mBits &= ~kArrayOwnerMask;
}
nsVoidArray::nsVoidArray()
: mImpl(nsnull)
{
MOZ_COUNT_CTOR(nsVoidArray);
}
nsVoidArray::nsVoidArray(PRInt32 aCount)
: mImpl(nsnull)
{
MOZ_COUNT_CTOR(nsVoidArray);
if (aCount) {
char* bytes = new char[sizeof(Impl) + sizeof(void*) * (aCount - 1)];
mImpl = NS_REINTERPRET_CAST(Impl*, bytes);
if (mImpl) {
mImpl->mBits = 0;
SetArraySize(aCount);
mImpl->mCount = aCount;
nsCRT::memset(mImpl->mArray, 0, mImpl->mCount * sizeof(void*));
SetArrayOwner(PR_TRUE);
}
}
}
nsVoidArray& nsVoidArray::operator=(const nsVoidArray& other)
{
if (mImpl && IsArrayOwner())
delete[] NS_REINTERPRET_CAST(char*, mImpl);
PRInt32 otherCount = other.Count();
if (otherCount) {
char* bytes = new char[sizeof(Impl) + sizeof(void*) * (otherCount - 1)];
mImpl = NS_REINTERPRET_CAST(Impl*, bytes);
if (mImpl) {
mImpl->mBits = 0;
SetArraySize(otherCount);
mImpl->mCount = otherCount;
SetArrayOwner(PR_TRUE);
nsCRT::memcpy(mImpl->mArray, other.mImpl->mArray, mImpl->mCount * sizeof(void*));
}
}
else {
mImpl = nsnull;
}
return *this;
}
nsVoidArray::~nsVoidArray()
{
MOZ_COUNT_DTOR(nsVoidArray);
if (mImpl && IsArrayOwner())
delete[] NS_REINTERPRET_CAST(char*, mImpl);
}
void
nsVoidArray::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const
{
if (aResult) {
*aResult = sizeof(*this) + (mImpl ? sizeof(Impl) + (sizeof(void*) * (GetArraySize() - 1)) : 0);
}
}
void* nsVoidArray::ElementAt(PRInt32 aIndex) const
{
if (aIndex < 0 || aIndex >= Count()) {
return nsnull;
}
return mImpl->mArray[aIndex];
}
PRInt32 nsVoidArray::IndexOf(void* aPossibleElement) const
{
if (mImpl) {
void** ap = mImpl->mArray;
void** end = ap + mImpl->mCount;
while (ap < end) {
if (*ap == aPossibleElement) {
return ap - mImpl->mArray;
}
ap++;
}
}
return -1;
}
PRBool nsVoidArray::InsertElementAt(void* aElement, PRInt32 aIndex)
{
PRInt32 oldCount = Count();
if (PRUint32(aIndex) > PRUint32(oldCount)) {
// An invalid index causes the insertion to fail
return PR_FALSE;
}
if (oldCount >= GetArraySize()) {
// We have to grow the array. Grow by 4 slots if we're smaller
// than 16 elements, or a power of two if we're larger.
PRInt32 newCount = oldCount + ((oldCount >= kLinearThreshold) ? (oldCount / kGrowthFactor) : kGrowArrayBy);
char* bytes = new char[sizeof(Impl) + sizeof(void*) * (newCount - 1)];
Impl* newImpl = NS_REINTERPRET_CAST(Impl*, bytes);
if (newImpl) {
if (aIndex != 0)
nsCRT::memcpy(newImpl->mArray, mImpl->mArray, aIndex * sizeof(void*));
PRInt32 slide = oldCount - aIndex;
if (0 != slide) {
// Slide data over to make room for the insertion
nsCRT::memcpy(newImpl->mArray + aIndex + 1, mImpl->mArray + aIndex,
slide * sizeof(void*));
}
if (IsArrayOwner())
delete[] NS_REINTERPRET_CAST(char*, mImpl);
mImpl = newImpl;
mImpl->mBits = 0;
SetArraySize(newCount);
mImpl->mCount = oldCount;
SetArrayOwner(PR_TRUE);
}
}
else {
// The array is already large enough
PRInt32 slide = oldCount - aIndex;
if (0 != slide) {
// Slide data over to make room for the insertion
nsCRT::memmove(mImpl->mArray + aIndex + 1, mImpl->mArray + aIndex,
slide * sizeof(void*));
}
}
mImpl->mArray[aIndex] = aElement;
mImpl->mCount++;
return PR_TRUE;
}
PRBool nsVoidArray::ReplaceElementAt(void* aElement, PRInt32 aIndex)
{
if (PRUint32(aIndex) >= PRUint32(GetArraySize())) {
PRInt32 oldCount = Count();
PRInt32 requestedCount = aIndex + 1;
PRInt32 growDelta = requestedCount - oldCount;
PRInt32 newCount = oldCount + (growDelta > kGrowArrayBy ? growDelta : kGrowArrayBy);
char* bytes = new char[sizeof(Impl) + sizeof(void*) * (newCount - 1)];
Impl* newImpl = NS_REINTERPRET_CAST(Impl*, bytes);
if (newImpl) {
nsCRT::memset(newImpl->mArray, 0, newCount * sizeof(void*));
if (mImpl != nsnull && aIndex != 0)
nsCRT::memcpy(newImpl->mArray, mImpl->mArray, mImpl->mCount * sizeof(void*));
if (IsArrayOwner())
delete[] NS_REINTERPRET_CAST(char*, mImpl);
mImpl = newImpl;
mImpl->mBits = 0;
SetArraySize(newCount);
mImpl->mCount = oldCount;
SetArrayOwner(PR_TRUE);
}
}
mImpl->mArray[aIndex] = aElement;
if (aIndex >= mImpl->mCount)
mImpl->mCount = aIndex + 1;
return PR_TRUE;
}
PRBool nsVoidArray::RemoveElementAt(PRInt32 aIndex)
{
PRInt32 oldCount = Count();
if (PRUint32(aIndex) >= PRUint32(oldCount)) {
// An invalid index causes the replace to fail
return PR_FALSE;
}
// We don't need to move any elements if we're removing the
// last element in the array
if (aIndex < (oldCount - 1)) {
nsCRT::memmove(mImpl->mArray + aIndex, mImpl->mArray + aIndex + 1,
(oldCount - 1 - aIndex) * sizeof(void*));
}
mImpl->mCount--;
return PR_TRUE;
}
PRBool nsVoidArray::RemoveElement(void* aElement)
{
if (mImpl) {
void** ep = mImpl->mArray;
void** end = ep + mImpl->mCount;
while (ep < end) {
void* e = *ep++;
if (e == aElement) {
ep--;
return RemoveElementAt(PRInt32(ep - mImpl->mArray));
}
}
}
return PR_FALSE;
}
void nsVoidArray::Clear()
{
if (mImpl) {
mImpl->mCount = 0;
}
}
void nsVoidArray::Compact()
{
if (mImpl) {
PRInt32 count = mImpl->mCount;
if (IsArrayOwner() && GetArraySize() > count) {
Impl* doomedImpl = mImpl;
if (count > 0) {
char* bytes = new char[sizeof(Impl) + sizeof(void*) * (count - 1)];
Impl* newImpl = NS_REINTERPRET_CAST(Impl*, bytes);
if (newImpl)
nsCRT::memcpy(newImpl->mArray, mImpl->mArray, count * sizeof(void*));
mImpl = newImpl;
mImpl->mBits = 0;
SetArraySize(count);
mImpl->mCount = count;
SetArrayOwner(PR_TRUE);
}
else {
mImpl = nsnull;
}
delete[] NS_REINTERPRET_CAST(char*, doomedImpl);
}
}
}
// Needed because we want to pass the pointer to the item in the array
// to the comparator function, not a pointer to the pointer in the array.
struct VoidArrayComparatorContext {
nsVoidArrayComparatorFunc mComparatorFunc;
void* mData;
};
PR_STATIC_CALLBACK(int)
VoidArrayComparator(const void* aElement1, const void* aElement2, void* aData)
{
VoidArrayComparatorContext* ctx = NS_STATIC_CAST(VoidArrayComparatorContext*, aData);
return (*ctx->mComparatorFunc)(*NS_STATIC_CAST(void* const*, aElement1),
*NS_STATIC_CAST(void* const*, aElement2),
ctx->mData);
}
void nsVoidArray::Sort(nsVoidArrayComparatorFunc aFunc, void* aData)
{
if (mImpl && mImpl->mCount > 1) {
VoidArrayComparatorContext ctx = {aFunc, aData};
NS_QuickSort(mImpl->mArray, mImpl->mCount, sizeof(void*),
VoidArrayComparator, &ctx);
}
}
PRBool nsVoidArray::EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData)
{
PRInt32 index = -1;
PRBool running = PR_TRUE;
if (mImpl) {
while (running && (++index < mImpl->mCount)) {
running = (*aFunc)(mImpl->mArray[index], aData);
}
}
return running;
}
PRBool nsVoidArray::EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData)
{
PRBool running = PR_TRUE;
if (mImpl) {
PRInt32 index = Count();
while (running && (0 <= --index)) {
running = (*aFunc)(mImpl->mArray[index], aData);
}
}
return running;
}
//----------------------------------------------------------------
// nsAutoVoidArray
nsAutoVoidArray::nsAutoVoidArray()
: nsVoidArray()
{
mImpl = NS_REINTERPRET_CAST(Impl*, mAutoBuf);
mImpl->mBits = 0;
SetArraySize(kAutoBufSize);
mImpl->mCount = 0;
SetArrayOwner(PR_FALSE);
}
//----------------------------------------------------------------
// nsStringArray
nsStringArray::nsStringArray(void)
: nsVoidArray()
{
}
nsStringArray::~nsStringArray(void)
{
Clear();
}
nsStringArray&
nsStringArray::operator=(const nsStringArray& other)
{
// Copy the pointers
nsVoidArray::operator=(other);
// Now copy the strings
for (PRInt32 i = Count() - 1; i >= 0; --i) {
nsString* oldString = NS_STATIC_CAST(nsString*, other.ElementAt(i));
mImpl->mArray[i] = new nsString(*oldString);
}
return *this;
}
void
nsStringArray::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const
{
PRUint32 sum = 0;
nsVoidArray::SizeOf(aHandler, &sum);
PRInt32 index = Count();
while (0 <= --index) {
nsString* string = NS_STATIC_CAST(nsString*, ElementAt(index));
PRUint32 size;
string->SizeOf(aHandler, &size);
sum += size;
}
}
void
nsStringArray::StringAt(PRInt32 aIndex, nsAWritableString& aString) const
{
nsString* string = NS_STATIC_CAST(nsString*, nsVoidArray::ElementAt(aIndex));
if (nsnull != string) {
aString.Assign(*string);
}
else {
aString.Truncate();
}
}
nsString*
nsStringArray::StringAt(PRInt32 aIndex) const
{
return NS_STATIC_CAST(nsString*, nsVoidArray::ElementAt(aIndex));
}
PRInt32
nsStringArray::IndexOf(const nsAReadableString& aPossibleString) const
{
if (mImpl) {
void** ap = mImpl->mArray;
void** end = ap + mImpl->mCount;
while (ap < end) {
nsString* string = NS_STATIC_CAST(nsString*, *ap);
if (string->Equals(aPossibleString)) {
return ap - mImpl->mArray;
}
ap++;
}
}
return -1;
}
PRInt32
nsStringArray::IndexOfIgnoreCase(const nsAReadableString& aPossibleString) const
{
if (mImpl) {
void** ap = mImpl->mArray;
void** end = ap + mImpl->mCount;
while (ap < end) {
nsString* string = NS_STATIC_CAST(nsString*, *ap);
if (!Compare(*string, aPossibleString, nsCaseInsensitiveStringComparator())) {
return ap - mImpl->mArray;
}
ap++;
}
}
return -1;
}
PRBool
nsStringArray::InsertStringAt(const nsAReadableString& aString, PRInt32 aIndex)
{
nsString* string = new nsString(aString);
if (nsVoidArray::InsertElementAt(string, aIndex)) {
return PR_TRUE;
}
delete string;
return PR_FALSE;
}
PRBool
nsStringArray::ReplaceStringAt(const nsAReadableString& aString,
PRInt32 aIndex)
{
nsString* string = NS_STATIC_CAST(nsString*, nsVoidArray::ElementAt(aIndex));
if (nsnull != string) {
*string = aString;
return PR_TRUE;
}
return PR_FALSE;
}
PRBool
nsStringArray::RemoveString(const nsAReadableString& aString)
{
PRInt32 index = IndexOf(aString);
if (-1 < index) {
return RemoveStringAt(index);
}
return PR_FALSE;
}
PRBool
nsStringArray::RemoveStringIgnoreCase(const nsAReadableString& aString)
{
PRInt32 index = IndexOfIgnoreCase(aString);
if (-1 < index) {
return RemoveStringAt(index);
}
return PR_FALSE;
}
PRBool nsStringArray::RemoveStringAt(PRInt32 aIndex)
{
nsString* string = StringAt(aIndex);
if (nsnull != string) {
nsVoidArray::RemoveElementAt(aIndex);
delete string;
return PR_TRUE;
}
return PR_FALSE;
}
void
nsStringArray::Clear(void)
{
PRInt32 index = Count();
while (0 <= --index) {
nsString* string = NS_STATIC_CAST(nsString*, mImpl->mArray[index]);
delete string;
}
nsVoidArray::Clear();
}
PR_STATIC_CALLBACK(int)
CompareString(const nsString* aString1, const nsString* aString2, void*)
{
return Compare(*aString1, *aString2);
}
PR_STATIC_CALLBACK(int)
CompareStringIgnoreCase(const nsString* aString1, const nsString* aString2, void*)
{
return Compare(*aString1, *aString2, nsCaseInsensitiveStringComparator());
}
void nsStringArray::Sort(void)
{
Sort(CompareString, nsnull);
}
void nsStringArray::SortIgnoreCase(void)
{
Sort(CompareStringIgnoreCase, nsnull);
}
void nsStringArray::Sort(nsStringArrayComparatorFunc aFunc, void* aData)
{
nsVoidArray::Sort(NS_REINTERPRET_CAST(nsVoidArrayComparatorFunc, aFunc), aData);
}
PRBool
nsStringArray::EnumerateForwards(nsStringArrayEnumFunc aFunc, void* aData)
{
PRInt32 index = -1;
PRBool running = PR_TRUE;
if (mImpl) {
while (running && (++index < mImpl->mCount)) {
running = (*aFunc)(*NS_STATIC_CAST(nsString*, mImpl->mArray[index]), aData);
}
}
return running;
}
PRBool
nsStringArray::EnumerateBackwards(nsStringArrayEnumFunc aFunc, void* aData)
{
PRInt32 index = Count();
PRBool running = PR_TRUE;
if (mImpl) {
while (running && (0 <= --index)) {
running = (*aFunc)(*NS_STATIC_CAST(nsString*, mImpl->mArray[index]), aData);
}
}
return running;
}
//----------------------------------------------------------------
// nsCStringArray
nsCStringArray::nsCStringArray(void)
: nsVoidArray()
{
}
nsCStringArray::~nsCStringArray(void)
{
Clear();
}
nsCStringArray&
nsCStringArray::operator=(const nsCStringArray& other)
{
// Copy the pointers
nsVoidArray::operator=(other);
// Now copy the strings
for (PRInt32 i = Count() - 1; i >= 0; --i) {
nsCString* oldString = NS_STATIC_CAST(nsCString*, other.ElementAt(i));
mImpl->mArray[i] = new nsCString(*oldString);
}
return *this;
}
void
nsCStringArray::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const
{
PRUint32 sum = 0;
nsVoidArray::SizeOf(aHandler, &sum);
PRInt32 index = Count();
while (0 <= --index) {
nsCString* string = NS_STATIC_CAST(nsCString*, mImpl->mArray[index]);
PRUint32 size;
string->SizeOf(aHandler, &size);
sum += size;
}
}
void
nsCStringArray::CStringAt(PRInt32 aIndex, nsCString& aCString) const
{
nsCString* string = NS_STATIC_CAST(nsCString*, nsVoidArray::ElementAt(aIndex));
if (nsnull != string) {
aCString = *string;
}
else {
aCString.Truncate();
}
}
nsCString*
nsCStringArray::CStringAt(PRInt32 aIndex) const
{
return NS_STATIC_CAST(nsCString*, nsVoidArray::ElementAt(aIndex));
}
PRInt32
nsCStringArray::IndexOf(const nsCString& aPossibleString) const
{
if (mImpl) {
void** ap = mImpl->mArray;
void** end = ap + mImpl->mCount;
while (ap < end) {
nsCString* string = NS_STATIC_CAST(nsCString*, *ap);
if (string->Equals(aPossibleString)) {
return ap - mImpl->mArray;
}
ap++;
}
}
return -1;
}
PRInt32
nsCStringArray::IndexOfIgnoreCase(const nsCString& aPossibleString) const
{
if (mImpl) {
void** ap = mImpl->mArray;
void** end = ap + mImpl->mCount;
while (ap < end) {
nsCString* string = NS_STATIC_CAST(nsCString*, *ap);
if (string->EqualsIgnoreCase(aPossibleString)) {
return ap - mImpl->mArray;
}
ap++;
}
}
return -1;
}
PRBool
nsCStringArray::InsertCStringAt(const nsCString& aCString, PRInt32 aIndex)
{
nsCString* string = new nsCString(aCString);
if (nsVoidArray::InsertElementAt(string, aIndex)) {
return PR_TRUE;
}
delete string;
return PR_FALSE;
}
PRBool
nsCStringArray::ReplaceCStringAt(const nsCString& aCString, PRInt32 aIndex)
{
nsCString* string = NS_STATIC_CAST(nsCString*, nsVoidArray::ElementAt(aIndex));
if (nsnull != string) {
*string = aCString;
return PR_TRUE;
}
return PR_FALSE;
}
PRBool
nsCStringArray::RemoveCString(const nsCString& aCString)
{
PRInt32 index = IndexOf(aCString);
if (-1 < index) {
return RemoveCStringAt(index);
}
return PR_FALSE;
}
PRBool
nsCStringArray::RemoveCStringIgnoreCase(const nsCString& aCString)
{
PRInt32 index = IndexOfIgnoreCase(aCString);
if (-1 < index) {
return RemoveCStringAt(index);
}
return PR_FALSE;
}
PRBool nsCStringArray::RemoveCStringAt(PRInt32 aIndex)
{
nsCString* string = CStringAt(aIndex);
if (nsnull != string) {
nsVoidArray::RemoveElementAt(aIndex);
delete string;
return PR_TRUE;
}
return PR_FALSE;
}
void
nsCStringArray::Clear(void)
{
PRInt32 index = Count();
while (0 <= --index) {
nsCString* string = NS_STATIC_CAST(nsCString*, mImpl->mArray[index]);
delete string;
}
nsVoidArray::Clear();
}
PR_STATIC_CALLBACK(int)
CompareCString(const nsCString* aCString1, const nsCString* aCString2, void*)
{
return Compare(*aCString1, *aCString2);
}
PR_STATIC_CALLBACK(int)
CompareCStringIgnoreCase(const nsCString* aCString1, const nsCString* aCString2, void*)
{
return Compare(*aCString1, *aCString2, nsCaseInsensitiveCStringComparator());
}
void nsCStringArray::Sort(void)
{
Sort(CompareCString, nsnull);
}
void nsCStringArray::SortIgnoreCase(void)
{
Sort(CompareCStringIgnoreCase, nsnull);
}
void nsCStringArray::Sort(nsCStringArrayComparatorFunc aFunc, void* aData)
{
nsVoidArray::Sort(NS_REINTERPRET_CAST(nsVoidArrayComparatorFunc, aFunc), aData);
}
PRBool
nsCStringArray::EnumerateForwards(nsCStringArrayEnumFunc aFunc, void* aData)
{
PRBool running = PR_TRUE;
if (mImpl) {
PRInt32 index = -1;
while (running && (++index < mImpl->mCount)) {
running = (*aFunc)(*NS_STATIC_CAST(nsCString*, mImpl->mArray[index]), aData);
}
}
return running;
}
PRBool
nsCStringArray::EnumerateBackwards(nsCStringArrayEnumFunc aFunc, void* aData)
{
PRBool running = PR_TRUE;
if (mImpl) {
PRInt32 index = Count();
while (running && (0 <= --index)) {
running = (*aFunc)(*NS_STATIC_CAST(nsCString*, mImpl->mArray[index]), aData);
}
}
return running;
}