mirror of
https://github.com/vimpunk/mio.git
synced 2026-01-01 03:12:20 +08:00
updated README and comments
This commit is contained in:
parent
2024c807f0
commit
adf941015f
119
README.md
119
README.md
@ -2,15 +2,51 @@
|
||||
A simple header-only cross-platform C++11 memory mapping library.
|
||||
|
||||
## Example
|
||||
There are three ways to a create a mapping:
|
||||
|
||||
- Using the constructor, which throws on failure:
|
||||
```c++
|
||||
mio::mmap_source mmap(path, offset, size_to_map);
|
||||
```
|
||||
|
||||
- Using the factory function:
|
||||
```c++
|
||||
std::error_code error;
|
||||
mio::mmap_source mmap = mio::make_mmap_source(path, offset, size_to_map, error);
|
||||
```
|
||||
|
||||
- Using the `map` member function:
|
||||
```c++
|
||||
std::error_code error;
|
||||
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.
|
||||
```c++
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <mio/mmap.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
// NOTE: error checking omitted for brevity.
|
||||
const int fd = open("file.txt", O_RDONLY);
|
||||
mio::mmap_source mmap(fd, 0, mio::map_entire_file);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
General usage:
|
||||
```c++
|
||||
#include <mio/mmap.hpp>
|
||||
#include <mio/page.hpp> // for mio::page_size
|
||||
#include <system_error> // for std::error_code
|
||||
#include <cstdio> // for std::printf
|
||||
|
||||
// This is just to clarify which is which when using the various mapping methods.
|
||||
using offset_type = mio::mmap_source::size_type;
|
||||
using length_type = mio::mmap_source::size_type;
|
||||
using offset_type = mio::mmap_sink::size_type;
|
||||
using length_type = mio::mmap_sink::size_type;
|
||||
|
||||
int handle_error(const std::error_code& error)
|
||||
{
|
||||
@ -21,30 +57,23 @@ int handle_error(const std::error_code& error)
|
||||
|
||||
int main()
|
||||
{
|
||||
// Read-only memory map the whole file by using `map_entire_file` where the
|
||||
// length of the mapping would otherwise be expected, with the factory method.
|
||||
// Read-write memory map the whole file by using `map_entire_file` where the
|
||||
// length of the mapping is otherwise expected, with the factory method.
|
||||
std::error_code error;
|
||||
mio::mmap_source mmap1 = mio::make_mmap_source("log.txt",
|
||||
mio::mmap_sink rw_mmap = mio::make_mmap_sink("file.txt",
|
||||
offset_type(0), length_type(mio::map_entire_file), error);
|
||||
if(error) { return handle_error(error); }
|
||||
|
||||
// Read-write memory map the beginning of some file using the `map` member function.
|
||||
mio::mmap_sink mmap2;
|
||||
mmap2.map("some/path/to/file", offset_type(0), length_type(2342), error);
|
||||
if(error) { return handle_error(error); }
|
||||
|
||||
// Iterate through the memory mapping just as if it were any other container.
|
||||
for(auto& c : mmap2) {
|
||||
// Since this is a read-write mapping, we can change c's value.
|
||||
c += 10;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Or just change one value with the subscript operator.
|
||||
mmap2[mmap2.size() / 2] = 42;
|
||||
const int answer_index = rw_mmap.size() / 2;
|
||||
|
||||
// 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();
|
||||
// Or just change one value with the subscript operator.
|
||||
rw_mmap[answer_index] = 42;
|
||||
|
||||
// You can also create a mapping using the constructor, which throws a
|
||||
// std::error_code upon failure.
|
||||
@ -57,23 +86,43 @@ int main()
|
||||
return handle_error(error);
|
||||
}
|
||||
|
||||
// It's possible to change the character type of the mmap object, which is useful
|
||||
// for wide strings or reading fixed width integers (although endianness is not
|
||||
// accounted for by mio, so appropriate conversions need be done by the user).
|
||||
using wmmap_source = mio::basic_mmap_source<wchar_t>;
|
||||
using i32mmap_source = mio::basic_mmap_source<int32_t>;
|
||||
// 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();
|
||||
// We can then remove the mapping, after which rw_mmap will be in a default
|
||||
// constructed state.
|
||||
rw_mmap.close();
|
||||
|
||||
// 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<mio::mmap_source>(mmap1)); // or use operator=
|
||||
mio::shared_mmap_source shared_mmap4;
|
||||
shared_mmap4.map("path", offset_type(0), length_type(page_size), error);
|
||||
// Now create the same mapping, but in read-only mode.
|
||||
mio::mmap_source ro_mmap = mio::make_mmap_source("file.txt",
|
||||
offset_type(0), length_type(mio::map_entire_file), error);
|
||||
if(error) { return handle_error(error); }
|
||||
|
||||
const int the_answer_to_everything = ro_mmap[answer_index];
|
||||
}
|
||||
```
|
||||
|
||||
# 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::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, which has the same interface as `mio::basic_mmap`.
|
||||
```c++
|
||||
#include <mio/shared_mmap.hpp>
|
||||
|
||||
mio::shared_mmap_source shared_mmap1("path", offset, size_to_map);
|
||||
mio::shared_mmap_source shared_mmap2(std::move(mmap1)); // or use operator=
|
||||
mio::shared_mmap_source shared_mmap3(std::make_shared<mio::mmap_source>(mmap1)); // or use operator=
|
||||
mio::shared_mmap_source shared_mmap4;
|
||||
shared_mmap4.map("path", offset, size_to_map, error);
|
||||
```
|
||||
|
||||
It's possible to change the character type of the mmap object, which is useful
|
||||
for wide strings or reading fixed width integers (although endianness is not
|
||||
accounted for by mio, so appropriate conversions need be done by the user).
|
||||
```c++
|
||||
using wmmap_source = mio::basic_mmap_source<wchar_t>;
|
||||
using i32mmap_source = mio::basic_mmap_source<int32_t>;
|
||||
using u16shared_mmap_sink = mio::basic_shared_mmap_sink<uint16_t>;
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
@ -45,6 +45,12 @@ enum class access_mode
|
||||
write
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
using file_handle_type = HANDLE;
|
||||
#else
|
||||
using file_handle_type = int;
|
||||
#endif
|
||||
|
||||
template<typename CharT> struct basic_mmap
|
||||
{
|
||||
using value_type = CharT;
|
||||
@ -59,11 +65,7 @@ template<typename CharT> struct basic_mmap
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
#ifdef _WIN32
|
||||
using handle_type = HANDLE;
|
||||
#else
|
||||
using handle_type = int;
|
||||
#endif
|
||||
using handle_type = file_handle_type;
|
||||
|
||||
private:
|
||||
|
||||
@ -108,6 +110,8 @@ public:
|
||||
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_); }
|
||||
size_type length_in_bytes() const noexcept { return num_bytes_; }
|
||||
size_type mapped_length_in_bytes() const noexcept { return num_mapped_bytes_; }
|
||||
|
||||
pointer data() noexcept { return data_; }
|
||||
const_pointer data() const noexcept { return data_; }
|
||||
|
||||
@ -38,9 +38,6 @@
|
||||
namespace mio {
|
||||
namespace detail {
|
||||
|
||||
// Generic handle type for use by free functions.
|
||||
using handle_type = basic_mmap<char>::handle_type;
|
||||
|
||||
#ifdef _WIN32
|
||||
inline DWORD int64_high(int64_t n) noexcept
|
||||
{
|
||||
@ -65,7 +62,7 @@ inline std::error_code last_error() noexcept
|
||||
}
|
||||
|
||||
template<typename Path>
|
||||
handle_type open_file(const Path& path, const access_mode mode, std::error_code& error)
|
||||
file_handle_type open_file(const Path& path, const access_mode mode, std::error_code& error)
|
||||
{
|
||||
error.clear();
|
||||
if(detail::empty(path))
|
||||
@ -92,7 +89,7 @@ handle_type open_file(const Path& path, const access_mode mode, std::error_code&
|
||||
return handle;
|
||||
}
|
||||
|
||||
inline int64_t query_file_size(handle_type handle, std::error_code& error)
|
||||
inline int64_t query_file_size(file_handle_type handle, std::error_code& error)
|
||||
{
|
||||
error.clear();
|
||||
#ifdef _WIN32
|
||||
|
||||
@ -228,11 +228,12 @@ public:
|
||||
* 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.
|
||||
* `offset` is the number of bytes, relative to the start of the file, where the
|
||||
* mapping should begin. When specifying it, 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.
|
||||
*
|
||||
* `num_bytes` must be the number of bytes to map, regardless of the underlying
|
||||
* value_type's size. That is, if CharT is a wide char, the value returned by
|
||||
@ -252,15 +253,17 @@ public:
|
||||
* 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.
|
||||
* `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.
|
||||
* `offset` is the number of bytes, relative to the start of the file, where the
|
||||
* mapping should begin. When specifying it, 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.
|
||||
*
|
||||
* `num_bytes` must be the number of bytes to map, regardless of the underlying
|
||||
* value_type's size. That is, if CharT is a wide char, the value returned by
|
||||
|
||||
@ -244,15 +244,16 @@ public:
|
||||
* 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.
|
||||
* `offset` is the number of bytes, relative to the start of the file, where the
|
||||
* mapping should begin. When specifying it, 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.
|
||||
*
|
||||
* `length` must be the number of bytes to map, regardless of the underlying
|
||||
* `num_bytes` must be the number of bytes to map, regardless of the underlying
|
||||
* value_type's size. That is, if CharT is a wide char, the value returned by
|
||||
* the `size` and `length` methods is not the same as this `length` value (TODO
|
||||
* the `size` and `length` methods is not the same as this `num_bytes` value (TODO
|
||||
* this can be confusing).
|
||||
* If it is `map_entire_file`, a mapping of the entire file is created.
|
||||
*/
|
||||
@ -268,19 +269,21 @@ public:
|
||||
* 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.
|
||||
* `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.
|
||||
* `offset` is the number of bytes, relative to the start of the file, where the
|
||||
* mapping should begin. When specifying it, 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.
|
||||
*
|
||||
* `length` must be the number of bytes to map, regardless of the underlying
|
||||
* `num_bytes` must be the number of bytes to map, regardless of the underlying
|
||||
* value_type's size. That is, if CharT is a wide char, the value returned by
|
||||
* the `size` and `length` methods is not the same as this `length` value (TODO
|
||||
* the `size` and `length` methods is not the same as this `num_bytes` value (TODO
|
||||
* this can be confusing).
|
||||
* If it is `map_entire_file`, a mapping of the entire file is created.
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user