text: //
  // 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)
  //
  // Official repository: https://github.com/boostorg/beast
  //
  
  #ifndef BOOST_BEAST_CORE_IMPL_FILE_WIN32_HPP
  #define BOOST_BEAST_CORE_IMPL_FILE_WIN32_HPP
  
  #include <boost/core/exchange.hpp>
  #include <boost/winapi/access_rights.hpp>
  #include <boost/winapi/error_codes.hpp>
  #include <boost/winapi/file_management.hpp>
  #include <boost/winapi/get_last_error.hpp>
  #include <limits>
  #include <utility>
  
  namespace boost {
  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::winapi::BOOL_
  set_file_pointer_ex(
      boost::winapi::HANDLE_ hFile,
      boost::winapi::LARGE_INTEGER_ lpDistanceToMove,
      boost::winapi::PLARGE_INTEGER_ lpNewFilePointer,
      boost::winapi::DWORD_ dwMoveMethod)
  {
      auto dwHighPart = lpDistanceToMove.u.HighPart;
      auto dwLowPart = boost::winapi::SetFilePointer(
          hFile,
          lpDistanceToMove.u.LowPart,
          &dwHighPart,
          dwMoveMethod);
      if(dwLowPart == boost::winapi::INVALID_SET_FILE_POINTER_)
          return 0;
      if(lpNewFilePointer)
      {
          lpNewFilePointer->u.LowPart = dwLowPart;
          lpNewFilePointer->u.HighPart = dwHighPart;
      }
      return 1;
  }
  
  } // detail
  
  file_win32::
  ~file_win32()
  {
      if(h_ != boost::winapi::INVALID_HANDLE_VALUE_)
          boost::winapi::CloseHandle(h_);
  }
  
  file_win32::
  file_win32(file_win32&& other)
      : h_(boost::exchange(other.h_,
          boost::winapi::INVALID_HANDLE_VALUE_))
  {
  }
  
  file_win32&
  file_win32::
  operator=(file_win32&& other)
  {
      if(&other == this)
          return *this;
      if(h_)
          boost::winapi::CloseHandle(h_);
      h_ = other.h_;
      other.h_ = boost::winapi::INVALID_HANDLE_VALUE_;
      return *this;
  }
  
  void
  file_win32::
  native_handle(native_handle_type h)
  {
       if(h_ != boost::winapi::INVALID_HANDLE_VALUE_)
          boost::winapi::CloseHandle(h_);
      h_ = h;
  }
  
  void
  file_win32::
  close(error_code& ec)
  {
      if(h_ != boost::winapi::INVALID_HANDLE_VALUE_)
      {
          if(! boost::winapi::CloseHandle(h_))
              ec.assign(boost::winapi::GetLastError(),
                  system_category());
          else
              ec = {};
          h_ = boost::winapi::INVALID_HANDLE_VALUE_;
      }
      else
      {
          ec = {};
      }
  }
  
  void
  file_win32::
  open(char const* path, file_mode mode, error_code& ec)
  {
      if(h_ != boost::winapi::INVALID_HANDLE_VALUE_)
      {
          boost::winapi::CloseHandle(h_);
          h_ = boost::winapi::INVALID_HANDLE_VALUE_;
      }
      boost::winapi::DWORD_ share_mode = 0;
      boost::winapi::DWORD_ desired_access = 0;
      boost::winapi::DWORD_ creation_disposition = 0;
      boost::winapi::DWORD_ flags_and_attributes = 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:
          desired_access = boost::winapi::GENERIC_READ_;
          share_mode = boost::winapi::FILE_SHARE_READ_;
          creation_disposition = boost::winapi::OPEN_EXISTING_;
          flags_and_attributes = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
          break;
  
      case file_mode::scan:           
          desired_access = boost::winapi::GENERIC_READ_;
          share_mode = boost::winapi::FILE_SHARE_READ_;
          creation_disposition = boost::winapi::OPEN_EXISTING_;
          flags_and_attributes = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
          break;
  
      case file_mode::write:          
          desired_access = boost::winapi::GENERIC_READ_ |
                           boost::winapi::GENERIC_WRITE_;
          creation_disposition = boost::winapi::CREATE_ALWAYS_;
          flags_and_attributes = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
          break;
  
      case file_mode::write_new:      
          desired_access = boost::winapi::GENERIC_READ_ |
                           boost::winapi::GENERIC_WRITE_;
          creation_disposition = boost::winapi::CREATE_NEW_;
          flags_and_attributes = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
          break;
  
      case file_mode::write_existing: 
          desired_access = boost::winapi::GENERIC_READ_ |
                           boost::winapi::GENERIC_WRITE_;
          creation_disposition = boost::winapi::OPEN_EXISTING_;
          flags_and_attributes = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
          break;
  
      case file_mode::append:         
          desired_access = boost::winapi::GENERIC_READ_ |
                           boost::winapi::GENERIC_WRITE_;
  
          creation_disposition = boost::winapi::CREATE_ALWAYS_;
          flags_and_attributes = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
          break;
  
      case file_mode::append_existing:
          desired_access = boost::winapi::GENERIC_READ_ |
                           boost::winapi::GENERIC_WRITE_;
          creation_disposition = boost::winapi::OPEN_EXISTING_;
          flags_and_attributes = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
          break;
      }
      h_ = ::CreateFileA(
          path,
          desired_access,
          share_mode,
          NULL,
          creation_disposition,
          flags_and_attributes,
          NULL);
      if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
          ec.assign(boost::winapi::GetLastError(),
              system_category());
      else
          ec = {};
  }
  
  std::uint64_t
  file_win32::
  size(error_code& ec) const
  {
      if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
      {
          ec = make_error_code(errc::bad_file_descriptor);
          return 0;
      }
      boost::winapi::LARGE_INTEGER_ fileSize;
      if(! boost::winapi::GetFileSizeEx(h_, &fileSize))
      {
          ec.assign(boost::winapi::GetLastError(),
              system_category());
          return 0;
      }
      ec = {};
      return fileSize.QuadPart;
  }
  
  std::uint64_t
  file_win32::
  pos(error_code& ec)
  {
      if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
      {
          ec = make_error_code(errc::bad_file_descriptor);
          return 0;
      }
      boost::winapi::LARGE_INTEGER_ in;
      boost::winapi::LARGE_INTEGER_ out;
      in.QuadPart = 0;
      if(! detail::set_file_pointer_ex(h_, in, &out,
          boost::winapi::FILE_CURRENT_))
      {
          ec.assign(boost::winapi::GetLastError(),
              system_category());
          return 0;
      }
      ec = {};
      return out.QuadPart;
  }
  
  void
  file_win32::
  seek(std::uint64_t offset, error_code& ec)
  {
      if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
      {
          ec = make_error_code(errc::bad_file_descriptor);
          return;
      }
      boost::winapi::LARGE_INTEGER_ in;
      in.QuadPart = offset;
      if(! detail::set_file_pointer_ex(h_, in, 0,
          boost::winapi::FILE_BEGIN_))
      {
          ec.assign(boost::winapi::GetLastError(),
              system_category());
          return;
      }
      ec = {};
  }
  
  std::size_t
  file_win32::
  read(void* buffer, std::size_t n, error_code& ec)
  {
      if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
      {
          ec = make_error_code(errc::bad_file_descriptor);
          return 0;
      }
      std::size_t nread = 0;
      while(n > 0)
      {
          boost::winapi::DWORD_ amount;
          if(n > (std::numeric_limits<
                  boost::winapi::DWORD_>::max)())
              amount = (std::numeric_limits<
                  boost::winapi::DWORD_>::max)();
          else
              amount = static_cast<
                  boost::winapi::DWORD_>(n);
          boost::winapi::DWORD_ bytesRead;
          if(! ::ReadFile(h_, buffer, amount, &bytesRead, 0))
          {
              auto const dwError = boost::winapi::GetLastError();
              if(dwError != boost::winapi::ERROR_HANDLE_EOF_)
                  ec.assign(dwError, system_category());
              else
                  ec = {};
              return nread;
          }
          if(bytesRead == 0)
              return nread;
          n -= bytesRead;
          nread += bytesRead;
          buffer = static_cast<char*>(buffer) + bytesRead;
      }
      ec = {};
      return nread;
  }
  
  std::size_t
  file_win32::
  write(void const* buffer, std::size_t n, error_code& ec)
  {
      if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
      {
          ec = make_error_code(errc::bad_file_descriptor);
          return 0;
      }
      std::size_t nwritten = 0;
      while(n > 0)
      {
          boost::winapi::DWORD_ amount;
          if(n > (std::numeric_limits<
                  boost::winapi::DWORD_>::max)())
              amount = (std::numeric_limits<
                  boost::winapi::DWORD_>::max)();
          else
              amount = static_cast<
                  boost::winapi::DWORD_>(n);
          boost::winapi::DWORD_ bytesWritten;
          if(! ::WriteFile(h_, buffer, amount, &bytesWritten, 0))
          {
              auto const dwError = boost::winapi::GetLastError();
              if(dwError != boost::winapi::ERROR_HANDLE_EOF_)
                  ec.assign(dwError, system_category());
              else
                  ec = {};
              return nwritten;
          }
          if(bytesWritten == 0)
              return nwritten;
          n -= bytesWritten;
          nwritten += bytesWritten;
          buffer = static_cast<char const*>(buffer) + bytesWritten;
      }
      ec = {};
      return nwritten;
  }
  
  } // beast
  } // boost
  
  #endif
  