mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-09 09:20:15 +01:00
1566 lines
36 KiB
C++
1566 lines
36 KiB
C++
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; c-file-offsets: ((substatement-open . 0)) -*- */
|
||
|
/* ***** 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.org code.
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* Netscape Communications Corporation.
|
||
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either of 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 ***** */
|
||
|
#include "nsVoidArray.h"
|
||
|
#include "nsQuickSort.h"
|
||
|
#include "prmem.h"
|
||
|
#include "nsCRT.h"
|
||
|
#include "nsString.h"
|
||
|
#include "prbit.h"
|
||
|
|
||
|
/**
|
||
|
* Grow the array by at least this many elements at a time.
|
||
|
*/
|
||
|
static const PRInt32 kMinGrowArrayBy = 8;
|
||
|
static const PRInt32 kMaxGrowArrayBy = 1024;
|
||
|
|
||
|
/**
|
||
|
* This is the threshold (in bytes) of the mImpl struct, past which
|
||
|
* we'll force the array to grow geometrically
|
||
|
*/
|
||
|
static const PRInt32 kLinearThreshold = 24 * sizeof(void *);
|
||
|
|
||
|
/**
|
||
|
* Compute the number of bytes requires for the mImpl struct that will
|
||
|
* hold |n| elements.
|
||
|
*/
|
||
|
#define SIZEOF_IMPL(n_) (sizeof(Impl) + sizeof(void *) * ((n_) - 1))
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Compute the number of elements that an mImpl struct of |n| bytes
|
||
|
* will hold.
|
||
|
*/
|
||
|
#define CAPACITYOF_IMPL(n_) ((((n_) - sizeof(Impl)) / sizeof(void *)) + 1)
|
||
|
|
||
|
#if DEBUG_VOIDARRAY
|
||
|
#define MAXVOID 10
|
||
|
|
||
|
class VoidStats {
|
||
|
public:
|
||
|
VoidStats();
|
||
|
~VoidStats();
|
||
|
|
||
|
};
|
||
|
|
||
|
static int sizesUsed; // number of the elements of the arrays used
|
||
|
static int sizesAlloced[MAXVOID]; // sizes of the allocations. sorted
|
||
|
static int NumberOfSize[MAXVOID]; // number of this allocation size (1 per array)
|
||
|
static int AllocedOfSize[MAXVOID]; // number of this allocation size (each size for array used)
|
||
|
static int MaxAuto[MAXVOID]; // AutoArrays that maxed out at this size
|
||
|
static int GrowInPlace[MAXVOID]; // arrays this size that grew in-place via realloc
|
||
|
|
||
|
// these are per-allocation
|
||
|
static int MaxElements[2000]; // # of arrays that maxed out at each size.
|
||
|
|
||
|
// statistics macros
|
||
|
#define ADD_TO_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
|
||
|
{ \
|
||
|
if (sizesAlloced[i] == (int)(size)) \
|
||
|
{ ((x)[i])++; break; } \
|
||
|
} \
|
||
|
if (i >= sizesUsed && sizesUsed < MAXVOID) \
|
||
|
{ sizesAlloced[sizesUsed] = (size); \
|
||
|
((x)[sizesUsed++])++; break; \
|
||
|
} \
|
||
|
} while (0)
|
||
|
|
||
|
#define SUB_FROM_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
|
||
|
{ \
|
||
|
if (sizesAlloced[i] == (int)(size)) \
|
||
|
{ ((x)[i])--; break; } \
|
||
|
} \
|
||
|
} while (0)
|
||
|
|
||
|
|
||
|
VoidStats::VoidStats()
|
||
|
{
|
||
|
sizesUsed = 1;
|
||
|
sizesAlloced[0] = 0;
|
||
|
}
|
||
|
|
||
|
VoidStats::~VoidStats()
|
||
|
{
|
||
|
int i;
|
||
|
for (i = 0; i < sizesUsed; i++)
|
||
|
{
|
||
|
printf("Size %d:\n",sizesAlloced[i]);
|
||
|
printf("\tNumber of VoidArrays this size (max): %d\n",NumberOfSize[i]-MaxAuto[i]);
|
||
|
printf("\tNumber of AutoVoidArrays this size (max): %d\n",MaxAuto[i]);
|
||
|
printf("\tNumber of allocations this size (total): %d\n",AllocedOfSize[i]);
|
||
|
printf("\tNumber of GrowsInPlace this size (total): %d\n",GrowInPlace[i]);
|
||
|
}
|
||
|
printf("Max Size of VoidArray:\n");
|
||
|
for (i = 0; i < (int)(sizeof(MaxElements)/sizeof(MaxElements[0])); i++)
|
||
|
{
|
||
|
if (MaxElements[i])
|
||
|
printf("\t%d: %d\n",i,MaxElements[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Just so constructor/destructor's get called
|
||
|
VoidStats gVoidStats;
|
||
|
#endif
|
||
|
|
||
|
inline void
|
||
|
nsVoidArray::SetArray(Impl *newImpl, PRInt32 aSize, PRInt32 aCount, PRBool owner)
|
||
|
{
|
||
|
// old mImpl has been realloced and so we don't free/delete it
|
||
|
NS_PRECONDITION(newImpl, "can't set size");
|
||
|
mImpl = newImpl;
|
||
|
mImpl->mCount = aCount;
|
||
|
mImpl->mBits = PRUint32(aSize & kArraySizeMask) |
|
||
|
(owner ? kArrayOwnerMask : 0);
|
||
|
}
|
||
|
|
||
|
// This does all allocation/reallocation of the array.
|
||
|
// It also will compact down to N - good for things that might grow a lot
|
||
|
// at times, but usually are smaller, like JS deferred GC releases.
|
||
|
PRBool nsVoidArray::SizeTo(PRInt32 aSize)
|
||
|
{
|
||
|
PRUint32 oldsize = GetArraySize();
|
||
|
|
||
|
if (aSize == (PRInt32) oldsize)
|
||
|
return PR_TRUE; // no change
|
||
|
|
||
|
if (aSize <= 0)
|
||
|
{
|
||
|
// free the array if allocated
|
||
|
if (mImpl)
|
||
|
{
|
||
|
if (IsArrayOwner())
|
||
|
{
|
||
|
PR_Free(NS_REINTERPRET_CAST(char *, mImpl));
|
||
|
mImpl = nsnull;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mImpl->mCount = 0; // nsAutoVoidArray
|
||
|
}
|
||
|
}
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
if (mImpl && IsArrayOwner())
|
||
|
{
|
||
|
// We currently own an array impl. Resize it appropriately.
|
||
|
if (aSize < mImpl->mCount)
|
||
|
{
|
||
|
// XXX Note: we could also just resize to mCount
|
||
|
return PR_TRUE; // can't make it that small, ignore request
|
||
|
}
|
||
|
|
||
|
char* bytes = (char *) PR_Realloc(mImpl,SIZEOF_IMPL(aSize));
|
||
|
Impl* newImpl = NS_REINTERPRET_CAST(Impl*, bytes);
|
||
|
if (!newImpl)
|
||
|
return PR_FALSE;
|
||
|
|
||
|
#if DEBUG_VOIDARRAY
|
||
|
if (mImpl == newImpl)
|
||
|
ADD_TO_STATS(GrowInPlace,oldsize);
|
||
|
ADD_TO_STATS(AllocedOfSize,SIZEOF_IMPL(aSize));
|
||
|
if (aSize > mMaxSize)
|
||
|
{
|
||
|
ADD_TO_STATS(NumberOfSize,SIZEOF_IMPL(aSize));
|
||
|
if (oldsize)
|
||
|
SUB_FROM_STATS(NumberOfSize,oldsize);
|
||
|
mMaxSize = aSize;
|
||
|
if (mIsAuto)
|
||
|
{
|
||
|
ADD_TO_STATS(MaxAuto,SIZEOF_IMPL(aSize));
|
||
|
SUB_FROM_STATS(MaxAuto,oldsize);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
SetArray(newImpl,aSize,newImpl->mCount,PR_TRUE);
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
// just allocate an array
|
||
|
// allocate the exact size requested
|
||
|
char* bytes = (char *) PR_Malloc(SIZEOF_IMPL(aSize));
|
||
|
Impl* newImpl = NS_REINTERPRET_CAST(Impl*, bytes);
|
||
|
if (!newImpl)
|
||
|
return PR_FALSE;
|
||
|
|
||
|
#if DEBUG_VOIDARRAY
|
||
|
ADD_TO_STATS(AllocedOfSize,SIZEOF_IMPL(aSize));
|
||
|
if (aSize > mMaxSize)
|
||
|
{
|
||
|
ADD_TO_STATS(NumberOfSize,SIZEOF_IMPL(aSize));
|
||
|
if (oldsize && !mImpl)
|
||
|
SUB_FROM_STATS(NumberOfSize,oldsize);
|
||
|
mMaxSize = aSize;
|
||
|
}
|
||
|
#endif
|
||
|
if (mImpl)
|
||
|
{
|
||
|
#if DEBUG_VOIDARRAY
|
||
|
ADD_TO_STATS(MaxAuto,SIZEOF_IMPL(aSize));
|
||
|
SUB_FROM_STATS(MaxAuto,0);
|
||
|
SUB_FROM_STATS(NumberOfSize,0);
|
||
|
mIsAuto = PR_TRUE;
|
||
|
#endif
|
||
|
// We must be growing an nsAutoVoidArray - copy since we didn't
|
||
|
// realloc.
|
||
|
memcpy(newImpl->mArray, mImpl->mArray,
|
||
|
mImpl->mCount * sizeof(mImpl->mArray[0]));
|
||
|
}
|
||
|
|
||
|
SetArray(newImpl,aSize,mImpl ? mImpl->mCount : 0,PR_TRUE);
|
||
|
// no memset; handled later in ReplaceElementAt if needed
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
PRBool nsVoidArray::GrowArrayBy(PRInt32 aGrowBy)
|
||
|
{
|
||
|
// We have to grow the array. Grow by kMinGrowArrayBy slots if we're
|
||
|
// smaller than kLinearThreshold bytes, or a power of two if we're
|
||
|
// larger. This is much more efficient with most memory allocators,
|
||
|
// especially if it's very large, or of the allocator is binned.
|
||
|
if (aGrowBy < kMinGrowArrayBy)
|
||
|
aGrowBy = kMinGrowArrayBy;
|
||
|
|
||
|
PRUint32 newCapacity = GetArraySize() + aGrowBy; // Minimum increase
|
||
|
PRUint32 newSize = SIZEOF_IMPL(newCapacity);
|
||
|
|
||
|
if (newSize >= (PRUint32) kLinearThreshold)
|
||
|
{
|
||
|
// newCount includes enough space for at least kMinGrowArrayBy new
|
||
|
// slots. Select the next power-of-two size in bytes above or
|
||
|
// equal to that.
|
||
|
// Also, limit the increase in size to about a VM page or two.
|
||
|
if (GetArraySize() >= kMaxGrowArrayBy)
|
||
|
{
|
||
|
newCapacity = GetArraySize() + PR_MAX(kMaxGrowArrayBy,aGrowBy);
|
||
|
newSize = SIZEOF_IMPL(newCapacity);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
newSize = PR_BIT(PR_CeilingLog2(newSize));
|
||
|
newCapacity = CAPACITYOF_IMPL(newSize);
|
||
|
}
|
||
|
}
|
||
|
// frees old mImpl IF this succeeds
|
||
|
if (!SizeTo(newCapacity))
|
||
|
return PR_FALSE;
|
||
|
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
nsVoidArray::nsVoidArray()
|
||
|
: mImpl(nsnull)
|
||
|
{
|
||
|
MOZ_COUNT_CTOR(nsVoidArray);
|
||
|
#if DEBUG_VOIDARRAY
|
||
|
mMaxCount = 0;
|
||
|
mMaxSize = 0;
|
||
|
mIsAuto = PR_FALSE;
|
||
|
ADD_TO_STATS(NumberOfSize,0);
|
||
|
MaxElements[0]++;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
nsVoidArray::nsVoidArray(PRInt32 aCount)
|
||
|
: mImpl(nsnull)
|
||
|
{
|
||
|
MOZ_COUNT_CTOR(nsVoidArray);
|
||
|
#if DEBUG_VOIDARRAY
|
||
|
mMaxCount = 0;
|
||
|
mMaxSize = 0;
|
||
|
mIsAuto = PR_FALSE;
|
||
|
MaxElements[0]++;
|
||
|
#endif
|
||
|
SizeTo(aCount);
|
||
|
}
|
||
|
|
||
|
nsVoidArray& nsVoidArray::operator=(const nsVoidArray& other)
|
||
|
{
|
||
|
PRInt32 otherCount = other.Count();
|
||
|
PRInt32 maxCount = GetArraySize();
|
||
|
if (otherCount)
|
||
|
{
|
||
|
if (otherCount > maxCount)
|
||
|
{
|
||
|
// frees old mImpl IF this succeeds
|
||
|
if (!GrowArrayBy(otherCount-maxCount))
|
||
|
return *this; // XXX The allocation failed - don't do anything
|
||
|
|
||
|
memcpy(mImpl->mArray, other.mImpl->mArray, otherCount * sizeof(mImpl->mArray[0]));
|
||
|
mImpl->mCount = otherCount;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// the old array can hold the new array
|
||
|
memcpy(mImpl->mArray, other.mImpl->mArray, otherCount * sizeof(mImpl->mArray[0]));
|
||
|
mImpl->mCount = otherCount;
|
||
|
// if it shrank a lot, compact it anyways
|
||
|
if ((otherCount*2) < maxCount && maxCount > 100)
|
||
|
{
|
||
|
Compact(); // shrank by at least 50 entries
|
||
|
}
|
||
|
}
|
||
|
#if DEBUG_VOIDARRAY
|
||
|
if (mImpl->mCount > mMaxCount &&
|
||
|
mImpl->mCount < (PRInt32)(sizeof(MaxElements)/sizeof(MaxElements[0])))
|
||
|
{
|
||
|
MaxElements[mImpl->mCount]++;
|
||
|
MaxElements[mMaxCount]--;
|
||
|
mMaxCount = mImpl->mCount;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (mImpl && IsArrayOwner())
|
||
|
PR_Free(NS_REINTERPRET_CAST(char*, mImpl));
|
||
|
|
||
|
mImpl = nsnull;
|
||
|
}
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
nsVoidArray::~nsVoidArray()
|
||
|
{
|
||
|
MOZ_COUNT_DTOR(nsVoidArray);
|
||
|
if (mImpl && IsArrayOwner())
|
||
|
PR_Free(NS_REINTERPRET_CAST(char*, mImpl));
|
||
|
}
|
||
|
|
||
|
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();
|
||
|
NS_ASSERTION(aIndex >= 0,"InsertElementAt(negative index)");
|
||
|
if (PRUint32(aIndex) > PRUint32(oldCount))
|
||
|
{
|
||
|
// An invalid index causes the insertion to fail
|
||
|
// Invalid indexes are ones that add more than one entry to the
|
||
|
// array (i.e., they can append).
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
|
||
|
if (oldCount >= GetArraySize())
|
||
|
{
|
||
|
if (!GrowArrayBy(1))
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
// else the array is already large enough
|
||
|
|
||
|
PRInt32 slide = oldCount - aIndex;
|
||
|
if (0 != slide)
|
||
|
{
|
||
|
// Slide data over to make room for the insertion
|
||
|
memmove(mImpl->mArray + aIndex + 1, mImpl->mArray + aIndex,
|
||
|
slide * sizeof(mImpl->mArray[0]));
|
||
|
}
|
||
|
|
||
|
mImpl->mArray[aIndex] = aElement;
|
||
|
mImpl->mCount++;
|
||
|
|
||
|
#if DEBUG_VOIDARRAY
|
||
|
if (mImpl->mCount > mMaxCount &&
|
||
|
mImpl->mCount < (PRInt32)(sizeof(MaxElements)/sizeof(MaxElements[0])))
|
||
|
{
|
||
|
MaxElements[mImpl->mCount]++;
|
||
|
MaxElements[mMaxCount]--;
|
||
|
mMaxCount = mImpl->mCount;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
PRBool nsVoidArray::InsertElementsAt(const nsVoidArray& other, PRInt32 aIndex)
|
||
|
{
|
||
|
PRInt32 oldCount = Count();
|
||
|
PRInt32 otherCount = other.Count();
|
||
|
|
||
|
NS_ASSERTION(aIndex >= 0,"InsertElementsAt(negative index)");
|
||
|
if (PRUint32(aIndex) > PRUint32(oldCount))
|
||
|
{
|
||
|
// An invalid index causes the insertion to fail
|
||
|
// Invalid indexes are ones that are more than one entry past the end of
|
||
|
// the array (i.e., they can append).
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
|
||
|
if (oldCount + otherCount > GetArraySize())
|
||
|
{
|
||
|
if (!GrowArrayBy(otherCount))
|
||
|
return PR_FALSE;;
|
||
|
}
|
||
|
// else the array is already large enough
|
||
|
|
||
|
PRInt32 slide = oldCount - aIndex;
|
||
|
if (0 != slide)
|
||
|
{
|
||
|
// Slide data over to make room for the insertion
|
||
|
memmove(mImpl->mArray + aIndex + otherCount, mImpl->mArray + aIndex,
|
||
|
slide * sizeof(mImpl->mArray[0]));
|
||
|
}
|
||
|
|
||
|
for (PRInt32 i = 0; i < otherCount; i++)
|
||
|
{
|
||
|
// copy all the elements (destroys aIndex)
|
||
|
mImpl->mArray[aIndex++] = other.mImpl->mArray[i];
|
||
|
mImpl->mCount++;
|
||
|
}
|
||
|
|
||
|
#if DEBUG_VOIDARRAY
|
||
|
if (mImpl->mCount > mMaxCount &&
|
||
|
mImpl->mCount < (PRInt32)(sizeof(MaxElements)/sizeof(MaxElements[0])))
|
||
|
{
|
||
|
MaxElements[mImpl->mCount]++;
|
||
|
MaxElements[mMaxCount]--;
|
||
|
mMaxCount = mImpl->mCount;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
PRBool nsVoidArray::ReplaceElementAt(void* aElement, PRInt32 aIndex)
|
||
|
{
|
||
|
NS_ASSERTION(aIndex >= 0,"ReplaceElementAt(negative index)");
|
||
|
if (aIndex < 0)
|
||
|
return PR_FALSE;
|
||
|
|
||
|
// Unlike InsertElementAt, ReplaceElementAt can implicitly add more
|
||
|
// than just the one element to the array.
|
||
|
if (PRUint32(aIndex) >= PRUint32(GetArraySize()))
|
||
|
{
|
||
|
PRInt32 oldCount = Count();
|
||
|
PRInt32 requestedCount = aIndex + 1;
|
||
|
PRInt32 growDelta = requestedCount - oldCount;
|
||
|
|
||
|
// frees old mImpl IF this succeeds
|
||
|
if (!GrowArrayBy(growDelta))
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
|
||
|
mImpl->mArray[aIndex] = aElement;
|
||
|
if (aIndex >= mImpl->mCount)
|
||
|
{
|
||
|
// Make sure that any entries implicitly added to the array by this
|
||
|
// ReplaceElementAt are cleared to 0. Some users of this assume that.
|
||
|
// This code means we don't have to memset when we allocate an array.
|
||
|
if (aIndex > mImpl->mCount) // note: not >=
|
||
|
{
|
||
|
// For example, if mCount is 2, and we do a ReplaceElementAt for
|
||
|
// element[5], then we need to set three entries ([2], [3], and [4])
|
||
|
// to 0.
|
||
|
memset(&mImpl->mArray[mImpl->mCount], 0,
|
||
|
(aIndex - mImpl->mCount) * sizeof(mImpl->mArray[0]));
|
||
|
}
|
||
|
|
||
|
mImpl->mCount = aIndex + 1;
|
||
|
|
||
|
#if DEBUG_VOIDARRAY
|
||
|
if (mImpl->mCount > mMaxCount &&
|
||
|
mImpl->mCount < (PRInt32)(sizeof(MaxElements)/sizeof(MaxElements[0])))
|
||
|
{
|
||
|
MaxElements[mImpl->mCount]++;
|
||
|
MaxElements[mMaxCount]--;
|
||
|
mMaxCount = mImpl->mCount;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
// useful for doing LRU arrays
|
||
|
PRBool nsVoidArray::MoveElement(PRInt32 aFrom, PRInt32 aTo)
|
||
|
{
|
||
|
void *tempElement;
|
||
|
|
||
|
if (aTo == aFrom)
|
||
|
return PR_TRUE;
|
||
|
|
||
|
NS_ASSERTION(aTo >= 0 && aFrom >= 0,"MoveElement(negative index)");
|
||
|
if (aTo >= Count() || aFrom >= Count())
|
||
|
{
|
||
|
// can't extend the array when moving an element. Also catches mImpl = null
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
tempElement = mImpl->mArray[aFrom];
|
||
|
|
||
|
if (aTo < aFrom)
|
||
|
{
|
||
|
// Moving one element closer to the head; the elements inbetween move down
|
||
|
memmove(mImpl->mArray + aTo + 1, mImpl->mArray + aTo,
|
||
|
(aFrom-aTo) * sizeof(mImpl->mArray[0]));
|
||
|
mImpl->mArray[aTo] = tempElement;
|
||
|
}
|
||
|
else // already handled aFrom == aTo
|
||
|
{
|
||
|
// Moving one element closer to the tail; the elements inbetween move up
|
||
|
memmove(mImpl->mArray + aFrom, mImpl->mArray + aFrom + 1,
|
||
|
(aTo-aFrom) * sizeof(mImpl->mArray[0]));
|
||
|
mImpl->mArray[aTo] = tempElement;
|
||
|
}
|
||
|
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
PRBool nsVoidArray::RemoveElementsAt(PRInt32 aIndex, PRInt32 aCount)
|
||
|
{
|
||
|
PRInt32 oldCount = Count();
|
||
|
NS_ASSERTION(aIndex >= 0,"RemoveElementsAt(negative index)");
|
||
|
if (PRUint32(aIndex) >= PRUint32(oldCount))
|
||
|
{
|
||
|
// An invalid index causes the replace to fail
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
// Limit to available entries starting at aIndex
|
||
|
if (aCount + aIndex > oldCount)
|
||
|
aCount = oldCount - aIndex;
|
||
|
|
||
|
// We don't need to move any elements if we're removing the
|
||
|
// last element in the array
|
||
|
if (aIndex < (oldCount - aCount))
|
||
|
{
|
||
|
memmove(mImpl->mArray + aIndex, mImpl->mArray + aIndex + aCount,
|
||
|
(oldCount - (aIndex + aCount)) * sizeof(mImpl->mArray[0]));
|
||
|
}
|
||
|
|
||
|
mImpl->mCount -= aCount;
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
PRBool nsVoidArray::RemoveElement(void* aElement)
|
||
|
{
|
||
|
PRInt32 theIndex = IndexOf(aElement);
|
||
|
if (theIndex != -1)
|
||
|
return RemoveElementAt(theIndex);
|
||
|
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
|
||
|
void nsVoidArray::Clear()
|
||
|
{
|
||
|
if (mImpl)
|
||
|
{
|
||
|
mImpl->mCount = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void nsVoidArray::Compact()
|
||
|
{
|
||
|
if (mImpl)
|
||
|
{
|
||
|
// XXX NOTE: this is quite inefficient in many cases if we're only
|
||
|
// compacting by a little, but some callers care more about memory use.
|
||
|
if (GetArraySize() > Count())
|
||
|
{
|
||
|
SizeTo(Count());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 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(mImpl->mArray[0]),
|
||
|
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()
|
||
|
{
|
||
|
// Don't need to clear it. Some users just call ReplaceElementAt(),
|
||
|
// but we'll clear it at that time if needed to save CPU cycles.
|
||
|
#if DEBUG_VOIDARRAY
|
||
|
mIsAuto = PR_TRUE;
|
||
|
ADD_TO_STATS(MaxAuto,0);
|
||
|
#endif
|
||
|
SetArray(NS_REINTERPRET_CAST(Impl*, mAutoBuf),kAutoBufSize,0,PR_FALSE);
|
||
|
}
|
||
|
|
||
|
void nsAutoVoidArray::Clear()
|
||
|
{
|
||
|
// We don't have to free on Clear, but since we have a built-in buffer,
|
||
|
// it's worth considering.
|
||
|
nsVoidArray::Clear();
|
||
|
if (IsArrayOwner() && GetArraySize() > 4*kAutoBufSize)
|
||
|
SizeTo(0); // we override CompactTo - delete and repoint at auto array
|
||
|
}
|
||
|
|
||
|
PRBool nsAutoVoidArray::SizeTo(PRInt32 aSize)
|
||
|
{
|
||
|
if (!nsVoidArray::SizeTo(aSize))
|
||
|
return PR_FALSE;
|
||
|
|
||
|
if (!mImpl)
|
||
|
{
|
||
|
// reset the array to point to our autobuf
|
||
|
SetArray(NS_REINTERPRET_CAST(Impl*, mAutoBuf),kAutoBufSize,0,PR_FALSE);
|
||
|
}
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
void nsAutoVoidArray::Compact()
|
||
|
{
|
||
|
nsVoidArray::Compact();
|
||
|
if (!mImpl)
|
||
|
{
|
||
|
// reset the array to point to our autobuf
|
||
|
SetArray(NS_REINTERPRET_CAST(Impl*, mAutoBuf),kAutoBufSize,0,PR_FALSE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------
|
||
|
// nsStringArray
|
||
|
|
||
|
nsStringArray::nsStringArray(void)
|
||
|
: nsVoidArray()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
nsStringArray::nsStringArray(PRInt32 aCount)
|
||
|
: nsVoidArray(aCount)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
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::StringAt(PRInt32 aIndex, nsAString& 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 nsAString& 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;
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
nsStringArray::InsertStringAt(const nsAString& 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 nsAString& 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 nsAString& aString)
|
||
|
{
|
||
|
PRInt32 index = IndexOf(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);
|
||
|
}
|
||
|
|
||
|
void nsStringArray::Sort(void)
|
||
|
{
|
||
|
Sort(CompareString, 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()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// Parses a given string using the delimiter passed in and appends items
|
||
|
// parsed to the array.
|
||
|
void
|
||
|
nsCStringArray::ParseString(const char* string, const char* delimiter)
|
||
|
{
|
||
|
if (string && *string && delimiter && *delimiter) {
|
||
|
char *newStr;
|
||
|
char *rest = nsCRT::strdup(string);
|
||
|
char *token = nsCRT::strtok(rest, delimiter, &newStr);
|
||
|
|
||
|
while (token) {
|
||
|
if (*token) {
|
||
|
/* calling AppendElement(void*) to avoid extra nsCString copy */
|
||
|
AppendElement(new nsCString(token));
|
||
|
}
|
||
|
token = nsCRT::strtok(newStr, delimiter, &newStr);
|
||
|
}
|
||
|
PR_FREEIF(rest);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nsCStringArray::nsCStringArray(PRInt32 aCount)
|
||
|
: nsVoidArray(aCount)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
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::CStringAt(PRInt32 aIndex, nsACString& 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 nsACString& 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 nsACString& 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, nsCaseInsensitiveCStringComparator()))
|
||
|
{
|
||
|
return ap - mImpl->mArray;
|
||
|
}
|
||
|
ap++;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
nsCStringArray::InsertCStringAt(const nsACString& 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 nsACString& 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 nsACString& aCString)
|
||
|
{
|
||
|
PRInt32 index = IndexOf(aCString);
|
||
|
if (-1 < index)
|
||
|
{
|
||
|
return RemoveCStringAt(index);
|
||
|
}
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
nsCStringArray::RemoveCStringIgnoreCase(const nsACString& 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// NOTE: nsSmallVoidArray elements MUST all have the low bit as 0.
|
||
|
// This means that normally it's only used for pointers, and in particular
|
||
|
// structures or objects.
|
||
|
nsSmallVoidArray::nsSmallVoidArray()
|
||
|
{
|
||
|
mChildren = nsnull;
|
||
|
}
|
||
|
|
||
|
nsSmallVoidArray::~nsSmallVoidArray()
|
||
|
{
|
||
|
if (HasVector())
|
||
|
{
|
||
|
nsVoidArray* vector = GetChildVector();
|
||
|
delete vector;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nsSmallVoidArray&
|
||
|
nsSmallVoidArray::operator=(nsSmallVoidArray& other)
|
||
|
{
|
||
|
nsVoidArray* ourArray = GetChildVector();
|
||
|
nsVoidArray* otherArray = other.GetChildVector();
|
||
|
|
||
|
if (HasVector())
|
||
|
{
|
||
|
if (other.HasVector())
|
||
|
{
|
||
|
// if both are real arrays, just use array= */
|
||
|
*ourArray = *otherArray;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// we have an array, but the other doesn't.
|
||
|
otherArray = other.SwitchToVector();
|
||
|
if (otherArray)
|
||
|
*ourArray = *otherArray;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (other.HasVector())
|
||
|
{
|
||
|
// we have no array, but other does
|
||
|
ourArray = SwitchToVector();
|
||
|
if (ourArray)
|
||
|
*ourArray = *otherArray;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// neither has an array (either may have 0 or 1 items)
|
||
|
SetSingleChild(other.GetSingleChild());
|
||
|
}
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
PRInt32
|
||
|
nsSmallVoidArray::GetArraySize() const
|
||
|
{
|
||
|
nsVoidArray* vector = GetChildVector();
|
||
|
if (vector)
|
||
|
return vector->GetArraySize();
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
PRInt32
|
||
|
nsSmallVoidArray::Count() const
|
||
|
{
|
||
|
if (HasSingleChild())
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
else {
|
||
|
nsVoidArray* vector = GetChildVector();
|
||
|
if (vector)
|
||
|
return vector->Count();
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void*
|
||
|
nsSmallVoidArray::ElementAt(PRInt32 aIndex) const
|
||
|
{
|
||
|
if (HasSingleChild())
|
||
|
{
|
||
|
if (0 == aIndex)
|
||
|
return (void*)GetSingleChild();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nsVoidArray* vector = GetChildVector();
|
||
|
if (vector)
|
||
|
return vector->ElementAt(aIndex);
|
||
|
}
|
||
|
|
||
|
return nsnull;
|
||
|
}
|
||
|
|
||
|
PRInt32
|
||
|
nsSmallVoidArray::IndexOf(void* aPossibleElement) const
|
||
|
{
|
||
|
if (HasSingleChild())
|
||
|
{
|
||
|
if (aPossibleElement == (void*)GetSingleChild())
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nsVoidArray* vector = GetChildVector();
|
||
|
if (vector)
|
||
|
return vector->IndexOf(aPossibleElement);
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
nsSmallVoidArray::InsertElementAt(void* aElement, PRInt32 aIndex)
|
||
|
{
|
||
|
nsVoidArray* vector;
|
||
|
NS_ASSERTION(!(PtrBits(aElement) & 0x1),"Attempt to add element with 0x1 bit set to nsSmallVoidArray");
|
||
|
NS_ASSERTION(aElement != nsnull,"Attempt to add a NULL element to an nsSmallVoidArray");
|
||
|
|
||
|
if (HasSingleChild())
|
||
|
{
|
||
|
vector = SwitchToVector();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vector = GetChildVector();
|
||
|
if (!vector)
|
||
|
{
|
||
|
if (0 == aIndex)
|
||
|
{
|
||
|
SetSingleChild(aElement);
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return vector->InsertElementAt(aElement, aIndex);
|
||
|
}
|
||
|
|
||
|
PRBool nsSmallVoidArray::InsertElementsAt(const nsVoidArray &other, PRInt32 aIndex)
|
||
|
{
|
||
|
nsVoidArray* vector;
|
||
|
PRInt32 count = other.Count();
|
||
|
if (count == 0)
|
||
|
return PR_TRUE;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
for (int i = 0; i < count; i++)
|
||
|
{
|
||
|
NS_ASSERTION(!(PtrBits(other.ElementAt(i)) & 0x1),"Attempt to add element with 0x1 bit set to nsSmallVoidArray");
|
||
|
NS_ASSERTION(other.ElementAt(i) != nsnull,"Attempt to add a NULL element to an nsSmallVoidArray");
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (!HasVector())
|
||
|
{
|
||
|
if (HasSingleChild() || count > 1 || aIndex > 0)
|
||
|
{
|
||
|
vector = SwitchToVector();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// count == 1, aIndex == 0, no elements already
|
||
|
SetSingleChild(other[0]);
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vector = GetChildVector();
|
||
|
}
|
||
|
|
||
|
if (vector)
|
||
|
{
|
||
|
return vector->InsertElementsAt(other,aIndex);
|
||
|
}
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
nsSmallVoidArray::ReplaceElementAt(void* aElement, PRInt32 aIndex)
|
||
|
{
|
||
|
NS_ASSERTION(!(PtrBits(aElement) & 0x1),"Attempt to add element with 0x1 bit set to nsSmallVoidArray");
|
||
|
NS_ASSERTION(aElement != nsnull,"Attempt to add a NULL element to an nsSmallVoidArray");
|
||
|
|
||
|
if (HasSingleChild())
|
||
|
{
|
||
|
if (aIndex == 0)
|
||
|
{
|
||
|
SetSingleChild(aElement);
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nsVoidArray* vector = GetChildVector();
|
||
|
if (!vector)
|
||
|
{
|
||
|
if (aIndex == 0)
|
||
|
{
|
||
|
SetSingleChild(aElement);
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
vector = SwitchToVector();
|
||
|
if (!vector)
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
return vector->ReplaceElementAt(aElement, aIndex);
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
nsSmallVoidArray::AppendElement(void* aElement)
|
||
|
{
|
||
|
NS_ASSERTION(!(PtrBits(aElement) & 0x1),"Attempt to add element with 0x1 bit set to nsSmallVoidArray");
|
||
|
NS_ASSERTION(aElement != nsnull,"Attempt to add a NULL element to an nsSmallVoidArray");
|
||
|
|
||
|
nsVoidArray* vector;
|
||
|
if (HasSingleChild())
|
||
|
{
|
||
|
vector = SwitchToVector();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vector = GetChildVector();
|
||
|
if (!vector)
|
||
|
{
|
||
|
SetSingleChild(aElement);
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return vector->AppendElement(aElement);
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
nsSmallVoidArray::RemoveElement(void* aElement)
|
||
|
{
|
||
|
if (HasSingleChild())
|
||
|
{
|
||
|
if (aElement == GetSingleChild())
|
||
|
{
|
||
|
SetSingleChild(nsnull);
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nsVoidArray* vector = GetChildVector();
|
||
|
if (vector)
|
||
|
return vector->RemoveElement(aElement);
|
||
|
}
|
||
|
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
nsSmallVoidArray::RemoveElementAt(PRInt32 aIndex)
|
||
|
{
|
||
|
if (HasSingleChild())
|
||
|
{
|
||
|
if (0 == aIndex)
|
||
|
{
|
||
|
SetSingleChild(nsnull);
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nsVoidArray* vector = GetChildVector();
|
||
|
if (vector)
|
||
|
{
|
||
|
return vector->RemoveElementAt(aIndex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
nsSmallVoidArray::RemoveElementsAt(PRInt32 aIndex, PRInt32 aCount)
|
||
|
{
|
||
|
nsVoidArray* vector = GetChildVector();
|
||
|
|
||
|
if (aCount == 0)
|
||
|
return PR_TRUE;
|
||
|
|
||
|
if (HasSingleChild())
|
||
|
{
|
||
|
if (aIndex == 0)
|
||
|
SetSingleChild(nsnull);
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
if (!vector)
|
||
|
return PR_TRUE;
|
||
|
|
||
|
// complex case; remove entries from an array
|
||
|
return vector->RemoveElementsAt(aIndex,aCount);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nsSmallVoidArray::Clear()
|
||
|
{
|
||
|
if (HasVector())
|
||
|
{
|
||
|
nsVoidArray* vector = GetChildVector();
|
||
|
vector->Clear();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetSingleChild(nsnull);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
nsSmallVoidArray::SizeTo(PRInt32 aMin)
|
||
|
{
|
||
|
nsVoidArray* vector;
|
||
|
if (!HasVector())
|
||
|
{
|
||
|
if (aMin <= 1)
|
||
|
return PR_TRUE;
|
||
|
vector = SwitchToVector();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vector = GetChildVector();
|
||
|
if (aMin <= 1)
|
||
|
{
|
||
|
void *prev = nsnull;
|
||
|
if (vector->Count() == 1)
|
||
|
{
|
||
|
prev = vector->ElementAt(0);
|
||
|
}
|
||
|
delete vector;
|
||
|
SetSingleChild(prev);
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
}
|
||
|
return vector->SizeTo(aMin);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nsSmallVoidArray::Compact()
|
||
|
{
|
||
|
if (!HasSingleChild())
|
||
|
{
|
||
|
nsVoidArray* vector = GetChildVector();
|
||
|
if (vector)
|
||
|
vector->Compact();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nsSmallVoidArray::Sort(nsVoidArrayComparatorFunc aFunc, void* aData)
|
||
|
{
|
||
|
if (HasVector())
|
||
|
{
|
||
|
nsVoidArray* vector = GetChildVector();
|
||
|
vector->Sort(aFunc,aData);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
nsSmallVoidArray::EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData)
|
||
|
{
|
||
|
if (HasVector())
|
||
|
{
|
||
|
nsVoidArray* vector = GetChildVector();
|
||
|
return vector->EnumerateForwards(aFunc,aData);
|
||
|
}
|
||
|
if (HasSingleChild())
|
||
|
{
|
||
|
return (*aFunc)(GetSingleChild(), aData);
|
||
|
}
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
nsSmallVoidArray::EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData)
|
||
|
{
|
||
|
if (HasVector())
|
||
|
{
|
||
|
nsVoidArray* vector = GetChildVector();
|
||
|
return vector->EnumerateBackwards(aFunc,aData);
|
||
|
}
|
||
|
if (HasSingleChild())
|
||
|
{
|
||
|
return (*aFunc)(GetSingleChild(), aData);
|
||
|
}
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nsSmallVoidArray::SetSingleChild(void* aChild)
|
||
|
{
|
||
|
if (aChild)
|
||
|
mChildren = (void*)(PtrBits(aChild) | 0x1);
|
||
|
else
|
||
|
mChildren = nsnull;
|
||
|
}
|
||
|
|
||
|
nsVoidArray*
|
||
|
nsSmallVoidArray::SwitchToVector()
|
||
|
{
|
||
|
void* child = GetSingleChild();
|
||
|
|
||
|
mChildren = (void*)new nsAutoVoidArray();
|
||
|
nsVoidArray* vector = GetChildVector();
|
||
|
if (vector && child)
|
||
|
vector->AppendElement(child);
|
||
|
|
||
|
return vector;
|
||
|
}
|