mirror of
https://github.com/vimpunk/mio.git
synced 2025-12-06 16:57:01 +08:00
further API changes and bugfixes
This commit is contained in:
parent
3a8722c27e
commit
2525a2e28b
@ -39,6 +39,8 @@ struct mmap
|
|||||||
using handle_type = int;
|
using handle_type = int;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static constexpr size_type use_full_file_size = 0;
|
||||||
|
|
||||||
enum class access_mode
|
enum class access_mode
|
||||||
{
|
{
|
||||||
read_only,
|
read_only,
|
||||||
@ -63,6 +65,12 @@ private:
|
|||||||
size_type length_ = 0;
|
size_type length_ = 0;
|
||||||
size_type mapped_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,
|
||||||
|
// but we must close it if we obtained it using the provided path. For this reason,
|
||||||
|
// this flag is used to determine when to close file_handle_.
|
||||||
|
bool is_handle_internal_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
mmap() = default;
|
mmap() = default;
|
||||||
@ -72,6 +80,9 @@ public:
|
|||||||
mmap& operator=(mmap&&);
|
mmap& operator=(mmap&&);
|
||||||
~mmap();
|
~mmap();
|
||||||
|
|
||||||
|
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;
|
||||||
bool is_mapped() const noexcept;
|
bool is_mapped() const noexcept;
|
||||||
bool empty() const noexcept { return length() == 0; }
|
bool empty() const noexcept { return length() == 0; }
|
||||||
@ -102,9 +113,13 @@ public:
|
|||||||
reference operator[](const size_type i) noexcept { return data_[i]; }
|
reference operator[](const size_type i) noexcept { return data_[i]; }
|
||||||
const_reference operator[](const size_type i) const noexcept { return data_[i]; }
|
const_reference operator[](const size_type i) const noexcept { return data_[i]; }
|
||||||
|
|
||||||
|
template<typename String>
|
||||||
|
void map(const 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,
|
void map(const handle_type handle, const size_type offset, const size_type length,
|
||||||
const access_mode mode, std::error_code& error);
|
const access_mode mode, std::error_code& error);
|
||||||
void unmap();
|
void unmap();
|
||||||
|
void close();
|
||||||
|
|
||||||
void sync(std::error_code& error);
|
void sync(std::error_code& error);
|
||||||
|
|
||||||
@ -117,19 +132,10 @@ private:
|
|||||||
|
|
||||||
pointer get_mapping_start() noexcept;
|
pointer get_mapping_start() noexcept;
|
||||||
|
|
||||||
/** NOTE: file_handle_ must be valid. */
|
void map(const size_type offset, size_type length,
|
||||||
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);
|
const access_mode mode, std::error_code& error);
|
||||||
|
|
||||||
void verify_file_handle(std::error_code& error) const noexcept;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Path>
|
|
||||||
mmap::handle_type open_file(const Path& path,
|
|
||||||
const mmap::access_mode mode, std::error_code& error);
|
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
} // namespace mio
|
} // namespace mio
|
||||||
|
|
||||||
|
|||||||
@ -2,17 +2,16 @@
|
|||||||
#define MIO_BASIC_MMAP_IMPL
|
#define MIO_BASIC_MMAP_IMPL
|
||||||
|
|
||||||
#include "mmap_impl.hpp"
|
#include "mmap_impl.hpp"
|
||||||
|
#include "type_traits.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <type_traits>
|
#include <cstdint>
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
# include <fcntl.h>
|
# include <fcntl.h>
|
||||||
# include <sys/mman.h>
|
# include <sys/mman.h>
|
||||||
# include <sys/stat.h>
|
# include <sys/stat.h>
|
||||||
# include <cassert>
|
|
||||||
# include <cstdint>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace mio {
|
namespace mio {
|
||||||
@ -48,7 +47,7 @@ inline size_t page_size()
|
|||||||
inline size_t make_page_aligned(size_t offset) noexcept
|
inline size_t make_page_aligned(size_t offset) noexcept
|
||||||
{
|
{
|
||||||
const static size_t page_size_ = page_size();
|
const static size_t page_size_ = page_size();
|
||||||
// use integer division to round down to the nearest page alignment
|
// Use integer division to round down to the nearest page alignment.
|
||||||
return offset / page_size_ * page_size_;
|
return offset / page_size_ * page_size_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,6 +62,58 @@ inline std::error_code last_error() noexcept
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Path>
|
||||||
|
mmap::handle_type open_file(const Path& path,
|
||||||
|
const mmap::access_mode mode, std::error_code& error)
|
||||||
|
{
|
||||||
|
error.clear();
|
||||||
|
if(detail::empty(path))
|
||||||
|
{
|
||||||
|
error = std::make_error_code(std::errc::invalid_argument);
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
#if defined(_WIN32)
|
||||||
|
const auto handle = ::CreateFile(c_str(path),
|
||||||
|
mode == mmap::access_mode::read_only
|
||||||
|
? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||||
|
0,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
0);
|
||||||
|
#else
|
||||||
|
const auto handle = ::open(c_str(path),
|
||||||
|
mode == mmap::access_mode::read_only ? O_RDONLY : O_RDWR);
|
||||||
|
#endif
|
||||||
|
if(handle == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
error = last_error();
|
||||||
|
}
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline mmap::size_type query_file_size(mmap::handle_type handle, std::error_code& error)
|
||||||
|
{
|
||||||
|
error.clear();
|
||||||
|
#ifdef _WIN32
|
||||||
|
LARGE_INTEGER file_size;
|
||||||
|
if(::GetFileSizeEx(handle, &file_size) == 0)
|
||||||
|
{
|
||||||
|
error = last_error();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return static_cast<mmap::size_type>(file_size.QuadPart);
|
||||||
|
#else
|
||||||
|
struct stat sbuf;
|
||||||
|
if(::fstat(handle, &sbuf) == -1)
|
||||||
|
{
|
||||||
|
error = last_error();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return sbuf.st_size;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// -- mmap --
|
// -- mmap --
|
||||||
|
|
||||||
inline mmap::~mmap()
|
inline mmap::~mmap()
|
||||||
@ -78,6 +129,7 @@ inline mmap::mmap(mmap&& other)
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
, file_mapping_handle_(std::move(other.file_mapping_handle_))
|
, file_mapping_handle_(std::move(other.file_mapping_handle_))
|
||||||
#endif
|
#endif
|
||||||
|
, is_handle_internal_(std::move(other.is_handle_internal_))
|
||||||
{
|
{
|
||||||
other.data_ = nullptr;
|
other.data_ = nullptr;
|
||||||
other.length_ = other.mapped_length_ = 0;
|
other.length_ = other.mapped_length_ = 0;
|
||||||
@ -91,6 +143,8 @@ inline mmap& mmap::operator=(mmap&& other)
|
|||||||
{
|
{
|
||||||
if(this != &other)
|
if(this != &other)
|
||||||
{
|
{
|
||||||
|
// First the existing mapping needs to be removed.
|
||||||
|
unmap();
|
||||||
data_ = std::move(other.data_);
|
data_ = std::move(other.data_);
|
||||||
length_ = std::move(other.length_);
|
length_ = std::move(other.length_);
|
||||||
mapped_length_ = std::move(other.mapped_length_);
|
mapped_length_ = std::move(other.mapped_length_);
|
||||||
@ -98,18 +152,52 @@ inline mmap& mmap::operator=(mmap&& other)
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
file_mapping_handle_ = std::move(other.file_mapping_handle_);
|
file_mapping_handle_ = std::move(other.file_mapping_handle_);
|
||||||
#endif
|
#endif
|
||||||
|
is_handle_internal_ = std::move(other.is_handle_internal_);
|
||||||
|
|
||||||
|
// The moved from 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.data_ = nullptr;
|
||||||
other.length_ = other.mapped_length_ = 0;
|
other.length_ = other.mapped_length_ = 0;
|
||||||
other.file_handle_ = INVALID_HANDLE_VALUE;
|
other.file_handle_ = INVALID_HANDLE_VALUE;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
other.file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
other.file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
||||||
#endif
|
#endif
|
||||||
|
other.is_handle_internal_ = false;
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline mmap::handle_type mmap::mapping_handle() const noexcept
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return file_mapping_handle_;
|
||||||
|
#else
|
||||||
|
return file_handle_;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename String>
|
||||||
|
void mmap::map(const String& path, const size_type offset, const size_type length,
|
||||||
|
const access_mode mode, std::error_code& error)
|
||||||
|
{
|
||||||
|
error.clear();
|
||||||
|
if(detail::empty(path))
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
// MUST be after the call to map, as that sets this to true.
|
||||||
|
is_handle_internal_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline void mmap::map(const handle_type handle, const size_type offset,
|
inline void mmap::map(const handle_type handle, const size_type offset,
|
||||||
const size_type length, const access_mode mode, std::error_code& error)
|
size_type length, const access_mode mode, std::error_code& error)
|
||||||
{
|
{
|
||||||
error.clear();
|
error.clear();
|
||||||
if(handle == INVALID_HANDLE_VALUE)
|
if(handle == INVALID_HANDLE_VALUE)
|
||||||
@ -118,17 +206,21 @@ inline void mmap::map(const handle_type handle, const size_type offset,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
file_handle_ = handle;
|
const auto file_size = query_file_size(handle, error);
|
||||||
|
|
||||||
const auto file_size = query_file_size(error);
|
|
||||||
if(error) { return; }
|
if(error) { return; }
|
||||||
|
|
||||||
if(offset + length > file_size)
|
if(length <= 0)
|
||||||
|
{
|
||||||
|
length = file_size;
|
||||||
|
}
|
||||||
|
else if(offset + length > file_size)
|
||||||
{
|
{
|
||||||
error = std::make_error_code(std::errc::invalid_argument);
|
error = std::make_error_code(std::errc::invalid_argument);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file_handle_ = handle;
|
||||||
|
is_handle_internal_ = false;
|
||||||
map(offset, length, mode, error);
|
map(offset, length, mode, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +257,7 @@ inline void mmap::map(const size_type offset, const size_type length,
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
const pointer mapping_start = static_cast<pointer>(::mmap(
|
const pointer mapping_start = static_cast<pointer>(::mmap(
|
||||||
0, // don't give hint as to where to map
|
0, // Don't give hint as to where to map.
|
||||||
length_to_map,
|
length_to_map,
|
||||||
mode == mmap::access_mode::read_only ? PROT_READ : PROT_WRITE,
|
mode == mmap::access_mode::read_only ? PROT_READ : PROT_WRITE,
|
||||||
MAP_SHARED, // TODO do we want to share it?
|
MAP_SHARED, // TODO do we want to share it?
|
||||||
@ -185,8 +277,11 @@ inline void mmap::map(const size_type offset, const size_type length,
|
|||||||
inline void mmap::sync(std::error_code& error)
|
inline void mmap::sync(std::error_code& error)
|
||||||
{
|
{
|
||||||
error.clear();
|
error.clear();
|
||||||
verify_file_handle(error);
|
if(!is_open())
|
||||||
if(error) { return; }
|
{
|
||||||
|
error = std::make_error_code(std::errc::bad_file_descriptor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(data() != nullptr)
|
if(data() != nullptr)
|
||||||
{
|
{
|
||||||
@ -211,17 +306,32 @@ inline void mmap::sync(std::error_code& error)
|
|||||||
|
|
||||||
inline void mmap::unmap()
|
inline void mmap::unmap()
|
||||||
{
|
{
|
||||||
|
if(!is_open()) { return; }
|
||||||
// TODO do we care about errors here?
|
// TODO do we care about errors here?
|
||||||
if((data_ != nullptr) && (file_handle_ != INVALID_HANDLE_VALUE))
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
if(is_mapped())
|
||||||
|
{
|
||||||
::UnmapViewOfFile(get_mapping_start());
|
::UnmapViewOfFile(get_mapping_start());
|
||||||
::CloseHandle(file_mapping_handle_);
|
::CloseHandle(file_mapping_handle_);
|
||||||
file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
file_mapping_handle_ = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
::munmap(const_cast<pointer>(get_mapping_start()), mapped_length_);
|
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,
|
||||||
|
// rather than an existing file handle), we need to close it, otherwise it must not
|
||||||
|
// be closed as it may still be used outside of this mmap instance.
|
||||||
|
if(is_handle_internal_)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
::CloseHandle(file_handle_);
|
||||||
|
#else
|
||||||
|
::close(file_handle_);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset fields to their default values.
|
||||||
data_ = nullptr;
|
data_ = nullptr;
|
||||||
length_ = mapped_length_ = 0;
|
length_ = mapped_length_ = 0;
|
||||||
file_handle_ = INVALID_HANDLE_VALUE;
|
file_handle_ = INVALID_HANDLE_VALUE;
|
||||||
@ -230,27 +340,6 @@ inline void mmap::unmap()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline mmap::size_type mmap::query_file_size(std::error_code& error) noexcept
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
LARGE_INTEGER file_size;
|
|
||||||
if(::GetFileSizeEx(file_handle_, &file_size) == 0)
|
|
||||||
{
|
|
||||||
error = last_error();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return static_cast<size_type>(file_size.QuadPart);
|
|
||||||
#else
|
|
||||||
struct stat sbuf;
|
|
||||||
if(::fstat(file_handle_, &sbuf) == -1)
|
|
||||||
{
|
|
||||||
error = last_error();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return sbuf.st_size;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
inline mmap::pointer mmap::get_mapping_start() noexcept
|
inline mmap::pointer mmap::get_mapping_start() noexcept
|
||||||
{
|
{
|
||||||
if(!data_) { return nullptr; }
|
if(!data_) { return nullptr; }
|
||||||
@ -258,14 +347,6 @@ inline mmap::pointer mmap::get_mapping_start() noexcept
|
|||||||
return data_ - offset;
|
return data_ - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void mmap::verify_file_handle(std::error_code& error) const noexcept
|
|
||||||
{
|
|
||||||
if(!is_open() || !is_mapped())
|
|
||||||
{
|
|
||||||
error = std::make_error_code(std::errc::bad_file_descriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool mmap::is_open() const noexcept
|
inline bool mmap::is_open() const noexcept
|
||||||
{
|
{
|
||||||
return file_handle_ != INVALID_HANDLE_VALUE;
|
return file_handle_ != INVALID_HANDLE_VALUE;
|
||||||
@ -292,6 +373,7 @@ inline void mmap::swap(mmap& other)
|
|||||||
#endif
|
#endif
|
||||||
swap(length_, other.length_);
|
swap(length_, other.length_);
|
||||||
swap(mapped_length_, other.mapped_length_);
|
swap(mapped_length_, other.mapped_length_);
|
||||||
|
swap(is_handle_internal_, other.is_handle_internal_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,53 +391,6 @@ inline bool operator!=(const mmap& a, const mmap& b)
|
|||||||
return !(a == b);
|
return !(a == b);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<
|
|
||||||
typename String,
|
|
||||||
typename = decltype(std::declval<String>().data()),
|
|
||||||
typename = typename std::enable_if<!std::is_same<const char*, String>::value>::type
|
|
||||||
> const char* c_str(const String& path)
|
|
||||||
{
|
|
||||||
return path.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<
|
|
||||||
typename String,
|
|
||||||
typename = typename std::enable_if<
|
|
||||||
std::is_same<const char*, typename std::decay<String>::type>::value
|
|
||||||
>::type
|
|
||||||
> const char* c_str(String path)
|
|
||||||
{
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Path>
|
|
||||||
mmap::handle_type open_file(const Path& path,
|
|
||||||
const mmap::access_mode mode, std::error_code& error)
|
|
||||||
{
|
|
||||||
#if defined(_WIN32)
|
|
||||||
const auto handle = ::CreateFile(c_str(path),
|
|
||||||
mode == mmap::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(c_str(path),
|
|
||||||
mode == mmap::access_mode::read_only ? O_RDONLY : O_RDWR);
|
|
||||||
if(handle == -1)
|
|
||||||
{
|
|
||||||
error = last_error();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
} // namespace mio
|
} // namespace mio
|
||||||
|
|
||||||
|
|||||||
53
include/mio/detail/type_traits.hpp
Normal file
53
include/mio/detail/type_traits.hpp
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#ifndef MIO_TYPE_TRAITS_HEADER
|
||||||
|
#define MIO_TYPE_TRAITS_HEADER
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace mio {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<typename String> struct is_c_str
|
||||||
|
{
|
||||||
|
static constexpr bool value = std::is_same<
|
||||||
|
const char*, typename std::decay<String>::type
|
||||||
|
>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename String,
|
||||||
|
typename = decltype(std::declval<String>().data()),
|
||||||
|
typename = typename std::enable_if<!is_c_str<String>::value>::type
|
||||||
|
> const char* c_str(const String& path)
|
||||||
|
{
|
||||||
|
return path.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename String,
|
||||||
|
typename = decltype(std::declval<String>().empty()),
|
||||||
|
typename = typename std::enable_if<!is_c_str<String>::value>::type
|
||||||
|
> bool empty(const String& path)
|
||||||
|
{
|
||||||
|
return path.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename String,
|
||||||
|
typename = typename std::enable_if<is_c_str<String>::value>::type
|
||||||
|
> const char* c_str(String path)
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename String,
|
||||||
|
typename = typename std::enable_if<is_c_str<String>::value>::type
|
||||||
|
> bool empty(String path)
|
||||||
|
{
|
||||||
|
return !path || (*path == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace mio
|
||||||
|
|
||||||
|
#endif // MIO_TYPE_TRAITS_HEADER
|
||||||
@ -2,24 +2,11 @@
|
|||||||
#define MIO_MMAP_HEADER
|
#define MIO_MMAP_HEADER
|
||||||
|
|
||||||
#include "detail/mmap_impl.hpp"
|
#include "detail/mmap_impl.hpp"
|
||||||
|
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
|
||||||
namespace mio {
|
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 single-ownership semantics, and transferring ownership may be
|
|
||||||
* accomplished by moving the mmap instance.
|
|
||||||
*
|
|
||||||
* Remapping a file is possible, but unmap must be called before that.
|
|
||||||
*
|
|
||||||
* 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. */
|
/** A read-only file memory mapping. */
|
||||||
class mmap_source
|
class mmap_source
|
||||||
{
|
{
|
||||||
@ -42,6 +29,17 @@ public:
|
|||||||
using iterator_category = impl_type::iterator_category;
|
using iterator_category = impl_type::iterator_category;
|
||||||
using handle_type = impl_type::handle_type;
|
using handle_type = 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.
|
||||||
|
*/
|
||||||
mmap_source() = default;
|
mmap_source() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,27 +61,31 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `mmap_source` has single-ownership semantics, so transferring ownership may only
|
* This class has single-ownership semantics, so transferring ownership may only be
|
||||||
* be accomplished by moving the instance.
|
* accomplished by moving the object.
|
||||||
*/
|
*/
|
||||||
mmap_source(const mmap_source&) = delete;
|
|
||||||
mmap_source(mmap_source&&) = default;
|
mmap_source(mmap_source&&) = default;
|
||||||
|
mmap_source& operator=(mmap_source&&) = default;
|
||||||
|
|
||||||
/** The destructor invokes unmap. */
|
/** The destructor invokes unmap. */
|
||||||
~mmap_source() = default;
|
~mmap_source() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On UNIX systems `is_open` and `is_mapped` are the same and don't actually say if
|
* On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows,
|
||||||
* the file itself is open or closed, they only refer to the mapping. This is
|
* however, a mapped region of a file gets its own handle, which is returned by
|
||||||
* because a mapping remains valid (as long as it's not unmapped) even if another
|
* 'mapping_handle'.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
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(); }
|
bool is_open() const noexcept { return impl_.is_open(); }
|
||||||
bool is_mapped() const noexcept { return impl_.is_mapped(); }
|
|
||||||
|
/**
|
||||||
|
* 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(); }
|
bool empty() const noexcept { return impl_.empty(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,13 +122,40 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a reference to the `i`th byte from the first requested byte (as returned
|
* Returns a reference to the `i`th byte from the first requested byte (as returned
|
||||||
* by `data`). If this is invoked when no valid memory mapping has been established
|
* by `data`). If this is invoked when no valid memory mapping has been created
|
||||||
* prior to this call, undefined behaviour ensues.
|
* prior to this call, undefined behaviour ensues.
|
||||||
*/
|
*/
|
||||||
const_reference operator[](const size_type i) const noexcept { return impl_[i]; }
|
const_reference operator[](const size_type i) const noexcept { return impl_[i]; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Establishes a read-only memory mapping.
|
* 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
|
* `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
|
* requested region. Upon failure, `error` is set to indicate the reason and the
|
||||||
@ -137,6 +166,8 @@ public:
|
|||||||
* This is adjusted by the implementation such that the first requested byte (as
|
* 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`
|
* returned by `data` or `begin`), so long as `offset` is valid, will be at `offset`
|
||||||
* from the start of the file.
|
* 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,
|
void map(const handle_type handle, const size_type offset,
|
||||||
const size_type length, std::error_code& error)
|
const size_type length, std::error_code& error)
|
||||||
@ -145,9 +176,13 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a valid memory mapping has been established prior to this call, this call
|
* If a valid memory mapping has been created prior to this call, this call
|
||||||
* instructs the kernel to unmap the memory region and dissasociate this object
|
* instructs the kernel to unmap the memory region and disassociate this object
|
||||||
* from the file.
|
* 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 unmap() { impl_.unmap(); }
|
||||||
|
|
||||||
@ -186,6 +221,17 @@ public:
|
|||||||
using iterator_category = impl_type::iterator_category;
|
using iterator_category = impl_type::iterator_category;
|
||||||
using handle_type = impl_type::handle_type;
|
using handle_type = 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.
|
||||||
|
*/
|
||||||
mmap_sink() = default;
|
mmap_sink() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -207,31 +253,35 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `mmap_sink` has single-ownership semantics, so transferring ownership may only
|
* This class has single-ownership semantics, so transferring ownership may only be
|
||||||
* be accomplished by moving the instance.
|
* accomplished by moving the object.
|
||||||
*/
|
*/
|
||||||
mmap_sink(const mmap_sink&) = delete;
|
|
||||||
mmap_sink(mmap_sink&&) = default;
|
mmap_sink(mmap_sink&&) = default;
|
||||||
|
mmap_sink& operator=(mmap_sink&&) = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The destructor invokes unmap, but does NOT invoke `sync`. Thus, if changes have
|
* The destructor invokes unmap, but does NOT invoke `sync`. Thus, if the mapped
|
||||||
* been made to the mapped region, `sync` needs to be called in order to persist
|
* region has been written to, `sync` needs to be called in order to persist the
|
||||||
* any writes to disk.
|
* changes to disk.
|
||||||
*/
|
*/
|
||||||
~mmap_sink() = default;
|
~mmap_sink() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On UNIX systems `is_open` and `is_mapped` are the same and don't actually say if
|
* On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows,
|
||||||
* the file itself is open or closed, they only refer to the mapping. This is
|
* however, a mapped region of a file gets its own handle, which is returned by
|
||||||
* because a mapping remains valid (as long as it's not unmapped) even if another
|
* 'mapping_handle'.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
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(); }
|
bool is_open() const noexcept { return impl_.is_open(); }
|
||||||
bool is_mapped() const noexcept { return impl_.is_mapped(); }
|
|
||||||
|
/**
|
||||||
|
* 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(); }
|
bool empty() const noexcept { return impl_.empty(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -273,14 +323,41 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a reference to the `i`th byte from the first requested byte (as returned
|
* Returns a reference to the `i`th byte from the first requested byte (as returned
|
||||||
* by `data`). If this is invoked when no valid memory mapping has been established
|
* by `data`). If this is invoked when no valid memory mapping has been created
|
||||||
* prior to this call, undefined behaviour ensues.
|
* prior to this call, undefined behaviour ensues.
|
||||||
*/
|
*/
|
||||||
reference operator[](const size_type i) noexcept { return impl_[i]; }
|
reference operator[](const size_type i) noexcept { return impl_[i]; }
|
||||||
const_reference operator[](const size_type i) const noexcept { return impl_[i]; }
|
const_reference operator[](const size_type i) const noexcept { return impl_[i]; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Establishes a read-only memory mapping.
|
* Establishes a read-write 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-write 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
|
* `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
|
* requested region. Upon failure, `error` is set to indicate the reason and the
|
||||||
@ -291,6 +368,8 @@ public:
|
|||||||
* This is adjusted by the implementation such that the first requested byte (as
|
* 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`
|
* returned by `data` or `begin`), so long as `offset` is valid, will be at `offset`
|
||||||
* from the start of the file.
|
* 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,
|
void map(const handle_type handle, const size_type offset,
|
||||||
const size_type length, std::error_code& error)
|
const size_type length, std::error_code& error)
|
||||||
@ -299,9 +378,13 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a valid memory mapping has been established prior to this call, this call
|
* If a valid memory mapping has been created prior to this call, this call
|
||||||
* instructs the kernel to unmap the memory region and dissasociate this object
|
* instructs the kernel to unmap the memory region and disassociate this object
|
||||||
* from the file.
|
* 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 unmap() { impl_.unmap(); }
|
||||||
|
|
||||||
@ -322,39 +405,34 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Since `mmap_source` works on the file descriptor/handle level of abstraction, a
|
* Convenience factory method.
|
||||||
* factory method is provided for the case when a file needs to be mapped using a file
|
|
||||||
* path.
|
|
||||||
*
|
*
|
||||||
* Path may be `std::string`, `std::string_view`, `const char*`,
|
* MappingToken may be a String (`std::string`, `std::string_view`, `const char*`,
|
||||||
* `std::filesystem::path`, `std::vector<char>`, or similar.
|
* `std::filesystem::path`, `std::vector<char>`, or similar), or a
|
||||||
|
* mmap_source::file_handle.
|
||||||
*/
|
*/
|
||||||
template<typename Path>
|
template<typename MappingToken>
|
||||||
mmap_source make_mmap_source(const Path& path, mmap_source::size_type offset,
|
mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset,
|
||||||
mmap_source::size_type length, std::error_code& error)
|
mmap_source::size_type length, std::error_code& error)
|
||||||
{
|
{
|
||||||
const auto handle = detail::open_file(path,
|
|
||||||
detail::mmap::access_mode::read_only, error);
|
|
||||||
mmap_source mmap;
|
mmap_source mmap;
|
||||||
if(!error) { mmap.map(handle, offset, length, error); }
|
mmap.map(token, offset, length, error);
|
||||||
return mmap;
|
return mmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Since `mmap_sink` works on the file descriptor/handle level of abstraction, a factory
|
* Convenience factory method.
|
||||||
* method is provided for the case when a file needs to be mapped using a file path.
|
|
||||||
*
|
*
|
||||||
* Path may be `std::string`, `std::string_view`, `const char*`,
|
* MappingToken may be a String (`std::string`, `std::string_view`, `const char*`,
|
||||||
* `std::filesystem::path`, `std::vector<char>`, or similar.
|
* `std::filesystem::path`, `std::vector<char>`, or similar), or a
|
||||||
|
* mmap_sink::file_handle.
|
||||||
*/
|
*/
|
||||||
template<typename Path>
|
template<typename MappingToken>
|
||||||
mmap_sink make_mmap_sink(const Path& path, mmap_sink::size_type offset,
|
mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset,
|
||||||
mmap_sink::size_type length, std::error_code& error)
|
mmap_sink::size_type length, std::error_code& error)
|
||||||
{
|
{
|
||||||
const auto handle = detail::open_file(path,
|
|
||||||
detail::mmap::access_mode::read_write, error);
|
|
||||||
mmap_sink mmap;
|
mmap_sink mmap;
|
||||||
if(!error) { mmap.map(handle, offset, length, error); }
|
mmap.map(token, offset, length, error);
|
||||||
return mmap;
|
return mmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,27 +8,33 @@
|
|||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
const char* test_file_name = "test-file";
|
// Both are 40 bytes on UNIX, 48 on Windows (TODO verify).
|
||||||
|
//std::printf("sizeof(mio::mmap_source) = %i bytes\n", sizeof(mio::mmap_source));
|
||||||
|
//std::printf("sizeof(mio::mmap_sink) = %i bytes\n", sizeof(mio::mmap_source));
|
||||||
|
|
||||||
|
const char* file_path = "test-file";
|
||||||
|
|
||||||
|
// Fill buffer, then write it to file.
|
||||||
std::string buffer(0x4000 - 250, 'M');
|
std::string buffer(0x4000 - 250, 'M');
|
||||||
|
std::ofstream file(file_path);
|
||||||
std::ofstream file(test_file_name);
|
|
||||||
file << buffer;
|
file << buffer;
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
|
// Map the region of the file to which buffer was written.
|
||||||
std::error_code error;
|
std::error_code error;
|
||||||
mio::mmap_source file_view = mio::make_mmap_source(test_file_name, 0, buffer.size(), error);
|
mio::mmap_source file_view = mio::make_mmap_source(file_path,
|
||||||
|
0, mio::mmap_source::use_full_file_size, error);
|
||||||
if(error)
|
if(error)
|
||||||
{
|
{
|
||||||
const auto& errmsg = error.message();
|
const auto& errmsg = error.message();
|
||||||
std::printf("error: %s, exiting...\n", errmsg.c_str());
|
std::printf("error mapping file: %s, exiting...\n", errmsg.c_str());
|
||||||
return error.value();
|
return error.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(file_view.is_open());
|
assert(file_view.is_open());
|
||||||
assert(file_view.is_mapped());
|
|
||||||
assert(file_view.size() == buffer.size());
|
assert(file_view.size() == buffer.size());
|
||||||
|
|
||||||
|
// Then verify that mmap's bytes correspond to that of buffer.
|
||||||
for(auto i = 0; i < buffer.size(); ++i)
|
for(auto i = 0; i < buffer.size(); ++i)
|
||||||
{
|
{
|
||||||
if(file_view[i] != buffer[i])
|
if(file_view[i] != buffer[i])
|
||||||
@ -39,9 +45,39 @@ int main()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// see if mapping an invalid file results in an error
|
#define CHECK_INVALID_MMAP(m) do { \
|
||||||
mio::make_mmap_source("garbage-that-hopefully-doesn't exist", 0, 0, error);
|
assert(error); \
|
||||||
assert(error);
|
assert(m.empty()); \
|
||||||
|
assert(!m.is_open()); \
|
||||||
|
error.clear(); } while(0)
|
||||||
|
|
||||||
|
mio::mmap_source m;
|
||||||
|
|
||||||
|
// FIXME move assignment DOES NOT WORK!
|
||||||
|
// See if mapping an invalid file results in an error.
|
||||||
|
m = mio::make_mmap_source("garbage-that-hopefully-doesnt-exist", 0, 0, error);
|
||||||
|
CHECK_INVALID_MMAP(m);
|
||||||
|
|
||||||
|
// Empty path?
|
||||||
|
m = mio::make_mmap_source(static_cast<const char*>(0), 0, 0, error);
|
||||||
|
CHECK_INVALID_MMAP(m);
|
||||||
|
m = mio::make_mmap_source(std::string(), 0, 0, error);
|
||||||
|
CHECK_INVALID_MMAP(m);
|
||||||
|
|
||||||
|
// Invalid handle?
|
||||||
|
m = mio::make_mmap_source(
|
||||||
|
INVALID_HANDLE_VALUE/*Psst... This is an implementation detail!*/, 0, 0, error);
|
||||||
|
CHECK_INVALID_MMAP(m);
|
||||||
|
|
||||||
|
// Invalid offset?
|
||||||
|
m = mio::make_mmap_source(file_path, 100 * buffer.size(), buffer.size(), error);
|
||||||
|
CHECK_INVALID_MMAP(m);
|
||||||
|
|
||||||
std::printf("all tests passed!\n");
|
std::printf("all tests passed!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO consider the following API: type safe length and offset parameters, as these
|
||||||
|
// two are easily interchanged due to the same type, with the optional feature of
|
||||||
|
// arbitrary ordering.
|
||||||
|
//mio::mmap_source file_view = mio::make_mmap_source(file_path,
|
||||||
|
// mio::offset(0), mio::length(buffer.size()), error);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user