blob: 98dce64c864502a435c87fc051b87e081d6eee53 [file] [log] [blame]
//
// Copyright 2014 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "common/MemoryBuffer.h"
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include "common/debug.h"
namespace angle
{
// MemoryBuffer implementation.
MemoryBuffer::~MemoryBuffer()
{
destroy();
}
void MemoryBuffer::destroy()
{
if (mData)
{
free(mData);
mData = nullptr;
}
mSize = 0;
mCapacity = 0;
#if defined(ANGLE_ENABLE_ASSERTS)
mTotalAllocatedBytes = 0;
mTotalCopiedBytes = 0;
#endif // ANGLE_ENABLE_ASSERTS
}
bool MemoryBuffer::resize(size_t newSize)
{
// If new size is within mCapacity, update mSize and early-return
if (newSize <= mCapacity)
{
mSize = newSize;
return true;
}
// New size exceeds mCapacity, need to reallocate and copy over previous content.
uint8_t *newMemory = static_cast<uint8_t *>(malloc(sizeof(uint8_t) * newSize));
if (newMemory == nullptr)
{
return false;
}
// Book keeping
#if defined(ANGLE_ENABLE_ASSERTS)
mTotalAllocatedBytes += newSize;
#endif // ANGLE_ENABLE_ASSERTS
if (mData)
{
if (mSize > 0)
{
// Copy the intersection of the old data and the new data
std::copy(mData, mData + mSize, newMemory);
// Book keeping
#if defined(ANGLE_ENABLE_ASSERTS)
mTotalCopiedBytes += mSize;
#endif // ANGLE_ENABLE_ASSERTS
}
free(mData);
}
mData = newMemory;
mSize = newSize;
mCapacity = newSize;
return true;
}
bool MemoryBuffer::clearAndReserve(size_t newSize)
{
clear();
return reserve(newSize);
}
bool MemoryBuffer::reserve(size_t newSize)
{
if (newSize <= mCapacity)
{
// Can already accommodate newSize, nothing to do.
return true;
}
// Cache original size
size_t originalSize = mSize;
if (!resize(newSize))
{
return false;
}
// reserve(...) won't affect mSize, reset to original value.
mSize = originalSize;
return true;
}
bool MemoryBuffer::append(const MemoryBuffer &other)
{
uint8_t *srcBuffer = other.mData;
const size_t srcBufferSize = other.mSize;
// Handle the corner case where we are appending to self
if (this == &other)
{
if (!reserve(mSize * 2))
{
return false;
}
srcBuffer = other.mData;
}
return appendRaw(srcBuffer, srcBufferSize);
}
bool MemoryBuffer::appendRaw(const uint8_t *buffer, const size_t bufferSize)
{
ASSERT(buffer && bufferSize > 0);
if (!reserve(mSize + bufferSize))
{
return false;
}
std::memcpy(mData + mSize, buffer, bufferSize);
mSize += bufferSize;
return true;
}
void MemoryBuffer::fill(uint8_t datum)
{
if (!empty())
{
std::fill(mData, mData + mSize, datum);
}
}
MemoryBuffer::MemoryBuffer(MemoryBuffer &&other) : MemoryBuffer()
{
*this = std::move(other);
}
MemoryBuffer &MemoryBuffer::operator=(MemoryBuffer &&other)
{
std::swap(mSize, other.mSize);
std::swap(mCapacity, other.mCapacity);
std::swap(mData, other.mData);
return *this;
}
namespace
{
static constexpr uint32_t kDefaultScratchBufferLifetime = 1000u;
} // anonymous namespace
// ScratchBuffer implementation.
ScratchBuffer::ScratchBuffer() : ScratchBuffer(kDefaultScratchBufferLifetime) {}
ScratchBuffer::ScratchBuffer(uint32_t lifetime) : mLifetime(lifetime), mResetCounter(lifetime) {}
ScratchBuffer::~ScratchBuffer() {}
ScratchBuffer::ScratchBuffer(ScratchBuffer &&other)
{
*this = std::move(other);
}
ScratchBuffer &ScratchBuffer::operator=(ScratchBuffer &&other)
{
std::swap(mLifetime, other.mLifetime);
std::swap(mResetCounter, other.mResetCounter);
std::swap(mScratchMemory, other.mScratchMemory);
return *this;
}
bool ScratchBuffer::get(size_t requestedSize, MemoryBuffer **memoryBufferOut)
{
return getImpl(requestedSize, memoryBufferOut, Optional<uint8_t>::Invalid());
}
bool ScratchBuffer::getInitialized(size_t requestedSize,
MemoryBuffer **memoryBufferOut,
uint8_t initValue)
{
return getImpl(requestedSize, memoryBufferOut, Optional<uint8_t>(initValue));
}
bool ScratchBuffer::getImpl(size_t requestedSize,
MemoryBuffer **memoryBufferOut,
Optional<uint8_t> initValue)
{
mScratchMemory.setSizeToCapacity();
if (mScratchMemory.size() == requestedSize)
{
mResetCounter = mLifetime;
*memoryBufferOut = &mScratchMemory;
return true;
}
if (mScratchMemory.size() > requestedSize)
{
tick();
}
if (mScratchMemory.size() < requestedSize)
{
if (!mScratchMemory.resize(requestedSize))
{
return false;
}
mResetCounter = mLifetime;
if (initValue.valid())
{
mScratchMemory.fill(initValue.value());
}
}
ASSERT(mScratchMemory.size() >= requestedSize);
*memoryBufferOut = &mScratchMemory;
return true;
}
void ScratchBuffer::tick()
{
if (mResetCounter > 0)
{
--mResetCounter;
if (mResetCounter == 0)
{
clear();
}
}
}
void ScratchBuffer::clear()
{
mResetCounter = mLifetime;
if (mScratchMemory.size() > 0)
{
mScratchMemory.clear();
}
}
} // namespace angle