hash.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import * as strings from './strings.js';
  6. /**
  7. * Return a hash value for an object.
  8. */
  9. export function hash(obj) {
  10. return doHash(obj, 0);
  11. }
  12. export function doHash(obj, hashVal) {
  13. switch (typeof obj) {
  14. case 'object':
  15. if (obj === null) {
  16. return numberHash(349, hashVal);
  17. }
  18. else if (Array.isArray(obj)) {
  19. return arrayHash(obj, hashVal);
  20. }
  21. return objectHash(obj, hashVal);
  22. case 'string':
  23. return stringHash(obj, hashVal);
  24. case 'boolean':
  25. return booleanHash(obj, hashVal);
  26. case 'number':
  27. return numberHash(obj, hashVal);
  28. case 'undefined':
  29. return numberHash(937, hashVal);
  30. default:
  31. return numberHash(617, hashVal);
  32. }
  33. }
  34. export function numberHash(val, initialHashVal) {
  35. return (((initialHashVal << 5) - initialHashVal) + val) | 0; // hashVal * 31 + ch, keep as int32
  36. }
  37. function booleanHash(b, initialHashVal) {
  38. return numberHash(b ? 433 : 863, initialHashVal);
  39. }
  40. export function stringHash(s, hashVal) {
  41. hashVal = numberHash(149417, hashVal);
  42. for (let i = 0, length = s.length; i < length; i++) {
  43. hashVal = numberHash(s.charCodeAt(i), hashVal);
  44. }
  45. return hashVal;
  46. }
  47. function arrayHash(arr, initialHashVal) {
  48. initialHashVal = numberHash(104579, initialHashVal);
  49. return arr.reduce((hashVal, item) => doHash(item, hashVal), initialHashVal);
  50. }
  51. function objectHash(obj, initialHashVal) {
  52. initialHashVal = numberHash(181387, initialHashVal);
  53. return Object.keys(obj).sort().reduce((hashVal, key) => {
  54. hashVal = stringHash(key, hashVal);
  55. return doHash(obj[key], hashVal);
  56. }, initialHashVal);
  57. }
  58. function leftRotate(value, bits, totalBits = 32) {
  59. // delta + bits = totalBits
  60. const delta = totalBits - bits;
  61. // All ones, expect `delta` zeros aligned to the right
  62. const mask = ~((1 << delta) - 1);
  63. // Join (value left-shifted `bits` bits) with (masked value right-shifted `delta` bits)
  64. return ((value << bits) | ((mask & value) >>> delta)) >>> 0;
  65. }
  66. function fill(dest, index = 0, count = dest.byteLength, value = 0) {
  67. for (let i = 0; i < count; i++) {
  68. dest[index + i] = value;
  69. }
  70. }
  71. function leftPad(value, length, char = '0') {
  72. while (value.length < length) {
  73. value = char + value;
  74. }
  75. return value;
  76. }
  77. export function toHexString(bufferOrValue, bitsize = 32) {
  78. if (bufferOrValue instanceof ArrayBuffer) {
  79. return Array.from(new Uint8Array(bufferOrValue)).map(b => b.toString(16).padStart(2, '0')).join('');
  80. }
  81. return leftPad((bufferOrValue >>> 0).toString(16), bitsize / 4);
  82. }
  83. /**
  84. * A SHA1 implementation that works with strings and does not allocate.
  85. */
  86. export class StringSHA1 {
  87. constructor() {
  88. this._h0 = 0x67452301;
  89. this._h1 = 0xEFCDAB89;
  90. this._h2 = 0x98BADCFE;
  91. this._h3 = 0x10325476;
  92. this._h4 = 0xC3D2E1F0;
  93. this._buff = new Uint8Array(64 /* BLOCK_SIZE */ + 3 /* to fit any utf-8 */);
  94. this._buffDV = new DataView(this._buff.buffer);
  95. this._buffLen = 0;
  96. this._totalLen = 0;
  97. this._leftoverHighSurrogate = 0;
  98. this._finished = false;
  99. }
  100. update(str) {
  101. const strLen = str.length;
  102. if (strLen === 0) {
  103. return;
  104. }
  105. const buff = this._buff;
  106. let buffLen = this._buffLen;
  107. let leftoverHighSurrogate = this._leftoverHighSurrogate;
  108. let charCode;
  109. let offset;
  110. if (leftoverHighSurrogate !== 0) {
  111. charCode = leftoverHighSurrogate;
  112. offset = -1;
  113. leftoverHighSurrogate = 0;
  114. }
  115. else {
  116. charCode = str.charCodeAt(0);
  117. offset = 0;
  118. }
  119. while (true) {
  120. let codePoint = charCode;
  121. if (strings.isHighSurrogate(charCode)) {
  122. if (offset + 1 < strLen) {
  123. const nextCharCode = str.charCodeAt(offset + 1);
  124. if (strings.isLowSurrogate(nextCharCode)) {
  125. offset++;
  126. codePoint = strings.computeCodePoint(charCode, nextCharCode);
  127. }
  128. else {
  129. // illegal => unicode replacement character
  130. codePoint = 65533 /* UNICODE_REPLACEMENT */;
  131. }
  132. }
  133. else {
  134. // last character is a surrogate pair
  135. leftoverHighSurrogate = charCode;
  136. break;
  137. }
  138. }
  139. else if (strings.isLowSurrogate(charCode)) {
  140. // illegal => unicode replacement character
  141. codePoint = 65533 /* UNICODE_REPLACEMENT */;
  142. }
  143. buffLen = this._push(buff, buffLen, codePoint);
  144. offset++;
  145. if (offset < strLen) {
  146. charCode = str.charCodeAt(offset);
  147. }
  148. else {
  149. break;
  150. }
  151. }
  152. this._buffLen = buffLen;
  153. this._leftoverHighSurrogate = leftoverHighSurrogate;
  154. }
  155. _push(buff, buffLen, codePoint) {
  156. if (codePoint < 0x0080) {
  157. buff[buffLen++] = codePoint;
  158. }
  159. else if (codePoint < 0x0800) {
  160. buff[buffLen++] = 0b11000000 | ((codePoint & 0b00000000000000000000011111000000) >>> 6);
  161. buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000000000111111) >>> 0);
  162. }
  163. else if (codePoint < 0x10000) {
  164. buff[buffLen++] = 0b11100000 | ((codePoint & 0b00000000000000001111000000000000) >>> 12);
  165. buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000111111000000) >>> 6);
  166. buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000000000111111) >>> 0);
  167. }
  168. else {
  169. buff[buffLen++] = 0b11110000 | ((codePoint & 0b00000000000111000000000000000000) >>> 18);
  170. buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000111111000000000000) >>> 12);
  171. buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000111111000000) >>> 6);
  172. buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000000000111111) >>> 0);
  173. }
  174. if (buffLen >= 64 /* BLOCK_SIZE */) {
  175. this._step();
  176. buffLen -= 64 /* BLOCK_SIZE */;
  177. this._totalLen += 64 /* BLOCK_SIZE */;
  178. // take last 3 in case of UTF8 overflow
  179. buff[0] = buff[64 /* BLOCK_SIZE */ + 0];
  180. buff[1] = buff[64 /* BLOCK_SIZE */ + 1];
  181. buff[2] = buff[64 /* BLOCK_SIZE */ + 2];
  182. }
  183. return buffLen;
  184. }
  185. digest() {
  186. if (!this._finished) {
  187. this._finished = true;
  188. if (this._leftoverHighSurrogate) {
  189. // illegal => unicode replacement character
  190. this._leftoverHighSurrogate = 0;
  191. this._buffLen = this._push(this._buff, this._buffLen, 65533 /* UNICODE_REPLACEMENT */);
  192. }
  193. this._totalLen += this._buffLen;
  194. this._wrapUp();
  195. }
  196. return toHexString(this._h0) + toHexString(this._h1) + toHexString(this._h2) + toHexString(this._h3) + toHexString(this._h4);
  197. }
  198. _wrapUp() {
  199. this._buff[this._buffLen++] = 0x80;
  200. fill(this._buff, this._buffLen);
  201. if (this._buffLen > 56) {
  202. this._step();
  203. fill(this._buff);
  204. }
  205. // this will fit because the mantissa can cover up to 52 bits
  206. const ml = 8 * this._totalLen;
  207. this._buffDV.setUint32(56, Math.floor(ml / 4294967296), false);
  208. this._buffDV.setUint32(60, ml % 4294967296, false);
  209. this._step();
  210. }
  211. _step() {
  212. const bigBlock32 = StringSHA1._bigBlock32;
  213. const data = this._buffDV;
  214. for (let j = 0; j < 64 /* 16*4 */; j += 4) {
  215. bigBlock32.setUint32(j, data.getUint32(j, false), false);
  216. }
  217. for (let j = 64; j < 320 /* 80*4 */; j += 4) {
  218. bigBlock32.setUint32(j, leftRotate((bigBlock32.getUint32(j - 12, false) ^ bigBlock32.getUint32(j - 32, false) ^ bigBlock32.getUint32(j - 56, false) ^ bigBlock32.getUint32(j - 64, false)), 1), false);
  219. }
  220. let a = this._h0;
  221. let b = this._h1;
  222. let c = this._h2;
  223. let d = this._h3;
  224. let e = this._h4;
  225. let f, k;
  226. let temp;
  227. for (let j = 0; j < 80; j++) {
  228. if (j < 20) {
  229. f = (b & c) | ((~b) & d);
  230. k = 0x5A827999;
  231. }
  232. else if (j < 40) {
  233. f = b ^ c ^ d;
  234. k = 0x6ED9EBA1;
  235. }
  236. else if (j < 60) {
  237. f = (b & c) | (b & d) | (c & d);
  238. k = 0x8F1BBCDC;
  239. }
  240. else {
  241. f = b ^ c ^ d;
  242. k = 0xCA62C1D6;
  243. }
  244. temp = (leftRotate(a, 5) + f + e + k + bigBlock32.getUint32(j * 4, false)) & 0xffffffff;
  245. e = d;
  246. d = c;
  247. c = leftRotate(b, 30);
  248. b = a;
  249. a = temp;
  250. }
  251. this._h0 = (this._h0 + a) & 0xffffffff;
  252. this._h1 = (this._h1 + b) & 0xffffffff;
  253. this._h2 = (this._h2 + c) & 0xffffffff;
  254. this._h3 = (this._h3 + d) & 0xffffffff;
  255. this._h4 = (this._h4 + e) & 0xffffffff;
  256. }
  257. }
  258. StringSHA1._bigBlock32 = new DataView(new ArrayBuffer(320)); // 80 * 4 = 320