diff options
Diffstat (limited to 'source/core/slang-file-system.cpp')
| -rw-r--r-- | source/core/slang-file-system.cpp | 888 |
1 files changed, 888 insertions, 0 deletions
diff --git a/source/core/slang-file-system.cpp b/source/core/slang-file-system.cpp new file mode 100644 index 000000000..992ae4155 --- /dev/null +++ b/source/core/slang-file-system.cpp @@ -0,0 +1,888 @@ +#include "slang-file-system.h" + +#include "../../slang-com-ptr.h" +#include "../core/slang-io.h" +#include "../core/slang-string-util.h" + +namespace Slang +{ + +// Allocate static const storage for the various interface IDs that the Slang API needs to expose +static const Guid IID_ISlangUnknown = SLANG_UUID_ISlangUnknown; +static const Guid IID_ISlangFileSystem = SLANG_UUID_ISlangFileSystem; +static const Guid IID_ISlangFileSystemExt = SLANG_UUID_ISlangFileSystemExt; +static const Guid IID_ISlangMutableFileSystem = SLANG_UUID_ISlangMutableFileSystem; + +static const Guid IID_SlangCacheFileSystem = SLANG_UUID_CacheFileSystem; + +SLANG_FORCE_INLINE static SlangResult _checkExt(FileSystemStyle style) { return Index(style) >= Index(FileSystemStyle::Ext) ? SLANG_OK : SLANG_E_NOT_IMPLEMENTED; } +SLANG_FORCE_INLINE static SlangResult _checkMutable(FileSystemStyle style) { return Index(style) >= Index(FileSystemStyle::Mutable) ? SLANG_OK : SLANG_E_NOT_IMPLEMENTED; } + +SLANG_FORCE_INLINE static bool _canCast(FileSystemStyle style, const Guid& guid) +{ + if (guid == IID_ISlangUnknown || guid == IID_ISlangFileSystem) + { + return true; + } + else if (guid == IID_ISlangFileSystemExt) + { + return Index(style) >= Index(FileSystemStyle::Ext); + } + else if (guid == IID_ISlangMutableFileSystem) + { + return Index(style) >= Index(FileSystemStyle::Mutable); + } + return false; +} + +static FileSystemStyle _getFileSystemStyle(ISlangFileSystem* system, ComPtr<ISlangFileSystem>& out) +{ + SLANG_ASSERT(system); + + FileSystemStyle style = FileSystemStyle::Load; + + if (SLANG_SUCCEEDED(system->queryInterface(IID_ISlangMutableFileSystem, (void**)out.writeRef()))) + { + style = FileSystemStyle::Mutable; + } + else if (SLANG_SUCCEEDED(system->queryInterface(IID_ISlangFileSystemExt, (void**)out.writeRef()))) + { + style = FileSystemStyle::Ext; + } + else + { + style = FileSystemStyle::Load; + out = system; + } + + SLANG_ASSERT(out); + return style; +} + +// Cacluate a combined path, just using Path:: string processing +static SlangResult _calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut) +{ + String relPath; + switch (fromPathType) + { + case SLANG_PATH_TYPE_FILE: + { + relPath = Path::combine(Path::getParentDirectory(fromPath), path); + break; + } + case SLANG_PATH_TYPE_DIRECTORY: + { + relPath = Path::combine(fromPath, path); + break; + } + } + + *pathOut = StringUtil::createStringBlob(relPath).detach(); + return SLANG_OK; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! OSFileSystem !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ + +/* static */OSFileSystem OSFileSystem::g_load(FileSystemStyle::Load); +/* static */OSFileSystem OSFileSystem::g_ext(FileSystemStyle::Ext); +/* static */OSFileSystem OSFileSystem::g_mutable(FileSystemStyle::Mutable); + +ISlangUnknown* OSFileSystem::getInterface(const Guid& guid) +{ + return _canCast(m_style, guid) ? static_cast<ISlangFileSystem*>(this) : nullptr; +} + +static String _fixPathDelimiters(const char* pathIn) +{ +#if SLANG_WINDOWS_FAMILY + return pathIn; +#else + // To allow windows style \ delimiters on other platforms, we convert to our standard delimiter + String path(pathIn); + return StringUtil::calcCharReplaced(pathIn, '\\', Path::kPathDelimiter); +#endif +} + +SlangResult OSFileSystem::getFileUniqueIdentity(const char* pathIn, ISlangBlob** outUniqueIdentity) +{ + SLANG_RETURN_ON_FAIL(_checkExt(m_style)); + + // By default we use the canonical path to uniquely identify a file + return getCanonicalPath(pathIn, outUniqueIdentity); +} + +SlangResult OSFileSystem::getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) +{ + SLANG_RETURN_ON_FAIL(_checkExt(m_style)); + + String canonicalPath; + SLANG_RETURN_ON_FAIL(Path::getCanonical(_fixPathDelimiters(path), canonicalPath)); + *outCanonicalPath = StringUtil::createStringBlob(canonicalPath).detach(); + return SLANG_OK; +} + +SlangResult OSFileSystem::getSimplifiedPath(const char* pathIn, ISlangBlob** outSimplifiedPath) +{ + SLANG_RETURN_ON_FAIL(_checkExt(m_style)); + + String simplifiedPath = Path::simplify(pathIn); + *outSimplifiedPath = StringUtil::createStringBlob(simplifiedPath).detach(); + return SLANG_OK; +} + +SlangResult OSFileSystem::calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut) +{ + SLANG_RETURN_ON_FAIL(_checkExt(m_style)); + + // Don't need to fix delimiters - because combine path handles both path delimiter types + return _calcCombinedPath(fromPathType, fromPath, path, pathOut); +} + +SlangResult SLANG_MCALL OSFileSystem::getPathType(const char* pathIn, SlangPathType* pathTypeOut) +{ + SLANG_RETURN_ON_FAIL(_checkExt(m_style)); + + return Path::getPathType(_fixPathDelimiters(pathIn), pathTypeOut); +} + + +SlangResult OSFileSystem::loadFile(char const* pathIn, ISlangBlob** outBlob) +{ + // Default implementation that uses the `core` libraries facilities for talking to the OS filesystem. + // + // TODO: we might want to conditionally compile these in, so that + // a user could create a build of Slang that doesn't include any OS + // filesystem calls. + + const String path = _fixPathDelimiters(pathIn); + if (!File::exists(path)) + { + return SLANG_E_NOT_FOUND; + } + + ScopedAllocation alloc; + SLANG_RETURN_ON_FAIL(File::readAllBytes(path, alloc)); + *outBlob = RawBlob::moveCreate(alloc).detach(); + return SLANG_OK; +} + +SlangResult OSFileSystem::enumeratePathContents(const char* path, FileSystemContentsCallBack callback, void* userData) +{ + SLANG_RETURN_ON_FAIL(_checkExt(m_style)); + + struct Visitor : Path::Visitor + { + void accept(Path::Type type, const UnownedStringSlice& filename) SLANG_OVERRIDE + { + m_buffer.Clear(); + m_buffer.append(filename); + + SlangPathType pathType; + switch (type) + { + case Path::Type::File: pathType = SLANG_PATH_TYPE_FILE; break; + case Path::Type::Directory: pathType = SLANG_PATH_TYPE_DIRECTORY; break; + default: return; + } + + m_callback(pathType, m_buffer.getBuffer(), m_userData); + } + + Visitor(FileSystemContentsCallBack callback, void* userData) : + m_callback(callback), + m_userData(userData) + { + } + StringBuilder m_buffer; + FileSystemContentsCallBack m_callback; + void* m_userData; + }; + + Visitor visitor(callback, userData); + Path::find(path, nullptr, &visitor); + + return SLANG_OK; +} + +SlangResult OSFileSystem::saveFile(const char* pathIn, const void* data, size_t size) +{ + SLANG_RETURN_ON_FAIL(_checkMutable(m_style)); + + const String path = _fixPathDelimiters(pathIn); + + try + { + FileStream stream(pathIn, FileMode::Create, FileAccess::Write, FileShare::ReadWrite); + + int64_t numWritten = stream.write(data, size); + + if (numWritten != int64_t(size)) + { + return SLANG_FAIL; + } + + } + catch (const IOException&) + { + return SLANG_E_CANNOT_OPEN; + } + + return SLANG_OK; +} + +SlangResult OSFileSystem::remove(const char* path) +{ + SLANG_RETURN_ON_FAIL(_checkMutable(m_style)); + return Path::remove(path); +} + +SlangResult OSFileSystem::createDirectory(const char* path) +{ + SLANG_RETURN_ON_FAIL(_checkMutable(m_style)); + return Path::createDirectory(path); +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CacheFileSystem !!!!!!!!!!!!!!!!!!!!!!!!!!! + +/* static */ const Result CacheFileSystem::s_compressedResultToResult[] = +{ + SLANG_E_UNINITIALIZED, + SLANG_OK, ///< Ok + SLANG_E_NOT_FOUND, ///< File not found + SLANG_E_CANNOT_OPEN, ///< CannotOpen, + SLANG_FAIL, ///< Fail +}; + +/* static */CacheFileSystem::CompressedResult CacheFileSystem::toCompressedResult(Result res) +{ + if (SLANG_SUCCEEDED(res)) + { + return CompressedResult::Ok; + } + switch (res) + { + case SLANG_E_CANNOT_OPEN: return CompressedResult::CannotOpen; + case SLANG_E_NOT_FOUND: return CompressedResult::NotFound; + default: return CompressedResult::Fail; + } +} + +SLANG_NO_THROW SlangResult SLANG_MCALL CacheFileSystem::queryInterface(SlangUUID const& uuid, void** outObject) +{ + if (uuid == IID_SlangCacheFileSystem) + { + *outObject = this; + return SLANG_OK; + } + + if (_canCast(FileSystemStyle::Ext, uuid)) + { + addReference(); + *outObject = static_cast<ISlangFileSystemExt*>(this); + return SLANG_OK; + } + return SLANG_E_NO_INTERFACE; +} + +CacheFileSystem::CacheFileSystem(ISlangFileSystem* fileSystem, UniqueIdentityMode uniqueIdentityMode, PathStyle pathStyle) +{ + setInnerFileSystem(fileSystem, uniqueIdentityMode, pathStyle); +} + +CacheFileSystem::~CacheFileSystem() +{ + for (const auto& pair : m_uniqueIdentityMap) + { + PathInfo* pathInfo = pair.Value; + delete pathInfo; + } +} + +void CacheFileSystem::setInnerFileSystem(ISlangFileSystem* fileSystem, UniqueIdentityMode uniqueIdentityMode, PathStyle pathStyle) +{ + m_fileSystem = fileSystem; + + m_uniqueIdentityMode = uniqueIdentityMode; + m_pathStyle = pathStyle; + + m_fileSystemExt.setNull(); + + if (fileSystem) + { + // Try to get the more sophisticated interface + fileSystem->queryInterface(IID_ISlangFileSystemExt, (void**)m_fileSystemExt.writeRef()); + } + + switch (m_uniqueIdentityMode) + { + case UniqueIdentityMode::Default: + case UniqueIdentityMode::FileSystemExt: + { + // If it's not a complete file system, we will default to SimplifyAndHash style by default + m_uniqueIdentityMode = m_fileSystemExt ? UniqueIdentityMode::FileSystemExt : UniqueIdentityMode::SimplifyPathAndHash; + break; + } + default: break; + } + + if (pathStyle == PathStyle::Default) + { + // We'll assume it's simplify-able + m_pathStyle = PathStyle::Simplifiable; + // If we have fileSystemExt, we defer to that + if (m_fileSystemExt) + { + // We just defer to the m_fileSystem + m_pathStyle = PathStyle::FileSystemExt; + } + } + + // It can't be default + SLANG_ASSERT(m_uniqueIdentityMode != UniqueIdentityMode::Default); +} + +void CacheFileSystem::clearCache() +{ + for (const auto& pair : m_uniqueIdentityMap) + { + PathInfo* pathInfo = pair.Value; + delete pathInfo; + } + + m_uniqueIdentityMap.Clear(); + m_pathMap.Clear(); + + if (m_fileSystemExt) + { + m_fileSystemExt->clearCache(); + } +} + + +// Determines if we can simplify a path for a given mode +static bool _canSimplifyPath(CacheFileSystem::UniqueIdentityMode mode) +{ + typedef CacheFileSystem::UniqueIdentityMode UniqueIdentityMode; + switch (mode) + { + case UniqueIdentityMode::SimplifyPath: + case UniqueIdentityMode::SimplifyPathAndHash: + { + return true; + } + default: + { + return false; + } + } +} + +SlangResult CacheFileSystem::enumeratePathContents(const char* path, FileSystemContentsCallBack callback, void* userData) +{ + if (m_fileSystemExt) + { + return m_fileSystemExt->enumeratePathContents(path, callback, userData); + } + + // Okay.. the contents of the 'cache' *is* the filesystem. So lets iterate over that + // This will win no prizes for efficiency, but that is unlikely to matter for typical usage + + if (!_canSimplifyPath(m_uniqueIdentityMode)) + { + // As it stands if we can't simplify paths, it's kind of hard to make this + // all work. As we use the simplified path cache + return SLANG_E_NOT_IMPLEMENTED; + } + + // Simplify the path + String simplifiedPath = Path::simplify(path); + + // If the simplified path is just a . then we don't have any prefix + if (simplifiedPath == ".") + { + simplifiedPath = ""; + } + + for (auto& pair : m_pathMap) + { + // NOTE! The currentPath can be a *non* simplified path (the m_pathMap is the cache of paths simplified and other to a file/directory) + // Also note that there will always be the simplified version of the path in cache. + const String& currentPath = pair.Key; + + // If it doesn't start with simplified path, then it can't be a hit + if (!currentPath.startsWith(simplifiedPath)) + { + continue; + } + + UnownedStringSlice remaining(currentPath.getBuffer() + simplifiedPath.getLength(), currentPath.end()); + + // If it starts with a / delimiter strip it + if (remaining.getLength() > 0 && remaining[0] == '/') + { + remaining = UnownedStringSlice(remaining.begin() + 1, remaining.end()); + } + + // If it has a path separator then it's either not simplified - so we ignore (we only want to invoke on the simplified path version as there is only one + // of these for every PathInfo) + // or it is a child file/directory, and so we ignore that too. + if (remaining.indexOf('/') >= 0 || remaining.indexOf('\\') >= 0) + { + continue; + } + + // We *know* that remaining comes from the end of currentPath .We also know currentPath is zero terminated. + // So we can just use (normally this would be a problem because UnownedStringSlice is generally *not* followed by zero termination. + const char* foundPath = remaining.begin(); + // Let's check that fact... + SLANG_ASSERT(foundPath[remaining.getLength()] == 0); + + PathInfo* pathInfo = pair.Value; + + SlangPathType pathType; + if (SLANG_FAILED(_getPathType(pathInfo, currentPath.getBuffer(), &pathType))) + { + continue; + } + + callback(pathType, foundPath, userData); + } + + return SLANG_OK; +} + + +SlangResult CacheFileSystem::_calcUniqueIdentity(const String& path, String& outUniqueIdentity, ComPtr<ISlangBlob>& outFileContents) +{ + switch (m_uniqueIdentityMode) + { + case UniqueIdentityMode::FileSystemExt: + { + // Try getting the uniqueIdentity by asking underlying file system + ComPtr<ISlangBlob> uniqueIdentity; + SLANG_RETURN_ON_FAIL(m_fileSystemExt->getFileUniqueIdentity(path.getBuffer(), uniqueIdentity.writeRef())); + // Get the path as a string + outUniqueIdentity = StringUtil::getString(uniqueIdentity); + return SLANG_OK; + } + case UniqueIdentityMode::Path: + { + outUniqueIdentity = path; + return SLANG_OK; + } + case UniqueIdentityMode::SimplifyPath: + { + outUniqueIdentity = Path::simplify(path); + // If it still has relative elements can't uniquely identify, so give up + return Path::hasRelativeElement(outUniqueIdentity) ? SLANG_FAIL : SLANG_OK; + } + case UniqueIdentityMode::SimplifyPathAndHash: + case UniqueIdentityMode::Hash: + { + // If we don't have a file system -> assume cannot be found + if (m_fileSystem == nullptr) + { + return SLANG_E_NOT_FOUND; + } + + // I can only see if this is the same file as already loaded by loading the file and doing a hash + Result res = m_fileSystem->loadFile(path.getBuffer(), outFileContents.writeRef()); + if (SLANG_FAILED(res) || outFileContents == nullptr) + { + return SLANG_FAIL; + } + + // Calculate the hash on the contents + const uint64_t hash = getHashCode64((const char*)outFileContents->getBufferPointer(), outFileContents->getBufferSize()); + + String hashString = Path::getFileName(path); + hashString = hashString.toLower(); + + hashString.append(':'); + + // The uniqueIdentity is a combination of name and hash + hashString.append(hash, 16); + + outUniqueIdentity = hashString; + return SLANG_OK; + } + } + + return SLANG_FAIL; +} + +CacheFileSystem::PathInfo* CacheFileSystem::_resolveUniqueIdentityCacheInfo(const String& path) +{ + // Use the path to produce uniqueIdentity information + ComPtr<ISlangBlob> fileContents; + String uniqueIdentity; + + SlangResult res = _calcUniqueIdentity(path, uniqueIdentity, fileContents); + if (SLANG_FAILED(res)) + { + // Was not able to create a uniqueIdentity - return failure as nullptr + return nullptr; + } + + // Now try looking up by uniqueIdentity path. If not found, add a new result + PathInfo* pathInfo = nullptr; + if (!m_uniqueIdentityMap.TryGetValue(uniqueIdentity, pathInfo)) + { + // Create with found uniqueIdentity + pathInfo = new PathInfo(uniqueIdentity); + m_uniqueIdentityMap.Add(uniqueIdentity, pathInfo); + } + + // At this point they must have same uniqueIdentity + SLANG_ASSERT(pathInfo->getUniqueIdentity() == uniqueIdentity); + + // If we have the file contents (because of calc-ing uniqueIdentity), and there isn't a read file blob already + // store the data as if read, so doesn't get read again + if (fileContents && !pathInfo->m_fileBlob) + { + pathInfo->m_fileBlob = fileContents; + pathInfo->m_loadFileResult = CompressedResult::Ok; + } + + return pathInfo; +} + +CacheFileSystem::PathInfo* CacheFileSystem::_resolveSimplifiedPathCacheInfo(const String& path) +{ + // If we can simplify the path, try looking up in path cache with simplified path (as long as it's different!) + if (_canSimplifyPath(m_uniqueIdentityMode)) + { + const String simplifiedPath = Path::simplify(path); + // Only lookup if the path is different - because otherwise will recurse forever... + if (simplifiedPath != path) + { + // This is a recursive call - and will ensure the simplified path is added to the cache + return _resolvePathCacheInfo(simplifiedPath); + } + } + + return _resolveUniqueIdentityCacheInfo(path); +} + +CacheFileSystem::PathInfo* CacheFileSystem::_resolvePathCacheInfo(const String& path) +{ + // Lookup in path cache + PathInfo* pathInfo; + if (m_pathMap.TryGetValue(path, pathInfo)) + { + // Found so done + return pathInfo; + } + + // Try getting or creating taking into account possible path simplification + pathInfo = _resolveSimplifiedPathCacheInfo(path); + // Always add the result to the path cache (even if null) + m_pathMap.Add(path, pathInfo); + return pathInfo; +} + +SlangResult CacheFileSystem::loadFile(char const* pathIn, ISlangBlob** blobOut) +{ + *blobOut = nullptr; + String path(pathIn); + PathInfo* info = _resolvePathCacheInfo(path); + if (!info) + { + return SLANG_FAIL; + } + + if (info->m_loadFileResult == CompressedResult::Uninitialized) + { + info->m_loadFileResult = toCompressedResult(m_fileSystem->loadFile(path.getBuffer(), info->m_fileBlob.writeRef())); + } + + *blobOut = info->m_fileBlob; + if (*blobOut) + { + (*blobOut)->addRef(); + } + return toResult(info->m_loadFileResult); +} + +SlangResult CacheFileSystem::getFileUniqueIdentity(const char* path, ISlangBlob** outUniqueIdentity) +{ + PathInfo* info = _resolvePathCacheInfo(path); + if (!info) + { + return SLANG_E_NOT_FOUND; + } + info->m_uniqueIdentity->addRef(); + *outUniqueIdentity = info->m_uniqueIdentity; + return SLANG_OK; +} + +SlangResult CacheFileSystem::calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut) +{ + // Just defer to contained implementation + switch (m_pathStyle) + { + case PathStyle::FileSystemExt: + { + return m_fileSystemExt->calcCombinedPath(fromPathType, fromPath, path, pathOut); + } + default: + { + // Just use the default implementation + return _calcCombinedPath(fromPathType, fromPath, path, pathOut); + } + } +} + +SlangResult CacheFileSystem::_getPathType(PathInfo* info, const char* inPath, SlangPathType* outPathType) +{ + if (info->m_getPathTypeResult == CompressedResult::Uninitialized) + { + if (m_fileSystemExt) + { + info->m_getPathTypeResult = toCompressedResult(m_fileSystemExt->getPathType(inPath, &info->m_pathType)); + } + else + { + // Okay try to load the file + if (info->m_loadFileResult == CompressedResult::Uninitialized) + { + info->m_loadFileResult = toCompressedResult(m_fileSystem->loadFile(inPath, info->m_fileBlob.writeRef())); + } + + // Make the getPathResult the same as the load result + info->m_getPathTypeResult = info->m_loadFileResult; + // Just set to file... the result is what matters in this case + info->m_pathType = SLANG_PATH_TYPE_FILE; + } + } + + *outPathType = info->m_pathType; + return toResult(info->m_getPathTypeResult); +} + +SlangResult CacheFileSystem::getPathType(const char* inPath, SlangPathType* outPathType) +{ + PathInfo* info = _resolvePathCacheInfo(inPath); + if (!info) + { + return SLANG_E_NOT_FOUND; + } + + return _getPathType(info, inPath, outPathType); +} + +SlangResult CacheFileSystem::getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath) +{ + // If we have a ISlangFileSystemExt we can just pass on the request to it + switch (m_pathStyle) + { + case PathStyle::FileSystemExt: + { + return m_fileSystemExt->getSimplifiedPath(path, outSimplifiedPath); + } + case PathStyle::Simplifiable: + { + String simplifiedPath = Path::simplify(path); + *outSimplifiedPath = StringUtil::createStringBlob(simplifiedPath).detach(); + return SLANG_OK; + } + default: return SLANG_E_NOT_IMPLEMENTED; + } +} + +SlangResult CacheFileSystem::getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) +{ + // A file must exist to get a canonical path... + PathInfo* info = _resolvePathCacheInfo(path); + if (!info) + { + return SLANG_E_NOT_FOUND; + } + + // We don't have this -> so read it ... + if (info->m_getCanonicalPathResult == CompressedResult::Uninitialized) + { + if (!m_fileSystemExt) + { + return SLANG_E_NOT_IMPLEMENTED; + } + + // Try getting the canonicalPath by asking underlying file system + ComPtr<ISlangBlob> canonicalPathBlob; + SlangResult res = m_fileSystemExt->getCanonicalPath(path, canonicalPathBlob.writeRef()); + + if (SLANG_SUCCEEDED(res)) + { + // Get the path as a string + String canonicalPath = StringUtil::getString(canonicalPathBlob); + if (canonicalPath.getLength() > 0) + { + info->m_canonicalPath = new StringBlob(canonicalPath); + } + else + { + res = SLANG_FAIL; + } + } + + // Save the result + info->m_getCanonicalPathResult = toCompressedResult(res); + } + + if (info->m_canonicalPath) + { + info->m_canonicalPath->addRef(); + } + *outCanonicalPath = info->m_canonicalPath; + return SLANG_OK; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! RelativeFileSystem !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +RelativeFileSystem::RelativeFileSystem(ISlangFileSystem* fileSystem, const String& relativePath, bool stripPath) : + m_relativePath(relativePath), + m_stripPath(stripPath) +{ + m_style = _getFileSystemStyle(fileSystem, m_fileSystem); +} + +ISlangUnknown* RelativeFileSystem::getInterface(const Guid& guid) +{ + return _canCast(m_style, guid) ? static_cast<ISlangMutableFileSystem*>(this) : nullptr; +} + +SlangResult RelativeFileSystem::_calcCombinedPathInner(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** outPath) +{ + ISlangFileSystemExt* fileSystem = _getExt(); + if (fileSystem) + { + return fileSystem->calcCombinedPath(fromPathType, fromPath, path, outPath); + } + else + { + return _calcCombinedPath(fromPathType, fromPath, path, outPath); + } +} + +SlangResult RelativeFileSystem::_getFixedPath(const char* path, String& outPath) +{ + ComPtr<ISlangBlob> blob; + if (m_stripPath) + { + String strippedPath = Path::getFileName(path); + SLANG_RETURN_ON_FAIL(_calcCombinedPathInner(SLANG_PATH_TYPE_DIRECTORY, m_relativePath.getBuffer(), strippedPath.getBuffer(), blob.writeRef())); + } + else + { + SLANG_RETURN_ON_FAIL(_calcCombinedPathInner(SLANG_PATH_TYPE_DIRECTORY, m_relativePath.getBuffer(), path, blob.writeRef())); + } + + outPath = StringUtil::getString(blob); + return SLANG_OK; +} + +SlangResult RelativeFileSystem::loadFile(char const* path, ISlangBlob** outBlob) +{ + String fixedPath; + SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath)); + return m_fileSystem->loadFile(fixedPath.getBuffer(), outBlob); +} + +SlangResult RelativeFileSystem::getFileUniqueIdentity(const char* path, ISlangBlob** outUniqueIdentity) +{ + auto fileSystem = _getExt(); + if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED; + + String fixedPath; + SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath)); + return fileSystem->getFileUniqueIdentity(fixedPath.getBuffer(), outUniqueIdentity); +} + +SlangResult RelativeFileSystem::calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** outPath) +{ + auto fileSystem = _getExt(); + if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED; + + String fixedFromPath; + SLANG_RETURN_ON_FAIL(_getFixedPath(fromPath, fixedFromPath)); + + return fileSystem->calcCombinedPath(fromPathType, fixedFromPath.getBuffer(), path, outPath); +} + +SlangResult RelativeFileSystem::getPathType(const char* path, SlangPathType* outPathType) +{ + auto fileSystem = _getExt(); + if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED; + + String fixedPath; + SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath)); + return fileSystem->getPathType(fixedPath.getBuffer(), outPathType); +} + +SlangResult RelativeFileSystem::getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath) +{ + auto fileSystem = _getExt(); + if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED; + + return fileSystem->getSimplifiedPath(path, outSimplifiedPath); +} + +SlangResult RelativeFileSystem::getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) +{ + auto fileSystem = _getExt(); + if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED; + + String fixedPath; + SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath)); + return fileSystem->getCanonicalPath(fixedPath.getBuffer(), outCanonicalPath); +} + +void RelativeFileSystem::clearCache() +{ + auto fileSystem = _getExt(); + if (!fileSystem) return; + + fileSystem->clearCache(); +} + +SlangResult RelativeFileSystem::enumeratePathContents(const char* path, FileSystemContentsCallBack callback, void* userData) +{ + auto fileSystem = _getExt(); + if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED; + + String fixedPath; + SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath)); + return fileSystem->enumeratePathContents(fixedPath.getBuffer(), callback, userData); +} + +SlangResult RelativeFileSystem::saveFile(const char* path, const void* data, size_t size) +{ + auto fileSystem = _getMutable(); + if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED; + + String fixedPath; + SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath)); + return fileSystem->saveFile(fixedPath.getBuffer(), data, size); +} + +SlangResult RelativeFileSystem::remove(const char* path) +{ + auto fileSystem = _getMutable(); + if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED; + + String fixedPath; + SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath)); + return fileSystem->remove(fixedPath.getBuffer()); +} + +SlangResult RelativeFileSystem::createDirectory(const char* path) +{ + auto fileSystem = _getMutable(); + if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED; + + String fixedPath; + SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath)); + return fileSystem->createDirectory(fixedPath.getBuffer()); +} + +} |
