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 /** Allocate an array of T elements, and free the array in the destructor 70 */ 71 template <typename T> class SkAutoTArray { 72 public: 73 SkAutoTArray() {} 74 /** Allocate count number of T elements 75 */ 76 explicit SkAutoTArray(int count) { 77 SkASSERT(count >= 0); 78 if (count) { 79 fArray.reset(new T[count]); 80 } 81 SkDEBUGCODE(fCount = count;) 82 } 83 84 SkAutoTArray(SkAutoTArray&& other) : fArray(std::move(other.fArray)) { 85 SkDEBUGCODE(fCount = other.fCount; other.fCount = 0;) 86 } 87 SkAutoTArray& operator=(SkAutoTArray&& other) { 88 if (this != &other) { 89 fArray = std::move(other.fArray); 90 SkDEBUGCODE(fCount = other.fCount; other.fCount = 0;) 91 } 92 return *this; 93 } 94 95 /** Reallocates given a new count. Reallocation occurs even if new count equals old count. 96 */ 97 void reset(int count) { *this = SkAutoTArray(count); } 98 99 /** Return the array of T elements. Will be NULL if count == 0 100 */ 101 T* get() const { return fArray.get(); } 102 103 /** Return the nth element in the array 104 */ 105 T& operator[](int index) const { 106 SkASSERT((unsigned)index < (unsigned)fCount); 107 return fArray[index]; 108 } 109 110 private: 111 std::unique_ptr<T[]> fArray; 112 SkDEBUGCODE(int fCount = 0;) 113 }; 114 115 /** Wraps SkAutoTArray, with room for kCountRequested elements preallocated. 116 */ 117 template <int kCountRequested, typename T> class SkAutoSTArray { 118 public: 119 SkAutoSTArray(SkAutoSTArray&&) = delete; 120 SkAutoSTArray(const SkAutoSTArray&) = delete; 121 SkAutoSTArray& operator=(SkAutoSTArray&&) = delete; 122 SkAutoSTArray& operator=(const SkAutoSTArray&) = delete; 123 124 /** Initialize with no objects */ 125 SkAutoSTArray() { 126 fArray = nullptr; 127 fCount = 0; 128 } 129 130 /** Allocate count number of T elements 131 */ 132 SkAutoSTArray(int count) { 133 fArray = nullptr; 134 fCount = 0; 135 this->reset(count); 136 } 137 138 ~SkAutoSTArray() { 139 this->reset(0); 140 } 141 142 /** Destroys previous objects in the array and default constructs count number of objects */ 143 void reset(int count) { 144 T* start = fArray; 145 T* iter = start + fCount; 146 while (iter > start) { 147 (--iter)->~T(); 148 } 149 150 SkASSERT(count >= 0); 151 if (fCount != count) { 152 if (fCount > kCount) { 153 // 'fArray' was allocated last time so free it now 154 SkASSERT((T*) fStorage != fArray); 155 sk_free(fArray); 156 } 157 158 if (count > kCount) { 159 fArray = (T*) sk_malloc_throw(count, sizeof(T)); 160 } else if (count > 0) { 161 fArray = (T*) fStorage; 162 } else { 163 fArray = nullptr; 164 } 165 166 fCount = count; 167 } 168 169 iter = fArray; 170 T* stop = fArray + count; 171 while (iter < stop) { 172 new (iter++) T; 173 } 174 } 175 176 /** Return the number of T elements in the array 177 */ 178 int count() const { return fCount; } 179 180 /** Return the array of T elements. Will be NULL if count == 0 181 */ 182 T* get() const { return fArray; } 183 184 T* begin() { return fArray; } 185 186 const T* begin() const { return fArray; } 187 188 T* end() { return fArray + fCount; } 189 190 const T* end() const { return fArray + fCount; } 191 192 /** Return the nth element in the array 193 */ 194 T& operator[](int index) const { 195 SkASSERT(index < fCount); 196 return fArray[index]; 197 } 198 199 private: 200 #if defined(SK_BUILD_FOR_GOOGLE3) 201 // Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max, but some functions 202 // have multiple large stack allocations. 203 static const int kMaxBytes = 4 * 1024; 204 static const int kCount = kCountRequested * sizeof(T) > kMaxBytes 205 ? kMaxBytes / sizeof(T) 206 : kCountRequested; 207 #else 208 static const int kCount = kCountRequested; 209 #endif 210 211 int fCount; 212 T* fArray; 213 // since we come right after fArray, fStorage should be properly aligned 214 char fStorage[kCount * sizeof(T)]; 215 }; 216 217 /** Manages an array of T elements, freeing the array in the destructor. 218 * Does NOT call any constructors/destructors on T (T must be POD). 219 */ 220 template <typename T> class SkAutoTMalloc { 221 public: 222 /** Takes ownership of the ptr. The ptr must be a value which can be passed to sk_free. */ 223 explicit SkAutoTMalloc(T* ptr = nullptr) : fPtr(ptr) {} 224 225 /** Allocates space for 'count' Ts. */ 226 explicit SkAutoTMalloc(size_t count) 227 : fPtr(count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr) {} 228 229 SkAutoTMalloc(SkAutoTMalloc&&) = default; 230 SkAutoTMalloc& operator=(SkAutoTMalloc&&) = default; 231 232 /** Resize the memory area pointed to by the current ptr preserving contents. */ 233 void realloc(size_t count) { 234 fPtr.reset(count ? (T*)sk_realloc_throw(fPtr.release(), count * sizeof(T)) : nullptr); 235 } 236 237 /** Resize the memory area pointed to by the current ptr without preserving contents. */ 238 T* reset(size_t count = 0) { 239 fPtr.reset(count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr); 240 return this->get(); 241 } 242 243 T* get() const { return fPtr.get(); } 244 245 operator T*() { return fPtr.get(); } 246 247 operator const T*() const { return fPtr.get(); } 248 249 T& operator[](int index) { return fPtr.get()[index]; } 250 251 const T& operator[](int index) const { return fPtr.get()[index]; } 252 253 /** 254 * Transfer ownership of the ptr to the caller, setting the internal 255 * pointer to NULL. Note that this differs from get(), which also returns 256 * the pointer, but it does not transfer ownership. 257 */ 258 T* release() { return fPtr.release(); } 259 260 private: 261 std::unique_ptr<T, SkFunctionWrapper<void, void, sk_free>> fPtr; 262 }; 263 264 template <size_t kCountRequested, typename T> class SkAutoSTMalloc { 265 public: 266 SkAutoSTMalloc() : fPtr(fTStorage) {} 267 268 SkAutoSTMalloc(size_t count) { 269 if (count > kCount) { 270 fPtr = (T*)sk_malloc_throw(count, sizeof(T)); 271 } else if (count) { 272 fPtr = fTStorage; 273 } else { 274 fPtr = nullptr; 275 } 276 } 277 278 SkAutoSTMalloc(SkAutoSTMalloc&&) = delete; 279 SkAutoSTMalloc(const SkAutoSTMalloc&) = delete; 280 SkAutoSTMalloc& operator=(SkAutoSTMalloc&&) = delete; 281 SkAutoSTMalloc& operator=(const SkAutoSTMalloc&) = delete; 282 283 ~SkAutoSTMalloc() { 284 if (fPtr != fTStorage) { 285 sk_free(fPtr); 286 } 287 } 288 289 // doesn't preserve contents 290 T* reset(size_t count) { 291 if (fPtr != fTStorage) { 292 sk_free(fPtr); 293 } 294 if (count > kCount) { 295 fPtr = (T*)sk_malloc_throw(count, sizeof(T)); 296 } else if (count) { 297 fPtr = fTStorage; 298 } else { 299 fPtr = nullptr; 300 } 301 return fPtr; 302 } 303 304 T* get() const { return fPtr; } 305 306 operator T*() { 307 return fPtr; 308 } 309 310 operator const T*() const { 311 return fPtr; 312 } 313 314 T& operator[](int index) { 315 return fPtr[index]; 316 } 317 318 const T& operator[](int index) const { 319 return fPtr[index]; 320 } 321 322 // Reallocs the array, can be used to shrink the allocation. Makes no attempt to be intelligent 323 void realloc(size_t count) { 324 if (count > kCount) { 325 if (fPtr == fTStorage) { 326 fPtr = (T*)sk_malloc_throw(count, sizeof(T)); 327 memcpy(fPtr, fTStorage, kCount * sizeof(T)); 328 } else { 329 fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T)); 330 } 331 } else if (count) { 332 if (fPtr != fTStorage) { 333 fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T)); 334 } 335 } else { 336 this->reset(0); 337 } 338 } 339 340 private: 341 // Since we use uint32_t storage, we might be able to get more elements for free. 342 static const size_t kCountWithPadding = SkAlign4(kCountRequested*sizeof(T)) / sizeof(T); 343 #if defined(SK_BUILD_FOR_GOOGLE3) 344 // Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max, but some functions 345 // have multiple large stack allocations. 346 static const size_t kMaxBytes = 4 * 1024; 347 static const size_t kCount = kCountRequested * sizeof(T) > kMaxBytes 348 ? kMaxBytes / sizeof(T) 349 : kCountWithPadding; 350 #else 351 static const size_t kCount = kCountWithPadding; 352 #endif 353 354 T* fPtr; 355 union { 356 uint32_t fStorage32[SkAlign4(kCount*sizeof(T)) >> 2]; 357 T fTStorage[1]; // do NOT want to invoke T::T() 358 }; 359 }; 360 361 ////////////////////////////////////////////////////////////////////////////////////////////////// 362 363 /** 364 * Pass the object and the storage that was offered during SkInPlaceNewCheck, and this will 365 * safely destroy (and free if it was dynamically allocated) the object. 366 */ 367 template <typename T> void SkInPlaceDeleteCheck(T* obj, void* storage) { 368 if (storage == obj) { 369 obj->~T(); 370 } else { 371 delete obj; 372 } 373 } 374 375 /** 376 * Allocates T, using storage if it is large enough, and allocating on the heap (via new) if 377 * storage is not large enough. 378 * 379 * obj = SkInPlaceNewCheck<Type>(storage, size); 380 * ... 381 * SkInPlaceDeleteCheck(obj, storage); 382 */ 383 template<typename T, typename... Args> 384 T* SkInPlaceNewCheck(void* storage, size_t size, Args&&... args) { 385 return (sizeof(T) <= size) ? new (storage) T(std::forward<Args>(args)...) 386 : new T(std::forward<Args>(args)...); 387 } 388 /** 389 * Reserves memory that is aligned on double and pointer boundaries. 390 * Hopefully this is sufficient for all practical purposes. 391 */ 392 template <size_t N> class SkAlignedSStorage { 393 public: 394 SkAlignedSStorage() {} 395 SkAlignedSStorage(SkAlignedSStorage&&) = delete; 396 SkAlignedSStorage(const SkAlignedSStorage&) = delete; 397 SkAlignedSStorage& operator=(SkAlignedSStorage&&) = delete; 398 SkAlignedSStorage& operator=(const SkAlignedSStorage&) = delete; 399 400 size_t size() const { return N; } 401 void* get() { return fData; } 402 const void* get() const { return fData; } 403 404 private: 405 union { 406 void* fPtr; 407 double fDouble; 408 char fData[N]; 409 }; 410 }; 411 412 /** 413 * Reserves memory that is aligned on double and pointer boundaries. 414 * Hopefully this is sufficient for all practical purposes. Otherwise, 415 * we have to do some arcane trickery to determine alignment of non-POD 416 * types. Lifetime of the memory is the lifetime of the object. 417 */ 418 template <int N, typename T> class SkAlignedSTStorage { 419 public: 420 SkAlignedSTStorage() {} 421 SkAlignedSTStorage(SkAlignedSTStorage&&) = delete; 422 SkAlignedSTStorage(const SkAlignedSTStorage&) = delete; 423 SkAlignedSTStorage& operator=(SkAlignedSTStorage&&) = delete; 424 SkAlignedSTStorage& operator=(const SkAlignedSTStorage&) = delete; 425 426 /** 427 * Returns void* because this object does not initialize the 428 * memory. Use placement new for types that require a cons. 429 */ 430 void* get() { return fStorage.get(); } 431 const void* get() const { return fStorage.get(); } 432 private: 433 SkAlignedSStorage<sizeof(T)*N> fStorage; 434 }; 435 436 using SkAutoFree = std::unique_ptr<void, SkFunctionWrapper<void, void, sk_free>>; 437 438 template<typename C, std::size_t... Is> 439 constexpr auto SkMakeArrayFromIndexSequence(C c, skstd::index_sequence<Is...>) 440 -> std::array<skstd::result_of_t<C(std::size_t)>, sizeof...(Is)> { 441 return {{ c(Is)... }}; 442 } 443 444 template<size_t N, typename C> constexpr auto SkMakeArray(C c) 445 -> std::array<skstd::result_of_t<C(std::size_t)>, N> { 446 return SkMakeArrayFromIndexSequence(c, skstd::make_index_sequence<N>{}); 447 } 448 449 #endif 450