mirror of
https://github.com/vimpunk/mio.git
synced 2025-12-06 08:46:51 +08:00
Merge branch 'master' into multi-target-windows-api
This commit is contained in:
commit
6125094190
61
README.md
61
README.md
@ -20,16 +20,25 @@ NOTE: the file must exist before creating a mapping.
|
|||||||
|
|
||||||
There are three ways to map a file into memory:
|
There are three ways to map a file into memory:
|
||||||
|
|
||||||
- Using the constructor, which throws on failure:
|
- Using the constructor, which throws a `std::system_error` on failure:
|
||||||
```c++
|
```c++
|
||||||
mio::mmap_source mmap(path, offset, size_to_map);
|
mio::mmap_source mmap(path, offset, size_to_map);
|
||||||
```
|
```
|
||||||
|
or you can omit the `offset` and `size_to_map` arguments, in which case the
|
||||||
|
entire file is mapped:
|
||||||
|
```c++
|
||||||
|
mio::mmap_source mmap(path);
|
||||||
|
```
|
||||||
|
|
||||||
- Using the factory function:
|
- Using the factory function:
|
||||||
```c++
|
```c++
|
||||||
std::error_code error;
|
std::error_code error;
|
||||||
mio::mmap_source mmap = mio::make_mmap_source(path, offset, size_to_map, error);
|
mio::mmap_source mmap = mio::make_mmap_source(path, offset, size_to_map, error);
|
||||||
```
|
```
|
||||||
|
or:
|
||||||
|
```c++
|
||||||
|
mio::mmap_source mmap = mio::make_mmap_source(path, error);
|
||||||
|
```
|
||||||
|
|
||||||
- Using the `map` member function:
|
- Using the `map` member function:
|
||||||
```c++
|
```c++
|
||||||
@ -37,6 +46,10 @@ std::error_code error;
|
|||||||
mio::mmap_source mmap;
|
mio::mmap_source mmap;
|
||||||
mmap.map(path, offset, size_to_map, error);
|
mmap.map(path, offset, size_to_map, error);
|
||||||
```
|
```
|
||||||
|
or:
|
||||||
|
```c++
|
||||||
|
mmap.map(path, 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.
|
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++
|
```c++
|
||||||
@ -69,19 +82,8 @@ types for functions where character strings are expected (e.g. path parameters).
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
int handle_error(const std::error_code& error)
|
int handle_error(const std::error_code& error);
|
||||||
{
|
void allocate_file(const std::string& path, const int size);
|
||||||
const auto& errmsg = error.message();
|
|
||||||
std::printf("error mapping file: %s, exiting...\n", errmsg.c_str());
|
|
||||||
return error.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
void allocate_file(const std::string& path, const int size)
|
|
||||||
{
|
|
||||||
std::ofstream file(path);
|
|
||||||
std::string s(size, '0');
|
|
||||||
file << s;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
@ -113,24 +115,41 @@ int main()
|
|||||||
const int answer_index = rw_mmap.size() / 2;
|
const int answer_index = rw_mmap.size() / 2;
|
||||||
rw_mmap[answer_index] = 42;
|
rw_mmap[answer_index] = 42;
|
||||||
|
|
||||||
// Don't forget to flush changes to disk, which is NOT done by the destructor for
|
// Don't forget to flush changes to disk before unmapping. However, if
|
||||||
// more explicit control of this potentially expensive operation.
|
// `rw_mmap` were to go out of scope at this point, the destructor would also
|
||||||
|
// automatically invoke `sync` before `unmap`.
|
||||||
rw_mmap.sync(error);
|
rw_mmap.sync(error);
|
||||||
if (error) { return 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 and the above call to `sync` have the same
|
||||||
// invoked.
|
// effect as if the destructor had been invoked.
|
||||||
rw_mmap.unmap();
|
rw_mmap.unmap();
|
||||||
|
|
||||||
// Now create the same mapping, but in read-only mode.
|
// Now create the same mapping, but in read-only mode. Note that calling the
|
||||||
mio::mmap_source ro_mmap = mio::make_mmap_source(
|
// overload without the offset and file length parameters maps the entire
|
||||||
path, 0, mio::map_entire_file, error);
|
// file.
|
||||||
|
mio::mmap_source ro_mmap;
|
||||||
|
ro_mmap.map(path, 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int handle_error(const std::error_code& error)
|
||||||
|
{
|
||||||
|
const auto& errmsg = error.message();
|
||||||
|
std::printf("error mapping file: %s, exiting...\n", errmsg.c_str());
|
||||||
|
return error.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void allocate_file(const std::string& path, const int size)
|
||||||
|
{
|
||||||
|
std::ofstream file(path);
|
||||||
|
std::string s(size, '0');
|
||||||
|
file << s;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`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`.
|
`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`.
|
||||||
|
|||||||
@ -5,7 +5,6 @@
|
|||||||
if(NOT subproject)
|
if(NOT subproject)
|
||||||
target_sources(mio_base INTERFACE
|
target_sources(mio_base INTERFACE
|
||||||
$<BUILD_INTERFACE:
|
$<BUILD_INTERFACE:
|
||||||
${CMAKE_CURRENT_LIST_DIR}/basic_mmap.hpp
|
${CMAKE_CURRENT_LIST_DIR}/mmap.ipp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/basic_mmap.ipp
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/string_util.hpp>)
|
${CMAKE_CURRENT_LIST_DIR}/string_util.hpp>)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@ -1,161 +0,0 @@
|
|||||||
/* Copyright 2017 https://github.com/mandreyel
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
* software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
* without restriction, including without limitation the rights to use, copy, modify,
|
|
||||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
* permit persons to whom the Software is furnished to do so, subject to the following
|
|
||||||
* conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all copies
|
|
||||||
* or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
||||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
||||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
|
||||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef MIO_BASIC_MMAP_HEADER
|
|
||||||
#define MIO_BASIC_MMAP_HEADER
|
|
||||||
|
|
||||||
#include "mio/page.hpp"
|
|
||||||
|
|
||||||
#include <iterator>
|
|
||||||
#include <string>
|
|
||||||
#include <system_error>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
# include <windows.h>
|
|
||||||
#else // ifdef _WIN32
|
|
||||||
# define INVALID_HANDLE_VALUE -1
|
|
||||||
#endif // ifdef _WIN32
|
|
||||||
|
|
||||||
namespace mio {
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
enum { map_entire_file = 0 };
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
using file_handle_type = HANDLE;
|
|
||||||
#else
|
|
||||||
using file_handle_type = int;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template<typename ByteT> struct basic_mmap
|
|
||||||
{
|
|
||||||
using value_type = ByteT;
|
|
||||||
using size_type = int64_t;
|
|
||||||
using reference = value_type&;
|
|
||||||
using const_reference = const value_type&;
|
|
||||||
using pointer = value_type*;
|
|
||||||
using const_pointer = const value_type*;
|
|
||||||
using difference_type = std::ptrdiff_t;
|
|
||||||
using iterator = pointer;
|
|
||||||
using const_iterator = const_pointer;
|
|
||||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
|
||||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
|
||||||
using iterator_category = std::random_access_iterator_tag;
|
|
||||||
using handle_type = file_handle_type;
|
|
||||||
|
|
||||||
static_assert(sizeof(ByteT) == sizeof(char), "ByteT must be the same size as char.");
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Points to the first requested byte, and not to the actual start of the mapping.
|
|
||||||
pointer data_ = nullptr;
|
|
||||||
|
|
||||||
// Length, in bytes, requested by user, which may not be the length of the full
|
|
||||||
// mapping, and the entire length of the full mapping.
|
|
||||||
size_type length_ = 0;
|
|
||||||
size_type mapped_length_ = 0;
|
|
||||||
|
|
||||||
// Letting user map a file using both an existing file handle and a path introcudes
|
|
||||||
// On POSIX, we only need a file handle to create a mapping, while on Windows
|
|
||||||
// systems the file handle is necessary to retrieve a file mapping handle, but any
|
|
||||||
// subsequent operations on the mapped region must be done through the latter.
|
|
||||||
handle_type file_handle_ = INVALID_HANDLE_VALUE;
|
|
||||||
#ifdef _WIN32
|
|
||||||
handle_type file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// some complexity in that we must not close the file handle if user provided it,
|
|
||||||
// but we must close it if we obtained it using the provided path. For this reason,
|
|
||||||
// this flag is used to determine when to close file_handle_.
|
|
||||||
bool is_handle_internal_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
basic_mmap() = default;
|
|
||||||
basic_mmap(const basic_mmap&) = delete;
|
|
||||||
basic_mmap& operator=(const basic_mmap&) = delete;
|
|
||||||
basic_mmap(basic_mmap&&);
|
|
||||||
basic_mmap& operator=(basic_mmap&&);
|
|
||||||
~basic_mmap();
|
|
||||||
|
|
||||||
handle_type file_handle() const noexcept { return file_handle_; }
|
|
||||||
handle_type mapping_handle() const noexcept;
|
|
||||||
|
|
||||||
bool is_open() const noexcept { return file_handle_ != INVALID_HANDLE_VALUE; }
|
|
||||||
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_; }
|
|
||||||
|
|
||||||
pointer data() noexcept { return data_; }
|
|
||||||
const_pointer data() const noexcept { return data_; }
|
|
||||||
|
|
||||||
iterator begin() noexcept { return data(); }
|
|
||||||
const_iterator begin() const noexcept { return data(); }
|
|
||||||
const_iterator cbegin() const noexcept { return data(); }
|
|
||||||
|
|
||||||
iterator end() noexcept { return begin() + length(); }
|
|
||||||
const_iterator end() const noexcept { return begin() + length(); }
|
|
||||||
const_iterator cend() const noexcept { return begin() + length(); }
|
|
||||||
|
|
||||||
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
|
||||||
const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
|
|
||||||
const_reverse_iterator crbegin() const { return const_reverse_iterator(end()); }
|
|
||||||
|
|
||||||
reverse_iterator rend() { return reverse_iterator(begin()); }
|
|
||||||
const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
|
|
||||||
const_reverse_iterator crend() const { return const_reverse_iterator(begin()); }
|
|
||||||
|
|
||||||
reference operator[](const size_type i) noexcept { return data_[i]; }
|
|
||||||
const_reference operator[](const size_type i) const noexcept { return data_[i]; }
|
|
||||||
|
|
||||||
template<typename String>
|
|
||||||
void map(String& path, size_type offset, size_type length,
|
|
||||||
access_mode mode, std::error_code& error);
|
|
||||||
void map(handle_type handle, size_type offset, size_type length,
|
|
||||||
access_mode mode, std::error_code& error);
|
|
||||||
void unmap();
|
|
||||||
void sync(std::error_code& error);
|
|
||||||
|
|
||||||
void swap(basic_mmap& other);
|
|
||||||
|
|
||||||
private:
|
|
||||||
pointer get_mapping_start() noexcept { return !data() ? nullptr : data() - offset(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename ByteT>
|
|
||||||
bool operator==(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b);
|
|
||||||
template<typename ByteT>
|
|
||||||
bool operator!=(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b);
|
|
||||||
template<typename ByteT>
|
|
||||||
bool operator<(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b);
|
|
||||||
template<typename ByteT>
|
|
||||||
bool operator<=(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b);
|
|
||||||
template<typename ByteT>
|
|
||||||
bool operator>(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b);
|
|
||||||
template<typename ByteT>
|
|
||||||
bool operator>=(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b);
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace mio
|
|
||||||
|
|
||||||
#include "mio/detail/basic_mmap.ipp"
|
|
||||||
|
|
||||||
#endif // MIO_BASIC_MMAP_HEADER
|
|
||||||
@ -21,12 +21,11 @@
|
|||||||
#ifndef MIO_BASIC_MMAP_IMPL
|
#ifndef MIO_BASIC_MMAP_IMPL
|
||||||
#define MIO_BASIC_MMAP_IMPL
|
#define MIO_BASIC_MMAP_IMPL
|
||||||
|
|
||||||
#include "mio/detail/basic_mmap.hpp"
|
#include "mio/mmap.hpp"
|
||||||
#include "mio/detail/string_util.hpp"
|
|
||||||
#include "mio/page.hpp"
|
#include "mio/page.hpp"
|
||||||
|
#include "mio/detail/string_util.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
@ -39,17 +38,23 @@ namespace mio {
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
/** Returns the 4 upper bytes of a 8-byte integer. */
|
||||||
inline DWORD int64_high(int64_t n) noexcept
|
inline DWORD int64_high(int64_t n) noexcept
|
||||||
{
|
{
|
||||||
return n >> 32;
|
return n >> 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the 4 lower bytes of a 8-byte integer. */
|
||||||
inline DWORD int64_low(int64_t n) noexcept
|
inline DWORD int64_low(int64_t n) noexcept
|
||||||
{
|
{
|
||||||
return n & 0xffffffff;
|
return n & 0xffffffff;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last platform specific system error (errno on POSIX and
|
||||||
|
* GetLastError on Win) as a `std::error_code`.
|
||||||
|
*/
|
||||||
inline std::error_code last_error() noexcept
|
inline std::error_code last_error() noexcept
|
||||||
{
|
{
|
||||||
std::error_code error;
|
std::error_code error;
|
||||||
@ -63,29 +68,29 @@ inline std::error_code last_error() noexcept
|
|||||||
|
|
||||||
template<typename String>
|
template<typename String>
|
||||||
file_handle_type open_file(const String& path,
|
file_handle_type open_file(const String& path,
|
||||||
const access_mode mode, std::error_code& error)
|
const access_mode mode, std::error_code& error)
|
||||||
{
|
{
|
||||||
error.clear();
|
error.clear();
|
||||||
if(detail::empty(path))
|
if(detail::empty(path))
|
||||||
{
|
{
|
||||||
error = std::make_error_code(std::errc::invalid_argument);
|
error = std::make_error_code(std::errc::invalid_argument);
|
||||||
return INVALID_HANDLE_VALUE;
|
return invalid_handle;
|
||||||
}
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
const auto handle = ::CreateFileA(c_str(path),
|
const auto handle = ::CreateFileA(c_str(path),
|
||||||
mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
|
mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||||
0,
|
0,
|
||||||
OPEN_EXISTING,
|
OPEN_EXISTING,
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
0);
|
0);
|
||||||
#else
|
#else // POSIX
|
||||||
const auto handle = ::open(c_str(path),
|
const auto handle = ::open(c_str(path),
|
||||||
mode == access_mode::read ? O_RDONLY : O_RDWR);
|
mode == access_mode::read ? O_RDONLY : O_RDWR);
|
||||||
#endif
|
#endif
|
||||||
if(handle == INVALID_HANDLE_VALUE)
|
if(handle == invalid_handle)
|
||||||
{
|
{
|
||||||
error = last_error();
|
error = detail::last_error();
|
||||||
}
|
}
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
@ -97,15 +102,15 @@ inline int64_t query_file_size(file_handle_type handle, std::error_code& error)
|
|||||||
LARGE_INTEGER file_size;
|
LARGE_INTEGER file_size;
|
||||||
if(::GetFileSizeEx(handle, &file_size) == 0)
|
if(::GetFileSizeEx(handle, &file_size) == 0)
|
||||||
{
|
{
|
||||||
error = last_error();
|
error = detail::last_error();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return static_cast<int64_t>(file_size.QuadPart);
|
return static_cast<int64_t>(file_size.QuadPart);
|
||||||
#else
|
#else // POSIX
|
||||||
struct stat sbuf;
|
struct stat sbuf;
|
||||||
if(::fstat(handle, &sbuf) == -1)
|
if(::fstat(handle, &sbuf) == -1)
|
||||||
{
|
{
|
||||||
error = last_error();
|
error = detail::last_error();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return sbuf.st_size;
|
return sbuf.st_size;
|
||||||
@ -130,39 +135,39 @@ inline mmap_context memory_map(const file_handle_type file_handle, const int64_t
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
const int64_t max_file_size = offset + length;
|
const int64_t max_file_size = offset + length;
|
||||||
const auto file_mapping_handle = ::CreateFileMapping(
|
const auto file_mapping_handle = ::CreateFileMapping(
|
||||||
file_handle,
|
file_handle,
|
||||||
0,
|
0,
|
||||||
mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE,
|
mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE,
|
||||||
int64_high(max_file_size),
|
int64_high(max_file_size),
|
||||||
int64_low(max_file_size),
|
int64_low(max_file_size),
|
||||||
0);
|
0);
|
||||||
if(file_mapping_handle == INVALID_HANDLE_VALUE)
|
if(file_mapping_handle == invalid_handle)
|
||||||
{
|
{
|
||||||
error = last_error();
|
error = detail::last_error();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
char* mapping_start = static_cast<char*>(::MapViewOfFile(
|
char* mapping_start = static_cast<char*>(::MapViewOfFile(
|
||||||
file_mapping_handle,
|
file_mapping_handle,
|
||||||
mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE,
|
mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE,
|
||||||
int64_high(aligned_offset),
|
int64_high(aligned_offset),
|
||||||
int64_low(aligned_offset),
|
int64_low(aligned_offset),
|
||||||
length_to_map));
|
length_to_map));
|
||||||
if(mapping_start == nullptr)
|
if(mapping_start == nullptr)
|
||||||
{
|
{
|
||||||
error = last_error();
|
error = detail::last_error();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
#else
|
#else // POSIX
|
||||||
char* mapping_start = static_cast<char*>(::mmap(
|
char* mapping_start = static_cast<char*>(::mmap(
|
||||||
0, // Don't give hint as to where to map.
|
0, // Don't give hint as to where to map.
|
||||||
length_to_map,
|
length_to_map,
|
||||||
mode == access_mode::read ? PROT_READ : PROT_WRITE,
|
mode == access_mode::read ? PROT_READ : PROT_WRITE,
|
||||||
MAP_SHARED,
|
MAP_SHARED,
|
||||||
file_handle,
|
file_handle,
|
||||||
aligned_offset));
|
aligned_offset));
|
||||||
if(mapping_start == MAP_FAILED)
|
if(mapping_start == MAP_FAILED)
|
||||||
{
|
{
|
||||||
error = last_error();
|
error = detail::last_error();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -176,16 +181,19 @@ inline mmap_context memory_map(const file_handle_type file_handle, const int64_t
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
// -- basic_mmap --
|
// -- basic_mmap --
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
basic_mmap<ByteT>::~basic_mmap()
|
basic_mmap<AccessMode, ByteT>::~basic_mmap()
|
||||||
{
|
{
|
||||||
|
conditional_sync();
|
||||||
unmap();
|
unmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
basic_mmap<ByteT>::basic_mmap(basic_mmap<ByteT>&& other)
|
basic_mmap<AccessMode, ByteT>::basic_mmap(basic_mmap&& other)
|
||||||
: data_(std::move(other.data_))
|
: data_(std::move(other.data_))
|
||||||
, length_(std::move(other.length_))
|
, length_(std::move(other.length_))
|
||||||
, mapped_length_(std::move(other.mapped_length_))
|
, mapped_length_(std::move(other.mapped_length_))
|
||||||
@ -197,14 +205,15 @@ basic_mmap<ByteT>::basic_mmap(basic_mmap<ByteT>&& other)
|
|||||||
{
|
{
|
||||||
other.data_ = nullptr;
|
other.data_ = nullptr;
|
||||||
other.length_ = other.mapped_length_ = 0;
|
other.length_ = other.mapped_length_ = 0;
|
||||||
other.file_handle_ = INVALID_HANDLE_VALUE;
|
other.file_handle_ = invalid_handle;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
other.file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
other.file_mapping_handle_ = invalid_handle;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
basic_mmap<ByteT>& basic_mmap<ByteT>::operator=(basic_mmap<ByteT>&& other)
|
basic_mmap<AccessMode, ByteT>&
|
||||||
|
basic_mmap<AccessMode, ByteT>::operator=(basic_mmap&& other)
|
||||||
{
|
{
|
||||||
if(this != &other)
|
if(this != &other)
|
||||||
{
|
{
|
||||||
@ -219,21 +228,23 @@ basic_mmap<ByteT>& basic_mmap<ByteT>::operator=(basic_mmap<ByteT>&& other)
|
|||||||
#endif
|
#endif
|
||||||
is_handle_internal_ = std::move(other.is_handle_internal_);
|
is_handle_internal_ = std::move(other.is_handle_internal_);
|
||||||
|
|
||||||
// The moved from basic_mmap's fields need to be reset, because otherwise other's
|
// The moved from basic_mmap's fields need to be reset, because
|
||||||
// destructor will unmap the same mapping that was just moved into this.
|
// otherwise other's destructor will unmap the same mapping that was
|
||||||
|
// just moved into this.
|
||||||
other.data_ = nullptr;
|
other.data_ = nullptr;
|
||||||
other.length_ = other.mapped_length_ = 0;
|
other.length_ = other.mapped_length_ = 0;
|
||||||
other.file_handle_ = INVALID_HANDLE_VALUE;
|
other.file_handle_ = invalid_handle;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
other.file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
other.file_mapping_handle_ = invalid_handle;
|
||||||
#endif
|
#endif
|
||||||
other.is_handle_internal_ = false;
|
other.is_handle_internal_ = false;
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
typename basic_mmap<ByteT>::handle_type basic_mmap<ByteT>::mapping_handle() const noexcept
|
typename basic_mmap<AccessMode, ByteT>::handle_type
|
||||||
|
basic_mmap<AccessMode, ByteT>::mapping_handle() const noexcept
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return file_mapping_handle_;
|
return file_mapping_handle_;
|
||||||
@ -242,10 +253,10 @@ typename basic_mmap<ByteT>::handle_type basic_mmap<ByteT>::mapping_handle() cons
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
template<typename String>
|
template<typename String>
|
||||||
void basic_mmap<ByteT>::map(String& path, size_type offset,
|
void basic_mmap<AccessMode, ByteT>::map(const String& path, const size_type offset,
|
||||||
size_type length, access_mode mode, std::error_code& error)
|
const size_type length, std::error_code& error)
|
||||||
{
|
{
|
||||||
error.clear();
|
error.clear();
|
||||||
if(detail::empty(path))
|
if(detail::empty(path))
|
||||||
@ -253,9 +264,13 @@ void basic_mmap<ByteT>::map(String& path, size_type offset,
|
|||||||
error = std::make_error_code(std::errc::invalid_argument);
|
error = std::make_error_code(std::errc::invalid_argument);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto handle = open_file(path, mode, error);
|
const auto handle = detail::open_file(path, AccessMode, error);
|
||||||
if(error) { return; }
|
if(error)
|
||||||
map(handle, offset, length, mode, error);
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
map(handle, offset, length, error);
|
||||||
// This MUST be after the call to map, as that sets this to true.
|
// This MUST be after the call to map, as that sets this to true.
|
||||||
if(!error)
|
if(!error)
|
||||||
{
|
{
|
||||||
@ -263,31 +278,32 @@ void basic_mmap<ByteT>::map(String& path, size_type offset,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
void basic_mmap<ByteT>::map(handle_type handle, size_type offset,
|
void basic_mmap<AccessMode, ByteT>::map(const handle_type handle,
|
||||||
size_type length, access_mode mode, std::error_code& error)
|
const size_type offset, const size_type length, std::error_code& error)
|
||||||
{
|
{
|
||||||
error.clear();
|
error.clear();
|
||||||
if(handle == INVALID_HANDLE_VALUE)
|
if(handle == invalid_handle)
|
||||||
{
|
{
|
||||||
error = std::make_error_code(std::errc::bad_file_descriptor);
|
error = std::make_error_code(std::errc::bad_file_descriptor);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto file_size = query_file_size(handle, error);
|
const auto file_size = detail::query_file_size(handle, error);
|
||||||
if(error) { return; }
|
if(error)
|
||||||
|
|
||||||
if(length <= map_entire_file)
|
|
||||||
{
|
{
|
||||||
length = file_size;
|
return;
|
||||||
}
|
}
|
||||||
else if(offset + length > file_size)
|
|
||||||
|
if(offset + length > file_size)
|
||||||
{
|
{
|
||||||
error = std::make_error_code(std::errc::invalid_argument);
|
error = std::make_error_code(std::errc::invalid_argument);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mmap_context ctx = memory_map(handle, offset, length, mode, error);
|
const auto ctx = detail::memory_map(handle, offset,
|
||||||
|
length == map_entire_file ? file_size : length,
|
||||||
|
AccessMode, error);
|
||||||
if(!error)
|
if(!error)
|
||||||
{
|
{
|
||||||
// We must unmap the previous mapping that may have existed prior to this call.
|
// We must unmap the previous mapping that may have existed prior to this call.
|
||||||
@ -307,8 +323,10 @@ void basic_mmap<ByteT>::map(handle_type handle, size_type offset,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
void basic_mmap<ByteT>::sync(std::error_code& error)
|
template<access_mode A>
|
||||||
|
typename std::enable_if<A == access_mode::write, void>::type
|
||||||
|
basic_mmap<AccessMode, ByteT>::sync(std::error_code& error)
|
||||||
{
|
{
|
||||||
error.clear();
|
error.clear();
|
||||||
if(!is_open())
|
if(!is_open())
|
||||||
@ -317,29 +335,29 @@ void basic_mmap<ByteT>::sync(std::error_code& error)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(data() != nullptr)
|
if(data())
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0
|
if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0
|
||||||
|| ::FlushFileBuffers(file_handle_) == 0)
|
|| ::FlushFileBuffers(file_handle_) == 0)
|
||||||
#else
|
#else // POSIX
|
||||||
if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0)
|
if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
error = last_error();
|
error = detail::last_error();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if(::FlushFileBuffers(file_handle_) == 0)
|
if(::FlushFileBuffers(file_handle_) == 0)
|
||||||
{
|
{
|
||||||
error = last_error();
|
error = detail::last_error();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
void basic_mmap<ByteT>::unmap()
|
void basic_mmap<AccessMode, ByteT>::unmap()
|
||||||
{
|
{
|
||||||
if(!is_open()) { return; }
|
if(!is_open()) { return; }
|
||||||
// TODO do we care about errors here?
|
// TODO do we care about errors here?
|
||||||
@ -349,7 +367,7 @@ void basic_mmap<ByteT>::unmap()
|
|||||||
::UnmapViewOfFile(get_mapping_start());
|
::UnmapViewOfFile(get_mapping_start());
|
||||||
::CloseHandle(file_mapping_handle_);
|
::CloseHandle(file_mapping_handle_);
|
||||||
}
|
}
|
||||||
#else
|
#else // POSIX
|
||||||
if(data_) { ::munmap(const_cast<pointer>(get_mapping_start()), mapped_length_); }
|
if(data_) { ::munmap(const_cast<pointer>(get_mapping_start()), mapped_length_); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -360,7 +378,7 @@ void basic_mmap<ByteT>::unmap()
|
|||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
::CloseHandle(file_handle_);
|
::CloseHandle(file_handle_);
|
||||||
#else
|
#else // POSIX
|
||||||
::close(file_handle_);
|
::close(file_handle_);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -368,24 +386,24 @@ void basic_mmap<ByteT>::unmap()
|
|||||||
// Reset fields to their default values.
|
// Reset fields to their default values.
|
||||||
data_ = nullptr;
|
data_ = nullptr;
|
||||||
length_ = mapped_length_ = 0;
|
length_ = mapped_length_ = 0;
|
||||||
file_handle_ = INVALID_HANDLE_VALUE;
|
file_handle_ = invalid_handle;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
file_mapping_handle_ = invalid_handle;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
bool basic_mmap<ByteT>::is_mapped() const noexcept
|
bool basic_mmap<AccessMode, ByteT>::is_mapped() const noexcept
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return file_mapping_handle_ != INVALID_HANDLE_VALUE;
|
return file_mapping_handle_ != invalid_handle;
|
||||||
#else
|
#else // POSIX
|
||||||
return is_open();
|
return is_open();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
void basic_mmap<ByteT>::swap(basic_mmap<ByteT>& other)
|
void basic_mmap<AccessMode, ByteT>::swap(basic_mmap& other)
|
||||||
{
|
{
|
||||||
if(this != &other)
|
if(this != &other)
|
||||||
{
|
{
|
||||||
@ -401,46 +419,70 @@ void basic_mmap<ByteT>::swap(basic_mmap<ByteT>& other)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
bool operator==(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b)
|
template<access_mode A>
|
||||||
|
typename std::enable_if<A == access_mode::write, void>::type
|
||||||
|
basic_mmap<AccessMode, ByteT>::conditional_sync()
|
||||||
|
{
|
||||||
|
// This is invoked from the destructor, so not much we can do about
|
||||||
|
// failures here.
|
||||||
|
std::error_code ec;
|
||||||
|
sync(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<access_mode AccessMode, typename ByteT>
|
||||||
|
template<access_mode A>
|
||||||
|
typename std::enable_if<A == access_mode::read, void>::type
|
||||||
|
basic_mmap<AccessMode, ByteT>::conditional_sync()
|
||||||
|
{
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
template<access_mode AccessMode, typename ByteT>
|
||||||
|
bool operator==(const basic_mmap<AccessMode, ByteT>& a,
|
||||||
|
const basic_mmap<AccessMode, ByteT>& b)
|
||||||
{
|
{
|
||||||
return a.data() == b.data()
|
return a.data() == b.data()
|
||||||
&& a.size() == b.size();
|
&& a.size() == b.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
bool operator!=(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b)
|
bool operator!=(const basic_mmap<AccessMode, ByteT>& a,
|
||||||
|
const basic_mmap<AccessMode, ByteT>& b)
|
||||||
{
|
{
|
||||||
return !(a == b);
|
return !(a == b);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
bool operator<(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b)
|
bool operator<(const basic_mmap<AccessMode, ByteT>& a,
|
||||||
|
const basic_mmap<AccessMode, ByteT>& b)
|
||||||
{
|
{
|
||||||
if(a.data() == b.data()) { return a.size() < b.size(); }
|
if(a.data() == b.data()) { return a.size() < b.size(); }
|
||||||
return a.data() < b.data();
|
return a.data() < b.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
bool operator<=(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b)
|
bool operator<=(const basic_mmap<AccessMode, ByteT>& a,
|
||||||
|
const basic_mmap<AccessMode, ByteT>& b)
|
||||||
{
|
{
|
||||||
return !(a > b);
|
return !(a > b);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
bool operator>(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b)
|
bool operator>(const basic_mmap<AccessMode, ByteT>& a,
|
||||||
|
const basic_mmap<AccessMode, ByteT>& b)
|
||||||
{
|
{
|
||||||
if(a.data() == b.data()) { return a.size() > b.size(); }
|
if(a.data() == b.data()) { return a.size() > b.size(); }
|
||||||
return a.data() > b.data();
|
return a.data() > b.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ByteT>
|
template<access_mode AccessMode, typename ByteT>
|
||||||
bool operator>=(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b)
|
bool operator>=(const basic_mmap<AccessMode, ByteT>& a,
|
||||||
|
const basic_mmap<AccessMode, ByteT>& b)
|
||||||
{
|
{
|
||||||
return !(a < b);
|
return !(a < b);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace mio
|
} // namespace mio
|
||||||
|
|
||||||
#endif // MIO_BASIC_MMAP_IMPL
|
#endif // MIO_BASIC_MMAP_IMPL
|
||||||
@ -21,97 +21,148 @@
|
|||||||
#ifndef MIO_MMAP_HEADER
|
#ifndef MIO_MMAP_HEADER
|
||||||
#define MIO_MMAP_HEADER
|
#define MIO_MMAP_HEADER
|
||||||
|
|
||||||
#include "detail/basic_mmap.hpp"
|
#include "mio/page.hpp"
|
||||||
#include "page.hpp"
|
|
||||||
|
|
||||||
|
#include <iterator>
|
||||||
|
#include <string>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
# endif // WIN32_LEAN_AND_MEAN
|
||||||
|
# include <windows.h>
|
||||||
|
#else // ifdef _WIN32
|
||||||
|
# define INVALID_HANDLE_VALUE -1
|
||||||
|
#endif // ifdef _WIN32
|
||||||
|
|
||||||
namespace mio {
|
namespace mio {
|
||||||
|
|
||||||
// This value may be provided as the `length` parameter to the constructor or
|
// This value may be provided as the `length` parameter to the constructor or
|
||||||
// `map`, in which case a memory mapping of the entire file is created.
|
// `map`, in which case a memory mapping of the entire file is created.
|
||||||
using detail::map_entire_file;
|
enum { map_entire_file = 0 };
|
||||||
|
|
||||||
template<
|
#ifdef _WIN32
|
||||||
access_mode AccessMode,
|
using file_handle_type = HANDLE;
|
||||||
typename ByteT
|
#else
|
||||||
> class basic_mmap
|
using file_handle_type = int;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This value represents an invalid file handle type. This can be used to
|
||||||
|
// determine whether `basic_mmap::file_handle` is valid, for example.
|
||||||
|
const static file_handle_type invalid_handle = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
template<access_mode AccessMode, typename ByteT>
|
||||||
|
struct basic_mmap
|
||||||
{
|
{
|
||||||
using impl_type = detail::basic_mmap<ByteT>;
|
using value_type = ByteT;
|
||||||
impl_type impl_;
|
using size_type = int64_t;
|
||||||
|
using reference = value_type&;
|
||||||
|
using const_reference = const value_type&;
|
||||||
|
using pointer = value_type*;
|
||||||
|
using const_pointer = const value_type*;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using iterator = pointer;
|
||||||
|
using const_iterator = const_pointer;
|
||||||
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||||
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||||
|
using iterator_category = std::random_access_iterator_tag;
|
||||||
|
using handle_type = file_handle_type;
|
||||||
|
|
||||||
|
static_assert(sizeof(ByteT) == sizeof(char), "ByteT must be the same size as char.");
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Points to the first requested byte, and not to the actual start of the mapping.
|
||||||
|
pointer data_ = nullptr;
|
||||||
|
|
||||||
|
// Length, in bytes, requested by user, which may not be the length of the full
|
||||||
|
// mapping, and the entire length of the full mapping.
|
||||||
|
size_type length_ = 0;
|
||||||
|
size_type mapped_length_ = 0;
|
||||||
|
|
||||||
|
// Letting user map a file using both an existing file handle and a path introcudes
|
||||||
|
// On POSIX, we only need a file handle to create a mapping, while on Windows
|
||||||
|
// systems the file handle is necessary to retrieve a file mapping handle, but any
|
||||||
|
// subsequent operations on the mapped region must be done through the latter.
|
||||||
|
handle_type file_handle_ = INVALID_HANDLE_VALUE;
|
||||||
|
#ifdef _WIN32
|
||||||
|
handle_type file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// some complexity in that we must not close the file handle if user provided it,
|
||||||
|
// but we must close it if we obtained it using the provided path. For this reason,
|
||||||
|
// this flag is used to determine when to close file_handle_.
|
||||||
|
bool is_handle_internal_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using value_type = typename impl_type::value_type;
|
|
||||||
using size_type = typename impl_type::size_type;
|
|
||||||
using reference = typename impl_type::reference;
|
|
||||||
using const_reference = typename impl_type::const_reference;
|
|
||||||
using pointer = typename impl_type::pointer;
|
|
||||||
using const_pointer = typename impl_type::const_pointer;
|
|
||||||
using difference_type = typename impl_type::difference_type;
|
|
||||||
using iterator = typename impl_type::iterator;
|
|
||||||
using const_iterator = typename impl_type::const_iterator;
|
|
||||||
using reverse_iterator = typename impl_type::reverse_iterator;
|
|
||||||
using const_reverse_iterator = typename impl_type::const_reverse_iterator;
|
|
||||||
using iterator_category = typename impl_type::iterator_category;
|
|
||||||
using handle_type = typename impl_type::handle_type;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default constructed mmap object is in a non-mapped state, that is, any
|
* The default constructed mmap object is in a non-mapped state, that is,
|
||||||
* operation that attempts to access nonexistent underlying data will result in
|
* any operation that attempts to access nonexistent underlying data will
|
||||||
* undefined behaviour/segmentation faults.
|
* result in undefined behaviour/segmentation faults.
|
||||||
*/
|
*/
|
||||||
basic_mmap() = default;
|
basic_mmap() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The same as invoking the `map` function, except any error that may occur while
|
* The same as invoking the `map` function, except any error that may occur
|
||||||
* establishing the mapping is thrown.
|
* while establishing the mapping is wrapped in a `std::system_error` and is
|
||||||
|
* thrown.
|
||||||
*/
|
*/
|
||||||
template<typename String>
|
template<typename String>
|
||||||
basic_mmap(const String& path, const size_type offset, const size_type length)
|
basic_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file)
|
||||||
{
|
{
|
||||||
std::error_code error;
|
std::error_code error;
|
||||||
map(path, offset, length, error);
|
map(path, offset, length, error);
|
||||||
if(error) { throw error; }
|
if(error) { throw std::system_error(error); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The same as invoking the `map` function, except any error that may occur while
|
* The same as invoking the `map` function, except any error that may occur
|
||||||
* establishing the mapping is thrown.
|
* while establishing the mapping is wrapped in a `std::system_error` and is
|
||||||
|
* thrown.
|
||||||
*/
|
*/
|
||||||
basic_mmap(const handle_type handle, const size_type offset, const size_type length)
|
basic_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file)
|
||||||
{
|
{
|
||||||
std::error_code error;
|
std::error_code error;
|
||||||
map(handle, offset, length, error);
|
map(handle, offset, length, error);
|
||||||
if(error) { throw error; }
|
if(error) { throw std::system_error(error); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class has single-ownership semantics, so transferring ownership may only be
|
* `basic_mmap` has single-ownership semantics, so transferring ownership
|
||||||
* accomplished by moving the object.
|
* may only be accomplished by moving the object.
|
||||||
*/
|
*/
|
||||||
basic_mmap(basic_mmap&&) = default;
|
basic_mmap(const basic_mmap&) = delete;
|
||||||
basic_mmap& operator=(basic_mmap&&) = default;
|
basic_mmap(basic_mmap&&);
|
||||||
|
basic_mmap& operator=(const basic_mmap&) = delete;
|
||||||
|
basic_mmap& operator=(basic_mmap&&);
|
||||||
|
|
||||||
/** The destructor invokes unmap. */
|
/**
|
||||||
~basic_mmap() = default;
|
* If this is a read-write mapping, the destructor invokes sync. Regardless
|
||||||
|
* of the access mode, unmap is invoked as a final step.
|
||||||
|
*/
|
||||||
|
~basic_mmap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows,
|
* On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows,
|
||||||
* however, a mapped region of a file gets its own handle, which is returned by
|
* however, a mapped region of a file gets its own handle, which is returned by
|
||||||
* 'mapping_handle'.
|
* 'mapping_handle'.
|
||||||
*/
|
*/
|
||||||
handle_type file_handle() const noexcept { return impl_.file_handle(); }
|
handle_type file_handle() const noexcept { return file_handle_; }
|
||||||
handle_type mapping_handle() const noexcept { return impl_.mapping_handle(); }
|
handle_type mapping_handle() const noexcept;
|
||||||
|
|
||||||
/** Returns whether a valid memory mapping has been created. */
|
/** Returns whether a valid memory mapping has been created. */
|
||||||
bool is_open() const noexcept { return impl_.is_open(); }
|
bool is_open() const noexcept { return file_handle_ != invalid_handle; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if no mapping was established, that is, conceptually the
|
* Returns true if no mapping was established, that is, conceptually the
|
||||||
* same as though the length that was mapped was 0. This function is
|
* same as though the length that was mapped was 0. This function is
|
||||||
* provided so that this class has Container semantics.
|
* provided so that this class has Container semantics.
|
||||||
*/
|
*/
|
||||||
bool empty() const noexcept { return impl_.empty(); }
|
bool empty() const noexcept { return length() == 0; }
|
||||||
|
|
||||||
|
/** Returns true if a mapping was established. */
|
||||||
|
bool is_mapped() const noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `size` and `length` both return the logical length, i.e. the number of bytes
|
* `size` and `length` both return the logical length, i.e. the number of bytes
|
||||||
@ -119,15 +170,15 @@ public:
|
|||||||
* bytes that were mapped which is a multiple of the underlying operating system's
|
* bytes that were mapped which is a multiple of the underlying operating system's
|
||||||
* page allocation granularity.
|
* page allocation granularity.
|
||||||
*/
|
*/
|
||||||
size_type size() const noexcept { return impl_.length(); }
|
size_type size() const noexcept { return length(); }
|
||||||
size_type length() const noexcept { return impl_.length(); }
|
size_type length() const noexcept { return length_; }
|
||||||
size_type mapped_length() const noexcept { return impl_.mapped_length(); }
|
size_type mapped_length() const noexcept { return mapped_length_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the offset, relative to the file's start, at which the mapping was
|
* Returns the offset, relative to the file's start, at which the mapping was
|
||||||
* requested to be created.
|
* requested to be created.
|
||||||
*/
|
*/
|
||||||
size_type offset() const noexcept { return impl_.offset(); }
|
size_type offset() const noexcept { return mapped_length_ - length_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a pointer to the first requested byte, or `nullptr` if no memory mapping
|
* Returns a pointer to the first requested byte, or `nullptr` if no memory mapping
|
||||||
@ -136,8 +187,8 @@ public:
|
|||||||
template<
|
template<
|
||||||
access_mode A = AccessMode,
|
access_mode A = AccessMode,
|
||||||
typename = typename std::enable_if<A == access_mode::write>::type
|
typename = typename std::enable_if<A == access_mode::write>::type
|
||||||
> pointer data() noexcept { return impl_.data(); }
|
> pointer data() noexcept { return data_; }
|
||||||
const_pointer data() const noexcept { return impl_.data(); }
|
const_pointer data() const noexcept { return data_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
@ -146,9 +197,9 @@ public:
|
|||||||
template<
|
template<
|
||||||
access_mode A = AccessMode,
|
access_mode A = AccessMode,
|
||||||
typename = typename std::enable_if<A == access_mode::write>::type
|
typename = typename std::enable_if<A == access_mode::write>::type
|
||||||
> iterator begin() noexcept { return impl_.begin(); }
|
> iterator begin() noexcept { return data(); }
|
||||||
const_iterator begin() const noexcept { return impl_.begin(); }
|
const_iterator begin() const noexcept { return data(); }
|
||||||
const_iterator cbegin() const noexcept { return impl_.cbegin(); }
|
const_iterator cbegin() const noexcept { return data(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an iterator one past the last requested byte, if a valid memory mapping
|
* Returns an iterator one past the last requested byte, if a valid memory mapping
|
||||||
@ -157,9 +208,9 @@ public:
|
|||||||
template<
|
template<
|
||||||
access_mode A = AccessMode,
|
access_mode A = AccessMode,
|
||||||
typename = typename std::enable_if<A == access_mode::write>::type
|
typename = typename std::enable_if<A == access_mode::write>::type
|
||||||
> iterator end() noexcept { return impl_.end(); }
|
> iterator end() noexcept { return data() + length(); }
|
||||||
const_iterator end() const noexcept { return impl_.end(); }
|
const_iterator end() const noexcept { return data() + length(); }
|
||||||
const_iterator cend() const noexcept { return impl_.cend(); }
|
const_iterator cend() const noexcept { return data() + length(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a reverse iterator to the last memory mapped byte, if a valid
|
* Returns a reverse iterator to the last memory mapped byte, if a valid
|
||||||
@ -169,9 +220,11 @@ public:
|
|||||||
template<
|
template<
|
||||||
access_mode A = AccessMode,
|
access_mode A = AccessMode,
|
||||||
typename = typename std::enable_if<A == access_mode::write>::type
|
typename = typename std::enable_if<A == access_mode::write>::type
|
||||||
> reverse_iterator rbegin() noexcept { return impl_.rbegin(); }
|
> reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
|
||||||
const_reverse_iterator rbegin() const noexcept { return impl_.rbegin(); }
|
const_reverse_iterator rbegin() const noexcept
|
||||||
const_reverse_iterator crbegin() const noexcept { return impl_.crbegin(); }
|
{ return const_reverse_iterator(end()); }
|
||||||
|
const_reverse_iterator crbegin() const noexcept
|
||||||
|
{ return const_reverse_iterator(end()); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a reverse iterator past the first mapped byte, if a valid memory
|
* Returns a reverse iterator past the first mapped byte, if a valid memory
|
||||||
@ -180,17 +233,19 @@ public:
|
|||||||
template<
|
template<
|
||||||
access_mode A = AccessMode,
|
access_mode A = AccessMode,
|
||||||
typename = typename std::enable_if<A == access_mode::write>::type
|
typename = typename std::enable_if<A == access_mode::write>::type
|
||||||
> reverse_iterator rend() noexcept { return impl_.rend(); }
|
> reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
|
||||||
const_reverse_iterator rend() const noexcept { return impl_.rend(); }
|
const_reverse_iterator rend() const noexcept
|
||||||
const_reverse_iterator crend() const noexcept { return impl_.crend(); }
|
{ return const_reverse_iterator(begin()); }
|
||||||
|
const_reverse_iterator crend() const noexcept
|
||||||
|
{ return const_reverse_iterator(begin()); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a reference to the `i`th byte from the first requested byte (as returned
|
* Returns a reference to the `i`th byte from the first requested byte (as returned
|
||||||
* by `data`). If this is invoked when no valid memory mapping has been created
|
* by `data`). If this is invoked when no valid memory mapping has been created
|
||||||
* prior to this call, undefined behaviour ensues.
|
* prior to this call, undefined behaviour ensues.
|
||||||
*/
|
*/
|
||||||
reference operator[](const size_type i) noexcept { return impl_[i]; }
|
reference operator[](const size_type i) noexcept { return data_[i]; }
|
||||||
const_reference operator[](const size_type i) const noexcept { return impl_[i]; }
|
const_reference operator[](const size_type i) const noexcept { return data_[i]; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
|
* Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
|
||||||
@ -214,16 +269,31 @@ public:
|
|||||||
*/
|
*/
|
||||||
template<typename String>
|
template<typename String>
|
||||||
void map(const String& path, const size_type offset,
|
void map(const String& path, const size_type offset,
|
||||||
const size_type length, std::error_code& error)
|
const size_type length, std::error_code& error);
|
||||||
{
|
|
||||||
impl_.map(path, offset, length, AccessMode, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
|
* Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
|
||||||
* reason is reported via `error` and the object remains in a state as if this
|
* reason is reported via `error` and the object remains in a state as if this
|
||||||
* function hadn't been called.
|
* function hadn't been called.
|
||||||
*
|
*
|
||||||
|
* `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.
|
||||||
|
*
|
||||||
|
* The entire file is mapped.
|
||||||
|
*/
|
||||||
|
template<typename String>
|
||||||
|
void map(const String& path, std::error_code& error)
|
||||||
|
{
|
||||||
|
map(path, 0, map_entire_file, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establishes a memory mapping with AccessMode. If the mapping is
|
||||||
|
* unsuccesful, the reason is reported via `error` and the object remains in
|
||||||
|
* a state as if this function hadn't been called.
|
||||||
|
*
|
||||||
* `handle`, which must be a valid file handle, which is used to memory map the
|
* `handle`, which must be a valid file handle, which is used to memory map the
|
||||||
* requested region. Upon failure, `error` is set to indicate the reason and the
|
* requested region. Upon failure, `error` is set to indicate the reason and the
|
||||||
* object remains in an unmapped state.
|
* object remains in an unmapped state.
|
||||||
@ -239,9 +309,22 @@ public:
|
|||||||
* case a mapping of the entire file is created.
|
* case a mapping of the entire file is created.
|
||||||
*/
|
*/
|
||||||
void map(const handle_type handle, const size_type offset,
|
void map(const handle_type handle, const size_type offset,
|
||||||
const size_type length, std::error_code& error)
|
const size_type length, std::error_code& error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establishes a memory mapping with AccessMode. If the mapping is
|
||||||
|
* unsuccesful, the reason is reported via `error` and the object remains in
|
||||||
|
* a state as if this function hadn't been called.
|
||||||
|
*
|
||||||
|
* `handle`, which must be a valid file handle, which is used to memory map the
|
||||||
|
* requested region. Upon failure, `error` is set to indicate the reason and the
|
||||||
|
* object remains in an unmapped state.
|
||||||
|
*
|
||||||
|
* The entire file is mapped.
|
||||||
|
*/
|
||||||
|
void map(const handle_type handle, std::error_code& error)
|
||||||
{
|
{
|
||||||
impl_.map(handle, offset, length, AccessMode, error);
|
map(handle, 0, map_entire_file, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -253,52 +336,70 @@ public:
|
|||||||
* mapping was created using a file path. If, on the other hand, an existing
|
* mapping was created using a file path. If, on the other hand, an existing
|
||||||
* file handle was used to create the mapping, the file handle is not closed.
|
* file handle was used to create the mapping, the file handle is not closed.
|
||||||
*/
|
*/
|
||||||
void unmap() { impl_.unmap(); }
|
void unmap();
|
||||||
|
|
||||||
void swap(basic_mmap& other) { impl_.swap(other.impl_); }
|
void swap(basic_mmap& other);
|
||||||
|
|
||||||
/** Flushes the memory mapped page to disk. Errors are reported via `error`. */
|
/** Flushes the memory mapped page to disk. Errors are reported via `error`. */
|
||||||
template<
|
template<access_mode A = AccessMode>
|
||||||
access_mode A = AccessMode,
|
typename std::enable_if<A == access_mode::write, void>::type
|
||||||
typename = typename std::enable_if<A == access_mode::write>::type
|
sync(std::error_code& error);
|
||||||
> void sync(std::error_code& error) { impl_.sync(error); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All operators compare the address of the first byte and size of the two mapped
|
* All operators compare the address of the first byte and size of the two mapped
|
||||||
* regions.
|
* regions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
friend bool operator==(const basic_mmap& a, const basic_mmap& b)
|
private:
|
||||||
|
template<
|
||||||
|
access_mode A = AccessMode,
|
||||||
|
typename = typename std::enable_if<A == access_mode::write>::type
|
||||||
|
> pointer get_mapping_start() noexcept
|
||||||
{
|
{
|
||||||
return a.impl_ == b.impl_;
|
return !data() ? nullptr : data() - offset();
|
||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator!=(const basic_mmap& a, const basic_mmap& b)
|
const_pointer get_mapping_start() const noexcept
|
||||||
{
|
{
|
||||||
return !(a == b);
|
return !data() ? nullptr : data() - offset();
|
||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator<(const basic_mmap& a, const basic_mmap& b)
|
/**
|
||||||
{
|
* The destructor syncs changes to disk if `AccessMode` is `write`, but not
|
||||||
return a.impl_ < b.impl_;
|
* if it's `read`, but since the destructor cannot be templated, we need to
|
||||||
}
|
* do SFINAE in a dedicated function, where one syncs and the other is a noop.
|
||||||
|
*/
|
||||||
friend bool operator<=(const basic_mmap& a, const basic_mmap& b)
|
template<access_mode A = AccessMode>
|
||||||
{
|
typename std::enable_if<A == access_mode::write, void>::type
|
||||||
return a.impl_ <= b.impl_;
|
conditional_sync();
|
||||||
}
|
template<access_mode A = AccessMode>
|
||||||
|
typename std::enable_if<A == access_mode::read, void>::type conditional_sync();
|
||||||
friend bool operator>(const basic_mmap& a, const basic_mmap& b)
|
|
||||||
{
|
|
||||||
return a.impl_ > b.impl_;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend bool operator>=(const basic_mmap& a, const basic_mmap& b)
|
|
||||||
{
|
|
||||||
return a.impl_ >= b.impl_;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<access_mode AccessMode, typename ByteT>
|
||||||
|
bool operator==(const basic_mmap<AccessMode, ByteT>& a,
|
||||||
|
const basic_mmap<AccessMode, ByteT>& b);
|
||||||
|
|
||||||
|
template<access_mode AccessMode, typename ByteT>
|
||||||
|
bool operator!=(const basic_mmap<AccessMode, ByteT>& a,
|
||||||
|
const basic_mmap<AccessMode, ByteT>& b);
|
||||||
|
|
||||||
|
template<access_mode AccessMode, typename ByteT>
|
||||||
|
bool operator<(const basic_mmap<AccessMode, ByteT>& a,
|
||||||
|
const basic_mmap<AccessMode, ByteT>& b);
|
||||||
|
|
||||||
|
template<access_mode AccessMode, typename ByteT>
|
||||||
|
bool operator<=(const basic_mmap<AccessMode, ByteT>& a,
|
||||||
|
const basic_mmap<AccessMode, ByteT>& b);
|
||||||
|
|
||||||
|
template<access_mode AccessMode, typename ByteT>
|
||||||
|
bool operator>(const basic_mmap<AccessMode, ByteT>& a,
|
||||||
|
const basic_mmap<AccessMode, ByteT>& b);
|
||||||
|
|
||||||
|
template<access_mode AccessMode, typename ByteT>
|
||||||
|
bool operator>=(const basic_mmap<AccessMode, ByteT>& a,
|
||||||
|
const basic_mmap<AccessMode, ByteT>& b);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the basis for all read-only mmap objects and should be preferred over
|
* This is the basis for all read-only mmap objects and should be preferred over
|
||||||
* directly using `basic_mmap`.
|
* directly using `basic_mmap`.
|
||||||
@ -323,12 +424,15 @@ using ummap_source = basic_mmap_source<unsigned char>;
|
|||||||
using mmap_sink = basic_mmap_sink<char>;
|
using mmap_sink = basic_mmap_sink<char>;
|
||||||
using ummap_sink = basic_mmap_sink<unsigned char>;
|
using ummap_sink = basic_mmap_sink<unsigned char>;
|
||||||
|
|
||||||
/** Convenience factory method that constructs a mapping for any `basic_mmap` type. */
|
/**
|
||||||
|
* Convenience factory method that constructs a mapping for any `basic_mmap` or
|
||||||
|
* `basic_mmap` type.
|
||||||
|
*/
|
||||||
template<
|
template<
|
||||||
typename MMap,
|
typename MMap,
|
||||||
typename MappingToken
|
typename MappingToken
|
||||||
> MMap make_mmap(const MappingToken& token,
|
> MMap make_mmap(const MappingToken& token,
|
||||||
int64_t offset, int64_t length, std::error_code& error)
|
int64_t offset, int64_t length, std::error_code& error)
|
||||||
{
|
{
|
||||||
MMap mmap;
|
MMap mmap;
|
||||||
mmap.map(token, offset, length, error);
|
mmap.map(token, offset, length, error);
|
||||||
@ -344,11 +448,17 @@ template<
|
|||||||
*/
|
*/
|
||||||
template<typename MappingToken>
|
template<typename MappingToken>
|
||||||
mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset,
|
mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset,
|
||||||
mmap_source::size_type length, std::error_code& error)
|
mmap_source::size_type length, std::error_code& error)
|
||||||
{
|
{
|
||||||
return make_mmap<mmap_source>(token, offset, length, error);
|
return make_mmap<mmap_source>(token, offset, length, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename MappingToken>
|
||||||
|
mmap_source make_mmap_source(const MappingToken& token, std::error_code& error)
|
||||||
|
{
|
||||||
|
return make_mmap_source(token, 0, map_entire_file, error);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience factory method.
|
* Convenience factory method.
|
||||||
*
|
*
|
||||||
@ -358,11 +468,19 @@ mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type o
|
|||||||
*/
|
*/
|
||||||
template<typename MappingToken>
|
template<typename MappingToken>
|
||||||
mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset,
|
mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset,
|
||||||
mmap_sink::size_type length, std::error_code& error)
|
mmap_sink::size_type length, std::error_code& error)
|
||||||
{
|
{
|
||||||
return make_mmap<mmap_sink>(token, offset, length, error);
|
return make_mmap<mmap_sink>(token, offset, length, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename MappingToken>
|
||||||
|
mmap_sink make_mmap_sink(const MappingToken& token, std::error_code& error)
|
||||||
|
{
|
||||||
|
return make_mmap_sink(token, 0, map_entire_file, error);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mio
|
} // namespace mio
|
||||||
|
|
||||||
|
#include "detail/mmap.ipp"
|
||||||
|
|
||||||
#endif // MIO_MMAP_HEADER
|
#endif // MIO_MMAP_HEADER
|
||||||
|
|||||||
@ -88,28 +88,35 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The same as invoking the `map` function, except any error that may occur while
|
* The same as invoking the `map` function, except any error that may occur
|
||||||
* establishing the mapping is thrown.
|
* while establishing the mapping is wrapped in a `std::system_error` and is
|
||||||
|
* thrown.
|
||||||
*/
|
*/
|
||||||
template<typename String>
|
template<typename String>
|
||||||
basic_shared_mmap(const String& path, const size_type offset, const size_type length)
|
basic_shared_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file)
|
||||||
{
|
{
|
||||||
std::error_code error;
|
std::error_code error;
|
||||||
map(path, offset, length, error);
|
map(path, offset, length, error);
|
||||||
if(error) { throw error; }
|
if(error) { throw std::system_error(error); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The same as invoking the `map` function, except any error that may occur while
|
* The same as invoking the `map` function, except any error that may occur
|
||||||
* establishing the mapping is thrown.
|
* while establishing the mapping is wrapped in a `std::system_error` and is
|
||||||
|
* thrown.
|
||||||
*/
|
*/
|
||||||
basic_shared_mmap(const handle_type handle, const size_type offset, const size_type length)
|
basic_shared_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file)
|
||||||
{
|
{
|
||||||
std::error_code error;
|
std::error_code error;
|
||||||
map(handle, offset, length, error);
|
map(handle, offset, length, error);
|
||||||
if(error) { throw error; }
|
if(error) { throw std::system_error(error); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this is a read-write mapping and the last reference to the mapping,
|
||||||
|
* the destructor invokes sync. Regardless of the access mode, unmap is
|
||||||
|
* invoked as a final step.
|
||||||
|
*/
|
||||||
~basic_shared_mmap() = default;
|
~basic_shared_mmap() = default;
|
||||||
|
|
||||||
/** Returns the underlying `std::shared_ptr` instance that holds the mmap. */
|
/** Returns the underlying `std::shared_ptr` instance that holds the mmap. */
|
||||||
@ -120,8 +127,15 @@ public:
|
|||||||
* however, a mapped region of a file gets its own handle, which is returned by
|
* however, a mapped region of a file gets its own handle, which is returned by
|
||||||
* 'mapping_handle'.
|
* 'mapping_handle'.
|
||||||
*/
|
*/
|
||||||
handle_type file_handle() const noexcept { return pimpl_->file_handle(); }
|
handle_type file_handle() const noexcept
|
||||||
handle_type mapping_handle() const noexcept { return pimpl_->mapping_handle(); }
|
{
|
||||||
|
return pimpl_ ? pimpl_->file_handle() : invalid_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_type mapping_handle() const noexcept
|
||||||
|
{
|
||||||
|
return pimpl_ ? pimpl_->mapping_handle() : invalid_handle;
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns whether a valid memory mapping has been created. */
|
/** Returns whether a valid memory mapping has been created. */
|
||||||
bool is_open() const noexcept { return pimpl_ && pimpl_->is_open(); }
|
bool is_open() const noexcept { return pimpl_ && pimpl_->is_open(); }
|
||||||
@ -142,7 +156,9 @@ public:
|
|||||||
size_type size() const noexcept { return pimpl_ ? pimpl_->length() : 0; }
|
size_type size() const noexcept { return pimpl_ ? pimpl_->length() : 0; }
|
||||||
size_type length() const noexcept { return pimpl_ ? pimpl_->length() : 0; }
|
size_type length() const noexcept { return pimpl_ ? pimpl_->length() : 0; }
|
||||||
size_type mapped_length() const noexcept
|
size_type mapped_length() const noexcept
|
||||||
{ return pimpl_ ? pimpl_->mapped_length() : 0; }
|
{
|
||||||
|
return pimpl_ ? pimpl_->mapped_length() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the offset, relative to the file's start, at which the mapping was
|
* Returns the offset, relative to the file's start, at which the mapping was
|
||||||
@ -237,6 +253,24 @@ public:
|
|||||||
map_impl(path, offset, length, error);
|
map_impl(path, offset, length, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
|
||||||
|
* reason is reported via `error` and the object remains in a state as if this
|
||||||
|
* function hadn't been called.
|
||||||
|
*
|
||||||
|
* `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.
|
||||||
|
*
|
||||||
|
* The entire file is mapped.
|
||||||
|
*/
|
||||||
|
template<typename String>
|
||||||
|
void map(const String& path, std::error_code& error)
|
||||||
|
{
|
||||||
|
map_impl(path, 0, map_entire_file, error);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
|
* Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
|
||||||
* reason is reported via `error` and the object remains in a state as if this
|
* reason is reported via `error` and the object remains in a state as if this
|
||||||
@ -262,6 +296,22 @@ public:
|
|||||||
map_impl(handle, offset, length, error);
|
map_impl(handle, offset, length, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
|
||||||
|
* reason is reported via `error` and the object remains in a state as if this
|
||||||
|
* function hadn't been called.
|
||||||
|
*
|
||||||
|
* `handle`, which must be a valid file handle, which is used to memory map the
|
||||||
|
* requested region. Upon failure, `error` is set to indicate the reason and the
|
||||||
|
* object remains in an unmapped state.
|
||||||
|
*
|
||||||
|
* The entire file is mapped.
|
||||||
|
*/
|
||||||
|
void map(const handle_type handle, std::error_code& error)
|
||||||
|
{
|
||||||
|
map_impl(handle, 0, map_entire_file, error);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a valid memory mapping has been created prior to this call, this call
|
* If a valid memory mapping has been created prior to this call, this call
|
||||||
* instructs the kernel to unmap the memory region and disassociate this object
|
* instructs the kernel to unmap the memory region and disassociate this object
|
||||||
|
|||||||
@ -5,19 +5,8 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
int handle_error(const std::error_code& error)
|
int handle_error(const std::error_code& error);
|
||||||
{
|
void allocate_file(const std::string& path, const int size);
|
||||||
const auto& errmsg = error.message();
|
|
||||||
std::printf("error mapping file: %s, exiting...\n", errmsg.c_str());
|
|
||||||
return error.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
void allocate_file(const std::string& path, const int size)
|
|
||||||
{
|
|
||||||
std::ofstream file(path);
|
|
||||||
std::string s(size, '0');
|
|
||||||
file << s;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
@ -49,21 +38,38 @@ int main()
|
|||||||
const int answer_index = rw_mmap.size() / 2;
|
const int answer_index = rw_mmap.size() / 2;
|
||||||
rw_mmap[answer_index] = 42;
|
rw_mmap[answer_index] = 42;
|
||||||
|
|
||||||
// Don't forget to flush changes to disk, which is NOT done by the destructor for
|
// Don't forget to flush changes to disk before unmapping. However, if
|
||||||
// more explicit control of this potentially expensive operation.
|
// `rw_mmap` were to go out of scope at this point, the destructor would also
|
||||||
|
// automatically invoke `sync` before `unmap`.
|
||||||
rw_mmap.sync(error);
|
rw_mmap.sync(error);
|
||||||
if (error) { return 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 and the above call to `sync` have the same
|
||||||
// invoked.
|
// effect as if the destructor had been invoked.
|
||||||
rw_mmap.unmap();
|
rw_mmap.unmap();
|
||||||
|
|
||||||
// Now create the same mapping, but in read-only mode.
|
// Now create the same mapping, but in read-only mode. Note that calling the
|
||||||
mio::mmap_source ro_mmap = mio::make_mmap_source(
|
// overload without the offset and file length parameters maps the entire
|
||||||
path, 0, mio::map_entire_file, error);
|
// file.
|
||||||
|
mio::mmap_source ro_mmap;
|
||||||
|
ro_mmap.map(path, 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int handle_error(const std::error_code& error)
|
||||||
|
{
|
||||||
|
const auto& errmsg = error.message();
|
||||||
|
std::printf("error mapping file: %s, exiting...\n", errmsg.c_str());
|
||||||
|
return error.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void allocate_file(const std::string& path, const int size)
|
||||||
|
{
|
||||||
|
std::ofstream file(path);
|
||||||
|
std::string s(size, '0');
|
||||||
|
file << s;
|
||||||
|
}
|
||||||
|
|||||||
@ -89,8 +89,7 @@ int main()
|
|||||||
CHECK_INVALID_MMAP(m);
|
CHECK_INVALID_MMAP(m);
|
||||||
|
|
||||||
// Invalid handle?
|
// Invalid handle?
|
||||||
m = mio::make_mmap_source(
|
m = mio::make_mmap_source(mio::invalid_handle, 0, 0, error);
|
||||||
INVALID_HANDLE_VALUE/*Psst... This is an implementation detail!*/, 0, 0, error);
|
|
||||||
CHECK_INVALID_MMAP(m);
|
CHECK_INVALID_MMAP(m);
|
||||||
|
|
||||||
// Invalid offset?
|
// Invalid offset?
|
||||||
@ -102,6 +101,11 @@ int main()
|
|||||||
// Make sure custom types compile.
|
// Make sure custom types compile.
|
||||||
mio::ummap_source _1;
|
mio::ummap_source _1;
|
||||||
mio::shared_ummap_source _2;
|
mio::shared_ummap_source _2;
|
||||||
|
// Make sure shared_mmap mapping compiles as all testing was done on
|
||||||
|
// normal mmaps.
|
||||||
|
mio::shared_mmap_source _3(path, 0, mio::map_entire_file);
|
||||||
|
auto _4 = mio::make_mmap_source(path, error);
|
||||||
|
auto _5 = mio::make_mmap<mio::shared_mmap_source>(path, 0, mio::map_entire_file, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::printf("all tests passed!\n");
|
std::printf("all tests passed!\n");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user