diff --git a/README.md b/README.md index a257f83..ac5a011 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # mio -A simple header-only cross-platform C++14 memory mapping library. +A simple header-only cross-platform C++11 memory mapping library. ## Example ```c++ @@ -42,10 +42,14 @@ int main() // Or just change one value with the subscript operator. mmap2[mmap2.size() / 2] = 42; + // Don't forget to flush changes to disk, which is NOT done by the constructor for + // more explicit control of this potentially expensive operation. + mmap2.sync(); + // You can also create a mapping using the constructor, which throws a // std::error_code upon failure. + const auto page_size = mio::page_size(); try { - const auto page_size = mio::page_size(); mio::mmap_sink mmap3("another/path/to/file", offset_type(page_size), length_type(page_size)); // ... @@ -58,6 +62,15 @@ int main() // accounted for by mio, so appropriate conversions need be done by the user). using wmmap_source = mio::basic_mmap_source; using i32mmap_source = mio::basic_mmap_source; + + // mio::basic_mmap<> has std::unique_ptr semantics, but if multiple copies to the + // same mapping is required, use mio::basic_shared_mmap<> for std::shared_ptr + // semantics, which has the same interface as mio::basic_mmap<>. + mio::shared_mmap_source shared_mmap1("path", offset_type(0), length_type(page_size)); + mio::shared_mmap_source shared_mmap2(std::move(mmap1)); // or use operator= + mio::shared_mmap_source shared_mmap3(std::make_shared(mmap1)); // or use operator= + mio::shared_mmap_source shared_mmap4; + shared_mmap4.map("path", offset_type(0), length_type(page_size), error); } ``` diff --git a/include/mio/detail/basic_mmap.hpp b/include/mio/detail/basic_mmap.hpp index e1e05c2..5ac5052 100644 --- a/include/mio/detail/basic_mmap.hpp +++ b/include/mio/detail/basic_mmap.hpp @@ -79,9 +79,9 @@ private: #endif // Length, in bytes, requested by user, which may not be the length of the full - // mapping. - size_type length_ = 0; - size_type mapped_length_ = 0; + // mapping, and the entire length of the full mapping. + size_type num_bytes_ = 0; + size_type num_mapped_bytes_ = 0; // 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, @@ -105,9 +105,9 @@ public: bool is_mapped() const noexcept; bool empty() const noexcept { return length() == 0; } - size_type offset() const noexcept { return mapped_length_ - length_; } - size_type length() const noexcept { return length_; } - size_type mapped_length() const noexcept { return mapped_length_; } + size_type offset() const noexcept { return to_char_size(num_mapped_bytes_ - num_bytes_); } + size_type length() const noexcept { return to_char_size(num_bytes_); } + size_type mapped_length() const noexcept { return to_char_size(num_mapped_bytes_); } pointer data() noexcept { return data_; } const_pointer data() const noexcept { return data_; } @@ -135,9 +135,9 @@ public: void set_offset(const size_type offset) noexcept; template - void map(String& path, size_type offset, size_type length, + void map(String& path, size_type offset, size_type num_bytes, access_mode mode, std::error_code& error); - void map(handle_type handle, size_type offset, size_type length, + void map(handle_type handle, size_type offset, size_type num_bytes, access_mode mode, std::error_code& error); void unmap(); void close(); @@ -150,8 +150,18 @@ private: pointer get_mapping_start() noexcept; - void map(const size_type offset, size_type length, + void map(const size_type offset, size_type num_bytes, const access_mode mode, std::error_code& error); + + static size_type to_char_size(const size_type num_bytes) noexcept + { + return num_bytes >> (sizeof(CharT) - 1); + } + + static size_type to_byte_size(const size_type num_chars) noexcept + { + return num_chars << (sizeof(CharT) - 1); + } }; template diff --git a/include/mio/detail/basic_mmap.ipp b/include/mio/detail/basic_mmap.ipp index a0d4a87..76d6d06 100644 --- a/include/mio/detail/basic_mmap.ipp +++ b/include/mio/detail/basic_mmap.ipp @@ -49,7 +49,7 @@ inline DWORD int64_high(int64_t n) noexcept inline DWORD int64_low(int64_t n) noexcept { - return n & 0xff'ff'ff'ff; + return n & 0xffffffff; } #endif @@ -125,8 +125,8 @@ basic_mmap::~basic_mmap() 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_)) + , num_bytes_(std::move(other.num_bytes_)) + , num_mapped_bytes_(std::move(other.num_mapped_bytes_)) , file_handle_(std::move(other.file_handle_)) #ifdef _WIN32 , file_mapping_handle_(std::move(other.file_mapping_handle_)) @@ -134,7 +134,7 @@ basic_mmap::basic_mmap(basic_mmap&& other) , is_handle_internal_(std::move(other.is_handle_internal_)) { other.data_ = nullptr; - other.length_ = other.mapped_length_ = 0; + other.num_bytes_ = other.num_mapped_bytes_ = 0; other.file_handle_ = INVALID_HANDLE_VALUE; #ifdef _WIN32 other.file_mapping_handle_ = INVALID_HANDLE_VALUE; @@ -149,8 +149,8 @@ basic_mmap& basic_mmap::operator=(basic_mmap&& 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_); + num_bytes_ = std::move(other.num_bytes_); + num_mapped_bytes_ = std::move(other.num_mapped_bytes_); file_handle_ = std::move(other.file_handle_); #ifdef _WIN32 file_mapping_handle_ = std::move(other.file_mapping_handle_); @@ -160,7 +160,7 @@ basic_mmap& basic_mmap::operator=(basic_mmap&& other) // 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.num_bytes_ = other.num_mapped_bytes_ = 0; other.file_handle_ = INVALID_HANDLE_VALUE; #ifdef _WIN32 other.file_mapping_handle_ = INVALID_HANDLE_VALUE; @@ -183,7 +183,7 @@ typename basic_mmap::handle_type basic_mmap::mapping_handle() cons template template void basic_mmap::map(String& path, size_type offset, - size_type length, access_mode mode, std::error_code& error) + size_type num_bytes, access_mode mode, std::error_code& error) { error.clear(); if(detail::empty(path)) @@ -195,7 +195,7 @@ void basic_mmap::map(String& path, size_type offset, { const auto handle = open_file(path, mode, error); if(error) { return; } - map(handle, offset, length, mode, error); + map(handle, offset, num_bytes, mode, error); // MUST be after the call to map, as that sets this to true. is_handle_internal_ = true; } @@ -203,7 +203,7 @@ void basic_mmap::map(String& path, size_type offset, template void basic_mmap::map(handle_type handle, size_type offset, - size_type length, access_mode mode, std::error_code& error) + size_type num_bytes, access_mode mode, std::error_code& error) { error.clear(); if(handle == INVALID_HANDLE_VALUE) @@ -215,11 +215,11 @@ void basic_mmap::map(handle_type handle, size_type offset, const auto file_size = query_file_size(handle, error); if(error) { return; } - if(length <= map_entire_file) + if(num_bytes <= map_entire_file) { - length = file_size; + num_bytes = file_size; } - else if(offset + length > file_size) + else if(offset + num_bytes > file_size) { error = std::make_error_code(std::errc::invalid_argument); return; @@ -227,17 +227,17 @@ void basic_mmap::map(handle_type handle, size_type offset, file_handle_ = handle; is_handle_internal_ = false; - map(offset, length, mode, error); + map(offset, num_bytes, mode, error); } template -void basic_mmap::map(const size_type offset, const size_type length, +void basic_mmap::map(const size_type offset, const size_type num_bytes, const access_mode mode, std::error_code& error) { const size_type aligned_offset = make_offset_page_aligned(offset); - const size_type length_to_map = offset - aligned_offset + length; + const size_type num_bytes_to_map = offset - aligned_offset + num_bytes; #ifdef _WIN32 - const size_type max_file_size = offset + length; + const size_type max_file_size = offset + num_bytes; file_mapping_handle_ = ::CreateFileMapping( file_handle_, 0, @@ -256,7 +256,7 @@ void basic_mmap::map(const size_type offset, const size_type length, mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE, int64_high(aligned_offset), int64_low(aligned_offset), - length_to_map)); + num_bytes_to_map)); if(mapping_start == nullptr) { error = last_error(); @@ -265,7 +265,7 @@ void basic_mmap::map(const size_type offset, const size_type length, #else const pointer mapping_start = static_cast(::mmap( 0, // Don't give hint as to where to map. - length_to_map, + num_bytes_to_map, mode == access_mode::read ? PROT_READ : PROT_WRITE, MAP_SHARED, file_handle_, @@ -277,8 +277,8 @@ void basic_mmap::map(const size_type offset, const size_type length, } #endif data_ = mapping_start + offset - aligned_offset; - length_ = length; - mapped_length_ = length_to_map; + num_bytes_ = num_bytes; + num_mapped_bytes_ = num_bytes_to_map; } template @@ -294,10 +294,10 @@ void basic_mmap::sync(std::error_code& error) if(data() != nullptr) { #ifdef _WIN32 - if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0 + if(::FlushViewOfFile(get_mapping_start(), num_mapped_bytes_) == 0 || ::FlushFileBuffers(file_handle_) == 0) #else - if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0) + if(::msync(get_mapping_start(), num_mapped_bytes_, MS_SYNC) != 0) #endif { error = last_error(); @@ -325,7 +325,7 @@ void basic_mmap::unmap() file_mapping_handle_ = INVALID_HANDLE_VALUE; } #else - if(data_) { ::munmap(const_cast(get_mapping_start()), mapped_length_); } + if(data_) { ::munmap(const_cast(get_mapping_start()), num_mapped_bytes_); } #endif // If file handle was obtained by our opening it (when map is called with a path, @@ -342,7 +342,7 @@ void basic_mmap::unmap() // Reset fields to their default values. data_ = nullptr; - length_ = mapped_length_ = 0; + num_bytes_ = num_mapped_bytes_ = 0; file_handle_ = INVALID_HANDLE_VALUE; #ifdef _WIN32 file_mapping_handle_ = INVALID_HANDLE_VALUE; @@ -377,7 +377,7 @@ template void basic_mmap::set_length(const size_type length) noexcept { if(length > this->length() - offset()) { throw std::invalid_argument(""); } - length_ = length; + num_bytes_ = to_byte_size(length); } template @@ -386,7 +386,7 @@ void basic_mmap::set_offset(const size_type offset) noexcept if(offset >= mapped_length()) { throw std::invalid_argument(""); } const auto diff = offset - this->offset(); data_ += diff; - length_ += -1 * diff; + num_bytes_ += -1 * to_byte_size(diff); } template @@ -400,8 +400,8 @@ void basic_mmap::swap(basic_mmap& other) #ifdef _WIN32 swap(file_mapping_handle_, other.file_mapping_handle_); #endif - swap(length_, other.length_); - swap(mapped_length_, other.mapped_length_); + swap(num_bytes_, other.num_bytes_); + swap(num_mapped_bytes_, other.num_mapped_bytes_); swap(is_handle_internal_, other.is_handle_internal_); } } diff --git a/include/mio/mmap.hpp b/include/mio/mmap.hpp index b1357fe..7f226b3 100644 --- a/include/mio/mmap.hpp +++ b/include/mio/mmap.hpp @@ -141,16 +141,16 @@ public: * 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 char_size(impl_.length()); } - size_type length() const noexcept { return char_size(impl_.length()); } - size_type mapped_length() const noexcept { return char_size(impl_.mapped_length()); } + 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(); } /** * Returns the offset, relative to the file's start, at which the mapping was * requested to be created, expressed in multiples of `sizeof(CharT)` rather than * in bytes. */ - size_type offset() const noexcept { return char_size(impl_.offset()); } + size_type offset() const noexcept { return impl_.offset(); } /** * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping @@ -215,8 +215,8 @@ public: * If `offset` is larger than the number of bytes mapped, an std::invalid_argument * exception is thrown. */ - void set_length(const size_type length) noexcept { impl_.set_length(byte_size(length)); } - void set_offset(const size_type offset) noexcept { impl_.set_offset(byte_size(offset)); } + void set_length(const size_type length) noexcept { impl_.set_length(length); } + void set_offset(const size_type offset) noexcept { impl_.set_offset(offset); } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the @@ -327,18 +327,6 @@ public: { return a.impl_ >= b.impl_; } - -private: - - static size_type char_size(const size_type num_bytes) noexcept - { - return num_bytes >> (sizeof(CharT) - 1); - } - - static size_type byte_size(const size_type num_bytes) noexcept - { - return num_bytes << (sizeof(CharT) - 1); - } }; /**