mirror of
https://github.com/vimpunk/mio.git
synced 2026-07-30 16:26:25 +08:00
extracted mmap_{source,sink} into one class
This commit is contained in:
parent
1a88259c02
commit
5c9c2e83ad
@ -4,6 +4,7 @@ A simple header-only cross-platform C++14 memory mapping library.
|
||||
## Example
|
||||
```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
|
||||
|
||||
@ -43,11 +44,16 @@ int main()
|
||||
|
||||
// Or map using the constructor, which throws a std::error_code upon failure.
|
||||
try {
|
||||
const auto page_size = mio::page_size();
|
||||
mio::mmap_sink mmap3("another/path/to/file", offset_type(4096), length_type(4096));
|
||||
// ...
|
||||
} catch(const std::error_code& error) {
|
||||
return handle_error(error);
|
||||
}
|
||||
|
||||
// mio exposes an interface that abstracts away memory as a string, so it is
|
||||
// possible to create mmap objects that have custom underlying character types:
|
||||
using wmmap_source = mio::basic_mmap_source<wchar_t>;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -37,16 +37,18 @@
|
||||
namespace mio {
|
||||
namespace detail {
|
||||
|
||||
template<typename CharT> struct basic_mmap;
|
||||
enum { use_full_file_size = 0 };
|
||||
|
||||
template<typename CharT>
|
||||
bool operator==(const basic_mmap<CharT>& a, const basic_mmap<CharT>& b);
|
||||
template<typename CharT>
|
||||
bool operator!=(const basic_mmap<CharT>& a, const basic_mmap<CharT>& b);
|
||||
enum class access_mode
|
||||
{
|
||||
read_only,
|
||||
read_write
|
||||
};
|
||||
|
||||
size_t page_size();
|
||||
|
||||
template<typename CharT> struct basic_mmap
|
||||
template<
|
||||
typename CharT,
|
||||
typename CharTraits = std::char_traits<CharT>
|
||||
> struct basic_mmap
|
||||
{
|
||||
using value_type = CharT;
|
||||
using size_type = int64_t;
|
||||
@ -66,14 +68,6 @@ template<typename CharT> struct basic_mmap
|
||||
using handle_type = int;
|
||||
#endif
|
||||
|
||||
static constexpr size_type use_full_file_size = 0;
|
||||
|
||||
enum class access_mode
|
||||
{
|
||||
read_only,
|
||||
read_write
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
// Points to the first requested byte, and not to the actual start of the mapping.
|
||||
@ -114,8 +108,6 @@ public:
|
||||
bool is_mapped() const noexcept;
|
||||
bool empty() const noexcept { return length() == 0; }
|
||||
|
||||
// TODO return the number of BYTES or the number of times sizeof(CharT) fits into length_?
|
||||
size_type size() const noexcept { return length_ >> sizeof(CharT); }
|
||||
size_type length() const noexcept { return length_ >> sizeof(CharT); }
|
||||
size_type mapped_length() const noexcept { return mapped_length_ >> sizeof(CharT); }
|
||||
|
||||
@ -153,9 +145,6 @@ public:
|
||||
|
||||
void swap(basic_mmap& other);
|
||||
|
||||
friend bool operator==<CharT>(const basic_mmap& a, const basic_mmap& b);
|
||||
friend bool operator!=<CharT>(const basic_mmap& a, const basic_mmap& b);
|
||||
|
||||
private:
|
||||
|
||||
pointer get_mapping_start() noexcept;
|
||||
@ -164,6 +153,30 @@ private:
|
||||
const access_mode mode, std::error_code& error);
|
||||
};
|
||||
|
||||
template<typename CharT, typename CharTraits>
|
||||
bool operator==(const basic_mmap<CharT, CharTraits>& a,
|
||||
const basic_mmap<CharT, CharTraits>& b);
|
||||
|
||||
template<typename CharT, typename CharTraits>
|
||||
bool operator!=(const basic_mmap<CharT, CharTraits>& a,
|
||||
const basic_mmap<CharT, CharTraits>& b);
|
||||
|
||||
template<typename CharT, typename CharTraits>
|
||||
bool operator<(const basic_mmap<CharT, CharTraits>& a,
|
||||
const basic_mmap<CharT, CharTraits>& b);
|
||||
|
||||
template<typename CharT, typename CharTraits>
|
||||
bool operator<=(const basic_mmap<CharT, CharTraits>& a,
|
||||
const basic_mmap<CharT, CharTraits>& b);
|
||||
|
||||
template<typename CharT, typename CharTraits>
|
||||
bool operator>(const basic_mmap<CharT, CharTraits>& a,
|
||||
const basic_mmap<CharT, CharTraits>& b);
|
||||
|
||||
template<typename CharT, typename CharTraits>
|
||||
bool operator>=(const basic_mmap<CharT, CharTraits>& a,
|
||||
const basic_mmap<CharT, CharTraits>& b);
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mio
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
|
||||
#include "mmap_impl.hpp"
|
||||
#include "type_traits.hpp"
|
||||
#include "../page.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
@ -37,6 +38,7 @@
|
||||
namespace mio {
|
||||
namespace detail {
|
||||
|
||||
// Generic handle type for use by free functions.
|
||||
using handle_type = basic_mmap<char>::handle_type;
|
||||
|
||||
#if defined(_WIN32)
|
||||
@ -51,28 +53,6 @@ inline DWORD int64_low(int64_t n) noexcept
|
||||
}
|
||||
#endif
|
||||
|
||||
inline size_t page_size()
|
||||
{
|
||||
static const size_t page_size = []
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO SystemInfo;
|
||||
GetSystemInfo(&SystemInfo);
|
||||
return SystemInfo.dwAllocationGranularity;
|
||||
#else
|
||||
return sysconf(_SC_PAGE_SIZE);
|
||||
#endif
|
||||
}();
|
||||
return page_size;
|
||||
}
|
||||
|
||||
inline size_t make_page_aligned(size_t t) noexcept
|
||||
{
|
||||
const static size_t page_size_ = page_size();
|
||||
// Use integer division to round down to the nearest page alignment.
|
||||
return t / page_size_ * page_size_;
|
||||
}
|
||||
|
||||
inline std::error_code last_error() noexcept
|
||||
{
|
||||
std::error_code error;
|
||||
@ -85,8 +65,7 @@ inline std::error_code last_error() noexcept
|
||||
}
|
||||
|
||||
template<typename Path>
|
||||
handle_type open_file(const Path& path,
|
||||
const basic_mmap<char>::access_mode mode, std::error_code& error)
|
||||
handle_type open_file(const Path& path, const access_mode mode, std::error_code& error)
|
||||
{
|
||||
error.clear();
|
||||
if(detail::empty(path))
|
||||
@ -96,8 +75,7 @@ handle_type open_file(const Path& path,
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
const auto handle = ::CreateFile(c_str(path),
|
||||
mode == basic_mmap<char>::access_mode::read_only
|
||||
? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
|
||||
mode == access_mode::read_only ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
@ -105,7 +83,7 @@ handle_type open_file(const Path& path,
|
||||
0);
|
||||
#else
|
||||
const auto handle = ::open(c_str(path),
|
||||
mode == basic_mmap<char>::access_mode::read_only ? O_RDONLY : O_RDWR);
|
||||
mode == access_mode::read_only ? O_RDONLY : O_RDWR);
|
||||
#endif
|
||||
if(handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@ -138,14 +116,14 @@ int64_t query_file_size(handle_type handle, std::error_code& error)
|
||||
|
||||
// -- basic_mmap --
|
||||
|
||||
template<typename CharT>
|
||||
basic_mmap<CharT>::~basic_mmap()
|
||||
template<typename CharT, typename CharTraits>
|
||||
basic_mmap<CharT, CharTraits>::~basic_mmap()
|
||||
{
|
||||
unmap();
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
basic_mmap<CharT>::basic_mmap(basic_mmap<CharT>&& other)
|
||||
template<typename CharT, typename CharTraits>
|
||||
basic_mmap<CharT, CharTraits>::basic_mmap(basic_mmap<CharT, CharTraits>&& other)
|
||||
: data_(std::move(other.data_))
|
||||
, length_(std::move(other.length_))
|
||||
, mapped_length_(std::move(other.mapped_length_))
|
||||
@ -163,8 +141,8 @@ basic_mmap<CharT>::basic_mmap(basic_mmap<CharT>&& other)
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
basic_mmap<CharT>& basic_mmap<CharT>::operator=(basic_mmap<CharT>&& other)
|
||||
template<typename CharT, typename CharTraits>
|
||||
basic_mmap<CharT, CharTraits>& basic_mmap<CharT, CharTraits>::operator=(basic_mmap<CharT, CharTraits>&& other)
|
||||
{
|
||||
if(this != &other)
|
||||
{
|
||||
@ -192,9 +170,9 @@ basic_mmap<CharT>& basic_mmap<CharT>::operator=(basic_mmap<CharT>&& other)
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
typename basic_mmap<CharT>::handle_type
|
||||
basic_mmap<CharT>::mapping_handle() const noexcept
|
||||
template<typename CharT, typename CharTraits>
|
||||
typename basic_mmap<CharT, CharTraits>::handle_type
|
||||
basic_mmap<CharT, CharTraits>::mapping_handle() const noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return file_mapping_handle_;
|
||||
@ -203,9 +181,9 @@ basic_mmap<CharT>::mapping_handle() const noexcept
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
template<typename CharT, typename CharTraits>
|
||||
template<typename String>
|
||||
void basic_mmap<CharT>::map(String& path, size_type offset,
|
||||
void basic_mmap<CharT, CharTraits>::map(String& path, size_type offset,
|
||||
size_type length, access_mode mode, std::error_code& error)
|
||||
{
|
||||
error.clear();
|
||||
@ -224,8 +202,8 @@ void basic_mmap<CharT>::map(String& path, size_type offset,
|
||||
}
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::map(handle_type handle, size_type offset,
|
||||
template<typename CharT, typename CharTraits>
|
||||
void basic_mmap<CharT, CharTraits>::map(handle_type handle, size_type offset,
|
||||
size_type length, access_mode mode, std::error_code& error)
|
||||
{
|
||||
error.clear();
|
||||
@ -238,7 +216,7 @@ void basic_mmap<CharT>::map(handle_type handle, size_type offset,
|
||||
const auto file_size = query_file_size(handle, error);
|
||||
if(error) { return; }
|
||||
|
||||
if(length <= 0)
|
||||
if(length <= use_full_file_size)
|
||||
{
|
||||
length = file_size;
|
||||
}
|
||||
@ -253,11 +231,11 @@ void basic_mmap<CharT>::map(handle_type handle, size_type offset,
|
||||
map(offset, length, mode, error);
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::map(const size_type offset, const size_type length,
|
||||
template<typename CharT, typename CharTraits>
|
||||
void basic_mmap<CharT, CharTraits>::map(const size_type offset, const size_type length,
|
||||
const access_mode mode, std::error_code& error)
|
||||
{
|
||||
const size_type aligned_offset = make_page_aligned(offset);
|
||||
const size_type aligned_offset = make_offset_page_aligned(offset);
|
||||
const size_type length_to_map = offset - aligned_offset + length;
|
||||
#if defined(_WIN32)
|
||||
const size_type max_file_size = offset + length;
|
||||
@ -289,7 +267,7 @@ void basic_mmap<CharT>::map(const size_type offset, const size_type length,
|
||||
const pointer mapping_start = static_cast<pointer>(::mmap(
|
||||
0, // Don't give hint as to where to map.
|
||||
length_to_map,
|
||||
mode == basic_mmap<CharT>::access_mode::read_only ? PROT_READ : PROT_WRITE,
|
||||
mode == access_mode::read_only ? PROT_READ : PROT_WRITE,
|
||||
MAP_SHARED, // TODO do we want to share it?
|
||||
file_handle_,
|
||||
aligned_offset));
|
||||
@ -304,8 +282,8 @@ void basic_mmap<CharT>::map(const size_type offset, const size_type length,
|
||||
mapped_length_ = length_to_map;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::sync(std::error_code& error)
|
||||
template<typename CharT, typename CharTraits>
|
||||
void basic_mmap<CharT, CharTraits>::sync(std::error_code& error)
|
||||
{
|
||||
error.clear();
|
||||
if(!is_open())
|
||||
@ -335,8 +313,8 @@ void basic_mmap<CharT>::sync(std::error_code& error)
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::unmap()
|
||||
template<typename CharT, typename CharTraits>
|
||||
void basic_mmap<CharT, CharTraits>::unmap()
|
||||
{
|
||||
if(!is_open()) { return; }
|
||||
// TODO do we care about errors here?
|
||||
@ -372,22 +350,23 @@ void basic_mmap<CharT>::unmap()
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
typename basic_mmap<CharT>::pointer basic_mmap<CharT>::get_mapping_start() noexcept
|
||||
template<typename CharT, typename CharTraits>
|
||||
typename basic_mmap<CharT, CharTraits>::pointer
|
||||
basic_mmap<CharT, CharTraits>::get_mapping_start() noexcept
|
||||
{
|
||||
if(!data_) { return nullptr; }
|
||||
const auto offset = mapped_length_ - length_;
|
||||
return data_ - offset;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool basic_mmap<CharT>::is_open() const noexcept
|
||||
template<typename CharT, typename CharTraits>
|
||||
bool basic_mmap<CharT, CharTraits>::is_open() const noexcept
|
||||
{
|
||||
return file_handle_ != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool basic_mmap<CharT>::is_mapped() const noexcept
|
||||
template<typename CharT, typename CharTraits>
|
||||
bool basic_mmap<CharT, CharTraits>::is_mapped() const noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return file_mapping_handle_ != INVALID_HANDLE_VALUE;
|
||||
@ -396,8 +375,8 @@ bool basic_mmap<CharT>::is_mapped() const noexcept
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::swap(basic_mmap<CharT>& other)
|
||||
template<typename CharT, typename CharTraits>
|
||||
void basic_mmap<CharT, CharTraits>::swap(basic_mmap<CharT, CharTraits>& other)
|
||||
{
|
||||
if(this != &other)
|
||||
{
|
||||
@ -413,22 +392,53 @@ void basic_mmap<CharT>::swap(basic_mmap<CharT>& other)
|
||||
}
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool operator==(const basic_mmap<CharT>& a, const basic_mmap<CharT>& b)
|
||||
template<typename CharT, typename CharTraits>
|
||||
bool operator==(const basic_mmap<CharT, CharTraits>& a,
|
||||
const basic_mmap<CharT, CharTraits>& b)
|
||||
{
|
||||
if(a.is_mapped() && b.is_mapped())
|
||||
{
|
||||
return (a.size() == b.size()) && std::equal(a.begin(), a.end(), b.begin());
|
||||
}
|
||||
return !a.is_mapped() && !b.is_mapped();
|
||||
return a.size() == b.size()
|
||||
&& CharTraits::compare(a.data(), b.data(), a.size()) == 0;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool operator!=(const basic_mmap<CharT>& a, const basic_mmap<CharT>& b)
|
||||
template<typename CharT, typename CharTraits>
|
||||
bool operator!=(const basic_mmap<CharT, CharTraits>& a,
|
||||
const basic_mmap<CharT, CharTraits>& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/*
|
||||
template<typename CharT, typename CharTraits>
|
||||
bool operator<(const basic_mmap<CharT, CharTraits>& a,
|
||||
const basic_mmap<CharT, CharTraits>& b)
|
||||
{
|
||||
return CharTraits::compare(a.data(), b.data(), a.size()) < 0;
|
||||
}
|
||||
|
||||
// TODO optimize
|
||||
template<typename CharT, typename CharTraits>
|
||||
bool operator<=(const basic_mmap<CharT, CharTraits>& a,
|
||||
const basic_mmap<CharT, CharTraits>& b)
|
||||
{
|
||||
return (a == b) || (a < b);
|
||||
}
|
||||
|
||||
template<typename CharT, typename CharTraits>
|
||||
bool operator>(const basic_mmap<CharT, CharTraits>& a,
|
||||
const basic_mmap<CharT, CharTraits>& b)
|
||||
{
|
||||
return CharTraits::compare(a.data(), b.data(), a.size()) > 0;
|
||||
}
|
||||
|
||||
// TODO optimize
|
||||
template<typename CharT, typename CharTraits>
|
||||
bool operator>=(const basic_mmap<CharT, CharTraits>& a,
|
||||
const basic_mmap<CharT, CharTraits>& b)
|
||||
{
|
||||
return (a == b) || (a > b);
|
||||
}
|
||||
*/
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mio
|
||||
|
||||
|
||||
@ -27,12 +27,27 @@
|
||||
|
||||
namespace mio {
|
||||
|
||||
/** A read-only file memory mapping. */
|
||||
template<typename CharT> class basic_mmap_source
|
||||
// This is used by basic_mmap to determine whether to create a read-only or a read-write
|
||||
// memory mapping. The two possible values are `read_only` and `read_write`.
|
||||
// TODO try to better expose these values
|
||||
using detail::access_mode;
|
||||
|
||||
// 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.
|
||||
using detail::use_full_file_size;
|
||||
|
||||
template<
|
||||
access_mode AccessMode,
|
||||
typename CharT,
|
||||
typename CharTraits = std::char_traits<CharT>
|
||||
> class basic_mmap
|
||||
{
|
||||
using impl_type = detail::basic_mmap<CharT>;
|
||||
using impl_type = detail::basic_mmap<CharT, CharTraits>;
|
||||
impl_type impl_;
|
||||
|
||||
static_assert(AccessMode == access_mode::read_only
|
||||
|| AccessMode == access_mode::read_write,
|
||||
"AccessMode must be either read_only or read_write");
|
||||
public:
|
||||
|
||||
using value_type = typename impl_type::value_type;
|
||||
@ -49,18 +64,12 @@ public:
|
||||
using iterator_category = typename impl_type::iterator_category;
|
||||
using handle_type = typename impl_type::handle_type;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
static constexpr size_type use_full_file_size = impl_type::use_full_file_size;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* undefined behaviour/segmentation faults.
|
||||
*/
|
||||
basic_mmap_source() = default;
|
||||
basic_mmap() = default;
|
||||
|
||||
/**
|
||||
* `handle` must be a valid file handle, which is then used to memory map the
|
||||
@ -73,7 +82,7 @@ public:
|
||||
* returned by `data` or `begin`), so long as `offset` is valid, will be at `offset`
|
||||
* from the start of the file.
|
||||
*/
|
||||
basic_mmap_source(const handle_type handle, const size_type offset, const size_type length)
|
||||
basic_mmap(const handle_type handle, const size_type offset, const size_type length)
|
||||
{
|
||||
std::error_code error;
|
||||
map(handle, offset, length, error);
|
||||
@ -84,11 +93,11 @@ public:
|
||||
* This class has single-ownership semantics, so transferring ownership may only be
|
||||
* accomplished by moving the object.
|
||||
*/
|
||||
basic_mmap_source(basic_mmap_source&&) = default;
|
||||
basic_mmap_source& operator=(basic_mmap_source&&) = default;
|
||||
basic_mmap(basic_mmap&&) = default;
|
||||
basic_mmap& operator=(basic_mmap&&) = default;
|
||||
|
||||
/** The destructor invokes unmap. */
|
||||
~basic_mmap_source() = default;
|
||||
~basic_mmap() = default;
|
||||
|
||||
/**
|
||||
* On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows,
|
||||
@ -109,11 +118,12 @@ public:
|
||||
bool empty() const noexcept { return impl_.empty(); }
|
||||
|
||||
/**
|
||||
* `size` and `length` both return the logical length (i.e. the one user requested),
|
||||
* while `mapped_length` returns the actual mapped length, which is usually a
|
||||
* multiple of the underlying operating system's page allocation granularity.
|
||||
* `size` and `length` both return the logical length, i.e. the number of bytes
|
||||
* user requested divided by `sizeof(CharT)`, while `mapped_length` returns the
|
||||
* actual number of bytes that were mapped, also divided by `sizeof(CharT)`, which
|
||||
* is a multiple of the underlying operating system's page allocation granularity.
|
||||
*/
|
||||
size_type size() const noexcept { return impl_.size(); }
|
||||
size_type size() const noexcept { return impl_.length(); }
|
||||
size_type length() const noexcept { return impl_.length(); }
|
||||
size_type mapped_length() const noexcept { return impl_.mapped_length(); }
|
||||
|
||||
@ -121,203 +131,10 @@ public:
|
||||
* Returns a pointer to the first requested byte, or `nullptr` if no memory mapping
|
||||
* exists.
|
||||
*/
|
||||
const_pointer data() const noexcept { return impl_.data(); }
|
||||
|
||||
/**
|
||||
* Returns an iterator to the first requested byte, if a valid memory mapping
|
||||
* exists, otherwise this function call is equivalent to invoking `end`.
|
||||
*/
|
||||
const_iterator begin() const noexcept { return impl_.begin(); }
|
||||
const_iterator cbegin() const noexcept { return impl_.cbegin(); }
|
||||
|
||||
/** Returns an iterator one past the last requested byte. */
|
||||
const_iterator end() const noexcept { return impl_.end(); }
|
||||
const_iterator cend() const noexcept { return impl_.cend(); }
|
||||
|
||||
const_reverse_iterator rbegin() const noexcept { return impl_.rbegin(); }
|
||||
const_reverse_iterator crbegin() const noexcept { return impl_.crbegin(); }
|
||||
|
||||
const_reverse_iterator rend() const noexcept { return impl_.rend(); }
|
||||
const_reverse_iterator crend() const noexcept { return impl_.crend(); }
|
||||
|
||||
/**
|
||||
* 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
|
||||
* prior to this call, undefined behaviour ensues.
|
||||
*/
|
||||
const_reference operator[](const size_type i) const noexcept { return impl_[i]; }
|
||||
|
||||
/**
|
||||
* Establishes a read-only memory mapping. 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* If `length` is `use_full_file_size`, a mapping of the entire file is created.
|
||||
*/
|
||||
template<typename String>
|
||||
void map(const String& path, const size_type offset,
|
||||
const size_type length, std::error_code& error)
|
||||
{
|
||||
impl_.map(path, offset, length, impl_type::access_mode::read_only, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a read-only memory mapping. 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` 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* If `length` is `use_full_file_size`, a mapping of the entire file is created.
|
||||
*/
|
||||
void map(const handle_type handle, const size_type offset,
|
||||
const size_type length, std::error_code& error)
|
||||
{
|
||||
impl_.map(handle, offset, length, impl_type::access_mode::read_only, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* from the file.
|
||||
*
|
||||
* The file handle associated with the file that is mapped is only closed if the
|
||||
* 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.
|
||||
*/
|
||||
void unmap() { impl_.unmap(); }
|
||||
|
||||
void swap(basic_mmap_source& other) { impl_.swap(other.impl_); }
|
||||
|
||||
friend bool operator==(const basic_mmap_source& a, const basic_mmap_source& b)
|
||||
{
|
||||
return a.impl_ == b.impl_;
|
||||
}
|
||||
|
||||
friend bool operator!=(const basic_mmap_source& a, const basic_mmap_source& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
/** A read-write file memory mapping. */
|
||||
template<typename CharT> class basic_mmap_sink
|
||||
{
|
||||
using impl_type = detail::basic_mmap<CharT>;
|
||||
impl_type impl_;
|
||||
|
||||
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 = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using iterator_category = typename impl_type::iterator_category;
|
||||
using handle_type = typename impl_type::handle_type;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
static constexpr size_type use_full_file_size = impl_type::use_full_file_size;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* undefined behaviour/segmentation faults.
|
||||
*/
|
||||
basic_mmap_sink() = default;
|
||||
|
||||
/**
|
||||
* `handle` must be a valid file handle, which is then used to memory map the
|
||||
* requested region. Upon failure a `std::error_code` is thrown, detailing the
|
||||
* cause of the error, 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.
|
||||
*/
|
||||
basic_mmap_sink(const handle_type handle, const size_type offset, const size_type length)
|
||||
{
|
||||
std::error_code error;
|
||||
map(handle, offset, length, error);
|
||||
if(error) { throw error; }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class has single-ownership semantics, so transferring ownership may only be
|
||||
* accomplished by moving the object.
|
||||
*/
|
||||
basic_mmap_sink(basic_mmap_sink&&) = default;
|
||||
basic_mmap_sink& operator=(basic_mmap_sink&&) = default;
|
||||
|
||||
/**
|
||||
* The destructor invokes unmap, but does NOT invoke `sync`. Thus, if the mapped
|
||||
* region has been written to, `sync` needs to be called in order to persist the
|
||||
* changes to disk.
|
||||
*/
|
||||
~basic_mmap_sink() = default;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 'mapping_handle'.
|
||||
*/
|
||||
handle_type file_handle() const noexcept { return impl_.file_handle(); }
|
||||
handle_type mapping_handle() const noexcept { return impl_.mapping_handle(); }
|
||||
|
||||
/** Returns whether a valid memory mapping has been created. */
|
||||
bool is_open() const noexcept { return impl_.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 impl_.empty(); }
|
||||
|
||||
/**
|
||||
* `size` and `length` both return the logical length (i.e. the one user requested),
|
||||
* while `mapped_length` returns the actual mapped length, which is usually a
|
||||
* multiple of the underlying operating system's page allocation granularity.
|
||||
*/
|
||||
size_type size() const noexcept { return impl_.size(); }
|
||||
size_type length() const noexcept { return impl_.length(); }
|
||||
size_type mapped_length() const noexcept { return impl_.mapped_length(); }
|
||||
|
||||
/**
|
||||
* Returns a pointer to the first requested byte, or `nullptr` if no memory mapping
|
||||
* exists.
|
||||
*/
|
||||
pointer data() noexcept { return impl_.data(); }
|
||||
template<
|
||||
access_mode A = AccessMode,
|
||||
typename = typename std::enable_if<A == access_mode::read_write>::type
|
||||
> pointer data() noexcept { return impl_.data(); }
|
||||
const_pointer data() const noexcept { return impl_.data(); }
|
||||
|
||||
/**
|
||||
@ -329,15 +146,24 @@ public:
|
||||
const_iterator cbegin() const noexcept { return impl_.cbegin(); }
|
||||
|
||||
/** Returns an iterator one past the last requested byte. */
|
||||
iterator end() noexcept { return impl_.end(); }
|
||||
template<
|
||||
access_mode A = AccessMode,
|
||||
typename = typename std::enable_if<A == access_mode::read_write>::type
|
||||
> iterator end() noexcept { return impl_.end(); }
|
||||
const_iterator end() const noexcept { return impl_.end(); }
|
||||
const_iterator cend() const noexcept { return impl_.cend(); }
|
||||
|
||||
reverse_iterator rbegin() noexcept { return impl_.rbegin(); }
|
||||
template<
|
||||
access_mode A = AccessMode,
|
||||
typename = typename std::enable_if<A == access_mode::read_write>::type
|
||||
> reverse_iterator rbegin() noexcept { return impl_.rbegin(); }
|
||||
const_reverse_iterator rbegin() const noexcept { return impl_.rbegin(); }
|
||||
const_reverse_iterator crbegin() const noexcept { return impl_.crbegin(); }
|
||||
|
||||
reverse_iterator rend() noexcept { return impl_.rend(); }
|
||||
template<
|
||||
access_mode A = AccessMode,
|
||||
typename = typename std::enable_if<A == access_mode::read_write>::type
|
||||
> reverse_iterator rend() noexcept { return impl_.rend(); }
|
||||
const_reverse_iterator rend() const noexcept { return impl_.rend(); }
|
||||
const_reverse_iterator crend() const noexcept { return impl_.crend(); }
|
||||
|
||||
@ -350,7 +176,7 @@ public:
|
||||
const_reference operator[](const size_type i) const noexcept { return impl_[i]; }
|
||||
|
||||
/**
|
||||
* Establishes a read-write memory mapping. 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
|
||||
* function hadn't been called.
|
||||
*
|
||||
@ -365,17 +191,19 @@ public:
|
||||
* returned by `data` or `begin`), so long as `offset` is valid, will be at `offset`
|
||||
* from the start of the file.
|
||||
*
|
||||
* If `length` is `use_full_file_size`, a mapping of the entire file is created.
|
||||
* `length` must be the number of bytes to map, regardless of the underlying
|
||||
* value_type's size! If it is `use_full_file_size`, a mapping of the entire file
|
||||
* is created.
|
||||
*/
|
||||
template<typename String>
|
||||
void map(const String& path, const size_type offset,
|
||||
const size_type length, std::error_code& error)
|
||||
{
|
||||
impl_.map(path, offset, length, impl_type::access_mode::read_only, error);
|
||||
impl_.map(path, offset, length, AccessMode, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a read-write memory mapping. 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
|
||||
* function hadn't been called.
|
||||
*
|
||||
@ -389,12 +217,14 @@ public:
|
||||
* returned by `data` or `begin`), so long as `offset` is valid, will be at `offset`
|
||||
* from the start of the file.
|
||||
*
|
||||
* If `length` is `use_full_file_size`, a mapping of the entire file is created.
|
||||
* `length` must be the number of bytes to map, regardless of the underlying
|
||||
* value_type's size! If it is `use_full_file_size`, a mapping of the entire file
|
||||
* is created.
|
||||
*/
|
||||
void map(const handle_type handle, const size_type offset,
|
||||
const size_type length, std::error_code& error)
|
||||
{
|
||||
impl_.map(handle, offset, length, impl_type::access_mode::read_write, error);
|
||||
impl_.map(handle, offset, length, AccessMode, error);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -408,28 +238,83 @@ public:
|
||||
*/
|
||||
void unmap() { impl_.unmap(); }
|
||||
|
||||
void swap(basic_mmap& other) { impl_.swap(other.impl_); }
|
||||
|
||||
/** Flushes the memory mapped page to disk. */
|
||||
void sync(std::error_code& error) { impl_.sync(error); }
|
||||
// TODO better name?
|
||||
template<
|
||||
access_mode A = AccessMode,
|
||||
typename = typename std::enable_if<A == access_mode::read_write>::type
|
||||
> void sync(std::error_code& error) { impl_.sync(error); }
|
||||
|
||||
void swap(basic_mmap_sink& other) { impl_.swap(other.impl_); }
|
||||
/** All operators compare two mapped area's according to CharTraits::compare. */
|
||||
|
||||
friend bool operator==(const basic_mmap_sink& a, const basic_mmap_sink& b)
|
||||
friend bool operator==(const basic_mmap& a, const basic_mmap& b)
|
||||
{
|
||||
return a.impl_ == b.impl_;
|
||||
}
|
||||
|
||||
friend bool operator!=(const basic_mmap_sink& a, const basic_mmap_sink& b)
|
||||
friend bool operator!=(const basic_mmap& a, const basic_mmap& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
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_;
|
||||
}
|
||||
|
||||
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_;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the basis for all read-only mmap objects and should be preferred over
|
||||
* directly using basic_mmap.
|
||||
*/
|
||||
template<
|
||||
typename CharT,
|
||||
typename CharTraits = std::char_traits<CharT>
|
||||
> using basic_mmap_source = basic_mmap<access_mode::read_only, CharT, CharTraits>;
|
||||
|
||||
/**
|
||||
* This is the basis for all read-write mmap objects and should be preferred over
|
||||
* directly using basic_mmap.
|
||||
*/
|
||||
template<
|
||||
typename CharT,
|
||||
typename CharTraits = std::char_traits<CharT>
|
||||
> using basic_mmap_sink = basic_mmap<access_mode::read_write, CharT, CharTraits>;
|
||||
|
||||
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>;
|
||||
|
||||
template<
|
||||
typename MMap,
|
||||
typename MappingToken
|
||||
> MMap make_mmap(const MappingToken& token,
|
||||
int64_t offset, int64_t length, std::error_code& error)
|
||||
{
|
||||
MMap mmap;
|
||||
mmap.map(token, offset, length, error);
|
||||
return mmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience factory method.
|
||||
*
|
||||
@ -441,9 +326,7 @@ template<typename MappingToken>
|
||||
mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset,
|
||||
mmap_source::size_type length, std::error_code& error)
|
||||
{
|
||||
mmap_source mmap;
|
||||
mmap.map(token, offset, length, error);
|
||||
return mmap;
|
||||
return make_mmap<mmap_source>(token, offset, length, error);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -457,9 +340,7 @@ template<typename MappingToken>
|
||||
mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset,
|
||||
mmap_sink::size_type length, std::error_code& error)
|
||||
{
|
||||
mmap_sink mmap;
|
||||
mmap.map(token, offset, length, error);
|
||||
return mmap;
|
||||
return make_mmap<mmap_sink>(token, offset, length, error);
|
||||
}
|
||||
|
||||
} // namespace mio
|
||||
|
||||
71
include/mio/page.hpp
Normal file
71
include/mio/page.hpp
Normal file
@ -0,0 +1,71 @@
|
||||
/* 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_PAGE_HEADER
|
||||
#define MIO_PAGE_HEADER
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif // WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace mio {
|
||||
|
||||
/**
|
||||
* Determines the operating system's page allocation granularity.
|
||||
*
|
||||
* On the first call to this function, it invokes the operating system specific syscall
|
||||
* to determine the page size, caches the value, and returns it. Any subsequent call to
|
||||
* this function serves the cached value, so no further syscalls are made.
|
||||
*/
|
||||
inline size_t page_size()
|
||||
{
|
||||
static const size_t page_size = []
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO SystemInfo;
|
||||
GetSystemInfo(&SystemInfo);
|
||||
return SystemInfo.dwAllocationGranularity;
|
||||
#else
|
||||
return sysconf(_SC_PAGE_SIZE);
|
||||
#endif
|
||||
}();
|
||||
return page_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alligns `offset` to the operating's system page size such that it subtracts the
|
||||
* difference until the nearest page boundary before `offset`, or does nothing if
|
||||
* `offset` is already page aligned.
|
||||
*/
|
||||
inline size_t make_offset_page_aligned(size_t offset) noexcept
|
||||
{
|
||||
const size_t page_size_ = page_size();
|
||||
// Use integer division to round down to the nearest page alignment.
|
||||
return offset / page_size_ * page_size_;
|
||||
}
|
||||
|
||||
} // namespace mio
|
||||
|
||||
#endif // MIO_PAGE_HEADER
|
||||
@ -18,8 +18,8 @@ int main()
|
||||
|
||||
// Map the region of the file to which buffer was written.
|
||||
std::error_code error;
|
||||
mio::mmap_source file_view = mio::make_mmap_source(path,
|
||||
0, mio::mmap_source::use_full_file_size, error);
|
||||
mio::mmap_source file_view = mio::make_mmap_source(
|
||||
path, 0, mio::use_full_file_size, error);
|
||||
if(error)
|
||||
{
|
||||
const auto& errmsg = error.message();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user