Unravel Engine C++ Reference
Loading...
Searching...
No Matches
filesystem.cpp
Go to the documentation of this file.
1#include "filesystem.h"
2#include <algorithm>
3#include <fstream>
4namespace fs
5{
6
7namespace detail
8{
9
10namespace
11{
12bool is_parent_path(const path& parent, const path& child)
13{
14 return child.parent_path() == parent;
15}
16
17bool is_indirect_parent_path(const path& parent, const path& child)
18{
19 path rel = child.lexically_relative(parent);
20 return !rel.empty() && rel.begin()->string() != "." && rel.begin()->string() != "..";
21}
22
23bool begins_with(const std::string& str, const std::string& value)
24{
25 // Validate requirements
26 if(str.length() < value.length())
27 {
28 return false;
29 }
30 if(str.empty() || value.empty())
31 {
32 return false;
33 }
34
35 // Do the submeshes match?
36 auto s1 = str.substr(0, value.length());
37
38 return s1.compare(value) == 0;
39}
40static std::string replace_seq(const std::string& str, const std::string& old_sequence, const std::string& new_sequence)
41{
42 std::string s = str;
43 std::string::size_type location = 0;
44 std::string::size_type old_length = old_sequence.length();
45 std::string::size_type new_length = new_sequence.length();
46
47 // Search for all replace std::string occurances.
48 if(!s.empty())
49 {
50 while(std::string::npos != (location = s.find(old_sequence, location)))
51 {
52 s.replace(location, old_length, new_sequence);
53 location += new_length;
54
55 // Break out if we're done
56 if(location >= s.length())
57 {
58 break;
59 }
60
61 } // Next
62
63 } // End if not empty
64
65 return s;
66}
67
68std::string to_lower(const std::string& str)
69{
70 std::string s(str);
71 std::transform(s.begin(), s.end(), s.begin(), tolower);
72 return s;
73}
74
75template<typename Container = std::string, typename CharT = char, typename Traits = std::char_traits<char>>
76auto read_stream_into_container(std::basic_istream<CharT, Traits>& in, Container& container) -> bool
77{
78 static_assert(
79 std::is_same<Container, std::basic_string<CharT, Traits, typename Container::allocator_type>>::value ||
80 std::is_same<Container, std::vector<CharT, typename Container::allocator_type>>::value ||
81 std::is_same<Container,
82 std::vector<std::make_unsigned_t<CharT>, typename Container::allocator_type>>::value ||
83 std::is_same<Container, std::vector<std::make_signed_t<CharT>, typename Container::allocator_type>>::value,
84 "only strings and vectors of ((un)signed) CharT allowed");
85
86 auto const start_pos = in.tellg();
87 if(std::streamsize(-1) == start_pos || !in.good())
88 {
89 return false;
90 }
91
92 if(!in.seekg(0, std::ios_base::end) || !in.good())
93 {
94 return false;
95 }
96
97 auto const end_pos = in.tellg();
98
99 if(std::streamsize(-1) == end_pos || !in.good())
100 {
101 return false;
102 }
103
104 auto const char_count = end_pos - start_pos;
105
106 if(!in.seekg(start_pos) || !in.good())
107 {
108 return false;
109 }
110
111 container.resize(static_cast<std::size_t>(char_count));
112
113 if(!container.empty())
114 {
115 in.read(reinterpret_cast<CharT*>(&container[0]), char_count);
116 container.resize(in.gcount());
117 }
118
119 return in.good() || in.eof();
120}
121}
122
123} // namespace detail
124
126{
127 static bool is_insensitive = []()
128 {
129 auto timestamp = fs::file_time_type::clock::now();
130 auto temp_path = fs::temp_directory_path();
131
132 auto salt = std::to_string(int64_t(timestamp.time_since_epoch().count())) + std::to_string(std::rand());
133 std::string temp_name_lower = "_case_sensitivity_test_" + salt + ".txt";
134 std::string temp_name_upper = "_CASE_SENSITIVITY_TEST_" + salt + ".txt";
135
136 auto file_lower = temp_path / temp_name_lower;
137 auto file_upper = temp_path / temp_name_upper;
138 {
139 std::ofstream os;
140 os.open(file_lower);
141 }
142
143 fs::error_code ec;
144 bool result = fs::equivalent(file_upper, file_lower, ec);
145 fs::remove(file_lower, ec);
146
147 return result;
148 }();
149
150 return is_insensitive;
151}
152
153byte_array_t read_stream(std::istream& stream)
154{
155 byte_array_t result{};
156 detail::read_stream_into_container<byte_array_t>(stream, result);
157 return result;
158}
159
160std::string read_stream_str(std::istream& stream)
161{
162 std::string result{};
163 detail::read_stream_into_container<std::string>(stream, result);
164 return result;
165}
166
168{
170 result.data = read_stream(stream);
171 return result;
172}
173
175{
177 result.data = read_stream_str(stream);
178 return result;
179}
180
181bool add_path_protocol(const std::string& protocol, const path& dir)
182{
183 // Protocol matching is case insensitive, convert to lower case
184 auto protocol_lower = detail::to_lower(protocol);
185
186 auto& protocols = get_path_protocols();
187 // Add to the list
188 protocols[protocol_lower] = fs::path(dir).make_preferred().string();
189
190 // Success!
191 return true;
192}
193
195{
196 static protocols_t protocols;
197 return protocols;
198}
199
200path extract_protocol(const path& _path)
201{
202 static const std::string separator = ":/";
203 const auto string_path = _path.generic_string();
204 auto pos = string_path.find(separator, 0);
205 if(pos == std::string::npos)
206 {
207 return {};
208 }
209
210 const auto root = string_path.substr(0, pos);
211
212 return root;
213}
214
215path resolve_protocol(const path& _path)
216{
217 static const std::string separator = ":/";
218 const auto string_path = _path.generic_string();
219 auto pos = string_path.find(separator, 0);
220 if(pos == std::string::npos)
221 {
222 return _path;
223 }
224
225 const auto root = string_path.substr(0, pos);
226
227 fs::path relative_path = string_path.substr(pos + separator.size());
228 // Matching path protocol in our list?
229 const auto& protocols = get_path_protocols();
230
231 auto it = protocols.find(root);
232
233 if(it == std::end(protocols))
234 {
235 return _path;
236 }
237
238 auto result = path(it->second);
239 if(!relative_path.empty())
240 {
241 result = result / relative_path.make_preferred();
242 }
243 return result;
244}
245
246bool has_known_protocol(const path& _path)
247{
248 static const std::string separator = ":/";
249
250 const auto string_path = _path.generic_string();
251 auto pos = string_path.find(separator, 0);
252 if(pos == std::string::npos)
253 {
254 return false;
255 }
256
257 const auto root = string_path.substr(0, pos);
258
259 const auto& protocols = get_path_protocols();
260
261 // Matching path protocol in our list?
262 return (protocols.find(root) != std::end(protocols));
263}
264
265path convert_to_protocol(const path& _path)
266{
267 const auto string_path = fs::path(_path).make_preferred().string();
268
269 const auto& protocols = get_path_protocols();
270
271 const protocols_t::value_type* best_protocol{};
272 for(const auto& protocol_pair : protocols)
273 {
274 // const auto& protocol = protocol_pair.first;
275 const auto& resolved_protocol = protocol_pair.second;
276
277 if(detail::begins_with(string_path, resolved_protocol))
278 {
279 if(best_protocol)
280 {
281 if(best_protocol->second.size() < resolved_protocol.size())
282 {
283 best_protocol = &protocol_pair;
284 }
285 }
286 else
287 {
288 best_protocol = &protocol_pair;
289 }
290 }
291 }
292 if(best_protocol)
293 {
294 const auto& protocol = best_protocol->first;
295 const auto& resolved_protocol = best_protocol->second;
296
297 auto arg1 = path(string_path).generic_string();
298 auto arg2 = path(resolved_protocol).generic_string();
299
300 return replace(arg1, arg2, protocol + ":").generic_string();
301 }
302 return _path;
303}
304
305path replace(const path& _path, const path& _sequence, const path& _new_sequence)
306{
307 return path(detail::replace_seq(_path.string(), _sequence.string(), _new_sequence.string()));
308}
309
310std::vector<path> split_until(const path& _path, const path& _predicate)
311{
312 std::vector<path> result;
313
314 auto f = _path;
315
316 while(f.has_parent_path() && f.has_filename() && f != _predicate)
317 {
318 result.push_back(f);
319 f = f.parent_path();
320 }
321
322 result.push_back(_predicate);
323 std::reverse(std::begin(result), std::end(result));
324
325 return result;
326}
327
328path reduce_trailing_extensions(const path& _path)
329{
330 fs::path reduced = _path;
331 for(auto temp = reduced; temp.has_extension(); temp = reduced.stem())
332 {
333 reduced = temp;
334 }
335
336 fs::path result = _path;
337 result.remove_filename();
338 result /= reduced;
339 return result;
340}
341
342bool is_any_parent_path(const path& parent, const path& child)
343{
344 return detail::is_parent_path(parent, child) || detail::is_indirect_parent_path(parent, child);
345}
346} // namespace fs
Definition cache.hpp:11
bool add_path_protocol(const std::string &protocol, const path &dir)
Allows us to map a protocol to a specific directory. A path protocol gives the caller the ability to ...
bool is_case_insensitive()
path extract_protocol(const path &_path)
Given the specified path/filename, resolve the final full filename. This will be based on either the ...
stream_buffer< std::string > read_stream_buffer_str(std::istream &stream)
std::unordered_map< std::string, std::string > protocols_t
Definition filesystem.h:13
path reduce_trailing_extensions(const path &_path)
another.
byte_array_t read_stream(std::istream &stream)
Load a byte_array_t with the contents of the specified file, be that file in a package or in the main...
path resolve_protocol(const path &_path)
Given the specified path/filename, resolve the final full filename. This will be based on either the ...
bool has_known_protocol(const path &_path)
Checks whether the path has a known protocol.
path replace(const path &_path, const path &_sequence, const path &_new_sequence)
Replacing any occurences of the specified path sequence with another.
std::string read_stream_str(std::istream &stream)
bool is_any_parent_path(const path &parent, const path &child)
std::vector< std::uint8_t > byte_array_t
Definition filesystem.h:14
protocols_t & get_path_protocols()
Returns the registered path protocols.
std::vector< path > split_until(const path &_path, const path &_predicate)
another.
stream_buffer< byte_array_t > read_stream_buffer(std::istream &stream)
path convert_to_protocol(const path &_path)
Oposite of the resolve_protocol this function tries to convert to protocol path from an absolute one.