mirror of
https://github.com/vimpunk/mio.git
synced 2025-12-07 01:06:51 +08:00
Merge branch 'master' of https://github.com/mandreyel/mio
This commit is contained in:
commit
ecf20ef27f
19
README.md
19
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.
|
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.
|
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.
|
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 <system_error> // for std::error_code
|
#include <system_error> // for std::error_code
|
||||||
#include <cstdio> // for std::printf
|
#include <cstdio> // for std::printf
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
int handle_error(const std::error_code& error)
|
int handle_error(const std::error_code& error)
|
||||||
{
|
{
|
||||||
@ -73,16 +76,14 @@ int main()
|
|||||||
std::error_code error;
|
std::error_code error;
|
||||||
mio::mmap_sink rw_mmap = mio::make_mmap_sink(
|
mio::mmap_sink rw_mmap = mio::make_mmap_sink(
|
||||||
"file.txt", 0, mio::map_entire_file, error);
|
"file.txt", 0, mio::map_entire_file, error);
|
||||||
if(error) { return handle_error(error); }
|
if (error) { return handle_error(error); }
|
||||||
assert(rw_mmap.is_open());
|
|
||||||
assert(!rw_mmap.empty());
|
|
||||||
|
|
||||||
// You can use any iterator based function.
|
// You can use any iterator based function.
|
||||||
std::fill(rw_mmap.begin(), rw_mmap.end(), 0);
|
std::fill(rw_mmap.begin(), rw_mmap.end(), 0);
|
||||||
|
|
||||||
// Or manually iterate through the mapped region just as if it were any other
|
// 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).
|
// 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;
|
b += 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +94,7 @@ int main()
|
|||||||
// Don't forget to flush changes to disk, which is NOT done by the destructor for
|
// Don't forget to flush changes to disk, which is NOT done by the destructor for
|
||||||
// more explicit control of this potentially expensive operation.
|
// more explicit control of this potentially expensive operation.
|
||||||
rw_mmap.sync(error);
|
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
|
// 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
|
// 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.
|
// Now create the same mapping, but in read-only mode.
|
||||||
mio::mmap_source ro_mmap = mio::make_mmap_source(
|
mio::mmap_source ro_mmap = mio::make_mmap_source(
|
||||||
"file.txt", 0, mio::map_entire_file, error);
|
"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];
|
const int the_answer_to_everything = ro_mmap[answer_index];
|
||||||
assert(the_answer_to_everything == 42);
|
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++
|
```c++
|
||||||
#include <mio/shared_mmap.hpp>
|
#include <mio/shared_mmap.hpp>
|
||||||
|
|
||||||
@ -121,7 +122,7 @@ mio::shared_mmap_source shared_mmap4;
|
|||||||
shared_mmap4.map("path", offset, size_to_map, error);
|
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++
|
```c++
|
||||||
using mmap_source = basic_mmap_source<char>;
|
using mmap_source = basic_mmap_source<char>;
|
||||||
using ummap_source = basic_mmap_source<unsigned char>;
|
using ummap_source = basic_mmap_source<unsigned char>;
|
||||||
@ -135,7 +136,7 @@ using mmap_source = mio::basic_mmap_source<std::byte>;
|
|||||||
using mmap_sink = mio::basic_mmap_sink<std::byte>;
|
using mmap_sink = mio::basic_mmap_sink<std::byte>;
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
### 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.
|
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.
|
||||||
|
|||||||
@ -122,7 +122,7 @@ struct mmap_context
|
|||||||
#endif
|
#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 length, const access_mode mode, std::error_code& error)
|
||||||
{
|
{
|
||||||
const int64_t aligned_offset = make_offset_page_aligned(offset);
|
const int64_t aligned_offset = make_offset_page_aligned(offset);
|
||||||
|
|||||||
@ -151,7 +151,10 @@ public:
|
|||||||
* Returns an iterator to the first requested byte, if a valid memory mapping
|
* Returns an iterator to the first requested byte, if a valid memory mapping
|
||||||
* exists, otherwise this function call is equivalent to invoking `end`.
|
* 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<A == access_mode::write>::type
|
||||||
|
> iterator begin() noexcept { return impl_.begin(); }
|
||||||
const_iterator begin() const noexcept { return impl_.begin(); }
|
const_iterator begin() const noexcept { return impl_.begin(); }
|
||||||
const_iterator cbegin() const noexcept { return impl_.cbegin(); }
|
const_iterator cbegin() const noexcept { return impl_.cbegin(); }
|
||||||
|
|
||||||
|
|||||||
@ -18,14 +18,14 @@ int main()
|
|||||||
std::error_code error;
|
std::error_code error;
|
||||||
mio::mmap_sink rw_mmap = mio::make_mmap_sink(
|
mio::mmap_sink rw_mmap = mio::make_mmap_sink(
|
||||||
"file.txt", 0, mio::map_entire_file, error);
|
"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.
|
// You can use any iterator based function.
|
||||||
std::fill(rw_mmap.begin(), rw_mmap.end(), 0);
|
std::fill(rw_mmap.begin(), rw_mmap.end(), 0);
|
||||||
|
|
||||||
// Or manually iterate through the mapped region just as if it were any other
|
// 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).
|
// 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;
|
b += 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ int main()
|
|||||||
// Don't forget to flush changes to disk, which is NOT done by the destructor for
|
// Don't forget to flush changes to disk, which is NOT done by the destructor for
|
||||||
// more explicit control of this potentially expensive operation.
|
// more explicit control of this potentially expensive operation.
|
||||||
rw_mmap.sync(error);
|
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
|
// 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
|
// 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.
|
// Now create the same mapping, but in read-only mode.
|
||||||
mio::mmap_source ro_mmap = mio::make_mmap_source(
|
mio::mmap_source ro_mmap = mio::make_mmap_source(
|
||||||
"file.txt", 0, mio::map_entire_file, error);
|
"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];
|
const int the_answer_to_everything = ro_mmap[answer_index];
|
||||||
assert(the_answer_to_everything == 42);
|
assert(the_answer_to_everything == 42);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user