--- /dev/null
+package core.hash.md5
+
+use core {io, memory, conv}
+use core.intrinsics.wasm {rotl_i32}
+
+
+digest :: #match #local -> MD5_Digest {}
+
+#overload
+digest :: (x: str) -> MD5_Digest {
+ string_reader, string_stream := io.reader_from_string(x);
+ defer cfree(string_stream);
+ defer delete(&string_reader);
+
+ return digest(&string_reader);
+}
+
+#overload
+digest :: (r: &io.Reader) -> MD5_Digest {
+ dig := MD5_Digest.make();
+
+ remaining_bytes_to_digest := 0;
+
+ bytes_to_digest: [64] u8;
+ while !r->is_empty() {
+ byte_count, err := r->read_bytes(bytes_to_digest);
+
+ // Exit early to handle the tail case.
+ // This is subject to read_pending errors if the reader
+ // does not have enough bytes and returns read_pending.
+ if byte_count < 64 {
+ remaining_bytes_to_digest = byte_count;
+ break;
+ }
+
+ do_cycle(&dig, bytes_to_digest);
+ }
+
+ dig->finish(bytes_to_digest[0..remaining_bytes_to_digest]);
+ return dig;
+}
+
+
+MD5_Digest :: struct {
+ a, b, c, d: u32;
+ bytes_digested: u64;
+}
+
+#inject MD5_Digest {
+ make :: () => MD5_Digest.{
+ a = 0x67452301,
+ b = 0xefcdab89,
+ c = 0x98badcfe,
+ d = 0x10325476,
+ bytes_digested = 0,
+ }
+
+ reset :: (self: &#Self) {
+ *self = MD5_Digest.make();
+ }
+
+ finish :: (self: &#Self, tail: [] u8) {
+ assert(tail.count < 64, "Tail too long");
+
+ bytes_to_digest: [64] u8;
+ memory.copy(~~bytes_to_digest, tail.data, tail.count);
+ bytes_to_digest[tail.count] = 0x80;
+
+ if tail.count >= 56 {
+ do_cycle(self, bytes_to_digest, accumulate=false);
+ memory.set(~~bytes_to_digest, 0, 64);
+
+ } else {
+ self.bytes_digested += ~~tail.count;
+ }
+
+ *cast(&u64, &bytes_to_digest[56]) = self.bytes_digested * 8;
+
+ do_cycle(self, bytes_to_digest, accumulate=false);
+ }
+
+ #doc "Returns a temporary byte array of the hash."
+ as_str :: (self: #Self) -> [] u8 {
+ result := make_temp([] u8, 16);
+ for i: 0 .. 4 do result[i] = ~~((self.a & (0xff << shift(i))) >> shift(i));
+ for i: 4 .. 8 do result[i] = ~~((self.b & (0xff << shift(i))) >> shift(i));
+ for i: 8 .. 12 do result[i] = ~~((self.c & (0xff << shift(i))) >> shift(i));
+ for i: 12 .. 16 do result[i] = ~~((self.d & (0xff << shift(i))) >> shift(i));
+ return result;
+ }
+
+ #doc "Returns a temporary string of the hash."
+ as_hex_str :: (self: #Self) -> str {
+ result := make_temp([..] u8, 32);
+ for i: 0 .. 4 do conv.format(&result, "{w2b16}", (self.a & (0xff << shift(i)) >> shift(i)));
+ for i: 4 .. 8 do conv.format(&result, "{w2b16}", (self.b & (0xff << shift(i)) >> shift(i)));
+ for i: 8 .. 12 do conv.format(&result, "{w2b16}", (self.c & (0xff << shift(i)) >> shift(i)));
+ for i: 12 .. 16 do conv.format(&result, "{w2b16}", (self.d & (0xff << shift(i)) >> shift(i)));
+ return result;
+ }
+}
+
+#local
+shift :: macro (x: u32) => ((x & 3) << 3);
+
+#local
+do_cycle :: (digest: &MD5_Digest, bytes: [64] u8, accumulate := true) {
+ // This assumes a little-endian implementation, but that
+ // is assumed by WebAssembly.
+ M: [&] u32 = ~~bytes;
+
+ a := digest.a;
+ b := digest.b;
+ c := digest.c;
+ d := digest.d;
+
+ for i: 0 .. 64 {
+ F, g: u32;
+ if 0 <= i && i <= 15 {
+ F = (b & c) | (~b & d);
+ g = i;
+ }
+ elseif 16 <= i && i <= 31 {
+ F = (d & b) | (~d & c);
+ g = (5 * i + 1) % 16;
+ }
+ elseif 32 <= i && i <= 47 {
+ F = b ^ (c ^ d);
+ g = (3 * i + 5) % 16;
+ }
+ elseif 48 <= i && i <= 63 {
+ F = c ^ (b | ~d);
+ g = (7 * i) % 16;
+ }
+
+ F += a + K[i] + M[g];
+ a = d;
+ d = c;
+ c = b;
+ b += rotl_i32(F, shifts[i]);
+ }
+
+ digest.a += a;
+ digest.b += b;
+ digest.c += c;
+ digest.d += d;
+
+ if accumulate {
+ digest.bytes_digested += 64;
+ }
+}
+
+
+#local {
+ shifts := u32.[
+ 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
+ 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
+ 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
+ 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
+ ];
+
+ K := u32.[
+ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+ 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+ 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+ 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+ 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+ 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+ 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+ 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+ 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
+ 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+ 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+ 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
+ ]
+}
+
+
VAL(instr->r).ctype = func( VAL(instr->a).ctype, VAL(instr->b).ctype ); \
VAL(instr->r).type = t;
-OVMI_INSTR_EXEC(rotl_i32) { OVM_OP(OVM_TYPE_I32, __rold, u32); NEXT_OP; }
-OVMI_INSTR_EXEC(rotl_i64) { OVM_OP(OVM_TYPE_I64, __rolq, u64); NEXT_OP; }
-OVMI_INSTR_EXEC(rotr_i32) { OVM_OP(OVM_TYPE_I32, __rord, u32); NEXT_OP; }
-OVMI_INSTR_EXEC(rotr_i64) { OVM_OP(OVM_TYPE_I64, __rorq, u64); NEXT_OP; }
+#ifndef ROTATION_FUNCTIONS
+#define ROTATION_FUNCTIONS
+
+static inline u32 rotl32(u32 value, u32 count) {
+ const unsigned int mask = 0xFF;
+ count &= mask;
+ return (value << count) | (value >> (-count & mask));
+}
+
+static inline u64 rotl64(u64 value, u32 count) {
+ const unsigned int mask = 0xFF;
+ count &= mask;
+ return (value << count) | (value >> (-count & mask));
+}
+
+static inline u32 rotr32(u32 value, u32 count) {
+ const unsigned int mask = 0xFF;
+ count &= mask;
+ return (value >> count) | (value << (-count & mask));
+}
+
+static inline u64 rotr64(u64 value, u32 count) {
+ const unsigned int mask = 0xFF;
+ count &= mask;
+ return (value >> count) | (value << (-count & mask));
+}
+
+#endif
+
+OVMI_INSTR_EXEC(rotl_i32) { OVM_OP(OVM_TYPE_I32, rotl32, u32); NEXT_OP; }
+OVMI_INSTR_EXEC(rotl_i64) { OVM_OP(OVM_TYPE_I64, rotl64, u64); NEXT_OP; }
+OVMI_INSTR_EXEC(rotr_i32) { OVM_OP(OVM_TYPE_I32, rotr32, u32); NEXT_OP; }
+OVMI_INSTR_EXEC(rotr_i64) { OVM_OP(OVM_TYPE_I64, rotr64, u64); NEXT_OP; }
OVM_OP_FLOAT_EXEC(min, bh_min)
OVM_OP_FLOAT_EXEC(max, bh_max)