CommonLib
Loading...
Searching...
No Matches
file.inl
Go to the documentation of this file.
1#include <array>
2#include <bit>
3#include <filesystem>
4#include <fstream>
5#include <iostream>
6#include <sstream>
7#include <vector>
8
9#include "../thirdparty/simdutf/singleheader/simdutf.h"
10
11#include "hash/hash_provider.h"
12#include "mem/mem.h"
13#include "ut/expr/string_expr.h"
14#include "ut/encoding.h"
15
16#define __CMNLIB_INTERNAL_STATIC_LIB_ENROLMENT
17
18namespace hedgedev::csl::io::file
19{
20 inline constexpr uint64_t k_optimal_buffer_size = 0x10000;
21
22 template <typename T>
23 inline bool try_compute_hash(hash::hash_provider<T>& in_hash_provider, std::ifstream& in_file, T& out_hash, bool in_from_beginning, hash_progress_callback_t<T> in_callback)
24 {
25 bool result{};
26
27 if (!in_file.is_open())
28 return result;
29
30 const auto start_pos = uint64_t(in_file.tellg());
31
32 in_file.seekg(0, std::ios::end);
33
34 const auto length = uint64_t(in_file.tellg());
35
36 in_file.seekg(in_from_beginning ? 0 : start_pos, std::ios::beg);
37
38 char buffer[k_optimal_buffer_size]{};
39
40 while (in_file)
41 {
42 in_file.read(buffer, sizeof(buffer));
43
44 const auto count = uint64_t(in_file.gcount());
45
46 if (!count)
47 break;
48
49 in_hash_provider.update(reinterpret_cast<uint8_t*>(buffer), count);
50
51 // Exit hash loop if the callback returned false.
52 if (in_callback && !in_callback(in_hash_provider.digest(), uint64_t(in_file.tellg()), length))
53 {
54 result = false;
55 break;
56 }
57
58 result = true;
59 }
60
61 in_file.clear();
62 in_file.seekg(start_pos, std::ios::beg);
63
64 out_hash = in_hash_provider.digest();
65
66 return result;
67 }
68
69 template <hash::hash_provider_t T>
70 inline bool try_compute_hash(std::ifstream& in_file, typename T::type& out_hash, bool in_from_beginning, hash_progress_callback_t<typename T::type> in_callback)
71 {
72 T hash_provider{};
73
74 return try_compute_hash(hash_provider, in_file, out_hash, in_from_beginning, in_callback);
75 }
76
77 template <typename T>
78 inline bool try_compute_hash(hash::hash_provider<T>& in_hash_provider, const std::filesystem::path& in_path, T& out_hash, hash_progress_callback_t<T> in_callback)
79 {
80 std::error_code error_code{};
81
82 if (!std::filesystem::exists(in_path, error_code))
83 return false;
84
85 auto file = std::ifstream(in_path, std::ios::binary);
86
87 return try_compute_hash(in_hash_provider, file, out_hash, true, in_callback);
88 }
89
90 template <hash::hash_provider_t T>
91 inline bool try_compute_hash(const std::filesystem::path& in_path, typename T::type& out_hash, hash_progress_callback_t<typename T::type> in_callback)
92 {
93 T hash_provider{};
94
95 return try_compute_hash(hash_provider, in_path, out_hash, in_callback);
96 }
97
98 template <hash::hash_provider_t T>
99 inline bool compare(std::ifstream& in_left, std::ifstream& in_right, bool in_from_beginning, hash_progress_callback_t<typename T::type> in_left_callback, hash_progress_callback_t<typename T::type> in_right_callback)
100 {
101 bool result{};
102
103 if (!in_left.is_open() || !in_right.is_open())
104 return result;
105
106 const auto left_start_pos = uint64_t(in_left.tellg());
107 const auto right_start_pos = uint64_t(in_right.tellg());
108
109 // Seek to the end of the files to get their lengths.
110 in_left.seekg(0, std::ios::end);
111 in_right.seekg(0, std::ios::end);
112
113 const auto left_length = uint64_t(in_left.tellg());
114 const auto right_length = uint64_t(in_right.tellg());
115
116 // Files are the same length, compute hashes for binary comparison.
117 if (left_length == right_length)
118 {
119 in_left.seekg(in_from_beginning ? 0 : left_start_pos, std::ios::beg);
120 in_right.seekg(in_from_beginning ? 0 : right_start_pos, std::ios::beg);
121
122 // The interval at which to place hash checkpoints (16 MiB).
123 constexpr auto hash_checkpoint_interval = mem::align<uint64_t>(16 * 1024 * 1024, k_optimal_buffer_size);
124
125 // The total number of hash checkpoints to place.
126 const auto hash_checkpoint_count = (left_length + hash_checkpoint_interval - 1) / hash_checkpoint_interval;
127
128 // Patent pending™️
129 auto hash_checkpoints = std::vector<typename T::type>(hash_checkpoint_count);
130
131 uint64_t hash_checkpoint_index{};
132
133 const auto left_hash_checkpoint_callback = [&](typename T::type in_hash, uint64_t in_processed_bytes, uint64_t in_total_bytes) -> bool
134 {
135 if ((in_processed_bytes % hash_checkpoint_interval) == 0)
136 {
137 // Store current hash at this position to compare earlier in the second file.
138 hash_checkpoints[hash_checkpoint_index] = in_hash;
139 hash_checkpoint_index++;
140 }
141
142 if (in_left_callback)
143 return in_left_callback(in_hash, in_processed_bytes, in_total_bytes);
144
145 return true;
146 };
147
148 const auto right_hash_checkpoint_callback = [&](typename T::type in_hash, uint64_t in_processed_bytes, uint64_t in_total_bytes) -> bool
149 {
150 if ((in_processed_bytes % hash_checkpoint_interval) == 0)
151 {
152 // Hashes at the same position do not match, abort.
153 if (hash_checkpoints[hash_checkpoint_index] != in_hash)
154 return false;
155
156 hash_checkpoint_index++;
157 }
158
159 if (in_right_callback)
160 return in_right_callback(in_hash, in_processed_bytes, in_total_bytes);
161
162 return true;
163 };
164
165 typename T::type left_hash{};
166
167 const auto left_callback = hash_checkpoint_count
168 ? left_hash_checkpoint_callback
169 : in_left_callback;
170
171 if (try_compute_hash<T>(in_left, left_hash, in_from_beginning, left_callback))
172 {
173 typename T::type right_hash{};
174
175 const auto right_callback = hash_checkpoint_count
176 ? right_hash_checkpoint_callback
177 : in_right_callback;
178
179 hash_checkpoint_index = 0;
180
181 if (try_compute_hash<T>(in_right, right_hash, in_from_beginning, right_callback))
182 result = left_hash == right_hash;
183 }
184 }
185
186 in_left.seekg(left_start_pos, std::ios::beg);
187 in_right.seekg(right_start_pos, std::ios::beg);
188
189 return result;
190 }
191
192 template <hash::hash_provider_t T>
193 inline bool compare(const std::filesystem::path& in_left, const std::filesystem::path& in_right, hash_progress_callback_t<typename T::type> in_left_callback, hash_progress_callback_t<typename T::type> in_right_callback)
194 {
195 std::error_code error_code{};
196
197 if (!std::filesystem::exists(in_left, error_code) || !std::filesystem::exists(in_right, error_code) || error_code)
198 return false;
199
200 if (in_left == in_right)
201 return true;
202
203 // The files aren't the same length, skip hashing.
204 if (std::filesystem::file_size(in_left, error_code) != std::filesystem::file_size(in_right, error_code) || error_code)
205 return false;
206
207 auto left = std::ifstream(in_left, std::ios::binary);
208 auto right = std::ifstream(in_right, std::ios::binary);
209
210 return compare(left, right, true, in_left_callback, in_right_callback);
211 }
212
213 inline ut::encoding::encoding_type get_encoding(const std::filesystem::path& in_path, ut::encoding::encoding_type* out_bom, bool in_native)
214 {
215 auto file = std::ifstream(in_path, std::ios::binary);
216
217 if (!file)
218 return ut::encoding::unknown;
219
220 auto buffer = std::vector<char>(k_optimal_buffer_size);
221
222 file.read(buffer.data(), k_optimal_buffer_size);
223
224 const auto length = size_t(file.gcount());
225
226 if (out_bom)
227 {
228 *out_bom = length
229 ? ut::encoding::encoding_type(simdutf::BOM::check_bom(buffer.data(), length))
230 : ut::encoding::unknown;
231 }
232
233 auto result = length
234 ? ut::encoding::encoding_type(simdutf::autodetect_encoding(buffer.data(), length))
235 : ut::encoding::unknown;
236
237 if (in_native)
238 {
239 if (out_bom)
240 *out_bom = ut::expr::get_native_encoding_type(*out_bom);
241
242 result = ut::expr::get_native_encoding_type(result);
243 }
244
245 return result;
246 }
247
248 template <ut::expr::basic_string T>
250 {
251 T result{};
252
253 std::error_code error_code{};
254
255 if (!std::filesystem::exists(in_path, error_code))
256 return result;
257
258 ut::encoding::encoding_type bom{};
259 const auto encoding = get_encoding(in_path, &bom);
260 const auto bom_length = ut::encoding::get_bom_length(bom);
261
262 auto file = std::ifstream(in_path, std::ios::binary | std::ios::ate);
263
264 if (!file)
265 return result;
266
267 const auto length = int64_t(file.tellg()) - bom_length;
268
269 if (length <= 0)
270 return result;
271
272 auto buffer = std::vector<char>(length);
273
274 // Skip the byte order mark, if present.
275 file.seekg(bom_length, std::ios::beg);
276
277 // Read file contents into buffer.
278 file.rdbuf()->sgetn(buffer.data(), length);
279
280 const auto native_encoding = ut::expr::get_native_encoding_type(encoding);
281
282 switch (ut::expr::get_encoding_char_size(native_encoding))
283 {
284 case sizeof(char16_t):
285 result = ut::encoding::convert<T>(std::u16string_view(reinterpret_cast<const char16_t*>(buffer.data()), length / sizeof(char16_t)), native_encoding, encoding);
286 break;
287
288 case sizeof(char32_t):
289 result = ut::encoding::convert<T>(std::u32string_view(reinterpret_cast<const char32_t*>(buffer.data()), length / sizeof(char32_t)), native_encoding, encoding);
290 break;
291
292 default:
293 result = ut::encoding::convert<T>(std::string_view(buffer.data(), length), native_encoding, encoding);
294 break;
295 }
296
297 return result;
298 }
299
300 template <ut::expr::any_string T, ut::encoding::encoding_type target_encoding>
301 inline bool write_all_text(const std::filesystem::path& in_path, const T& in_str, bool in_write_bom)
302 {
303 const auto str = ut::encoding::convert<target_encoding>(in_str);
304
305 if (str.empty())
306 return false;
307
308 auto file = std::ofstream(in_path, std::ios::binary);
309
310 if (!file)
311 return false;
312
313 if (in_write_bom)
314 {
315 const auto bom = get_bom(target_encoding);
316 file.write(reinterpret_cast<const char*>(bom.data()), bom.size());
317 }
318
319 file.write(reinterpret_cast<const char*>(str.data()), str.size() * sizeof(ut::expr::get_encoding_char_type_t<target_encoding>));
320 file.close();
321
322 return true;
323 }
324
325 template <ut::encoding::encoding_type target_encoding, ut::expr::any_string T>
326 inline bool write_all_text(const std::filesystem::path& in_path, const T& in_str, bool in_write_bom)
327 {
328 return write_all_text<T, target_encoding>(in_path, in_str, in_write_bom);
329 }
330}
Definition hash_provider.h:9
Definition xxhash64_provider.inl:4
bool write_all_text(const std::filesystem::path &in_path, const T &in_str, bool in_write_bom)
Definition file.inl:301
constexpr uint64_t k_optimal_buffer_size
Definition file.inl:20
bool compare(std::ifstream &in_left, std::ifstream &in_right, bool in_from_beginning, hash_progress_callback_t< typename T::type > in_left_callback, hash_progress_callback_t< typename T::type > in_right_callback)
Definition file.inl:99
ut::encoding::encoding_type get_encoding(const std::filesystem::path &in_path, ut::encoding::encoding_type *out_bom, bool in_native)
Definition file.inl:213
bool try_compute_hash(hash::hash_provider< T > &in_hash_provider, std::ifstream &in_file, T &out_hash, bool in_from_beginning, hash_progress_callback_t< T > in_callback)
Definition file.inl:23
bool try_compute_hash(hash::hash_provider< T > &in_hash_provider, const std::filesystem::path &in_path, T &out_hash, hash_progress_callback_t< T > in_callback)
Definition file.inl:78
bool try_compute_hash(const std::filesystem::path &in_path, typename T::type &out_hash, hash_progress_callback_t< typename T::type > in_callback)
Definition file.inl:91
T read_all_text(const std::filesystem::path &in_path)
Definition file.inl:249
bool try_compute_hash(std::ifstream &in_file, typename T::type &out_hash, bool in_from_beginning, hash_progress_callback_t< typename T::type > in_callback)
Definition file.inl:70
bool compare(const std::filesystem::path &in_left, const std::filesystem::path &in_right, hash_progress_callback_t< typename T::type > in_left_callback, hash_progress_callback_t< typename T::type > in_right_callback)
Definition file.inl:193
Definition file.inl:19
Definition encoding.h:10
encoding_type
Definition encoding_type.h:6
@ unknown
Definition encoding_type.h:7
constexpr encoding::encoding_type get_native_encoding_type(encoding::encoding_type in_encoding)
Definition encoding_expr.inl:43
Definition expr.h:7