sandbox
Loading...
Searching...
No Matches
memory_filesystem.hpp
1// SPDX-License-Identifier: MIT
2#ifndef LIBSBX_FILESYSTEM_MEMORY_FILESYSTEM_HPP_
3#define LIBSBX_FILESYSTEM_MEMORY_FILESYSTEM_HPP_
4
5#include <unordered_map>
6#include <vector>
7#include <string>
8#include <memory>
9#include <mutex>
10#include <algorithm>
11
12#include <libsbx/utility/lockable.hpp>
13
14#include <libsbx/filesystem/filesystem_base.hpp>
15#include <libsbx/filesystem/memory_file.hpp>
16
17namespace sbx::filesystem {
18
19template<utility::lockable Lockable>
21
22public:
23
24 using lockable_type = Lockable;
25
26 explicit basic_memory_filesystem(std::string alias_path)
27 : _alias_path(std::move(alias_path)) {}
28
29 ~basic_memory_filesystem() override {
30 shutdown();
31 }
32
33 [[nodiscard]] auto initialize() -> bool override {
34 auto lock = std::scoped_lock{_mutex};
35
36 if (_initialized) {
37 return true;
38 }
39
40 _initialized = true;
41
42 return true;
43 }
44
45 auto shutdown() -> void override {
46 auto lock = std::scoped_lock{_mutex};
47
48 _files.clear();
49 _initialized = false;
50 }
51
52 [[nodiscard]] auto is_initialized() const -> bool override {
53 auto lock = std::scoped_lock{_mutex};
54
55 return _initialized;
56 }
57
58 [[nodiscard]] auto base_path() const -> const std::string& override {
59 auto lock = std::scoped_lock{_mutex};
60
61 return _alias_path;
62 }
63
64 [[nodiscard]] auto virtual_path() const -> const std::string& override {
65 auto lock = std::scoped_lock{_mutex};
66
67 return _alias_path;
68 }
69
70 [[nodiscard]] auto files() const -> files_list override {
71 auto lock = std::scoped_lock{_mutex};
72
73 auto list = files_list{};
74 list.reserve(_files.size());
75
76 for (const auto& [path, entry] : _files) {
77 list.push_back(entry.info);
78 }
79
80 return list;
81 }
82
83 [[nodiscard]] auto is_read_only() const -> bool override {
84 return false;
85 }
86
87 [[nodiscard]] auto open_file(const std::string& path, const file_base::mode mode) -> file_ptr override {
88 auto lock = std::scoped_lock{_mutex};
89
90 auto info = file_info{_alias_path, _alias_path, path};
91
92 auto [entry, inserted] = _files.try_emplace(path, file_entry{info, std::make_shared<detail::memory_file_object<lockable_type>>()});
93
94 auto& file_entry = entry->second;
95
96 if (!file_entry.object) {
97 file_entry.object = std::make_shared<detail::memory_file_object<lockable_type>>();
98 }
99
100 auto file = std::make_shared<basic_memory_file<lockable_type>>(file_info{file_entry.info}, detail::memory_file_object_ptr<lockable_type>{file_entry.object});
101
102 if (!file || !file->open(mode)) {
103 return nullptr;
104 }
105
106 file_entry.opened_handles.push_back(file);
107
108 return file;
109 }
110
111 auto close_file(const file_ptr& file) -> void override {
112 auto lock = std::scoped_lock{_mutex};
113
114 _cleanup_handles(file);
115 }
116
117 [[nodiscard]] auto create_file(const std::string& path) -> file_ptr override {
118 return open_file(path, file_base::mode::read_write | file_base::mode::truncate);
119 }
120
121 [[nodiscard]] auto remove_file(const std::string& path) -> bool override {
122 auto lock = std::scoped_lock{_mutex};
123
124 auto entry = _files.find(path);
125
126 if (entry == _files.end()) {
127 return false;
128 }
129
130 _cleanup_handles();
131 _files.erase(entry);
132
133 return true;
134 }
135
136 [[nodiscard]] auto copy_file(const std::string& source, const std::string& destination, const bool overwrite = false) -> bool override {
137 auto lock = std::scoped_lock{_mutex};
138
139 auto source_entry = _files.find(source);
140
141 if (source_entry == _files.end()) {
142 return false;
143 }
144
145 auto destination_entry = _files.find(destination);
146
147 if (destination_entry != _files.end() && !overwrite) {
148 return false;
149 }
150
151 if (destination_entry != _files.end()) {
152 _files.erase(destination_entry);
153 }
154
155 auto new_object = detail::memory_file_object_ptr<lockable_type>{};
156
157 if (source_entry->second.object) {
158 new_object = std::make_shared<detail::memory_file_object<lockable_type>>(*source_entry->second.object);
159 } else {
160 new_object = std::make_shared<detail::memory_file_object<lockable_type>>();
161 }
162
163 auto info = file_info{_alias_path, _alias_path, destination};
164
165 _files.emplace(destination, file_entry{info, std::move(new_object)});
166
167 return true;
168 }
169
170 [[nodiscard]] auto rename_file(const std::string& source, const std::string& destination) -> bool override {
171 if (!copy_file(source, destination, false)) {
172 return false;
173 }
174
175 return remove_file(source);
176 }
177
178 [[nodiscard]] auto exists(const std::string& path) const -> bool override {
179 auto lock = std::scoped_lock{_mutex};
180 return _files.find(path) != _files.end();
181 }
182
183private:
184
185 struct file_entry {
186
187 using weak_handle = std::weak_ptr<basic_memory_file<lockable_type>>;
188
189 file_info info;
190 detail::memory_file_object_ptr<lockable_type> object;
191 std::vector<weak_handle> opened_handles;
192
193 void cleanup(const file_ptr& exclude = nullptr) {
194 opened_handles.erase(std::remove_if(opened_handles.begin(), opened_handles.end(), [&](const weak_handle& handle) {
195 return handle.expired() || handle.lock() == exclude;
196 }), opened_handles.end());
197 }
198
199 }; // struct file_entry
200
201 auto _cleanup_handles(const file_ptr& to_close = nullptr) -> void {
202 if (to_close) {
203 const auto& path = to_close->info().virtual_path();
204
205 auto entry = _files.find(path);
206
207 if (entry != _files.end()) {
208 to_close->close();
209 }
210 }
211
212 for (auto& [_, entry] : _files) {
213 entry.cleanup(to_close);
214 }
215 }
216
217private:
218
219 std::string _alias_path;
220 bool _initialized{false};
221
222 mutable lockable_type _mutex;
223 std::unordered_map<std::string, file_entry> _files;
224
225}; // class basic_memory_filesystem
226
229
231
232} // namespace sbx::filesystem
233
234#endif // LIBSBX_FILESYSTEM_MEMORY_FILESYSTEM_HPP_
Definition: memory_filesystem.hpp:20
Definition: file_info.hpp:10
Definition: filesystem_base.hpp:14