/* This code is hereby released to the public domain. ~aaaaaa123456789, 2016-12-02, updated 2016-12-05 */ function ResizableMemoryBlock (initial_size = 0) { var self = this; var current_size; var parts = []; for (current_size = 0; current_size < initial_size; current_size += 8192) parts[current_size >>> 13] = new DataView(new ArrayBuffer(8192)); current_size = initial_size; self.size = function () { return current_size; }; function shrink (size) { var new_parts = (size + 8191) >>> 13; var old_offset = current_size & 8191, new_offset = size & 8191; if (old_offset === 0) old_offset = 8192; if (new_offset === 0) new_offset = 8192; if (new_parts != parts.length) { while (parts.length > new_parts) parts.pop(); old_offset = 8192; } var offset, part = parts.length - 1; for (offset = new_offset; offset < old_offset; offset ++) parts[part].setUint8(offset, 0); current_size = size; } function expand (size) { var new_parts = (size + 8191) >>> 13; while (parts.length < new_parts) parts.push(new DataView(new ArrayBuffer(8192))); current_size = size; } self.resize = function (size) { if (size == current_size) return; if (size > current_size) return expand(size); return shrink(size); }; function break_into_bytes (value, size) { var bytes = []; while (bytes.length < size) { bytes.push(value & 0xff); value >>>= 8; } return bytes; } self.set_byte = function (position, value) { if (position >= current_size) expand(position + 1); parts[position >>> 13].setUint8(position & 8191, value & 0xff); }; self.set_halfword = function (position, value) { if (position > (current_size - 2)) expand(position + 2); var offset = position & 8191, part = position >>> 13; value &= 0xffff; if (offset <= 8190) return parts[part].setUint16(offset, value, true); var bytes = break_into_bytes(value, 2); while (bytes.length > 0) { parts[part].setUint8(offset ++, bytes.shift()); if (offset == 8192) { part ++; offset = 0; } } }; self.set_word = function (position, value) { if (position > (current_size - 4)) expand(position + 4); var offset = position & 8191, part = position >>> 13; value >>>= 0; if (offset <= 8188) return parts[part].setUint32(offset, value, true); var bytes = break_into_bytes(value, 4); while (bytes.length > 0) { parts[part].setUint8(offset ++, bytes.shift()); if (offset == 8192) { part ++; offset = 0; } } }; function gather_bytes (bytes) { var result = 0; while (bytes.length) result = ((result << 8) >>> 0) + bytes.pop(); return result; } self.get_byte = function (position) { if (position >= current_size) throw "attempted to read past the end of the buffer"; return parts[position >>> 13].getUint8(position & 8191); }; self.get_halfword = function (position) { if (position > (current_size - 2)) throw "attempted to read past the end of the buffer"; var offset = position & 8191, part = position >>> 13; if (offset <= 8190) return parts[part].getUint16(offset, true); var bytes = []; var count; for (count = 0; count < 2; count ++) { bytes.push(parts[part].getUint8(offset ++)); if (offset == 8192) { offset = 0; part ++; } } return gather_bytes(bytes); }; self.get_word = function (position) { if (position > (current_size - 4)) throw "attempted to read past the end of the buffer"; var offset = position & 8191, part = position >>> 13; if (offset <= 8188) return parts[part].getUint32(offset, true); var bytes = []; var count; for (count = 0; count < 4; count ++) { bytes.push(parts[part].getUint8(offset ++)); if (offset == 8192) { offset = 0; part ++; } } return gather_bytes(bytes); }; self.generate_array_buffer = function () { var result = new ArrayBuffer(current_size); var view = new Uint8Array(result); var part_count = current_size >>> 13, extra_offset = current_size & 8191; var pos; for (pos = 0; pos < part_count; pos ++) view.set(new Uint8Array(parts[pos].buffer), (pos << 13) >>> 0); for (pos = 0; pos < extra_offset; pos ++) view[pos + ((part_count << 13) >>> 0)] = parts[part_count].getUint8(pos); return result; }; self.calculate_sha1 = function () { function rotate (value, count) { return ((value << count) | (value >>> (32 - count))) >>> 0; } function mixing_function (counter, first, second, third) { if (counter < 20) return ((first & second) | (~first & third)) >>> 0; else if ((counter >= 40) && (counter < 60)) return ((first & second) | (first & third) | (second & third)) >>> 0; else return (first ^ second ^ third) >>> 0; } function hash_constant (counter) { // constants used by SHA-1; they are actually simply the square roots of 2, 3, 5 and 10 as a fixed-point number (2.30 format) return ([0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6])[(counter / 20) >>> 0]; } function process_block (state, block) { var words = new Uint32Array(80); var pos; for (pos = 0; pos < 16; pos ++) words[pos] = block.getUint32(pos << 2); for (; pos < 80; pos ++) words[pos] = rotate((words[pos - 3] ^ words[pos - 8] ^ words[pos - 14] ^ words[pos - 16]) >>> 0, 1); var temp, a = state[0], b = state[1], c = state[2], d = state[3], e = state[4]; for (pos = 0; pos < 80; pos ++) { temp = (rotate(a, 5) + mixing_function(pos, b, c, d) + e + words[pos] + hash_constant(pos)) >>> 0; e = d; d = c; c = rotate(b, 30); b = a; a = temp; } return [(state[0] + a) >>> 0, (state[1] + b) >>> 0, (state[2] + c) >>> 0, (state[3] + d) >>> 0, (state[4] + e) >>> 0]; } var state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; var pos, buf; for (pos = 0; pos < ((current_size & 0xffffffc0) >>> 0); pos += 64) state = process_block(state, new DataView(parts[pos >>> 13].buffer.slice(pos & 8191, (pos & 8191) + 64))); buf = new DataView(new ArrayBuffer(64)); for (; pos < current_size; pos ++) buf.setUint8(pos & 63, parts[pos >>> 13].getUint8(pos & 8191)); buf.setUint8(pos & 63, 0x80); if ((pos & 63) >= 56) { state = process_block(state, buf); buf = new DataView(new ArrayBuffer(64)); } buf.setUint8(59, current_size >>> 29); buf.setUint32(60, (current_size << 3) >>> 0); state = process_block(state, buf); buf = new Uint8Array(20); for (pos = 0; pos < 20; pos ++) buf[pos] = (state[pos >> 2] >>> ((3 - (pos & 3)) << 3)) & 0xff; return buf; }; }