Home | History | Annotate | Download | only in core
      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 #include <algorithm>
      9 #include <cstddef>
     10 #include "SkArenaAlloc.h"
     11 
     12 static char* end_chain(char*) { return nullptr; }
     13 
     14 SkArenaAlloc::SkArenaAlloc(char* block, size_t size, size_t extraSize, Tracking tracking)
     15     : fDtorCursor {block}
     16     , fCursor     {block}
     17     , fEnd        {block + SkTo<uint32_t>(size)}
     18     , fFirstBlock {block}
     19     , fFirstSize  {SkTo<uint32_t>(size)}
     20     , fExtraSize  {SkTo<uint32_t>(extraSize)}
     21 {
     22     if (size < sizeof(Footer)) {
     23         fEnd = fCursor = fDtorCursor = nullptr;
     24     }
     25 
     26     if (tracking == kTrack) {
     27         fTotalSlop = 0;
     28     }
     29 
     30     if (fCursor != nullptr) {
     31         this->installFooter(end_chain, 0);
     32         if (fTotalSlop >= 0) {
     33             fTotalAlloc += fFirstSize;
     34         }
     35     }
     36 }
     37 
     38 SkArenaAlloc::~SkArenaAlloc() {
     39     if (fTotalSlop >= 0) {
     40         int32_t lastSlop = fEnd - fCursor;
     41         fTotalSlop += lastSlop;
     42         SkDebugf("SkArenaAlloc initial: %p %u %u total alloc: %u total slop: %d last slop: %d\n",
     43             fFirstBlock, fFirstSize, fExtraSize, fTotalAlloc, fTotalSlop, lastSlop);
     44     }
     45     RunDtorsOnBlock(fDtorCursor);
     46 }
     47 
     48 void SkArenaAlloc::reset() {
     49     this->~SkArenaAlloc();
     50     new (this) SkArenaAlloc{fFirstBlock, fFirstSize, fExtraSize,
     51                             fTotalSlop < 0 ? kDontTrack : kTrack};
     52 }
     53 
     54 void SkArenaAlloc::installFooter(FooterAction* action, uint32_t padding) {
     55     SkASSERT(padding < 64);
     56     int64_t actionInt = (int64_t)(intptr_t)action;
     57 
     58     // The top 14 bits should be either all 0s or all 1s. Check this.
     59     SkASSERT((actionInt << 6) >> 6 == actionInt);
     60     Footer encodedFooter = (actionInt << 6) | padding;
     61     memmove(fCursor, &encodedFooter, sizeof(Footer));
     62     fCursor += sizeof(Footer);
     63     fDtorCursor = fCursor;
     64 }
     65 
     66 void SkArenaAlloc::installPtrFooter(FooterAction* action, char* ptr, uint32_t padding) {
     67     memmove(fCursor, &ptr, sizeof(char*));
     68     fCursor += sizeof(char*);
     69     this->installFooter(action, padding);
     70 }
     71 
     72 char* SkArenaAlloc::SkipPod(char* footerEnd) {
     73     char* objEnd = footerEnd - (sizeof(Footer) + sizeof(int32_t));
     74     int32_t skip;
     75     memmove(&skip, objEnd, sizeof(int32_t));
     76     return objEnd - skip;
     77 }
     78 
     79 void SkArenaAlloc::RunDtorsOnBlock(char* footerEnd) {
     80     while (footerEnd != nullptr) {
     81         Footer footer;
     82         memcpy(&footer, footerEnd - sizeof(Footer), sizeof(Footer));
     83 
     84         FooterAction* action = (FooterAction*)(footer >> 6);
     85         ptrdiff_t padding = footer & 63;
     86 
     87         footerEnd = action(footerEnd) - padding;
     88     }
     89 }
     90 
     91 char* SkArenaAlloc::NextBlock(char* footerEnd) {
     92     char* objEnd = footerEnd - (sizeof(Footer) + sizeof(char*));
     93     char* next;
     94     memmove(&next, objEnd, sizeof(char*));
     95     RunDtorsOnBlock(next);
     96     delete [] objEnd;
     97     return nullptr;
     98 }
     99 
    100 void SkArenaAlloc::installUint32Footer(FooterAction* action, uint32_t value, uint32_t padding) {
    101     memmove(fCursor, &value, sizeof(uint32_t));
    102     fCursor += sizeof(uint32_t);
    103     this->installFooter(action, padding);
    104 }
    105 
    106 void SkArenaAlloc::ensureSpace(uint32_t size, uint32_t alignment) {
    107     constexpr uint32_t headerSize = sizeof(Footer) + sizeof(ptrdiff_t);
    108     // The chrome c++ library we use does not define std::max_align_t.
    109     // This must be conservative to add the right amount of extra memory to handle the alignment
    110     // padding.
    111     constexpr uint32_t alignof_max_align_t = 8;
    112     uint32_t objSizeAndOverhead = size + headerSize + sizeof(Footer);
    113     if (alignment > alignof_max_align_t) {
    114         objSizeAndOverhead += alignment - 1;
    115     }
    116 
    117     uint32_t allocationSize = std::max(objSizeAndOverhead, fExtraSize * fFib0);
    118     fFib0 += fFib1;
    119     std::swap(fFib0, fFib1);
    120 
    121     // Round up to a nice size. If > 32K align to 4K boundary else up to max_align_t. The > 32K
    122     // heuristic is from the JEMalloc behavior.
    123     {
    124         uint32_t mask = allocationSize > (1 << 15) ? (1 << 12) - 1 : 16 - 1;
    125         allocationSize = (allocationSize + mask) & ~mask;
    126     }
    127 
    128     char* newBlock = new char[allocationSize];
    129 
    130     if (fTotalSlop >= 0) {
    131         fTotalAlloc += allocationSize;
    132         fTotalSlop += fEnd - fCursor;
    133     }
    134 
    135     auto previousDtor = fDtorCursor;
    136     fCursor = newBlock;
    137     fDtorCursor = newBlock;
    138     fEnd = fCursor + allocationSize;
    139     this->installPtrFooter(NextBlock, previousDtor, 0);
    140 }
    141 
    142 char* SkArenaAlloc::allocObjectWithFooter(uint32_t sizeIncludingFooter, uint32_t alignment) {
    143     uintptr_t mask = alignment - 1;
    144 
    145 restart:
    146     uint32_t skipOverhead = 0;
    147     bool needsSkipFooter = fCursor != fDtorCursor;
    148     if (needsSkipFooter) {
    149         skipOverhead = sizeof(Footer) + sizeof(uint32_t);
    150     }
    151     char* objStart = (char*)((uintptr_t)(fCursor + skipOverhead + mask) & ~mask);
    152     uint32_t totalSize = sizeIncludingFooter + skipOverhead;
    153 
    154     if ((ptrdiff_t)totalSize > fEnd - objStart) {
    155         this->ensureSpace(totalSize, alignment);
    156         goto restart;
    157     }
    158 
    159     SkASSERT((ptrdiff_t)totalSize <= fEnd - objStart);
    160 
    161     // Install a skip footer if needed, thus terminating a run of POD data. The calling code is
    162     // responsible for installing the footer after the object.
    163     if (needsSkipFooter) {
    164         this->installUint32Footer(SkipPod, SkTo<uint32_t>(fCursor - fDtorCursor), 0);
    165     }
    166 
    167     return objStart;
    168 }
    169 
    170