From 15604f78868c487b94596e0396a83e5b8082e9cd Mon Sep 17 00:00:00 2001 From: Mihai Parparita Date: Tue, 19 Nov 2019 15:29:26 -0800 Subject: [PATCH] Reduce memory overhead of sourcemapLocations It was using an object to store positions in the string. This meant that those offsets were first stringified and then added as object properties. For a 200K JS code file (as parsed by rollup), the sourcemapLocations object was taking up 980K. Switching it to a BitSet that uses one bit per offset makes it take up 36K instead. Fixes Rich-Harris/magic-string#167 --- src/BitSet.js | 15 +++++++++++++++ src/MagicString.js | 9 ++++----- src/utils/Mappings.js | 2 +- 3 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 src/BitSet.js diff --git a/src/BitSet.js b/src/BitSet.js new file mode 100644 index 0000000..eccd909 --- /dev/null +++ b/src/BitSet.js @@ -0,0 +1,15 @@ +export default class BitSet { + constructor(arg) { + this.bits = arg instanceof BitSet ? arg.bits.slice() : []; + } + + add(n) { + this.bits[Math.floor(n / BITS)] |= 1 << n % BITS; + } + + has(n) { + return !!(this.bits[Math.floor(n / BITS)] & (1 << n % BITS)); + } +} + +const BITS = 32; diff --git a/src/MagicString.js b/src/MagicString.js index 18bf1f4..14f5cb2 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -1,3 +1,4 @@ +import BitSet from './BitSet.js'; import Chunk from './Chunk.js'; import SourceMap from './SourceMap.js'; import guessIndent from './utils/guessIndent.js'; @@ -30,7 +31,7 @@ export default class MagicString { byEnd: { writable: true, value: {} }, filename: { writable: true, value: options.filename }, indentExclusionRanges: { writable: true, value: options.indentExclusionRanges }, - sourcemapLocations: { writable: true, value: {} }, + sourcemapLocations: { writable: true, value: new BitSet() }, storedNames: { writable: true, value: {} }, indentStr: { writable: true, value: guessIndent(string) } }); @@ -44,7 +45,7 @@ export default class MagicString { } addSourcemapLocation(char) { - this.sourcemapLocations[char] = true; + this.sourcemapLocations.add(char); } append(content) { @@ -121,9 +122,7 @@ export default class MagicString { cloned.indentExclusionRanges = this.indentExclusionRanges.slice(); } - Object.keys(this.sourcemapLocations).forEach(loc => { - cloned.sourcemapLocations[loc] = true; - }); + cloned.sourcemapLocations = new BitSet(this.sourcemapLocations); cloned.intro = this.intro; cloned.outro = this.outro; diff --git a/src/utils/Mappings.js b/src/utils/Mappings.js index 6026200..7de80d9 100644 --- a/src/utils/Mappings.js +++ b/src/utils/Mappings.js @@ -28,7 +28,7 @@ export default class Mappings { let first = true; while (originalCharIndex < chunk.end) { - if (this.hires || first || sourcemapLocations[originalCharIndex]) { + if (this.hires || first || sourcemapLocations.has(originalCharIndex)) { this.rawSegments.push([this.generatedCodeColumn, sourceIndex, loc.line, loc.column]); }