BulkUpdateHash.js 4.1 KB

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