import type { SourceMapSegment, ReverseSegment } from './sourcemap-segment'; import { COLUMN } from './sourcemap-segment'; export type MemoState = { lastKey: number; lastNeedle: number; lastIndex: number; }; export let found = false; /** * A binary search implementation that returns the index if a match is found. * If no match is found, then the left-index (the index associated with the item that comes just * before the desired index) is returned. To maintain proper sort order, a splice would happen at * the next index: * * ```js * const array = [1, 3]; * const needle = 2; * const index = binarySearch(array, needle, (item, needle) => item - needle); * * assert.equal(index, 0); * array.splice(index + 1, 0, needle); * assert.deepEqual(array, [1, 2, 3]); * ``` */ export function binarySearch( haystack: SourceMapSegment[] | ReverseSegment[], needle: number, low: number, high: number, ): number { while (low <= high) { const mid = low + ((high - low) >> 1); const cmp = haystack[mid][COLUMN] - needle; if (cmp === 0) { found = true; return mid; } if (cmp < 0) { low = mid + 1; } else { high = mid - 1; } } found = false; return low - 1; } export function upperBound( haystack: SourceMapSegment[] | ReverseSegment[], needle: number, index: number, ): number { for (let i = index + 1; i < haystack.length; index = i++) { if (haystack[i][COLUMN] !== needle) break; } return index; } export function lowerBound( haystack: SourceMapSegment[] | ReverseSegment[], needle: number, index: number, ): number { for (let i = index - 1; i >= 0; index = i--) { if (haystack[i][COLUMN] !== needle) break; } return index; } export function memoizedState(): MemoState { return { lastKey: -1, lastNeedle: -1, lastIndex: -1, }; } /** * This overly complicated beast is just to record the last tested line/column and the resulting * index, allowing us to skip a few tests if mappings are monotonically increasing. */ export function memoizedBinarySearch( haystack: SourceMapSegment[] | ReverseSegment[], needle: number, state: MemoState, key: number, ): number { const { lastKey, lastNeedle, lastIndex } = state; let low = 0; let high = haystack.length - 1; if (key === lastKey) { if (needle === lastNeedle) { found = lastIndex !== -1 && haystack[lastIndex][COLUMN] === needle; return lastIndex; } if (needle >= lastNeedle) { // lastIndex may be -1 if the previous needle was not found. low = lastIndex === -1 ? 0 : lastIndex; } else { high = lastIndex; } } state.lastKey = key; state.lastNeedle = needle; return (state.lastIndex = binarySearch(haystack, needle, low, high)); }