Skip to content

Commit

Permalink
Use span chars for hash instead of bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
DiFFoZ committed Jun 18, 2024
1 parent 88112ef commit 48813da
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 57 deletions.
45 changes: 16 additions & 29 deletions BepInExFasterLoadAssetBundles/Helpers/HashingHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ internal class HashingHelper
{
private const int c_BufferSize = 16 * (int)FileHelper.c_MBToBytes;

public static byte[] HashFile(string path)
public static int HashFile(Span<char> destination, string path)
{
using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None, c_BufferSize, FileOptions.SequentialScan);
return HashStream(fileStream);
return WriteHash(destination, fileStream);
}

public static unsafe byte[] HashStream(Stream stream)
public static unsafe int WriteHash(Span<char> destination, Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);

Expand All @@ -34,39 +34,26 @@ public static unsafe byte[] HashStream(Stream stream)

UnsafeUtility.Free(buffer, Allocator.Temp);

var hashArray = new byte[16];
BinaryPrimitives.WriteUInt64LittleEndian(hashArray, hash.u64_0);
BinaryPrimitives.WriteUInt64LittleEndian(hashArray.AsSpan(8), hash.u64_1);
Span<byte> hashSpan = stackalloc byte[16];
BinaryPrimitives.WriteUInt64LittleEndian(hashSpan, hash.u64_0);
BinaryPrimitives.WriteUInt64LittleEndian(hashSpan.Slice(8), hash.u64_1);

return hashArray;
return HashToString(destination, hashSpan);
}

public static string HashToString(Span<byte> hash)
/// <summary>
/// Writes user readable hash from bytes
/// </summary>
/// <param name="destination"></param>
/// <param name="hash"></param>
/// <returns>Written <see langword="char"/> count</returns>
private static int HashToString(Span<char> destination, ReadOnlySpan<byte> hash)
{
Span<char> chars = stackalloc char[hash.Length * 2];

for (var i = 0; i < hash.Length; i++)
{
var b = hash[i];
b.TryFormat(chars[(i * 2)..], out _, "X2", CultureInfo.InvariantCulture);
}

return chars.ToString();
}

public static int WriteHash(Span<byte> destination, string hash)
{
if ((hash.Length / 2) > destination.Length)
{
throw new ArgumentOutOfRangeException("Destination is small to write hash", nameof(destination));
}

for (var i = 0; i < hash.Length; i += 2)
{
var s = hash.AsSpan(i, 2);
destination[i / 2] = byte.Parse(s, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
hash[i].TryFormat(destination.Slice(i * 2), out _, "X2", CultureInfo.InvariantCulture);
}

return hash.Length / 2;
return hash.Length * 2;
}
}
48 changes: 26 additions & 22 deletions BepInExFasterLoadAssetBundles/Managers/AssetBundleManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ public unsafe bool TryRecompressAssetBundle(Stream stream, [NotNullWhen(true)] o
return false;
}

var hash = HashingHelper.HashStream(stream);

path = null!;
Span<char> hash = stackalloc char[32];
HashingHelper.WriteHash(hash, stream);

path = null;
if (FindCachedBundleByHash(hash, out var newPath))
{
if (newPath != null)
Expand All @@ -102,7 +103,7 @@ public unsafe bool TryRecompressAssetBundle(Stream stream, [NotNullWhen(true)] o
if (stream is FileStream fileStream)
{
path = string.Copy(fileStream.Name);
RecompressAssetBundleInternal(new(path, hash, false, compressionType));
RecompressAssetBundleInternal(new(path, hash.ToString(), false, compressionType));
return false;
}

Expand All @@ -124,7 +125,7 @@ public unsafe bool TryRecompressAssetBundle(Stream stream, [NotNullWhen(true)] o
}
}

RecompressAssetBundleInternal(new(tempFile, hash, true, compressionType));
RecompressAssetBundleInternal(new(tempFile, hash.ToString(), true, compressionType));
return false;
}

Expand All @@ -137,7 +138,7 @@ public void DeleteCachedAssetBundle(string path)
}
}

