1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "nsSupportsArray.h"
9 #include "nsSupportsArrayEnumerator.h"
10 #include "nsAString.h"
11 #include "nsIObjectInputStream.h"
12 #include "nsIObjectOutputStream.h"
14 #if DEBUG_SUPPORTSARRAY
15 #define MAXSUPPORTS 20
24 static int sizesUsed
; // number of the elements of the arrays used
25 static int sizesAlloced
[MAXSUPPORTS
]; // sizes of the allocations. sorted
26 static int NumberOfSize
[MAXSUPPORTS
]; // number of this allocation size (1 per array)
27 static int AllocedOfSize
[MAXSUPPORTS
]; // number of this allocation size (each size for array used)
28 static int GrowInPlace
[MAXSUPPORTS
];
30 // these are per-allocation
31 static int MaxElements
[3000];
34 #define ADD_TO_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
36 if (sizesAlloced[i] == (int)(size)) \
37 { ((x)[i])++; break; } \
39 if (i >= sizesUsed && sizesUsed < MAXSUPPORTS) \
40 { sizesAlloced[sizesUsed] = (size); \
41 ((x)[sizesUsed++])++; break; \
45 #define SUB_FROM_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
47 if (sizesAlloced[i] == (int)(size)) \
48 { ((x)[i])--; break; } \
53 SupportsStats::SupportsStats()
59 SupportsStats::~SupportsStats()
62 for (i
= 0; i
< sizesUsed
; i
++)
64 printf("Size %d:\n",sizesAlloced
[i
]);
65 printf("\tNumber of SupportsArrays this size (max): %d\n",NumberOfSize
[i
]);
66 printf("\tNumber of allocations this size (total): %d\n",AllocedOfSize
[i
]);
67 printf("\tNumber of GrowsInPlace this size (total): %d\n",GrowInPlace
[i
]);
69 printf("Max Size of SupportsArray:\n");
70 for (i
= 0; i
< (int)(sizeof(MaxElements
)/sizeof(MaxElements
[0])); i
++)
73 printf("\t%d: %d\n",i
,MaxElements
[i
]);
77 // Just so constructor/destructor get called
78 SupportsStats gSupportsStats
;
82 nsQueryElementAt::operator()( const nsIID
& aIID
, void** aResult
) const
84 nsresult status
= mCollection
85 ? mCollection
->QueryElementAt(mIndex
, aIID
, aResult
)
86 : NS_ERROR_NULL_POINTER
;
94 static const int32_t kGrowArrayBy
= 8;
95 static const int32_t kLinearThreshold
= 16 * sizeof(nsISupports
*);
97 nsSupportsArray::nsSupportsArray()
100 mArraySize
= kAutoArraySize
;
102 #if DEBUG_SUPPORTSARRAY
105 ADD_TO_STATS(NumberOfSize
,kAutoArraySize
*sizeof(mArray
[0]));
110 nsSupportsArray::~nsSupportsArray()
115 void nsSupportsArray::GrowArrayBy(int32_t aGrowBy
)
117 // We have to grow the array. Grow by kGrowArrayBy slots if we're smaller
118 // than kLinearThreshold bytes, or a power of two if we're larger.
119 // This is much more efficient with most memory allocators, especially
120 // if it's very large, or of the allocator is binned.
121 if (aGrowBy
< kGrowArrayBy
)
122 aGrowBy
= kGrowArrayBy
;
124 uint32_t newCount
= mArraySize
+ aGrowBy
; // Minimum increase
125 uint32_t newSize
= sizeof(mArray
[0]) * newCount
;
127 if (newSize
>= (uint32_t) kLinearThreshold
)
129 // newCount includes enough space for at least kGrowArrayBy new slots.
130 // Select the next power-of-two size in bytes above that if newSize is
131 // not a power of two.
132 if (newSize
& (newSize
- 1))
133 newSize
= 1u << PR_CeilingLog2(newSize
);
135 newCount
= newSize
/ sizeof(mArray
[0]);
137 // XXX This would be far more efficient in many allocators if we used
138 // XXX PR_Realloc(), etc
139 nsISupports
** oldArray
= mArray
;
141 mArray
= new nsISupports
*[newCount
];
142 mArraySize
= newCount
;
144 #if DEBUG_SUPPORTSARRAY
145 if (oldArray
== mArray
) // can't happen without use of realloc
146 ADD_TO_STATS(GrowInPlace
,mCount
);
147 ADD_TO_STATS(AllocedOfSize
,mArraySize
*sizeof(mArray
[0]));
148 if (mArraySize
> mMaxSize
)
150 ADD_TO_STATS(NumberOfSize
,mArraySize
*sizeof(mArray
[0]));
151 if (oldArray
!= &(mAutoArray
[0]))
152 SUB_FROM_STATS(NumberOfSize
,mCount
*sizeof(mArray
[0]));
153 mMaxSize
= mArraySize
;
156 if (oldArray
) { // need to move old data
158 ::memcpy(mArray
, oldArray
, mCount
* sizeof(nsISupports
*));
160 if (oldArray
!= &(mAutoArray
[0])) {
167 nsSupportsArray::Create(nsISupports
*aOuter
, REFNSIID aIID
, void **aResult
)
170 return NS_ERROR_NO_AGGREGATION
;
172 nsCOMPtr
<nsISupportsArray
> it
= new nsSupportsArray();
174 return it
->QueryInterface(aIID
, aResult
);
177 NS_IMPL_THREADSAFE_ISUPPORTS3(nsSupportsArray
, nsISupportsArray
, nsICollection
, nsISerializable
)
180 nsSupportsArray::Read(nsIObjectInputStream
*aStream
)
184 uint32_t newArraySize
;
185 rv
= aStream
->Read32(&newArraySize
);
187 if (newArraySize
<= kAutoArraySize
) {
188 if (mArray
!= mAutoArray
) {
192 newArraySize
= kAutoArraySize
;
195 if (newArraySize
<= mArraySize
) {
196 // Keep non-default-size mArray, it's more than big enough.
197 newArraySize
= mArraySize
;
200 nsISupports
** array
= new nsISupports
*[newArraySize
];
201 if (mArray
!= mAutoArray
)
206 mArraySize
= newArraySize
;
208 rv
= aStream
->Read32(&mCount
);
209 if (NS_FAILED(rv
)) return rv
;
211 NS_ASSERTION(mCount
<= mArraySize
, "overlarge mCount!");
212 if (mCount
> mArraySize
)
215 for (uint32_t i
= 0; i
< mCount
; i
++) {
216 rv
= aStream
->ReadObject(true, &mArray
[i
]);
217 if (NS_FAILED(rv
)) return rv
;
224 nsSupportsArray::Write(nsIObjectOutputStream
*aStream
)
228 rv
= aStream
->Write32(mArraySize
);
229 if (NS_FAILED(rv
)) return rv
;
231 rv
= aStream
->Write32(mCount
);
232 if (NS_FAILED(rv
)) return rv
;
234 for (uint32_t i
= 0; i
< mCount
; i
++) {
235 rv
= aStream
->WriteObject(mArray
[i
], true);
236 if (NS_FAILED(rv
)) return rv
;
242 void nsSupportsArray::DeleteArray(void)
245 if (mArray
!= &(mAutoArray
[0])) {
248 mArraySize
= kAutoArraySize
;
254 nsSupportsArray::Equals(const nsISupportsArray
* aOther
)
258 nsISupportsArray
* other
= const_cast<nsISupportsArray
*>(aOther
);
259 nsresult rv
= other
->Count(&countOther
);
263 if (mCount
== countOther
) {
264 uint32_t index
= mCount
;
265 nsCOMPtr
<nsISupports
> otherElem
;
267 if (NS_FAILED(other
->GetElementAt(index
, getter_AddRefs(otherElem
))))
269 if (mArray
[index
] != otherElem
)
278 NS_IMETHODIMP_(nsISupports
*)
279 nsSupportsArray::ElementAt(uint32_t aIndex
)
281 if (aIndex
< mCount
) {
282 nsISupports
* element
= mArray
[aIndex
];
283 NS_IF_ADDREF(element
);
289 NS_IMETHODIMP_(int32_t)
290 nsSupportsArray::IndexOf(const nsISupports
* aPossibleElement
)
292 return IndexOfStartingAt(aPossibleElement
, 0);
295 NS_IMETHODIMP_(int32_t)
296 nsSupportsArray::IndexOfStartingAt(const nsISupports
* aPossibleElement
,
297 uint32_t aStartIndex
)
299 if (aStartIndex
< mCount
) {
300 const nsISupports
** start
= (const nsISupports
**)mArray
; // work around goofy compiler behavior
301 const nsISupports
** ep
= (start
+ aStartIndex
);
302 const nsISupports
** end
= (start
+ mCount
);
304 if (aPossibleElement
== *ep
) {
313 NS_IMETHODIMP_(int32_t)
314 nsSupportsArray::LastIndexOf(const nsISupports
* aPossibleElement
)
317 const nsISupports
** start
= (const nsISupports
**)mArray
; // work around goofy compiler behavior
318 const nsISupports
** ep
= (start
+ mCount
);
319 while (start
<= --ep
) {
320 if (aPossibleElement
== *ep
) {
329 nsSupportsArray::InsertElementAt(nsISupports
* aElement
, uint32_t aIndex
)
331 if (aIndex
<= mCount
) {
332 if (mArraySize
< (mCount
+ 1)) {
333 // need to grow the array
337 // Could be slightly more efficient if GrowArrayBy knew about the
338 // split, but the difference is trivial.
339 uint32_t slide
= (mCount
- aIndex
);
341 ::memmove(mArray
+ aIndex
+ 1, mArray
+ aIndex
, slide
* sizeof(nsISupports
*));
344 mArray
[aIndex
] = aElement
;
345 NS_IF_ADDREF(aElement
);
348 #if DEBUG_SUPPORTSARRAY
349 if (mCount
> mMaxCount
&&
350 mCount
< (int32_t)(sizeof(MaxElements
)/sizeof(MaxElements
[0])))
352 MaxElements
[mCount
]++;
353 MaxElements
[mMaxCount
]--;
363 nsSupportsArray::InsertElementsAt(nsISupportsArray
* aElements
, uint32_t aIndex
)
368 uint32_t countElements
;
369 if (NS_FAILED( aElements
->Count( &countElements
) ))
372 if (aIndex
<= mCount
) {
373 if (mArraySize
< (mCount
+ countElements
)) {
374 // need to grow the array
375 GrowArrayBy(countElements
);
378 // Could be slightly more efficient if GrowArrayBy knew about the
379 // split, but the difference is trivial.
380 uint32_t slide
= (mCount
- aIndex
);
382 ::memmove(mArray
+ aIndex
+ countElements
, mArray
+ aIndex
,
383 slide
* sizeof(nsISupports
*));
386 for (uint32_t i
= 0; i
< countElements
; ++i
, ++mCount
) {
387 // use GetElementAt to copy and do AddRef for us
388 if (NS_FAILED( aElements
->GetElementAt( i
, mArray
+ aIndex
+ i
) ))
392 #if DEBUG_SUPPORTSARRAY
393 if (mCount
> mMaxCount
&&
394 mCount
< (int32_t)(sizeof(MaxElements
)/sizeof(MaxElements
[0])))
396 MaxElements
[mCount
]++;
397 MaxElements
[mMaxCount
]--;
407 nsSupportsArray::ReplaceElementAt(nsISupports
* aElement
, uint32_t aIndex
)
409 if (aIndex
< mCount
) {
410 NS_IF_ADDREF(aElement
); // addref first in case it's the same object!
411 NS_IF_RELEASE(mArray
[aIndex
]);
412 mArray
[aIndex
] = aElement
;
419 nsSupportsArray::RemoveElementsAt(uint32_t aIndex
, uint32_t aCount
)
421 if (aIndex
+ aCount
<= mCount
) {
422 for (uint32_t i
= 0; i
< aCount
; i
++)
423 NS_IF_RELEASE(mArray
[aIndex
+i
]);
425 int32_t slide
= (mCount
- aIndex
);
427 ::memmove(mArray
+ aIndex
, mArray
+ aIndex
+ aCount
,
428 slide
* sizeof(nsISupports
*));
436 nsSupportsArray::RemoveElement(const nsISupports
* aElement
, uint32_t aStartIndex
)
438 int32_t theIndex
= IndexOfStartingAt(aElement
,aStartIndex
);
440 return RemoveElementAt(theIndex
);
446 nsSupportsArray::RemoveLastElement(const nsISupports
* aElement
)
448 int32_t theIndex
= LastIndexOf(aElement
);
450 return RemoveElementAt(theIndex
);
456 nsSupportsArray::MoveElement(int32_t aFrom
, int32_t aTo
)
458 nsISupports
*tempElement
;
463 if (aTo
< 0 || aFrom
< 0 ||
464 (uint32_t) aTo
>= mCount
|| (uint32_t) aFrom
>= mCount
)
466 // can't extend the array when moving an element. Also catches mImpl = null
469 tempElement
= mArray
[aFrom
];
473 // Moving one element closer to the head; the elements inbetween move down
474 ::memmove(mArray
+ aTo
+ 1, mArray
+ aTo
,
475 (aFrom
-aTo
) * sizeof(mArray
[0]));
476 mArray
[aTo
] = tempElement
;
478 else // already handled aFrom == aTo
480 // Moving one element closer to the tail; the elements inbetween move up
481 ::memmove(mArray
+ aFrom
, mArray
+ aFrom
+ 1,
482 (aTo
-aFrom
) * sizeof(mArray
[0]));
483 mArray
[aTo
] = tempElement
;
490 nsSupportsArray::Clear(void)
495 NS_IF_RELEASE(mArray
[mCount
]);
496 } while (0 != mCount
);
502 nsSupportsArray::Compact(void)
504 #if DEBUG_SUPPORTSARRAY
505 uint32_t oldArraySize
= mArraySize
;
507 if ((mArraySize
!= mCount
) && (kAutoArraySize
< mArraySize
)) {
508 nsISupports
** oldArray
= mArray
;
509 if (mCount
<= kAutoArraySize
) {
511 mArraySize
= kAutoArraySize
;
514 mArray
= new nsISupports
*[mCount
];
521 #if DEBUG_SUPPORTSARRAY
522 if (oldArray
== mArray
&&
523 oldArray
!= &(mAutoArray
[0])) // can't happen without use of realloc
524 ADD_TO_STATS(GrowInPlace
,oldArraySize
);
525 if (oldArray
!= &(mAutoArray
[0]))
526 ADD_TO_STATS(AllocedOfSize
,mArraySize
*sizeof(mArray
[0]));
528 ::memcpy(mArray
, oldArray
, mCount
* sizeof(nsISupports
*));
535 nsSupportsArray::SizeTo(int32_t aSize
)
537 #if DEBUG_SUPPORTSARRAY
538 uint32_t oldArraySize
= mArraySize
;
540 NS_ASSERTION(aSize
>= 0, "negative aSize!");
542 // XXX for aSize < mCount we could resize to mCount
543 if (mArraySize
== (uint32_t) aSize
|| (uint32_t) aSize
< mCount
)
544 return true; // nothing to do
546 // switch back to autoarray if possible
547 nsISupports
** oldArray
= mArray
;
548 if ((uint32_t) aSize
<= kAutoArraySize
) {
550 mArraySize
= kAutoArraySize
;
553 mArray
= new nsISupports
*[aSize
];
560 #if DEBUG_SUPPORTSARRAY
561 if (oldArray
== mArray
&&
562 oldArray
!= &(mAutoArray
[0])) // can't happen without use of realloc
563 ADD_TO_STATS(GrowInPlace
,oldArraySize
);
564 if (oldArray
!= &(mAutoArray
[0]))
565 ADD_TO_STATS(AllocedOfSize
,mArraySize
*sizeof(mArray
[0]));
567 ::memcpy(mArray
, oldArray
, mCount
* sizeof(nsISupports
*));
568 if (oldArray
!= mAutoArray
)
575 nsSupportsArray::EnumerateForwards(nsISupportsArrayEnumFunc aFunc
, void* aData
)
580 while (running
&& (++aIndex
< (int32_t)mCount
)) {
581 running
= (*aFunc
)(mArray
[aIndex
], aData
);
587 nsSupportsArray::EnumerateBackwards(nsISupportsArrayEnumFunc aFunc
, void* aData
)
589 uint32_t aIndex
= mCount
;
592 while (running
&& (0 < aIndex
--)) {
593 running
= (*aFunc
)(mArray
[aIndex
], aData
);
599 nsSupportsArray::Enumerate(nsIEnumerator
* *result
)
601 nsSupportsArrayEnumerator
* e
= new nsSupportsArrayEnumerator(this);
603 return NS_ERROR_OUT_OF_MEMORY
;
610 CopyElement(nsISupports
* aElement
, void *aData
)
613 nsISupportsArray
* newArray
= (nsISupportsArray
*)aData
;
614 rv
= newArray
->AppendElement(aElement
);
615 return NS_SUCCEEDED(rv
);
619 nsSupportsArray::Clone(nsISupportsArray
* *result
)
622 nsISupportsArray
* newArray
;
623 rv
= NS_NewISupportsArray(&newArray
);
624 bool ok
= EnumerateForwards(CopyElement
, newArray
);
625 if (!ok
) return NS_ERROR_OUT_OF_MEMORY
;
631 NS_NewISupportsArray(nsISupportsArray
** aInstancePtrResult
)
634 rv
= nsSupportsArray::Create(NULL
, NS_GET_IID(nsISupportsArray
),
635 (void**)aInstancePtrResult
);
639 class nsArrayEnumerator MOZ_FINAL
: public nsISimpleEnumerator
642 // nsISupports interface
645 // nsISimpleEnumerator interface
646 NS_IMETHOD
HasMoreElements(bool* aResult
);
647 NS_IMETHOD
GetNext(nsISupports
** aResult
);
649 // nsArrayEnumerator methods
650 nsArrayEnumerator(nsISupportsArray
* aValueArray
);
653 ~nsArrayEnumerator(void);
656 nsISupportsArray
* mValueArray
;
660 nsArrayEnumerator::nsArrayEnumerator(nsISupportsArray
* aValueArray
)
661 : mValueArray(aValueArray
),
664 NS_IF_ADDREF(mValueArray
);
667 nsArrayEnumerator::~nsArrayEnumerator(void)
669 NS_IF_RELEASE(mValueArray
);
672 NS_IMPL_ISUPPORTS1(nsArrayEnumerator
, nsISimpleEnumerator
)
675 nsArrayEnumerator::HasMoreElements(bool* aResult
)
677 NS_PRECONDITION(aResult
!= 0, "null ptr");
679 return NS_ERROR_NULL_POINTER
;
687 nsresult rv
= mValueArray
->Count(&cnt
);
688 if (NS_FAILED(rv
)) return rv
;
689 *aResult
= (mIndex
< (int32_t) cnt
);
694 nsArrayEnumerator::GetNext(nsISupports
** aResult
)
696 NS_PRECONDITION(aResult
!= 0, "null ptr");
698 return NS_ERROR_NULL_POINTER
;
706 nsresult rv
= mValueArray
->Count(&cnt
);
707 if (NS_FAILED(rv
)) return rv
;
708 if (mIndex
>= (int32_t) cnt
)
709 return NS_ERROR_UNEXPECTED
;
711 *aResult
= mValueArray
->ElementAt(mIndex
++);
716 NS_NewArrayEnumerator(nsISimpleEnumerator
* *result
,
717 nsISupportsArray
* array
)
719 nsArrayEnumerator
* enumer
= new nsArrayEnumerator(array
);