// // 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. // #ifndef COMMON_MEMORYBUFFER_H_ #define COMMON_MEMORYBUFFER_H_ #include #include #include "common/Optional.h" #include "common/angleutils.h" #include "common/debug.h" #include "common/span.h" #include "common/unsafe_buffers.h" namespace angle { // MemoryBuffers are used in place of std::vector when an uninitialized buffer // as would be obtained via malloc is required. class MemoryBuffer final : NonCopyable { public: MemoryBuffer() = default; ~MemoryBuffer(); MemoryBuffer(MemoryBuffer &&other); MemoryBuffer &operator=(MemoryBuffer &&other); // Free underlying memory. After this call, the MemoryBuffer has zero size // but can be reused given a subsequent resize()/reserve(). void destroy(); // Updates mSize to newSize. May cause a reallocation iff newSize > mCapacity. [[nodiscard]] bool resize(size_t newSize); // Updates mCapacity iff newCapacity > mCapacity. May cause a reallocation if // newCapacity > mCapacity. [[nodiscard]] bool reserve(size_t newCapacity); // Sets size to zero and updates mCapacity iff newCapacity > mCapacity. May cause // a reallocation if newCapacity is greater than mCapacity prior to the clear. [[nodiscard]] bool clearAndReserve(size_t newCapacity); // Sets size bound by capacity. void setSize(size_t size) { ASSERT(size <= mCapacity); mSize = size; } void setSizeToCapacity() { setSize(mCapacity); } // Sets current size to 0, but retains buffer for future use. void clear() { (void)resize(0); } size_t size() const { return mSize; } size_t capacity() const { return mCapacity; } bool empty() const { return mSize == 0; } const uint8_t *data() const { return mData; } uint8_t *data() { ASSERT(mData); return mData; } // Access entire buffer, although MemoryBuffer should be implicitly convertible to // any span implementation because it has both data() and size() methods. angle::Span span() { // SAFETY: `mData` is valid for `mSize` bytes. return ANGLE_UNSAFE_BUFFERS(angle::Span(mData, mSize)); } angle::Span span() const { // SAFETY: `mData` is valid for `mSize` bytes. return ANGLE_UNSAFE_BUFFERS(angle::Span(mData, mSize)); } // Convenience methods for accessing portions of the buffer. angle::Span first(size_t count) { return span().first(count); } angle::Span last(size_t count) { return span().last(count); } angle::Span subspan(size_t offset) { return span().subspan(offset); } angle::Span subspan(size_t offset, size_t count) { return span().subspan(offset, count); } uint8_t &operator[](size_t pos) { ASSERT(mData && pos < mSize); // SAFETY: assert on previous line. return ANGLE_UNSAFE_BUFFERS(mData[pos]); } const uint8_t &operator[](size_t pos) const { ASSERT(mData && pos < mSize); // SAFETY: assert on previous line. return ANGLE_UNSAFE_BUFFERS(mData[pos]); } void fill(uint8_t datum); // Only used by unit tests // Validate total bytes allocated during a resize void assertTotalAllocatedBytes(size_t totalAllocatedBytes) const { #if defined(ANGLE_ENABLE_ASSERTS) ASSERT(totalAllocatedBytes == mTotalAllocatedBytes); #endif // ANGLE_ENABLE_ASSERTS } // Validate total bytes copied during a resize void assertTotalCopiedBytes(size_t totalCopiedBytes) const { #if defined(ANGLE_ENABLE_ASSERTS) ASSERT(totalCopiedBytes == mTotalCopiedBytes); #endif // ANGLE_ENABLE_ASSERTS } // Validate data buffer is no longer present. Needed because data() may // assert a non-null buffer. void assertDataBufferFreed() const { ASSERT(mData == nullptr); } private: size_t mSize = 0; size_t mCapacity = 0; uint8_t *mData = nullptr; #if defined(ANGLE_ENABLE_ASSERTS) size_t mTotalAllocatedBytes = 0; size_t mTotalCopiedBytes = 0; #endif // ANGLE_ENABLE_ASSERTS }; class ScratchBuffer final : NonCopyable { public: ScratchBuffer(); // If we request a scratch buffer requesting a smaller size this many times, release the // scratch buffer. This ensures we don't have a degenerate case where we are stuck // hogging memory. Zero means eternal lifetime. ScratchBuffer(uint32_t lifetime); ~ScratchBuffer(); ScratchBuffer(ScratchBuffer &&other); ScratchBuffer &operator=(ScratchBuffer &&other); // Returns true with a memory buffer of the requested size, or false on failure. bool get(size_t requestedSize, MemoryBuffer **memoryBufferOut); // Same as get, but ensures new values are initialized to a fixed constant. bool getInitialized(size_t requestedSize, MemoryBuffer **memoryBufferOut, uint8_t initValue); // Ticks the release counter for the scratch buffer. Also done implicitly in get(). Memory // will be returned to the system after tick expiration. void tick(); // clear() the underlying MemoryBuffer, setting size to zero but retaining // any allocated memory. void clear(); // destroy() the underlying MemoryBuffer, setting size to zero and freeing // any allocated memory. void destroy(); MemoryBuffer *getMemoryBuffer() { return &mScratchMemory; } private: bool getImpl(size_t requestedSize, MemoryBuffer **memoryBufferOut, Optional initValue); uint32_t mLifetime; uint32_t mResetCounter; MemoryBuffer mScratchMemory; }; } // namespace angle #endif // COMMON_MEMORYBUFFER_H_