bug 812562 - click-to-play: reshow notification for blocklisted plugins r=jaws
[gecko.git] / xpcom / ds / nsSupportsArray.cpp
blob1391011200a7d080b68288b4afa2a6c406ddb510
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/. */
6 #include <string.h>
7 #include "prbit.h"
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
17 class SupportsStats {
18 public:
19 SupportsStats();
20 ~SupportsStats();
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];
33 // very evil
34 #define ADD_TO_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
35 { \
36 if (sizesAlloced[i] == (int)(size)) \
37 { ((x)[i])++; break; } \
38 } \
39 if (i >= sizesUsed && sizesUsed < MAXSUPPORTS) \
40 { sizesAlloced[sizesUsed] = (size); \
41 ((x)[sizesUsed++])++; break; \
42 } \
43 } while (0);
45 #define SUB_FROM_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
46 { \
47 if (sizesAlloced[i] == (int)(size)) \
48 { ((x)[i])--; break; } \
49 } \
50 } while (0);
53 SupportsStats::SupportsStats()
55 sizesUsed = 1;
56 sizesAlloced[0] = 0;
59 SupportsStats::~SupportsStats()
61 int i;
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++)
72 if (MaxElements[i])
73 printf("\t%d: %d\n",i,MaxElements[i]);
77 // Just so constructor/destructor get called
78 SupportsStats gSupportsStats;
79 #endif
81 nsresult
82 nsQueryElementAt::operator()( const nsIID& aIID, void** aResult ) const
84 nsresult status = mCollection
85 ? mCollection->QueryElementAt(mIndex, aIID, aResult)
86 : NS_ERROR_NULL_POINTER;
88 if ( mErrorPtr )
89 *mErrorPtr = status;
91 return status;
94 static const int32_t kGrowArrayBy = 8;
95 static const int32_t kLinearThreshold = 16 * sizeof(nsISupports *);
97 nsSupportsArray::nsSupportsArray()
99 mArray = mAutoArray;
100 mArraySize = kAutoArraySize;
101 mCount = 0;
102 #if DEBUG_SUPPORTSARRAY
103 mMaxCount = 0;
104 mMaxSize = 0;
105 ADD_TO_STATS(NumberOfSize,kAutoArraySize*sizeof(mArray[0]));
106 MaxElements[0]++;
107 #endif
110 nsSupportsArray::~nsSupportsArray()
112 DeleteArray();
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;
155 #endif
156 if (oldArray) { // need to move old data
157 if (0 < mCount) {
158 ::memcpy(mArray, oldArray, mCount * sizeof(nsISupports*));
160 if (oldArray != &(mAutoArray[0])) {
161 delete[] oldArray;
166 nsresult
167 nsSupportsArray::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
169 if (aOuter)
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)
179 NS_IMETHODIMP
180 nsSupportsArray::Read(nsIObjectInputStream *aStream)
182 nsresult rv;
184 uint32_t newArraySize;
185 rv = aStream->Read32(&newArraySize);
187 if (newArraySize <= kAutoArraySize) {
188 if (mArray != mAutoArray) {
189 delete[] mArray;
190 mArray = mAutoArray;
192 newArraySize = kAutoArraySize;
194 else {
195 if (newArraySize <= mArraySize) {
196 // Keep non-default-size mArray, it's more than big enough.
197 newArraySize = mArraySize;
199 else {
200 nsISupports** array = new nsISupports*[newArraySize];
201 if (mArray != mAutoArray)
202 delete[] mArray;
203 mArray = array;
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)
213 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;
220 return NS_OK;
223 NS_IMETHODIMP
224 nsSupportsArray::Write(nsIObjectOutputStream *aStream)
226 nsresult rv;
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;
239 return NS_OK;
242 void nsSupportsArray::DeleteArray(void)
244 Clear();
245 if (mArray != &(mAutoArray[0])) {
246 delete[] mArray;
247 mArray = mAutoArray;
248 mArraySize = kAutoArraySize;
253 NS_IMETHODIMP_(bool)
254 nsSupportsArray::Equals(const nsISupportsArray* aOther)
256 if (aOther) {
257 uint32_t countOther;
258 nsISupportsArray* other = const_cast<nsISupportsArray*>(aOther);
259 nsresult rv = other->Count(&countOther);
260 if (NS_FAILED( rv ))
261 return false;
263 if (mCount == countOther) {
264 uint32_t index = mCount;
265 nsCOMPtr<nsISupports> otherElem;
266 while (index--) {
267 if (NS_FAILED(other->GetElementAt(index, getter_AddRefs(otherElem))))
268 return false;
269 if (mArray[index] != otherElem)
270 return false;
272 return true;
275 return false;
278 NS_IMETHODIMP_(nsISupports*)
279 nsSupportsArray::ElementAt(uint32_t aIndex)
281 if (aIndex < mCount) {
282 nsISupports* element = mArray[aIndex];
283 NS_IF_ADDREF(element);
284 return element;
286 return 0;
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);
303 while (ep < end) {
304 if (aPossibleElement == *ep) {
305 return (ep - start);
307 ep++;
310 return -1;
313 NS_IMETHODIMP_(int32_t)
314 nsSupportsArray::LastIndexOf(const nsISupports* aPossibleElement)
316 if (0 < mCount) {
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) {
321 return (ep - start);
325 return -1;
328 NS_IMETHODIMP_(bool)
329 nsSupportsArray::InsertElementAt(nsISupports* aElement, uint32_t aIndex)
331 if (aIndex <= mCount) {
332 if (mArraySize < (mCount + 1)) {
333 // need to grow the array
334 GrowArrayBy(1);
337 // Could be slightly more efficient if GrowArrayBy knew about the
338 // split, but the difference is trivial.
339 uint32_t slide = (mCount - aIndex);
340 if (0 < slide) {
341 ::memmove(mArray + aIndex + 1, mArray + aIndex, slide * sizeof(nsISupports*));
344 mArray[aIndex] = aElement;
345 NS_IF_ADDREF(aElement);
346 mCount++;
348 #if DEBUG_SUPPORTSARRAY
349 if (mCount > mMaxCount &&
350 mCount < (int32_t)(sizeof(MaxElements)/sizeof(MaxElements[0])))
352 MaxElements[mCount]++;
353 MaxElements[mMaxCount]--;
354 mMaxCount = mCount;
356 #endif
357 return true;
359 return false;
362 NS_IMETHODIMP_(bool)
363 nsSupportsArray::InsertElementsAt(nsISupportsArray* aElements, uint32_t aIndex)
365 if (!aElements) {
366 return false;
368 uint32_t countElements;
369 if (NS_FAILED( aElements->Count( &countElements ) ))
370 return false;
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);
381 if (0 < slide) {
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) ))
389 return false;
392 #if DEBUG_SUPPORTSARRAY
393 if (mCount > mMaxCount &&
394 mCount < (int32_t)(sizeof(MaxElements)/sizeof(MaxElements[0])))
396 MaxElements[mCount]++;
397 MaxElements[mMaxCount]--;
398 mMaxCount = mCount;
400 #endif
401 return true;
403 return false;
406 NS_IMETHODIMP_(bool)
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;
413 return true;
415 return false;
418 NS_IMETHODIMP_(bool)
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]);
424 mCount -= aCount;
425 int32_t slide = (mCount - aIndex);
426 if (0 < slide) {
427 ::memmove(mArray + aIndex, mArray + aIndex + aCount,
428 slide * sizeof(nsISupports*));
430 return true;
432 return false;
435 NS_IMETHODIMP_(bool)
436 nsSupportsArray::RemoveElement(const nsISupports* aElement, uint32_t aStartIndex)
438 int32_t theIndex = IndexOfStartingAt(aElement,aStartIndex);
439 if (theIndex >= 0)
440 return RemoveElementAt(theIndex);
442 return false;
445 NS_IMETHODIMP_(bool)
446 nsSupportsArray::RemoveLastElement(const nsISupports* aElement)
448 int32_t theIndex = LastIndexOf(aElement);
449 if (theIndex >= 0)
450 return RemoveElementAt(theIndex);
452 return false;
455 NS_IMETHODIMP_(bool)
456 nsSupportsArray::MoveElement(int32_t aFrom, int32_t aTo)
458 nsISupports *tempElement;
460 if (aTo == aFrom)
461 return true;
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
467 return false;
469 tempElement = mArray[aFrom];
471 if (aTo < 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;
486 return true;
489 NS_IMETHODIMP
490 nsSupportsArray::Clear(void)
492 if (0 < mCount) {
493 do {
494 --mCount;
495 NS_IF_RELEASE(mArray[mCount]);
496 } while (0 != mCount);
498 return NS_OK;
501 NS_IMETHODIMP
502 nsSupportsArray::Compact(void)
504 #if DEBUG_SUPPORTSARRAY
505 uint32_t oldArraySize = mArraySize;
506 #endif
507 if ((mArraySize != mCount) && (kAutoArraySize < mArraySize)) {
508 nsISupports** oldArray = mArray;
509 if (mCount <= kAutoArraySize) {
510 mArray = mAutoArray;
511 mArraySize = kAutoArraySize;
513 else {
514 mArray = new nsISupports*[mCount];
515 if (!mArray) {
516 mArray = oldArray;
517 return NS_OK;
519 mArraySize = 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]));
527 #endif
528 ::memcpy(mArray, oldArray, mCount * sizeof(nsISupports*));
529 delete[] oldArray;
531 return NS_OK;
534 NS_IMETHODIMP_(bool)
535 nsSupportsArray::SizeTo(int32_t aSize)
537 #if DEBUG_SUPPORTSARRAY
538 uint32_t oldArraySize = mArraySize;
539 #endif
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) {
549 mArray = mAutoArray;
550 mArraySize = kAutoArraySize;
552 else {
553 mArray = new nsISupports*[aSize];
554 if (!mArray) {
555 mArray = oldArray;
556 return false;
558 mArraySize = 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]));
566 #endif
567 ::memcpy(mArray, oldArray, mCount * sizeof(nsISupports*));
568 if (oldArray != mAutoArray)
569 delete[] oldArray;
571 return true;
574 NS_IMETHODIMP_(bool)
575 nsSupportsArray::EnumerateForwards(nsISupportsArrayEnumFunc aFunc, void* aData)
577 int32_t aIndex = -1;
578 bool running = true;
580 while (running && (++aIndex < (int32_t)mCount)) {
581 running = (*aFunc)(mArray[aIndex], aData);
583 return running;
586 NS_IMETHODIMP_(bool)
587 nsSupportsArray::EnumerateBackwards(nsISupportsArrayEnumFunc aFunc, void* aData)
589 uint32_t aIndex = mCount;
590 bool running = true;
592 while (running && (0 < aIndex--)) {
593 running = (*aFunc)(mArray[aIndex], aData);
595 return running;
598 NS_IMETHODIMP
599 nsSupportsArray::Enumerate(nsIEnumerator* *result)
601 nsSupportsArrayEnumerator* e = new nsSupportsArrayEnumerator(this);
602 if (!e)
603 return NS_ERROR_OUT_OF_MEMORY;
604 *result = e;
605 NS_ADDREF(e);
606 return NS_OK;
609 static bool
610 CopyElement(nsISupports* aElement, void *aData)
612 nsresult rv;
613 nsISupportsArray* newArray = (nsISupportsArray*)aData;
614 rv = newArray->AppendElement(aElement);
615 return NS_SUCCEEDED(rv);
618 NS_IMETHODIMP
619 nsSupportsArray::Clone(nsISupportsArray* *result)
621 nsresult rv;
622 nsISupportsArray* newArray;
623 rv = NS_NewISupportsArray(&newArray);
624 bool ok = EnumerateForwards(CopyElement, newArray);
625 if (!ok) return NS_ERROR_OUT_OF_MEMORY;
626 *result = newArray;
627 return NS_OK;
630 nsresult
631 NS_NewISupportsArray(nsISupportsArray** aInstancePtrResult)
633 nsresult rv;
634 rv = nsSupportsArray::Create(NULL, NS_GET_IID(nsISupportsArray),
635 (void**)aInstancePtrResult);
636 return rv;
639 class nsArrayEnumerator MOZ_FINAL : public nsISimpleEnumerator
641 public:
642 // nsISupports interface
643 NS_DECL_ISUPPORTS
645 // nsISimpleEnumerator interface
646 NS_IMETHOD HasMoreElements(bool* aResult);
647 NS_IMETHOD GetNext(nsISupports** aResult);
649 // nsArrayEnumerator methods
650 nsArrayEnumerator(nsISupportsArray* aValueArray);
652 private:
653 ~nsArrayEnumerator(void);
655 protected:
656 nsISupportsArray* mValueArray;
657 int32_t mIndex;
660 nsArrayEnumerator::nsArrayEnumerator(nsISupportsArray* aValueArray)
661 : mValueArray(aValueArray),
662 mIndex(0)
664 NS_IF_ADDREF(mValueArray);
667 nsArrayEnumerator::~nsArrayEnumerator(void)
669 NS_IF_RELEASE(mValueArray);
672 NS_IMPL_ISUPPORTS1(nsArrayEnumerator, nsISimpleEnumerator)
674 NS_IMETHODIMP
675 nsArrayEnumerator::HasMoreElements(bool* aResult)
677 NS_PRECONDITION(aResult != 0, "null ptr");
678 if (! aResult)
679 return NS_ERROR_NULL_POINTER;
681 if (!mValueArray) {
682 *aResult = false;
683 return NS_OK;
686 uint32_t cnt;
687 nsresult rv = mValueArray->Count(&cnt);
688 if (NS_FAILED(rv)) return rv;
689 *aResult = (mIndex < (int32_t) cnt);
690 return NS_OK;
693 NS_IMETHODIMP
694 nsArrayEnumerator::GetNext(nsISupports** aResult)
696 NS_PRECONDITION(aResult != 0, "null ptr");
697 if (! aResult)
698 return NS_ERROR_NULL_POINTER;
700 if (!mValueArray) {
701 *aResult = nullptr;
702 return NS_OK;
705 uint32_t cnt;
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++);
712 return NS_OK;
715 nsresult
716 NS_NewArrayEnumerator(nsISimpleEnumerator* *result,
717 nsISupportsArray* array)
719 nsArrayEnumerator* enumer = new nsArrayEnumerator(array);
720 *result = enumer;
721 NS_ADDREF(*result);
722 return NS_OK;