From 7254cc8976c0b14ccf753c7ddf665b7dbc2f8e23 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Thu, 6 Jul 2017 02:04:19 -0700 Subject: [PATCH] Add file_win32 --- CHANGELOG.md | 1 + doc/quickref.xml | 1 + doc/source.dox | 2 +- include/beast/core/file.hpp | 8 + include/beast/core/file_win32.hpp | 173 ++++++++++++ include/beast/core/impl/file_win32.ipp | 356 +++++++++++++++++++++++++ test/core/CMakeLists.txt | 5 +- test/core/Jamfile | 1 + test/core/file_win32.cpp | 35 +++ 9 files changed, 579 insertions(+), 3 deletions(-) create mode 100644 include/beast/core/file_win32.hpp create mode 100644 include/beast/core/impl/file_win32.ipp create mode 100644 test/core/file_win32.cpp 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