diff --git a/src/core/internal/array/equality.d b/src/core/internal/array/equality.d index 1dd7de1e6a..b12e2f24cc 100644 --- a/src/core/internal/array/equality.d +++ b/src/core/internal/array/equality.d @@ -14,7 +14,41 @@ module core.internal.array.equality; // * dynamic arrays, // * (most) arrays of different (unqualified) element types, and // * arrays of structs with custom opEquals. + + // The scalar-only overload takes advantage of known properties of scalars to + // reduce template instantiation. This is expected to be the most common case. +bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs) +@nogc nothrow pure @trusted +if (__traits(isScalar, T1) && __traits(isScalar, T2)) +{ + if (lhs.length != rhs.length) + return false; + + static if (T1.sizeof == T2.sizeof + // Signedness needs to match for types that promote to int. + // (Actually it would be okay to memcmp bool[] and byte[] but that is + // probably too uncommon to be worth checking for.) + && (T1.sizeof >= 4 || __traits(isUnsigned, T1) == __traits(isUnsigned, T2)) + && !__traits(isFloating, T1) && !__traits(isFloating, T2)) + { + if (!__ctfe) + { + // This would improperly allow equality of integers and pointers + // but the CTFE branch will stop this function from compiling then. + import core.stdc.string : memcmp; + return lhs.length == 0 || + 0 == memcmp(cast(const void*) lhs.ptr, cast(const void*) rhs.ptr, lhs.length * T1.sizeof); + } + } + + foreach (const i; 0 .. lhs.length) + if (lhs.ptr[i] != rhs.ptr[i]) + return false; + return true; +} + bool __equals(T1, T2)(scope T1[] lhs, scope T2[] rhs) +if (!__traits(isScalar, T1) || !__traits(isScalar, T2)) { if (lhs.length != rhs.length) return false;