BulkUpdateDecorator.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. const BULK_SIZE = 2000;
  2. // We are using an object instead of a Map as this will stay static during the runtime
  3. // so access to it can be optimized by v8
  4. const digestCaches = {};
  5. class BulkUpdateDecorator {
  6. /**
  7. * @param {Hash | function(): Hash} hashOrFactory function to create a hash
  8. * @param {string=} hashKey key for caching
  9. */
  10. constructor(hashOrFactory, hashKey) {
  11. this.hashKey = hashKey;
  12. if (typeof hashOrFactory === "function") {
  13. this.hashFactory = hashOrFactory;
  14. this.hash = undefined;
  15. } else {
  16. this.hashFactory = undefined;
  17. this.hash = hashOrFactory;
  18. }
  19. this.buffer = "";
  20. }
  21. /**
  22. * Update hash {@link https://nodejs.org/api/crypto.html#crypto_hash_update_data_inputencoding}
  23. * @param {string|Buffer} data data
  24. * @param {string=} inputEncoding data encoding
  25. * @returns {this} updated hash
  26. */
  27. update(data, inputEncoding) {
  28. if (
  29. inputEncoding !== undefined ||
  30. typeof data !== "string" ||
  31. data.length > BULK_SIZE
  32. ) {
  33. if (this.hash === undefined) {
  34. this.hash = this.hashFactory();
  35. }
  36. if (this.buffer.length > 0) {
  37. this.hash.update(this.buffer);
  38. this.buffer = "";
  39. }
  40. this.hash.update(data, inputEncoding);
  41. } else {
  42. this.buffer += data;
  43. if (this.buffer.length > BULK_SIZE) {
  44. if (this.hash === undefined) {
  45. this.hash = this.hashFactory();
  46. }
  47. this.hash.update(this.buffer);
  48. this.buffer = "";
  49. }
  50. }
  51. return this;
  52. }
  53. /**
  54. * Calculates the digest {@link https://nodejs.org/api/crypto.html#crypto_hash_digest_encoding}
  55. * @param {string=} encoding encoding of the return value
  56. * @returns {string|Buffer} digest
  57. */
  58. digest(encoding) {
  59. let digestCache;
  60. const buffer = this.buffer;
  61. if (this.hash === undefined) {
  62. // short data for hash, we can use caching
  63. const cacheKey = `${this.hashKey}-${encoding}`;
  64. digestCache = digestCaches[cacheKey];
  65. if (digestCache === undefined) {
  66. digestCache = digestCaches[cacheKey] = new Map();
  67. }
  68. const cacheEntry = digestCache.get(buffer);
  69. if (cacheEntry !== undefined) {
  70. return cacheEntry;
  71. }
  72. this.hash = this.hashFactory();
  73. }
  74. if (buffer.length > 0) {
  75. this.hash.update(buffer);
  76. }
  77. const digestResult = this.hash.digest(encoding);
  78. if (digestCache !== undefined) {
  79. digestCache.set(buffer, digestResult);
  80. }
  81. return digestResult;
  82. }
  83. }
  84. module.exports = BulkUpdateDecorator;