mapped file.

This commit is contained in:
Kelvin Sherlock 2016-02-01 22:32:21 -05:00
parent af4b0e2eeb
commit 532c62ec15
4 changed files with 512 additions and 1 deletions

View File

@ -111,7 +111,7 @@ add_custom_command(
# mpw-shell-execute.cpp mpw-shell-builtins.cpp
add_executable(mpw-shell mpw-shell.cpp mpw-shell-read.cpp mpw-shell-token.cpp mpw-shell-expand.cpp
mpw-shell-parser.cpp value.cpp mpw-shell-quote.cpp
phase1.cpp phase2.cpp phase2-parser.cpp command.cpp environment.cpp builtins.cpp)
phase1.cpp phase2.cpp phase2-parser.cpp command.cpp environment.cpp builtins.cpp mapped_file.cpp)
target_link_libraries(mpw-shell edit)

369
mapped_file.cpp Normal file
View File

@ -0,0 +1,369 @@
#include "mapped_file.h"
#include <memory>
#include <functional>
#include <system_error>
#include "unique_resource.h"
namespace {
class defer {
public:
typedef std::function<void()> FX;
defer() = default;
defer(FX fx) : _fx(fx) {}
defer(const defer &) = delete;
defer(defer &&) = default;
defer & operator=(const defer &) = delete;
defer & operator=(defer &&) = default;
void cancel() { _fx = nullptr; }
~defer() { if (_fx) _fx(); }
private:
FX _fx;
};
void throw_error(int error) {
throw std::system_error(error, std::system_category());
}
void throw_error(int error, const std::string &what) {
throw std::system_error(error, std::system_category(), what);
}
}
#ifdef _WIN32
#include <windows.h>
namespace {
void throw_error() {
throw_error(GetLastError());
}
void throw_error(const std::string &what) {
throw_error(GetLastError(), what);
}
}
void mapped_file_base::close() {
if (is_open()) {
UnmapViewOfFile(_data);
CloseHandle(_map_handle);
CloseHandle(_file_handle);
reset();
}
}
void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, size_t offset) {
HANDLE fh;
HANDLE mh;
LARGE_INTEGER file_size;
// length of 0 in CreateFileMapping / MapViewOfFile
// means map the entire file.
if (is_open()) {
throw std::runtime_error("mapped_file_base::open - file already open");
}
fh = CreateFile(p.c_str(),
flags == readonly ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
flags == readonly ? FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL,
nullptr
);
if (fh == INVALID_HANDLE_VALUE) {
throw_error();
}
auto fh_close = make_unique_resource(fh, CloseHandle);
GetFileSizeEx(fh, &file_size);
if (length == -1)
length = file_size.QuadPart;
DWORD protect = 0;
DWORD access = 0;
switch (flags) {
case readonly:
protect = PAGE_READONLY;
access = FILE_MAP_READ;
break;
case readwrite:
protect = PAGE_READWRITE;
access = FILE_MAP_WRITE;
break;
case priv:
protect = PAGE_WRITECOPY;
access = FILE_MAP_WRITE;
break;
}
mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0);
if (mh == INVALID_HANDLE_VALUE) {
throw_error();
}
auto mh_close = make_unique_resource(mh, CloseHandle);
_data = MapViewOfFileEx(mh,
access,
(DWORD)(offset >> 32),
(DWORD)offset,
length,
nullptr);
if (!_data) {
throw_error();
}
_file_handle = fh_close.release();
_map_handle = mh_close.release();
_size = length;
_flags = flags;
}
void mapped_file_base::create(const path_type& p, size_t length) {
const size_t offset = 0;
HANDLE fh;
HANDLE mh;
LARGE_INTEGER file_size;
const DWORD protect = PAGE_READWRITE;
const DWORD access = FILE_MAP_WRITE;
if (is_open()) {
throw std::runtime_error("mapped_file_base::create - file already open");
}
fh = CreateFile(p.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
nullptr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
nullptr
);
if (fh == INVALID_HANDLE_VALUE) {
throw_error();
}
auto fh_close = make_unique_resource(fh, CloseHandle);
file_size.QuadPart = length;
if (!SetFilePointerEx(fh, file_size, nullptr, FILE_BEGIN));
if (!SetEndOfFile(fh)) throw_error();
mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0);
if (mh == INVALID_HANDLE_VALUE) {
throw_error();
}
auto mh_close = make_unique_resource(mh, CloseHandle);
_data = MapViewOfFileEx(mh,
access,
(DWORD)(offset >> 32),
(DWORD)offset,
length,
nullptr);
if (!_data) {
throw_error();
}
_file_handle = fh_close.release();
_map_handle = mh_close.release();
_size = length;
_flags = readwrite;
}
#else
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <cerrno>
namespace {
void throw_error() {
throw_error(errno);
}
void throw_error(const std::string &what) {
throw_error(errno, what);
}
}
void mapped_file_base::close() {
if (is_open()) {
::munmap(_data, _size);
::close(_fd);
reset();
}
}
void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, size_t offset) {
int fd;
struct stat st;
int oflags = 0;
if (is_open()) {
throw std::runtime_error("mapped_file_base::open - file already open");
}
switch (flags) {
case readonly:
oflags = O_RDONLY;
break;
default:
oflags = O_RDWR;
break;
}
fd = ::open(p.c_str(), oflags);
if (fd < 0) {
throw_error(errno);
}
//defer([fd](){::close(fd); });
auto close_fd = make_unique_resource(fd, ::close);
if (::fstat(fd, &st) < 0) {
throw_error(errno);
}
if (length == -1) length = st.st_size;
_data = ::mmap(0, length,
flags == readonly ? PROT_READ : PROT_READ | PROT_WRITE,
flags == priv ? MAP_PRIVATE : MAP_SHARED,
fd, offset);
if (_data == MAP_FAILED) {
_data = nullptr;
throw_error(errno);
}
_fd = close_fd.release();
_size = length;
_flags = flags;
}
void mapped_file_base::create(const path_type& p, size_t length) {
int fd;
const size_t offset = 0;
if (is_open()) {
throw std::runtime_error("mapped_file_base::create - file already open");
}
fd = ::open(p.c_str(), O_RDWR | O_CREAT | O_TRUNC);
if (fd < 0) {
throw_error(errno);
}
//defer([fd](){::close(fd); });
auto close_fd = make_unique_resource(fd, ::close);
if (::ftruncate(fd, length) < 0) {
throw_error(errno);
}
_data = ::mmap(0, length,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd, offset);
if (_data == MAP_FAILED) {
_data = nullptr;
throw_error(errno);
}
_fd = close_fd.release();
_size = length;
_flags = readwrite;
}
#endif
void mapped_file_base::reset() {
_data = nullptr;
_size = 0;
_flags = readonly;
#ifdef _WIN32
_file_handle = nullptr;
_map_handle = nullptr;
#else
_fd = -1;
#endif
}
void mapped_file_base::swap(mapped_file_base &rhs)
{
if (std::addressof(rhs) != this) {
std::swap(_data, rhs._data);
std::swap(_size, rhs._size);
std::swap(_flags, rhs._flags);
#ifdef _WIN32
std::swap(_file_handle, rhs._file_handle);
std::swap(_map_handle, rhs._map_handle);
#else
std::swap(_fd, rhs._fd);
#endif
}
}
mapped_file::mapped_file(mapped_file &&rhs) : mapped_file() {
swap(rhs);
//rhs.reset();
}
mapped_file& mapped_file::operator=(mapped_file &&rhs) {
if (std::addressof(rhs) == this) return *this;
swap(rhs);
rhs.close();
//rhs.reset();
return *this;
}

