Skip to content

Commit 9e10e97

Browse files
committed
0.1.2 Throw if iterating over a transient set that has been mutated
1 parent 31e6ae5 commit 9e10e97

File tree

8 files changed

+228
-161
lines changed

8 files changed

+228
-161
lines changed

CHANGES.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# 0.1.2
2+
3+
Throw if iterating over a transient set that has been mutated.
4+
5+
# 0.1.1
6+
7+
Recompiled for Java 8.
8+
9+
# 0.1.0
10+
11+
Initial.

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ Almost a drop-in replacement for `clojure.core/sorted-set`, the only difference
1212

1313
Implementations are provided for Clojure and ClojureScript.
1414

15+
## Building
16+
17+
```
18+
export JAVA8_HOME="/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home"
19+
lein jar
20+
```
21+
1522
## Support us
1623

1724
<a href="https://www.patreon.com/bePatron?u=4230547"><img src="./extras/become_a_patron_button@2x.png" alt="Become a Patron!" width="217" height="51"></a>
@@ -21,7 +28,7 @@ Implementations are provided for Clojure and ClojureScript.
2128
Dependency:
2229

2330
```clj
24-
[persistent-sorted-set "0.1.0"]
31+
[persistent-sorted-set "0.1.2"]
2532
```
2633

2734
Code:

project.clj

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
(defproject persistent-sorted-set "0.1.1"
2-
:description "TODO"
1+
(defproject persistent-sorted-set "0.1.2"
2+
:description "Fast B-tree based persistent sorted set for Clojure/Script"
33
:license {:name "MIT"}
44
:url "https://github.com/tonsky/persistent-sorted-set"
55

66
:dependencies [
77
[org.clojure/clojure "1.10.0" :scope "provided"]
8-
[org.clojure/clojurescript "1.10.516" :scope "provided"]
8+
[org.clojure/clojurescript "1.10.520" :scope "provided"]
99
]
1010

1111
:plugins [
@@ -17,17 +17,18 @@
1717
:java-source-paths ["src-java"]
1818
:test-paths ["test-clojure"]
1919

20-
:javac-options ["-target" "8" "-source" "8"]
20+
:javac-options ["-target" "8" "-source" "8" "-bootclasspath" ~(str (or (System/getenv "JAVA8_HOME") (throw (Exception. "Please set JAVA8_HOME"))) "/jre/lib/rt.jar")]
21+
:jvm-opts ["-ea"]
2122

2223
:aliases {"test-all" ["do" ["test"] ["test-cljs"]]
2324
"test-cljs" ["run" "-m" "me.tonsky.persistent-sorted-set.repl/run-tests"]
2425
"repl-cljs" ["run" "-m" "me.tonsky.persistent-sorted-set.repl/repl"]
2526
"bench" ["trampoline" "with-profile" "+bench" "run" "-m" "me.tonsky.persistent_sorted_set.Bench"]}
2627

2728
:profiles {
28-
:1.9 { :dependencies [[org.clojure/clojure "1.9.0" :scope "provided"]
29-
[org.clojure/clojurescript "1.9.946" :scope "provided"]] }
30-
:bench { :dependencies [[com.datomic/datomic-free "0.9.5703"]]
31-
:java-source-paths ["bench-java"] }
29+
:1.9 {:dependencies [[org.clojure/clojure "1.9.0" :scope "provided"]
30+
[org.clojure/clojurescript "1.9.946" :scope "provided"]] }
31+
:bench {:dependencies [[com.datomic/datomic-free "0.9.5703"]]
32+
:java-source-paths ["bench-java"] }
3233
}
3334
)

src-clojure/me/tonsky/persistent_sorted_set.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
(loop [nodes (mapv ->Leaf (split keys len Object avg max))]
9191
(case (count nodes)
9292
0 (PersistentSortedSet. cmp)
93-
1 (PersistentSortedSet. {} cmp (first nodes) len edit)
93+
1 (PersistentSortedSet. {} cmp (first nodes) len edit 0)
9494
(recur (mapv ->Node (split nodes (count nodes) Leaf avg max))))))))
9595

9696

src-java/me/tonsky/persistent_sorted_set/Chunk.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@
55

