#ifndef SLANG_CORE_MEMORY_FILE_SYSTEM_H #define SLANG_CORE_MEMORY_FILE_SYSTEM_H #include "slang-basic.h" #include "slang-com-object.h" #include "slang-com-ptr.h" namespace Slang { /* MemoryFileSystem is an implementation of ISlangMutableFileSystem that stores file contents in 'blobs' (typically) in memory. A derived class can change how the contents of the contents blob is interpretted (so for example the RiffFileSystem is implemented such that the Entry.m_contents is the files contents compressed). The implementation uses a map to store the file/directory based on their canonical path. This makes access relatively fast and simple - an access only requires a path being converted into a canonical path, and then a lookup. Whilst this makes typical access fast, it means doing an enumeration of a directory slower as it requires traversing all entries to find which are in the path. This is in contrast with an implementation that held items in directories 'objects'. In that scenario the path through the hierarchy would need to be traversed to find the item. Finding all of the items in a directory is very fast - it's all the items held in the the directory 'object'. TODO(JS): * We may want to make saveFile take a blob, or have a version that does. Doing so would allow the application to handle memory management around the blob. */ class MemoryFileSystem : public ComBaseObject, public ISlangMutableFileSystem { public: // ISlangUnknown SLANG_COM_BASE_IUNKNOWN_ALL // ISlangCastable virtual SLANG_NO_THROW void* SLANG_MCALL castAs(const Guid& guid) SLANG_OVERRIDE; // ISlangFileSystem virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadFile(char const* path, ISlangBlob** outBlob) SLANG_OVERRIDE; // ISlangFileSystemExt virtual SLANG_NO_THROW SlangResult SLANG_MCALL getFileUniqueIdentity(const char* path, ISlangBlob** uniqueIdentityOut) SLANG_OVERRIDE; virtual SLANG_NO_THROW SlangResult SLANG_MCALL calcCombinedPath( SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut) SLANG_OVERRIDE; virtual SLANG_NO_THROW SlangResult SLANG_MCALL getPathType(const char* path, SlangPathType* pathTypeOut) SLANG_OVERRIDE; virtual SLANG_NO_THROW SlangResult SLANG_MCALL getPath(PathKind pathKind, const char* path, ISlangBlob** outPath) SLANG_OVERRIDE; virtual SLANG_NO_THROW void SLANG_MCALL clearCache() SLANG_OVERRIDE {} virtual SLANG_NO_THROW SlangResult SLANG_MCALL enumeratePathContents( const char* path, FileSystemContentsCallBack callback, void* userData) SLANG_OVERRIDE; virtual SLANG_NO_THROW OSPathKind SLANG_MCALL getOSPathKind() SLANG_OVERRIDE { return OSPathKind::None; } // ISlangModifyableFileSystem virtual SLANG_NO_THROW SlangResult SLANG_MCALL saveFile(const char* path, const void* data, size_t size) SLANG_OVERRIDE; virtual SLANG_NO_THROW SlangResult SLANG_MCALL saveFileBlob(const char* path, ISlangBlob* dataBlob) SLANG_OVERRIDE; virtual SLANG_NO_THROW SlangResult SLANG_MCALL remove(const char* path) SLANG_OVERRIDE; virtual SLANG_NO_THROW SlangResult SLANG_MCALL createDirectory(const char* path) SLANG_OVERRIDE; /// Ctor MemoryFileSystem(); protected: struct Entry { void reset() { m_type = SLANG_PATH_TYPE_FILE; m_canonicalPath = String(); m_uncompressedSizeInBytes = 0; m_contents.setNull(); } void initDirectory(const String& canonicalPath) { m_type = SLANG_PATH_TYPE_DIRECTORY; m_canonicalPath = canonicalPath; m_uncompressedSizeInBytes = 0; m_contents.setNull(); } void initFile(const String& canonicalPath, size_t uncompressedSize, ISlangBlob* blob) { m_type = SLANG_PATH_TYPE_FILE; m_canonicalPath = canonicalPath; setContents(uncompressedSize, blob); } void initFile(const String& canonicalPath) { m_type = SLANG_PATH_TYPE_FILE; m_canonicalPath = canonicalPath; m_contents.setNull(); m_uncompressedSizeInBytes = 0; } void setContents(size_t uncompressedSize, ISlangBlob* blob) { SLANG_ASSERT(m_type == SLANG_PATH_TYPE_FILE); SLANG_ASSERT(blob); m_uncompressedSizeInBytes = uncompressedSize; m_contents = blob; } SlangPathType m_type; String m_canonicalPath; /// The size as seen on the file system. Might be different from the size of m_contents /// if it's actually being stored in some other representation (such as compressed) size_t m_uncompressedSizeInBytes; ComPtr m_contents; ///< Can be compressed or not }; void* getInterface(const Guid& guid); void* getObject(const Guid& guid); Entry* _getEntryFromPath(const char* path, String* outPath = nullptr); Entry* _getEntryFromCanonicalPath(const String& canonicalPath); /// Creates or returns a file entry for the given path. /// If created the entry is empty. SlangResult _requireFile(const char* path, Entry** outEntry); /// Given the path returns the entry if it's a file, or returns an error SlangResult _loadFile(const char* path, Entry** outEntry); /// Given a path returns a canonical path. /// The canonical path must have *existing* parent paths. SlangResult _getCanonicalWithExistingParent(const char* path, StringBuilder& canonicalPath); /// Given a path returns a canonical path. SlangResult _getCanonical(const char* path, StringBuilder& canonicalPath); /// Clear, ensures any backing memory is also freed void _clear(); // Maps canonical paths to an entries (which could be files or directories) Dictionary m_entries; Entry m_rootEntry; }; } // namespace Slang #endif