Automatically resize the romfs dictionary

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

View file

@ -52,40 +52,20 @@ namespace LibHac
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)
{
if (min < 0)
throw new ArgumentException(nameof(min));
// RomFS dictionaries choose from all possible primes
//for (int i = 0; i < Primes.Length; i++)
//{
// int prime = Primes[i];
// if (prime >= min)
// return prime;
//}
for (int i = 0; i < Primes.Length; i++)
{
int prime = Primes[i];
if (prime >= min)
return prime;
}
//outside of our predefined table.
//compute the hard way.
for (int i = (min | 1); i < int.MaxValue; i += 2)
{
if (IsPrime(i) && ((i - 1) % HashPrime != 0))
@ -109,5 +89,19 @@ namespace LibHac
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);
}
public HierarchicalRomFileTable() : this(0, 0) { }
public HierarchicalRomFileTable(int directoryCapacity, int fileCapacity)
{
FileTable = new RomFsDictionary<FileRomEntry>(fileCapacity);
@ -155,6 +157,12 @@ namespace LibHac.IO.RomFs
CreateDirectoryRecursive(GetUtf8Bytes(path));
}
public void TrimExcess()
{
DirectoryTable.TrimExcess();
FileTable.TrimExcess();
}
private static ReadOnlySpan<byte> GetUtf8Bytes(string value)
{
return Encoding.UTF8.GetBytes(value).AsSpan();

View file

@ -16,17 +16,14 @@ namespace LibHac.IO.RomFs
public RomFsBuilder(IFileSystem input)
{
DirectoryEntry[] entries = input.EnumerateEntries().ToArray();
int fileCount = entries.Count(x => x.Type == DirectoryEntryType.File);
int dirCount = entries.Count(x => x.Type == DirectoryEntryType.Directory);
FileTable = new HierarchicalRomFileTable(dirCount, fileCount);
FileTable = new HierarchicalRomFileTable();
var fileInfo = new RomFileInfo();
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.Length = file.Size;
@ -41,6 +38,8 @@ namespace LibHac.IO.RomFs
FileTable.CreateFile(file.FullPath, ref fileInfo);
}
FileTable.TrimExcess();
}
public IStorage Build()

View file

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