66
@SuppressWarnings("unchecked")
77
class Chunk implements IChunk {
8+
final PersistentSortedSet _set;
89
final Object[] _keys;
910
final int _idx, _end;
1011
final boolean _asc;
12+
final int _version;
1113

1214
Chunk(Seq seq) {
15+
_set = seq._set;
1316
_asc = seq._asc;
1417
_idx = seq._idx;
1518
_keys = seq._node._keys;
19+
_version = seq._version;
1620
if (_asc) {
1721
int end = seq._node._len - 1;
1822
if (seq._keyTo != null)
@@ -28,20 +32,29 @@ class Chunk implements IChunk {
2832
}
2933
}
3034

31-
Chunk(Object[] keys, int idx, int end, boolean asc) {
35+
Chunk(PersistentSortedSet set, Object[] keys, int idx, int end, boolean asc, int version) {
36+
_set = set;
3237
_keys = keys;
3338
_idx = idx;
3439
_end = end;
3540
_asc = asc;
41+
_version = version;
42+
}
43+
44+
void checkVersion() {
45+
if (_version != _set._version)
46+
throw new RuntimeException("Tovarisch, you are iterating and mutating a transient set at the same time!");
3647
}
3748

3849
public IChunk dropFirst() {
50+
checkVersion();
3951
if (_idx == _end)
4052
throw new IllegalStateException("dropFirst of empty chunk");
41-
return new Chunk(_keys, _asc ? _idx+1 : _idx-1, _end, _asc);
53+
return new Chunk(_set, _keys, _asc ? _idx+1 : _idx-1, _end, _asc, _version);
4254
}
4355

4456
public Object reduce(IFn f, Object start) {
57+
checkVersion();
4558
Object ret = f.invoke(start, _keys[_idx]);
4659
if (ret instanceof Reduced)
4760
return ((Reduced) ret).deref();
@@ -61,17 +74,20 @@ public Object reduce(IFn f, Object start) {
6174
}
6275

6376
public Object nth(int i) {
77+
checkVersion();
6478
assert (i >= 0 && i < count());
6579
return _asc ? _keys[_idx + i] : _keys[_idx - i];
6680
}
6781

6882
public Object nth(int i, Object notFound) {
83+
checkVersion();
6984
if (i >= 0 && i < count())
7085
return nth(i);
7186
return notFound;
7287
}
7388

7489
public int count() {
90+
checkVersion();
7591
if (_asc) return _end - _idx + 1;
7692
else return _idx - _end + 1;
7793
}

src-java/me/tonsky/persistent_sorted_set/PersistentSortedSet.java

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public static void setMaxLen(int maxLen) {
2121
Leaf _root;
2222
int _count;
2323
final Edit _edit;
24+
int _version = 0;
2425

2526
PersistentSortedSet() { this(null, RT.DEFAULT_COMPARATOR); }
2627
public PersistentSortedSet(Comparator cmp) { this(null, cmp); }
@@ -31,11 +32,12 @@ public PersistentSortedSet(IPersistentMap meta, Comparator cmp) {
3132
_count = 0;
3233
}
3334

34-
public PersistentSortedSet(IPersistentMap meta, Comparator cmp, Leaf root, int count, Edit edit) {
35+
public PersistentSortedSet(IPersistentMap meta, Comparator cmp, Leaf root, int count, Edit edit, int version) {
3536
super(meta, cmp);
3637
_root = root;
3738
_count = count;
3839
_edit = edit;
40+
_version = version;
3941
}
4042

4143
void ensureEditable(boolean value) {
@@ -55,10 +57,10 @@ public Seq slice(Object from, Object to, Comparator cmp) {
5557
if (from == null) {
5658
while (true) {
5759
if (node instanceof Node) {
58-
seq = new Seq(null, this,seq, node, 0, null, null, true);
60+
seq = new Seq(null, this,seq, node, 0, null, null, true, _version);
5961
node = seq.child();
6062
} else {
61-
seq = new Seq(null, this,seq, node, 0, to, cmp, true);
63+
seq = new Seq(null, this,seq, node, 0, to, cmp, true, _version);
6264
return seq.over() ? null : seq;
6365
}
6466
}
@@ -69,10 +71,10 @@ public Seq slice(Object from, Object to, Comparator cmp) {
6971
if (idx < 0) idx = -idx-1;
7072
if (idx == node._len) return null;
7173
if (node instanceof Node) {
72-
seq = new Seq(null, this,seq, node, idx, null, null, true);
74+
seq = new Seq(null, this,seq, node, idx, null, null, true, _version);
7375
node = seq.child();
7476
} else { // Leaf
75-
seq = new Seq(null, this,seq, node, idx, to, cmp, true);
77+
seq = new Seq(null, this,seq, node, idx, to, cmp, true, _version);
7678
return seq.over() ? null : seq;
7779
}
7880
}
@@ -90,10 +92,10 @@ public Seq rslice(Object from, Object to, Comparator cmp) {
9092
while (true) {
9193
int idx = node._len-1;
9294
if (node instanceof Node) {
93-
seq = new Seq(null, this,seq, node, idx, null, null, false);
95+
seq = new Seq(null, this,seq, node, idx, null, null, false, _version);
9496
node = seq.child();
9597
} else {
96-
seq = new Seq(null, this,seq, node, idx, to, cmp, false);
98+
seq = new Seq(null, this,seq, node, idx, to, cmp, false, _version);
9799
return seq.over() ? null : seq;
98100
}
99101
}
@@ -103,15 +105,15 @@ public Seq rslice(Object from, Object to, Comparator cmp) {
103105
if (node instanceof Node) {
104106
int idx = node.searchLast(from, cmp) + 1;
105107
if (idx == node._len) --idx; // last or beyond, clamp to last
106-
seq = new Seq(null, this,seq, node, idx, null, null, false);
108+
seq = new Seq(null, this,seq, node, idx, null, null, false, _version);
107109
node = seq.child();
108110
} else { // Leaf
109111
int idx = node.searchLast(from, cmp);
110112
if (idx == -1) { // not in this, so definitely in prev
111-
seq = new Seq(null, this,seq, node, 0, to, cmp, false);
113+
seq = new Seq(null, this,seq, node, 0, to, cmp, false, _version);
112114
return seq.advance() ? seq : null;
113115
} else { // exact match
114-
seq = new Seq(null, this,seq, node, idx, to, cmp, false);
116+
seq = new Seq(null, this,seq, node, idx, to, cmp, false, _version);
115117
return seq.over() ? null : seq;
116118
}
117119
}
@@ -134,7 +136,7 @@ public String toString() {
134136
// IObj
135137
public PersistentSortedSet withMeta(IPersistentMap meta) {
136138
if(_meta == meta) return this;
137-
return new PersistentSortedSet(meta, _cmp, _root, _count, _edit);
139+
return new PersistentSortedSet(meta, _cmp, _root, _count, _edit, _version);
138140
}
139141

140142
// Counted
@@ -178,15 +180,16 @@ public PersistentSortedSet cons(Object key, Comparator cmp) {
178180
_root = new Node(keys, nodes, 2, _edit);
179181
}
180182
_count++;
183+
_version++;
181184
return this;
182185
}
183186

184187
if (1 == nodes.length)
185-
return new PersistentSortedSet(_meta, _cmp, nodes[0], _count+1, _edit);
188+
return new PersistentSortedSet(_meta, _cmp, nodes[0], _count+1, _edit, _version+1);
186189

187190
Object keys[] = new Object[] { nodes[0].maxKey(), nodes[1].maxKey() };
188191
Leaf newRoot = new Node(keys, nodes, 2, _edit);
189-
return new PersistentSortedSet(_meta, _cmp, newRoot, _count+1, _edit);
192+
return new PersistentSortedSet(_meta, _cmp, newRoot, _count+1, _edit, _version+1);
190193
}
191194

192195
// IPersistentSet
@@ -200,20 +203,21 @@ public PersistentSortedSet disjoin(Object key, Comparator cmp) {
200203
// not in set
201204
if (UNCHANGED == nodes) return this;
202205
// in place update
203-
if (nodes == EARLY_EXIT) { _count--; return this; }
206+
if (nodes == EARLY_EXIT) { _count--; _version++; return this; }
204207
Leaf newRoot = nodes[1];
205208
if (_edit.editable()) {
206209
if (newRoot instanceof Node && newRoot._len == 1)
207210
newRoot = ((Node) newRoot)._children[0];
208211
_root = newRoot;
209212
_count--;
213+
_version++;
210214
return this;
211215
}
212216
if (newRoot instanceof Node && newRoot._len == 1) {
213217
newRoot = ((Node) newRoot)._children[0];
214-
return new PersistentSortedSet(_meta, _cmp, newRoot, _count-1, _edit);
218+
return new PersistentSortedSet(_meta, _cmp, newRoot, _count-1, _edit, _version+1);
215219
}
216-
return new PersistentSortedSet(_meta, _cmp, newRoot, _count-1, _edit);
220+
return new PersistentSortedSet(_meta, _cmp, newRoot, _count-1, _edit, _version+1);
217221
}
218222

219223
public boolean contains(Object key) {
@@ -223,7 +227,7 @@ public boolean contains(Object key) {
223227
// IEditableCollection
224228
public PersistentSortedSet asTransient() {
225229
ensureEditable(false);
226-
return new PersistentSortedSet(_meta, _cmp, _root, _count, new Edit(true));
230+
return new PersistentSortedSet(_meta, _cmp, _root, _count, new Edit(true), _version);
227231
}
228232

229233
// ITransientCollection

0 commit comments

Comments
 (0)