From 0b51ea6bb54b1d8a12695ccc2c259fd591069791 Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Mon, 3 Oct 2022 21:09:16 -0400 Subject: IMutableFileSystem::saveFileBlob (#2427) * #include an absolute path didn't work - because paths were taken to always be relative. * Remove ref count for Entry in RiffFileSystem. Free up backing Entry types (to work around Dictionary not doing this). * Some small improvements to RiffFileSystem. * Add testing for file systems. * Split out MemoryFileSystem. * Add some documentation around different FileSystems. * Small tiry up - removing unused headers, fixing some comments. Use StringBlob::moveCreate where appropriate. * Small improvement to MemoryFileSystem. Improve documentation comments a little. * Added PathKind * * Make MemoryFileSystem not have implicit directories * Make RelativeFileSystem only allow access to files in file system (kind of like chroot) * Added Path::simplifyAbsolute * Special handling for root of MemoryFileSystem * Improvements around paths for different impls * More improvements around RelativeFileSystem. Special case root handling. * Test archive serialization. Move testinf from compression. Remove the implicit directory test -> doesn't work on all file systems. * Small optimization that removes need for check for a parent unless an item is being *created*. * Add implicit path testing. * Add support for saveFileBlob Add testing for saveFileBlob * Removed TemporaryFileSet Added PlatformUtil::outputDebugMessage * Some small improvements around RelativeFileSystem. * Split out ImplicitDirectoryCollector so can use without requiring compression systems. * Split out StringSliceIndexMap into own files. --- source/core/slang-archive-file-system.cpp | 107 -------------- source/core/slang-archive-file-system.h | 126 +--------------- source/core/slang-file-system.cpp | 163 ++++++++++++++++----- source/core/slang-file-system.h | 22 ++- source/core/slang-implicit-directory-collector.cpp | 75 ++++++++++ source/core/slang-implicit-directory-collector.h | 68 +++++++++ source/core/slang-io.cpp | 32 ++++ source/core/slang-io.h | 62 +------- source/core/slang-memory-file-system.cpp | 159 +++++++++++--------- source/core/slang-memory-file-system.h | 20 ++- source/core/slang-platform.cpp | 10 +- source/core/slang-platform.h | 7 + source/core/slang-riff-file-system.cpp | 33 ++++- source/core/slang-riff-file-system.h | 3 +- source/core/slang-string-slice-index-map.cpp | 44 ++++++ source/core/slang-string-slice-index-map.h | 78 ++++++++++ source/core/slang-zip-file-system.cpp | 59 +++++--- 17 files changed, 633 insertions(+), 435 deletions(-) create mode 100644 source/core/slang-implicit-directory-collector.cpp create mode 100644 source/core/slang-implicit-directory-collector.h create mode 100644 source/core/slang-string-slice-index-map.cpp create mode 100644 source/core/slang-string-slice-index-map.h (limited to 'source/core') diff --git a/source/core/slang-archive-file-system.cpp b/source/core/slang-archive-file-system.cpp index 88093239b..63e1c0b01 100644 --- a/source/core/slang-archive-file-system.cpp +++ b/source/core/slang-archive-file-system.cpp @@ -23,113 +23,6 @@ namespace Slang { -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! StringSliceIndexMap !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -StringSliceIndexMap::CountIndex StringSliceIndexMap::add(const UnownedStringSlice& key, Index valueIndex) -{ - StringSlicePool::Handle handle; - m_pool.findOrAdd(key, handle); - const CountIndex countIndex = StringSlicePool::asIndex(handle); - if (countIndex >= m_indexMap.getCount()) - { - SLANG_ASSERT(countIndex == m_indexMap.getCount()); - m_indexMap.add(valueIndex); - } - else - { - m_indexMap[countIndex] = valueIndex; - } - return countIndex; -} - -StringSliceIndexMap::CountIndex StringSliceIndexMap::findOrAdd(const UnownedStringSlice& key, Index defaultValueIndex) -{ - StringSlicePool::Handle handle; - m_pool.findOrAdd(key, handle); - const CountIndex countIndex = StringSlicePool::asIndex(handle); - if (countIndex >= m_indexMap.getCount()) - { - SLANG_ASSERT(countIndex == m_indexMap.getCount()); - m_indexMap.add(defaultValueIndex); - } - return countIndex; -} - -void StringSliceIndexMap::clear() -{ - m_pool.clear(); - m_indexMap.clear(); -} - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ImplicitDirectoryCollector !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -ImplicitDirectoryCollector::ImplicitDirectoryCollector(const String& canonicalPath, bool directoryExists) : - m_directoryExists(directoryExists) -{ - StringBuilder buffer; - if (canonicalPath != ".") - { - buffer << canonicalPath; - buffer.append('/'); - } - m_prefix = buffer.ProduceString(); -} - -void ImplicitDirectoryCollector::addRemainingPath(SlangPathType pathType, const UnownedStringSlice& inPathRemainder) -{ - // If it's zero length we probably don't want to add it - if (inPathRemainder.getLength() == 0) - { - // It's empty so don't add normal way - implies the directory exists - m_directoryExists = true; - return; - } - - UnownedStringSlice pathRemainder(inPathRemainder); - const Index slashIndex = pathRemainder.indexOf('/'); - - // If we have a following / that means it's an implicit directory. - if (slashIndex >= 0) - { - pathType = SLANG_PATH_TYPE_DIRECTORY; - pathRemainder = UnownedStringSlice(pathRemainder.begin(), pathRemainder.begin() + slashIndex); - } - - const Index countIndex = m_map.findOrAdd(pathRemainder, pathType); - SLANG_UNUSED(countIndex); - // Make sure they are the same type - SLANG_ASSERT(SlangPathType(m_map.getValueAt(countIndex)) == pathType); -} - -void ImplicitDirectoryCollector::addPath(SlangPathType pathType, const UnownedStringSlice& canonicalPath) -{ - if (hasPrefix(canonicalPath)) - { - UnownedStringSlice remainder = getRemainder(canonicalPath); - addRemainingPath(pathType, remainder); - } -} - -SlangResult ImplicitDirectoryCollector::enumerate(FileSystemContentsCallBack callback, void* userData) -{ - const Int count = m_map.getCount(); - - for (Index i = 0; i < count; ++i) - { - const auto& pair = m_map.getAt(i); - - UnownedStringSlice path = pair.Key; - SlangPathType pathType = SlangPathType(pair.Value); - - // Note *is* 0 terminated in the pool - // Let's check tho - SLANG_ASSERT(path.begin()[path.getLength()] == 0); - callback(pathType, path.begin(), userData); - } - - return getDirectoryExists() ? SLANG_OK : SLANG_E_NOT_FOUND; -} - SlangResult loadArchiveFileSystem(const void* data, size_t dataSizeInBytes, ComPtr& outFileSystem) { ComPtr fileSystem; diff --git a/source/core/slang-archive-file-system.h b/source/core/slang-archive-file-system.h index 678791245..cee119c13 100644 --- a/source/core/slang-archive-file-system.h +++ b/source/core/slang-archive-file-system.h @@ -1,5 +1,5 @@ -#ifndef SLANG_ARCHIVE_FILE_SYSTEM_H -#define SLANG_ARCHIVE_FILE_SYSTEM_H +#ifndef SLANG_CORE_ARCHIVE_FILE_SYSTEM_H +#define SLANG_CORE_ARCHIVE_FILE_SYSTEM_H #include "slang-basic.h" @@ -7,9 +7,6 @@ #include "slang-compression-system.h" -#include "slang-string-slice-pool.h" -#include "slang-com-object.h" - namespace Slang { @@ -26,125 +23,6 @@ class IArchiveFileSystem : public ISlangCastable SLANG_NO_THROW virtual void SLANG_MCALL setCompressionStyle(const CompressionStyle& style) = 0; }; -/* Maps an UnownedStringSlice to an index. All substrings are held internally in a StringSlicePool, and so -owned by the type. */ -class StringSliceIndexMap -{ -public: - /// An index that identifies a key value pair. - typedef Index CountIndex; - - /// Adds a key, value pair. Returns the CountIndex of the pair. - /// If there is already a value stored for the key it is replaced. - CountIndex add(const UnownedStringSlice& key, Index valueIndex); - - /// Finds or adds the slice. If the slice is added the defaultValueIndex is set. - /// If not the index associated with the slice remains the same. - /// Returns the CountIndex where the key,value pair are stored - CountIndex findOrAdd(const UnownedStringSlice& key, Index defaultValueIndex); - - /// Gets the index associated with the key. Returns -1 if there is no associated index. - SLANG_FORCE_INLINE Index getValue(const UnownedStringSlice& key); - - /// Get the amount of pairs in the map - Index getCount() const { return m_indexMap.getCount(); } - - /// Get the slice and the index at the specified index - SLANG_INLINE KeyValuePair getAt(CountIndex countIndex) const; - - /// Clear the contents of the map - void clear(); - - /// Get the key at the specified index - UnownedStringSlice getKeyAt(CountIndex index) const { return m_pool.getSlice(StringSlicePool::Handle(index)); } - /// Get the value at the specified index - Index& getValueAt(CountIndex index) { return m_indexMap[index]; } - - /// Get the amount of key,value pairs - Index getCount() { return m_indexMap.getCount(); } - - /// Ctor - StringSliceIndexMap() : - m_pool(StringSlicePool::Style::Empty) - { - } - -protected: - StringSlicePool m_pool; ///< Pool holds the substrings - List m_indexMap; ///< Maps a pool index to the output index -}; - -// --------------------------------------------------------------------------- -Index StringSliceIndexMap::getValue(const UnownedStringSlice& key) -{ - const Index poolIndex = m_pool.findIndex(key); - return (poolIndex >= 0) ? m_indexMap[poolIndex] : -1; -} - -// --------------------------------------------------------------------------- -KeyValuePair StringSliceIndexMap::getAt(CountIndex countIndex) const -{ - KeyValuePair pair; - pair.Key = m_pool.getSlice(StringSlicePool::Handle(countIndex)); - pair.Value = m_indexMap[countIndex]; - return pair; -} - -/* This class helps to find the contents and/or existence of an implicit directory.This finds the contents of a directory. - -This is achieved by using a path prefix that any contained path must at least match. If the remainder of the path contains a folder - - detectable because it's not a leaf and so contains a delimiter - that directory is added. As a sub folder may contain many - files, and the directory itself may also be defined, it is necessary to dedup. The deduping is handled by the StringSliceIndexMap. */ -class ImplicitDirectoryCollector -{ -public: - - enum class State - { - None, ///< Neither the directory or content have been found - DirectoryExists, ///< The directory exists - HasContent, ///< If it has content, the directory must exist - }; - - /// Get the current state - State getState() const { return (m_map.getCount() > 0) ? State::HasContent : (m_directoryExists ? State::DirectoryExists : State::None); } - /// True if collector at least has the specified state - bool hasState(State state) { return Index(getState()) >= Index(state); } - - /// Set that it exists - void setDirectoryExists(bool directoryExists) { m_directoryExists = directoryExists; } - /// Get if it exists (implicitly or explicitly) - bool getDirectoryExists() const { return m_directoryExists || m_map.getCount() > 0; } - - /// True if the path matches the prefix - bool hasPrefix(const UnownedStringSlice& path) const { return path.startsWith(m_prefix.getUnownedSlice()); } - - /// True if the directory has content - bool hasContent() const { return m_map.getCount() > 0; } - - /// Gets the remainder or path after the prefix - UnownedStringSlice getRemainder(const UnownedStringSlice& path) const - { - SLANG_ASSERT(hasPrefix(path)); - return UnownedStringSlice(path.begin() + m_prefix.getLength(), path.end()); - } - - /// Add a remaining path - void addRemainingPath(SlangPathType pathType, const UnownedStringSlice& inPathRemainder); - /// Add a path - void addPath(SlangPathType pathType, const UnownedStringSlice& canonicalPath); - /// Enumerate the contents - SlangResult enumerate(FileSystemContentsCallBack callback, void* userData); - - /// Ctor - ImplicitDirectoryCollector(const String& canonicalPath, bool directoryExists = false); - - protected: - StringSliceIndexMap m_map; - String m_prefix; - bool m_directoryExists; -}; - SlangResult loadArchiveFileSystem(const void* data, size_t dataSizeInBytes, ComPtr& outFileSystem); SlangResult createArchiveFileSystem(SlangArchiveType type, ComPtr& outFileSystem); diff --git a/source/core/slang-file-system.cpp b/source/core/slang-file-system.cpp index 283dab712..0697810f4 100644 --- a/source/core/slang-file-system.cpp +++ b/source/core/slang-file-system.cpp @@ -53,7 +53,7 @@ static FileSystemStyle _getFileSystemStyle(ISlangFileSystem* system, ComPtrgetBufferPointer(), dataBlob->getBufferSize()); +} + SlangResult OSFileSystem::remove(const char* path) { SLANG_RETURN_ON_FAIL(_checkMutable(m_style)); @@ -702,14 +727,37 @@ SlangResult CacheFileSystem::getPathType(const char* inPath, SlangPathType* outP return _getPathType(info, inPath, outPathType); } -SlangResult CacheFileSystem::getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath) +SlangResult CacheFileSystem::getPath(PathKind kind, const char* path, ISlangBlob** outPath) +{ + switch (kind) + { + case PathKind::Simplified: return _getSimplifiedPath(path, outPath); + case PathKind::Canonical: return _getCanonicalPath(path, outPath); + default: break; + } + + if (m_fileSystemExt) + { + return m_fileSystemExt->getPath(kind, path, outPath); + } + + // If we don't have a fileSystem, we can try the canonical path + if (SLANG_SUCCEEDED(getPath(PathKind::Canonical, path, outPath))) + { + return SLANG_OK; + } + // Else we can try simplified + return getPath(PathKind::Simplified, path, outPath); +} + +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); + return m_fileSystemExt->getPath(PathKind::Simplified, path, outSimplifiedPath); } case PathStyle::Simplifiable: { @@ -721,7 +769,7 @@ SlangResult CacheFileSystem::getSimplifiedPath(const char* path, ISlangBlob** ou } } -SlangResult CacheFileSystem::getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) +SlangResult CacheFileSystem::_getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) { *outCanonicalPath = nullptr; @@ -742,7 +790,7 @@ SlangResult CacheFileSystem::getCanonicalPath(const char* path, ISlangBlob** out // Try getting the canonicalPath by asking underlying file system ComPtr canonicalPathBlob; - SlangResult res = m_fileSystemExt->getCanonicalPath(path, canonicalPathBlob.writeRef()); + SlangResult res = m_fileSystemExt->getPath(PathKind::Canonical, path, canonicalPathBlob.writeRef()); if (SLANG_SUCCEEDED(res)) { @@ -782,10 +830,10 @@ RelativeFileSystem::RelativeFileSystem(ISlangFileSystem* fileSystem, const Strin { m_osPathKind = ext->getOSPathKind(); - // If it's direct, but we have a relative path, canonical should work + // If it's direct, but we have a relative path, "operating system" should work if (m_osPathKind == OSPathKind::Direct && relativePath.getLength()) { - m_osPathKind = OSPathKind::Canonical; + m_osPathKind = OSPathKind::OperatingSystem; } } } @@ -823,18 +871,30 @@ SlangResult RelativeFileSystem::_calcCombinedPathInner(SlangPathType fromPathTyp } } -SlangResult RelativeFileSystem::_getFixedPath(const char* path, String& outPath) +SlangResult RelativeFileSystem::_getCanonicalPath(const char* path, StringBuilder& outPath) { - ComPtr 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())); + // We are just using the filename. There is no path that could go outside of the the relative path so we can use as is + auto fileName = Path::getFileName(path); + + outPath.swapWith(fileName); } else { - SLANG_RETURN_ON_FAIL(_calcCombinedPathInner(SLANG_PATH_TYPE_DIRECTORY, m_relativePath.getBuffer(), path, blob.writeRef())); + // We want the input path to be local to this file system + SLANG_RETURN_ON_FAIL(Path::simplifyAbsolute(path, outPath)); } + return SLANG_OK; +} + +SlangResult RelativeFileSystem::_getFixedPath(const char* path, String& outPath) +{ + ComPtr blob; + + StringBuilder canonicalPath; + SLANG_RETURN_ON_FAIL(_getCanonicalPath(path, canonicalPath)); + SLANG_RETURN_ON_FAIL(_calcCombinedPathInner(SLANG_PATH_TYPE_DIRECTORY, m_relativePath.getBuffer(), canonicalPath.getBuffer(), blob.writeRef())); outPath = StringUtil::getString(blob); return SLANG_OK; @@ -878,22 +938,39 @@ SlangResult RelativeFileSystem::getPathType(const char* path, SlangPathType* out 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) +SlangResult RelativeFileSystem::getPath(PathKind kind, const char* path, ISlangBlob** outPath) { auto fileSystem = _getExt(); if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED; + + switch (kind) + { + case PathKind::Simplified: + { + return fileSystem->getPath(kind, path, outPath); + } + case PathKind::Display: + { + // If not backed by OS, just use simplified path, else use the Operating system path + kind = (fileSystem->getOSPathKind() == OSPathKind::None) ? PathKind::Simplified : PathKind::OperatingSystem; + return getPath(kind, path, outPath); + } + case PathKind::Canonical: + { + StringBuilder canonicalPath; + SLANG_RETURN_ON_FAIL(_getCanonicalPath(path, canonicalPath)); + *outPath = StringBlob::moveCreate(canonicalPath).detach(); + return SLANG_OK; + } + case PathKind::OperatingSystem: + { + String fixedPath; + SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath)); + return fileSystem->getPath(kind, fixedPath.getBuffer(), outPath); + } + } - String fixedPath; - SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath)); - return fileSystem->getCanonicalPath(fixedPath.getBuffer(), outCanonicalPath); + return SLANG_FAIL; } void RelativeFileSystem::clearCache() @@ -924,6 +1001,16 @@ SlangResult RelativeFileSystem::saveFile(const char* path, const void* data, siz return fileSystem->saveFile(fixedPath.getBuffer(), data, size); } +SlangResult RelativeFileSystem::saveFileBlob(const char* path, ISlangBlob* dataBlob) +{ + auto fileSystem = _getMutable(); + if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED; + + String fixedPath; + SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath)); + return fileSystem->saveFileBlob(fixedPath.getBuffer(), dataBlob); +} + SlangResult RelativeFileSystem::remove(const char* path) { auto fileSystem = _getMutable(); diff --git a/source/core/slang-file-system.h b/source/core/slang-file-system.h index d83d7d343..a3efbfe68 100644 --- a/source/core/slang-file-system.h +++ b/source/core/slang-file-system.h @@ -40,14 +40,14 @@ public: 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 getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath) SLANG_OVERRIDE; - virtual SLANG_NO_THROW SlangResult SLANG_MCALL getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) 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::Direct; } // 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; @@ -157,8 +157,8 @@ class CacheFileSystem: public ISlangFileSystemExt, public ComBaseObject virtual SLANG_NO_THROW SlangResult SLANG_MCALL getFileUniqueIdentity(const char* path, ISlangBlob** outUniqueIdentity) 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* outPathType) SLANG_OVERRIDE; - virtual SLANG_NO_THROW SlangResult SLANG_MCALL getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath) SLANG_OVERRIDE; - virtual SLANG_NO_THROW SlangResult SLANG_MCALL getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) SLANG_OVERRIDE; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getPath(PathKind kind, 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 m_osPathKind; } @@ -185,6 +185,9 @@ protected: void* getInterface(const Guid& guid); void* getObject(const Guid& guid); + SlangResult _getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath); + SlangResult _getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath); + /// Given a path, works out a uniqueIdentity, based on the uniqueIdentityMode. /// outFileContents will be set if file had to be read to produce the uniqueIdentity (ie with Hash) /// If the file doesn't have to be read, then outFileContents will be nullptr, even if it is backed by a file. @@ -231,14 +234,14 @@ public: virtual SLANG_NO_THROW SlangResult SLANG_MCALL getFileUniqueIdentity(const char* path, ISlangBlob** outUniqueIdentity) 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* outPathType) SLANG_OVERRIDE; - virtual SLANG_NO_THROW SlangResult SLANG_MCALL getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath) SLANG_OVERRIDE; - virtual SLANG_NO_THROW SlangResult SLANG_MCALL getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) 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 m_osPathKind; } // 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; @@ -253,15 +256,18 @@ protected: SlangResult _calcCombinedPathInner(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut); + /// Get the fixed path to the item for the backing file system. SlangResult _getFixedPath(const char* path, String& outPath); + /// Get the canonical path + SlangResult _getCanonicalPath(const char* path, StringBuilder& outPath); ISlangUnknown* getInterface(const Guid& guid); void* getObject(const Guid& guid); - bool m_stripPath; + bool m_stripPath; ///< If set any path prior to an item will be stripped (making the directory in effect flat) FileSystemStyle m_style; - ComPtr m_fileSystem; ///< NOTE! Has to match what's in style, such style can be reached via reinterpret_cast + ComPtr m_fileSystem; ///< NOTE! Has to match what's in style, such style can be reached via reinterpret_cast String m_relativePath; OSPathKind m_osPathKind = OSPathKind::None; ///< OS path kind diff --git a/source/core/slang-implicit-directory-collector.cpp b/source/core/slang-implicit-directory-collector.cpp new file mode 100644 index 000000000..199fd6155 --- /dev/null +++ b/source/core/slang-implicit-directory-collector.cpp @@ -0,0 +1,75 @@ +#include "slang-implicit-directory-collector.h" + +namespace Slang +{ + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ImplicitDirectoryCollector !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +ImplicitDirectoryCollector::ImplicitDirectoryCollector(const String& canonicalPath, bool directoryExists) : + m_directoryExists(directoryExists) +{ + StringBuilder buffer; + if (canonicalPath != ".") + { + buffer << canonicalPath; + buffer.append('/'); + } + m_prefix = buffer.ProduceString(); +} + +void ImplicitDirectoryCollector::addRemainingPath(SlangPathType pathType, const UnownedStringSlice& inPathRemainder) +{ + // If it's zero length we probably don't want to add it + if (inPathRemainder.getLength() == 0) + { + // It's empty so don't add normal way - implies the directory exists + m_directoryExists = true; + return; + } + + UnownedStringSlice pathRemainder(inPathRemainder); + const Index slashIndex = pathRemainder.indexOf('/'); + + // If we have a following / that means it's an implicit directory. + if (slashIndex >= 0) + { + pathType = SLANG_PATH_TYPE_DIRECTORY; + pathRemainder = UnownedStringSlice(pathRemainder.begin(), pathRemainder.begin() + slashIndex); + } + + const Index countIndex = m_map.findOrAdd(pathRemainder, pathType); + SLANG_UNUSED(countIndex); + // Make sure they are the same type + SLANG_ASSERT(SlangPathType(m_map.getValueAt(countIndex)) == pathType); +} + +void ImplicitDirectoryCollector::addPath(SlangPathType pathType, const UnownedStringSlice& canonicalPath) +{ + if (canonicalPath != toSlice(".") && hasPrefix(canonicalPath)) + { + UnownedStringSlice remainder = getRemainder(canonicalPath); + addRemainingPath(pathType, remainder); + } +} + +SlangResult ImplicitDirectoryCollector::enumerate(FileSystemContentsCallBack callback, void* userData) +{ + const Int count = m_map.getCount(); + + for (Index i = 0; i < count; ++i) + { + const auto& pair = m_map.getAt(i); + + UnownedStringSlice path = pair.Key; + SlangPathType pathType = SlangPathType(pair.Value); + + // Note *is* 0 terminated in the pool + // Let's check tho + SLANG_ASSERT(path.begin()[path.getLength()] == 0); + callback(pathType, path.begin(), userData); + } + + return getDirectoryExists() ? SLANG_OK : SLANG_E_NOT_FOUND; +} + +} // namespace Slang diff --git a/source/core/slang-implicit-directory-collector.h b/source/core/slang-implicit-directory-collector.h new file mode 100644 index 000000000..280ba33e6 --- /dev/null +++ b/source/core/slang-implicit-directory-collector.h @@ -0,0 +1,68 @@ +#ifndef SLANG_CORE_IMPLICIT_DIRECTORY_COLLECTOR_H +#define SLANG_CORE_IMPLICIT_DIRECTORY_COLLECTOR_H + +#include "slang-basic.h" + +#include "slang-string-slice-index-map.h" + +namespace Slang +{ + +/* This class helps to find the contents and/or existence of an implicit directory.This finds the contents of a directory. + +This is achieved by using a path prefix that any contained path must at least match. If the remainder of the path contains a folder + - detectable because it's not a leaf and so contains a delimiter - that directory is added. As a sub folder may contain many + files, and the directory itself may also be defined, it is necessary to dedup. The deduping is handled by the StringSliceIndexMap. */ +class ImplicitDirectoryCollector +{ +public: + + enum class State + { + None, ///< Neither the directory or content have been found + DirectoryExists, ///< The directory exists + HasContent, ///< If it has content, the directory must exist + }; + + /// Get the current state + State getState() const { return (m_map.getCount() > 0) ? State::HasContent : (m_directoryExists ? State::DirectoryExists : State::None); } + /// True if collector at least has the specified state + bool hasState(State state) { return Index(getState()) >= Index(state); } + + /// Set that it exists + void setDirectoryExists(bool directoryExists) { m_directoryExists = directoryExists; } + /// Get if it exists (implicitly or explicitly) + bool getDirectoryExists() const { return m_directoryExists || m_map.getCount() > 0; } + + /// True if the path matches the prefix + bool hasPrefix(const UnownedStringSlice& path) const { return path.startsWith(m_prefix.getUnownedSlice()); } + + /// True if the directory has content + bool hasContent() const { return m_map.getCount() > 0; } + + /// Gets the remainder or path after the prefix + UnownedStringSlice getRemainder(const UnownedStringSlice& path) const + { + SLANG_ASSERT(hasPrefix(path)); + return UnownedStringSlice(path.begin() + m_prefix.getLength(), path.end()); + } + + /// Add a remaining path + void addRemainingPath(SlangPathType pathType, const UnownedStringSlice& inPathRemainder); + /// Add a path + void addPath(SlangPathType pathType, const UnownedStringSlice& canonicalPath); + /// Enumerate the contents + SlangResult enumerate(FileSystemContentsCallBack callback, void* userData); + + /// Ctor + ImplicitDirectoryCollector(const String& canonicalPath, bool directoryExists = false); + + protected: + StringSliceIndexMap m_map; + String m_prefix; + bool m_directoryExists; +}; + +} + +#endif diff --git a/source/core/slang-io.cpp b/source/core/slang-io.cpp index ef260f088..b25d5562d 100644 --- a/source/core/slang-io.cpp +++ b/source/core/slang-io.cpp @@ -419,6 +419,38 @@ namespace Slang return false; } + /* static */SlangResult Path::simplifyAbsolute(const UnownedStringSlice& path, StringBuilder& outPath) + { + if (path.getLength() == 0) + { + return SLANG_FAIL; + } + + List splitPath; + Path::split(UnownedStringSlice(path), splitPath); + + // If the first part of a path is "", it means path of form "/some/path". Turn into "some/path". + if (splitPath.getCount() > 1 && splitPath[0].getLength() == 0) + { + splitPath.removeAt(0); + } + + Path::simplify(splitPath); + + // If it has a relative part then it's not absolute + if (splitPath.indexOf(UnownedStringSlice::fromLiteral("..")) >= 0) + { + return SLANG_E_NOT_FOUND; + } + + // We allow splitPath.getCount() == 0, because + // the original path could have been '.' or './.' + // Special handling for this is in join + + Path::join(splitPath.getBuffer(), splitPath.getCount(), outPath); + return SLANG_OK; + } + /* static */void Path::simplify(List& ioSplit) { // Strictly speaking we could do something about case on platforms like window, but here we won't worry about that diff --git a/source/core/slang-io.h b/source/core/slang-io.h index aa4ccae42..acdcd0124 100644 --- a/source/core/slang-io.h +++ b/source/core/slang-io.h @@ -126,6 +126,12 @@ namespace Slang static String simplify(const UnownedStringSlice& path); static String simplify(const String& path) { return simplify(path.getUnownedSlice()); } + /// Given a path simplifies it such the the resultant path is absolute (ie contains no . or ..) + /// Can return '.' as the path as the directory 'root'. + static SlangResult simplifyAbsolute(const UnownedStringSlice& path, StringBuilder& outPath); + static SlangResult simplifyAbsolute(const String& path, StringBuilder& outPath) { return simplifyAbsolute(path.getUnownedSlice(), outPath); } + static SlangResult simplifyAbsolute(const char* path, StringBuilder& outPath) { return simplifyAbsolute(UnownedStringSlice(path), outPath); } + /// Simplifies the path split up static void simplify(List& ioSplit); @@ -186,62 +192,6 @@ namespace Slang static bool isSafeURIChar(char ch); }; - // Helper class to clean up temporary files on dtor - class TemporaryFileSet: public RefObject - { - public: - typedef RefObject Super; - typedef TemporaryFileSet ThisType; - - void remove(const String& path) - { - if (const Index index = m_paths.indexOf(path) >= 0) - { - m_paths.removeAt(index); - } - } - - void add(const String& path) - { - if (m_paths.indexOf(path) < 0) - { - m_paths.add(path); - } - } - void add(const List& paths) - { - for (const auto& path : paths) - { - add(path); - } - } - void clear() - { - m_paths.clear(); - } - - void swapWith(ThisType& rhs) - { - m_paths.swapWith(rhs.m_paths); - } - - ~TemporaryFileSet() - { - for (const auto& path : m_paths) - { - File::remove(path); - } - } - /// Default Ctor - TemporaryFileSet() {} - - List m_paths; - - private: - // Disable ctor/assignment - TemporaryFileSet(const ThisType& rhs) = delete; - void operator=(const ThisType& rhs) = delete; - }; } #endif diff --git a/source/core/slang-memory-file-system.cpp b/source/core/slang-memory-file-system.cpp index 9704542f7..06c0a4ebe 100644 --- a/source/core/slang-memory-file-system.cpp +++ b/source/core/slang-memory-file-system.cpp @@ -4,8 +4,7 @@ #include "slang-io.h" #include "slang-blob.h" -// For ImplicitDirectoryCollector -#include "slang-archive-file-system.h" +#include "slang-implicit-directory-collector.h" namespace Slang { @@ -38,32 +37,13 @@ void* MemoryFileSystem::castAs(const Guid& guid) return getObject(guid); } -SlangResult MemoryFileSystem::_calcCanonicalPath(const char* path, StringBuilder& out) -{ - List splitPath; - Path::split(UnownedStringSlice(path), splitPath); - - // If the first part of a path is "", it means path of form "/some/path". Turn into "some/path". - if (splitPath.getCount() > 1 && splitPath[0].getLength() == 0) - { - splitPath.removeAt(0); - } - - Path::simplify(splitPath); - - if (splitPath.indexOf(UnownedStringSlice::fromLiteral("..")) >= 0) - { - return SLANG_E_NOT_FOUND; - } - - if (splitPath.getCount() == 0) - { - // It's an empty path; - return SLANG_FAIL; - } - - Path::join(splitPath.getBuffer(), splitPath.getCount(), out); - return SLANG_OK; +void MemoryFileSystem::_clear() +{ + m_entries = Dictionary(); + // Add the root + Entry entry; + entry.initDirectory("."); + m_entries.Add(entry.m_canonicalPath, entry); } MemoryFileSystem::Entry* MemoryFileSystem::_getEntryFromCanonicalPath(const String& canonicalPath) @@ -74,7 +54,7 @@ MemoryFileSystem::Entry* MemoryFileSystem::_getEntryFromCanonicalPath(const Stri MemoryFileSystem::Entry* MemoryFileSystem::_getEntryFromPath(const char* path, String* outPath) { StringBuilder buffer; - if (SLANG_FAILED(_calcCanonicalPath(path, buffer))) + if (SLANG_FAILED(_getCanonical(path, buffer))) { return nullptr; } @@ -112,7 +92,7 @@ SlangResult MemoryFileSystem::loadFile(char const* path, ISlangBlob** outBlob) SlangResult MemoryFileSystem::getFileUniqueIdentity(const char* path, ISlangBlob** outUniqueIdentity) { - return getCanonicalPath(path, outUniqueIdentity); + return getPath(PathKind::Canonical, path, outUniqueIdentity); } SlangResult MemoryFileSystem::calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut) @@ -138,46 +118,36 @@ SlangResult MemoryFileSystem::calcCombinedPath(SlangPathType fromPathType, const SlangResult MemoryFileSystem::getPathType(const char* path, SlangPathType* outPathType) { - String canonicalPath; - Entry* entry = _getEntryFromPath(path, &canonicalPath); - if (entry == nullptr) + if (auto entry = _getEntryFromPath(path)) { - // Could be an implicit path - ImplicitDirectoryCollector collector(canonicalPath); - for (const auto& pair : m_entries) - { - const Entry* childEntry = &pair.Value; - collector.addPath(childEntry->m_type, childEntry->m_canonicalPath.getUnownedSlice()); - // If on adding a path we determine a directory exists, then we are done - if (collector.getDirectoryExists()) - { - *outPathType = SLANG_PATH_TYPE_DIRECTORY; - return SLANG_OK; - } - } - - // If not implicit or explicit we are done. - return SLANG_E_NOT_FOUND; + *outPathType = entry->m_type; + return SLANG_OK; } - - // Explicit type - *outPathType = entry->m_type; - return SLANG_OK; -} - -SlangResult MemoryFileSystem::getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath) -{ - String simplifiedPath = Path::simplify(path); - *outSimplifiedPath = StringBlob::moveCreate(simplifiedPath).detach(); - return SLANG_OK; + // Not found + return SLANG_E_NOT_FOUND; } -SlangResult MemoryFileSystem::getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) +SlangResult MemoryFileSystem::getPath(PathKind kind, const char* path, ISlangBlob** outPath) { - StringBuilder buffer; - SLANG_RETURN_ON_FAIL(_calcCanonicalPath(path, buffer)); - *outCanonicalPath = StringBlob::moveCreate(buffer).detach(); - return SLANG_OK; + 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(Path::simplifyAbsolute(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) @@ -185,10 +155,12 @@ SlangResult MemoryFileSystem::enumeratePathContents(const char* path, FileSystem String canonicalPath; Entry* entry = _getEntryFromPath(path, &canonicalPath); - const bool foundDirectory = (entry && entry->m_type == SLANG_PATH_TYPE_DIRECTORY); + if (!entry || entry->m_type != SLANG_PATH_TYPE_DIRECTORY) + { + return SLANG_E_NOT_FOUND; + } - // We allow implicit directories, so this works even if there isn't an explicit one - ImplicitDirectoryCollector collector(canonicalPath, foundDirectory); + 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) @@ -209,12 +181,52 @@ SlangResult MemoryFileSystem::saveFile(const char* path, const void* data, size_ 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::simplifyAbsolute(UnownedStringSlice(path), 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(_calcCanonicalPath(path, canonicalPath)); + SLANG_RETURN_ON_FAIL(_getCanonicalWithExistingParent(path, canonicalPath)); Entry* foundEntry = _getEntryFromCanonicalPath(canonicalPath); @@ -247,7 +259,8 @@ SlangResult MemoryFileSystem::remove(const char* path) String canonicalPath; Entry* entry = _getEntryFromPath(path, &canonicalPath); - if (entry) + // If there is an entry and not the root of the file system + if (entry && entry->m_canonicalPath != toSlice(".")) { if (entry->m_type == SLANG_PATH_TYPE_DIRECTORY) { @@ -277,8 +290,10 @@ SlangResult MemoryFileSystem::remove(const char* path) SlangResult MemoryFileSystem::createDirectory(const char* path) { - String canonicalPath; - if (_getEntryFromPath(path, &canonicalPath)) + StringBuilder canonicalPath; + SLANG_RETURN_ON_FAIL(_getCanonicalWithExistingParent(path, canonicalPath)); + + if (_getEntryFromCanonicalPath(canonicalPath)) { return SLANG_FAIL; } diff --git a/source/core/slang-memory-file-system.h b/source/core/slang-memory-file-system.h index 72bacf2bd..cd2701fe4 100644 --- a/source/core/slang-memory-file-system.h +++ b/source/core/slang-memory-file-system.h @@ -22,9 +22,6 @@ This is in contrast with an implementation that held items in directories 'objec 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'. -The implementation allows for 'implicit' directories. If we have a file "a/b" it's existance *implicitly* implies the existance of the -directory 'a'. This is similar to how archive file formats such as zip works. - 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. @@ -46,17 +43,20 @@ public: 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 getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath) SLANG_OVERRIDE; - virtual SLANG_NO_THROW SlangResult SLANG_MCALL getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) 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() { _clear(); } + protected: struct Entry @@ -109,7 +109,6 @@ protected: void* getInterface(const Guid& guid); void* getObject(const Guid& guid); - SlangResult _calcCanonicalPath(const char* path, StringBuilder& out); Entry* _getEntryFromPath(const char* path, String* outPath = nullptr); Entry* _getEntryFromCanonicalPath(const String& canonicalPath); /// Creates or returns a file entry for the given path. @@ -118,8 +117,15 @@ protected: /// 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() { m_entries = Dictionary(); } + void _clear(); // Maps canonical paths to an entries (which could be files or directories) Dictionary m_entries; diff --git a/source/core/slang-platform.cpp b/source/core/slang-platform.cpp index 921c85612..dc2d563df 100644 --- a/source/core/slang-platform.cpp +++ b/source/core/slang-platform.cpp @@ -289,6 +289,14 @@ static const PlatformFlags s_familyFlags[int(PlatformFamily::CountOf)] = return s_familyFlags[int(family)]; } - +/* static */SlangResult PlatformUtil::outputDebugMessage(const char* text) +{ +#ifdef _WIN32 + OutputDebugStringA(text); + return SLANG_OK; +#else + return SLANG_E_NOT_AVAILABLE; +#endif +} } diff --git a/source/core/slang-platform.h b/source/core/slang-platform.h index c8bea747f..2aa9184b0 100644 --- a/source/core/slang-platform.h +++ b/source/core/slang-platform.h @@ -136,6 +136,13 @@ namespace Slang /// Get the path to this instance (the path to the dll/executable/shared library the call is in) /// NOTE! This is not supported on all platforms, and will return SLANG_E_NOT_IMPLEMENTED in that scenario static SlangResult getInstancePath(StringBuilder& out); + + /// Outputs message to a debug stream. Not all platforms support + /// this feature. + /// + /// @param text Text to be displayed in 'debugger output' + /// @return SLANG_E_NOT_AVAILABLE if not on this platform, and potentially other errors + static SlangResult outputDebugMessage(const char* text); }; #ifndef _MSC_VER diff --git a/source/core/slang-riff-file-system.cpp b/source/core/slang-riff-file-system.cpp index da73cb2a5..6fe8b0952 100644 --- a/source/core/slang-riff-file-system.cpp +++ b/source/core/slang-riff-file-system.cpp @@ -93,6 +93,23 @@ SlangResult RiffFileSystem::saveFile(const char* path, const void* data, size_t return SLANG_OK; } +SlangResult RiffFileSystem::saveFileBlob(const char* path, ISlangBlob* dataBlob) +{ + if (!dataBlob) + { + return SLANG_E_INVALID_ARG; + } + + if (m_compressionSystem) + { + return saveFile(path, dataBlob->getBufferPointer(), dataBlob->getBufferSize()); + } + else + { + return Super::saveFileBlob(path, dataBlob); + } +} + SlangResult RiffFileSystem::loadArchive(const void* archive, size_t archiveSizeInBytes) { // Load the riff @@ -189,6 +206,12 @@ SlangResult RiffFileSystem::loadArchive(const void* archive, size_t archiveSizeI default: return SLANG_FAIL; } + // If it's the root entry we can ignore (as already added) + if (dstEntry.m_canonicalPath == ".") + { + continue; + } + // Add to the list of entries m_entries.Add(dstEntry.m_canonicalPath, dstEntry); } @@ -214,10 +237,16 @@ SlangResult RiffFileSystem::storeArchive(bool blobOwnsContent, ISlangBlob** outB for (const auto& pair : m_entries) { - RiffContainer::ScopeChunk scopeData(&container, RiffContainer::Chunk::Kind::Data, RiffFileSystemBinary::kEntryFourCC); - const Entry* srcEntry = &pair.Value; + // Ignore the root entry + if (srcEntry->m_canonicalPath == toSlice(".")) + { + continue; + } + + RiffContainer::ScopeChunk scopeData(&container, RiffContainer::Chunk::Kind::Data, RiffFileSystemBinary::kEntryFourCC); + RiffFileSystemBinary::Entry dstEntry; dstEntry.uncompressedSize = 0; dstEntry.compressedSize = 0; diff --git a/source/core/slang-riff-file-system.h b/source/core/slang-riff-file-system.h index eb9f24c73..60e5a7ae2 100644 --- a/source/core/slang-riff-file-system.h +++ b/source/core/slang-riff-file-system.h @@ -63,7 +63,8 @@ public: // 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; + // IArchiveFileSystem virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadArchive(const void* archive, size_t archiveSizeInBytes) SLANG_OVERRIDE; virtual SLANG_NO_THROW SlangResult SLANG_MCALL storeArchive(bool blobOwnsContent, ISlangBlob** outBlob) SLANG_OVERRIDE; diff --git a/source/core/slang-string-slice-index-map.cpp b/source/core/slang-string-slice-index-map.cpp new file mode 100644 index 000000000..d147556e4 --- /dev/null +++ b/source/core/slang-string-slice-index-map.cpp @@ -0,0 +1,44 @@ +#include "slang-string-slice-index-map.h" + +namespace Slang +{ + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! StringSliceIndexMap !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +StringSliceIndexMap::CountIndex StringSliceIndexMap::add(const UnownedStringSlice& key, Index valueIndex) +{ + StringSlicePool::Handle handle; + m_pool.findOrAdd(key, handle); + const CountIndex countIndex = StringSlicePool::asIndex(handle); + if (countIndex >= m_indexMap.getCount()) + { + SLANG_ASSERT(countIndex == m_indexMap.getCount()); + m_indexMap.add(valueIndex); + } + else + { + m_indexMap[countIndex] = valueIndex; + } + return countIndex; +} + +StringSliceIndexMap::CountIndex StringSliceIndexMap::findOrAdd(const UnownedStringSlice& key, Index defaultValueIndex) +{ + StringSlicePool::Handle handle; + m_pool.findOrAdd(key, handle); + const CountIndex countIndex = StringSlicePool::asIndex(handle); + if (countIndex >= m_indexMap.getCount()) + { + SLANG_ASSERT(countIndex == m_indexMap.getCount()); + m_indexMap.add(defaultValueIndex); + } + return countIndex; +} + +void StringSliceIndexMap::clear() +{ + m_pool.clear(); + m_indexMap.clear(); +} + +} // namespace Slang diff --git a/source/core/slang-string-slice-index-map.h b/source/core/slang-string-slice-index-map.h new file mode 100644 index 000000000..5aef62253 --- /dev/null +++ b/source/core/slang-string-slice-index-map.h @@ -0,0 +1,78 @@ +#ifndef SLANG_CORE_STRING_SLICE_INDEX_MAP_H +#define SLANG_CORE_STRING_SLICE_INDEX_MAP_H + +#include "slang-basic.h" + +#include "slang-string-slice-pool.h" + +namespace Slang +{ + +/* Maps an UnownedStringSlice to an index. All substrings are held internally in a StringSlicePool, and so +owned by the type. +*/ +class StringSliceIndexMap +{ +public: + /// An index that identifies a key value pair. + typedef Index CountIndex; + + /// Adds a key, value pair. Returns the CountIndex of the pair. + /// If there is already a value stored for the key it is replaced. + CountIndex add(const UnownedStringSlice& key, Index valueIndex); + + /// Finds or adds the slice. If the slice is added the defaultValueIndex is set. + /// If not the index associated with the slice remains the same. + /// Returns the CountIndex where the key,value pair are stored + CountIndex findOrAdd(const UnownedStringSlice& key, Index defaultValueIndex); + + /// Gets the index associated with the key. Returns -1 if there is no associated index. + SLANG_FORCE_INLINE Index getValue(const UnownedStringSlice& key); + + /// Get the amount of pairs in the map + Index getCount() const { return m_indexMap.getCount(); } + + /// Get the slice and the index at the specified index + SLANG_INLINE KeyValuePair getAt(CountIndex countIndex) const; + + /// Clear the contents of the map + void clear(); + + /// Get the key at the specified index + UnownedStringSlice getKeyAt(CountIndex index) const { return m_pool.getSlice(StringSlicePool::Handle(index)); } + /// Get the value at the specified index + Index& getValueAt(CountIndex index) { return m_indexMap[index]; } + + /// Get the amount of key,value pairs + Index getCount() { return m_indexMap.getCount(); } + + /// Ctor + StringSliceIndexMap() : + m_pool(StringSlicePool::Style::Empty) + { + } + +protected: + StringSlicePool m_pool; ///< Pool holds the substrings + List m_indexMap; ///< Maps a pool index to the output index +}; + +// --------------------------------------------------------------------------- +Index StringSliceIndexMap::getValue(const UnownedStringSlice& key) +{ + const Index poolIndex = m_pool.findIndex(key); + return (poolIndex >= 0) ? m_indexMap[poolIndex] : -1; +} + +// --------------------------------------------------------------------------- +KeyValuePair StringSliceIndexMap::getAt(CountIndex countIndex) const +{ + KeyValuePair pair; + pair.Key = m_pool.getSlice(StringSlicePool::Handle(countIndex)); + pair.Value = m_indexMap[countIndex]; + return pair; +} + +} + +#endif diff --git a/source/core/slang-zip-file-system.cpp b/source/core/slang-zip-file-system.cpp index d9c0a900e..28782dba7 100644 --- a/source/core/slang-zip-file-system.cpp +++ b/source/core/slang-zip-file-system.cpp @@ -10,6 +10,8 @@ #include "slang-uint-set.h" #include "slang-riff.h" +#include "slang-implicit-directory-collector.h" + #include "../../external/miniz/miniz.h" #include "../../external/miniz/miniz_common.h" #include "../../external/miniz/miniz_tdef.h" @@ -35,14 +37,14 @@ public: 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 getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath) SLANG_OVERRIDE; - virtual SLANG_NO_THROW SlangResult SLANG_MCALL getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) 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; @@ -519,25 +521,40 @@ SlangResult ZipFileSystemImpl::getPathType(const char* path, SlangPathType* outP return SLANG_E_NOT_FOUND; } -SlangResult ZipFileSystemImpl::getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) +SlangResult ZipFileSystemImpl::getPath(PathKind pathKind, const char* path, ISlangBlob** outPath) { - mz_uint index; - SLANG_RETURN_ON_FAIL(_findEntryIndex(path, index)); - - mz_zip_archive_file_stat fileStat; - if (!mz_zip_reader_file_stat(&m_archive, index, &fileStat)) + switch (pathKind) { - return SLANG_FAIL; + case PathKind::Display: + case PathKind::Canonical: + { + mz_uint index; + SLANG_RETURN_ON_FAIL(_findEntryIndex(path, index)); + + mz_zip_archive_file_stat fileStat; + if (!mz_zip_reader_file_stat(&m_archive, index, &fileStat)) + { + return SLANG_FAIL; + } + + // Use the path in the archive itself + *outPath = StringUtil::createStringBlob(fileStat.m_filename).detach(); + return SLANG_OK; + } + case PathKind::Simplified: + { + *outPath = StringUtil::createStringBlob(Path::simplify(path)).detach(); + return SLANG_OK; + } + default: break; } - // Use the path in the archive itself - *outCanonicalPath = StringUtil::createStringBlob(fileStat.m_filename).detach(); - return SLANG_OK; + return SLANG_E_NOT_AVAILABLE; } SlangResult ZipFileSystemImpl::getFileUniqueIdentity(const char* path, ISlangBlob** outUniqueIdentity) { - return getCanonicalPath(path, outUniqueIdentity); + return getPath(PathKind::Canonical, path, outUniqueIdentity); } SlangResult ZipFileSystemImpl::calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut) @@ -561,12 +578,6 @@ SlangResult ZipFileSystemImpl::calcCombinedPath(SlangPathType fromPathType, cons return SLANG_OK; } -SlangResult ZipFileSystemImpl::getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath) -{ - *outSimplifiedPath = StringUtil::createStringBlob(Path::simplify(path)).detach(); - return SLANG_OK; -} - SlangResult ZipFileSystemImpl::_getPathContents(ImplicitDirectoryCollector::State terminationState, ImplicitDirectoryCollector* outCollector) { if (!_hasArchive()) @@ -619,6 +630,16 @@ SlangResult ZipFileSystemImpl::enumeratePathContents(const char* path, FileSyste return collector.enumerate(callback, userData); } +SlangResult ZipFileSystemImpl::saveFileBlob(const char* path, ISlangBlob* dataBlob) +{ + if (!dataBlob) + { + return SLANG_E_INVALID_ARG; + } + + return saveFile(path, dataBlob->getBufferPointer(), dataBlob->getBufferSize()); +} + SlangResult ZipFileSystemImpl::saveFile(const char* path, const void* data, size_t size) { String fixedPath; -- cgit v1.2.3