chore: implement base skeleton for architecture independent zone loading

This commit is contained in:
Jan Laupetin
2025-05-01 17:10:34 +02:00
committed by Jan
parent 280f74d4dd
commit f6d7831e6e
30 changed files with 393 additions and 176 deletions

View File

@ -10,22 +10,38 @@
#include <cstring>
#include <stack>
ZoneStreamFillReadAccessor::ZoneStreamFillReadAccessor(const void* buffer, const size_t bufferSize, const unsigned pointerByteCount)
: m_buffer(buffer),
m_buffer_size(bufferSize),
m_pointer_byte_count(pointerByteCount)
{
}
ZoneStreamFillReadAccessor ZoneStreamFillReadAccessor::AtOffset(const size_t offset) const
{
assert(offset < m_buffer_size);
return ZoneStreamFillReadAccessor(static_cast<const char*>(m_buffer) + offset, m_buffer_size - offset, m_pointer_byte_count);
}
namespace
{
class XBlockInputStream final : public ZoneInputStream
{
public:
XBlockInputStream(std::vector<XBlock*>& blocks, ILoadingStream& stream, const unsigned blockBitCount, const block_t insertBlock)
XBlockInputStream(
const unsigned pointerBitCount, const unsigned blockBitCount, std::vector<XBlock*>& blocks, const block_t insertBlock, ILoadingStream& stream)
: m_blocks(blocks),
m_stream(stream)
m_stream(stream),
m_pointer_byte_count(pointerBitCount / 8u),
m_block_bit_count(blockBitCount)
{
assert(pointerBitCount % 8u == 0u);
assert(insertBlock < static_cast<block_t>(blocks.size()));
const auto blockCount = static_cast<unsigned>(blocks.size());
m_block_offsets = std::make_unique<size_t[]>(blockCount);
std::memset(m_block_offsets.get(), 0, sizeof(size_t) * blockCount);
m_block_bit_count = blockBitCount;
assert(insertBlock < static_cast<block_t>(blocks.size()));
m_insert_block = blocks[insertBlock];
}
@ -87,50 +103,42 @@ namespace
void LoadDataInBlock(void* dst, const size_t size) override
{
assert(!m_block_stack.empty());
if (m_block_stack.empty())
return;
auto* block = m_block_stack.top();
if (block->m_buffer > dst || block->m_buffer + block->m_buffer_size < dst)
throw OutOfBlockBoundsException(block);
if (static_cast<uint8_t*>(dst) + size > block->m_buffer + block->m_buffer_size)
throw BlockOverflowException(block);
// Theoretically ptr should always be at the current block offset.
assert(dst == &block->m_buffer[m_block_offsets[block->m_index]]);
switch (block->m_type)
// If no block has been pushed, load raw
if (!m_block_stack.empty())
{
case XBlock::Type::BLOCK_TYPE_TEMP:
case XBlock::Type::BLOCK_TYPE_NORMAL:
m_stream.Load(dst, size);
break;
auto* block = m_block_stack.top();
case XBlock::Type::BLOCK_TYPE_RUNTIME:
memset(dst, 0, size);
break;
if (block->m_buffer > dst || block->m_buffer + block->m_buffer_size < dst)
throw OutOfBlockBoundsException(block);
case XBlock::Type::BLOCK_TYPE_DELAY:
assert(false);
break;
if (static_cast<uint8_t*>(dst) + size > block->m_buffer + block->m_buffer_size)
throw BlockOverflowException(block);
// Theoretically ptr should always be at the current block offset.
assert(dst == &block->m_buffer[m_block_offsets[block->m_index]]);
switch (block->m_type)
{
case XBlock::Type::BLOCK_TYPE_TEMP:
case XBlock::Type::BLOCK_TYPE_NORMAL:
m_stream.Load(dst, size);
break;
case XBlock::Type::BLOCK_TYPE_RUNTIME:
std::memset(dst, 0, size);
break;
case XBlock::Type::BLOCK_TYPE_DELAY:
assert(false);
break;
}
IncBlockPos(size);
}
else
{
m_stream.Load(dst, size);
}
IncBlockPos(size);
}
void IncBlockPos(const size_t size) override
{
assert(!m_block_stack.empty());
if (m_block_stack.empty())
return;
const auto* block = m_block_stack.top();
m_block_offsets[block->m_index] += size;
}
void LoadNullTerminated(void* dst) override
@ -162,25 +170,61 @@ namespace
m_block_offsets[block->m_index] = offset;
}
void** InsertPointer() override
ZoneStreamFillReadAccessor LoadWithFill(const size_t size) override
{
m_fill_buffer.reserve(size);
auto* dst = m_fill_buffer.data();
// If no block has been pushed, load raw
if (!m_block_stack.empty())
{
const auto* block = m_block_stack.top();
switch (block->m_type)
{
case XBlock::Type::BLOCK_TYPE_TEMP:
case XBlock::Type::BLOCK_TYPE_NORMAL:
m_stream.Load(dst, size);
break;
case XBlock::Type::BLOCK_TYPE_RUNTIME:
std::memset(dst, 0, size);
break;
case XBlock::Type::BLOCK_TYPE_DELAY:
assert(false);
break;
}
IncBlockPos(size);
}
else
{
m_stream.Load(dst, size);
}
return ZoneStreamFillReadAccessor(dst, size, m_pointer_byte_count);
}
void* InsertPointer() override
{
m_block_stack.push(m_insert_block);
Align(alignof(void*));
// Alignment of pointer should always be its size
Align(m_pointer_byte_count);
if (m_block_offsets[m_insert_block->m_index] + sizeof(void*) > m_insert_block->m_buffer_size)
if (m_block_offsets[m_insert_block->m_index] + m_pointer_byte_count > m_insert_block->m_buffer_size)
throw BlockOverflowException(m_insert_block);
auto* ptr = reinterpret_cast<void**>(&m_insert_block->m_buffer[m_block_offsets[m_insert_block->m_index]]);
auto* ptr = static_cast<void*>(&m_insert_block->m_buffer[m_block_offsets[m_insert_block->m_index]]);
IncBlockPos(sizeof(void*));
IncBlockPos(m_pointer_byte_count);
m_block_stack.pop();
return ptr;
}
void* ConvertOffsetToPointer(const void* offset) override
void* ConvertOffsetToPointerNative(const void* offset) override
{
// -1 because otherwise Block 0 Offset 0 would be just 0 which is already used to signalize a nullptr.
// So all offsets are moved by 1.
@ -200,7 +244,7 @@ namespace
return &block->m_buffer[blockOffset];
}
void* ConvertOffsetToAlias(const void* offset) override
void* ConvertOffsetToAliasNative(const void* offset) override
{
// For details see ConvertOffsetToPointer
const auto offsetInt = reinterpret_cast<uintptr_t>(offset) - 1u;
@ -220,6 +264,17 @@ namespace
}
private:
void IncBlockPos(const size_t size)
{
assert(!m_block_stack.empty());
if (m_block_stack.empty())
return;
const auto* block = m_block_stack.top();
m_block_offsets[block->m_index] += size;
}
void Align(const unsigned align)
{
assert(!m_block_stack.empty());
@ -238,12 +293,16 @@ namespace
std::stack<size_t> m_temp_offsets;
ILoadingStream& m_stream;
unsigned m_pointer_byte_count;
unsigned m_block_bit_count;
XBlock* m_insert_block;
std::vector<uint8_t> m_fill_buffer;
};
} // namespace
std::unique_ptr<ZoneInputStream> ZoneInputStream::Create(std::vector<XBlock*>& blocks, ILoadingStream& stream, unsigned blockBitCount, block_t insertBlock)
std::unique_ptr<ZoneInputStream> ZoneInputStream::Create(
const unsigned pointerBitCount, const unsigned blockBitCount, std::vector<XBlock*>& blocks, const block_t insertBlock, ILoadingStream& stream)
{
return std::make_unique<XBlockInputStream>(blocks, stream, blockBitCount, insertBlock);
return std::make_unique<XBlockInputStream>(pointerBitCount, blockBitCount, blocks, insertBlock, stream);
}

