/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkArenaAlloc_DEFINED #define SkArenaAlloc_DEFINED #include "include/private/base/SkASAN.h" #include "include/private/base/SkAssert.h" #include "include/private/base/SkTFitsIn.h" #include "include/private/base/SkTo.h" #include <algorithm> #include <array> #include <cstdint> #include <cstdlib> #include <cstring> #include <limits> #include <new> #include <type_traits> #include <utility> // We found allocating strictly doubling amounts of memory from the heap left too // much unused slop, particularly on Android. Instead we'll follow a Fibonacci-like // progression. // SkFibonacci47 is the first 47 Fibonacci numbers. Fib(47) is the largest value less than 2 ^ 32. extern std::array<const uint32_t, 47> SkFibonacci47; template<uint32_t kMaxSize> class SkFibBlockSizes { … }; // SkArenaAlloc allocates object and destroys the allocated objects when destroyed. It's designed // to minimize the number of underlying block allocations. SkArenaAlloc allocates first out of an // (optional) user-provided block of memory, and when that's exhausted it allocates on the heap, // starting with an allocation of firstHeapAllocation bytes. If your data (plus a small overhead) // fits in the user-provided block, SkArenaAlloc never uses the heap, and if it fits in // firstHeapAllocation bytes, it'll use the heap only once. If 0 is specified for // firstHeapAllocation, then blockSize is used unless that too is 0, then 1024 is used. // // Examples: // // char block[mostCasesSize]; // SkArenaAlloc arena(block, mostCasesSize); // // If mostCasesSize is too large for the stack, you can use the following pattern. // // std::unique_ptr<char[]> block{new char[mostCasesSize]}; // SkArenaAlloc arena(block.get(), mostCasesSize, almostAllCasesSize); // // If the program only sometimes allocates memory, use the following pattern. // // SkArenaAlloc arena(nullptr, 0, almostAllCasesSize); // // The storage does not necessarily need to be on the stack. Embedding the storage in a class also // works. // // class Foo { // char storage[mostCasesSize]; // SkArenaAlloc arena (storage, mostCasesSize); // }; // // In addition, the system is optimized to handle POD data including arrays of PODs (where // POD is really data with no destructors). For POD data it has zero overhead per item, and a // typical per block overhead of 8 bytes. For non-POD objects there is a per item overhead of 4 // bytes. For arrays of non-POD objects there is a per array overhead of typically 8 bytes. There // is an addition overhead when switching from POD data to non-POD data of typically 8 bytes. // // If additional blocks are needed they are increased exponentially. This strategy bounds the // recursion of the RunDtorsOnBlock to be limited to O(log size-of-memory). Block size grow using // the Fibonacci sequence which means that for 2^32 memory there are 48 allocations, and for 2^48 // there are 71 allocations. class SkArenaAlloc { … }; class SkArenaAllocWithReset : public SkArenaAlloc { … }; // Helper for defining allocators with inline/reserved storage. // For argument declarations, stick to the base type (SkArenaAlloc). // Note: Inheriting from the storage first means the storage will outlive the // SkArenaAlloc, letting ~SkArenaAlloc read it as it calls destructors. // (This is mostly only relevant for strict tools like MSAN.) template <size_t InlineStorageSize> class SkSTArenaAlloc : private std::array<char, InlineStorageSize>, public SkArenaAlloc { … }; template <size_t InlineStorageSize> class SkSTArenaAllocWithReset : private std::array<char, InlineStorageSize>, public SkArenaAllocWithReset { … }; #endif // SkArenaAlloc_DEFINED