BulkUpdateHash.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Alexander Akait @alexander-akait
  4. */
  5. "use strict";
  6. const Hash = require("../Hash");
  7. const { digest, update } = require("./hash-digest");
  8. /**
  9. * @import {
  10. * HashDigest as Encoding
  11. * } from "../../../declarations/WebpackOptions"
  12. */
  13. /** @typedef {() => Hash} HashFactory */
  14. const BULK_SIZE = 3;
  15. // We are using an object instead of a Map as this will stay static during the runtime
  16. // so access to it can be optimized by v8
  17. /** @type {{ [key: string]: Map<string, string> }} */
  18. const digestCaches = {};
  19. class BulkUpdateHash extends Hash {
  20. /**
  21. * Creates an instance of BulkUpdateHash.
  22. * @param {Hash | HashFactory} hashOrFactory function to create a hash
  23. * @param {string=} hashKey key for caching
  24. */
  25. constructor(hashOrFactory, hashKey) {
  26. super();
  27. /** @type {undefined | string} */
  28. this.hashKey = hashKey;
  29. if (typeof hashOrFactory === "function") {
  30. /** @type {undefined | HashFactory} */
  31. this.hashFactory = hashOrFactory;
  32. /** @type {undefined | Hash} */
  33. this.hash = undefined;
  34. } else {
  35. /** @type {undefined | HashFactory} */
  36. this.hashFactory = undefined;
  37. /** @type {undefined | Hash} */
  38. this.hash = hashOrFactory;
  39. }
  40. /** @type {string} */
  41. this.buffer = "";
  42. }
  43. /**
  44. * Update hash {@link https://nodejs.org/api/crypto.html#crypto_hash_update_data_inputencoding}
  45. * @overload
  46. * @param {string | Buffer} data data
  47. * @returns {Hash} updated hash
  48. */
  49. /**
  50. * Update hash {@link https://nodejs.org/api/crypto.html#crypto_hash_update_data_inputencoding}
  51. * @overload
  52. * @param {string} data data
  53. * @param {Encoding} inputEncoding data encoding
  54. * @returns {Hash} updated hash
  55. */
  56. /**
  57. * Update hash {@link https://nodejs.org/api/crypto.html#crypto_hash_update_data_inputencoding}
  58. * @param {string | Buffer} data data
  59. * @param {Encoding=} inputEncoding data encoding
  60. * @returns {Hash} updated hash
  61. */
  62. update(data, inputEncoding) {
  63. if (
  64. inputEncoding !== undefined ||
  65. typeof data !== "string" ||
  66. data.length > BULK_SIZE
  67. ) {
  68. if (this.hash === undefined) {
  69. this.hash = /** @type {HashFactory} */ (this.hashFactory)();
  70. }
  71. if (this.buffer.length > 0) {
  72. update(this.hash, this.buffer);
  73. this.buffer = "";
  74. }
  75. if (typeof data === "string" && inputEncoding) {
  76. update(this.hash, data, inputEncoding);
  77. } else {
  78. update(this.hash, data);
  79. }
  80. } else {
  81. this.buffer += data;
  82. if (this.buffer.length > BULK_SIZE) {
  83. if (this.hash === undefined) {
  84. this.hash = /** @type {HashFactory} */ (this.hashFactory)();
  85. }
  86. update(this.hash, this.buffer);
  87. this.buffer = "";
  88. }
  89. }
  90. return this;
  91. }
  92. /**
  93. * Calculates the digest {@link https://nodejs.org/api/crypto.html#crypto_hash_digest_encoding}
  94. * @overload
  95. * @returns {Buffer} digest
  96. */
  97. /**
  98. * Calculates the digest {@link https://nodejs.org/api/crypto.html#crypto_hash_digest_encoding}
  99. * @overload
  100. * @param {Encoding} encoding encoding of the return value
  101. * @returns {string} digest
  102. */
  103. /**
  104. * Calculates the digest {@link https://nodejs.org/api/crypto.html#crypto_hash_digest_encoding}
  105. * @param {Encoding=} encoding encoding of the return value
  106. * @returns {string | Buffer} digest
  107. */
  108. digest(encoding) {
  109. /** @type {undefined | Map<string, string | Buffer>} */
  110. let digestCache;
  111. const buffer = this.buffer;
  112. if (this.hash === undefined) {
  113. // short data for hash, we can use caching
  114. const cacheKey = `${this.hashKey}-${encoding}`;
  115. digestCache = digestCaches[cacheKey];
  116. if (digestCache === undefined) {
  117. digestCache = digestCaches[cacheKey] = new Map();
  118. }
  119. const cacheEntry = digestCache.get(buffer);
  120. if (cacheEntry !== undefined) return cacheEntry;
  121. this.hash = /** @type {HashFactory} */ (this.hashFactory)();
  122. }
  123. if (buffer.length > 0) {
  124. update(this.hash, buffer);
  125. }
  126. if (!encoding) {
  127. const result = digest(this.hash, undefined, Boolean(this.hashKey));
  128. if (digestCache !== undefined) {
  129. digestCache.set(buffer, result);
  130. }
  131. return result;
  132. }
  133. const result = digest(this.hash, encoding, Boolean(this.hashKey));
  134. if (digestCache !== undefined) {
  135. digestCache.set(buffer, result);
  136. }
  137. return result;
  138. }
  139. }
  140. module.exports = BulkUpdateHash;