mirror of
https://github.com/vimpunk/mio.git
synced 2025-12-06 08:46:51 +08:00
Fix shared_mmap API
This commit is contained in:
parent
61470daa08
commit
dcfce4d0ca
24
README.md
24
README.md
@ -1,5 +1,5 @@
|
||||
# mio
|
||||
A simple header-only cross-platform C++11 memory mapping library.
|
||||
A simple header-only cross-platform C++11 memory mapping library with an MIT license.
|
||||
|
||||
## Example
|
||||
There are three ways to a create a mapping:
|
||||
@ -22,21 +22,23 @@ mio::mmap_source mmap;
|
||||
mmap.map(path, offset, size_to_map, error);
|
||||
```
|
||||
|
||||
Moreover, in each case, you can provide either some string type for the file's path, or you can use an existing, valid file handle. However, mio does not check whether the provided file descriptor has the same access permissions as the desired mapping, so the mapping may fail.
|
||||
Moreover, in each case, you can provide either some string type for the file's path, or you can use an existing, valid file handle.
|
||||
```c++
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <mio/mmap.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
int main()
|
||||
{
|
||||
// NOTE: error checking omitted for brevity.
|
||||
// NOTE: error handling omitted for brevity.
|
||||
const int fd = open("file.txt", O_RDONLY);
|
||||
mio::mmap_source mmap(fd, 0, mio::map_entire_file);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
However, mio does not check whether the provided file descriptor has the same access permissions as the desired mapping, so the mapping may fail.
|
||||
|
||||
General usage:
|
||||
```c++
|
||||
@ -60,9 +62,14 @@ int main()
|
||||
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());
|
||||
|
||||
// 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).
|
||||
// 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) {
|
||||
b += 10;
|
||||
}
|
||||
@ -91,7 +98,7 @@ int main()
|
||||
}
|
||||
```
|
||||
|
||||
`mio::basic_mmap` has move-only semantics, 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` 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`.
|
||||
```c++
|
||||
#include <mio/shared_mmap.hpp>
|
||||
|
||||
@ -102,10 +109,13 @@ mio::shared_mmap_source shared_mmap4;
|
||||
shared_mmap4.map("path", offset, size_to_map, error);
|
||||
```
|
||||
|
||||
It's possible to define the character type of a byte, though aliases for the most commonly used types are provided:
|
||||
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:
|
||||
```c++
|
||||
using mmap_source = basic_mmap_source<char>;
|
||||
using ummap_source = basic_mmap_source<unsigned char>;
|
||||
|
||||
using mmap_sink = basic_mmap_sink<char>;
|
||||
using ummap_sink = basic_mmap_sink<unsigned char>;
|
||||
```
|
||||
But it may be useful to define your own types, say when using the new `std::byte` type in C++17:
|
||||
```c++
|
||||
|
||||
@ -290,11 +290,11 @@ void basic_mmap<ByteT>::map(handle_type handle, size_type offset,
|
||||
const mmap_context ctx = memory_map(handle, offset, length, mode, error);
|
||||
if(!error)
|
||||
{
|
||||
// We must unmap any previous mapping that may have existed prior to this call.
|
||||
// Note that this must only be invoked after the new mapping has been created
|
||||
// in order to provide the strong guarantee that, should a mapping fail, the
|
||||
// `map` function leaves the this instance in a state as though the function
|
||||
// had never been called.
|
||||
// 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;
|
||||
@ -354,9 +354,9 @@ void basic_mmap<ByteT>::unmap()
|
||||
if(data_) { ::munmap(const_cast<pointer>(get_mapping_start()), mapped_length_); }
|
||||
#endif
|
||||
|
||||
// If file handle was obtained by our opening it (when map is called with a path,
|
||||
// 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 of this basic_mmap instance.
|
||||
// be closed as it may still be used outside this instance.
|
||||
if(is_handle_internal_)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
||||
@ -65,7 +65,7 @@ public:
|
||||
|
||||
/**
|
||||
* 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
|
||||
* operation that attempts to access nonexistent underlying data will result in
|
||||
* undefined behaviour/segmentation faults.
|
||||
*/
|
||||
basic_mmap() = default;
|
||||
|
||||
@ -34,9 +34,6 @@ namespace mio {
|
||||
*
|
||||
* This is not the default behaviour of `basic_mmap` to avoid allocating on the heap if
|
||||
* shared semantics are not required.
|
||||
*
|
||||
* If basic_shared_mmap is not a valid mappig (i.e. owns no `basic_mmap`), using its
|
||||
* methods is undefined behaviour.
|
||||
*/
|
||||
template<
|
||||
access_mode AccessMode,
|
||||
@ -128,14 +125,14 @@ public:
|
||||
handle_type mapping_handle() const noexcept { return pimpl_->mapping_handle(); }
|
||||
|
||||
/** Returns whether a valid memory mapping has been created. */
|
||||
bool is_open() const noexcept { return pimpl_->is_open(); }
|
||||
bool is_open() const noexcept { return pimpl_ && pimpl_->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 pimpl_->empty(); }
|
||||
bool empty() const noexcept { return !pimpl_ || pimpl_->empty(); }
|
||||
|
||||
/**
|
||||
* `size` and `length` both return the logical length, i.e. the number of bytes
|
||||
@ -143,15 +140,16 @@ public:
|
||||
* bytes that were mapped which is a multiple of the underlying operating system's
|
||||
* page allocation granularity.
|
||||
*/
|
||||
size_type size() const noexcept { return pimpl_->length(); }
|
||||
size_type length() const noexcept { return pimpl_->length(); }
|
||||
size_type mapped_length() const noexcept { return pimpl_->mapped_length(); }
|
||||
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_->offset(); }
|
||||
size_type offset() const noexcept { return pimpl_ ? pimpl_->offset() : 0; }
|
||||
|
||||
/**
|
||||
* Returns a pointer to the first requested byte, or `nullptr` if no memory mapping
|
||||
@ -161,7 +159,7 @@ public:
|
||||
access_mode A = AccessMode,
|
||||
typename = typename std::enable_if<A == access_mode::write>::type
|
||||
> pointer data() noexcept { return pimpl_->data(); }
|
||||
const_pointer data() const 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
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include <system_error> // for std::error_code
|
||||
#include <cstdio> // for std::printf
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
int handle_error(const std::error_code& error)
|
||||
{
|
||||
@ -19,8 +20,11 @@ int main()
|
||||
"file.txt", 0, mio::map_entire_file, error);
|
||||
if(error) { return handle_error(error); }
|
||||
|
||||
// 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).
|
||||
// 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) {
|
||||
b += 10;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user