Unravel Engine C++ Reference
Loading...
Searching...
No Matches
cache.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "watcher.h"
4#include "pattern_filter.h"
5#include <chrono>
6#include <filesystem>
7#include <type_traits>
8#include <vector>
9
10namespace fs
11{
12
13template<typename T>
14class cache
15{
16public:
17 using iterator_t = T;
18 static_assert(std::is_same<iterator_t, recursive_directory_iterator>::value ||
19 std::is_same<iterator_t, directory_iterator>::value,
20 "T must be a valid directory iterator type");
21
22 using clock_t = std::chrono::steady_clock;
23
24 cache() = default;
25
26 cache(const fs::path& p, clock_t::duration scan_frequency)
27 : path_(p)
28 , scan_frequency_(scan_frequency)
29 , should_refresh_(true)
30 {
31 watch();
32 }
33
34 cache(const fs::path& p, const pattern_filter& filter, clock_t::duration scan_frequency)
35 : path_(p)
36 , filter_(filter)
37 , scan_frequency_(scan_frequency)
38 , should_refresh_(true)
39 {
40 watch();
41 }
42
43 cache(const cache& rhs)
44 : path_(rhs.path_)
45 , scan_frequency_(rhs.scan_frequency_)
46 , entries_(rhs.entries_)
47 , should_refresh_(rhs.should_refresh_.load())
48 , filter_(rhs.filter_)
49 {
50 watch();
51 }
52
53 cache(cache&& rhs) noexcept
54 : path_(std::move(rhs.path_))
55 , scan_frequency_(rhs.scan_frequency_)
56 , entries_(std::move(rhs.entries_))
57 , should_refresh_(rhs.should_refresh_.load())
58 , filter_(std::move(rhs.filter_))
59 {
60 rhs.unwatch();
61 watch();
62 }
63
64 cache& operator=(const cache& rhs)
65 {
66 unwatch();
67 path_ = rhs.path_;
68 scan_frequency_ = rhs.scan_frequency_;
69 entries_ = rhs.entries_;
70 should_refresh_ = rhs.should_refresh_.load();
71 filter_ = rhs.filter_;
72 watch();
73
74 return *this;
75 }
76
77 cache& operator=(cache&& rhs) noexcept
78 {
79 rhs.unwatch();
80 unwatch();
81 path_ = std::move(rhs.path_);
82 scan_frequency_ = std::move(rhs.scan_frequency_);
83 entries_ = std::move(rhs.entries_);
84 should_refresh_ = rhs.should_refresh_.load();
85 filter_ = std::move(rhs.filter_);
86 watch();
87
88 return *this;
89 }
90
92 {
93 unwatch();
94 }
95 //-----------------------------------------------------------------------------
96 // Name : end ()
101 //-----------------------------------------------------------------------------
102 decltype(auto) begin() const
103 {
104 if(should_refresh())
105 {
106 refresh();
107 }
108 return entries_.begin();
109 }
110
111 //-----------------------------------------------------------------------------
112 // Name : end ()
116 //-----------------------------------------------------------------------------
117 decltype(auto) end() const
118 {
119 return entries_.end();
120 }
121
122 //-----------------------------------------------------------------------------
123 // Name : size ()
127 //-----------------------------------------------------------------------------
128 decltype(auto) size() const
129 {
130 if(should_refresh())
131 {
132 refresh();
133 }
134 return entries_.size();
135 }
136
137 //-----------------------------------------------------------------------------
138 // Name : at ()
142 //-----------------------------------------------------------------------------
143 decltype(auto) at(size_t idx) const
144 {
145 return entries_.at(idx);
146 }
147 decltype(auto) operator[](size_t idx) const
148 {
149 return entries_[idx];
150 }
151
152 //-----------------------------------------------------------------------------
153 // Name : refresh ()
160 //-----------------------------------------------------------------------------
161 void refresh() const
162 {
163 entries_.clear();
164
165 fs::error_code err;
166 iterator_t it(path_, err);
167 for(const auto& p : it)
168 {
169 // Apply pattern filter if enabled
170 if(!filter_.should_include(p.path()))
171 {
172 continue;
173 }
174
175 entries_.emplace_back();
176 auto& cache_entry = entries_.back();
177 cache_entry.entry = p;
178 const auto& absolute_path = cache_entry.entry.path();
179 auto filename = absolute_path.filename();
180 cache_entry.protocol_path = fs::convert_to_protocol(absolute_path).generic_string();
181 cache_entry.filename = absolute_path.filename().string();
182 cache_entry.extension = filename.extension().string();
183 cache_entry.stem = filename.stem().string();
184 }
185
186 std::sort(std::begin(entries_),
187 std::end(entries_),
188 [](const auto& lhs, const auto& rhs)
189 {
190 return fs::is_directory(lhs.entry.status()) > fs::is_directory(rhs.entry.status());
191 });
192
193 should_refresh_ = false;
194 }
195
196 const fs::path& get_path() const
197 {
198 return path_;
199 }
200
201 void clear()
202 {
203 unwatch();
204 path_.clear();
205 entries_.clear();
206 should_refresh_ = true;
207 }
208
209 void set_path(const fs::path& path, const fs::pattern_filter& filter)
210 {
211 if(path_ == path)
212 {
213 return;
214 }
215 unwatch();
216 path_ = path;
217 filter_ = filter;
218 should_refresh_ = true;
219 watch();
220 }
221
222 void set_scan_frequency(clock_t::duration scan_frequency)
223 {
224 if(scan_frequency_ == scan_frequency)
225 {
226 return;
227 }
228 unwatch();
229 scan_frequency_ = scan_frequency;
230 should_refresh_ = true;
231 watch();
232 }
233
235 {
236 directory_entry entry;
237 std::string filename;
238 std::string stem;
239 std::string extension;
240 std::string protocol_path;
241 };
242
243private:
244 //-----------------------------------------------------------------------------
245 // Name : should_refresh ()
249 //-----------------------------------------------------------------------------
250 bool should_refresh() const
251 {
252 return should_refresh_;
253 }
254
255 void watch()
256 {
257 using namespace std::literals;
258 constexpr bool is_recursive = std::is_same<iterator_t, recursive_directory_iterator>::value;
259
260 watch_id_ = watcher::watch(path_,
261 filter_,
262 is_recursive,
263 false,
264 scan_frequency_,
265 [this](const auto&, bool)
266 {
267 should_refresh_ = true;
268 }, "directory_cache");
269
270 }
271 void unwatch()
272 {
273 if(watch_id_ != 0)
274 {
275 watcher::unwatch(watch_id_);
276 watch_id_ = 0;
277 }
278 }
279
281 fs::path path_;
282
283 clock_t::duration scan_frequency_ = std::chrono::milliseconds(500);
285 mutable std::vector<cache_entry> entries_;
287 mutable std::atomic_bool should_refresh_ = {true};
289 std::uint64_t watch_id_ = 0;
291 pattern_filter filter_;
292};
293
296
305template<typename Iterator = recursive_directory_iterator>
306auto make_filtered_cache(const fs::path& path,
307 const std::vector<std::string>& includes,
308 const std::vector<std::string>& excludes = {},
309 std::chrono::steady_clock::duration scan_frequency = std::chrono::milliseconds(500))
311{
312 return cache<Iterator>(path, make_pattern_filter(includes, excludes), scan_frequency);
313}
314
338} // namespace fs
void * load(bx::FileReaderI *_reader, bx::AllocatorI *_allocator, const bx::FilePath &_filePath, uint32_t *_size)
void set_scan_frequency(clock_t::duration scan_frequency)
Definition cache.hpp:222
cache & operator=(cache &&rhs) noexcept
Definition cache.hpp:77
T iterator_t
Definition cache.hpp:17
cache(const fs::path &p, clock_t::duration scan_frequency)
Definition cache.hpp:26
const fs::path & get_path() const
Definition cache.hpp:196
cache()=default
void set_path(const fs::path &path, const fs::pattern_filter &filter)
Definition cache.hpp:209
decltype(auto) size() const
Returns the size for the underlying cached container.
Definition cache.hpp:128
std::chrono::steady_clock clock_t
Definition cache.hpp:22
cache(const fs::path &p, const pattern_filter &filter, clock_t::duration scan_frequency)
Definition cache.hpp:34
decltype(auto) at(size_t idx) const
Directly index into the underlying cached container.
Definition cache.hpp:143
decltype(auto) begin() const
Returns the begin iterator for the underlying cached container and also refreshes the container if ne...
Definition cache.hpp:102
decltype(auto) end() const
Returns the end iterator for the underlying cached container.
Definition cache.hpp:117
cache(const cache &rhs)
Definition cache.hpp:43
void clear()
Definition cache.hpp:201
cache & operator=(const cache &rhs)
Definition cache.hpp:64
cache(cache &&rhs) noexcept
Definition cache.hpp:53
void refresh() const
Refreshes the cache and updates the timestamp of the last refresh. This operation is slow so try to n...
Definition cache.hpp:161
A filter that combines include and exclude patterns for file/directory filtering.
auto should_include(const fs::path &path) const -> bool
Tests if a path should be included based on the filter rules Logic: (matches any include pattern OR n...
static void unwatch(std::uint64_t key)
Un-watches a previously registered file or directory.
Definition watcher.cpp:45
static auto watch(const fs::path &path, const pattern_filter &filter, bool recursive, bool initial_list, clock_t::duration poll_interval, notify_callback callback, const std::string &watcher_name="") -> std::uint64_t
Watches a file or directory for modification and call back the specified std::function....
Definition watcher.cpp:27
const fs::path * filename
Definition cache.hpp:11
auto make_filtered_cache(const fs::path &path, const std::vector< std::string > &includes, const std::vector< std::string > &excludes={}, std::chrono::steady_clock::duration scan_frequency=std::chrono::milliseconds(500)) -> cache< Iterator >
Convenience function to create a directory cache with pattern filtering.
Definition cache.hpp:306
auto make_pattern_filter(const std::string &pattern) -> pattern_filter
Convenience function to create a pattern filter from a single wildcard string Maintains backward comp...
path convert_to_protocol(const path &_path)
Oposite of the resolve_protocol this function tries to convert to protocol path from an absolute one.
Definition cache.hpp:235
std::string extension
Definition cache.hpp:239
std::string filename
Definition cache.hpp:237
std::string protocol_path
Definition cache.hpp:240
std::string stem
Definition cache.hpp:238
directory_entry entry
Definition cache.hpp:236
cache_t cache
Definition uniform.cpp:15