UNPKG

monaco-editor-core

Version:

A browser based code editor

224 lines (223 loc) • 7.72 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { arrayInsert } from '../../../base/common/arrays.js'; import { toUint32 } from '../../../base/common/uint.js'; export class PrefixSumComputer { constructor(values) { this.values = values; this.prefixSum = new Uint32Array(values.length); this.prefixSumValidIndex = new Int32Array(1); this.prefixSumValidIndex[0] = -1; } insertValues(insertIndex, insertValues) { insertIndex = toUint32(insertIndex); const oldValues = this.values; const oldPrefixSum = this.prefixSum; const insertValuesLen = insertValues.length; if (insertValuesLen === 0) { return false; } this.values = new Uint32Array(oldValues.length + insertValuesLen); this.values.set(oldValues.subarray(0, insertIndex), 0); this.values.set(oldValues.subarray(insertIndex), insertIndex + insertValuesLen); this.values.set(insertValues, insertIndex); if (insertIndex - 1 < this.prefixSumValidIndex[0]) { this.prefixSumValidIndex[0] = insertIndex - 1; } this.prefixSum = new Uint32Array(this.values.length); if (this.prefixSumValidIndex[0] >= 0) { this.prefixSum.set(oldPrefixSum.subarray(0, this.prefixSumValidIndex[0] + 1)); } return true; } setValue(index, value) { index = toUint32(index); value = toUint32(value); if (this.values[index] === value) { return false; } this.values[index] = value; if (index - 1 < this.prefixSumValidIndex[0]) { this.prefixSumValidIndex[0] = index - 1; } return true; } removeValues(startIndex, count) { startIndex = toUint32(startIndex); count = toUint32(count); const oldValues = this.values; const oldPrefixSum = this.prefixSum; if (startIndex >= oldValues.length) { return false; } const maxCount = oldValues.length - startIndex; if (count >= maxCount) { count = maxCount; } if (count === 0) { return false; } this.values = new Uint32Array(oldValues.length - count); this.values.set(oldValues.subarray(0, startIndex), 0); this.values.set(oldValues.subarray(startIndex + count), startIndex); this.prefixSum = new Uint32Array(this.values.length); if (startIndex - 1 < this.prefixSumValidIndex[0]) { this.prefixSumValidIndex[0] = startIndex - 1; } if (this.prefixSumValidIndex[0] >= 0) { this.prefixSum.set(oldPrefixSum.subarray(0, this.prefixSumValidIndex[0] + 1)); } return true; } getTotalSum() { if (this.values.length === 0) { return 0; } return this._getPrefixSum(this.values.length - 1); } /** * Returns the sum of the first `index + 1` many items. * @returns `SUM(0 <= j <= index, values[j])`. */ getPrefixSum(index) { if (index < 0) { return 0; } index = toUint32(index); return this._getPrefixSum(index); } _getPrefixSum(index) { if (index <= this.prefixSumValidIndex[0]) { return this.prefixSum[index]; } let startIndex = this.prefixSumValidIndex[0] + 1; if (startIndex === 0) { this.prefixSum[0] = this.values[0]; startIndex++; } if (index >= this.values.length) { index = this.values.length - 1; } for (let i = startIndex; i <= index; i++) { this.prefixSum[i] = this.prefixSum[i - 1] + this.values[i]; } this.prefixSumValidIndex[0] = Math.max(this.prefixSumValidIndex[0], index); return this.prefixSum[index]; } getIndexOf(sum) { sum = Math.floor(sum); // Compute all sums (to get a fully valid prefixSum) this.getTotalSum(); let low = 0; let high = this.values.length - 1; let mid = 0; let midStop = 0; let midStart = 0; while (low <= high) { mid = low + ((high - low) / 2) | 0; midStop = this.prefixSum[mid]; midStart = midStop - this.values[mid]; if (sum < midStart) { high = mid - 1; } else if (sum >= midStop) { low = mid + 1; } else { break; } } return new PrefixSumIndexOfResult(mid, sum - midStart); } } /** * {@link getIndexOf} has an amortized runtime complexity of O(1). * * ({@link PrefixSumComputer.getIndexOf} is just O(log n)) */ export class ConstantTimePrefixSumComputer { constructor(values) { this._values = values; this._isValid = false; this._validEndIndex = -1; this._prefixSum = []; this._indexBySum = []; } /** * @returns SUM(0 <= j < values.length, values[j]) */ getTotalSum() { this._ensureValid(); return this._indexBySum.length; } /** * Returns the sum of the first `count` many items. * @returns `SUM(0 <= j < count, values[j])`. */ getPrefixSum(count) { this._ensureValid(); if (count === 0) { return 0; } return this._prefixSum[count - 1]; } /** * @returns `result`, such that `getPrefixSum(result.index) + result.remainder = sum` */ getIndexOf(sum) { this._ensureValid(); const idx = this._indexBySum[sum]; const viewLinesAbove = idx > 0 ? this._prefixSum[idx - 1] : 0; return new PrefixSumIndexOfResult(idx, sum - viewLinesAbove); } removeValues(start, deleteCount) { this._values.splice(start, deleteCount); this._invalidate(start); } insertValues(insertIndex, insertArr) { this._values = arrayInsert(this._values, insertIndex, insertArr); this._invalidate(insertIndex); } _invalidate(index) { this._isValid = false; this._validEndIndex = Math.min(this._validEndIndex, index - 1); } _ensureValid() { if (this._isValid) { return; } for (let i = this._validEndIndex + 1, len = this._values.length; i < len; i++) { const value = this._values[i]; const sumAbove = i > 0 ? this._prefixSum[i - 1] : 0; this._prefixSum[i] = sumAbove + value; for (let j = 0; j < value; j++) { this._indexBySum[sumAbove + j] = i; } } // trim things this._prefixSum.length = this._values.length; this._indexBySum.length = this._prefixSum[this._prefixSum.length - 1]; // mark as valid this._isValid = true; this._validEndIndex = this._values.length - 1; } setValue(index, value) { if (this._values[index] === value) { // no change return; } this._values[index] = value; this._invalidate(index); } } export class PrefixSumIndexOfResult { constructor(index, remainder) { this.index = index; this.remainder = remainder; this._prefixSumIndexOfResultBrand = undefined; this.index = index; this.remainder = remainder; } }