xref: /aosp_15_r20/external/executorch/backends/apple/coreml/runtime/inmemoryfs/inmemory_filesystem.hpp (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
1 //
2 // inmemory_filesystem.hpp
3 //
4 // Copyright © 2024 Apple Inc. All rights reserved.
5 //
6 // Please refer to the license found in the LICENSE file in the root directory of the source tree.
7 
8 #pragma once
9 
10 #include <functional>
11 #include <memory>
12 #include <optional>
13 #include <stdio.h>
14 #include <string>
15 #include <system_error>
16 
17 #include "inmemory_filesystem_metadata.hpp"
18 #include "memory_buffer.hpp"
19 
20 namespace inmemoryfs {
21 
22 /// A class representing an in-memory file system.
23 class InMemoryFileSystem final {
24 public:
25     /// Error codes for `InMemoryFileSystem`.
26     enum class ErrorCode: int8_t {
27         DirectoryExists = 1,   // If the path already exists.
28         ItemNotFound,          // If the path does not exist.
29         ItemExists,            // If an item at the path already exists.
30         DirectoryExpected,     // If path is not a directory.
31         FileExpected,          // If the path is not a file.
32     };
33 
34     /// Options for loading file content.
35     enum class FileLoadOption: int8_t {
36         Malloc = 1,   // Copy file contents into memory.
37         MMap,         // Memory map file contents.
38         LazyMMap      // Memory map file contents but lazily.
39     };
40 
41     /// The error category for `InMemoryFileSystem`.
42     struct ErrorCategory final: public std::error_category {
43     public:
nameinmemoryfs::InMemoryFileSystem::ErrorCategory44         inline const char* name() const noexcept override {
45             return "InMemoryFileSystem";
46         }
47 
48         std::string message(int code) const override;
49     };
50 
51     struct Attributes {
52         time_t modificationTime;
53 
Attributesinmemoryfs::InMemoryFileSystem::Attributes54         inline Attributes() noexcept:
55         modificationTime(time(0))
56         {}
57     };
58 
59     using MetadataWriter = std::function<bool(const InMemoryFileSystemMetadata&, std::ostream&)>;
60     using MetadataWriterInMemory = std::function<size_t(const InMemoryFileSystemMetadata&, void *)>;
61     using MetadataReader = std::function<std::optional<InMemoryFileSystemMetadata>(std::istream&)>;
62 
63     /// A class representing an in-memory node. This could either be a file node or a directory node.
64     class InMemoryNode {
65     public:
66         /// The node kind.
67         enum class Kind: uint8_t {
68             File = 0,   /// Node is a File.
69             Directory   /// Node is a Directory.
70         };
71 
72         /// Constructs an in-memory node instance.
73         ///
74         /// @param name The name of the Node. It must be unique in the enclosing Directory.
75         /// @param attributes   The node attributes.
76         /// @param kind   The node kind.
InMemoryNode(std::string name,Attributes attributes,Kind kind)77         inline InMemoryNode(std::string name, Attributes attributes, Kind kind) noexcept:
78         name_(std::move(name)),
79         attributes_(std::move(attributes)),
80         kind_(kind)
81         {}
82 
83         InMemoryNode(InMemoryNode const&) = delete;
84         InMemoryNode& operator=(InMemoryNode const&) = delete;
85 
~InMemoryNode()86         inline virtual ~InMemoryNode() {}
87 
88         /// Returns the node attributes.
attributes() const89         inline Attributes attributes() const noexcept {
90             return attributes_;
91         }
92 
93         /// Sets the node attributes.
94         ///
95         /// @param attributes The node attributes.
set_attributes(Attributes attributes)96         inline void set_attributes(Attributes attributes) noexcept {
97             attributes_ = std::move(attributes);
98         }
99 
100         /// Returns the node kind, possible values are `File` and `Directory`.
kind() const101         inline Kind kind() const noexcept {
102             return kind_;
103         }
104 
105         /// Returns the name of the node.
name() const106         inline const std::string& name() const noexcept {
107             return name_;
108         }
109 
set_name(std::string name)110         inline void set_name(std::string name) noexcept {
111             std::swap(name_, name);
112         }
113 
114         /// Returns `true` if the node is a directory otherwise `false`.
isDirectory() const115         inline bool isDirectory() const noexcept {
116             switch (kind_) {
117                 case InMemoryFileSystem::InMemoryNode::Kind::Directory:
118                     return true;
119                 default:
120                     return false;
121             }
122         }
123 
124         /// Returns `true` if the node is a file otherwise `false`.
isFile() const125         inline bool isFile() const noexcept {
126             return !isDirectory();
127         }
128 
129     private:
130         std::string name_;
131         InMemoryFileSystem::Attributes attributes_;
132         const Kind kind_;
133     };
134 
135     /// Constructs an`InMemoryFileSystem` instance with an empty root and the specified name.
136     ///
137     /// @param rootName The name of the root node.
138     explicit InMemoryFileSystem(std::string rootName = "root") noexcept;
139 
140     /// Constructs an`InMemoryFileSystem` instance with the specified root.
141     ///
142     /// @param root The root node.
InMemoryFileSystem(std::unique_ptr<InMemoryNode> root)143     explicit InMemoryFileSystem(std::unique_ptr<InMemoryNode> root) noexcept
144     :root_(std::move(root))
145     {}
146 
147     InMemoryFileSystem(InMemoryFileSystem const&) = delete;
148     InMemoryFileSystem& operator=(InMemoryFileSystem const&) = delete;
149 
~InMemoryFileSystem()150     virtual ~InMemoryFileSystem() {}
151 
152     /// Returns the root.
root() const153     InMemoryNode *root() const noexcept {
154         return root_.get();
155     }
156 
157     /// Checks if the node at the specified path is a directory.
158     ///
159     /// @param canonical_path   The path components from the root.
160     /// @retval `true` if the node at the specified path is a directory otherwise `false`.
161     bool is_directory(const std::vector<std::string>& canonical_path) noexcept;
162 
163     /// Checks if the node at the specified path is a file.
164     ///
165     /// @param canonical_path   The path components from the root.
166     /// @retval `true` if the node at the specified path is a file otherwise `false`.
167     bool is_file(const std::vector<std::string>& canonical_path) noexcept;
168 
169     /// Checks if the node at the specified path exists.
170     ///
171     /// @param canonical_path   The path components from the root.
172     /// @retval `true` if the node at the specified path exists.
173     bool exists(const std::vector<std::string>& canonical_path) const noexcept;
174 
175     /// Retrieves the canonical path of all the child nodes at the specified path. The node
176     /// at the specified path must be a directory otherwise it returns an empty vector with the `error`
177     /// populated.
178     ///
179     /// @param canonical_path  The path components from the root.
180     /// @param error   On failure, error is populated with the failure reason.
181     /// @retval paths to all the items at the specified path.
182     std::vector<std::vector<std::string>> get_item_paths(const std::vector<std::string>& canonical_path,
183                                                          std::error_code& error) const noexcept;
184 
185     /// Retrieves the attributes of the item at the specified path.
186     ///
187     /// @param canonical_path  The path components from the root.
188     /// @param error   On failure, error is populated with the failure reason.
189     /// @retval The item attributes at the specified path.
190     std::optional<Attributes> get_attributes(const std::vector<std::string>& canonical_path,
191                                              std::error_code& error) const noexcept;
192 
193     /// Retrieves the contents of the file at the specified path.
194     ///
195     /// @param canonical_path  The path components from the root.
196     /// @param error   On failure, error is populated with the failure reason.
197     /// @retval The file contents or `nullptr` if the item at the specified path is not a file.
198     std::shared_ptr<MemoryBuffer> get_file_content(const std::vector<std::string>& canonical_path,
199                                                    std::error_code& error) const noexcept;
200 
201     /// Creates an in-memory directory at the specified path.
202     ///
203     /// @param canonical_path  The path components from the root.
204     /// @param attributes  The directory attributes.
205     /// @param create_intermediate_directories   If this is `true` then the method will also create intermediate directories if not present.
206     /// @param error   On failure, error is populated with the failure reason.
207     /// @retval `true` if the directory is created otherwise `false`.
208     bool make_directory(const std::vector<std::string>& canonical_path,
209                         Attributes attributes,
210                         bool create_intermediate_directories,
211                         std::error_code& error) noexcept;
212 
213     /// Creates an in-memory file at the specified path.
214     ///
215     /// @param canonical_path  The path components from the root.
216     /// @param buffer  The file contents.
217     /// @param attributes  The file attributes.
218     /// @param overwrite   If this is `true` then the the method will overwrite the contents at the specified path.
219     /// @param error   On failure, error is populated with the failure reason.
220     /// @retval `true` if the file is created otherwise `false`.
221     bool make_file(const std::vector<std::string>& canonical_path,
222                    std::shared_ptr<MemoryBuffer> buffer,
223                    Attributes attributes,
224                    bool overwrite,
225                    std::error_code& error) noexcept;
226 
227     /// Removes the item at the specified path.
228     ///
229     /// @param canonical_path  The path components from the root.
230     /// @param error   On failure, error is populated with the failure reason.
231     /// @retval `true` if the item is removed otherwise `false`.
232     bool remove_item(const std::vector<std::string>& canonical_path,
233                      std::error_code& error) noexcept;
234 
235     /// Sets the attributes at the specified path.
236     ///
237     /// @param canonical_path  The path components from the root.
238     /// @param error   On failure, error is populated with the failure reason.
239     /// @retval `true` if the attributes are updated otherwise `false`.
240     bool set_attributes(const std::vector<std::string>& canonical_path,
241                         Attributes attributes,
242                         std::error_code& error) noexcept;
243 
244     /// Writes the item at the specified path to the filesystem.
245     ///
246     /// @param canonical_path  The path components from the root.
247     /// @param dst_path  The filesystem path where the item contents will be saved.
248     /// @param recursive   If this is `true` then the the method will recursively write the contents of nested directory items.
249     /// @param error   On failure, error is populated with the failure reason.
250     /// @retval `true` if the write succeeded otherwise `false`.
251     bool write_item_to_disk(const std::vector<std::string>& canonical_path,
252                             const std::string& dst_path,
253                             bool recursive,
254                             std::error_code& error) const noexcept;
255 
256     /// Renames the item at the specified path, if there is already an item with the same name then
257     /// the rename would fail.
258     ///
259     /// @param canonical_path  The path components from the root.
260     /// @param name The new name,
261     /// @param error   On failure, error is populated with the failure reason.
262     /// @retval `true` if the write succeeded otherwise `false`.
263     bool rename_item(const std::vector<std::string>& canonical_path,
264                      const std::string& name,
265                      std::error_code& error) noexcept;
266 
267     /// Creates  an`InMemoryFileSystem` from the filesystem path.
268     ///
269     /// The structure of the `InMemoryFileSystem` is identical to the structure of the filesystem at the
270     /// specified path.
271     ///
272     /// @param path  The filesystem path.
273     /// @param option The loading option.
274     /// @param error   On failure, error is populated with the failure reason.
275     /// @retval The `InMemoryFileSystem` instance if the construction succeeded otherwise `nullptr`.
276     static std::unique_ptr<InMemoryFileSystem> make_from_directory(const std::string& path,
277                                                                    FileLoadOption option,
278                                                                    std::error_code& error) noexcept;
279 
280     /// Serializes the item at the specified path and writes it to the stream.
281     ///
282     /// The structure of the `InMemoryFileSystem` is identical to the structure of the filesystem at the
283     /// specified path.
284     ///
285     /// @param canonical_path  The path components from the root.
286     /// @param alignment  The alignment of the offset where an item is written to the stream.
287     /// @param metadata_writer The function to use when serializing the filesystem metadata.
288     /// @param ostream   The output stream.
289     /// @param error   On failure, error is populated with the failure reason.
290     /// @retval `true` if the serialized bytes were written to `ostream` otherwise `false`.
291     bool serialize(const std::vector<std::string>& canonical_path,
292                    size_t alignment,
293                    const MetadataWriter& metadata_writer,
294                    std::ostream& ostream,
295                    std::error_code& error) const noexcept;
296 
297     /// Serializes the item at the specified path and writes it to the stream.
298     ///
299     /// The structure of the `InMemoryFileSystem` is identical to the structure of the filesystem at the
300     /// specified path.
301     ///
302     /// @param canonical_path  The path components from the root.
303     /// @param alignment  The alignment of the offset where an item is written to the stream.
304     /// @param metadata_writer The function to use when serializing the filesystem metadata.
305     /// @param dst   The destination pointer.
306     /// @param error   On failure, error is populated with the failure reason.
307     /// @retval `true` if the serialized bytes were written to `ostream` otherwise `false`.
308     bool serialize(const std::vector<std::string>& canonical_path,
309                    size_t alignment,
310                    const MetadataWriterInMemory& metadata_writer,
311                    void *dst,
312                    std::error_code& error) const noexcept;
313 
314     /// Computes the size of the buffer that would be needed to serialized the item at the specified path.
315     ///
316     /// @param canonical_path  The path components from the root.
317     /// @param alignment  The offset alignment where an item is written to the stream.
318     /// @param metadata_writer The function to use when serializing the filesystem metadata.
319     /// @retval The size of the buffer that will be needed to write the item at the specified path.
320     size_t get_buffer_size_for_serialization(const std::vector<std::string>& canonical_path,
321                                              size_t alignment,
322                                              const MetadataWriter& metadata_writer) const noexcept;
323 
324     /// Constructs an `InMemoryFileSystem` instance from the buffer contents.
325     ///
326     /// @param buffer  The memory buffer.
327     /// @param metadata_reader The function to use when deserializing the filesystem metadata.
328     /// @retval The constructed `InMemoryFileSystem` or `nullptr` if the deserialization failed.
329     static std::unique_ptr<InMemoryFileSystem> make_from_buffer(const std::shared_ptr<MemoryBuffer>& buffer,
330                                                                 const MetadataReader& metadata_reader) noexcept;
331 
332 private:
333     const std::unique_ptr<InMemoryNode> root_;
334 };
335 
336 /// Constructs an `error_code` from a `InMemoryFileSystem::ErrorCode`.
make_error_code(InMemoryFileSystem::ErrorCode code)337 inline std::error_code make_error_code(InMemoryFileSystem::ErrorCode code) {
338     static InMemoryFileSystem::ErrorCategory errorCategory;
339     return {static_cast<int>(code), errorCategory};
340 }
341 
342 }; // namespace inmemoryfs
343 
344 namespace std {
345 
346 template <> struct is_error_code_enum<inmemoryfs::InMemoryFileSystem::ErrorCode> : true_type {};
347 
348 } // namespace std
349