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