Bigfoot / Sonarqube (push) Successful in 5m42s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m25s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m5s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 32s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m4s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m27s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m11s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 29s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 55s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m0s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m5s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 56s
Bigfoot / Clang Format Checks (push) Successful in 12s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 43s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 25s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 52s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 30s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m19s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m1s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 48s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 49s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 35s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m20s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 36s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 42s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 50s
Only time I'm allowing a merge of single feature inside main (we prefer merging a version) But I need a first Sonarqube run Reviewed-on: #2 Co-authored-by: Romain BOULLARD <romain.boullard@protonmail.com>
235 lines
8.6 KiB
C++
235 lines
8.6 KiB
C++
/*********************************************************************
|
|
* \file MemoryMappedFile.cpp
|
|
*
|
|
* \author Romain BOULLARD
|
|
* \date August 2026
|
|
*********************************************************************/
|
|
#include <System/File/MemoryMappedFile.hpp>
|
|
|
|
#include <System/SystemAssertHandler.hpp>
|
|
|
|
#include <string>
|
|
|
|
#ifdef BIGFOOT_WINDOWS
|
|
#include <Windows.h>
|
|
#elif defined BIGFOOT_LINUX
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
namespace Bigfoot
|
|
{
|
|
MemoryMappedFile::MemoryMappedFile(const File& p_file)
|
|
{
|
|
const File absoluteFile = p_file.ToAbsolute();
|
|
const eastl::string_view path = absoluteFile.GetPath();
|
|
|
|
#ifdef BIGFOOT_WINDOWS
|
|
m_fileHandle = CreateFileA(path.data(),
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
nullptr,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
|
|
nullptr);
|
|
CRITICAL_ASSERT(SystemAssertHandler, m_fileHandle != INVALID_HANDLE_VALUE, "Failed to open file {}", path);
|
|
|
|
LARGE_INTEGER fileSize {};
|
|
[[maybe_unused]]
|
|
const BOOL gotFileSize = GetFileSizeEx(m_fileHandle, &fileSize);
|
|
CRITICAL_ASSERT(SystemAssertHandler, gotFileSize, "Failed to get the size of file {}", path);
|
|
m_size = static_cast<std::size_t>(fileSize.QuadPart);
|
|
|
|
if (m_size != 0)
|
|
{
|
|
m_mappingHandle = CreateFileMappingA(m_fileHandle, nullptr, PAGE_READONLY, 0, 0, nullptr);
|
|
CRITICAL_ASSERT(SystemAssertHandler, m_mappingHandle != nullptr, "Failed create the file mapping {}", path);
|
|
|
|
m_data = static_cast<std::byte*>(MapViewOfFile(m_mappingHandle, FILE_MAP_READ, 0, 0, 0));
|
|
CRITICAL_ASSERT(SystemAssertHandler, m_data != nullptr, "Failed to map the file {}", path);
|
|
}
|
|
#elif defined BIGFOOT_LINUX
|
|
m_fileDescriptor = open(path.data(), O_RDONLY);
|
|
CRITICAL_ASSERT(SystemAssertHandler, m_fileDescriptor != -1, "Failed to open file {}", path);
|
|
|
|
struct stat fileStat {};
|
|
[[maybe_unused]]
|
|
const int statResult = fstat(m_fileDescriptor, &fileStat);
|
|
CRITICAL_ASSERT(SystemAssertHandler, statResult == 0, "Failed to get the size of file {}", path);
|
|
m_size = static_cast<std::size_t>(fileStat.st_size);
|
|
|
|
if (m_size != 0)
|
|
{
|
|
void* const mapped = mmap(nullptr, m_size, PROT_READ, MAP_PRIVATE, m_fileDescriptor, 0);
|
|
CRITICAL_ASSERT(SystemAssertHandler, mapped != MAP_FAILED, "Failed to map file {}", path);
|
|
m_data = static_cast<std::byte*>(mapped);
|
|
|
|
[[maybe_unused]]
|
|
const int adviseResult = madvise(mapped, m_size, MADV_RANDOM);
|
|
}
|
|
#else
|
|
CRITICAL_ASSERT(SystemAssertHandler, false, "MemoryMappedFileBase is not implemented on this platform");
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************************/
|
|
|
|
MemoryMappedFile::~MemoryMappedFile()
|
|
{
|
|
#ifdef BIGFOOT_WINDOWS
|
|
if (m_data != nullptr)
|
|
{
|
|
[[maybe_unused]]
|
|
const BOOL unmapped = UnmapViewOfFile(m_data);
|
|
CRITICAL_ASSERT(SystemAssertHandler, unmapped, "Failed to unmap the file");
|
|
}
|
|
|
|
if (m_mappingHandle != nullptr)
|
|
{
|
|
[[maybe_unused]]
|
|
const BOOL closedMapping = CloseHandle(m_mappingHandle);
|
|
CRITICAL_ASSERT(SystemAssertHandler, closedMapping, "Failed to close the file mapping");
|
|
}
|
|
|
|
if (m_fileHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
[[maybe_unused]]
|
|
const BOOL closedFile = CloseHandle(m_fileHandle);
|
|
CRITICAL_ASSERT(SystemAssertHandler, closedFile, "Failed to close the file");
|
|
}
|
|
#elif defined BIGFOOT_LINUX
|
|
if (m_data != nullptr)
|
|
{
|
|
[[maybe_unused]]
|
|
const int unmapped = munmap(m_data, m_size);
|
|
CRITICAL_ASSERT(SystemAssertHandler, unmapped == 0, "Failed to unmap the file");
|
|
}
|
|
|
|
if (m_fileDescriptor != -1)
|
|
{
|
|
[[maybe_unused]]
|
|
const int closedFile = close(m_fileDescriptor);
|
|
CRITICAL_ASSERT(SystemAssertHandler, closedFile == 0, "Failed to close the file");
|
|
}
|
|
#else
|
|
CRITICAL_ASSERT(SystemAssertHandler, false, "MemoryMappedFileBase is not implemented on this platform");
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************************/
|
|
|
|
eastl::span<const std::byte> MemoryMappedFile::GetData() const
|
|
{
|
|
return eastl::span<const std::byte> {m_data, m_size};
|
|
}
|
|
|
|
/****************************************************************************************/
|
|
|
|
WritableMemoryMappedFile::WritableMemoryMappedFile(const File& p_file, const std::size_t p_size):
|
|
m_size(p_size)
|
|
{
|
|
const File absoluteFile = p_file.ToAbsolute();
|
|
const eastl::string_view path = absoluteFile.GetPath();
|
|
|
|
#ifdef BIGFOOT_WINDOWS
|
|
m_fileHandle =
|
|
CreateFileA(path.data(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
CRITICAL_ASSERT(SystemAssertHandler, m_fileHandle != INVALID_HANDLE_VALUE, "Failed to create file {}", path);
|
|
|
|
const LARGE_INTEGER size {.QuadPart = static_cast<LONGLONG>(p_size)};
|
|
m_mappingHandle = CreateFileMappingA(m_fileHandle, nullptr, PAGE_READWRITE, size.HighPart, size.LowPart, nullptr);
|
|
CRITICAL_ASSERT(SystemAssertHandler, m_mappingHandle != nullptr, "Failed create the file mapping {}", path);
|
|
|
|
m_data = static_cast<std::byte*>(MapViewOfFile(m_mappingHandle, FILE_MAP_WRITE, 0, 0, 0));
|
|
CRITICAL_ASSERT(SystemAssertHandler, m_data != nullptr, "Failed to map the file {}", path);
|
|
#elif defined BIGFOOT_LINUX
|
|
m_fileDescriptor = open(path.data(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
|
|
CRITICAL_ASSERT(SystemAssertHandler, m_fileDescriptor != -1, "Failed to create file {}", path);
|
|
|
|
struct stat fileStat {};
|
|
fstat(m_fileDescriptor, &fileStat);
|
|
if (static_cast<std::size_t>(fileStat.st_size) < p_size)
|
|
{
|
|
[[maybe_unused]]
|
|
const int truncateResult = ftruncate(m_fileDescriptor, static_cast<off_t>(p_size));
|
|
CRITICAL_ASSERT(SystemAssertHandler, truncateResult == 0, "Failed to size file {}", path);
|
|
}
|
|
|
|
void* const mapped = mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fileDescriptor, 0);
|
|
CRITICAL_ASSERT(SystemAssertHandler, mapped != MAP_FAILED, "Failed to map file {}", path);
|
|
m_data = static_cast<std::byte*>(mapped);
|
|
#else
|
|
CRITICAL_ASSERT(SystemAssertHandler, false, "MemoryMappedFileBase is not implemented on this platform");
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************************/
|
|
|
|
WritableMemoryMappedFile::~WritableMemoryMappedFile()
|
|
{
|
|
#ifdef BIGFOOT_WINDOWS
|
|
if (m_data != nullptr)
|
|
{
|
|
[[maybe_unused]]
|
|
const BOOL unmapped = UnmapViewOfFile(m_data);
|
|
CRITICAL_ASSERT(SystemAssertHandler, unmapped, "Failed to unmap the file");
|
|
}
|
|
|
|
if (m_mappingHandle != nullptr)
|
|
{
|
|
[[maybe_unused]]
|
|
const BOOL closedMapping = CloseHandle(m_mappingHandle);
|
|
CRITICAL_ASSERT(SystemAssertHandler, closedMapping, "Failed to close the file mapping");
|
|
}
|
|
|
|
if (m_fileHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
[[maybe_unused]]
|
|
const BOOL closedFile = CloseHandle(m_fileHandle);
|
|
CRITICAL_ASSERT(SystemAssertHandler, closedFile, "Failed to close the file");
|
|
}
|
|
#elif defined BIGFOOT_LINUX
|
|
if (m_data != nullptr)
|
|
{
|
|
[[maybe_unused]]
|
|
const int unmapped = munmap(m_data, m_size);
|
|
CRITICAL_ASSERT(SystemAssertHandler, unmapped == 0, "Failed to unmap the file");
|
|
}
|
|
|
|
if (m_fileDescriptor != -1)
|
|
{
|
|
[[maybe_unused]]
|
|
const int closedFile = close(m_fileDescriptor);
|
|
CRITICAL_ASSERT(SystemAssertHandler, closedFile == 0, "Failed to close the file");
|
|
}
|
|
#else
|
|
CRITICAL_ASSERT(SystemAssertHandler, false, "MemoryMappedFileBase is not implemented on this platform");
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************************/
|
|
|
|
eastl::span<std::byte> WritableMemoryMappedFile::GetData() const
|
|
{
|
|
return eastl::span<std::byte> {m_data, m_size};
|
|
}
|
|
|
|
/****************************************************************************************/
|
|
|
|
void WritableMemoryMappedFile::Prefetch(const std::size_t p_offset, const std::size_t p_size) const
|
|
{
|
|
#ifdef BIGFOOT_WINDOWS
|
|
WIN32_MEMORY_RANGE_ENTRY range {.VirtualAddress = m_data + p_offset, .NumberOfBytes = p_size};
|
|
[[maybe_unused]]
|
|
const BOOL prefetched = PrefetchVirtualMemory(GetCurrentProcess(), 1, &range, 0);
|
|
#elif defined BIGFOOT_LINUX
|
|
[[maybe_unused]]
|
|
const int adviseResult = madvise(m_data + p_offset, p_size, MADV_WILLNEED);
|
|
#else
|
|
CRITICAL_ASSERT(SystemAssertHandler, false, "MemoryMappedFileBase is not implemented on this platform");
|
|
#endif
|
|
}
|
|
} // namespace Bigfoot
|