#include "src/core/SkArenaAlloc.h"
#include <algorithm>
#include <new>
namespace pk {
static char* end_chain(char*) { return nullptr; }
SkArenaAlloc::SkArenaAlloc(char* block, size_t size, size_t firstHeapAllocation)
: fDtorCursor {block}
, fCursor {block}
, fEnd {block + ToU32(size)}
, fFibonacciProgression{ToU32(size), ToU32(firstHeapAllocation)}
{
if (size < sizeof(Footer)) {
fEnd = fCursor = fDtorCursor = nullptr;
}
if (fCursor != nullptr) {
this->installFooter(end_chain, 0);
}
}
SkArenaAlloc::~SkArenaAlloc() {
RunDtorsOnBlock(fDtorCursor);
}
void SkArenaAlloc::installFooter(FooterAction* action, uint32_t padding) {
assert(SkTFitsIn<uint8_t>(padding));
this->installRaw(action);
this->installRaw((uint8_t)padding);
fDtorCursor = fCursor;
}
char* SkArenaAlloc::SkipPod(char* footerEnd) {
char* objEnd = footerEnd - (sizeof(Footer) + sizeof(int32_t));
int32_t skip;
memmove(&skip, objEnd, sizeof(int32_t));
return objEnd - skip;
}
void SkArenaAlloc::RunDtorsOnBlock(char* footerEnd) {
while (footerEnd != nullptr) {
FooterAction* action;
uint8_t padding;
memcpy(&action, footerEnd - sizeof( Footer), sizeof( action));
memcpy(&padding, footerEnd - sizeof(padding), sizeof(padding));
footerEnd = action(footerEnd) - (ptrdiff_t)padding;
}
}
char* SkArenaAlloc::NextBlock(char* footerEnd) {
char* objEnd = footerEnd - (sizeof(char*) + sizeof(Footer));
char* next;
memmove(&next, objEnd, sizeof(char*));
RunDtorsOnBlock(next);
delete [] objEnd;
return nullptr;
}
void SkArenaAlloc::ensureSpace(uint32_t size, uint32_t alignment) {
constexpr uint32_t headerSize = sizeof(Footer) + sizeof(ptrdiff_t);
constexpr uint32_t maxSize = std::numeric_limits<uint32_t>::max();
constexpr uint32_t overhead = headerSize + sizeof(Footer);
AssertRelease(size <= maxSize - overhead);
uint32_t objSizeAndOverhead = size + overhead;
const uint32_t alignmentOverhead = alignment - 1;
AssertRelease(objSizeAndOverhead <= maxSize - alignmentOverhead);
objSizeAndOverhead += alignmentOverhead;
uint32_t minAllocationSize = fFibonacciProgression.nextBlockSize();
uint32_t allocationSize = std::max(objSizeAndOverhead, minAllocationSize);
{
uint32_t mask = allocationSize > (1 << 15) ? (1 << 12) - 1 : 16 - 1;
AssertRelease(allocationSize <= maxSize - mask);
allocationSize = (allocationSize + mask) & ~mask;
}
char* newBlock = new char[allocationSize];
auto previousDtor = fDtorCursor;
fCursor = newBlock;
fDtorCursor = newBlock;
fEnd = fCursor + allocationSize;
this->installRaw(previousDtor);
this->installFooter(NextBlock, 0);
}
char* SkArenaAlloc::allocObjectWithFooter(uint32_t sizeIncludingFooter, uint32_t alignment) {
uintptr_t mask = alignment - 1;
restart:
uint32_t skipOverhead = 0;
const bool needsSkipFooter = fCursor != fDtorCursor;
if (needsSkipFooter) {
skipOverhead = sizeof(Footer) + sizeof(uint32_t);
}
const uint32_t totalSize = sizeIncludingFooter + skipOverhead;
if (!fCursor) {
this->ensureSpace(totalSize, alignment);
goto restart;
}
assert(fEnd);
char* objStart = (char*)((uintptr_t)(fCursor + skipOverhead + mask) & ~mask);
if ((ptrdiff_t)totalSize > fEnd - objStart) {
this->ensureSpace(totalSize, alignment);
goto restart;
}
AssertRelease((ptrdiff_t)totalSize <= fEnd - objStart);
if (needsSkipFooter) {
this->installRaw(ToU32(fCursor - fDtorCursor));
this->installFooter(SkipPod, 0);
}
return objStart;
}
std::array<const uint32_t, 47> SkFibonacci47 {
1, 1, 2, 3, 5, 8,
13, 21, 34, 55, 89, 144,
233, 377, 610, 987, 1597, 2584,
4181, 6765, 10946, 17711, 28657, 46368,
75025, 121393, 196418, 317811, 514229, 832040,
1346269, 2178309, 3524578, 5702887, 9227465, 14930352,
24157817, 39088169, 63245986, 102334155, 165580141, 267914296,
433494437, 701408733, 1134903170, 1836311903, 2971215073,
};
}