#include "common/PoolAlloc.h"
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include "common/angleutils.h"
#include "common/debug.h"
#include "common/mathutil.h"
#include "common/platform.h"
#include "common/tls.h"
namespace angle
{
PoolAllocator::PoolAllocator(int growthIncrement, int allocationAlignment)
: mAlignment(allocationAlignment),
#if !defined(ANGLE_DISABLE_POOL_ALLOC)
mPageSize(growthIncrement),
mFreeList(0),
mInUseList(0),
mNumCalls(0),
mTotalBytes(0),
#endif
mLocked(false)
{
initialize(growthIncrement, allocationAlignment);
}
void PoolAllocator::initialize(int pageSize, int alignment)
{
mAlignment = alignment;
#if !defined(ANGLE_DISABLE_POOL_ALLOC)
mPageSize = pageSize;
if (mAlignment == 1)
{
mAlignmentMask = 0;
mHeaderSkip = sizeof(Header);
}
else
{
#endif
size_t minAlign = sizeof(void *);
mAlignment &= ~(minAlign - 1);
if (mAlignment < minAlign)
mAlignment = minAlign;
mAlignment = gl::ceilPow2(static_cast<unsigned int>(mAlignment));
mAlignmentMask = mAlignment - 1;
#if !defined(ANGLE_DISABLE_POOL_ALLOC)
mHeaderSkip = minAlign;
if (mHeaderSkip < sizeof(Header))
{
mHeaderSkip = rx::roundUpPow2(sizeof(Header), mAlignment);
}
}
if (mPageSize < 4 * 1024)
mPageSize = 4 * 1024;
mCurrentPageOffset = mPageSize;
#else
mStack.push_back({});
#endif
}
PoolAllocator::~PoolAllocator()
{
#if !defined(ANGLE_DISABLE_POOL_ALLOC)
while (mInUseList)
{
Header *next = mInUseList->nextPage;
mInUseList->~Header();
delete[] reinterpret_cast<char *>(mInUseList);
mInUseList = next;
}
while (mFreeList)
{
Header *next = mFreeList->nextPage;
delete[] reinterpret_cast<char *>(mFreeList);
mFreeList = next;
}
#else
for (auto &allocs : mStack)
{
for (auto alloc : allocs)
{
free(alloc);
}
}
mStack.clear();
#endif
}
void Allocation::checkGuardBlock(unsigned char *blockMem,
unsigned char val,
const char *locText) const
{
#if defined(ANGLE_POOL_ALLOC_GUARD_BLOCKS)
for (size_t x = 0; x < kGuardBlockSize; x++)
{
if (blockMem[x] != val)
{
char assertMsg[80];
snprintf(assertMsg, sizeof(assertMsg),
"PoolAlloc: Damage %s %zu byte allocation at 0x%p\n", locText, mSize, data());
assert(0 && "PoolAlloc: Damage in guard block");
}
}
#endif
}
void PoolAllocator::push()
{
#if !defined(ANGLE_DISABLE_POOL_ALLOC)
AllocState state = {mCurrentPageOffset, mInUseList};
mStack.push_back(state);
mCurrentPageOffset = mPageSize;
#else
mStack.push_back({});
#endif
}
void PoolAllocator::pop()
{
if (mStack.size() < 1)
return;
#if !defined(ANGLE_DISABLE_POOL_ALLOC)
Header *page = mStack.back().page;
mCurrentPageOffset = mStack.back().offset;
while (mInUseList != page)
{
mInUseList->~Header();
Header *nextInUse = mInUseList->nextPage;
if (mInUseList->pageCount > 1)
delete[] reinterpret_cast<char *>(mInUseList);
else
{
mInUseList->nextPage = mFreeList;
mFreeList = mInUseList;
}
mInUseList = nextInUse;
}
mStack.pop_back();
#else
for (auto &alloc : mStack.back())
{
free(alloc);
}
mStack.pop_back();
#endif
}
void PoolAllocator::popAll()
{
while (mStack.size() > 0)
pop();
}
void *PoolAllocator::allocate(size_t numBytes)
{
ASSERT(!mLocked);
#if !defined(ANGLE_DISABLE_POOL_ALLOC)
++mNumCalls;
mTotalBytes += numBytes;
size_t allocationSize = Allocation::AllocationSize(numBytes) + mAlignment;
if (allocationSize < numBytes)
return 0;
if (allocationSize <= mPageSize - mCurrentPageOffset)
{
unsigned char *memory = reinterpret_cast<unsigned char *>(mInUseList) + mCurrentPageOffset;
mCurrentPageOffset += allocationSize;
mCurrentPageOffset = (mCurrentPageOffset + mAlignmentMask) & ~mAlignmentMask;
return initializeAllocation(mInUseList, memory, numBytes);
}
if (allocationSize > mPageSize - mHeaderSkip)
{
size_t numBytesToAlloc = allocationSize + mHeaderSkip;
if (numBytesToAlloc < allocationSize)
return 0;
Header *memory = reinterpret_cast<Header *>(::new char[numBytesToAlloc]);
if (memory == 0)
return 0;
new (memory) Header(mInUseList, (numBytesToAlloc + mPageSize - 1) / mPageSize);
mInUseList = memory;
mCurrentPageOffset = mPageSize;
void *unalignedPtr =
reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(memory) + mHeaderSkip);
return std::align(mAlignment, numBytes, unalignedPtr, allocationSize);
}
unsigned char *newPageAddr =
static_cast<unsigned char *>(allocateNewPage(numBytes, allocationSize));
return initializeAllocation(mInUseList, newPageAddr, numBytes);
#else
void *alloc = malloc(numBytes + mAlignmentMask);
mStack.back().push_back(alloc);
intptr_t intAlloc = reinterpret_cast<intptr_t>(alloc);
intAlloc = (intAlloc + mAlignmentMask) & ~mAlignmentMask;
return reinterpret_cast<void *>(intAlloc);
#endif
}
#if !defined(ANGLE_DISABLE_POOL_ALLOC)
void *PoolAllocator::allocateNewPage(size_t numBytes, size_t allocationSize)
{
Header *memory;
if (mFreeList)
{
memory = mFreeList;
mFreeList = mFreeList->nextPage;
}
else
{
memory = reinterpret_cast<Header *>(::new char[mPageSize]);
if (memory == 0)
return 0;
}
new (memory) Header(mInUseList, 1);
mInUseList = memory;
unsigned char *ret = reinterpret_cast<unsigned char *>(mInUseList) + mHeaderSkip;
mCurrentPageOffset = (mHeaderSkip + allocationSize + mAlignmentMask) & ~mAlignmentMask;
return ret;
}
#endif
void PoolAllocator::lock()
{
ASSERT(!mLocked);
mLocked = true;
}
void PoolAllocator::unlock()
{
ASSERT(mLocked);
mLocked = false;
}
void Allocation::checkAllocList() const
{
for (const Allocation *alloc = this; alloc != 0; alloc = alloc->mPrevAlloc)
alloc->checkAlloc();
}
}