1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; c-file-offsets: ((substatement-open . 0)) -*- */
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 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include <stdlib.h>
39 :
40 : #include "nsVoidArray.h"
41 : #include "nsQuickSort.h"
42 : #include "prbit.h"
43 : #include "nsISupportsImpl.h" // for nsTraceRefcnt
44 : #include "nsAlgorithm.h"
45 :
46 : /**
47 : * Grow the array by at least this many elements at a time.
48 : */
49 : static const PRInt32 kMinGrowArrayBy = 8;
50 : static const PRInt32 kMaxGrowArrayBy = 1024;
51 : static const PRInt32 kAutoClearCompactSizeFactor = 4;
52 :
53 : /**
54 : * This is the threshold (in bytes) of the mImpl struct, past which
55 : * we'll force the array to grow geometrically
56 : */
57 : static const PRInt32 kLinearThreshold = 24 * sizeof(void *);
58 :
59 : /**
60 : * Compute the number of bytes requires for the mImpl struct that will
61 : * hold |n| elements.
62 : */
63 : #define SIZEOF_IMPL(n_) (sizeof(Impl) + sizeof(void *) * ((n_) - 1))
64 :
65 :
66 : /**
67 : * Compute the number of elements that an mImpl struct of |n| bytes
68 : * will hold.
69 : */
70 : #define CAPACITYOF_IMPL(n_) ((((n_) - sizeof(Impl)) / sizeof(void *)) + 1)
71 :
72 : #if DEBUG_VOIDARRAY
73 : #define MAXVOID 10
74 :
75 : class VoidStats {
76 : public:
77 : VoidStats();
78 : ~VoidStats();
79 :
80 : };
81 :
82 : static int sizesUsed; // number of the elements of the arrays used
83 : static int sizesAlloced[MAXVOID]; // sizes of the allocations. sorted
84 : static int NumberOfSize[MAXVOID]; // number of this allocation size (1 per array)
85 : static int AllocedOfSize[MAXVOID]; // number of this allocation size (each size for array used)
86 : static int MaxAuto[MAXVOID]; // AutoArrays that maxed out at this size
87 : static int GrowInPlace[MAXVOID]; // arrays this size that grew in-place via realloc
88 :
89 : // these are per-allocation
90 : static int MaxElements[2000]; // # of arrays that maxed out at each size.
91 :
92 : // statistics macros
93 : #define ADD_TO_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
94 : { \
95 : if (sizesAlloced[i] == (int)(size)) \
96 : { ((x)[i])++; break; } \
97 : } \
98 : if (i >= sizesUsed && sizesUsed < MAXVOID) \
99 : { sizesAlloced[sizesUsed] = (size); \
100 : ((x)[sizesUsed++])++; break; \
101 : } \
102 : } while (0)
103 :
104 : #define SUB_FROM_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
105 : { \
106 : if (sizesAlloced[i] == (int)(size)) \
107 : { ((x)[i])--; break; } \
108 : } \
109 : } while (0)
110 :
111 :
112 : VoidStats::VoidStats()
113 : {
114 : sizesUsed = 1;
115 : sizesAlloced[0] = 0;
116 : }
117 :
118 : VoidStats::~VoidStats()
119 : {
120 : int i;
121 : for (i = 0; i < sizesUsed; i++)
122 : {
123 : printf("Size %d:\n",sizesAlloced[i]);
124 : printf("\tNumber of VoidArrays this size (max): %d\n",NumberOfSize[i]-MaxAuto[i]);
125 : printf("\tNumber of AutoVoidArrays this size (max): %d\n",MaxAuto[i]);
126 : printf("\tNumber of allocations this size (total): %d\n",AllocedOfSize[i]);
127 : printf("\tNumber of GrowsInPlace this size (total): %d\n",GrowInPlace[i]);
128 : }
129 : printf("Max Size of VoidArray:\n");
130 : for (i = 0; i < (int)(sizeof(MaxElements)/sizeof(MaxElements[0])); i++)
131 : {
132 : if (MaxElements[i])
133 : printf("\t%d: %d\n",i,MaxElements[i]);
134 : }
135 : }
136 :
137 : // Just so constructor/destructor's get called
138 : VoidStats gVoidStats;
139 : #endif
140 :
141 : void
142 2806 : nsVoidArray::SetArray(Impl *newImpl, PRInt32 aSize, PRInt32 aCount,
143 : bool aOwner, bool aHasAuto)
144 : {
145 : // old mImpl has been realloced and so we don't free/delete it
146 2806 : NS_PRECONDITION(newImpl, "can't set size");
147 2806 : mImpl = newImpl;
148 2806 : mImpl->mCount = aCount;
149 : mImpl->mBits = static_cast<PRUint32>(aSize & kArraySizeMask) |
150 : (aOwner ? kArrayOwnerMask : 0) |
151 2806 : (aHasAuto ? kArrayHasAutoBufferMask : 0);
152 2806 : }
153 :
154 : // This does all allocation/reallocation of the array.
155 : // It also will compact down to N - good for things that might grow a lot
156 : // at times, but usually are smaller, like JS deferred GC releases.
157 1409 : bool nsVoidArray::SizeTo(PRInt32 aSize)
158 : {
159 1409 : PRUint32 oldsize = GetArraySize();
160 1409 : bool isOwner = IsArrayOwner();
161 1409 : bool hasAuto = HasAutoBuffer();
162 :
163 1409 : if (aSize == (PRInt32) oldsize)
164 0 : return true; // no change
165 :
166 1409 : if (aSize <= 0)
167 : {
168 : // free the array if allocated
169 1 : if (mImpl)
170 : {
171 1 : if (isOwner)
172 : {
173 0 : free(reinterpret_cast<char *>(mImpl));
174 0 : if (hasAuto) {
175 0 : static_cast<nsAutoVoidArray*>(this)->ResetToAutoBuffer();
176 : }
177 : else {
178 0 : mImpl = nsnull;
179 : }
180 : }
181 : else
182 : {
183 1 : mImpl->mCount = 0; // nsAutoVoidArray
184 : }
185 : }
186 1 : return true;
187 : }
188 :
189 1408 : if (mImpl && isOwner)
190 : {
191 : // We currently own an array impl. Resize it appropriately.
192 7 : if (aSize < mImpl->mCount)
193 : {
194 : // XXX Note: we could also just resize to mCount
195 0 : return true; // can't make it that small, ignore request
196 : }
197 :
198 7 : char* bytes = (char *) realloc(mImpl,SIZEOF_IMPL(aSize));
199 7 : Impl* newImpl = reinterpret_cast<Impl*>(bytes);
200 7 : if (!newImpl)
201 0 : return false;
202 :
203 : #if DEBUG_VOIDARRAY
204 : if (mImpl == newImpl)
205 : ADD_TO_STATS(GrowInPlace,oldsize);
206 : ADD_TO_STATS(AllocedOfSize,SIZEOF_IMPL(aSize));
207 : if (aSize > mMaxSize)
208 : {
209 : ADD_TO_STATS(NumberOfSize,SIZEOF_IMPL(aSize));
210 : if (oldsize)
211 : SUB_FROM_STATS(NumberOfSize,oldsize);
212 : mMaxSize = aSize;
213 : if (mIsAuto)
214 : {
215 : ADD_TO_STATS(MaxAuto,SIZEOF_IMPL(aSize));
216 : SUB_FROM_STATS(MaxAuto,oldsize);
217 : }
218 : }
219 : #endif
220 7 : SetArray(newImpl, aSize, newImpl->mCount, true, hasAuto);
221 7 : return true;
222 : }
223 :
224 1401 : if ((PRUint32) aSize < oldsize) {
225 : // No point in allocating if it won't free the current Impl anyway.
226 0 : return true;
227 : }
228 :
229 : // just allocate an array
230 : // allocate the exact size requested
231 1401 : char* bytes = (char *) malloc(SIZEOF_IMPL(aSize));
232 1401 : Impl* newImpl = reinterpret_cast<Impl*>(bytes);
233 1401 : if (!newImpl)
234 0 : return false;
235 :
236 : #if DEBUG_VOIDARRAY
237 : ADD_TO_STATS(AllocedOfSize,SIZEOF_IMPL(aSize));
238 : if (aSize > mMaxSize)
239 : {
240 : ADD_TO_STATS(NumberOfSize,SIZEOF_IMPL(aSize));
241 : if (oldsize && !mImpl)
242 : SUB_FROM_STATS(NumberOfSize,oldsize);
243 : mMaxSize = aSize;
244 : }
245 : #endif
246 1401 : if (mImpl)
247 : {
248 : #if DEBUG_VOIDARRAY
249 : ADD_TO_STATS(MaxAuto,SIZEOF_IMPL(aSize));
250 : SUB_FROM_STATS(MaxAuto,0);
251 : SUB_FROM_STATS(NumberOfSize,0);
252 : mIsAuto = true;
253 : #endif
254 : // We must be growing an nsAutoVoidArray - copy since we didn't
255 : // realloc.
256 : memcpy(newImpl->mArray, mImpl->mArray,
257 5 : mImpl->mCount * sizeof(mImpl->mArray[0]));
258 : }
259 :
260 1401 : SetArray(newImpl, aSize, mImpl ? mImpl->mCount : 0, true, hasAuto);
261 : // no memset; handled later in ReplaceElementAt if needed
262 1401 : return true;
263 : }
264 :
265 1406 : bool nsVoidArray::GrowArrayBy(PRInt32 aGrowBy)
266 : {
267 : // We have to grow the array. Grow by kMinGrowArrayBy slots if we're
268 : // smaller than kLinearThreshold bytes, or a power of two if we're
269 : // larger. This is much more efficient with most memory allocators,
270 : // especially if it's very large, or of the allocator is binned.
271 1406 : if (aGrowBy < kMinGrowArrayBy)
272 1401 : aGrowBy = kMinGrowArrayBy;
273 :
274 1406 : PRUint32 newCapacity = GetArraySize() + aGrowBy; // Minimum increase
275 1406 : PRUint32 newSize = SIZEOF_IMPL(newCapacity);
276 :
277 1406 : if (newSize >= (PRUint32) kLinearThreshold)
278 : {
279 : // newCount includes enough space for at least kMinGrowArrayBy new
280 : // slots. Select the next power-of-two size in bytes above or
281 : // equal to that.
282 : // Also, limit the increase in size to about a VM page or two.
283 4 : if (GetArraySize() >= kMaxGrowArrayBy)
284 : {
285 0 : newCapacity = GetArraySize() + NS_MAX(kMaxGrowArrayBy,aGrowBy);
286 0 : newSize = SIZEOF_IMPL(newCapacity);
287 : }
288 : else
289 : {
290 4 : PR_CEILING_LOG2(newSize, newSize);
291 4 : newCapacity = CAPACITYOF_IMPL(PR_BIT(newSize));
292 : }
293 : }
294 : // frees old mImpl IF this succeeds
295 1406 : if (!SizeTo(newCapacity))
296 0 : return false;
297 :
298 1406 : return true;
299 : }
300 :
301 2792 : nsVoidArray::nsVoidArray()
302 2792 : : mImpl(nsnull)
303 : {
304 2792 : MOZ_COUNT_CTOR(nsVoidArray);
305 : #if DEBUG_VOIDARRAY
306 : mMaxCount = 0;
307 : mMaxSize = 0;
308 : mIsAuto = false;
309 : ADD_TO_STATS(NumberOfSize,0);
310 : MaxElements[0]++;
311 : #endif
312 2792 : }
313 :
314 2 : nsVoidArray::nsVoidArray(PRInt32 aCount)
315 2 : : mImpl(nsnull)
316 : {
317 2 : MOZ_COUNT_CTOR(nsVoidArray);
318 : #if DEBUG_VOIDARRAY
319 : mMaxCount = 0;
320 : mMaxSize = 0;
321 : mIsAuto = false;
322 : MaxElements[0]++;
323 : #endif
324 2 : SizeTo(aCount);
325 2 : }
326 :
327 1395 : nsVoidArray& nsVoidArray::operator=(const nsVoidArray& other)
328 : {
329 1395 : PRInt32 otherCount = other.Count();
330 1395 : PRInt32 maxCount = GetArraySize();
331 1395 : if (otherCount)
332 : {
333 1394 : if (otherCount > maxCount)
334 : {
335 : // frees old mImpl IF this succeeds
336 3 : if (!GrowArrayBy(otherCount-maxCount))
337 0 : return *this; // XXX The allocation failed - don't do anything
338 :
339 3 : memcpy(mImpl->mArray, other.mImpl->mArray, otherCount * sizeof(mImpl->mArray[0]));
340 3 : mImpl->mCount = otherCount;
341 : }
342 : else
343 : {
344 : // the old array can hold the new array
345 1391 : memcpy(mImpl->mArray, other.mImpl->mArray, otherCount * sizeof(mImpl->mArray[0]));
346 1391 : mImpl->mCount = otherCount;
347 : // if it shrank a lot, compact it anyways
348 1391 : if ((otherCount*2) < maxCount && maxCount > 100)
349 : {
350 0 : Compact(); // shrank by at least 50 entries
351 : }
352 : }
353 : #if DEBUG_VOIDARRAY
354 : if (mImpl->mCount > mMaxCount &&
355 : mImpl->mCount < (PRInt32)(sizeof(MaxElements)/sizeof(MaxElements[0])))
356 : {
357 : MaxElements[mImpl->mCount]++;
358 : MaxElements[mMaxCount]--;
359 : mMaxCount = mImpl->mCount;
360 : }
361 : #endif
362 : }
363 : else
364 : {
365 : // Why do we drop the buffer here when we don't in Clear()?
366 1 : SizeTo(0);
367 : }
368 :
369 1395 : return *this;
370 : }
371 :
372 2794 : nsVoidArray::~nsVoidArray()
373 : {
374 2794 : MOZ_COUNT_DTOR(nsVoidArray);
375 2794 : if (mImpl && IsArrayOwner())
376 1401 : free(reinterpret_cast<char*>(mImpl));
377 2794 : }
378 :
379 5 : bool nsVoidArray::SetCount(PRInt32 aNewCount)
380 : {
381 5 : NS_ASSERTION(aNewCount >= 0,"SetCount(negative index)");
382 5 : if (aNewCount < 0)
383 0 : return false;
384 :
385 5 : if (aNewCount == 0)
386 : {
387 0 : Clear();
388 0 : return true;
389 : }
390 :
391 5 : if (PRUint32(aNewCount) > PRUint32(GetArraySize()))
392 : {
393 2 : PRInt32 oldCount = Count();
394 2 : PRInt32 growDelta = aNewCount - oldCount;
395 :
396 : // frees old mImpl IF this succeeds
397 2 : if (!GrowArrayBy(growDelta))
398 0 : return false;
399 : }
400 :
401 5 : if (aNewCount > mImpl->mCount)
402 : {
403 : // Make sure that new entries added to the array by this
404 : // SetCount are cleared to 0. Some users of this assume that.
405 : // This code means we don't have to memset when we allocate an array.
406 3 : memset(&mImpl->mArray[mImpl->mCount], 0,
407 6 : (aNewCount - mImpl->mCount) * sizeof(mImpl->mArray[0]));
408 : }
409 :
410 5 : mImpl->mCount = aNewCount;
411 :
412 : #if DEBUG_VOIDARRAY
413 : if (mImpl->mCount > mMaxCount &&
414 : mImpl->mCount < (PRInt32)(sizeof(MaxElements)/sizeof(MaxElements[0])))
415 : {
416 : MaxElements[mImpl->mCount]++;
417 : MaxElements[mMaxCount]--;
418 : mMaxCount = mImpl->mCount;
419 : }
420 : #endif
421 :
422 5 : return true;
423 : }
424 :
425 41 : PRInt32 nsVoidArray::IndexOf(void* aPossibleElement) const
426 : {
427 41 : if (mImpl)
428 : {
429 41 : void** ap = mImpl->mArray;
430 41 : void** end = ap + mImpl->mCount;
431 226 : while (ap < end)
432 : {
433 145 : if (*ap == aPossibleElement)
434 : {
435 1 : return ap - mImpl->mArray;
436 : }
437 144 : ap++;
438 : }
439 : }
440 40 : return -1;
441 : }
442 :
443 1470 : bool nsVoidArray::InsertElementAt(void* aElement, PRInt32 aIndex)
444 : {
445 1470 : PRInt32 oldCount = Count();
446 1470 : NS_ASSERTION(aIndex >= 0,"InsertElementAt(negative index)");
447 1470 : if (PRUint32(aIndex) > PRUint32(oldCount))
448 : {
449 : // An invalid index causes the insertion to fail
450 : // Invalid indexes are ones that add more than one entry to the
451 : // array (i.e., they can append).
452 0 : return false;
453 : }
454 :
455 1470 : if (oldCount >= GetArraySize())
456 : {
457 1401 : if (!GrowArrayBy(1))
458 0 : return false;
459 : }
460 : // else the array is already large enough
461 :
462 1470 : PRInt32 slide = oldCount - aIndex;
463 1470 : if (0 != slide)
464 : {
465 : // Slide data over to make room for the insertion
466 0 : memmove(mImpl->mArray + aIndex + 1, mImpl->mArray + aIndex,
467 0 : slide * sizeof(mImpl->mArray[0]));
468 : }
469 :
470 1470 : mImpl->mArray[aIndex] = aElement;
471 1470 : mImpl->mCount++;
472 :
473 : #if DEBUG_VOIDARRAY
474 : if (mImpl->mCount > mMaxCount &&
475 : mImpl->mCount < (PRInt32)(sizeof(MaxElements)/sizeof(MaxElements[0])))
476 : {
477 : MaxElements[mImpl->mCount]++;
478 : MaxElements[mMaxCount]--;
479 : mMaxCount = mImpl->mCount;
480 : }
481 : #endif
482 :
483 1470 : return true;
484 : }
485 :
486 0 : bool nsVoidArray::InsertElementsAt(const nsVoidArray& other, PRInt32 aIndex)
487 : {
488 0 : PRInt32 oldCount = Count();
489 0 : PRInt32 otherCount = other.Count();
490 :
491 0 : NS_ASSERTION(aIndex >= 0,"InsertElementsAt(negative index)");
492 0 : if (PRUint32(aIndex) > PRUint32(oldCount))
493 : {
494 : // An invalid index causes the insertion to fail
495 : // Invalid indexes are ones that are more than one entry past the end of
496 : // the array (i.e., they can append).
497 0 : return false;
498 : }
499 :
500 0 : if (oldCount + otherCount > GetArraySize())
501 : {
502 0 : if (!GrowArrayBy(otherCount))
503 0 : return false;;
504 : }
505 : // else the array is already large enough
506 :
507 0 : PRInt32 slide = oldCount - aIndex;
508 0 : if (0 != slide)
509 : {
510 : // Slide data over to make room for the insertion
511 0 : memmove(mImpl->mArray + aIndex + otherCount, mImpl->mArray + aIndex,
512 0 : slide * sizeof(mImpl->mArray[0]));
513 : }
514 :
515 0 : for (PRInt32 i = 0; i < otherCount; i++)
516 : {
517 : // copy all the elements (destroys aIndex)
518 0 : mImpl->mArray[aIndex++] = other.mImpl->mArray[i];
519 0 : mImpl->mCount++;
520 : }
521 :
522 : #if DEBUG_VOIDARRAY
523 : if (mImpl->mCount > mMaxCount &&
524 : mImpl->mCount < (PRInt32)(sizeof(MaxElements)/sizeof(MaxElements[0])))
525 : {
526 : MaxElements[mImpl->mCount]++;
527 : MaxElements[mMaxCount]--;
528 : mMaxCount = mImpl->mCount;
529 : }
530 : #endif
531 :
532 0 : return true;
533 : }
534 :
535 20 : bool nsVoidArray::ReplaceElementAt(void* aElement, PRInt32 aIndex)
536 : {
537 20 : NS_ASSERTION(aIndex >= 0,"ReplaceElementAt(negative index)");
538 20 : if (aIndex < 0)
539 0 : return false;
540 :
541 : // Unlike InsertElementAt, ReplaceElementAt can implicitly add more
542 : // than just the one element to the array.
543 20 : if (PRUint32(aIndex) >= PRUint32(GetArraySize()))
544 : {
545 0 : PRInt32 oldCount = Count();
546 0 : PRInt32 requestedCount = aIndex + 1;
547 0 : PRInt32 growDelta = requestedCount - oldCount;
548 :
549 : // frees old mImpl IF this succeeds
550 0 : if (!GrowArrayBy(growDelta))
551 0 : return false;
552 : }
553 :
554 20 : mImpl->mArray[aIndex] = aElement;
555 20 : if (aIndex >= mImpl->mCount)
556 : {
557 : // Make sure that any entries implicitly added to the array by this
558 : // ReplaceElementAt are cleared to 0. Some users of this assume that.
559 : // This code means we don't have to memset when we allocate an array.
560 0 : if (aIndex > mImpl->mCount) // note: not >=
561 : {
562 : // For example, if mCount is 2, and we do a ReplaceElementAt for
563 : // element[5], then we need to set three entries ([2], [3], and [4])
564 : // to 0.
565 0 : memset(&mImpl->mArray[mImpl->mCount], 0,
566 0 : (aIndex - mImpl->mCount) * sizeof(mImpl->mArray[0]));
567 : }
568 :
569 0 : mImpl->mCount = aIndex + 1;
570 :
571 : #if DEBUG_VOIDARRAY
572 : if (mImpl->mCount > mMaxCount &&
573 : mImpl->mCount < (PRInt32)(sizeof(MaxElements)/sizeof(MaxElements[0])))
574 : {
575 : MaxElements[mImpl->mCount]++;
576 : MaxElements[mMaxCount]--;
577 : mMaxCount = mImpl->mCount;
578 : }
579 : #endif
580 : }
581 :
582 20 : return true;
583 : }
584 :
585 : // useful for doing LRU arrays
586 0 : bool nsVoidArray::MoveElement(PRInt32 aFrom, PRInt32 aTo)
587 : {
588 : void *tempElement;
589 :
590 0 : if (aTo == aFrom)
591 0 : return true;
592 :
593 0 : NS_ASSERTION(aTo >= 0 && aFrom >= 0,"MoveElement(negative index)");
594 0 : if (aTo >= Count() || aFrom >= Count())
595 : {
596 : // can't extend the array when moving an element. Also catches mImpl = null
597 0 : return false;
598 : }
599 0 : tempElement = mImpl->mArray[aFrom];
600 :
601 0 : if (aTo < aFrom)
602 : {
603 : // Moving one element closer to the head; the elements inbetween move down
604 0 : memmove(mImpl->mArray + aTo + 1, mImpl->mArray + aTo,
605 0 : (aFrom-aTo) * sizeof(mImpl->mArray[0]));
606 0 : mImpl->mArray[aTo] = tempElement;
607 : }
608 : else // already handled aFrom == aTo
609 : {
610 : // Moving one element closer to the tail; the elements inbetween move up
611 0 : memmove(mImpl->mArray + aFrom, mImpl->mArray + aFrom + 1,
612 0 : (aTo-aFrom) * sizeof(mImpl->mArray[0]));
613 0 : mImpl->mArray[aTo] = tempElement;
614 : }
615 :
616 0 : return true;
617 : }
618 :
619 4 : bool nsVoidArray::RemoveElementsAt(PRInt32 aIndex, PRInt32 aCount)
620 : {
621 4 : PRInt32 oldCount = Count();
622 4 : NS_ASSERTION(aIndex >= 0,"RemoveElementsAt(negative index)");
623 4 : if (PRUint32(aIndex) >= PRUint32(oldCount))
624 : {
625 : // An invalid index causes the replace to fail
626 0 : return false;
627 : }
628 : // Limit to available entries starting at aIndex
629 4 : if (aCount + aIndex > oldCount)
630 0 : aCount = oldCount - aIndex;
631 :
632 : // We don't need to move any elements if we're removing the
633 : // last element in the array
634 4 : if (aIndex < (oldCount - aCount))
635 : {
636 2 : memmove(mImpl->mArray + aIndex, mImpl->mArray + aIndex + aCount,
637 3 : (oldCount - (aIndex + aCount)) * sizeof(mImpl->mArray[0]));
638 : }
639 :
640 4 : mImpl->mCount -= aCount;
641 4 : return true;
642 : }
643 :
644 41 : bool nsVoidArray::RemoveElement(void* aElement)
645 : {
646 41 : PRInt32 theIndex = IndexOf(aElement);
647 41 : if (theIndex != -1)
648 1 : return RemoveElementAt(theIndex);
649 :
650 40 : return false;
651 : }
652 :
653 1395 : void nsVoidArray::Clear()
654 : {
655 1395 : if (mImpl)
656 : {
657 1395 : mImpl->mCount = 0;
658 : // We don't have to free on Clear, but if we have a built-in buffer,
659 : // it's worth considering.
660 1395 : if (HasAutoBuffer() && IsArrayOwner() &&
661 0 : GetArraySize() > kAutoClearCompactSizeFactor * kAutoBufSize) {
662 0 : SizeTo(0);
663 : }
664 : }
665 1395 : }
666 :
667 0 : void nsVoidArray::Compact()
668 : {
669 0 : if (mImpl)
670 : {
671 : // XXX NOTE: this is quite inefficient in many cases if we're only
672 : // compacting by a little, but some callers care more about memory use.
673 0 : PRInt32 count = Count();
674 0 : if (HasAutoBuffer() && count <= kAutoBufSize)
675 : {
676 0 : Impl* oldImpl = mImpl;
677 0 : static_cast<nsAutoVoidArray*>(this)->ResetToAutoBuffer();
678 : memcpy(mImpl->mArray, oldImpl->mArray,
679 0 : count * sizeof(mImpl->mArray[0]));
680 0 : free(reinterpret_cast<char *>(oldImpl));
681 : }
682 0 : else if (GetArraySize() > count)
683 : {
684 0 : SizeTo(Count());
685 : }
686 : }
687 0 : }
688 :
689 : // Needed because we want to pass the pointer to the item in the array
690 : // to the comparator function, not a pointer to the pointer in the array.
691 : struct VoidArrayComparatorContext {
692 : nsVoidArrayComparatorFunc mComparatorFunc;
693 : void* mData;
694 : };
695 :
696 : static int
697 0 : VoidArrayComparator(const void* aElement1, const void* aElement2, void* aData)
698 : {
699 0 : VoidArrayComparatorContext* ctx = static_cast<VoidArrayComparatorContext*>(aData);
700 : return (*ctx->mComparatorFunc)(*static_cast<void* const*>(aElement1),
701 : *static_cast<void* const*>(aElement2),
702 0 : ctx->mData);
703 : }
704 :
705 0 : void nsVoidArray::Sort(nsVoidArrayComparatorFunc aFunc, void* aData)
706 : {
707 0 : if (mImpl && mImpl->mCount > 1)
708 : {
709 0 : VoidArrayComparatorContext ctx = {aFunc, aData};
710 : NS_QuickSort(mImpl->mArray, mImpl->mCount, sizeof(mImpl->mArray[0]),
711 0 : VoidArrayComparator, &ctx);
712 : }
713 0 : }
714 :
715 1398 : bool nsVoidArray::EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData)
716 : {
717 1398 : PRInt32 index = -1;
718 1398 : bool running = true;
719 :
720 1398 : if (mImpl) {
721 4276 : while (running && (++index < mImpl->mCount)) {
722 1480 : running = (*aFunc)(mImpl->mArray[index], aData);
723 : }
724 : }
725 1398 : return running;
726 : }
727 :
728 0 : bool nsVoidArray::EnumerateForwards(nsVoidArrayEnumFuncConst aFunc,
729 : void* aData) const
730 : {
731 0 : PRInt32 index = -1;
732 0 : bool running = true;
733 :
734 0 : if (mImpl) {
735 0 : while (running && (++index < mImpl->mCount)) {
736 0 : running = (*aFunc)(mImpl->mArray[index], aData);
737 : }
738 : }
739 0 : return running;
740 : }
741 :
742 0 : bool nsVoidArray::EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData)
743 : {
744 0 : bool running = true;
745 :
746 0 : if (mImpl)
747 : {
748 0 : PRInt32 index = Count();
749 0 : while (running && (0 <= --index))
750 : {
751 0 : running = (*aFunc)(mImpl->mArray[index], aData);
752 : }
753 : }
754 0 : return running;
755 : }
756 :
757 : struct SizeOfElementIncludingThisData
758 : {
759 : size_t mSize;
760 : nsVoidArraySizeOfElementIncludingThisFunc mSizeOfElementIncludingThis;
761 : nsMallocSizeOfFun mMallocSizeOf;
762 : void *mData; // the arg passed by the user
763 : };
764 :
765 : static bool
766 0 : SizeOfElementIncludingThisEnumerator(const void *aElement, void *aData)
767 : {
768 0 : SizeOfElementIncludingThisData *d = (SizeOfElementIncludingThisData *)aData;
769 0 : d->mSize += d->mSizeOfElementIncludingThis(aElement, d->mMallocSizeOf, d->mData);
770 0 : return true;
771 : }
772 :
773 : size_t
774 0 : nsVoidArray::SizeOfExcludingThis(
775 : nsVoidArraySizeOfElementIncludingThisFunc aSizeOfElementIncludingThis,
776 : nsMallocSizeOfFun aMallocSizeOf, void* aData) const
777 : {
778 0 : size_t n = 0;
779 : // Measure the element storage.
780 0 : if (mImpl) {
781 0 : n += aMallocSizeOf(mImpl);
782 : }
783 : // Measure things pointed to by the elements.
784 0 : if (aSizeOfElementIncludingThis) {
785 : SizeOfElementIncludingThisData data2 =
786 0 : { 0, aSizeOfElementIncludingThis, aMallocSizeOf, aData };
787 0 : EnumerateForwards(SizeOfElementIncludingThisEnumerator, &data2);
788 0 : n += data2.mSize;
789 : }
790 0 : return n;
791 : }
792 :
793 : //----------------------------------------------------------------
794 : // nsAutoVoidArray
795 :
796 1398 : nsAutoVoidArray::nsAutoVoidArray()
797 1398 : : nsVoidArray()
798 : {
799 : // Don't need to clear it. Some users just call ReplaceElementAt(),
800 : // but we'll clear it at that time if needed to save CPU cycles.
801 : #if DEBUG_VOIDARRAY
802 : mIsAuto = true;
803 : ADD_TO_STATS(MaxAuto,0);
804 : #endif
805 1398 : ResetToAutoBuffer();
806 1398 : }
807 :
808 : //----------------------------------------------------------------------
809 : // NOTE: nsSmallVoidArray elements MUST all have the low bit as 0.
810 : // This means that normally it's only used for pointers, and in particular
811 : // structures or objects.
812 0 : nsSmallVoidArray::~nsSmallVoidArray()
813 : {
814 0 : if (HasSingle())
815 : {
816 : // Have to null out mImpl before the nsVoidArray dtor runs.
817 0 : mImpl = nsnull;
818 : }
819 0 : }
820 :
821 : nsSmallVoidArray&
822 0 : nsSmallVoidArray::operator=(nsSmallVoidArray& other)
823 : {
824 0 : PRInt32 count = other.Count();
825 0 : switch (count) {
826 : case 0:
827 0 : Clear();
828 0 : break;
829 : case 1:
830 0 : Clear();
831 0 : AppendElement(other.ElementAt(0));
832 0 : break;
833 : default:
834 0 : if (GetArraySize() >= count || SizeTo(count)) {
835 0 : *AsArray() = *other.AsArray();
836 : }
837 : }
838 :
839 0 : return *this;
840 : }
841 :
842 : PRInt32
843 0 : nsSmallVoidArray::GetArraySize() const
844 : {
845 0 : if (HasSingle()) {
846 0 : return 1;
847 : }
848 :
849 0 : return AsArray()->GetArraySize();
850 : }
851 :
852 : PRInt32
853 0 : nsSmallVoidArray::Count() const
854 : {
855 0 : if (HasSingle()) {
856 0 : return 1;
857 : }
858 :
859 0 : return AsArray()->Count();
860 : }
861 :
862 : void*
863 0 : nsSmallVoidArray::FastElementAt(PRInt32 aIndex) const
864 : {
865 0 : NS_ASSERTION(0 <= aIndex && aIndex < Count(), "nsSmallVoidArray::FastElementAt: index out of range");
866 :
867 0 : if (HasSingle()) {
868 0 : return GetSingle();
869 : }
870 :
871 0 : return AsArray()->FastElementAt(aIndex);
872 : }
873 :
874 : PRInt32
875 0 : nsSmallVoidArray::IndexOf(void* aPossibleElement) const
876 : {
877 0 : if (HasSingle()) {
878 0 : return aPossibleElement == GetSingle() ? 0 : -1;
879 : }
880 :
881 0 : return AsArray()->IndexOf(aPossibleElement);
882 : }
883 :
884 : bool
885 0 : nsSmallVoidArray::InsertElementAt(void* aElement, PRInt32 aIndex)
886 : {
887 0 : NS_ASSERTION(!(NS_PTR_TO_INT32(aElement) & 0x1),
888 : "Attempt to add element with 0x1 bit set to nsSmallVoidArray");
889 :
890 0 : if (aIndex == 0 && IsEmpty()) {
891 0 : SetSingle(aElement);
892 :
893 0 : return true;
894 : }
895 :
896 0 : if (!EnsureArray()) {
897 0 : return false;
898 : }
899 :
900 0 : return AsArray()->InsertElementAt(aElement, aIndex);
901 : }
902 :
903 0 : bool nsSmallVoidArray::InsertElementsAt(const nsVoidArray &aOther, PRInt32 aIndex)
904 : {
905 : #ifdef DEBUG
906 0 : for (int i = 0; i < aOther.Count(); i++) {
907 0 : NS_ASSERTION(!(NS_PTR_TO_INT32(aOther.ElementAt(i)) & 0x1),
908 : "Attempt to add element with 0x1 bit set to nsSmallVoidArray");
909 : }
910 : #endif
911 :
912 0 : if (aIndex == 0 && IsEmpty() && aOther.Count() == 1) {
913 0 : SetSingle(aOther.FastElementAt(0));
914 :
915 0 : return true;
916 : }
917 :
918 0 : if (!EnsureArray()) {
919 0 : return false;
920 : }
921 :
922 0 : return AsArray()->InsertElementsAt(aOther, aIndex);
923 : }
924 :
925 : bool
926 0 : nsSmallVoidArray::ReplaceElementAt(void* aElement, PRInt32 aIndex)
927 : {
928 0 : NS_ASSERTION(!(NS_PTR_TO_INT32(aElement) & 0x1),
929 : "Attempt to add element with 0x1 bit set to nsSmallVoidArray");
930 :
931 0 : if (aIndex == 0 && (IsEmpty() || HasSingle())) {
932 0 : SetSingle(aElement);
933 :
934 0 : return true;
935 : }
936 :
937 0 : if (!EnsureArray()) {
938 0 : return false;
939 : }
940 :
941 0 : return AsArray()->ReplaceElementAt(aElement, aIndex);
942 : }
943 :
944 : bool
945 0 : nsSmallVoidArray::AppendElement(void* aElement)
946 : {
947 0 : NS_ASSERTION(!(NS_PTR_TO_INT32(aElement) & 0x1),
948 : "Attempt to add element with 0x1 bit set to nsSmallVoidArray");
949 :
950 0 : if (IsEmpty()) {
951 0 : SetSingle(aElement);
952 :
953 0 : return true;
954 : }
955 :
956 0 : if (!EnsureArray()) {
957 0 : return false;
958 : }
959 :
960 0 : return AsArray()->AppendElement(aElement);
961 : }
962 :
963 : bool
964 0 : nsSmallVoidArray::RemoveElement(void* aElement)
965 : {
966 0 : if (HasSingle()) {
967 0 : if (aElement == GetSingle()) {
968 0 : mImpl = nsnull;
969 0 : return true;
970 : }
971 :
972 0 : return false;
973 : }
974 :
975 0 : return AsArray()->RemoveElement(aElement);
976 : }
977 :
978 : bool
979 0 : nsSmallVoidArray::RemoveElementAt(PRInt32 aIndex)
980 : {
981 0 : if (HasSingle()) {
982 0 : if (aIndex == 0) {
983 0 : mImpl = nsnull;
984 :
985 0 : return true;
986 : }
987 :
988 0 : return false;
989 : }
990 :
991 0 : return AsArray()->RemoveElementAt(aIndex);
992 : }
993 :
994 : bool
995 0 : nsSmallVoidArray::RemoveElementsAt(PRInt32 aIndex, PRInt32 aCount)
996 : {
997 0 : if (HasSingle()) {
998 0 : if (aIndex == 0) {
999 0 : if (aCount > 0) {
1000 0 : mImpl = nsnull;
1001 : }
1002 :
1003 0 : return true;
1004 : }
1005 :
1006 0 : return false;
1007 : }
1008 :
1009 0 : return AsArray()->RemoveElementsAt(aIndex, aCount);
1010 : }
1011 :
1012 : void
1013 0 : nsSmallVoidArray::Clear()
1014 : {
1015 0 : if (HasSingle()) {
1016 0 : mImpl = nsnull;
1017 : }
1018 : else {
1019 0 : AsArray()->Clear();
1020 : }
1021 0 : }
1022 :
1023 : bool
1024 0 : nsSmallVoidArray::SizeTo(PRInt32 aMin)
1025 : {
1026 0 : if (!HasSingle()) {
1027 0 : return AsArray()->SizeTo(aMin);
1028 : }
1029 :
1030 0 : if (aMin <= 0) {
1031 0 : mImpl = nsnull;
1032 :
1033 0 : return true;
1034 : }
1035 :
1036 0 : if (aMin == 1) {
1037 0 : return true;
1038 : }
1039 :
1040 0 : void* single = GetSingle();
1041 0 : mImpl = nsnull;
1042 0 : if (!AsArray()->SizeTo(aMin)) {
1043 0 : SetSingle(single);
1044 :
1045 0 : return false;
1046 : }
1047 :
1048 0 : AsArray()->AppendElement(single);
1049 :
1050 0 : return true;
1051 : }
1052 :
1053 : void
1054 0 : nsSmallVoidArray::Compact()
1055 : {
1056 0 : if (!HasSingle()) {
1057 0 : AsArray()->Compact();
1058 : }
1059 0 : }
1060 :
1061 : void
1062 0 : nsSmallVoidArray::Sort(nsVoidArrayComparatorFunc aFunc, void* aData)
1063 : {
1064 0 : if (!HasSingle()) {
1065 0 : AsArray()->Sort(aFunc,aData);
1066 : }
1067 0 : }
1068 :
1069 : bool
1070 0 : nsSmallVoidArray::EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData)
1071 : {
1072 0 : if (HasSingle()) {
1073 0 : return (*aFunc)(GetSingle(), aData);
1074 : }
1075 0 : return AsArray()->EnumerateForwards(aFunc,aData);
1076 : }
1077 :
1078 : bool
1079 0 : nsSmallVoidArray::EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData)
1080 : {
1081 0 : if (HasSingle()) {
1082 0 : return (*aFunc)(GetSingle(), aData);
1083 : }
1084 0 : return AsArray()->EnumerateBackwards(aFunc,aData);
1085 : }
1086 :
1087 : bool
1088 0 : nsSmallVoidArray::EnsureArray()
1089 : {
1090 0 : if (!HasSingle()) {
1091 0 : return true;
1092 : }
1093 :
1094 0 : void* single = GetSingle();
1095 0 : mImpl = nsnull;
1096 0 : if (!AsArray()->AppendElement(single)) {
1097 0 : SetSingle(single);
1098 :
1099 0 : return false;
1100 : }
1101 :
1102 0 : return true;
1103 : }
|