Skip to content

Commit 304497b

Browse files
perf: avoid extra calculation for hash updates
1 parent c4bca6b commit 304497b

File tree

9 files changed

+443
-14
lines changed

9 files changed

+443
-14
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,4 @@ jobs:
5959
- uses: codecov/codecov-action@v5
6060
with:
6161
flags: integration
62+
token: ${{ secrets.CODECOV_TOKEN }}

lib/OriginalSource.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ class OriginalSource extends Source {
3232
*/
3333
constructor(value, name) {
3434
super();
35+
3536
const isBuffer = Buffer.isBuffer(value);
37+
3638
/**
3739
* @private
3840
* @type {undefined | string}
@@ -184,7 +186,11 @@ class OriginalSource extends Source {
184186
*/
185187
updateHash(hash) {
186188
hash.update("OriginalSource");
187-
hash.update(this.buffer());
189+
hash.update(
190+
this._valueAsBuffer
191+
? /** @type {Buffer} */ (this._valueAsBuffer)
192+
: /** @type {string} */ (this._value)
193+
);
188194
hash.update(this._name || "");
189195
}
190196
}

lib/RawSource.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,11 @@ class RawSource extends Source {
128128
*/
129129
updateHash(hash) {
130130
hash.update("RawSource");
131-
hash.update(this.buffer());
131+
hash.update(
132+
this._valueAsBuffer
133+
? /** @type {Buffer} */ (this._valueAsBuffer)
134+
: /** @type {string} */ (this._valueAsString)
135+
);
132136
}
133137
}
134138

lib/SourceMapSource.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,22 @@ class SourceMapSource extends Source {
346346
*/
347347
updateHash(hash) {
348348
hash.update("SourceMapSource");
349-
hash.update(this.buffer());
350-
hash.update(this._sourceMapBuffer());
349+
hash.update(
350+
this._valueAsBuffer
351+
? this._valueAsBuffer
352+
: typeof this._valueAsString === "string"
353+
? this._valueAsString
354+
: // Fallback when memory optimization enabled
355+
this.buffer()
356+
);
357+
hash.update(
358+
this._sourceMapAsBuffer
359+
? this._sourceMapAsBuffer
360+
: typeof this._sourceMapAsString === "string"
361+
? this._sourceMapAsString
362+
: // Fallback when memory optimization enabled
363+
this._sourceMapBuffer()
364+
);
351365

352366
if (this._hasOriginalSource) {
353367
hash.update(

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
"source-map": "^0.7.3",
3636
"sourcemap-validator": "^2.1.0",
3737
"tooling": "webpack/tooling#v1.23.9",
38-
"typescript": "^5.3.3"
38+
"typescript": "^5.3.3",
39+
"webpack": "^5.99.9"
3940
},
4041
"files": [
4142
"lib/",

test/OriginalSource.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
"use strict";
22

3+
const crypto = require("crypto");
4+
const BatchedHash = require("webpack/lib/util/hash/BatchedHash");
5+
const createMd4 = require("webpack/lib/util/hash/md4");
6+
const createXXHash64 = require("webpack/lib/util/hash/xxhash64");
7+
38
/** @typedef {import("../lib/Source").RawSourceMap} RawSourceMap */
49

510
jest.mock("./__mocks__/createMappingsSerializer");
@@ -123,4 +128,26 @@ describe.each([
123128
(source.sourceAndMap({ columns: false }).map).mappings
124129
).toBe(expected2);
125130
});
131+
132+
for (const hash of [
133+
["md5", [crypto.createHash("md5"), crypto.createHash("md5")]],
134+
["md4", [new BatchedHash(createMd4()), new BatchedHash(createMd4())]],
135+
[
136+
"xxhash64",
137+
[new BatchedHash(createXXHash64()), new BatchedHash(createXXHash64())]
138+
]
139+
]) {
140+
it(`should have the same hash (${hash[0]}) for string and Buffer`, () => {
141+
const sourceString = new OriginalSource("Text", "file.js");
142+
const sourceBuffer = new OriginalSource(Buffer.from("Text"), "file.js");
143+
144+
expect(sourceString.source()).toBe("Text");
145+
expect(sourceString.source()).toBe(sourceBuffer.source());
146+
147+
sourceString.updateHash(hash[1][0]);
148+
sourceBuffer.updateHash(hash[1][1]);
149+
150+
expect(hash[1][0].digest("hex")).toBe(hash[1][1].digest("hex"));
151+
});
152+
}
126153
});

test/RawSource.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
"use strict";
22

33
const RawSource = require("../").RawSource;
4+
const crypto = require("crypto");
5+
const BatchedHash = require("webpack/lib/util/hash/BatchedHash");
6+
const createMd4 = require("webpack/lib/util/hash/md4");
7+
const createXXHash64 = require("webpack/lib/util/hash/xxhash64");
48
const {
59
enableDualStringBufferCaching,
610
enterStringInterningRange,
@@ -29,6 +33,50 @@ describe("RawSource", () => {
2933
expect.assertions(3);
3034
});
3135

36+
for (const hash of [
37+
["md5", [crypto.createHash("md5"), crypto.createHash("md5")]],
38+
["md4", [new BatchedHash(createMd4()), new BatchedHash(createMd4())]],
39+
[
40+
"xxhash64",
41+
[new BatchedHash(createXXHash64()), new BatchedHash(createXXHash64())]
42+
]
43+
]) {
44+
it(`should have the same hash (${hash[0]}) for string and Buffer`, () => {
45+
const sourceString = new RawSource("Text");
46+
const sourceBuffer = new RawSource(Buffer.from("Text"));
47+
48+
expect(sourceString.source()).toBe("Text");
49+
expect(sourceString.buffer()).toEqual(sourceBuffer.buffer());
50+
51+
sourceString.updateHash(hash[1][0]);
52+
sourceBuffer.updateHash(hash[1][1]);
53+
54+
expect(hash[1][0].digest("hex")).toBe(hash[1][1].digest("hex"));
55+
});
56+
}
57+
58+
for (const hash of [
59+
["md5", [crypto.createHash("md5"), crypto.createHash("md5")]],
60+
["md4", [new BatchedHash(createMd4()), new BatchedHash(createMd4())]],
61+
[
62+
"xxhash64",
63+
[new BatchedHash(createXXHash64()), new BatchedHash(createXXHash64())]
64+
]
65+
]) {
66+
it(`should have the same hash (${hash[0]}) for string and Buffer (convert to string)`, () => {
67+
const sourceString = new RawSource("Text", true);
68+
const sourceBuffer = new RawSource(Buffer.from("Text"), true);
69+
70+
expect(sourceString.source()).toBe("Text");
71+
expect(sourceString.buffer()).toEqual(sourceBuffer.buffer());
72+
73+
sourceString.updateHash(hash[1][0]);
74+
sourceBuffer.updateHash(hash[1][1]);
75+
76+
expect(hash[1][0].digest("hex")).toBe(hash[1][1].digest("hex"));
77+
});
78+
}
79+
3280
describe("memory optimizations are enabled", () => {
3381
beforeEach(() => {
3482
disableDualStringBufferCaching();

test/SourceMapSource.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ const {
2121
exitStringInterningRange,
2222
disableDualStringBufferCaching
2323
} = require("../lib/helpers/stringBufferUtils");
24+
const crypto = require("crypto");
25+
const BatchedHash = require("webpack/lib/util/hash/BatchedHash");
26+
const createMd4 = require("webpack/lib/util/hash/md4");
27+
const createXXHash64 = require("webpack/lib/util/hash/xxhash64");
2428

2529
describe.each([
2630
{
@@ -508,4 +512,46 @@ describe.each([
508512
const buffer2 = sourceMapSource.buffer();
509513
expect(buffer2).toBe(buffer1);
510514
});
515+
516+
for (const hash of [
517+
["md5", [crypto.createHash("md5"), crypto.createHash("md5")]],
518+
["md4", [new BatchedHash(createMd4()), new BatchedHash(createMd4())]],
519+
[
520+
"xxhash64",
521+
[new BatchedHash(createXXHash64()), new BatchedHash(createXXHash64())]
522+
]
523+
]) {
524+
it(`should have the same hash (${hash[0]}) for string and Buffer`, () => {
525+
const sourceString = new SourceMapSource("hello world\n", "hello.txt", {
526+
version: 3,
527+
sources: ["hello-source.txt"],
528+
sourcesContent: ["hello world\n"],
529+
mappings: "AAAA",
530+
names: [],
531+
file: ""
532+
});
533+
const sourceBuffer = new SourceMapSource(
534+
Buffer.from("hello world\n"),
535+
"hello.txt",
536+
Buffer.from(
537+
JSON.stringify({
538+
version: 3,
539+
sources: ["hello-source.txt"],
540+
sourcesContent: ["hello world\n"],
541+
mappings: "AAAA",
542+
names: [],
543+
file: ""
544+
})
545+
)
546+
);
547+
548+
expect(sourceString.source()).toBe("hello world\n");
549+
expect(sourceString.buffer()).toEqual(sourceBuffer.buffer());
550+
551+
sourceString.updateHash(hash[1][0]);
552+
sourceBuffer.updateHash(hash[1][1]);
553+
554+
expect(hash[1][0].digest("hex")).toBe(hash[1][1].digest("hex"));
555+
});
556+
}
511557
});

0 commit comments

Comments
 (0)