LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkArenaAlloc.h (source / functions) Hit Total Coverage
Test: output.info Lines: 33 36 91.7 %
Date: 2017-07-14 16:53:18 Functions: 60 545 11.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright 2016 Google Inc.
       3             :  *
       4             :  * Use of this source code is governed by a BSD-style license that can be
       5             :  * found in the LICENSE file.
       6             :  */
       7             : 
       8             : #ifndef SkFixedAlloc_DEFINED
       9             : #define SkFixedAlloc_DEFINED
      10             : 
      11             : #include "SkRefCnt.h"
      12             : #include "SkTFitsIn.h"
      13             : #include "SkTypes.h"
      14             : #include <cstddef>
      15             : #include <new>
      16             : #include <type_traits>
      17             : #include <utility>
      18             : #include <vector>
      19             : 
      20             : // SkArenaAlloc allocates object and destroys the allocated objects when destroyed. It's designed
      21             : // to minimize the number of underlying block allocations. SkArenaAlloc allocates first out of an
      22             : // (optional) user-provided block of memory, and when that's exhausted it allocates on the heap,
      23             : // starting with an allocation of extraSize bytes.  If your data (plus a small overhead) fits in
      24             : // the user-provided block, SkArenaAlloc never uses the heap, and if it fits in extraSize bytes,
      25             : // it'll use the heap only once.  If you pass extraSize = 0, it allocates blocks for each call to
      26             : // make<T>.
      27             : //
      28             : // Examples:
      29             : //
      30             : //   char block[mostCasesSize];
      31             : //   SkArenaAlloc arena(block, almostAllCasesSize);
      32             : //
      33             : // If mostCasesSize is too large for the stack, you can use the following pattern.
      34             : //
      35             : //   std::unique_ptr<char[]> block{new char[mostCasesSize]};
      36             : //   SkArenaAlloc arena(block.get(), mostCasesSize, almostAllCasesSize);
      37             : //
      38             : // If the program only sometimes allocates memory, use the following.
      39             : //
      40             : //   SkArenaAlloc arena(nullptr, 0, almostAllCasesSize);
      41             : //
      42             : // The storage does not necessarily need to be on the stack. Embedding the storage in a class also
      43             : // works.
      44             : //
      45             : //   class Foo {
      46             : //       char storage[mostCasesSize];
      47             : //       SkArenaAlloc arena (storage, almostAllCasesSize);
      48             : //   };
      49             : //
      50             : // In addition, the system is optimized to handle POD data including arrays of PODs (where
      51             : // POD is really data with no destructors). For POD data it has zero overhead per item, and a
      52             : // typical block overhead of 8 bytes. For non-POD objects there is a per item overhead of 4 bytes.
      53             : // For arrays of non-POD objects there is a per array overhead of typically 8 bytes. There is an
      54             : // addition overhead when switching from POD data to non-POD data of typically 8 bytes.
      55             : //
      56             : // If additional blocks are needed they are increased exponentially. This strategy bounds the
      57             : // recursion of the RunDtorsOnBlock to be limited to O(log size-of-memory). Block size grow using
      58             : // the Fibonacci sequence which means that for 2^32 memory there are 48 allocations, and for 2^48
      59             : // there are 71 allocations.
      60             : class SkArenaAlloc {
      61             : public:
      62             :     SkArenaAlloc(char* block, size_t size, size_t extraSize = 0);
      63             : 
      64             :     template <size_t kSize>
      65         708 :     SkArenaAlloc(char (&block)[kSize], size_t extraSize = kSize)
      66         708 :         : SkArenaAlloc(block, kSize, extraSize)
      67         708 :     {}
      68             : 
      69         228 :     SkArenaAlloc(size_t extraSize)
      70         228 :         : SkArenaAlloc(nullptr, 0, extraSize)
      71         228 :     {}
      72             : 
      73             :     ~SkArenaAlloc();
      74             : 
      75             :     template <typename T, typename... Args>
      76        1895 :     T* make(Args&&... args) {
      77        1895 :         uint32_t size      = SkTo<uint32_t>(sizeof(T));
      78        1895 :         uint32_t alignment = SkTo<uint32_t>(alignof(T));
      79             :         char* objStart;
      80             :         if (skstd::is_trivially_destructible<T>::value) {
      81         848 :             objStart = this->allocObject(size, alignment);
      82         848 :             fCursor = objStart + size;
      83             :         } else {
      84        1047 :             objStart = this->allocObjectWithFooter(size + sizeof(Footer), alignment);
      85             :             // Can never be UB because max value is alignof(T).
      86        1047 :             uint32_t padding = SkTo<uint32_t>(objStart - fCursor);
      87             : 
      88             :             // Advance to end of object to install footer.
      89        1047 :             fCursor = objStart + size;
      90        3141 :             FooterAction* releaser = [](char* objEnd) {
      91        1047 :                 char* objStart = objEnd - (sizeof(T) + sizeof(Footer));
      92        1047 :                 ((T*)objStart)->~T();
      93        1047 :                 return objStart;
      94        1047 :             };
      95        1047 :             this->installFooter(releaser, padding);
      96             :         }
      97             : 
      98             :         // This must be last to make objects with nested use of this allocator work.
      99        1895 :         return new(objStart) T(std::forward<Args>(args)...);
     100             :     }
     101             : 
     102             :     template <typename T, typename... Args>
     103           0 :     sk_sp<T> makeSkSp(Args&&... args) {
     104           0 :         SkASSERT(SkTFitsIn<uint32_t>(sizeof(T)));
     105             : 
     106             :         // The arena takes a ref for itself to account for the destructor. The sk_sp count can't
     107             :         // become zero or the sk_sp will try to call free on the pointer.
     108           0 :         return sk_sp<T>(SkRef(this->make<T>(std::forward<Args>(args)...)));
     109             :     }
     110             : 
     111             :     template <typename T>
     112         378 :     T* makeArrayDefault(size_t count) {
     113         378 :         uint32_t safeCount = SkTo<uint32_t>(count);
     114         378 :         T* array = (T*)this->commonArrayAlloc<T>(safeCount);
     115             : 
     116             :         // If T is primitive then no initialization takes place.
     117       20308 :         for (size_t i = 0; i < safeCount; i++) {
     118       19930 :             new (&array[i]) T;
     119             :         }
     120         378 :         return array;
     121             :     }
     122             : 
     123             :     template <typename T>
     124             :     T* makeArray(size_t count) {
     125             :         uint32_t safeCount = SkTo<uint32_t>(count);
     126             :         T* array = (T*)this->commonArrayAlloc<T>(safeCount);
     127             : 
     128             :         // If T is primitive then the memory is initialized. For example, an array of chars will
     129             :         // be zeroed.
     130             :         for (size_t i = 0; i < safeCount; i++) {
     131             :             new (&array[i]) T();
     132             :         }
     133             :         return array;
     134             :     }
     135             : 
     136             :     // Destroy all allocated objects, free any heap allocations.
     137             :     void reset();
     138             : 
     139             : private:
     140             :     using Footer = int64_t;
     141             :     using FooterAction = char* (char*);
     142             : 
     143             :     static char* SkipPod(char* footerEnd);
     144             :     static void RunDtorsOnBlock(char* footerEnd);
     145             :     static char* NextBlock(char* footerEnd);
     146             : 
     147             :     void installFooter(FooterAction* releaser, uint32_t padding);
     148             :     void installUint32Footer(FooterAction* action, uint32_t value, uint32_t padding);
     149             :     void installPtrFooter(FooterAction* action, char* ptr, uint32_t padding);
     150             : 
     151             :     void ensureSpace(uint32_t size, uint32_t alignment);
     152             : 
     153             :     char* allocObject(uint32_t size, uint32_t alignment);
     154             : 
     155             :     char* allocObjectWithFooter(uint32_t sizeIncludingFooter, uint32_t alignment);
     156             : 
     157             :     template <typename T>
     158         378 :     char* commonArrayAlloc(uint32_t count) {
     159             :         char* objStart;
     160         378 :         uint32_t arraySize = SkTo<uint32_t>(count * sizeof(T));
     161         378 :         uint32_t alignment = SkTo<uint32_t>(alignof(T));
     162             : 
     163             :         if (skstd::is_trivially_destructible<T>::value) {
     164         378 :             objStart = this->allocObject(arraySize, alignment);
     165         378 :             fCursor = objStart + arraySize;
     166             :         } else {
     167             :             uint32_t totalSize = arraySize + sizeof(Footer) + sizeof(uint32_t);
     168             :             objStart = this->allocObjectWithFooter(totalSize, alignment);
     169             : 
     170             :             // Can never be UB because max value is alignof(T).
     171             :             uint32_t padding = SkTo<uint32_t>(objStart - fCursor);
     172             : 
     173             :             // Advance to end of array to install footer.?
     174             :             fCursor = objStart + arraySize;
     175             :             this->installUint32Footer(
     176             :                 [](char* footerEnd) {
     177             :                     char* objEnd = footerEnd - (sizeof(Footer) + sizeof(uint32_t));
     178             :                     uint32_t count;
     179             :                     memmove(&count, objEnd, sizeof(uint32_t));
     180             :                     char* objStart = objEnd - count * sizeof(T);
     181             :                     T* array = (T*) objStart;
     182             :                     for (uint32_t i = 0; i < count; i++) {
     183             :                         array[i].~T();
     184             :                     }
     185             :                     return objStart;
     186             :                 },
     187             :                 SkTo<uint32_t>(count),
     188             :                 padding);
     189             :         }
     190             : 
     191         378 :         return objStart;
     192             :     }
     193             : 
     194             :     char*          fDtorCursor;
     195             :     char*          fCursor;
     196             :     char*          fEnd;
     197             :     char* const    fFirstBlock;
     198             :     const uint32_t fFirstSize;
     199             :     const uint32_t fExtraSize;
     200             :     // Use the Fibonacci sequence as the growth factor for block size. The size of the block
     201             :     // allocated is fFib0 * fExtraSize. Using 2 ^ n * fExtraSize had too much slop for Android.
     202             :     uint32_t       fFib0 {1}, fFib1 {1};
     203             : };
     204             : 
     205             : #endif//SkFixedAlloc_DEFINED

Generated by: LCOV version 1.13