Last active
December 15, 2023 21:43
-
-
Save jridgewell/9cf1800a21b25e7cc0af56649178b174 to your computer and use it in GitHub Desktop.
Source Map encode with TypedArrays #jsbench #jsperf (https://jsbench.github.io/#9cf1800a21b25e7cc0af56649178b174) #jsbench #jsperf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"/> | |
<title>Source Map encode with TypedArrays #jsbench #jsperf</title> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/benchmark/1.0.0/benchmark.min.js"></script> | |
<script src="./suite.js"></script> | |
</head> | |
<body> | |
<h1>Open the console to view the results</h1> | |
<h2><code>cmd + alt + j</code> or <code>ctrl + alt + j</code></h2> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
(function (factory) { | |
if (typeof Benchmark !== "undefined") { | |
factory(Benchmark); | |
} else { | |
factory(require("benchmark")); | |
} | |
})(function (Benchmark) { | |
var suite = new Benchmark.Suite; | |
Benchmark.prototype.setup = function () { | |
const comma = ','.charCodeAt(0); | |
const semicolon = ';'.charCodeAt(0); | |
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; | |
const intToChar = new Uint8Array(64); // 64 possible chars. | |
const charToInt = new Uint8Array(128); // z is 122 in ASCII | |
for (let i = 0; i < chars.length; i++) { | |
const c = chars.charCodeAt(i); | |
intToChar[i] = c; | |
charToInt[c] = i; | |
} | |
// Provide a fallback for older environments. | |
const td = typeof TextDecoder !== 'undefined' | |
? /* #__PURE__ */ new TextDecoder() | |
: typeof Buffer !== 'undefined' | |
? { | |
decode(buf) { | |
const out = Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength); | |
return out.toString(); | |
}, | |
} | |
: { | |
decode(buf) { | |
let out = ''; | |
for (let i = 0; i < buf.length; i++) { | |
out += String.fromCharCode(buf[i]); | |
} | |
return out; | |
}, | |
}; | |
function decode(mappings) { | |
const state = new Int32Array(5); | |
const decoded = []; | |
let index = 0; | |
do { | |
const semi = indexOf(mappings, index); | |
const line = []; | |
let sorted = true; | |
let lastCol = 0; | |
state[0] = 0; | |
for (let i = index; i < semi; i++) { | |
let seg; | |
i = decodeInteger(mappings, i, state, 0); // genColumn | |
const col = state[0]; | |
if (col < lastCol) | |
sorted = false; | |
lastCol = col; | |
if (hasMoreVlq(mappings, i, semi)) { | |
i = decodeInteger(mappings, i, state, 1); // sourcesIndex | |
i = decodeInteger(mappings, i, state, 2); // sourceLine | |
i = decodeInteger(mappings, i, state, 3); // sourceColumn | |
if (hasMoreVlq(mappings, i, semi)) { | |
i = decodeInteger(mappings, i, state, 4); // namesIndex | |
seg = [col, state[1], state[2], state[3], state[4]]; | |
} | |
else { | |
seg = [col, state[1], state[2], state[3]]; | |
} | |
} | |
else { | |
seg = [col]; | |
} | |
line.push(seg); | |
} | |
if (!sorted) | |
sort(line); | |
decoded.push(line); | |
index = semi + 1; | |
} while (index <= mappings.length); | |
return decoded; | |
} | |
function indexOf(mappings, index) { | |
const idx = mappings.indexOf(';', index); | |
return idx === -1 ? mappings.length : idx; | |
} | |
function decodeInteger(mappings, pos, state, j) { | |
let value = 0; | |
let shift = 0; | |
let integer = 0; | |
do { | |
const c = mappings.charCodeAt(pos++); | |
integer = charToInt[c]; | |
value |= (integer & 31) << shift; | |
shift += 5; | |
} while (integer & 32); | |
const shouldNegate = value & 1; | |
value >>>= 1; | |
if (shouldNegate) { | |
value = -0x80000000 | -value; | |
} | |
state[j] += value; | |
return pos; | |
} | |
function hasMoreVlq(mappings, i, length) { | |
if (i >= length) | |
return false; | |
return mappings.charCodeAt(i) !== comma; | |
} | |
function sort(line) { | |
line.sort(sortComparator); | |
} | |
function sortComparator(a, b) { | |
return a[0] - b[0]; | |
} | |
function encodeInteger(buf, pos, state, segment, j) { | |
const next = segment[j]; | |
let num = next - state[j]; | |
state[j] = next; | |
num = num < 0 ? (-num << 1) | 1 : num << 1; | |
do { | |
let clamped = num & 0b011111; | |
num >>>= 5; | |
if (num > 0) | |
clamped |= 0b100000; | |
buf[pos++] = intToChar[clamped]; | |
} while (num > 0); | |
return pos; | |
} | |
function encodeSingle(decoded) { | |
const state = new Int32Array(5); | |
const bufLength = 1024 * 16; | |
const subLength = bufLength - 36; | |
const buf = new Uint8Array(bufLength); | |
const sub = buf.subarray(0, subLength); | |
let pos = 0; | |
let out = ''; | |
for (let i = 0; i < decoded.length; i++) { | |
const line = decoded[i]; | |
if (i > 0) { | |
if (pos === bufLength) { | |
out += td.decode(buf); | |
pos = 0; | |
} | |
buf[pos++] = semicolon; | |
} | |
if (line.length === 0) | |
continue; | |
state[0] = 0; | |
for (let j = 0; j < line.length; j++) { | |
const segment = line[j]; | |
// We can push up to 5 ints, each int can take at most 7 chars, and we | |
// may push a comma. | |
if (pos > subLength) { | |
out += td.decode(sub); | |
buf.copyWithin(0, subLength, pos); | |
pos -= subLength; | |
} | |
if (j > 0) | |
buf[pos++] = comma; | |
pos = encodeInteger(buf, pos, state, segment, 0); // genColumn | |
if (segment.length === 1) | |
continue; | |
pos = encodeInteger(buf, pos, state, segment, 1); // sourcesIndex | |
pos = encodeInteger(buf, pos, state, segment, 2); // sourceLine | |
pos = encodeInteger(buf, pos, state, segment, 3); // sourceColumn | |
if (segment.length === 4) | |
continue; | |
pos = encodeInteger(buf, pos, state, segment, 4); // namesIndex | |
} | |
} | |
return out + td.decode(buf.subarray(0, pos)); | |
} | |
function encodeMultiple(decoded) { | |
const state = new Int32Array(5); | |
const bufLength = 1024 * 16; | |
const subLength = bufLength - 36; | |
const buf = new Uint8Array(bufLength); | |
let pos = 0; | |
let out = ''; | |
for (let i = 0; i < decoded.length; i++) { | |
const line = decoded[i]; | |
if (i > 0) { | |
if (pos === bufLength) { | |
out += td.decode(buf); | |
pos = 0; | |
} | |
buf[pos++] = semicolon; | |
} | |
if (line.length === 0) | |
continue; | |
state[0] = 0; | |
for (let j = 0; j < line.length; j++) { | |
const segment = line[j]; | |
// We can push up to 5 ints, each int can take at most 7 chars, and we | |
// may push a comma. | |
if (pos > subLength) { | |
out += td.decode(buf.subarray(0, pos)); | |
pos = 0; | |
} | |
if (j > 0) | |
buf[pos++] = comma; | |
pos = encodeInteger(buf, pos, state, segment, 0); // genColumn | |
if (segment.length === 1) | |
continue; | |
pos = encodeInteger(buf, pos, state, segment, 1); // sourcesIndex | |
pos = encodeInteger(buf, pos, state, segment, 2); // sourceLine | |
pos = encodeInteger(buf, pos, state, segment, 3); // sourceColumn | |
if (segment.length === 4) | |
continue; | |
pos = encodeInteger(buf, pos, state, segment, 4); // namesIndex | |
} | |
} | |
return out + td.decode(buf.subarray(0, pos)); | |
} | |
const mappings = decode("") | |
}; | |
suite.add("Encoding using a single subarray", function () { | |
// Encoding using a single subarray | |
encodeSingle(mappings); | |
}); | |
suite.add("Encoding using a throwaway subarrays", function () { | |
// Encoding using a throwaway subarrays | |
encodeMultiple(mappings); | |
}); | |
suite.on("cycle", function (evt) { | |
console.log(" - " + evt.target); | |
}); | |
suite.on("complete", function (evt) { | |
console.log(new Array(30).join("-")); | |
var results = evt.currentTarget.sort(function (a, b) { | |
return b.hz - a.hz; | |
}); | |
results.forEach(function (item) { | |
console.log((idx + 1) + ". " + item); | |
}); | |
}); | |
console.log("Source Map encode with TypedArrays #jsbench #jsperf"); | |
console.log(new Array(30).join("-")); | |
suite.run(); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment