RosettaCodeData/Task/MD5-Implementation/D/md5-implementation.d

248 lines
7.0 KiB
D

import std.bitmanip, core.stdc.string, std.conv, std.math, std.array,
std.string;
version (D_InlineAsm_X86) {} else {
static assert(false, "For X86 machine only.");
}
// CTFE construction of transform expressions.
uint S(in uint n) pure nothrow @safe @nogc {
static immutable aux = [7u, 12, 17, 22, 5, 9, 14, 20, 4, 11,
16, 23, 6, 10, 15, 21];
return aux[(n / 16) * 4 + (n % 4)];
}
uint K(in uint n) pure nothrow @safe @nogc {
uint r = 0;
if (n <= 15)
r = n;
else if (n <= 31)
r = 5 * n + 1;
else if (n <= 47)
r = 3 * n + 5;
else
r = 7 * n;
return r % 16;
}
uint T(in uint n) pure nothrow @nogc {
return cast(uint)(abs(sin(n + 1.0L)) * (2UL ^^ 32));
}
string[] ABCD(in int n) pure nothrow {
enum abcd = ["EAX", "EBX", "ECX", "EDX"];
return abcd[(64 - n) % 4 .. 4] ~ abcd[0 .. (64 - n) % 4];
}
string SUB(in int n, in string s) pure nothrow {
return s
.replace("ax", n.ABCD[0])
.replace("bx", n.ABCD[1])
.replace("cx", n.ABCD[2])
.replace("dx", n.ABCD[3]);
}
// FF, GG, HH & II expressions part 1 (F, G, H, I).
string fghi1(in int n) pure nothrow @nogc {
switch (n / 16) {
case 0:
// (bb & cc) | (~bb & dd)
return q{
mov ESI, bx;
mov EDI, bx;
not ESI;
and EDI, cx;
and ESI, dx;
or EDI, ESI;
add ax, EDI;
};
case 1:
// (dd & bb) | (~dd & cc)
return q{
mov ESI, dx;
mov EDI, dx;
not ESI;
and EDI, bx;
and ESI, cx;
or EDI, ESI;
add ax, EDI;
};
case 2: // (bb ^ cc ^ dd)
return q{
mov EDI, bx;
xor EDI, cx;
xor EDI, dx;
add ax, EDI;
};
case 3: // (cc ^ (bb | ~dd))
return q{
mov EDI, dx;
not EDI;
or EDI, bx;
xor EDI, cx;
add ax, EDI;
};
default:
assert(false);
}
}
// FF, GG, HH & II expressions part 2.
string fghi2(in int n) pure nothrow {
return q{
add ax, [EBP + 4 * KK];
add ax, TT;
} ~ n.fghi1;
}
// FF, GG, HH & II expressions prepended with previous parts
// & subsitute ABCD.
string FGHI(in int n) pure nothrow {
// aa = ((aa << SS)|( aa >>> (32 - SS))) + bb = ROL(aa, SS) + bb
return SUB(n, n.fghi2 ~ q{
rol ax, SS;
add ax, bx;
});
}
string genExpr(uint n) pure nothrow {
return FGHI(n)
.replace("SS", n.S.text)
.replace("KK", n.K.text)
.replace("TT", "0x" ~ to!string(n.T, 16));
}
string genTransformCode(int n) pure nothrow {
return (n < 63) ? n.genExpr ~ genTransformCode(n + 1) : n.genExpr;
}
enum string coreZMD5 = 0.genTransformCode;
struct ZMD5 {
uint[4] state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476];
ulong count;
ubyte[64] buffer;
ubyte[64] padding = [
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0];
private void transform(ubyte* block) pure nothrow @nogc {
uint[16] x = void;
version (BigEndian) {
foreach (immutable i; 0 .. 16)
x[i] = littleEndianToNative!uint(*cast(ubyte[4]*)&block[i * 4]);
} else {
(cast(ubyte*)x.ptr)[0 .. 64] = block[0 .. 64];
}
auto pState = state.ptr;
auto pBuffer = x.ptr;
asm pure nothrow @nogc {
mov ESI, pState[EBP];
mov EDX, [ESI + 3 * 4];
mov ECX, [ESI + 2 * 4];
mov EBX, [ESI + 1 * 4];
mov EAX, [ESI + 0 * 4];
push EBP;
push ESI;
mov EBP, pBuffer[EBP];
}
mixin("asm pure nothrow @nogc { " ~ coreZMD5 ~ "}");
asm pure nothrow @nogc {
pop ESI;
pop EBP;
add [ESI + 0 * 4], EAX;
add [ESI + 1 * 4], EBX;
add [ESI + 2 * 4], ECX;
add [ESI + 3 * 4], EDX;
}
x[] = 0;
}
void update(in void[] input) pure nothrow @nogc {
auto inputLen = input.length;
uint index = (count >> 3) & 0b11_1111U;
count += inputLen * 8;
immutable uint partLen = 64 - index;
uint i;
if (inputLen >= partLen) {
memcpy(&buffer[index], input.ptr, partLen);
transform(buffer.ptr);
for (i = partLen; i + 63 < inputLen; i += 64)
transform((cast(ubyte[])input)[i .. i + 64].ptr);
index = 0;
} else
i = 0;
if (inputLen - i)
memcpy(&buffer[index], &input[i], inputLen - i);
}
void finish(ref ubyte[16] digest) pure nothrow @nogc {
ubyte[8] bits = void;
bits[0 .. 8] = nativeToLittleEndian(count)[];
immutable uint index = (count >> 3) & 0b11_1111U;
immutable uint padLen = (index < 56) ?
(56 - index) : (120 - index);
update(padding[0 .. padLen]);
update(bits);
digest[0 .. 4] = nativeToLittleEndian(state[0])[];
digest[4 .. 8] = nativeToLittleEndian(state[1])[];
digest[8 .. 12] = nativeToLittleEndian(state[2])[];
digest[12 .. 16] = nativeToLittleEndian(state[3])[];
// Zeroize sensitive information.
memset(&this, 0, ZMD5.sizeof);
}
}
string getDigestString(in void[][] data...) pure {
ZMD5 ctx;
foreach (datum; data)
ctx.update(datum);
ubyte[16] digest;
ctx.finish(digest);
return format("%-(%02X%)", digest);
}
void main() { // Benchmark code --------------
import std.stdio, std.datetime, std.digest.md;
writefln(`md5 digest("") = %-(%02X%)`, "".md5Of);
writefln(`zmd5 digest("") = %s`, "".getDigestString);
enum megaBytes = 512;
writefln("\nTest performance / message size %dMBytes:", megaBytes);
auto data = new float[megaBytes * 0x40000 + 13];
StopWatch sw;
sw.start;
immutable d1 = data.md5Of;
sw.stop;
immutable time1 = sw.peek.msecs / 1000.0;
writefln("digest(data) = %-(%02X%)", d1);
writefln("std.md5: %8.2f M/sec ( %8.2f secs)",
megaBytes / time1, time1);
sw.reset;
sw.start;
immutable d2 = data.getDigestString;
sw.stop;
immutable time2 = sw.peek.msecs / 1000.0;
writefln("digest(data) = %s", d2);
writefln("zmd5 : %8.2f M/sec ( %8.2f secs)",
megaBytes / time2, time2);
}