#include "slang-memory-file-system.h" // For Path:: #include "slang-io.h" #include "slang-blob.h" #include "slang-implicit-directory-collector.h" namespace Slang { MemoryFileSystem::MemoryFileSystem() { m_rootEntry.initDirectory("/"); } void* MemoryFileSystem::getInterface(const Guid& guid) { if ( guid == ISlangUnknown::getTypeGuid() || guid == ISlangCastable::getTypeGuid() || guid == ISlangFileSystem::getTypeGuid() || guid == ISlangFileSystemExt::getTypeGuid() || guid == ISlangMutableFileSystem::getTypeGuid()) { return static_cast(this); } return nullptr; } void* MemoryFileSystem::getObject(const Guid& guid) { SLANG_UNUSED(guid); return nullptr; } void* MemoryFileSystem::castAs(const Guid& guid) { if (auto ptr = getInterface(guid)) { return ptr; } return getObject(guid); } void MemoryFileSystem::_clear() { m_entries = Dictionary(); } MemoryFileSystem::Entry* MemoryFileSystem::_getEntryFromCanonicalPath(const String& canonicalPath) { if (canonicalPath == toSlice(".")) { return &m_rootEntry; } else { return m_entries.TryGetValue(canonicalPath); } } MemoryFileSystem::Entry* MemoryFileSystem::_getEntryFromPath(const char* path, String* outPath) { StringBuilder buffer; if (SLANG_FAILED(_getCanonical(path, buffer))) { return nullptr; } if (outPath) { *outPath = buffer; } return _getEntryFromCanonicalPath(buffer); } SlangResult MemoryFileSystem::_loadFile(const char* path, Entry** outEntry) { *outEntry = nullptr; Entry* entry = _getEntryFromPath(path); if (entry == nullptr || entry->m_type != SLANG_PATH_TYPE_FILE) { return SLANG_E_NOT_FOUND; } *outEntry = entry; return SLANG_OK; } SlangResult MemoryFileSystem::loadFile(char const* path, ISlangBlob** outBlob) { Entry* entry; SLANG_RETURN_ON_FAIL(_loadFile(path, &entry)); ISlangBlob* contents = entry->m_contents; contents->addRef(); *outBlob = contents; return SLANG_OK; } SlangResult MemoryFileSystem::getFileUniqueIdentity(const char* path, ISlangBlob** outUniqueIdentity) { return getPath(PathKind::Canonical, path, outUniqueIdentity); } SlangResult MemoryFileSystem::calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut) { String combinedPath; switch (fromPathType) { case SLANG_PATH_TYPE_FILE: { combinedPath = Path::combine(Path::getParentDirectory(fromPath), path); break; } case SLANG_PATH_TYPE_DIRECTORY: { combinedPath = Path::combine(fromPath, path); break; } } *pathOut = StringBlob::moveCreate(combinedPath).detach(); return SLANG_OK; } SlangResult MemoryFileSystem::getPathType(const char* path, SlangPathType* outPathType) { if (auto entry = _getEntryFromPath(path)) { *outPathType = entry->m_type; return SLANG_OK; } // Not found return SLANG_E_NOT_FOUND; } SlangResult MemoryFileSystem::getPath(PathKind kind, const char* path, ISlangBlob** outPath) { switch (kind) { case PathKind::Simplified: { String simplifiedPath = Path::simplify(path); *outPath = StringBlob::moveCreate(simplifiedPath).detach(); return SLANG_OK; } case PathKind::Display: case PathKind::Canonical: { StringBuilder buffer; SLANG_RETURN_ON_FAIL(_getCanonical(path, buffer)); *outPath = StringBlob::moveCreate(buffer).detach(); return SLANG_OK; } default: break; } return SLANG_E_NOT_AVAILABLE; } SlangResult MemoryFileSystem::enumeratePathContents(const char* path, FileSystemContentsCallBack callback, void* userData) { String canonicalPath; Entry* entry = _getEntryFromPath(path, &canonicalPath); if (!entry || entry->m_type != SLANG_PATH_TYPE_DIRECTORY) { return SLANG_E_NOT_FOUND; } ImplicitDirectoryCollector collector(canonicalPath, true); // If it is a directory, we need to see if there is anything in it for (const auto& pair : m_entries) { const Entry* childEntry = &pair.Value; collector.addPath(childEntry->m_type, childEntry->m_canonicalPath.getUnownedSlice()); } return collector.enumerate(callback, userData); } SlangResult MemoryFileSystem::saveFile(const char* path, const void* data, size_t size) { Entry* entry; SLANG_RETURN_ON_FAIL(_requireFile(path, &entry)); auto contents = RawBlob::create(data, size); entry->setContents(size, contents); return SLANG_OK; } SlangResult MemoryFileSystem::saveFileBlob(const char* path, ISlangBlob* dataBlob) { if (!dataBlob) { return SLANG_E_INVALID_ARG; } Entry* entry; SLANG_RETURN_ON_FAIL(_requireFile(path, &entry)); entry->setContents(dataBlob->getBufferSize(), dataBlob); return SLANG_OK; } SlangResult MemoryFileSystem::_getCanonical(const char* path, StringBuilder& outCanonicalPath) { StringBuilder canonicalPath; SLANG_RETURN_ON_FAIL(Path::simplify(UnownedStringSlice(path), Path::SimplifyStyle::AbsoluteOnlyAndNoRoot, outCanonicalPath)); return SLANG_OK; } SlangResult MemoryFileSystem::_getCanonicalWithExistingParent(const char* path, StringBuilder& outCanonicalPath) { SLANG_RETURN_ON_FAIL(_getCanonical(path, outCanonicalPath)); // Get the parent to the canoncial path (which should be canonical itself) auto parent = Path::getParentDirectory(outCanonicalPath); if (parent.getLength()) { // The parent has to be a directory Entry* parentEntry = _getEntryFromCanonicalPath(parent); if (parentEntry == nullptr || parentEntry->m_type != SLANG_PATH_TYPE_DIRECTORY) { return SLANG_E_NOT_FOUND; } } return SLANG_OK; } SlangResult MemoryFileSystem::_requireFile(const char* path, Entry** outEntry) { *outEntry = nullptr; StringBuilder canonicalPath; SLANG_RETURN_ON_FAIL(_getCanonicalWithExistingParent(path, canonicalPath)); Entry* foundEntry = _getEntryFromCanonicalPath(canonicalPath); if (foundEntry) { if (foundEntry->m_type != SLANG_PATH_TYPE_FILE) { // Can only set if it's already a file, if it's anything else it's an error return SLANG_FAIL; } } else { Entry entry; entry.initFile(canonicalPath); m_entries.Add(canonicalPath, entry); foundEntry = _getEntryFromCanonicalPath(canonicalPath); } // It must be found and be a file SLANG_ASSERT(foundEntry && foundEntry->m_type == SLANG_PATH_TYPE_FILE && foundEntry->m_canonicalPath == canonicalPath); *outEntry = foundEntry; return SLANG_OK; } SlangResult MemoryFileSystem::remove(const char* path) { String canonicalPath; Entry* entry = _getEntryFromPath(path, &canonicalPath); // If there is an entry and not the root of the file system if (entry && entry != &m_rootEntry) { if (entry->m_type == SLANG_PATH_TYPE_DIRECTORY) { ImplicitDirectoryCollector collector(canonicalPath); // If it is a directory, we need to see if there is anything in it for (const auto& pair : m_entries) { const Entry* childEntry = &pair.Value; collector.addPath(childEntry->m_type, childEntry->m_canonicalPath.getUnownedSlice()); if (collector.hasContent()) { // Directory is not empty return SLANG_FAIL; } } } // Reset so doesn't hold references/keep memory in scope entry->reset(); m_entries.Remove(canonicalPath); return SLANG_OK; } return SLANG_E_NOT_FOUND; } SlangResult MemoryFileSystem::createDirectory(const char* path) { StringBuilder canonicalPath; SLANG_RETURN_ON_FAIL(_getCanonicalWithExistingParent(path, canonicalPath)); if (_getEntryFromCanonicalPath(canonicalPath)) { return SLANG_FAIL; } Entry entry; entry.initDirectory(canonicalPath); m_entries.Add(canonicalPath, entry); return SLANG_OK; } } // namespace Slang