Automatically resize the romfs dictionary

This commit is contained in:
Alex Barney 2019-02-03 15:47:24 -06:00
parent d2f7aa52dc
commit c1beb9d1fd
4 changed files with 53 additions and 36 deletions

View file

@ -52,40 +52,20 @@ namespace LibHac
return (candidate == 2); return (candidate == 2);
} }
public static int GetHashTableEntryCount(int entries)
{
uint count = (uint) entries;
if (entries < 3)
count = 3;
else if (count < 19)
count |= 1;
else
{
while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 || count % 11 == 0 || count % 13 == 0 || count % 17 == 0)
{
count++;
}
}
return (int) count;
}
public static int GetPrime(int min) public static int GetPrime(int min)
{ {
if (min < 0) if (min < 0)
throw new ArgumentException(nameof(min)); throw new ArgumentException(nameof(min));
// RomFS dictionaries choose from all possible primes for (int i = 0; i < Primes.Length; i++)
{
//for (int i = 0; i < Primes.Length; i++) int prime = Primes[i];
//{ if (prime >= min)
// int prime = Primes[i]; return prime;
// if (prime >= min) }
// return prime;
//}
//outside of our predefined table. //outside of our predefined table.
//compute the hard way. //compute the hard way.
for (int i = (min | 1); i < int.MaxValue; i += 2) for (int i = (min | 1); i < int.MaxValue; i += 2)
{ {
if (IsPrime(i) && ((i - 1) % HashPrime != 0)) if (IsPrime(i) && ((i - 1) % HashPrime != 0))
@ -109,5 +89,19 @@ namespace LibHac
return GetPrime(newSize); return GetPrime(newSize);
} }
public static int GetRomFsPrime(int min)
{
if (min < 3) return 3;
if (min < 19) return min | 1;
for (int i = (min | 1); i < int.MaxValue; i += 2)
{
if (i % 2 != 0 && i % 3 != 0 && i % 5 != 0 && i % 7 != 0 && i % 11 != 0 && i % 13 != 0 && i % 17 != 0)
return i;
}
return min;
}
} }
} }

View file

@ -27,6 +27,8 @@ namespace LibHac.IO.RomFs
DirectoryTable = new RomFsDictionary<DirectoryRomEntry>(DirHashTableStorage, DirEntryTableStorage); DirectoryTable = new RomFsDictionary<DirectoryRomEntry>(DirHashTableStorage, DirEntryTableStorage);
} }
public HierarchicalRomFileTable() : this(0, 0) { }
public HierarchicalRomFileTable(int directoryCapacity, int fileCapacity) public HierarchicalRomFileTable(int directoryCapacity, int fileCapacity)
{ {
FileTable = new RomFsDictionary<FileRomEntry>(fileCapacity); FileTable = new RomFsDictionary<FileRomEntry>(fileCapacity);
@ -155,6 +157,12 @@ namespace LibHac.IO.RomFs
CreateDirectoryRecursive(GetUtf8Bytes(path)); CreateDirectoryRecursive(GetUtf8Bytes(path));
} }
public void TrimExcess()
{
DirectoryTable.TrimExcess();
FileTable.TrimExcess();
}
private static ReadOnlySpan<byte> GetUtf8Bytes(string value) private static ReadOnlySpan<byte> GetUtf8Bytes(string value)
{ {
return Encoding.UTF8.GetBytes(value).AsSpan(); return Encoding.UTF8.GetBytes(value).AsSpan();

View file

@ -16,17 +16,14 @@ namespace LibHac.IO.RomFs
public RomFsBuilder(IFileSystem input) public RomFsBuilder(IFileSystem input)
{ {
DirectoryEntry[] entries = input.EnumerateEntries().ToArray(); FileTable = new HierarchicalRomFileTable();
int fileCount = entries.Count(x => x.Type == DirectoryEntryType.File); var fileInfo = new RomFileInfo();
int dirCount = entries.Count(x => x.Type == DirectoryEntryType.Directory);
FileTable = new HierarchicalRomFileTable(dirCount, fileCount);
long offset = 0; long offset = 0;
foreach (DirectoryEntry file in entries.Where(x => x.Type == DirectoryEntryType.File).OrderBy(x => x.FullPath, StringComparer.Ordinal)) foreach (DirectoryEntry file in input.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File)
.OrderBy(x => x.FullPath, StringComparer.Ordinal))
{ {
var fileInfo = new RomFileInfo();
fileInfo.Offset = offset; fileInfo.Offset = offset;
fileInfo.Length = file.Size; fileInfo.Length = file.Size;
@ -41,6 +38,8 @@ namespace LibHac.IO.RomFs
FileTable.CreateFile(file.FullPath, ref fileInfo); FileTable.CreateFile(file.FullPath, ref fileInfo);
} }
FileTable.TrimExcess();
} }
public IStorage Build() public IStorage Build()

View file

@ -27,9 +27,11 @@ namespace LibHac.IO.RomFs
_count = CountEntries(); _count = CountEntries();
} }
public RomFsDictionary() : this(0) { }
public RomFsDictionary(int capacity) public RomFsDictionary(int capacity)
{ {
int size = HashHelpers.GetHashTableEntryCount(capacity); int size = HashHelpers.GetRomFsPrime(capacity);
Buckets = new int[size]; Buckets = new int[size];
Buckets.AsSpan().Fill(-1); Buckets.AsSpan().Fill(-1);
@ -124,6 +126,12 @@ namespace LibHac.IO.RomFs
Buckets[bucket] = newOffset; Buckets[bucket] = newOffset;
_count++; _count++;
if (Buckets.Length < _count)
{
Resize();
entry = ref GetEntryReference(newOffset, out name, key.Name.Length);
}
return ref entry.Value; return ref entry.Value;
} }
} }
@ -210,7 +218,9 @@ namespace LibHac.IO.RomFs
return count; return count;
} }
public void Resize(int newSize) private void Resize() => Resize(HashHelpers.ExpandPrime(_count));
private void Resize(int newSize)
{ {
var newBuckets = new int[newSize]; var newBuckets = new int[newSize];
newBuckets.AsSpan().Fill(-1); newBuckets.AsSpan().Fill(-1);
@ -231,6 +241,12 @@ namespace LibHac.IO.RomFs
Buckets = newBuckets; Buckets = newBuckets;
} }
public void TrimExcess()
{
Resize(HashHelpers.GetRomFsPrime(_count));
SetCapacity(_length);
}
private ref RomFsEntry GetEntryReference(int offset) private ref RomFsEntry GetEntryReference(int offset)
{ {
return ref MemoryMarshal.Cast<byte, RomFsEntry>(Entries.AsSpan(offset))[0]; return ref MemoryMarshal.Cast<byte, RomFsEntry>(Entries.AsSpan(offset))[0];