From 532c62ec15c55f2acb4ff6087b3a50fdf44666fa Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 1 Feb 2016 22:32:21 -0500 Subject: [PATCH] mapped file. --- CMakeLists.txt | 2 +- mapped_file.cpp | 369 ++++++++++++++++++++++++++++++++++++++++++++++++ mapped_file.h | 129 +++++++++++++++++ mpw-shell.cpp | 13 ++ 4 files changed, 512 insertions(+), 1 deletion(-) create mode 100644 mapped_file.cpp create mode 100644 mapped_file.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 09bb9f4..b954be1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/mapped_file.cpp b/mapped_file.cpp new file mode 100644 index 0000000..3d82233 --- /dev/null +++ b/mapped_file.cpp @@ -0,0 +1,369 @@ +#include "mapped_file.h" +#include +#include +#include + +#include "unique_resource.h" + +namespace { + class defer { + public: + typedef std::function 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 + +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 +#include +#include +#include +#include + +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; +} + + diff --git a/mapped_file.h b/mapped_file.h new file mode 100644 index 0000000..413adf5 --- /dev/null +++ b/mapped_file.h @@ -0,0 +1,129 @@ +#ifndef __mapped_file_h__ +#define __mapped_file_h__ + +#ifdef HAVE_TSFS +#include +#else +#include +#endif + +#include + + +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 + void swap(mapped_file &a, mapped_file &b) { + a.swap(b); + } +} + +#endif diff --git a/mpw-shell.cpp b/mpw-shell.cpp index f53068e..6cbd98b 100644 --- a/mpw-shell.cpp +++ b/mpw-shell.cpp @@ -16,6 +16,8 @@ #include "phase2.h" #include "command.h" +#include "mapped_file.h" + //#include #include @@ -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();