blob: 5d4f59409ae43e45599069dd0faea71b3437a1a4 [file] [log] [blame]
// Copyright 2023 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_TINT_UTILS_MEMORY_BUMP_ALLOCATOR_H_
#define SRC_TINT_UTILS_MEMORY_BUMP_ALLOCATOR_H_
#include <array>
#include <cstring>
#include <utility>
#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 {
static constexpr size_t kBlockSize = 64 * 1024;
/// Block is linked list of memory blocks.
/// Blocks are allocated out of heap memory.
struct Block {
uint8_t data[kBlockSize];
Block* next;
};
public:
/// 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
char* Allocate(size_t size_in_bytes) {
auto& block = data.block;
if (block.current_offset + size_in_bytes > kBlockSize) {
// Allocate a new block from the heap
auto* prev_block = block.current;
block.current = new Block;
if (!block.current) {
return nullptr; // out of memory
}
block.current->next = nullptr;
block.current_offset = 0;
if (prev_block) {
prev_block->next = block.current;
} else {
block.root = block.current;
}
}
auto* base = &block.current->data[0];
auto* ptr = reinterpret_cast<char*>(base + block.current_offset);
block.current_offset += size_in_bytes;
data.count++;
return ptr;
}
/// Frees all allocations from the allocator.
void Reset() {
auto* block = data.block.root;
while (block != nullptr) {
auto* next = block->next;
delete 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 {
struct {
/// The root block of the block linked list
Block* root = nullptr;
/// The current (end) block of the blocked linked list.
/// New allocations come from this block
Block* current = nullptr;
/// The byte offset in #current for the next allocation.
/// Initialized with kBlockSize so that the first allocation triggers a block
/// allocation.
size_t current_offset = kBlockSize;
} block;
size_t count = 0;
} data;
};
} // namespace tint
#endif // SRC_TINT_UTILS_MEMORY_BUMP_ALLOCATOR_H_