diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4c802a95..f0fc5b7d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
Version 74:
* Add file_stdio and File concept
+* Add file_win32
--------------------------------------------------------------------------------
diff --git a/doc/quickref.xml b/doc/quickref.xml
index affa6d26..02784257 100644
--- a/doc/quickref.xml
+++ b/doc/quickref.xml
@@ -180,6 +180,7 @@
file_stdio
+ file_win32
flat_buffer
handler_alloc
handler_ptr
diff --git a/doc/source.dox b/doc/source.dox
index 905811e4..0cb3812b 100644
--- a/doc/source.dox
+++ b/doc/source.dox
@@ -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
diff --git a/include/beast/core/file.hpp b/include/beast/core/file.hpp
index 200b2c90..34817a28 100644
--- a/include/beast/core/file.hpp
+++ b/include/beast/core/file.hpp
@@ -10,6 +10,8 @@
#include
#include
+#include
+#include
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
diff --git a/include/beast/core/file_win32.hpp b/include/beast/core/file_win32.hpp
new file mode 100644
index 00000000..fac82ccb
--- /dev/null
+++ b/include/beast/core/file_win32.hpp
@@ -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
+
+#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
+#include
+#include
+#include
+#include
+#include
+
+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
+
+#endif
+
+#endif
diff --git a/include/beast/core/impl/file_win32.ipp b/include/beast/core/impl/file_win32.ipp
new file mode 100644
index 00000000..5d36a55e
--- /dev/null
+++ b/include/beast/core/impl/file_win32.ipp
@@ -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
+#include
+#include
+#include
+#include
+#include
+
+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(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(buffer) + bytesWritten;
+ }
+ ec.assign(0, ec.category());
+ return nwritten;
+}
+
+} // beast
+
+#endif
diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt
index 7eac1631..7ae4cdcc 100644
--- a/test/core/CMakeLists.txt
+++ b/test/core/CMakeLists.txt
@@ -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
diff --git a/test/core/Jamfile b/test/core/Jamfile
index d8845617..0170d7b4 100644
--- a/test/core/Jamfile
+++ b/test/core/Jamfile
@@ -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
diff --git a/test/core/file_win32.cpp b/test/core/file_win32.cpp
new file mode 100644
index 00000000..91530a70
--- /dev/null
+++ b/test/core/file_win32.cpp
@@ -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
+
+#if BEAST_USE_WIN32_FILE
+
+#include "file_test.hpp"
+
+#include
+#include
+
+namespace beast {
+
+class file_win32_test
+ : public beast::unit_test::suite
+{
+public:
+ void
+ run()
+ {
+ doTestFile(*this);
+ }
+};
+
+BEAST_DEFINE_TESTSUITE(file_win32,core,beast);
+
+} // beast
+
+#endif