@sussudio/base
Version:
Internal APIs for VS Code's utilities and user interface building blocks.
179 lines (178 loc) • 4.26 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
class Node {
level;
key;
value;
forward;
constructor(level, key, value) {
this.level = level;
this.key = key;
this.value = value;
this.forward = [];
}
}
const NIL = undefined;
export class SkipList {
comparator;
[Symbol.toStringTag] = 'SkipList';
_maxLevel;
_level = 0;
_header;
_size = 0;
/**
*
* @param capacity Capacity at which the list performs best
*/
constructor(comparator, capacity = 2 ** 16) {
this.comparator = comparator;
this._maxLevel = Math.max(1, Math.log2(capacity) | 0);
this._header = new Node(this._maxLevel, NIL, NIL);
}
get size() {
return this._size;
}
clear() {
this._header = new Node(this._maxLevel, NIL, NIL);
}
has(key) {
return Boolean(SkipList._search(this, key, this.comparator));
}
get(key) {
return SkipList._search(this, key, this.comparator)?.value;
}
set(key, value) {
if (SkipList._insert(this, key, value, this.comparator)) {
this._size += 1;
}
return this;
}
delete(key) {
const didDelete = SkipList._delete(this, key, this.comparator);
if (didDelete) {
this._size -= 1;
}
return didDelete;
}
// --- iteration
forEach(callbackfn, thisArg) {
let node = this._header.forward[0];
while (node) {
callbackfn.call(thisArg, node.value, node.key, this);
node = node.forward[0];
}
}
[Symbol.iterator]() {
return this.entries();
}
*entries() {
let node = this._header.forward[0];
while (node) {
yield [node.key, node.value];
node = node.forward[0];
}
}
*keys() {
let node = this._header.forward[0];
while (node) {
yield node.key;
node = node.forward[0];
}
}
*values() {
let node = this._header.forward[0];
while (node) {
yield node.value;
node = node.forward[0];
}
}
toString() {
// debug string...
let result = '[SkipList]:';
let node = this._header.forward[0];
while (node) {
result += `node(${node.key}, ${node.value}, lvl:${node.level})`;
node = node.forward[0];
}
return result;
}
// from https://www.epaperpress.com/sortsearch/download/skiplist.pdf
static _search(list, searchKey, comparator) {
let x = list._header;
for (let i = list._level - 1; i >= 0; i--) {
while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) {
x = x.forward[i];
}
}
x = x.forward[0];
if (x && comparator(x.key, searchKey) === 0) {
return x;
}
return undefined;
}
static _insert(list, searchKey, value, comparator) {
const update = [];
let x = list._header;
for (let i = list._level - 1; i >= 0; i--) {
while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) {
x = x.forward[i];
}
update[i] = x;
}
x = x.forward[0];
if (x && comparator(x.key, searchKey) === 0) {
// update
x.value = value;
return false;
} else {
// insert
const lvl = SkipList._randomLevel(list);
if (lvl > list._level) {
for (let i = list._level; i < lvl; i++) {
update[i] = list._header;
}
list._level = lvl;
}
x = new Node(lvl, searchKey, value);
for (let i = 0; i < lvl; i++) {
x.forward[i] = update[i].forward[i];
update[i].forward[i] = x;
}
return true;
}
}
static _randomLevel(list, p = 0.5) {
let lvl = 1;
while (Math.random() < p && lvl < list._maxLevel) {
lvl += 1;
}
return lvl;
}
static _delete(list, searchKey, comparator) {
const update = [];
let x = list._header;
for (let i = list._level - 1; i >= 0; i--) {
while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) {
x = x.forward[i];
}
update[i] = x;
}
x = x.forward[0];
if (!x || comparator(x.key, searchKey) !== 0) {
// not found
return false;
}
for (let i = 0; i < list._level; i++) {
if (update[i].forward[i] !== x) {
break;
}
update[i].forward[i] = x.forward[i];
}
while (list._level > 0 && list._header.forward[list._level - 1] === NIL) {
list._level -= 1;
}
return true;
}
}