mirror of
https://github.com/vimpunk/mio.git
synced 2025-12-11 22:20:01 +08:00
updated example, removed multi-byte support, bugfix
This commit is contained in:
parent
adf941015f
commit
61470daa08
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
test/**
|
||||
!test/test.cpp
|
||||
!test/example.cpp
|
||||
|
||||
53
README.md
53
README.md
@ -43,10 +43,7 @@ General usage:
|
||||
#include <mio/mmap.hpp>
|
||||
#include <system_error> // for std::error_code
|
||||
#include <cstdio> // for std::printf
|
||||
|
||||
// This is just to clarify which is which when using the various mapping methods.
|
||||
using offset_type = mio::mmap_sink::size_type;
|
||||
using length_type = mio::mmap_sink::size_type;
|
||||
#include <cassert>
|
||||
|
||||
int handle_error(const std::error_code& error)
|
||||
{
|
||||
@ -60,8 +57,8 @@ int main()
|
||||
// Read-write memory map the whole file by using `map_entire_file` where the
|
||||
// length of the mapping is otherwise expected, with the factory method.
|
||||
std::error_code error;
|
||||
mio::mmap_sink rw_mmap = mio::make_mmap_sink("file.txt",
|
||||
offset_type(0), length_type(mio::map_entire_file), error);
|
||||
mio::mmap_sink rw_mmap = mio::make_mmap_sink(
|
||||
"file.txt", 0, mio::map_entire_file, error);
|
||||
if(error) { return handle_error(error); }
|
||||
|
||||
// Iterate through the mapped region just as if it were any other container, and
|
||||
@ -70,39 +67,31 @@ int main()
|
||||
b += 10;
|
||||
}
|
||||
|
||||
const int answer_index = rw_mmap.size() / 2;
|
||||
|
||||
// Or just change one value with the subscript operator.
|
||||
const int answer_index = rw_mmap.size() / 2;
|
||||
rw_mmap[answer_index] = 42;
|
||||
|
||||
// You can also create a mapping using the constructor, which throws a
|
||||
// std::error_code upon failure.
|
||||
const auto page_size = mio::page_size();
|
||||
try {
|
||||
mio::mmap_sink mmap3("another/path/to/file",
|
||||
offset_type(page_size), length_type(page_size));
|
||||
// ...
|
||||
} catch(const std::error_code& error) {
|
||||
return handle_error(error);
|
||||
}
|
||||
|
||||
// Don't forget to flush changes to disk, which is NOT done by the destructor for
|
||||
// more explicit control of this potentially expensive operation.
|
||||
rw_mmap.sync();
|
||||
rw_mmap.sync(error);
|
||||
if(error) { handle_error(error); }
|
||||
|
||||
// We can then remove the mapping, after which rw_mmap will be in a default
|
||||
// constructed state.
|
||||
rw_mmap.close();
|
||||
// constructed state, i.e. this has the same effect as if the destructor had been
|
||||
// invoked.
|
||||
rw_mmap.unmap();
|
||||
|
||||
// Now create the same mapping, but in read-only mode.
|
||||
mio::mmap_source ro_mmap = mio::make_mmap_source("file.txt",
|
||||
offset_type(0), length_type(mio::map_entire_file), error);
|
||||
mio::mmap_source ro_mmap = mio::make_mmap_source(
|
||||
"file.txt", 0, mio::map_entire_file, error);
|
||||
if(error) { return handle_error(error); }
|
||||
|
||||
const int the_answer_to_everything = ro_mmap[answer_index];
|
||||
assert(the_answer_to_everything == 42);
|
||||
}
|
||||
```
|
||||
|
||||
`mio::basic_mmap` has move-only semantics, but if multiple copies to the same mapping is required, use `mio::basic_shared_mmap`, which has `std::shared_ptr` semantics, which has the same interface as `mio::basic_mmap`.
|
||||
`mio::basic_mmap` has move-only semantics, but if multiple copies to the same mapping is required, use `mio::basic_shared_mmap` which has `std::shared_ptr` semantics and has the same interface as `mio::basic_mmap`.
|
||||
```c++
|
||||
#include <mio/shared_mmap.hpp>
|
||||
|
||||
@ -113,13 +102,15 @@ mio::shared_mmap_source shared_mmap4;
|
||||
shared_mmap4.map("path", offset, size_to_map, error);
|
||||
```
|
||||
|
||||
It's possible to change the character type of the mmap object, which is useful
|
||||
for wide strings or reading fixed width integers (although endianness is not
|
||||
accounted for by mio, so appropriate conversions need be done by the user).
|
||||
It's possible to define the character type of a byte, though aliases for the most commonly used types are provided:
|
||||
```c++
|
||||
using wmmap_source = mio::basic_mmap_source<wchar_t>;
|
||||
using i32mmap_source = mio::basic_mmap_source<int32_t>;
|
||||
using u16shared_mmap_sink = mio::basic_shared_mmap_sink<uint16_t>;
|
||||
using mmap_source = basic_mmap_source<char>;
|
||||
using ummap_source = basic_mmap_source<unsigned char>;
|
||||
```
|
||||
But it may be useful to define your own types, say when using the new `std::byte` type in C++17:
|
||||
```c++
|
||||
using mmap_source = mio::basic_mmap_source<std::byte>;
|
||||
using mmap_sink = mio::basic_mmap_sink<std::byte>;
|
||||
```
|
||||
|
||||
You can query the underlying system's page allocation granularity by invoking `mio::page_size()`, which is located in `mio/page.hpp`.
|
||||
|
||||
@ -51,9 +51,9 @@ enum class access_mode
|
||||
using file_handle_type = int;
|
||||
#endif
|
||||
|
||||
template<typename CharT> struct basic_mmap
|
||||
template<typename ByteT> struct basic_mmap
|
||||
{
|
||||
using value_type = CharT;
|
||||
using value_type = ByteT;
|
||||
using size_type = int64_t;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
@ -67,6 +67,8 @@ template<typename CharT> struct basic_mmap
|
||||
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.
|
||||
@ -82,8 +84,8 @@ private:
|
||||
|
||||
// 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 num_bytes_ = 0;
|
||||
size_type num_mapped_bytes_ = 0;
|
||||
size_type length_ = 0;
|
||||
size_type mapped_length_ = 0;
|
||||
|
||||
// Letting user map a file using both an existing file handle and a path introcudes
|
||||
// some complexity in that we must not close the file handle if user provided it,
|
||||
@ -103,15 +105,13 @@ public:
|
||||
handle_type file_handle() const noexcept { return file_handle_; }
|
||||
handle_type mapping_handle() const noexcept;
|
||||
|
||||
bool is_open() 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 to_char_size(num_mapped_bytes_ - num_bytes_); }
|
||||
size_type length() const noexcept { return to_char_size(num_bytes_); }
|
||||
size_type mapped_length() const noexcept { return to_char_size(num_mapped_bytes_); }
|
||||
size_type length_in_bytes() const noexcept { return num_bytes_; }
|
||||
size_type mapped_length_in_bytes() const noexcept { return num_mapped_bytes_; }
|
||||
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_; }
|
||||
@ -135,56 +135,33 @@ public:
|
||||
reference operator[](const size_type i) noexcept { return data_[i]; }
|
||||
const_reference operator[](const size_type i) const noexcept { return data_[i]; }
|
||||
|
||||
void set_length(const size_type length) noexcept;
|
||||
void set_offset(const size_type offset) noexcept;
|
||||
|
||||
template<typename String>
|
||||
void map(String& path, size_type offset, size_type num_bytes,
|
||||
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 num_bytes,
|
||||
void map(handle_type handle, size_type offset, size_type length,
|
||||
access_mode mode, std::error_code& error);
|
||||
void unmap();
|
||||
void close();
|
||||
|
||||
void sync(std::error_code& error);
|
||||
|
||||
void swap(basic_mmap& other);
|
||||
|
||||
private:
|
||||
|
||||
pointer get_mapping_start() noexcept;
|
||||
|
||||
void map(const size_type offset, size_type num_bytes,
|
||||
const access_mode mode, std::error_code& error);
|
||||
|
||||
static size_type to_char_size(const size_type num_bytes) noexcept
|
||||
{
|
||||
return num_bytes >> (sizeof(CharT) - 1);
|
||||
}
|
||||
|
||||
static size_type to_byte_size(const size_type num_chars) noexcept
|
||||
{
|
||||
return num_chars << (sizeof(CharT) - 1);
|
||||
}
|
||||
pointer get_mapping_start() noexcept { return !data() ? nullptr : data() - offset(); }
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
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
|
||||
|
||||
@ -61,8 +61,9 @@ inline std::error_code last_error() noexcept
|
||||
return error;
|
||||
}
|
||||
|
||||
template<typename Path>
|
||||
file_handle_type open_file(const Path& path, const access_mode mode, std::error_code& error)
|
||||
template<typename String>
|
||||
file_handle_type open_file(const String& path,
|
||||
const access_mode mode, std::error_code& error)
|
||||
{
|
||||
error.clear();
|
||||
if(detail::empty(path))
|
||||
@ -111,19 +112,83 @@ inline int64_t query_file_size(file_handle_type handle, std::error_code& error)
|
||||
#endif
|
||||
}
|
||||
|
||||
struct mmap_context
|
||||
{
|
||||
char* data;
|
||||
int64_t length;
|
||||
int64_t mapped_length;
|
||||
#ifdef _WIN32
|
||||
file_handle_type file_mapping_handle;
|
||||
#endif
|
||||
};
|
||||
|
||||
mmap_context memory_map(const file_handle_type file_handle, const int64_t offset,
|
||||
const int64_t length, const access_mode mode, std::error_code& error)
|
||||
{
|
||||
const int64_t aligned_offset = make_offset_page_aligned(offset);
|
||||
const int64_t length_to_map = offset - aligned_offset + length;
|
||||
#ifdef _WIN32
|
||||
const int64_t max_file_size = offset + length;
|
||||
const auto file_mapping_handle = ::CreateFileMapping(
|
||||
file_handle,
|
||||
0,
|
||||
mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE,
|
||||
int64_high(max_file_size),
|
||||
int64_low(max_file_size),
|
||||
0);
|
||||
if(file_mapping_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
error = last_error();
|
||||
return {};
|
||||
}
|
||||
char* mapping_start = static_cast<char*>(::MapViewOfFile(
|
||||
file_mapping_handle,
|
||||
mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE,
|
||||
int64_high(aligned_offset),
|
||||
int64_low(aligned_offset),
|
||||
length_to_map));
|
||||
if(mapping_start == nullptr)
|
||||
{
|
||||
error = last_error();
|
||||
return {};
|
||||
}
|
||||
#else
|
||||
char* mapping_start = static_cast<char*>(::mmap(
|
||||
0, // Don't give hint as to where to map.
|
||||
length_to_map,
|
||||
mode == access_mode::read ? PROT_READ : PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
file_handle,
|
||||
aligned_offset));
|
||||
if(mapping_start == MAP_FAILED)
|
||||
{
|
||||
error = last_error();
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
mmap_context ctx;
|
||||
ctx.data = mapping_start + offset - aligned_offset;
|
||||
ctx.length = length;
|
||||
ctx.mapped_length = length_to_map;
|
||||
#ifdef _WIN32
|
||||
ctx.file_mapping_handle = file_mapping_handle;
|
||||
#endif
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// -- basic_mmap --
|
||||
|
||||
template<typename CharT>
|
||||
basic_mmap<CharT>::~basic_mmap()
|
||||
template<typename ByteT>
|
||||
basic_mmap<ByteT>::~basic_mmap()
|
||||
{
|
||||
unmap();
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
basic_mmap<CharT>::basic_mmap(basic_mmap<CharT>&& other)
|
||||
template<typename ByteT>
|
||||
basic_mmap<ByteT>::basic_mmap(basic_mmap<ByteT>&& other)
|
||||
: data_(std::move(other.data_))
|
||||
, num_bytes_(std::move(other.num_bytes_))
|
||||
, num_mapped_bytes_(std::move(other.num_mapped_bytes_))
|
||||
, length_(std::move(other.length_))
|
||||
, mapped_length_(std::move(other.mapped_length_))
|
||||
, file_handle_(std::move(other.file_handle_))
|
||||
#ifdef _WIN32
|
||||
, file_mapping_handle_(std::move(other.file_mapping_handle_))
|
||||
@ -131,23 +196,23 @@ basic_mmap<CharT>::basic_mmap(basic_mmap<CharT>&& other)
|
||||
, is_handle_internal_(std::move(other.is_handle_internal_))
|
||||
{
|
||||
other.data_ = nullptr;
|
||||
other.num_bytes_ = other.num_mapped_bytes_ = 0;
|
||||
other.length_ = other.mapped_length_ = 0;
|
||||
other.file_handle_ = INVALID_HANDLE_VALUE;
|
||||
#ifdef _WIN32
|
||||
other.file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
basic_mmap<CharT>& basic_mmap<CharT>::operator=(basic_mmap<CharT>&& other)
|
||||
template<typename ByteT>
|
||||
basic_mmap<ByteT>& basic_mmap<ByteT>::operator=(basic_mmap<ByteT>&& other)
|
||||
{
|
||||
if(this != &other)
|
||||
{
|
||||
// First the existing mapping needs to be removed.
|
||||
unmap();
|
||||
data_ = std::move(other.data_);
|
||||
num_bytes_ = std::move(other.num_bytes_);
|
||||
num_mapped_bytes_ = std::move(other.num_mapped_bytes_);
|
||||
length_ = std::move(other.length_);
|
||||
mapped_length_ = std::move(other.mapped_length_);
|
||||
file_handle_ = std::move(other.file_handle_);
|
||||
#ifdef _WIN32
|
||||
file_mapping_handle_ = std::move(other.file_mapping_handle_);
|
||||
@ -157,7 +222,7 @@ basic_mmap<CharT>& basic_mmap<CharT>::operator=(basic_mmap<CharT>&& other)
|
||||
// The moved from basic_mmap's fields need to be reset, because otherwise other's
|
||||
// destructor will unmap the same mapping that was just moved into this.
|
||||
other.data_ = nullptr;
|
||||
other.num_bytes_ = other.num_mapped_bytes_ = 0;
|
||||
other.length_ = other.mapped_length_ = 0;
|
||||
other.file_handle_ = INVALID_HANDLE_VALUE;
|
||||
#ifdef _WIN32
|
||||
other.file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
||||
@ -167,8 +232,8 @@ 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 ByteT>
|
||||
typename basic_mmap<ByteT>::handle_type basic_mmap<ByteT>::mapping_handle() const noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return file_mapping_handle_;
|
||||
@ -177,10 +242,10 @@ typename basic_mmap<CharT>::handle_type basic_mmap<CharT>::mapping_handle() cons
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
template<typename ByteT>
|
||||
template<typename String>
|
||||
void basic_mmap<CharT>::map(String& path, size_type offset,
|
||||
size_type num_bytes, access_mode mode, std::error_code& error)
|
||||
void basic_mmap<ByteT>::map(String& path, size_type offset,
|
||||
size_type length, access_mode mode, std::error_code& error)
|
||||
{
|
||||
error.clear();
|
||||
if(detail::empty(path))
|
||||
@ -188,19 +253,19 @@ void basic_mmap<CharT>::map(String& path, size_type offset,
|
||||
error = std::make_error_code(std::errc::invalid_argument);
|
||||
return;
|
||||
}
|
||||
if(!is_open())
|
||||
const auto handle = open_file(path, mode, error);
|
||||
if(error) { return; }
|
||||
map(handle, offset, length, mode, error);
|
||||
// This MUST be after the call to map, as that sets this to true.
|
||||
if(!error)
|
||||
{
|
||||
const auto handle = open_file(path, mode, error);
|
||||
if(error) { return; }
|
||||
map(handle, offset, num_bytes, mode, error);
|
||||
// MUST be after the call to map, as that sets this to true.
|
||||
is_handle_internal_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::map(handle_type handle, size_type offset,
|
||||
size_type num_bytes, access_mode mode, std::error_code& error)
|
||||
template<typename ByteT>
|
||||
void basic_mmap<ByteT>::map(handle_type handle, size_type offset,
|
||||
size_type length, access_mode mode, std::error_code& error)
|
||||
{
|
||||
error.clear();
|
||||
if(handle == INVALID_HANDLE_VALUE)
|
||||
@ -212,74 +277,38 @@ void basic_mmap<CharT>::map(handle_type handle, size_type offset,
|
||||
const auto file_size = query_file_size(handle, error);
|
||||
if(error) { return; }
|
||||
|
||||
if(num_bytes <= map_entire_file)
|
||||
if(length <= map_entire_file)
|
||||
{
|
||||
num_bytes = file_size;
|
||||
length = file_size;
|
||||
}
|
||||
else if(offset + num_bytes > file_size)
|
||||
else if(offset + length > file_size)
|
||||
{
|
||||
error = std::make_error_code(std::errc::invalid_argument);
|
||||
return;
|
||||
}
|
||||
|
||||
file_handle_ = handle;
|
||||
is_handle_internal_ = false;
|
||||
map(offset, num_bytes, mode, error);
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::map(const size_type offset, const size_type num_bytes,
|
||||
const access_mode mode, std::error_code& error)
|
||||
{
|
||||
const size_type aligned_offset = make_offset_page_aligned(offset);
|
||||
const size_type num_bytes_to_map = offset - aligned_offset + num_bytes;
|
||||
const mmap_context ctx = memory_map(handle, offset, length, mode, error);
|
||||
if(!error)
|
||||
{
|
||||
// We must unmap any previous mapping that may have existed prior to this call.
|
||||
// Note that this must only be invoked after the new mapping has been created
|
||||
// in order to provide the strong guarantee that, should a mapping fail, the
|
||||
// `map` function leaves the this instance in a state as though the function
|
||||
// had never been called.
|
||||
unmap();
|
||||
file_handle_ = handle;
|
||||
is_handle_internal_ = false;
|
||||
data_ = reinterpret_cast<pointer>(ctx.data);
|
||||
length_ = ctx.length;
|
||||
mapped_length_ = ctx.mapped_length;
|
||||
#ifdef _WIN32
|
||||
const size_type max_file_size = offset + num_bytes;
|
||||
file_mapping_handle_ = ::CreateFileMapping(
|
||||
file_handle_,
|
||||
0,
|
||||
mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE,
|
||||
int64_high(max_file_size),
|
||||
int64_low(max_file_size),
|
||||
0);
|
||||
if(file_mapping_handle_ == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
error = last_error();
|
||||
return;
|
||||
}
|
||||
|
||||
const pointer mapping_start = static_cast<pointer>(::MapViewOfFile(
|
||||
file_mapping_handle_,
|
||||
mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE,
|
||||
int64_high(aligned_offset),
|
||||
int64_low(aligned_offset),
|
||||
num_bytes_to_map));
|
||||
if(mapping_start == nullptr)
|
||||
{
|
||||
error = last_error();
|
||||
return;
|
||||
}
|
||||
#else
|
||||
const pointer mapping_start = static_cast<pointer>(::mmap(
|
||||
0, // Don't give hint as to where to map.
|
||||
num_bytes_to_map,
|
||||
mode == access_mode::read ? PROT_READ : PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
file_handle_,
|
||||
aligned_offset));
|
||||
if(mapping_start == MAP_FAILED)
|
||||
{
|
||||
error = last_error();
|
||||
return;
|
||||
}
|
||||
file_mapping_handle_ = ctx.file_mapping_handle;
|
||||
#endif
|
||||
data_ = mapping_start + offset - aligned_offset;
|
||||
num_bytes_ = num_bytes;
|
||||
num_mapped_bytes_ = num_bytes_to_map;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::sync(std::error_code& error)
|
||||
template<typename ByteT>
|
||||
void basic_mmap<ByteT>::sync(std::error_code& error)
|
||||
{
|
||||
error.clear();
|
||||
if(!is_open())
|
||||
@ -291,10 +320,10 @@ void basic_mmap<CharT>::sync(std::error_code& error)
|
||||
if(data() != nullptr)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(::FlushViewOfFile(get_mapping_start(), num_mapped_bytes_) == 0
|
||||
if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0
|
||||
|| ::FlushFileBuffers(file_handle_) == 0)
|
||||
#else
|
||||
if(::msync(get_mapping_start(), num_mapped_bytes_, MS_SYNC) != 0)
|
||||
if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0)
|
||||
#endif
|
||||
{
|
||||
error = last_error();
|
||||
@ -309,8 +338,8 @@ void basic_mmap<CharT>::sync(std::error_code& error)
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::unmap()
|
||||
template<typename ByteT>
|
||||
void basic_mmap<ByteT>::unmap()
|
||||
{
|
||||
if(!is_open()) { return; }
|
||||
// TODO do we care about errors here?
|
||||
@ -322,7 +351,7 @@ void basic_mmap<CharT>::unmap()
|
||||
file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
#else
|
||||
if(data_) { ::munmap(const_cast<pointer>(get_mapping_start()), num_mapped_bytes_); }
|
||||
if(data_) { ::munmap(const_cast<pointer>(get_mapping_start()), mapped_length_); }
|
||||
#endif
|
||||
|
||||
// If file handle was obtained by our opening it (when map is called with a path,
|
||||
@ -339,29 +368,15 @@ void basic_mmap<CharT>::unmap()
|
||||
|
||||
// Reset fields to their default values.
|
||||
data_ = nullptr;
|
||||
num_bytes_ = num_mapped_bytes_ = 0;
|
||||
length_ = mapped_length_ = 0;
|
||||
file_handle_ = INVALID_HANDLE_VALUE;
|
||||
#ifdef _WIN32
|
||||
file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
typename basic_mmap<CharT>::pointer
|
||||
basic_mmap<CharT>::get_mapping_start() noexcept
|
||||
{
|
||||
if(!data()) { return nullptr; }
|
||||
return data() - offset();
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool basic_mmap<CharT>::is_open() const noexcept
|
||||
{
|
||||
return file_handle_ != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool basic_mmap<CharT>::is_mapped() const noexcept
|
||||
template<typename ByteT>
|
||||
bool basic_mmap<ByteT>::is_mapped() const noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return file_mapping_handle_ != INVALID_HANDLE_VALUE;
|
||||
@ -370,24 +385,8 @@ bool basic_mmap<CharT>::is_mapped() const noexcept
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::set_length(const size_type length) noexcept
|
||||
{
|
||||
if(length > this->length() - offset()) { throw std::invalid_argument(""); }
|
||||
num_bytes_ = to_byte_size(length);
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::set_offset(const size_type offset) noexcept
|
||||
{
|
||||
if(offset >= mapped_length()) { throw std::invalid_argument(""); }
|
||||
const auto diff = offset - this->offset();
|
||||
data_ += diff;
|
||||
num_bytes_ += -1 * to_byte_size(diff);
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::swap(basic_mmap<CharT>& other)
|
||||
template<typename ByteT>
|
||||
void basic_mmap<ByteT>::swap(basic_mmap<ByteT>& other)
|
||||
{
|
||||
if(this != &other)
|
||||
{
|
||||
@ -397,47 +396,47 @@ void basic_mmap<CharT>::swap(basic_mmap<CharT>& other)
|
||||
#ifdef _WIN32
|
||||
swap(file_mapping_handle_, other.file_mapping_handle_);
|
||||
#endif
|
||||
swap(num_bytes_, other.num_bytes_);
|
||||
swap(num_mapped_bytes_, other.num_mapped_bytes_);
|
||||
swap(length_, other.length_);
|
||||
swap(mapped_length_, other.mapped_length_);
|
||||
swap(is_handle_internal_, other.is_handle_internal_);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool operator==(const basic_mmap<CharT>& a, const basic_mmap<CharT>& b)
|
||||
template<typename ByteT>
|
||||
bool operator==(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b)
|
||||
{
|
||||
return a.data() == b.data()
|
||||
&& a.size() == b.size();
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool operator!=(const basic_mmap<CharT>& a, const basic_mmap<CharT>& b)
|
||||
template<typename ByteT>
|
||||
bool operator!=(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool operator<(const basic_mmap<CharT>& a, const basic_mmap<CharT>& b)
|
||||
template<typename ByteT>
|
||||
bool operator<(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b)
|
||||
{
|
||||
if(a.data() == b.data()) { return a.size() < b.size(); }
|
||||
return a.data() < b.data();
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool operator<=(const basic_mmap<CharT>& a, const basic_mmap<CharT>& b)
|
||||
template<typename ByteT>
|
||||
bool operator<=(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b)
|
||||
{
|
||||
return !(a > b);
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool operator>(const basic_mmap<CharT>& a, const basic_mmap<CharT>& b)
|
||||
template<typename ByteT>
|
||||
bool operator>(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b)
|
||||
{
|
||||
if(a.data() == b.data()) { return a.size() > b.size(); }
|
||||
return a.data() > b.data();
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool operator>=(const basic_mmap<CharT>& a, const basic_mmap<CharT>& b)
|
||||
template<typename ByteT>
|
||||
bool operator>=(const basic_mmap<ByteT>& a, const basic_mmap<ByteT>& b)
|
||||
{
|
||||
return !(a < b);
|
||||
}
|
||||
|
||||
@ -31,20 +31,20 @@ namespace mio {
|
||||
// memory mapping. The two possible values are `read` and `write`.
|
||||
using detail::access_mode;
|
||||
|
||||
// This value may be provided as the `num_bytes` 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.
|
||||
using detail::map_entire_file;
|
||||
|
||||
template<
|
||||
access_mode AccessMode,
|
||||
typename CharT
|
||||
typename ByteT
|
||||
> class basic_mmap
|
||||
{
|
||||
static_assert(AccessMode == access_mode::read
|
||||
|| AccessMode == access_mode::write,
|
||||
"AccessMode must be either read or write");
|
||||
|
||||
using impl_type = detail::basic_mmap<CharT>;
|
||||
using impl_type = detail::basic_mmap<ByteT>;
|
||||
impl_type impl_;
|
||||
|
||||
public:
|
||||
@ -71,36 +71,22 @@ public:
|
||||
basic_mmap() = default;
|
||||
|
||||
/**
|
||||
* `path` must be a path to an existing file, 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.
|
||||
* The same as invoking the `map` function, except any error that may occur while
|
||||
* establishing the mapping is thrown.
|
||||
*/
|
||||
template<typename String>
|
||||
basic_mmap(const String& path, const size_type offset, const size_type num_bytes)
|
||||
basic_mmap(const String& path, const size_type offset, const size_type length)
|
||||
{
|
||||
std::error_code error;
|
||||
map(path, offset, num_bytes, error);
|
||||
map(path, offset, length, error);
|
||||
if(error) { throw error; }
|
||||
}
|
||||
|
||||
/**
|
||||
* `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.
|
||||
* The same as invoking the `map` function, except any error that may occur while
|
||||
* establishing the mapping is thrown.
|
||||
*/
|
||||
basic_mmap(const handle_type handle, const size_type offset, const size_type num_bytes)
|
||||
basic_mmap(const handle_type handle, const size_type offset, const size_type length)
|
||||
{
|
||||
std::error_code error;
|
||||
map(handle, offset, length, error);
|
||||
@ -137,9 +123,9 @@ public:
|
||||
|
||||
/**
|
||||
* `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.
|
||||
* user requested to be mapped, while `mapped_length` returns the actual number of
|
||||
* bytes that were mapped which is a multiple of the underlying operating system's
|
||||
* page allocation granularity.
|
||||
*/
|
||||
size_type size() const noexcept { return impl_.length(); }
|
||||
size_type length() const noexcept { return impl_.length(); }
|
||||
@ -147,8 +133,7 @@ public:
|
||||
|
||||
/**
|
||||
* Returns the offset, relative to the file's start, at which the mapping was
|
||||
* requested to be created, expressed in multiples of `sizeof(CharT)` rather than
|
||||
* in bytes.
|
||||
* requested to be created.
|
||||
*/
|
||||
size_type offset() const noexcept { return impl_.offset(); }
|
||||
|
||||
@ -200,24 +185,6 @@ public:
|
||||
reference operator[](const size_type i) noexcept { return impl_[i]; }
|
||||
const_reference operator[](const size_type i) const noexcept { return impl_[i]; }
|
||||
|
||||
/**
|
||||
* These functions can be used to alter the _conceptual_ size of the mapping.
|
||||
* That is, the actual mapped memory region is not affected, just the conceptual
|
||||
* range of memory on which the accessor methods ('data', 'begin', `operator[]` etc)
|
||||
* operate.
|
||||
*
|
||||
* If `length` is larger than the number of bytes mapped minus the offset, an
|
||||
* std::invalid_argument exception is thrown.
|
||||
*
|
||||
* `length` must be the conceptual length of the mapping, that is, the number of
|
||||
* bytes mapped divided by `sizeof(CharT)`, i.e. the Container's size.
|
||||
*
|
||||
* If `offset` is larger than the number of bytes mapped, an std::invalid_argument
|
||||
* exception is thrown.
|
||||
*/
|
||||
void set_length(const size_type length) noexcept { impl_.set_length(length); }
|
||||
void set_offset(const size_type offset) noexcept { impl_.set_offset(offset); }
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -235,17 +202,14 @@ public:
|
||||
* byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
|
||||
* `offset` from the start of the file.
|
||||
*
|
||||
* `num_bytes` must be the number of bytes to map, regardless of the underlying
|
||||
* value_type's size. That is, if CharT is a wide char, the value returned by
|
||||
* the `size` and `length` methods is not the same as this `num_bytes` value (TODO
|
||||
* this can be confusing).
|
||||
* If it is `map_entire_file`, a mapping of the entire file is created.
|
||||
* `length` is the number of bytes to map. It may be `map_entire_file`, in which
|
||||
* case a mapping of the entire file is created.
|
||||
*/
|
||||
template<typename String>
|
||||
void map(const String& path, const size_type offset,
|
||||
const size_type num_bytes, std::error_code& error)
|
||||
const size_type length, std::error_code& error)
|
||||
{
|
||||
impl_.map(path, offset, num_bytes, AccessMode, error);
|
||||
impl_.map(path, offset, length, AccessMode, error);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -253,10 +217,9 @@ public:
|
||||
* 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.
|
||||
* `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.
|
||||
*
|
||||
* `offset` is the number of bytes, relative to the start of the file, where the
|
||||
* mapping should begin. When specifying it, there is no need to worry about
|
||||
@ -265,16 +228,13 @@ public:
|
||||
* byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
|
||||
* `offset` from the start of the file.
|
||||
*
|
||||
* `num_bytes` must be the number of bytes to map, regardless of the underlying
|
||||
* value_type's size. That is, if CharT is a wide char, the value returned by
|
||||
* the `size` and `length` methods is not the same as this `num_bytes` value (TODO
|
||||
* this can be confusing).
|
||||
* If it is `map_entire_file`, a mapping of the entire file is created.
|
||||
* `length` is the number of bytes to map. It may be `map_entire_file`, in which
|
||||
* case a mapping of the entire file is created.
|
||||
*/
|
||||
void map(const handle_type handle, const size_type offset,
|
||||
const size_type num_bytes, std::error_code& error)
|
||||
const size_type length, std::error_code& error)
|
||||
{
|
||||
impl_.map(handle, offset, num_bytes, AccessMode, error);
|
||||
impl_.map(handle, offset, length, AccessMode, error);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -336,15 +296,15 @@ public:
|
||||
* This is the basis for all read-only mmap objects and should be preferred over
|
||||
* directly using basic_mmap.
|
||||
*/
|
||||
template<typename CharT>
|
||||
using basic_mmap_source = basic_mmap<access_mode::read, CharT>;
|
||||
template<typename ByteT>
|
||||
using basic_mmap_source = basic_mmap<access_mode::read, ByteT>;
|
||||
|
||||
/**
|
||||
* This is the basis for all read-write mmap objects and should be preferred over
|
||||
* directly using basic_mmap.
|
||||
*/
|
||||
template<typename CharT>
|
||||
using basic_mmap_sink = basic_mmap<access_mode::write, CharT>;
|
||||
template<typename ByteT>
|
||||
using basic_mmap_sink = basic_mmap<access_mode::write, ByteT>;
|
||||
|
||||
/**
|
||||
* These aliases cover the most common use cases, both representing a raw byte stream
|
||||
@ -361,10 +321,10 @@ template<
|
||||
typename MMap,
|
||||
typename MappingToken
|
||||
> MMap make_mmap(const MappingToken& token,
|
||||
int64_t offset, int64_t num_bytes, std::error_code& error)
|
||||
int64_t offset, int64_t length, std::error_code& error)
|
||||
{
|
||||
MMap mmap;
|
||||
mmap.map(token, offset, num_bytes, error);
|
||||
mmap.map(token, offset, length, error);
|
||||
return mmap;
|
||||
}
|
||||
|
||||
@ -377,9 +337,9 @@ template<
|
||||
*/
|
||||
template<typename MappingToken>
|
||||
mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset,
|
||||
mmap_source::size_type num_bytes, std::error_code& error)
|
||||
mmap_source::size_type length, std::error_code& error)
|
||||
{
|
||||
return make_mmap<mmap_source>(token, offset, num_bytes, error);
|
||||
return make_mmap<mmap_source>(token, offset, length, error);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -391,9 +351,9 @@ mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type o
|
||||
*/
|
||||
template<typename MappingToken>
|
||||
mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset,
|
||||
mmap_sink::size_type num_bytes, std::error_code& error)
|
||||
mmap_sink::size_type length, std::error_code& error)
|
||||
{
|
||||
return make_mmap<mmap_sink>(token, offset, num_bytes, error);
|
||||
return make_mmap<mmap_sink>(token, offset, length, error);
|
||||
}
|
||||
|
||||
} // namespace mio
|
||||
|
||||
@ -40,10 +40,10 @@ namespace mio {
|
||||
*/
|
||||
template<
|
||||
access_mode AccessMode,
|
||||
typename CharT
|
||||
typename ByteT
|
||||
> class basic_shared_mmap
|
||||
{
|
||||
using impl_type = basic_mmap<AccessMode, CharT>;
|
||||
using impl_type = basic_mmap<AccessMode, ByteT>;
|
||||
std::shared_ptr<impl_type> pimpl_;
|
||||
|
||||
public:
|
||||
@ -92,15 +92,8 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* `path` must be a path to an existing file, 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.
|
||||
* The same as invoking the `map` function, except any error that may occur while
|
||||
* establishing the mapping is thrown.
|
||||
*/
|
||||
template<typename String>
|
||||
basic_shared_mmap(const String& path, const size_type offset, const size_type length)
|
||||
@ -111,15 +104,8 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* `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.
|
||||
* The same as invoking the `map` function, except any error that may occur while
|
||||
* establishing the mapping is thrown.
|
||||
*/
|
||||
basic_shared_mmap(const handle_type handle, const size_type offset, const size_type length)
|
||||
{
|
||||
@ -153,9 +139,9 @@ public:
|
||||
|
||||
/**
|
||||
* `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.
|
||||
* user requested to be mapped, while `mapped_length` returns the actual number of
|
||||
* bytes that were mapped which is a multiple of the underlying operating system's
|
||||
* page allocation granularity.
|
||||
*/
|
||||
size_type size() const noexcept { return pimpl_->length(); }
|
||||
size_type length() const noexcept { return pimpl_->length(); }
|
||||
@ -163,8 +149,7 @@ public:
|
||||
|
||||
/**
|
||||
* Returns the offset, relative to the file's start, at which the mapping was
|
||||
* requested to be created, expressed in multiples of `sizeof(CharT)` rather than
|
||||
* in bytes.
|
||||
* requested to be created.
|
||||
*/
|
||||
size_type offset() const noexcept { return pimpl_->offset(); }
|
||||
|
||||
@ -216,24 +201,6 @@ public:
|
||||
reference operator[](const size_type i) noexcept { return (*pimpl_)[i]; }
|
||||
const_reference operator[](const size_type i) const noexcept { return (*pimpl_)[i]; }
|
||||
|
||||
/**
|
||||
* These functions can be used to alter the _conceptual_ size of the mapping.
|
||||
* That is, the actual mapped memory region is not affected, just the conceptual
|
||||
* range of memory on which the accessor methods ('data', 'begin', `operator[]` etc)
|
||||
* operate.
|
||||
*
|
||||
* If `length` is larger than the number of bytes mapped minus the offset, an
|
||||
* std::invalid_argument exception is thrown.
|
||||
*
|
||||
* `length` must be the conceptual length of the mapping, that is, the number of
|
||||
* bytes mapped divided by `sizeof(CharT)`, i.e. the Container's size.
|
||||
*
|
||||
* If `offset` is larger than the number of bytes mapped, an std::invalid_argument
|
||||
* exception is thrown.
|
||||
*/
|
||||
void set_length(const size_type length) noexcept { pimpl_->set_length(length); }
|
||||
void set_offset(const size_type offset) noexcept { pimpl_->set_offset(offset); }
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -251,11 +218,8 @@ public:
|
||||
* byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
|
||||
* `offset` from the start of the file.
|
||||
*
|
||||
* `num_bytes` must be the number of bytes to map, regardless of the underlying
|
||||
* value_type's size. That is, if CharT is a wide char, the value returned by
|
||||
* the `size` and `length` methods is not the same as this `num_bytes` value (TODO
|
||||
* this can be confusing).
|
||||
* If it is `map_entire_file`, a mapping of the entire file is created.
|
||||
* `length` is the number of bytes to map. It may be `map_entire_file`, in which
|
||||
* case a mapping of the entire file is created.
|
||||
*/
|
||||
template<typename String>
|
||||
void map(const String& path, const size_type offset,
|
||||
@ -269,10 +233,9 @@ public:
|
||||
* 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.
|
||||
* `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.
|
||||
*
|
||||
* `offset` is the number of bytes, relative to the start of the file, where the
|
||||
* mapping should begin. When specifying it, there is no need to worry about
|
||||
@ -281,11 +244,8 @@ public:
|
||||
* byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
|
||||
* `offset` from the start of the file.
|
||||
*
|
||||
* `num_bytes` must be the number of bytes to map, regardless of the underlying
|
||||
* value_type's size. That is, if CharT is a wide char, the value returned by
|
||||
* the `size` and `length` methods is not the same as this `num_bytes` value (TODO
|
||||
* this can be confusing).
|
||||
* If it is `map_entire_file`, a mapping of the entire file is created.
|
||||
* `length` is the number of bytes to map. It may be `map_entire_file`, in which
|
||||
* case 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)
|
||||
@ -367,15 +327,15 @@ private:
|
||||
* This is the basis for all read-only mmap objects and should be preferred over
|
||||
* directly using basic_shared_mmap.
|
||||
*/
|
||||
template<typename CharT>
|
||||
using basic_shared_mmap_source = basic_shared_mmap<access_mode::read, CharT>;
|
||||
template<typename ByteT>
|
||||
using basic_shared_mmap_source = basic_shared_mmap<access_mode::read, ByteT>;
|
||||
|
||||
/**
|
||||
* This is the basis for all read-write mmap objects and should be preferred over
|
||||
* directly using basic_shared_mmap.
|
||||
*/
|
||||
template<typename CharT>
|
||||
using basic_shared_mmap_sink = basic_shared_mmap<access_mode::write, CharT>;
|
||||
template<typename ByteT>
|
||||
using basic_shared_mmap_sink = basic_shared_mmap<access_mode::write, ByteT>;
|
||||
|
||||
/**
|
||||
* These aliases cover the most common use cases, both representing a raw byte stream
|
||||
|
||||
49
test/example.cpp
Normal file
49
test/example.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include <mio/mmap.hpp>
|
||||
#include <system_error> // for std::error_code
|
||||
#include <cstdio> // for std::printf
|
||||
#include <cassert>
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Read-write memory map the whole file by using `map_entire_file` where the
|
||||
// length of the mapping is otherwise expected, with the factory method.
|
||||
std::error_code error;
|
||||
mio::mmap_sink rw_mmap = mio::make_mmap_sink(
|
||||
"file.txt", 0, mio::map_entire_file, error);
|
||||
if(error) { return handle_error(error); }
|
||||
|
||||
// Iterate through the mapped region just as if it were any other container, and
|
||||
// change each byte's value (since this is a read-write mapping).
|
||||
for(auto& b : rw_mmap) {
|
||||
b += 10;
|
||||
}
|
||||
|
||||
// Or just change one value with the subscript operator.
|
||||
const int answer_index = rw_mmap.size() / 2;
|
||||
rw_mmap[answer_index] = 42;
|
||||
|
||||
// Don't forget to flush changes to disk, which is NOT done by the destructor for
|
||||
// more explicit control of this potentially expensive operation.
|
||||
rw_mmap.sync(error);
|
||||
if(error) { handle_error(error); }
|
||||
|
||||
// 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
|
||||
// invoked.
|
||||
rw_mmap.unmap();
|
||||
|
||||
// Now create the same mapping, but in read-only mode.
|
||||
mio::mmap_source ro_mmap = mio::make_mmap_source(
|
||||
"file.txt", 0, mio::map_entire_file, error);
|
||||
if(error) { return handle_error(error); }
|
||||
|
||||
const int the_answer_to_everything = ro_mmap[answer_index];
|
||||
assert(the_answer_to_everything == 42);
|
||||
}
|
||||
@ -14,6 +14,12 @@ int handle_error(const std::error_code& error)
|
||||
return error.value();
|
||||
}
|
||||
|
||||
// Just make sure this compiles.
|
||||
#ifdef CXX17
|
||||
# include <cstddef>
|
||||
using mmap_source = mio::basic_mmap_source<std::byte>;
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
const char* path = "test-file";
|
||||
@ -98,15 +104,5 @@ int main()
|
||||
mio::shared_ummap_source _2;
|
||||
}
|
||||
|
||||
const auto page_size = mio::page_size();
|
||||
{
|
||||
// Now check if an mmap with a wider char type reports the correct size.
|
||||
using u16mmap_source = mio::basic_mmap_source<char16_t>;
|
||||
u16mmap_source wide_mmap = mio::make_mmap<u16mmap_source>(path, 0, page_size, error);
|
||||
if(error) { return handle_error(error); }
|
||||
|
||||
assert(wide_mmap.size() == page_size / 2);
|
||||
}
|
||||
|
||||
std::printf("all tests passed!\n");
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user