Skip to content

Commit be415a2

Browse files
authored
Merge pull request #553 from streamich/avl-improvements
AVL map and set iterator improvements
2 parents d34c4ae + 8d80187 commit be415a2

File tree

6 files changed

+267
-6
lines changed

6 files changed

+267
-6
lines changed

src/util/buffers/cmpUint8Array2.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
/**
2+
* Compares two `Uint8Arrays` byte-by-byte. Returns a negative number if `a` is
3+
* less than `b`, a positive number if `a` is greater than `b`, or 0 if `a` is
4+
* equal to `b`.
5+
*
6+
* @returns A negative number if a is less than b, a positive number if a is
7+
* greater than b, or 0 if a is equal to b.
8+
*/
19
export const cmpUint8Array2 = (a: Uint8Array, b: Uint8Array): number => {
210
const len1 = a.length;
311
const len2 = b.length;

src/util/buffers/cmpUint8Array3.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Compares two `Uint8Arrays`, first by length, then by each byte. Returns a
3+
* negative number if `a` is less than `b`, a positive number if `a` is greater
4+
* than `b`, or 0 if `a` is equal to `b`.
5+
*
6+
* @returns A negative number if a is less than b, a positive number if a is
7+
* greater than b, or 0 if a is equal to b.
8+
*/
9+
export const cmpUint8Array3 = (a: Uint8Array, b: Uint8Array): number => {
10+
const len1 = a.length;
11+
const len2 = b.length;
12+
const diff = len1 - len2;
13+
if (diff !== 0) return diff;
14+
for (let i = 0; i < len1; i++) {
15+
const diff = a[i] - b[i];
16+
if (diff !== 0) return diff;
17+
}
18+
return 0;
19+
};

src/util/trees/avl/AvlMap.ts

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,44 @@ export class AvlMap<K, V> implements Printable {
9898
}
9999

100100
public forEach(fn: (node: AvlNode<K, V>) => void): void {
101-
const root = this.root;
102-
if (!root) return;
103-
let curr = first(root);
101+
let curr = this.first();
102+
if (!curr) return;
104103
do fn(curr!);
105104
while ((curr = next(curr as HeadlessNode) as AvlNode<K, V> | undefined));
106105
}
107106

107+
public first(): AvlNode<K, V> | undefined {
108+
const root = this.root;
109+
return root ? first(root) : undefined;
110+
}
111+
112+
public readonly next = next;
113+
114+
public iterator0(): () => undefined | AvlNode<K, V> {
115+
let curr = this.first();
116+
return () => {
117+
if (!curr) return;
118+
const value = curr;
119+
curr = next(curr as HeadlessNode) as AvlNode<K, V> | undefined;
120+
return value;
121+
};
122+
}
123+
124+
public iterator(): Iterator<AvlNode<K, V>> {
125+
const iterator = this.iterator0();
126+
return {
127+
next: () => {
128+
const value = iterator();
129+
const res = <IteratorResult<AvlNode<K, V>>>{value, done: !value};
130+
return res;
131+
},
132+
};
133+
}
134+
135+
public entries(): IterableIterator<AvlNode<K, V>> {
136+
return <any>{[Symbol.iterator]: () => this.iterator()};
137+
}
138+
108139
public toString(tab: string): string {
109140
return this.constructor.name + printTree(tab, [(tab) => print(this.root, tab)]);
110141
}

src/util/trees/avl/AvlSet.ts

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,44 @@ export class AvlSet<V> implements Printable {
9191
}
9292

9393
public forEach(fn: (node: AvlSetNode<V>) => void): void {
94-
const root = this.root;
95-
if (!root) return;
96-
let curr = first(root);
94+
let curr = this.first();
95+
if (!curr) return;
9796
do fn(curr!);
9897
while ((curr = next(curr as HeadlessNode) as AvlSetNode<V> | undefined));
9998
}
10099

100+
public first(): AvlSetNode<V> | undefined {
101+
const root = this.root;
102+
return root ? first(root) : undefined;
103+
}
104+
105+
public readonly next = next;
106+
107+
public iterator0(): () => undefined | AvlSetNode<V> {
108+
let curr = this.first();
109+
return () => {
110+
if (!curr) return undefined;
111+
const value = curr;
112+
curr = next(curr as HeadlessNode) as AvlSetNode<V> | undefined;
113+
return value;
114+
};
115+
}
116+
117+
public iterator(): Iterator<AvlSetNode<V>> {
118+
const iterator = this.iterator0();
119+
return {
120+
next: () => {
121+
const value = iterator();
122+
const res = <IteratorResult<AvlSetNode<V>>>{value, done: !value};
123+
return res;
124+
},
125+
};
126+
}
127+
128+
public entries(): IterableIterator<AvlSetNode<V>> {
129+
return <any>{[Symbol.iterator]: () => this.iterator()};
130+
}
131+
101132
public toString(tab: string): string {
102133
return this.constructor.name + printTree(tab, [(tab) => print(this.root, tab)]);
103134
}

src/util/trees/avl/__tests__/AvlMap.spec.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,97 @@ test('smoke test', () => {
1414
tree.forEach((node) => keys.push(node.k));
1515
expect(keys).toEqual([1, 3, 4, 4.1, 44]);
1616
});
17+
18+
describe('.first()/next() iteration', () => {
19+
test('for empty map, returns finished iterator', () => {
20+
const tree = new AvlMap<string, number>();
21+
const entry = tree.first();
22+
expect(entry).toEqual(undefined);
23+
});
24+
25+
test('can iterate through map entries', () => {
26+
const tree = new AvlMap<string, number>();
27+
tree.set('a', 1);
28+
tree.set('b', 2);
29+
tree.set('c', 3);
30+
const list: [string, number][] = [];
31+
for (let entry = tree.first(); entry; entry = tree.next(entry)) {
32+
list.push([entry.k, entry.v]);
33+
}
34+
expect(list).toEqual([
35+
['a', 1],
36+
['b', 2],
37+
['c', 3],
38+
]);
39+
});
40+
});
41+
42+
describe('.iterator0()', () => {
43+
test('for empty map, returns finished iterator', () => {
44+
const tree = new AvlMap<string, number>();
45+
const iterator = tree.iterator0();
46+
const entry = iterator();
47+
expect(entry).toEqual(undefined);
48+
});
49+
50+
test('can iterate through map entries', () => {
51+
const tree = new AvlMap<string, number>();
52+
tree.set('a', 1);
53+
tree.set('b', 2);
54+
tree.set('c', 3);
55+
const list: [string, number][] = [];
56+
const iterator = tree.iterator0();
57+
for (let entry = iterator(); entry; entry = iterator()) {
58+
list.push([entry.k, entry.v]);
59+
}
60+
expect(list).toEqual([
61+
['a', 1],
62+
['b', 2],
63+
['c', 3],
64+
]);
65+
});
66+
});
67+
68+
describe('.iterator()', () => {
69+
test('for empty map, returns finished iterator', () => {
70+
const tree = new AvlMap<string, number>();
71+
const iterator = tree.iterator();
72+
const entry = iterator.next();
73+
expect(entry).toEqual({done: true, value: undefined});
74+
});
75+
76+
test('can iterate through map entries', () => {
77+
const tree = new AvlMap<string, number>();
78+
tree.set('a', 1);
79+
tree.set('b', 2);
80+
tree.set('c', 3);
81+
const iterator = tree.iterator();
82+
const list: [string, number][] = [];
83+
for (let entry = iterator.next(); !entry.done; entry = iterator.next()) {
84+
list.push([entry.value!.k, entry.value!.v]);
85+
}
86+
expect(list).toEqual([
87+
['a', 1],
88+
['b', 2],
89+
['c', 3],
90+
]);
91+
});
92+
});
93+
94+
describe('for...of iteration', () => {
95+
test('can iterate through map entries', () => {
96+
const tree = new AvlMap<string, number>();
97+
tree.set('a', 1);
98+
tree.set('b', 2);
99+
tree.set('c', 3);
100+
const list: [string, number][] = [];
101+
for (const entry of tree.entries()) {
102+
list.push([entry.k, entry.v]);
103+
}
104+
expect(list).toEqual([
105+
['a', 1],
106+
['b', 2],
107+
['c', 3],
108+
]);
109+
});
110+
});

src/util/trees/avl/__tests__/AvlSet.spec.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,81 @@ test('can store structs', () => {
6464
expect(set.has(new Struct(3, 3))).toBe(true);
6565
expect(set.has(new Struct(2, 3))).toBe(true);
6666
});
67+
68+
describe('.first()/next() iteration', () => {
69+
test('for empty map, returns finished iterator', () => {
70+
const tree = new AvlSet<string>();
71+
const entry = tree.first();
72+
expect(entry).toEqual(undefined);
73+
});
74+
75+
test('can iterate through map entries', () => {
76+
const tree = new AvlSet<number>();
77+
tree.add(1);
78+
tree.add(2);
79+
tree.add(3);
80+
const list: number[] = [];
81+
for (let entry = tree.first(); entry; entry = tree.next(entry)) {
82+
list.push(entry.k);
83+
}
84+
expect(list).toEqual([1, 2, 3]);
85+
});
86+
});
87+
88+
describe('.iterator0()', () => {
89+
test('for empty map, returns finished iterator', () => {
90+
const tree = new AvlSet<number>();
91+
const iterator = tree.iterator0();
92+
const entry = iterator();
93+
expect(entry).toEqual(undefined);
94+
});
95+
96+
test('can iterate through map entries', () => {
97+
const tree = new AvlSet<number>();
98+
tree.add(1);
99+
tree.add(2);
100+
tree.add(3);
101+
const list: number[] = [];
102+
const iterator = tree.iterator0();
103+
for (let entry = iterator(); entry; entry = iterator()) {
104+
list.push(entry.k);
105+
}
106+
expect(list).toEqual([1, 2, 3]);
107+
});
108+
});
109+
110+
describe('.iterator()', () => {
111+
test('for empty map, returns finished iterator', () => {
112+
const tree = new AvlSet<number>();
113+
const iterator = tree.iterator();
114+
const entry = iterator.next();
115+
expect(entry).toEqual({done: true, value: undefined});
116+
});
117+
118+
test('can iterate through map entries', () => {
119+
const tree = new AvlSet<number>();
120+
tree.add(1);
121+
tree.add(2);
122+
tree.add(3);
123+
const list: number[] = [];
124+
const iterator = tree.iterator();
125+
for (let entry = iterator.next(); !entry.done; entry = iterator.next()) {
126+
list.push(entry.value!.k);
127+
}
128+
expect(list).toEqual([1, 2, 3]);
129+
});
130+
});
131+
132+
describe('for...of iteration', () => {
133+
test('can iterate through map entries', () => {
134+
const tree = new AvlSet<number>();
135+
tree.add(1);
136+
tree.add(2);
137+
tree.add(3);
138+
const list: number[] = [];
139+
for (const entry of tree.entries()) {
140+
list.push(entry.k);
141+
}
142+
expect(list).toEqual([1, 2, 3]);
143+
});
144+
});

0 commit comments

Comments
 (0)