1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Scott Collins <scc@mozilla.org>: |do_QueryElementAt|
24 * Pierre Phaneuf <pp@ludusdesign.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
42 #include "nsSupportsArray.h"
43 #include "nsSupportsArrayEnumerator.h"
44 #include "nsAString.h"
45 #include "nsIObjectInputStream.h"
46 #include "nsIObjectOutputStream.h"
48 #if DEBUG_SUPPORTSARRAY
49 #define MAXSUPPORTS 20
58 static int sizesUsed
; // number of the elements of the arrays used
59 static int sizesAlloced
[MAXSUPPORTS
]; // sizes of the allocations. sorted
60 static int NumberOfSize
[MAXSUPPORTS
]; // number of this allocation size (1 per array)
61 static int AllocedOfSize
[MAXSUPPORTS
]; // number of this allocation size (each size for array used)
62 static int GrowInPlace
[MAXSUPPORTS
];
64 // these are per-allocation
65 static int MaxElements
[3000];
68 #define ADD_TO_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
70 if (sizesAlloced[i] == (int)(size)) \
71 { ((x)[i])++; break; } \
73 if (i >= sizesUsed && sizesUsed < MAXSUPPORTS) \
74 { sizesAlloced[sizesUsed] = (size); \
75 ((x)[sizesUsed++])++; break; \
79 #define SUB_FROM_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
81 if (sizesAlloced[i] == (int)(size)) \
82 { ((x)[i])--; break; } \
87 SupportsStats::SupportsStats()
93 SupportsStats::~SupportsStats()
96 for (i
= 0; i
< sizesUsed
; i
++)
98 printf("Size %d:\n",sizesAlloced
[i
]);
99 printf("\tNumber of SupportsArrays this size (max): %d\n",NumberOfSize
[i
]);
100 printf("\tNumber of allocations this size (total): %d\n",AllocedOfSize
[i
]);
101 printf("\tNumber of GrowsInPlace this size (total): %d\n",GrowInPlace
[i
]);
103 printf("Max Size of SupportsArray:\n");
104 for (i
= 0; i
< (int)(sizeof(MaxElements
)/sizeof(MaxElements
[0])); i
++)
107 printf("\t%d: %d\n",i
,MaxElements
[i
]);
111 // Just so constructor/destructor get called
112 SupportsStats gSupportsStats
;
116 nsQueryElementAt::operator()( const nsIID
& aIID
, void** aResult
) const
118 nsresult status
= mCollection
119 ? mCollection
->QueryElementAt(mIndex
, aIID
, aResult
)
120 : NS_ERROR_NULL_POINTER
;
128 static const PRInt32 kGrowArrayBy
= 8;
129 static const PRInt32 kLinearThreshold
= 16 * sizeof(nsISupports
*);
131 nsSupportsArray::nsSupportsArray()
134 mArraySize
= kAutoArraySize
;
136 #if DEBUG_SUPPORTSARRAY
139 ADD_TO_STATS(NumberOfSize
,kAutoArraySize
*sizeof(mArray
[0]));
144 nsSupportsArray::~nsSupportsArray()
149 PRBool
nsSupportsArray::GrowArrayBy(PRInt32 aGrowBy
)
151 // We have to grow the array. Grow by kGrowArrayBy slots if we're smaller
152 // than kLinearThreshold bytes, or a power of two if we're larger.
153 // This is much more efficient with most memory allocators, especially
154 // if it's very large, or of the allocator is binned.
155 if (aGrowBy
< kGrowArrayBy
)
156 aGrowBy
= kGrowArrayBy
;
158 PRUint32 newCount
= mArraySize
+ aGrowBy
; // Minimum increase
159 PRUint32 newSize
= sizeof(mArray
[0]) * newCount
;
161 if (newSize
>= (PRUint32
) kLinearThreshold
)
163 // newCount includes enough space for at least kGrowArrayBy new slots.
164 // Select the next power-of-two size in bytes above that if newSize is
165 // not a power of two.
166 if (newSize
& (newSize
- 1))
167 newSize
= PR_BIT(PR_CeilingLog2(newSize
));
169 newCount
= newSize
/ sizeof(mArray
[0]);
171 // XXX This would be far more efficient in many allocators if we used
172 // XXX PR_Realloc(), etc
173 nsISupports
** oldArray
= mArray
;
175 mArray
= new nsISupports
*[newCount
];
176 if (!mArray
) { // ran out of memory
180 mArraySize
= newCount
;
182 #if DEBUG_SUPPORTSARRAY
183 if (oldArray
== mArray
) // can't happen without use of realloc
184 ADD_TO_STATS(GrowInPlace
,mCount
);
185 ADD_TO_STATS(AllocedOfSize
,mArraySize
*sizeof(mArray
[0]));
186 if (mArraySize
> mMaxSize
)
188 ADD_TO_STATS(NumberOfSize
,mArraySize
*sizeof(mArray
[0]));
189 if (oldArray
!= &(mAutoArray
[0]))
190 SUB_FROM_STATS(NumberOfSize
,mCount
*sizeof(mArray
[0]));
191 mMaxSize
= mArraySize
;
194 if (oldArray
) { // need to move old data
196 ::memcpy(mArray
, oldArray
, mCount
* sizeof(nsISupports
*));
198 if (oldArray
!= &(mAutoArray
[0])) {
207 nsSupportsArray::Create(nsISupports
*aOuter
, REFNSIID aIID
, void **aResult
)
210 return NS_ERROR_NO_AGGREGATION
;
212 nsCOMPtr
<nsISupportsArray
> it
= new nsSupportsArray();
214 return NS_ERROR_OUT_OF_MEMORY
;
216 return it
->QueryInterface(aIID
, aResult
);
219 NS_IMPL_THREADSAFE_ISUPPORTS3(nsSupportsArray
, nsISupportsArray
, nsICollection
, nsISerializable
)
222 nsSupportsArray::Read(nsIObjectInputStream
*aStream
)
226 PRUint32 newArraySize
;
227 rv
= aStream
->Read32(&newArraySize
);
229 if (newArraySize
<= kAutoArraySize
) {
230 if (mArray
!= mAutoArray
) {
234 newArraySize
= kAutoArraySize
;
237 if (newArraySize
<= mArraySize
) {
238 // Keep non-default-size mArray, it's more than big enough.
239 newArraySize
= mArraySize
;
242 nsISupports
** array
= new nsISupports
*[newArraySize
];
244 return NS_ERROR_OUT_OF_MEMORY
;
245 if (mArray
!= mAutoArray
)
250 mArraySize
= newArraySize
;
252 rv
= aStream
->Read32(&mCount
);
253 if (NS_FAILED(rv
)) return rv
;
255 NS_ASSERTION(mCount
<= mArraySize
, "overlarge mCount!");
256 if (mCount
> mArraySize
)
259 for (PRUint32 i
= 0; i
< mCount
; i
++) {
260 rv
= aStream
->ReadObject(PR_TRUE
, &mArray
[i
]);
261 if (NS_FAILED(rv
)) return rv
;
268 nsSupportsArray::Write(nsIObjectOutputStream
*aStream
)
272 rv
= aStream
->Write32(mArraySize
);
273 if (NS_FAILED(rv
)) return rv
;
275 rv
= aStream
->Write32(mCount
);
276 if (NS_FAILED(rv
)) return rv
;
278 for (PRUint32 i
= 0; i
< mCount
; i
++) {
279 rv
= aStream
->WriteObject(mArray
[i
], PR_TRUE
);
280 if (NS_FAILED(rv
)) return rv
;
286 void nsSupportsArray::DeleteArray(void)
289 if (mArray
!= &(mAutoArray
[0])) {
292 mArraySize
= kAutoArraySize
;
297 NS_IMETHODIMP_(PRBool
)
298 nsSupportsArray::Equals(const nsISupportsArray
* aOther
)
302 nsISupportsArray
* other
= const_cast<nsISupportsArray
*>(aOther
);
303 nsresult rv
= other
->Count(&countOther
);
307 if (mCount
== countOther
) {
308 PRUint32 index
= mCount
;
309 nsCOMPtr
<nsISupports
> otherElem
;
311 if (NS_FAILED(other
->GetElementAt(index
, getter_AddRefs(otherElem
))))
313 if (mArray
[index
] != otherElem
)
322 NS_IMETHODIMP_(nsISupports
*)
323 nsSupportsArray::ElementAt(PRUint32 aIndex
)
325 if (aIndex
< mCount
) {
326 nsISupports
* element
= mArray
[aIndex
];
327 NS_IF_ADDREF(element
);
333 NS_IMETHODIMP_(PRInt32
)
334 nsSupportsArray::IndexOf(const nsISupports
* aPossibleElement
)
336 return IndexOfStartingAt(aPossibleElement
, 0);
339 NS_IMETHODIMP_(PRInt32
)
340 nsSupportsArray::IndexOfStartingAt(const nsISupports
* aPossibleElement
,
341 PRUint32 aStartIndex
)
343 if (aStartIndex
< mCount
) {
344 const nsISupports
** start
= (const nsISupports
**)mArray
; // work around goofy compiler behavior
345 const nsISupports
** ep
= (start
+ aStartIndex
);
346 const nsISupports
** end
= (start
+ mCount
);
348 if (aPossibleElement
== *ep
) {
357 NS_IMETHODIMP_(PRInt32
)
358 nsSupportsArray::LastIndexOf(const nsISupports
* aPossibleElement
)
361 const nsISupports
** start
= (const nsISupports
**)mArray
; // work around goofy compiler behavior
362 const nsISupports
** ep
= (start
+ mCount
);
363 while (start
<= --ep
) {
364 if (aPossibleElement
== *ep
) {
372 NS_IMETHODIMP_(PRBool
)
373 nsSupportsArray::InsertElementAt(nsISupports
* aElement
, PRUint32 aIndex
)
375 if (aIndex
<= mCount
) {
376 if (mArraySize
< (mCount
+ 1)) {
377 // need to grow the array
382 // Could be slightly more efficient if GrowArrayBy knew about the
383 // split, but the difference is trivial.
384 PRUint32 slide
= (mCount
- aIndex
);
386 ::memmove(mArray
+ aIndex
+ 1, mArray
+ aIndex
, slide
* sizeof(nsISupports
*));
389 mArray
[aIndex
] = aElement
;
390 NS_IF_ADDREF(aElement
);
393 #if DEBUG_SUPPORTSARRAY
394 if (mCount
> mMaxCount
&&
395 mCount
< (PRInt32
)(sizeof(MaxElements
)/sizeof(MaxElements
[0])))
397 MaxElements
[mCount
]++;
398 MaxElements
[mMaxCount
]--;
407 NS_IMETHODIMP_(PRBool
)
408 nsSupportsArray::InsertElementsAt(nsISupportsArray
* aElements
, PRUint32 aIndex
)
413 PRUint32 countElements
;
414 if (NS_FAILED( aElements
->Count( &countElements
) ))
417 if (aIndex
<= mCount
) {
418 if (mArraySize
< (mCount
+ countElements
)) {
419 // need to grow the array
420 if (!GrowArrayBy(countElements
))
424 // Could be slightly more efficient if GrowArrayBy knew about the
425 // split, but the difference is trivial.
426 PRUint32 slide
= (mCount
- aIndex
);
428 ::memmove(mArray
+ aIndex
+ countElements
, mArray
+ aIndex
,
429 slide
* sizeof(nsISupports
*));
432 for (PRUint32 i
= 0; i
< countElements
; ++i
, ++mCount
) {
433 // use GetElementAt to copy and do AddRef for us
434 if (NS_FAILED( aElements
->GetElementAt( i
, mArray
+ aIndex
+ i
) ))
438 #if DEBUG_SUPPORTSARRAY
439 if (mCount
> mMaxCount
&&
440 mCount
< (PRInt32
)(sizeof(MaxElements
)/sizeof(MaxElements
[0])))
442 MaxElements
[mCount
]++;
443 MaxElements
[mMaxCount
]--;
452 NS_IMETHODIMP_(PRBool
)
453 nsSupportsArray::ReplaceElementAt(nsISupports
* aElement
, PRUint32 aIndex
)
455 if (aIndex
< mCount
) {
456 NS_IF_ADDREF(aElement
); // addref first in case it's the same object!
457 NS_IF_RELEASE(mArray
[aIndex
]);
458 mArray
[aIndex
] = aElement
;
464 NS_IMETHODIMP_(PRBool
)
465 nsSupportsArray::RemoveElementsAt(PRUint32 aIndex
, PRUint32 aCount
)
467 if (aIndex
+ aCount
<= mCount
) {
468 for (PRUint32 i
= 0; i
< aCount
; i
++)
469 NS_IF_RELEASE(mArray
[aIndex
+i
]);
471 PRInt32 slide
= (mCount
- aIndex
);
473 ::memmove(mArray
+ aIndex
, mArray
+ aIndex
+ aCount
,
474 slide
* sizeof(nsISupports
*));
481 NS_IMETHODIMP_(PRBool
)
482 nsSupportsArray::RemoveElement(const nsISupports
* aElement
, PRUint32 aStartIndex
)
484 PRInt32 theIndex
= IndexOfStartingAt(aElement
,aStartIndex
);
486 return RemoveElementAt(theIndex
);
491 NS_IMETHODIMP_(PRBool
)
492 nsSupportsArray::RemoveLastElement(const nsISupports
* aElement
)
494 PRInt32 theIndex
= LastIndexOf(aElement
);
496 return RemoveElementAt(theIndex
);
501 NS_IMETHODIMP_(PRBool
)
502 nsSupportsArray::MoveElement(PRInt32 aFrom
, PRInt32 aTo
)
504 nsISupports
*tempElement
;
509 if (aTo
< 0 || aFrom
< 0 ||
510 (PRUint32
) aTo
>= mCount
|| (PRUint32
) aFrom
>= mCount
)
512 // can't extend the array when moving an element. Also catches mImpl = null
515 tempElement
= mArray
[aFrom
];
519 // Moving one element closer to the head; the elements inbetween move down
520 ::memmove(mArray
+ aTo
+ 1, mArray
+ aTo
,
521 (aFrom
-aTo
) * sizeof(mArray
[0]));
522 mArray
[aTo
] = tempElement
;
524 else // already handled aFrom == aTo
526 // Moving one element closer to the tail; the elements inbetween move up
527 ::memmove(mArray
+ aFrom
, mArray
+ aFrom
+ 1,
528 (aTo
-aFrom
) * sizeof(mArray
[0]));
529 mArray
[aTo
] = tempElement
;
536 nsSupportsArray::Clear(void)
541 NS_IF_RELEASE(mArray
[mCount
]);
542 } while (0 != mCount
);
548 nsSupportsArray::Compact(void)
550 #if DEBUG_SUPPORTSARRAY
551 PRUint32 oldArraySize
= mArraySize
;
553 if ((mArraySize
!= mCount
) && (kAutoArraySize
< mArraySize
)) {
554 nsISupports
** oldArray
= mArray
;
555 if (mCount
<= kAutoArraySize
) {
557 mArraySize
= kAutoArraySize
;
560 mArray
= new nsISupports
*[mCount
];
567 #if DEBUG_SUPPORTSARRAY
568 if (oldArray
== mArray
&&
569 oldArray
!= &(mAutoArray
[0])) // can't happen without use of realloc
570 ADD_TO_STATS(GrowInPlace
,oldArraySize
);
571 if (oldArray
!= &(mAutoArray
[0]))
572 ADD_TO_STATS(AllocedOfSize
,mArraySize
*sizeof(mArray
[0]));
574 ::memcpy(mArray
, oldArray
, mCount
* sizeof(nsISupports
*));
580 NS_IMETHODIMP_(PRBool
)
581 nsSupportsArray::SizeTo(PRInt32 aSize
)
583 #if DEBUG_SUPPORTSARRAY
584 PRUint32 oldArraySize
= mArraySize
;
586 NS_ASSERTION(aSize
>= 0, "negative aSize!");
588 // XXX for aSize < mCount we could resize to mCount
589 if (mArraySize
== (PRUint32
) aSize
|| (PRUint32
) aSize
< mCount
)
590 return PR_TRUE
; // nothing to do
592 // switch back to autoarray if possible
593 nsISupports
** oldArray
= mArray
;
594 if ((PRUint32
) aSize
<= kAutoArraySize
) {
596 mArraySize
= kAutoArraySize
;
599 mArray
= new nsISupports
*[aSize
];
606 #if DEBUG_SUPPORTSARRAY
607 if (oldArray
== mArray
&&
608 oldArray
!= &(mAutoArray
[0])) // can't happen without use of realloc
609 ADD_TO_STATS(GrowInPlace
,oldArraySize
);
610 if (oldArray
!= &(mAutoArray
[0]))
611 ADD_TO_STATS(AllocedOfSize
,mArraySize
*sizeof(mArray
[0]));
613 ::memcpy(mArray
, oldArray
, mCount
* sizeof(nsISupports
*));
614 if (oldArray
!= mAutoArray
)
620 NS_IMETHODIMP_(PRBool
)
621 nsSupportsArray::EnumerateForwards(nsISupportsArrayEnumFunc aFunc
, void* aData
)
624 PRBool running
= PR_TRUE
;
626 while (running
&& (++aIndex
< (PRInt32
)mCount
)) {
627 running
= (*aFunc
)(mArray
[aIndex
], aData
);
632 NS_IMETHODIMP_(PRBool
)
633 nsSupportsArray::EnumerateBackwards(nsISupportsArrayEnumFunc aFunc
, void* aData
)
635 PRUint32 aIndex
= mCount
;
636 PRBool running
= PR_TRUE
;
638 while (running
&& (0 < aIndex
--)) {
639 running
= (*aFunc
)(mArray
[aIndex
], aData
);
645 nsSupportsArray::Enumerate(nsIEnumerator
* *result
)
647 nsSupportsArrayEnumerator
* e
= new nsSupportsArrayEnumerator(this);
649 return NS_ERROR_OUT_OF_MEMORY
;
656 CopyElement(nsISupports
* aElement
, void *aData
)
659 nsISupportsArray
* newArray
= (nsISupportsArray
*)aData
;
660 rv
= newArray
->AppendElement(aElement
);
661 return NS_SUCCEEDED(rv
);
665 nsSupportsArray::Clone(nsISupportsArray
* *result
)
668 nsISupportsArray
* newArray
;
669 rv
= NS_NewISupportsArray(&newArray
);
670 PRBool ok
= EnumerateForwards(CopyElement
, newArray
);
671 if (!ok
) return NS_ERROR_OUT_OF_MEMORY
;
677 NS_NewISupportsArray(nsISupportsArray
** aInstancePtrResult
)
680 rv
= nsSupportsArray::Create(NULL
, NS_GET_IID(nsISupportsArray
),
681 (void**)aInstancePtrResult
);
685 class nsArrayEnumerator
: public nsISimpleEnumerator
688 // nsISupports interface
691 // nsISimpleEnumerator interface
692 NS_IMETHOD
HasMoreElements(PRBool
* aResult
);
693 NS_IMETHOD
GetNext(nsISupports
** aResult
);
695 // nsArrayEnumerator methods
696 nsArrayEnumerator(nsISupportsArray
* aValueArray
);
699 ~nsArrayEnumerator(void);
702 nsISupportsArray
* mValueArray
;
706 nsArrayEnumerator::nsArrayEnumerator(nsISupportsArray
* aValueArray
)
707 : mValueArray(aValueArray
),
710 NS_IF_ADDREF(mValueArray
);
713 nsArrayEnumerator::~nsArrayEnumerator(void)
715 NS_IF_RELEASE(mValueArray
);
718 NS_IMPL_ISUPPORTS1(nsArrayEnumerator
, nsISimpleEnumerator
)
721 nsArrayEnumerator::HasMoreElements(PRBool
* aResult
)
723 NS_PRECONDITION(aResult
!= 0, "null ptr");
725 return NS_ERROR_NULL_POINTER
;
733 nsresult rv
= mValueArray
->Count(&cnt
);
734 if (NS_FAILED(rv
)) return rv
;
735 *aResult
= (mIndex
< (PRInt32
) cnt
);
740 nsArrayEnumerator::GetNext(nsISupports
** aResult
)
742 NS_PRECONDITION(aResult
!= 0, "null ptr");
744 return NS_ERROR_NULL_POINTER
;
752 nsresult rv
= mValueArray
->Count(&cnt
);
753 if (NS_FAILED(rv
)) return rv
;
754 if (mIndex
>= (PRInt32
) cnt
)
755 return NS_ERROR_UNEXPECTED
;
757 *aResult
= mValueArray
->ElementAt(mIndex
++);
762 NS_NewArrayEnumerator(nsISimpleEnumerator
* *result
,
763 nsISupportsArray
* array
)
765 nsArrayEnumerator
* enumer
= new nsArrayEnumerator(array
);
766 if (enumer
== nsnull
)
767 return NS_ERROR_OUT_OF_MEMORY
;