diff --git a/src/LibHac/HashHelpers.cs b/src/LibHac/HashHelpers.cs index b30778a6..06d29deb 100644 --- a/src/LibHac/HashHelpers.cs +++ b/src/LibHac/HashHelpers.cs @@ -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; + } } } \ No newline at end of file diff --git a/src/LibHac/IO/RomFs/HierarchicalRomFileTable.cs b/src/LibHac/IO/RomFs/HierarchicalRomFileTable.cs index d47549d3..43e998da 100644 --- a/src/LibHac/IO/RomFs/HierarchicalRomFileTable.cs +++ b/src/LibHac/IO/RomFs/HierarchicalRomFileTable.cs @@ -27,6 +27,8 @@ namespace LibHac.IO.RomFs DirectoryTable = new RomFsDictionary(DirHashTableStorage, DirEntryTableStorage); } + public HierarchicalRomFileTable() : this(0, 0) { } + public HierarchicalRomFileTable(int directoryCapacity, int fileCapacity) { FileTable = new RomFsDictionary(fileCapacity); @@ -155,6 +157,12 @@ namespace LibHac.IO.RomFs CreateDirectoryRecursive(GetUtf8Bytes(path)); } + public void TrimExcess() + { + DirectoryTable.TrimExcess(); + FileTable.TrimExcess(); + } + private static ReadOnlySpan GetUtf8Bytes(string value) { return Encoding.UTF8.GetBytes(value).AsSpan(); diff --git a/src/LibHac/IO/RomFs/RomFsBuilder.cs b/src/LibHac/IO/RomFs/RomFsBuilder.cs index aa6f671e..09dfde59 100644 --- a/src/LibHac/IO/RomFs/RomFsBuilder.cs +++ b/src/LibHac/IO/RomFs/RomFsBuilder.cs @@ -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() diff --git a/src/LibHac/IO/RomFs/RomFsDictionary.cs b/src/LibHac/IO/RomFs/RomFsDictionary.cs index 18b80dd9..007a275e 100644 --- a/src/LibHac/IO/RomFs/RomFsDictionary.cs +++ b/src/LibHac/IO/RomFs/RomFsDictionary.cs @@ -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(Entries.AsSpan(offset))[0];