///| SHA-1 implementation for Git object hashing
///|
/// SHA-1 initial hash values
let sha1_h0 : Int = 0x67452301
///|
let sha1_h1 : Int = 0xefcdab89
///|
let sha1_h2 : Int = 0x98badcfe
///|
let sha1_h3 : Int = 0x10325476
///|
let sha1_h4 : Int = 0xc3d2e1f0
///|
/// SHA-1 round constants
let sha1_k0 : Int = 0x5a827999
///|
let sha1_k1 : Int = 0x6ed9eba1
///|
let sha1_k2 : Int = 0x8f1bbcdc
///|
let sha1_k3 : Int = 0xca62c1d6
///|
/// Left rotate 32-bit integer
fn rotl32(x : Int, n : Int) -> Int {
((x << n) | (x.reinterpret_as_uint() >> (32 - n)).reinterpret_as_int()) &
0xffffffff
}
///|
/// Incremental SHA-1 state
pub struct Sha1State {
h : FixedArray[Int]
block : FixedArray[Byte]
w : FixedArray[Int]
mut block_len : Int
mut total_len : Int64
}
///|
pub fn Sha1State::new() -> Sha1State {
{
h: [sha1_h0, sha1_h1, sha1_h2, sha1_h3, sha1_h4],
block: FixedArray::make(64, b'\x00'),
w: FixedArray::make(80, 0),
block_len: 0,
total_len: 0L,
}
}
///|
pub fn Sha1State::reset(self : Sha1State) -> Unit {
self.h[0] = sha1_h0
self.h[1] = sha1_h1
self.h[2] = sha1_h2
self.h[3] = sha1_h3
self.h[4] = sha1_h4
self.block_len = 0
self.total_len = 0L
}
///|
fn Sha1State::process_block(self : Sha1State) -> Unit {
let h = self.h
let w = self.w
let block = self.block
for i = 0; i < 16; i = i + 1 {
w[i] = (block[i * 4].to_int() << 24) |
(block[i * 4 + 1].to_int() << 16) |
(block[i * 4 + 2].to_int() << 8) |
block[i * 4 + 3].to_int()
}
for i = 16; i < 80; i = i + 1 {
w[i] = rotl32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1)
}
let mut a = h[0]
let mut b = h[1]
let mut c = h[2]
let mut d = h[3]
let mut e = h[4]
for i = 0; i < 20; i = i + 1 {
let f = (b & c) | (b.lnot() & d)
let temp = (rotl32(a, 5) + f + e + sha1_k0 + w[i]) & 0xffffffff
e = d
d = c
c = rotl32(b, 30)
b = a
a = temp
}
for i = 20; i < 40; i = i + 1 {
let f = b ^ c ^ d
let temp = (rotl32(a, 5) + f + e + sha1_k1 + w[i]) & 0xffffffff
e = d
d = c
c = rotl32(b, 30)
b = a
a = temp
}
for i = 40; i < 60; i = i + 1 {
let f = (b & c) | (b & d) | (c & d)
let temp = (rotl32(a, 5) + f + e + sha1_k2 + w[i]) & 0xffffffff
e = d
d = c
c = rotl32(b, 30)
b = a
a = temp
}
for i = 60; i < 80; i = i + 1 {
let f = b ^ c ^ d
let temp = (rotl32(a, 5) + f + e + sha1_k3 + w[i]) & 0xffffffff
e = d
d = c
c = rotl32(b, 30)
b = a
a = temp
}
h[0] = (h[0] + a) & 0xffffffff
h[1] = (h[1] + b) & 0xffffffff
h[2] = (h[2] + c) & 0xffffffff
h[3] = (h[3] + d) & 0xffffffff
h[4] = (h[4] + e) & 0xffffffff
}
///|
pub fn Sha1State::update(self : Sha1State, data : Bytes) -> Unit {
self.update_slice(data, 0, data.length())
}
///|
pub fn Sha1State::update_slice(
self : Sha1State,
data : Bytes,
offset : Int,
len : Int,
) -> Unit {
let mut pos = offset
let end = offset + len
self.total_len += len.to_int64()
while pos < end {
let space = 64 - self.block_len
let to_copy = if end - pos < space { end - pos } else { space }
for i = 0; i < to_copy; i = i + 1 {
self.block[self.block_len + i] = data[pos + i]
}
self.block_len += to_copy
pos += to_copy
if self.block_len == 64 {
self.process_block()
self.block_len = 0
}
}
}
///|
pub fn Sha1State::update_byte(self : Sha1State, b : Byte) -> Unit {
self.block[self.block_len] = b
self.block_len += 1
self.total_len += 1L
if self.block_len == 64 {
self.process_block()
self.block_len = 0
}
}
///|
pub fn Sha1State::update_string(self : Sha1State, s : String) -> Unit {
for c in s {
self.update_byte(c.to_int().to_byte())
}
}
///|
pub fn Sha1State::finish(self : Sha1State) -> ObjectId {
let bit_len = self.total_len * 8L
self.block[self.block_len] = b'\x80'
self.block_len += 1
if self.block_len > 56 {
while self.block_len < 64 {
self.block[self.block_len] = b'\x00'
self.block_len += 1
}
self.process_block()
self.block_len = 0
}
while self.block_len < 56 {
self.block[self.block_len] = b'\x00'
self.block_len += 1
}
self.block[56] = ((bit_len >> 56) & 0xffL).to_byte()
self.block[57] = ((bit_len >> 48) & 0xffL).to_byte()
self.block[58] = ((bit_len >> 40) & 0xffL).to_byte()
self.block[59] = ((bit_len >> 32) & 0xffL).to_byte()
self.block[60] = ((bit_len >> 24) & 0xffL).to_byte()
self.block[61] = ((bit_len >> 16) & 0xffL).to_byte()
self.block[62] = ((bit_len >> 8) & 0xffL).to_byte()
self.block[63] = (bit_len & 0xffL).to_byte()
self.process_block()
let result : FixedArray[Byte] = FixedArray::make(20, b'\x00')
for i = 0; i < 5; i = i + 1 {
result[i * 4] = ((self.h[i] >> 24) & 0xff).to_byte()
result[i * 4 + 1] = ((self.h[i] >> 16) & 0xff).to_byte()
result[i * 4 + 2] = ((self.h[i] >> 8) & 0xff).to_byte()
result[i * 4 + 3] = (self.h[i] & 0xff).to_byte()
}
ObjectId::new(result)
}
///|
/// Compute SHA-1 hash of data prefix (first `len` bytes).
pub fn sha1_prefix(data : Bytes, len : Int) -> ObjectId {
let msg_len = if len < 0 {
0
} else if len > data.length() {
data.length()
} else {
len
}
let state = Sha1State::new()
state.update_slice(data, 0, msg_len)
state.finish()
}
///|
/// Compute SHA-1 hash of data
pub fn sha1(data : Bytes) -> ObjectId {
sha1_prefix(data, data.length())
}
///|
/// Compute SHA-1 hash of Array prefix (first `len` bytes).
pub fn sha1_array_prefix(data : Array[Byte], len : Int) -> ObjectId {
let msg_len = if len < 0 {
0
} else if len > data.length() {
data.length()
} else {
len
}
let state = Sha1State::new()
for i = 0; i < msg_len; i = i + 1 {
state.update_byte(data[i])
}
state.finish()
}
///|
/// Compute SHA-1 hash of data from Array
pub fn sha1_array(data : Array[Byte]) -> ObjectId {
sha1_array_prefix(data, data.length())
}