VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/ds/nsSupportsArray.cpp@ 4837

Last change on this file since 4837 was 1, checked in by vboxsync, 54 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.2 KB
Line 
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
4 *
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/
9 *
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
13 * License.
14 *
15 * The Original Code is mozilla.org code.
16 *
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.
21 *
22 * Contributor(s):
23 * Scott Collins <scc@mozilla.org>: |do_QueryElementAt|
24 * Pierre Phaneuf <pp@ludusdesign.com>
25 *
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.
37 *
38 * ***** END LICENSE BLOCK ***** */
39
40#include <string.h>
41#include "prbit.h"
42#include "nsSupportsArray.h"
43#include "nsSupportsArrayEnumerator.h"
44#include "nsAString.h"
45#include "nsIObjectInputStream.h"
46#include "nsIObjectOutputStream.h"
47
48#if DEBUG_SUPPORTSARRAY
49#define MAXSUPPORTS 20
50
51class SupportsStats {
52public:
53 SupportsStats();
54 ~SupportsStats();
55
56};
57
58static int sizesUsed; // number of the elements of the arrays used
59static int sizesAlloced[MAXSUPPORTS]; // sizes of the allocations. sorted
60static int NumberOfSize[MAXSUPPORTS]; // number of this allocation size (1 per array)
61static int AllocedOfSize[MAXSUPPORTS]; // number of this allocation size (each size for array used)
62static int GrowInPlace[MAXSUPPORTS];
63
64// these are per-allocation
65static int MaxElements[3000];
66
67// very evil
68#define ADD_TO_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
69 { \
70 if (sizesAlloced[i] == (int)(size)) \
71 { ((x)[i])++; break; } \
72 } \
73 if (i >= sizesUsed && sizesUsed < MAXSUPPORTS) \
74 { sizesAlloced[sizesUsed] = (size); \
75 ((x)[sizesUsed++])++; break; \
76 } \
77 } while (0);
78
79#define SUB_FROM_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
80 { \
81 if (sizesAlloced[i] == (int)(size)) \
82 { ((x)[i])--; break; } \
83 } \
84 } while (0);
85
86
87SupportsStats::SupportsStats()
88{
89 sizesUsed = 1;
90 sizesAlloced[0] = 0;
91}
92
93SupportsStats::~SupportsStats()
94{
95 int i;
96 for (i = 0; i < sizesUsed; i++)
97 {
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]);
102 }
103 printf("Max Size of SupportsArray:\n");
104 for (i = 0; i < (int)(sizeof(MaxElements)/sizeof(MaxElements[0])); i++)
105 {
106 if (MaxElements[i])
107 printf("\t%d: %d\n",i,MaxElements[i]);
108 }
109}
110
111// Just so constructor/destructor get called
112SupportsStats gSupportsStats;
113#endif
114
115nsresult
116nsQueryElementAt::operator()( const nsIID& aIID, void** aResult ) const
117 {
118 nsresult status = mCollection
119 ? mCollection->QueryElementAt(mIndex, aIID, aResult)
120 : NS_ERROR_NULL_POINTER;
121
122 if ( mErrorPtr )
123 *mErrorPtr = status;
124
125 return status;
126 }
127
128static const PRInt32 kGrowArrayBy = 8;
129static const PRInt32 kLinearThreshold = 16 * sizeof(nsISupports *);
130
131nsSupportsArray::nsSupportsArray()
132{
133 mArray = mAutoArray;
134 mArraySize = kAutoArraySize;
135 mCount = 0;
136#if DEBUG_SUPPORTSARRAY
137 mMaxCount = 0;
138 mMaxSize = 0;
139 ADD_TO_STATS(NumberOfSize,kAutoArraySize*sizeof(mArray[0]));
140 MaxElements[0]++;
141#endif
142}
143
144nsSupportsArray::~nsSupportsArray()
145{
146 DeleteArray();
147}
148
149PRBool nsSupportsArray::GrowArrayBy(PRInt32 aGrowBy)
150{
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;
157
158 PRUint32 newCount = mArraySize + aGrowBy; // Minimum increase
159 PRUint32 newSize = sizeof(mArray[0]) * newCount;
160
161 if (newSize >= (PRUint32) kLinearThreshold)
162 {
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));
168
169 newCount = newSize / sizeof(mArray[0]);
170 }
171 // XXX This would be far more efficient in many allocators if we used
172 // XXX PR_Realloc(), etc
173 nsISupports** oldArray = mArray;
174
175 mArray = new nsISupports*[newCount];
176 if (!mArray) { // ran out of memory
177 mArray = oldArray;
178 return PR_FALSE;
179 }
180 mArraySize = newCount;
181
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)
187 {
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;
192 }
193#endif
194 if (oldArray) { // need to move old data
195 if (0 < mCount) {
196 ::memcpy(mArray, oldArray, mCount * sizeof(nsISupports*));
197 }
198 if (oldArray != &(mAutoArray[0])) {
199 delete[] oldArray;
200 }
201 }
202
203 return PR_TRUE;
204}
205
206NS_METHOD
207nsSupportsArray::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
208{
209 if (aOuter)
210 return NS_ERROR_NO_AGGREGATION;
211
212 nsCOMPtr<nsISupportsArray> it = new nsSupportsArray();
213 if (!it)
214 return NS_ERROR_OUT_OF_MEMORY;
215
216 return it->QueryInterface(aIID, aResult);
217}
218
219NS_IMPL_THREADSAFE_ISUPPORTS3(nsSupportsArray, nsISupportsArray, nsICollection, nsISerializable)
220
221NS_IMETHODIMP
222nsSupportsArray::Read(nsIObjectInputStream *aStream)
223{
224 nsresult rv;
225
226 PRUint32 newArraySize;
227 rv = aStream->Read32(&newArraySize);
228
229 if (newArraySize <= kAutoArraySize) {
230 if (mArray != mAutoArray) {
231 delete[] mArray;
232 mArray = mAutoArray;
233 }
234 newArraySize = kAutoArraySize;
235 }
236 else {
237 if (newArraySize <= mArraySize) {
238 // Keep non-default-size mArray, it's more than big enough.
239 newArraySize = mArraySize;
240 }
241 else {
242 nsISupports** array = new nsISupports*[newArraySize];
243 if (!array)
244 return NS_ERROR_OUT_OF_MEMORY;
245 if (mArray != mAutoArray)
246 delete[] mArray;
247 mArray = array;
248 }
249 }
250 mArraySize = newArraySize;
251
252 rv = aStream->Read32(&mCount);
253 if (NS_FAILED(rv)) return rv;
254
255 NS_ASSERTION(mCount <= mArraySize, "overlarge mCount!");
256 if (mCount > mArraySize)
257 mCount = mArraySize;
258
259 for (PRUint32 i = 0; i < mCount; i++) {
260 rv = aStream->ReadObject(PR_TRUE, &mArray[i]);
261 if (NS_FAILED(rv)) return rv;
262 }
263
264 return NS_OK;
265}
266
267NS_IMETHODIMP
268nsSupportsArray::Write(nsIObjectOutputStream *aStream)
269{
270 nsresult rv;
271
272 rv = aStream->Write32(mArraySize);
273 if (NS_FAILED(rv)) return rv;
274
275 rv = aStream->Write32(mCount);
276 if (NS_FAILED(rv)) return rv;
277
278 for (PRUint32 i = 0; i < mCount; i++) {
279 rv = aStream->WriteObject(mArray[i], PR_TRUE);
280 if (NS_FAILED(rv)) return rv;
281 }
282
283 return NS_OK;
284}
285
286void nsSupportsArray::DeleteArray(void)
287{
288 Clear();
289 if (mArray != &(mAutoArray[0])) {
290 delete[] mArray;
291 mArray = mAutoArray;
292 mArraySize = kAutoArraySize;
293 }
294}
295
296
297NS_IMETHODIMP_(PRBool)
298nsSupportsArray::Equals(const nsISupportsArray* aOther)
299{
300 if (aOther) {
301 PRUint32 countOther;
302 nsISupportsArray* other = NS_CONST_CAST(nsISupportsArray*, aOther);
303 nsresult rv = other->Count(&countOther);
304 if (NS_FAILED( rv ))
305 return PR_FALSE;
306
307 if (mCount == countOther) {
308 PRUint32 index = mCount;
309 nsCOMPtr<nsISupports> otherElem;
310 while (index--) {
311 if (NS_FAILED(other->GetElementAt(index, getter_AddRefs(otherElem))))
312 return PR_FALSE;
313 if (mArray[index] != otherElem)
314 return PR_FALSE;
315 }
316 return PR_TRUE;
317 }
318 }
319 return PR_FALSE;
320}
321
322NS_IMETHODIMP_(nsISupports*)
323nsSupportsArray::ElementAt(PRUint32 aIndex)
324{
325 if (aIndex < mCount) {
326 nsISupports* element = mArray[aIndex];
327 NS_IF_ADDREF(element);
328 return element;
329 }
330 return 0;
331}
332
333NS_IMETHODIMP_(PRInt32)
334nsSupportsArray::IndexOf(const nsISupports* aPossibleElement)
335{
336 return IndexOfStartingAt(aPossibleElement, 0);
337}
338
339NS_IMETHODIMP_(PRInt32)
340nsSupportsArray::IndexOfStartingAt(const nsISupports* aPossibleElement,
341 PRUint32 aStartIndex)
342{
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);
347 while (ep < end) {
348 if (aPossibleElement == *ep) {
349 return (ep - start);
350 }
351 ep++;
352 }
353 }
354 return -1;
355}
356
357NS_IMETHODIMP_(PRInt32)
358nsSupportsArray::LastIndexOf(const nsISupports* aPossibleElement)
359{
360 if (0 < mCount) {
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) {
365 return (ep - start);
366 }
367 }
368 }
369 return -1;
370}
371
372NS_IMETHODIMP_(PRBool)
373nsSupportsArray::InsertElementAt(nsISupports* aElement, PRUint32 aIndex)
374{
375 if (aIndex <= mCount) {
376 if (mArraySize < (mCount + 1)) {
377 // need to grow the array
378 if (!GrowArrayBy(1))
379 return PR_FALSE;
380 }
381
382 // Could be slightly more efficient if GrowArrayBy knew about the
383 // split, but the difference is trivial.
384 PRUint32 slide = (mCount - aIndex);
385 if (0 < slide) {
386 ::memmove(mArray + aIndex + 1, mArray + aIndex, slide * sizeof(nsISupports*));
387 }
388
389 mArray[aIndex] = aElement;
390 NS_IF_ADDREF(aElement);
391 mCount++;
392
393#if DEBUG_SUPPORTSARRAY
394 if (mCount > mMaxCount &&
395 mCount < (PRInt32)(sizeof(MaxElements)/sizeof(MaxElements[0])))
396 {
397 MaxElements[mCount]++;
398 MaxElements[mMaxCount]--;
399 mMaxCount = mCount;
400 }
401#endif
402 return PR_TRUE;
403 }
404 return PR_FALSE;
405}
406
407NS_IMETHODIMP_(PRBool)
408nsSupportsArray::InsertElementsAt(nsISupportsArray* aElements, PRUint32 aIndex)
409{
410 if (!aElements) {
411 return PR_FALSE;
412 }
413 PRUint32 countElements;
414 if (NS_FAILED( aElements->Count( &countElements ) ))
415 return PR_FALSE;
416
417 if (aIndex <= mCount) {
418 if (mArraySize < (mCount + countElements)) {
419 // need to grow the array
420 if (!GrowArrayBy(countElements))
421 return PR_FALSE;
422 }
423
424 // Could be slightly more efficient if GrowArrayBy knew about the
425 // split, but the difference is trivial.
426 PRUint32 slide = (mCount - aIndex);
427 if (0 < slide) {
428 ::memmove(mArray + aIndex + countElements, mArray + aIndex,
429 slide * sizeof(nsISupports*));
430 }
431
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) ))
435 return PR_FALSE;
436 }
437
438#if DEBUG_SUPPORTSARRAY
439 if (mCount > mMaxCount &&
440 mCount < (PRInt32)(sizeof(MaxElements)/sizeof(MaxElements[0])))
441 {
442 MaxElements[mCount]++;
443 MaxElements[mMaxCount]--;
444 mMaxCount = mCount;
445 }
446#endif
447 return PR_TRUE;
448 }
449 return PR_FALSE;
450}
451
452NS_IMETHODIMP_(PRBool)
453nsSupportsArray::ReplaceElementAt(nsISupports* aElement, PRUint32 aIndex)
454{
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;
459 return PR_TRUE;
460 }
461 return PR_FALSE;
462}
463
464NS_IMETHODIMP_(PRBool)
465nsSupportsArray::RemoveElementsAt(PRUint32 aIndex, PRUint32 aCount)
466{
467 if (aIndex + aCount <= mCount) {
468 for (PRUint32 i = 0; i < aCount; i++)
469 NS_IF_RELEASE(mArray[aIndex+i]);
470 mCount -= aCount;
471 PRInt32 slide = (mCount - aIndex);
472 if (0 < slide) {
473 ::memmove(mArray + aIndex, mArray + aIndex + aCount,
474 slide * sizeof(nsISupports*));
475 }
476 return PR_TRUE;
477 }
478 return PR_FALSE;
479}
480
481NS_IMETHODIMP_(PRBool)
482nsSupportsArray::RemoveElement(const nsISupports* aElement, PRUint32 aStartIndex)
483{
484 PRInt32 theIndex = IndexOfStartingAt(aElement,aStartIndex);
485 if (theIndex >= 0)
486 return RemoveElementAt(theIndex);
487
488 return PR_FALSE;
489}
490
491NS_IMETHODIMP_(PRBool)
492nsSupportsArray::RemoveLastElement(const nsISupports* aElement)
493{
494 PRInt32 theIndex = LastIndexOf(aElement);
495 if (theIndex >= 0)
496 return RemoveElementAt(theIndex);
497
498 return PR_FALSE;
499}
500
501NS_IMETHODIMP_(PRBool)
502nsSupportsArray::MoveElement(PRInt32 aFrom, PRInt32 aTo)
503{
504 nsISupports *tempElement;
505
506 if (aTo == aFrom)
507 return PR_TRUE;
508
509 if (aTo < 0 || aFrom < 0 ||
510 (PRUint32) aTo >= mCount || (PRUint32) aFrom >= mCount)
511 {
512 // can't extend the array when moving an element. Also catches mImpl = null
513 return PR_FALSE;
514 }
515 tempElement = mArray[aFrom];
516
517 if (aTo < aFrom)
518 {
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;
523 }
524 else // already handled aFrom == aTo
525 {
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;
530 }
531
532 return PR_TRUE;
533}
534
535NS_IMETHODIMP
536nsSupportsArray::Clear(void)
537{
538 if (0 < mCount) {
539 do {
540 --mCount;
541 NS_IF_RELEASE(mArray[mCount]);
542 } while (0 != mCount);
543 }
544 return NS_OK;
545}
546
547NS_IMETHODIMP
548nsSupportsArray::Compact(void)
549{
550#if DEBUG_SUPPORTSARRAY
551 PRUint32 oldArraySize = mArraySize;
552#endif
553 if ((mArraySize != mCount) && (kAutoArraySize < mArraySize)) {
554 nsISupports** oldArray = mArray;
555 if (mCount <= kAutoArraySize) {
556 mArray = mAutoArray;
557 mArraySize = kAutoArraySize;
558 }
559 else {
560 mArray = new nsISupports*[mCount];
561 if (!mArray) {
562 mArray = oldArray;
563 return NS_OK;
564 }
565 mArraySize = mCount;
566 }
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]));
573#endif
574 ::memcpy(mArray, oldArray, mCount * sizeof(nsISupports*));
575 delete[] oldArray;
576 }
577 return NS_OK;
578}
579
580NS_IMETHODIMP_(PRBool)
581nsSupportsArray::SizeTo(PRInt32 aSize)
582{
583#if DEBUG_SUPPORTSARRAY
584 PRUint32 oldArraySize = mArraySize;
585#endif
586 NS_ASSERTION(aSize >= 0, "negative aSize!");
587
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
591
592 // switch back to autoarray if possible
593 nsISupports** oldArray = mArray;
594 if ((PRUint32) aSize <= kAutoArraySize) {
595 mArray = mAutoArray;
596 mArraySize = kAutoArraySize;
597 }
598 else {
599 mArray = new nsISupports*[aSize];
600 if (!mArray) {
601 mArray = oldArray;
602 return PR_FALSE;
603 }
604 mArraySize = aSize;
605 }
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]));
612#endif
613 ::memcpy(mArray, oldArray, mCount * sizeof(nsISupports*));
614 if (oldArray != mAutoArray)
615 delete[] oldArray;
616
617 return PR_TRUE;
618}
619
620NS_IMETHODIMP_(PRBool)
621nsSupportsArray::EnumerateForwards(nsISupportsArrayEnumFunc aFunc, void* aData)
622{
623 PRInt32 aIndex = -1;
624 PRBool running = PR_TRUE;
625
626 while (running && (++aIndex < (PRInt32)mCount)) {
627 running = (*aFunc)(mArray[aIndex], aData);
628 }
629 return running;
630}
631
632NS_IMETHODIMP_(PRBool)
633nsSupportsArray::EnumerateBackwards(nsISupportsArrayEnumFunc aFunc, void* aData)
634{
635 PRUint32 aIndex = mCount;
636 PRBool running = PR_TRUE;
637
638 while (running && (0 < aIndex--)) {
639 running = (*aFunc)(mArray[aIndex], aData);
640 }
641 return running;
642}
643
644NS_IMETHODIMP
645nsSupportsArray::Enumerate(nsIEnumerator* *result)
646{
647 nsSupportsArrayEnumerator* e = new nsSupportsArrayEnumerator(this);
648 if (!e)
649 return NS_ERROR_OUT_OF_MEMORY;
650 *result = e;
651 NS_ADDREF(e);
652 return NS_OK;
653}
654
655static PRBool
656CopyElement(nsISupports* aElement, void *aData)
657{
658 nsresult rv;
659 nsISupportsArray* newArray = (nsISupportsArray*)aData;
660 rv = newArray->AppendElement(aElement);
661 return NS_SUCCEEDED(rv);
662}
663
664NS_IMETHODIMP
665nsSupportsArray::Clone(nsISupportsArray* *result)
666{
667 nsresult rv;
668 nsISupportsArray* newArray;
669 rv = NS_NewISupportsArray(&newArray);
670 PRBool ok = EnumerateForwards(CopyElement, newArray);
671 if (!ok) return NS_ERROR_OUT_OF_MEMORY;
672 *result = newArray;
673 return NS_OK;
674}
675
676NS_COM nsresult
677NS_NewISupportsArray(nsISupportsArray** aInstancePtrResult)
678{
679 nsresult rv;
680 rv = nsSupportsArray::Create(NULL, NS_GET_IID(nsISupportsArray),
681 (void**)aInstancePtrResult);
682 return rv;
683}
684
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use