Improve rom file table read/write performance

This commit is contained in:
Alex Barney 2019-02-01 16:15:28 -06:00
parent b99bb84e9c
commit 5457a81068
3 changed files with 56 additions and 68 deletions

View file

@ -308,5 +308,19 @@ namespace LibHac.IO.RomFs
return DirectoryTable.ContainsKey(ref key) || return DirectoryTable.ContainsKey(ref key) ||
FileTable.ContainsKey(ref key); FileTable.ContainsKey(ref key);
} }
[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct DirectoryRomEntry
{
public int NextSibling;
public FindPosition Pos;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct FileRomEntry
{
public int NextSibling;
public RomFileInfo Info;
}
} }
} }

View file

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace LibHac.IO.RomFs namespace LibHac.IO.RomFs
@ -47,7 +46,7 @@ namespace LibHac.IO.RomFs
if (i >= 0) if (i >= 0)
{ {
GetEntryInternal(i, out RomFsEntry<T> entry); ref RomFsEntry entry = ref GetEntryReference(i);
value = new RomKeyValuePair<T> { Key = key, Value = entry.Value, Offset = i }; value = new RomKeyValuePair<T> { Key = key, Value = entry.Value, Offset = i };
return true; return true;
@ -67,7 +66,9 @@ namespace LibHac.IO.RomFs
value = new RomKeyValuePair<T>(); value = new RomKeyValuePair<T>();
GetEntryInternal(offset, out RomFsEntry<T> entry, out value.Key.Name); ref RomFsEntry entry = ref GetEntryReference(offset, out Span<byte> name);
value.Key.Name = name;
value.Value = entry.Value; value.Value = entry.Value;
value.Key.Parent = entry.Parent; value.Key.Parent = entry.Parent;
return true; return true;
@ -78,9 +79,8 @@ namespace LibHac.IO.RomFs
int i = FindEntry(ref key); int i = FindEntry(ref key);
if (i < 0) return false; if (i < 0) return false;
GetEntryInternal(i, out RomFsEntry<T> entry); ref RomFsEntry entry = ref GetEntryReference(i);
entry.Value = value; entry.Value = value;
SetEntryInternal(i, ref entry);
return true; return true;
} }
@ -99,13 +99,14 @@ namespace LibHac.IO.RomFs
int bucket = (int)(hashCode % Buckets.Length); int bucket = (int)(hashCode % Buckets.Length);
int newOffset = FindOffsetForInsert(ref key); int newOffset = FindOffsetForInsert(ref key);
var entry = new RomFsEntry<T>(); ref RomFsEntry entry = ref GetEntryReference(newOffset, out Span<byte> name, key.Name.Length);
entry.Next = Buckets[bucket]; entry.Next = Buckets[bucket];
entry.Parent = key.Parent; entry.Parent = key.Parent;
entry.KeyLength = key.Name.Length; entry.KeyLength = key.Name.Length;
entry.Value = value; entry.Value = value;
SetEntryInternal(newOffset, ref entry, ref key.Name); key.Name.CopyTo(name);
Buckets[bucket] = newOffset; Buckets[bucket] = newOffset;
_count++; _count++;
@ -135,7 +136,7 @@ namespace LibHac.IO.RomFs
while (i != -1) while (i != -1)
{ {
GetEntryInternal(i, out RomFsEntry<T> entry, out ReadOnlySpan<byte> name); ref RomFsEntry entry = ref GetEntryReference(i, out Span<byte> name);
if (key.Parent == entry.Parent && key.Name.SequenceEqual(name)) if (key.Parent == entry.Parent && key.Name.SequenceEqual(name))
{ {
@ -148,35 +149,6 @@ namespace LibHac.IO.RomFs
return i; return i;
} }
private void GetEntryInternal(int offset, out RomFsEntry<T> outEntry)
{
outEntry = MemoryMarshal.Read<RomFsEntry<T>>(Entries.AsSpan(offset));
}
private void GetEntryInternal(int offset, out RomFsEntry<T> outEntry, out ReadOnlySpan<byte> entryName)
{
GetEntryInternal(offset, out outEntry);
if (outEntry.KeyLength > 0x300)
{
throw new InvalidDataException("Rom entry name is too long.");
}
entryName = Entries.AsSpan(offset + _sizeOfEntry, outEntry.KeyLength);
}
private void SetEntryInternal(int offset, ref RomFsEntry<T> entry)
{
MemoryMarshal.Write(Entries.AsSpan(offset), ref entry);
}
private void SetEntryInternal(int offset, ref RomFsEntry<T> entry, ref ReadOnlySpan<byte> entryName)
{
MemoryMarshal.Write(Entries.AsSpan(offset), ref entry);
entryName.CopyTo(Entries.AsSpan(offset + _sizeOfEntry, entry.KeyLength));
}
private void EnsureCapacityBytes(int value) private void EnsureCapacityBytes(int value)
{ {
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value)); if (value < 0) throw new ArgumentOutOfRangeException(nameof(value));
@ -229,14 +201,12 @@ namespace LibHac.IO.RomFs
newBuckets.AsSpan().Fill(-1); newBuckets.AsSpan().Fill(-1);
List<int> offsets = GetEntryOffsets(); List<int> offsets = GetEntryOffsets();
var key = new RomEntryKey();
for (int i = 0; i < offsets.Count; i++) for (int i = 0; i < offsets.Count; i++)
{ {
ref RomFsEntry<T> entry = ref GetEntryReference(offsets[i], out key.Name); ref RomFsEntry entry = ref GetEntryReference(offsets[i], out Span<byte> name);
key.Parent = entry.Parent;
uint hashCode = key.GetRomHashCode(); uint hashCode = RomEntryKey.GetRomHashCode(entry.Parent, name);
int bucket = (int)(hashCode % newSize); int bucket = (int)(hashCode % newSize);
entry.Next = newBuckets[bucket]; entry.Next = newBuckets[bucket];
@ -246,14 +216,27 @@ namespace LibHac.IO.RomFs
Buckets = newBuckets; Buckets = newBuckets;
} }
private ref RomFsEntry<T> GetEntryReference(int offset, out ReadOnlySpan<byte> name) private ref RomFsEntry GetEntryReference(int offset)
{ {
ref RomFsEntry<T> entry = ref MemoryMarshal.Cast<byte, RomFsEntry<T>>(Entries.AsSpan(offset))[0]; return ref MemoryMarshal.Cast<byte, RomFsEntry>(Entries.AsSpan(offset))[0];
}
private ref RomFsEntry GetEntryReference(int offset, out Span<byte> name)
{
ref RomFsEntry entry = ref MemoryMarshal.Cast<byte, RomFsEntry>(Entries.AsSpan(offset))[0];
name = Entries.AsSpan(offset + _sizeOfEntry, entry.KeyLength); name = Entries.AsSpan(offset + _sizeOfEntry, entry.KeyLength);
return ref entry; return ref entry;
} }
private ref RomFsEntry GetEntryReference(int offset, out Span<byte> name, int nameLength)
{
ref RomFsEntry entry = ref MemoryMarshal.Cast<byte, RomFsEntry>(Entries.AsSpan(offset))[0];
name = Entries.AsSpan(offset + _sizeOfEntry, nameLength);
return ref entry;
}
private List<int> GetEntryOffsets() private List<int> GetEntryOffsets()
{ {
var offsets = new List<int>(_count); var offsets = new List<int>(_count);
@ -277,5 +260,14 @@ namespace LibHac.IO.RomFs
} }
private int EstimateEntryTableSize(int count) => (_sizeOfEntry + 0x10) * count; // Estimate 0x10 bytes per name private int EstimateEntryTableSize(int count) => (_sizeOfEntry + 0x10) * count; // Estimate 0x10 bytes per name
[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct RomFsEntry
{
public int Parent;
public T Value;
public int Next;
public int KeyLength;
}
} }
} }

View file

@ -16,9 +16,14 @@ namespace LibHac.IO.RomFs
public uint GetRomHashCode() public uint GetRomHashCode()
{ {
uint hash = 123456789 ^ (uint)Parent; return GetRomHashCode(Parent, Name);
}
foreach (byte c in Name) public static uint GetRomHashCode(int parent, ReadOnlySpan<byte> name)
{
uint hash = 123456789 ^ (uint)parent;
foreach (byte c in name)
{ {
hash = c ^ ((hash << 27) | (hash >> 5)); hash = c ^ ((hash << 27) | (hash >> 5));
} }
@ -34,22 +39,6 @@ namespace LibHac.IO.RomFs
public T Value; public T Value;
} }
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct RomFsEntry<T> where T : unmanaged
{
public int Parent;
public T Value;
public int Next;
public int KeyLength;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct FileRomEntry
{
public int NextSibling;
public RomFileInfo Info;
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct RomFileInfo public struct RomFileInfo
{ {
@ -57,13 +46,6 @@ namespace LibHac.IO.RomFs
public long Length; public long Length;
} }
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct DirectoryRomEntry
{
public int NextSibling;
public FindPosition Pos;
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct FindPosition public struct FindPosition
{ {