diff --git a/src/xenia/base/mapped_memory.h b/src/xenia/base/mapped_memory.h index 771824a8f..90e8ee8b5 100644 --- a/src/xenia/base/mapped_memory.h +++ b/src/xenia/base/mapped_memory.h @@ -31,6 +31,8 @@ class MappedMemory { uint8_t* data() const { return reinterpret_cast(data_); } size_t size() const { return size_; } + virtual void Flush() = 0; + protected: MappedMemory(const std::wstring& path, Mode mode) : path_(path), mode_(mode), data_(nullptr), size_(0) {} @@ -41,6 +43,25 @@ class MappedMemory { size_t size_; }; +class ChunkedMappedMemoryWriter { + public: + virtual ~ChunkedMappedMemoryWriter() = default; + + static std::unique_ptr Open( + const std::wstring& path, size_t chunk_size); + + virtual uint8_t* Allocate(size_t length) = 0; + virtual void Flush() = 0; + virtual void FlushNew() = 0; + + protected: + ChunkedMappedMemoryWriter(const std::wstring& path, size_t chunk_size) + : path_(path), chunk_size_(chunk_size) {} + + std::wstring path_; + size_t chunk_size_; +}; + } // namespace xe #endif // XENIA_BASE_MAPPED_MEMORY_H_ diff --git a/src/xenia/base/mapped_memory_win.cc b/src/xenia/base/mapped_memory_win.cc index 70d3b9d49..dbe1d1ebb 100644 --- a/src/xenia/base/mapped_memory_win.cc +++ b/src/xenia/base/mapped_memory_win.cc @@ -11,6 +11,12 @@ #include +#include +#include +#include + +#include "xenia/base/math.h" + namespace xe { class Win32MappedMemory : public MappedMemory { @@ -32,6 +38,10 @@ class Win32MappedMemory : public MappedMemory { } } + void Flush() override { + FlushViewOfFile(data(), size()); + } + HANDLE file_handle; HANDLE mapping_handle; }; @@ -61,11 +71,11 @@ std::unique_ptr MappedMemory::Open(const std::wstring& path, break; } - SYSTEM_INFO systemInfo; - GetSystemInfo(&systemInfo); + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); const size_t aligned_offset = - offset & ~static_cast(systemInfo.dwAllocationGranularity - 1); + offset & ~static_cast(system_info.dwAllocationGranularity - 1); const size_t aligned_length = length + (offset - aligned_offset); auto mm = std::make_unique(path, mode); @@ -102,4 +112,139 @@ std::unique_ptr MappedMemory::Open(const std::wstring& path, return std::move(mm); } +class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter { + public: + Win32ChunkedMappedMemoryWriter(const std::wstring& path, size_t chunk_size) + : ChunkedMappedMemoryWriter(path, chunk_size) {} + + ~Win32ChunkedMappedMemoryWriter() override { + std::lock_guard lock(mutex_); + chunks_.clear(); + } + + uint8_t* Allocate(size_t length) override { + std::lock_guard lock(mutex_); + if (!chunks_.empty()) { + uint8_t* result = chunks_.back()->Allocate(length); + if (result != nullptr) { + return result; + } + } + auto chunk = std::make_unique(chunk_size_); + auto chunk_path = path_ + L"." + std::to_wstring(chunks_.size()); + if (!chunk->Open(chunk_path)) { + return nullptr; + } + uint8_t* result = chunk->Allocate(length); + chunks_.push_back(std::move(chunk)); + return result; + } + + void Flush() override { + std::lock_guard lock(mutex_); + for (auto& chunk : chunks_) { + chunk->Flush(); + } + } + + void FlushNew() override { + std::lock_guard lock(mutex_); + for (auto& chunk : chunks_) { + chunk->FlushNew(); + } + } + + private: + class Chunk { + public: + Chunk(size_t capacity) + : file_handle_(0), + mapping_handle_(0), + data_(nullptr), + offset_(0), + capacity_(capacity), + last_flush_offset_(0) {} + + ~Chunk() { + if (data_) { + UnmapViewOfFile(data_); + } + if (mapping_handle_) { + CloseHandle(mapping_handle_); + } + if (file_handle_) { + CloseHandle(file_handle_); + } + } + + bool Open(const std::wstring& path) { + DWORD file_access = GENERIC_READ | GENERIC_WRITE; + DWORD file_share = 0; + DWORD create_mode = OPEN_EXISTING; + DWORD mapping_protect = PAGE_READWRITE; + DWORD view_access = FILE_MAP_READ | FILE_MAP_WRITE; + + file_handle_ = CreateFile(path.c_str(), file_access, file_share, nullptr, + create_mode, FILE_ATTRIBUTE_NORMAL, nullptr); + if (!file_handle_) { + return false; + } + + mapping_handle_ = + CreateFileMapping(file_handle_, nullptr, mapping_protect, 0, + static_cast(capacity_), nullptr); + if (!mapping_handle_) { + return false; + } + + data_ = reinterpret_cast( + MapViewOfFile(mapping_handle_, view_access, 0, 0, capacity_)); + if (!data_) { + return false; + } + + return true; + } + + uint8_t* Allocate(size_t length) { + if (capacity_ - offset_ < length) { + return nullptr; + } + uint8_t* result = data_ + offset_; + offset_ += length; + return result; + } + + void Flush() { + FlushViewOfFile(data_, offset_); + } + + void FlushNew() { + FlushViewOfFile(data_ + last_flush_offset_, offset_ - last_flush_offset_); + last_flush_offset_ = offset_; + } + + private: + HANDLE file_handle_; + HANDLE mapping_handle_; + uint8_t* data_; + size_t offset_; + size_t capacity_; + size_t last_flush_offset_; + }; + + std::mutex mutex_; + std::vector> chunks_; +}; + +std::unique_ptr ChunkedMappedMemoryWriter::Open( + const std::wstring& path, size_t chunk_size) { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + size_t aligned_chunk_size = + xe::round_up(chunk_size, system_info.dwAllocationGranularity); + return std::make_unique(path, + aligned_chunk_size); +} + } // namespace xe