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