1
0
mirror of synced 2024-11-27 17:10:51 +01:00
ImHex/source/providers/file_provider.cpp
WerWolv 5a0f965125 Use file mapping instead of of normal file IO
This drastically reduces disk reads and improves performance
2021-01-03 15:00:16 +01:00

170 lines
5.4 KiB
C++

#include "providers/file_provider.hpp"
#undef __STRICT_ANSI__
#include <cstdio>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <time.h>
#include "helpers/utils.hpp"
#include "helpers/project_file_handler.hpp"
namespace hex::prv {
FileProvider::FileProvider(std::string_view path) : Provider(), m_path(path) {
this->m_fileStatsValid = stat(path.data(), &this->m_fileStats) == 0;
this->m_readable = true;
this->m_writable = true;
#if defined(OS_WINDOWS)
LARGE_INTEGER fileSize = { 0 };
OFSTRUCT ofStruct;
this->m_file = reinterpret_cast<HANDLE>(OpenFile(path.data(), &ofStruct, OF_READ));
GetFileSizeEx(this->m_file, &fileSize);
this->m_fileSize = fileSize.QuadPart;
this->m_file = reinterpret_cast<HANDLE>(OpenFile(path.data(), &ofStruct, OF_READWRITE));
if (this->m_file == nullptr || this->m_file == INVALID_HANDLE_VALUE) {
this->m_file = reinterpret_cast<HANDLE>(OpenFile(path.data(), &ofStruct, OF_READ));
this->m_writable = false;
}
ScopeExit fileCleanup([this]{
this->m_readable = false;
this->m_file = nullptr;
CloseHandle(this->m_file);
});
if (this->m_file == nullptr || this->m_file == INVALID_HANDLE_VALUE) {
return;
}
this->m_mapping = CreateFileMapping(this->m_file, nullptr, PAGE_READWRITE, fileSize.HighPart, fileSize.LowPart, path.data());
if (this->m_file == nullptr || this->m_file == INVALID_HANDLE_VALUE) {
return;
}
ScopeExit mappingCleanup([this]{
this->m_readable = false;
this->m_mapping = nullptr;
CloseHandle(this->m_mapping);
});
this->m_mappedFile = MapViewOfFile(this->m_mapping, FILE_MAP_ALL_ACCESS, 0, 0, this->m_fileSize);
if (this->m_mappedFile == nullptr) {
this->m_readable = false;
return;
}
fileCleanup.release();
mappingCleanup.release();
ProjectFile::setFilePath(path);
#else
this->m_file = open(path.data(), O_RDWR);
if (this->m_file == -1) {
this->m_file = open(path.data(), O_RDONLY);
this->m_writable = false;
}
if (this->m_file == -1) {
this->m_readable = false;
return;
}
this->m_fileSize = this->m_fileStats.st_size;
this->m_mappedFile = mmap(nullptr, this->m_fileSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, this->m_file, 0);
#endif
}
FileProvider::~FileProvider() {
#if defined(OS_WINDOWS)
if (this->m_mappedFile != nullptr)
UnmapViewOfFile(this->m_mappedFile);
if (this->m_mapping != nullptr)
CloseHandle(this->m_mapping);
if (this->m_file != nullptr)
CloseHandle(this->m_file);
#else
munmap(this->m_mappedFile, this->m_fileSize);
close(this->m_file);
#endif
}
bool FileProvider::isAvailable() {
#if defined(OS_WINDOWS)
return this->m_file != nullptr && this->m_mapping != nullptr && this->m_mappedFile != nullptr;
#else
return this->m_file != -1 && this->m_mappedFile != nullptr;
#endif
}
bool FileProvider::isReadable() {
return isAvailable() && this->m_readable;
}
bool FileProvider::isWritable() {
return isAvailable() && this->m_writable;
}
void FileProvider::read(u64 offset, void *buffer, size_t size) {
if ((offset + size) > this->getSize() || buffer == nullptr || size == 0)
return;
memcpy(buffer, reinterpret_cast<u8*>(this->m_mappedFile) + offset, size);
for (u64 i = 0; i < size; i++)
if (this->m_patches.back().contains(offset + i))
reinterpret_cast<u8*>(buffer)[i] = this->m_patches.back()[offset + i];
}
void FileProvider::write(u64 offset, const void *buffer, size_t size) {
if (buffer == nullptr || size == 0)
return;
this->m_patches.push_back(this->m_patches.back());
for (u64 i = 0; i < size; i++)
this->m_patches.back()[offset + i] = reinterpret_cast<const u8*>(buffer)[i];
}
void FileProvider::readRaw(u64 offset, void *buffer, size_t size) {
if ((offset + size) > this->getSize() || buffer == nullptr || size == 0)
return;
memcpy(buffer, reinterpret_cast<u8*>(this->m_mappedFile) + offset, size);
}
void FileProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
if (buffer == nullptr || size == 0)
return;
memcpy(reinterpret_cast<u8*>(this->m_mappedFile) + offset, buffer, size);
}
size_t FileProvider::getActualSize() {
return this->m_fileSize;
}
std::vector<std::pair<std::string, std::string>> FileProvider::getDataInformation() {
std::vector<std::pair<std::string, std::string>> result;
result.emplace_back("File path", this->m_path);
result.emplace_back("Size", hex::toByteString(this->getActualSize()));
if (this->m_fileStatsValid) {
result.emplace_back("Creation time", ctime(&this->m_fileStats.st_ctime));
result.emplace_back("Last access time", ctime(&this->m_fileStats.st_atime));
result.emplace_back("Last modification time", ctime(&this->m_fileStats.st_mtime));
}
return result;
}
}