mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-01-31 19:02:59 +01:00
skiplist: Move point creation to a new Point
class
This commit is contained in:
parent
8ae40e80f9
commit
3c1be95e07
1 changed files with 45 additions and 42 deletions
|
@ -25,6 +25,49 @@
|
||||||
const _entryWidth = (e) => (e && e.width) || 0;
|
const _entryWidth = (e) => (e && e.width) || 0;
|
||||||
const _getNodeAtPoint = (point) => point.nodes[0].downPtrs[0];
|
const _getNodeAtPoint = (point) => point.nodes[0].downPtrs[0];
|
||||||
|
|
||||||
|
// A "point" object at index x allows modifications immediately after the first x elements of the
|
||||||
|
// skiplist, such as multiple inserts or deletes. After an insert or delete using point P, the point
|
||||||
|
// is still valid and points to the same index in the skiplist. Other operations with other points
|
||||||
|
// invalidate this point.
|
||||||
|
class Point {
|
||||||
|
constructor(skipList, loc) {
|
||||||
|
this._skipList = skipList;
|
||||||
|
this.loc = loc;
|
||||||
|
const numLevels = this._skipList._start.levels;
|
||||||
|
let lvl = numLevels - 1;
|
||||||
|
let i = -1;
|
||||||
|
let ws = 0;
|
||||||
|
const nodes = new Array(numLevels);
|
||||||
|
const idxs = new Array(numLevels);
|
||||||
|
const widthSkips = new Array(numLevels);
|
||||||
|
nodes[lvl] = this._skipList._start;
|
||||||
|
idxs[lvl] = -1;
|
||||||
|
widthSkips[lvl] = 0;
|
||||||
|
while (lvl >= 0) {
|
||||||
|
let n = nodes[lvl];
|
||||||
|
while (n.downPtrs[lvl] && (i + n.downSkips[lvl] < this.loc)) {
|
||||||
|
i += n.downSkips[lvl];
|
||||||
|
ws += n.downSkipWidths[lvl];
|
||||||
|
n = n.downPtrs[lvl];
|
||||||
|
}
|
||||||
|
nodes[lvl] = n;
|
||||||
|
idxs[lvl] = i;
|
||||||
|
widthSkips[lvl] = ws;
|
||||||
|
lvl--;
|
||||||
|
if (lvl >= 0) {
|
||||||
|
nodes[lvl] = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.idxs = idxs;
|
||||||
|
this.nodes = nodes;
|
||||||
|
this.widthSkips = widthSkips;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return `Point(${this.loc})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The skip-list contains "entries", JavaScript objects that each must have a unique "key"
|
* The skip-list contains "entries", JavaScript objects that each must have a unique "key"
|
||||||
* property that is a string.
|
* property that is a string.
|
||||||
|
@ -55,46 +98,6 @@ class SkipList {
|
||||||
this._end.upPtrs[0] = this._start;
|
this._end.upPtrs[0] = this._start;
|
||||||
}
|
}
|
||||||
|
|
||||||
// a "point" object at location x allows modifications immediately after the first
|
|
||||||
// x elements of the skiplist, such as multiple inserts or deletes.
|
|
||||||
// After an insert or delete using point P, the point is still valid and points
|
|
||||||
// to the same index in the skiplist. Other operations with other points invalidate
|
|
||||||
// this point.
|
|
||||||
_getPoint(targetLoc) {
|
|
||||||
const numLevels = this._start.levels;
|
|
||||||
let lvl = numLevels - 1;
|
|
||||||
let i = -1;
|
|
||||||
let ws = 0;
|
|
||||||
const nodes = new Array(numLevels);
|
|
||||||
const idxs = new Array(numLevels);
|
|
||||||
const widthSkips = new Array(numLevels);
|
|
||||||
nodes[lvl] = this._start;
|
|
||||||
idxs[lvl] = -1;
|
|
||||||
widthSkips[lvl] = 0;
|
|
||||||
while (lvl >= 0) {
|
|
||||||
let n = nodes[lvl];
|
|
||||||
while (n.downPtrs[lvl] && (i + n.downSkips[lvl] < targetLoc)) {
|
|
||||||
i += n.downSkips[lvl];
|
|
||||||
ws += n.downSkipWidths[lvl];
|
|
||||||
n = n.downPtrs[lvl];
|
|
||||||
}
|
|
||||||
nodes[lvl] = n;
|
|
||||||
idxs[lvl] = i;
|
|
||||||
widthSkips[lvl] = ws;
|
|
||||||
lvl--;
|
|
||||||
if (lvl >= 0) {
|
|
||||||
nodes[lvl] = n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
nodes,
|
|
||||||
idxs,
|
|
||||||
loc: targetLoc,
|
|
||||||
widthSkips,
|
|
||||||
toString: () => `getPoint(${targetLoc})`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_getNodeAtOffset(targetOffset) {
|
_getNodeAtOffset(targetOffset) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let n = this._start;
|
let n = this._start;
|
||||||
|
@ -258,7 +261,7 @@ class SkipList {
|
||||||
atIndex(i) {
|
atIndex(i) {
|
||||||
if (i < 0) console.warn(`atIndex(${i})`);
|
if (i < 0) console.warn(`atIndex(${i})`);
|
||||||
if (i >= this._numNodes) console.warn(`atIndex(${i}>=${this._numNodes})`);
|
if (i >= this._numNodes) console.warn(`atIndex(${i}>=${this._numNodes})`);
|
||||||
return _getNodeAtPoint(this._getPoint(i)).entry;
|
return _getNodeAtPoint(new Point(this, i)).entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
// differs from Array.splice() in that new elements are in an array, not varargs
|
// differs from Array.splice() in that new elements are in an array, not varargs
|
||||||
|
@ -271,7 +274,7 @@ class SkipList {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newEntryArray) newEntryArray = [];
|
if (!newEntryArray) newEntryArray = [];
|
||||||
const pt = this._getPoint(start);
|
const pt = new Point(this, start);
|
||||||
for (let i = 0; i < deleteCount; i++) {
|
for (let i = 0; i < deleteCount; i++) {
|
||||||
this._deleteKeyAtPoint(pt);
|
this._deleteKeyAtPoint(pt);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue