HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
file_view_win32_impl.hpp
1// Copyright Jhalak Patel 2021.
2// Copyright Take Vos 2019, 2021-2022.
3// Distributed under the Boost Software License, Version 1.0.
4// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
5
6#pragma once
7
9
10#include "access_mode.hpp"
11#include "file_intf.hpp"
12#include "file_win32_impl.hpp"
13#include "../utility/utility.hpp"
14#include "../char_maps/char_maps.hpp" // XXX #616
15#include "../telemetry/telemetry.hpp"
16#include "../container/container.hpp"
17#include "../macros.hpp"
18#include <format>
19#include <span>
20#include <cstddef>
21#include <chrono>
22
23hi_export_module(hikogui.file.file_view_impl);
24
25hi_export namespace hi { inline namespace v1 {
26namespace detail {
27
29public:
30 file_view_impl(file_view_impl const&) = delete;
32 file_view_impl& operator=(file_view_impl const&) = delete;
33 file_view_impl& operator=(file_view_impl&&) = delete;
34
36 {
37 if (_data != nullptr) {
38 destroy_view(_data);
39 }
40 if (_mapping_handle) {
41 destroy_mapping(_mapping_handle);
42 }
43 }
44
46 _file(std::move(file)), _offset(offset), _size(size)
47 {
48 if (_size == 0) {
49 _size = _file->size() - _offset;
50 }
51
52 if (_offset + _size > _file->size()) {
53 throw io_error("Requested mapping is beyond file size.");
54 }
55
56 if (_file->size() == 0) {
57 // Don't map a zero byte file.
58 _data = nullptr;
59
60 } else {
61 _mapping_handle = make_mapping(file_handle(), access_mode(), _offset + _size);
62 try {
63 _data = make_view(_mapping_handle, access_mode(), _offset, _size);
64 } catch (...) {
65 destroy_mapping(_mapping_handle);
66 throw;
67 }
68 }
69 }
70
71 [[nodiscard]] std::size_t offset() const noexcept
72 {
73 return _offset;
74 }
75
76 [[nodiscard]] std::size_t size() const noexcept
77 {
78 return _size;
79 }
80
81 [[nodiscard]] hi::access_mode access_mode() const noexcept
82 {
83 hi_assert_not_null(_file);
84 return _file->access_mode();
85 }
86
87 [[nodiscard]] void_span void_span() const noexcept
88 {
89 hi_assert_not_null(_file);
90 hi_assert(to_bool(_file->access_mode() & access_mode::write));
91 return {_data, _size};
92 }
93
94 [[nodiscard]] const_void_span const_void_span() const noexcept
95 {
96 return {_data, _size};
97 }
98
99 [[nodiscard]] bool unmapped() const noexcept
100 {
101 if (_file != nullptr) {
102 if (_file->closed()) {
103 _file = nullptr;
104 return true;
105 } else {
106 return false;
107 }
108 } else {
109 return true;
110 }
111 }
112
113 void unmap()
114 {
115 if (_data) {
116 destroy_view(_data);
117 _data = nullptr;
118 _size = 0;
119 destroy_mapping(_mapping_handle);
120 _mapping_handle = nullptr;
121 }
122 _file = nullptr;
123 }
124
125 void flush(hi::void_span span) const noexcept
126 {
127 if (not FlushViewOfFile(span.data(), span.size())) {
128 hi_log_error_once("file::error::flush-view", "Could not flush file. '{}'", get_last_error_message());
129 }
130 }
131
132private:
133 mutable HANDLE _mapping_handle = nullptr;
134 mutable std::shared_ptr<file_impl> _file;
135 std::size_t _offset;
136 std::size_t _size;
137 void *_data = nullptr;
138
139 [[nodiscard]] HANDLE file_handle() noexcept
140 {
141 hi_assert_not_null(_file);
142 return _file->file_handle();
143 }
144
145 static void destroy_mapping(HANDLE mapping)
146 {
147 if (not CloseHandle(mapping)) {
148 throw io_error(std::format("Could not close file mapping object on file '{}'", get_last_error_message()));
149 }
150 }
151
152 [[nodiscard]] static HANDLE make_mapping(HANDLE file, hi::access_mode access_mode, std::size_t size)
153 {
154 hi_assert(size != 0);
155
156 DWORD protect;
157 if (to_bool(access_mode & hi::access_mode::read) and to_bool(access_mode & hi::access_mode::write)) {
158 protect = PAGE_READWRITE;
159 } else if (to_bool(access_mode & hi::access_mode::read)) {
160 protect = PAGE_READONLY;
161 } else {
162 throw io_error("Illegal access mode when mapping file.");
163 }
164
165 DWORD size_high = size >> 32;
166 DWORD size_low = size & 0xffffffff;
167
168 if (auto r = CreateFileMappingW(file, NULL, protect, size_high, size_low, nullptr)) {
169 return r;
170 } else {
171 throw io_error(std::format("Could not create file mapping. '{}'", get_last_error_message()));
172 }
173 }
174
175 static void destroy_view(void *data)
176 {
177 if (not UnmapViewOfFile(data)) {
178 throw io_error(std::format("Could not unmap view on file '{}'", get_last_error_message()));
179 }
180 }
181
182 [[nodiscard]] static void *make_view(HANDLE mapping, hi::access_mode access_mode, std::size_t offset, std::size_t size)
183 {
184 hi_assert(size != 0);
185
186 DWORD desired_access;
187 if (to_bool(access_mode & access_mode::read) and to_bool(access_mode & access_mode::write)) {
188 desired_access = FILE_MAP_WRITE;
189 } else if (to_bool(access_mode & access_mode::read)) {
190 desired_access = FILE_MAP_READ;
191 } else {
192 throw io_error(std::format("Illegal access mode when viewing file."));
193 }
194
195 DWORD offset_high = offset >> 32;
196 DWORD offset_low = offset & 0xffffffff;
197
198 if (auto r = MapViewOfFile(mapping, desired_access, offset_high, offset_low, size)) {
199 return r;
200 } else {
201 throw io_error(std::format("Could not map view of file. '{}'", get_last_error_message()));
202 }
203 }
204};
205
206} // namespace detail
207}} // namespace hi::v1
Rules for working with win32 headers.
access_mode
The mode in which way to open a file.
Definition access_mode.hpp:17
@ read
Allow read access to a file.
@ write
Allow write access to a file.
@ flush
Align the text naturally based on the writing direction of each paragraph.
The HikoGUI namespace.
Definition array_generic.hpp:20
hi_export std::string get_last_error_message()
Get the OS error message from the last error received on this thread.
Definition exception_win32_impl.hpp:30
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
A File object.
Definition file_intf.hpp:33
Definition file_view_win32_impl.hpp:28
Exception thrown during I/O on an error.
Definition exception_intf.hpp:173
T move(T... args)