summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj1
-rw-r--r--build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj.filters3
-rw-r--r--source/core/slang-file-system.h2
-rw-r--r--source/core/slang-riff-file-system.cpp92
-rw-r--r--source/core/slang-riff-file-system.h39
-rw-r--r--tools/slang-unit-test/unit-test-file-system.cpp197
6 files changed, 281 insertions, 53 deletions
diff --git a/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj b/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj
index 1c5473dd8..a6a5dad7d 100644
--- a/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj
+++ b/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj
@@ -276,6 +276,7 @@
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-com-host-callable.cpp" />
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-command-line-args.cpp" />
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-compression.cpp" />
+ <ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-file-system.cpp" />
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-find-type-by-name.cpp" />
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-free-list.cpp" />
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-io.cpp" />
diff --git a/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj.filters b/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj.filters
index 21d943292..c84d21b30 100644
--- a/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj.filters
+++ b/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj.filters
@@ -29,6 +29,9 @@
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-compression.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-file-system.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-find-type-by-name.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/source/core/slang-file-system.h b/source/core/slang-file-system.h
index ea432ad30..d83d7d343 100644
--- a/source/core/slang-file-system.h
+++ b/source/core/slang-file-system.h
@@ -242,6 +242,8 @@ public:
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;
+ /// stripPath will remove any path for an access, making an access always just
+ /// access the *filename* from the input path, in the contained filesystem at the relative path
RelativeFileSystem(ISlangFileSystem* fileSystem, const String& relativePath, bool stripPath = false);
protected:
diff --git a/source/core/slang-riff-file-system.cpp b/source/core/slang-riff-file-system.cpp
index a8e0f2073..9402a2b4e 100644
--- a/source/core/slang-riff-file-system.cpp
+++ b/source/core/slang-riff-file-system.cpp
@@ -83,8 +83,7 @@ SlangResult RiffFileSystem::_calcCanonicalPath(const char* path, StringBuilder&
RiffFileSystem::Entry* RiffFileSystem::_getEntryFromCanonicalPath(const String& canonicalPath)
{
- RefPtr<Entry>* entryPtr = m_entries.TryGetValue(canonicalPath);
- return entryPtr ? *entryPtr : nullptr;
+ return m_entries.TryGetValue(canonicalPath);
}
RiffFileSystem::Entry* RiffFileSystem::_getEntryFromPath(const char* path, String* outPath)
@@ -170,7 +169,7 @@ SlangResult RiffFileSystem::getPathType(const char* path, SlangPathType* outPath
ImplicitDirectoryCollector collector(canonicalPath);
for (const auto& pair : m_entries)
{
- Entry* childEntry = pair.Value;
+ 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())
@@ -204,23 +203,20 @@ SlangResult RiffFileSystem::getCanonicalPath(const char* path, ISlangBlob** outC
return SLANG_OK;
}
-
SlangResult RiffFileSystem::enumeratePathContents(const char* path, FileSystemContentsCallBack callback, void* userData)
{
String canonicalPath;
Entry* entry = _getEntryFromPath(path, &canonicalPath);
- if (entry && entry->m_type != SLANG_PATH_TYPE_DIRECTORY)
- {
- return SLANG_FAIL;
- }
- // If we didn't find an explicit directory, lets handle an implicit one
- ImplicitDirectoryCollector collector(canonicalPath);
+ const bool foundDirectory = (entry && entry->m_type == SLANG_PATH_TYPE_DIRECTORY);
+
+ // We allow implicit directories, so this works even if there isn't an explicit one
+ ImplicitDirectoryCollector collector(canonicalPath, foundDirectory);
// If it is a directory, we need to see if there is anything in it
for (const auto& pair : m_entries)
{
- Entry* childEntry = pair.Value;
+ const Entry* childEntry = &pair.Value;
collector.addPath(childEntry->m_type, childEntry->m_canonicalPath.getUnownedSlice());
}
@@ -245,20 +241,23 @@ SlangResult RiffFileSystem::saveFile(const char* path, const void* data, size_t
contents = RawBlob::create(data, size);
}
- Entry* entry = _getEntryFromCanonicalPath(canonicalPath);
- if (!entry)
+ if (Entry* foundEntry = _getEntryFromCanonicalPath(canonicalPath))
{
- entry = new Entry;
- entry->m_type = SLANG_PATH_TYPE_FILE;
- entry->m_canonicalPath = canonicalPath;
- entry->m_uncompressedSizeInBytes = size;
+ if (foundEntry->m_type != SLANG_PATH_TYPE_FILE)
+ {
+ // Can only set if it's already a file, if it's anything else it's an error
+ return SLANG_FAIL;
+ }
+ foundEntry->setContents(size, contents);
+ }
+ else
+ {
+ Entry entry;
+ entry.initFile(canonicalPath, size, contents);
m_entries.Add(canonicalPath, entry);
}
- entry->m_uncompressedSizeInBytes = size;
- entry->m_contents = contents;
-
return SLANG_OK;
}
@@ -269,26 +268,25 @@ SlangResult RiffFileSystem::remove(const char* path)
if (entry)
{
- if (entry->m_type == SLANG_PATH_TYPE_FILE)
+ if (entry->m_type == SLANG_PATH_TYPE_DIRECTORY)
{
- m_entries.Remove(canonicalPath);
- return SLANG_OK;
- }
+ ImplicitDirectoryCollector collector(canonicalPath);
- ImplicitDirectoryCollector collector(canonicalPath);
-
- // If it is a directory, we need to see if there is anything in it
- for (const auto& pair : m_entries)
- {
- Entry* childEntry = pair.Value;
- collector.addPath(childEntry->m_type, childEntry->m_canonicalPath.getUnownedSlice());
- if (collector.hasContent())
+ // If it is a directory, we need to see if there is anything in it
+ for (const auto& pair : m_entries)
{
- // Directory is not empty
- return SLANG_FAIL;
+ const Entry* childEntry = &pair.Value;
+ collector.addPath(childEntry->m_type, childEntry->m_canonicalPath.getUnownedSlice());
+ if (collector.hasContent())
+ {
+ // Directory is not empty
+ return SLANG_FAIL;
+ }
}
}
+ // Reset so doesn't hold references/keep memory in scope
+ entry->reset();
m_entries.Remove(canonicalPath);
return SLANG_OK;
}
@@ -299,17 +297,13 @@ SlangResult RiffFileSystem::remove(const char* path)
SlangResult RiffFileSystem::createDirectory(const char* path)
{
String canonicalPath;
- Entry* entry = _getEntryFromPath(path, &canonicalPath);
- if (entry)
+ if (_getEntryFromPath(path, &canonicalPath))
{
return SLANG_FAIL;
}
- entry = new Entry;
- entry->m_type = SLANG_PATH_TYPE_DIRECTORY;
- entry->m_canonicalPath = canonicalPath;
- entry->m_uncompressedSizeInBytes = 0;
-
+ Entry entry;
+ entry.initDirectory(canonicalPath);
m_entries.Add(canonicalPath, entry);
return SLANG_OK;
}
@@ -384,16 +378,16 @@ SlangResult RiffFileSystem::loadArchive(const void* archive, size_t archiveSizeI
return SLANG_FAIL;
}
- RefPtr<Entry> dstEntry = new Entry;
+ Entry dstEntry;
const char* path = (const char*)srcData;
srcData += srcEntry->pathSize;
- dstEntry->m_canonicalPath = UnownedStringSlice(path, srcEntry->pathSize - 1);
- dstEntry->m_type = (SlangPathType)srcEntry->pathType;
- dstEntry->m_uncompressedSizeInBytes = srcEntry->uncompressedSize;
+ dstEntry.m_canonicalPath = UnownedStringSlice(path, srcEntry->pathSize - 1);
+ dstEntry.m_type = (SlangPathType)srcEntry->pathType;
+ dstEntry.m_uncompressedSizeInBytes = srcEntry->uncompressedSize;
- switch (dstEntry->m_type)
+ switch (dstEntry.m_type)
{
case SLANG_PATH_TYPE_FILE:
{
@@ -403,7 +397,7 @@ SlangResult RiffFileSystem::loadArchive(const void* archive, size_t archiveSizeI
}
// Get the compressed data
- dstEntry->m_contents = RawBlob::create(srcData, srcEntry->compressedSize);
+ dstEntry.m_contents = RawBlob::create(srcData, srcEntry->compressedSize);
break;
}
case SLANG_PATH_TYPE_DIRECTORY: break;
@@ -411,7 +405,7 @@ SlangResult RiffFileSystem::loadArchive(const void* archive, size_t archiveSizeI
}
// Add to the list of entries
- m_entries.Add(dstEntry->m_canonicalPath, dstEntry);
+ m_entries.Add(dstEntry.m_canonicalPath, dstEntry);
}
}
@@ -437,7 +431,7 @@ SlangResult RiffFileSystem::storeArchive(bool blobOwnsContent, ISlangBlob** outB
{
RiffContainer::ScopeChunk scopeData(&container, RiffContainer::Chunk::Kind::Data, RiffFileSystemBinary::kEntryFourCC);
- const Entry* srcEntry = pair.Value;
+ const Entry* srcEntry = &pair.Value;
RiffFileSystemBinary::Entry dstEntry;
dstEntry.uncompressedSize = 0;
diff --git a/source/core/slang-riff-file-system.h b/source/core/slang-riff-file-system.h
index 656b14325..6cd66c3d5 100644
--- a/source/core/slang-riff-file-system.h
+++ b/source/core/slang-riff-file-system.h
@@ -66,15 +66,45 @@ public:
virtual SLANG_NO_THROW SlangResult SLANG_MCALL storeArchive(bool blobOwnsContent, ISlangBlob** outBlob) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setCompressionStyle(const CompressionStyle& style) SLANG_OVERRIDE { m_compressionStyle = style; }
- RiffFileSystem(ICompressionSystem* compressionSystem);
+ /// Pass in nullptr, if no compression is wanted. In that scenario the contents will be stored in memory as is
+ explicit RiffFileSystem(ICompressionSystem* compressionSystem);
/// True if this appears to be Riff archive
static bool isArchive(const void* data, size_t sizeInBytes);
protected:
- struct Entry : RefObject
+ struct Entry
{
+ void reset()
+ {
+ m_type = SLANG_PATH_TYPE_FILE;
+ m_canonicalPath = String();
+ m_uncompressedSizeInBytes = 0;
+ m_contents.setNull();
+ }
+
+ void initDirectory(const String& canonicalPath)
+ {
+ m_type = SLANG_PATH_TYPE_DIRECTORY;
+ m_canonicalPath = canonicalPath;
+ m_uncompressedSizeInBytes = 0;
+ m_contents.setNull();
+ }
+ void initFile(const String& canonicalPath, size_t uncompressedSize, ISlangBlob* blob)
+ {
+ m_type = SLANG_PATH_TYPE_FILE;
+ m_canonicalPath = canonicalPath;
+ setContents(uncompressedSize, blob);
+ }
+ void setContents(size_t uncompressedSize, ISlangBlob* blob)
+ {
+ SLANG_ASSERT(m_type == SLANG_PATH_TYPE_FILE);
+ SLANG_ASSERT(blob);
+ m_uncompressedSizeInBytes = uncompressedSize;
+ m_contents = blob;
+ }
+
SlangPathType m_type;
String m_canonicalPath;
size_t m_uncompressedSizeInBytes; ///< Needed if m_contents is compressed.
@@ -88,10 +118,11 @@ protected:
Entry* _getEntryFromPath(const char* path, String* outPath = nullptr);
Entry* _getEntryFromCanonicalPath(const String& canonicalPath);
- void _clear() { m_entries.Clear(); }
+ /// Clear, ensures any backing memory is also freed
+ void _clear() { m_entries = Dictionary<String, Entry>(); }
// Maps a path to an entry
- Dictionary<String, RefPtr<Entry>> m_entries;
+ Dictionary<String, Entry> m_entries;
ComPtr<ICompressionSystem> m_compressionSystem;
diff --git a/tools/slang-unit-test/unit-test-file-system.cpp b/tools/slang-unit-test/unit-test-file-system.cpp
new file mode 100644
index 000000000..f1fd2c6bc
--- /dev/null
+++ b/tools/slang-unit-test/unit-test-file-system.cpp
@@ -0,0 +1,197 @@
+// unit-test-file-system.cpp
+
+#include "../../source/core/slang-file-system.h"
+#include "../../source/core/slang-riff-file-system.h"
+#include "../../source/core/slang-zip-file-system.h"
+
+#include "../../source/core/slang-deflate-compression-system.h"
+#include "../../source/core/slang-lz4-compression-system.h"
+
+#include "tools/unit-test/slang-unit-test.h"
+
+using namespace Slang;
+
+namespace {
+
+enum class FileSystemType
+{
+ Zip,
+ RiffUncompressed,
+ RiffDeflate,
+ RiffLZ4,
+ Relative,
+ CountOf,
+};
+
+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 !(*this == rhs); }
+
+ SlangPathType type;
+ String name;
+};
+
+} //
+
+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()));
+
+ SlangPathType pathType;
+ SLANG_RETURN_ON_FAIL(fileSystem->getPathType(path, &pathType));
+
+ if (pathType != SLANG_PATH_TYPE_FILE)
+ {
+ return SLANG_FAIL;
+ }
+
+ ComPtr<ISlangBlob> blob;
+ SLANG_RETURN_ON_FAIL(fileSystem->loadFile(path, blob.writeRef()));
+
+ if (blob->getBufferSize() != contentsSlice.getLength())
+ {
+ return SLANG_FAIL;
+ }
+ if (contentsSlice != UnownedStringSlice((const char*)blob->getBufferPointer(), blob->getBufferSize()))
+ {
+ return SLANG_FAIL;
+ }
+
+ return SLANG_OK;
+}
+
+static SlangResult _createAndCheckDirectory(ISlangMutableFileSystem* fileSystem, const char* path)
+{
+ SLANG_RETURN_ON_FAIL(fileSystem->createDirectory(path));
+
+ SlangPathType pathType;
+ SLANG_RETURN_ON_FAIL(fileSystem->getPathType(path, &pathType));
+
+ if (pathType != SLANG_PATH_TYPE_DIRECTORY)
+ {
+ return SLANG_FAIL;
+ }
+
+ return SLANG_OK;
+}
+
+static void _entryCallback(SlangPathType pathType, const char* name, void* userData)
+{
+ List<Entry>& out = *(List<Entry>*)userData;
+ out.add(Entry{pathType, name});
+}
+
+static SlangResult _enumeratePath(ISlangMutableFileSystem* fileSystem, const char* path, const ConstArrayView<Entry>& entries)
+{
+ List<Entry> contents;
+
+ SLANG_RETURN_ON_FAIL(fileSystem->enumeratePathContents(path, _entryCallback, (void*)&contents));
+
+ contents.sort();
+
+ if (contents.getArrayView() != entries)
+ {
+ return SLANG_FAIL;
+ }
+
+ return SLANG_OK;
+}
+
+static SlangResult _test(FileSystemType type)
+{
+ ComPtr<ISlangMutableFileSystem> fileSystem;
+
+ switch (type)
+ {
+ case FileSystemType::Zip:
+ {
+ SLANG_RETURN_ON_FAIL(ZipFileSystem::create(fileSystem));
+ break;
+ }
+ case FileSystemType::RiffUncompressed:
+ {
+ fileSystem = new RiffFileSystem(nullptr);
+ break;
+ }
+ case FileSystemType::RiffDeflate:
+ {
+ fileSystem = new RiffFileSystem(DeflateCompressionSystem::getSingleton());
+ break;
+ }
+ case FileSystemType::RiffLZ4:
+ {
+ fileSystem = new RiffFileSystem(LZ4CompressionSystem::getSingleton());
+ break;
+ }
+ case FileSystemType::Relative:
+ {
+ ComPtr<ISlangMutableFileSystem> memoryFileSystem(new RiffFileSystem(nullptr));
+ memoryFileSystem->createDirectory("base");
+
+ fileSystem = new RelativeFileSystem(memoryFileSystem, "base");
+ break;
+ }
+ }
+
+
+ SLANG_RETURN_ON_FAIL(_createAndCheckFile(fileSystem, "a", "someText"));
+ SLANG_RETURN_ON_FAIL(_createAndCheckFile(fileSystem, "b", "A longer bit of text...."));
+
+ SLANG_RETURN_ON_FAIL(_createAndCheckDirectory(fileSystem, "d"));
+ SLANG_RETURN_ON_FAIL(_createAndCheckFile(fileSystem, "d/a", "Some more silly stuff"));
+ SLANG_RETURN_ON_FAIL(_createAndCheckFile(fileSystem, "d\\b", "Lets go empty"));
+
+ // Lets find all the files in the directory
+
+ {
+ const Entry entries[] = { {SLANG_PATH_TYPE_FILE, "a" }, {SLANG_PATH_TYPE_FILE, "b" } };
+ SLANG_RETURN_ON_FAIL(_enumeratePath(fileSystem, "d", makeConstArrayView(entries)));
+ }
+
+ {
+ const Entry entries[] = { {SLANG_PATH_TYPE_FILE, "a" }, {SLANG_PATH_TYPE_FILE, "b" }, {SLANG_PATH_TYPE_DIRECTORY, "d" } };
+ SLANG_RETURN_ON_FAIL(_enumeratePath(fileSystem, ".", makeConstArrayView(entries)));
+ }
+
+ SLANG_RETURN_ON_FAIL(fileSystem->remove("d/a"));
+ {
+ const Entry entries[] = { {SLANG_PATH_TYPE_FILE, "b" } };
+ SLANG_RETURN_ON_FAIL(_enumeratePath(fileSystem, "d", makeConstArrayView(entries)));
+ }
+ SLANG_RETURN_ON_FAIL(fileSystem->remove("d\\b"));
+ {
+ SLANG_RETURN_ON_FAIL(_enumeratePath(fileSystem, "d", makeConstArrayView((const Entry*)nullptr, 0)));
+ }
+
+ // If it's removed it can't be removed again
+ SLANG_CHECK(SLANG_FAILED(fileSystem->remove("d\\b")));
+
+ // Remove the directory
+ SLANG_RETURN_ON_FAIL(fileSystem->remove("d"));
+
+ {
+ const Entry entries[] = { {SLANG_PATH_TYPE_FILE, "a" }, {SLANG_PATH_TYPE_FILE, "b" } };
+ SLANG_RETURN_ON_FAIL(_enumeratePath(fileSystem, ".", makeConstArrayView(entries)));
+ }
+
+ return SLANG_OK;
+}
+
+SLANG_UNIT_TEST(fileSystem)
+{
+ for (Index i = 0; i < Count(FileSystemType::CountOf); ++i)
+ {
+ const auto type = FileSystemType(i);
+
+ auto const res = _test(type);
+
+ SLANG_CHECK(SLANG_SUCCEEDED(res));
+ }
+}
+