MemoryFileSystem
MemoryFileSystem is a memory based filesystem.
IFileSystem fileSystem = new MemoryFileSystem();
Files are based on blocks. Maximum number of blocks is 2^31-1. The blockSize can be set in constructor. The default blocksize is 1024.
IFileSystem fileSystem = new MemoryFileSystem(blockSize: 4096);
Files can be browsed.
IFileSystem fileSystem = new MemoryFileSystem();
foreach (var entry in fileSystem.Browse(""))
Console.WriteLine(entry.Path);
.OpenStream(path, fileMode, fileAccess, fileShare) opens as stream.
IFileSystem fileSystem = new MemoryFileSystem();
using Stream s = fileSystem.OpenStream("file.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
s.WriteByte(10);
.OpenMemory(path, fileMode, fileAccess, fileShare, concurrentUsable) opens as memory.
IFileSystem fileSystem = new MemoryFileSystem();
using IMemory<byte> m = fileSystem.OpenMemory("file.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, concurrentUsable: true);
m.Add((byte)10);
FileShare exclusion applies whether file is opened as stream or memory.
try
{
// Create ram
IFileSystem fileSystem = new MemoryFileSystem();
// Open-or-create file with share exclusion
using Stream s = fileSystem.OpenStream("file.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
// Open-or-create file with share exclusion (Throws IOException)
using IMemory<byte> memory = fileSystem.OpenMemory("file.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
}
catch (FileSystemException e)
{
// Capture "no access"
Console.WriteLine(e.Message);
}
Files and directories can be observed for changes.
IObserver<Avalanche.FileSystem.IEvent> observer = new Observer();
using IDisposable handle = fileSystem.Observe("**", observer);
Directories can be created.
fileSystem.CreateDirectory("dir/");
Directories can be created recursively.
fileSystem.CreateDirectory("dir1/dir2/dir3/");
fileSystem.PrintTreeTo(Console.Out);
The root is "".
"" └──"dir1" └──"dir2" └──"dir3"
MemoryFileSystem can create empty directory names. For example, a slash '/' at the start of a path refers to an empty directory right under the root.
fileSystem.CreateDirectory("/tmp/dir/");
"" └──"" └──"tmp" └──"dir"
Path "file://" refers to three directories; the root, "file:" and a empty-named directory between two slashes "//".
fileSystem.CreateDirectory("file://");
"" └──"file:" └──""
Directories can be deleted.
fileSystem.Delete("dir/", recurse: true);
Files and directories can be renamed and moved.
fileSystem.CreateDirectory("dir/");
fileSystem.Move("dir/", "new-name/");
Disposing
Disposable objects can be attached to be disposed along with FileSystem.
// Init
object obj = new ReaderWriterLockSlim();
IFileSystemDisposable fileSystem = new FileSystem("").AttachDisposable(obj);
// ... do work ...
// Dispose both
fileSystem.Dispose();
Delegates can be attached to be executed at dispose of FileSystem.
IFileSystemDisposable fileSystem = new FileSystem("")
.AddDisposeAction(f => Console.WriteLine("Disposed"));
.BelateDispose() creates a handle that postpones dispose on .Dispose(). Actual dispose proceeds once .Dispose() is called and all belate handles are disposed. This can be used for passing the IFileSystem to worker threads.
MemoryFileSystem fileSystem = new MemoryFileSystem();
fileSystem.CreateDirectory("/tmp/dir/");
// Postpone dispose
fileSystem.TryBelateDispose(out IDisposable? belateDisposeHandle);
// Start concurrent work
Task.Run(() =>
{
// Do work
Thread.Sleep(1000);
fileSystem.GetEntry("");
// Release belate handle. Disposes here or below, depending which thread runs last.
belateDisposeHandle?.Dispose();
});
// Start dispose, but postpone it until belatehandle is disposed in another thread.
fileSystem.Dispose();
Size Limit
Constructor new MemoryFileSystem(blockSize, maxSpace) creates size limited filesystem. Memory limitation applies to files only, not to directory structure.
IFileSystem fileSystem = new MemoryFileSystem(blockSize: 1024, maxSpace: 1L << 34);
Printing with PrintTree.Format.DriveFreespace | PrintTree.Format.DriveSize flags show drive size.
IFileSystem fileSystem = new MemoryFileSystem(blockSize: 1024, maxSpace: 1L << 34);
fileSystem.CreateFile("file", new byte[1 << 30]);
fileSystem.PrintTreeTo(Console.Out, format: FileSystemPrintTreeExtensions.Format.AllWithName);
"" [Freespace: 15G, Size: 1G/16G, Ram]
└── "file" [1073741824]
If filesystem runs out of space, it throws FileSystemExceptionOutOfDiskSpace.
IFileSystem fileSystem = new MemoryFileSystem(blockSize: 1024, maxSpace: 2048);
fileSystem.CreateFile("file1", new byte[1024]);
fileSystem.CreateFile("file2", new byte[1024]);
// throws FileSystemExceptionOutOfDiskSpace
fileSystem.CreateFile("file3", new byte[1024]);
Available space can be shared between MemoryFileSystem instances with IBlockPool.
IBlockPool<byte> pool = new BlockPool<byte>(blockSize: 1024, maxBlockCount: 3, maxRecycleQueue: 3);
IFileSystem ms1 = new MemoryFileSystem(pool);
IFileSystem ms2 = new MemoryFileSystem(pool);
// Reserve 2048 from shared pool
ms1.CreateFile("file1", new byte[2048]);
// Not enough for another 3072, throws FileSystemExceptionOutOfDiskSpace
ms2.CreateFile("file2", new byte[2048]);
Deleted file is returned back to pool once all open streams are closed.
IBlockPool<byte> pool = new BlockPool<byte>(blockSize: 1024, maxBlockCount: 3, maxRecycleQueue: 3);
IFileSystem fileSystem = new MemoryFileSystem(pool);
Stream s = fileSystem.OpenStream("file", FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
s.Write(new byte[3072], 0, 3072);
fileSystem.Delete("file");
Console.WriteLine(((IBlockPoolStatistics)pool).BytesAvailable); // Prints 0
s.Dispose();
Console.WriteLine(((IBlockPoolStatistics)pool).BytesAvailable); // Prints 3072