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/dist/README.md b/dist/README.md
new file mode 100644
index 0000000..9a62446
--- /dev/null
+++ b/dist/README.md
@@ -0,0 +1,6 @@
+# Windows MinGW build version adapted from PS
+
+- 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)
+
diff --git a/dist/android-elf-cleaner.zip b/dist/android-elf-cleaner.zip
new file mode 100644
index 0000000..9035659
Binary files /dev/null and b/dist/android-elf-cleaner.zip differ
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