///|
/// Incremental RFC 1321 state; only one partial block is retained.
struct Md5 {
h : Array[UInt32]
pending : FixedArray[Byte]
mut used : Int32
words : Array[UInt32]
mut length : Int64
}
///|
fn Md5::new() -> Md5 {
{
h: [0x67452301U, 0xefcdab89U, 0x98badcfeU, 0x10325476U],
pending: FixedArray::make(64, 0),
used: 0,
words: Array::make(16, 0U),
length: 0L,
}
}
///|
// Generated by scripts/faster-than-libflac-md5-unroll.py from the archived RFC1321 tables.
// Private block contract: callers invoke only with exactly 64 pending bytes;
// new/digest allocate exactly 16 scratch words and four state words.
fn Md5::block(self : Md5, bytes : FixedArray[Byte]) -> Unit {
let words = self.words
for i in 0..<16 {
let j = i * 4
words.unsafe_set(
i,
bytes.unsafe_get(j).to_uint() |
(bytes.unsafe_get(j + 1).to_uint() << 8) |
(bytes.unsafe_get(j + 2).to_uint() << 16) |
(bytes.unsafe_get(j + 3).to_uint() << 24),
)
}
let mut a = self.h.unsafe_get(0)
let mut b = self.h.unsafe_get(1)
let mut c = self.h.unsafe_get(2)
let mut d = self.h.unsafe_get(3)
// RFC1321 operation 1: round 1, word 0, rotate 7.
let t0 = a + ((b & c) | (b.lnot() & d)) + 0xd76aa478U + words.unsafe_get(0)
a = b + ((t0 << 7) | (t0 >> 25))
// RFC1321 operation 2: round 1, word 1, rotate 12.
let t1 = d + ((a & b) | (a.lnot() & c)) + 0xe8c7b756U + words.unsafe_get(1)
d = a + ((t1 << 12) | (t1 >> 20))
// RFC1321 operation 3: round 1, word 2, rotate 17.
let t2 = c + ((d & a) | (d.lnot() & b)) + 0x242070dbU + words.unsafe_get(2)
c = d + ((t2 << 17) | (t2 >> 15))
// RFC1321 operation 4: round 1, word 3, rotate 22.
let t3 = b + ((c & d) | (c.lnot() & a)) + 0xc1bdceeeU + words.unsafe_get(3)
b = c + ((t3 << 22) | (t3 >> 10))
// RFC1321 operation 5: round 1, word 4, rotate 7.
let t4 = a + ((b & c) | (b.lnot() & d)) + 0xf57c0fafU + words.unsafe_get(4)
a = b + ((t4 << 7) | (t4 >> 25))
// RFC1321 operation 6: round 1, word 5, rotate 12.
let t5 = d + ((a & b) | (a.lnot() & c)) + 0x4787c62aU + words.unsafe_get(5)
d = a + ((t5 << 12) | (t5 >> 20))
// RFC1321 operation 7: round 1, word 6, rotate 17.
let t6 = c + ((d & a) | (d.lnot() & b)) + 0xa8304613U + words.unsafe_get(6)
c = d + ((t6 << 17) | (t6 >> 15))
// RFC1321 operation 8: round 1, word 7, rotate 22.
let t7 = b + ((c & d) | (c.lnot() & a)) + 0xfd469501U + words.unsafe_get(7)
b = c + ((t7 << 22) | (t7 >> 10))
// RFC1321 operation 9: round 1, word 8, rotate 7.
let t8 = a + ((b & c) | (b.lnot() & d)) + 0x698098d8U + words.unsafe_get(8)
a = b + ((t8 << 7) | (t8 >> 25))
// RFC1321 operation 10: round 1, word 9, rotate 12.
let t9 = d + ((a & b) | (a.lnot() & c)) + 0x8b44f7afU + words.unsafe_get(9)
d = a + ((t9 << 12) | (t9 >> 20))
// RFC1321 operation 11: round 1, word 10, rotate 17.
let t10 = c + ((d & a) | (d.lnot() & b)) + 0xffff5bb1U + words.unsafe_get(10)
c = d + ((t10 << 17) | (t10 >> 15))
// RFC1321 operation 12: round 1, word 11, rotate 22.
let t11 = b + ((c & d) | (c.lnot() & a)) + 0x895cd7beU + words.unsafe_get(11)
b = c + ((t11 << 22) | (t11 >> 10))
// RFC1321 operation 13: round 1, word 12, rotate 7.
let t12 = a + ((b & c) | (b.lnot() & d)) + 0x6b901122U + words.unsafe_get(12)
a = b + ((t12 << 7) | (t12 >> 25))
// RFC1321 operation 14: round 1, word 13, rotate 12.
let t13 = d + ((a & b) | (a.lnot() & c)) + 0xfd987193U + words.unsafe_get(13)
d = a + ((t13 << 12) | (t13 >> 20))
// RFC1321 operation 15: round 1, word 14, rotate 17.
let t14 = c + ((d & a) | (d.lnot() & b)) + 0xa679438eU + words.unsafe_get(14)
c = d + ((t14 << 17) | (t14 >> 15))
// RFC1321 operation 16: round 1, word 15, rotate 22.
let t15 = b + ((c & d) | (c.lnot() & a)) + 0x49b40821U + words.unsafe_get(15)
b = c + ((t15 << 22) | (t15 >> 10))
// RFC1321 operation 17: round 2, word 1, rotate 5.
let t16 = a + ((d & b) | (d.lnot() & c)) + 0xf61e2562U + words.unsafe_get(1)
a = b + ((t16 << 5) | (t16 >> 27))
// RFC1321 operation 18: round 2, word 6, rotate 9.
let t17 = d + ((c & a) | (c.lnot() & b)) + 0xc040b340U + words.unsafe_get(6)
d = a + ((t17 << 9) | (t17 >> 23))
// RFC1321 operation 19: round 2, word 11, rotate 14.
let t18 = c + ((b & d) | (b.lnot() & a)) + 0x265e5a51U + words.unsafe_get(11)
c = d + ((t18 << 14) | (t18 >> 18))
// RFC1321 operation 20: round 2, word 0, rotate 20.
let t19 = b + ((a & c) | (a.lnot() & d)) + 0xe9b6c7aaU + words.unsafe_get(0)
b = c + ((t19 << 20) | (t19 >> 12))
// RFC1321 operation 21: round 2, word 5, rotate 5.
let t20 = a + ((d & b) | (d.lnot() & c)) + 0xd62f105dU + words.unsafe_get(5)
a = b + ((t20 << 5) | (t20 >> 27))
// RFC1321 operation 22: round 2, word 10, rotate 9.
let t21 = d + ((c & a) | (c.lnot() & b)) + 0x02441453U + words.unsafe_get(10)
d = a + ((t21 << 9) | (t21 >> 23))
// RFC1321 operation 23: round 2, word 15, rotate 14.
let t22 = c + ((b & d) | (b.lnot() & a)) + 0xd8a1e681U + words.unsafe_get(15)
c = d + ((t22 << 14) | (t22 >> 18))
// RFC1321 operation 24: round 2, word 4, rotate 20.
let t23 = b + ((a & c) | (a.lnot() & d)) + 0xe7d3fbc8U + words.unsafe_get(4)
b = c + ((t23 << 20) | (t23 >> 12))
// RFC1321 operation 25: round 2, word 9, rotate 5.
let t24 = a + ((d & b) | (d.lnot() & c)) + 0x21e1cde6U + words.unsafe_get(9)
a = b + ((t24 << 5) | (t24 >> 27))
// RFC1321 operation 26: round 2, word 14, rotate 9.
let t25 = d + ((c & a) | (c.lnot() & b)) + 0xc33707d6U + words.unsafe_get(14)
d = a + ((t25 << 9) | (t25 >> 23))
// RFC1321 operation 27: round 2, word 3, rotate 14.
let t26 = c + ((b & d) | (b.lnot() & a)) + 0xf4d50d87U + words.unsafe_get(3)
c = d + ((t26 << 14) | (t26 >> 18))
// RFC1321 operation 28: round 2, word 8, rotate 20.
let t27 = b + ((a & c) | (a.lnot() & d)) + 0x455a14edU + words.unsafe_get(8)
b = c + ((t27 << 20) | (t27 >> 12))
// RFC1321 operation 29: round 2, word 13, rotate 5.
let t28 = a + ((d & b) | (d.lnot() & c)) + 0xa9e3e905U + words.unsafe_get(13)
a = b + ((t28 << 5) | (t28 >> 27))
// RFC1321 operation 30: round 2, word 2, rotate 9.
let t29 = d + ((c & a) | (c.lnot() & b)) + 0xfcefa3f8U + words.unsafe_get(2)
d = a + ((t29 << 9) | (t29 >> 23))
// RFC1321 operation 31: round 2, word 7, rotate 14.
let t30 = c + ((b & d) | (b.lnot() & a)) + 0x676f02d9U + words.unsafe_get(7)
c = d + ((t30 << 14) | (t30 >> 18))
// RFC1321 operation 32: round 2, word 12, rotate 20.
let t31 = b + ((a & c) | (a.lnot() & d)) + 0x8d2a4c8aU + words.unsafe_get(12)
b = c + ((t31 << 20) | (t31 >> 12))
// RFC1321 operation 33: round 3, word 5, rotate 4.
let t32 = a + (b ^ c ^ d) + 0xfffa3942U + words.unsafe_get(5)
a = b + ((t32 << 4) | (t32 >> 28))
// RFC1321 operation 34: round 3, word 8, rotate 11.
let t33 = d + (a ^ b ^ c) + 0x8771f681U + words.unsafe_get(8)
d = a + ((t33 << 11) | (t33 >> 21))
// RFC1321 operation 35: round 3, word 11, rotate 16.
let t34 = c + (d ^ a ^ b) + 0x6d9d6122U + words.unsafe_get(11)
c = d + ((t34 << 16) | (t34 >> 16))
// RFC1321 operation 36: round 3, word 14, rotate 23.
let t35 = b + (c ^ d ^ a) + 0xfde5380cU + words.unsafe_get(14)
b = c + ((t35 << 23) | (t35 >> 9))
// RFC1321 operation 37: round 3, word 1, rotate 4.
let t36 = a + (b ^ c ^ d) + 0xa4beea44U + words.unsafe_get(1)
a = b + ((t36 << 4) | (t36 >> 28))
// RFC1321 operation 38: round 3, word 4, rotate 11.
let t37 = d + (a ^ b ^ c) + 0x4bdecfa9U + words.unsafe_get(4)
d = a + ((t37 << 11) | (t37 >> 21))
// RFC1321 operation 39: round 3, word 7, rotate 16.
let t38 = c + (d ^ a ^ b) + 0xf6bb4b60U + words.unsafe_get(7)
c = d + ((t38 << 16) | (t38 >> 16))
// RFC1321 operation 40: round 3, word 10, rotate 23.
let t39 = b + (c ^ d ^ a) + 0xbebfbc70U + words.unsafe_get(10)
b = c + ((t39 << 23) | (t39 >> 9))
// RFC1321 operation 41: round 3, word 13, rotate 4.
let t40 = a + (b ^ c ^ d) + 0x289b7ec6U + words.unsafe_get(13)
a = b + ((t40 << 4) | (t40 >> 28))
// RFC1321 operation 42: round 3, word 0, rotate 11.
let t41 = d + (a ^ b ^ c) + 0xeaa127faU + words.unsafe_get(0)
d = a + ((t41 << 11) | (t41 >> 21))
// RFC1321 operation 43: round 3, word 3, rotate 16.
let t42 = c + (d ^ a ^ b) + 0xd4ef3085U + words.unsafe_get(3)
c = d + ((t42 << 16) | (t42 >> 16))
// RFC1321 operation 44: round 3, word 6, rotate 23.
let t43 = b + (c ^ d ^ a) + 0x04881d05U + words.unsafe_get(6)
b = c + ((t43 << 23) | (t43 >> 9))
// RFC1321 operation 45: round 3, word 9, rotate 4.
let t44 = a + (b ^ c ^ d) + 0xd9d4d039U + words.unsafe_get(9)
a = b + ((t44 << 4) | (t44 >> 28))
// RFC1321 operation 46: round 3, word 12, rotate 11.
let t45 = d + (a ^ b ^ c) + 0xe6db99e5U + words.unsafe_get(12)
d = a + ((t45 << 11) | (t45 >> 21))
// RFC1321 operation 47: round 3, word 15, rotate 16.
let t46 = c + (d ^ a ^ b) + 0x1fa27cf8U + words.unsafe_get(15)
c = d + ((t46 << 16) | (t46 >> 16))
// RFC1321 operation 48: round 3, word 2, rotate 23.
let t47 = b + (c ^ d ^ a) + 0xc4ac5665U + words.unsafe_get(2)
b = c + ((t47 << 23) | (t47 >> 9))
// RFC1321 operation 49: round 4, word 0, rotate 6.
let t48 = a + (c ^ (b | d.lnot())) + 0xf4292244U + words.unsafe_get(0)
a = b + ((t48 << 6) | (t48 >> 26))
// RFC1321 operation 50: round 4, word 7, rotate 10.
let t49 = d + (b ^ (a | c.lnot())) + 0x432aff97U + words.unsafe_get(7)
d = a + ((t49 << 10) | (t49 >> 22))
// RFC1321 operation 51: round 4, word 14, rotate 15.
let t50 = c + (a ^ (d | b.lnot())) + 0xab9423a7U + words.unsafe_get(14)
c = d + ((t50 << 15) | (t50 >> 17))
// RFC1321 operation 52: round 4, word 5, rotate 21.
let t51 = b + (d ^ (c | a.lnot())) + 0xfc93a039U + words.unsafe_get(5)
b = c + ((t51 << 21) | (t51 >> 11))
// RFC1321 operation 53: round 4, word 12, rotate 6.
let t52 = a + (c ^ (b | d.lnot())) + 0x655b59c3U + words.unsafe_get(12)
a = b + ((t52 << 6) | (t52 >> 26))
// RFC1321 operation 54: round 4, word 3, rotate 10.
let t53 = d + (b ^ (a | c.lnot())) + 0x8f0ccc92U + words.unsafe_get(3)
d = a + ((t53 << 10) | (t53 >> 22))
// RFC1321 operation 55: round 4, word 10, rotate 15.
let t54 = c + (a ^ (d | b.lnot())) + 0xffeff47dU + words.unsafe_get(10)
c = d + ((t54 << 15) | (t54 >> 17))
// RFC1321 operation 56: round 4, word 1, rotate 21.
let t55 = b + (d ^ (c | a.lnot())) + 0x85845dd1U + words.unsafe_get(1)
b = c + ((t55 << 21) | (t55 >> 11))
// RFC1321 operation 57: round 4, word 8, rotate 6.
let t56 = a + (c ^ (b | d.lnot())) + 0x6fa87e4fU + words.unsafe_get(8)
a = b + ((t56 << 6) | (t56 >> 26))
// RFC1321 operation 58: round 4, word 15, rotate 10.
let t57 = d + (b ^ (a | c.lnot())) + 0xfe2ce6e0U + words.unsafe_get(15)
d = a + ((t57 << 10) | (t57 >> 22))
// RFC1321 operation 59: round 4, word 6, rotate 15.
let t58 = c + (a ^ (d | b.lnot())) + 0xa3014314U + words.unsafe_get(6)
c = d + ((t58 << 15) | (t58 >> 17))
// RFC1321 operation 60: round 4, word 13, rotate 21.
let t59 = b + (d ^ (c | a.lnot())) + 0x4e0811a1U + words.unsafe_get(13)
b = c + ((t59 << 21) | (t59 >> 11))
// RFC1321 operation 61: round 4, word 4, rotate 6.
let t60 = a + (c ^ (b | d.lnot())) + 0xf7537e82U + words.unsafe_get(4)
a = b + ((t60 << 6) | (t60 >> 26))
// RFC1321 operation 62: round 4, word 11, rotate 10.
let t61 = d + (b ^ (a | c.lnot())) + 0xbd3af235U + words.unsafe_get(11)
d = a + ((t61 << 10) | (t61 >> 22))
// RFC1321 operation 63: round 4, word 2, rotate 15.
let t62 = c + (a ^ (d | b.lnot())) + 0x2ad7d2bbU + words.unsafe_get(2)
c = d + ((t62 << 15) | (t62 >> 17))
// RFC1321 operation 64: round 4, word 9, rotate 21.
let t63 = b + (d ^ (c | a.lnot())) + 0xeb86d391U + words.unsafe_get(9)
b = c + ((t63 << 21) | (t63 >> 11))
self.h.unsafe_set(0, self.h.unsafe_get(0) + a)
self.h.unsafe_set(1, self.h.unsafe_get(1) + b)
self.h.unsafe_set(2, self.h.unsafe_get(2) + c)
self.h.unsafe_set(3, self.h.unsafe_get(3) + d)
}
///|
/// Bulk-copy input into one fixed 64-byte block. used is always in 0..63 on
/// entry/exit; only a fully initialized 64-byte block reaches compression.
fn Md5::update(self : Md5, bytes : Bytes) -> Unit {
self.length += bytes.length().to_int64()
let mut cursor = 0
while cursor < bytes.length() {
let room = 64 - self.used
let remaining = bytes.length() - cursor
let count = if remaining < room { remaining } else { room }
self.pending.blit_from_bytes(self.used, bytes, cursor, count)
self.used += count
cursor += count
if self.used == 64 {
self.block(self.pending)
self.used = 0
}
}
}
///|
/// Finalization operates on a copy, so callers may keep appending afterward.
fn Md5::digest(self : Md5) -> Bytes {
let copy : Md5 = {
h: self.h.copy(),
pending: self.pending.copy(),
used: self.used,
words: Array::make(16, 0U),
length: self.length,
}
let bit_length = self.length << 3
let padding = if self.used < 56 { 56 - self.used } else { 120 - self.used }
copy.update(Bytes::makei(padding, i => if i == 0 { 0x80 } else { 0 }))
copy.update(
Bytes::makei(8, i => ((bit_length >> (i * 8)) & 255L).to_int().to_byte()),
)
Bytes::makei(16, i => ((copy.h[i / 4] >> (i % 4 * 8)) & 255U).to_byte())
}
///|
/// Width-specialized signed little-endian stores preserve arbitrary prior
/// update alignment. The rare crossing path works for every width, including
/// 24-bit PCM and a nonaligned prefix before 16/32-bit PCM.
fn md5_update_frame(state : Md5, frame : Frame) -> Unit {
let width = (frame.bits_per_sample + 7) / 8
state.length += (frame.block_size * frame.channels.length() * width).to_int64()
match width {
1 =>
for i in 0..> 0) & 255).to_byte())
state.used = used + 1
if state.used == 64 {
state.block(state.pending)
state.used = 0
}
}
}
2 =>
for i in 0..> 0) & 255).to_byte())
state.pending.unsafe_set(used + 1, ((sample >> 8) & 255).to_byte())
state.used = used + 2
if state.used == 64 {
state.block(state.pending)
state.used = 0
}
} else {
for byte in 0..<2 {
state.pending.unsafe_set(
state.used,
((sample >> (byte * 8)) & 255).to_byte(),
)
state.used += 1
if state.used == 64 {
state.block(state.pending)
state.used = 0
}
}
}
}
}
3 =>
for i in 0..> 0) & 255).to_byte())
state.pending.unsafe_set(used + 1, ((sample >> 8) & 255).to_byte())
state.pending.unsafe_set(used + 2, ((sample >> 16) & 255).to_byte())
state.used = used + 3
if state.used == 64 {
state.block(state.pending)
state.used = 0
}
} else {
for byte in 0..<3 {
state.pending.unsafe_set(
state.used,
((sample >> (byte * 8)) & 255).to_byte(),
)
state.used += 1
if state.used == 64 {
state.block(state.pending)
state.used = 0
}
}
}
}
}
4 =>
for i in 0..> 0) & 255).to_byte())
state.pending.unsafe_set(used + 1, ((sample >> 8) & 255).to_byte())
state.pending.unsafe_set(used + 2, ((sample >> 16) & 255).to_byte())
state.pending.unsafe_set(used + 3, ((sample >> 24) & 255).to_byte())
state.used = used + 4
if state.used == 64 {
state.block(state.pending)
state.used = 0
}
} else {
for byte in 0..<4 {
state.pending.unsafe_set(
state.used,
((sample >> (byte * 8)) & 255).to_byte(),
)
state.used += 1
if state.used == 64 {
state.block(state.pending)
state.used = 0
}
}
}
}
}
_ => abort("invalid internal MD5 PCM byte width")
}
}