Skip to content

Commit ae0e3d7

Browse files
committed
use a skip list for keeping canonical uris, microsoft#93368
1 parent 64c4407 commit ae0e3d7

3 files changed

Lines changed: 468 additions & 14 deletions

File tree

src/vs/base/common/skipList.ts

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
7+
class Node<K, V> {
8+
readonly forward: Node<K, V>[];
9+
constructor(readonly level: number, readonly key: K, public value: V) {
10+
this.forward = [];
11+
}
12+
}
13+
14+
const NIL: undefined = undefined;
15+
16+
interface Comparator<K> {
17+
(a: K, b: K): number;
18+
}
19+
20+
export class SkipList<K, V> implements Map<K, V> {
21+
22+
readonly [Symbol.toStringTag] = 'SkipList';
23+
24+
private _maxLevel: number;
25+
private _level: number = 1;
26+
private _header: Node<K, V>;
27+
private _size: number = 0;
28+
29+
/**
30+
*
31+
* @param capacity Capacity at which the list performs best
32+
*/
33+
constructor(
34+
readonly comparator: (a: K, b: K) => number,
35+
capacity: number = 2 ** 16
36+
) {
37+
this._maxLevel = Math.max(1, Math.log2(capacity) | 0);
38+
this._header = <any>new Node(this._maxLevel, NIL, NIL);
39+
}
40+
41+
get size(): number {
42+
return this._size;
43+
}
44+
45+
clear(): void {
46+
this._header = <any>new Node(this._maxLevel, NIL, NIL);
47+
}
48+
49+
has(key: K): boolean {
50+
return Boolean(SkipList._search(this, key, this.comparator));
51+
}
52+
53+
get(key: K): V | undefined {
54+
return SkipList._search(this, key, this.comparator)?.value;
55+
}
56+
57+
set(key: K, value: V): this {
58+
if (SkipList._insert(this, key, value, this.comparator)) {
59+
this._size += 1;
60+
}
61+
return this;
62+
}
63+
64+
delete(key: K): boolean {
65+
const didDelete = SkipList._delete(this, key, this.comparator);
66+
if (didDelete) {
67+
this._size -= 1;
68+
}
69+
return didDelete;
70+
}
71+
72+
// --- iteration
73+
74+
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void {
75+
let node = this._header.forward[0];
76+
while (node) {
77+
callbackfn.call(thisArg, node.value, node.key, this);
78+
node = node.forward[0];
79+
}
80+
}
81+
82+
[Symbol.iterator](): IterableIterator<[K, V]> {
83+
return this.entries();
84+
}
85+
86+
*entries(): IterableIterator<[K, V]> {
87+
let node = this._header.forward[0];
88+
while (node) {
89+
yield [node.key, node.value];
90+
node = node.forward[0];
91+
}
92+
}
93+
94+
*keys(): IterableIterator<K> {
95+
let node = this._header.forward[0];
96+
while (node) {
97+
yield node.key;
98+
node = node.forward[0];
99+
}
100+
}
101+
102+
*values(): IterableIterator<V> {
103+
let node = this._header.forward[0];
104+
while (node) {
105+
yield node.value;
106+
node = node.forward[0];
107+
}
108+
}
109+
110+
toString(): string {
111+
// debug string...
112+
let result = '[SkipList]:';
113+
let node = this._header.forward[0];
114+
while (node) {
115+
result += `node(${node.key}, ${node.value}, lvl:${node.level})`;
116+
node = node.forward[0];
117+
}
118+
return result;
119+
}
120+
121+
// from https://www.epaperpress.com/sortsearch/download/skiplist.pdf
122+
123+
private static _search<K, V>(list: SkipList<K, V>, searchKey: K, comparator: Comparator<K>) {
124+
let x = list._header;
125+
for (let i = list._level; i >= 0; i--) {
126+
while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) {
127+
x = x.forward[i];
128+
}
129+
}
130+
x = x.forward[0];
131+
if (x && comparator(x.key, searchKey) === 0) {
132+
return x;
133+
}
134+
return undefined;
135+
}
136+
137+
private static _insert<K, V>(list: SkipList<K, V>, searchKey: K, value: V, comparator: Comparator<K>) {
138+
let update: Node<K, V>[] = [];
139+
let x = list._header;
140+
for (let i = list._level; i >= 0; i--) {
141+
while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) {
142+
x = x.forward[i];
143+
}
144+
update[i] = x;
145+
}
146+
x = x.forward[0];
147+
if (x && comparator(x.key, searchKey) === 0) {
148+
// update
149+
x.value = value;
150+
return false;
151+
} else {
152+
// insert
153+
let lvl = SkipList._randomLevel(list);
154+
if (lvl > list._level) {
155+
for (let i = list._level + 1; i <= lvl; i++) {
156+
update[i] = list._header;
157+
}
158+
list._level = lvl;
159+
}
160+
x = new Node<K, V>(lvl, searchKey, value);
161+
for (let i = 0; i <= lvl; i++) {
162+
x.forward[i] = update[i].forward[i];
163+
update[i].forward[i] = x;
164+
}
165+
return true;
166+
}
167+
}
168+
169+
private static _randomLevel(list: SkipList<any, any>, p: number = 0.5): number {
170+
let lvl = 1;
171+
while (Math.random() < p && lvl < list._maxLevel) {
172+
lvl += 1;
173+
}
174+
return lvl;
175+
}
176+
177+
private static _delete<K, V>(list: SkipList<K, V>, searchKey: K, comparator: Comparator<K>) {
178+
let update: Node<K, V>[] = [];
179+
let x = list._header;
180+
for (let i = list._level; i >= 0; i--) {
181+
while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) {
182+
x = x.forward[i];
183+
}
184+
update[i] = x;
185+
}
186+
x = x.forward[0];
187+
if (!x || comparator(x.key, searchKey) !== 0) {
188+
// not found
189+
return false;
190+
}
191+
for (let i = 0; i < list._level; i++) {
192+
if (update[i].forward[i] !== x) {
193+
break;
194+
}
195+
update[i].forward[i] = x.forward[i];
196+
}
197+
while (list._level >= 1 && list._header.forward[list._level] === NIL) {
198+
list._level -= 1;
199+
}
200+
return true;
201+
}
202+
203+
}

0 commit comments

Comments
 (0)