mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-06-22 12:47:53 -05:00
Add ZoneWriting basis
This commit is contained in:
@ -1,7 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
class IZoneOutputStream
|
||||
#include <cstddef>
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
|
||||
#include "Zone/Stream/IZoneStream.h"
|
||||
|
||||
class IZoneOutputStream : public IZoneStream
|
||||
{
|
||||
public:
|
||||
virtual void Align(int alignTo) = 0;
|
||||
};
|
||||
|
||||
virtual void* WriteDataRaw(void* dst, size_t size) = 0;
|
||||
virtual void* WriteDataInBlock(void* dst, size_t size) = 0;
|
||||
virtual void IncBlockPos(size_t size) = 0;
|
||||
virtual void WriteNullTerminated(void* dst) = 0;
|
||||
|
||||
virtual bool ReusableShouldWrite(void** pPtr, size_t size, size_t count, std::type_index type) = 0;
|
||||
|
||||
template<typename T>
|
||||
bool ReusableShouldWrite(T** pPtr)
|
||||
{
|
||||
return ReusableShouldWrite(pPtr, sizeof(T), 1, std::type_index(typeid(T)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ReusableShouldWrite(T** pPtr, size_t count)
|
||||
{
|
||||
return ReusableShouldWrite(pPtr, sizeof(T), count, std::type_index(typeid(T)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Write(T* dst)
|
||||
{
|
||||
return static_cast<T*>(WriteDataInBlock(const_cast<void*>(reinterpret_cast<const void*>(dst)), sizeof(T)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Write(T* dst, const uint32_t count)
|
||||
{
|
||||
return static_cast<T*>(WriteDataInBlock(const_cast<void*>(reinterpret_cast<const void*>(dst)), count * sizeof(T)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* WritePartial(T* dst, const size_t size)
|
||||
{
|
||||
return static_cast<T*>(WriteDataInBlock(const_cast<void*>(reinterpret_cast<const void*>(dst)), size));
|
||||
}
|
||||
};
|
||||
|
196
src/ZoneWriting/Zone/Stream/Impl/InMemoryZoneOutputStream.cpp
Normal file
196
src/ZoneWriting/Zone/Stream/Impl/InMemoryZoneOutputStream.cpp
Normal file
@ -0,0 +1,196 @@
|
||||
#include "InMemoryZoneOutputStream.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
InMemoryZoneOutputStream::InMemoryZoneOutputStream(InMemoryZoneData* zoneData, std::vector<XBlock*> blocks, const int blockBitCount, const block_t insertBlock)
|
||||
: m_zone_data(zoneData),
|
||||
m_blocks(std::move(blocks)),
|
||||
m_block_bit_count(blockBitCount),
|
||||
m_insert_block(m_blocks[insertBlock])
|
||||
{
|
||||
}
|
||||
|
||||
InMemoryZoneOutputStream::ReusableEntry::ReusableEntry(void* startPtr, const size_t entrySize, const size_t entryCount, const uintptr_t startZonePtr)
|
||||
: m_start_ptr(startPtr),
|
||||
m_end_ptr(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(startPtr) + entrySize * entryCount)),
|
||||
m_start_zone_ptr(startZonePtr),
|
||||
m_entry_size(entrySize), m_entry_count(entryCount)
|
||||
{
|
||||
}
|
||||
|
||||
void InMemoryZoneOutputStream::PushBlock(const block_t block)
|
||||
{
|
||||
assert(block >= 0 && block < static_cast<block_t>(m_blocks.size()));
|
||||
|
||||
auto* newBlock = m_blocks[block];
|
||||
|
||||
assert(newBlock->m_index == block);
|
||||
|
||||
m_block_stack.push(newBlock);
|
||||
|
||||
if (newBlock->m_type == XBlock::Type::BLOCK_TYPE_TEMP)
|
||||
{
|
||||
if (m_temp_sizes.empty())
|
||||
m_temp_sizes.push(0);
|
||||
else
|
||||
m_temp_sizes.push(m_temp_sizes.top());
|
||||
}
|
||||
}
|
||||
|
||||
block_t InMemoryZoneOutputStream::PopBlock()
|
||||
{
|
||||
assert(!m_block_stack.empty());
|
||||
|
||||
if (m_block_stack.empty())
|
||||
return -1;
|
||||
|
||||
auto* poppedBlock = m_block_stack.top();
|
||||
m_block_stack.pop();
|
||||
|
||||
// If temp block is popped, see if its size is bigger than the current maximum temp size
|
||||
if (poppedBlock->m_type == XBlock::Type::BLOCK_TYPE_TEMP)
|
||||
{
|
||||
const auto tempSize = m_temp_sizes.top();
|
||||
m_temp_sizes.pop();
|
||||
|
||||
if (tempSize > poppedBlock->m_buffer_size)
|
||||
poppedBlock->m_buffer_size = tempSize;
|
||||
}
|
||||
|
||||
return poppedBlock->m_index;
|
||||
}
|
||||
|
||||
void InMemoryZoneOutputStream::Align(const int align)
|
||||
{
|
||||
assert(!m_block_stack.empty());
|
||||
|
||||
if (align > 0)
|
||||
{
|
||||
auto* block = m_block_stack.top();
|
||||
|
||||
if (block->m_type == XBlock::Type::BLOCK_TYPE_TEMP)
|
||||
m_temp_sizes.top() = (m_temp_sizes.top() + align - 1) / align * align;
|
||||
else
|
||||
block->m_buffer_size = (block->m_buffer_size + align - 1) / align * align;
|
||||
}
|
||||
}
|
||||
|
||||
void* InMemoryZoneOutputStream::WriteDataRaw(void* src, const size_t size)
|
||||
{
|
||||
auto* result = m_zone_data->GetBufferOfSize(size);
|
||||
memcpy(result, src, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
void* InMemoryZoneOutputStream::WriteDataInBlock(void* src, const size_t size)
|
||||
{
|
||||
assert(!m_block_stack.empty());
|
||||
|
||||
if (m_block_stack.empty())
|
||||
return nullptr;
|
||||
|
||||
auto* block = m_block_stack.top();
|
||||
|
||||
void* result = nullptr;
|
||||
switch (block->m_type)
|
||||
{
|
||||
case XBlock::Type::BLOCK_TYPE_TEMP:
|
||||
case XBlock::Type::BLOCK_TYPE_NORMAL:
|
||||
result = m_zone_data->GetBufferOfSize(size);
|
||||
memcpy(result, src, size);
|
||||
break;
|
||||
|
||||
case XBlock::Type::BLOCK_TYPE_RUNTIME:
|
||||
break;
|
||||
|
||||
case XBlock::Type::BLOCK_TYPE_DELAY:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
IncBlockPos(size);
|
||||
return result;
|
||||
}
|
||||
|
||||
void InMemoryZoneOutputStream::IncBlockPos(const size_t size)
|
||||
{
|
||||
assert(!m_block_stack.empty());
|
||||
|
||||
if (m_block_stack.empty())
|
||||
return;
|
||||
|
||||
auto* block = m_block_stack.top();
|
||||
if (block->m_type == XBlock::Type::BLOCK_TYPE_TEMP)
|
||||
{
|
||||
m_temp_sizes.top() += size;
|
||||
}
|
||||
else
|
||||
{
|
||||
block->m_buffer_size += size;
|
||||
}
|
||||
}
|
||||
|
||||
void InMemoryZoneOutputStream::WriteNullTerminated(void* src)
|
||||
{
|
||||
const auto len = strlen(static_cast<char*>(src));
|
||||
WriteDataInBlock(src, len + 1);
|
||||
}
|
||||
|
||||
uintptr_t InMemoryZoneOutputStream::GetCurrentZonePointer()
|
||||
{
|
||||
assert(!m_block_stack.empty());
|
||||
|
||||
uintptr_t ptr = 0;
|
||||
ptr |= static_cast<uintptr_t>(m_block_stack.top()->m_index) << (sizeof(uintptr_t) * 8 - m_block_bit_count);
|
||||
ptr |= m_block_stack.top()->m_buffer_size & (UINTPTR_MAX >> m_block_bit_count);
|
||||
ptr++;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
uintptr_t InMemoryZoneOutputStream::InsertPointer()
|
||||
{
|
||||
PushBlock(m_insert_block->m_index);
|
||||
|
||||
Align(sizeof(uintptr_t));
|
||||
const auto result = GetCurrentZonePointer();
|
||||
IncBlockPos(sizeof(uintptr_t));
|
||||
|
||||
PopBlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool InMemoryZoneOutputStream::ReusableShouldWrite(void** pPtr, const size_t entrySize, const size_t entryCount, std::type_index type)
|
||||
{
|
||||
assert(!m_block_stack.empty());
|
||||
|
||||
const auto inTemp = m_block_stack.top()->m_type == XBlock::Type::BLOCK_TYPE_TEMP;
|
||||
const auto foundEntriesForType = m_reusable_entries.find(type);
|
||||
if (foundEntriesForType == m_reusable_entries.end())
|
||||
{
|
||||
std::vector<ReusableEntry> entries;
|
||||
auto zoneOffset = inTemp ? InsertPointer() : GetCurrentZonePointer();
|
||||
entries.emplace_back(*pPtr, entrySize, entryCount, zoneOffset);
|
||||
m_reusable_entries.emplace(std::make_pair(type, std::move(entries)));
|
||||
|
||||
*pPtr = inTemp ? PTR_INSERT : PTR_FOLLOWING;
|
||||
return true;
|
||||
}
|
||||
|
||||
for(const auto& entry : foundEntriesForType->second)
|
||||
{
|
||||
if(*pPtr >= entry.m_start_ptr && *pPtr < entry.m_end_ptr)
|
||||
{
|
||||
assert((reinterpret_cast<uintptr_t>(*pPtr) - reinterpret_cast<uintptr_t>(entry.m_start_ptr)) % entrySize == 0);
|
||||
*pPtr = reinterpret_cast<void*>(entry.m_start_zone_ptr + (reinterpret_cast<uintptr_t>(*pPtr) - reinterpret_cast<uintptr_t>(entry.m_start_ptr)));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto zoneOffset = inTemp ? InsertPointer() : GetCurrentZonePointer();
|
||||
foundEntriesForType->second.emplace_back(*pPtr, entrySize, entryCount, zoneOffset);
|
||||
|
||||
*pPtr = inTemp ? PTR_INSERT : PTR_FOLLOWING;
|
||||
return true;
|
||||
}
|
52
src/ZoneWriting/Zone/Stream/Impl/InMemoryZoneOutputStream.h
Normal file
52
src/ZoneWriting/Zone/Stream/Impl/InMemoryZoneOutputStream.h
Normal file
@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "Writing/InMemoryZoneData.h"
|
||||
#include "Zone/XBlock.h"
|
||||
#include "Zone/Stream/IZoneOutputStream.h"
|
||||
|
||||
class InMemoryZoneOutputStream final : public IZoneOutputStream
|
||||
{
|
||||
static constexpr void* PTR_FOLLOWING = reinterpret_cast<void*>(-1);
|
||||
static constexpr void* PTR_INSERT = reinterpret_cast<void*>(-2);
|
||||
|
||||
class ReusableEntry
|
||||
{
|
||||
public:
|
||||
void* m_start_ptr;
|
||||
void* m_end_ptr;
|
||||
uintptr_t m_start_zone_ptr;
|
||||
size_t m_entry_size;
|
||||
size_t m_entry_count;
|
||||
|
||||
ReusableEntry(void* startPtr, size_t entrySize, size_t entryCount, uintptr_t startZonePtr);
|
||||
};
|
||||
|
||||
InMemoryZoneData* m_zone_data;
|
||||
std::vector<XBlock*> m_blocks;
|
||||
|
||||
std::stack<XBlock*> m_block_stack;
|
||||
std::stack<size_t> m_temp_sizes;
|
||||
|
||||
int m_block_bit_count;
|
||||
XBlock* m_insert_block;
|
||||
|
||||
std::unordered_map<std::type_index, std::vector<ReusableEntry>> m_reusable_entries;
|
||||
|
||||
uintptr_t GetCurrentZonePointer();
|
||||
uintptr_t InsertPointer();
|
||||
|
||||
public:
|
||||
InMemoryZoneOutputStream(InMemoryZoneData* zoneData, std::vector<XBlock*> blocks, int blockBitCount, block_t insertBlock);
|
||||
|
||||
void PushBlock(block_t block) override;
|
||||
block_t PopBlock() override;
|
||||
void Align(int align) override;
|
||||
void* WriteDataRaw(void* src, size_t size) override;
|
||||
void* WriteDataInBlock(void* src, size_t size) override;
|
||||
void IncBlockPos(size_t size) override;
|
||||
void WriteNullTerminated(void* src) override;
|
||||
bool ReusableShouldWrite(void** pPtr, size_t entrySize, size_t entryCount, std::type_index type) override;
|
||||
};
|
Reference in New Issue
Block a user