1 /* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SkTArray_DEFINED 9 #define SkTArray_DEFINED 10 11 #include "../private/SkTLogic.h" 12 #include "../private/SkTemplates.h" 13 #include "SkTypes.h" 14 15 #include <new> 16 #include <utility> 17 18 /** When MEM_COPY is true T will be bit copied when moved. 19 When MEM_COPY is false, T will be copy constructed / destructed. 20 In all cases T will be default-initialized on allocation, 21 and its destructor will be called from this object's destructor. 22 */ 23 template <typename T, bool MEM_COPY = false> class SkTArray { 24 public: 25 /** 26 * Creates an empty array with no initial storage 27 */ SkTArray()28 SkTArray() { 29 fCount = 0; 30 fReserveCount = gMIN_ALLOC_COUNT; 31 fAllocCount = 0; 32 fMemArray = NULL; 33 fPreAllocMemArray = NULL; 34 } 35 36 /** 37 * Creates an empty array that will preallocate space for reserveCount 38 * elements. 39 */ SkTArray(int reserveCount)40 explicit SkTArray(int reserveCount) { 41 this->init(NULL, 0, NULL, reserveCount); 42 } 43 44 /** 45 * Copies one array to another. The new array will be heap allocated. 46 */ SkTArray(const SkTArray & array)47 explicit SkTArray(const SkTArray& array) { 48 this->init(array.fItemArray, array.fCount, NULL, 0); 49 } 50 51 /** 52 * Creates a SkTArray by copying contents of a standard C array. The new 53 * array will be heap allocated. Be careful not to use this constructor 54 * when you really want the (void*, int) version. 55 */ SkTArray(const T * array,int count)56 SkTArray(const T* array, int count) { 57 this->init(array, count, NULL, 0); 58 } 59 60 /** 61 * assign copy of array to this 62 */ 63 SkTArray& operator =(const SkTArray& array) { 64 for (int i = 0; i < fCount; ++i) { 65 fItemArray[i].~T(); 66 } 67 fCount = 0; 68 this->checkRealloc((int)array.count()); 69 fCount = array.count(); 70 this->copy(static_cast<const T*>(array.fMemArray)); 71 return *this; 72 } 73 ~SkTArray()74 ~SkTArray() { 75 for (int i = 0; i < fCount; ++i) { 76 fItemArray[i].~T(); 77 } 78 if (fMemArray != fPreAllocMemArray) { 79 sk_free(fMemArray); 80 } 81 } 82 83 /** 84 * Resets to count() == 0 85 */ reset()86 void reset() { this->pop_back_n(fCount); } 87 88 /** 89 * Resets to count() = n newly constructed T objects. 90 */ reset(int n)91 void reset(int n) { 92 SkASSERT(n >= 0); 93 for (int i = 0; i < fCount; ++i) { 94 fItemArray[i].~T(); 95 } 96 // set fCount to 0 before calling checkRealloc so that no copy cons. are called. 97 fCount = 0; 98 this->checkRealloc(n); 99 fCount = n; 100 for (int i = 0; i < fCount; ++i) { 101 new (fItemArray + i) T; 102 } 103 } 104 105 /** 106 * Resets to a copy of a C array. 107 */ reset(const T * array,int count)108 void reset(const T* array, int count) { 109 for (int i = 0; i < fCount; ++i) { 110 fItemArray[i].~T(); 111 } 112 int delta = count - fCount; 113 this->checkRealloc(delta); 114 fCount = count; 115 this->copy(array); 116 } 117 removeShuffle(int n)118 void removeShuffle(int n) { 119 SkASSERT(n < fCount); 120 int newCount = fCount - 1; 121 fCount = newCount; 122 fItemArray[n].~T(); 123 if (n != newCount) { 124 this->move(n, newCount); 125 } 126 } 127 128 /** 129 * Number of elements in the array. 130 */ count()131 int count() const { return fCount; } 132 133 /** 134 * Is the array empty. 135 */ empty()136 bool empty() const { return !fCount; } 137 138 /** 139 * Adds 1 new default-initialized T value and returns it by reference. Note 140 * the reference only remains valid until the next call that adds or removes 141 * elements. 142 */ push_back()143 T& push_back() { 144 T* newT = reinterpret_cast<T*>(this->push_back_raw(1)); 145 new (newT) T; 146 return *newT; 147 } 148 149 /** 150 * Version of above that uses a copy constructor to initialize the new item 151 */ push_back(const T & t)152 T& push_back(const T& t) { 153 T* newT = reinterpret_cast<T*>(this->push_back_raw(1)); 154 new (newT) T(t); 155 return *newT; 156 } 157 158 /** 159 * Construct a new T at the back of this array. 160 */ emplace_back(Args &&...args)161 template<class... Args> T& emplace_back(Args&&... args) { 162 T* newT = reinterpret_cast<T*>(this->push_back_raw(1)); 163 return *new (newT) T(std::forward<Args>(args)...); 164 } 165 166 /** 167 * Allocates n more default-initialized T values, and returns the address of 168 * the start of that new range. Note: this address is only valid until the 169 * next API call made on the array that might add or remove elements. 170 */ push_back_n(int n)171 T* push_back_n(int n) { 172 SkASSERT(n >= 0); 173 T* newTs = reinterpret_cast<T*>(this->push_back_raw(n)); 174 for (int i = 0; i < n; ++i) { 175 new (newTs + i) T; 176 } 177 return newTs; 178 } 179 180 /** 181 * Version of above that uses a copy constructor to initialize all n items 182 * to the same T. 183 */ push_back_n(int n,const T & t)184 T* push_back_n(int n, const T& t) { 185 SkASSERT(n >= 0); 186 T* newTs = reinterpret_cast<T*>(this->push_back_raw(n)); 187 for (int i = 0; i < n; ++i) { 188 new (newTs[i]) T(t); 189 } 190 return newTs; 191 } 192 193 /** 194 * Version of above that uses a copy constructor to initialize the n items 195 * to separate T values. 196 */ push_back_n(int n,const T t[])197 T* push_back_n(int n, const T t[]) { 198 SkASSERT(n >= 0); 199 this->checkRealloc(n); 200 for (int i = 0; i < n; ++i) { 201 new (fItemArray + fCount + i) T(t[i]); 202 } 203 fCount += n; 204 return fItemArray + fCount - n; 205 } 206 207 /** 208 * Removes the last element. Not safe to call when count() == 0. 209 */ pop_back()210 void pop_back() { 211 SkASSERT(fCount > 0); 212 --fCount; 213 fItemArray[fCount].~T(); 214 this->checkRealloc(0); 215 } 216 217 /** 218 * Removes the last n elements. Not safe to call when count() < n. 219 */ pop_back_n(int n)220 void pop_back_n(int n) { 221 SkASSERT(n >= 0); 222 SkASSERT(fCount >= n); 223 fCount -= n; 224 for (int i = 0; i < n; ++i) { 225 fItemArray[fCount + i].~T(); 226 } 227 this->checkRealloc(0); 228 } 229 230 /** 231 * Pushes or pops from the back to resize. Pushes will be default 232 * initialized. 233 */ resize_back(int newCount)234 void resize_back(int newCount) { 235 SkASSERT(newCount >= 0); 236 237 if (newCount > fCount) { 238 this->push_back_n(newCount - fCount); 239 } else if (newCount < fCount) { 240 this->pop_back_n(fCount - newCount); 241 } 242 } 243 244 /** Swaps the contents of this array with that array. Does a pointer swap if possible, 245 otherwise copies the T values. */ swap(SkTArray * that)246 void swap(SkTArray* that) { 247 if (this == that) { 248 return; 249 } 250 if (this->fPreAllocMemArray != this->fItemArray && 251 that->fPreAllocMemArray != that->fItemArray) { 252 // If neither is using a preallocated array then just swap. 253 SkTSwap(fItemArray, that->fItemArray); 254 SkTSwap(fCount, that->fCount); 255 SkTSwap(fAllocCount, that->fAllocCount); 256 } else { 257 // This could be more optimal... 258 SkTArray copy(*that); 259 *that = *this; 260 *this = copy; 261 } 262 } 263 begin()264 T* begin() { 265 return fItemArray; 266 } begin()267 const T* begin() const { 268 return fItemArray; 269 } end()270 T* end() { 271 return fItemArray ? fItemArray + fCount : NULL; 272 } end()273 const T* end() const { 274 return fItemArray ? fItemArray + fCount : NULL; 275 } 276 277 /** 278 * Get the i^th element. 279 */ 280 T& operator[] (int i) { 281 SkASSERT(i < fCount); 282 SkASSERT(i >= 0); 283 return fItemArray[i]; 284 } 285 286 const T& operator[] (int i) const { 287 SkASSERT(i < fCount); 288 SkASSERT(i >= 0); 289 return fItemArray[i]; 290 } 291 292 /** 293 * equivalent to operator[](0) 294 */ front()295 T& front() { SkASSERT(fCount > 0); return fItemArray[0];} 296 front()297 const T& front() const { SkASSERT(fCount > 0); return fItemArray[0];} 298 299 /** 300 * equivalent to operator[](count() - 1) 301 */ back()302 T& back() { SkASSERT(fCount); return fItemArray[fCount - 1];} 303 back()304 const T& back() const { SkASSERT(fCount > 0); return fItemArray[fCount - 1];} 305 306 /** 307 * equivalent to operator[](count()-1-i) 308 */ fromBack(int i)309 T& fromBack(int i) { 310 SkASSERT(i >= 0); 311 SkASSERT(i < fCount); 312 return fItemArray[fCount - i - 1]; 313 } 314 fromBack(int i)315 const T& fromBack(int i) const { 316 SkASSERT(i >= 0); 317 SkASSERT(i < fCount); 318 return fItemArray[fCount - i - 1]; 319 } 320 321 bool operator==(const SkTArray<T, MEM_COPY>& right) const { 322 int leftCount = this->count(); 323 if (leftCount != right.count()) { 324 return false; 325 } 326 for (int index = 0; index < leftCount; ++index) { 327 if (fItemArray[index] != right.fItemArray[index]) { 328 return false; 329 } 330 } 331 return true; 332 } 333 334 bool operator!=(const SkTArray<T, MEM_COPY>& right) const { 335 return !(*this == right); 336 } 337 338 protected: 339 /** 340 * Creates an empty array that will use the passed storage block until it 341 * is insufficiently large to hold the entire array. 342 */ 343 template <int N> SkTArray(SkAlignedSTStorage<N,T> * storage)344 SkTArray(SkAlignedSTStorage<N,T>* storage) { 345 this->init(NULL, 0, storage->get(), N); 346 } 347 348 /** 349 * Copy another array, using preallocated storage if preAllocCount >= 350 * array.count(). Otherwise storage will only be used when array shrinks 351 * to fit. 352 */ 353 template <int N> SkTArray(const SkTArray & array,SkAlignedSTStorage<N,T> * storage)354 SkTArray(const SkTArray& array, SkAlignedSTStorage<N,T>* storage) { 355 this->init(array.fItemArray, array.fCount, storage->get(), N); 356 } 357 358 /** 359 * Copy a C array, using preallocated storage if preAllocCount >= 360 * count. Otherwise storage will only be used when array shrinks 361 * to fit. 362 */ 363 template <int N> SkTArray(const T * array,int count,SkAlignedSTStorage<N,T> * storage)364 SkTArray(const T* array, int count, SkAlignedSTStorage<N,T>* storage) { 365 this->init(array, count, storage->get(), N); 366 } 367 init(const T * array,int count,void * preAllocStorage,int preAllocOrReserveCount)368 void init(const T* array, int count, 369 void* preAllocStorage, int preAllocOrReserveCount) { 370 SkASSERT(count >= 0); 371 SkASSERT(preAllocOrReserveCount >= 0); 372 fCount = count; 373 fReserveCount = (preAllocOrReserveCount > 0) ? 374 preAllocOrReserveCount : 375 gMIN_ALLOC_COUNT; 376 fPreAllocMemArray = preAllocStorage; 377 if (fReserveCount >= fCount && 378 preAllocStorage) { 379 fAllocCount = fReserveCount; 380 fMemArray = preAllocStorage; 381 } else { 382 fAllocCount = SkMax32(fCount, fReserveCount); 383 fMemArray = sk_malloc_throw(fAllocCount * sizeof(T)); 384 } 385 386 this->copy(array); 387 } 388 389 private: 390 /** In the following move and copy methods, 'dst' is assumed to be uninitialized raw storage. 391 * In the following move methods, 'src' is destroyed leaving behind uninitialized raw storage. 392 */ SK_WHEN(E,void)393 template <bool E = MEM_COPY> SK_WHEN(E, void) copy(const T* src) { 394 sk_careful_memcpy(fMemArray, src, fCount * sizeof(T)); 395 } SK_WHEN(E,void)396 template <bool E = MEM_COPY> SK_WHEN(E, void) move(int dst, int src) { 397 memcpy(&fItemArray[dst], &fItemArray[src], sizeof(T)); 398 } SK_WHEN(E,void)399 template <bool E = MEM_COPY> SK_WHEN(E, void) move(char* dst) { 400 sk_careful_memcpy(dst, fMemArray, fCount * sizeof(T)); 401 } 402 copy(const T * src)403 template <bool E = MEM_COPY> SK_WHEN(!E, void) copy(const T* src) { 404 for (int i = 0; i < fCount; ++i) { 405 new (fItemArray + i) T(src[i]); 406 } 407 } move(int dst,int src)408 template <bool E = MEM_COPY> SK_WHEN(!E, void) move(int dst, int src) { 409 new (&fItemArray[dst]) T(std::move(fItemArray[src])); 410 fItemArray[src].~T(); 411 } move(char * dst)412 template <bool E = MEM_COPY> SK_WHEN(!E, void) move(char* dst) { 413 for (int i = 0; i < fCount; ++i) { 414 new (dst + sizeof(T) * i) T(std::move(fItemArray[i])); 415 fItemArray[i].~T(); 416 } 417 } 418 419 static const int gMIN_ALLOC_COUNT = 8; 420 421 // Helper function that makes space for n objects, adjusts the count, but does not initialize 422 // the new objects. push_back_raw(int n)423 void* push_back_raw(int n) { 424 this->checkRealloc(n); 425 void* ptr = fItemArray + fCount; 426 fCount += n; 427 return ptr; 428 } 429 checkRealloc(int delta)430 inline void checkRealloc(int delta) { 431 SkASSERT(fCount >= 0); 432 SkASSERT(fAllocCount >= 0); 433 434 SkASSERT(-delta <= fCount); 435 436 int newCount = fCount + delta; 437 int newAllocCount = fAllocCount; 438 439 if (newCount > fAllocCount || newCount < (fAllocCount / 3)) { 440 // whether we're growing or shrinking, we leave at least 50% extra space for future 441 // growth (clamped to the reserve count). 442 newAllocCount = SkMax32(newCount + ((newCount + 1) >> 1), fReserveCount); 443 } 444 if (newAllocCount != fAllocCount) { 445 446 fAllocCount = newAllocCount; 447 char* newMemArray; 448 449 if (fAllocCount == fReserveCount && fPreAllocMemArray) { 450 newMemArray = (char*) fPreAllocMemArray; 451 } else { 452 newMemArray = (char*) sk_malloc_throw(fAllocCount*sizeof(T)); 453 } 454 455 this->move(newMemArray); 456 457 if (fMemArray != fPreAllocMemArray) { 458 sk_free(fMemArray); 459 } 460 fMemArray = newMemArray; 461 } 462 } 463 464 int fReserveCount; 465 int fCount; 466 int fAllocCount; 467 void* fPreAllocMemArray; 468 union { 469 T* fItemArray; 470 void* fMemArray; 471 }; 472 }; 473 474 /** 475 * Subclass of SkTArray that contains a preallocated memory block for the array. 476 */ 477 template <int N, typename T, bool MEM_COPY = false> 478 class SkSTArray : public SkTArray<T, MEM_COPY> { 479 private: 480 typedef SkTArray<T, MEM_COPY> INHERITED; 481 482 public: SkSTArray()483 SkSTArray() : INHERITED(&fStorage) { 484 } 485 SkSTArray(const SkSTArray & array)486 SkSTArray(const SkSTArray& array) 487 : INHERITED(array, &fStorage) { 488 } 489 SkSTArray(const INHERITED & array)490 explicit SkSTArray(const INHERITED& array) 491 : INHERITED(array, &fStorage) { 492 } 493 SkSTArray(int reserveCount)494 explicit SkSTArray(int reserveCount) 495 : INHERITED(reserveCount) { 496 } 497 SkSTArray(const T * array,int count)498 SkSTArray(const T* array, int count) 499 : INHERITED(array, count, &fStorage) { 500 } 501 502 SkSTArray& operator= (const SkSTArray& array) { 503 return *this = *(const INHERITED*)&array; 504 } 505 506 SkSTArray& operator= (const INHERITED& array) { 507 INHERITED::operator=(array); 508 return *this; 509 } 510 511 private: 512 SkAlignedSTStorage<N,T> fStorage; 513 }; 514 515 #endif 516