diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2022-10-03 21:09:16 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-10-03 18:09:16 -0700 |
| commit | 0b51ea6bb54b1d8a12695ccc2c259fd591069791 (patch) | |
| tree | 1ff0587eb1454891bf8421a86b95ed5e95419e75 | |
| parent | cc3548c92b1cf028b94d7a264a55df83e6d4d212 (diff) | |
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.
29 files changed, 961 insertions, 690 deletions
diff --git a/build/visual-studio/core/core.vcxproj b/build/visual-studio/core/core.vcxproj index 6311e4b8b..a49ccd040 100644 --- a/build/visual-studio/core/core.vcxproj +++ b/build/visual-studio/core/core.vcxproj @@ -277,6 +277,7 @@ <ClInclude Include="..\..\..\source\core\slang-hash.h" /> <ClInclude Include="..\..\..\source\core\slang-hex-dump-util.h" /> <ClInclude Include="..\..\..\source\core\slang-http.h" /> + <ClInclude Include="..\..\..\source\core\slang-implicit-directory-collector.h" /> <ClInclude Include="..\..\..\source\core\slang-io.h" /> <ClInclude Include="..\..\..\source\core\slang-lazy-castable-list.h" /> <ClInclude Include="..\..\..\source\core\slang-linked-list.h" /> @@ -305,6 +306,7 @@ <ClInclude Include="..\..\..\source\core\slang-std-writers.h" /> <ClInclude Include="..\..\..\source\core\slang-stream.h" /> <ClInclude Include="..\..\..\source\core\slang-string-escape-util.h" /> + <ClInclude Include="..\..\..\source\core\slang-string-slice-index-map.h" /> <ClInclude Include="..\..\..\source\core\slang-string-slice-pool.h" /> <ClInclude Include="..\..\..\source\core\slang-string-util.h" /> <ClInclude Include="..\..\..\source\core\slang-string.h" /> @@ -333,6 +335,7 @@ <ClCompile Include="..\..\..\source\core\slang-free-list.cpp" /> <ClCompile Include="..\..\..\source\core\slang-hex-dump-util.cpp" /> <ClCompile Include="..\..\..\source\core\slang-http.cpp" /> + <ClCompile Include="..\..\..\source\core\slang-implicit-directory-collector.cpp" /> <ClCompile Include="..\..\..\source\core\slang-io.cpp" /> <ClCompile Include="..\..\..\source\core\slang-lazy-castable-list.cpp" /> <ClCompile Include="..\..\..\source\core\slang-lz4-compression-system.cpp" /> @@ -353,6 +356,7 @@ <ClCompile Include="..\..\..\source\core\slang-std-writers.cpp" /> <ClCompile Include="..\..\..\source\core\slang-stream.cpp" /> <ClCompile Include="..\..\..\source\core\slang-string-escape-util.cpp" /> + <ClCompile Include="..\..\..\source\core\slang-string-slice-index-map.cpp" /> <ClCompile Include="..\..\..\source\core\slang-string-slice-pool.cpp" /> <ClCompile Include="..\..\..\source\core\slang-string-util.cpp" /> <ClCompile Include="..\..\..\source\core\slang-string.cpp" /> diff --git a/build/visual-studio/core/core.vcxproj.filters b/build/visual-studio/core/core.vcxproj.filters index bf553e327..9266db917 100644 --- a/build/visual-studio/core/core.vcxproj.filters +++ b/build/visual-studio/core/core.vcxproj.filters @@ -90,6 +90,9 @@ <ClInclude Include="..\..\..\source\core\slang-http.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="..\..\..\source\core\slang-implicit-directory-collector.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="..\..\..\source\core\slang-io.h"> <Filter>Header Files</Filter> </ClInclude> @@ -174,6 +177,9 @@ <ClInclude Include="..\..\..\source\core\slang-string-escape-util.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="..\..\..\source\core\slang-string-slice-index-map.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="..\..\..\source\core\slang-string-slice-pool.h"> <Filter>Header Files</Filter> </ClInclude> @@ -254,6 +260,9 @@ <ClCompile Include="..\..\..\source\core\slang-http.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\..\..\source\core\slang-implicit-directory-collector.cpp"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="..\..\..\source\core\slang-io.cpp"> <Filter>Source Files</Filter> </ClCompile> @@ -314,6 +323,9 @@ <ClCompile Include="..\..\..\source\core\slang-string-escape-util.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\..\..\source\core\slang-string-slice-index-map.cpp"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="..\..\..\source\core\slang-string-slice-pool.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/build/visual-studio/slang-rt/slang-rt.vcxproj b/build/visual-studio/slang-rt/slang-rt.vcxproj index a411c7c9b..55c69a6c2 100644 --- a/build/visual-studio/slang-rt/slang-rt.vcxproj +++ b/build/visual-studio/slang-rt/slang-rt.vcxproj @@ -289,6 +289,7 @@ <ClInclude Include="..\..\..\source\core\slang-hash.h" /> <ClInclude Include="..\..\..\source\core\slang-hex-dump-util.h" /> <ClInclude Include="..\..\..\source\core\slang-http.h" /> + <ClInclude Include="..\..\..\source\core\slang-implicit-directory-collector.h" /> <ClInclude Include="..\..\..\source\core\slang-io.h" /> <ClInclude Include="..\..\..\source\core\slang-lazy-castable-list.h" /> <ClInclude Include="..\..\..\source\core\slang-linked-list.h" /> @@ -317,6 +318,7 @@ <ClInclude Include="..\..\..\source\core\slang-std-writers.h" /> <ClInclude Include="..\..\..\source\core\slang-stream.h" /> <ClInclude Include="..\..\..\source\core\slang-string-escape-util.h" /> + <ClInclude Include="..\..\..\source\core\slang-string-slice-index-map.h" /> <ClInclude Include="..\..\..\source\core\slang-string-slice-pool.h" /> <ClInclude Include="..\..\..\source\core\slang-string-util.h" /> <ClInclude Include="..\..\..\source\core\slang-string.h" /> @@ -346,6 +348,7 @@ <ClCompile Include="..\..\..\source\core\slang-free-list.cpp" /> <ClCompile Include="..\..\..\source\core\slang-hex-dump-util.cpp" /> <ClCompile Include="..\..\..\source\core\slang-http.cpp" /> + <ClCompile Include="..\..\..\source\core\slang-implicit-directory-collector.cpp" /> <ClCompile Include="..\..\..\source\core\slang-io.cpp" /> <ClCompile Include="..\..\..\source\core\slang-lazy-castable-list.cpp" /> <ClCompile Include="..\..\..\source\core\slang-lz4-compression-system.cpp" /> @@ -366,6 +369,7 @@ <ClCompile Include="..\..\..\source\core\slang-std-writers.cpp" /> <ClCompile Include="..\..\..\source\core\slang-stream.cpp" /> <ClCompile Include="..\..\..\source\core\slang-string-escape-util.cpp" /> + <ClCompile Include="..\..\..\source\core\slang-string-slice-index-map.cpp" /> <ClCompile Include="..\..\..\source\core\slang-string-slice-pool.cpp" /> <ClCompile Include="..\..\..\source\core\slang-string-util.cpp" /> <ClCompile Include="..\..\..\source\core\slang-string.cpp" /> diff --git a/build/visual-studio/slang-rt/slang-rt.vcxproj.filters b/build/visual-studio/slang-rt/slang-rt.vcxproj.filters index b8f9b468f..d5726e5f9 100644 --- a/build/visual-studio/slang-rt/slang-rt.vcxproj.filters +++ b/build/visual-studio/slang-rt/slang-rt.vcxproj.filters @@ -90,6 +90,9 @@ <ClInclude Include="..\..\..\source\core\slang-http.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="..\..\..\source\core\slang-implicit-directory-collector.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="..\..\..\source\core\slang-io.h"> <Filter>Header Files</Filter> </ClInclude> @@ -174,6 +177,9 @@ <ClInclude Include="..\..\..\source\core\slang-string-escape-util.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="..\..\..\source\core\slang-string-slice-index-map.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="..\..\..\source\core\slang-string-slice-pool.h"> <Filter>Header Files</Filter> </ClInclude> @@ -257,6 +263,9 @@ <ClCompile Include="..\..\..\source\core\slang-http.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\..\..\source\core\slang-implicit-directory-collector.cpp"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="..\..\..\source\core\slang-io.cpp"> <Filter>Source Files</Filter> </ClCompile> @@ -317,6 +326,9 @@ <ClCompile Include="..\..\..\source\core\slang-string-escape-util.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\..\..\source\core\slang-string-slice-index-map.cpp"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="..\..\..\source\core\slang-string-slice-pool.cpp"> <Filter>Source Files</Filter> </ClCompile> @@ -1090,7 +1090,39 @@ extern "C" { None, ///< Paths do not map to the file system Direct, ///< Paths map directly to the file system - Canonical, ///< Only canonical paths map to the file system + OperatingSystem, ///< Only paths gained via PathKind::OperatingSystem map to the operating system file system + }; + + /* Used to determine what kind of path is required from an input path */ + enum class PathKind + { + /// Given a path, returns a simplified version of that path. + /// This typically means removing '..' and/or '.' from the path. + /// A simplified path must point to the same object as the original. + Simplified, + + /// Given a path, returns a 'canonical path' to the item. + /// This may be the operating system 'canonical path' that is the unique path to the item. + /// + /// If the item exists the returned canonical path should always be usable to access the item. + /// + /// If the item the path specifies doesn't exist, the canonical path may not be returnable + /// or be a path simplification. + /// Not all file systems support canonical paths. + Canonical, + + /// Given a path returns a path such that it is suitable to be displayed to the user. + /// + /// For example if the file system is a zip file - it might include the path to the zip + /// container as well as the path to the specific file. + /// + /// NOTE! The display path won't necessarily work on the file system to access the item + Display, + + /// Get the path to the item on the *operating system* file system, if available. + OperatingSystem, + + CountOf, }; /** An extended file system abstraction. @@ -1161,36 +1193,17 @@ extern "C" const char* path, SlangPathType* pathTypeOut) = 0; - /** Get a simplified path. - Given a path, returns a simplified version of that path - typically removing '..' and/or '.'. A simplified - path must point to the same object as the original. - - This method is optional, if not implemented return SLANG_E_NOT_IMPLEMENTED. + /** Get a path based on the kind. - @param path - @param outSimplifiedPath + @param kind The kind of path wanted + @param path The input path + @param outPath The output path held in a blob @returns SLANG_OK if successfully simplified the path (SLANG_E_NOT_IMPLEMENTED if not implemented, or some other error code) */ - virtual SLANG_NO_THROW SlangResult SLANG_MCALL getSimplifiedPath( + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getPath( + PathKind kind, const char* path, - ISlangBlob** outSimplifiedPath) = 0; - - /** Get a canonical path identifies an object of the file system. - - Given a path, returns a 'canonicalPath' to the file. This may be a file system 'canonical path' to - show where a file was read from. If the file system is say a zip file - it might include the path to the zip - container as well as the absolute path to the specific file. The main purpose of the method is to be able - to display to uses unambiguously where a file was read from. - - This method is optional, if not implemented return SLANG_E_NOT_IMPLEMENTED. - - @param path - @param outCanonicalPath - @returns SLANG_OK if successfully canonicalized the path (SLANG_E_NOT_IMPLEMENTED if not implemented, or some other error code) - */ - virtual SLANG_NO_THROW SlangResult SLANG_MCALL getCanonicalPath( - const char* path, - ISlangBlob** outCanonicalPath) = 0; + ISlangBlob** outPath) = 0; /** Clears any cached information */ virtual SLANG_NO_THROW void SLANG_MCALL clearCache() = 0; @@ -1211,7 +1224,7 @@ extern "C" /** Returns how paths map to the OS file system - @returns true if this + @returns OSPathKind that describes how paths map to the Operating System file system */ virtual SLANG_NO_THROW OSPathKind SLANG_MCALL getOSPathKind() = 0; }; @@ -1222,11 +1235,11 @@ extern "C" { SLANG_COM_INTERFACE(0xa058675c, 0x1d65, 0x452a, { 0x84, 0x58, 0xcc, 0xde, 0xd1, 0x42, 0x71, 0x5 }) - /** Write the data specified with data and size to the specified path. + /** Write data to the specified path. @param path The path for data to be saved to @param data The data to be saved - @param size The size of the data + @param size The size of the data in bytes @returns SLANG_OK if successful (SLANG_E_NOT_IMPLEMENTED if not implemented, or some other error code) */ virtual SLANG_NO_THROW SlangResult SLANG_MCALL saveFile( @@ -1234,6 +1247,22 @@ extern "C" const void* data, size_t size) = 0; + /** Write data in the form of a blob to the specified path. + + Depending on the implementation writing a blob might be faster/use less memory. It is assumed the + blob is *immutable* and that an implementation can reference count it. + + It is not guaranteed loading the same file will return the *same* blob - just a blob with same + contents. + + @param path The path for data to be saved to + @param dataBlob The data to be saved + @returns SLANG_OK if successful (SLANG_E_NOT_IMPLEMENTED if not implemented, or some other error code) + */ + virtual SLANG_NO_THROW SlangResult SLANG_MCALL saveFileBlob( + const char* path, + ISlangBlob* dataBlob) = 0; + /** Remove the entry in the path (directory of file). Will only delete an empty directory, if not empty will return an error. diff --git a/source/compiler-core/slang-artifact-handler-impl.cpp b/source/compiler-core/slang-artifact-handler-impl.cpp index d7c3f02b6..9bf4313a9 100644 --- a/source/compiler-core/slang-artifact-handler-impl.cpp +++ b/source/compiler-core/slang-artifact-handler-impl.cpp @@ -176,12 +176,12 @@ SlangResult DefaultArtifactHandler::_createOSFile(IArtifact* artifact, ArtifactK path = UnownedStringSlice(extRep->getPath()); break; } - case OSPathKind::Canonical: + case OSPathKind::OperatingSystem: { - ComPtr<ISlangBlob> canonicalPathBlob; - if (SLANG_SUCCEEDED(system->getCanonicalPath(extRep->getPath(), canonicalPathBlob.writeRef()))) + ComPtr<ISlangBlob> osPathBlob; + if (SLANG_SUCCEEDED(system->getPath(PathKind::OperatingSystem, extRep->getPath(), osPathBlob.writeRef()))) { - path = StringUtil::getString(canonicalPathBlob); + path = StringUtil::getString(osPathBlob); } break; } diff --git a/source/compiler-core/slang-include-system.cpp b/source/compiler-core/slang-include-system.cpp index ebfc8db05..f0a850a81 100644 --- a/source/compiler-core/slang-include-system.cpp +++ b/source/compiler-core/slang-include-system.cpp @@ -68,7 +68,7 @@ SlangResult IncludeSystem::findFile(SlangPathType fromPathType, const String& fr String IncludeSystem::simplifyPath(const String& path) { ComPtr<ISlangBlob> simplifiedPath; - if (SLANG_FAILED(m_fileSystemExt->getSimplifiedPath(path.getBuffer(), simplifiedPath.writeRef()))) + if (SLANG_FAILED(m_fileSystemExt->getPath(PathKind::Simplified, path.getBuffer(), simplifiedPath.writeRef()))) { return path; } diff --git a/source/compiler-core/slang-source-loc.cpp b/source/compiler-core/slang-source-loc.cpp index ac6589e76..95c6f5db3 100644 --- a/source/compiler-core/slang-source-loc.cpp +++ b/source/compiler-core/slang-source-loc.cpp @@ -419,15 +419,15 @@ String SourceFile::calcVerbosePath() const if (fileSystemExt) { - String canonicalPath; - ComPtr<ISlangBlob> canonicalPathBlob; - if (SLANG_SUCCEEDED(fileSystemExt->getCanonicalPath(m_pathInfo.foundPath.getBuffer(), canonicalPathBlob.writeRef()))) + String displayPath; + ComPtr<ISlangBlob> displayPathBlob; + if (SLANG_SUCCEEDED(fileSystemExt->getPath(PathKind::Display, m_pathInfo.foundPath.getBuffer(), displayPathBlob.writeRef()))) { - canonicalPath = StringUtil::getString(canonicalPathBlob); + displayPath = StringUtil::getString(displayPathBlob); } - if (canonicalPath.getLength() > 0) + if (displayPath.getLength() > 0) { - return canonicalPath; + return displayPath; } } 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<ISlangFileSystemExt>& outFileSystem) { ComPtr<ISlangMutableFileSystem> 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<UnownedStringSlice, Index> 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<Index> 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<UnownedStringSlice, Index> StringSliceIndexMap::getAt(CountIndex countIndex) const -{ - KeyValuePair<UnownedStringSlice, Index> 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<ISlangFileSystemExt>& outFileSystem); SlangResult createArchiveFileSystem(SlangArchiveType type, ComPtr<ISlangMutableFileSystem>& 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, ComPtr<ISla return style; } -// Cacluate a combined path, just using Path:: string processing +// Calcuate a combined path, just using Path:: string processing static SlangResult _calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut) { String relPath; @@ -117,26 +117,42 @@ SlangResult OSFileSystem::getFileUniqueIdentity(const char* pathIn, ISlangBlob** SLANG_RETURN_ON_FAIL(_checkExt(m_style)); // By default we use the canonical path to uniquely identify a file - return getCanonicalPath(pathIn, outUniqueIdentity); + return getPath(PathKind::Canonical, pathIn, outUniqueIdentity); } -SlangResult OSFileSystem::getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) +SlangResult OSFileSystem::getPath(PathKind pathKind, const char* path, ISlangBlob** outPath) { 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)); + switch (pathKind) + { + case PathKind::OperatingSystem: + case PathKind::Display: + { + // It's possible canonical path fail... + if (SLANG_SUCCEEDED(getPath(PathKind::Canonical, path, outPath))) + { + return SLANG_OK; + } + // If so try simplified + return getPath(PathKind::Simplified, path, outPath); + } + case PathKind::Canonical: + { + String canonicalPath; + SLANG_RETURN_ON_FAIL(Path::getCanonical(_fixPathDelimiters(path), canonicalPath)); + *outPath = StringUtil::createStringBlob(canonicalPath).detach(); + return SLANG_OK; + } + case PathKind::Simplified: + { + String simplifiedPath = Path::simplify(path); + *outPath = StringUtil::createStringBlob(simplifiedPath).detach(); + return SLANG_OK; + } + } - String simplifiedPath = Path::simplify(pathIn); - *outSimplifiedPath = StringUtil::createStringBlob(simplifiedPath).detach(); - return SLANG_OK; + return SLANG_E_NOT_AVAILABLE; } SlangResult OSFileSystem::calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut) @@ -223,6 +239,15 @@ SlangResult OSFileSystem::saveFile(const char* pathIn, const void* data, size_t return SLANG_OK; } +SlangResult OSFileSystem::saveFileBlob(const char* path, ISlangBlob* dataBlob) +{ + if (!dataBlob) + { + return SLANG_E_INVALID_ARG; + } + return saveFile(path, dataBlob->getBufferPointer(), 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<ISlangBlob> 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<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())); + // 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<ISlangBlob> 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<ISlangFileSystem> m_fileSystem; ///< NOTE! Has to match what's in style, such style can be reached via reinterpret_cast + ComPtr<ISlangFileSystem> 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<UnownedStringSlice> 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<UnownedStringSlice>& 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<UnownedStringSlice>& 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<String>& 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<String> 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<UnownedStringSlice> 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<String, Entry>(); + // 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<String, Entry>(); } + void _clear(); // Maps canonical paths to an entries (which could be files or directories) Dictionary<String, Entry> 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<UnownedStringSlice, Index> 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<Index> 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<UnownedStringSlice, Index> StringSliceIndexMap::getAt(CountIndex countIndex) const +{ + KeyValuePair<UnownedStringSlice, Index> 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; diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index f2668b995..a3fd91ce0 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -49,14 +49,6 @@ // Used to print exception type names in internal-compiler-error messages #include <typeinfo> -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include <Windows.h> -#undef WIN32_LEAN_AND_MEAN -#undef NOMINMAX -#endif - extern Slang::String get_slang_cuda_prelude(); extern Slang::String get_slang_cpp_prelude(); extern Slang::String get_slang_hlsl_prelude(); @@ -4396,9 +4388,7 @@ void Session::addBuiltinSource( char const* diagnostics = sink.outputBuffer.getBuffer(); fprintf(stderr, "%s", diagnostics); -#ifdef _WIN32 - OutputDebugStringA(diagnostics); -#endif + PlatformUtil::outputDebugMessage(diagnostics); SLANG_UNEXPECTED("error in Slang standard library"); } diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp index a767f4f3b..8a8686a6b 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -1216,16 +1216,6 @@ static SlangResult _createArtifactFromHexDump(const UnownedStringSlice& hexDump, return SLANG_OK; } -static SlangResult _loadAsSharedLibrary(const UnownedStringSlice& hexDump, TemporaryFileSet& inOutTemporaryFileSet, ComPtr<ISlangSharedLibrary>& outSharedLibrary) -{ - ComPtr<IArtifact> artifact; - SLANG_RETURN_ON_FAIL(_createArtifactFromHexDump(hexDump, ArtifactDesc::make(ArtifactKind::SharedLibrary, ArtifactPayload::HostCPU, ArtifactStyle::Unknown), artifact)); - ComPtr<ICastable> castable; - SLANG_RETURN_ON_FAIL(artifact->getOrCreateRepresentation(ISlangSharedLibrary::getTypeGuid(), ArtifactKeep::Yes, castable.writeRef())); - outSharedLibrary = as<ISlangSharedLibrary>(castable); - return SLANG_OK; -} - static SlangResult _executeBinary(const UnownedStringSlice& hexDump, ExecuteResult& outExeRes) { ComPtr<IArtifact> artifact; diff --git a/tools/slang-unit-test/unit-test-compression.cpp b/tools/slang-unit-test/unit-test-compression.cpp index e3bcbcf00..486a252b2 100644 --- a/tools/slang-unit-test/unit-test-compression.cpp +++ b/tools/slang-unit-test/unit-test-compression.cpp @@ -1,174 +1,32 @@ // unit-compression.cpp - #include "tools/unit-test/slang-unit-test.h" -#include "source/core/slang-io.h" -#include "../../source/core/slang-zip-file-system.h" - #include "../../source/core/slang-lz4-compression-system.h" #include "../../source/core/slang-deflate-compression-system.h" -#include "../../source/core/slang-destroyable.h" - using namespace Slang; -static bool _equals(const void* data, size_t size, ISlangBlob* blob) -{ - return blob && blob->getBufferSize() == size && memcmp(blob->getBufferPointer(), data, size) == 0; -} - -template <size_t SIZE> -static bool _equals(const char (&text)[SIZE], ISlangBlob* blob) -{ - return _equals(text, SIZE, blob); -} - -static List<String> _getContents(ISlangFileSystemExt* fileSystem, const char* path) +static ICompressionSystem* _getCompressionSystem(CompressionSystemType type) { - List<String> objs; - - fileSystem->enumeratePathContents(path, [](SlangPathType pathType, const char* name, void* userData) { - List<String>& out = *(List<String>*)userData; - out.add(name); - }, &objs); - - return objs; + switch (type) + { + case CompressionSystemType::Deflate: return DeflateCompressionSystem::getSingleton(); break; + case CompressionSystemType::LZ4: return LZ4CompressionSystem::getSingleton(); break; + default: break; + } + return nullptr; } SLANG_UNIT_TEST(compression) { - const SlangArchiveType archiveTypes[] = - { - SLANG_ARCHIVE_TYPE_RIFF, - SLANG_ARCHIVE_TYPE_RIFF_DEFLATE, - SLANG_ARCHIVE_TYPE_RIFF_LZ4, - SLANG_ARCHIVE_TYPE_ZIP - }; - - for (auto archiveType : archiveTypes) - { - // Test out archive file systems - ComPtr<ISlangMutableFileSystem> fileSystem; - SLANG_CHECK(SLANG_SUCCEEDED(createArchiveFileSystem(archiveType, fileSystem))); - - const char contents[] = "I'm compressed"; - const char contents2[] = "Some more stuff"; - const char contents3[] = "Replace it"; - - { - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->createDirectory("hello"))); - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->createDirectory("hello2"))); - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->remove("hello"))); - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->createDirectory("hello"))); - - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->saveFile("file.txt", contents, SLANG_COUNT_OF(contents)))); - - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->saveFile("file2.txt", contents2, SLANG_COUNT_OF(contents2)))); - - ComPtr<ISlangBlob> blob; - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->loadFile("file.txt", blob.writeRef()))); - SLANG_CHECK(_equals(contents, blob)); - - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->loadFile("file2.txt", blob.writeRef()))); - SLANG_CHECK(_equals(contents2, blob)); - - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->saveFile("file2.txt", contents3, SLANG_COUNT_OF(contents3)))); - - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->loadFile("file2.txt", blob.writeRef()))); - SLANG_CHECK(_equals(contents3, blob)); - - // Check the path type - { - SlangPathType pathType; - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->getPathType("file2.txt", &pathType))); - SLANG_CHECK(pathType == SLANG_PATH_TYPE_FILE); - - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->getPathType("hello", &pathType))); - SLANG_CHECK(pathType == SLANG_PATH_TYPE_DIRECTORY); - } - - // Enumerate - { - for (const auto& obj : _getContents(fileSystem, "")) - { - // All of these should exist - SlangPathType pathType; - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->getPathType(obj.getBuffer(), &pathType))); - } - } - - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->saveFile("implicit-path/file2.txt", contents3, SLANG_COUNT_OF(contents3)))); - - { - SlangPathType pathType; - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->getPathType("implicit-path", &pathType))); - - SLANG_CHECK(pathType == SLANG_PATH_TYPE_DIRECTORY); - - List<String> objs = _getContents(fileSystem, "implicit-path"); - - // It contains a file - SLANG_CHECK(objs.getCount() == 1); - - for (const auto& obj : objs) - { - String path = Path::combine("implicit-path", obj); - - // All of these should exist - SlangPathType pathType; - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->getPathType(path.getBuffer(), &pathType))); - } - - // Make an explicit path, and see whe have the same results - fileSystem->createDirectory("implicit-path"); - - objs = _getContents(fileSystem, "implicit-path"); - SLANG_CHECK(objs.getCount() == 1); - } - } - - - // Load and check its okay - - { - IArchiveFileSystem* archiveFileSystem = as<IArchiveFileSystem>(fileSystem); - - ComPtr<ISlangBlob> archiveBlob; - SLANG_CHECK(SLANG_SUCCEEDED(archiveFileSystem->storeArchive(false, archiveBlob.writeRef()))); - - - ComPtr<ISlangFileSystemExt> fileSystem; -#if 0 - SLANG_CHECK(SLANG_SUCCEEDED(createArchiveFileSystem(archiveType, fileSystem))); - - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->loadArchive(archiveBlob->getBufferPointer(), archiveBlob->getBufferSize()))); -#else - SLANG_CHECK(SLANG_SUCCEEDED(loadArchiveFileSystem(archiveBlob->getBufferPointer(), archiveBlob->getBufferSize(), fileSystem))); -#endif - - ComPtr<ISlangBlob> blob; - - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->loadFile("file.txt", blob.writeRef()))); - SLANG_CHECK(_equals(contents, blob)); - - SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->loadFile("file2.txt", blob.writeRef()))); - SLANG_CHECK(_equals(contents3, blob)); - } - } - // Test out compression systems - for (Index i = 0; i < 2; ++i) + for (Index i = 0; i < Count(CompressionSystemType::CountOf); ++i) { - // Lets try lz4 - - ICompressionSystem* system = nullptr; - if (i == 0) + ICompressionSystem* system = _getCompressionSystem(CompressionSystemType(i)); + + if (!system) { - system = LZ4CompressionSystem::getSingleton(); - } - else - { - system = DeflateCompressionSystem::getSingleton(); + continue; } const char src[] = "Some text to compress"; @@ -176,16 +34,16 @@ SLANG_UNIT_TEST(compression) ComPtr<ISlangBlob> compressedBlob; + // Use the default style CompressionStyle style; SLANG_CHECK(SLANG_SUCCEEDED(system->compress(&style, src, srcSize, compressedBlob.writeRef()))); // Now lets decompress - List<char> decompressedData; decompressedData.setCount(srcSize); SLANG_CHECK(SLANG_SUCCEEDED(system->decompress(compressedBlob->getBufferPointer(), compressedBlob->getBufferSize(), srcSize, decompressedData.getBuffer()))); - SLANG_CHECK(memcmp(src, decompressedData.getBuffer(), srcSize) == 0); + SLANG_CHECK(::memcmp(src, decompressedData.getBuffer(), srcSize) == 0); } } diff --git a/tools/slang-unit-test/unit-test-file-system.cpp b/tools/slang-unit-test/unit-test-file-system.cpp index e3e2bb3cf..1575f4dfd 100644 --- a/tools/slang-unit-test/unit-test-file-system.cpp +++ b/tools/slang-unit-test/unit-test-file-system.cpp @@ -10,11 +10,15 @@ #include "../../source/core/slang-deflate-compression-system.h" #include "../../source/core/slang-lz4-compression-system.h" +#include "../../source/core/slang-destroyable.h" + +#include "../../source/core/slang-io.h" + #include "tools/unit-test/slang-unit-test.h" using namespace Slang; -namespace { +namespace { // anonymous enum class FileSystemType { @@ -31,22 +35,18 @@ struct Entry { typedef Entry ThisType; - bool operator<(const ThisType& rhs) const { return name < rhs.name; } - bool operator==(const ThisType& rhs) const { return name == rhs.name && type == rhs.type; } + bool operator<(const ThisType& rhs) const { return path < rhs.path; } + bool operator==(const ThisType& rhs) const { return path == rhs.path && type == rhs.type; } bool operator!=(const ThisType& rhs) const { return !(*this == rhs); } SlangPathType type; - String name; + String path; }; -} // +} // anonymous -static SlangResult _createAndCheckFile(ISlangMutableFileSystem* fileSystem, const char* path, const char* contents) +static SlangResult _checkFile(ISlangFileSystemExt* fileSystem, const char* path, const UnownedStringSlice& contentsSlice) { - UnownedStringSlice contentsSlice(contents); - - SLANG_RETURN_ON_FAIL(fileSystem->saveFile(path, contentsSlice.begin(), contentsSlice.getLength())); - SlangPathType pathType; SLANG_RETURN_ON_FAIL(fileSystem->getPathType(path, &pathType)); @@ -66,6 +66,31 @@ static SlangResult _createAndCheckFile(ISlangMutableFileSystem* fileSystem, cons { return SLANG_FAIL; } + return SLANG_OK; +} + +static SlangResult _createAndCheckFile(ISlangMutableFileSystem* fileSystem, const char* path, const char* contents) +{ + UnownedStringSlice contentsSlice(contents); + + SLANG_RETURN_ON_FAIL(fileSystem->saveFile(path, contentsSlice.begin(), contentsSlice.getLength())); + SLANG_RETURN_ON_FAIL(_checkFile(fileSystem, path, contentsSlice)); + + // Delete it + SLANG_RETURN_ON_FAIL(fileSystem->remove(path)); + + // Check it's gone + SlangPathType pathType; + if (SLANG_SUCCEEDED(fileSystem->getPathType(path, &pathType))) + { + return SLANG_FAIL; + } + + // Save as a blob + ComPtr<ISlangBlob> blob = RawBlob::create(contentsSlice.begin(), contentsSlice.getLength()); + + SLANG_RETURN_ON_FAIL(fileSystem->saveFileBlob(path, blob)); + SLANG_RETURN_ON_FAIL(_checkFile(fileSystem, path, contentsSlice)); return SLANG_OK; } @@ -91,7 +116,7 @@ static void _entryCallback(SlangPathType pathType, const char* name, void* userD out.add(Entry{pathType, name}); } -static SlangResult _enumeratePath(ISlangMutableFileSystem* fileSystem, const char* path, const ConstArrayView<Entry>& entries) +static SlangResult _enumeratePath(ISlangFileSystemExt* fileSystem, const char* path, const ConstArrayView<Entry>& entries) { List<Entry> contents; @@ -107,10 +132,10 @@ static SlangResult _enumeratePath(ISlangMutableFileSystem* fileSystem, const cha return SLANG_OK; } -static SlangResult _checkSimplifiedPath(ISlangMutableFileSystem* fileSystem, const char* path, const char* normalPath) +static SlangResult _checkSimplifiedPath(ISlangFileSystemExt* fileSystem, const char* path, const char* normalPath) { ComPtr<ISlangBlob> simplifiedPathBlob; - SLANG_RETURN_ON_FAIL(fileSystem->getSimplifiedPath(path, simplifiedPathBlob.writeRef())); + SLANG_RETURN_ON_FAIL(fileSystem->getPath(PathKind::Simplified, path, simplifiedPathBlob.writeRef())); auto simplifiedPath = StringUtil::getString(simplifiedPathBlob); @@ -122,47 +147,168 @@ static SlangResult _checkSimplifiedPath(ISlangMutableFileSystem* fileSystem, con return SLANG_OK; } -static SlangResult _test(FileSystemType type) +SlangResult _appendPathEntries(ISlangFileSystemExt* fileSystem, const char* inBasePath, List<Entry>& outEntries) { - ComPtr<ISlangMutableFileSystem> fileSystem; - - switch (type) + const UnownedStringSlice basePath(inBasePath); + if (basePath == toSlice(".") || basePath.getLength() == 0) { - case FileSystemType::Zip: + // We don't need to append path prefixes if we are at the root. + SLANG_RETURN_ON_FAIL(fileSystem->enumeratePathContents(inBasePath, _entryCallback, (void*)&outEntries)); + } + else + { + const Index startIndex = outEntries.getCount(); + SLANG_RETURN_ON_FAIL(fileSystem->enumeratePathContents(inBasePath, _entryCallback, (void*)&outEntries)); + + const String basePathString(basePath); + + // we need to fix all of the added paths to make absolute + const Count count = outEntries.getCount(); + for (Index i = startIndex; i < count; ++i) { - SLANG_RETURN_ON_FAIL(ZipFileSystem::create(fileSystem)); - break; + auto& entry = outEntries[i]; + entry.path = Path::combine(basePathString, entry.path); } - case FileSystemType::RiffUncompressed: + } + + return SLANG_OK; +} + +static SlangResult _getAllEntries(ISlangFileSystemExt* fileSystem, const char* inBasePath, List<Entry>& outEntries) +{ + outEntries.clear(); + + // Simplify the base + auto basePath = Path::simplify(inBasePath); + + _appendPathEntries(fileSystem, basePath.getBuffer(), outEntries); + + for (Index i = 0; i < outEntries.getCount(); ++i) + { + // We need to make a copy as outEntries is mutated + const Entry entry = outEntries[i]; + if (entry.type == SLANG_PATH_TYPE_DIRECTORY) { - fileSystem = new RiffFileSystem(nullptr); - break; + _appendPathEntries(fileSystem, entry.path.getBuffer(), outEntries); } - case FileSystemType::RiffDeflate: + } + + // Sort to remove issues with traversal ordering + outEntries.sort(); + return SLANG_OK; +} + +static SlangResult _checkEqual(ISlangFileSystemExt* a, ISlangFileSystemExt* b) +{ + List<Entry> aEntries, bEntries; + + SLANG_RETURN_ON_FAIL(_getAllEntries(a, ".", aEntries)); + SLANG_RETURN_ON_FAIL(_getAllEntries(b, ".", bEntries)); + + if (aEntries != bEntries) + { + return SLANG_FAIL; + } + + // For all the files check the contents is the same + + for (const auto& entry : aEntries) + { + if (entry.type != SLANG_PATH_TYPE_FILE) { - fileSystem = new RiffFileSystem(DeflateCompressionSystem::getSingleton()); - break; + continue; } - case FileSystemType::RiffLZ4: + + ComPtr<ISlangBlob> blobA, blobB; + + SLANG_RETURN_ON_FAIL(a->loadFile(entry.path.getBuffer(), blobA.writeRef())); + SLANG_RETURN_ON_FAIL(b->loadFile(entry.path.getBuffer(), blobB.writeRef())); + + if (blobA->getBufferSize() != blobB->getBufferSize()) { - fileSystem = new RiffFileSystem(LZ4CompressionSystem::getSingleton()); - break; + return SLANG_FAIL; } - case FileSystemType::Memory: + + if (::memcmp(blobA->getBufferPointer(), blobB->getBufferPointer(), blobA->getBufferSize()) != 0) { - fileSystem = new MemoryFileSystem; - break; + return SLANG_FAIL; } + } + + return SLANG_OK; +} + +static SlangResult _createFileSystem(FileSystemType type, ComPtr<ISlangMutableFileSystem>& outFileSystem) +{ + outFileSystem.setNull(); + switch (type) + { + case FileSystemType::Zip: return ZipFileSystem::create(outFileSystem); + case FileSystemType::RiffUncompressed: outFileSystem = new RiffFileSystem(nullptr); break; + case FileSystemType::RiffDeflate: outFileSystem = new RiffFileSystem(DeflateCompressionSystem::getSingleton()); break; + case FileSystemType::RiffLZ4: outFileSystem = new RiffFileSystem(LZ4CompressionSystem::getSingleton()); break; + case FileSystemType::Memory: outFileSystem = new MemoryFileSystem; break; case FileSystemType::Relative: { ComPtr<ISlangMutableFileSystem> memoryFileSystem(new MemoryFileSystem); memoryFileSystem->createDirectory("base"); - fileSystem = new RelativeFileSystem(memoryFileSystem, "base"); + outFileSystem = new RelativeFileSystem(memoryFileSystem, "base"); break; } } + return outFileSystem ? SLANG_OK : SLANG_FAIL; +} + +static SlangResult _testImplicitDirectory(FileSystemType type) +{ + ComPtr<ISlangMutableFileSystem> fileSystem; + SLANG_RETURN_ON_FAIL(_createFileSystem(type, fileSystem)); + + const char contents3[] = "Some text...."; + + SLANG_RETURN_ON_FAIL(fileSystem->saveFile("implicit-path/file2.txt", contents3, SLANG_COUNT_OF(contents3))); + + { + SlangPathType pathType; + SLANG_RETURN_ON_FAIL(fileSystem->getPathType("implicit-path", &pathType)); + + SLANG_CHECK(pathType == SLANG_PATH_TYPE_DIRECTORY); + + auto checkEntries = [&]() -> SlangResult + { + List<Entry> entries; + SLANG_RETURN_ON_FAIL(_getAllEntries(fileSystem, "implicit-path", entries)); + + // It contains a file + SLANG_CHECK(entries.getCount() == 1); + + for (const auto& entry : entries) + { + // All of these should exist + SlangPathType pathType; + SLANG_RETURN_ON_FAIL(fileSystem->getPathType(entry.path.getBuffer(), &pathType)); + } + return SLANG_OK; + }; + + SLANG_RETURN_ON_FAIL(checkEntries()); + + // Make an explicit path, and see whe have the same results + fileSystem->createDirectory("implicit-path"); + + SLANG_RETURN_ON_FAIL(checkEntries()); + } + + return SLANG_OK; +} + +static SlangResult _test(FileSystemType type) +{ + ComPtr<ISlangMutableFileSystem> fileSystem; + SLANG_RETURN_ON_FAIL(_createFileSystem(type, fileSystem)); + SLANG_RETURN_ON_FAIL(_createAndCheckFile(fileSystem, "a", "someText")); SLANG_RETURN_ON_FAIL(_createAndCheckFile(fileSystem, "b", "A longer bit of text....")); @@ -186,6 +332,22 @@ static SlangResult _test(FileSystemType type) SLANG_RETURN_ON_FAIL(_checkSimplifiedPath(fileSystem, "d/../a", "a")); } + + // If we have an archive file system check out it's behavior + if (IArchiveFileSystem* archiveFileSystem = as<IArchiveFileSystem>(fileSystem)) + { + // Load and check its okay + + ComPtr<ISlangBlob> archiveBlob; + SLANG_RETURN_ON_FAIL(archiveFileSystem->storeArchive(false, archiveBlob.writeRef())); + + ComPtr<ISlangFileSystemExt> loadedFileSystem; + SLANG_RETURN_ON_FAIL(loadArchiveFileSystem(archiveBlob->getBufferPointer(), archiveBlob->getBufferSize(), loadedFileSystem)); + + // Check the file systems contents are the same + SLANG_RETURN_ON_FAIL(_checkEqual(loadedFileSystem, fileSystem)); + } + SLANG_RETURN_ON_FAIL(fileSystem->remove("d/a")); { const Entry entries[] = { {SLANG_PATH_TYPE_FILE, "b" } }; @@ -216,9 +378,21 @@ SLANG_UNIT_TEST(fileSystem) { const auto type = FileSystemType(i); - auto const res = _test(type); - - SLANG_CHECK(SLANG_SUCCEEDED(res)); + SLANG_CHECK(SLANG_SUCCEEDED(_test(type))); + + // Some file system types support 'implicit directories'. + // This means that if a file is created with a path, the directories + // required to make that path valid are 'implicitly' created. + // + // Currently this behavior is supported by zip, and this test checks + // that it is working correctly, as we require the file system to + // behave correctly in other ways irrespectively of if the directory is + // implicit or not. + const bool hasImplicitDirectory = (type == FileSystemType::Zip); + if (hasImplicitDirectory) + { + SLANG_CHECK(SLANG_SUCCEEDED(_testImplicitDirectory(type))); + } } } |
