#include #include #include #include #include "lib/mlr_arch.h" #include "input/byte_readers.h" #include "lib/mlr_globals.h" #include "lib/mlrutil.h" #if MLR_ARCH_MMAP_ENABLED static char empty_buf[1] = { 0 }; #endif typedef struct _mmap_byte_reader_state_t { char* filename; int fd; char* sof; char* p; char* eof; } mmap_byte_reader_state_t; static int mmap_byte_reader_open_func(struct _byte_reader_t* pbr, char* prepipe, char* filename); static int mmap_byte_reader_read_func(struct _byte_reader_t* pbr); static void mmap_byte_reader_close_func(struct _byte_reader_t* pbr, char* prepipe); // ---------------------------------------------------------------- byte_reader_t* mmap_byte_reader_alloc() { byte_reader_t* pbr = mlr_malloc_or_die(sizeof(byte_reader_t)); pbr->pvstate = NULL; pbr->popen_func = mmap_byte_reader_open_func; pbr->pread_func = mmap_byte_reader_read_func; pbr->pclose_func = mmap_byte_reader_close_func; return pbr; } void mmap_byte_reader_free(byte_reader_t* pbr) { mmap_byte_reader_state_t* pstate = pbr->pvstate; if (pstate != NULL) { free(pstate->filename); // null-ok semantics } free(pbr); } // ---------------------------------------------------------------- static int mmap_byte_reader_open_func(struct _byte_reader_t* pbr, char* prepipe, char* filename) { #if MLR_ARCH_MMAP_ENABLED // popen is a stdio construct, not an mmap construct, and it can't be supported here. if (prepipe != NULL) { fprintf(stderr, "%s: coding error detected in file %s at line %d.\n", MLR_GLOBALS.bargv0, __FILE__, __LINE__); exit(1); } mmap_byte_reader_state_t* pstate = mlr_malloc_or_die(sizeof(mmap_byte_reader_state_t)); pstate->filename = mlr_strdup_or_die(filename); pstate->fd = open(filename, O_RDONLY); if (pstate->fd < 0) { perror("open"); fprintf(stderr, "%s: Couldn't open \"%s\" for read.\n", MLR_GLOBALS.bargv0, filename); exit(1); } struct stat stat; if (fstat(pstate->fd, &stat) < 0) { perror("fstat"); fprintf(stderr, "%s: could not fstat \"%s\"\n", MLR_GLOBALS.bargv0, filename); exit(1); } if (stat.st_size == 0) { // mmap doesn't allow us to map zero-length files but zero-length files do exist. pstate->sof = &empty_buf[0]; } else { pstate->sof = mmap(NULL, (size_t)stat.st_size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE, pstate->fd, (off_t)0); if (pstate->sof == MAP_FAILED) { perror("mmap"); fprintf(stderr, "%s: could not mmap \"%s\"\n", MLR_GLOBALS.bargv0, filename); exit(1); } } pstate->eof = pstate->sof + stat.st_size; pstate->p = pstate->sof; pbr->pvstate = pstate; return TRUE; #else fprintf(stderr, "%s: mmap is unsupported on this architecture.\n", MLR_GLOBALS.bargv0); exit(1); return TRUE; #endif } static int mmap_byte_reader_read_func(struct _byte_reader_t* pbr) { mmap_byte_reader_state_t* pstate = pbr->pvstate; if (pstate->p >= pstate->eof) { return EOF; } else { int c = *pstate->p; pstate->p++; return c; } } static void mmap_byte_reader_close_func(struct _byte_reader_t* pbr, char* prepipe) { mmap_byte_reader_state_t* pstate = pbr->pvstate; if (close(pstate->fd) < 0) { perror("close"); fprintf(stderr, "%s: close error on file \"%s\".\n", MLR_GLOBALS.bargv0, pstate->filename); exit(1); } }