129
mapped_file.h Normal file
View File

@ -0,0 +1,129 @@
#ifndef __mapped_file_h__
#define __mapped_file_h__
#ifdef HAVE_TSFS
#include <filesystem>
#else
#include <string>
#endif
#include <cstddef>
class mapped_file_base {
public:
#ifdef HAVE_TSFS
typedef std::filesystem::path path_type ;
#else
typedef std::string path_type ;
#endif
enum mapmode { readonly, readwrite, priv };
void close();
bool is_open() const {
return _data != nullptr;
}
size_t size() const {
return _size;
}
operator bool() const { return is_open(); }
bool operator !() const { return !is_open(); }
~mapped_file_base() { close(); }
protected:
void swap(mapped_file_base &rhs);
void open(const path_type& p, mapmode flags, size_t length, size_t offset);
void create(const path_type &p, size_t new_size); // always creates readwrite.
void reset();
size_t _size = 0;
void *_data = nullptr;
mapmode _flags = readonly;
#ifdef _WIN32
void *_file_handle = nullptr;
void *_map_handle = nullptr;
#else
int _fd = -1;
#endif
};
class mapped_file : public mapped_file_base {
typedef mapped_file_base base;
public:
typedef char *iterator;
typedef const char *const_iterator;
mapped_file() = default;
mapped_file(const path_type& p, mapmode flags = readonly, size_t length = -1, size_t offset = 0) {
open(p, flags, length, offset);
}
mapped_file(mapped_file &&);
mapped_file(const mapped_file &) = delete;
mapped_file &operator=(mapped_file &&);
mapped_file &operator=(const mapped_file &) = delete;
void open(const path_type& p, mapmode flags, size_t length = -1, size_t offset = 0) {
base::open(p, flags, length, offset);
}
const char *const_data() const {
return (const char *)_data;
}
const_iterator const_begin() const {
return const_data();
}
const_iterator const_end() const {
return const_data() + size();
}
char *data() const {
return _flags == readonly ? (char *)nullptr : (char *)_data;
}
iterator begin() const {
return _flags == readonly ? (iterator)nullptr : (iterator)_data;
}
iterator end() const {
return _flags == readonly ? (iterator)nullptr : (iterator)_data + size();
}
mapmode flags() const {
return _flags;
}
void swap(mapped_file &rhs) {
base::swap(rhs);
}
};
namespace std {
template<class T>
void swap(mapped_file &a, mapped_file &b) {
a.swap(b);
}
}
#endif

View File

@ -16,6 +16,8 @@
#include "phase2.h"
#include "command.h"
#include "mapped_file.h"
//#include <histedit.h>
#include <editline/readline.h>
@ -26,6 +28,14 @@ void init(Environment &e) {
e.set("echo", std::string("1"));
}
int read_file(phase1 &p, const std::string &file) {
mapped_file mf;
mf.open(file, mapped_file::readonly);
p.process(mf.const_begin(), mf.const_end(), true);
return 0;
}
int read_stdin(phase1 &p) {
unsigned char buffer[2048];
@ -110,6 +120,9 @@ int main(int argc, char **argv) {
p2.process(s);
};
*/
read_file(p1, "/Users/kelvin/mpw/Startup");
p2.finish();
interactive(p1);
p2.finish();