mirror of
https://github.com/vimpunk/mio.git
synced 2025-12-07 01:06:51 +08:00
first commit
This commit is contained in:
commit
4583a6529e
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
test/**
|
||||
!test/test.cpp
|
||||
146
include/mio/detail/basic_mmap.hpp
Normal file
146
include/mio/detail/basic_mmap.hpp
Normal file
@ -0,0 +1,146 @@
|
||||
#ifndef MIO_BASIC_MMAP_HEADER
|
||||
#define MIO_BASIC_MMAP_HEADER
|
||||
|
||||
#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
|
||||
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
|
||||
namespace mio {
|
||||
namespace detail {
|
||||
|
||||
size_t page_size();
|
||||
|
||||
/**
|
||||
* Most of the logic in establishing a memory mapping is the same for both read-only and
|
||||
* read-write mappings, so they both inherit from basic_mmap.
|
||||
*/
|
||||
template<typename CharT> struct basic_mmap
|
||||
{
|
||||
using value_type = CharT;
|
||||
using size_type = int64_t;
|
||||
using reference = value_type&;
|
||||
using const_reference = const reference;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const pointer;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
// TODO these don't seem to work with regular pointers
|
||||
//using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
//using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
#ifdef _WIN32
|
||||
using handle_type = HANDLE;
|
||||
#else
|
||||
using handle_type = int;
|
||||
#endif
|
||||
|
||||
enum class access_mode
|
||||
{
|
||||
read_only,
|
||||
read_write
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
// Points to the first requested byte, and not to the actual start of the mapping.
|
||||
pointer data_ = nullptr;
|
||||
|
||||
// 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 file mapping
|
||||
// handle.
|
||||
handle_type file_handle_ = INVALID_HANDLE_VALUE;
|
||||
#if defined(_WIN32)
|
||||
handle_type file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
||||
#endif
|
||||
|
||||
// Length requested by user, which may not be the length of the full mapping.
|
||||
size_type length_ = 0;
|
||||
size_type mapped_length_ = 0;
|
||||
|
||||
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();
|
||||
|
||||
/**
|
||||
* On *nix systems is_open and is_mapped are the same and don't actually say if
|
||||
* the file itself is open or closed, they only refer to the mapping. This is
|
||||
* because a mapping remains valid (as long as it's not unmapped) even if another
|
||||
* entity closes the file which is being mapped.
|
||||
*
|
||||
* On Windows, however, in order to map a file, both an active file handle and a
|
||||
* mapping handle is required, so is_open checks for a valid file handle, while
|
||||
* is_mapped checks for a valid mapping handle.
|
||||
*/
|
||||
bool is_open() const noexcept;
|
||||
bool is_mapped() const noexcept;
|
||||
bool empty() const noexcept { return length() == 0; }
|
||||
|
||||
/**
|
||||
* size/length returns the logical length (i.e. the one user requested), while
|
||||
* mapped_length returns the actual mapped length, which is usually a multiple of
|
||||
* the OS' page size.
|
||||
*/
|
||||
size_type size() const noexcept { return 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(); }
|
||||
|
||||
reference operator[](const size_type i) noexcept { return data_[i]; }
|
||||
const_reference operator[](const size_type i) const noexcept { return data_[i]; }
|
||||
|
||||
void map(const std::string& path, const size_type offset, const size_type length,
|
||||
const access_mode mode, std::error_code& error);
|
||||
void map(const handle_type handle, const size_type offset, const size_type length,
|
||||
const access_mode mode, std::error_code& error);
|
||||
|
||||
void unmap();
|
||||
|
||||
void sync(std::error_code& error);
|
||||
|
||||
private:
|
||||
|
||||
static handle_type open_file(const std::string& path,
|
||||
const access_mode mode, std::error_code& error);
|
||||
|
||||
pointer get_mapping_start() noexcept;
|
||||
|
||||
/** NOTE: file_handle_ must be valid. */
|
||||
size_type query_file_size(std::error_code& error) noexcept;
|
||||
|
||||
void map(const size_type offset, const size_type length,
|
||||
const access_mode mode, std::error_code& error);
|
||||
|
||||
void verify_file_handle(std::error_code& error) const noexcept;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mio
|
||||
|
||||
#include "basic_mmap.ipp"
|
||||
|
||||
#endif // MIO_BASIC_MMAP_HEADER
|
||||
336
include/mio/detail/basic_mmap.ipp
Normal file
336
include/mio/detail/basic_mmap.ipp
Normal file
@ -0,0 +1,336 @@
|
||||
#ifndef MIO_BASIC_MMAP_IMPL
|
||||
#define MIO_BASIC_MMAP_IMPL
|
||||
|
||||
#include "basic_mmap.hpp"
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
# include <fcntl.h>
|
||||
# include <sys/mman.h>
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
namespace mio {
|
||||
namespace detail {
|
||||
|
||||
#if defined(_WIN32)
|
||||
inline DWORD int64_high(int64_t n) noexcept
|
||||
{
|
||||
return n >> 32;
|
||||
}
|
||||
|
||||
inline DWORD int64_low(int64_t n) noexcept
|
||||
{
|
||||
return n & 0xFF'FF'FF'FF;
|
||||
}
|
||||
#endif
|
||||
|
||||
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 offset) noexcept
|
||||
{
|
||||
const static size_t page_size_ = page_size();
|
||||
// use integer division to round down to the nearest page alignment
|
||||
return offset / page_size_ * page_size_;
|
||||
}
|
||||
|
||||
inline std::error_code last_error() noexcept
|
||||
{
|
||||
std::error_code error;
|
||||
#ifdef _WIN32
|
||||
error.assign(GetLastError(), std::system_category());
|
||||
#else
|
||||
error.assign(errno, std::system_category());
|
||||
#endif
|
||||
return error;
|
||||
}
|
||||
|
||||
// -- basic_mmap --
|
||||
|
||||
template<typename CharT>
|
||||
basic_mmap<CharT>::~basic_mmap()
|
||||
{
|
||||
unmap();
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
basic_mmap<CharT>::basic_mmap(basic_mmap&& other)
|
||||
: data_(std::move(other.data_))
|
||||
, 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_))
|
||||
#endif
|
||||
{
|
||||
other.data_ = nullptr;
|
||||
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&& other)
|
||||
{
|
||||
if(this != &other)
|
||||
{
|
||||
data_ = std::move(other.data_);
|
||||
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_);
|
||||
#endif
|
||||
other.data_ = nullptr;
|
||||
other.length_ = other.mapped_length_ = 0;
|
||||
other.file_handle_ = INVALID_HANDLE_VALUE;
|
||||
#ifdef _WIN32
|
||||
other.file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
||||
#endif
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::map(const std::string& path, const size_type offset,
|
||||
const size_type length, const access_mode mode, std::error_code& error)
|
||||
{
|
||||
if(!path.empty() && !is_open())
|
||||
{
|
||||
error.clear();
|
||||
const auto handle = open_file(path, mode, error);
|
||||
if(error) { return; }
|
||||
map(handle, offset, length, mode, error);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
typename basic_mmap<CharT>::handle_type
|
||||
basic_mmap<CharT>::open_file(const std::string& path,
|
||||
const basic_mmap<CharT>::access_mode mode, std::error_code& error)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
const auto handle = ::CreateFile(path.c_str(),
|
||||
mode == basic_mmap<CharT>::access_mode::read_only
|
||||
? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
0);
|
||||
if(handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
error = last_error();
|
||||
}
|
||||
#else
|
||||
const auto handle = ::open(path.c_str(),
|
||||
mode == basic_mmap<CharT>::access_mode::read_only ? O_RDONLY : O_RDWR);
|
||||
if(handle == -1)
|
||||
{
|
||||
error = last_error();
|
||||
}
|
||||
#endif
|
||||
return handle;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::map(const handle_type handle, const size_type offset,
|
||||
const size_type length, const access_mode mode, std::error_code& error)
|
||||
{
|
||||
error.clear();
|
||||
if(handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
error = std::make_error_code(std::errc::bad_file_descriptor);
|
||||
return;
|
||||
}
|
||||
|
||||
file_handle_ = handle;
|
||||
|
||||
const auto file_size = query_file_size(error);
|
||||
if(error) { return; }
|
||||
|
||||
if(offset + length > file_size)
|
||||
{
|
||||
error = std::make_error_code(std::errc::invalid_argument);
|
||||
return;
|
||||
}
|
||||
|
||||
map(offset, length, mode, error);
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::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 length_to_map = offset - aligned_offset + length;
|
||||
#if defined(_WIN32)
|
||||
const size_type max_file_size = offset + length;
|
||||
file_mapping_handle_ = ::CreateFileMapping(
|
||||
file_handle_,
|
||||
0,
|
||||
mode == access_mode::read_only ? 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_only ? 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
|
||||
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,
|
||||
MAP_SHARED, // TODO do we want to share it?
|
||||
file_handle_,
|
||||
aligned_offset));
|
||||
if(mapping_start == MAP_FAILED)
|
||||
{
|
||||
error = last_error();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
data_ = mapping_start + offset - aligned_offset;
|
||||
length_ = length;
|
||||
mapped_length_ = length_to_map;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::sync(std::error_code& error)
|
||||
{
|
||||
error.clear();
|
||||
verify_file_handle(error);
|
||||
if(error) { return; }
|
||||
|
||||
if(data() != nullptr)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0
|
||||
|| ::FlushFileBuffers(file_handle_) == 0)
|
||||
#else
|
||||
if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0)
|
||||
#endif
|
||||
{
|
||||
error = last_error();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if(::FlushFileBuffers(file_handle_) == 0)
|
||||
{
|
||||
error = last_error();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::unmap()
|
||||
{
|
||||
// TODO do we care about errors here?
|
||||
if((data_ != nullptr) && (file_handle_ != INVALID_HANDLE_VALUE))
|
||||
{
|
||||
#ifdef _WIN32
|
||||
::UnmapViewOfFile(get_mapping_start());
|
||||
::CloseHandle(file_mapping_handle_);
|
||||
file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
::munmap(const_cast<pointer>(get_mapping_start()), mapped_length_);
|
||||
#endif
|
||||
}
|
||||
data_ = nullptr;
|
||||
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>::size_type
|
||||
basic_mmap<CharT>::query_file_size(std::error_code& error) noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
PLARGE_INTEGER file_size;
|
||||
if(::GetFileSizeEx(file_handle_, &file_size) == 0)
|
||||
{
|
||||
error = last_error();
|
||||
return 0;
|
||||
}
|
||||
return file_size;
|
||||
#else
|
||||
struct stat sbuf;
|
||||
if(::fstat(file_handle_, &sbuf) == -1)
|
||||
{
|
||||
error = last_error();
|
||||
return 0;
|
||||
}
|
||||
return sbuf.st_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
typename basic_mmap<CharT>::pointer basic_mmap<CharT>::get_mapping_start() noexcept
|
||||
{
|
||||
if(!data_) { return nullptr; }
|
||||
const auto offset = mapped_length_ - length_;
|
||||
return data_ - offset;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap<CharT>::verify_file_handle(std::error_code& error) const noexcept
|
||||
{
|
||||
if(!is_open() || !is_mapped())
|
||||
{
|
||||
error = std::make_error_code(std::errc::bad_file_descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return file_mapping_handle_ != INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
return is_open();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mio
|
||||
|
||||
#endif // MIO_BASIC_MMAP_IMPL
|
||||
81
include/mio/detail/mmap.ipp
Normal file
81
include/mio/detail/mmap.ipp
Normal file
@ -0,0 +1,81 @@
|
||||
#ifndef MIO_MMAP_IMPL
|
||||
#define MIO_MMAP_IMPL
|
||||
|
||||
#include "../mmap.hpp"
|
||||
|
||||
namespace mio {
|
||||
|
||||
// -- basic_mmap_source --
|
||||
|
||||
template<typename CharT>
|
||||
basic_mmap_source<CharT>::basic_mmap_source(const std::string& path,
|
||||
const size_type offset, const size_type length)
|
||||
{
|
||||
std::error_code error;
|
||||
map(path, offset, length, error);
|
||||
if(error) { throw error; }
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
basic_mmap_source<CharT>::basic_mmap_source(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; }
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap_source<CharT>::map(const std::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);
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap_source<CharT>::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);
|
||||
}
|
||||
|
||||
// -- basic_mmap_sink --
|
||||
|
||||
template<typename CharT>
|
||||
basic_mmap_sink<CharT>::basic_mmap_sink(const std::string& path,
|
||||
const size_type offset, const size_type length)
|
||||
{
|
||||
std::error_code error;
|
||||
map(path, offset, length, error);
|
||||
if(error) { throw error; }
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
basic_mmap_sink<CharT>::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; }
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap_sink<CharT>::map(const std::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);
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap_sink<CharT>::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);
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
void basic_mmap_sink<CharT>::sync(std::error_code& error) { impl_.sync(error); }
|
||||
|
||||
} // namespace mio
|
||||
|
||||
#endif // MIO_MMAP_IMPL
|
||||
185
include/mio/mmap.hpp
Normal file
185
include/mio/mmap.hpp
Normal file
@ -0,0 +1,185 @@
|
||||
#ifndef MIO_MMAP_HEADER
|
||||
#define MIO_MMAP_HEADER
|
||||
|
||||
#include "detail/basic_mmap.hpp"
|
||||
|
||||
namespace mio {
|
||||
|
||||
/**
|
||||
* When specifying a file to map, there is no need to worry about providing
|
||||
* offsets that are aligned with the operating system's page granularity, this is taken
|
||||
* care of within the class in both cases.
|
||||
*
|
||||
* Both classes have std::unique_ptr<> semantics, thus only a single entity may own
|
||||
* a mapping to a file at any given time.
|
||||
*
|
||||
* Remapping a file is possible, but unmap must be called before that.
|
||||
*
|
||||
* For now, both classes may only be used with an existing open file by providing the
|
||||
* file's handle.
|
||||
*
|
||||
* Both classes' destructors unmap the file. However, mmap_sink's destructor does not
|
||||
* sync the mapped file view to disk, this has to be done manually with a call tosink.
|
||||
*/
|
||||
|
||||
/** A read-only file memory mapping. */
|
||||
template<typename CharT> struct basic_mmap_source
|
||||
{
|
||||
using impl_type = detail::basic_mmap<CharT>;
|
||||
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;
|
||||
|
||||
private:
|
||||
impl_type impl_;
|
||||
public:
|
||||
|
||||
basic_mmap_source() = default;
|
||||
basic_mmap_source(const std::string& path,
|
||||
const size_type offset, const size_type length);
|
||||
basic_mmap_source(const handle_type handle,
|
||||
const size_type offset, const size_type length);
|
||||
|
||||
/**
|
||||
* On *nix systems is_open and is_mapped are the same and don't actually say if
|
||||
* the file itself is open or closed, they only refer to the mapping. This is
|
||||
* because a mapping remains valid (as long as it's not unmapped) even if another
|
||||
* entity closes the file which is being mapped.
|
||||
*
|
||||
* On Windows, however, in order to map a file, both an active file handle and a
|
||||
* mapping handle is required, so is_open checks for a valid file handle, while
|
||||
* is_mapped checks for a valid mapping handle.
|
||||
*/
|
||||
bool is_open() const noexcept { return impl_.is_open(); }
|
||||
bool is_mapped() const noexcept { return impl_.is_mapped(); }
|
||||
bool empty() const noexcept { return impl_.is_empty(); }
|
||||
|
||||
/**
|
||||
* size/length returns the logical length (i.e. the one user requested), while
|
||||
* mapped_length returns the actual mapped length, which is usually a multiple of
|
||||
* the OS' page size.
|
||||
*/
|
||||
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(); }
|
||||
|
||||
const_pointer data() const noexcept { return impl_.data(); }
|
||||
|
||||
const_iterator begin() const noexcept { return impl_.begin(); }
|
||||
const_iterator cbegin() const noexcept { return impl_.cbegin(); }
|
||||
const_iterator end() const noexcept { return impl_.end(); }
|
||||
const_iterator cend() const noexcept { return impl_.cend(); }
|
||||
|
||||
const_reference operator[](const size_type i) const noexcept { return impl_[i]; }
|
||||
|
||||
void map(const std::string& path, const size_type offset,
|
||||
const size_type length, std::error_code& error);
|
||||
void map(const handle_type handle, const size_type offset,
|
||||
const size_type length, std::error_code& error);
|
||||
|
||||
void unmap() { impl_.unmap(); }
|
||||
};
|
||||
|
||||
/** A read-write file memory mapping. */
|
||||
template<typename CharT> struct basic_mmap_sink
|
||||
{
|
||||
using impl_type = detail::basic_mmap<CharT>;
|
||||
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;
|
||||
|
||||
private:
|
||||
impl_type impl_;
|
||||
public:
|
||||
|
||||
basic_mmap_sink() = default;
|
||||
basic_mmap_sink(const std::string& path,
|
||||
const size_type offset, const size_type length);
|
||||
basic_mmap_sink(const handle_type handle,
|
||||
const size_type offset, const size_type length);
|
||||
|
||||
/**
|
||||
* On *nix systems is_open and is_mapped are the same and don't actually say if
|
||||
* the file itself is open or closed, they only refer to the mapping. This is
|
||||
* because a mapping remains valid (as long as it's not unmapped) even if another
|
||||
* entity closes the file which is being mapped.
|
||||
*
|
||||
* On Windows, however, in order to map a file, both an active file handle and a
|
||||
* mapping handle is required, so is_open checks for a valid file handle, while
|
||||
* is_mapped checks for a valid mapping handle.
|
||||
*/
|
||||
bool is_open() const noexcept { return impl_.is_open(); }
|
||||
bool is_mapped() const noexcept { return impl_.is_mapped(); }
|
||||
bool empty() const noexcept { return impl_.is_empty(); }
|
||||
|
||||
/**
|
||||
* size/length returns the logical length (i.e. the one user requested), while
|
||||
* mapped_length returns the actual mapped length, which is usually a multiple of
|
||||
* the OS' page size.
|
||||
*/
|
||||
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(); }
|
||||
|
||||
pointer data() noexcept { return impl_.data(); }
|
||||
const_pointer data() const noexcept { return impl_.data(); }
|
||||
|
||||
iterator begin() noexcept;
|
||||
const_iterator begin() const noexcept { return impl_.begin(); }
|
||||
const_iterator cbegin() const noexcept { return impl_.cbegin(); }
|
||||
|
||||
iterator end() noexcept;
|
||||
const_iterator end() const noexcept { return impl_.end(); }
|
||||
const_iterator cend() const noexcept { return impl_.cend(); }
|
||||
|
||||
reference operator[](const size_type i) noexcept { return impl_[i]; }
|
||||
const_reference operator[](const size_type i) const noexcept { return impl_[i]; }
|
||||
|
||||
void map(const std::string& path, const size_type offset,
|
||||
const size_type length, std::error_code& error);
|
||||
void map(const handle_type handle, const size_type offset,
|
||||
const size_type length, std::error_code& error);
|
||||
|
||||
/** Flushes the memory mapped page to disk. */
|
||||
void sync(std::error_code& error);
|
||||
|
||||
void unmap() { impl_.unmap(); }
|
||||
};
|
||||
|
||||
using mmap_sink = basic_mmap_sink<char>;
|
||||
using mmap_source = basic_mmap_source<char>;
|
||||
|
||||
using wmmap_sink = basic_mmap_sink<wchar_t>;
|
||||
using wmmap_source = basic_mmap_source<wchar_t>;
|
||||
|
||||
using u16mmap_sink = basic_mmap_sink<char16_t>;
|
||||
using u16mmap_source = basic_mmap_source<char16_t>;
|
||||
|
||||
using u32mmap_sink = basic_mmap_sink<char32_t>;
|
||||
using u32mmap_source = basic_mmap_source<char32_t>;
|
||||
|
||||
} // namespace mio
|
||||
|
||||
#include "detail/mmap.ipp"
|
||||
|
||||
#endif // MIO_MMAP_HEADER
|
||||
42
test/test.cpp
Normal file
42
test/test.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include "../include/mio/mmap.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <system_error>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* path = argc >= 2 ? argv[1] : ".";
|
||||
|
||||
std::error_code error;
|
||||
std::string buffer(0x4000 - 250, 'M');
|
||||
|
||||
std::ofstream file(path);
|
||||
file << buffer;
|
||||
file.close();
|
||||
|
||||
mio::mmap_source file_view;
|
||||
file_view.map(path, 0, buffer.size(), error);
|
||||
if(error)
|
||||
{
|
||||
const auto& errmsg = error.message();
|
||||
std::printf("error: %s, exiting...\n", errmsg.c_str());
|
||||
return error.value();
|
||||
}
|
||||
|
||||
assert(file_view.is_open());
|
||||
assert(file_view.is_mapped());
|
||||
assert(file_view.size() == buffer.size());
|
||||
|
||||
for(auto i = 0; i < buffer.size(); ++i)
|
||||
{
|
||||
if(file_view[i] != buffer[i])
|
||||
{
|
||||
std::printf("%ith byte mismatch: expected(%i) <> actual(%i)",
|
||||
i, buffer[i], file_view[i]);
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user