Skip to content

Commit 2c80e25

Browse files
spinningtopsofdoomswannodette
authored andcommitted
CLJS-836: Replace seq-based iterators with direct iterator for all non-seq collections that use SeqIterator
Iterators are now created for PesistentHashMap, PersistentHashSet, PersistentQueue, and any type created by defrecord.
1 parent bc0cd45 commit 2c80e25

File tree

4 files changed

+198
-3
lines changed

4 files changed

+198
-3
lines changed

benchmark/cljs/benchmark_runner.cljs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,26 @@
322322
(simple-benchmark [r r] (last r) 1)
323323
(println)
324324

325+
(println ";; iterators")
326+
(def ipmap (apply hash-map (range 2000)))
327+
328+
(println ";; Sequence iterator")
329+
(simple-benchmark [s (seq ipmap)]
330+
(let [iter (seq-iter s)]
331+
(loop [v nil]
332+
(if (.hasNext iter)
333+
(recur (.next iter))
334+
v)))
335+
1000)
336+
(println ";; Direct iterator")
337+
(simple-benchmark []
338+
(let [iter (-iterator ipmap)]
339+
(loop [v nil]
340+
(if (.hasNext iter)
341+
(recur (.next iter))
342+
v)))
343+
1000)
344+
325345
(println ";;; comprehensions")
326346
(simple-benchmark [xs (range 512)] (last (for [x xs y xs] (+ x y))) 1)
327347
(simple-benchmark [xs (vec (range 512))] (last (for [x xs y xs] (+ x y))) 4)

src/main/cljs/cljs/core.cljs

Lines changed: 138 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5330,6 +5330,21 @@ reduces them without incurring seq initialization"
53305330

53315331
;;; PersistentQueue ;;;
53325332

5333+
(deftype PersistentQueueIter [^:mutable fseq riter]
5334+
Object
5335+
(hasNext [_]
5336+
(or (and (some? fseq) (seq fseq)) (and (some? riter) (.hasNext riter))))
5337+
(next [_]
5338+
(cond
5339+
(some? fseq)
5340+
(let [ret (first fseq)]
5341+
(set! fseq (next fseq))
5342+
ret)
5343+
(and (some? riter) ^boolean (.hasNext riter))
5344+
(.next riter)
5345+
:else (throw (js/Error. "No such element"))))
5346+
(remove [_] (js/Error. "Unsupported operation")))
5347+
53335348
(deftype PersistentQueueSeq [meta front rear ^:mutable __hash]
53345349
Object
53355350
(toString [coll]
@@ -5380,6 +5395,10 @@ reduces them without incurring seq initialization"
53805395
ICloneable
53815396
(-clone [coll] (PersistentQueue. meta count front rear __hash))
53825397

5398+
IIterable
5399+
(-iterator [coll]
5400+
(PersistentQueueIter. front (-iterator rear)))
5401+
53835402
IWithMeta
53845403
(-with-meta [coll meta] (PersistentQueue. meta count front rear __hash))
53855404

@@ -5607,6 +5626,19 @@ reduces them without incurring seq initialization"
56075626

56085627
(set! (.-fromObject ObjMap) (fn [ks obj] (ObjMap. nil ks obj 0 nil)))
56095628

5629+
;; Record Iterator
5630+
(deftype RecordIter [^:mutable i record base-count fields ext-map-iter]
5631+
Object
5632+
(hasNext [_]
5633+
(or (< i base-count) (.hasNext ext-map-iter)))
5634+
(next [_]
5635+
(if (< i base-count)
5636+
(let [k (nth fields i)]
5637+
(set! i (inc i))
5638+
[k (-lookup record k)])
5639+
(.next ext-map-iter)))
5640+
(remove [_] (js/Error. "Unsupported operation")))
5641+
56105642
;; EXPERIMENTAL: subject to change
56115643
(deftype ES6EntriesIterator [^:mutable s]
56125644
Object
@@ -6109,6 +6141,44 @@ reduces them without incurring seq initialization"
61096141

61106142
(declare ArrayNode)
61116143

6144+
(deftype NodeIterator [arr ^:mutable i ^:mutable next-entry ^:mutable next-iter]
6145+
Object
6146+
(advance [this]
6147+
(let [len (alength arr)]
6148+
(loop []
6149+
(if (< i len)
6150+
(let [key (aget arr i)
6151+
node-or-val (aget arr (inc i))
6152+
^boolean found
6153+
(cond (some? key)
6154+
(set! next-entry [key node-or-val])
6155+
(some? node-or-val)
6156+
(let [new-iter (-iterator node-or-val)]
6157+
(if ^boolean (.hasNext new-iter)
6158+
(set! next-iter new-iter)
6159+
false))
6160+
:else false)]
6161+
(set! i (+ i 2))
6162+
(if found true (recur)))
6163+
false))))
6164+
(hasNext [this]
6165+
(or (some? next-entry) (some? next-iter) (.advance this)))
6166+
(next [this]
6167+
(cond
6168+
(some? next-entry)
6169+
(let [ret next-entry]
6170+
(set! next-entry nil)
6171+
ret)
6172+
(some? next-iter)
6173+
(let [ret (.next next-iter)]
6174+
(when-not ^boolean (.hasNext next-iter)
6175+
(set! next-iter nil))
6176+
ret)
6177+
^boolean (.advance this)
6178+
(.next this)
6179+
:else (throw (js/Error. "No such element"))))
6180+
(remove [_] (js/Error. "Unsupported operation")))
6181+
61126182
(deftype BitmapIndexedNode [edit ^:mutable bitmap ^:mutable arr]
61136183
Object
61146184
(inode-assoc [inode shift hash key val added-leaf?]
@@ -6303,7 +6373,11 @@ reduces them without incurring seq initialization"
63036373
:else inode)))))
63046374

63056375
(kv-reduce [inode f init]
6306-
(inode-kv-reduce arr f init)))
6376+
(inode-kv-reduce arr f init))
6377+
6378+
IIterable
6379+
(-iterator [coll]
6380+
(NodeIterator. arr 0 nil nil)))
63076381

63086382
(set! (.-EMPTY BitmapIndexedNode) (BitmapIndexedNode. nil 0 (make-array 0)))
63096383

@@ -6320,6 +6394,26 @@ reduces them without incurring seq initialization"
63206394
(recur (inc i) j bitmap))
63216395
(BitmapIndexedNode. edit bitmap new-arr)))))
63226396

6397+
(deftype ArrayNodeIterator [arr ^:mutable i ^:mutable next-iter]
6398+
Object
6399+
(hasNext [this]
6400+
(let [len (alength arr)]
6401+
(loop []
6402+
(if-not (and (some? next-iter) ^boolean (.hasNext next-iter))
6403+
(if (< i len)
6404+
(let [node (aget arr i)]
6405+
(set! i (inc i))
6406+
(when (some? node)
6407+
(set! next-iter (-iterator node)))
6408+
(recur))
6409+
false)
6410+
true))))
6411+
(next [this]
6412+
(if ^boolean (.hasNext this)
6413+
(.next next-iter)
6414+
(throw (js/Error. "No such element"))))
6415+
(remove [_] (js/Error. "Unsupported operation")))
6416+
63236417
(deftype ArrayNode [edit ^:mutable cnt ^:mutable arr]
63246418
Object
63256419
(inode-assoc [inode shift hash key val added-leaf?]
@@ -6415,7 +6509,11 @@ reduces them without incurring seq initialization"
64156509
@init
64166510
(recur (inc i) init)))
64176511
(recur (inc i) init)))
6418-
init)))))
6512+
init))))
6513+
6514+
IIterable
6515+
(-iterator [coll]
6516+
(ArrayNodeIterator. arr 0 nil)))
64196517

64206518
(defn- hash-collision-node-find-index [arr cnt key]
64216519
(let [lim (* 2 cnt)]
@@ -6522,7 +6620,11 @@ reduces them without incurring seq initialization"
65226620
editable))))))
65236621

65246622
(kv-reduce [inode f init]
6525-
(inode-kv-reduce arr f init)))
6623+
(inode-kv-reduce arr f init))
6624+
6625+
IIterable
6626+
(-iterator [coll]
6627+
(NodeIterator. arr 0 nil nil)))
65266628

