mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2026-10-24 08:02:27 +00:00
improved findLineForOffset() performance using binary search and Map access
This commit is contained in:
1 parent
df93d0a706
commit
9b8e831cb4
4 files changed
+35
-27
No files matched your search
+22
-13
@@ -33,6 +33,7 @@ export class SourceFile {
|
||||
text: string;
|
||||
offset2loc: Map<number,SourceLine>; //{[offset:number]:number};
|
||||
line2offset: Map<number,number>; //{[line:number]:number};
|
||||
sortedOffsets: number[];
|
||||
|
||||
constructor(lines:SourceLine[], text:string) {
|
||||
lines = lines || [];
|
||||
@@ -44,25 +45,33 @@ export class SourceFile {
|
||||
if (info.offset >= 0) {
|
||||
// first line wins (is assigned to offset)
|
||||
// TODO: handle macros/includes w/ multiple offsets per line
|
||||
if (!this.offset2loc[info.offset])
|
||||
this.offset2loc[info.offset] = info;
|
||||
if (!this.line2offset[info.line])
|
||||
this.line2offset[info.line] = info.offset;
|
||||
if (!this.offset2loc.has(info.offset))
|
||||
this.offset2loc.set(info.offset, info);
|
||||
if (!this.line2offset.has(info.line))
|
||||
this.line2offset.set(info.line, info.offset);
|
||||
}
|
||||
}
|
||||
this.sortedOffsets = Array.from(this.offset2loc.keys()).sort((a,b)=>a-b);
|
||||
}
|
||||
// TODO: smarter about looking for source lines between two addresses
|
||||
// returns the line whose offset is nearest to (but not greater than) PC,
|
||||
// provided it is within `lookbehind` bytes; null otherwise
|
||||
findLineForOffset(PC:number, lookbehind:number) : SourceLine {
|
||||
if (this.offset2loc) {
|
||||
for (var i=0; i<=lookbehind; i++) {
|
||||
var loc = this.offset2loc[PC];
|
||||
if (loc) {
|
||||
return loc;
|
||||
}
|
||||
PC--;
|
||||
const offsets = this.sortedOffsets;
|
||||
// binary search for last offset <= PC
|
||||
var lo = 0, hi = offsets.length-1, ans = -1;
|
||||
while (lo <= hi) {
|
||||
var mid = (lo + hi) >> 1;
|
||||
if (offsets[mid] <= PC) {
|
||||
ans = mid;
|
||||
lo = mid + 1;
|
||||
} else {
|
||||
hi = mid - 1;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
if (ans < 0) return null;
|
||||
var off = offsets[ans];
|
||||
if (PC - off > lookbehind) return null;
|
||||
return this.offset2loc.get(off);
|
||||
}
|
||||
lineCount():number { return this.lines.length; }
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ export function resolveBreakpoint(bp: Breakpoint): ResolvedBreakpoint {
|
||||
let lst = current_project.getListingForFile(bp.file);
|
||||
let sf = lst && (lst.sourcefile || lst.assemblyfile);
|
||||
if (!sf) return { bp, error: "no debug info (build first?)" };
|
||||
pc = sf.line2offset[bp.line];
|
||||
pc = sf.line2offset.get(bp.line);
|
||||
if (!(pc >= 0)) return { bp, error: "line has no code" };
|
||||
} else {
|
||||
let r = parseTarget(bp.target, makeSymbolLookup());
|
||||
|
||||
@@ -294,7 +294,7 @@ export class SourceEditor implements ProjectView {
|
||||
if (effect.is(currentPcMarker.runToLine)) {
|
||||
const lineNum = effect.value;
|
||||
if (this.sourcefile && this.sourcefile.line2offset) {
|
||||
const pc = this.sourcefile.line2offset[lineNum];
|
||||
const pc = this.sourcefile.line2offset.get(lineNum);
|
||||
if (pc >= 0) {
|
||||
runToPC([pc]);
|
||||
}
|
||||
@@ -576,8 +576,7 @@ export class SourceEditor implements ProjectView {
|
||||
setTimingResult(result: CodeAnalyzer): void {
|
||||
if (this.sourcefile == null) return;
|
||||
var newBytes = new Map<number, string>();
|
||||
for (const line of Object.keys(this.sourcefile.line2offset)) {
|
||||
let pc = this.sourcefile.line2offset[line];
|
||||
for (const [line, pc] of this.sourcefile.line2offset) {
|
||||
let clocks = result.pc2clockrange[pc];
|
||||
var minclocks = clocks && clocks.minclocks;
|
||||
var maxclocks = clocks && clocks.maxclocks;
|
||||
@@ -589,7 +588,7 @@ export class SourceEditor implements ProjectView {
|
||||
s = minclocks + "-" + maxclocks;
|
||||
if (maxclocks == result.MAX_CLOCKS)
|
||||
s += "+";
|
||||
newBytes.set(parseInt(line), s);
|
||||
newBytes.set(line, s);
|
||||
}
|
||||
}
|
||||
this.editor.dispatch({
|
||||
@@ -719,7 +718,7 @@ export class SourceEditor implements ProjectView {
|
||||
getCursorPC(): number {
|
||||
var line = this.getCurrentLine();
|
||||
while (this.sourcefile && line >= 0) {
|
||||
var pc = this.sourcefile.line2offset[line];
|
||||
var pc = this.sourcefile.line2offset.get(line);
|
||||
if (pc >= 0) return pc;
|
||||
line--;
|
||||
}
|
||||
|
||||
@@ -113,9 +113,9 @@ describe('SourceFile Class', function () {
|
||||
];
|
||||
const sf = new SourceFile(lines, '');
|
||||
|
||||
assert.ok(sf.offset2loc[0]);
|
||||
assert.ok(sf.offset2loc[2]);
|
||||
assert.ok(sf.offset2loc[4]);
|
||||
assert.ok(sf.offset2loc.get(0));
|
||||
assert.ok(sf.offset2loc.get(2));
|
||||
assert.ok(sf.offset2loc.get(4));
|
||||
});
|
||||
|
||||
it('should build line to offset mapping', function () {
|
||||
@@ -126,9 +126,9 @@ describe('SourceFile Class', function () {
|
||||
];
|
||||
const sf = new SourceFile(lines, '');
|
||||
|
||||
assert.strictEqual(sf.line2offset[1], 0);
|
||||
assert.strictEqual(sf.line2offset[2], 2);
|
||||
assert.strictEqual(sf.line2offset[3], 4);
|
||||
assert.strictEqual(sf.line2offset.get(1), 0);
|
||||
assert.strictEqual(sf.line2offset.get(2), 2);
|
||||
assert.strictEqual(sf.line2offset.get(3), 4);
|
||||
});
|
||||
|
||||
it('should handle negative offsets gracefully', function () {
|
||||
@@ -140,8 +140,8 @@ describe('SourceFile Class', function () {
|
||||
const sf = new SourceFile(lines, '');
|
||||
|
||||
// Negative offsets should not be added to mapping
|
||||
assert.ok(!sf.offset2loc[-1]);
|
||||
assert.ok(sf.offset2loc[0]);
|
||||
assert.ok(!sf.offset2loc.has(-1));
|
||||
assert.ok(sf.offset2loc.get(0));
|
||||
});
|
||||
|
||||
it('findLineForOffset should locate source line by address', function () {
|
||||
|
||||
Reference in new issue
Block a user