/// SPDX-License-Identifier: GPL-3.0-or-later /// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones export class SpanResult { readonly searchTarget: number; readonly start: number; readonly items: Array<{ end: number, item: T }> = []; constructor(searchTarget: number, start: number) { this.searchTarget = searchTarget; this.start = start; } get offset(): number { return this.searchTarget - this.start; } get firstItem(): T { return this.items[0].item; } get lastItem(): T { return this.items[this.items.length - 1].item; } } export class SpanIndex { readonly index: Array<[number, Array<[number, T]>]> = []; get(pos: number): SpanResult | null { if (this.index.length === 0) return null; let lo = 0; let hi = this.index.length; // console.log(`\nsearching for ${target}`); while (true) { if (lo === hi) { if (lo === 0) return null; const e = this.index[lo - 1]; if (e[0] > pos) throw new Error("INTERNAL ERROR: bad binary search (1)"); if (this.index[lo]?.[0] <= pos) throw new Error("INTERNAL ERROR: bad binary search (2)"); // console.log(`found ${JSON.stringify(e)}, ${JSON.stringify(items[lo] ?? null)}`); const r = new SpanResult(pos, e[0]); e[1].forEach(([end, item]) => { if (pos < end) { r.items.push({ end, item }); } }); return (r.items.length > 0) ? r : null; } const mid = (lo + hi) >> 1; const e = this.index[mid]; // console.log(`${target} lo ${lo} hi ${hi} mid ${mid} probe ${JSON.stringify([e[0], e[1].target])}`); if (e[0] <= pos) { lo = mid + 1; } else { hi = mid; } } } } export class SpanInfo { readonly spans: Map> = new Map(); add(start: number, end: number, t: T) { if (!this.spans.has(start)) { this.spans.set(start, []); } this.spans.get(start)!.push([end, t]); } index(): SpanIndex { const i = new SpanIndex(); this.spans.forEach((ends, start) => { ends.sort((a, b) => a[0] - b[0]); i.index.push([start, ends]); }); i.index.sort((a, b) => a[0] - b[0]); return i; } }