Add file_win32

This commit is contained in:
Vinnie Falco
2017-07-06 02:04:19 -07:00
parent ddc3099281
commit 7254cc8976
9 changed files with 579 additions and 3 deletions

View File

@ -1,6 +1,7 @@
Version 74:
* Add file_stdio and File concept
* Add file_win32
--------------------------------------------------------------------------------

View File

@ -180,6 +180,7 @@
<bridgehead renderas="sect3">&nbsp;</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.beast__file_stdio">file_stdio</link></member>
<member><link linkend="beast.ref.beast__file_win32">file_win32</link></member>
<member><link linkend="beast.ref.beast__flat_buffer">flat_buffer</link></member>
<member><link linkend="beast.ref.beast__handler_alloc">handler_alloc</link></member>
<member><link linkend="beast.ref.beast__handler_ptr">handler_ptr</link></member>

View File

@ -285,7 +285,7 @@ INCLUDE_FILE_PATTERNS =
PREDEFINED = \
BEAST_DOXYGEN \
BEAST_INITFN_RESULT_TYPE(t,a)=void_or_deduced
BEAST_USE_WIN32_FILE=1
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES

View File

@ -10,6 +10,8 @@
#include <beast/core/file_base.hpp>
#include <beast/core/file_stdio.hpp>
#include <beast/core/file_win32.hpp>
#include <boost/config.hpp>
namespace beast {
@ -18,8 +20,14 @@ namespace beast {
This alias is set to the best available implementation
of @b File given the platform and build settings.
*/
#if defined(BOOST_MSVC) && BEAST_USE_WIN32_FILE
using file = file_win32;
#else
using file = file_stdio;
#endif
} // beast
#endif

View File

@ -0,0 +1,173 @@
//
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_CORE_FILE_WIN32_HPP
#define BEAST_CORE_FILE_WIN32_HPP
#include <boost/config.hpp>
#if ! defined(BEAST_USE_WIN32_FILE)
# ifdef BOOST_MSVC
# define BEAST_USE_WIN32_FILE 1
# else
# define BEAST_USE_WIN32_FILE 0
# endif
#endif
#if BEAST_USE_WIN32_FILE
#include <beast/core/error.hpp>
#include <beast/core/file_base.hpp>
#include <boost/detail/winapi/basic_types.hpp>
#include <boost/detail/winapi/handles.hpp>
#include <cstdio>
#include <cstdint>
namespace beast {
/** An implementation of File for Win32.
This class implements a @b File using Win32 native interfaces.
*/
class file_win32
{
boost::detail::winapi::HANDLE_ h_ =
boost::detail::winapi::INVALID_HANDLE_VALUE_;
public:
/** The type of the underlying file handle.
This is platform-specific.
*/
#if BEAST_DOXYGEN
using native_handle_type = HANDLE;
#else
using native_handle_type = boost::detail::winapi::HANDLE_;
#endif
/** Destructor
If the file is open it is first closed.
*/
~file_win32();
/** Constructor
There is no open file initially.
*/
file_win32() = default;
/** Constructor
The moved-from object behaves as if default constructed.
*/
file_win32(file_win32&& other);
/** Assignment
The moved-from object behaves as if default constructed.
*/
file_win32& operator=(file_win32&& other);
/// Returns the native handle associated with the file.
native_handle_type
native_handle() const
{
return h_;
}
/** Set the native handle associated with the file.
If the file is open it is first closed.
@param h The native file handle to assign.
*/
void
native_handle(native_handle_type h);
/// Returns `true` if the file is open
bool
is_open() const
{
return h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_;
}
/** Close the file if open
@param ec Set to the error, if any occurred.
*/
void
close(error_code& ec);
/** Open a file at the given path with the specified mode
@param path The utf-8 encoded path to the file
@param mode The file mode to use
@param ec Set to the error, if any occurred
*/
void
open(char const* path, file_mode mode, error_code& ec);
/** Return the size of the open file
@param ec Set to the error, if any occurred
@return The size in bytes
*/
std::uint64_t
size(error_code& ec) const;
/** Return the current position in the open file
@param ec Set to the error, if any occurred
@return The offset in bytes from the beginning of the file
*/
std::uint64_t
pos(error_code& ec) const;
/** Adjust the current position in the open file
@param offset The offset in bytes from the beginning of the file
@param ec Set to the error, if any occurred
*/
void
seek(std::uint64_t offset, error_code& ec);
/** Read from the open file
@param buffer The buffer for storing the result of the read
@param n The number of bytes to read
@param ec Set to the error, if any occurred
*/
std::size_t
read(void* buffer, std::size_t n, error_code& ec) const;
/** Write to the open file
@param buffer The buffer holding the data to write
@param n The number of bytes to write
@param ec Set to the error, if any occurred
*/
std::size_t
write(void const* buffer, std::size_t n, error_code& ec);
};
} // beast
#include <beast/core/impl/file_win32.ipp>
#endif
#endif

View File

@ -0,0 +1,356 @@
//
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_CORE_IMPL_FILE_WIN32_IPP
#define BEAST_CORE_IMPL_FILE_WIN32_IPP
#include <boost/detail/winapi/access_rights.hpp>
#include <boost/detail/winapi/error_codes.hpp>
#include <boost/detail/winapi/file_management.hpp>
#include <boost/detail/winapi/get_last_error.hpp>
#include <limits>
#include <utility>
namespace beast {
namespace detail {
// VFALCO Can't seem to get boost/detail/winapi to work with
// this so use the non-Ex version for now.
inline
boost::detail::winapi::BOOL_
set_file_pointer_ex(
boost::detail::winapi::HANDLE_ hFile,
boost::detail::winapi::LARGE_INTEGER_ lpDistanceToMove,
boost::detail::winapi::PLARGE_INTEGER_ lpNewFilePointer,
boost::detail::winapi::DWORD_ dwMoveMethod)
{
auto dwHighPart = lpDistanceToMove.u.HighPart;
auto dwLowPart = boost::detail::winapi::SetFilePointer(
hFile,
lpDistanceToMove.u.LowPart,
&dwHighPart,
dwMoveMethod);
if(dwLowPart == boost::detail::winapi::INVALID_SET_FILE_POINTER_)
return 0;
if(lpNewFilePointer)
{
lpNewFilePointer->u.LowPart = dwLowPart;
lpNewFilePointer->u.HighPart = dwHighPart;
}
return 1;
}
} // detail
inline
file_win32::
~file_win32()
{
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
boost::detail::winapi::CloseHandle(h_);
}
inline
file_win32::
file_win32(file_win32&& other)
: h_(other.h_)
{
other.h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
}
inline
file_win32&
file_win32::
operator=(file_win32&& other)
{
if(&other == this)
return *this;
if(h_)
boost::detail::winapi::CloseHandle(h_);
h_ = other.h_;
other.h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
return *this;
}
inline
void
file_win32::
native_handle(native_handle_type h)
{
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
boost::detail::winapi::CloseHandle(h_);
h_ = h;
}
inline
void
file_win32::
close(error_code& ec)
{
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
{
if(! boost::detail::winapi::CloseHandle(h_))
ec.assign(boost::detail::winapi::GetLastError(),
system_category());
else
ec.assign(0, ec.category());
h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
}
else
{
ec.assign(0, ec.category());
}
}
inline
void
file_win32::
open(char const* path, file_mode mode, error_code& ec)
{
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
{
boost::detail::winapi::CloseHandle(h_);
h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
}
boost::detail::winapi::DWORD_ dw1 = 0;
boost::detail::winapi::DWORD_ dw2 = 0;
boost::detail::winapi::DWORD_ dw3 = 0;
/*
| When the file...
This argument: | Exists Does not exist
-------------------------+------------------------------------------------------
CREATE_ALWAYS | Truncates Creates
CREATE_NEW +-----------+ Fails Creates
OPEN_ALWAYS ===| does this |===> Opens Creates
OPEN_EXISTING +-----------+ Opens Fails
TRUNCATE_EXISTING | Truncates Fails
*/
switch(mode)
{
default:
case file_mode::read:
dw1 = boost::detail::winapi::GENERIC_READ_;
dw2 = boost::detail::winapi::OPEN_EXISTING_;
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
break;
case file_mode::scan:
dw1 = boost::detail::winapi::GENERIC_READ_;
dw2 = boost::detail::winapi::OPEN_EXISTING_;
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
break;
case file_mode::write:
dw1 = boost::detail::winapi::GENERIC_READ_ |
boost::detail::winapi::GENERIC_WRITE_;
dw2 = boost::detail::winapi::CREATE_ALWAYS_;
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
break;
case file_mode::write_new:
dw1 = boost::detail::winapi::GENERIC_READ_ |
boost::detail::winapi::GENERIC_WRITE_;
dw2 = boost::detail::winapi::CREATE_NEW_;
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
break;
case file_mode::write_existing:
dw1 = boost::detail::winapi::GENERIC_READ_ |
boost::detail::winapi::GENERIC_WRITE_;
dw2 = boost::detail::winapi::OPEN_EXISTING_;
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
break;
case file_mode::append:
dw1 = boost::detail::winapi::GENERIC_READ_ |
boost::detail::winapi::GENERIC_WRITE_;
dw2 = boost::detail::winapi::CREATE_ALWAYS_;
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
break;
case file_mode::append_new:
dw1 = boost::detail::winapi::GENERIC_READ_ |
boost::detail::winapi::GENERIC_WRITE_;
dw2 = boost::detail::winapi::CREATE_NEW_;
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
break;
case file_mode::append_existing:
dw1 = boost::detail::winapi::GENERIC_READ_ |
boost::detail::winapi::GENERIC_WRITE_;
dw2 = boost::detail::winapi::OPEN_EXISTING_;
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
break;
}
h_ = ::CreateFileA(
path,
dw1,
0,
NULL,
dw2,
dw3,
NULL);
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
ec.assign(boost::detail::winapi::GetLastError(),
system_category());
else
ec.assign(0, ec.category());
}
inline
std::uint64_t
file_win32::
size(error_code& ec) const
{
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
boost::detail::winapi::LARGE_INTEGER_ fileSize;
if(! boost::detail::winapi::GetFileSizeEx(h_, &fileSize))
{
ec.assign(boost::detail::winapi::GetLastError(),
system_category());
return 0;
}
ec.assign(0, ec.category());
return fileSize.QuadPart;
}
inline
std::uint64_t
file_win32::
pos(error_code& ec) const
{
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
boost::detail::winapi::LARGE_INTEGER_ in;
boost::detail::winapi::LARGE_INTEGER_ out;
in.QuadPart = 0;
if(! detail::set_file_pointer_ex(h_, in, &out,
boost::detail::winapi::FILE_CURRENT_))
{
ec.assign(boost::detail::winapi::GetLastError(),
system_category());
return 0;
}
ec.assign(0, ec.category());
return out.QuadPart;
}
inline
void
file_win32::
seek(std::uint64_t offset, error_code& ec)
{
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
{
ec.assign(errc::invalid_argument, generic_category());
return;
}
boost::detail::winapi::LARGE_INTEGER_ in;
in.QuadPart = offset;
if(! detail::set_file_pointer_ex(h_, in, 0,
boost::detail::winapi::FILE_BEGIN_))
{
ec.assign(boost::detail::winapi::GetLastError(),
system_category());
return;
}
ec.assign(0, ec.category());
}
inline
std::size_t
file_win32::
read(void* buffer, std::size_t n, error_code& ec) const
{
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
std::size_t nread = 0;
while(n > 0)
{
boost::detail::winapi::DWORD_ amount;
if(n > (std::numeric_limits<
boost::detail::winapi::DWORD_>::max)())
amount = (std::numeric_limits<
boost::detail::winapi::DWORD_>::max)();
else
amount = static_cast<
boost::detail::winapi::DWORD_>(n);
boost::detail::winapi::DWORD_ bytesRead;
if(! ::ReadFile(h_, buffer, amount, &bytesRead, 0))
{
auto const dwError = ::GetLastError();
if(dwError != boost::detail::winapi::ERROR_HANDLE_EOF_)
ec.assign(::GetLastError(), system_category());
else
ec.assign(0, ec.category());
return nread;
}
if(bytesRead == 0)
return nread;
n -= bytesRead;
nread += bytesRead;
buffer = reinterpret_cast<char*>(buffer) + bytesRead;
}
ec.assign(0, ec.category());
return nread;
}
inline
std::size_t
file_win32::
write(void const* buffer, std::size_t n, error_code& ec)
{
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
std::size_t nwritten = 0;
while(n > 0)
{
boost::detail::winapi::DWORD_ amount;
if(n > (std::numeric_limits<
boost::detail::winapi::DWORD_>::max)())
amount = (std::numeric_limits<
boost::detail::winapi::DWORD_>::max)();
else
amount = static_cast<
boost::detail::winapi::DWORD_>(n);
boost::detail::winapi::DWORD_ bytesWritten;
if(! ::WriteFile(h_, buffer, amount, &bytesWritten, 0))
{
auto const dwError = ::GetLastError();
if(dwError != boost::detail::winapi::ERROR_HANDLE_EOF_)
ec.assign(::GetLastError(), system_category());
else
ec.assign(0, ec.category());
return nwritten;
}
if(bytesWritten == 0)
return nwritten;
n -= bytesWritten;
nwritten += bytesWritten;
buffer = reinterpret_cast<char const*>(buffer) + bytesWritten;
}
ec.assign(0, ec.category());
return nwritten;
}
} // beast
#endif

View File

@ -11,11 +11,12 @@ add_executable (core-tests
${EXAMPLE_INCLUDES}
${EXTRAS_INCLUDES}
../../extras/beast/unit_test/main.cpp
buffer_test.hpp
file_test.hpp
async_result.cpp
bind_handler.cpp
buffer_cat.cpp
buffer_prefix.cpp
buffer_test.hpp
buffered_read_stream.cpp
buffers_adapter.cpp
clamp.cpp
@ -26,7 +27,7 @@ add_executable (core-tests
error.cpp
file.cpp
file_stdio.cpp
file_test.hpp
file_win32.cpp
flat_buffer.cpp
handler_alloc.cpp
handler_ptr.cpp

View File

@ -21,6 +21,7 @@ unit-test core-tests :
error.cpp
file.cpp
file_stdio.cpp
file_win32.cpp
flat_buffer.cpp
handler_alloc.cpp
handler_ptr.cpp

35
test/core/file_win32.cpp Normal file
View File

@ -0,0 +1,35 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Test that header file is self-contained.
#include <beast/core/file_win32.hpp>
#if BEAST_USE_WIN32_FILE
#include "file_test.hpp"
#include <beast/core/type_traits.hpp>
#include <beast/unit_test/suite.hpp>
namespace beast {
class file_win32_test
: public beast::unit_test::suite
{
public:
void
run()
{
doTestFile<file_win32>(*this);
}
};
BEAST_DEFINE_TESTSUITE(file_win32,core,beast);
} // beast
#endif