Make the value stored by HierarchicalRomFileTable generic (#76)

* Make the value stored by HierarchicalRomFileTable generic

* Don't use new language features yet
This commit is contained in:
Alex Barney 2019-08-08 17:46:49 -05:00 committed by GitHub
parent ea2572c479
commit 7099b3cdf2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 45 additions and 35 deletions

View file

@ -6,11 +6,13 @@ namespace LibHac.Fs.RomFs
/// <summary>
/// Represents the file table used by the RomFS format.
/// </summary>
/// <typeparam name="T">The type of the value to be stored for each file entry.</typeparam>
/// <remarks>
/// This file table stores the structure of the file tree in a RomFS.
/// Each file or directory entry is stored in the table using its full path as a key.
/// Once added, a file or directory is assigned an ID that can also be used to retrieve it.
/// Each file entry contains the size of the file and its offset in the RomFS.
/// Each file entry contains a structure of type <typeparamref name="T"/>.
/// In a standard RomFS, this includes the size of the file and its offset in the RomFS.
/// Each directory entry contains the IDs for its first child file and first child directory.
///
/// The table is represented by four byte arrays. Two of the arrays contain the hash buckets and
@ -19,13 +21,13 @@ namespace LibHac.Fs.RomFs
/// Once all files have been added to the table, <see cref="TrimExcess"/> should be called
/// to optimize the size of the table.
/// </remarks>
public class HierarchicalRomFileTable
public class HierarchicalRomFileTable<T> where T : unmanaged
{
private RomFsDictionary<FileRomEntry> FileTable { get; }
private RomFsDictionary<DirectoryRomEntry> DirectoryTable { get; }
/// <summary>
/// Initializes a <see cref="HierarchicalRomFileTable"/> from an existing table.
/// Initializes a <see cref="HierarchicalRomFileTable{T}"/> from an existing table.
/// </summary>
/// <param name="dirHashTable"></param>
/// <param name="dirEntryTable"></param>
@ -39,17 +41,17 @@ namespace LibHac.Fs.RomFs
}
/// <summary>
/// Initializes a new <see cref="HierarchicalRomFileTable"/> that has the default initial capacity.
/// Initializes a new <see cref="HierarchicalRomFileTable{T}"/> that has the default initial capacity.
/// </summary>
public HierarchicalRomFileTable() : this(0, 0) { }
/// <summary>
/// Initializes a new <see cref="HierarchicalRomFileTable"/> that has the specified initial capacity.
/// Initializes a new <see cref="HierarchicalRomFileTable{T}"/> that has the specified initial capacity.
/// </summary>
/// <param name="directoryCapacity">The initial number of directories that the
/// <see cref="HierarchicalRomFileTable"/> can contain.</param>
/// <see cref="HierarchicalRomFileTable{T}"/> can contain.</param>
/// <param name="fileCapacity">The initial number of files that the
/// <see cref="HierarchicalRomFileTable"/> can contain.</param>
/// <see cref="HierarchicalRomFileTable{T}"/> can contain.</param>
public HierarchicalRomFileTable(int directoryCapacity, int fileCapacity)
{
FileTable = new RomFsDictionary<FileRomEntry>(fileCapacity);
@ -78,7 +80,7 @@ namespace LibHac.Fs.RomFs
return FileTable.GetEntryData().ToArray();
}
public bool TryOpenFile(string path, out RomFileInfo fileInfo)
public bool TryOpenFile(string path, out T fileInfo)
{
FindPathRecursive(Util.GetUtf8Bytes(path), out RomEntryKey key);
@ -92,7 +94,7 @@ namespace LibHac.Fs.RomFs
return false;
}
public bool TryOpenFile(int fileId, out RomFileInfo fileInfo)
public bool TryOpenFile(int fileId, out T fileInfo)
{
if (FileTable.TryGetValue(fileId, out RomKeyValuePair<FileRomEntry> keyValuePair))
{
@ -153,7 +155,7 @@ namespace LibHac.Fs.RomFs
/// <param name="name">When this method returns, contains the file's name (Not the full path).</param>
/// <returns><see langword="true"/> if the next file was successfully returned.
/// <see langword="false"/> if there are no more files to enumerate.</returns>
public bool FindNextFile(ref FindPosition position, out RomFileInfo info, out string name)
public bool FindNextFile(ref FindPosition position, out T info, out string name)
{
if (position.NextFile == -1)
{
@ -201,7 +203,7 @@ namespace LibHac.Fs.RomFs
/// </summary>
/// <param name="path">The full path of the file to be added.</param>
/// <param name="fileInfo">The file information to be stored.</param>
public void AddFile(string path, ref RomFileInfo fileInfo)
public void AddFile(string path, ref T fileInfo)
{
path = PathTools.Normalize(path);
ReadOnlySpan<byte> pathBytes = Util.GetUtf8Bytes(path);
@ -288,7 +290,7 @@ namespace LibHac.Fs.RomFs
}
}
private void CreateFileRecursiveInternal(ReadOnlySpan<byte> path, ref RomFileInfo fileInfo)
private void CreateFileRecursiveInternal(ReadOnlySpan<byte> path, ref T fileInfo)
{
var parser = new PathParser(path);
var key = new RomEntryKey();
@ -367,18 +369,18 @@ namespace LibHac.Fs.RomFs
}
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct DirectoryRomEntry
{
public int NextSibling;
public FindPosition Pos;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct FileRomEntry
{
public int NextSibling;
public RomFileInfo Info;
public T Info;
}
}
}

View file

@ -18,7 +18,7 @@ namespace LibHac.Fs.RomFs
private const int HeaderWithPaddingSize = 0x200;
private List<IStorage> Sources { get; } = new List<IStorage>();
private HierarchicalRomFileTable FileTable { get; } = new HierarchicalRomFileTable();
private HierarchicalRomFileTable<RomFileInfo> FileTable { get; } = new HierarchicalRomFileTable<RomFileInfo>();
private long CurrentOffset { get; set; }
/// <summary>

View file

@ -5,7 +5,9 @@ using System.Runtime.InteropServices;
namespace LibHac.Fs.RomFs
{
internal class RomFsDictionary<T> where T : unmanaged
// todo: Change constraint to "unmanaged" after updating to
// a newer SDK https://github.com/dotnet/csharplang/issues/1937
internal class RomFsDictionary<T> where T : struct
{
private int _count;
private int _length;
@ -266,7 +268,7 @@ namespace LibHac.Fs.RomFs
{
var offsets = new List<int>(_count);
int nextStructOffset = (sizeof(int) + Marshal.SizeOf<T>()) / 4;
int nextStructOffset = (sizeof(int) + Unsafe.SizeOf<T>()) / 4;
Span<int> data = MemoryMarshal.Cast<byte, int>(Entries.AsSpan());
for (int i = 0; i < Buckets.Length; i++)

View file

@ -23,7 +23,7 @@ namespace LibHac.Fs.RomFs
public IEnumerable<DirectoryEntry> Read()
{
FindPosition position = InitialPosition;
HierarchicalRomFileTable tab = ParentFileSystem.FileTable;
HierarchicalRomFileTable<RomFileInfo> tab = ParentFileSystem.FileTable;
if (Mode.HasFlag(OpenDirectoryMode.Directories))
{
@ -47,7 +47,7 @@ namespace LibHac.Fs.RomFs
int count = 0;
FindPosition position = InitialPosition;
HierarchicalRomFileTable tab = ParentFileSystem.FileTable;
HierarchicalRomFileTable<RomFileInfo> tab = ParentFileSystem.FileTable;
if (Mode.HasFlag(OpenDirectoryMode.Directories))
{

View file

@ -32,7 +32,9 @@ namespace LibHac.Fs.RomFs
}
}
internal ref struct RomKeyValuePair<T> where T : unmanaged
// todo: Change constraint to "unmanaged" after updating to
// a newer SDK https://github.com/dotnet/csharplang/issues/1937
internal ref struct RomKeyValuePair<T> where T : struct
{
public RomEntryKey Key;
public int Offset;

View file

@ -6,7 +6,7 @@ namespace LibHac.Fs.RomFs
{
public RomfsHeader Header { get; }
public HierarchicalRomFileTable FileTable { get; }
public HierarchicalRomFileTable<RomFileInfo> FileTable { get; }
private IStorage BaseStorage { get; }
public RomFsFileSystem(IStorage storage)
@ -19,7 +19,7 @@ namespace LibHac.Fs.RomFs
IStorage fileHashTable = storage.Slice(Header.FileHashTableOffset, Header.FileHashTableSize);
IStorage fileEntryTable = storage.Slice(Header.FileMetaTableOffset, Header.FileMetaTableSize);
FileTable = new HierarchicalRomFileTable(dirHashTable, dirEntryTable, fileHashTable, fileEntryTable);
FileTable = new HierarchicalRomFileTable<RomFileInfo>(dirHashTable, dirEntryTable, fileHashTable, fileEntryTable);
}
public DirectoryEntryType GetEntryType(string path)

View file

@ -416,6 +416,8 @@ namespace LibHac.Fs.Save
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
// todo: Change constraint to "unmanaged" after updating to
// a newer SDK https://github.com/dotnet/csharplang/issues/1937
private struct TableEntry<T> where T : struct
{
public int NextSibling;

View file

@ -6,6 +6,8 @@ using System.Runtime.InteropServices;
namespace LibHac.Fs.Save
{
// todo: Change constraint to "unmanaged" after updating to
// a newer SDK https://github.com/dotnet/csharplang/issues/1937
internal class SaveFsList<T> where T : struct
{
private const int FreeListHeadIndex = 0;

View file

@ -11,7 +11,7 @@ namespace LibHac.Tests
{
const string path = "/a/b";
var table = new HierarchicalRomFileTable();
var table = new HierarchicalRomFileTable<RomFileInfo>();
var item = new RomFileInfo { Length = 1, Offset = 1 };
table.AddFile(path, ref item);
@ -26,7 +26,7 @@ namespace LibHac.Tests
{
const string path = "/a/b";
var table = new HierarchicalRomFileTable();
var table = new HierarchicalRomFileTable<RomFileInfo>();
var originalItem = new RomFileInfo { Length = 1, Offset = 1 };
var newItem = new RomFileInfo { Length = 1, Offset = 1 };
@ -42,7 +42,7 @@ namespace LibHac.Tests
[Fact]
public void AddingDirectory()
{
var table = new HierarchicalRomFileTable();
var table = new HierarchicalRomFileTable<RomFileInfo>();
var expectedPosition = new FindPosition { NextDirectory = -1, NextFile = -1 };
table.AddDirectory("/dir");
@ -55,7 +55,7 @@ namespace LibHac.Tests
[Fact]
public void AddingEmptyPathThrows()
{
var table = new HierarchicalRomFileTable();
var table = new HierarchicalRomFileTable<RomFileInfo>();
var item = new RomFileInfo();
Assert.Throws<ArgumentException>(() => table.AddFile("", ref item));
@ -64,7 +64,7 @@ namespace LibHac.Tests
[Fact]
public void OpeningNonexistentFileFails()
{
var table = new HierarchicalRomFileTable();
var table = new HierarchicalRomFileTable<RomFileInfo>();
bool success = table.TryOpenFile("/foo", out _);
Assert.False(success);
@ -73,7 +73,7 @@ namespace LibHac.Tests
[Fact]
public void OpeningNonexistentDirectoryFails()
{
var table = new HierarchicalRomFileTable();
var table = new HierarchicalRomFileTable<RomFileInfo>();
bool success = table.TryOpenDirectory("/foo", out _);
Assert.False(success);
@ -82,7 +82,7 @@ namespace LibHac.Tests
[Fact]
public void OpeningFileAsDirectoryFails()
{
var table = new HierarchicalRomFileTable();
var table = new HierarchicalRomFileTable<RomFileInfo>();
var fileInfo = new RomFileInfo();
table.AddFile("/file", ref fileInfo);
@ -93,7 +93,7 @@ namespace LibHac.Tests
[Fact]
public void OpeningDirectoryAsFileFails()
{
var table = new HierarchicalRomFileTable();
var table = new HierarchicalRomFileTable<RomFileInfo>();
table.AddDirectory("/dir");
bool success = table.TryOpenFile("/dir", out _);
@ -104,7 +104,7 @@ namespace LibHac.Tests
public void ChildFileIteration()
{
const int fileCount = 10;
var table = new HierarchicalRomFileTable();
var table = new HierarchicalRomFileTable<RomFileInfo>();
for (int i = 0; i < fileCount; i++)
{
@ -134,7 +134,7 @@ namespace LibHac.Tests
[Fact]
public void ChildFileIterationPeek()
{
var table = new HierarchicalRomFileTable();
var table = new HierarchicalRomFileTable<RomFileInfo>();
var itemA = new RomFileInfo { Length = 1, Offset = 1 };
var itemB = new RomFileInfo { Length = 2, Offset = 2 };
@ -160,7 +160,7 @@ namespace LibHac.Tests
[Fact]
public void AddingCousinFiles()
{
var table = new HierarchicalRomFileTable();
var table = new HierarchicalRomFileTable<RomFileInfo>();
var itemB1 = new RomFileInfo { Length = 1, Offset = 1 };
var itemB2 = new RomFileInfo { Length = 2, Offset = 2 };
@ -182,7 +182,7 @@ namespace LibHac.Tests
[Fact]
public void AddingSiblingFiles()
{
var table = new HierarchicalRomFileTable();
var table = new HierarchicalRomFileTable<RomFileInfo>();
var itemC1 = new RomFileInfo { Length = 1, Offset = 1 };
var itemC2 = new RomFileInfo { Length = 2, Offset = 2 };