diff --git a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.cs b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.cs index 2438e56c..95659603 100644 --- a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.cs +++ b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.cs @@ -672,7 +672,7 @@ internal static int SetAndGetTextureID(byte[] textureBytes) { int highestID = 0; foreach (var tex in TextureDictionary) - if (tex.Value.Data.SequenceEqual(textureBytes)) + if (Utility.SequenceEqualFast(tex.Value.Data,textureBytes)) return tex.Key; else if (tex.Key > highestID) highestID = tex.Key; diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.cs b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.cs index 943c5cf6..b8d4bc8b 100644 --- a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.cs +++ b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.cs @@ -855,7 +855,7 @@ private int SetAndGetTextureID(byte[] textureBytes) { int highestID = 0; foreach (var tex in TextureDictionary) - if (tex.Value.Data.SequenceEqual(textureBytes)) + if (Utility.SequenceEqualFast(tex.Value.Data,textureBytes)) return tex.Key; else if (tex.Key > highestID) highestID = tex.Key; diff --git a/src/Shared.TextureContainer/Shared.TextureContainerManager.cs b/src/Shared.TextureContainer/Shared.TextureContainerManager.cs index b05d9dae..58b10106 100644 --- a/src/Shared.TextureContainer/Shared.TextureContainerManager.cs +++ b/src/Shared.TextureContainer/Shared.TextureContainerManager.cs @@ -41,20 +41,18 @@ public TextureKey(byte[] data, TextureFormat format = TextureFormat.ARGB32, bool public override bool Equals(object obj) { - if (obj is TextureKey) + if (obj is TextureKey other) { - var other = (TextureKey)obj; - - return - other.hash == hash && + return other.hash == hash && other.format == format && other.mipmaps == mipmaps && - other.data.SequenceEqual(data); + Utility.SequenceEqualFast(other.data, data); } return false; } + public override int GetHashCode() { return hash; diff --git a/src/Shared/Shared.Utility.cs b/src/Shared/Shared.Utility.cs new file mode 100644 index 00000000..6fdbe4de --- /dev/null +++ b/src/Shared/Shared.Utility.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace KK_Plugins +{ + internal class Utility + { + + /// + /// Compares two byte arrays for equality in a high-performance manner using unsafe code. + /// + /// The first byte array to compare. + /// The second byte array to compare. + /// True if the byte arrays are equal, false otherwise. + static public bool SequenceEqualFast(byte[] a, byte[] b) + { + // Check if both references are the same, if so, return true. + if (System.Object.ReferenceEquals(a, b)) + return true; + + if (a == null || b == null) + return false; + + int bytes = a.Length; + + if (bytes != b.Length) + return false; + + if (bytes <= 0) + return true; + + unsafe + { + // Fix the memory locations of the arrays to prevent the garbage collector from moving them. + fixed (byte* pA = &a[0]) + fixed (byte* pB = &b[0]) + { + int offset = 0; + + // If both pointers are 8-byte aligned, use 64-bit comparison. + if (((int)pA & 7) == 0 && ((int)pB & 7) == 0 && bytes >= 32) + { + offset = bytes & ~31; // Round down to the nearest multiple of 32. + + byte* pA_ = pA; + byte* pB_ = pB; + byte* pALast = pA + offset; + + do + { + if (*(ulong*)pA_ != *(ulong*)pB_) + goto NotEquals; + + pA_ += 8; + pB_ += 8; + + if (*(ulong*)pA_ != *(ulong*)pB_) + goto NotEquals; + + pA_ += 8; + pB_ += 8; + + if (*(ulong*)pA_ != *(ulong*)pB_) + goto NotEquals; + + pA_ += 8; + pB_ += 8; + + if (*(ulong*)pA_ != *(ulong*)pB_) + goto NotEquals; + + pA_ += 8; + pB_ += 8; + } + while (pA_ != pALast); + } + // If both pointers are 4-byte aligned, use 32-bit comparison. + else if (((int)pA & 3) == 0 && ((int)pB & 3) == 0 && bytes >= 16) + { + offset = bytes & ~15; // Round down to the nearest multiple of 16. + + byte* pA_ = pA; + byte* pB_ = pB; + byte* pALast = pA + offset; + + do + { + if (*(uint*)pA_ != *(uint*)pB_) + goto NotEquals; + + pA_ += 4; + pB_ += 4; + + if (*(uint*)pA_ != *(uint*)pB_) + goto NotEquals; + + pA_ += 4; + pB_ += 4; + + if (*(uint*)pA_ != *(uint*)pB_) + goto NotEquals; + + pA_ += 4; + pB_ += 4; + + if (*(uint*)pA_ != *(uint*)pB_) + goto NotEquals; + + pA_ += 4; + pB_ += 4; + } + while (pA_ != pALast); + } + + // Compare remaining bytes one by one. + for (int i = offset; i < bytes; ++i) + if (pA[i] != pB[i]) + goto NotEquals; + } + } + + return true; + +NotEquals: + // Return false indicating arrays are not equal. + // Note: Using a return statement in the loop can potentially degrade performance due to the generated binary code, + return false; + } + } +} diff --git a/src/Shared/Shared.projitems b/src/Shared/Shared.projitems index 06660f8b..806939b0 100644 --- a/src/Shared/Shared.projitems +++ b/src/Shared/Shared.projitems @@ -15,5 +15,6 @@ + \ No newline at end of file