diff --git a/README.md b/README.md index fc6ead4..8499935 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ Because memory mapping is the best thing since sliced bread! More seriously, the primary motivation for writing this library instead of using Boost.Iostreams, was the lack of support for establishing a memory mapping with an already open file handle/descriptor. This is possible with mio. +Furthermore, Boost.Iostreams' solution requires that the user pick offsets exactly at page boundaries, which is cumbersome and error prone. mio, on the other hand, manages this internally, accepting any offset and finding the nearest page boundary. + Albeit a minor nitpick, Boost.Iostreams implements memory mapped file IO with a `std::shared_ptr` to provide shared semantics, even if not needed, and the overhead of the heap allocation may be unnecessary and/or unwanted. In mio, there are two classes to cover the two use-cases: one that is move-only (basically a zero-cost abstraction over the system specific mmapping functions), and the other that acts just like its Boost.Iostreams counterpart, with shared semantics. @@ -58,6 +60,7 @@ However, mio does not check whether the provided file descriptor has the same ac #include // for std::error_code #include // for std::printf #include +#include int handle_error(const std::error_code& error) { @@ -73,16 +76,14 @@ int main() std::error_code error; mio::mmap_sink rw_mmap = mio::make_mmap_sink( "file.txt", 0, mio::map_entire_file, error); - if(error) { return handle_error(error); } - assert(rw_mmap.is_open()); - assert(!rw_mmap.empty()); + if (error) { return handle_error(error); } // You can use any iterator based function. std::fill(rw_mmap.begin(), rw_mmap.end(), 0); // Or manually iterate through the mapped region just as if it were any other // container, and change each byte's value (since this is a read-write mapping). - for(auto& b : rw_mmap) { + for (auto& b : rw_mmap) { b += 10; } @@ -93,7 +94,7 @@ int main() // Don't forget to flush changes to disk, which is NOT done by the destructor for // more explicit control of this potentially expensive operation. rw_mmap.sync(error); - if(error) { handle_error(error); } + if (error) { return handle_error(error); } // We can then remove the mapping, after which rw_mmap will be in a default // constructed state, i.e. this has the same effect as if the destructor had been @@ -103,14 +104,14 @@ int main() // Now create the same mapping, but in read-only mode. mio::mmap_source ro_mmap = mio::make_mmap_source( "file.txt", 0, mio::map_entire_file, error); - if(error) { return handle_error(error); } + if (error) { return handle_error(error); } const int the_answer_to_everything = ro_mmap[answer_index]; assert(the_answer_to_everything == 42); } ``` -`mio::basic_mmap` move-only, but if multiple copies to the same mapping is required, use `mio::basic_shared_mmap` which has `std::shared_ptr` semantics and has the same interface as `mio::basic_mmap`. +`mio::basic_mmap` is move-only, but if multiple copies to the same mapping are needed, use `mio::basic_shared_mmap` which has `std::shared_ptr` semantics and has the same interface as `mio::basic_mmap`. ```c++ #include @@ -121,7 +122,7 @@ mio::shared_mmap_source shared_mmap4; shared_mmap4.map("path", offset, size_to_map, error); ``` -It's possible to define the type of a byte (which has to be the same width as `char`), though aliases for the most commonly ones are provided by default: +It's possible to define the type of a byte (which has to be the same width as `char`), though aliases for the most common ones are provided by default: ```c++ using mmap_source = basic_mmap_source; using ummap_source = basic_mmap_source; @@ -135,7 +136,7 @@ using mmap_source = mio::basic_mmap_source; using mmap_sink = mio::basic_mmap_sink; ``` -You can query the underlying system's page allocation granularity by invoking `mio::page_size()`, which is located in `mio/page.hpp`. +Though generally not needed, since mio maps users requested offsets to page boundaries, you can query the underlying system's page allocation granularity by invoking `mio::page_size()`, which is located in `mio/page.hpp`. ### Installation mio is a header-only library, so just copy the contents in `mio/include` into your system wide include path, such as `/usr/include`, or into your project's lib folder. diff --git a/include/mio/detail/basic_mmap.ipp b/include/mio/detail/basic_mmap.ipp index bfa19d6..1fd4e70 100644 --- a/include/mio/detail/basic_mmap.ipp +++ b/include/mio/detail/basic_mmap.ipp @@ -122,7 +122,7 @@ struct mmap_context #endif }; -mmap_context memory_map(const file_handle_type file_handle, const int64_t offset, +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); diff --git a/include/mio/mmap.hpp b/include/mio/mmap.hpp index 2b8b2ed..6d71b25 100644 --- a/include/mio/mmap.hpp +++ b/include/mio/mmap.hpp @@ -151,7 +151,10 @@ public: * Returns an iterator to the first requested byte, if a valid memory mapping * exists, otherwise this function call is equivalent to invoking `end`. */ - iterator begin() noexcept { return impl_.begin(); } + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > iterator begin() noexcept { return impl_.begin(); } const_iterator begin() const noexcept { return impl_.begin(); } const_iterator cbegin() const noexcept { return impl_.cbegin(); } diff --git a/test/example.cpp b/test/example.cpp index bfb294f..5385ffc 100644 --- a/test/example.cpp +++ b/test/example.cpp @@ -18,14 +18,14 @@ int main() std::error_code error; mio::mmap_sink rw_mmap = mio::make_mmap_sink( "file.txt", 0, mio::map_entire_file, error); - if(error) { return handle_error(error); } + if (error) { return handle_error(error); } // You can use any iterator based function. std::fill(rw_mmap.begin(), rw_mmap.end(), 0); // Or manually iterate through the mapped region just as if it were any other // container, and change each byte's value (since this is a read-write mapping). - for(auto& b : rw_mmap) { + for (auto& b : rw_mmap) { b += 10; } @@ -36,7 +36,7 @@ int main() // Don't forget to flush changes to disk, which is NOT done by the destructor for // more explicit control of this potentially expensive operation. rw_mmap.sync(error); - if(error) { handle_error(error); } + if (error) { return handle_error(error); } // We can then remove the mapping, after which rw_mmap will be in a default // constructed state, i.e. this has the same effect as if the destructor had been @@ -46,7 +46,7 @@ int main() // Now create the same mapping, but in read-only mode. mio::mmap_source ro_mmap = mio::make_mmap_source( "file.txt", 0, mio::map_entire_file, error); - if(error) { return handle_error(error); } + if (error) { return handle_error(error); } const int the_answer_to_everything = ro_mmap[answer_index]; assert(the_answer_to_everything == 42);