blob: 30875a599d09bea79d10a55e7f788bb8eb2752ee [file] [log] [blame]
// Copyright 2023 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef SRC_TINT_UTILS_MEMORY_BUMP_ALLOCATOR_H_
#define SRC_TINT_UTILS_MEMORY_BUMP_ALLOCATOR_H_
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstring>
#include <utility>
#include "src/tint/utils/macros/compiler.h"
#include "src/tint/utils/math/math.h"
#include "src/tint/utils/memory/bitcast.h"
namespace tint {
/// A allocator for chunks of memory. The memory is owned by the BumpAllocator. When the
/// BumpAllocator is freed all of the allocated memory is freed.
class BumpAllocator {
/// BlockHeader is linked list of memory blocks.
/// Blocks are allocated out of heap memory.
struct BlockHeader {
BlockHeader* next;
};
public:
/// The default size for a block's data. Allocations can be greater than this, but smaller
/// allocations will use this size.
static constexpr size_t kDefaultBlockDataSize = 64 * 1024;
/// Constructor
BumpAllocator() = default;
/// Move constructor
/// @param rhs the BumpAllocator to move
BumpAllocator(BumpAllocator&& rhs) { std::swap(data, rhs.data); }
/// Move assignment operator
/// @param rhs the BumpAllocator to move
/// @return this BumpAllocator
BumpAllocator& operator=(BumpAllocator&& rhs) {
if (this != &rhs) {
Reset();
std::swap(data, rhs.data);
}
return *this;
}
/// Destructor
~BumpAllocator() { Reset(); }
/// Allocates @p size_in_bytes from the current block, or from a newly allocated block if the
/// current block is full.
/// @param size_in_bytes the number of bytes to allocate
/// @returns the pointer to the allocated memory or `nullptr` if the memory can not be allocated
std::byte* Allocate(size_t size_in_bytes) {
if (TINT_UNLIKELY(data.current_offset + size_in_bytes < size_in_bytes)) {
return nullptr; // integer overflow
}
if (data.current_offset + size_in_bytes > data.current_data_size) {
// Allocate a new block from the heap
auto* prev_block = data.current;
size_t data_size = std::max(size_in_bytes, kDefaultBlockDataSize);
data.current = Bitcast<BlockHeader*>(new (std::nothrow)
std::byte[sizeof(BlockHeader) + data_size]);
if (TINT_UNLIKELY(!data.current)) {
return nullptr; // out of memory
}
data.current->next = nullptr;
data.current_data_size = data_size;
data.current_offset = 0;
if (prev_block) {
prev_block->next = data.current;
} else {
data.root = data.current;
}
}
auto* base = Bitcast<std::byte*>(data.current) + sizeof(BlockHeader);
auto* ptr = base + data.current_offset;
data.current_offset += size_in_bytes;
data.count++;
return ptr;
}
/// Frees all allocations from the allocator.
void Reset() {
auto* block = data.root;
while (block != nullptr) {
auto* next = block->next;
delete[] Bitcast<std::byte*>(block);
block = next;
}
data = {};
}
/// @returns the total number of allocations
size_t Count() const { return data.count; }
private:
BumpAllocator(const BumpAllocator&) = delete;
BumpAllocator& operator=(const BumpAllocator&) = delete;
struct {
/// The root block of the block linked list
BlockHeader* root = nullptr;
/// The current (end) block of the blocked linked list.
/// New allocations come from this block
BlockHeader* current = nullptr;
/// The byte offset in #current for the next allocation.
size_t current_offset = 0;
/// The size of the #current, excluding the header size
size_t current_data_size = 0;
/// Total number of allocations
size_t count = 0;
} data;
};
} // namespace tint
#endif // SRC_TINT_UTILS_MEMORY_BUMP_ALLOCATOR_H_