BulkUpdateHash.js 4.2 KB

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