From fc99be7aea1f2b46015e88178323a5131f2d081c Mon Sep 17 00:00:00 2001
From: Jozef Wagner <jozef.wagner@gmail.com>
Date: Fri, 4 Jan 2013 19:21:27 +0100
Subject: [PATCH] ArrayVector: New vector implementation for small vectors

---
 src/clj/cljs/compiler.clj |   13 +++-
 src/clj/cljs/core.clj     |   10 ++-
 src/cljs/cljs/core.cljs   |  190 ++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 204 insertions(+), 9 deletions(-)

diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj
index 2fc41f6..7b0ef11 100644
--- a/src/clj/cljs/compiler.clj
+++ b/src/clj/cljs/compiler.clj
@@ -265,10 +265,15 @@
 (defmethod emit :vector
   [{:keys [items env]}]
   (emit-wrap env
-    (if (empty? items)
-      (emits "cljs.core.PersistentVector.EMPTY")
-      (emits "cljs.core.PersistentVector.fromArray(["
-             (comma-sep items) "], true)"))))
+    (cond
+     (empty? items)
+     (emits "cljs.core.ArrayVector.EMPTY")
+     (< (count items) 32)
+     (emits "(new cljs.core.ArrayVector(null, ["
+            (comma-sep items) "], null))")
+     :else
+     (emits "cljs.core.PersistentVector.fromArray(["
+            (comma-sep items) "], true)"))))
 
 (defmethod emit :set
   [{:keys [items env]}]
diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj
index de58a39..6c82154 100644
--- a/src/clj/cljs/core.clj
+++ b/src/clj/cljs/core.clj
@@ -56,8 +56,9 @@
          pb (fn pb [bvec b v]
               (core/let [pvec
                      (fn [bvec b val]
-                       (core/let [gvec (gensym "vec__")]
-                         (core/loop [ret (-> bvec (conj gvec) (conj val))
+                       (core/let [gvec (gensym "vec__")
+                                  arr-vec? (clojure.core/= 'ArrayVector (:tag (meta val)))]
+                         (core/loop [ret (-> bvec (conj gvec) (conj (if arr-vec? (with-meta val nil) val)))
                                      n 0
                                      bs b
                                      seen-rest? false]
@@ -71,7 +72,10 @@
                                  (= firstb :as) (pb ret (second bs) gvec)
                                  :else (if seen-rest?
                                          (throw (new Exception "Unsupported binding form, only :as can follow & parameter"))
-                                         (recur (pb ret firstb  (list `nth gvec n nil))
+                                         (recur (pb ret firstb
+                                                    (if arr-vec?
+                                                      (list 'js* "(~{}[~{}])" (list `.-arr gvec) n)
+                                                      (list `nth gvec n nil)))
                                                 (core/inc n)
                                                 (next bs)
                                                 seen-rest?))))
diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs
index a6a4379..80e3295 100644
--- a/src/cljs/cljs/core.cljs
+++ b/src/cljs/cljs/core.cljs
@@ -2858,6 +2858,184 @@ reduces them without incurring seq initialization"
 
 (set! cljs.core.Vector/fromArray (fn [xs] (Vector. nil xs nil)))
 
+;;; ArrayVector
+
+(declare PersistentVector TransientArrayVector)
+
+(deftype ArrayVector [meta arr ^:mutable __hash]
+  Object
+  (toString [this] (pr-str this))
+
+  IWithMeta
+  (-with-meta [coll meta]
+    (ArrayVector. meta arr __hash))
+
+  IMeta
+  (-meta [coll] meta)
+
+  IStack
+  (-peek [coll]
+    (let [l (.-length arr)]
+      (when-not (zero? l)
+        (aget arr (dec l)))))
+  (-pop [coll]
+    (let [l (.-length arr)]
+      (cond (zero? l) (throw (js/Error. "Can't pop empty vector"))
+            (== l 1) (-with-meta cljs.core.ArrayVector/EMPTY meta)
+            :else (ArrayVector. meta (.slice arr 0 (dec l)) nil))))
+
+  ICollection
+  (-conj [coll o]
+    (let [new-arr (.slice arr)]
+      (.push new-arr o)
+      (if (< (.-length arr) cljs.core.ArrayVector/PERSISTENTVECTOR_THRESHOLD)
+        (ArrayVector. meta new-arr nil)
+        (cljs.core.PersistentVector/fromArray new-arr false))))
+
+  IEmptyableCollection
+  (-empty [coll] (-with-meta cljs.core.ArrayVector/EMPTY meta))
+
+  ISequential
+  IEquiv
+  (-equiv [coll other] (equiv-sequential coll other))
+
+  IHash
+  (-hash [coll] (caching-hash coll hash-coll __hash))
+
+  ISeqable
+  (-seq [coll] (when (pos? (.-length arr)) coll))
+
+  ISeq
+  (-first [coll] (aget arr 0))
+  (-rest [coll]
+    (let [l (.-length arr)]
+      (cond (zero? l) coll
+            (== l 1) (-with-meta cljs.core.ArrayVector/EMPTY meta)
+            :else (ArrayVector. meta (.slice arr 1) nil))))
+
+  INext
+  (-next [coll]
+    (let [l (.-length arr)]
+        (cond (zero? l) nil
+              (== l 1) nil
+              :else (ArrayVector. meta (.slice arr 1) nil))))
+
+  ICounted
+  (-count [coll] (.-length arr))
+
+  IIndexed
+  (-nth [coll n] (aget arr n))
+  (-nth [coll n not-found]
+    (if (and (<= 0 n) (< n (.-length arr)))
+      (aget arr n)
+      not-found))
+
+  ILookup
+  (-lookup [coll k] (-nth coll k nil))
+  (-lookup [coll k not-found] (-nth coll k not-found))
+
+  IMapEntry
+  (-key [coll] (-nth coll 0))
+  (-val [coll] (-nth coll 1))
+
+  IAssociative
+  (-assoc [coll k v]
+    (let [new-arr (.slice arr)
+          l (.-length arr)]
+      (cond (and (<= 0 k) (< k l))
+            (do (aset new-arr k v)
+                (ArrayVector. meta new-arr nil))
+            (== k l)
+            (-conj coll v)
+            :else
+            (throw (js/Error.
+                    (str "Index " k " out of bounds  [0," l "]"))))))
+
+  IVector
+  (-assoc-n [coll n val] (-assoc coll n val))
+
+  IReduce
+  (-reduce [v f] (ci-reduce v f))
+  (-reduce [v f start] (ci-reduce v f start))
+
+  IKVReduce
+  (-kv-reduce [v f init]
+    (let [l (.-length arr)]
+      (loop [i 0 init init]
+        (if (< i l)
+          (let [x (aget arr i)
+                new-init (f init i x)]
+            (if (reduced? new-init)
+              @new-init
+              (recur (inc i) new-init)))
+          init))))
+
+  IFn
+  (-invoke [coll k] (-lookup coll k))
+  (-invoke [coll k not-found] (-lookup coll k not-found))
+
+  IEditableCollection
+  (-as-transient [coll] (TransientArrayVector. (.slice arr)))
+
+  IReversible
+  (-rseq [coll]
+    (let [l (.-length arr)]
+      (if (pos? l)
+        (RSeq. coll (dec l) nil)
+        ()))))
+
+(set! cljs.core.ArrayVector/EMPTY (ArrayVector. nil (array) 0))
+
+(set! cljs.core.ArrayVector/PERSISTENTVECTOR_THRESHOLD 32)
+
+(deftype TransientArrayVector [arr]
+  ITransientCollection
+  (-conj! [tcoll o]
+    (.push arr o)
+    (if (< (.-length arr) cljs.core.ArrayVector/PERSISTENTVECTOR_THRESHOLD)
+      tcoll
+      (-as-transient (cljs.core.PersistentVector/fromArray arr true))))
+  (-persistent! [tcoll]
+    (ArrayVector. nil arr nil)) ;; should clone here?
+
+  ITransientAssociative
+  (-assoc! [tcoll key val] (-assoc-n! tcoll key val))
+
+  ITransientVector
+  (-assoc-n! [tcoll k v]
+    (let [l (.-length arr)]
+      (cond (and (<= 0 k) (< k l))
+            (do (aset arr k v)
+                tcoll)
+            (== k l)
+            (-conj! tcoll v)
+            :else
+            (throw (js/Error.
+                    (str "Index " k " out of bounds  [0," l "]"))))))
+  (-pop! [tcoll]
+    (when (zero? (.-length arr))
+      (throw (js/Error. "Can't pop empty vector")))
+    (.pop arr)
+    tcoll)
+
+  ICounted
+  (-count [coll] (.-length arr))
+
+  IIndexed
+  (-nth [coll n] (aget arr n))
+  (-nth [coll n not-found]
+    (if (and (<= 0 n) (< n (.-length arr)))
+      (aget arr n)
+      not-found))
+
+  ILookup
+  (-lookup [coll k] (-nth coll k nil))
+  (-lookup [coll k not-found] (-nth coll k not-found))
+
+  IFn
+  (-invoke [coll k] (-lookup coll k))
+  (-invoke [coll k not-found] (-lookup coll k not-found)))
+
 ;;; PersistentVector
 
 (deftype VectorNode [edit arr])
@@ -3103,7 +3281,7 @@ reduces them without incurring seq initialization"
 (defn vec [coll]
   (-persistent!
    (reduce -conj!
-           (-as-transient cljs.core.PersistentVector/EMPTY)
+           (-as-transient cljs.core.ArrayVector/EMPTY)
            coll)))
 
 (defn vector [& args] (vec args))
@@ -3148,7 +3326,7 @@ reduces them without incurring seq initialization"
 
   IEmptyableCollection
   (-empty [coll]
-    (with-meta cljs.core.PersistentVector/EMPTY meta))
+    (with-meta cljs.core.ArrayVector/EMPTY meta))
 
   IChunkedSeq
   (-chunked-first [coll]
@@ -6501,6 +6679,9 @@ reduces them without incurring seq initialization"
   PersistentVector
   (-pr-seq [coll opts] ^:deprecation-nowarn (pr-sequential pr-seq "[" " " "]" opts coll))
 
+  ArrayVector
+  (-pr-seq [coll opts] ^:deprecation-nowarn (pr-sequential pr-seq "[" " " "]" opts coll))
+
   ChunkedCons
   (-pr-seq [coll opts] ^:deprecation-nowarn (pr-sequential pr-seq "(" " " ")" opts coll))
 
@@ -6634,6 +6815,9 @@ reduces them without incurring seq initialization"
   Vector
   (-pr-writer [coll writer opts] ^:deprecation-nowarn (pr-sequential-writer writer pr-writer "[" " " "]" opts coll))
 
+  ArrayVector
+  (-pr-writer [coll writer opts] ^:deprecation-nowarn (pr-sequential-writer writer pr-writer "[" " " "]" opts coll))
+
   PersistentVector
   (-pr-writer [coll writer opts] ^:deprecation-nowarn (pr-sequential-writer writer pr-writer "[" " " "]" opts coll))
 
@@ -6689,6 +6873,8 @@ reduces them without incurring seq initialization"
 
 ;; IComparable
 (extend-protocol IComparable
+  ArrayVector
+  (-compare [x y] (compare-indexed x y))
   PersistentVector
   (-compare [x y] (compare-indexed x y)))
 
-- 
1.7.10.4

