Last active
June 21, 2023 20:51
-
-
Save hbobenicio/e258d68771e7db8910e0c81f9a0162b8 to your computer and use it in GitHub Desktop.
Read/Map file to memory at once with mmap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* This program demonstrates how to read/map a file into memory using mmap. | |
* | |
* In order to get the file size we also avoid the "problematic" fseek + SEEK_END approach. | |
* Instead, we use open/fstat for it (although fopen/fileno/fstat would also work just fine). | |
*/ | |
// libc | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <assert.h> | |
// posix | |
#include <errno.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <unistd.h> | |
// From man mmap page: | |
// > On POSIX systems on which mmap(), msync(2), and munmap() are available, | |
// > _POSIX_MAPPED_FILES is defined in <unistd.h> to a value greater than 0. (See also sysconf(3).) | |
#if _POSIX_MAPPED_FILES <= 0 | |
#error "Systems without mmap are not supported ATM" | |
#endif | |
#include <sys/mman.h> | |
// linux | |
#include <fcntl.h> | |
struct mmap_buffer { | |
unsigned char* data; | |
size_t size; | |
}; | |
void mmap_buffer_free(struct mmap_buffer* buffer) | |
{ | |
int rc = munmap(buffer->data, buffer->size); | |
assert(rc == 0); | |
buffer->data = NULL; | |
buffer->size = 0; | |
} | |
int file_load(const char* path, size_t max_file_size, struct mmap_buffer* out_buffer) | |
{ | |
int fd = open(path, O_RDONLY); | |
if (fd == -1) { | |
int error_code = errno; | |
fprintf(stderr, "error: failed to open file \"%s\": %s\n", path, strerror(error_code)); | |
return 1; | |
} | |
//fstat | |
struct stat file_stat = {0}; | |
int rc = fstat(fd, &file_stat); | |
if (rc != 0) { | |
int error_code = errno; | |
fprintf(stderr, "error: failed to get file stats: %s\n", strerror(error_code)); | |
goto err_close; | |
} | |
// signed int casting from off_t (aka long int) to size_t | |
size_t file_size = (size_t) file_stat.st_size; | |
fprintf(stderr, "info: file=\"%s\" size=%zu\n", path, file_size); | |
//Validate file size so we ensure it's ok to mmap it | |
if (file_size > max_file_size) { | |
fprintf(stderr, "error: file size is too large. file_size=%zu, max_file_size=%zu\n", file_size, max_file_size); | |
goto err_close; | |
} | |
// mmap has a huge range of parameters configuration and possible use cases. | |
// Adjust it to your own needs. | |
// NOTE: the returned pointer points to a buffer data, not a c string! | |
// Do not assume it's NUL-terminated! | |
unsigned char* file_data = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0); | |
if (file_data == MAP_FAILED) { | |
int error_code = errno; | |
fprintf(stderr, "error: failed to memory map file: %s\n", strerror(error_code)); | |
goto err_close; | |
} | |
rc = close(fd); | |
assert(rc == 0); | |
*out_buffer = (struct mmap_buffer) { | |
.data = file_data, | |
.size = file_size, | |
}; | |
return 0; | |
err_close: | |
rc = close(fd); | |
assert(rc == 0); | |
*out_buffer = (struct mmap_buffer) { | |
.data = NULL, | |
.size = 0, | |
}; | |
return 1; | |
} | |
int main(int argc, char** argv) | |
{ | |
assert(argc == 2); | |
const char* file_path = argv[1]; | |
// Adjust it to your own needs | |
const size_t max_file_size = 256 * 1024 * 1024; | |
struct mmap_buffer buffer; | |
int rc = file_load(file_path, max_file_size, &buffer); | |
if (rc != 0) { | |
fprintf(stderr, "error: the program has failed\n"); | |
exit(1); | |
} | |
fprintf(stderr, "info: done.\n"); | |
mmap_buffer_free(&buffer); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment