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