市场夺宝奇兵
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

115 lines
2.7 KiB

  1. import type { SourceMapSegment, ReverseSegment } from './sourcemap-segment';
  2. import { COLUMN } from './sourcemap-segment';
  3. export type MemoState = {
  4. lastKey: number;
  5. lastNeedle: number;
  6. lastIndex: number;
  7. };
  8. export let found = false;
  9. /**
  10. * A binary search implementation that returns the index if a match is found.
  11. * If no match is found, then the left-index (the index associated with the item that comes just
  12. * before the desired index) is returned. To maintain proper sort order, a splice would happen at
  13. * the next index:
  14. *
  15. * ```js
  16. * const array = [1, 3];
  17. * const needle = 2;
  18. * const index = binarySearch(array, needle, (item, needle) => item - needle);
  19. *
  20. * assert.equal(index, 0);
  21. * array.splice(index + 1, 0, needle);
  22. * assert.deepEqual(array, [1, 2, 3]);
  23. * ```
  24. */
  25. export function binarySearch(
  26. haystack: SourceMapSegment[] | ReverseSegment[],
  27. needle: number,
  28. low: number,
  29. high: number,
  30. ): number {
  31. while (low <= high) {
  32. const mid = low + ((high - low) >> 1);
  33. const cmp = haystack[mid][COLUMN] - needle;
  34. if (cmp === 0) {
  35. found = true;
  36. return mid;
  37. }
  38. if (cmp < 0) {
  39. low = mid + 1;
  40. } else {
  41. high = mid - 1;
  42. }
  43. }
  44. found = false;
  45. return low - 1;
  46. }
  47. export function upperBound(
  48. haystack: SourceMapSegment[] | ReverseSegment[],
  49. needle: number,
  50. index: number,
  51. ): number {
  52. for (let i = index + 1; i < haystack.length; index = i++) {
  53. if (haystack[i][COLUMN] !== needle) break;
  54. }
  55. return index;
  56. }
  57. export function lowerBound(
  58. haystack: SourceMapSegment[] | ReverseSegment[],
  59. needle: number,
  60. index: number,
  61. ): number {
  62. for (let i = index - 1; i >= 0; index = i--) {
  63. if (haystack[i][COLUMN] !== needle) break;
  64. }
  65. return index;
  66. }
  67. export function memoizedState(): MemoState {
  68. return {
  69. lastKey: -1,
  70. lastNeedle: -1,
  71. lastIndex: -1,
  72. };
  73. }
  74. /**
  75. * This overly complicated beast is just to record the last tested line/column and the resulting
  76. * index, allowing us to skip a few tests if mappings are monotonically increasing.
  77. */
  78. export function memoizedBinarySearch(
  79. haystack: SourceMapSegment[] | ReverseSegment[],
  80. needle: number,
  81. state: MemoState,
  82. key: number,
  83. ): number {
  84. const { lastKey, lastNeedle, lastIndex } = state;
  85. let low = 0;
  86. let high = haystack.length - 1;
  87. if (key === lastKey) {
  88. if (needle === lastNeedle) {
  89. found = lastIndex !== -1 && haystack[lastIndex][COLUMN] === needle;
  90. return lastIndex;
  91. }
  92. if (needle >= lastNeedle) {
  93. // lastIndex may be -1 if the previous needle was not found.
  94. low = lastIndex === -1 ? 0 : lastIndex;
  95. } else {
  96. high = lastIndex;
  97. }
  98. }
  99. state.lastKey = key;
  100. state.lastNeedle = needle;
  101. return (state.lastIndex = binarySearch(haystack, needle, low, high));
  102. }