summaryrefslogtreecommitdiffstats
path: root/source/core
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2022-09-29 14:12:15 -0400
committerGitHub <noreply@github.com>2022-09-29 14:12:15 -0400
commit9296405a2e15c07b5a8b7a002a2fa082232d559b (patch)
tree9dd7bff82645e04f1dc53fbff8f5e366bda49cb9 /source/core
parent8e0750fb193a8d2b9e8c3a0d81e367d6a9bdeb30 (diff)
Split out MemoryFileSystem (#2422)
* #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.
Diffstat (limited to 'source/core')
-rw-r--r--source/core/slang-archive-file-system.cpp2
-rw-r--r--source/core/slang-archive-file-system.h8
-rw-r--r--source/core/slang-memory-file-system.cpp292
-rw-r--r--source/core/slang-memory-file-system.h130
-rw-r--r--source/core/slang-riff-file-system.cpp243
-rw-r--r--source/core/slang-riff-file-system.h87
6 files changed, 457 insertions, 305 deletions
diff --git a/source/core/slang-archive-file-system.cpp b/source/core/slang-archive-file-system.cpp
index 98c995d23..88093239b 100644
--- a/source/core/slang-archive-file-system.cpp
+++ b/source/core/slang-archive-file-system.cpp
@@ -6,8 +6,6 @@
#include "slang-io.h"
#include "slang-string-util.h"
#include "slang-blob.h"
-#include "slang-string-slice-pool.h"
-#include "slang-uint-set.h"
#include "slang-riff-file-system.h"
diff --git a/source/core/slang-archive-file-system.h b/source/core/slang-archive-file-system.h
index 03571bdc1..678791245 100644
--- a/source/core/slang-archive-file-system.h
+++ b/source/core/slang-archive-file-system.h
@@ -26,12 +26,6 @@ class IArchiveFileSystem : public ISlangCastable
SLANG_NO_THROW virtual void SLANG_MCALL setCompressionStyle(const CompressionStyle& style) = 0;
};
-class ArchiveFileSystem : public ISlangMutableFileSystem, public ComBaseObject
-{
-public:
-
-};
-
/* Maps an UnownedStringSlice to an index. All substrings are held internally in a StringSlicePool, and so
owned by the type. */
class StringSliceIndexMap
@@ -96,7 +90,6 @@ KeyValuePair<UnownedStringSlice, Index> StringSliceIndexMap::getAt(CountIndex co
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
@@ -152,7 +145,6 @@ public:
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-memory-file-system.cpp b/source/core/slang-memory-file-system.cpp
new file mode 100644
index 000000000..9704542f7
--- /dev/null
+++ b/source/core/slang-memory-file-system.cpp
@@ -0,0 +1,292 @@
+#include "slang-memory-file-system.h"
+
+// For Path::
+#include "slang-io.h"
+#include "slang-blob.h"
+
+// For ImplicitDirectoryCollector
+#include "slang-archive-file-system.h"
+
+namespace Slang
+{
+
+void* MemoryFileSystem::getInterface(const Guid& guid)
+{
+ if ( guid == ISlangUnknown::getTypeGuid() ||
+ guid == ISlangCastable::getTypeGuid() ||
+ guid == ISlangFileSystem::getTypeGuid() ||
+ guid == ISlangFileSystemExt::getTypeGuid() ||
+ guid == ISlangMutableFileSystem::getTypeGuid())
+ {
+ return static_cast<ISlangMutableFileSystem*>(this);
+ }
+ return nullptr;
+}
+
+void* MemoryFileSystem::getObject(const Guid& guid)
+{
+ SLANG_UNUSED(guid);
+ return nullptr;
+}
+
+void* MemoryFileSystem::castAs(const Guid& guid)
+{
+ if (auto ptr = getInterface(guid))
+ {
+ return ptr;
+ }
+ 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;
+}
+
+MemoryFileSystem::Entry* MemoryFileSystem::_getEntryFromCanonicalPath(const String& canonicalPath)
+{
+ return m_entries.TryGetValue(canonicalPath);
+}
+
+MemoryFileSystem::Entry* MemoryFileSystem::_getEntryFromPath(const char* path, String* outPath)
+{
+ StringBuilder buffer;
+ if (SLANG_FAILED(_calcCanonicalPath(path, buffer)))
+ {
+ return nullptr;
+ }
+
+ if (outPath)
+ {
+ *outPath = buffer;
+ }
+ return _getEntryFromCanonicalPath(buffer);
+}
+
+SlangResult MemoryFileSystem::_loadFile(const char* path, Entry** outEntry)
+{
+ *outEntry = nullptr;
+ Entry* entry = _getEntryFromPath(path);
+ if (entry == nullptr || entry->m_type != SLANG_PATH_TYPE_FILE)
+ {
+ return SLANG_E_NOT_FOUND;
+ }
+ *outEntry = entry;
+ return SLANG_OK;
+}
+
+SlangResult MemoryFileSystem::loadFile(char const* path, ISlangBlob** outBlob)
+{
+ Entry* entry;
+ SLANG_RETURN_ON_FAIL(_loadFile(path, &entry));
+
+ ISlangBlob* contents = entry->m_contents;
+ contents->addRef();
+ *outBlob = contents;
+
+ return SLANG_OK;
+}
+
+SlangResult MemoryFileSystem::getFileUniqueIdentity(const char* path, ISlangBlob** outUniqueIdentity)
+{
+ return getCanonicalPath(path, outUniqueIdentity);
+}
+
+SlangResult MemoryFileSystem::calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut)
+{
+ String combinedPath;
+ switch (fromPathType)
+ {
+ case SLANG_PATH_TYPE_FILE:
+ {
+ combinedPath = Path::combine(Path::getParentDirectory(fromPath), path);
+ break;
+ }
+ case SLANG_PATH_TYPE_DIRECTORY:
+ {
+ combinedPath = Path::combine(fromPath, path);
+ break;
+ }
+ }
+
+ *pathOut = StringBlob::moveCreate(combinedPath).detach();
+ return SLANG_OK;
+}
+
+SlangResult MemoryFileSystem::getPathType(const char* path, SlangPathType* outPathType)
+{
+ String canonicalPath;
+ Entry* entry = _getEntryFromPath(path, &canonicalPath);
+ if (entry == nullptr)
+ {
+ // 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;
+ }
+
+ // 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;
+}
+
+SlangResult MemoryFileSystem::getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath)
+{
+ StringBuilder buffer;
+ SLANG_RETURN_ON_FAIL(_calcCanonicalPath(path, buffer));
+ *outCanonicalPath = StringBlob::moveCreate(buffer).detach();
+ return SLANG_OK;
+}
+
+SlangResult MemoryFileSystem::enumeratePathContents(const char* path, FileSystemContentsCallBack callback, void* userData)
+{
+ String canonicalPath;
+ Entry* entry = _getEntryFromPath(path, &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)
+ {
+ const Entry* childEntry = &pair.Value;
+ collector.addPath(childEntry->m_type, childEntry->m_canonicalPath.getUnownedSlice());
+ }
+
+ return collector.enumerate(callback, userData);
+}
+
+SlangResult MemoryFileSystem::saveFile(const char* path, const void* data, size_t size)
+{
+ Entry* entry;
+ SLANG_RETURN_ON_FAIL(_requireFile(path, &entry));
+ auto contents = RawBlob::create(data, size);
+ entry->setContents(size, contents);
+ return SLANG_OK;
+}
+
+SlangResult MemoryFileSystem::_requireFile(const char* path, Entry** outEntry)
+{
+ *outEntry = nullptr;
+
+ StringBuilder canonicalPath;
+ SLANG_RETURN_ON_FAIL(_calcCanonicalPath(path, canonicalPath));
+
+ Entry* foundEntry = _getEntryFromCanonicalPath(canonicalPath);
+
+ if (foundEntry)
+ {
+ 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;
+ }
+ }
+ else
+ {
+ Entry entry;
+ entry.initFile(canonicalPath);
+ m_entries.Add(canonicalPath, entry);
+
+ foundEntry = _getEntryFromCanonicalPath(canonicalPath);
+ }
+
+ // It must be found and be a file
+ SLANG_ASSERT(foundEntry && foundEntry->m_type == SLANG_PATH_TYPE_FILE && foundEntry->m_canonicalPath == canonicalPath);
+
+ *outEntry = foundEntry;
+ return SLANG_OK;
+}
+
+SlangResult MemoryFileSystem::remove(const char* path)
+{
+ String canonicalPath;
+ Entry* entry = _getEntryFromPath(path, &canonicalPath);
+
+ if (entry)
+ {
+ if (entry->m_type == SLANG_PATH_TYPE_DIRECTORY)
+ {
+ ImplicitDirectoryCollector collector(canonicalPath);
+
+ // If it is a directory, we need to see if there is anything in it
+ for (const auto& pair : m_entries)
+ {
+ 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;
+ }
+
+ return SLANG_E_NOT_FOUND;
+}
+
+SlangResult MemoryFileSystem::createDirectory(const char* path)
+{
+ String canonicalPath;
+ if (_getEntryFromPath(path, &canonicalPath))
+ {
+ return SLANG_FAIL;
+ }
+
+ Entry entry;
+ entry.initDirectory(canonicalPath);
+ m_entries.Add(canonicalPath, entry);
+ return SLANG_OK;
+}
+
+} // namespace Slang
diff --git a/source/core/slang-memory-file-system.h b/source/core/slang-memory-file-system.h
new file mode 100644
index 000000000..72bacf2bd
--- /dev/null
+++ b/source/core/slang-memory-file-system.h
@@ -0,0 +1,130 @@
+#ifndef SLANG_CORE_MEMORY_FILE_SYSTEM_H
+#define SLANG_CORE_MEMORY_FILE_SYSTEM_H
+
+#include "slang-basic.h"
+
+#include "../../slang-com-ptr.h"
+#include "slang-com-object.h"
+
+namespace Slang
+{
+
+/* MemoryFileSystem is an implementation of ISlangMutableFileSystem that stores file contents in 'blobs' (typically) in memory.
+
+A derived class can change how the contents of the contents blob is interpretted (so for example the RiffFileSystem is implemented
+such that the Entry.m_contents is the files contents compressed).
+
+The implementation uses a map to store the file/directory based on their canonical path. This makes access relatively fast and simple -
+an access only requires a path being converted into a canonical path, and then a lookup. Whilst this makes typical access fast, it means
+doing an enumeration of a directory slower as it requires traversing all entries to find which are in the path.
+
+This is in contrast with an implementation that held items in directories 'objects'. In that scenario the path through the hierarchy
+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.
+*/
+class MemoryFileSystem : public ISlangMutableFileSystem, public ComBaseObject
+{
+public:
+
+ // ISlangUnknown
+ SLANG_COM_BASE_IUNKNOWN_ALL
+
+ // ISlangCastable
+ virtual SLANG_NO_THROW void* SLANG_MCALL castAs(const Guid& guid) SLANG_OVERRIDE;
+
+ // ISlangFileSystem
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadFile(char const* path, ISlangBlob** outBlob) SLANG_OVERRIDE;
+
+ // ISlangFileSystemExt
+ 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 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 remove(const char* path) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL createDirectory(const char* path) SLANG_OVERRIDE;
+
+protected:
+
+ 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 initFile(const String& canonicalPath)
+ {
+ m_type = SLANG_PATH_TYPE_FILE;
+ m_canonicalPath = canonicalPath;
+ m_contents.setNull();
+ m_uncompressedSizeInBytes = 0;
+ }
+ 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;
+
+ /// The size as seen on the file system. Might be different from the size of m_contents
+ /// if it's actually being stored in some other representation (such as compressed)
+ size_t m_uncompressedSizeInBytes;
+ ComPtr<ISlangBlob> m_contents; ///< Can be compressed or not
+ };
+
+ 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.
+ /// If created the entry is empty.
+ SlangResult _requireFile(const char* path, Entry** outEntry);
+ /// Given the path returns the entry if it's a file, or returns an error
+ SlangResult _loadFile(const char* path, Entry** outEntry);
+
+ /// Clear, ensures any backing memory is also freed
+ void _clear() { m_entries = Dictionary<String, Entry>(); }
+
+ // Maps canonical paths to an entries (which could be files or directories)
+ Dictionary<String, Entry> m_entries;
+};
+
+}
+
+#endif
diff --git a/source/core/slang-riff-file-system.cpp b/source/core/slang-riff-file-system.cpp
index 9402a2b4e..da73cb2a5 100644
--- a/source/core/slang-riff-file-system.cpp
+++ b/source/core/slang-riff-file-system.cpp
@@ -3,11 +3,7 @@
#include "../../slang-com-helper.h"
#include "../../slang-com-ptr.h"
-#include "slang-io.h"
-#include "slang-string-util.h"
#include "slang-blob.h"
-#include "slang-string-slice-pool.h"
-#include "slang-uint-set.h"
// Compression systems
#include "slang-deflate-compression-system.h"
@@ -23,13 +19,9 @@ RiffFileSystem::RiffFileSystem(ICompressionSystem* compressionSystem):
void* RiffFileSystem::getInterface(const Guid& guid)
{
- if ( guid == ISlangUnknown::getTypeGuid() ||
- guid == ISlangCastable::getTypeGuid() ||
- guid == ISlangFileSystem::getTypeGuid() ||
- guid == ISlangFileSystemExt::getTypeGuid() ||
- guid == ISlangMutableFileSystem::getTypeGuid())
+ if (auto ptr = Super::getInterface(guid))
{
- return static_cast<ISlangMutableFileSystem*>(this);
+ return ptr;
}
else if (guid == IArchiveFileSystem::getTypeGuid())
{
@@ -53,183 +45,40 @@ void* RiffFileSystem::castAs(const Guid& guid)
return getObject(guid);
}
-SlangResult RiffFileSystem::_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;
-}
-
-RiffFileSystem::Entry* RiffFileSystem::_getEntryFromCanonicalPath(const String& canonicalPath)
-{
- return m_entries.TryGetValue(canonicalPath);
-}
-
-RiffFileSystem::Entry* RiffFileSystem::_getEntryFromPath(const char* path, String* outPath)
-{
- StringBuilder buffer;
- if (SLANG_FAILED(_calcCanonicalPath(path, buffer)))
- {
- return nullptr;
- }
-
- if (outPath)
- {
- *outPath = buffer;
- }
- return _getEntryFromCanonicalPath(buffer);
-}
-
SlangResult RiffFileSystem::loadFile(char const* path, ISlangBlob** outBlob)
{
- Entry* entry = _getEntryFromPath(path);
- if (entry == nullptr || entry->m_type != SLANG_PATH_TYPE_FILE)
- {
- return SLANG_E_NOT_FOUND;
- }
+ Entry* entry;
+ SLANG_RETURN_ON_FAIL(_loadFile(path, &entry));
+
+ ISlangBlob* contents = entry->m_contents;
if (m_compressionSystem)
{
// Okay lets decompress into a blob
ScopedAllocation alloc;
void* dst = alloc.allocateTerminated(entry->m_uncompressedSizeInBytes);
-
- ISlangBlob* compressedData = entry->m_contents;
- SLANG_RETURN_ON_FAIL(m_compressionSystem->decompress(compressedData->getBufferPointer(), compressedData->getBufferSize(), entry->m_uncompressedSizeInBytes, dst));
+ SLANG_RETURN_ON_FAIL(m_compressionSystem->decompress(contents->getBufferPointer(), contents->getBufferSize(), entry->m_uncompressedSizeInBytes, dst));
auto blob = RawBlob::moveCreate(alloc);
*outBlob = blob.detach();
+ return SLANG_OK;
}
else
{
- // We don't have any compression, so can just return the blob
- ISlangBlob* contents = entry->m_contents;
+ // Just return as is
contents->addRef();
*outBlob = contents;
+ return SLANG_OK;
}
-
- return SLANG_OK;
-}
-
-SlangResult RiffFileSystem::getFileUniqueIdentity(const char* path, ISlangBlob** outUniqueIdentity)
-{
- return getCanonicalPath(path, outUniqueIdentity);
-}
-
-SlangResult RiffFileSystem::calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut)
-{
- String combinedPath;
- switch (fromPathType)
- {
- case SLANG_PATH_TYPE_FILE:
- {
- combinedPath = Path::combine(Path::getParentDirectory(fromPath), path);
- break;
- }
- case SLANG_PATH_TYPE_DIRECTORY:
- {
- combinedPath = Path::combine(fromPath, path);
- break;
- }
- }
-
- *pathOut = StringUtil::createStringBlob(combinedPath).detach();
- return SLANG_OK;
-}
-
-SlangResult RiffFileSystem::getPathType(const char* path, SlangPathType* outPathType)
-{
- String canonicalPath;
- Entry* entry = _getEntryFromPath(path, &canonicalPath);
- if (entry == nullptr)
- {
- // 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;
- }
-
- // Explicit type
- *outPathType = entry->m_type;
- return SLANG_OK;
-}
-
-SlangResult RiffFileSystem::getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath)
-{
- String simplifiedPath = Path::simplify(path);
- *outSimplifiedPath = StringUtil::createStringBlob(simplifiedPath).detach();
- return SLANG_OK;
-}
-
-SlangResult RiffFileSystem::getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath)
-{
- StringBuilder buffer;
- SLANG_RETURN_ON_FAIL(_calcCanonicalPath(path, buffer));
- *outCanonicalPath = StringUtil::createStringBlob(buffer).detach();
- return SLANG_OK;
-}
-
-SlangResult RiffFileSystem::enumeratePathContents(const char* path, FileSystemContentsCallBack callback, void* userData)
-{
- String canonicalPath;
- Entry* entry = _getEntryFromPath(path, &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)
- {
- const Entry* childEntry = &pair.Value;
- collector.addPath(childEntry->m_type, childEntry->m_canonicalPath.getUnownedSlice());
- }
-
- return collector.enumerate(callback, userData);
}
SlangResult RiffFileSystem::saveFile(const char* path, const void* data, size_t size)
-{
- StringBuilder canonicalPath;
- SLANG_RETURN_ON_FAIL(_calcCanonicalPath(path, canonicalPath));
+{
+ Entry* entry;
+ SLANG_RETURN_ON_FAIL(_requireFile(path, &entry));
ComPtr<ISlangBlob> contents;
-
if (m_compressionSystem)
{
// Lets try compressing the input
@@ -240,71 +89,7 @@ SlangResult RiffFileSystem::saveFile(const char* path, const void* data, size_t
// Just store the data directly.
contents = RawBlob::create(data, size);
}
-
- if (Entry* foundEntry = _getEntryFromCanonicalPath(canonicalPath))
- {
- 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);
- }
-
- return SLANG_OK;
-}
-
-SlangResult RiffFileSystem::remove(const char* path)
-{
- String canonicalPath;
- Entry* entry = _getEntryFromPath(path, &canonicalPath);
-
- if (entry)
- {
- if (entry->m_type == SLANG_PATH_TYPE_DIRECTORY)
- {
- ImplicitDirectoryCollector collector(canonicalPath);
-
- // If it is a directory, we need to see if there is anything in it
- for (const auto& pair : m_entries)
- {
- 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;
- }
-
- return SLANG_E_NOT_FOUND;
-}
-
-SlangResult RiffFileSystem::createDirectory(const char* path)
-{
- String canonicalPath;
- if (_getEntryFromPath(path, &canonicalPath))
- {
- return SLANG_FAIL;
- }
-
- Entry entry;
- entry.initDirectory(canonicalPath);
- m_entries.Add(canonicalPath, entry);
+ entry->setContents(size, contents);
return SLANG_OK;
}
diff --git a/source/core/slang-riff-file-system.h b/source/core/slang-riff-file-system.h
index 6cd66c3d5..eb9f24c73 100644
--- a/source/core/slang-riff-file-system.h
+++ b/source/core/slang-riff-file-system.h
@@ -2,9 +2,9 @@
#define SLANG_RIFF_FILE_SYSTEM_H
#include "slang-archive-file-system.h"
+#include "slang-memory-file-system.h"
#include "slang-riff.h"
-#include "slang-io.h"
namespace Slang
{
@@ -28,14 +28,29 @@ struct RiffFileSystemBinary
uint32_t pathSize; ///< The size of the path in bytes, including terminating 0
uint32_t pathType; ///< One of SlangPathType
- // Followed by the path (including terminating0)
+ // Followed by the path (including terminating 0)
// Followed by the compressed data
};
};
-class RiffFileSystem : public ISlangMutableFileSystem, public IArchiveFileSystem, public ComBaseObject
+/* RiffFileSystem implements ISlangMutableFileSystem and can be used to save and load the whole of it's contents as an 'archive' blob.
+
+The 'RIFF' part provides the structure to store out the contents. The data is only accessed in the RIFF format when being
+read/written to an archive. Normal operations on the file system act in memory.
+
+A RiffFileSystem allows for compression to be used on files. To use compression pass in a suitable ICompressionSystem
+implementation on construction. If constructed without an ICompressionSystem, data is stored uncompressed. When compression is
+used, files 'contents' blob is actually the *compressed* version of the contents. Calling loadFile/saveFile will
+uncompress/compress as need. If there is no compression contents is identical to the file contents.
+
+NOTE:
+* The RIFF chunk IDs are *slang specific*. It conforms to RIFF but is unlikely to be usable with other tooling.
+* The RIFF chunk IDs are in RiffFileSystemBinary struct
+*/
+class RiffFileSystem : public MemoryFileSystem, public IArchiveFileSystem
{
public:
+ typedef MemoryFileSystem Super;
// ISlangUnknown
SLANG_COM_BASE_IUNKNOWN_ALL
@@ -46,84 +61,24 @@ public:
// ISlangFileSystem
virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadFile(char const* path, ISlangBlob** outBlob) SLANG_OVERRIDE;
- // ISlangFileSystemExt
- 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 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 remove(const char* path) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL createDirectory(const char* path) SLANG_OVERRIDE;
-
- // ArchiveFileSystem
+
+ // 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;
virtual SLANG_NO_THROW void SLANG_MCALL setCompressionStyle(const CompressionStyle& style) SLANG_OVERRIDE { m_compressionStyle = style; }
- /// Pass in nullptr, if no compression is wanted. In that scenario the contents will be stored in memory as is
+ /// Pass in nullptr, if no compression is wanted.
explicit RiffFileSystem(ICompressionSystem* compressionSystem);
/// True if this appears to be Riff archive
static bool isArchive(const void* data, size_t sizeInBytes);
protected:
-
- 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.
- ComPtr<ISlangBlob> m_contents; ///< Can be compressed or not
- };
-
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);
-
- /// Clear, ensures any backing memory is also freed
- void _clear() { m_entries = Dictionary<String, Entry>(); }
-
- // Maps a path to an entry
- Dictionary<String, Entry> m_entries;
-
ComPtr<ICompressionSystem> m_compressionSystem;
CompressionStyle m_compressionStyle;