Skip to content

Commit 39c8346

Browse files
author
Adam Hrbac
committed
Initial implementation
1 parent 5c5d8f5 commit 39c8346

File tree

4 files changed

+155
-7
lines changed

4 files changed

+155
-7
lines changed

graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/builtins/modules/HamtTests.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@
4242

4343
import static org.junit.Assert.assertEquals;
4444
import static org.junit.Assert.assertNull;
45+
import static org.junit.Assert.assertTrue;
4546
import static org.junit.Assert.fail;
4647

4748
import org.junit.After;
4849
import org.junit.Before;
4950
import org.junit.Test;
5051

5152
import com.oracle.graal.python.builtins.objects.contextvars.Hamt;
53+
import com.oracle.graal.python.builtins.objects.contextvars.HamtIterator;
5254
import com.oracle.graal.python.test.PythonTests;
5355

5456
public class HamtTests {
@@ -177,6 +179,26 @@ public void testHamtSize() {
177179
}
178180
}
179181

182+
@Test
183+
public void testHamtIterator() {
184+
int limit = 100;
185+
boolean[] seen = new boolean[limit];
186+
Hamt hamt = new Hamt();
187+
for (int i = 0; i < limit; ++i) {
188+
hamt = hamt.withEntry(new Hamt.Entry(i, String.valueOf(i).hashCode(), String.valueOf(i)));
189+
}
190+
HamtIterator hi = new HamtIterator(hamt);
191+
Hamt.Entry el = hi.next();
192+
while (el != null) {
193+
seen[(int) el.key] = true;
194+
assertEquals(String.valueOf(el.key), el.value);
195+
el = hi.next();
196+
}
197+
for (int i = 0; i < limit; ++i) {
198+
assertTrue("failed at " + i, seen[i]);
199+
}
200+
}
201+
180202
// @Test
181203
public void measureTimeForSmallHamtAcceses() {
182204
Hamt hamt = new Hamt().withEntry(new Hamt.Entry(1, 0, 1)).withEntry(new Hamt.Entry(2, 31, 2));

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/contextvars/Hamt.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private static int computeSize(TreePart part) {
9191
throw CompilerDirectives.shouldNotReachHere("TreePart type is not handled");
9292
}
9393

94-
private final TreePart root;
94+
final TreePart root;
9595

9696
public Hamt() {
9797
this(null);
@@ -396,11 +396,11 @@ public Hamt without(Object key, int hash) {
396396
return new Hamt(partWithoutKey(root, key, hash, 0));
397397
}
398398

399-
private interface TreePart {
399+
interface TreePart {
400400
String dump(int indent);
401401
}
402402

403-
private static final class BitmapPart implements TreePart {
403+
static final class BitmapPart implements TreePart {
404404
final int bitmap;
405405
final TreePart[] elems;
406406

@@ -426,7 +426,7 @@ public String dump(int indent) {
426426
}
427427
}
428428

429-
private static final class ArrayPart implements TreePart {
429+
static final class ArrayPart implements TreePart {
430430
final TreePart[] elems;
431431

432432
public ArrayPart(TreePart[] elems) {
@@ -446,7 +446,7 @@ public String dump(int indent) {
446446
}
447447
}
448448

449-
private static final class CollisionPart implements TreePart {
449+
static final class CollisionPart implements TreePart {
450450
final int hash;
451451
final Entry[] elems;
452452

@@ -471,9 +471,9 @@ public String dump(int indent) {
471471

472472
@CompilerDirectives.ValueType
473473
public static final class Entry implements TreePart {
474-
final Object key;
474+
public final Object key;
475475
final int hash;
476-
final Object value;
476+
public final Object value;
477477

478478
public Entry(Object key, int hash, Object value) {
479479
this.key = key;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package com.oracle.graal.python.builtins.objects.contextvars;
2+
3+
import com.oracle.truffle.api.CompilerDirectives;
4+
5+
public final class HamtIterator {
6+
// the depth of the tree is limited by the number of 5-bit bitstrings in the 32-bit hash + 1
7+
// CollisionNode with 1 entry child
8+
private static final int MAX_DEPTH = 8;
9+
10+
// stores the indices used to get to the next node in path. nodeIndices[i] is the index in
11+
// path[i] that gets to path[i+1]. Only meaningful up to level-1.
12+
private final int[] nodeIndices = new int[MAX_DEPTH];
13+
14+
// Stores the path to the entry that should be returned by next, including the entry.
15+
private final Hamt.TreePart[] path = new Hamt.TreePart[MAX_DEPTH];
16+
17+
// the highest index of path that contains useful values, -1 if the iterator is exhausted
18+
private int level = 0;
19+
20+
public HamtIterator(Hamt hamt) {
21+
path[0] = hamt.root;
22+
findFirstEntry();
23+
}
24+
25+
private void visitLeftmost(Hamt.TreePart parent) {
26+
++level;
27+
if (parent instanceof Hamt.CollisionPart) {
28+
Hamt.CollisionPart part = (Hamt.CollisionPart) parent;
29+
path[level] = part.elems[0];
30+
// at level - 1 is the index we used to get to part.elems[0].
31+
// At level would be the index used on part.elems[0] to get deeper
32+
nodeIndices[level - 1] = 0;
33+
} else if (parent instanceof Hamt.BitmapPart) {
34+
Hamt.BitmapPart part = (Hamt.BitmapPart) parent;
35+
path[level] = part.elems[0];
36+
nodeIndices[level - 1] = 0;
37+
} else if (parent instanceof Hamt.ArrayPart) {
38+
Hamt.ArrayPart part = (Hamt.ArrayPart) parent;
39+
for (int ret = 0; ret < part.elems.length; ++ret) {
40+
if (part.elems[ret] != null) {
41+
path[level] = part.elems[ret];
42+
nodeIndices[level - 1] = ret;
43+
break;
44+
}
45+
}
46+
} else if (parent instanceof Hamt.Entry) {
47+
throw CompilerDirectives.shouldNotReachHere("got Entry in method for non-leaf nodes");
48+
} else {
49+
throw CompilerDirectives.shouldNotReachHere("unhandled TreePart type");
50+
}
51+
52+
}
53+
54+
private void findFirstEntry() {
55+
if (path[level] == null) {
56+
level = -1;
57+
return;
58+
}
59+
while (!(path[level] instanceof Hamt.Entry)) {
60+
visitLeftmost(path[level]);
61+
}
62+
// path[level] is now the first Entry
63+
}
64+
65+
private void nextInArr(Hamt.TreePart[] arr, int idx) {
66+
for (int nextIdx = idx + 1; nextIdx < arr.length; nextIdx++) {
67+
if (arr[nextIdx] != null) {
68+
nodeIndices[level] = nextIdx;
69+
level++;
70+
path[level] = arr[nextIdx];
71+
findFirstEntry();
72+
return; // skip the nextEntry() call
73+
}
74+
}
75+
nextEntry(); // used up entire node
76+
}
77+
78+
private void nextEntry() {
79+
level--;
80+
if (level < 0) {
81+
level = -1;
82+
return; // iterator exhausted
83+
}
84+
// the part above the just-yielded entry (or one of its parents)
85+
Hamt.TreePart toAdvance = path[level];
86+
int idx = nodeIndices[level];
87+
88+
if (toAdvance instanceof Hamt.CollisionPart) {
89+
nextInArr(((Hamt.CollisionPart) toAdvance).elems, idx);
90+
} else if (toAdvance instanceof Hamt.BitmapPart) {
91+
nextInArr(((Hamt.BitmapPart) toAdvance).elems, idx);
92+
} else if (toAdvance instanceof Hamt.ArrayPart) {
93+
nextInArr(((Hamt.ArrayPart) toAdvance).elems, idx);
94+
} else if (toAdvance instanceof Hamt.Entry) {
95+
throw CompilerDirectives.shouldNotReachHere("Entry in non-leaf method");
96+
} else {
97+
throw CompilerDirectives.shouldNotReachHere("TreePart type not handled");
98+
}
99+
}
100+
101+
public Hamt.Entry next() {
102+
if (level == -1) {
103+
return null;
104+
}
105+
Hamt.TreePart result = path[level];
106+
if (!(result instanceof Hamt.Entry)) {
107+
throw CompilerDirectives.shouldNotReachHere("Hamt path in invalid state");
108+
}
109+
nextEntry();
110+
return (Hamt.Entry) result;
111+
}
112+
113+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.oracle.graal.python.builtins.objects.contextvars;
2+
3+
import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
4+
import com.oracle.truffle.api.object.Shape;
5+
6+
public class PHamtIterator extends PythonBuiltinObject {
7+
public final HamtIterator it;
8+
9+
public PHamtIterator(Object cls, Shape instanceShape, Hamt hamt) {
10+
super(cls, instanceShape);
11+
this.it = new HamtIterator(hamt);
12+
}
13+
}

0 commit comments

Comments
 (0)