├── .gitignore ├── LICENSE ├── README.md ├── project.clj ├── src └── devec │ ├── core.clj │ └── debug.clj └── test └── devec ├── core_bench.clj └── core_test.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | .\#* 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor tocontrol, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of New York and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # devec 2 | 3 | Double-Ended Vectors for Clojure, seamlessly interoperable with 4 | Clojure's built-in PersistentVectors. 5 | 6 | ## Usage 7 | 8 | *This is an experimental library!* 9 | 10 | devec's purpose is to expand the API of regular Clojure vectors with 11 | four new operations: 12 | 13 | 1. "conj to front" (`devec.core/conjl`); 14 | 15 | 2. "pop off the front" (`devec.core/popl`); 16 | 17 | 3. non-view slice (`devec.core/subvec`); 18 | 19 | 4. reverse (`devec.core/rvec`). 20 | 21 | 1 and 2 are already implemented, 3 and 4 will arrive in a future 22 | release. 23 | 24 | All of these can take as inputs either regular Clojure vectors or 25 | existing devec double-ended vectors. Interoperability with 26 | core.rrb-vector is possible in principle and will be implemented in a 27 | future release. 28 | 29 | Currently transients and vectors of primitives ("gvec") are not 30 | supported; they will be in a future release. 31 | 32 | In the present proof-of-concept release some features (notably seq 33 | over devec) are implemented naively so that the data structure may be 34 | tested and benchmarked on key vector operations (`conj`, `nth`, 35 | `assoc`, `pop`). Optimized implementations are forthcoming in a future 36 | release. 37 | 38 | ClojureScript support is likewise forthcoming. 39 | 40 | The above notices will be replaced with confirmations that the desired 41 | features are implemented in future releases. A more detailed 42 | discussion of devec's internals is forthcoming. 43 | 44 | ## Performance 45 | 46 | An initial benchmark suite using 47 | [Criterium](https://github.com/hugoduncan/criterium) is included and 48 | can be run with `lein test devec.core-bench`. Here are the results of 49 | some lookup benchmarks: 50 | 51 | ```clj 52 | ;; pv: 53 | (vec (range -1000000 2000000)) 54 | 55 | ;; dv: 56 | (apply devec/conjl (vec (range 2000000)) (range -1 -1000001 -1)) 57 | 58 | (criterium.core/bench (nth pv 1234)) 59 | WARNING: Final GC required 13.97028911742518 % of runtime 60 | WARNING: Final GC required 3.206823423747328 % of runtime 61 | Evaluation count : 2410190280 in 60 samples of 40169838 calls. 62 | Execution time mean : 9.289051 ns 63 | Execution time std-deviation : 0.304145 ns 64 | Execution time lower quantile : 8.918948 ns ( 2.5%) 65 | Execution time upper quantile : 9.977462 ns (97.5%) 66 | Overhead used : 15.613667 ns 67 | 68 | Found 7 outliers in 60 samples (11.6667 %) 69 | low-severe 7 (11.6667 %) 70 | Variance from outliers : 19.0283 % Variance is moderately inflated by outliers 71 | (criterium.core/bench (nth dv 1234)) 72 | WARNING: Final GC required 3.777409164183418 % of runtime 73 | Evaluation count : 2570210700 in 60 samples of 42836845 calls. 74 | Execution time mean : 8.068430 ns 75 | Execution time std-deviation : 0.332720 ns 76 | Execution time lower quantile : 7.637039 ns ( 2.5%) 77 | Execution time upper quantile : 8.924783 ns (97.5%) 78 | Overhead used : 15.613667 ns 79 | 80 | Found 4 outliers in 60 samples (6.6667 %) 81 | low-severe 3 (5.0000 %) 82 | low-mild 1 (1.6667 %) 83 | Variance from outliers : 27.1040 % Variance is moderately inflated by outliers 84 | 85 | (criterium.core/bench (nth pv 1500000)) 86 | WARNING: Final GC required 3.694663028274491 % of runtime 87 | Evaluation count : 2238508020 in 60 samples of 37308467 calls. 88 | Execution time mean : 8.701542 ns 89 | Execution time std-deviation : 0.327771 ns 90 | Execution time lower quantile : 8.311595 ns ( 2.5%) 91 | Execution time upper quantile : 9.373833 ns (97.5%) 92 | Overhead used : 15.613667 ns 93 | 94 | Found 4 outliers in 60 samples (6.6667 %) 95 | low-severe 3 (5.0000 %) 96 | low-mild 1 (1.6667 %) 97 | Variance from outliers : 23.8529 % Variance is moderately inflated by outliers 98 | (criterium.core/bench (nth dv 1500000)) 99 | WARNING: Final GC required 3.079413482216065 % of runtime 100 | Evaluation count : 2402544300 in 60 samples of 40042405 calls. 101 | Execution time mean : 9.531229 ns 102 | Execution time std-deviation : 0.398040 ns 103 | Execution time lower quantile : 9.165994 ns ( 2.5%) 104 | Execution time upper quantile : 10.594534 ns (97.5%) 105 | Overhead used : 15.613667 ns 106 | 107 | Found 5 outliers in 60 samples (8.3333 %) 108 | low-severe 2 (3.3333 %) 109 | low-mild 3 (5.0000 %) 110 | Variance from outliers : 28.6561 % Variance is moderately inflated by outliers 111 | ``` 112 | 113 | ## Underlying data structure 114 | 115 | The underlying data structure is internally very similar to a Clojure 116 | PersistentVector, with the following differences: 117 | 118 | 1. it uses a "head" array in addition to a tail; 119 | 120 | 2. its trie section admits "empty prefixes". 121 | 122 | Lookups which land in the trie section transform the supplied index by 123 | adding an `int` and subtracting another `int`, then proceed by radix 124 | search. The trie may be higher by at most one level than it would be 125 | in a PersistentVector of the same length. 126 | 127 | ## Releases and dependency information 128 | 129 | devec is currently being developed against Clojure 1.7.0-alpha5, 130 | however it should work under (and eventually will be tested against) 131 | earlier versions. 132 | 133 | [devec releases are available from Clojars](https://clojars.org/devec). 134 | 135 | Please follow the link above to discover the current release number. 136 | 137 | [Leiningen](http://leiningen.org/) dependency information: 138 | 139 | [devec "${version}"] 140 | 141 | [Maven](http://maven.apache.org/) dependency information: 142 | 143 | 144 | devec 145 | devec 146 | ${version} 147 | 148 | 149 | [Gradle](http://www.gradle.org/) dependency information: 150 | 151 | compile "devec:devec:${version}" 152 | 153 | ## Developer information 154 | 155 | Patches will only be accepted from developers who have signed the 156 | Clojure Contributor Agreement and would be happy for the code they 157 | contribute to devec to become part of the Clojure project. 158 | 159 | ## Clojure(Script) code reuse 160 | 161 | devec's double-ended vectors support the same basic functionality 162 | regular Clojure's vectors do (with the omissions listed above). Where 163 | possible, this is achieved by reusing code from Clojure's gvec and 164 | ClojureScript's PersistentVector implementations. The Clojure(Script) 165 | source files containing the relevant code carry the following 166 | copyright notice: 167 | 168 | Copyright (c) Rich Hickey. All rights reserved. 169 | The use and distribution terms for this software are covered by the 170 | Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 171 | which can be found in the file epl-v10.html at the root of this distribution. 172 | By using this software in any fashion, you are agreeing to be bound by 173 | the terms of this license. 174 | You must not remove this notice, or any other, from this software. 175 | 176 | ## Licence 177 | 178 | Copyright © 2015 Michał Marczyk 179 | 180 | Distributed under the Eclipse Public License either version 1.0 or (at 181 | your option) any later version. 182 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject devec "0.0.2-SNAPSHOT" 2 | :description "Double-Ended Vectors for Clojure" 3 | :url "https://github.com/michalmarczyk/devec" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.7.0-alpha5"]] 7 | :jvm-opts ^:replace ["-XX:+UseG1GC"] 8 | :profiles {:dev {:dependencies [[org.clojure/test.check "0.7.0"] 9 | [collection-check "0.1.5"] 10 | [criterium "0.4.3"]]}}) 11 | -------------------------------------------------------------------------------- /src/devec/core.clj: -------------------------------------------------------------------------------- 1 | (ns devec.core 2 | #_(:refer-clojure :exclude [subvec]) 3 | (:import (clojure.lang Util RT))) 4 | 5 | (defprotocol AsDoubleEndedVector 6 | (-as-devec [this])) 7 | 8 | (set! *warn-on-reflection* true) 9 | (set! *unchecked-math* :warn-on-boxed) 10 | 11 | (defn ^:private throw-unsupported [] 12 | (throw (UnsupportedOperationException.))) 13 | 14 | (defmacro ^:private compile-if [test then else] 15 | (if (eval test) 16 | then 17 | else)) 18 | 19 | (def ^:private empty-node 20 | clojure.lang.PersistentVector/EMPTY_NODE) 21 | 22 | (def ^:private empty-array 23 | (object-array 0)) 24 | 25 | #_ 26 | (gen-interface 27 | :name devec.core.IReversibleVector 28 | :extends [clojure.lang.IPersistentVector] 29 | :methods 30 | [[rvec [] devec.core.IReversibleVector]]) 31 | 32 | #_ 33 | (gen-interface 34 | :name devec.core.ISliceableVector 35 | :extends [clojure.lang.IPersistentVector] 36 | :methods 37 | [[subvec [int] devec.core.ISliceableVector] 38 | [subvec [int int] devec.core.ISliceableVector]]) 39 | 40 | (gen-interface 41 | :name devec.core.IDoubleEndedVector 42 | :extends [clojure.lang.IPersistentVector] 43 | :methods 44 | [[consLeft [Object] devec.core.IDoubleEndedVector] 45 | [popLeft [] Object]]) 46 | 47 | (gen-interface 48 | :name devec.core.IDoubleEndedVectorImpl 49 | :methods 50 | [[trieArrayFor [int] "[Ljava.lang.Object;"] 51 | [popTail [int clojure.lang.PersistentVector$Node] 52 | clojure.lang.PersistentVector$Node] 53 | [popHead [int clojure.lang.PersistentVector$Node] 54 | clojure.lang.PersistentVector$Node] 55 | [assocInTrie [int clojure.lang.PersistentVector$Node int Object] Object] 56 | [newPath [int Object] Object] 57 | [newPathRight [int Object] Object] 58 | [pushTail [int clojure.lang.PersistentVector$Node Object] Object] 59 | [pushHead [int clojure.lang.PersistentVector$Node Object] Object]]) 60 | 61 | (declare empty-devec #_->ReversedDoubleEndedVector) 62 | 63 | (deftype DoubleEndedVector [^objects head 64 | trie 65 | ^objects tail 66 | ^int headoff 67 | ^int trieoff 68 | ^int tailoff 69 | ^int shift 70 | ^int cnt 71 | ^clojure.lang.IPersistentMap _meta 72 | ^:unsynchronized-mutable ^int _hash 73 | ^:unsynchronized-mutable ^int _hasheq] 74 | AsDoubleEndedVector 75 | (-as-devec [this] 76 | this) 77 | 78 | Object 79 | (equals [this that] 80 | (cond 81 | (identical? this that) true 82 | 83 | (or (instance? clojure.lang.IPersistentVector that) 84 | (instance? java.util.RandomAccess that)) 85 | (and (== cnt (count that)) 86 | (loop [i (int 0)] 87 | (cond 88 | (== i cnt) 89 | true 90 | 91 | (.equals (.nth this i) (nth that i)) 92 | (recur (unchecked-inc-int i)) 93 | 94 | :else false))) 95 | 96 | (or (instance? clojure.lang.Sequential that) 97 | (instance? java.util.List that)) 98 | (.equals (seq this) (seq that)) 99 | 100 | :else false)) 101 | 102 | (hashCode [this] 103 | (if (== _hash (int -1)) 104 | (loop [h (int 1) i (int 0)] 105 | (if (== i cnt) 106 | (do (set! _hash (int h)) 107 | h) 108 | (let [val (.nth this i)] 109 | (recur (unchecked-add-int 110 | (unchecked-multiply-int (int 31) h) 111 | (Util/hash val)) 112 | (unchecked-inc-int i))))) 113 | _hash)) 114 | 115 | (toString [this] 116 | (pr-str this)) 117 | 118 | clojure.lang.IHashEq 119 | (hasheq [this] 120 | (if (== _hasheq (int -1)) 121 | (compile-if (resolve 'clojure.core/hash-ordered-coll) 122 | (let [h (hash-ordered-coll this)] 123 | (do (set! _hasheq (int h)) 124 | h)) 125 | (loop [h (int 1) xs (seq this)] 126 | (if xs 127 | (recur (unchecked-add-int 128 | (unchecked-multiply-int (int 31) h) 129 | (Util/hasheq (first xs))) 130 | (next xs)) 131 | (do (set! _hasheq (int h)) 132 | h)))) 133 | _hasheq)) 134 | 135 | clojure.lang.Counted 136 | (count [this] 137 | cnt) 138 | 139 | clojure.lang.IMeta 140 | (meta [this] 141 | _meta) 142 | 143 | clojure.lang.IObj 144 | (withMeta [this m] 145 | (DoubleEndedVector. 146 | head trie tail headoff trieoff tailoff shift cnt m _hash _hasheq)) 147 | 148 | clojure.lang.Indexed 149 | (nth [this i] 150 | (cond 151 | (< i headoff) 152 | (aget head i) 153 | 154 | (>= i tailoff) 155 | (if (>= i cnt) 156 | (throw (IndexOutOfBoundsException.)) 157 | (aget tail (- i tailoff))) 158 | 159 | :else 160 | (let [idx (unchecked-subtract-int i headoff) 161 | arr (.trieArrayFor this idx)] 162 | (aget arr (bit-and idx (int 0x1f)))))) 163 | 164 | (nth [this i not-found] 165 | (if (and (>= i (int 0)) (< i cnt)) 166 | (.nth this i) 167 | not-found)) 168 | 169 | clojure.lang.IPersistentCollection 170 | (cons [this x] 171 | (if (< (- cnt tailoff) (int 32)) 172 | (let [tail-len (alength tail) 173 | new-tail (object-array (inc tail-len))] 174 | (System/arraycopy tail 0 new-tail 0 tail-len) 175 | (aset new-tail tail-len x) 176 | (DoubleEndedVector. 177 | head trie new-tail headoff trieoff tailoff 178 | shift (inc cnt) _meta -1 -1)) 179 | (let [tail-node (clojure.lang.PersistentVector$Node. nil tail) 180 | effective-trie-count (+ (- cnt headoff) trieoff)] 181 | (if (> (bit-shift-right effective-trie-count 5) 182 | (bit-shift-left 1 shift)) 183 | (let [arr (object-array 32) 184 | new-trie (clojure.lang.PersistentVector$Node. nil arr) 185 | new-tail (object-array 1)] 186 | (aset arr 0 trie) 187 | (aset arr 1 (.newPath this shift tail-node)) 188 | (aset new-tail 0 x) 189 | (DoubleEndedVector. 190 | head new-trie new-tail headoff trieoff (+ tailoff 32) 191 | (+ shift 5) (inc cnt) _meta -1 -1)) 192 | (let [new-tail (object-array 1)] 193 | (aset new-tail 0 x) 194 | (DoubleEndedVector. 195 | head (.pushTail this shift trie tail-node) new-tail 196 | headoff trieoff (+ tailoff 32) 197 | shift (inc cnt) _meta -1 -1)))))) 198 | 199 | (empty [this] 200 | (with-meta empty-devec _meta)) 201 | 202 | (equiv [this that] 203 | (cond 204 | (or (instance? clojure.lang.IPersistentVector that) 205 | (instance? java.util.RandomAccess that)) 206 | (and (== cnt (count that)) 207 | (loop [i (int 0)] 208 | (cond 209 | (== i cnt) 210 | true 211 | 212 | (= (.nth this i) (nth that i)) 213 | (recur (unchecked-inc-int i)) 214 | 215 | :else false))) 216 | 217 | (or (instance? clojure.lang.Sequential that) 218 | (instance? java.util.List that)) 219 | (Util/equiv (seq this) (seq that)) 220 | 221 | :else false)) 222 | 223 | clojure.lang.IPersistentStack 224 | (peek [this] 225 | (if (pos? cnt) 226 | (aget tail (dec (alength tail))))) 227 | 228 | (pop [this] 229 | (cond 230 | (zero? cnt) 231 | (throw (IllegalStateException. "Can't pop empty vector")) 232 | 233 | (== 1 cnt) 234 | (with-meta empty-devec _meta) 235 | 236 | (> (- cnt tailoff) 1) 237 | (let [tail-len (dec (alength tail)) 238 | new-tail (object-array tail-len)] 239 | (System/arraycopy tail 0 new-tail 0 tail-len) 240 | (DoubleEndedVector. 241 | head trie new-tail headoff trieoff tailoff 242 | shift (dec cnt) _meta -1 -1)) 243 | 244 | (== headoff tailoff) 245 | (DoubleEndedVector. 246 | empty-array trie head 0 0 0 247 | 5 (dec cnt) _meta -1 -1) 248 | 249 | :else 250 | (let [new-tail (.trieArrayFor this (- (- cnt headoff) 2)) 251 | ^clojure.lang.PersistentVector$Node 252 | new-trie (.popTail this shift trie)] 253 | (cond 254 | (nil? new-trie) 255 | (DoubleEndedVector. 256 | head empty-node new-tail headoff 0 headoff 257 | 5 (dec cnt) _meta -1 -1) 258 | 259 | (> shift 5) 260 | (let [arr ^objects (.-array new-trie)] 261 | (cond 262 | (nil? (aget arr 1)) 263 | (DoubleEndedVector. 264 | head (aget arr 0) new-tail 265 | headoff 266 | (bit-and (bit-not (bit-shift-left 0x1f shift)) trieoff) 267 | (- tailoff 32) 268 | (- shift 5) (dec cnt) _meta -1 -1) 269 | 270 | (nil? (aget arr 2)) 271 | (let [^clojure.lang.PersistentVector$Node 272 | child1 (aget arr 0) 273 | arr1 (.-array child1) 274 | ^clojure.lang.PersistentVector$Node 275 | child2 (aget arr 1) 276 | arr2 (.-array child2) 277 | i (bit-and (bit-shift-left 0x1f shift) trieoff) 278 | i' (- 32 i) 279 | j (bit-and (bit-shift-left 0x1f shift) (- tailoff 33)) 280 | j' (inc j)] 281 | (if (<= (+ i' j') 32) 282 | (let [new-arr (object-array 32) 283 | new-trie (clojure.lang.PersistentVector$Node. 284 | nil new-arr)] 285 | (System/arraycopy arr1 i new-arr 0 i') 286 | (System/arraycopy arr2 0 new-arr i' j') 287 | (DoubleEndedVector. 288 | head new-trie new-tail 289 | headoff 290 | (bit-and 291 | (bit-not (bit-shift-left 0x1f shift)) 292 | trieoff) 293 | (- tailoff 32) 294 | (- shift 5) (dec cnt) _meta -1 -1)) 295 | (DoubleEndedVector. 296 | head new-trie new-tail headoff trieoff (- tailoff 32) 297 | shift (dec cnt) _meta -1 -1))) 298 | 299 | :else 300 | (DoubleEndedVector. 301 | head new-trie new-tail headoff trieoff (- tailoff 32) 302 | shift (dec cnt) _meta -1 -1))) 303 | 304 | :else 305 | (DoubleEndedVector. 306 | head new-trie new-tail headoff trieoff (- tailoff 32) 307 | shift (dec cnt) _meta -1 -1))))) 308 | 309 | clojure.lang.IPersistentVector 310 | (assocN [this i x] 311 | (cond 312 | (and (<= 0 i) (< i cnt)) 313 | (cond 314 | (< i headoff) 315 | (DoubleEndedVector. 316 | (doto head (aset i x)) trie tail headoff trieoff tailoff 317 | shift cnt _meta -1 -1) 318 | 319 | (>= i tailoff) 320 | (DoubleEndedVector. 321 | head trie (doto tail (aset (- i tailoff) x)) headoff trieoff tailoff 322 | shift cnt _meta -1 -1) 323 | 324 | :else 325 | (DoubleEndedVector. 326 | head (.assocInTrie this shift trie (+ trieoff (- i headoff)) x) tail 327 | headoff trieoff tailoff 328 | shift cnt _meta -1 -1)) 329 | 330 | (== i cnt) 331 | (.cons this x) 332 | 333 | :else 334 | (throw (IndexOutOfBoundsException.)))) 335 | 336 | (length [this] 337 | (.count this)) 338 | 339 | clojure.lang.Reversible 340 | (rseq [this] 341 | ;; FIXME 342 | (seq (map #(nth this %) (range (dec cnt) -1 -1)))) 343 | 344 | clojure.lang.Associative 345 | (assoc [this k v] 346 | (if (Util/isInteger k) 347 | (.assocN this k v) 348 | (throw (IllegalArgumentException. "Key must be integer")))) 349 | 350 | (containsKey [this k] 351 | (and (Util/isInteger k) 352 | (<= (int 0) (int k)) 353 | (< (int k) cnt))) 354 | 355 | (entryAt [this k] 356 | (if (.containsKey this k) 357 | (clojure.lang.MapEntry. k (.nth this (int k))) 358 | nil)) 359 | 360 | clojure.lang.ILookup 361 | (valAt [this k not-found] 362 | (if (Util/isInteger k) 363 | (let [i (int k)] 364 | (if (and (>= i (int 0)) (< i cnt)) 365 | (.nth this i) 366 | not-found)) 367 | not-found)) 368 | 369 | (valAt [this k] 370 | (.valAt this k nil)) 371 | 372 | clojure.lang.IFn 373 | (invoke [this k] 374 | (if (Util/isInteger k) 375 | (let [i (int k)] 376 | (if (and (>= i (int 0)) (< i cnt)) 377 | (.nth this i) 378 | (throw (IndexOutOfBoundsException.)))) 379 | (throw (IllegalArgumentException. "Key must be integer")))) 380 | 381 | (applyTo [this args] 382 | (let [n (RT/boundedLength args 1)] 383 | (case n 384 | 0 (throw (clojure.lang.ArityException. 385 | n (.. this (getClass) (getSimpleName)))) 386 | 1 (.invoke this (first args)) 387 | 2 (throw (clojure.lang.ArityException. 388 | n (.. this (getClass) (getSimpleName))))))) 389 | 390 | clojure.lang.Seqable 391 | (seq [this] 392 | ;; FIXME 393 | (seq (map #(nth this %) (range cnt)))) 394 | 395 | clojure.lang.Sequential 396 | 397 | #_#_ 398 | devec.core.IReversibleVector 399 | (rvec [this] 400 | ;; TODO 401 | ) 402 | 403 | #_#_#_ 404 | devec.core.ISliceableVector 405 | (subvec [this start] 406 | (.subvec this start cnt)) 407 | 408 | (subvec [this start end] 409 | ;; TODO 410 | ) 411 | 412 | devec.core.IDoubleEndedVector 413 | (consLeft [this x] 414 | (if (== headoff 32) 415 | (let [new-head (doto (object-array 1) (aset 0 x)) 416 | head-node (clojure.lang.PersistentVector$Node. nil head)] 417 | (cond 418 | (pos? trieoff) 419 | (DoubleEndedVector. 420 | new-head (.pushHead this shift trie head-node) tail 421 | 1 (- trieoff 32) (inc tailoff) 422 | shift (inc cnt) _meta -1 -1) 423 | 424 | (some? (aget (.-array ^clojure.lang.PersistentVector$Node trie) 31)) 425 | (let [arr (object-array 32) 426 | new-child (.newPathRight this shift head-node)] 427 | (aset arr 0 new-child) 428 | (aset arr 1 trie) 429 | (DoubleEndedVector. 430 | new-head (clojure.lang.PersistentVector$Node. nil arr) tail 431 | 1 (- (bit-shift-left 1 (+ shift 5)) 32) (inc tailoff) 432 | (+ shift 5) (inc cnt) _meta -1 -1)) 433 | 434 | :else 435 | (let [arr (.-array ^clojure.lang.PersistentVector$Node trie) 436 | new-child (.newPathRight this (- shift 5) head-node) 437 | new-arr (object-array 32)] 438 | ;; not bothering to compute the actual number of slots 439 | (System/arraycopy arr 0 new-arr 1 31) 440 | (aset new-arr 0 new-child) 441 | (DoubleEndedVector. 442 | new-head (clojure.lang.PersistentVector$Node. nil new-arr) tail 443 | 1 (- (bit-shift-left 1 shift) 32) (inc tailoff) 444 | shift (inc cnt) _meta -1 -1)))) 445 | (let [new-head (object-array (inc headoff))] 446 | (System/arraycopy head 0 new-head 1 headoff) 447 | (aset new-head 0 x) 448 | (DoubleEndedVector. 449 | new-head trie tail (inc headoff) trieoff (inc tailoff) 450 | shift (inc cnt) _meta -1 -1)))) 451 | 452 | (popLeft [this] 453 | (cond 454 | (zero? cnt) 455 | (throw (IllegalStateException. "Can't pop empty vector")) 456 | 457 | (== 1 cnt) 458 | (with-meta empty-devec _meta) 459 | 460 | (pos? headoff) 461 | (let [head-len (dec headoff) 462 | new-head (object-array head-len)] 463 | (System/arraycopy head 1 new-head 0 head-len) 464 | (DoubleEndedVector. 465 | new-head trie tail head-len trieoff (dec tailoff) 466 | shift (dec cnt) _meta -1 -1)) 467 | 468 | (zero? tailoff) 469 | (let [tail-len (dec cnt) 470 | new-tail (object-array tail-len)] 471 | (System/arraycopy tail 1 new-tail 0 tail-len) 472 | (DoubleEndedVector. 473 | empty-array empty-node new-tail 0 0 0 474 | 5 (dec cnt) _meta -1 -1)) 475 | 476 | :else 477 | (let [new-head (.trieArrayFor this 0) 478 | new-head (if (zero? headoff) 479 | (let [arr (object-array 31)] 480 | (System/arraycopy new-head 1 arr 0 31) 481 | arr) 482 | new-head) 483 | new-hoff (alength new-head) 484 | ^clojure.lang.PersistentVector$Node 485 | new-trie (.popHead this shift trie)] 486 | (cond 487 | (nil? new-trie) 488 | (DoubleEndedVector. 489 | new-head empty-node tail new-hoff 0 new-hoff 490 | 5 (dec cnt) _meta -1 -1) 491 | 492 | (> shift 5) 493 | (let [arr ^objects (.-array new-trie)] 494 | (cond 495 | (nil? (aget arr 1)) 496 | (DoubleEndedVector. 497 | new-head (aget arr 0) tail 498 | new-hoff 499 | (bit-and (bit-not (bit-shift-left 0x1f shift)) (+ trieoff 32)) 500 | (dec tailoff) 501 | (- shift 5) (dec cnt) _meta -1 -1) 502 | 503 | (nil? (aget arr 2)) 504 | (let [^clojure.lang.PersistentVector$Node 505 | child1 (aget arr 0) 506 | arr1 (.-array child1) 507 | ^clojure.lang.PersistentVector$Node 508 | child2 (aget arr 1) 509 | arr2 (.-array child2) 510 | i (bit-and (bit-shift-left 0x1f shift) (+ trieoff 32)) 511 | i' (- 32 i) 512 | j (bit-and (bit-shift-left 0x1f shift) (dec tailoff)) 513 | j' (inc j)] 514 | (if (<= (+ i' j') 32) 515 | (let [new-arr (object-array 32) 516 | new-trie (clojure.lang.PersistentVector$Node. 517 | nil new-arr)] 518 | (System/arraycopy arr1 i new-arr 0 i') 519 | (System/arraycopy arr2 0 new-arr i' j') 520 | (DoubleEndedVector. 521 | new-head new-trie tail 522 | new-hoff 523 | (bit-and 524 | (bit-not (bit-shift-left 0x1f shift)) 525 | (+ trieoff 32)) 526 | (dec tailoff) 527 | (- shift 5) (dec cnt) _meta -1 -1)) 528 | (DoubleEndedVector. 529 | new-head new-trie tail new-hoff (+ trieoff 32) (dec tailoff) 530 | shift (dec cnt) _meta -1 -1))) 531 | 532 | :else 533 | (DoubleEndedVector. 534 | new-head new-trie tail new-hoff (+ trieoff 32) (dec tailoff) 535 | shift (dec cnt) _meta -1 -1))) 536 | 537 | :else 538 | (DoubleEndedVector. 539 | new-head new-trie tail new-hoff (+ trieoff 32) (dec tailoff) 540 | shift (dec cnt) _meta -1 -1))))) 541 | 542 | devec.core.IDoubleEndedVectorImpl 543 | (trieArrayFor [this i] 544 | (let [i (+ i trieoff)] 545 | (loop [shift shift node trie] 546 | (if (zero? shift) 547 | (.-array ^clojure.lang.PersistentVector$Node node) 548 | (let [arr (.-array ^clojure.lang.PersistentVector$Node node)] 549 | (recur (- shift 5) 550 | (aget arr (bit-and (bit-shift-right i shift) 0x1f)))))))) 551 | 552 | (popTail [this shift node] 553 | (let [sub (bit-and 554 | (bit-shift-right 555 | (+ trieoff (- (- cnt headoff) 2)) shift) 556 | 0x1f)] 557 | (cond 558 | (> shift 5) 559 | (let [new-child (.popTail this (- shift 5) (aget (.-array node) sub))] 560 | (if (and (nil? new-child) (zero? sub)) 561 | nil 562 | (let [arr (aclone (.-array node))] 563 | (aset arr sub new-child) 564 | (clojure.lang.PersistentVector$Node. nil arr)))) 565 | 566 | (zero? sub) 567 | nil 568 | 569 | :else 570 | (let [arr (aclone (.-array node))] 571 | (aset arr sub nil) 572 | (clojure.lang.PersistentVector$Node. nil arr))))) 573 | 574 | (popHead [this shift node] 575 | (let [sub (bit-and (bit-shift-right trieoff shift) 0x1f)] 576 | (cond 577 | (> shift 5) 578 | (let [new-child (.popHead this (- shift 5) 579 | (aget (.-array node) sub))] 580 | (if (and (nil? new-child) (== 31 sub)) 581 | nil 582 | (let [arr (aclone (.-array node))] 583 | (aset arr sub new-child) 584 | (clojure.lang.PersistentVector$Node. nil arr)))) 585 | 586 | (== 31 sub) 587 | nil 588 | 589 | :else 590 | (try 591 | (let [arr (aclone (.-array node))] 592 | (aset arr sub nil) 593 | (clojure.lang.PersistentVector$Node. nil arr)) 594 | (catch Exception e 595 | (prn shift sub) 596 | (throw e)))))) 597 | 598 | (assocInTrie [this shift node i x] 599 | (if (zero? shift) 600 | (let [arr (aclone (.-array node))] 601 | (aset arr (bit-and i 0x1f) x) 602 | (clojure.lang.PersistentVector$Node. nil arr)) 603 | (let [arr (aclone (.-array node)) 604 | sub (bit-and (bit-shift-right i shift) 0x1f)] 605 | (aset arr sub (.assocInTrie this (- shift 5) (aget arr sub) i x)) 606 | (clojure.lang.PersistentVector$Node. nil arr)))) 607 | 608 | (newPath [this shift node] 609 | (if (zero? shift) 610 | node 611 | (let [ret (clojure.lang.PersistentVector$Node. nil (object-array 32))] 612 | (aset (.-array ret) 0 (.newPath this (- shift 5) node)) 613 | ret))) 614 | 615 | (newPathRight [this shift node] 616 | (if (zero? shift) 617 | node 618 | (let [ret (clojure.lang.PersistentVector$Node. nil (object-array 32))] 619 | (aset (.-array ret) 31 (.newPathRight this (- shift 5) node)) 620 | ret))) 621 | 622 | (pushTail [this shift parent tail-node] 623 | (let [sub (bit-and 624 | (bit-shift-right (+ trieoff (- (dec cnt) headoff)) shift) 625 | 0x1f) 626 | arr (aclone (.-array parent)) 627 | ret (clojure.lang.PersistentVector$Node. nil arr) 628 | node (if (== shift 5) 629 | tail-node 630 | (let [child (aget arr sub)] 631 | (if child 632 | (.pushTail this (- shift 5) child tail-node) 633 | (.newPath this (- shift 5) tail-node))))] 634 | (aset arr sub node) 635 | ret)) 636 | 637 | (pushHead [this shift parent head-node] 638 | (let [sub (bit-and (bit-shift-right (dec trieoff) shift) 0x1f) 639 | arr (aclone (.-array parent)) 640 | ret (clojure.lang.PersistentVector$Node. nil arr) 641 | node (if (== shift 5) 642 | head-node 643 | (let [child (aget arr sub)] 644 | (if child 645 | (.pushHead this (- shift 5) child head-node) 646 | (.newPathRight this (- shift 5) head-node))))] 647 | (aset arr sub node) 648 | ret)) 649 | 650 | java.io.Serializable 651 | 652 | java.lang.Comparable 653 | (compareTo [this that] 654 | (if (identical? this that) 655 | 0 656 | (let [^clojure.lang.IPersistentVector v 657 | (cast clojure.lang.IPersistentVector that) 658 | vcnt (.count v)] 659 | (cond 660 | (< cnt vcnt) -1 661 | (> cnt vcnt) 1 662 | :else 663 | (loop [i (int 0)] 664 | (if (== i cnt) 665 | 0 666 | (let [comp (Util/compare (.nth this i) (.nth v i))] 667 | (if (zero? comp) 668 | (recur (unchecked-inc-int i)) 669 | comp)))))))) 670 | 671 | java.lang.Iterable 672 | (iterator [this] 673 | (let [i (java.util.concurrent.atomic.AtomicInteger. 0)] 674 | (reify java.util.Iterator 675 | (hasNext [_] (< (.get i) cnt)) 676 | (next [_] 677 | (try 678 | (.nth this (unchecked-dec-int (.incrementAndGet i))) 679 | (catch IndexOutOfBoundsException e 680 | (throw (java.util.NoSuchElementException. 681 | "no more elements in RRB vector iterator"))))) 682 | (remove [_] (throw-unsupported))))) 683 | 684 | java.util.Collection 685 | (contains [this o] 686 | (boolean (some #(= % o) this))) 687 | 688 | (containsAll [this c] 689 | (every? #(.contains this %) c)) 690 | 691 | (isEmpty [_] 692 | (zero? cnt)) 693 | 694 | (toArray [this] 695 | (into-array Object this)) 696 | 697 | (toArray [this arr] 698 | (if (>= (count arr) cnt) 699 | (do (dotimes [i cnt] 700 | (aset arr i (.nth this i))) 701 | arr) 702 | (into-array Object this))) 703 | 704 | (size [_] cnt) 705 | 706 | (add [_ o] (throw-unsupported)) 707 | (addAll [_ c] (throw-unsupported)) 708 | (clear [_] (throw-unsupported)) 709 | (^boolean remove [_ o] (throw-unsupported)) 710 | (removeAll [_ c] (throw-unsupported)) 711 | (retainAll [_ c] (throw-unsupported)) 712 | 713 | java.util.RandomAccess 714 | java.util.List 715 | (get [this i] (.nth this i)) 716 | 717 | (indexOf [this o] 718 | (loop [i (int 0)] 719 | (cond 720 | (== i cnt) -1 721 | (= o (.nth this i)) i 722 | :else (recur (unchecked-inc-int i))))) 723 | 724 | (lastIndexOf [this o] 725 | (loop [i (unchecked-dec-int cnt)] 726 | (cond 727 | (neg? i) -1 728 | (= o (.nth this i)) i 729 | :else (recur (unchecked-dec-int i))))) 730 | 731 | (listIterator [this] 732 | (.listIterator this 0)) 733 | 734 | (listIterator [this i] 735 | (let [i (java.util.concurrent.atomic.AtomicInteger. i)] 736 | (reify java.util.ListIterator 737 | (hasNext [_] (< (.get i) cnt)) 738 | (hasPrevious [_] (pos? (int i))) 739 | (next [_] 740 | (try 741 | (.nth this (unchecked-dec-int (.incrementAndGet i))) 742 | (catch IndexOutOfBoundsException e 743 | (throw (java.util.NoSuchElementException. 744 | "no more elements in RRB vector list iterator"))))) 745 | (nextIndex [_] (.get i)) 746 | (previous [_] (.nth this (.decrementAndGet i))) 747 | (previousIndex [_] (unchecked-dec-int (.get i))) 748 | (add [_ e] (throw-unsupported)) 749 | (remove [_] (throw-unsupported)) 750 | (set [_ e] (throw-unsupported))))) 751 | 752 | (subList [this a z] 753 | ;; FIXME 754 | (subvec this a z)) 755 | 756 | (add [_ i o] (throw-unsupported)) 757 | (addAll [_ i c] (throw-unsupported)) 758 | (^Object remove [_ ^int i] (throw-unsupported)) 759 | (set [_ i e] (throw-unsupported))) 760 | 761 | (def ^:private ^devec.core.DoubleEndedVector empty-devec 762 | (DoubleEndedVector. 763 | empty-array empty-node (object-array 0) 0 0 0 5 0 nil -1 -1)) 764 | 765 | (extend-protocol AsDoubleEndedVector 766 | 767 | clojure.lang.PersistentVector 768 | (-as-devec [this] 769 | (DoubleEndedVector. 770 | empty-array (.-root this) (.-tail this) 771 | 0 0 (- (.count this) (alength (.-tail this))) 772 | (.-shift this) (.count this) (meta this) -1 -1)) 773 | 774 | #_#_ 775 | clojure.lang.APersistentVector$SubVector 776 | (-as-devec [this] 777 | )) 778 | 779 | #_ 780 | (deftype ReversedDoubleEndedVector [v] 781 | ) 782 | 783 | (doseq [v [#'AsDoubleEndedVector 784 | #'-as-devec 785 | #'->DoubleEndedVector 786 | #_#'->ReversedDoubleEndedVector]] 787 | (alter-meta! v assoc :private true)) 788 | 789 | (defn conjl 790 | "Conjoins an item on to the given vector from the left, returning a 791 | double-ended vector." 792 | ([] 793 | []) 794 | ([v] 795 | v) 796 | ([v x] 797 | (.consLeft ^devec.core.IDoubleEndedVector (-as-devec v) x)) 798 | ([v x & xs] 799 | (if xs 800 | (recur (conjl v x) (first xs) (next xs)) 801 | (conjl v x)))) 802 | 803 | (defn popl 804 | "Pops an item off a double-ended vector, returning a shorter 805 | double-ended vector." 806 | [v] 807 | (.popLeft ^devec.core.IDoubleEndedVector (-as-devec v))) 808 | 809 | (defn peekl 810 | "(nth v 0), counterpart to devec.core/popl." 811 | [v] 812 | (nth v 0)) 813 | 814 | #_ 815 | (defn rvec 816 | "Reverses the given vector." 817 | [v] 818 | (.rvec ^devec.core.IReversibleVector (-as-devec v))) 819 | 820 | #_ 821 | (defn subvec 822 | "Returns a true (non-view) slice of the input vector bound by the 823 | given indices." 824 | ([v start] 825 | (.subvec ^devec.core.ISliceableVector (-as-devec v) start)) 826 | ([v start end] 827 | (.subvec ^devec.core.ISliceableVector (-as-devec v) start end))) 828 | -------------------------------------------------------------------------------- /src/devec/debug.clj: -------------------------------------------------------------------------------- 1 | (ns devec.debug 2 | (:require [devec.core :as devec]) 3 | (:import (clojure.lang PersistentVector PersistentVector$Node) 4 | (devec.core DoubleEndedVector))) 5 | 6 | (def empty-devec @#'devec.core/empty-devec) 7 | 8 | (def empty-node (.-root [])) 9 | 10 | (defn devec 11 | "Creates a new double-ended vector with the contents of coll." 12 | [coll] 13 | (reduce conj empty-devec coll)) 14 | 15 | (defmacro ^:private gen-devector-method [& params] 16 | (let [arr (with-meta (gensym "arr__") {:tag 'objects})] 17 | `(let [~arr (object-array ~(count params))] 18 | ~@(map-indexed (fn [i param] 19 | `(aset ~arr ~i ~param)) 20 | params) 21 | (devec.core.DoubleEndedVector. 22 | (object-array 0) empty-node ~arr 0 0 0 5 ~(count params) 23 | nil ~(if params -1 1) ~(if params -1 (hash [])))))) 24 | 25 | (defn devector 26 | "Creates a new double-ended vector containing the given items." 27 | ([] 28 | (gen-devector-method)) 29 | ([x1] 30 | (gen-devector-method x1)) 31 | ([x1 x2] 32 | (gen-devector-method x1 x2)) 33 | ([x1 x2 x3] 34 | (gen-devector-method x1 x2 x3)) 35 | ([x1 x2 x3 x4] 36 | (gen-devector-method x1 x2 x3 x4)) 37 | ([x1 x2 x3 x4 & xn] 38 | (loop [v (devector x1 x2 x3 x4) 39 | xn xn] 40 | (if xn 41 | (recur (.cons ^clojure.lang.IPersistentCollection v (first xn)) 42 | (next xn)) 43 | v)))) 44 | 45 | (defn accessors-for [v] 46 | (condp identical? (class v) 47 | PersistentVector [(constantly (object-array 0)) 48 | #(.-root ^PersistentVector %) 49 | #(.-shift ^PersistentVector %) 50 | #(.-tail ^PersistentVector %) 51 | (constantly 0) 52 | (constantly 0) 53 | #(- (count %) (alength ^objects (.-tail %)))] 54 | DoubleEndedVector [#(.-head ^DoubleEndedVector %) 55 | #(.-trie ^DoubleEndedVector %) 56 | #(.-shift ^DoubleEndedVector %) 57 | #(.-tail ^DoubleEndedVector %) 58 | #(.-headoff ^DoubleEndedVector %) 59 | #(.-trieoff ^DoubleEndedVector %) 60 | #(.-tailoff ^DoubleEndedVector %)])) 61 | 62 | (defn dbg-vec [v] 63 | (let [[head root shift tail headoff trieoff tailoff] 64 | ((apply juxt (accessors-for v)) v)] 65 | (letfn [(go [indent shift i node] 66 | (when node 67 | (dotimes [_ indent] 68 | (print " ")) 69 | (printf "%02d:%02d " shift i) 70 | (if (zero? shift) 71 | (print ":" (vec (.-array ^PersistentVector$Node node)))) 72 | (println) 73 | (if-not (zero? shift) 74 | (let [arr (.-array ^PersistentVector$Node node) 75 | fst (reduce-kv (fn [out k v] 76 | (if (some? v) 77 | (reduced k) 78 | out)) 79 | 0 80 | (vec arr))] 81 | (when (pos? fst) 82 | (dotimes [_ (inc indent)] 83 | (print " ")) 84 | (printf "%02d:%02d - %02d:%02d empty\n" 85 | (- shift 5) 0 (- shift 5) (dec fst))) 86 | (dorun 87 | (map-indexed (partial go (inc indent) (- shift 5)) 88 | arr))))))] 89 | (printf "%s (%d elements):\n" (.getName (class v)) (count v)) 90 | (println 91 | "headoff" headoff 92 | "trieoff" trieoff 93 | "tailoff" tailoff) 94 | (println "head:" (pr-str (vec head))) 95 | (go 0 shift 0 root) 96 | (println "tail:" (pr-str (vec tail)))))) 97 | -------------------------------------------------------------------------------- /test/devec/core_bench.clj: -------------------------------------------------------------------------------- 1 | (ns devec.core-bench 2 | (:require [devec.core :as devec] 3 | [criterium.core :as c] 4 | [collection-check :refer [assert-equivalent-vectors]] 5 | [clojure.test :refer :all])) 6 | 7 | (defn subst [m xs] 8 | (map #(if (contains? m %) (m %) %) xs)) 9 | 10 | (defmacro echo [expr] 11 | `(do 12 | (prn '~expr) 13 | ~expr)) 14 | 15 | (defmacro b [expr] 16 | (let [pvexpr (subst '{% pv} expr) 17 | dvexpr (subst '{% dv} expr)] 18 | `(do 19 | (if-not ~(contains? (meta &form) :nv) 20 | (assert-equivalent-vectors ~pvexpr ~dvexpr) 21 | (assert (= ~pvexpr ~dvexpr))) 22 | (echo (c/bench ~pvexpr)) 23 | (echo (c/bench ~dvexpr)) 24 | (println)))) 25 | 26 | (defn run-benchmarks [] 27 | (let [pv (vec (range -1000000 2000000)) 28 | dv (apply devec/conjl (vec (range 2000000)) (range -1 -1000001 -1))] 29 | (assert-equivalent-vectors pv dv) 30 | 31 | (println "basic vector ops") 32 | (println "================") 33 | ^:nv (b (nth % 1234)) 34 | ^:nv (b (nth % 1500000)) 35 | (b (conj % :foo)) 36 | (b (assoc % 1234 :foo)) 37 | (b (assoc % 1500000 :foo)) 38 | (b (pop %)) 39 | (println) 40 | 41 | (println "devec ops") 42 | (println "=========") 43 | (b (devec/conjl % :foo)) 44 | (b (devec/popl %)) 45 | (println) 46 | 47 | true)) 48 | 49 | (deftest test-bench 50 | (is (run-benchmarks))) 51 | -------------------------------------------------------------------------------- /test/devec/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns devec.core-test 2 | (:require [clojure.test :refer :all] 3 | [clojure.test.check :as tc] 4 | [clojure.test.check.generators :as gen] 5 | [clojure.test.check.properties :as prop] 6 | [clojure.test.check.clojure-test :refer [defspec]] 7 | [collection-check :refer [assert-vector-like 8 | assert-equivalent-vectors]] 9 | [devec.core :as devec] 10 | [devec.debug :refer [devector]])) 11 | 12 | (deftest collection-check 13 | (is (assert-vector-like (devector) gen/int)) 14 | (is (every? nil? (.-array ^clojure.lang.PersistentVector$Node 15 | (.-root ^clojure.lang.PersistentVector (vector)))))) 16 | 17 | (deftest examples 18 | (let [pv (vec (range -1000000 2000000)) 19 | dv (apply devec/conjl (vec (range 2000000)) (range -1 -1000001 -1))] 20 | (assert-equivalent-vectors pv dv) 21 | (assert-equivalent-vectors (conj pv :foo) (conj dv :foo)) 22 | (assert-equivalent-vectors 23 | (into pv (range 1000)) 24 | (into dv (range 1000))) 25 | (assert-equivalent-vectors (assoc pv 1234 :foo) (assoc dv 1234 :foo)) 26 | (assert-equivalent-vectors (assoc pv 1500000 :foo) (assoc dv 1500000 :foo)) 27 | (assert-equivalent-vectors (pop pv) (pop dv)) 28 | (assert-equivalent-vectors (devec/conjl pv :foo) (devec/conjl dv :foo)) 29 | (assert-equivalent-vectors (devec/popl pv) (devec/popl dv)) 30 | (assert-equivalent-vectors 31 | (vec (concat (reverse (range 1000)) (range 1000))) 32 | (as-> (vec (range 2000000)) v 33 | (apply devec/conjl v (range 2000000)) 34 | (iterate pop v) 35 | (nth v 1999000) 36 | (iterate devec/popl v) 37 | (nth v 1999000))))) 38 | 39 | (defn prep-pv [l m r] 40 | (into (into l m) r)) 41 | 42 | (defn prep-dv [l m r] 43 | (into (apply devec/conjl m (rseq l)) r)) 44 | 45 | (defspec check-conjl-before-conj 1000 46 | (prop/for-all [l (gen/vector gen/int) 47 | m (gen/vector gen/int) 48 | r (gen/vector gen/int)] 49 | (let [pv (prep-pv l m r) 50 | dv (prep-dv l m r)] 51 | (assert-equivalent-vectors pv dv) 52 | true))) 53 | 54 | (defspec check-pop 1000 55 | (prop/for-all [l (gen/not-empty (gen/vector gen/int)) 56 | m (gen/not-empty (gen/vector gen/int)) 57 | r (gen/not-empty (gen/vector gen/int))] 58 | (let [pv (prep-pv l m r) 59 | dv (prep-dv l m r)] 60 | (dorun 61 | (map assert-equivalent-vectors 62 | (take 4 (iterate pop pv)) 63 | (take 4 (iterate pop dv)))) 64 | true))) 65 | --------------------------------------------------------------------------------