Skip to content

Commit

Permalink
Shim around mmap on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
ISSOtm committed Aug 31, 2020
1 parent dfd2f38 commit df77693
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 26 deletions.
6 changes: 6 additions & 0 deletions include/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,10 @@
# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif

/* MSVC does not have `mmap`, nor `<sys/mman.h>` */
#ifdef _MSC_VER
# define mmap rgbds_mmap
# include "mmap.h"
#endif

#endif /* RGBDS_PLATFORM_H */
84 changes: 58 additions & 26 deletions src/asm/lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* SPDX-License-Identifier: MIT
*/

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <ctype.h>
Expand All @@ -22,6 +22,7 @@
#include <unistd.h>

#include "extern/utf8decoder.h"
#include "platform.h" /* For `mmap` */

#include "asm/asm.h"
#include "asm/lexer.h"
Expand Down Expand Up @@ -312,12 +313,55 @@ static void initState(struct LexerState *state)
state->expansionOfs = 0;
}

/* Neither MSVC nor MinGW provide `mmap` */
#if defined(_MSC_VER) || defined(__MINGW32__)
# include <windows.h>
# include <fileapi.h>
# include <winbase.h>
# define MAP_FAILED NULL
# define mapFile(ptr, fd, path, size) do { \
HANDLE file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, \
FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_RANDOM_ACCESS, NULL); \
HANDLE mappingObj; \
\
if (file == INVALID_HANDLE_VALUE) \
break; \
mappingObj = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL); \
(ptr) = mappingObj == INVALID_HANDLE_VALUE \
? NULL \
: MapViewOfFile(mappingObj, FILE_MAP_READ, 0, 0, 0); \
CloseHandle(mappingObj); \
CloseHandle(file); \
} while (0)
# define munmap(ptr, size) UnmapViewOfFile((ptr))

#else /* defined(_MSC_VER) || defined(__MINGW32__) */

# include <sys/mman.h>
# define mapFile(ptr, fd, path, size) do { \
(ptr) = mmap(NULL, (size), PROT_READ, MAP_PRIVATE, (fd), 0); \
\
if ((ptr) == 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.
*/ \
if (verbose) \
printf("mmap(%s, MAP_PRIVATE) failed, retrying with MAP_SHARED\n", path); \
(ptr) = mmap(NULL, (size), PROT_READ, MAP_SHARED, (fd), 0); \
} \
} while (0)
#endif /* !( defined(_MSC_VER) || defined(__MINGW32__) ) */

struct LexerState *lexer_OpenFile(char const *path)
{
dbgPrint("Opening file \"%s\"\n", path);

bool isStdin = !strcmp(path, "-");
struct LexerState *state = malloc(sizeof(*state));
struct stat fileInfo;

/* Give stdin a nicer file name */
if (isStdin)
Expand All @@ -326,47 +370,36 @@ struct LexerState *lexer_OpenFile(char const *path)
yyerror("Failed to allocate memory for lexer state: %s", strerror(errno));
return NULL;
}
state->path = path;

state->isFile = true;
state->fd = isStdin ? STDIN_FILENO : open(path, O_RDONLY);
if (state->fd == -1) {
yyerror("Failed to open file \"%s\": %s", path, strerror(errno));
if (!isStdin && stat(path, &fileInfo) != 0) {
yyerror("Failed to stat file \"%s\": %s", path, strerror(errno));
free(state);
return NULL;
}
state->path = path;
state->isFile = true;
state->fd = isStdin ? STDIN_FILENO : open(path, O_RDONLY);
state->isMmapped = false; /* By default, assume it won't be mmap()ed */
off_t size = lseek(state->fd, 0, SEEK_END);

if (size != 1) {
/* The file is a regular file, so use `mmap` for better performance */
if (!isStdin && fileInfo.st_size > 0) {
/* Try using `mmap` for better performance */

/*
* Important: do NOT assign to `state->ptr` directly, to avoid a cast that may
* alter an eventual `MAP_FAILED` value. It would also invalidate `state->fd`,
* being on the other side of the union.
*/
void *pa = mmap(NULL, size, PROT_READ, MAP_PRIVATE, state->fd, 0);

if (pa == 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.
*/
pa = mmap(NULL, size, PROT_READ, MAP_SHARED, state->fd, 0);

if (pa == MAP_FAILED) {
void *mappingAddr;

mapFile(mappingAddr, state->fd, state->path, fileInfo.st_size);
if (mappingAddr == MAP_FAILED) {
/* If mmap()ing failed, try again using another method (below) */
state->isMmapped = false;
} else {
/* IMPORTANT: the `union` mandates this is accessed before other members! */
close(state->fd);

state->isMmapped = true;
state->ptr = pa;
state->size = size;
state->ptr = mappingAddr;
state->size = fileInfo.st_size;
state->offset = 0;

if (verbose)
Expand All @@ -378,7 +411,6 @@ struct LexerState *lexer_OpenFile(char const *path)
if (verbose)
printf("File %s opened as regular, errno reports \"%s\"\n",
path, strerror(errno));
lseek(state->fd, 0, SEEK_SET);
state->index = 0;
state->nbChars = 0;
}
Expand Down

0 comments on commit df77693

Please sign in to comment.