summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2022-10-03 21:09:16 -0400
committerGitHub <noreply@github.com>2022-10-03 18:09:16 -0700
commit0b51ea6bb54b1d8a12695ccc2c259fd591069791 (patch)
tree1ff0587eb1454891bf8421a86b95ed5e95419e75
parentcc3548c92b1cf028b94d7a264a55df83e6d4d212 (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.
-rw-r--r--build/visual-studio/core/core.vcxproj4
-rw-r--r--build/visual-studio/core/core.vcxproj.filters12
-rw-r--r--build/visual-studio/slang-rt/slang-rt.vcxproj4
-rw-r--r--build/visual-studio/slang-rt/slang-rt.vcxproj.filters12
-rw-r--r--slang.h89
-rw-r--r--source/compiler-core/slang-artifact-handler-impl.cpp8
-rw-r--r--source/compiler-core/slang-include-system.cpp2
-rw-r--r--source/compiler-core/slang-source-loc.cpp12
-rw-r--r--source/core/slang-archive-file-system.cpp107
-rw-r--r--source/core/slang-archive-file-system.h126
-rw-r--r--source/core/slang-file-system.cpp163
-rw-r--r--source/core/slang-file-system.h22
-rw-r--r--source/core/slang-implicit-directory-collector.cpp75
-rw-r--r--source/core/slang-implicit-directory-collector.h68
-rw-r--r--source/core/slang-io.cpp32
-rw-r--r--source/core/slang-io.h62
-rw-r--r--source/core/slang-memory-file-system.cpp159
-rw-r--r--source/core/slang-memory-file-system.h20
-rw-r--r--source/core/slang-platform.cpp10
-rw-r--r--source/core/slang-platform.h7
-rw-r--r--source/core/slang-riff-file-system.cpp33
-rw-r--r--source/core/slang-riff-file-system.h3
-rw-r--r--source/core/slang-string-slice-index-map.cpp44
-rw-r--r--source/core/slang-string-slice-index-map.h78
-rw-r--r--source/core/slang-zip-file-system.cpp59
-rw-r--r--source/slang/slang.cpp12
-rw-r--r--tools/slang-test/slang-test-main.cpp10
-rw-r--r--tools/slang-unit-test/unit-test-compression.cpp172
-rw-r--r--tools/slang-unit-test/unit-test-file-system.cpp246
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>
diff --git a/slang.h b/slang.h
index 72d39ec5f..42825f5c4 100644
--- a/slang.h
+++ b/slang.h
@@ -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)));
+ }
}
}