Skip to content

Instantly share code, notes, and snippets.

@hbobenicio
Last active June 21, 2023 20:51
Show Gist options
  • Save hbobenicio/e258d68771e7db8910e0c81f9a0162b8 to your computer and use it in GitHub Desktop.
Save hbobenicio/e258d68771e7db8910e0c81f9a0162b8 to your computer and use it in GitHub Desktop.
Read/Map file to memory at once with mmap
/**
* 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