From 5ac943a14a42637eebc2a9deb0bda5630447d7f8 Mon Sep 17 00:00:00 2001 From: NewView <40295938+ClnViewer@users.noreply.github.com> Date: Wed, 8 May 2019 22:30:30 +0300 Subject: [PATCH 1/6] Create README.md --- dist/README.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 dist/README.md diff --git a/dist/README.md b/dist/README.md new file mode 100644 index 0000000..2fec437 --- /dev/null +++ b/dist/README.md @@ -0,0 +1,5 @@ +# Windows MinGW version adapted from PS + +- origin: https://github.com/termux/termux-elf-cleaner +- using mapping: https://github.com/mandreyel/mio + From 7b8a14f3b7e2ac40c269b3540b356ddf49739242 Mon Sep 17 00:00:00 2001 From: NewView <40295938+ClnViewer@users.noreply.github.com> Date: Wed, 8 May 2019 22:31:02 +0300 Subject: [PATCH 2/6] Add files via upload --- dist/android-elf-cleaner.zip | Bin 0 -> 87309 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 dist/android-elf-cleaner.zip diff --git a/dist/android-elf-cleaner.zip b/dist/android-elf-cleaner.zip new file mode 100644 index 0000000000000000000000000000000000000000..9035659149d15429b97f6d95c70a808738e46df0 GIT binary patch literal 87309 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 literal 0 HcmV?d00001 From a3431810230004fcb0ad3a1fff6f562d405de513 Mon Sep 17 00:00:00 2001 From: NewView <40295938+ClnViewer@users.noreply.github.com> Date: Wed, 8 May 2019 22:32:07 +0300 Subject: [PATCH 3/6] Update README.md --- dist/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/README.md b/dist/README.md index 2fec437..86193a1 100644 --- a/dist/README.md +++ b/dist/README.md @@ -2,4 +2,4 @@ - origin: https://github.com/termux/termux-elf-cleaner - using mapping: https://github.com/mandreyel/mio - +- download WIN32 binary: https://github.com/ClnViewer/termux-elf-cleaner/raw/master/dist/android-elf-cleaner.zip From c328934eb182c540fa5b7fa2212a58a5da1439d4 Mon Sep 17 00:00:00 2001 From: NewView <40295938+ClnViewer@users.noreply.github.com> Date: Wed, 8 May 2019 22:32:32 +0300 Subject: [PATCH 4/6] Update README.md --- dist/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dist/README.md b/dist/README.md index 86193a1..42d4884 100644 --- a/dist/README.md +++ b/dist/README.md @@ -2,4 +2,5 @@ - origin: https://github.com/termux/termux-elf-cleaner - using mapping: https://github.com/mandreyel/mio -- download WIN32 binary: https://github.com/ClnViewer/termux-elf-cleaner/raw/master/dist/android-elf-cleaner.zip +- download [WIN32 binary](https://github.com/ClnViewer/termux-elf-cleaner/raw/master/dist/android-elf-cleaner.zip) + From 3f30ed7ba106211aa86ce4b7557d7360c76f00bc Mon Sep 17 00:00:00 2001 From: NewView <40295938+ClnViewer@users.noreply.github.com> Date: Wed, 8 May 2019 22:32:56 +0300 Subject: [PATCH 5/6] Update README.md --- dist/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/README.md b/dist/README.md index 42d4884..9a62446 100644 --- a/dist/README.md +++ b/dist/README.md @@ -1,4 +1,4 @@ -# Windows MinGW version adapted from PS +# Windows MinGW build version adapted from PS - origin: https://github.com/termux/termux-elf-cleaner - using mapping: https://github.com/mandreyel/mio From 956402453f553cf888fb25dcd21b4f030f8c6a26 Mon Sep 17 00:00:00 2001 From: NewView <40295938+ClnViewer@users.noreply.github.com> Date: Wed, 8 May 2019 22:34:14 +0300 Subject: [PATCH 6/6] Windows MinGW build version --- android-elf-cleaner.cbp | 52 ++ android-elf-cleaner.cpp | 299 +++++++ mio.hpp | 1751 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 2102 insertions(+) create mode 100644 android-elf-cleaner.cbp create mode 100644 android-elf-cleaner.cpp create mode 100644 mio.hpp diff --git a/android-elf-cleaner.cbp b/android-elf-cleaner.cbp new file mode 100644 index 0000000..7813747 --- /dev/null +++ b/android-elf-cleaner.cbp @@ -0,0 +1,52 @@ + + + + + + diff --git a/android-elf-cleaner.cpp b/android-elf-cleaner.cpp new file mode 100644 index 0000000..3b182dd --- /dev/null +++ b/android-elf-cleaner.cpp @@ -0,0 +1,299 @@ + +/// Windows MinGW build version adapted from PS +/// https://github.com/termux/termux-elf-cleaner +/// using mapping: https://github.com/mandreyel/mio + +#include "mio.hpp" +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#endif + +#include +#include +#include + +#ifndef __ANDROID_API__ +#define __ANDROID_API__ 21 +#endif + +// Include a local elf.h copy as not all platforms have it. +#include "elf.h" + +#define DT_GNU_HASH 0x6ffffef5 +#define DT_VERSYM 0x6ffffff0 +#define DT_FLAGS_1 0x6ffffffb +#define DT_VERNEEDED 0x6ffffffe +#define DT_VERNEEDNUM 0x6fffffff + +#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ +#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ +#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ + +#if __ANDROID_API__ < 23 +#define SUPPORTED_DT_FLAGS_1 (DF_1_NOW | DF_1_GLOBAL) +#else +// The supported DT_FLAGS_1 values as of Android 6.0. +#define SUPPORTED_DT_FLAGS_1 (DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE) +#endif + +template +bool process_elf(uint8_t* bytes, size_t elf_file_size, char const* file_name) +{ + if (sizeof(ElfSectionHeaderType) > elf_file_size) + { + fprintf(stderr, "termux-elf-cleaner: Elf header for '%s' would end at %zu but file size only %zu\n", file_name, sizeof(ElfSectionHeaderType), elf_file_size); + return false; + } + ElfHeaderType* elf_hdr = reinterpret_cast(bytes); + + size_t last_section_header_byte = elf_hdr->e_shoff + sizeof(ElfSectionHeaderType) * elf_hdr->e_shnum; + if (last_section_header_byte > elf_file_size) + { + fprintf(stderr, "termux-elf-cleaner: Section header for '%s' would end at %zu but file size only %zu\n", file_name, last_section_header_byte, elf_file_size); + return false; + } + ElfSectionHeaderType* section_header_table = reinterpret_cast(bytes + elf_hdr->e_shoff); + + for (unsigned int i = 1; i < elf_hdr->e_shnum; i++) + { + ElfSectionHeaderType* section_header_entry = section_header_table + i; + if (section_header_entry->sh_type == SHT_DYNAMIC) + { + size_t const last_dynamic_section_byte = section_header_entry->sh_offset + section_header_entry->sh_size; + if (last_dynamic_section_byte > elf_file_size) + { + fprintf(stderr, "termux-elf-cleaner: Dynamic section for '%s' would end at %zu but file size only %zu\n", file_name, last_dynamic_section_byte, elf_file_size); + return false; + } + + size_t const dynamic_section_entries = section_header_entry->sh_size / sizeof(ElfDynamicSectionEntryType); + ElfDynamicSectionEntryType* const dynamic_section = + reinterpret_cast(bytes + section_header_entry->sh_offset); + + unsigned int last_nonnull_entry_idx = 0; + for (unsigned int j = dynamic_section_entries - 1; j > 0; j--) + { + ElfDynamicSectionEntryType* dynamic_section_entry = dynamic_section + j; + if (dynamic_section_entry->d_tag != DT_NULL) + { + last_nonnull_entry_idx = j; + break; + } + } + + for (unsigned int j = 0; j < dynamic_section_entries; j++) + { + ElfDynamicSectionEntryType* dynamic_section_entry = dynamic_section + j; + char const* removed_name = NULL; + switch (dynamic_section_entry->d_tag) + { +#if __ANDROID_API__ <= 21 + case DT_GNU_HASH: + removed_name = "DT_GNU_HASH"; + break; +#endif +#if __ANDROID_API__ < 23 + case DT_VERSYM: + removed_name = "DT_VERSYM"; + break; + case DT_VERNEEDED: + removed_name = "DT_VERNEEDED"; + break; + case DT_VERNEEDNUM: + removed_name = "DT_VERNEEDNUM"; + break; + case DT_VERDEF: + removed_name = "DT_VERDEF"; + break; + case DT_VERDEFNUM: + removed_name = "DT_VERDEFNUM"; + break; +#endif + case DT_RPATH: + removed_name = "DT_RPATH"; + break; +#if __ANDROID_API__ < 24 + case DT_RUNPATH: + removed_name = "DT_RUNPATH"; + break; +#endif + } + if (removed_name != NULL) + { + printf("termux-elf-cleaner: Removing the %s dynamic section entry from '%s'\n", removed_name, file_name); + // Tag the entry with DT_NULL and put it last: + dynamic_section_entry->d_tag = DT_NULL; + // Decrease j to process new entry index: + std::swap(dynamic_section[j--], dynamic_section[last_nonnull_entry_idx--]); + } + else if (dynamic_section_entry->d_tag == DT_FLAGS_1) + { + // Remove unsupported DF_1_* flags to avoid linker warnings. + int orig_d_val = + dynamic_section_entry->d_un.d_val; + int new_d_val = + (orig_d_val & SUPPORTED_DT_FLAGS_1); + if (new_d_val != orig_d_val) + { + printf("termux-elf-cleaner: Replacing unsupported DF_1_* flags %llu with %llu in '%s'\n", + (unsigned long long) orig_d_val, + (unsigned long long) new_d_val, + file_name); + dynamic_section_entry->d_un.d_val = new_d_val; + } + } + } + } +#if __ANDROID_API__ < 23 + else if (section_header_entry->sh_type == SHT_GNU_verdef || + section_header_entry->sh_type == SHT_GNU_verneed || + section_header_entry->sh_type == SHT_GNU_versym) + { + printf("termux-elf-cleaner: Removing version section from '%s'\n", file_name); + section_header_entry->sh_type = SHT_NULL; + } +#endif + } + return true; +} + + +int main(int argc, char const** argv) +{ + if (argc < 2 || (argc == 2 && strcmp(argv[1], "-h")==0)) + { + fprintf(stderr, "usage: %s \n", argv[0]); + fprintf(stderr, "\nProcesses ELF files to remove unsupported section types\n" + "and dynamic section entries which the Android linker (API %d)\nwarns about.\n", + __ANDROID_API__); + return 127; + } + + for (int i = 1; i < argc; i++) + { + char const *file_name = argv[i]; + +# ifdef _WIN32 + + std::error_code error{}; + mio::mmap_sink rw_mmap = mio::make_mmap_sink(file_name, 0, mio::map_entire_file, error); + + if (error) + { + const auto& errmsg = error.message(); + printf("error mapping file: %s, exiting...\n", errmsg.c_str()); + return error.value(); + } + + void *mem = &rw_mmap[0]; + size_t const file_size = rw_mmap.size(); + +# else + + int fd = open(file_name, O_RDWR); + if (fd < 0) + { + char* error_message; + if (asprintf(&error_message, "open(\"%s\")", file_name) == -1) + error_message = (char*) "open()"; + perror(error_message); + return 1; + } + + struct stat st; + if (fstat(fd, &st) < 0) + { + perror("fstat()"); + return 1; + } + + size_t const file_size = static_cast(st.st_size); + + if (file_size < sizeof(Elf32_Ehdr)) + { + close(fd); + continue; + } + + void* mem = mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (mem == MAP_FAILED) + { + perror("mmap()"); + return 1; + } +# endif + + uint8_t* bytes = reinterpret_cast(mem); + if (!(bytes[0] == 0x7F && bytes[1] == 'E' && bytes[2] == 'L' && bytes[3] == 'F')) + { + // Not the ELF magic number. +# ifdef _WIN32 + rw_mmap.unmap(); +# else + munmap(mem, file_size); + close(fd); +# endif + continue; + } + + if (bytes[/*EI_DATA*/5] != 1) + { + fprintf(stderr, "termux-elf-cleaner: Not little endianness in '%s'\n", file_name); +# ifdef _WIN32 + rw_mmap.unmap(); +# else + munmap(mem, file_size); + close(fd); +# endif + continue; + } + + uint8_t const bit_value = bytes[/*EI_CLASS*/4]; + if (bit_value == 1) + { + if (!process_elf(bytes, file_size, file_name)) + return 1; + } + else if (bit_value == 2) + { + if (!process_elf(bytes, file_size, file_name)) + return 1; + } + else + { + printf("termux-elf-cleaner: Incorrect bit value %d in '%s'\n", bit_value, file_name); + return 1; + } + +# ifdef _WIN32 + rw_mmap.sync(error); + if (error) + { + const auto& errmsg = error.message(); + printf("error mapping sync: %s, exiting...\n", errmsg.c_str()); + return error.value(); + } + rw_mmap.unmap(); + +# else + + if (msync(mem, file_size, MS_SYNC) < 0) + { + perror("msync()"); + return 1; + } + + munmap(mem, st.st_size); + close(fd); +# endif + } + return 0; +} diff --git a/mio.hpp b/mio.hpp new file mode 100644 index 0000000..1d43307 --- /dev/null +++ b/mio.hpp @@ -0,0 +1,1751 @@ +/* Copyright 2017 https://github.com/mandreyel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MIO_MMAP_HEADER +#define MIO_MMAP_HEADER + +// #include "mio/page.hpp" +/* Copyright 2017 https://github.com/mandreyel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MIO_PAGE_HEADER +#define MIO_PAGE_HEADER + +#ifdef _WIN32 +# include +#else +# include +#endif + +namespace mio { + +/** + * This is used by `basic_mmap` to determine whether to create a read-only or + * a read-write memory mapping. + */ +enum class access_mode +{ + read, + write +}; + +/** + * Determines the operating system's page allocation granularity. + * + * On the first call to this function, it invokes the operating system specific syscall + * to determine the page size, caches the value, and returns it. Any subsequent call to + * this function serves the cached value, so no further syscalls are made. + */ +inline size_t page_size() +{ + static const size_t page_size = [] + { +#ifdef _WIN32 + SYSTEM_INFO SystemInfo; + GetSystemInfo(&SystemInfo); + return SystemInfo.dwAllocationGranularity; +#else + return sysconf(_SC_PAGE_SIZE); +#endif + }(); + return page_size; +} + +/** + * Alligns `offset` to the operating's system page size such that it subtracts the + * difference until the nearest page boundary before `offset`, or does nothing if + * `offset` is already page aligned. + */ +inline size_t make_offset_page_aligned(size_t offset) noexcept +{ + const size_t page_size_ = page_size(); + // Use integer division to round down to the nearest page alignment. + return offset / page_size_ * page_size_; +} + +} // namespace mio + +#endif // MIO_PAGE_HEADER + + +#include +#include +#include +#include + +#ifdef _WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif // WIN32_LEAN_AND_MEAN +# include +#else // ifdef _WIN32 +# define INVALID_HANDLE_VALUE -1 +#endif // ifdef _WIN32 + +namespace mio { + +// This value may be provided as the `length` parameter to the constructor or +// `map`, in which case a memory mapping of the entire file is created. +enum { map_entire_file = 0 }; + +#ifdef _WIN32 +using file_handle_type = HANDLE; +#else +using file_handle_type = int; +#endif + +// This value represents an invalid file handle type. This can be used to +// determine whether `basic_mmap::file_handle` is valid, for example. +const static file_handle_type invalid_handle = INVALID_HANDLE_VALUE; + +template +struct basic_mmap +{ + using value_type = ByteT; + using size_type = size_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using difference_type = std::ptrdiff_t; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using iterator_category = std::random_access_iterator_tag; + using handle_type = file_handle_type; + + static_assert(sizeof(ByteT) == sizeof(char), "ByteT must be the same size as char."); + +private: + // Points to the first requested byte, and not to the actual start of the mapping. + pointer data_ = nullptr; + + // Length, in bytes, requested by user, which may not be the length of the full + // mapping, and the entire length of the full mapping. + size_type length_ = 0; + size_type mapped_length_ = 0; + + // Letting user map a file using both an existing file handle and a path introcudes + // On POSIX, we only need a file handle to create a mapping, while on Windows + // systems the file handle is necessary to retrieve a file mapping handle, but any + // subsequent operations on the mapped region must be done through the latter. + handle_type file_handle_ = INVALID_HANDLE_VALUE; +#ifdef _WIN32 + handle_type file_mapping_handle_ = INVALID_HANDLE_VALUE; +#endif + + // Letting user map a file using both an existing file handle and a path + // introcudes some complexity in that we must not close the file handle if + // user provided it, but we must close it if we obtained it using the + // provided path. For this reason, this flag is used to determine when to + // close file_handle_. + bool is_handle_internal_; + +public: + /** + * The default constructed mmap object is in a non-mapped state, that is, + * any operation that attempts to access nonexistent underlying data will + * result in undefined behaviour/segmentation faults. + */ + basic_mmap() = default; + +#ifdef __cpp_exceptions + /** + * The same as invoking the `map` function, except any error that may occur + * while establishing the mapping is wrapped in a `std::system_error` and is + * thrown. + */ + template + basic_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) + { + std::error_code error; + map(path, offset, length, error); + if(error) { throw std::system_error(error); } + } + + /** + * The same as invoking the `map` function, except any error that may occur + * while establishing the mapping is wrapped in a `std::system_error` and is + * thrown. + */ + basic_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) + { + std::error_code error; + map(handle, offset, length, error); + if(error) { throw std::system_error(error); } + } +#endif // __cpp_exceptions + + /** + * `basic_mmap` has single-ownership semantics, so transferring ownership + * may only be accomplished by moving the object. + */ + basic_mmap(const basic_mmap&) = delete; + basic_mmap(basic_mmap&&); + basic_mmap& operator=(const basic_mmap&) = delete; + basic_mmap& operator=(basic_mmap&&); + + /** + * If this is a read-write mapping, the destructor invokes sync. Regardless + * of the access mode, unmap is invoked as a final step. + */ + ~basic_mmap(); + + /** + * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, + * however, a mapped region of a file gets its own handle, which is returned by + * 'mapping_handle'. + */ + handle_type file_handle() const noexcept { return file_handle_; } + handle_type mapping_handle() const noexcept; + + /** Returns whether a valid memory mapping has been created. */ + bool is_open() const noexcept { return file_handle_ != invalid_handle; } + + /** + * Returns true if no mapping was established, that is, conceptually the + * same as though the length that was mapped was 0. This function is + * provided so that this class has Container semantics. + */ + bool empty() const noexcept { return length() == 0; } + + /** Returns true if a mapping was established. */ + bool is_mapped() const noexcept; + + /** + * `size` and `length` both return the logical length, i.e. the number of bytes + * user requested to be mapped, while `mapped_length` returns the actual number of + * bytes that were mapped which is a multiple of the underlying operating system's + * page allocation granularity. + */ + size_type size() const noexcept { return length(); } + size_type length() const noexcept { return length_; } + size_type mapped_length() const noexcept { return mapped_length_; } + + /** + * Returns the offset, relative to the file's start, at which the mapping was + * requested to be created. + */ + size_type offset() const noexcept { return mapped_length_ - length_; } + + /** + * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping + * exists. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > pointer data() noexcept { return data_; } + const_pointer data() const noexcept { return data_; } + + /** + * Returns an iterator to the first requested byte, if a valid memory mapping + * exists, otherwise this function call is undefined behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > iterator begin() noexcept { return data(); } + const_iterator begin() const noexcept { return data(); } + const_iterator cbegin() const noexcept { return data(); } + + /** + * Returns an iterator one past the last requested byte, if a valid memory mapping + * exists, otherwise this function call is undefined behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > iterator end() noexcept { return data() + length(); } + const_iterator end() const noexcept { return data() + length(); } + const_iterator cend() const noexcept { return data() + length(); } + + /** + * Returns a reverse iterator to the last memory mapped byte, if a valid + * memory mapping exists, otherwise this function call is undefined + * behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const noexcept + { return const_reverse_iterator(end()); } + const_reverse_iterator crbegin() const noexcept + { return const_reverse_iterator(end()); } + + /** + * Returns a reverse iterator past the first mapped byte, if a valid memory + * mapping exists, otherwise this function call is undefined behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + const_reverse_iterator rend() const noexcept + { return const_reverse_iterator(begin()); } + const_reverse_iterator crend() const noexcept + { return const_reverse_iterator(begin()); } + + /** + * Returns a reference to the `i`th byte from the first requested byte (as returned + * by `data`). If this is invoked when no valid memory mapping has been created + * prior to this call, undefined behaviour ensues. + */ + reference operator[](const size_type i) noexcept { return data_[i]; } + const_reference operator[](const size_type i) const noexcept { return data_[i]; } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `path`, which must be a path to an existing file, is used to retrieve a file + * handle (which is closed when the object destructs or `unmap` is called), which is + * then used to memory map the requested region. Upon failure, `error` is set to + * indicate the reason and the object remains in an unmapped state. + * + * `offset` is the number of bytes, relative to the start of the file, where the + * mapping should begin. When specifying it, there is no need to worry about + * providing a value that is aligned with the operating system's page allocation + * granularity. This is adjusted by the implementation such that the first requested + * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at + * `offset` from the start of the file. + * + * `length` is the number of bytes to map. It may be `map_entire_file`, in which + * case a mapping of the entire file is created. + */ + template + void map(const String& path, const size_type offset, + const size_type length, std::error_code& error); + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `path`, which must be a path to an existing file, is used to retrieve a file + * handle (which is closed when the object destructs or `unmap` is called), which is + * then used to memory map the requested region. Upon failure, `error` is set to + * indicate the reason and the object remains in an unmapped state. + * + * The entire file is mapped. + */ + template + void map(const String& path, std::error_code& error) + { + map(path, 0, map_entire_file, error); + } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is + * unsuccesful, the reason is reported via `error` and the object remains in + * a state as if this function hadn't been called. + * + * `handle`, which must be a valid file handle, which is used to memory map the + * requested region. Upon failure, `error` is set to indicate the reason and the + * object remains in an unmapped state. + * + * `offset` is the number of bytes, relative to the start of the file, where the + * mapping should begin. When specifying it, there is no need to worry about + * providing a value that is aligned with the operating system's page allocation + * granularity. This is adjusted by the implementation such that the first requested + * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at + * `offset` from the start of the file. + * + * `length` is the number of bytes to map. It may be `map_entire_file`, in which + * case a mapping of the entire file is created. + */ + void map(const handle_type handle, const size_type offset, + const size_type length, std::error_code& error); + + /** + * Establishes a memory mapping with AccessMode. If the mapping is + * unsuccesful, the reason is reported via `error` and the object remains in + * a state as if this function hadn't been called. + * + * `handle`, which must be a valid file handle, which is used to memory map the + * requested region. Upon failure, `error` is set to indicate the reason and the + * object remains in an unmapped state. + * + * The entire file is mapped. + */ + void map(const handle_type handle, std::error_code& error) + { + map(handle, 0, map_entire_file, error); + } + + /** + * If a valid memory mapping has been created prior to this call, this call + * instructs the kernel to unmap the memory region and disassociate this object + * from the file. + * + * The file handle associated with the file that is mapped is only closed if the + * mapping was created using a file path. If, on the other hand, an existing + * file handle was used to create the mapping, the file handle is not closed. + */ + void unmap(); + + void swap(basic_mmap& other); + + /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ + template + typename std::enable_if::type + sync(std::error_code& error); + + /** + * All operators compare the address of the first byte and size of the two mapped + * regions. + */ + +private: + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > pointer get_mapping_start() noexcept + { + return !data() ? nullptr : data() - offset(); + } + + const_pointer get_mapping_start() const noexcept + { + return !data() ? nullptr : data() - offset(); + } + + /** + * The destructor syncs changes to disk if `AccessMode` is `write`, but not + * if it's `read`, but since the destructor cannot be templated, we need to + * do SFINAE in a dedicated function, where one syncs and the other is a noop. + */ + template + typename std::enable_if::type + conditional_sync(); + template + typename std::enable_if::type conditional_sync(); +}; + +template +bool operator==(const basic_mmap& a, + const basic_mmap& b); + +template +bool operator!=(const basic_mmap& a, + const basic_mmap& b); + +template +bool operator<(const basic_mmap& a, + const basic_mmap& b); + +template +bool operator<=(const basic_mmap& a, + const basic_mmap& b); + +template +bool operator>(const basic_mmap& a, + const basic_mmap& b); + +template +bool operator>=(const basic_mmap& a, + const basic_mmap& b); + +/** + * This is the basis for all read-only mmap objects and should be preferred over + * directly using `basic_mmap`. + */ +template +using basic_mmap_source = basic_mmap; + +/** + * This is the basis for all read-write mmap objects and should be preferred over + * directly using `basic_mmap`. + */ +template +using basic_mmap_sink = basic_mmap; + +/** + * These aliases cover the most common use cases, both representing a raw byte stream + * (either with a char or an unsigned char/uint8_t). + */ +using mmap_source = basic_mmap_source; +using ummap_source = basic_mmap_source; + +using mmap_sink = basic_mmap_sink; +using ummap_sink = basic_mmap_sink; + +/** + * Convenience factory method that constructs a mapping for any `basic_mmap` or + * `basic_mmap` type. + */ +template< + typename MMap, + typename MappingToken +> MMap make_mmap(const MappingToken& token, + int64_t offset, int64_t length, std::error_code& error) +{ + MMap mmap; + mmap.map(token, offset, length, error); + return mmap; +} + +/** + * Convenience factory method. + * + * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`, + * `std::filesystem::path`, `std::vector`, or similar), or a + * `mmap_source::handle_type`. + */ +template +mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset, + mmap_source::size_type length, std::error_code& error) +{ + return make_mmap(token, offset, length, error); +} + +template +mmap_source make_mmap_source(const MappingToken& token, std::error_code& error) +{ + return make_mmap_source(token, 0, map_entire_file, error); +} + +/** + * Convenience factory method. + * + * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`, + * `std::filesystem::path`, `std::vector`, or similar), or a + * `mmap_sink::handle_type`. + */ +template +mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset, + mmap_sink::size_type length, std::error_code& error) +{ + return make_mmap(token, offset, length, error); +} + +template +mmap_sink make_mmap_sink(const MappingToken& token, std::error_code& error) +{ + return make_mmap_sink(token, 0, map_entire_file, error); +} + +} // namespace mio + +// #include "detail/mmap.ipp" +/* Copyright 2017 https://github.com/mandreyel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MIO_BASIC_MMAP_IMPL +#define MIO_BASIC_MMAP_IMPL + +// #include "mio/mmap.hpp" + +// #include "mio/page.hpp" + +// #include "mio/detail/string_util.hpp" +/* Copyright 2017 https://github.com/mandreyel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MIO_STRING_UTIL_HEADER +#define MIO_STRING_UTIL_HEADER + +#include + +namespace mio { +namespace detail { + +template< + typename S, + typename C = typename std::decay::type, + typename = decltype(std::declval().data()), + typename = typename std::enable_if< + std::is_same::value +#ifdef _WIN32 + || std::is_same::value +#endif + >::type +> struct char_type_helper { + using type = typename C::value_type; +}; + +template +struct char_type { + using type = typename char_type_helper::type; +}; + +// TODO: can we avoid this brute force approach? +template<> +struct char_type { + using type = char; +}; + +template<> +struct char_type { + using type = char; +}; + +template +struct char_type { + using type = char; +}; + +template +struct char_type { + using type = char; +}; + +#ifdef _WIN32 +template<> +struct char_type { + using type = wchar_t; +}; + +template<> +struct char_type { + using type = wchar_t; +}; + +template +struct char_type { + using type = wchar_t; +}; + +template +struct char_type { + using type = wchar_t; +}; +#endif // _WIN32 + +template +struct is_c_str_helper +{ + static constexpr bool value = std::is_same< + CharT*, + // TODO: I'm so sorry for this... Can this be made cleaner? + typename std::add_pointer< + typename std::remove_cv< + typename std::remove_pointer< + typename std::decay< + S + >::type + >::type + >::type + >::type + >::value; +}; + +template +struct is_c_str +{ + static constexpr bool value = is_c_str_helper::value; +}; + +#ifdef _WIN32 +template +struct is_c_wstr +{ + static constexpr bool value = is_c_str_helper::value; +}; +#endif // _WIN32 + +template +struct is_c_str_or_c_wstr +{ + static constexpr bool value = is_c_str::value +#ifdef _WIN32 + || is_c_wstr::value +#endif + ; +}; + +template< + typename String, + typename = decltype(std::declval().data()), + typename = typename std::enable_if::value>::type +> const typename char_type::type* c_str(const String& path) +{ + return path.data(); +} + +template< + typename String, + typename = decltype(std::declval().empty()), + typename = typename std::enable_if::value>::type +> bool empty(const String& path) +{ + return path.empty(); +} + +template< + typename String, + typename = typename std::enable_if::value>::type +> const typename char_type::type* c_str(String path) +{ + return path; +} + +template< + typename String, + typename = typename std::enable_if::value>::type +> bool empty(String path) +{ + return !path || (*path == 0); +} + +} // namespace detail +} // namespace mio + +#endif // MIO_STRING_UTIL_HEADER + + +#include + +#ifndef _WIN32 +# include +# include +# include +# include +#endif + +namespace mio { +namespace detail { + +#ifdef _WIN32 +namespace win { + +/** Returns the 4 upper bytes of an 8-byte integer. */ +inline DWORD int64_high(int64_t n) noexcept +{ + return n >> 32; +} + +/** Returns the 4 lower bytes of an 8-byte integer. */ +inline DWORD int64_low(int64_t n) noexcept +{ + return n & 0xffffffff; +} + +template< + typename String, + typename = typename std::enable_if< + std::is_same::type, char>::value + >::type +> file_handle_type open_file_helper(const String& path, const access_mode mode) +{ + return ::CreateFileA(c_str(path), + mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + 0); +} + +template +typename std::enable_if< + std::is_same::type, wchar_t>::value, + file_handle_type +>::type open_file_helper(const String& path, const access_mode mode) +{ + return ::CreateFileW(c_str(path), + mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + 0); +} + +} // win +#endif // _WIN32 + +/** + * Returns the last platform specific system error (errno on POSIX and + * GetLastError on Win) as a `std::error_code`. + */ +inline std::error_code last_error() noexcept +{ + std::error_code error; +#ifdef _WIN32 + error.assign(GetLastError(), std::system_category()); +#else + error.assign(errno, std::system_category()); +#endif + return error; +} + +template +file_handle_type open_file(const String& path, const access_mode mode, + std::error_code& error) +{ + error.clear(); + if(detail::empty(path)) + { + error = std::make_error_code(std::errc::invalid_argument); + return invalid_handle; + } +#ifdef _WIN32 + const auto handle = win::open_file_helper(path, mode); +#else // POSIX + const auto handle = ::open(c_str(path), + mode == access_mode::read ? O_RDONLY : O_RDWR); +#endif + if(handle == invalid_handle) + { + error = detail::last_error(); + } + return handle; +} + +inline size_t query_file_size(file_handle_type handle, std::error_code& error) +{ + error.clear(); +#ifdef _WIN32 + LARGE_INTEGER file_size; + if(::GetFileSizeEx(handle, &file_size) == 0) + { + error = detail::last_error(); + return 0; + } + return static_cast(file_size.QuadPart); +#else // POSIX + struct stat sbuf; + if(::fstat(handle, &sbuf) == -1) + { + error = detail::last_error(); + return 0; + } + return sbuf.st_size; +#endif +} + +struct mmap_context +{ + char* data; + int64_t length; + int64_t mapped_length; +#ifdef _WIN32 + file_handle_type file_mapping_handle; +#endif +}; + +inline mmap_context memory_map(const file_handle_type file_handle, const int64_t offset, + const int64_t length, const access_mode mode, std::error_code& error) +{ + const int64_t aligned_offset = make_offset_page_aligned(offset); + const int64_t length_to_map = offset - aligned_offset + length; +#ifdef _WIN32 + const int64_t max_file_size = offset + length; + const auto file_mapping_handle = ::CreateFileMapping( + file_handle, + 0, + mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE, + win::int64_high(max_file_size), + win::int64_low(max_file_size), + 0); + if(file_mapping_handle == invalid_handle) + { + error = detail::last_error(); + return {}; + } + char* mapping_start = static_cast(::MapViewOfFile( + file_mapping_handle, + mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE, + win::int64_high(aligned_offset), + win::int64_low(aligned_offset), + length_to_map)); + if(mapping_start == nullptr) + { + error = detail::last_error(); + return {}; + } +#else // POSIX + char* mapping_start = static_cast(::mmap( + 0, // Don't give hint as to where to map. + length_to_map, + mode == access_mode::read ? PROT_READ : PROT_WRITE, + MAP_SHARED, + file_handle, + aligned_offset)); + if(mapping_start == MAP_FAILED) + { + error = detail::last_error(); + return {}; + } +#endif + mmap_context ctx; + ctx.data = mapping_start + offset - aligned_offset; + ctx.length = length; + ctx.mapped_length = length_to_map; +#ifdef _WIN32 + ctx.file_mapping_handle = file_mapping_handle; +#endif + return ctx; +} + +} // namespace detail + +// -- basic_mmap -- + +template +basic_mmap::~basic_mmap() +{ + conditional_sync(); + unmap(); +} + +template +basic_mmap::basic_mmap(basic_mmap&& other) + : data_(std::move(other.data_)) + , length_(std::move(other.length_)) + , mapped_length_(std::move(other.mapped_length_)) + , file_handle_(std::move(other.file_handle_)) +#ifdef _WIN32 + , file_mapping_handle_(std::move(other.file_mapping_handle_)) +#endif + , is_handle_internal_(std::move(other.is_handle_internal_)) +{ + other.data_ = nullptr; + other.length_ = other.mapped_length_ = 0; + other.file_handle_ = invalid_handle; +#ifdef _WIN32 + other.file_mapping_handle_ = invalid_handle; +#endif +} + +template +basic_mmap& +basic_mmap::operator=(basic_mmap&& other) +{ + if(this != &other) + { + // First the existing mapping needs to be removed. + unmap(); + data_ = std::move(other.data_); + length_ = std::move(other.length_); + mapped_length_ = std::move(other.mapped_length_); + file_handle_ = std::move(other.file_handle_); +#ifdef _WIN32 + file_mapping_handle_ = std::move(other.file_mapping_handle_); +#endif + is_handle_internal_ = std::move(other.is_handle_internal_); + + // The moved from basic_mmap's fields need to be reset, because + // otherwise other's destructor will unmap the same mapping that was + // just moved into this. + other.data_ = nullptr; + other.length_ = other.mapped_length_ = 0; + other.file_handle_ = invalid_handle; +#ifdef _WIN32 + other.file_mapping_handle_ = invalid_handle; +#endif + other.is_handle_internal_ = false; + } + return *this; +} + +template +typename basic_mmap::handle_type +basic_mmap::mapping_handle() const noexcept +{ +#ifdef _WIN32 + return file_mapping_handle_; +#else + return file_handle_; +#endif +} + +template +template +void basic_mmap::map(const String& path, const size_type offset, + const size_type length, std::error_code& error) +{ + error.clear(); + if(detail::empty(path)) + { + error = std::make_error_code(std::errc::invalid_argument); + return; + } + const auto handle = detail::open_file(path, AccessMode, error); + if(error) + { + return; + } + + map(handle, offset, length, error); + // This MUST be after the call to map, as that sets this to true. + if(!error) + { + is_handle_internal_ = true; + } +} + +template +void basic_mmap::map(const handle_type handle, + const size_type offset, const size_type length, std::error_code& error) +{ + error.clear(); + if(handle == invalid_handle) + { + error = std::make_error_code(std::errc::bad_file_descriptor); + return; + } + + const auto file_size = detail::query_file_size(handle, error); + if(error) + { + return; + } + + if(offset + length > file_size) + { + error = std::make_error_code(std::errc::invalid_argument); + return; + } + + const auto ctx = detail::memory_map(handle, offset, + length == map_entire_file ? (file_size - offset) : length, + AccessMode, error); + if(!error) + { + // We must unmap the previous mapping that may have existed prior to this call. + // Note that this must only be invoked after a new mapping has been created in + // order to provide the strong guarantee that, should the new mapping fail, the + // `map` function leaves this instance in a state as though the function had + // never been invoked. + unmap(); + file_handle_ = handle; + is_handle_internal_ = false; + data_ = reinterpret_cast(ctx.data); + length_ = ctx.length; + mapped_length_ = ctx.mapped_length; +#ifdef _WIN32 + file_mapping_handle_ = ctx.file_mapping_handle; +#endif + } +} + +template +template +typename std::enable_if::type +basic_mmap::sync(std::error_code& error) +{ + error.clear(); + if(!is_open()) + { + error = std::make_error_code(std::errc::bad_file_descriptor); + return; + } + + if(data()) + { +#ifdef _WIN32 + if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0 + || ::FlushFileBuffers(file_handle_) == 0) +#else // POSIX + if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0) +#endif + { + error = detail::last_error(); + return; + } + } +#ifdef _WIN32 + if(::FlushFileBuffers(file_handle_) == 0) + { + error = detail::last_error(); + } +#endif +} + +template +void basic_mmap::unmap() +{ + if(!is_open()) { return; } + // TODO do we care about errors here? +#ifdef _WIN32 + if(is_mapped()) + { + ::UnmapViewOfFile(get_mapping_start()); + ::CloseHandle(file_mapping_handle_); + } +#else // POSIX + if(data_) { ::munmap(const_cast(get_mapping_start()), mapped_length_); } +#endif + + // If file_handle_ was obtained by our opening it (when map is called with a path, + // rather than an existing file handle), we need to close it, otherwise it must not + // be closed as it may still be used outside this instance. + if(is_handle_internal_) + { +#ifdef _WIN32 + ::CloseHandle(file_handle_); +#else // POSIX + ::close(file_handle_); +#endif + } + + // Reset fields to their default values. + data_ = nullptr; + length_ = mapped_length_ = 0; + file_handle_ = invalid_handle; +#ifdef _WIN32 + file_mapping_handle_ = invalid_handle; +#endif +} + +template +bool basic_mmap::is_mapped() const noexcept +{ +#ifdef _WIN32 + return file_mapping_handle_ != invalid_handle; +#else // POSIX + return is_open(); +#endif +} + +template +void basic_mmap::swap(basic_mmap& other) +{ + if(this != &other) + { + using std::swap; + swap(data_, other.data_); + swap(file_handle_, other.file_handle_); +#ifdef _WIN32 + swap(file_mapping_handle_, other.file_mapping_handle_); +#endif + swap(length_, other.length_); + swap(mapped_length_, other.mapped_length_); + swap(is_handle_internal_, other.is_handle_internal_); + } +} + +template +template +typename std::enable_if::type +basic_mmap::conditional_sync() +{ + // This is invoked from the destructor, so not much we can do about + // failures here. + std::error_code ec; + sync(ec); +} + +template +template +typename std::enable_if::type +basic_mmap::conditional_sync() +{ + // noop +} + +template +bool operator==(const basic_mmap& a, + const basic_mmap& b) +{ + return a.data() == b.data() + && a.size() == b.size(); +} + +template +bool operator!=(const basic_mmap& a, + const basic_mmap& b) +{ + return !(a == b); +} + +template +bool operator<(const basic_mmap& a, + const basic_mmap& b) +{ + if(a.data() == b.data()) { return a.size() < b.size(); } + return a.data() < b.data(); +} + +template +bool operator<=(const basic_mmap& a, + const basic_mmap& b) +{ + return !(a > b); +} + +template +bool operator>(const basic_mmap& a, + const basic_mmap& b) +{ + if(a.data() == b.data()) { return a.size() > b.size(); } + return a.data() > b.data(); +} + +template +bool operator>=(const basic_mmap& a, + const basic_mmap& b) +{ + return !(a < b); +} + +} // namespace mio + +#endif // MIO_BASIC_MMAP_IMPL + + +#endif // MIO_MMAP_HEADER +/* Copyright 2017 https://github.com/mandreyel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MIO_PAGE_HEADER +#define MIO_PAGE_HEADER + +#ifdef _WIN32 +# include +#else +# include +#endif + +namespace mio { + +/** + * This is used by `basic_mmap` to determine whether to create a read-only or + * a read-write memory mapping. + */ +enum class access_mode +{ + read, + write +}; + +/** + * Determines the operating system's page allocation granularity. + * + * On the first call to this function, it invokes the operating system specific syscall + * to determine the page size, caches the value, and returns it. Any subsequent call to + * this function serves the cached value, so no further syscalls are made. + */ +inline size_t page_size() +{ + static const size_t page_size = [] + { +#ifdef _WIN32 + SYSTEM_INFO SystemInfo; + GetSystemInfo(&SystemInfo); + return SystemInfo.dwAllocationGranularity; +#else + return sysconf(_SC_PAGE_SIZE); +#endif + }(); + return page_size; +} + +/** + * Alligns `offset` to the operating's system page size such that it subtracts the + * difference until the nearest page boundary before `offset`, or does nothing if + * `offset` is already page aligned. + */ +inline size_t make_offset_page_aligned(size_t offset) noexcept +{ + const size_t page_size_ = page_size(); + // Use integer division to round down to the nearest page alignment. + return offset / page_size_ * page_size_; +} + +} // namespace mio + +#endif // MIO_PAGE_HEADER +/* Copyright 2017 https://github.com/mandreyel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MIO_SHARED_MMAP_HEADER +#define MIO_SHARED_MMAP_HEADER + +// #include "mio/mmap.hpp" + + +#include // std::error_code +#include // std::shared_ptr + +namespace mio { + +/** + * Exposes (nearly) the same interface as `basic_mmap`, but endowes it with + * `std::shared_ptr` semantics. + * + * This is not the default behaviour of `basic_mmap` to avoid allocating on the heap if + * shared semantics are not required. + */ +template< + access_mode AccessMode, + typename ByteT +> class basic_shared_mmap +{ + using impl_type = basic_mmap; + std::shared_ptr pimpl_; + +public: + using value_type = typename impl_type::value_type; + using size_type = typename impl_type::size_type; + using reference = typename impl_type::reference; + using const_reference = typename impl_type::const_reference; + using pointer = typename impl_type::pointer; + using const_pointer = typename impl_type::const_pointer; + using difference_type = typename impl_type::difference_type; + using iterator = typename impl_type::iterator; + using const_iterator = typename impl_type::const_iterator; + using reverse_iterator = typename impl_type::reverse_iterator; + using const_reverse_iterator = typename impl_type::const_reverse_iterator; + using iterator_category = typename impl_type::iterator_category; + using handle_type = typename impl_type::handle_type; + using mmap_type = impl_type; + + basic_shared_mmap() = default; + basic_shared_mmap(const basic_shared_mmap&) = default; + basic_shared_mmap& operator=(const basic_shared_mmap&) = default; + basic_shared_mmap(basic_shared_mmap&&) = default; + basic_shared_mmap& operator=(basic_shared_mmap&&) = default; + + /** Takes ownership of an existing mmap object. */ + basic_shared_mmap(mmap_type&& mmap) + : pimpl_(std::make_shared(std::move(mmap))) + {} + + /** Takes ownership of an existing mmap object. */ + basic_shared_mmap& operator=(mmap_type&& mmap) + { + pimpl_ = std::make_shared(std::move(mmap)); + return *this; + } + + /** Initializes this object with an already established shared mmap. */ + basic_shared_mmap(std::shared_ptr mmap) : pimpl_(std::move(mmap)) {} + + /** Initializes this object with an already established shared mmap. */ + basic_shared_mmap& operator=(std::shared_ptr mmap) + { + pimpl_ = std::move(mmap); + return *this; + } + +#ifdef __cpp_exceptions + /** + * The same as invoking the `map` function, except any error that may occur + * while establishing the mapping is wrapped in a `std::system_error` and is + * thrown. + */ + template + basic_shared_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) + { + std::error_code error; + map(path, offset, length, error); + if(error) { throw std::system_error(error); } + } + + /** + * The same as invoking the `map` function, except any error that may occur + * while establishing the mapping is wrapped in a `std::system_error` and is + * thrown. + */ + basic_shared_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) + { + std::error_code error; + map(handle, offset, length, error); + if(error) { throw std::system_error(error); } + } +#endif // __cpp_exceptions + + /** + * If this is a read-write mapping and the last reference to the mapping, + * the destructor invokes sync. Regardless of the access mode, unmap is + * invoked as a final step. + */ + ~basic_shared_mmap() = default; + + /** Returns the underlying `std::shared_ptr` instance that holds the mmap. */ + std::shared_ptr get_shared_ptr() { return pimpl_; } + + /** + * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, + * however, a mapped region of a file gets its own handle, which is returned by + * 'mapping_handle'. + */ + handle_type file_handle() const noexcept + { + return pimpl_ ? pimpl_->file_handle() : invalid_handle; + } + + handle_type mapping_handle() const noexcept + { + return pimpl_ ? pimpl_->mapping_handle() : invalid_handle; + } + + /** Returns whether a valid memory mapping has been created. */ + bool is_open() const noexcept { return pimpl_ && pimpl_->is_open(); } + + /** + * Returns true if no mapping was established, that is, conceptually the + * same as though the length that was mapped was 0. This function is + * provided so that this class has Container semantics. + */ + bool empty() const noexcept { return !pimpl_ || pimpl_->empty(); } + + /** + * `size` and `length` both return the logical length, i.e. the number of bytes + * user requested to be mapped, while `mapped_length` returns the actual number of + * bytes that were mapped which is a multiple of the underlying operating system's + * page allocation granularity. + */ + size_type size() const noexcept { return pimpl_ ? pimpl_->length() : 0; } + size_type length() const noexcept { return pimpl_ ? pimpl_->length() : 0; } + size_type mapped_length() const noexcept + { + return pimpl_ ? pimpl_->mapped_length() : 0; + } + + /** + * Returns the offset, relative to the file's start, at which the mapping was + * requested to be created. + */ + size_type offset() const noexcept { return pimpl_ ? pimpl_->offset() : 0; } + + /** + * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping + * exists. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > pointer data() noexcept { return pimpl_->data(); } + const_pointer data() const noexcept { return pimpl_ ? pimpl_->data() : nullptr; } + + /** + * Returns an iterator to the first requested byte, if a valid memory mapping + * exists, otherwise this function call is undefined behaviour. + */ + iterator begin() noexcept { return pimpl_->begin(); } + const_iterator begin() const noexcept { return pimpl_->begin(); } + const_iterator cbegin() const noexcept { return pimpl_->cbegin(); } + + /** + * Returns an iterator one past the last requested byte, if a valid memory mapping + * exists, otherwise this function call is undefined behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > iterator end() noexcept { return pimpl_->end(); } + const_iterator end() const noexcept { return pimpl_->end(); } + const_iterator cend() const noexcept { return pimpl_->cend(); } + + /** + * Returns a reverse iterator to the last memory mapped byte, if a valid + * memory mapping exists, otherwise this function call is undefined + * behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > reverse_iterator rbegin() noexcept { return pimpl_->rbegin(); } + const_reverse_iterator rbegin() const noexcept { return pimpl_->rbegin(); } + const_reverse_iterator crbegin() const noexcept { return pimpl_->crbegin(); } + + /** + * Returns a reverse iterator past the first mapped byte, if a valid memory + * mapping exists, otherwise this function call is undefined behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > reverse_iterator rend() noexcept { return pimpl_->rend(); } + const_reverse_iterator rend() const noexcept { return pimpl_->rend(); } + const_reverse_iterator crend() const noexcept { return pimpl_->crend(); } + + /** + * Returns a reference to the `i`th byte from the first requested byte (as returned + * by `data`). If this is invoked when no valid memory mapping has been created + * prior to this call, undefined behaviour ensues. + */ + reference operator[](const size_type i) noexcept { return (*pimpl_)[i]; } + const_reference operator[](const size_type i) const noexcept { return (*pimpl_)[i]; } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `path`, which must be a path to an existing file, is used to retrieve a file + * handle (which is closed when the object destructs or `unmap` is called), which is + * then used to memory map the requested region. Upon failure, `error` is set to + * indicate the reason and the object remains in an unmapped state. + * + * `offset` is the number of bytes, relative to the start of the file, where the + * mapping should begin. When specifying it, there is no need to worry about + * providing a value that is aligned with the operating system's page allocation + * granularity. This is adjusted by the implementation such that the first requested + * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at + * `offset` from the start of the file. + * + * `length` is the number of bytes to map. It may be `map_entire_file`, in which + * case a mapping of the entire file is created. + */ + template + void map(const String& path, const size_type offset, + const size_type length, std::error_code& error) + { + map_impl(path, offset, length, error); + } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `path`, which must be a path to an existing file, is used to retrieve a file + * handle (which is closed when the object destructs or `unmap` is called), which is + * then used to memory map the requested region. Upon failure, `error` is set to + * indicate the reason and the object remains in an unmapped state. + * + * The entire file is mapped. + */ + template + void map(const String& path, std::error_code& error) + { + map_impl(path, 0, map_entire_file, error); + } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `handle`, which must be a valid file handle, which is used to memory map the + * requested region. Upon failure, `error` is set to indicate the reason and the + * object remains in an unmapped state. + * + * `offset` is the number of bytes, relative to the start of the file, where the + * mapping should begin. When specifying it, there is no need to worry about + * providing a value that is aligned with the operating system's page allocation + * granularity. This is adjusted by the implementation such that the first requested + * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at + * `offset` from the start of the file. + * + * `length` is the number of bytes to map. It may be `map_entire_file`, in which + * case a mapping of the entire file is created. + */ + void map(const handle_type handle, const size_type offset, + const size_type length, std::error_code& error) + { + map_impl(handle, offset, length, error); + } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `handle`, which must be a valid file handle, which is used to memory map the + * requested region. Upon failure, `error` is set to indicate the reason and the + * object remains in an unmapped state. + * + * The entire file is mapped. + */ + void map(const handle_type handle, std::error_code& error) + { + map_impl(handle, 0, map_entire_file, error); + } + + /** + * If a valid memory mapping has been created prior to this call, this call + * instructs the kernel to unmap the memory region and disassociate this object + * from the file. + * + * The file handle associated with the file that is mapped is only closed if the + * mapping was created using a file path. If, on the other hand, an existing + * file handle was used to create the mapping, the file handle is not closed. + */ + void unmap() { if(pimpl_) pimpl_->unmap(); } + + void swap(basic_shared_mmap& other) { pimpl_.swap(other.pimpl_); } + + /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > void sync(std::error_code& error) { if(pimpl_) pimpl_->sync(error); } + + /** All operators compare the underlying `basic_mmap`'s addresses. */ + + friend bool operator==(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return a.pimpl_ == b.pimpl_; + } + + friend bool operator!=(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return !(a == b); + } + + friend bool operator<(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return a.pimpl_ < b.pimpl_; + } + + friend bool operator<=(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return a.pimpl_ <= b.pimpl_; + } + + friend bool operator>(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return a.pimpl_ > b.pimpl_; + } + + friend bool operator>=(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return a.pimpl_ >= b.pimpl_; + } + +private: + template + void map_impl(const MappingToken& token, const size_type offset, + const size_type length, std::error_code& error) + { + if(!pimpl_) + { + mmap_type mmap = make_mmap(token, offset, length, error); + if(error) { return; } + pimpl_ = std::make_shared(std::move(mmap)); + } + else + { + pimpl_->map(token, offset, length, error); + } + } +}; + +/** + * This is the basis for all read-only mmap objects and should be preferred over + * directly using basic_shared_mmap. + */ +template +using basic_shared_mmap_source = basic_shared_mmap; + +/** + * This is the basis for all read-write mmap objects and should be preferred over + * directly using basic_shared_mmap. + */ +template +using basic_shared_mmap_sink = basic_shared_mmap; + +/** + * These aliases cover the most common use cases, both representing a raw byte stream + * (either with a char or an unsigned char/uint8_t). + */ +using shared_mmap_source = basic_shared_mmap_source; +using shared_ummap_source = basic_shared_mmap_source; + +using shared_mmap_sink = basic_shared_mmap_sink; +using shared_ummap_sink = basic_shared_mmap_sink; + +} // namespace mio + +#endif // MIO_SHARED_MMAP_HEADER