From 772e87739c848a10a014d3118ff14bd0ca48b2d0 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 2 Oct 2018 08:55:29 +0200 Subject: [PATCH] Replace memory.op.cmp with optimized equal version Deprecate cmp. Fix #68. --- arch/build.ninja | 4 +- arch/x64/linux/memory/{cmp.S => equal.S} | 32 ++---- source/tanya/algorithm/comparison.d | 6 +- source/tanya/format.d | 18 +-- source/tanya/memory/op.d | 136 +++++++++++++---------- 5 files changed, 105 insertions(+), 91 deletions(-) rename arch/x64/linux/memory/{cmp.S => equal.S} (62%) diff --git a/arch/build.ninja b/arch/build.ninja index 824efa4..cf05696 100644 --- a/arch/build.ninja +++ b/arch/build.ninja @@ -6,9 +6,9 @@ rule archive build abs.o: gas x64/linux/math/abs.S build log.o: gas x64/linux/math/log.S -build cmp.o: gas x64/linux/memory/cmp.S +build equal.o: gas x64/linux/memory/equal.S build fill.o: gas x64/linux/memory/fill.S build copy.o: gas x64/linux/memory/copy.S build syscall.o: gas x64/linux/syscall.S -build tanya.a: archive syscall.o copy.o fill.o cmp.o log.o abs.o +build tanya.a: archive syscall.o copy.o fill.o equal.o log.o abs.o diff --git a/arch/x64/linux/memory/cmp.S b/arch/x64/linux/memory/equal.S similarity index 62% rename from arch/x64/linux/memory/cmp.S rename to arch/x64/linux/memory/equal.S index bd9f02e..37a906a 100644 --- a/arch/x64/linux/memory/cmp.S +++ b/arch/x64/linux/memory/equal.S @@ -1,20 +1,19 @@ .text /* - * cmpMemory. + * equalMemory. * * rdi - r1 length * rsi - r1 data. * rdx - r2 length. * rcx - r2 data. */ - .globl _D5tanya6memory2op9cmpMemoryFNaNbNixAvxQdZi - .type _D5tanya6memory2op9cmpMemoryFNaNbNixAvxQdZi, @function -_D5tanya6memory2op9cmpMemoryFNaNbNixAvxQdZi: + .globl _D5tanya6memory2op11equalMemoryFNaNbNixAvxQdZb + .type _D5tanya6memory2op11equalMemoryFNaNbNixAvxQdZb, @function +_D5tanya6memory2op11equalMemoryFNaNbNixAvxQdZb: // Compare the lengths cmp %rdx, %rdi - jl less - jg greater + jne not_equal mov %rcx, %rdi @@ -26,8 +25,7 @@ _D5tanya6memory2op9cmpMemoryFNaNbNixAvxQdZi: naligned: cmpsb - jl less - jg greater + jne not_equal dec %rdx test $0x07, %edi @@ -38,8 +36,7 @@ _D5tanya6memory2op9cmpMemoryFNaNbNixAvxQdZi: shr $0x03, %rcx repe cmpsq - jl less - jg greater + jne not_equal and $0x07, %edx jz equal @@ -49,19 +46,14 @@ _D5tanya6memory2op9cmpMemoryFNaNbNixAvxQdZi: cmp $0x0, %rcx repe cmpsb - jl less - jg greater + jne not_equal equal: + mov $0x01, %rax // Return 1 + jmp end + + not_equal: xor %rax, %rax // Return 0 - jmp end - - greater: - mov $0x01, %rax - jmp end - - less: - mov $-0x01, %rax end: ret diff --git a/source/tanya/algorithm/comparison.d b/source/tanya/algorithm/comparison.d index 88f6860..904f045 100644 --- a/source/tanya/algorithm/comparison.d +++ b/source/tanya/algorithm/comparison.d @@ -15,8 +15,8 @@ module tanya.algorithm.comparison; import tanya.algorithm.mutation; -import tanya.math : isNaN; -import tanya.memory.op; +import tanya.math; +static import tanya.memory.op; import tanya.meta.metafunction; import tanya.meta.trait; import tanya.meta.transform; @@ -296,7 +296,7 @@ if (allSatisfy!(isInputRange, R1, R2) && is(R1 == R2) && __traits(isPOD, ElementType!R1)) { - return cmp(r1, r2) == 0; + return tanya.memory.op.equal(r1, r2); } else { diff --git a/source/tanya/format.d b/source/tanya/format.d index 2a30822..fa46945 100644 --- a/source/tanya/format.d +++ b/source/tanya/format.d @@ -43,7 +43,7 @@ import tanya.algorithm.comparison; import tanya.container.string; import tanya.encoding.ascii; import tanya.math; -import tanya.memory.op; +static import tanya.memory.op; import tanya.meta.metafunction; import tanya.meta.trait; import tanya.meta.transform; @@ -1351,7 +1351,7 @@ do intSlice.popBack(); } const begin = buffer.length - intSlice.length; - copy(intSlice, buffer[begin .. $]); + tanya.memory.op.copy(intSlice, buffer[begin .. $]); exponent = cast(int) (intSlice.length + mismatch); @@ -1388,7 +1388,7 @@ do char[21] intBuffer; auto intSlice = integral2String(decimal, intBuffer); - copy(intSlice, buffer); + tanya.memory.op.copy(intSlice, buffer); exponent = cast(int) intSlice.length; size_t position = exponent; @@ -1903,7 +1903,7 @@ private char[] errol3(double value, if (pathologies[middle].representation == bits.integral) { exponent = pathologies[middle].exponent; - copy(pathologies[middle].digits, buffer); + tanya.memory.op.copy(pathologies[middle].digits, buffer); return buffer[0 .. pathologies[middle].digits.length]; } else if (pathologies[middle].representation < bits.integral) @@ -2054,7 +2054,7 @@ if (isFloatingPoint!T) { length = precision + 1; } - realString[1 .. length].copy(bufferSlice); + tanya.memory.op.copy(realString[1 .. length], bufferSlice); bufferSlice.popFrontExactly(length - 1); // Dump the exponent. @@ -2116,7 +2116,7 @@ if (isFloatingPoint!T) n = precision; } - fill!'0'(bufferSlice[0 .. n]); + tanya.memory.op.fill!'0'(bufferSlice[0 .. n]); bufferSlice.popFrontExactly(n); if ((length + n) > precision) @@ -2124,7 +2124,7 @@ if (isFloatingPoint!T) length = precision - n; } - realString[0 .. length].copy(bufferSlice); + tanya.memory.op.copy(realString[0 .. length], bufferSlice); bufferSlice.popFrontExactly(length); } else if (cast(uint) decimalPoint >= length) @@ -2142,7 +2142,7 @@ if (isFloatingPoint!T) { n = decimalPoint - n; - fill!'0'(bufferSlice[0 .. n]); + tanya.memory.op.fill!'0'(bufferSlice[0 .. n]); bufferSlice.popFrontExactly(n); } if (precision != 0) @@ -2173,7 +2173,7 @@ if (isFloatingPoint!T) length = precision + decimalPoint; } - realString[n .. length].copy(bufferSlice); + tanya.memory.op.copy(realString[n .. length], bufferSlice); bufferSlice.popFrontExactly(length - n); } } diff --git a/source/tanya/memory/op.d b/source/tanya/memory/op.d index 8e56c80..b411ed7 100644 --- a/source/tanya/memory/op.d +++ b/source/tanya/memory/op.d @@ -24,7 +24,7 @@ version (TanyaNative) extern private void moveMemory(const void[], void[]) pure nothrow @system @nogc; - extern private int cmpMemory(const void[], const void[]) + extern private bool equalMemory(const void[], const void[]) pure nothrow @system @nogc; } else @@ -43,7 +43,7 @@ version (TanyaNative) @nogc nothrow pure @safe unittest { - assert(cmp(null, null) == 0); + assert(equal(null, null)); } } @@ -91,7 +91,7 @@ do ubyte[9] source = [1, 2, 3, 4, 5, 6, 7, 8, 9]; ubyte[9] target; source.copy(target); - assert(cmp(source, target) == 0); + assert(equal(source, target)); } @nogc nothrow pure @safe unittest @@ -110,7 +110,7 @@ do ubyte[8] source = [1, 2, 3, 4, 5, 6, 7, 8]; ubyte[8] target; source.copy(target); - assert(cmp(source, target) == 0); + assert(equal(source, target)); } } @@ -212,7 +212,7 @@ do ubyte[6] expected = [ 'a', 'a', 'a', 'a', 'b', 'b' ]; copyBackward(mem[0 .. 4], mem[2 .. $]); - assert(cmp(expected, mem) == 0); + assert(equal(expected, mem)); } @nogc nothrow pure @safe unittest @@ -221,7 +221,7 @@ do ubyte[9] r2; copyBackward(r1, r2); - assert(cmp(r1, r2) == 0); + assert(equal(r1, r2)); } /** @@ -241,6 +241,7 @@ do * negative integer if $(D_INLINECODE r2 > r1), * `0` if $(D_INLINECODE r1 == r2). */ +deprecated("Use tanya.memory.op.equal() or tanya.algorithm.comparison.compare() instead") int cmp(const void[] r1, const void[] r2) @nogc nothrow pure @trusted in { @@ -249,48 +250,13 @@ in } do { - version (TanyaNative) + import core.stdc.string : memcmp; + + if (r1.length > r2.length) { - return cmpMemory(r1, r2); + return 1; } - else - { - if (r1.length > r2.length) - { - return 1; - } - return r1.length < r2.length ? -1 : memcmp(r1.ptr, r2.ptr, r1.length); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - ubyte[4] r1 = [ 'a', 'b', 'c', 'd' ]; - ubyte[3] r2 = [ 'c', 'a', 'b' ]; - - assert(cmp(r1[0 .. 3], r2[]) < 0); - assert(cmp(r2[], r1[0 .. 3]) > 0); - - assert(cmp(r1, r2) > 0); - assert(cmp(r2, r1) < 0); -} - -@nogc nothrow pure @safe unittest -{ - ubyte[16] r1 = [ - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', - ]; - ubyte[16] r2 = [ - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', - ]; - - assert(cmp(r1, r2) == 0); - assert(cmp(r1[1 .. $], r2[1 .. $]) == 0); - assert(cmp(r1[0 .. $ - 1], r2[0 .. $ - 1]) == 0); - assert(cmp(r1[0 .. 8], r2[0 .. 8]) == 0); + return r1.length < r2.length ? -1 : memcmp(r1.ptr, r2.ptr, r1.length); } /** @@ -362,13 +328,13 @@ do { const ubyte[9] haystack = ['a', 'b', 'c', 'd', 'e', 'f', 'b', 'g', 'h']; - assert(cmp(find(haystack, 'a'), haystack[]) == 0); - assert(cmp(find(haystack, 'b'), haystack[1 .. $]) == 0); - assert(cmp(find(haystack, 'c'), haystack[2 .. $]) == 0); - assert(cmp(find(haystack, 'd'), haystack[3 .. $]) == 0); - assert(cmp(find(haystack, 'e'), haystack[4 .. $]) == 0); - assert(cmp(find(haystack, 'f'), haystack[5 .. $]) == 0); - assert(cmp(find(haystack, 'h'), haystack[8 .. $]) == 0); + assert(equal(find(haystack, 'a'), haystack[])); + assert(equal(find(haystack, 'b'), haystack[1 .. $])); + assert(equal(find(haystack, 'c'), haystack[2 .. $])); + assert(equal(find(haystack, 'd'), haystack[3 .. $])); + assert(equal(find(haystack, 'e'), haystack[4 .. $])); + assert(equal(find(haystack, 'f'), haystack[5 .. $])); + assert(equal(find(haystack, 'h'), haystack[8 .. $])); assert(find(haystack, 'i').length == 0); assert(find(null, 'a').length == 0); @@ -441,10 +407,66 @@ do /// @nogc nothrow pure @safe unittest { - assert(cmp(findNullTerminated("abcdef\0gh"), "abcdef") == 0); - assert(cmp(findNullTerminated("\0garbage"), "") == 0); - assert(cmp(findNullTerminated("\0"), "") == 0); - assert(cmp(findNullTerminated("cstring\0"), "cstring") == 0); + assert(equal(findNullTerminated("abcdef\0gh"), "abcdef")); + assert(equal(findNullTerminated("\0garbage"), "")); + assert(equal(findNullTerminated("\0"), "")); + assert(equal(findNullTerminated("cstring\0"), "cstring")); assert(findNullTerminated(null) is null); assert(findNullTerminated("abcdef") is null); } + +/** + * Compares two memory areas $(D_PARAM r1) and $(D_PARAM r2) for equality. + * + * Params: + * haystack = First memory block. + * needle = First memory block. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM r1) and $(D_PARAM r2) are equal, + * $(D_KEYWORD false) otherwise. + */ +bool equal(const void[] r1, const void[] r2) @nogc nothrow pure @trusted +in +{ + assert(r1.length == 0 || r1.ptr !is null); + assert(r2.length == 0 || r2.ptr !is null); +} +do +{ + version (TanyaNative) + { + return equalMemory(r1, r2); + } + else + { + return r1.length == r2.length + && memcmp(r1.ptr, r2.ptr, r1.length) == 0; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(equal("asdf", "asdf")); + assert(!equal("asd", "asdf")); + assert(!equal("asdf", "asd")); + assert(!equal("asdf", "qwer")); +} + +// Compares unanligned memory +@nogc nothrow pure @safe unittest +{ + ubyte[16] r1 = [ + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + ]; + ubyte[16] r2 = [ + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + ]; + + assert(equal(r1, r2)); + assert(equal(r1[1 .. $], r2[1 .. $])); + assert(equal(r1[0 .. $ - 1], r2[0 .. $ - 1])); + assert(equal(r1[0 .. 8], r2[0 .. 8])); +}