xpcom/glue/nsVoidArray.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial