Skip to content

Commit 4013c47

Browse files
crisptrutskiChris Truter
authored andcommitted
Replace _mappings array with data structure
Optimized for best case (called with sorted mappings), with comparable performance otherwise.
1 parent 9c77f7a commit 4013c47

File tree

3 files changed

+90
-14
lines changed

3 files changed

+90
-14
lines changed

lib/source-map/mapping-list.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/* -*- Mode: js; js-indent-level: 2; -*- */
2+
/*
3+
* Copyright 2014 Mozilla Foundation and contributors
4+
* Licensed under the New BSD license. See LICENSE or:
5+
* http://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
if (typeof define !== 'function') {
8+
var define = require('amdefine')(module, require);
9+
}
10+
define(function (require, exports, module) {
11+
12+
var util = require('./util');
13+
14+
/**
15+
* Determine whether mappingB is after mappingA with respect to generated
16+
* position.
17+
*/
18+
function generatedPositionAfter(mappingA, mappingB) {
19+
// Optimized for most common case
20+
var lineA = mappingA.generatedLine;
21+
var lineB = mappingB.generatedLine;
22+
var columnA = mappingA.generatedColumn;
23+
var columnB = mappingB.generatedColumn;
24+
return lineB > lineA || lineB == lineA && columnB >= columnA ||
25+
util.compareByGeneratedPositions(mappingA, mappingB) <= 0;
26+
}
27+
28+
/**
29+
* A data structure to provide a sorted view of accumulated mappings in a
30+
* performance conscious manner. It trades a neglibable overhead in general
31+
* case for a large speedup in case of mappings being added in order.
32+
*/
33+
function MappingList() {
34+
this._array = [];
35+
this._sorted = true;
36+
// Serves as infimum
37+
this._last = {generatedLine: -1, generatedColumn: 0};
38+
}
39+
40+
/**
41+
* Iterate through internal items. NOTE: order is NOT guaranteed.
42+
*/
43+
MappingList.prototype.unsortedForEach = function MappingList_forEach() {
44+
Array.prototype.forEach.apply(this._array, arguments);
45+
};
46+
47+
/**
48+
* Add the given source mapping.
49+
*
50+
* @param Object aMapping
51+
*/
52+
MappingList.prototype.add = function MappingList_add(aMapping) {
53+
var mapping;
54+
if (generatedPositionAfter(this._last, aMapping)) {
55+
this._last = aMapping;
56+
this._array.push(aMapping);
57+
} else {
58+
this._sorted = false;
59+
this._array.push(aMapping);
60+
}
61+
};
62+
63+
/**
64+
* Returns the flat array representation of this structure.
65+
*
66+
* WARNING: Returns internal data without copying, for performance
67+
*/
68+
MappingList.prototype.toArray = function MappingList_toArray() {
69+
if (this._sorted) {
70+
return this._array;
71+
} else {
72+
// Sort runs in place
73+
this._sorted = true;
74+
return this._array.sort(util.compareByGeneratedPositions);
75+
}
76+
};
77+
78+
exports.MappingList = MappingList;
79+
80+
});

lib/source-map/source-map-consumer.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ define(function (require, exports, module) {
102102
smc.sourceRoot);
103103
smc.file = aSourceMap._file;
104104

105-
smc.__generatedMappings = aSourceMap._mappings.slice()
105+
smc.__generatedMappings = aSourceMap._mappings.toArray()
106106
.sort(util.compareByGeneratedPositions);
107-
smc.__originalMappings = aSourceMap._mappings.slice()
107+
smc.__originalMappings = aSourceMap._mappings.toArray()
108108
.sort(util.compareByOriginalPositions);
109109

110110
return smc;

lib/source-map/source-map-generator.js

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ define(function (require, exports, module) {
1212
var base64VLQ = require('./base64-vlq');
1313
var util = require('./util');
1414
var ArraySet = require('./array-set').ArraySet;
15+
var MappingList = require('./mapping-list').MappingList;
1516

1617
/**
1718
* An instance of the SourceMapGenerator represents a source map which is
@@ -29,7 +30,7 @@ define(function (require, exports, module) {
2930
this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);
3031
this._sources = new ArraySet();
3132
this._names = new ArraySet();
32-
this._mappings = [];
33+
this._mappings = new MappingList();
3334
this._sourcesContents = null;
3435
}
3536

@@ -109,7 +110,7 @@ define(function (require, exports, module) {
109110
this._names.add(name);
110111
}
111112

112-
this._mappings.push({
113+
this._mappings.add({
113114
generatedLine: generated.line,
114115
generatedColumn: generated.column,
115116
originalLine: original != null && original.line,
@@ -186,7 +187,7 @@ define(function (require, exports, module) {
186187
var newNames = new ArraySet();
187188

188189
// Find mappings for the "sourceFile"
189-
this._mappings.forEach(function (mapping) {
190+
this._mappings.unsortedForEach(function (mapping) {
190191
if (mapping.source === sourceFile && mapping.originalLine != null) {
191192
// Check if it can be mapped by the source map, then update the mapping.
192193
var original = aSourceMapConsumer.originalPositionFor({
@@ -292,15 +293,10 @@ define(function (require, exports, module) {
292293
var result = '';
293294
var mapping;
294295

295-
// The mappings must be guaranteed to be in sorted order before we start
296-
// serializing them or else the generated line numbers (which are defined
297-
// via the ';' separators) will be all messed up. Note: it might be more
298-
// performant to maintain the sorting as we insert them, rather than as we
299-
// serialize them, but the big O is the same either way.
300-
this._mappings.sort(util.compareByGeneratedPositions);
296+
var mappings = this._mappings.toArray();
301297

302-
for (var i = 0, len = this._mappings.length; i < len; i++) {
303-
mapping = this._mappings[i];
298+
for (var i = 0, len = mappings.length; i < len; i++) {
299+
mapping = mappings[i];
304300

305301
if (mapping.generatedLine !== previousGeneratedLine) {
306302
previousGeneratedColumn = 0;
@@ -311,7 +307,7 @@ define(function (require, exports, module) {
311307
}
312308
else {
313309
if (i > 0) {
314-
if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) {
310+
if (!util.compareByGeneratedPositions(mapping, mappings[i - 1])) {
315311
continue;
316312
}
317313
result += ',';

0 commit comments

Comments
 (0)