private bool FindCachedBundleByHash(byte[] hash, out string? path)
private bool FindCachedBundleByHash(ReadOnlySpan<char> hash, out string? path)
{
path = null!;

Expand Down Expand Up @@ -184,22 +185,16 @@ static void ModifyAccessTimeAndSave(Metadata metadata)

private void RecompressAssetBundleInternal(WorkAsset workAsset)
{
if (!File.Exists(workAsset.Path))
if (!DriveHelper.HasDriveSpaceOnPath(CachePath, 10))
{
Patcher.Logger.LogWarning($"Ignoring request of decompressing, because the free drive space is less than 10GB");
return;
}

if (DriveHelper.HasDriveSpaceOnPath(CachePath, 10))
{
Patcher.Logger.LogDebug($"Queued recompress of \"{Path.GetFileName(workAsset.Path)}\" assetbundle");

m_WorkAssets.Enqueue(workAsset);
StartRunner();
return;
}
Patcher.Logger.LogDebug($"Queued recompress of \"{Path.GetFileName(workAsset.Path)}\" assetbundle");

Patcher.Logger.LogWarning($"Ignoring request of decompressing, because the free drive space is less than 10GB");
return;
m_WorkAssets.Enqueue(workAsset);
StartRunner();
}

private void StartRunner()
Expand Down Expand Up @@ -247,7 +242,7 @@ private async Task DecompressAssetBundleAsync(WorkAsset workAsset)
{
var metadata = new Metadata()
{
OriginalAssetBundleHash = HashingHelper.HashToString(workAsset.Hash),
OriginalAssetBundleHash = workAsset.Hash,
LastAccessTime = DateTime.Now,
};
var originalFileName = Path.GetFileNameWithoutExtension(workAsset.Path);
Expand All @@ -274,7 +269,8 @@ private async Task DecompressAssetBundleAsync(WorkAsset workAsset)
var result = op.result;
var humanReadableResult = op.humanReadableResult;
var success = op.success;
var newHash = HashingHelper.HashFile(outputPath);

var newHash = GetHashOfFile(outputPath);

await AsyncHelper.SwitchToThreadPool();

Expand All @@ -292,7 +288,7 @@ private async Task DecompressAssetBundleAsync(WorkAsset workAsset)
}

// check if unity returned the same assetbundle (means that assetbundle is already decompressed)
if (workAsset.Hash.AsSpan().SequenceEqual(newHash))
if (newHash.Equals(workAsset.Hash, StringComparison.InvariantCultureIgnoreCase))
{
Patcher.Logger.LogDebug($"Assetbundle \"{originalFileName}\" is already uncompressed, adding to ignore list");

Expand All @@ -307,11 +303,19 @@ private async Task DecompressAssetBundleAsync(WorkAsset workAsset)

metadata.UncompressedAssetBundleName = outputName;
Patcher.MetadataManager.SaveMetadata(metadata);

static string GetHashOfFile(string filePath)
{
Span<char> hash = stackalloc char[32];
HashingHelper.HashFile(hash, filePath);

return hash.ToString();
}
}

private readonly struct WorkAsset
{
public WorkAsset(string path, byte[] hash, bool deleteBundleAfterOperation, CompressionType compressionType)
public WorkAsset(string path, string hash, bool deleteBundleAfterOperation, CompressionType compressionType)
{
Path = path;
Hash = hash;
Expand All @@ -320,7 +324,7 @@ public WorkAsset(string path, byte[] hash, bool deleteBundleAfterOperation, Comp
}

public string Path { get; }
public byte[] Hash { get; }
public string Hash { get; }
public bool DeleteBundleAfterOperation { get; }
public CompressionType CompressionType { get; }
}
Expand Down
7 changes: 2 additions & 5 deletions BepInExFasterLoadAssetBundles/Managers/MetadataManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,13 @@ public MetadataManager(string metadataFile)
LoadFile();
}

public Metadata? FindMetadataByHash(byte[] hash)
public Metadata? FindMetadataByHash(ReadOnlySpan<char> hash)
{
Span<byte> tempHash = stackalloc byte[20];

lock (m_Lock)
{
foreach (var metadata in m_Metadata)
{
var writtenBytes = HashingHelper.WriteHash(tempHash, metadata.OriginalAssetBundleHash);
if (tempHash[..writtenBytes].SequenceEqual(hash))
if (hash.SequenceEqual(metadata.OriginalAssetBundleHash))
{
return metadata;
}
Expand Down
2 changes: 1 addition & 1 deletion BepInExFasterLoadAssetBundles/Patcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ private static void LoadAssetBundleFromFileFast(ref string path)

try
{
using var bundleFileStream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
using var bundleFileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None, 16 * 1024 * 1024, FileOptions.SequentialScan);

if (HandleStreamBundle(bundleFileStream, out var newPath))
{
Expand Down

0 comments on commit 48813da

Please sign in to comment.