160 lines
3.4 KiB
C++
160 lines
3.4 KiB
C++
// BLANKART
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2024 by Ronald "Eidolon" Kinard
|
|
// Copyright (C) 2024 by Kart Krew
|
|
//
|
|
// This program is free software distributed under the
|
|
// terms of the GNU General Public License, version 2.
|
|
// See the 'LICENSE' file for more details.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "memory.h"
|
|
|
|
#include <new>
|
|
#include <cstdint>
|
|
|
|
#include "../cxxutil.hpp"
|
|
#include "../z_zone.h"
|
|
#include "../lua_script.h"
|
|
|
|
using namespace srb2;
|
|
|
|
namespace
|
|
{
|
|
|
|
class LinearMemory
|
|
{
|
|
size_t size_;
|
|
size_t height_;
|
|
void* memory_;
|
|
|
|
public:
|
|
constexpr explicit LinearMemory(size_t size) noexcept;
|
|
|
|
void* allocate(size_t size);
|
|
void reset() noexcept;
|
|
};
|
|
|
|
constexpr LinearMemory::LinearMemory(size_t size) noexcept : size_(size), height_{0}, memory_{nullptr} {}
|
|
|
|
void* LinearMemory::allocate(size_t size)
|
|
{
|
|
size_t aligned_size = (size + 15) & ~15;
|
|
if (height_ + aligned_size > size_)
|
|
{
|
|
throw std::bad_alloc();
|
|
}
|
|
|
|
if (memory_ == nullptr)
|
|
{
|
|
memory_ = Z_Malloc(size_, PU_STATIC, nullptr);
|
|
}
|
|
|
|
void* ptr = (void*)((uintptr_t)(memory_) + height_);
|
|
height_ += aligned_size;
|
|
return ptr;
|
|
}
|
|
|
|
void LinearMemory::reset() noexcept
|
|
{
|
|
height_ = 0;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
PoolAllocator::~PoolAllocator()
|
|
{
|
|
release();
|
|
}
|
|
|
|
constexpr static size_t nearest_multiple(size_t v, size_t divisor)
|
|
{
|
|
return (v + (divisor - 1)) & ~(divisor - 1);
|
|
}
|
|
|
|
PoolAllocator::ChunkFooter* PoolAllocator::allocate_chunk()
|
|
{
|
|
uint8_t* chunk = (uint8_t*)Z_Malloc(nearest_multiple(blocks_ * block_size_, alignof(ChunkFooter)) + sizeof(ChunkFooter), tag_, nullptr);
|
|
ChunkFooter* footer = (ChunkFooter*)(chunk + (blocks_ * block_size_));
|
|
footer->next = nullptr;
|
|
footer->start = (void*)chunk;
|
|
for (size_t i = 0; i < blocks_; i++)
|
|
{
|
|
FreeBlock* cur = (FreeBlock*)(chunk + (i * block_size_));
|
|
FreeBlock* next = (FreeBlock*)(chunk + ((i + 1) * block_size_));
|
|
cur->next = next;
|
|
}
|
|
((FreeBlock*)(chunk + ((blocks_ - 1) * block_size_)))->next = nullptr;
|
|
return footer;
|
|
}
|
|
|
|
void* PoolAllocator::allocate()
|
|
{
|
|
if (first_chunk_ == nullptr)
|
|
{
|
|
SRB2_ASSERT(head_ == nullptr);
|
|
|
|
// No chunks allocated yet
|
|
first_chunk_ = allocate_chunk();
|
|
head_ = (FreeBlock*)first_chunk_->start;
|
|
}
|
|
|
|
if (head_->next == nullptr)
|
|
{
|
|
// Current chunk will be full; allocate another at the end of the list
|
|
ChunkFooter* last_chunk = first_chunk_;
|
|
while (last_chunk->next != nullptr)
|
|
{
|
|
last_chunk = last_chunk->next;
|
|
}
|
|
ChunkFooter* new_chunk = allocate_chunk();
|
|
last_chunk->next = new_chunk;
|
|
head_->next = (FreeBlock*)new_chunk->start;
|
|
}
|
|
|
|
FreeBlock* ret = head_;
|
|
head_ = head_->next;
|
|
return ret;
|
|
}
|
|
|
|
void PoolAllocator::deallocate(void* p)
|
|
{
|
|
// Required in case this block is reused
|
|
LUA_InvalidateUserdata(p);
|
|
|
|
FreeBlock* block = reinterpret_cast<FreeBlock*>(p);
|
|
block->next = head_;
|
|
head_ = block;
|
|
}
|
|
|
|
void PoolAllocator::release()
|
|
{
|
|
ChunkFooter* next = nullptr;
|
|
for (ChunkFooter* i = first_chunk_; i != nullptr; i = next)
|
|
{
|
|
uint8_t *chunk = (uint8_t*)i->start;
|
|
for (size_t j = 0; j < blocks_; j++)
|
|
{
|
|
// Invalidate all blocks that possibly weren't passed to deallocate
|
|
LUA_InvalidateUserdata(chunk + (j * block_size_));
|
|
}
|
|
next = i->next;
|
|
Z_Free(i->start);
|
|
}
|
|
|
|
first_chunk_ = nullptr;
|
|
head_ = nullptr;
|
|
}
|
|
|
|
static LinearMemory g_frame_memory {4 * 1024 * 1024};
|
|
|
|
void* Z_Frame_Alloc(size_t size)
|
|
{
|
|
return g_frame_memory.allocate(size);
|
|
}
|
|
|
|
void Z_Frame_Reset()
|
|
{
|
|
g_frame_memory.reset();
|
|
}
|