mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Replace platform-specific mmap with reading the entire .asm file
This commit is contained in:
@@ -13,8 +13,8 @@
|
|||||||
|
|
||||||
#include "platform.hpp" // SSIZE_MAX
|
#include "platform.hpp" // SSIZE_MAX
|
||||||
|
|
||||||
// This value is a compromise between `LexerState` allocation performance when `mmap` works, and
|
// This value is a compromise between `LexerState` allocation performance when reading the entire
|
||||||
// buffering performance when it doesn't/can't (e.g. when piping a file into RGBASM).
|
// file works, and buffering performance when it doesn't (e.g. when piping a file into RGBASM).
|
||||||
static constexpr size_t LEXER_BUF_SIZE = 64;
|
static constexpr size_t LEXER_BUF_SIZE = 64;
|
||||||
// The buffer needs to be large enough for the maximum `lexerState->peek()` lookahead distance
|
// The buffer needs to be large enough for the maximum `lexerState->peek()` lookahead distance
|
||||||
static_assert(LEXER_BUF_SIZE > 1, "Lexer buffer size is too small");
|
static_assert(LEXER_BUF_SIZE > 1, "Lexer buffer size is too small");
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <fstream>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
@@ -32,72 +33,6 @@
|
|||||||
// Include this last so it gets all type & constant definitions
|
// Include this last so it gets all type & constant definitions
|
||||||
#include "parser.hpp" // For token definitions, generated from parser.y
|
#include "parser.hpp" // For token definitions, generated from parser.y
|
||||||
|
|
||||||
// Neither MSVC nor MinGW provide `mmap`
|
|
||||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
|
||||||
// clang-format off: maintain `include` order
|
|
||||||
#define WIN32_LEAN_AND_MEAN // Include less from `windows.h`
|
|
||||||
#include <windows.h> // target architecture
|
|
||||||
// clang-format on
|
|
||||||
#include <fileapi.h> // CreateFileA
|
|
||||||
#include <handleapi.h> // CloseHandle
|
|
||||||
#include <memoryapi.h> // MapViewOfFile
|
|
||||||
#include <winbase.h> // CreateFileMappingA
|
|
||||||
|
|
||||||
static char *mapFile(int fd, std::string const &path, size_t) {
|
|
||||||
void *mappingAddr = nullptr;
|
|
||||||
if (HANDLE file = CreateFileA(
|
|
||||||
path.c_str(),
|
|
||||||
GENERIC_READ,
|
|
||||||
FILE_SHARE_READ,
|
|
||||||
nullptr,
|
|
||||||
OPEN_EXISTING,
|
|
||||||
FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_RANDOM_ACCESS,
|
|
||||||
nullptr
|
|
||||||
);
|
|
||||||
file != INVALID_HANDLE_VALUE) {
|
|
||||||
if (HANDLE mappingObj = CreateFileMappingA(file, nullptr, PAGE_READONLY, 0, 0, nullptr);
|
|
||||||
mappingObj != INVALID_HANDLE_VALUE) {
|
|
||||||
mappingAddr = MapViewOfFile(mappingObj, FILE_MAP_READ, 0, 0, 0);
|
|
||||||
CloseHandle(mappingObj);
|
|
||||||
}
|
|
||||||
CloseHandle(file);
|
|
||||||
}
|
|
||||||
return static_cast<char *>(mappingAddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FileUnmapDeleter {
|
|
||||||
FileUnmapDeleter(size_t) {}
|
|
||||||
|
|
||||||
void operator()(char *mappingAddr) { UnmapViewOfFile(mappingAddr); }
|
|
||||||
};
|
|
||||||
|
|
||||||
#else // defined(_MSC_VER) || defined(__MINGW32__)
|
|
||||||
#include <sys/mman.h>
|
|
||||||
|
|
||||||
static char *mapFile(int fd, std::string const &path, size_t size) {
|
|
||||||
void *mappingAddr = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
||||||
// LCOV_EXCL_START
|
|
||||||
if (mappingAddr == MAP_FAILED && errno == ENOTSUP) {
|
|
||||||
// The implementation may not support MAP_PRIVATE; try again with MAP_SHARED
|
|
||||||
// instead, offering, I believe, weaker guarantees about external modifications to
|
|
||||||
// the file while reading it. That's still better than not opening it at all, though.
|
|
||||||
verbosePrint("mmap(%s, MAP_PRIVATE) failed, retrying with MAP_SHARED\n", path.c_str());
|
|
||||||
mappingAddr = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
|
|
||||||
}
|
|
||||||
// LCOV_EXCL_STOP
|
|
||||||
return mappingAddr != MAP_FAILED ? static_cast<char *>(mappingAddr) : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FileUnmapDeleter {
|
|
||||||
size_t mappingSize;
|
|
||||||
|
|
||||||
FileUnmapDeleter(size_t mappingSize_) : mappingSize(mappingSize_) {}
|
|
||||||
|
|
||||||
void operator()(char *mappingAddr) { munmap(mappingAddr, mappingSize); }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // !( defined(_MSC_VER) || defined(__MINGW32__) )
|
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
|
||||||
// Bison 3.6 changed token "types" to "kinds"; cast to int for simple compatibility
|
// Bison 3.6 changed token "types" to "kinds"; cast to int for simple compatibility
|
||||||
@@ -406,39 +341,43 @@ void LexerState::setFileAsNextState(std::string const &filePath, bool updateStat
|
|||||||
}
|
}
|
||||||
path = filePath;
|
path = filePath;
|
||||||
|
|
||||||
int fd = open(path.c_str(), O_RDONLY);
|
|
||||||
if (fd < 0) {
|
|
||||||
// LCOV_EXCL_START
|
|
||||||
fatal("Failed to open file \"%s\": %s", path.c_str(), strerror(errno));
|
|
||||||
// LCOV_EXCL_STOP
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isMmapped = false;
|
|
||||||
|
|
||||||
if (size_t size = static_cast<size_t>(statBuf.st_size); statBuf.st_size > 0) {
|
if (size_t size = static_cast<size_t>(statBuf.st_size); statBuf.st_size > 0) {
|
||||||
// Try using `mmap` for better performance
|
// Read the entire file for better performance
|
||||||
if (char *mappingAddr = mapFile(fd, path, size); mappingAddr != nullptr) {
|
// Ideally we'd use C++20 `auto ptr = std::make_shared<char[]>(size)`,
|
||||||
close(fd);
|
// but it has insufficient compiler support
|
||||||
content.emplace<ViewedContent>(
|
auto ptr = std::shared_ptr<char[]>(new char[size]);
|
||||||
std::shared_ptr<char[]>(mappingAddr, FileUnmapDeleter(size)), size
|
|
||||||
);
|
|
||||||
verbosePrint("File \"%s\" is mmap()ped\n", path.c_str()); // LCOV_EXCL_LINE
|
|
||||||
isMmapped = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isMmapped) {
|
if (std::ifstream fs(path, std::ios::binary); !fs) {
|
||||||
// Sometimes mmap() fails or isn't available, so have a fallback
|
// LCOV_EXCL_START
|
||||||
content.emplace<BufferedContent>(fd);
|
fatal("Failed to open file \"%s\": %s", path.c_str(), strerror(errno));
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
} else if (!fs.read(ptr.get(), size)) {
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
fatal("Failed to read file \"%s\": %s", path.c_str(), strerror(errno));
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
}
|
||||||
|
content.emplace<ViewedContent>(ptr, size);
|
||||||
|
|
||||||
|
verbosePrint("File \"%s\" is fully read\n", path.c_str()); // LCOV_EXCL_LINE
|
||||||
|
} else {
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
if (statBuf.st_size == 0) {
|
if (statBuf.st_size == 0) {
|
||||||
verbosePrint("File \"%s\" is empty\n", path.c_str());
|
verbosePrint("File \"%s\" is empty\n", path.c_str());
|
||||||
} else {
|
} else {
|
||||||
verbosePrint(
|
verbosePrint("Failed to stat file \"%s\": %s\n", path.c_str(), strerror(errno));
|
||||||
"File \"%s\" is opened; errno reports: %s\n", path.c_str(), strerror(errno)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
|
// Have a fallback if reading the file failed
|
||||||
|
int fd = open(path.c_str(), O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
fatal("Failed to open file \"%s\": %s", path.c_str(), strerror(errno));
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
}
|
||||||
|
content.emplace<BufferedContent>(fd);
|
||||||
|
|
||||||
|
verbosePrint("File \"%s\" is opened\n", path.c_str()); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user