From b99bb84e9cda80c1ad0fbc2a8f59b26849e51736 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Fri, 1 Feb 2019 15:31:13 -0600 Subject: [PATCH] Make RomFsDictionary hash table resizable --- src/LibHac/IO/RomFs/RomFsDictionary.cs | 86 +++++++++++++++++++++++++- src/LibHac/IO/RomFs/RomFsEntries.cs | 6 +- 2 files changed, 86 insertions(+), 6 deletions(-) diff --git a/src/LibHac/IO/RomFs/RomFsDictionary.cs b/src/LibHac/IO/RomFs/RomFsDictionary.cs index b30ac1ad..d9f43035 100644 --- a/src/LibHac/IO/RomFs/RomFsDictionary.cs +++ b/src/LibHac/IO/RomFs/RomFsDictionary.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; @@ -6,6 +7,7 @@ namespace LibHac.IO.RomFs { internal class RomFsDictionary where T : unmanaged { + private int _count; private int _length; private int _capacity; @@ -22,6 +24,8 @@ namespace LibHac.IO.RomFs _length = Entries.Length; _capacity = Entries.Length; + + _count = CountEntries(); } public RomFsDictionary(int capacity) @@ -30,7 +34,7 @@ namespace LibHac.IO.RomFs Buckets = new int[size]; Buckets.AsSpan().Fill(-1); - Entries = new byte[(_sizeOfEntry + 0x10) * size]; // Estimate 0x10 bytes per name + Entries = new byte[EstimateEntryTableSize(size)]; _capacity = Entries.Length; } @@ -104,6 +108,7 @@ namespace LibHac.IO.RomFs SetEntryInternal(newOffset, ref entry, ref key.Name); Buckets[bucket] = newOffset; + _count++; return newOffset; } @@ -113,7 +118,7 @@ namespace LibHac.IO.RomFs if (_length + bytesNeeded > _capacity) { - EnsureEntryTableCapacity(_length + bytesNeeded); + EnsureCapacityBytes(_length + bytesNeeded); } int offset = _length; @@ -172,7 +177,7 @@ namespace LibHac.IO.RomFs entryName.CopyTo(Entries.AsSpan(offset + _sizeOfEntry, entry.KeyLength)); } - private void EnsureEntryTableCapacity(int value) + private void EnsureCapacityBytes(int value) { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value)); if (value <= _capacity) return; @@ -197,5 +202,80 @@ namespace LibHac.IO.RomFs _capacity = value; } } + + public int CountEntries() + { + int count = 0; + int nextStructOffset = (sizeof(int) + Marshal.SizeOf()) / 4; + Span data = MemoryMarshal.Cast(Entries.AsSpan()); + + for (int i = 0; i < Buckets.Length; i++) + { + int next = Buckets[i]; + + while (next != -1) + { + next = data[next / 4 + nextStructOffset]; + count++; + } + } + + return count; + } + + public void Resize(int newSize) + { + var newBuckets = new int[newSize]; + newBuckets.AsSpan().Fill(-1); + + List offsets = GetEntryOffsets(); + var key = new RomEntryKey(); + + for (int i = 0; i < offsets.Count; i++) + { + ref RomFsEntry entry = ref GetEntryReference(offsets[i], out key.Name); + key.Parent = entry.Parent; + + uint hashCode = key.GetRomHashCode(); + int bucket = (int)(hashCode % newSize); + + entry.Next = newBuckets[bucket]; + newBuckets[bucket] = offsets[i]; + } + + Buckets = newBuckets; + } + + private ref RomFsEntry GetEntryReference(int offset, out ReadOnlySpan name) + { + ref RomFsEntry entry = ref MemoryMarshal.Cast>(Entries.AsSpan(offset))[0]; + + name = Entries.AsSpan(offset + _sizeOfEntry, entry.KeyLength); + return ref entry; + } + + private List GetEntryOffsets() + { + var offsets = new List(_count); + + int nextStructOffset = (sizeof(int) + Marshal.SizeOf()) / 4; + Span data = MemoryMarshal.Cast(Entries.AsSpan()); + + for (int i = 0; i < Buckets.Length; i++) + { + int next = Buckets[i]; + + while (next != -1) + { + offsets.Add(next); + next = data[next / 4 + nextStructOffset]; + } + } + + offsets.Sort(); + return offsets; + } + + private int EstimateEntryTableSize(int count) => (_sizeOfEntry + 0x10) * count; // Estimate 0x10 bytes per name } } diff --git a/src/LibHac/IO/RomFs/RomFsEntries.cs b/src/LibHac/IO/RomFs/RomFsEntries.cs index 06c00a8a..06d49c4b 100644 --- a/src/LibHac/IO/RomFs/RomFsEntries.cs +++ b/src/LibHac/IO/RomFs/RomFsEntries.cs @@ -34,7 +34,7 @@ namespace LibHac.IO.RomFs public T Value; } - [StructLayout(LayoutKind.Sequential, Pack = 1)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct RomFsEntry where T : unmanaged { public int Parent; @@ -43,7 +43,7 @@ namespace LibHac.IO.RomFs public int KeyLength; } - [StructLayout(LayoutKind.Sequential, Pack = 1)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct FileRomEntry { public int NextSibling; @@ -57,7 +57,7 @@ namespace LibHac.IO.RomFs public long Length; } - [StructLayout(LayoutKind.Sequential, Pack = 1)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct DirectoryRomEntry { public int NextSibling;