BulkUpdateHash.js 3.9 KB

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