1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | "use strict";
|
7 |
|
8 | const Hash = require("./Hash");
|
9 |
|
10 | const BULK_SIZE = 2000;
|
11 |
|
12 |
|
13 |
|
14 | const digestCaches = {};
|
15 |
|
16 | class BulkUpdateDecorator extends Hash {
|
17 | |
18 |
|
19 |
|
20 |
|
21 | constructor(hashOrFactory, hashKey) {
|
22 | super();
|
23 | this.hashKey = hashKey;
|
24 | if (typeof hashOrFactory === "function") {
|
25 | this.hashFactory = hashOrFactory;
|
26 | this.hash = undefined;
|
27 | } else {
|
28 | this.hashFactory = undefined;
|
29 | this.hash = hashOrFactory;
|
30 | }
|
31 | this.buffer = "";
|
32 | }
|
33 |
|
34 | |
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 | update(data, inputEncoding) {
|
41 | if (
|
42 | inputEncoding !== undefined ||
|
43 | typeof data !== "string" ||
|
44 | data.length > BULK_SIZE
|
45 | ) {
|
46 | if (this.hash === undefined) this.hash = this.hashFactory();
|
47 | if (this.buffer.length > 0) {
|
48 | this.hash.update(this.buffer);
|
49 | this.buffer = "";
|
50 | }
|
51 | this.hash.update(data, inputEncoding);
|
52 | } else {
|
53 | this.buffer += data;
|
54 | if (this.buffer.length > BULK_SIZE) {
|
55 | if (this.hash === undefined) this.hash = this.hashFactory();
|
56 | this.hash.update(this.buffer);
|
57 | this.buffer = "";
|
58 | }
|
59 | }
|
60 | return this;
|
61 | }
|
62 |
|
63 | |
64 |
|
65 |
|
66 |
|
67 |
|
68 | digest(encoding) {
|
69 | let digestCache;
|
70 | const buffer = this.buffer;
|
71 | if (this.hash === undefined) {
|
72 |
|
73 | const cacheKey = `${this.hashKey}-${encoding}`;
|
74 | digestCache = digestCaches[cacheKey];
|
75 | if (digestCache === undefined) {
|
76 | digestCache = digestCaches[cacheKey] = new Map();
|
77 | }
|
78 | const cacheEntry = digestCache.get(buffer);
|
79 | if (cacheEntry !== undefined) return cacheEntry;
|
80 | this.hash = this.hashFactory();
|
81 | }
|
82 | if (buffer.length > 0) {
|
83 | this.hash.update(buffer);
|
84 | }
|
85 | const digestResult = this.hash.digest(encoding);
|
86 | const result =
|
87 | typeof digestResult === "string" ? digestResult : digestResult.toString();
|
88 | if (digestCache !== undefined) {
|
89 | digestCache.set(buffer, result);
|
90 | }
|
91 | return result;
|
92 | }
|
93 | }
|
94 |
|
95 |
|
96 | class DebugHash extends Hash {
|
97 | constructor() {
|
98 | super();
|
99 | this.string = "";
|
100 | }
|
101 |
|
102 | |
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 | update(data, inputEncoding) {
|
109 | if (typeof data !== "string") data = data.toString("utf-8");
|
110 | if (data.startsWith("debug-digest-")) {
|
111 | data = Buffer.from(data.slice("debug-digest-".length), "hex").toString();
|
112 | }
|
113 | this.string += `[${data}](${new Error().stack.split("\n", 3)[2]})\n`;
|
114 | return this;
|
115 | }
|
116 |
|
117 | |
118 |
|
119 |
|
120 |
|
121 |
|
122 | digest(encoding) {
|
123 | return "debug-digest-" + Buffer.from(this.string).toString("hex");
|
124 | }
|
125 | }
|
126 |
|
127 | let crypto = undefined;
|
128 | let createXXHash64 = undefined;
|
129 | let BatchedHash = undefined;
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 | module.exports = algorithm => {
|
137 | if (typeof algorithm === "function") {
|
138 | return new BulkUpdateDecorator(() => new algorithm());
|
139 | }
|
140 | switch (algorithm) {
|
141 |
|
142 | case "debug":
|
143 | return new DebugHash();
|
144 | case "xxhash64":
|
145 | if (createXXHash64 === undefined) {
|
146 | createXXHash64 = require("./hash/xxhash64");
|
147 | if (BatchedHash === undefined) {
|
148 | BatchedHash = require("./hash/BatchedHash");
|
149 | }
|
150 | }
|
151 | return new BatchedHash(createXXHash64());
|
152 | default:
|
153 | if (crypto === undefined) crypto = require("crypto");
|
154 | return new BulkUpdateDecorator(
|
155 | () => crypto.createHash(algorithm),
|
156 | algorithm
|
157 | );
|
158 | }
|
159 | };
|