Files
boost_beast/beast/nudb/win32_file.h
T

463 lines
10 KiB
C++

//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2014, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_NUDB_DETAIL_WIN32_FILE_H_INCLUDED
#define BEAST_NUDB_DETAIL_WIN32_FILE_H_INCLUDED
#include <beast/nudb/common.h>
#include <cassert>
#include <string>
#ifndef BEAST_NUDB_WIN32_FILE
# ifdef _MSC_VER
# define BEAST_NUDB_WIN32_FILE 1
# else
# define BEAST_NUDB_WIN32_FILE 0
# endif
#endif
#if BEAST_NUDB_WIN32_FILE
#pragma push_macro("NOMINMAX")
#pragma push_macro("UNICODE")
#pragma push_macro("STRICT")
# ifndef NOMINMAX
# define NOMINMAX
# endif
# ifndef UNICODE
# define UNICODE
# endif
# ifndef STRICT
# define STRICT
# endif
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <Windows.h>
#pragma pop_macro("STRICT")
#pragma pop_macro("UNICODE")
#pragma pop_macro("NOMINMAX")
#endif
namespace beast {
namespace nudb {
#if BEAST_NUDB_WIN32_FILE
namespace detail {
// Win32 error code
class file_win32_error
: public file_error
{
public:
explicit
file_win32_error (char const* m,
DWORD dwError = ::GetLastError())
: file_error (std::string("nudb: ") + m +
", " + text(dwError))
{
}
explicit
file_win32_error (std::string const& m,
DWORD dwError = ::GetLastError())
: file_error (std::string("nudb: ") + m +
", " + text(dwError))
{
}
private:
template <class = void>
static
std::string
text (DWORD dwError);
};
template <class>
std::string
file_win32_error::text (DWORD dwError)
{
LPSTR buf = nullptr;
size_t const size = FormatMessageA (
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dwError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&buf,
0,
NULL);
std::string s;
if (size)
{
s.append(buf, size);
LocalFree (buf);
}
else
{
s = "error " + std::to_string(dwError);
}
return s;
}
//------------------------------------------------------------------------------
template <class = void>
class win32_file
{
private:
HANDLE hf_ = INVALID_HANDLE_VALUE;
public:
win32_file() = default;
win32_file (win32_file const&) = delete;
win32_file& operator= (win32_file const&) = delete;
~win32_file();
win32_file (win32_file&&);
win32_file&
operator= (win32_file&& other);
bool
is_open() const
{
return hf_ != INVALID_HANDLE_VALUE;
}
void
close();
// Returns:
// `false` if the file already exists
// `true` on success, else throws
//
bool
create (file_mode mode, std::string const& path);
// Returns:
// `false` if the file doesnt exist
// `true` on success, else throws
//
bool
open (file_mode mode, std::string const& path);
// Effects:
// Removes the file from the file system.
//
// Throws:
// Throws is an error occurs.
//
// Returns:
// `true` if the file was erased
// `false` if the file was not present
//
static
bool
erase (path_type const& path);
// Returns:
// Current file size in bytes measured by operating system
// Requires:
// is_open() == true
//
std::size_t
actual_size() const;
void
read (std::size_t offset,
void* buffer, std::size_t bytes);
void
write (std::size_t offset,
void const* buffer, std::size_t bytes);
void
sync();
void
trunc (std::size_t length);
private:
static
std::pair<DWORD, DWORD>
flags (file_mode mode);
};
template <class _>
win32_file<_>::~win32_file()
{
close();
}
template <class _>
win32_file<_>::win32_file (win32_file&& other)
: hf_ (other.hf_)
{
other.hf_ = INVALID_HANDLE_VALUE;
}
template <class _>
win32_file<_>&
win32_file<_>::operator= (win32_file&& other)
{
if (&other == this)
return *this;
close();
hf_ = other.hf_;
other.hf_ = INVALID_HANDLE_VALUE;
return *this;
}
template <class _>
void
win32_file<_>::close()
{
if (hf_ != INVALID_HANDLE_VALUE)
{
::CloseHandle(hf_);
hf_ = INVALID_HANDLE_VALUE;
}
}
template <class _>
bool
win32_file<_>::create (file_mode mode,
std::string const& path)
{
assert(! is_open());
auto const f = flags(mode);
hf_ = ::CreateFileA (path.c_str(),
f.first,
0,
NULL,
CREATE_NEW,
f.second,
NULL);
if (hf_ == INVALID_HANDLE_VALUE)
{
DWORD const dwError = ::GetLastError();
if (dwError != ERROR_FILE_EXISTS)
throw file_win32_error(
"create file", dwError);
return false;
}
return true;
}
template <class _>
bool
win32_file<_>::open (file_mode mode,
std::string const& path)
{
assert(! is_open());
auto const f = flags(mode);
hf_ = ::CreateFileA (path.c_str(),
f.first,
0,
NULL,
OPEN_EXISTING,
f.second,
NULL);
if (hf_ == INVALID_HANDLE_VALUE)
{
DWORD const dwError = ::GetLastError();
if (dwError != ERROR_FILE_NOT_FOUND &&
dwError != ERROR_PATH_NOT_FOUND)
throw file_win32_error(
"open file", dwError);
return false;
}
return true;
}
template <class _>
bool
win32_file<_>::erase (path_type const& path)
{
BOOL const bSuccess =
::DeleteFileA(path.c_str());
if (! bSuccess)
{
DWORD dwError = ::GetLastError();
if (dwError != ERROR_FILE_NOT_FOUND &&
dwError != ERROR_PATH_NOT_FOUND)
throw file_win32_error(
"erase file");
return false;
}
return true;
}
// Return: Current file size in bytes measured by operating system
template <class _>
std::size_t
win32_file<_>::actual_size() const
{
assert(is_open());
LARGE_INTEGER fileSize;
if (! ::GetFileSizeEx(hf_, &fileSize))
throw file_win32_error(
"size file");
return static_cast<std::size_t>(fileSize.QuadPart);
}
template <class _>
void
win32_file<_>::read (std::size_t offset,
void* buffer, std::size_t bytes)
{
while(bytes > 0)
{
DWORD bytesRead;
LARGE_INTEGER li;
li.QuadPart = static_cast<LONGLONG>(offset);
OVERLAPPED ov;
ov.Offset = li.LowPart;
ov.OffsetHigh = li.HighPart;
ov.hEvent = NULL;
BOOL const bSuccess = ::ReadFile(
hf_, buffer, bytes, &bytesRead, &ov);
if (! bSuccess)
{
DWORD const dwError = ::GetLastError();
if (dwError != ERROR_HANDLE_EOF)
throw file_win32_error(
"read file", dwError);
throw file_short_read_error();
}
if (bytesRead == 0)
throw file_short_read_error();
offset += bytesRead;
bytes -= bytesRead;
buffer = reinterpret_cast<char*>(
buffer) + bytesRead;
}
}
template <class _>
void
win32_file<_>::write (std::size_t offset,
void const* buffer, std::size_t bytes)
{
while(bytes > 0)
{
LARGE_INTEGER li;
li.QuadPart = static_cast<LONGLONG>(offset);
OVERLAPPED ov;
ov.Offset = li.LowPart;
ov.OffsetHigh = li.HighPart;
ov.hEvent = NULL;
DWORD bytesWritten;
BOOL const bSuccess = ::WriteFile(
hf_, buffer, bytes, &bytesWritten, &ov);
if (! bSuccess)
throw file_win32_error(
"write file");
if (bytesWritten == 0)
throw file_short_write_error();
offset += bytesWritten;
bytes -= bytesWritten;
buffer = reinterpret_cast<
char const*>(buffer) +
bytesWritten;
}
}
template <class _>
void
win32_file<_>::sync()
{
BOOL const bSuccess =
::FlushFileBuffers(hf_);
if (! bSuccess)
throw file_win32_error(
"sync file");
}
template <class _>
void
win32_file<_>::trunc (std::size_t length)
{
LARGE_INTEGER li;
li.QuadPart = length;
BOOL bSuccess;
bSuccess = ::SetFilePointerEx(
hf_, li, NULL, FILE_BEGIN);
if (bSuccess)
bSuccess = SetEndOfFile(hf_);
if (! bSuccess)
throw file_win32_error(
"trunc file");
}
template <class _>
std::pair<DWORD, DWORD>
win32_file<_>::flags (file_mode mode)
{
std::pair<DWORD, DWORD> result(0, 0);
switch (mode)
{
case file_mode::scan:
result.first =
GENERIC_READ;
result.second =
FILE_FLAG_SEQUENTIAL_SCAN;
break;
case file_mode::read:
result.first =
GENERIC_READ;
result.second =
FILE_FLAG_RANDOM_ACCESS;
break;
case file_mode::append:
result.first =
GENERIC_READ | GENERIC_WRITE;
result.second =
FILE_FLAG_RANDOM_ACCESS
//| FILE_FLAG_NO_BUFFERING
//| FILE_FLAG_WRITE_THROUGH
;
break;
case file_mode::write:
result.first =
GENERIC_READ | GENERIC_WRITE;
result.second =
FILE_FLAG_RANDOM_ACCESS;
break;
}
return result;
}
} // detail
using win32_file = detail::win32_file<>;
#endif
} // nudb
} // detail
#endif