RESTinio
sendfile_defs_win.hpp
Go to the documentation of this file.
1 /*
2  restinio
3 */
4 
5 /*!
6  Sendfile routine definitions (win implementation).
7 
8  @since v.0.4.3
9 */
10 
11 #pragma once
12 
13 //eao197: this code has to be uncommented to check the default
14 //implementation of sendfile operation.
15 //#if defined(RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
16 //#undef RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
17 //#endif
18 
19 #if defined(RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
20 
21 #include <cstdio>
22 
23 namespace restinio
24 {
25 
26 /** @name Aliases for sendfile operation.
27  */
28 ///@{
29 using file_descriptor_t = HANDLE;
30 using file_offset_t = std::uint64_t;
31 using file_size_t = std::uint64_t;
32 ///@}
33 
34 /** @name File operations.
35  * @brief A minimal set of file operations.
36  *
37  * Incapsulates details of windows API for a set of file operations neccessary
38  * for sendfile_t class implementation.
39  */
40 ///@{
41 //! Get file descriptor which stands for null.
42 [[nodiscard]]
43 inline file_descriptor_t null_file_descriptor(){ return INVALID_HANDLE_VALUE; }
44 
45 //! Open file.
46 [[nodiscard]]
47 inline file_descriptor_t
48 open_file( const char * file_path )
49 {
50  file_descriptor_t file_descriptor =
51  // We don't support Unicode on Windows, so call Ansi-version of
52  // CreateFile directly.
53  ::CreateFileA(
54  file_path,
55  GENERIC_READ,
56  FILE_SHARE_READ,
57  0,
58  OPEN_EXISTING,
59  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
60  0 );
61 
62  if( null_file_descriptor() == file_descriptor )
63  {
64  throw exception_t{
65  fmt::format(
66  RESTINIO_FMT_FORMAT_STRING( "unable to openfile '{}': error({})" ),
67  file_path, GetLastError() )
68  };
69  }
70 
71  return file_descriptor;
72 }
73 
74 /*!
75  * @brief Version of %open_file that accepts std::filesystem::path.
76  *
77  * @attention
78  * It uses std::filesystem::path::wstring() to get the file name and
79  * calls CreateFileW. We assume that @a file_path contains a valid
80  * file name constructed from a wide-char string or from utf-8 string
81  * literal (as `const std::char8_t[N]` in C++20). Or @a file_path is
82  * specified as a narrow string, but it can be automatically converted
83  * to wide-string in the current code page.
84  *
85  * @since v.0.7.1
86  */
87 [[nodiscard]]
88 inline file_descriptor_t
89 open_file( const std::filesystem::path & file_path )
90 {
91  const auto wide_file_path = file_path.wstring();
92  file_descriptor_t file_descriptor =
93  // Use wide-char version of CreateFile.
94  ::CreateFileW(
95  wide_file_path.c_str(),
96  GENERIC_READ,
97  FILE_SHARE_READ,
98  0,
99  OPEN_EXISTING,
100  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
101  0 );
102 
103  if( null_file_descriptor() == file_descriptor )
104  {
105  //NOTE(eao197): I don't know a simple way to add `file_path` value into
106  //error message (with respect to the fact that file_path can contain name
107  //in Unicode, in UCS-2, but not in UTF-8).
108  //Because of that the current version doesn't include file name in the
109  //error description.
110  throw exception_t{
111  fmt::format(
112  RESTINIO_FMT_FORMAT_STRING(
113  "open_file(std::filesystem::path) "
114  "unable to openfile: error({})" ),
115  GetLastError() )
116  };
117  }
118 
119  return file_descriptor;
120 }
121 
122 
123 //! Get file meta.
124 template < typename META >
125 [[nodiscard]]
126 META
127 get_file_meta( file_descriptor_t fd )
128 {
129  file_size_t fsize = 0;
130  std::chrono::system_clock::time_point flastmodified;
131 
132  if( null_file_descriptor() != fd )
133  {
134  LARGE_INTEGER file_size;
135  // Obtain file size:
136  if( GetFileSizeEx( fd, &file_size ) )
137  {
138  fsize = static_cast< file_size_t >( file_size.QuadPart );
139  }
140  else
141  {
142  throw exception_t{
143  fmt::format(
144  RESTINIO_FMT_FORMAT_STRING(
145  "unable to get file size: error code:{}" ),
146  GetLastError() )
147  };
148  }
149 
150  FILETIME ftWrite;
151  if( GetFileTime( fd, NULL, NULL, &ftWrite ) )
152  {
153  // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx
154 
155  // Microseconds between 1601-01-01 00:00:00 UTC and 1970-01-01 00:00:00 UTC
156  constexpr std::uint64_t nanosec100_in_microsec = 10;
157  constexpr std::uint64_t epoch_difference_in_microsec =
158  11644473600ULL * 1000 *1000;
159 
160  // First convert 100-ns intervals to microseconds, then adjust for the
161  // epoch difference
162  ULARGE_INTEGER ull;
163  ull.LowPart = ftWrite.dwLowDateTime;
164  ull.HighPart = ftWrite.dwHighDateTime;
165 
166  flastmodified =
167  std::chrono::system_clock::time_point{
168  std::chrono::microseconds(
169  ull.QuadPart / nanosec100_in_microsec - epoch_difference_in_microsec ) };
170  }
171  else
172  {
173  throw exception_t{
174  fmt::format(
175  RESTINIO_FMT_FORMAT_STRING(
176  "unable to get file last modification: error code:{}" ),
177  GetLastError() )
178  };
179  }
180  }
181 
182  return META{ fsize, flastmodified};
183 }
184 
185 //! Close file by its descriptor.
186 inline void
187 close_file( file_descriptor_t fd )
188 {
189  CloseHandle( fd );
190 }
191 ///@}
192 
193 } /* namespace restinio */
194 
195 #else // #if defined(RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
196 
197 #include <restinio/sendfile_defs_default.hpp>
198 
199 #endif // #if defined(RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)