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