xpcom/glue/nsTArray.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #ifndef nsTArray_h__
michael@0 8 #define nsTArray_h__
michael@0 9
michael@0 10 #include "nsTArrayForwardDeclare.h"
michael@0 11 #include "mozilla/Alignment.h"
michael@0 12 #include "mozilla/Assertions.h"
michael@0 13 #include "mozilla/MemoryReporting.h"
michael@0 14 #include "mozilla/TypeTraits.h"
michael@0 15
michael@0 16 #include <string.h>
michael@0 17
michael@0 18 #include "nsCycleCollectionNoteChild.h"
michael@0 19 #include "nsAlgorithm.h"
michael@0 20 #include "nscore.h"
michael@0 21 #include "nsQuickSort.h"
michael@0 22 #include "nsDebug.h"
michael@0 23 #include "nsISupportsImpl.h"
michael@0 24 #include <new>
michael@0 25
michael@0 26 namespace JS {
michael@0 27 template <class T>
michael@0 28 class Heap;
michael@0 29 } /* namespace JS */
michael@0 30
michael@0 31 class nsRegion;
michael@0 32 class nsIntRegion;
michael@0 33
michael@0 34 //
michael@0 35 // nsTArray is a resizable array class, like std::vector.
michael@0 36 //
michael@0 37 // Unlike std::vector, which follows C++'s construction/destruction rules,
michael@0 38 // nsTArray assumes that your "T" can be memmoved()'ed safely.
michael@0 39 //
michael@0 40 // The public classes defined in this header are
michael@0 41 //
michael@0 42 // nsTArray<T>,
michael@0 43 // FallibleTArray<T>,
michael@0 44 // nsAutoTArray<T, N>, and
michael@0 45 // AutoFallibleTArray<T, N>.
michael@0 46 //
michael@0 47 // nsTArray and nsAutoTArray are infallible; if one tries to make an allocation
michael@0 48 // which fails, it crashes the program. In contrast, FallibleTArray and
michael@0 49 // AutoFallibleTArray are fallible; if you use one of these classes, you must
michael@0 50 // check the return values of methods such as Append() which may allocate. If
michael@0 51 // in doubt, choose an infallible type.
michael@0 52 //
michael@0 53 // InfallibleTArray and AutoInfallibleTArray are aliases for nsTArray and
michael@0 54 // nsAutoTArray.
michael@0 55 //
michael@0 56 // If you just want to declare the nsTArray types (e.g., if you're in a header
michael@0 57 // file and don't need the full nsTArray definitions) consider including
michael@0 58 // nsTArrayForwardDeclare.h instead of nsTArray.h.
michael@0 59 //
michael@0 60 // The template parameter (i.e., T in nsTArray<T>) specifies the type of the
michael@0 61 // elements and has the following requirements:
michael@0 62 //
michael@0 63 // T MUST be safely memmove()'able.
michael@0 64 // T MUST define a copy-constructor.
michael@0 65 // T MAY define operator< for sorting.
michael@0 66 // T MAY define operator== for searching.
michael@0 67 //
michael@0 68 // (Note that the memmove requirement may be relaxed for certain types - see
michael@0 69 // nsTArray_CopyChooser below.)
michael@0 70 //
michael@0 71 // For methods taking a Comparator instance, the Comparator must be a class
michael@0 72 // defining the following methods:
michael@0 73 //
michael@0 74 // class Comparator {
michael@0 75 // public:
michael@0 76 // /** @return True if the elements are equals; false otherwise. */
michael@0 77 // bool Equals(const elem_type& a, const Item& b) const;
michael@0 78 //
michael@0 79 // /** @return True if (a < b); false otherwise. */
michael@0 80 // bool LessThan(const elem_type& a, const Item& b) const;
michael@0 81 // };
michael@0 82 //
michael@0 83 // The Equals method is used for searching, and the LessThan method is used for
michael@0 84 // searching and sorting. The |Item| type above can be arbitrary, but must
michael@0 85 // match the Item type passed to the sort or search function.
michael@0 86 //
michael@0 87
michael@0 88
michael@0 89 //
michael@0 90 // nsTArrayFallibleResult and nsTArrayInfallibleResult types are proxy types
michael@0 91 // which are used because you cannot use a templated type which is bound to
michael@0 92 // void as an argument to a void function. In order to work around that, we
michael@0 93 // encode either a void or a boolean inside these proxy objects, and pass them
michael@0 94 // to the aforementioned function instead, and then use the type information to
michael@0 95 // decide what to do in the function.
michael@0 96 //
michael@0 97 // Note that public nsTArray methods should never return a proxy type. Such
michael@0 98 // types are only meant to be used in the internal nsTArray helper methods.
michael@0 99 // Public methods returning non-proxy types cannot be called from other
michael@0 100 // nsTArray members.
michael@0 101 //
michael@0 102 struct nsTArrayFallibleResult
michael@0 103 {
michael@0 104 // Note: allows implicit conversions from and to bool
michael@0 105 nsTArrayFallibleResult(bool result)
michael@0 106 : mResult(result)
michael@0 107 {}
michael@0 108
michael@0 109 operator bool() {
michael@0 110 return mResult;
michael@0 111 }
michael@0 112
michael@0 113 private:
michael@0 114 bool mResult;
michael@0 115 };
michael@0 116
michael@0 117 struct nsTArrayInfallibleResult
michael@0 118 {
michael@0 119 };
michael@0 120
michael@0 121 //
michael@0 122 // nsTArray*Allocators must all use the same |free()|, to allow swap()'ing
michael@0 123 // between fallible and infallible variants.
michael@0 124 //
michael@0 125
michael@0 126 struct nsTArrayFallibleAllocatorBase
michael@0 127 {
michael@0 128 typedef bool ResultType;
michael@0 129 typedef nsTArrayFallibleResult ResultTypeProxy;
michael@0 130
michael@0 131 static ResultType Result(ResultTypeProxy result) {
michael@0 132 return result;
michael@0 133 }
michael@0 134
michael@0 135 static bool Successful(ResultTypeProxy result) {
michael@0 136 return result;
michael@0 137 }
michael@0 138
michael@0 139 static ResultTypeProxy SuccessResult() {
michael@0 140 return true;
michael@0 141 }
michael@0 142
michael@0 143 static ResultTypeProxy FailureResult() {
michael@0 144 return false;
michael@0 145 }
michael@0 146
michael@0 147 static ResultType ConvertBoolToResultType(bool aValue) {
michael@0 148 return aValue;
michael@0 149 }
michael@0 150 };
michael@0 151
michael@0 152 struct nsTArrayInfallibleAllocatorBase
michael@0 153 {
michael@0 154 typedef void ResultType;
michael@0 155 typedef nsTArrayInfallibleResult ResultTypeProxy;
michael@0 156
michael@0 157 static ResultType Result(ResultTypeProxy result) {
michael@0 158 }
michael@0 159
michael@0 160 static bool Successful(ResultTypeProxy) {
michael@0 161 return true;
michael@0 162 }
michael@0 163
michael@0 164 static ResultTypeProxy SuccessResult() {
michael@0 165 return ResultTypeProxy();
michael@0 166 }
michael@0 167
michael@0 168 static ResultTypeProxy FailureResult() {
michael@0 169 NS_RUNTIMEABORT("Infallible nsTArray should never fail");
michael@0 170 return ResultTypeProxy();
michael@0 171 }
michael@0 172
michael@0 173 static ResultType ConvertBoolToResultType(bool aValue) {
michael@0 174 if (!aValue) {
michael@0 175 NS_RUNTIMEABORT("infallible nsTArray should never convert false to ResultType");
michael@0 176 }
michael@0 177 }
michael@0 178 };
michael@0 179
michael@0 180 #if defined(MOZALLOC_HAVE_XMALLOC)
michael@0 181 #include "mozilla/mozalloc_abort.h"
michael@0 182
michael@0 183 struct nsTArrayFallibleAllocator : nsTArrayFallibleAllocatorBase
michael@0 184 {
michael@0 185 static void* Malloc(size_t size) {
michael@0 186 return moz_malloc(size);
michael@0 187 }
michael@0 188
michael@0 189 static void* Realloc(void* ptr, size_t size) {
michael@0 190 return moz_realloc(ptr, size);
michael@0 191 }
michael@0 192
michael@0 193 static void Free(void* ptr) {
michael@0 194 moz_free(ptr);
michael@0 195 }
michael@0 196
michael@0 197 static void SizeTooBig(size_t) {
michael@0 198 }
michael@0 199 };
michael@0 200
michael@0 201 struct nsTArrayInfallibleAllocator : nsTArrayInfallibleAllocatorBase
michael@0 202 {
michael@0 203 static void* Malloc(size_t size) {
michael@0 204 return moz_xmalloc(size);
michael@0 205 }
michael@0 206
michael@0 207 static void* Realloc(void* ptr, size_t size) {
michael@0 208 return moz_xrealloc(ptr, size);
michael@0 209 }
michael@0 210
michael@0 211 static void Free(void* ptr) {
michael@0 212 moz_free(ptr);
michael@0 213 }
michael@0 214
michael@0 215 static void SizeTooBig(size_t size) {
michael@0 216 NS_ABORT_OOM(size);
michael@0 217 }
michael@0 218 };
michael@0 219
michael@0 220 #else
michael@0 221 #include <stdlib.h>
michael@0 222
michael@0 223 struct nsTArrayFallibleAllocator : nsTArrayFallibleAllocatorBase
michael@0 224 {
michael@0 225 static void* Malloc(size_t size) {
michael@0 226 return malloc(size);
michael@0 227 }
michael@0 228
michael@0 229 static void* Realloc(void* ptr, size_t size) {
michael@0 230 return realloc(ptr, size);
michael@0 231 }
michael@0 232
michael@0 233 static void Free(void* ptr) {
michael@0 234 free(ptr);
michael@0 235 }
michael@0 236
michael@0 237 static void SizeTooBig(size_t) {
michael@0 238 }
michael@0 239 };
michael@0 240
michael@0 241 struct nsTArrayInfallibleAllocator : nsTArrayInfallibleAllocatorBase
michael@0 242 {
michael@0 243 static void* Malloc(size_t size) {
michael@0 244 void* ptr = malloc(size);
michael@0 245 if (MOZ_UNLIKELY(!ptr)) {
michael@0 246 NS_ABORT_OOM(size);
michael@0 247 }
michael@0 248 return ptr;
michael@0 249 }
michael@0 250
michael@0 251 static void* Realloc(void* ptr, size_t size) {
michael@0 252 void* newptr = realloc(ptr, size);
michael@0 253 if (MOZ_UNLIKELY(!ptr && size)) {
michael@0 254 NS_ABORT_OOM(size);
michael@0 255 }
michael@0 256 return newptr;
michael@0 257 }
michael@0 258
michael@0 259 static void Free(void* ptr) {
michael@0 260 free(ptr);
michael@0 261 }
michael@0 262
michael@0 263 static void SizeTooBig(size_t size) {
michael@0 264 NS_ABORT_OOM(size);
michael@0 265 }
michael@0 266 };
michael@0 267
michael@0 268 #endif
michael@0 269
michael@0 270 // nsTArray_base stores elements into the space allocated beyond
michael@0 271 // sizeof(*this). This is done to minimize the size of the nsTArray
michael@0 272 // object when it is empty.
michael@0 273 struct NS_COM_GLUE nsTArrayHeader
michael@0 274 {
michael@0 275 static nsTArrayHeader sEmptyHdr;
michael@0 276
michael@0 277 uint32_t mLength;
michael@0 278 uint32_t mCapacity : 31;
michael@0 279 uint32_t mIsAutoArray : 1;
michael@0 280 };
michael@0 281
michael@0 282 // This class provides a SafeElementAt method to nsTArray<T*> which does
michael@0 283 // not take a second default value parameter.
michael@0 284 template <class E, class Derived>
michael@0 285 struct nsTArray_SafeElementAtHelper
michael@0 286 {
michael@0 287 typedef E* elem_type;
michael@0 288 typedef uint32_t index_type;
michael@0 289
michael@0 290 // No implementation is provided for these two methods, and that is on
michael@0 291 // purpose, since we don't support these functions on non-pointer type
michael@0 292 // instantiations.
michael@0 293 elem_type& SafeElementAt(index_type i);
michael@0 294 const elem_type& SafeElementAt(index_type i) const;
michael@0 295 };
michael@0 296
michael@0 297 template <class E, class Derived>
michael@0 298 struct nsTArray_SafeElementAtHelper<E*, Derived>
michael@0 299 {
michael@0 300 typedef E* elem_type;
michael@0 301 typedef uint32_t index_type;
michael@0 302
michael@0 303 elem_type SafeElementAt(index_type i) {
michael@0 304 return static_cast<Derived*> (this)->SafeElementAt(i, nullptr);
michael@0 305 }
michael@0 306
michael@0 307 const elem_type SafeElementAt(index_type i) const {
michael@0 308 return static_cast<const Derived*> (this)->SafeElementAt(i, nullptr);
michael@0 309 }
michael@0 310 };
michael@0 311
michael@0 312 // E is the base type that the smart pointer is templated over; the
michael@0 313 // smart pointer can act as E*.
michael@0 314 template <class E, class Derived>
michael@0 315 struct nsTArray_SafeElementAtSmartPtrHelper
michael@0 316 {
michael@0 317 typedef E* elem_type;
michael@0 318 typedef uint32_t index_type;
michael@0 319
michael@0 320 elem_type SafeElementAt(index_type i) {
michael@0 321 return static_cast<Derived*> (this)->SafeElementAt(i, nullptr);
michael@0 322 }
michael@0 323
michael@0 324 const elem_type SafeElementAt(index_type i) const {
michael@0 325 return static_cast<const Derived*> (this)->SafeElementAt(i, nullptr);
michael@0 326 }
michael@0 327 };
michael@0 328
michael@0 329 template <class T> class nsCOMPtr;
michael@0 330
michael@0 331 template <class E, class Derived>
michael@0 332 struct nsTArray_SafeElementAtHelper<nsCOMPtr<E>, Derived> :
michael@0 333 public nsTArray_SafeElementAtSmartPtrHelper<E, Derived>
michael@0 334 {
michael@0 335 };
michael@0 336
michael@0 337 template <class T> class nsRefPtr;
michael@0 338
michael@0 339 template <class E, class Derived>
michael@0 340 struct nsTArray_SafeElementAtHelper<nsRefPtr<E>, Derived> :
michael@0 341 public nsTArray_SafeElementAtSmartPtrHelper<E, Derived>
michael@0 342 {
michael@0 343 };
michael@0 344
michael@0 345 //
michael@0 346 // This class serves as a base class for nsTArray. It shouldn't be used
michael@0 347 // directly. It holds common implementation code that does not depend on the
michael@0 348 // element type of the nsTArray.
michael@0 349 //
michael@0 350 template<class Alloc, class Copy>
michael@0 351 class nsTArray_base
michael@0 352 {
michael@0 353 // Allow swapping elements with |nsTArray_base|s created using a
michael@0 354 // different allocator. This is kosher because all allocators use
michael@0 355 // the same free().
michael@0 356 template<class Allocator, class Copier>
michael@0 357 friend class nsTArray_base;
michael@0 358
michael@0 359 protected:
michael@0 360 typedef nsTArrayHeader Header;
michael@0 361
michael@0 362 public:
michael@0 363 typedef uint32_t size_type;
michael@0 364 typedef uint32_t index_type;
michael@0 365
michael@0 366 // @return The number of elements in the array.
michael@0 367 size_type Length() const {
michael@0 368 return mHdr->mLength;
michael@0 369 }
michael@0 370
michael@0 371 // @return True if the array is empty or false otherwise.
michael@0 372 bool IsEmpty() const {
michael@0 373 return Length() == 0;
michael@0 374 }
michael@0 375
michael@0 376 // @return The number of elements that can fit in the array without forcing
michael@0 377 // the array to be re-allocated. The length of an array is always less
michael@0 378 // than or equal to its capacity.
michael@0 379 size_type Capacity() const {
michael@0 380 return mHdr->mCapacity;
michael@0 381 }
michael@0 382
michael@0 383 #ifdef DEBUG
michael@0 384 void* DebugGetHeader() const {
michael@0 385 return mHdr;
michael@0 386 }
michael@0 387 #endif
michael@0 388
michael@0 389 protected:
michael@0 390 nsTArray_base();
michael@0 391
michael@0 392 ~nsTArray_base();
michael@0 393
michael@0 394 // Resize the storage if necessary to achieve the requested capacity.
michael@0 395 // @param capacity The requested number of array elements.
michael@0 396 // @param elemSize The size of an array element.
michael@0 397 // @return False if insufficient memory is available; true otherwise.
michael@0 398 typename Alloc::ResultTypeProxy EnsureCapacity(size_type capacity, size_type elemSize);
michael@0 399
michael@0 400 // Resize the storage to the minimum required amount.
michael@0 401 // @param elemSize The size of an array element.
michael@0 402 // @param elemAlign The alignment in bytes of an array element.
michael@0 403 void ShrinkCapacity(size_type elemSize, size_t elemAlign);
michael@0 404
michael@0 405 // This method may be called to resize a "gap" in the array by shifting
michael@0 406 // elements around. It updates mLength appropriately. If the resulting
michael@0 407 // array has zero elements, then the array's memory is free'd.
michael@0 408 // @param start The starting index of the gap.
michael@0 409 // @param oldLen The current length of the gap.
michael@0 410 // @param newLen The desired length of the gap.
michael@0 411 // @param elemSize The size of an array element.
michael@0 412 // @param elemAlign The alignment in bytes of an array element.
michael@0 413 void ShiftData(index_type start, size_type oldLen, size_type newLen,
michael@0 414 size_type elemSize, size_t elemAlign);
michael@0 415
michael@0 416 // This method increments the length member of the array's header.
michael@0 417 // Note that mHdr may actually be sEmptyHdr in the case where a
michael@0 418 // zero-length array is inserted into our array. But then n should
michael@0 419 // always be 0.
michael@0 420 void IncrementLength(uint32_t n) {
michael@0 421 if (mHdr == EmptyHdr()) {
michael@0 422 if (MOZ_UNLIKELY(n != 0)) {
michael@0 423 // Writing a non-zero length to the empty header would be extremely bad.
michael@0 424 MOZ_CRASH();
michael@0 425 }
michael@0 426 } else {
michael@0 427 mHdr->mLength += n;
michael@0 428 }
michael@0 429 }
michael@0 430
michael@0 431 // This method inserts blank slots into the array.
michael@0 432 // @param index the place to insert the new elements. This must be no
michael@0 433 // greater than the current length of the array.
michael@0 434 // @param count the number of slots to insert
michael@0 435 // @param elementSize the size of an array element.
michael@0 436 // @param elemAlign the alignment in bytes of an array element.
michael@0 437 bool InsertSlotsAt(index_type index, size_type count,
michael@0 438 size_type elementSize, size_t elemAlign);
michael@0 439
michael@0 440 protected:
michael@0 441 template<class Allocator>
michael@0 442 typename Alloc::ResultTypeProxy
michael@0 443 SwapArrayElements(nsTArray_base<Allocator, Copy>& other,
michael@0 444 size_type elemSize,
michael@0 445 size_t elemAlign);
michael@0 446
michael@0 447 // This is an RAII class used in SwapArrayElements.
michael@0 448 class IsAutoArrayRestorer {
michael@0 449 public:
michael@0 450 IsAutoArrayRestorer(nsTArray_base<Alloc, Copy> &array, size_t elemAlign);
michael@0 451 ~IsAutoArrayRestorer();
michael@0 452
michael@0 453 private:
michael@0 454 nsTArray_base<Alloc, Copy> &mArray;
michael@0 455 size_t mElemAlign;
michael@0 456 bool mIsAuto;
michael@0 457 };
michael@0 458
michael@0 459 // Helper function for SwapArrayElements. Ensures that if the array
michael@0 460 // is an nsAutoTArray that it doesn't use the built-in buffer.
michael@0 461 bool EnsureNotUsingAutoArrayBuffer(size_type elemSize);
michael@0 462
michael@0 463 // Returns true if this nsTArray is an nsAutoTArray with a built-in buffer.
michael@0 464 bool IsAutoArray() const {
michael@0 465 return mHdr->mIsAutoArray;
michael@0 466 }
michael@0 467
michael@0 468 // Returns a Header for the built-in buffer of this nsAutoTArray.
michael@0 469 Header* GetAutoArrayBuffer(size_t elemAlign) {
michael@0 470 MOZ_ASSERT(IsAutoArray(), "Should be an auto array to call this");
michael@0 471 return GetAutoArrayBufferUnsafe(elemAlign);
michael@0 472 }
michael@0 473 const Header* GetAutoArrayBuffer(size_t elemAlign) const {
michael@0 474 MOZ_ASSERT(IsAutoArray(), "Should be an auto array to call this");
michael@0 475 return GetAutoArrayBufferUnsafe(elemAlign);
michael@0 476 }
michael@0 477
michael@0 478 // Returns a Header for the built-in buffer of this nsAutoTArray, but doesn't
michael@0 479 // assert that we are an nsAutoTArray.
michael@0 480 Header* GetAutoArrayBufferUnsafe(size_t elemAlign) {
michael@0 481 return const_cast<Header*>(static_cast<const nsTArray_base<Alloc, Copy>*>(this)->
michael@0 482 GetAutoArrayBufferUnsafe(elemAlign));
michael@0 483 }
michael@0 484 const Header* GetAutoArrayBufferUnsafe(size_t elemAlign) const;
michael@0 485
michael@0 486 // Returns true if this is an nsAutoTArray and it currently uses the
michael@0 487 // built-in buffer to store its elements.
michael@0 488 bool UsesAutoArrayBuffer() const;
michael@0 489
michael@0 490 // The array's elements (prefixed with a Header). This pointer is never
michael@0 491 // null. If the array is empty, then this will point to sEmptyHdr.
michael@0 492 Header *mHdr;
michael@0 493
michael@0 494 Header* Hdr() const {
michael@0 495 return mHdr;
michael@0 496 }
michael@0 497
michael@0 498 Header** PtrToHdr() {
michael@0 499 return &mHdr;
michael@0 500 }
michael@0 501
michael@0 502 static Header* EmptyHdr() {
michael@0 503 return &Header::sEmptyHdr;
michael@0 504 }
michael@0 505 };
michael@0 506
michael@0 507 //
michael@0 508 // This class defines convenience functions for element specific operations.
michael@0 509 // Specialize this template if necessary.
michael@0 510 //
michael@0 511 template<class E>
michael@0 512 class nsTArrayElementTraits
michael@0 513 {
michael@0 514 public:
michael@0 515 // Invoke the default constructor in place.
michael@0 516 static inline void Construct(E *e) {
michael@0 517 // Do NOT call "E()"! That triggers C++ "default initialization"
michael@0 518 // which zeroes out POD ("plain old data") types such as regular
michael@0 519 // ints. We don't want that because it can be a performance issue
michael@0 520 // and people don't expect it; nsTArray should work like a regular
michael@0 521 // C/C++ array in this respect.
michael@0 522 new (static_cast<void *>(e)) E;
michael@0 523 }
michael@0 524 // Invoke the copy-constructor in place.
michael@0 525 template<class A>
michael@0 526 static inline void Construct(E *e, const A &arg) {
michael@0 527 typedef typename mozilla::RemoveCV<E>::Type E_NoCV;
michael@0 528 typedef typename mozilla::RemoveCV<A>::Type A_NoCV;
michael@0 529 static_assert(!mozilla::IsSame<E_NoCV*, A_NoCV>::value,
michael@0 530 "For safety, we disallow constructing nsTArray<E> elements "
michael@0 531 "from E* pointers. See bug 960591.");
michael@0 532 new (static_cast<void *>(e)) E(arg);
michael@0 533 }
michael@0 534 // Invoke the destructor in place.
michael@0 535 static inline void Destruct(E *e) {
michael@0 536 e->~E();
michael@0 537 }
michael@0 538 };
michael@0 539
michael@0 540 // The default comparator used by nsTArray
michael@0 541 template<class A, class B>
michael@0 542 class nsDefaultComparator
michael@0 543 {
michael@0 544 public:
michael@0 545 bool Equals(const A& a, const B& b) const {
michael@0 546 return a == b;
michael@0 547 }
michael@0 548 bool LessThan(const A& a, const B& b) const {
michael@0 549 return a < b;
michael@0 550 }
michael@0 551 };
michael@0 552
michael@0 553 template <class E> class InfallibleTArray;
michael@0 554 template <class E> class FallibleTArray;
michael@0 555
michael@0 556 template<bool IsPod, bool IsSameType>
michael@0 557 struct AssignRangeAlgorithm {
michael@0 558 template<class Item, class ElemType, class IndexType, class SizeType>
michael@0 559 static void implementation(ElemType* elements, IndexType start,
michael@0 560 SizeType count, const Item *values) {
michael@0 561 ElemType *iter = elements + start, *end = iter + count;
michael@0 562 for (; iter != end; ++iter, ++values)
michael@0 563 nsTArrayElementTraits<ElemType>::Construct(iter, *values);
michael@0 564 }
michael@0 565 };
michael@0 566
michael@0 567 template<>
michael@0 568 struct AssignRangeAlgorithm<true, true> {
michael@0 569 template<class Item, class ElemType, class IndexType, class SizeType>
michael@0 570 static void implementation(ElemType* elements, IndexType start,
michael@0 571 SizeType count, const Item *values) {
michael@0 572 memcpy(elements + start, values, count * sizeof(ElemType));
michael@0 573 }
michael@0 574 };
michael@0 575
michael@0 576 //
michael@0 577 // Normally elements are copied with memcpy and memmove, but for some element
michael@0 578 // types that is problematic. The nsTArray_CopyChooser template class can be
michael@0 579 // specialized to ensure that copying calls constructors and destructors
michael@0 580 // instead, as is done below for JS::Heap<E> elements.
michael@0 581 //
michael@0 582
michael@0 583 //
michael@0 584 // A class that defines how to copy elements using memcpy/memmove.
michael@0 585 //
michael@0 586 struct nsTArray_CopyWithMemutils
michael@0 587 {
michael@0 588 const static bool allowRealloc = true;
michael@0 589
michael@0 590 static void CopyElements(void* dest, const void* src, size_t count, size_t elemSize) {
michael@0 591 memcpy(dest, src, count * elemSize);
michael@0 592 }
michael@0 593
michael@0 594 static void CopyHeaderAndElements(void* dest, const void* src, size_t count, size_t elemSize) {
michael@0 595 memcpy(dest, src, sizeof(nsTArrayHeader) + count * elemSize);
michael@0 596 }
michael@0 597
michael@0 598 static void MoveElements(void* dest, const void* src, size_t count, size_t elemSize) {
michael@0 599 memmove(dest, src, count * elemSize);
michael@0 600 }
michael@0 601 };
michael@0 602
michael@0 603 //
michael@0 604 // A template class that defines how to copy elements calling their constructors
michael@0 605 // and destructors appropriately.
michael@0 606 //
michael@0 607 template <class ElemType>
michael@0 608 struct nsTArray_CopyWithConstructors
michael@0 609 {
michael@0 610 typedef nsTArrayElementTraits<ElemType> traits;
michael@0 611
michael@0 612 const static bool allowRealloc = false;
michael@0 613
michael@0 614 static void CopyElements(void* dest, void* src, size_t count, size_t elemSize) {
michael@0 615 ElemType* destElem = static_cast<ElemType*>(dest);
michael@0 616 ElemType* srcElem = static_cast<ElemType*>(src);
michael@0 617 ElemType* destElemEnd = destElem + count;
michael@0 618 #ifdef DEBUG
michael@0 619 ElemType* srcElemEnd = srcElem + count;
michael@0 620 MOZ_ASSERT(srcElemEnd <= destElem || srcElemEnd > destElemEnd);
michael@0 621 #endif
michael@0 622 while (destElem != destElemEnd) {
michael@0 623 traits::Construct(destElem, *srcElem);
michael@0 624 traits::Destruct(srcElem);
michael@0 625 ++destElem;
michael@0 626 ++srcElem;
michael@0 627 }
michael@0 628 }
michael@0 629
michael@0 630 static void CopyHeaderAndElements(void* dest, void* src, size_t count, size_t elemSize) {
michael@0 631 nsTArrayHeader* destHeader = static_cast<nsTArrayHeader*>(dest);
michael@0 632 nsTArrayHeader* srcHeader = static_cast<nsTArrayHeader*>(src);
michael@0 633 *destHeader = *srcHeader;
michael@0 634 CopyElements(static_cast<uint8_t*>(dest) + sizeof(nsTArrayHeader),
michael@0 635 static_cast<uint8_t*>(src) + sizeof(nsTArrayHeader),
michael@0 636 count, elemSize);
michael@0 637 }
michael@0 638
michael@0 639 static void MoveElements(void* dest, void* src, size_t count, size_t elemSize) {
michael@0 640 ElemType* destElem = static_cast<ElemType*>(dest);
michael@0 641 ElemType* srcElem = static_cast<ElemType*>(src);
michael@0 642 ElemType* destElemEnd = destElem + count;
michael@0 643 ElemType* srcElemEnd = srcElem + count;
michael@0 644 if (destElem == srcElem) {
michael@0 645 return; // In practice, we don't do this.
michael@0 646 } else if (srcElemEnd > destElem && srcElemEnd < destElemEnd) {
michael@0 647 while (destElemEnd != destElem) {
michael@0 648 --destElemEnd;
michael@0 649 --srcElemEnd;
michael@0 650 traits::Construct(destElemEnd, *srcElemEnd);
michael@0 651 traits::Destruct(srcElem);
michael@0 652 }
michael@0 653 } else {
michael@0 654 CopyElements(dest, src, count, elemSize);
michael@0 655 }
michael@0 656 }
michael@0 657 };
michael@0 658
michael@0 659 //
michael@0 660 // The default behaviour is to use memcpy/memmove for everything.
michael@0 661 //
michael@0 662 template <class E>
michael@0 663 struct nsTArray_CopyChooser {
michael@0 664 typedef nsTArray_CopyWithMemutils Type;
michael@0 665 };
michael@0 666
michael@0 667 //
michael@0 668 // Some classes require constructors/destructors to be called, so they are
michael@0 669 // specialized here.
michael@0 670 //
michael@0 671 template <class E>
michael@0 672 struct nsTArray_CopyChooser<JS::Heap<E> > {
michael@0 673 typedef nsTArray_CopyWithConstructors<JS::Heap<E> > Type;
michael@0 674 };
michael@0 675
michael@0 676 template<>
michael@0 677 struct nsTArray_CopyChooser<nsRegion> {
michael@0 678 typedef nsTArray_CopyWithConstructors<nsRegion> Type;
michael@0 679 };
michael@0 680
michael@0 681 template<>
michael@0 682 struct nsTArray_CopyChooser<nsIntRegion> {
michael@0 683 typedef nsTArray_CopyWithConstructors<nsIntRegion> Type;
michael@0 684 };
michael@0 685
michael@0 686 //
michael@0 687 // Base class for nsTArray_Impl that is templated on element type and derived
michael@0 688 // nsTArray_Impl class, to allow extra conversions to be added for specific
michael@0 689 // types.
michael@0 690 //
michael@0 691 template <class E, class Derived>
michael@0 692 struct nsTArray_TypedBase : public nsTArray_SafeElementAtHelper<E, Derived> {};
michael@0 693
michael@0 694 //
michael@0 695 // Specialization of nsTArray_TypedBase for arrays containing JS::Heap<E>
michael@0 696 // elements.
michael@0 697 //
michael@0 698 // These conversions are safe because JS::Heap<E> and E share the same
michael@0 699 // representation, and since the result of the conversions are const references
michael@0 700 // we won't miss any barriers.
michael@0 701 //
michael@0 702 // The static_cast is necessary to obtain the correct address for the derived
michael@0 703 // class since we are a base class used in multiple inheritance.
michael@0 704 //
michael@0 705 template <class E, class Derived>
michael@0 706 struct nsTArray_TypedBase<JS::Heap<E>, Derived>
michael@0 707 : public nsTArray_SafeElementAtHelper<JS::Heap<E>, Derived>
michael@0 708 {
michael@0 709 operator const nsTArray<E>& () {
michael@0 710 static_assert(sizeof(E) == sizeof(JS::Heap<E>),
michael@0 711 "JS::Heap<E> must be binary compatible with E.");
michael@0 712 Derived* self = static_cast<Derived*>(this);
michael@0 713 return *reinterpret_cast<nsTArray<E> *>(self);
michael@0 714 }
michael@0 715
michael@0 716 operator const FallibleTArray<E>& () {
michael@0 717 Derived* self = static_cast<Derived*>(this);
michael@0 718 return *reinterpret_cast<FallibleTArray<E> *>(self);
michael@0 719 }
michael@0 720 };
michael@0 721
michael@0 722
michael@0 723 //
michael@0 724 // nsTArray_Impl contains most of the guts supporting nsTArray, FallibleTArray,
michael@0 725 // nsAutoTArray, and AutoFallibleTArray.
michael@0 726 //
michael@0 727 // The only situation in which you might need to use nsTArray_Impl in your code
michael@0 728 // is if you're writing code which mutates a TArray which may or may not be
michael@0 729 // infallible.
michael@0 730 //
michael@0 731 // Code which merely reads from a TArray which may or may not be infallible can
michael@0 732 // simply cast the TArray to |const nsTArray&|; both fallible and infallible
michael@0 733 // TArrays can be cast to |const nsTArray&|.
michael@0 734 //
michael@0 735 template<class E, class Alloc>
michael@0 736 class nsTArray_Impl : public nsTArray_base<Alloc, typename nsTArray_CopyChooser<E>::Type>,
michael@0 737 public nsTArray_TypedBase<E, nsTArray_Impl<E, Alloc> >
michael@0 738 {
michael@0 739 public:
michael@0 740 typedef typename nsTArray_CopyChooser<E>::Type copy_type;
michael@0 741 typedef nsTArray_base<Alloc, copy_type> base_type;
michael@0 742 typedef typename base_type::size_type size_type;
michael@0 743 typedef typename base_type::index_type index_type;
michael@0 744 typedef E elem_type;
michael@0 745 typedef nsTArray_Impl<E, Alloc> self_type;
michael@0 746 typedef nsTArrayElementTraits<E> elem_traits;
michael@0 747 typedef nsTArray_SafeElementAtHelper<E, self_type> safeelementat_helper_type;
michael@0 748
michael@0 749 using safeelementat_helper_type::SafeElementAt;
michael@0 750 using base_type::EmptyHdr;
michael@0 751
michael@0 752 // A special value that is used to indicate an invalid or unknown index
michael@0 753 // into the array.
michael@0 754 enum {
michael@0 755 NoIndex = index_type(-1)
michael@0 756 };
michael@0 757
michael@0 758 using base_type::Length;
michael@0 759
michael@0 760 //
michael@0 761 // Finalization method
michael@0 762 //
michael@0 763
michael@0 764 ~nsTArray_Impl() { Clear(); }
michael@0 765
michael@0 766 //
michael@0 767 // Initialization methods
michael@0 768 //
michael@0 769
michael@0 770 nsTArray_Impl() {}
michael@0 771
michael@0 772 // Initialize this array and pre-allocate some number of elements.
michael@0 773 explicit nsTArray_Impl(size_type capacity) {
michael@0 774 SetCapacity(capacity);
michael@0 775 }
michael@0 776
michael@0 777 // The array's copy-constructor performs a 'deep' copy of the given array.
michael@0 778 // @param other The array object to copy.
michael@0 779 //
michael@0 780 // It's very important that we declare this method as taking |const
michael@0 781 // self_type&| as opposed to taking |const nsTArray_Impl<E, OtherAlloc>| for
michael@0 782 // an arbitrary OtherAlloc.
michael@0 783 //
michael@0 784 // If we don't declare a constructor taking |const self_type&|, C++ generates
michael@0 785 // a copy-constructor for this class which merely copies the object's
michael@0 786 // members, which is obviously wrong.
michael@0 787 //
michael@0 788 // You can pass an nsTArray_Impl<E, OtherAlloc> to this method because
michael@0 789 // nsTArray_Impl<E, X> can be cast to const nsTArray_Impl<E, Y>&. So the
michael@0 790 // effect on the API is the same as if we'd declared this method as taking
michael@0 791 // |const nsTArray_Impl<E, OtherAlloc>&|.
michael@0 792 explicit nsTArray_Impl(const self_type& other) {
michael@0 793 AppendElements(other);
michael@0 794 }
michael@0 795
michael@0 796 // Allow converting to a const array with a different kind of allocator,
michael@0 797 // Since the allocator doesn't matter for const arrays
michael@0 798 template<typename Allocator>
michael@0 799 operator const nsTArray_Impl<E, Allocator>&() const {
michael@0 800 return *reinterpret_cast<const nsTArray_Impl<E, Allocator>*>(this);
michael@0 801 }
michael@0 802 // And we have to do this for our subclasses too
michael@0 803 operator const nsTArray<E>&() const {
michael@0 804 return *reinterpret_cast<const InfallibleTArray<E>*>(this);
michael@0 805 }
michael@0 806 operator const FallibleTArray<E>&() const {
michael@0 807 return *reinterpret_cast<const FallibleTArray<E>*>(this);
michael@0 808 }
michael@0 809
michael@0 810 // The array's assignment operator performs a 'deep' copy of the given
michael@0 811 // array. It is optimized to reuse existing storage if possible.
michael@0 812 // @param other The array object to copy.
michael@0 813 self_type& operator=(const self_type& other) {
michael@0 814 ReplaceElementsAt(0, Length(), other.Elements(), other.Length());
michael@0 815 return *this;
michael@0 816 }
michael@0 817
michael@0 818 // Return true if this array has the same length and the same
michael@0 819 // elements as |other|.
michael@0 820 template<typename Allocator>
michael@0 821 bool operator==(const nsTArray_Impl<E, Allocator>& other) const {
michael@0 822 size_type len = Length();
michael@0 823 if (len != other.Length())
michael@0 824 return false;
michael@0 825
michael@0 826 // XXX std::equal would be as fast or faster here
michael@0 827 for (index_type i = 0; i < len; ++i)
michael@0 828 if (!(operator[](i) == other[i]))
michael@0 829 return false;
michael@0 830
michael@0 831 return true;
michael@0 832 }
michael@0 833
michael@0 834 // Return true if this array does not have the same length and the same
michael@0 835 // elements as |other|.
michael@0 836 bool operator!=(const self_type& other) const {
michael@0 837 return !operator==(other);
michael@0 838 }
michael@0 839
michael@0 840 template<typename Allocator>
michael@0 841 self_type& operator=(const nsTArray_Impl<E, Allocator>& other) {
michael@0 842 ReplaceElementsAt(0, Length(), other.Elements(), other.Length());
michael@0 843 return *this;
michael@0 844 }
michael@0 845
michael@0 846 // @return The amount of memory used by this nsTArray_Impl, excluding
michael@0 847 // sizeof(*this).
michael@0 848 size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
michael@0 849 if (this->UsesAutoArrayBuffer() || Hdr() == EmptyHdr())
michael@0 850 return 0;
michael@0 851 return mallocSizeOf(this->Hdr());
michael@0 852 }
michael@0 853
michael@0 854 // @return The amount of memory used by this nsTArray_Impl, including
michael@0 855 // sizeof(*this).
michael@0 856 size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
michael@0 857 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
michael@0 858 }
michael@0 859
michael@0 860 //
michael@0 861 // Accessor methods
michael@0 862 //
michael@0 863
michael@0 864 // This method provides direct access to the array elements.
michael@0 865 // @return A pointer to the first element of the array. If the array is
michael@0 866 // empty, then this pointer must not be dereferenced.
michael@0 867 elem_type* Elements() {
michael@0 868 return reinterpret_cast<elem_type *>(Hdr() + 1);
michael@0 869 }
michael@0 870
michael@0 871 // This method provides direct, readonly access to the array elements.
michael@0 872 // @return A pointer to the first element of the array. If the array is
michael@0 873 // empty, then this pointer must not be dereferenced.
michael@0 874 const elem_type* Elements() const {
michael@0 875 return reinterpret_cast<const elem_type *>(Hdr() + 1);
michael@0 876 }
michael@0 877
michael@0 878 // This method provides direct access to the i'th element of the array.
michael@0 879 // The given index must be within the array bounds.
michael@0 880 // @param i The index of an element in the array.
michael@0 881 // @return A reference to the i'th element of the array.
michael@0 882 elem_type& ElementAt(index_type i) {
michael@0 883 MOZ_ASSERT(i < Length(), "invalid array index");
michael@0 884 return Elements()[i];
michael@0 885 }
michael@0 886
michael@0 887 // This method provides direct, readonly access to the i'th element of the
michael@0 888 // array. The given index must be within the array bounds.
michael@0 889 // @param i The index of an element in the array.
michael@0 890 // @return A const reference to the i'th element of the array.
michael@0 891 const elem_type& ElementAt(index_type i) const {
michael@0 892 MOZ_ASSERT(i < Length(), "invalid array index");
michael@0 893 return Elements()[i];
michael@0 894 }
michael@0 895
michael@0 896 // This method provides direct access to the i'th element of the array in
michael@0 897 // a bounds safe manner. If the requested index is out of bounds the
michael@0 898 // provided default value is returned.
michael@0 899 // @param i The index of an element in the array.
michael@0 900 // @param def The value to return if the index is out of bounds.
michael@0 901 elem_type& SafeElementAt(index_type i, elem_type& def) {
michael@0 902 return i < Length() ? Elements()[i] : def;
michael@0 903 }
michael@0 904
michael@0 905 // This method provides direct access to the i'th element of the array in
michael@0 906 // a bounds safe manner. If the requested index is out of bounds the
michael@0 907 // provided default value is returned.
michael@0 908 // @param i The index of an element in the array.
michael@0 909 // @param def The value to return if the index is out of bounds.
michael@0 910 const elem_type& SafeElementAt(index_type i, const elem_type& def) const {
michael@0 911 return i < Length() ? Elements()[i] : def;
michael@0 912 }
michael@0 913
michael@0 914 // Shorthand for ElementAt(i)
michael@0 915 elem_type& operator[](index_type i) {
michael@0 916 return ElementAt(i);
michael@0 917 }
michael@0 918
michael@0 919 // Shorthand for ElementAt(i)
michael@0 920 const elem_type& operator[](index_type i) const {
michael@0 921 return ElementAt(i);
michael@0 922 }
michael@0 923
michael@0 924 // Shorthand for ElementAt(length - 1)
michael@0 925 elem_type& LastElement() {
michael@0 926 return ElementAt(Length() - 1);
michael@0 927 }
michael@0 928
michael@0 929 // Shorthand for ElementAt(length - 1)
michael@0 930 const elem_type& LastElement() const {
michael@0 931 return ElementAt(Length() - 1);
michael@0 932 }
michael@0 933
michael@0 934 // Shorthand for SafeElementAt(length - 1, def)
michael@0 935 elem_type& SafeLastElement(elem_type& def) {
michael@0 936 return SafeElementAt(Length() - 1, def);
michael@0 937 }
michael@0 938
michael@0 939 // Shorthand for SafeElementAt(length - 1, def)
michael@0 940 const elem_type& SafeLastElement(const elem_type& def) const {
michael@0 941 return SafeElementAt(Length() - 1, def);
michael@0 942 }
michael@0 943
michael@0 944 //
michael@0 945 // Search methods
michael@0 946 //
michael@0 947
michael@0 948 // This method searches for the first element in this array that is equal
michael@0 949 // to the given element.
michael@0 950 // @param item The item to search for.
michael@0 951 // @param comp The Comparator used to determine element equality.
michael@0 952 // @return true if the element was found.
michael@0 953 template<class Item, class Comparator>
michael@0 954 bool Contains(const Item& item, const Comparator& comp) const {
michael@0 955 return IndexOf(item, 0, comp) != NoIndex;
michael@0 956 }
michael@0 957
michael@0 958 // This method searches for the first element in this array that is equal
michael@0 959 // to the given element. This method assumes that 'operator==' is defined
michael@0 960 // for elem_type.
michael@0 961 // @param item The item to search for.
michael@0 962 // @return true if the element was found.
michael@0 963 template<class Item>
michael@0 964 bool Contains(const Item& item) const {
michael@0 965 return IndexOf(item) != NoIndex;
michael@0 966 }
michael@0 967
michael@0 968 // This method searches for the offset of the first element in this
michael@0 969 // array that is equal to the given element.
michael@0 970 // @param item The item to search for.
michael@0 971 // @param start The index to start from.
michael@0 972 // @param comp The Comparator used to determine element equality.
michael@0 973 // @return The index of the found element or NoIndex if not found.
michael@0 974 template<class Item, class Comparator>
michael@0 975 index_type IndexOf(const Item& item, index_type start,
michael@0 976 const Comparator& comp) const {
michael@0 977 const elem_type* iter = Elements() + start, *end = Elements() + Length();
michael@0 978 for (; iter != end; ++iter) {
michael@0 979 if (comp.Equals(*iter, item))
michael@0 980 return index_type(iter - Elements());
michael@0 981 }
michael@0 982 return NoIndex;
michael@0 983 }
michael@0 984
michael@0 985 // This method searches for the offset of the first element in this
michael@0 986 // array that is equal to the given element. This method assumes
michael@0 987 // that 'operator==' is defined for elem_type.
michael@0 988 // @param item The item to search for.
michael@0 989 // @param start The index to start from.
michael@0 990 // @return The index of the found element or NoIndex if not found.
michael@0 991 template<class Item>
michael@0 992 index_type IndexOf(const Item& item, index_type start = 0) const {
michael@0 993 return IndexOf(item, start, nsDefaultComparator<elem_type, Item>());
michael@0 994 }
michael@0 995
michael@0 996 // This method searches for the offset of the last element in this
michael@0 997 // array that is equal to the given element.
michael@0 998 // @param item The item to search for.
michael@0 999 // @param start The index to start from. If greater than or equal to the
michael@0 1000 // length of the array, then the entire array is searched.
michael@0 1001 // @param comp The Comparator used to determine element equality.
michael@0 1002 // @return The index of the found element or NoIndex if not found.
michael@0 1003 template<class Item, class Comparator>
michael@0 1004 index_type LastIndexOf(const Item& item, index_type start,
michael@0 1005 const Comparator& comp) const {
michael@0 1006 size_type endOffset = start >= Length() ? Length() : start + 1;
michael@0 1007 const elem_type* end = Elements() - 1, *iter = end + endOffset;
michael@0 1008 for (; iter != end; --iter) {
michael@0 1009 if (comp.Equals(*iter, item))
michael@0 1010 return index_type(iter - Elements());
michael@0 1011 }
michael@0 1012 return NoIndex;
michael@0 1013 }
michael@0 1014
michael@0 1015 // This method searches for the offset of the last element in this
michael@0 1016 // array that is equal to the given element. This method assumes
michael@0 1017 // that 'operator==' is defined for elem_type.
michael@0 1018 // @param item The item to search for.
michael@0 1019 // @param start The index to start from. If greater than or equal to the
michael@0 1020 // length of the array, then the entire array is searched.
michael@0 1021 // @return The index of the found element or NoIndex if not found.
michael@0 1022 template<class Item>
michael@0 1023 index_type LastIndexOf(const Item& item,
michael@0 1024 index_type start = NoIndex) const {
michael@0 1025 return LastIndexOf(item, start, nsDefaultComparator<elem_type, Item>());
michael@0 1026 }
michael@0 1027
michael@0 1028 // This method searches for the offset for the element in this array
michael@0 1029 // that is equal to the given element. The array is assumed to be sorted.
michael@0 1030 // @param item The item to search for.
michael@0 1031 // @param comp The Comparator used.
michael@0 1032 // @return The index of the found element or NoIndex if not found.
michael@0 1033 template<class Item, class Comparator>
michael@0 1034 index_type BinaryIndexOf(const Item& item, const Comparator& comp) const {
michael@0 1035 index_type low = 0, high = Length();
michael@0 1036 while (high > low) {
michael@0 1037 index_type mid = (high + low) >> 1;
michael@0 1038 if (comp.Equals(ElementAt(mid), item))
michael@0 1039 return mid;
michael@0 1040 if (comp.LessThan(ElementAt(mid), item))
michael@0 1041 low = mid + 1;
michael@0 1042 else
michael@0 1043 high = mid;
michael@0 1044 }
michael@0 1045 return NoIndex;
michael@0 1046 }
michael@0 1047
michael@0 1048 // This method searches for the offset for the element in this array
michael@0 1049 // that is equal to the given element. The array is assumed to be sorted.
michael@0 1050 // This method assumes that 'operator==' and 'operator<' are defined.
michael@0 1051 // @param item The item to search for.
michael@0 1052 // @return The index of the found element or NoIndex if not found.
michael@0 1053 template<class Item>
michael@0 1054 index_type BinaryIndexOf(const Item& item) const {
michael@0 1055 return BinaryIndexOf(item, nsDefaultComparator<elem_type, Item>());
michael@0 1056 }
michael@0 1057
michael@0 1058 //
michael@0 1059 // Mutation methods
michael@0 1060 //
michael@0 1061 // This method call the destructor on each element of the array, empties it,
michael@0 1062 // but does not shrink the array's capacity.
michael@0 1063 // See also SetLengthAndRetainStorage.
michael@0 1064 // Make sure to call Compact() if needed to avoid keeping a huge array
michael@0 1065 // around.
michael@0 1066 void ClearAndRetainStorage() {
michael@0 1067 if (base_type::mHdr == EmptyHdr()) {
michael@0 1068 return;
michael@0 1069 }
michael@0 1070
michael@0 1071 DestructRange(0, Length());
michael@0 1072 base_type::mHdr->mLength = 0;
michael@0 1073 }
michael@0 1074
michael@0 1075 // This method modifies the length of the array, but unlike SetLength
michael@0 1076 // it doesn't deallocate/reallocate the current internal storage.
michael@0 1077 // The new length MUST be shorter than or equal to the current capacity.
michael@0 1078 // If the new length is larger than the existing length of the array,
michael@0 1079 // then new elements will be constructed using elem_type's default
michael@0 1080 // constructor. If shorter, elements will be destructed and removed.
michael@0 1081 // See also ClearAndRetainStorage.
michael@0 1082 // @param newLen The desired length of this array.
michael@0 1083 void SetLengthAndRetainStorage(size_type newLen) {
michael@0 1084 MOZ_ASSERT(newLen <= base_type::Capacity());
michael@0 1085 size_type oldLen = Length();
michael@0 1086 if (newLen > oldLen) {
michael@0 1087 InsertElementsAt(oldLen, newLen - oldLen);
michael@0 1088 return;
michael@0 1089 }
michael@0 1090 if (newLen < oldLen) {
michael@0 1091 DestructRange(newLen, oldLen - newLen);
michael@0 1092 base_type::mHdr->mLength = newLen;
michael@0 1093 }
michael@0 1094 }
michael@0 1095
michael@0 1096 // This method replaces a range of elements in this array.
michael@0 1097 // @param start The starting index of the elements to replace.
michael@0 1098 // @param count The number of elements to replace. This may be zero to
michael@0 1099 // insert elements without removing any existing elements.
michael@0 1100 // @param array The values to copy into this array. Must be non-null,
michael@0 1101 // and these elements must not already exist in the array
michael@0 1102 // being modified.
michael@0 1103 // @param arrayLen The number of values to copy into this array.
michael@0 1104 // @return A pointer to the new elements in the array, or null if
michael@0 1105 // the operation failed due to insufficient memory.
michael@0 1106 template<class Item>
michael@0 1107 elem_type *ReplaceElementsAt(index_type start, size_type count,
michael@0 1108 const Item* array, size_type arrayLen) {
michael@0 1109 // Adjust memory allocation up-front to catch errors.
michael@0 1110 if (!Alloc::Successful(this->EnsureCapacity(Length() + arrayLen - count, sizeof(elem_type))))
michael@0 1111 return nullptr;
michael@0 1112 DestructRange(start, count);
michael@0 1113 this->ShiftData(start, count, arrayLen, sizeof(elem_type), MOZ_ALIGNOF(elem_type));
michael@0 1114 AssignRange(start, arrayLen, array);
michael@0 1115 return Elements() + start;
michael@0 1116 }
michael@0 1117
michael@0 1118 // A variation on the ReplaceElementsAt method defined above.
michael@0 1119 template<class Item>
michael@0 1120 elem_type *ReplaceElementsAt(index_type start, size_type count,
michael@0 1121 const nsTArray<Item>& array) {
michael@0 1122 return ReplaceElementsAt(start, count, array.Elements(), array.Length());
michael@0 1123 }
michael@0 1124
michael@0 1125 // A variation on the ReplaceElementsAt method defined above.
michael@0 1126 template<class Item>
michael@0 1127 elem_type *ReplaceElementsAt(index_type start, size_type count,
michael@0 1128 const Item& item) {
michael@0 1129 return ReplaceElementsAt(start, count, &item, 1);
michael@0 1130 }
michael@0 1131
michael@0 1132 // A variation on the ReplaceElementsAt method defined above.
michael@0 1133 template<class Item>
michael@0 1134 elem_type *ReplaceElementAt(index_type index, const Item& item) {
michael@0 1135 return ReplaceElementsAt(index, 1, &item, 1);
michael@0 1136 }
michael@0 1137
michael@0 1138 // A variation on the ReplaceElementsAt method defined above.
michael@0 1139 template<class Item>
michael@0 1140 elem_type *InsertElementsAt(index_type index, const Item* array,
michael@0 1141 size_type arrayLen) {
michael@0 1142 return ReplaceElementsAt(index, 0, array, arrayLen);
michael@0 1143 }
michael@0 1144
michael@0 1145 // A variation on the ReplaceElementsAt method defined above.
michael@0 1146 template<class Item, class Allocator>
michael@0 1147 elem_type *InsertElementsAt(index_type index, const nsTArray_Impl<Item, Allocator>& array) {
michael@0 1148 return ReplaceElementsAt(index, 0, array.Elements(), array.Length());
michael@0 1149 }
michael@0 1150
michael@0 1151 // A variation on the ReplaceElementsAt method defined above.
michael@0 1152 template<class Item>
michael@0 1153 elem_type *InsertElementAt(index_type index, const Item& item) {
michael@0 1154 return ReplaceElementsAt(index, 0, &item, 1);
michael@0 1155 }
michael@0 1156
michael@0 1157 // Insert a new element without copy-constructing. This is useful to avoid
michael@0 1158 // temporaries.
michael@0 1159 // @return A pointer to the newly inserted element, or null on OOM.
michael@0 1160 elem_type* InsertElementAt(index_type index) {
michael@0 1161 if (!Alloc::Successful(this->EnsureCapacity(Length() + 1, sizeof(elem_type))))
michael@0 1162 return nullptr;
michael@0 1163 this->ShiftData(index, 0, 1, sizeof(elem_type), MOZ_ALIGNOF(elem_type));
michael@0 1164 elem_type *elem = Elements() + index;
michael@0 1165 elem_traits::Construct(elem);
michael@0 1166 return elem;
michael@0 1167 }
michael@0 1168
michael@0 1169 // This method searches for the smallest index of an element that is strictly
michael@0 1170 // greater than |item|. If |item| is inserted at this index, the array will
michael@0 1171 // remain sorted and |item| would come after all elements that are equal to
michael@0 1172 // it. If |item| is greater than or equal to all elements in the array, the
michael@0 1173 // array length is returned.
michael@0 1174 //
michael@0 1175 // Note that consumers who want to know whether there are existing items equal
michael@0 1176 // to |item| in the array can just check that the return value here is > 0 and
michael@0 1177 // indexing into the previous slot gives something equal to |item|.
michael@0 1178 //
michael@0 1179 //
michael@0 1180 // @param item The item to search for.
michael@0 1181 // @param comp The Comparator used.
michael@0 1182 // @return The index of greatest element <= to |item|
michael@0 1183 // @precondition The array is sorted
michael@0 1184 template<class Item, class Comparator>
michael@0 1185 index_type
michael@0 1186 IndexOfFirstElementGt(const Item& item,
michael@0 1187 const Comparator& comp) const {
michael@0 1188 // invariant: low <= [idx] <= high
michael@0 1189 index_type low = 0, high = Length();
michael@0 1190 while (high > low) {
michael@0 1191 index_type mid = (high + low) >> 1;
michael@0 1192 // Comparators are not required to provide a LessThan(Item&, elem_type),
michael@0 1193 // so we can't do comp.LessThan(item, ElementAt(mid)).
michael@0 1194 if (comp.LessThan(ElementAt(mid), item) ||
michael@0 1195 comp.Equals(ElementAt(mid), item)) {
michael@0 1196 // item >= ElementAt(mid), so our desired index is at least mid+1.
michael@0 1197 low = mid + 1;
michael@0 1198 } else {
michael@0 1199 // item < ElementAt(mid). Our desired index is therefore at most mid.
michael@0 1200 high = mid;
michael@0 1201 }
michael@0 1202 }
michael@0 1203 MOZ_ASSERT(high == low);
michael@0 1204 return low;
michael@0 1205 }
michael@0 1206
michael@0 1207 // A variation on the IndexOfFirstElementGt method defined above.
michael@0 1208 template<class Item>
michael@0 1209 index_type
michael@0 1210 IndexOfFirstElementGt(const Item& item) const {
michael@0 1211 return IndexOfFirstElementGt(item, nsDefaultComparator<elem_type, Item>());
michael@0 1212 }
michael@0 1213
michael@0 1214 // Inserts |item| at such an index to guarantee that if the array
michael@0 1215 // was previously sorted, it will remain sorted after this
michael@0 1216 // insertion.
michael@0 1217 template<class Item, class Comparator>
michael@0 1218 elem_type *InsertElementSorted(const Item& item, const Comparator& comp) {
michael@0 1219 index_type index = IndexOfFirstElementGt(item, comp);
michael@0 1220 return InsertElementAt(index, item);
michael@0 1221 }
michael@0 1222
michael@0 1223 // A variation on the InsertElementSorted method defined above.
michael@0 1224 template<class Item>
michael@0 1225 elem_type *InsertElementSorted(const Item& item) {
michael@0 1226 return InsertElementSorted(item, nsDefaultComparator<elem_type, Item>());
michael@0 1227 }
michael@0 1228
michael@0 1229 // This method appends elements to the end of this array.
michael@0 1230 // @param array The elements to append to this array.
michael@0 1231 // @param arrayLen The number of elements to append to this array.
michael@0 1232 // @return A pointer to the new elements in the array, or null if
michael@0 1233 // the operation failed due to insufficient memory.
michael@0 1234 template<class Item>
michael@0 1235 elem_type *AppendElements(const Item* array, size_type arrayLen) {
michael@0 1236 if (!Alloc::Successful(this->EnsureCapacity(Length() + arrayLen, sizeof(elem_type))))
michael@0 1237 return nullptr;
michael@0 1238 index_type len = Length();
michael@0 1239 AssignRange(len, arrayLen, array);
michael@0 1240 this->IncrementLength(arrayLen);
michael@0 1241 return Elements() + len;
michael@0 1242 }
michael@0 1243
michael@0 1244 // A variation on the AppendElements method defined above.
michael@0 1245 template<class Item, class Allocator>
michael@0 1246 elem_type *AppendElements(const nsTArray_Impl<Item, Allocator>& array) {
michael@0 1247 return AppendElements(array.Elements(), array.Length());
michael@0 1248 }
michael@0 1249
michael@0 1250 // A variation on the AppendElements method defined above.
michael@0 1251 template<class Item>
michael@0 1252 elem_type *AppendElement(const Item& item) {
michael@0 1253 return AppendElements(&item, 1);
michael@0 1254 }
michael@0 1255
michael@0 1256 // Append new elements without copy-constructing. This is useful to avoid
michael@0 1257 // temporaries.
michael@0 1258 // @return A pointer to the newly appended elements, or null on OOM.
michael@0 1259 elem_type *AppendElements(size_type count) {
michael@0 1260 if (!Alloc::Successful(this->EnsureCapacity(Length() + count, sizeof(elem_type))))
michael@0 1261 return nullptr;
michael@0 1262 elem_type *elems = Elements() + Length();
michael@0 1263 size_type i;
michael@0 1264 for (i = 0; i < count; ++i) {
michael@0 1265 elem_traits::Construct(elems + i);
michael@0 1266 }
michael@0 1267 this->IncrementLength(count);
michael@0 1268 return elems;
michael@0 1269 }
michael@0 1270
michael@0 1271 // Append a new element without copy-constructing. This is useful to avoid
michael@0 1272 // temporaries.
michael@0 1273 // @return A pointer to the newly appended element, or null on OOM.
michael@0 1274 elem_type *AppendElement() {
michael@0 1275 return AppendElements(1);
michael@0 1276 }
michael@0 1277
michael@0 1278 // Move all elements from another array to the end of this array without
michael@0 1279 // calling copy constructors or destructors.
michael@0 1280 // @return A pointer to the newly appended elements, or null on OOM.
michael@0 1281 template<class Item, class Allocator>
michael@0 1282 elem_type *MoveElementsFrom(nsTArray_Impl<Item, Allocator>& array) {
michael@0 1283 MOZ_ASSERT(&array != this, "argument must be different array");
michael@0 1284 index_type len = Length();
michael@0 1285 index_type otherLen = array.Length();
michael@0 1286 if (!Alloc::Successful(this->EnsureCapacity(len + otherLen, sizeof(elem_type))))
michael@0 1287 return nullptr;
michael@0 1288 copy_type::CopyElements(Elements() + len, array.Elements(), otherLen, sizeof(elem_type));
michael@0 1289 this->IncrementLength(otherLen);
michael@0 1290 array.ShiftData(0, otherLen, 0, sizeof(elem_type), MOZ_ALIGNOF(elem_type));
michael@0 1291 return Elements() + len;
michael@0 1292 }
michael@0 1293
michael@0 1294 // This method removes a range of elements from this array.
michael@0 1295 // @param start The starting index of the elements to remove.
michael@0 1296 // @param count The number of elements to remove.
michael@0 1297 void RemoveElementsAt(index_type start, size_type count) {
michael@0 1298 MOZ_ASSERT(count == 0 || start < Length(), "Invalid start index");
michael@0 1299 MOZ_ASSERT(start + count <= Length(), "Invalid length");
michael@0 1300 // Check that the previous assert didn't overflow
michael@0 1301 MOZ_ASSERT(start <= start + count, "Start index plus length overflows");
michael@0 1302 DestructRange(start, count);
michael@0 1303 this->ShiftData(start, count, 0, sizeof(elem_type), MOZ_ALIGNOF(elem_type));
michael@0 1304 }
michael@0 1305
michael@0 1306 // A variation on the RemoveElementsAt method defined above.
michael@0 1307 void RemoveElementAt(index_type index) {
michael@0 1308 RemoveElementsAt(index, 1);
michael@0 1309 }
michael@0 1310
michael@0 1311 // A variation on the RemoveElementsAt method defined above.
michael@0 1312 void Clear() {
michael@0 1313 RemoveElementsAt(0, Length());
michael@0 1314 }
michael@0 1315
michael@0 1316 // This helper function combines IndexOf with RemoveElementAt to "search
michael@0 1317 // and destroy" the first element that is equal to the given element.
michael@0 1318 // @param item The item to search for.
michael@0 1319 // @param comp The Comparator used to determine element equality.
michael@0 1320 // @return true if the element was found
michael@0 1321 template<class Item, class Comparator>
michael@0 1322 bool RemoveElement(const Item& item, const Comparator& comp) {
michael@0 1323 index_type i = IndexOf(item, 0, comp);
michael@0 1324 if (i == NoIndex)
michael@0 1325 return false;
michael@0 1326
michael@0 1327 RemoveElementAt(i);
michael@0 1328 return true;
michael@0 1329 }
michael@0 1330
michael@0 1331 // A variation on the RemoveElement method defined above that assumes
michael@0 1332 // that 'operator==' is defined for elem_type.
michael@0 1333 template<class Item>
michael@0 1334 bool RemoveElement(const Item& item) {
michael@0 1335 return RemoveElement(item, nsDefaultComparator<elem_type, Item>());
michael@0 1336 }
michael@0 1337
michael@0 1338 // This helper function combines IndexOfFirstElementGt with
michael@0 1339 // RemoveElementAt to "search and destroy" the last element that
michael@0 1340 // is equal to the given element.
michael@0 1341 // @param item The item to search for.
michael@0 1342 // @param comp The Comparator used to determine element equality.
michael@0 1343 // @return true if the element was found
michael@0 1344 template<class Item, class Comparator>
michael@0 1345 bool RemoveElementSorted(const Item& item, const Comparator& comp) {
michael@0 1346 index_type index = IndexOfFirstElementGt(item, comp);
michael@0 1347 if (index > 0 && comp.Equals(ElementAt(index - 1), item)) {
michael@0 1348 RemoveElementAt(index - 1);
michael@0 1349 return true;
michael@0 1350 }
michael@0 1351 return false;
michael@0 1352 }
michael@0 1353
michael@0 1354 // A variation on the RemoveElementSorted method defined above.
michael@0 1355 template<class Item>
michael@0 1356 bool RemoveElementSorted(const Item& item) {
michael@0 1357 return RemoveElementSorted(item, nsDefaultComparator<elem_type, Item>());
michael@0 1358 }
michael@0 1359
michael@0 1360 // This method causes the elements contained in this array and the given
michael@0 1361 // array to be swapped.
michael@0 1362 template<class Allocator>
michael@0 1363 typename Alloc::ResultType
michael@0 1364 SwapElements(nsTArray_Impl<E, Allocator>& other) {
michael@0 1365 return Alloc::Result(this->SwapArrayElements(other, sizeof(elem_type),
michael@0 1366 MOZ_ALIGNOF(elem_type)));
michael@0 1367 }
michael@0 1368
michael@0 1369 //
michael@0 1370 // Allocation
michael@0 1371 //
michael@0 1372
michael@0 1373 // This method may increase the capacity of this array object by the
michael@0 1374 // specified amount. This method may be called in advance of several
michael@0 1375 // AppendElement operations to minimize heap re-allocations. This method
michael@0 1376 // will not reduce the number of elements in this array.
michael@0 1377 // @param capacity The desired capacity of this array.
michael@0 1378 // @return True if the operation succeeded; false if we ran out of memory
michael@0 1379 typename Alloc::ResultType SetCapacity(size_type capacity) {
michael@0 1380 return Alloc::Result(this->EnsureCapacity(capacity, sizeof(elem_type)));
michael@0 1381 }
michael@0 1382
michael@0 1383 // This method modifies the length of the array. If the new length is
michael@0 1384 // larger than the existing length of the array, then new elements will be
michael@0 1385 // constructed using elem_type's default constructor. Otherwise, this call
michael@0 1386 // removes elements from the array (see also RemoveElementsAt).
michael@0 1387 // @param newLen The desired length of this array.
michael@0 1388 // @return True if the operation succeeded; false otherwise.
michael@0 1389 // See also TruncateLength if the new length is guaranteed to be
michael@0 1390 // smaller than the old.
michael@0 1391 typename Alloc::ResultType SetLength(size_type newLen) {
michael@0 1392 size_type oldLen = Length();
michael@0 1393 if (newLen > oldLen) {
michael@0 1394 return Alloc::ConvertBoolToResultType(InsertElementsAt(oldLen, newLen - oldLen) != nullptr);
michael@0 1395 }
michael@0 1396
michael@0 1397 TruncateLength(newLen);
michael@0 1398 return Alloc::ConvertBoolToResultType(true);
michael@0 1399 }
michael@0 1400
michael@0 1401 // This method modifies the length of the array, but may only be
michael@0 1402 // called when the new length is shorter than the old. It can
michael@0 1403 // therefore be called when elem_type has no default constructor,
michael@0 1404 // unlike SetLength. It removes elements from the array (see also
michael@0 1405 // RemoveElementsAt).
michael@0 1406 // @param newLen The desired length of this array.
michael@0 1407 void TruncateLength(size_type newLen) {
michael@0 1408 size_type oldLen = Length();
michael@0 1409 NS_ABORT_IF_FALSE(newLen <= oldLen,
michael@0 1410 "caller should use SetLength instead");
michael@0 1411 RemoveElementsAt(newLen, oldLen - newLen);
michael@0 1412 }
michael@0 1413
michael@0 1414 // This method ensures that the array has length at least the given
michael@0 1415 // length. If the current length is shorter than the given length,
michael@0 1416 // then new elements will be constructed using elem_type's default
michael@0 1417 // constructor.
michael@0 1418 // @param minLen The desired minimum length of this array.
michael@0 1419 // @return True if the operation succeeded; false otherwise.
michael@0 1420 typename Alloc::ResultType EnsureLengthAtLeast(size_type minLen) {
michael@0 1421 size_type oldLen = Length();
michael@0 1422 if (minLen > oldLen) {
michael@0 1423 return Alloc::ConvertBoolToResultType(!!InsertElementsAt(oldLen, minLen - oldLen));
michael@0 1424 }
michael@0 1425 return Alloc::ConvertBoolToResultType(true);
michael@0 1426 }
michael@0 1427
michael@0 1428 // This method inserts elements into the array, constructing
michael@0 1429 // them using elem_type's default constructor.
michael@0 1430 // @param index the place to insert the new elements. This must be no
michael@0 1431 // greater than the current length of the array.
michael@0 1432 // @param count the number of elements to insert
michael@0 1433 elem_type *InsertElementsAt(index_type index, size_type count) {
michael@0 1434 if (!base_type::InsertSlotsAt(index, count, sizeof(elem_type), MOZ_ALIGNOF(elem_type))) {
michael@0 1435 return nullptr;
michael@0 1436 }
michael@0 1437
michael@0 1438 // Initialize the extra array elements
michael@0 1439 elem_type *iter = Elements() + index, *end = iter + count;
michael@0 1440 for (; iter != end; ++iter) {
michael@0 1441 elem_traits::Construct(iter);
michael@0 1442 }
michael@0 1443
michael@0 1444 return Elements() + index;
michael@0 1445 }
michael@0 1446
michael@0 1447 // This method inserts elements into the array, constructing them
michael@0 1448 // elem_type's copy constructor (or whatever one-arg constructor
michael@0 1449 // happens to match the Item type).
michael@0 1450 // @param index the place to insert the new elements. This must be no
michael@0 1451 // greater than the current length of the array.
michael@0 1452 // @param count the number of elements to insert.
michael@0 1453 // @param item the value to use when constructing the new elements.
michael@0 1454 template<class Item>
michael@0 1455 elem_type *InsertElementsAt(index_type index, size_type count,
michael@0 1456 const Item& item) {
michael@0 1457 if (!base_type::InsertSlotsAt(index, count, sizeof(elem_type), MOZ_ALIGNOF(elem_type))) {
michael@0 1458 return nullptr;
michael@0 1459 }
michael@0 1460
michael@0 1461 // Initialize the extra array elements
michael@0 1462 elem_type *iter = Elements() + index, *end = iter + count;
michael@0 1463 for (; iter != end; ++iter) {
michael@0 1464 elem_traits::Construct(iter, item);
michael@0 1465 }
michael@0 1466
michael@0 1467 return Elements() + index;
michael@0 1468 }
michael@0 1469
michael@0 1470 // This method may be called to minimize the memory used by this array.
michael@0 1471 void Compact() {
michael@0 1472 ShrinkCapacity(sizeof(elem_type), MOZ_ALIGNOF(elem_type));
michael@0 1473 }
michael@0 1474
michael@0 1475 //
michael@0 1476 // Sorting
michael@0 1477 //
michael@0 1478
michael@0 1479 // This function is meant to be used with the NS_QuickSort function. It
michael@0 1480 // maps the callback API expected by NS_QuickSort to the Comparator API
michael@0 1481 // used by nsTArray_Impl. See nsTArray_Impl::Sort.
michael@0 1482 template<class Comparator>
michael@0 1483 static int Compare(const void* e1, const void* e2, void *data) {
michael@0 1484 const Comparator* c = reinterpret_cast<const Comparator*>(data);
michael@0 1485 const elem_type* a = static_cast<const elem_type*>(e1);
michael@0 1486 const elem_type* b = static_cast<const elem_type*>(e2);
michael@0 1487 return c->LessThan(*a, *b) ? -1 : (c->Equals(*a, *b) ? 0 : 1);
michael@0 1488 }
michael@0 1489
michael@0 1490 // This method sorts the elements of the array. It uses the LessThan
michael@0 1491 // method defined on the given Comparator object to collate elements.
michael@0 1492 // @param comp The Comparator used to collate elements.
michael@0 1493 template<class Comparator>
michael@0 1494 void Sort(const Comparator& comp) {
michael@0 1495 NS_QuickSort(Elements(), Length(), sizeof(elem_type),
michael@0 1496 Compare<Comparator>, const_cast<Comparator*>(&comp));
michael@0 1497 }
michael@0 1498
michael@0 1499 // A variation on the Sort method defined above that assumes that
michael@0 1500 // 'operator<' is defined for elem_type.
michael@0 1501 void Sort() {
michael@0 1502 Sort(nsDefaultComparator<elem_type, elem_type>());
michael@0 1503 }
michael@0 1504
michael@0 1505 //
michael@0 1506 // Binary Heap
michael@0 1507 //
michael@0 1508
michael@0 1509 // Sorts the array into a binary heap.
michael@0 1510 // @param comp The Comparator used to create the heap
michael@0 1511 template<class Comparator>
michael@0 1512 void MakeHeap(const Comparator& comp) {
michael@0 1513 if (!Length()) {
michael@0 1514 return;
michael@0 1515 }
michael@0 1516 index_type index = (Length() - 1) / 2;
michael@0 1517 do {
michael@0 1518 SiftDown(index, comp);
michael@0 1519 } while (index--);
michael@0 1520 }
michael@0 1521
michael@0 1522 // A variation on the MakeHeap method defined above.
michael@0 1523 void MakeHeap() {
michael@0 1524 MakeHeap(nsDefaultComparator<elem_type, elem_type>());
michael@0 1525 }
michael@0 1526
michael@0 1527 // Adds an element to the heap
michael@0 1528 // @param item The item to add
michael@0 1529 // @param comp The Comparator used to sift-up the item
michael@0 1530 template<class Item, class Comparator>
michael@0 1531 elem_type *PushHeap(const Item& item, const Comparator& comp) {
michael@0 1532 if (!base_type::InsertSlotsAt(Length(), 1, sizeof(elem_type), MOZ_ALIGNOF(elem_type))) {
michael@0 1533 return nullptr;
michael@0 1534 }
michael@0 1535 // Sift up the new node
michael@0 1536 elem_type *elem = Elements();
michael@0 1537 index_type index = Length() - 1;
michael@0 1538 index_type parent_index = (index - 1) / 2;
michael@0 1539 while (index && comp.LessThan(elem[parent_index], item)) {
michael@0 1540 elem[index] = elem[parent_index];
michael@0 1541 index = parent_index;
michael@0 1542 parent_index = (index - 1) / 2;
michael@0 1543 }
michael@0 1544 elem[index] = item;
michael@0 1545 return &elem[index];
michael@0 1546 }
michael@0 1547
michael@0 1548 // A variation on the PushHeap method defined above.
michael@0 1549 template<class Item>
michael@0 1550 elem_type *PushHeap(const Item& item) {
michael@0 1551 return PushHeap(item, nsDefaultComparator<elem_type, Item>());
michael@0 1552 }
michael@0 1553
michael@0 1554 // Delete the root of the heap and restore the heap
michael@0 1555 // @param comp The Comparator used to restore the heap
michael@0 1556 template<class Comparator>
michael@0 1557 void PopHeap(const Comparator& comp) {
michael@0 1558 if (!Length()) {
michael@0 1559 return;
michael@0 1560 }
michael@0 1561 index_type last_index = Length() - 1;
michael@0 1562 elem_type *elem = Elements();
michael@0 1563 elem[0] = elem[last_index];
michael@0 1564 TruncateLength(last_index);
michael@0 1565 if (Length()) {
michael@0 1566 SiftDown(0, comp);
michael@0 1567 }
michael@0 1568 }
michael@0 1569
michael@0 1570 // A variation on the PopHeap method defined above.
michael@0 1571 void PopHeap() {
michael@0 1572 PopHeap(nsDefaultComparator<elem_type, elem_type>());
michael@0 1573 }
michael@0 1574
michael@0 1575 protected:
michael@0 1576 using base_type::Hdr;
michael@0 1577 using base_type::ShrinkCapacity;
michael@0 1578
michael@0 1579 // This method invokes elem_type's destructor on a range of elements.
michael@0 1580 // @param start The index of the first element to destroy.
michael@0 1581 // @param count The number of elements to destroy.
michael@0 1582 void DestructRange(index_type start, size_type count) {
michael@0 1583 elem_type *iter = Elements() + start, *end = iter + count;
michael@0 1584 for (; iter != end; ++iter) {
michael@0 1585 elem_traits::Destruct(iter);
michael@0 1586 }
michael@0 1587 }
michael@0 1588
michael@0 1589 // This method invokes elem_type's copy-constructor on a range of elements.
michael@0 1590 // @param start The index of the first element to construct.
michael@0 1591 // @param count The number of elements to construct.
michael@0 1592 // @param values The array of elements to copy.
michael@0 1593 template<class Item>
michael@0 1594 void AssignRange(index_type start, size_type count,
michael@0 1595 const Item *values) {
michael@0 1596 AssignRangeAlgorithm<mozilla::IsPod<Item>::value,
michael@0 1597 mozilla::IsSame<Item, elem_type>::value>
michael@0 1598 ::implementation(Elements(), start, count, values);
michael@0 1599 }
michael@0 1600
michael@0 1601 // This method sifts an item down to its proper place in a binary heap
michael@0 1602 // @param index The index of the node to start sifting down from
michael@0 1603 // @param comp The Comparator used to sift down
michael@0 1604 template<class Comparator>
michael@0 1605 void SiftDown(index_type index, const Comparator& comp) {
michael@0 1606 elem_type *elem = Elements();
michael@0 1607 elem_type item = elem[index];
michael@0 1608 index_type end = Length() - 1;
michael@0 1609 while ((index * 2) < end) {
michael@0 1610 const index_type left = (index * 2) + 1;
michael@0 1611 const index_type right = (index * 2) + 2;
michael@0 1612 const index_type parent_index = index;
michael@0 1613 if (comp.LessThan(item, elem[left])) {
michael@0 1614 if (left < end &&
michael@0 1615 comp.LessThan(elem[left], elem[right])) {
michael@0 1616 index = right;
michael@0 1617 } else {
michael@0 1618 index = left;
michael@0 1619 }
michael@0 1620 } else if (left < end &&
michael@0 1621 comp.LessThan(item, elem[right])) {
michael@0 1622 index = right;
michael@0 1623 } else {
michael@0 1624 break;
michael@0 1625 }
michael@0 1626 elem[parent_index] = elem[index];
michael@0 1627 }
michael@0 1628 elem[index] = item;
michael@0 1629 }
michael@0 1630 };
michael@0 1631
michael@0 1632 template <typename E, typename Alloc>
michael@0 1633 inline void
michael@0 1634 ImplCycleCollectionUnlink(nsTArray_Impl<E, Alloc>& aField)
michael@0 1635 {
michael@0 1636 aField.Clear();
michael@0 1637 }
michael@0 1638
michael@0 1639 template <typename E, typename Alloc>
michael@0 1640 inline void
michael@0 1641 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
michael@0 1642 nsTArray_Impl<E, Alloc>& aField,
michael@0 1643 const char* aName,
michael@0 1644 uint32_t aFlags = 0)
michael@0 1645 {
michael@0 1646 aFlags |= CycleCollectionEdgeNameArrayFlag;
michael@0 1647 uint32_t length = aField.Length();
michael@0 1648 for (uint32_t i = 0; i < length; ++i) {
michael@0 1649 ImplCycleCollectionTraverse(aCallback, aField[i], aName, aFlags);
michael@0 1650 }
michael@0 1651 }
michael@0 1652
michael@0 1653 //
michael@0 1654 // nsTArray is an infallible vector class. See the comment at the top of this
michael@0 1655 // file for more details.
michael@0 1656 //
michael@0 1657 template <class E>
michael@0 1658 class nsTArray : public nsTArray_Impl<E, nsTArrayInfallibleAllocator>
michael@0 1659 {
michael@0 1660 public:
michael@0 1661 typedef nsTArray_Impl<E, nsTArrayInfallibleAllocator> base_type;
michael@0 1662 typedef nsTArray<E> self_type;
michael@0 1663 typedef typename base_type::size_type size_type;
michael@0 1664
michael@0 1665 nsTArray() {}
michael@0 1666 explicit nsTArray(size_type capacity) : base_type(capacity) {}
michael@0 1667 explicit nsTArray(const nsTArray& other) : base_type(other) {}
michael@0 1668
michael@0 1669 template<class Allocator>
michael@0 1670 explicit nsTArray(const nsTArray_Impl<E, Allocator>& other) : base_type(other) {}
michael@0 1671 };
michael@0 1672
michael@0 1673 //
michael@0 1674 // FallibleTArray is a fallible vector class.
michael@0 1675 //
michael@0 1676 template <class E>
michael@0 1677 class FallibleTArray : public nsTArray_Impl<E, nsTArrayFallibleAllocator>
michael@0 1678 {
michael@0 1679 public:
michael@0 1680 typedef nsTArray_Impl<E, nsTArrayFallibleAllocator> base_type;
michael@0 1681 typedef FallibleTArray<E> self_type;
michael@0 1682 typedef typename base_type::size_type size_type;
michael@0 1683
michael@0 1684 FallibleTArray() {}
michael@0 1685 explicit FallibleTArray(size_type capacity) : base_type(capacity) {}
michael@0 1686 explicit FallibleTArray(const FallibleTArray<E>& other) : base_type(other) {}
michael@0 1687
michael@0 1688 template<class Allocator>
michael@0 1689 explicit FallibleTArray(const nsTArray_Impl<E, Allocator>& other) : base_type(other) {}
michael@0 1690 };
michael@0 1691
michael@0 1692 //
michael@0 1693 // nsAutoArrayBase is a base class for AutoFallibleTArray and nsAutoTArray.
michael@0 1694 // You shouldn't use this class directly.
michael@0 1695 //
michael@0 1696 template <class TArrayBase, uint32_t N>
michael@0 1697 class nsAutoArrayBase : public TArrayBase
michael@0 1698 {
michael@0 1699 public:
michael@0 1700 typedef nsAutoArrayBase<TArrayBase, N> self_type;
michael@0 1701 typedef TArrayBase base_type;
michael@0 1702 typedef typename base_type::Header Header;
michael@0 1703 typedef typename base_type::elem_type elem_type;
michael@0 1704
michael@0 1705 template<typename Allocator>
michael@0 1706 self_type& operator=(const nsTArray_Impl<elem_type, Allocator>& other) {
michael@0 1707 base_type::operator=(other);
michael@0 1708 return *this;
michael@0 1709 }
michael@0 1710
michael@0 1711 protected:
michael@0 1712 nsAutoArrayBase() {
michael@0 1713 Init();
michael@0 1714 }
michael@0 1715
michael@0 1716 // We need this constructor because nsAutoTArray and friends all have
michael@0 1717 // implicit copy-constructors. If we don't have this method, those
michael@0 1718 // copy-constructors will call nsAutoArrayBase's implicit copy-constructor,
michael@0 1719 // which won't call Init() and set up the auto buffer!
michael@0 1720 nsAutoArrayBase(const self_type &aOther) {
michael@0 1721 Init();
michael@0 1722 this->AppendElements(aOther);
michael@0 1723 }
michael@0 1724
michael@0 1725 private:
michael@0 1726 // nsTArray_base casts itself as an nsAutoArrayBase in order to get a pointer
michael@0 1727 // to mAutoBuf.
michael@0 1728 template<class Allocator, class Copier>
michael@0 1729 friend class nsTArray_base;
michael@0 1730
michael@0 1731 void Init() {
michael@0 1732 static_assert(MOZ_ALIGNOF(elem_type) <= 8,
michael@0 1733 "can't handle alignments greater than 8, "
michael@0 1734 "see nsTArray_base::UsesAutoArrayBuffer()");
michael@0 1735 // Temporary work around for VS2012 RC compiler crash
michael@0 1736 Header** phdr = base_type::PtrToHdr();
michael@0 1737 *phdr = reinterpret_cast<Header*>(&mAutoBuf);
michael@0 1738 (*phdr)->mLength = 0;
michael@0 1739 (*phdr)->mCapacity = N;
michael@0 1740 (*phdr)->mIsAutoArray = 1;
michael@0 1741
michael@0 1742 MOZ_ASSERT(base_type::GetAutoArrayBuffer(MOZ_ALIGNOF(elem_type)) ==
michael@0 1743 reinterpret_cast<Header*>(&mAutoBuf),
michael@0 1744 "GetAutoArrayBuffer needs to be fixed");
michael@0 1745 }
michael@0 1746
michael@0 1747 // Declare mAutoBuf aligned to the maximum of the header's alignment and
michael@0 1748 // elem_type's alignment. We need to use a union rather than
michael@0 1749 // MOZ_ALIGNED_DECL because GCC is picky about what goes into
michael@0 1750 // __attribute__((aligned(foo))).
michael@0 1751 union {
michael@0 1752 char mAutoBuf[sizeof(nsTArrayHeader) + N * sizeof(elem_type)];
michael@0 1753 // Do the max operation inline to ensure that it is a compile-time constant.
michael@0 1754 mozilla::AlignedElem<(MOZ_ALIGNOF(Header) > MOZ_ALIGNOF(elem_type))
michael@0 1755 ? MOZ_ALIGNOF(Header) : MOZ_ALIGNOF(elem_type)> mAlign;
michael@0 1756 };
michael@0 1757 };
michael@0 1758
michael@0 1759 //
michael@0 1760 // nsAutoTArray<E, N> is an infallible vector class with N elements of inline
michael@0 1761 // storage. If you try to store more than N elements inside an
michael@0 1762 // nsAutoTArray<E, N>, we'll call malloc() and store them all on the heap.
michael@0 1763 //
michael@0 1764 // Note that you can cast an nsAutoTArray<E, N> to
michael@0 1765 // |const AutoFallibleTArray<E, N>&|.
michael@0 1766 //
michael@0 1767 template<class E, uint32_t N>
michael@0 1768 class nsAutoTArray : public nsAutoArrayBase<nsTArray<E>, N>
michael@0 1769 {
michael@0 1770 typedef nsAutoTArray<E, N> self_type;
michael@0 1771 typedef nsAutoArrayBase<nsTArray<E>, N> Base;
michael@0 1772
michael@0 1773 public:
michael@0 1774 nsAutoTArray() {}
michael@0 1775
michael@0 1776 template<typename Allocator>
michael@0 1777 explicit nsAutoTArray(const nsTArray_Impl<E, Allocator>& other) {
michael@0 1778 Base::AppendElements(other);
michael@0 1779 }
michael@0 1780
michael@0 1781 operator const AutoFallibleTArray<E, N>&() const {
michael@0 1782 return *reinterpret_cast<const AutoFallibleTArray<E, N>*>(this);
michael@0 1783 }
michael@0 1784 };
michael@0 1785
michael@0 1786 //
michael@0 1787 // AutoFallibleTArray<E, N> is a fallible vector class with N elements of
michael@0 1788 // inline storage.
michael@0 1789 //
michael@0 1790 template<class E, uint32_t N>
michael@0 1791 class AutoFallibleTArray : public nsAutoArrayBase<FallibleTArray<E>, N>
michael@0 1792 {
michael@0 1793 typedef AutoFallibleTArray<E, N> self_type;
michael@0 1794 typedef nsAutoArrayBase<FallibleTArray<E>, N> Base;
michael@0 1795
michael@0 1796 public:
michael@0 1797 AutoFallibleTArray() {}
michael@0 1798
michael@0 1799 template<typename Allocator>
michael@0 1800 explicit AutoFallibleTArray(const nsTArray_Impl<E, Allocator>& other) {
michael@0 1801 Base::AppendElements(other);
michael@0 1802 }
michael@0 1803
michael@0 1804 operator const nsAutoTArray<E, N>&() const {
michael@0 1805 return *reinterpret_cast<const nsAutoTArray<E, N>*>(this);
michael@0 1806 }
michael@0 1807 };
michael@0 1808
michael@0 1809 // Assert that nsAutoTArray doesn't have any extra padding inside.
michael@0 1810 //
michael@0 1811 // It's important that the data stored in this auto array takes up a multiple of
michael@0 1812 // 8 bytes; e.g. nsAutoTArray<uint32_t, 1> wouldn't work. Since nsAutoTArray
michael@0 1813 // contains a pointer, its size must be a multiple of alignof(void*). (This is
michael@0 1814 // because any type may be placed into an array, and there's no padding between
michael@0 1815 // elements of an array.) The compiler pads the end of the structure to
michael@0 1816 // enforce this rule.
michael@0 1817 //
michael@0 1818 // If we used nsAutoTArray<uint32_t, 1> below, this assertion would fail on a
michael@0 1819 // 64-bit system, where the compiler inserts 4 bytes of padding at the end of
michael@0 1820 // the auto array to make its size a multiple of alignof(void*) == 8 bytes.
michael@0 1821
michael@0 1822 static_assert(sizeof(nsAutoTArray<uint32_t, 2>) ==
michael@0 1823 sizeof(void*) + sizeof(nsTArrayHeader) + sizeof(uint32_t) * 2,
michael@0 1824 "nsAutoTArray shouldn't contain any extra padding, "
michael@0 1825 "see the comment");
michael@0 1826
michael@0 1827 // Definitions of nsTArray_Impl methods
michael@0 1828 #include "nsTArray-inl.h"
michael@0 1829
michael@0 1830 #endif // nsTArray_h__

mercurial