From 038d13a33c61d0401f10b31fa2b06199b726939a Mon Sep 17 00:00:00 2001 From: Valentin Semirulnik Date: Sun, 1 Feb 2026 21:03:02 +0400 Subject: [PATCH] perf: add fast path for single-char VLQ encode Add lookup table for VLQ encoding values -15 to 15, which encode to a single base64 character. This avoids function call overhead for the most common small delta values in source maps. Results: -3% serialization time Co-Authored-By: Claude Opus 4.5 --- lib/source-map-generator.js | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/source-map-generator.js b/lib/source-map-generator.js index bab04ff8..fb9a8fa4 100644 --- a/lib/source-map-generator.js +++ b/lib/source-map-generator.js @@ -323,6 +323,17 @@ SourceMapGenerator.prototype._validateMapping = } }; +// Fast VLQ encode lookup for values -15 to 15 (single char output) +var vlqEncodeTable = []; +(function() { + var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + for (var i = -15; i <= 15; i++) { + // VLQ signed: negative becomes odd, positive becomes even + var vlq = i < 0 ? ((-i) << 1) + 1 : (i << 1); + vlqEncodeTable[i + 15] = chars[vlq]; + } +})(); + /** * Serialize the accumulated mappings in to the stream of base 64 VLQs * specified by the source map format. @@ -336,7 +347,7 @@ SourceMapGenerator.prototype._serializeMappings = var previousName = 0; var previousSource = 0; var result = ''; - var next; + var next, val; var mapping; var nameIdx; var sourceIdx; @@ -362,27 +373,29 @@ SourceMapGenerator.prototype._serializeMappings = } } - next += base64VLQ.encode(mapping.generatedColumn - - previousGeneratedColumn); + val = mapping.generatedColumn - previousGeneratedColumn; + next += (val >= -15 && val <= 15) ? vlqEncodeTable[val + 15] : base64VLQ.encode(val); previousGeneratedColumn = mapping.generatedColumn; if (mapping.source != null) { sourceIdx = this._sources.indexOf(mapping.source); - next += base64VLQ.encode(sourceIdx - previousSource); + val = sourceIdx - previousSource; + next += (val >= -15 && val <= 15) ? vlqEncodeTable[val + 15] : base64VLQ.encode(val); previousSource = sourceIdx; // lines are stored 0-based in SourceMap spec version 3 - next += base64VLQ.encode(mapping.originalLine - 1 - - previousOriginalLine); + val = mapping.originalLine - 1 - previousOriginalLine; + next += (val >= -15 && val <= 15) ? vlqEncodeTable[val + 15] : base64VLQ.encode(val); previousOriginalLine = mapping.originalLine - 1; - next += base64VLQ.encode(mapping.originalColumn - - previousOriginalColumn); + val = mapping.originalColumn - previousOriginalColumn; + next += (val >= -15 && val <= 15) ? vlqEncodeTable[val + 15] : base64VLQ.encode(val); previousOriginalColumn = mapping.originalColumn; if (mapping.name != null) { nameIdx = this._names.indexOf(mapping.name); - next += base64VLQ.encode(nameIdx - previousName); + val = nameIdx - previousName; + next += (val >= -15 && val <= 15) ? vlqEncodeTable[val + 15] : base64VLQ.encode(val); previousName = nameIdx; } }