RESTinio
Loading...
Searching...
No Matches
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
23namespace restinio
24{
25
26/** @name Aliases for sendfile operation.
27 */
28///@{
29using file_descriptor_t = HANDLE;
30using file_offset_t = std::uint64_t;
31using 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]]
43inline file_descriptor_t null_file_descriptor(){ return INVALID_HANDLE_VALUE; }
44
45//! Open file.
46[[nodiscard]]
47inline file_descriptor_t
48open_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]]
88inline file_descriptor_t
89open_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.
124template < typename META >
125[[nodiscard]]
126META
127get_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.
186inline void
187close_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)