Skip to content

Commit 375ad58

Browse files
committed
Fix accidentally quadratic algorithm.
Previously we would only save the position of the last known line break, so calling source_location() for name positions within a single long line would keep searching from that point for the next line break and therefore take O(n²) time. We now save the full (line, column) result from the last call. servo/servo#9897 (comment) servo/servo#9897 (comment)
1 parent 5cc897f commit 375ad58

File tree

1 file changed

+21
-24
lines changed

1 file changed

+21
-24
lines changed

src/tokenizer.rs

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ pub struct Tokenizer<'a> {
210210
/// Counted in bytes, not code points. From 0.
211211
position: usize,
212212
/// Cache for `source_location()`
213-
last_known_line_break: Cell<(usize, usize)>,
213+
last_known_source_location: Cell<(SourcePosition, SourceLocation)>,
214214
var_functions: SeenStatus,
215215
viewport_percentages: SeenStatus,
216216
}
@@ -229,7 +229,8 @@ impl<'a> Tokenizer<'a> {
229229
Tokenizer {
230230
input: input,
231231
position: 0,
232-
last_known_line_break: Cell::new((1, 0)),
232+
last_known_source_location: Cell::new((SourcePosition(0),
233+
SourceLocation { line: 1, column: 1 })),
233234
var_functions: SeenStatus::DontCare,
234235
viewport_percentages: SeenStatus::DontCare,
235236
}
@@ -292,37 +293,33 @@ impl<'a> Tokenizer<'a> {
292293

293294
pub fn source_location(&self, position: SourcePosition) -> SourceLocation {
294295
let target = position.0;
295-
let mut line_number;
296+
let mut location;
296297
let mut position;
297-
let (last_known_line_number, position_after_last_known_newline) =
298-
self.last_known_line_break.get();
299-
if target >= position_after_last_known_newline {
300-
position = position_after_last_known_newline;
301-
line_number = last_known_line_number;
298+
let (SourcePosition(last_known_position), last_known_location) =
299+
self.last_known_source_location.get();
300+
if target >= last_known_position {
301+
position = last_known_position;
302+
location = last_known_location;
302303
} else {
304+
// For now we’re only traversing the source *forwards* to count newlines.
305+
// So if the requested position is before the last known one,
306+
// start over from the beginning.
303307
position = 0;
304-
line_number = 1;
308+
location = SourceLocation { line: 1, column: 1 };
305309
}
306310
let mut source = &self.input[position..target];
307-
while let Some(newline_position) = source.find(&['\n', '\r', '\x0C'][..]) {
311+
while let Some(newline_position) = source.find(|c| matches!(c, '\n' | '\r' | '\x0C')) {
308312
let offset = newline_position +
309-
if source[newline_position..].starts_with("\r\n") {
310-
2
311-
} else {
312-
1
313-
};
313+
if source[newline_position..].starts_with("\r\n") { 2 } else { 1 };
314314
source = &source[offset..];
315315
position += offset;
316-
line_number += 1;
316+
location.line += 1;
317+
location.column = 1;
317318
}
318319
debug_assert!(position <= target);
319-
self.last_known_line_break.set((line_number, position));
320-
SourceLocation {
321-
line: line_number,
322-
// `target == position` when `target` is at the beginning of the line,
323-
// so add 1 so that the column numbers start at 1.
324-
column: target - position + 1,
325-
}
320+
location.column += target - position;
321+
self.last_known_source_location.set((SourcePosition(target), location));
322+
location
326323
}
327324

328325
#[inline]
@@ -385,7 +382,7 @@ pub struct SourceLocation {
385382
/// The line number, starting at 1 for the first line.
386383
pub line: usize,
387384

388-
/// The column number within a line, starting at 1 for the character of the line.
385+
/// The column number within a line, starting at 1 for first the character of the line.
389386
pub column: usize,
390387
}
391388

0 commit comments

Comments
 (0)