HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
file_file_win32_impl.hpp
1// Copyright Take Vos 2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
8
9#include "access_mode.hpp"
10#include "seek_whence.hpp"
11#include "../utility/utility.hpp"
12#include "../macros.hpp"
13#include <filesystem>
14
15hi_export_module(hikogui.file.file : impl);
16
17namespace hi { inline namespace v1 { namespace detail {
18
19hi_export class file_impl {
20public:
21 file_impl(file_impl const&) = delete;
22 file_impl(file_impl&&) = delete;
23 file_impl& operator=(file_impl const&) = delete;
24 file_impl& operator=(file_impl&&) = delete;
25
27 {
28 close();
29 }
30
31 file_impl(std::filesystem::path const& path, hi::access_mode access_mode) : _access_mode(access_mode)
32 {
36 } else if (to_bool(access_mode & access_mode::read)) {
38 } else if (to_bool(access_mode & access_mode::write)) {
40 } else {
41 throw io_error(std::format("{}: Invalid AccessMode; expecting Readable and/or Writeable.", path.string()));
42 }
43
45 if (to_bool(access_mode & access_mode::write_lock)) {
46 share_mode = 0;
47 } else if (to_bool(access_mode & access_mode::read_lock)) {
49 } else {
50 // Allow files to be renamed and deleted.
52 }
53
56 if (to_bool(access_mode & access_mode::truncate)) {
58 } else {
60 }
61
62 } else if (to_bool(access_mode & access_mode::create)) {
64
65 } else if (to_bool(access_mode & access_mode::open)) {
66 if (to_bool(access_mode & access_mode::truncate)) {
68 } else {
70 }
71
72 } else {
73 throw io_error(std::format("{}: Invalid AccessMode; expecting CreateFile and/or OpenFile.", path.string()));
74 }
75
77 if (to_bool(access_mode & access_mode::random)) {
79 }
80 if (to_bool(access_mode & access_mode::sequential)) {
82 }
85 }
86
87 if (to_bool(access_mode & access_mode::rename)) {
89 }
90
91 hilet file_name = path.native();
92 if ((_file_handle = CreateFileW(
95 return;
96 }
97
98 hilet error = GetLastError();
102 // Retry opening the file, by first creating the directory hierarchy.
103 auto directory = path;
104 directory.remove_filename();
105 std::filesystem::create_directories(directory);
106
107 if ((_file_handle = CreateFileW(
110 return;
111 }
112 }
113 throw io_error(std::format("{}: Could not open file, '{}'", path.string(), get_last_error_message()));
114 }
115
116 [[nodiscard]] bool closed() noexcept
117 {
118 return _file_handle == nullptr;
119 }
120
122 {
123 return _access_mode;
124 }
125
126 [[nodiscard]] HANDLE file_handle() const noexcept
127 {
128 return _file_handle;
129 }
130
131 void flush()
132 {
133 hi_assert_not_null(_file_handle);
134
135 if (not FlushFileBuffers(_file_handle)) {
136 throw io_error(std::format("{}: Could not flush file.", get_last_error_message()));
137 }
138 }
139
140 void close()
141 {
142 if (_file_handle != INVALID_HANDLE_VALUE) {
143 if (not CloseHandle(_file_handle)) {
144 throw io_error(std::format("{}: Could not close file.", get_last_error_message()));
145 }
146 _file_handle = INVALID_HANDLE_VALUE;
147 }
148 }
149
150 [[nodiscard]] std::size_t size() const
151 {
153
154 if (not GetFileInformationByHandle(_file_handle, &file_information)) {
155 throw io_error(std::format("{}: Could not get file information.", get_last_error_message()));
156 }
157
158 return merge_bit_cast<std::size_t>(file_information.nFileSizeHigh, file_information.nFileSizeLow);
159 }
160
162 {
163 hi_assert_not_null(_file_handle);
164
166 switch (whence) {
167 using enum seek_whence;
168 case begin:
170 break;
171 case current:
173 break;
174 case end:
176 break;
177 default:
178 hi_no_default();
179 }
180
183 offset_.QuadPart = narrow_cast<LONGLONG>(offset);
184 if (not SetFilePointerEx(_file_handle, offset_, &new_offset, whence_)) {
185 throw io_error(std::format("{}: Could not seek in file.", get_last_error_message()));
186 }
187
188 return narrow_cast<std::size_t>(new_offset.QuadPart);
189 }
190
191 void rename(std::filesystem::path const& destination, bool overwrite_existing)
192 {
193 auto dst_filename = destination.native();
194 auto dst_filename_wsize = (dst_filename.size() + 1) * sizeof(WCHAR);
195
197
200
201 auto rename_info = reinterpret_cast<PFILE_RENAME_INFO>(rename_info_alloc.data());
202
203 rename_info->ReplaceIfExists = overwrite_existing;
204 rename_info->RootDirectory = nullptr;
207
209 throw io_error(std::format("Could not rename file to '{}': {}", destination.string(), get_last_error_message()));
210 }
211 }
212
213 void write(void const *data, std::size_t size)
214 {
215 hi_assert(_file_handle != INVALID_HANDLE_VALUE);
216
217 while (size != 0) {
218 // Copy in blocks of 32 kByte
220 auto written = DWORD{};
221 if (not WriteFile(_file_handle, data, to_write, &written, nullptr)) {
222 throw io_error(std::format("{}: Could not write to file.", get_last_error_message()));
223
224 } else if (written == 0) {
225 throw io_error("Could not write to file. Reached end-of-file.");
226 }
227
228 advance_bytes(data, written);
229 size -= written;
230 }
231 }
232
233 [[nodiscard]] std::size_t read(void *data, std::size_t size)
234 {
235 hi_assert(_file_handle != INVALID_HANDLE_VALUE);
236
238 while (size) {
239 auto to_read = size < 0x8000 ? narrow_cast<DWORD>(size) : DWORD{0x8000};
240 auto has_read = DWORD{};
241
242 if (!ReadFile(_file_handle, data, to_read, &has_read, nullptr)) {
243 throw io_error(std::format("{}: Could not read from file.", get_last_error_message()));
244
245 } else if (has_read == 0) {
246 // Read to end-of-file.
247 break;
248 }
249
250 advance_bytes(data, has_read);
251 size -= has_read;
253 }
254
255 return total_read;
256 }
257
258private:
259 hi::access_mode _access_mode;
260 HANDLE _file_handle = nullptr;
261};
262
263}}} // namespace hi::v1::detail
Rules for working with win32 headers.
seek_whence
The position in the file to seek from.
Definition seek_whence.hpp:14
access_mode
The mode in which way to open a file.
Definition access_mode.hpp:17
@ current
Continue from the current position.
@ end
Start from the end of the file.
@ begin
Start from the beginning of the file.
@ truncate
After the file has been opened, truncate it.
@ read_lock
Lock the file for reading, i.e. shared-lock.
@ sequential
Hint that the data should be prefetched.
@ create
Create file if it does not exist, or fail.
@ open
Open file if it exist, or fail.
@ random
Hint the data should not be prefetched.
@ write_through
Hint that writes should be send directly to disk.
@ write_lock
Lock the file for writing, i.e. exclusive-lock.
@ rename
Allow renaming an open file.
@ read
Allow read access to a file.
@ write
Allow write access to a file.
@ create_directories
Create directory hierarchy, if the file could not be created.
DOXYGEN BUG.
Definition algorithm.hpp:16
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
std::string get_last_error_message()
Get the OS error message from the last error received on this thread.
Definition exception_win32_impl.hpp:31
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
Definition file_file_win32_impl.hpp:19
Exception thrown during I/O on an error.
Definition exception_intf.hpp:172
T memcpy(T... args)
T resize(T... args)