From 5975d2814b33979fd249ad1fc3b3226f8cf817e2 Mon Sep 17 00:00:00 2001 From: Sebastian Steinhauer Date: Fri, 27 Sep 2024 09:27:41 +0200 Subject: [PATCH 1/3] created memory mapped API --- include/SDL3/SDL_filesystem.h | 56 +++++++++++++++++++++++++++++ src/filesystem/SDL_filesystem.c | 26 ++++++++++++++ src/filesystem/SDL_sysfilesystem.h | 4 +++ src/filesystem/dummy/SDL_sysfsops.c | 19 ++++++++++ 4 files changed, 105 insertions(+) diff --git a/include/SDL3/SDL_filesystem.h b/include/SDL3/SDL_filesystem.h index 58d86efe14ac4..88ae00cff7bca 100644 --- a/include/SDL3/SDL_filesystem.h +++ b/include/SDL3/SDL_filesystem.h @@ -363,6 +363,62 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetPathInfo(const char *path, SDL_PathInfo */ extern SDL_DECLSPEC char ** SDLCALL SDL_GlobDirectory(const char *path, const char *pattern, SDL_GlobFlags flags, int *count); +/** + * The memory mapped file structure. + * + * This operates an opaque handle. + * + * \since This struct is available since SDL 3.0.0. + */ +typedef struct SDL_MemoryMappedFile SDL_MemoryMappedFile; + +/** + * Memory map all the data from a file path. + * + * The data will not be loaded into the memory right away, but the + * operating system will create memory region where you can read from. + * Every read from that memory region will cause the operating system + * to load that specific part from the file to RAM and cache it there. + * + * You have to unmap the file. + * + * \param file the path to read all available data from. + * \param offset the absolute offset of the file where the mapping + * should start. + * \returns a pointer to a new SDL_MemoryMappedFile structure or NULL on + * failure; call SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0. + * + */ +extern SDL_DECLSPEC SDL_MemoryMappedFile * SDLCALL SDL_MemoryMapFile(const char *file, size_t offset); + +/** + * Unmap a memory mapped file. + * + * This will unmap a previously mapped memory file. + * + * \param mmfile a pointer to SDL_MemoryMappedFile + * \returns true when the operation was successful or false on failure; + * call SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0. + */ +extern SDL_DECLSPEC bool SDLCALL SDL_UnmapMemoryFile(SDL_MemoryMappedFile *mmfile); + +/** + * Get the data and size from a memory mapped file. + * + * This will return a pointer to the memory mapped file content. + * + * \param mmfile a pointer to SDL_MemoryMappedFile + * \param datasize if not NULL, will store the number of bytes mapped + * \returns the data or NULL on failure; call SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0. + */ + extern SDL_DECLSPEC void * SDLCALL SDL_GetMemoryMappedData(const SDL_MemoryMappedFile *mmfile, size_t *datasize); + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/src/filesystem/SDL_filesystem.c b/src/filesystem/SDL_filesystem.c index 69056148ea551..9acda1c137c8d 100644 --- a/src/filesystem/SDL_filesystem.c +++ b/src/filesystem/SDL_filesystem.c @@ -451,6 +451,32 @@ char *SDL_GetPrefPath(const char *org, const char *app) } +SDL_MemoryMappedFile * SDL_MemoryMapFile(const char *file, size_t offset) { + if (file == NULL) { + SDL_InvalidParamError("file"); + return NULL; + } + return SDL_SYS_MemoryMapFile(file, offset); +} + + +bool SDL_UnmapMemoryFile(SDL_MemoryMappedFile *mmfile) { + if (mmfile == NULL) { + return SDL_InvalidParamError("mmfile"); + } + return SDL_SYS_UnmapMemoryFile(mmfile); +} + + +void * SDL_GetMemoryMappedData(const SDL_MemoryMappedFile *mmfile, size_t *datasize) { + if (mmfile == NULL) { + SDL_InvalidParamError("mmfile"); + return NULL; + } + return SDL_SYS_GetMemoryMappedData(mmfile, datasize); +} + + void SDL_InitFilesystem(void) { } diff --git a/src/filesystem/SDL_sysfilesystem.h b/src/filesystem/SDL_sysfilesystem.h index c35035bce3030..8aa158551c76b 100644 --- a/src/filesystem/SDL_sysfilesystem.h +++ b/src/filesystem/SDL_sysfilesystem.h @@ -34,6 +34,10 @@ extern bool SDL_SYS_CopyFile(const char *oldpath, const char *newpath); extern bool SDL_SYS_CreateDirectory(const char *path); extern bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info); +extern SDL_MemoryMappedFile *SDL_SYS_MemoryMapFile(const char *file, size_t offset); +extern bool SDL_SYS_UnmapMemoryFile(SDL_MemoryMappedFile *mmfile); +extern void *SDL_SYS_GetMemoryMappedData(const SDL_MemoryMappedFile *mmfile, size_t *datasize); + typedef bool (*SDL_GlobEnumeratorFunc)(const char *path, SDL_EnumerateDirectoryCallback cb, void *cbuserdata, void *userdata); typedef bool (*SDL_GlobGetPathInfoFunc)(const char *path, SDL_PathInfo *info, void *userdata); extern char **SDL_InternalGlobDirectory(const char *path, const char *pattern, SDL_GlobFlags flags, int *count, SDL_GlobEnumeratorFunc enumerator, SDL_GlobGetPathInfoFunc getpathinfo, void *userdata); diff --git a/src/filesystem/dummy/SDL_sysfsops.c b/src/filesystem/dummy/SDL_sysfsops.c index f1acdfd07caf0..7802e87f7e803 100644 --- a/src/filesystem/dummy/SDL_sysfsops.c +++ b/src/filesystem/dummy/SDL_sysfsops.c @@ -59,5 +59,24 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info) return SDL_Unsupported(); } +struct SDL_MemoryMappedFile {}; + +SDL_MemoryMappedFile *SDL_SYS_MemoryMapFile(const char *file, size_t offset) { + (void)file; (void)offset; + SDL_Unsupported(); + return NULL; +} + +bool SDL_SYS_UnmapMemoryFile(SDL_MemoryMappedFile *mmfile) { + (void)mmfile; + return SDL_Unsupported(); +} + +void *SDL_SYS_GetMemoryMappedData(const SDL_MemoryMappedFile *mmfile, size_t *datasize) { + (void)mmfile; (void)datasize; + SDL_Unsupported(); + return NULL; +} + #endif // SDL_FSOPS_DUMMY From 917af94a0613eaa50a3be83e8a079b38eb8e6b0f Mon Sep 17 00:00:00 2001 From: Sebastian Steinhauer Date: Fri, 27 Sep 2024 09:27:56 +0200 Subject: [PATCH 2/3] added POSIX implementation --- src/filesystem/posix/SDL_sysfsops.c | 66 +++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/filesystem/posix/SDL_sysfsops.c b/src/filesystem/posix/SDL_sysfsops.c index 9b1d963dfb3ba..8ebd8a61903db 100644 --- a/src/filesystem/posix/SDL_sysfsops.c +++ b/src/filesystem/posix/SDL_sysfsops.c @@ -33,6 +33,9 @@ #include #include #include +#include +#include +#include int SDL_SYS_EnumerateDirectory(const char *path, const char *dirname, SDL_EnumerateDirectoryCallback cb, void *userdata) { @@ -198,5 +201,68 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info) return true; } +/* define the internal SDL_MemoryMappedFile structure */ +struct SDL_MemoryMappedFile { + void *addr; + size_t size; +}; + +SDL_MemoryMappedFile *SDL_SYS_MemoryMapFile(const char *file, size_t offset) { + struct stat statbuf; + SDL_MemoryMappedFile *mmfile; + + const int fd = open(file, O_RDONLY); + if (fd < 0) { + SDL_SetError("Can't open file: %s", strerror(errno)); + return NULL; + } + if (fstat(fd, &statbuf)) { + close(fd); + SDL_SetError("Can't stat: %s", strerror(errno)); + return NULL; + } + if (offset >= statbuf.st_size) { + close(fd); + SDL_SetError("Can't use bigger offset"); + return NULL; + } + if ((mmfile = SDL_malloc(sizeof(SDL_MemoryMappedFile))) == NULL) { + close(fd); + SDL_SetError("Can't allocate SDL_MemoryMappedFile"); + return NULL; + } + mmfile->size = statbuf.st_size - offset; + if ((mmfile->addr = mmap(NULL, mmfile->size, PROT_READ, MAP_PRIVATE, fd, offset)) == NULL) { + close(fd); + SDL_free(mmfile); + SDL_SetError("Can't mmap: %s", strerror(errno)); + return NULL; + } + close(fd); /* file descriptor can be closed after mapping the file */ + return mmfile; +} + +bool SDL_SYS_UnmapMemoryFile(SDL_MemoryMappedFile *mmfile) { + if (mmfile->addr == NULL) { + return SDL_SetError("Invalid memory mapped file"); + } + if (munmap(mmfile->addr, mmfile->size)) { + return SDL_SetError("Can't munmap: %s", strerror(errno)); + } + SDL_free(mmfile); + return true; +} + +void * SDL_SYS_GetMemoryMappedData(const SDL_MemoryMappedFile *mmfile, size_t *datasize) { + if ((mmfile == NULL) || (mmfile->addr == NULL)) { + SDL_SetError("Invalid memory mapped file"); + return NULL; + } + if (datasize != NULL) { + *datasize = mmfile->size; + } + return mmfile->addr; +} + #endif // SDL_FSOPS_POSIX From d05ba29bb7088990b1075fa909ea9bebb0802d60 Mon Sep 17 00:00:00 2001 From: Sebastian Steinhauer Date: Fri, 27 Sep 2024 09:28:12 +0200 Subject: [PATCH 3/3] mmap: added Windows implementation --- src/filesystem/windows/SDL_sysfsops.c | 72 +++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/filesystem/windows/SDL_sysfsops.c b/src/filesystem/windows/SDL_sysfsops.c index 29aa4e7992a15..ab19aca473f0e 100644 --- a/src/filesystem/windows/SDL_sysfsops.c +++ b/src/filesystem/windows/SDL_sysfsops.c @@ -207,5 +207,77 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info) return true; } +/* define the internal SDL_MemoryMappedFile structure */ +struct SDL_MemoryMappedFile { + void *addr; + size_t size; + HANDLE file_handle, map_handle; +}; + +SDL_MemoryMappedFile *SDL_SYS_MemoryMapFile(const char *file, size_t offset) { + HANDLE fileHandle, mapHandle; + size_t size; + void *addr; + SDL_MemoryMappedFile *mmfile; + + file_handle = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (file_handle == INVALID_HANDLE_VALUE) { + WIN_SetError("Can't open"); + return NULL; + } + size = GetFileLength(file_handle); + if (offset >= size) { + CloseHandle(file_handle); + SDL_SetError("Can't use offset"); + return NULL; + } + map_handle = CreateFileMapping(file_handle, NULL, PAGE_READONLY, 0, 0, NULL); + if (map_handle == NULL) { + CloseHandle(file_handle); + WIN_SetError("Can't create map"); + return NULL; + } + addr = MapViewOfFile(map_handle, FILE_MAP_READ, 0, offset, 0); + if (addr == NULL) { + CloseHandle(map_handle); + CloseHandle(file_handle); + WIN_SetError("Can't crate view"); + return NULL; + } + mmfile = SDL_malloc(sizeof(SDL_MemoryMappedFile)); + if (mmfile == NULL) { + CloseHandle(map_handle); + CloseHandle(file_handle); + SDL_SetError("Can't allocate SDL_MemoryMappedFile"); + return NULL; + } + mmfile->addr = addr; + mmfile->size = size - offset; + mmfile->file_handle = file_handle; + mmfile->map_handle = map_handle; + return mmfile; +} + +bool SDL_UnmapMemoryFile(SDL_MemoryMappedFile *mmfile) { + if (mmfile->addr != NULL) { + UnmapViewOfFile(mmfile->addr); + } + if (mmfile->map_handle != NULL) { + CloseHandle(mmfile->map_handle); + } + if (mmfile->file_handle != INVALID_FILE_HANDLE) { + CloseHandle(mmfile->file_handle); + } + SDL_free(mmfile); + return true; +} + +void *SDL_GetMemoryMappedData(const SDL_MemoryMappedFile *mmfile, size_t *datasize) { + if (datasize != NULL) { + *datasize = mmfile->size; + } + return mmfile->addr; +} + #endif // SDL_FSOPS_WINDOWS