1 2 /* 3 * Copyright 2006 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10 #ifndef SkTemplates_DEFINED 11 #define SkTemplates_DEFINED 12 13 #include "SkMath.h" 14 #include "SkTypes.h" 15 #include <limits.h> 16 #include <new> 17 18 /** \file SkTemplates.h 19 20 This file contains light-weight template classes for type-safe and exception-safe 21 resource management. 22 */ 23 24 /** 25 * Marks a local variable as known to be unused (to avoid warnings). 26 * Note that this does *not* prevent the local variable from being optimized away. 27 */ 28 template<typename T> inline void sk_ignore_unused_variable(const T&) { } 29 30 /** 31 * SkTIsConst<T>::value is true if the type T is const. 32 * The type T is constrained not to be an array or reference type. 33 */ 34 template <typename T> struct SkTIsConst { 35 static T* t; 36 static uint16_t test(const volatile void*); 37 static uint32_t test(volatile void *); 38 static const bool value = (sizeof(uint16_t) == sizeof(test(t))); 39 }; 40 41 ///@{ 42 /** SkTConstType<T, CONST>::type will be 'const T' if CONST is true, 'T' otherwise. */ 43 template <typename T, bool CONST> struct SkTConstType { 44 typedef T type; 45 }; 46 template <typename T> struct SkTConstType<T, true> { 47 typedef const T type; 48 }; 49 ///@} 50 51 /** 52 * Returns a pointer to a D which comes immediately after S[count]. 53 */ 54 template <typename D, typename S> static D* SkTAfter(S* ptr, size_t count = 1) { 55 return reinterpret_cast<D*>(ptr + count); 56 } 57 58 /** 59 * Returns a pointer to a D which comes byteOffset bytes after S. 60 */ 61 template <typename D, typename S> static D* SkTAddOffset(S* ptr, size_t byteOffset) { 62 // The intermediate char* has the same const-ness as D as this produces better error messages. 63 // This relies on the fact that reinterpret_cast can add constness, but cannot remove it. 64 return reinterpret_cast<D*>( 65 reinterpret_cast<typename SkTConstType<char, SkTIsConst<D>::value>::type*>(ptr) + byteOffset 66 ); 67 } 68 69 /** \class SkAutoTCallVProc 70 71 Call a function when this goes out of scope. The template uses two 72 parameters, the object, and a function that is to be called in the destructor. 73 If detach() is called, the object reference is set to null. If the object 74 reference is null when the destructor is called, we do not call the 75 function. 76 */ 77 template <typename T, void (*P)(T*)> class SkAutoTCallVProc : SkNoncopyable { 78 public: 79 SkAutoTCallVProc(T* obj): fObj(obj) {} 80 ~SkAutoTCallVProc() { if (fObj) P(fObj); } 81 82 operator T*() const { return fObj; } 83 T* operator->() const { SkASSERT(fObj); return fObj; } 84 85 T* detach() { T* obj = fObj; fObj = NULL; return obj; } 86 void reset(T* obj = NULL) { 87 if (fObj != obj) { 88 if (fObj) { 89 P(fObj); 90 } 91 fObj = obj; 92 } 93 } 94 private: 95 T* fObj; 96 }; 97 98 /** \class SkAutoTCallIProc 99 100 Call a function when this goes out of scope. The template uses two 101 parameters, the object, and a function that is to be called in the destructor. 102 If detach() is called, the object reference is set to null. If the object 103 reference is null when the destructor is called, we do not call the 104 function. 105 */ 106 template <typename T, int (*P)(T*)> class SkAutoTCallIProc : SkNoncopyable { 107 public: 108 SkAutoTCallIProc(T* obj): fObj(obj) {} 109 ~SkAutoTCallIProc() { if (fObj) P(fObj); } 110 111 operator T*() const { return fObj; } 112 T* operator->() const { SkASSERT(fObj); return fObj; } 113 114 T* detach() { T* obj = fObj; fObj = NULL; return obj; } 115 private: 116 T* fObj; 117 }; 118 119 /** \class SkAutoTDelete 120 An SkAutoTDelete<T> is like a T*, except that the destructor of SkAutoTDelete<T> 121 automatically deletes the pointer it holds (if any). That is, SkAutoTDelete<T> 122 owns the T object that it points to. Like a T*, an SkAutoTDelete<T> may hold 123 either NULL or a pointer to a T object. Also like T*, SkAutoTDelete<T> is 124 thread-compatible, and once you dereference it, you get the threadsafety 125 guarantees of T. 126 127 The size of a SkAutoTDelete is small: sizeof(SkAutoTDelete<T>) == sizeof(T*) 128 */ 129 template <typename T> class SkAutoTDelete : SkNoncopyable { 130 public: 131 SkAutoTDelete(T* obj = NULL) : fObj(obj) {} 132 ~SkAutoTDelete() { SkDELETE(fObj); } 133 134 T* get() const { return fObj; } 135 operator T*() const { return fObj; } 136 T& operator*() const { SkASSERT(fObj); return *fObj; } 137 T* operator->() const { SkASSERT(fObj); return fObj; } 138 139 void reset(T* obj) { 140 if (fObj != obj) { 141 SkDELETE(fObj); 142 fObj = obj; 143 } 144 } 145 146 /** 147 * Delete the owned object, setting the internal pointer to NULL. 148 */ 149 void free() { 150 SkDELETE(fObj); 151 fObj = NULL; 152 } 153 154 /** 155 * Transfer ownership of the object to the caller, setting the internal 156 * pointer to NULL. Note that this differs from get(), which also returns 157 * the pointer, but it does not transfer ownership. 158 */ 159 T* detach() { 160 T* obj = fObj; 161 fObj = NULL; 162 return obj; 163 } 164 165 void swap(SkAutoTDelete* that) { 166 SkTSwap(fObj, that->fObj); 167 } 168 169 private: 170 T* fObj; 171 }; 172 173 // Calls ~T() in the destructor. 174 template <typename T> class SkAutoTDestroy : SkNoncopyable { 175 public: 176 SkAutoTDestroy(T* obj = NULL) : fObj(obj) {} 177 ~SkAutoTDestroy() { 178 if (fObj) { 179 fObj->~T(); 180 } 181 } 182 183 T* get() const { return fObj; } 184 T& operator*() const { SkASSERT(fObj); return *fObj; } 185 T* operator->() const { SkASSERT(fObj); return fObj; } 186 187 private: 188 T* fObj; 189 }; 190 191 template <typename T> class SkAutoTDeleteArray : SkNoncopyable { 192 public: 193 SkAutoTDeleteArray(T array[]) : fArray(array) {} 194 ~SkAutoTDeleteArray() { SkDELETE_ARRAY(fArray); } 195 196 T* get() const { return fArray; } 197 void free() { SkDELETE_ARRAY(fArray); fArray = NULL; } 198 T* detach() { T* array = fArray; fArray = NULL; return array; } 199 200 void reset(T array[]) { 201 if (fArray != array) { 202 SkDELETE_ARRAY(fArray); 203 fArray = array; 204 } 205 } 206 207 private: 208 T* fArray; 209 }; 210 211 /** Allocate an array of T elements, and free the array in the destructor 212 */ 213 template <typename T> class SkAutoTArray : SkNoncopyable { 214 public: 215 SkAutoTArray() { 216 fArray = NULL; 217 SkDEBUGCODE(fCount = 0;) 218 } 219 /** Allocate count number of T elements 220 */ 221 explicit SkAutoTArray(int count) { 222 SkASSERT(count >= 0); 223 fArray = NULL; 224 if (count) { 225 fArray = SkNEW_ARRAY(T, count); 226 } 227 SkDEBUGCODE(fCount = count;) 228 } 229 230 /** Reallocates given a new count. Reallocation occurs even if new count equals old count. 231 */ 232 void reset(int count) { 233 SkDELETE_ARRAY(fArray); 234 SkASSERT(count >= 0); 235 fArray = NULL; 236 if (count) { 237 fArray = SkNEW_ARRAY(T, count); 238 } 239 SkDEBUGCODE(fCount = count;) 240 } 241 242 ~SkAutoTArray() { 243 SkDELETE_ARRAY(fArray); 244 } 245 246 /** Return the array of T elements. Will be NULL if count == 0 247 */ 248 T* get() const { return fArray; } 249 250 /** Return the nth element in the array 251 */ 252 T& operator[](int index) const { 253 SkASSERT((unsigned)index < (unsigned)fCount); 254 return fArray[index]; 255 } 256 257 void swap(SkAutoTArray& other) { 258 SkTSwap(fArray, other.fArray); 259 SkDEBUGCODE(SkTSwap(fCount, other.fCount)); 260 } 261 262 private: 263 T* fArray; 264 SkDEBUGCODE(int fCount;) 265 }; 266 267 /** Wraps SkAutoTArray, with room for up to N elements preallocated 268 */ 269 template <int N, typename T> class SkAutoSTArray : SkNoncopyable { 270 public: 271 /** Initialize with no objects */ 272 SkAutoSTArray() { 273 fArray = NULL; 274 fCount = 0; 275 } 276 277 /** Allocate count number of T elements 278 */ 279 SkAutoSTArray(int count) { 280 fArray = NULL; 281 fCount = 0; 282 this->reset(count); 283 } 284 285 ~SkAutoSTArray() { 286 this->reset(0); 287 } 288 289 /** Destroys previous objects in the array and default constructs count number of objects */ 290 void reset(int count) { 291 T* start = fArray; 292 T* iter = start + fCount; 293 while (iter > start) { 294 (--iter)->~T(); 295 } 296 297 if (fCount != count) { 298 if (fCount > N) { 299 // 'fArray' was allocated last time so free it now 300 SkASSERT((T*) fStorage != fArray); 301 sk_free(fArray); 302 } 303 304 if (count > N) { 305 const uint64_t size64 = sk_64_mul(count, sizeof(T)); 306 const size_t size = static_cast<size_t>(size64); 307 if (size != size64) { 308 sk_out_of_memory(); 309 } 310 fArray = (T*) sk_malloc_throw(size); 311 } else if (count > 0) { 312 fArray = (T*) fStorage; 313 } else { 314 fArray = NULL; 315 } 316 317 fCount = count; 318 } 319 320 iter = fArray; 321 T* stop = fArray + count; 322 while (iter < stop) { 323 SkNEW_PLACEMENT(iter++, T); 324 } 325 } 326 327 /** Return the number of T elements in the array 328 */ 329 int count() const { return fCount; } 330 331 /** Return the array of T elements. Will be NULL if count == 0 332 */ 333 T* get() const { return fArray; } 334 335 /** Return the nth element in the array 336 */ 337 T& operator[](int index) const { 338 SkASSERT(index < fCount); 339 return fArray[index]; 340 } 341 342 private: 343 int fCount; 344 T* fArray; 345 // since we come right after fArray, fStorage should be properly aligned 346 char fStorage[N * sizeof(T)]; 347 }; 348 349 /** Manages an array of T elements, freeing the array in the destructor. 350 * Does NOT call any constructors/destructors on T (T must be POD). 351 */ 352 template <typename T> class SkAutoTMalloc : SkNoncopyable { 353 public: 354 /** Takes ownership of the ptr. The ptr must be a value which can be passed to sk_free. */ 355 explicit SkAutoTMalloc(T* ptr = NULL) { 356 fPtr = ptr; 357 } 358 359 /** Allocates space for 'count' Ts. */ 360 explicit SkAutoTMalloc(size_t count) { 361 fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW); 362 } 363 364 ~SkAutoTMalloc() { 365 sk_free(fPtr); 366 } 367 368 /** Resize the memory area pointed to by the current ptr preserving contents. */ 369 void realloc(size_t count) { 370 fPtr = reinterpret_cast<T*>(sk_realloc_throw(fPtr, count * sizeof(T))); 371 } 372 373 /** Resize the memory area pointed to by the current ptr without preserving contents. */ 374 void reset(size_t count) { 375 sk_free(fPtr); 376 fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW); 377 } 378 379 T* get() const { return fPtr; } 380 381 operator T*() { 382 return fPtr; 383 } 384 385 operator const T*() const { 386 return fPtr; 387 } 388 389 T& operator[](int index) { 390 return fPtr[index]; 391 } 392 393 const T& operator[](int index) const { 394 return fPtr[index]; 395 } 396 397 /** 398 * Transfer ownership of the ptr to the caller, setting the internal 399 * pointer to NULL. Note that this differs from get(), which also returns 400 * the pointer, but it does not transfer ownership. 401 */ 402 T* detach() { 403 T* ptr = fPtr; 404 fPtr = NULL; 405 return ptr; 406 } 407 408 private: 409 T* fPtr; 410 }; 411 412 template <size_t N, typename T> class SkAutoSTMalloc : SkNoncopyable { 413 public: 414 SkAutoSTMalloc() : fPtr(fTStorage) {} 415 416 SkAutoSTMalloc(size_t count) { 417 if (count > N) { 418 fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP); 419 } else { 420 fPtr = fTStorage; 421 } 422 } 423 424 ~SkAutoSTMalloc() { 425 if (fPtr != fTStorage) { 426 sk_free(fPtr); 427 } 428 } 429 430 // doesn't preserve contents 431 T* reset(size_t count) { 432 if (fPtr != fTStorage) { 433 sk_free(fPtr); 434 } 435 if (count > N) { 436 fPtr = (T*)sk_malloc_throw(count * sizeof(T)); 437 } else { 438 fPtr = fTStorage; 439 } 440 return fPtr; 441 } 442 443 T* get() const { return fPtr; } 444 445 operator T*() { 446 return fPtr; 447 } 448 449 operator const T*() const { 450 return fPtr; 451 } 452 453 T& operator[](int index) { 454 return fPtr[index]; 455 } 456 457 const T& operator[](int index) const { 458 return fPtr[index]; 459 } 460 461 // Reallocs the array, can be used to shrink the allocation. Makes no attempt to be intelligent 462 void realloc(size_t count) { 463 if (count > N) { 464 if (fPtr == fTStorage) { 465 fPtr = (T*)sk_malloc_throw(count * sizeof(T)); 466 memcpy(fPtr, fTStorage, N * sizeof(T)); 467 } else { 468 fPtr = (T*)sk_realloc_throw(fPtr, count * sizeof(T)); 469 } 470 } else if (fPtr != fTStorage) { 471 fPtr = (T*)sk_realloc_throw(fPtr, count * sizeof(T)); 472 } 473 } 474 475 private: 476 T* fPtr; 477 union { 478 uint32_t fStorage32[(N*sizeof(T) + 3) >> 2]; 479 T fTStorage[1]; // do NOT want to invoke T::T() 480 }; 481 }; 482 483 /** 484 * Reserves memory that is aligned on double and pointer boundaries. 485 * Hopefully this is sufficient for all practical purposes. 486 */ 487 template <size_t N> class SkAlignedSStorage : SkNoncopyable { 488 public: 489 void* get() { return fData; } 490 const void* get() const { return fData; } 491 private: 492 union { 493 void* fPtr; 494 double fDouble; 495 char fData[N]; 496 }; 497 }; 498 499 /** 500 * Reserves memory that is aligned on double and pointer boundaries. 501 * Hopefully this is sufficient for all practical purposes. Otherwise, 502 * we have to do some arcane trickery to determine alignment of non-POD 503 * types. Lifetime of the memory is the lifetime of the object. 504 */ 505 template <int N, typename T> class SkAlignedSTStorage : SkNoncopyable { 506 public: 507 /** 508 * Returns void* because this object does not initialize the 509 * memory. Use placement new for types that require a cons. 510 */ 511 void* get() { return fStorage.get(); } 512 private: 513 SkAlignedSStorage<sizeof(T)*N> fStorage; 514 }; 515 516 #endif 517