diff --git a/README.md b/README.md index 191ab21..ac35eae 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ A simple header-only cross-platform C++14 memory mapping library. ## Example ```c++ #include +#include // for mio::page_size #include // for std::error_code #include // for std::printf @@ -43,11 +44,16 @@ int main() // Or map using the constructor, which throws a std::error_code upon failure. try { + const auto page_size = mio::page_size(); mio::mmap_sink mmap3("another/path/to/file", offset_type(4096), length_type(4096)); // ... } catch(const std::error_code& error) { return handle_error(error); } + + // mio exposes an interface that abstracts away memory as a string, so it is + // possible to create mmap objects that have custom underlying character types: + using wmmap_source = mio::basic_mmap_source; } ``` diff --git a/include/mio/detail/mmap_impl.hpp b/include/mio/detail/mmap_impl.hpp index 2f7107f..8fce86c 100644 --- a/include/mio/detail/mmap_impl.hpp +++ b/include/mio/detail/mmap_impl.hpp @@ -37,16 +37,18 @@ namespace mio { namespace detail { -template struct basic_mmap; +enum { use_full_file_size = 0 }; -template -bool operator==(const basic_mmap& a, const basic_mmap& b); -template -bool operator!=(const basic_mmap& a, const basic_mmap& b); +enum class access_mode +{ + read_only, + read_write +}; -size_t page_size(); - -template struct basic_mmap +template< + typename CharT, + typename CharTraits = std::char_traits +> struct basic_mmap { using value_type = CharT; using size_type = int64_t; @@ -66,14 +68,6 @@ template struct basic_mmap using handle_type = int; #endif - static constexpr size_type use_full_file_size = 0; - - enum class access_mode - { - read_only, - read_write - }; - private: // Points to the first requested byte, and not to the actual start of the mapping. @@ -114,8 +108,6 @@ public: bool is_mapped() const noexcept; bool empty() const noexcept { return length() == 0; } - // TODO return the number of BYTES or the number of times sizeof(CharT) fits into length_? - size_type size() const noexcept { return length_ >> sizeof(CharT); } size_type length() const noexcept { return length_ >> sizeof(CharT); } size_type mapped_length() const noexcept { return mapped_length_ >> sizeof(CharT); } @@ -153,9 +145,6 @@ public: void swap(basic_mmap& other); - friend bool operator==(const basic_mmap& a, const basic_mmap& b); - friend bool operator!=(const basic_mmap& a, const basic_mmap& b); - private: pointer get_mapping_start() noexcept; @@ -164,6 +153,30 @@ private: const access_mode mode, std::error_code& error); }; +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); + } // namespace detail } // namespace mio diff --git a/include/mio/detail/mmap_impl.ipp b/include/mio/detail/mmap_impl.ipp index c254507..35f5c5c 100644 --- a/include/mio/detail/mmap_impl.ipp +++ b/include/mio/detail/mmap_impl.ipp @@ -23,6 +23,7 @@ #include "mmap_impl.hpp" #include "type_traits.hpp" +#include "../page.hpp" #include #include @@ -37,6 +38,7 @@ namespace mio { namespace detail { +// Generic handle type for use by free functions. using handle_type = basic_mmap::handle_type; #if defined(_WIN32) @@ -51,28 +53,6 @@ inline DWORD int64_low(int64_t n) noexcept } #endif -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; -} - -inline size_t make_page_aligned(size_t t) noexcept -{ - const static size_t page_size_ = page_size(); - // Use integer division to round down to the nearest page alignment. - return t / page_size_ * page_size_; -} - inline std::error_code last_error() noexcept { std::error_code error; @@ -85,8 +65,7 @@ inline std::error_code last_error() noexcept } template -handle_type open_file(const Path& path, - const basic_mmap::access_mode mode, std::error_code& error) +handle_type open_file(const Path& path, const access_mode mode, std::error_code& error) { error.clear(); if(detail::empty(path)) @@ -96,8 +75,7 @@ handle_type open_file(const Path& path, } #if defined(_WIN32) const auto handle = ::CreateFile(c_str(path), - mode == basic_mmap::access_mode::read_only - ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, + mode == access_mode::read_only ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, @@ -105,7 +83,7 @@ handle_type open_file(const Path& path, 0); #else const auto handle = ::open(c_str(path), - mode == basic_mmap::access_mode::read_only ? O_RDONLY : O_RDWR); + mode == access_mode::read_only ? O_RDONLY : O_RDWR); #endif if(handle == INVALID_HANDLE_VALUE) { @@ -138,14 +116,14 @@ int64_t query_file_size(handle_type handle, std::error_code& error) // -- basic_mmap -- -template -basic_mmap::~basic_mmap() +template +basic_mmap::~basic_mmap() { unmap(); } -template -basic_mmap::basic_mmap(basic_mmap&& other) +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_)) @@ -163,8 +141,8 @@ basic_mmap::basic_mmap(basic_mmap&& other) #endif } -template -basic_mmap& basic_mmap::operator=(basic_mmap&& other) +template +basic_mmap& basic_mmap::operator=(basic_mmap&& other) { if(this != &other) { @@ -192,9 +170,9 @@ basic_mmap& basic_mmap::operator=(basic_mmap&& other) return *this; } -template -typename basic_mmap::handle_type -basic_mmap::mapping_handle() const noexcept +template +typename basic_mmap::handle_type +basic_mmap::mapping_handle() const noexcept { #ifdef _WIN32 return file_mapping_handle_; @@ -203,9 +181,9 @@ basic_mmap::mapping_handle() const noexcept #endif } -template +template template -void basic_mmap::map(String& path, size_type offset, +void basic_mmap::map(String& path, size_type offset, size_type length, access_mode mode, std::error_code& error) { error.clear(); @@ -224,8 +202,8 @@ void basic_mmap::map(String& path, size_type offset, } } -template -void basic_mmap::map(handle_type handle, size_type offset, +template +void basic_mmap::map(handle_type handle, size_type offset, size_type length, access_mode mode, std::error_code& error) { error.clear(); @@ -238,7 +216,7 @@ void basic_mmap::map(handle_type handle, size_type offset, const auto file_size = query_file_size(handle, error); if(error) { return; } - if(length <= 0) + if(length <= use_full_file_size) { length = file_size; } @@ -253,11 +231,11 @@ void basic_mmap::map(handle_type handle, size_type offset, map(offset, length, mode, error); } -template -void basic_mmap::map(const size_type offset, const size_type length, +template +void basic_mmap::map(const size_type offset, const size_type length, const access_mode mode, std::error_code& error) { - const size_type aligned_offset = make_page_aligned(offset); + const size_type aligned_offset = make_offset_page_aligned(offset); const size_type length_to_map = offset - aligned_offset + length; #if defined(_WIN32) const size_type max_file_size = offset + length; @@ -289,7 +267,7 @@ void basic_mmap::map(const size_type offset, const size_type length, const pointer mapping_start = static_cast(::mmap( 0, // Don't give hint as to where to map. length_to_map, - mode == basic_mmap::access_mode::read_only ? PROT_READ : PROT_WRITE, + mode == access_mode::read_only ? PROT_READ : PROT_WRITE, MAP_SHARED, // TODO do we want to share it? file_handle_, aligned_offset)); @@ -304,8 +282,8 @@ void basic_mmap::map(const size_type offset, const size_type length, mapped_length_ = length_to_map; } -template -void basic_mmap::sync(std::error_code& error) +template +void basic_mmap::sync(std::error_code& error) { error.clear(); if(!is_open()) @@ -335,8 +313,8 @@ void basic_mmap::sync(std::error_code& error) #endif } -template -void basic_mmap::unmap() +template +void basic_mmap::unmap() { if(!is_open()) { return; } // TODO do we care about errors here? @@ -372,22 +350,23 @@ void basic_mmap::unmap() #endif } -template -typename basic_mmap::pointer basic_mmap::get_mapping_start() noexcept +template +typename basic_mmap::pointer +basic_mmap::get_mapping_start() noexcept { if(!data_) { return nullptr; } const auto offset = mapped_length_ - length_; return data_ - offset; } -template -bool basic_mmap::is_open() const noexcept +template +bool basic_mmap::is_open() const noexcept { return file_handle_ != INVALID_HANDLE_VALUE; } -template -bool basic_mmap::is_mapped() const noexcept +template +bool basic_mmap::is_mapped() const noexcept { #ifdef _WIN32 return file_mapping_handle_ != INVALID_HANDLE_VALUE; @@ -396,8 +375,8 @@ bool basic_mmap::is_mapped() const noexcept #endif } -template -void basic_mmap::swap(basic_mmap& other) +template +void basic_mmap::swap(basic_mmap& other) { if(this != &other) { @@ -413,22 +392,53 @@ void basic_mmap::swap(basic_mmap& other) } } -template -bool operator==(const basic_mmap& a, const basic_mmap& b) +template +bool operator==(const basic_mmap& a, + const basic_mmap& b) { - if(a.is_mapped() && b.is_mapped()) - { - return (a.size() == b.size()) && std::equal(a.begin(), a.end(), b.begin()); - } - return !a.is_mapped() && !b.is_mapped(); + return a.size() == b.size() + && CharTraits::compare(a.data(), b.data(), a.size()) == 0; } -template -bool operator!=(const basic_mmap& a, const basic_mmap& b) +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) +{ + return CharTraits::compare(a.data(), b.data(), a.size()) < 0; +} + +// TODO optimize +template +bool operator<=(const basic_mmap& a, + const basic_mmap& b) +{ + return (a == b) || (a < b); +} + +template +bool operator>(const basic_mmap& a, + const basic_mmap& b) +{ + return CharTraits::compare(a.data(), b.data(), a.size()) > 0; +} + +// TODO optimize +template +bool operator>=(const basic_mmap& a, + const basic_mmap& b) +{ + return (a == b) || (a > b); +} +*/ + } // namespace detail } // namespace mio diff --git a/include/mio/mmap.hpp b/include/mio/mmap.hpp index ea1cc23..35cea4c 100644 --- a/include/mio/mmap.hpp +++ b/include/mio/mmap.hpp @@ -27,12 +27,27 @@ namespace mio { -/** A read-only file memory mapping. */ -template class basic_mmap_source +// This is used by basic_mmap to determine whether to create a read-only or a read-write +// memory mapping. The two possible values are `read_only` and `read_write`. +// TODO try to better expose these values +using detail::access_mode; + +// 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. +using detail::use_full_file_size; + +template< + access_mode AccessMode, + typename CharT, + typename CharTraits = std::char_traits +> class basic_mmap { - using impl_type = detail::basic_mmap; + using impl_type = detail::basic_mmap; impl_type impl_; + static_assert(AccessMode == access_mode::read_only + || AccessMode == access_mode::read_write, + "AccessMode must be either read_only or read_write"); public: using value_type = typename impl_type::value_type; @@ -49,18 +64,12 @@ public: using iterator_category = typename impl_type::iterator_category; using handle_type = typename impl_type::handle_type; - /** - * 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. - */ - static constexpr size_type use_full_file_size = impl_type::use_full_file_size; - /** * The default constructed mmap object is in a non-mapped state, that is, any * operations that attempt to access nonexistent underlying date will result in * undefined behaviour/segmentation faults. */ - basic_mmap_source() = default; + basic_mmap() = default; /** * `handle` must be a valid file handle, which is then used to memory map the @@ -73,7 +82,7 @@ public: * returned by `data` or `begin`), so long as `offset` is valid, will be at `offset` * from the start of the file. */ - basic_mmap_source(const handle_type handle, const size_type offset, const size_type length) + basic_mmap(const handle_type handle, const size_type offset, const size_type length) { std::error_code error; map(handle, offset, length, error); @@ -84,11 +93,11 @@ public: * This class has single-ownership semantics, so transferring ownership may only be * accomplished by moving the object. */ - basic_mmap_source(basic_mmap_source&&) = default; - basic_mmap_source& operator=(basic_mmap_source&&) = default; + basic_mmap(basic_mmap&&) = default; + basic_mmap& operator=(basic_mmap&&) = default; /** The destructor invokes unmap. */ - ~basic_mmap_source() = default; + ~basic_mmap() = default; /** * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, @@ -109,11 +118,12 @@ public: bool empty() const noexcept { return impl_.empty(); } /** - * `size` and `length` both return the logical length (i.e. the one user requested), - * while `mapped_length` returns the actual mapped length, which is usually a - * multiple of the underlying operating system's page allocation granularity. + * `size` and `length` both return the logical length, i.e. the number of bytes + * user requested divided by `sizeof(CharT)`, while `mapped_length` returns the + * actual number of bytes that were mapped, also divided by `sizeof(CharT)`, which + * is a multiple of the underlying operating system's page allocation granularity. */ - size_type size() const noexcept { return impl_.size(); } + size_type size() const noexcept { return impl_.length(); } size_type length() const noexcept { return impl_.length(); } size_type mapped_length() const noexcept { return impl_.mapped_length(); } @@ -121,203 +131,10 @@ public: * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping * exists. */ - const_pointer data() const noexcept { return impl_.data(); } - - /** - * Returns an iterator to the first requested byte, if a valid memory mapping - * exists, otherwise this function call is equivalent to invoking `end`. - */ - const_iterator begin() const noexcept { return impl_.begin(); } - const_iterator cbegin() const noexcept { return impl_.cbegin(); } - - /** Returns an iterator one past the last requested byte. */ - const_iterator end() const noexcept { return impl_.end(); } - const_iterator cend() const noexcept { return impl_.cend(); } - - const_reverse_iterator rbegin() const noexcept { return impl_.rbegin(); } - const_reverse_iterator crbegin() const noexcept { return impl_.crbegin(); } - - const_reverse_iterator rend() const noexcept { return impl_.rend(); } - const_reverse_iterator crend() const noexcept { return impl_.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. - */ - const_reference operator[](const size_type i) const noexcept { return impl_[i]; } - - /** - * Establishes a read-only memory mapping. 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. - * - * When specifying `offset`, 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. - * - * If `length` is `use_full_file_size`, 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) - { - impl_.map(path, offset, length, impl_type::access_mode::read_only, error); - } - - /** - * Establishes a read-only memory mapping. 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` must be a valid file handle, 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. - * - * When specifying `offset`, 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. - * - * If `length` is `use_full_file_size`, 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) - { - impl_.map(handle, offset, length, impl_type::access_mode::read_only, 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() { impl_.unmap(); } - - void swap(basic_mmap_source& other) { impl_.swap(other.impl_); } - - friend bool operator==(const basic_mmap_source& a, const basic_mmap_source& b) - { - return a.impl_ == b.impl_; - } - - friend bool operator!=(const basic_mmap_source& a, const basic_mmap_source& b) - { - return !(a == b); - } -}; - -/** A read-write file memory mapping. */ -template class basic_mmap_sink -{ - using impl_type = detail::basic_mmap; - impl_type impl_; - -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 = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using iterator_category = typename impl_type::iterator_category; - using handle_type = typename impl_type::handle_type; - - /** - * 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. - */ - static constexpr size_type use_full_file_size = impl_type::use_full_file_size; - - /** - * The default constructed mmap object is in a non-mapped state, that is, any - * operations that attempt to access nonexistent underlying date will result in - * undefined behaviour/segmentation faults. - */ - basic_mmap_sink() = default; - - /** - * `handle` must be a valid file handle, which is then used to memory map the - * requested region. Upon failure a `std::error_code` is thrown, detailing the - * cause of the error, and the object remains in an unmapped state. - * - * When specifying `offset`, 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. - */ - basic_mmap_sink(const handle_type handle, const size_type offset, const size_type length) - { - std::error_code error; - map(handle, offset, length, error); - if(error) { throw error; } - } - - /** - * This class has single-ownership semantics, so transferring ownership may only be - * accomplished by moving the object. - */ - basic_mmap_sink(basic_mmap_sink&&) = default; - basic_mmap_sink& operator=(basic_mmap_sink&&) = default; - - /** - * The destructor invokes unmap, but does NOT invoke `sync`. Thus, if the mapped - * region has been written to, `sync` needs to be called in order to persist the - * changes to disk. - */ - ~basic_mmap_sink() = default; - - /** - * 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 impl_.file_handle(); } - handle_type mapping_handle() const noexcept { return impl_.mapping_handle(); } - - /** Returns whether a valid memory mapping has been created. */ - bool is_open() const noexcept { return impl_.is_open(); } - - /** - * Returns if the length that was mapped was 0, in which case no mapping was - * established, i.e. `is_open` returns false. This function is provided so that - * this class has some Container semantics. - */ - bool empty() const noexcept { return impl_.empty(); } - - /** - * `size` and `length` both return the logical length (i.e. the one user requested), - * while `mapped_length` returns the actual mapped length, which is usually a - * multiple of the underlying operating system's page allocation granularity. - */ - size_type size() const noexcept { return impl_.size(); } - size_type length() const noexcept { return impl_.length(); } - size_type mapped_length() const noexcept { return impl_.mapped_length(); } - - /** - * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping - * exists. - */ - pointer data() noexcept { return impl_.data(); } + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > pointer data() noexcept { return impl_.data(); } const_pointer data() const noexcept { return impl_.data(); } /** @@ -329,15 +146,24 @@ public: const_iterator cbegin() const noexcept { return impl_.cbegin(); } /** Returns an iterator one past the last requested byte. */ - iterator end() noexcept { return impl_.end(); } + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > iterator end() noexcept { return impl_.end(); } const_iterator end() const noexcept { return impl_.end(); } const_iterator cend() const noexcept { return impl_.cend(); } - reverse_iterator rbegin() noexcept { return impl_.rbegin(); } + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > reverse_iterator rbegin() noexcept { return impl_.rbegin(); } const_reverse_iterator rbegin() const noexcept { return impl_.rbegin(); } const_reverse_iterator crbegin() const noexcept { return impl_.crbegin(); } - reverse_iterator rend() noexcept { return impl_.rend(); } + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > reverse_iterator rend() noexcept { return impl_.rend(); } const_reverse_iterator rend() const noexcept { return impl_.rend(); } const_reverse_iterator crend() const noexcept { return impl_.crend(); } @@ -350,7 +176,7 @@ public: const_reference operator[](const size_type i) const noexcept { return impl_[i]; } /** - * Establishes a read-write memory mapping. If the mapping is unsuccesful, the + * 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. * @@ -365,17 +191,19 @@ public: * returned by `data` or `begin`), so long as `offset` is valid, will be at `offset` * from the start of the file. * - * If `length` is `use_full_file_size`, a mapping of the entire file is created. + * `length` must be the number of bytes to map, regardless of the underlying + * value_type's size! If it is `use_full_file_size`, 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) { - impl_.map(path, offset, length, impl_type::access_mode::read_only, error); + impl_.map(path, offset, length, AccessMode, error); } /** - * Establishes a read-write memory mapping. If the mapping is unsuccesful, the + * 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. * @@ -389,12 +217,14 @@ public: * returned by `data` or `begin`), so long as `offset` is valid, will be at `offset` * from the start of the file. * - * If `length` is `use_full_file_size`, a mapping of the entire file is created. + * `length` must be the number of bytes to map, regardless of the underlying + * value_type's size! If it is `use_full_file_size`, 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) { - impl_.map(handle, offset, length, impl_type::access_mode::read_write, error); + impl_.map(handle, offset, length, AccessMode, error); } /** @@ -408,28 +238,83 @@ public: */ void unmap() { impl_.unmap(); } + void swap(basic_mmap& other) { impl_.swap(other.impl_); } + /** Flushes the memory mapped page to disk. */ - void sync(std::error_code& error) { impl_.sync(error); } + // TODO better name? + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > void sync(std::error_code& error) { impl_.sync(error); } - void swap(basic_mmap_sink& other) { impl_.swap(other.impl_); } + /** All operators compare two mapped area's according to CharTraits::compare. */ - friend bool operator==(const basic_mmap_sink& a, const basic_mmap_sink& b) + friend bool operator==(const basic_mmap& a, const basic_mmap& b) { return a.impl_ == b.impl_; } - friend bool operator!=(const basic_mmap_sink& a, const basic_mmap_sink& b) + friend bool operator!=(const basic_mmap& a, const basic_mmap& b) { return !(a == b); } + + friend bool operator<(const basic_mmap& a, const basic_mmap& b) + { + return a.impl_ < b.impl_; + } + + friend bool operator<=(const basic_mmap& a, const basic_mmap& b) + { + return a.impl_ <= b.impl_; + } + + friend bool operator>(const basic_mmap& a, const basic_mmap& b) + { + return a.impl_ > b.impl_; + } + + friend bool operator>=(const basic_mmap& a, const basic_mmap& b) + { + return a.impl_ >= b.impl_; + } }; +/** + * This is the basis for all read-only mmap objects and should be preferred over + * directly using basic_mmap. + */ +template< + typename CharT, + typename CharTraits = std::char_traits +> 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< + typename CharT, + typename CharTraits = std::char_traits +> using basic_mmap_sink = basic_mmap; + using mmap_source = basic_mmap_source; using ummap_source = basic_mmap_source; using mmap_sink = basic_mmap_sink; using ummap_sink = basic_mmap_sink; +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. * @@ -441,9 +326,7 @@ template mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset, mmap_source::size_type length, std::error_code& error) { - mmap_source mmap; - mmap.map(token, offset, length, error); - return mmap; + return make_mmap(token, offset, length, error); } /** @@ -457,9 +340,7 @@ template mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset, mmap_sink::size_type length, std::error_code& error) { - mmap_sink mmap; - mmap.map(token, offset, length, error); - return mmap; + return make_mmap(token, offset, length, error); } } // namespace mio diff --git a/include/mio/page.hpp b/include/mio/page.hpp new file mode 100644 index 0000000..c5a73b4 --- /dev/null +++ b/include/mio/page.hpp @@ -0,0 +1,71 @@ +/* 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 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif // WIN32_LEAN_AND_MEAN +# include +#else +# include +#endif + +namespace mio { + +/** + * 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 diff --git a/test/test.cpp b/test/test.cpp index 37b5247..efa949b 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -18,8 +18,8 @@ int main() // Map the region of the file to which buffer was written. std::error_code error; - mio::mmap_source file_view = mio::make_mmap_source(path, - 0, mio::mmap_source::use_full_file_size, error); + mio::mmap_source file_view = mio::make_mmap_source( + path, 0, mio::use_full_file_size, error); if(error) { const auto& errmsg = error.message();