|
12 | 12 | #include <fstream> |
13 | 13 | #include <iostream> |
14 | 14 | #include <string> |
| 15 | +#include <system_error> |
15 | 16 | #include <thread> |
16 | 17 |
|
| 18 | +#ifdef _WIN32 |
| 19 | +#include <windows.h> |
| 20 | +#endif |
| 21 | + |
17 | 22 | #include "binary_version.hh" |
18 | 23 | #include "mmap_file.hh" |
19 | 24 | #include "pdqsort.h" |
20 | 25 |
|
21 | 26 | namespace { |
22 | 27 |
|
23 | | -// Creates an empty file portably. On Windows, opening a new file without |
24 | | -// writing did not reliably create it, while resize_file could briefly retain |
25 | | -// an exclusive handle. Copying first and explicitly truncating and closing the |
26 | | -// stream avoids both behaviors. |
27 | | -void copy_as_empty(const std::filesystem::path& source_path, |
28 | | - const std::filesystem::path& destination_path) { |
29 | | - std::filesystem::copy_file(source_path, destination_path); |
30 | | - |
31 | | - std::ofstream destination(destination_path, |
| 28 | +// Creates an empty file and closes it before returning. The Windows standard |
| 29 | +// library did not reliably materialize an empty output file, so use the native |
| 30 | +// file API there. |
| 31 | +void create_empty_file(const std::filesystem::path& path) { |
| 32 | +#ifdef _WIN32 |
| 33 | + HANDLE file = CreateFileW(path.c_str(), GENERIC_WRITE, |
| 34 | + FILE_SHARE_READ | FILE_SHARE_WRITE | |
| 35 | + FILE_SHARE_DELETE, |
| 36 | + nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, |
| 37 | + nullptr); |
| 38 | + if (file == INVALID_HANDLE_VALUE) { |
| 39 | + throw std::system_error(GetLastError(), std::system_category(), |
| 40 | + "Could not create empty file " + |
| 41 | + path.string()); |
| 42 | + } |
| 43 | + if (!CloseHandle(file)) { |
| 44 | + throw std::system_error(GetLastError(), std::system_category(), |
| 45 | + "Could not close empty file " + |
| 46 | + path.string()); |
| 47 | + } |
| 48 | +#else |
| 49 | + std::ofstream destination(path, |
32 | 50 | std::ios_base::out | std::ios_base::binary | |
33 | 51 | std::ios_base::trunc); |
34 | 52 | destination.exceptions(std::ofstream::badbit | std::ofstream::failbit); |
35 | 53 | destination.close(); |
| 54 | +#endif |
36 | 55 | } |
37 | 56 |
|
38 | 57 | // Copies selected entries from a byte-offset table into a new file. |
@@ -138,8 +157,7 @@ void filter_database(const char* source, const char* destination, |
138 | 157 | std::filesystem::path destination_subject_ids_path = |
139 | 158 | destination_path / "subject_id"; |
140 | 159 | if (subject_ids.empty()) { |
141 | | - copy_as_empty(source_path / "subject_id", |
142 | | - destination_subject_ids_path); |
| 160 | + create_empty_file(destination_subject_ids_path); |
143 | 161 | } else { |
144 | 162 | std::ofstream subject_ids_file( |
145 | 163 | destination_subject_ids_path, |
@@ -196,8 +214,7 @@ void filter_database(const char* source, const char* destination, |
196 | 214 | std::filesystem::path destination_subject_lengths_path = |
197 | 215 | destination_path / "meds_reader.length"; |
198 | 216 | if (subject_lengths.empty()) { |
199 | | - copy_as_empty(source_path / "meds_reader.length", |
200 | | - destination_subject_lengths_path); |
| 217 | + create_empty_file(destination_subject_lengths_path); |
201 | 218 | } else { |
202 | 219 | std::ofstream subject_lengths_file( |
203 | 220 | destination_subject_lengths_path, |
|
0 commit comments