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 = nullptr; 91 SkDEBUGCODE(fCount = 0;) 92 } 93 /** Allocate count number of T elements 94 */ 95 explicit SkAutoTArray(int count) { 96 SkASSERT(count >= 0); 97 fArray = nullptr; 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 = nullptr; 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 = nullptr; 146 fCount = 0; 147 } 148 149 /** Allocate count number of T elements 150 */ 151 SkAutoSTArray(int count) { 152 fArray = nullptr; 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 fArray = (T*) sk_malloc_throw(count, sizeof(T)); 179 } else if (count > 0) { 180 fArray = (T*) fStorage; 181 } else { 182 fArray = nullptr; 183 } 184 185 fCount = count; 186 } 187 188 iter = fArray; 189 T* stop = fArray + count; 190 while (iter < stop) { 191 new (iter++) T; 192 } 193 } 194 195 /** Return the number of T elements in the array 196 */ 197 int count() const { return fCount; } 198 199 /** Return the array of T elements. Will be NULL if count == 0 200 */ 201 T* get() const { return fArray; } 202 203 T* begin() { return fArray; } 204 205 const T* begin() const { return fArray; } 206 207 T* end() { return fArray + fCount; } 208 209 const T* end() const { return fArray + fCount; } 210 211 /** Return the nth element in the array 212 */ 213 T& operator[](int index) const { 214 SkASSERT(index < fCount); 215 return fArray[index]; 216 } 217 218 private: 219 #if defined(SK_BUILD_FOR_GOOGLE3) 220 // Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max, but some functions 221 // have multiple large stack allocations. 222 static const int kMaxBytes = 4 * 1024; 223 static const int kCount = kCountRequested * sizeof(T) > kMaxBytes 224 ? kMaxBytes / sizeof(T) 225 : kCountRequested; 226 #else 227 static const int kCount = kCountRequested; 228 #endif 229 230 int fCount; 231 T* fArray; 232 // since we come right after fArray, fStorage should be properly aligned 233 char fStorage[kCount * sizeof(T)]; 234 }; 235 236 /** Manages an array of T elements, freeing the array in the destructor. 237 * Does NOT call any constructors/destructors on T (T must be POD). 238 */ 239 template <typename T> class SkAutoTMalloc : SkNoncopyable { 240 public: 241 /** Takes ownership of the ptr. The ptr must be a value which can be passed to sk_free. */ 242 explicit SkAutoTMalloc(T* ptr = nullptr) { 243 fPtr = ptr; 244 } 245 246 /** Allocates space for 'count' Ts. */ 247 explicit SkAutoTMalloc(size_t count) { 248 fPtr = count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr; 249 } 250 251 SkAutoTMalloc(SkAutoTMalloc<T>&& that) : fPtr(that.release()) {} 252 253 ~SkAutoTMalloc() { 254 sk_free(fPtr); 255 } 256 257 /** Resize the memory area pointed to by the current ptr preserving contents. */ 258 void realloc(size_t count) { 259 if (count) { 260 fPtr = reinterpret_cast<T*>(sk_realloc_throw(fPtr, count * sizeof(T))); 261 } else { 262 this->reset(0); 263 } 264 } 265 266 /** Resize the memory area pointed to by the current ptr without preserving contents. */ 267 T* reset(size_t count = 0) { 268 sk_free(fPtr); 269 fPtr = count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr; 270 return fPtr; 271 } 272 273 T* get() const { return fPtr; } 274 275 operator T*() { 276 return fPtr; 277 } 278 279 operator const T*() const { 280 return fPtr; 281 } 282 283 T& operator[](int index) { 284 return fPtr[index]; 285 } 286 287 const T& operator[](int index) const { 288 return fPtr[index]; 289 } 290 291 SkAutoTMalloc& operator=(SkAutoTMalloc<T>&& that) { 292 if (this != &that) { 293 sk_free(fPtr); 294 fPtr = that.release(); 295 } 296 return *this; 297 } 298 299 /** 300 * Transfer ownership of the ptr to the caller, setting the internal 301 * pointer to NULL. Note that this differs from get(), which also returns 302 * the pointer, but it does not transfer ownership. 303 */ 304 T* release() { 305 T* ptr = fPtr; 306 fPtr = nullptr; 307 return ptr; 308 } 309 310 private: 311 T* fPtr; 312 }; 313 314 template <size_t kCountRequested, typename T> class SkAutoSTMalloc : SkNoncopyable { 315 public: 316 SkAutoSTMalloc() : fPtr(fTStorage) {} 317 318 SkAutoSTMalloc(size_t count) { 319 if (count > kCount) { 320 fPtr = (T*)sk_malloc_throw(count, sizeof(T)); 321 } else if (count) { 322 fPtr = fTStorage; 323 } else { 324 fPtr = nullptr; 325 } 326 } 327 328 ~SkAutoSTMalloc() { 329 if (fPtr != fTStorage) { 330 sk_free(fPtr); 331 } 332 } 333 334 // doesn't preserve contents 335 T* reset(size_t count) { 336 if (fPtr != fTStorage) { 337 sk_free(fPtr); 338 } 339 if (count > kCount) { 340 fPtr = (T*)sk_malloc_throw(count, sizeof(T)); 341 } else if (count) { 342 fPtr = fTStorage; 343 } else { 344 fPtr = nullptr; 345 } 346 return fPtr; 347 } 348 349 T* get() const { return fPtr; } 350 351 operator T*() { 352 return fPtr; 353 } 354 355 operator const T*() const { 356 return fPtr; 357 } 358 359 T& operator[](int index) { 360 return fPtr[index]; 361 } 362 363 const T& operator[](int index) const { 364 return fPtr[index]; 365 } 366 367 // Reallocs the array, can be used to shrink the allocation. Makes no attempt to be intelligent 368 void realloc(size_t count) { 369 if (count > kCount) { 370 if (fPtr == fTStorage) { 371 fPtr = (T*)sk_malloc_throw(count, sizeof(T)); 372 memcpy(fPtr, fTStorage, kCount * sizeof(T)); 373 } else { 374 fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T)); 375 } 376 } else if (count) { 377 if (fPtr != fTStorage) { 378 fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T)); 379 } 380 } else { 381 this->reset(0); 382 } 383 } 384 385 private: 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(SK_BUILD_FOR_GOOGLE3) 389 // Stack frame size is limited for SK_BUILD_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 */ 412 template <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 */ 428 template <typename T> T* SkInPlaceNewCheck(void* storage, size_t size) { 429 return (sizeof(T) <= size) ? new (storage) T : new T; 430 } 431 432 template <typename T, typename A1, typename A2, typename A3> 433 T* 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 template <typename T, typename A1, typename A2, typename A3, typename A4> 438 T* SkInPlaceNewCheck(void* storage, size_t size, 439 const A1& a1, const A2& a2, const A3& a3, const A4& a4) { 440 return (sizeof(T) <= size) ? new (storage) T(a1, a2, a3, a4) : new T(a1, a2, a3, a4); 441 } 442 443 /** 444 * Reserves memory that is aligned on double and pointer boundaries. 445 * Hopefully this is sufficient for all practical purposes. 446 */ 447 template <size_t N> class SkAlignedSStorage : SkNoncopyable { 448 public: 449 size_t size() const { return N; } 450 void* get() { return fData; } 451 const void* get() const { return fData; } 452 453 private: 454 union { 455 void* fPtr; 456 double fDouble; 457 char fData[N]; 458 }; 459 }; 460 461 /** 462 * Reserves memory that is aligned on double and pointer boundaries. 463 * Hopefully this is sufficient for all practical purposes. Otherwise, 464 * we have to do some arcane trickery to determine alignment of non-POD 465 * types. Lifetime of the memory is the lifetime of the object. 466 */ 467 template <int N, typename T> class SkAlignedSTStorage : SkNoncopyable { 468 public: 469 /** 470 * Returns void* because this object does not initialize the 471 * memory. Use placement new for types that require a cons. 472 */ 473 void* get() { return fStorage.get(); } 474 const void* get() const { return fStorage.get(); } 475 private: 476 SkAlignedSStorage<sizeof(T)*N> fStorage; 477 }; 478 479 using SkAutoFree = std::unique_ptr<void, SkFunctionWrapper<void, void, sk_free>>; 480 481 template<typename C, std::size_t... Is> 482 constexpr auto SkMakeArrayFromIndexSequence(C c, skstd::index_sequence<Is...>) 483 -> std::array<skstd::result_of_t<C(std::size_t)>, sizeof...(Is)> { 484 return {{ c(Is)... }}; 485 } 486 487 template<size_t N, typename C> constexpr auto SkMakeArray(C c) 488 -> std::array<skstd::result_of_t<C(std::size_t)>, N> { 489 return SkMakeArrayFromIndexSequence(c, skstd::make_index_sequence<N>{}); 490 } 491 492 #endif 493