65276629
(defn- create-node
65286630
([shift key1 val1 key2hash key2 val2]
@@ -6660,6 +6762,18 @@ reduces them without incurring seq initialization"
66606762

66616763
(declare TransientHashMap)
66626764

6765+
(deftype HashMapIter [nil-val root-iter ^:mutable seen]
6766+
Object
6767+
(hasNext [_]
6768+
(and ^boolean seen ^boolean (.hasNext root-iter)))
6769+
(next [_]
6770+
(if-not ^boolean seen
6771+
(do
6772+
(set! seen true)
6773+
nil-val)
6774+
(.next root-iter)))
6775+
(remove [_] (js/Error. "Unsupported operation")))
6776+
66636777
(deftype PersistentHashMap [meta cnt root ^boolean has-nil? nil-val ^:mutable __hash]
66646778
Object
66656779
(toString [coll]
@@ -6685,6 +6799,13 @@ reduces them without incurring seq initialization"
66856799
ICloneable
66866800
(-clone [_] (PersistentHashMap. meta cnt root has-nil? nil-val __hash))
66876801

6802+
IIterable
6803+
(-iterator [coll]
6804+
(let [root-iter (if ^boolean root (-iterator root) nil-iter)]
6805+
(if has-nil?
6806+
(HashMapIter. nil-val root-iter false)
6807+
root-iter)))
6808+
66886809
IWithMeta
66896810
(-with-meta [coll meta] (PersistentHashMap. meta cnt root has-nil? nil-val __hash))
66906811

@@ -7812,6 +7933,16 @@ reduces them without incurring seq initialization"
78127933

78137934
(declare TransientHashSet)
78147935

7936+
(deftype HashSetIter [iter]
7937+
Object
7938+
(hasNext [_]
7939+
(.hasNext iter))
7940+
(next [_]
7941+
(if ^boolean (.hasNext iter)
7942+
(aget (.-tail (.next iter)) 0)
7943+
(throw (js/Error. "No such element"))))
7944+
(remove [_] (js/Error. "Unsupported operation")))
7945+
78157946
(deftype PersistentHashSet [meta hash-map ^:mutable __hash]
78167947
Object
78177948
(toString [coll]
@@ -7835,6 +7966,10 @@ reduces them without incurring seq initialization"
78357966
ICloneable
78367967
(-clone [_] (PersistentHashSet. meta hash-map __hash))
78377968

7969+
IIterable
7970+
(-iterator [coll]
7971+
(HashSetIter. (-iterator hash-map)))
7972+
78387973
IWithMeta
78397974
(-with-meta [coll meta] (PersistentHashSet. meta hash-map __hash))
78407975

src/main/clojure/cljs/core.cljc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,6 +1634,9 @@
16341634
`(~'-seq [this#] (seq (concat [~@(map #(core/list `vector (keyword %) %) base-fields)]
16351635
~'__extmap)))
16361636

1637+
'IIterable
1638+
`(~'-iterator [~gs] (RecordIter. 0 ~gs ~(count base-fields) [~@(map keyword base-fields)] (-iterator ~'__extmap) ))
1639+
16371640
'IPrintWithWriter
16381641
`(~'-pr-writer [this# writer# opts#]
16391642
(let [pr-pair# (fn [keyval#] (pr-sequential-writer writer# pr-writer "" " " "" opts# keyval#))]

src/test/cljs/cljs/core_test.cljs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,6 +1418,43 @@
14181418
(is (.equiv #{:cat :dog :bird} #{:cat :dog :bird}))
14191419
))
14201420

1421+
(defn seq-iter-match
1422+
[coll]
1423+
(let [i (-iterator coll)]
1424+
(loop [s (seq coll)
1425+
n 0]
1426+
(if (seq s)
1427+
(do
1428+
(when-not (.hasNext i)
1429+
(throw
1430+
(js/Error.
1431+
(str "Iterator exhausted before seq at(" n ")" ))))
1432+
(let [iv (.next i)
1433+
sv (first s)]
1434+
(when-not (= iv sv)
1435+
(throw
1436+
(js/Error.
1437+
(str "Iterator value " iv " and seq value " sv " did not match at ( " n ")")))))
1438+
(recur (rest s) (inc n)))
1439+
(if (.hasNext i)
1440+
(throw
1441+
(js/Error.
1442+
(str "Seq exhausted before iterator at (" n ")")))
1443+
true)))))
1444+
1445+
(defrecord TestIterRec [a b])
1446+
1447+
(deftest coll-iter-seq-match
1448+
(testing "Direct iterators match sequences"
1449+
(let [test-map (apply hash-map (range 200))
1450+
test-set (apply hash-set (range 200))
1451+
test-queue (into cljs.core.PersistentQueue.EMPTY (vec (range 100)))
1452+
test-record (into (TestIterRec. 1 2) {:c 3 :d 4})]
1453+
(is (= true (seq-iter-match test-map)))
1454+
(is (= true (seq-iter-match test-set)))
1455+
(is (= true (seq-iter-match test-queue)))
1456+
(is (= true (seq-iter-match test-record))))))
1457+
14211458
(deftest test-es6-interfaces
14221459
(testing "ES6 collection interfaces"
14231460
(let [iter (es6-iterator [1 2 3])]

0 commit comments

Comments
 (0)