-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
perf: merge context ranges as they're found
- Loading branch information
Showing
3 changed files
with
116 additions
and
25 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import test from 'ava'; | ||
|
||
import mergeRange from './mergeRange'; | ||
|
||
test('inserts a new range before the existing ones', t => { | ||
const result = mergeRange([[1, 2], [2, 3]], 0, 1); | ||
t.deepEqual(result, [[0, 1], [1, 2], [2, 3]]); | ||
}); | ||
|
||
test('inserts in between two ranges', t => { | ||
const result = mergeRange([[0, 2], [4, 6]], 2, 4); | ||
t.deepEqual(result, [[0, 2], [2, 4], [4, 6]]); | ||
}); | ||
|
||
test('inserts after the last range', t => { | ||
const result = mergeRange([[0, 2], [4, 6]], 6, 8); | ||
t.deepEqual(result, [[0, 2], [4, 6], [6, 8]]); | ||
}); | ||
|
||
test('extends the beginning of a range', t => { | ||
const result = mergeRange([[0, 2], [4, 6]], 3, 5); | ||
t.deepEqual(result, [[0, 2], [3, 6]]); | ||
}); | ||
|
||
test('extends the end of a range', t => { | ||
const result = mergeRange([[0, 2], [4, 6]], 1, 4); | ||
t.deepEqual(result, [[0, 4], [4, 6]]); | ||
}); | ||
|
||
test('extends the last range', t => { | ||
const result = mergeRange([[0, 2], [4, 6]], 5, 7); | ||
t.deepEqual(result, [[0, 2], [4, 7]]); | ||
}); | ||
|
||
test('connects two ranges', t => { | ||
const result = mergeRange([[0, 2], [4, 6]], 1, 5); | ||
t.deepEqual(result, [[0, 6]]); | ||
}); | ||
|
||
test('connects more than two ranges', t => { | ||
const result = mergeRange([[0, 2], [4, 6], [8, 10], [12, 14]], 1, 10); | ||
t.deepEqual(result, [[0, 10], [12, 14]]); | ||
}); |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/** | ||
* Merges the range defined by the provided start and end into the list of | ||
* existing ranges. The merge is done in place on the existing range for | ||
* performance and is also returned. | ||
* | ||
* @param ranges Existing range list | ||
* @param newRangeStart Start position of the range to merge, inclusive | ||
* @param newRangeEnd End position of range to merge, exclusive | ||
*/ | ||
export default function mergeRange(ranges: [number, number][], newRangeStart: number, newRangeEnd: number): [number, number][] { | ||
let inRange = false; | ||
for (let i = 0; i < ranges.length; i++) { | ||
const range = ranges[i]; | ||
if (!inRange) { | ||
if (newRangeEnd <= range[0]) { | ||
// Case 1: New range is before the search range | ||
ranges.splice(i, 0, [newRangeStart, newRangeEnd]); | ||
return ranges; | ||
} else if (newRangeEnd <= range[1]) { | ||
// Case 2: New range is either wholly contained within the | ||
// search range or overlaps with the front of it | ||
range[0] = Math.min(newRangeStart, range[0]); | ||
return ranges; | ||
} else if (newRangeStart < range[1]) { | ||
// Case 3: New range either wholly contains the search range | ||
// or overlaps with the end of it | ||
range[0] = Math.min(newRangeStart, range[0]); | ||
inRange = true; | ||
} else { | ||
// Case 4: New range starts after the search range | ||
continue; | ||
} | ||
} else { | ||
if (newRangeEnd <= range[0]) { | ||
// Case 5: New range extends from previous range but doesn't | ||
// reach the current one | ||
ranges[i - 1][1] = newRangeEnd; | ||
return ranges; | ||
} else if (newRangeEnd <= range[1]) { | ||
// Case 6: New range extends from prvious range into the | ||
// current range | ||
ranges[i - 1][1] = Math.max(newRangeEnd, range[1]); | ||
ranges.splice(i, 1); | ||
inRange = false; | ||
return ranges; | ||
} else { | ||
// Case 7: New range extends from previous range past the | ||
// end of the current range | ||
ranges.splice(i, 1); | ||
i--; | ||
} | ||
} | ||
} | ||
|
||
if (inRange) { | ||
// Case 8: New range extends past the last existing range | ||
ranges[ranges.length - 1][1] = newRangeEnd; | ||
} else { | ||
// Case 9: New range starts after the last existing range | ||
ranges.push([newRangeStart, newRangeEnd]); | ||
} | ||
|
||
return ranges; | ||
} |