blob: 75d9d97b1711a47a870bb70b064fd411654fda98 [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 <stdlib.h>
#include <string.h>
#include <algorithm>
#include <utility>
#include "common/debug.h"
#include "common/unsafe_buffers.h"
namespace angle
{
// MemoryBuffer implementation.
MemoryBuffer::~MemoryBuffer()
{
destroy();
}
void MemoryBuffer::destroy()
{
free(std::exchange(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 (!reserve(newSize))
{
return false;
}
mSize = newSize;
return true;
}
bool MemoryBuffer::reserve(size_t newCapacity)
{
if (newCapacity <= mCapacity)
{
// Can already accommodate newCapacity, nothing to do.
return true;
}
uint8_t *newMemory = static_cast<uint8_t *>(malloc(newCapacity));
if (newMemory == nullptr)
{
return false;
}
// Book keeping
#if defined(ANGLE_ENABLE_ASSERTS)
mTotalAllocatedBytes += newCapacity;
#endif // ANGLE_ENABLE_ASSERTS
if (mSize > 0)
{
// Copy the intersection of the old data and the new data.
// SAFETY: Relies on correct size allocation above.
ANGLE_UNSAFE_BUFFERS(memcpy(newMemory, mData, mSize));
// Book keeping
#if defined(ANGLE_ENABLE_ASSERTS)
mTotalCopiedBytes += mSize;
#endif // ANGLE_ENABLE_ASSERTS
}
free(std::exchange(mData, newMemory));
mCapacity = newCapacity;
return true;
}
bool MemoryBuffer::clearAndReserve(size_t newCapacity)
{
clear();
return reserve(newCapacity);
}
void MemoryBuffer::fill(uint8_t datum)
{
if (!empty())
{
// SAFETY: `mData` is always valid for `mSize`.
ANGLE_UNSAFE_BUFFERS(std::fill(mData, mData + mSize, datum));
}
}
MemoryBuffer::MemoryBuffer(MemoryBuffer &&other)
{
*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)
{
destroy();
}
}
}
void ScratchBuffer::clear()
{
mResetCounter = mLifetime;
mScratchMemory.clear();
}
void ScratchBuffer::destroy()
{
mScratchMemory.destroy();
}
} // namespace angle