diff --git a/xpcom/glue/nsCOMArray.cpp b/xpcom/glue/nsCOMArray.cpp index b22bbfb68ad..53fbec3f562 100644 --- a/xpcom/glue/nsCOMArray.cpp +++ b/xpcom/glue/nsCOMArray.cpp @@ -159,3 +159,18 @@ nsCOMArray_base::Clear() mArray.Clear(); } +PRBool +nsCOMArray_base::SetCount(PRInt32 aNewCount) +{ + NS_ASSERTION(aNewCount >= 0,"SetCount(negative index)"); + if (aNewCount < 0) + return PR_FALSE; + + PRInt32 count = Count(), i; + for (i = aNewCount; i < count; ++i) { + nsISupports* obj = ObjectAt(i); + NS_IF_RELEASE(obj); + } + return mArray.SetCount(aNewCount); +} + diff --git a/xpcom/glue/nsCOMArray.h b/xpcom/glue/nsCOMArray.h index 9b25fef4bee..b4c3c5aa08a 100644 --- a/xpcom/glue/nsCOMArray.h +++ b/xpcom/glue/nsCOMArray.h @@ -95,6 +95,9 @@ public: PRInt32 Count() const { return mArray.Count(); } + // If the array grows, the newly created entries will all be null; + // if the array shrinks, the excess entries will all be released. + PRBool SetCount(PRInt32 aNewCount); nsISupports* ObjectAt(PRInt32 aIndex) const { return static_cast(mArray.FastElementAt(aIndex)); diff --git a/xpcom/glue/nsVoidArray.cpp b/xpcom/glue/nsVoidArray.cpp index 64dc7a7fb37..d2437b8d031 100644 --- a/xpcom/glue/nsVoidArray.cpp +++ b/xpcom/glue/nsVoidArray.cpp @@ -376,6 +376,52 @@ nsVoidArray::~nsVoidArray() free(reinterpret_cast(mImpl)); } +PRBool nsVoidArray::SetCount(PRInt32 aNewCount) +{ + NS_ASSERTION(aNewCount >= 0,"SetCount(negative index)"); + if (aNewCount < 0) + return PR_FALSE; + + if (aNewCount == 0) + { + Clear(); + return PR_TRUE; + } + + if (PRUint32(aNewCount) > PRUint32(GetArraySize())) + { + PRInt32 oldCount = Count(); + PRInt32 growDelta = aNewCount - oldCount; + + // frees old mImpl IF this succeeds + if (!GrowArrayBy(growDelta)) + return PR_FALSE; + } + + if (aNewCount > mImpl->mCount) + { + // Make sure that new entries added to the array by this + // SetCount are cleared to 0. Some users of this assume that. + // This code means we don't have to memset when we allocate an array. + memset(&mImpl->mArray[mImpl->mCount], 0, + (aNewCount - mImpl->mCount) * sizeof(mImpl->mArray[0])); + } + + mImpl->mCount = aNewCount; + +#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; +} + PRInt32 nsVoidArray::IndexOf(void* aPossibleElement) const { if (mImpl) diff --git a/xpcom/glue/nsVoidArray.h b/xpcom/glue/nsVoidArray.h index 0d05162162b..db90853a5b3 100644 --- a/xpcom/glue/nsVoidArray.h +++ b/xpcom/glue/nsVoidArray.h @@ -62,6 +62,8 @@ public: inline PRInt32 Count() const { return mImpl ? mImpl->mCount : 0; } + // If the array grows, the newly created entries will all be null + PRBool SetCount(PRInt32 aNewCount); // returns the max number that can be held without allocating inline PRInt32 GetArraySize() const { return mImpl ? (PRInt32(mImpl->mBits) & kArraySizeMask) : 0; diff --git a/xpcom/tests/Makefile.in b/xpcom/tests/Makefile.in index 02ee02dd1f4..f272b865ac3 100644 --- a/xpcom/tests/Makefile.in +++ b/xpcom/tests/Makefile.in @@ -77,6 +77,7 @@ SIMPLE_PROGRAMS := $(CPPSRCS:.cpp=$(BIN_SUFFIX)) CPP_UNIT_TESTS = \ ShowSSEConfig.cpp \ TestAutoPtr.cpp \ + TestCOMArray.cpp \ TestCOMPtr.cpp \ TestCOMPtrEq.cpp \ TestFactory.cpp \ diff --git a/xpcom/tests/TestCOMArray.cpp b/xpcom/tests/TestCOMArray.cpp new file mode 100644 index 00000000000..77ea8a93f98 --- /dev/null +++ b/xpcom/tests/TestCOMArray.cpp @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=4:et:sw=4: +/* ***** 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 the Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * 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 "TestHarness.h" +#include "nsCOMArray.h" + +// {9e70a320-be02-11d1-8031-006008159b5a} +#define NS_IFOO_IID \ + {0x9e70a320, 0xbe02, 0x11d1, \ + {0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a}} + +class IFoo : public nsISupports { +public: + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID) + + NS_IMETHOD_(nsrefcnt) RefCnt() = 0; + NS_IMETHOD_(PRInt32) ID() = 0; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(IFoo, NS_IFOO_IID) + +class Foo : public IFoo { +public: + + Foo(PRInt32 aID); + ~Foo(); + + // nsISupports implementation + NS_DECL_ISUPPORTS + + // IFoo implementation + NS_IMETHOD_(nsrefcnt) RefCnt() { return mRefCnt; } + NS_IMETHOD_(PRInt32) ID() { return mID; } + + static PRInt32 gCount; + + PRInt32 mID; +}; + +PRInt32 Foo::gCount = 0; + +Foo::Foo(PRInt32 aID) +{ + mID = aID; + ++gCount; +} + +Foo::~Foo() +{ + --gCount; +} + +NS_IMPL_ISUPPORTS1(Foo, IFoo) + + +typedef nsCOMArray Array; + + +int main(int argc, char **argv) +{ + ScopedXPCOM xpcom("nsCOMArrayTests"); + if (xpcom.failed()) { + return 1; + } + + int rv = 0; + + Array arr; + + for (PRInt32 i = 0; i < 20; ++i) { + nsCOMPtr foo = new Foo(i); + arr.AppendObject(foo); + } + + if (arr.Count() != 20 || Foo::gCount != 20) { + fail("nsCOMArray::AppendObject failed"); + rv = 1; + } + + arr.SetCount(10); + + if (arr.Count() != 10 || Foo::gCount != 10) { + fail("nsCOMArray::SetCount shortening of array failed"); + rv = 1; + } + + arr.SetCount(30); + + if (arr.Count() != 30 || Foo::gCount != 10) { + fail("nsCOMArray::SetCount lengthening of array failed"); + rv = 1; + } + + for (PRInt32 i = 0; i < 10; ++i) { + if (arr[i] == nsnull) { + fail("nsCOMArray elements should be non-null"); + rv = 1; + break; + } + } + + for (PRInt32 i = 10; i < 30; ++i) { + if (arr[i] != nsnull) { + fail("nsCOMArray elements should be null"); + rv = 1; + break; + } + } + + return rv; +}