View File

@ -4,23 +4,85 @@
#include "Zone/Stream/IZoneStream.h"
#include "Zone/XBlock.h"
#include <cassert>
#include <memory>
#include <vector>
class ZoneStreamFillReadAccessor
{
public:
ZoneStreamFillReadAccessor(const void* buffer, size_t bufferSize, unsigned pointerByteCount);
[[nodiscard]] ZoneStreamFillReadAccessor AtOffset(size_t offset) const;
template<typename T> void Fill(T& value, const size_t offset) const
{
assert(offset + sizeof(T) <= m_buffer_size);
value = *reinterpret_cast<const T*>(static_cast<const char*>(m_buffer) + offset);
}
template<typename T> void FillArray(T* value, const size_t offset, const size_t arraySize) const
{
assert(offset + sizeof(T) * arraySize <= m_buffer_size);
std::memcpy(value, static_cast<const char*>(m_buffer) + offset, sizeof(T) * arraySize);
}
template<typename T> void FillPtr(T*& value, const size_t offset) const
{
assert(offset + m_pointer_byte_count <= m_buffer_size);
assert(m_pointer_byte_count <= sizeof(uintptr_t));
value = nullptr;
std::memcpy(&value, static_cast<const char*>(m_buffer) + offset, m_pointer_byte_count);
}
private:
const void* m_buffer;
size_t m_buffer_size;
unsigned m_pointer_byte_count;
};
class ZoneInputStream : public IZoneStream
{
public:
/**
* \brief Retrieves the new read position in the current block by aligning the position with the specified value and then returning the current read
* position in the block.
* \param align The alignment value that the read position is aligned with before being returned. This should typically be the alignment of the struct that
* should be read afterward.
* \return A pointer to the memory in which following load calls will load data to.
*/
virtual void* Alloc(unsigned align) = 0;
/**
* \copydoc ZoneInputStream#Alloc(unsigned)
*/
template<typename T> T* Alloc(const unsigned align)
{
return static_cast<T*>(Alloc(align));
}
/**
* \brief Loads data from the underlying stream without considering the current block or advancing the block position.
* The data is read directly to the specified location instead of block memory.
* \param dst The memory location to load data to.
* \param size The amount of data to load.
*/
virtual void LoadDataRaw(void* dst, size_t size) = 0;
/**
* \brief Loads data with the current blocks read operation into its block memory.
* Depending on the block type, the underlying stream might be read from or not.
* The current block position is advanced by the number of bytes read.
* The destination must be inside the current block's memory space, otherwise an exception is thrown.
* \param dst The destination where the data is loaded to. Must be inside the current block's memory bounds.
* \param size The amount of data to load.
*/
virtual void LoadDataInBlock(void* dst, size_t size) = 0;
virtual void IncBlockPos(size_t size) = 0;
virtual void LoadNullTerminated(void* dst) = 0;
virtual ZoneStreamFillReadAccessor LoadWithFill(size_t size) = 0;
template<typename T> void Load(T* dst)
{
@ -37,26 +99,27 @@ public:
LoadDataInBlock(const_cast<void*>(reinterpret_cast<const void*>(dst)), size);
}
virtual void** InsertPointer() = 0;
virtual void* InsertPointer() = 0;
template<typename T> T** InsertPointer()
template<typename T> T** InsertPointerNative()
{
return reinterpret_cast<T**>(InsertPointer());
return static_cast<T**>(InsertPointer());
}
virtual void* ConvertOffsetToPointer(const void* offset) = 0;
virtual void* ConvertOffsetToPointerNative(const void* offset) = 0;
template<typename T> T* ConvertOffsetToPointer(T* offset)
template<typename T> T* ConvertOffsetToPointerNative(T* offset)
{
return static_cast<T*>(ConvertOffsetToPointer(static_cast<const void*>(offset)));
return static_cast<T*>(ConvertOffsetToPointerNative(static_cast<const void*>(offset)));
}
virtual void* ConvertOffsetToAlias(const void* offset) = 0;
virtual void* ConvertOffsetToAliasNative(const void* offset) = 0;
template<typename T> T* ConvertOffsetToAlias(T* offset)
template<typename T> T* ConvertOffsetToAliasNative(T* offset)
{
return static_cast<T*>(ConvertOffsetToAlias(static_cast<const void*>(offset)));
return static_cast<T*>(ConvertOffsetToAliasNative(static_cast<const void*>(offset)));
}
static std::unique_ptr<ZoneInputStream> Create(std::vector<XBlock*>& blocks, ILoadingStream& stream, unsigned blockBitCount, block_t insertBlock);
static std::unique_ptr<ZoneInputStream>
Create(unsigned pointerBitCount, unsigned blockBitCount, std::vector<XBlock*>& blocks, block_t insertBlock, ILoadingStream& stream);
};