mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Make RomFsDictionary hash table resizable
This commit is contained in:
parent
eeb6ebf0a7
commit
b99bb84e9c
2 changed files with 86 additions and 6 deletions
|
@ -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<T> 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<T>()) / 4;
|
||||
Span<int> data = MemoryMarshal.Cast<byte, int>(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<int> offsets = GetEntryOffsets();
|
||||
var key = new RomEntryKey();
|
||||
|
||||
for (int i = 0; i < offsets.Count; i++)
|
||||
{
|
||||
ref RomFsEntry<T> 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<T> GetEntryReference(int offset, out ReadOnlySpan<byte> name)
|
||||
{
|
||||
ref RomFsEntry<T> entry = ref MemoryMarshal.Cast<byte, RomFsEntry<T>>(Entries.AsSpan(offset))[0];
|
||||
|
||||
name = Entries.AsSpan(offset + _sizeOfEntry, entry.KeyLength);
|
||||
return ref entry;
|
||||
}
|
||||
|
||||
private List<int> GetEntryOffsets()
|
||||
{
|
||||
var offsets = new List<int>(_count);
|
||||
|
||||
int nextStructOffset = (sizeof(int) + Marshal.SizeOf<T>()) / 4;
|
||||
Span<int> data = MemoryMarshal.Cast<byte, int>(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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace LibHac.IO.RomFs
|
|||
public T Value;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
public struct RomFsEntry<T> 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;
|
||||
|
|
Loading…
Reference in a new issue