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 watch();
61 }
62
63 cache& operator=(const cache& rhs)
64 {
65 unwatch();
66 path_ = rhs.path_;
67 scan_frequency_ = rhs.scan_frequency_;
68 entries_ = rhs.entries_;
69 should_refresh_ = rhs.should_refresh_.load();
70 filter_ = rhs.filter_;
71 watch();
72
73 return *this;
74 }
75
76 cache& operator=(cache&& rhs) noexcept
77 {
78 unwatch();
79 path_ = std::move(rhs.path_);
80 scan_frequency_ = std::move(rhs.scan_frequency_);
81 entries_ = std::move(rhs.entries_);
82 should_refresh_ = rhs.should_refresh_.load();
83 filter_ = std::move(rhs.filter_);
84 watch();
85
86 return *this;
87 }
88
90 {
91 unwatch();
92 }
93 //-----------------------------------------------------------------------------
94 // Name : end ()
99 //-----------------------------------------------------------------------------
100 decltype(auto) begin() const
101 {
102 if(should_refresh())
103 {
104 refresh();
105 }
106 return entries_.begin();
107 }
108
109 //-----------------------------------------------------------------------------
110 // Name : end ()
114 //-----------------------------------------------------------------------------
115 decltype(auto) end() const
116 {
117 return entries_.end();
118 }
119
120 //-----------------------------------------------------------------------------
121 // Name : size ()
125 //-----------------------------------------------------------------------------
126 decltype(auto) size() const
127 {
128 if(should_refresh())
129 {
130 refresh();
131 }
132 return entries_.size();
133 }
134
135 //-----------------------------------------------------------------------------
136 // Name : at ()
140 //-----------------------------------------------------------------------------
141 decltype(auto) at(size_t idx) const
142 {
143 return entries_.at(idx);
144 }
145 decltype(auto) operator[](size_t idx) const
146 {
147 return entries_[idx];
148 }
149
150 //-----------------------------------------------------------------------------
151 // Name : refresh ()
158 //-----------------------------------------------------------------------------
159 void refresh() const
160 {
161 entries_.clear();
162
163 fs::error_code err;
164 iterator_t it(path_, err);
165 for(const auto& p : it)
166 {
167 // Apply pattern filter if enabled
168 if(!filter_.should_include(p.path()))
169 {
170 continue;
171 }
172
173 entries_.emplace_back();
174 auto& cache_entry = entries_.back();
175 cache_entry.entry = p;
176 const auto& absolute_path = cache_entry.entry.path();
177 auto filename = absolute_path.filename();
178 cache_entry.protocol_path = fs::convert_to_protocol(absolute_path).generic_string();
179 cache_entry.filename = absolute_path.filename().string();
180 cache_entry.extension = filename.extension().string();
181 cache_entry.stem = filename.stem().string();
182 }
183
184 std::sort(std::begin(entries_),
185 std::end(entries_),
186 [](const auto& lhs, const auto& rhs)
187 {
188 return fs::is_directory(lhs.entry.status()) > fs::is_directory(rhs.entry.status());
189 });
190
191 should_refresh_ = false;
192 }
193
194 const fs::path& get_path() const
195 {
196 return path_;
197 }
198
199 void set_path(const fs::path& path, const fs::pattern_filter& filter)
200 {
201 if(path_ == path)
202 {
203 return;
204 }
205 unwatch();
206 path_ = path;
207 filter_ = filter;
208 should_refresh_ = true;
209 watch();
210 }
211
212 void set_scan_frequency(clock_t::duration scan_frequency)
213 {
214 if(scan_frequency_ == scan_frequency)
215 {
216 return;
217 }
218 unwatch();
219 scan_frequency_ = scan_frequency;
220 should_refresh_ = true;
221 watch();
222 }
223
225 {
226 directory_entry entry;
227 std::string filename;
228 std::string stem;
229 std::string extension;
230 std::string protocol_path;
231 };
232
233private:
234 //-----------------------------------------------------------------------------
235 // Name : should_refresh ()
239 //-----------------------------------------------------------------------------
240 bool should_refresh() const
241 {
242 return should_refresh_;
243 }
244
245 void watch()
246 {
247 using namespace std::literals;
248 constexpr bool is_recursive = std::is_same<iterator_t, recursive_directory_iterator>::value;
249
250 watch_id_ = watcher::watch(path_,
251 filter_,
252 is_recursive,
253 false,
254 scan_frequency_,
255 [this](const auto&, bool)
256 {
257 should_refresh_ = true;
258 });
259
260 }
261 void unwatch()
262 {
263 watcher::unwatch(watch_id_);
264 }
265
267 fs::path path_;
268
269 clock_t::duration scan_frequency_ = std::chrono::milliseconds(500);
271 mutable std::vector<cache_entry> entries_;
273 mutable std::atomic_bool should_refresh_ = {true};
275 std::uint64_t watch_id_ = 0;
277 pattern_filter filter_;
278};
279
282
291template<typename Iterator = recursive_directory_iterator>
292auto make_filtered_cache(const fs::path& path,
293 const std::vector<std::string>& includes,
294 const std::vector<std::string>& excludes = {},
295 std::chrono::steady_clock::duration scan_frequency = std::chrono::milliseconds(500))
297{
298 return cache<Iterator>(path, make_pattern_filter(includes, excludes), scan_frequency);
299}
300
324} // namespace fs
void * load(bx::FileReaderI *_reader, bx::AllocatorI *_allocator, const char *_filePath, uint32_t *_size)
void set_scan_frequency(clock_t::duration scan_frequency)
Definition cache.hpp:212
cache & operator=(cache &&rhs) noexcept
Definition cache.hpp:76
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:194
cache()=default
void set_path(const fs::path &path, const fs::pattern_filter &filter)
Definition cache.hpp:199
decltype(auto) size() const
Returns the size for the underlying cached container.
Definition cache.hpp:126
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:141
decltype(auto) begin() const
Returns the begin iterator for the underlying cached container and also refreshes the container if ne...
Definition cache.hpp:100
decltype(auto) end() const
Returns the end iterator for the underlying cached container.
Definition cache.hpp:115
cache(const cache &rhs)
Definition cache.hpp:43
cache & operator=(const cache &rhs)
Definition cache.hpp:63
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:159
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:426
static auto watch(const fs::path &path, const pattern_filter &filter, bool recursive, bool initial_list, clock_t::duration poll_interval, notify_callback callback) -> std::uint64_t
Watches a file or directory for modification and call back the specified std::function....
Definition watcher.cpp:416
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:292
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:225
std::string extension
Definition cache.hpp:229
std::string filename
Definition cache.hpp:227
std::string protocol_path
Definition cache.hpp:230
std::string stem
Definition cache.hpp:228
directory_entry entry
Definition cache.hpp:226
cache_t cache
Definition uniform.cpp:15