├── .classpath ├── .gitignore ├── .project ├── .settings ├── org.eclipse.jdt.core.prefs ├── org.eclipse.m2e.core.prefs ├── org.eclipse.wst.common.component └── org.eclipse.wst.common.project.facet.core.xml ├── LICENSE ├── README.md ├── pom.xml └── src └── main └── java └── org └── javastack └── kvstore ├── Example.java ├── KVStoreFactory.java ├── Options.java ├── holders ├── BooleanHolder.java ├── ByteArray8Holder.java ├── ByteHolder.java ├── DataHolder.java ├── HolderSerializable.java ├── IntHolder.java ├── LongHolder.java ├── NullHolder.java ├── ShortHolder.java └── StringHolder.java ├── io ├── Constants.java ├── FileBlockStore.java ├── FileLongStore.java ├── FileStreamStore.java └── StringSerializer.java ├── pool ├── BufferStacker.java └── StringPool.java ├── structures ├── bitset │ └── SimpleBitSet.java ├── btree │ ├── BplusTree.java │ ├── BplusTreeFile.java │ ├── BplusTreeMemory.java │ ├── InternalNode.java │ ├── LeafNode.java │ └── Node.java ├── hash │ ├── FixedIntHashMap.java │ ├── IntHashMap.java │ ├── IntLinkedHashMap.java │ └── WeakSet.java ├── set │ └── SortedIntArraySet.java └── stack │ ├── IntStack.java │ └── ObjectStack.java ├── test ├── BenchMarkDiskStore.java └── BenchMarkMemoryStructures.java └── utils ├── CRC32.java ├── CRC32C.java ├── Check64bitsJVM.java ├── GenericFactory.java ├── HexStrings.java └── PrimeFinder.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | target 3 | .metadata 4 | log4j.properties -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | kvstore 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.wst.common.project.facet.core.builder 10 | 11 | 12 | 13 | 14 | org.eclipse.jdt.core.javabuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.wst.validation.validationbuilder 20 | 21 | 22 | 23 | 24 | org.eclipse.m2e.core.maven2Builder 25 | 26 | 27 | 28 | 29 | 30 | org.eclipse.jem.workbench.JavaEMFNature 31 | org.eclipse.wst.common.modulecore.ModuleCoreNature 32 | org.eclipse.jdt.core.javanature 33 | org.eclipse.m2e.core.maven2Nature 34 | org.eclipse.wst.common.project.facet.core.nature 35 | 36 | 37 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | #Sat Mar 02 21:45:32 CET 2013 2 | eclipse.preferences.version=1 3 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=1.6 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 12 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 13 | org.eclipse.jdt.core.compiler.source=1.6 14 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | #Sat Sep 22 20:17:06 CEST 2012 2 | activeProfiles= 3 | eclipse.preferences.version=1 4 | resolveWorkspaceProjects=true 5 | version=1 6 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.common.component: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.common.project.facet.core.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2014 github.com/ggrandes 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KVStore 2 | 3 | KVStore is a Key-Value Store based on B+Tree for Memory & Disk (for on disk, keys and values must be fixed length) for Java. Project is Open source (Apache License, Version 2.0) 4 | 5 | API is similar to [TreeMap](http://docs.oracle.com/javase/6/docs/api/java/util/TreeMap.html). 6 | 7 | ### Current Stable Version is [1.0.2](https://search.maven.org/#search|ga|1|g%3Aorg.javastack%20a%3Akvstore) 8 | 9 | --- 10 | 11 | ## DOC 12 | 13 | #### Usage Example 14 | 15 | ```java 16 | import java.util.Iterator; 17 | import org.javastack.kvstore.KVStoreFactory; 18 | import org.javastack.kvstore.Options; 19 | import org.javastack.kvstore.holders.IntHolder; 20 | import org.javastack.kvstore.structures.btree.BplusTree.TreeEntry; 21 | import org.javastack.kvstore.structures.btree.BplusTreeFile; 22 | 23 | public class Example { 24 | private static final String btreeFile = "/tmp/test"; 25 | 26 | // 27 | public static void main(final String[] args) throws Exception { 28 | final int[] keys = new int[] { 5, 7, -11, 111, 0 }; 29 | // 30 | KVStoreFactory factory = new KVStoreFactory( 31 | IntHolder.class, IntHolder.class); 32 | Options opts = factory.createTreeOptionsDefault() 33 | .set(KVStoreFactory.FILENAME, btreeFile); 34 | BplusTreeFile tree = factory.createTreeFile(opts); 35 | // 36 | // Open & Recovery tree if needed 37 | try { 38 | if (tree.open()) 39 | System.out.println("open tree ok"); 40 | } catch (InvalidDataException e) { 41 | System.out.println("open tree error, recovery needed"); 42 | if (tree.recovery(false) && tree.open()) { 43 | System.out.println("recovery ok, tree opened"); 44 | } else { 45 | throw new Exception("Fatal Error Opening Tree"); 46 | } 47 | } 48 | // clear all previous content if any 49 | tree.clear(); 50 | // ============== PUT 51 | for (int i = 0; i < keys.length; i++) { 52 | final IntHolder key = IntHolder.valueOf(keys[i]); 53 | final IntHolder value = IntHolder.valueOf(i); 54 | tree.put(key, value); 55 | } 56 | tree.sync(); 57 | // ============== GET 58 | System.out.println("tree.get(7)=" + tree.get(IntHolder.valueOf(7))); 59 | // ============== REMOVE 60 | tree.remove(IntHolder.valueOf(7)); 61 | // ============== ITERATOR 62 | for (Iterator> i = tree.iterator(); i 63 | .hasNext();) { 64 | TreeEntry e = i.next(); 65 | System.out.println("Key=" + e.getKey() + " Value=" + e.getValue()); 66 | } 67 | // ============== FIRST / LAST 68 | System.out.println("tree.firstKey()=" + tree.firstKey()); 69 | System.out.println("tree.lastKey()=" + tree.lastKey()); 70 | // 71 | tree.sync(); 72 | tree.close(); 73 | } 74 | } 75 | ``` 76 | 77 | ##### The Result: 78 | 79 | tree.get(7)=1 80 | Key=-11 Value=2 81 | Key=0 Value=4 82 | Key=5 Value=0 83 | Key=111 Value=3 84 | tree.firstKey()=-11 85 | tree.lastKey()=111 86 | 87 | 88 | * More examples in [Test package](https://github.com/ggrandes/kvstore/tree/master/src/main/java/org/javastack/kvstore/test) 89 | 90 | --- 91 | 92 | ## MAVEN 93 | 94 | Add the KVStore dependency to your pom.xml: 95 | 96 | 97 | org.javastack 98 | kvstore 99 | 1.0.2 100 | 101 | 102 | --- 103 | 104 | ## TODOs 105 | 106 | * A lot of Doc 107 | * Describe disk formats 108 | * HashMap on disk 109 | * Separate size of read cache and write cache 110 | 111 | ## DONEs 112 | 113 | * BlockStore (Fixed length chunks) 114 | * Support for mmaped files 115 | * StreamStore (Variable length chunks) 116 | * B+Tree for Index 117 | * Buffer reuse 118 | * Memory mode 119 | * Persistence on disk (BlockStore) 120 | * Cache of nodes 121 | * Reuse free blocks on disk 122 | * Redo log 123 | * Recovery system 124 | * Allow open without populate read cache 125 | * Allow open readOnly mode 126 | * File locking (multi-process) 127 | * HashMaps for natives (memory) 128 | * Holders for data and NIO serialization 129 | * Fixed length 130 | * Boolean 131 | * Byte 132 | * Short 133 | * Integer 134 | * Long 135 | * Null 136 | * Variable length 137 | * Strings 138 | * Create Factory 139 | * Options object for factory 140 | * Use Log4J 141 | * Maven repository 142 | 143 | 144 | ## MISC 145 | 146 | --- 147 | 148 | ## Benchmarks 149 | 150 | ###### Values are not accurate, but orientative. Higher better. All test Running on Laptop { Windows Vista (32bits), Core 2 Duo 1.4Ghz (U9400), 4GB Ram, Magnetic Disk (WDC-WD5000BEVT-22ZAT0) }. 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 |
Test-1Writes/sReads/s
BlockStore (RAF)46k58k
BlockStore (MMAP)67k182k
StreamStore101k55k
174 | 175 | ###### Test-1 (org.javastack.kvstore.test.BenchMarkDiskStore): Registry { count=1e6, datalen=256bytes } BlockStore { blockSize=512 (2reg/block), fileSize=250MB } StreamStore { outBufferSize=0x10000, align=true, fileSize=256MB } 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 |
Test-2Put/sGet/sRemove/s
BplusTreeMemory457k1041k324k
IntHashMap1154k31250k16000k
IntLinkedHashMap1114k31250k11696k
203 | 204 | ###### Test-2 (org.javastack.kvstore.test.BenchMarkMemoryStructures): Registry { count=2e6, datalen=256bytes } BplusTreeMemory { key=Integer, b-order=511 } IntHashMap { initialSize=(2e6 * 2) } 205 | 206 | --- 207 | Inspired in [Book: Open Data Structures in Java](http://opendatastructures.org/ods-java/14_2_B_Trees.html), [Perl DB_File](http://search.cpan.org/~pmqs/DB_File-1.827/DB_File.pm), [JDBM3](https://github.com/jankotek/JDBM3) and [H2-Database](http://www.h2database.com/), this code is Java-minimalistic version. 208 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.javastack 6 | kvstore 7 | 1.0.2 8 | KVStore is a simple Key-Value Store based on B+Tree (disk and memory) for Java 9 | 10 | ${project.groupId}:${project.artifactId} 11 | https://github.com/ggrandes/${project.artifactId} 12 | 13 | 14 | The Apache License, Version 2.0 15 | http://www.apache.org/licenses/LICENSE-2.0.txt 16 | 17 | 18 | 19 | 20 | ggrandes 21 | Guillermo Grandes 22 | guillermo.grandes@gmail.com 23 | 24 | 25 | 26 | scm:git:git@github.com:ggrandes/${project.artifactId}.git 27 | scm:git:git@github.com:ggrandes/${project.artifactId}.git 28 | git@github.com:ggrandes/${project.artifactId}.git 29 | 30 | 31 | 32 | 33 | 34 | ch.qos.reload4j 35 | reload4j 36 | 1.2.19 37 | 38 | 39 | 40 | 41 | 1.7 42 | 43 | 44 | 45 | 46 | 47 | org.apache.maven.plugins 48 | maven-compiler-plugin 49 | 3.8.1 50 | 51 | ${java.version} 52 | ${java.version} 53 | 54 | 55 | 56 | 57 | maven-jar-plugin 58 | 3.2.0 59 | 60 | ${project.artifactId}-${project.version} 61 | true 62 | 63 | 64 | maven 65 | 66 | 67 | 68 | **/pom.properties 69 | **/log4j.properties 70 | **/sandbox/* 71 | 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-source-plugin 78 | 3.2.1 79 | 80 | 81 | 82 | jar 83 | 84 | 85 | 86 | 87 | maven 88 | 89 | 90 | true 91 | 92 | **/sandbox/* 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | org.apache.maven.plugins 101 | maven-javadoc-plugin 102 | 3.2.0 103 | 104 | 105 | attach-javadocs 106 | 107 | jar 108 | 109 | 110 | 111 | 112 | ${java.version} 113 | 114 | 115 | threadSafe 116 | a 117 | ThreadSafe: 118 | 119 | 120 | *.sandbox.* 121 | 122 | none 123 | 124 | 125 | 126 | 127 | org.apache.maven.plugins 128 | maven-gpg-plugin 129 | 1.6 130 | 131 | 132 | sign-artifacts 133 | verify 134 | 135 | sign 136 | 137 | 138 | 139 | 140 | 141 | 142 | org.sonatype.plugins 143 | nexus-staging-maven-plugin 144 | 1.6.8 145 | true 146 | 147 | ossrh 148 | https://oss.sonatype.org/ 149 | true 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | ossrh 159 | https://oss.sonatype.org/content/repositories/snapshots 160 | 161 | 162 | ossrh 163 | https://oss.sonatype.org/service/local/staging/deploy/maven2 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/Example.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore; 16 | 17 | import java.util.Iterator; 18 | 19 | import org.javastack.kvstore.holders.IntHolder; 20 | import org.javastack.kvstore.structures.btree.BplusTreeFile; 21 | import org.javastack.kvstore.structures.btree.BplusTree.InvalidDataException; 22 | import org.javastack.kvstore.structures.btree.BplusTree.TreeEntry; 23 | 24 | /** 25 | * Code example 26 | * 27 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 28 | */ 29 | public class Example { 30 | private static final String btreeFile = "/tmp/test"; 31 | 32 | public static void main(final String[] args) throws Exception { 33 | final int[] keys = new int[] { 34 | 5, 7, -11, 111, 0 35 | }; 36 | // 37 | KVStoreFactory factory = new KVStoreFactory( 38 | IntHolder.class, IntHolder.class); 39 | Options opts = factory.createTreeOptionsDefault().set(KVStoreFactory.FILENAME, btreeFile); 40 | BplusTreeFile tree = factory.createTreeFile(opts); 41 | // 42 | // Open & Recovery tree if needed 43 | try { 44 | if (tree.open()) { 45 | System.out.println("open tree ok"); 46 | } 47 | } catch (InvalidDataException e) { 48 | System.out.println("open tree error, recovery needed"); 49 | if (tree.recovery(false) && tree.open()) { 50 | System.out.println("recovery ok, tree opened"); 51 | } else { 52 | throw new Exception("Fatal Error Opening Tree"); 53 | } 54 | } 55 | // clear all previous content if any 56 | tree.clear(); 57 | // ============== PUT 58 | for (int i = 0; i < keys.length; i++) { 59 | final IntHolder key = IntHolder.valueOf(keys[i]); 60 | final IntHolder value = IntHolder.valueOf(i); 61 | tree.put(key, value); 62 | } 63 | tree.sync(); 64 | // ============== GET 65 | System.out.println("tree.get(7)=" + tree.get(IntHolder.valueOf(7))); 66 | // ============== REMOVE 67 | tree.remove(IntHolder.valueOf(7)); 68 | // ============== ITERATOR 69 | for (Iterator> i = tree.iterator(); i.hasNext();) { 70 | TreeEntry e = i.next(); 71 | System.out.println("Key=" + e.getKey() + " Value=" + e.getValue()); 72 | } 73 | // ============== FIRST / LAST 74 | System.out.println("tree.firstKey()=" + tree.firstKey()); 75 | System.out.println("tree.lastKey()=" + tree.lastKey()); 76 | // 77 | tree.sync(); 78 | tree.close(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/KVStoreFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore; 16 | 17 | import org.apache.log4j.Logger; 18 | import org.javastack.kvstore.holders.DataHolder; 19 | import org.javastack.kvstore.structures.btree.BplusTree; 20 | import org.javastack.kvstore.structures.btree.BplusTreeFile; 21 | import org.javastack.kvstore.structures.btree.BplusTreeMemory; 22 | 23 | public class KVStoreFactory, V extends DataHolder> { 24 | private static final Logger log = Logger.getLogger(KVStoreFactory.class); 25 | 26 | public static final String FILENAME = "opt.kvstore.persistence.string.filename"; // String 27 | public static final String CACHE_SIZE = "opt.kvstore.persistence.int.cachesize"; // int 28 | public static final String AUTO_TUNE = "opt.kvstore.persistence.boolean.autotune"; // Boolean 29 | public static final String USE_REDO = "opt.kvstore.persistence.boolean.useredo"; // Boolean 30 | public static final String USE_REDO_THREAD = "opt.kvstore.persistence.boolean.useredothread"; // Boolean 31 | public static final String DISABLE_POPULATE_CACHE = "opt.kvstore.persistence.boolean.disablepopulatecache"; // Boolean 32 | public static final String DISABLE_AUTOSYNC_STORE = "opt.kvstore.persistence.boolean.disableautosyncstore"; // Boolean 33 | public static final String ENABLE_MMAP = "opt.kvstore.persistence.boolean.enablemmap"; // Boolean 34 | public static final String ENABLE_MMAP_IF_SUPPORTED = "opt.kvstore.persistence.boolean.enablemmapifsupported"; // Boolean 35 | public static final String READONLY = "opt.kvstore.persistence.boolean.readonly"; // Boolean 36 | public static final String ENABLE_LOCKING = "opt.kvstore.persistence.boolean.enablelocking"; // Boolean 37 | 38 | public static final String B_SIZE = "opt.kvstore.btree.int.bsize"; // int 39 | 40 | final Class typeK; 41 | final Class typeV; 42 | 43 | public KVStoreFactory(final Class typeK, final Class typeV) { 44 | this.typeK = typeK; 45 | this.typeV = typeV; 46 | } 47 | 48 | public BplusTree createTree(final Options opts) throws InstantiationException, 49 | IllegalAccessException { 50 | final String fileName = opts.getString(FILENAME); 51 | if (fileName == null) { 52 | return createTreeMemory(opts); 53 | } else { 54 | return createTreeFile(opts); 55 | } 56 | } 57 | 58 | public BplusTreeMemory createTreeMemory(final Options opts) throws InstantiationException, 59 | IllegalAccessException { 60 | final int b_size = opts.getInt(B_SIZE, 512); 61 | return new BplusTreeMemory(b_size, typeK, typeV); 62 | } 63 | 64 | public BplusTreeFile createTreeFile(final Options opts) throws InstantiationException, 65 | IllegalAccessException { 66 | final String fileName = opts.getString(FILENAME); 67 | if (fileName == null) { 68 | log.error("Invalid filename for createTreeFile"); 69 | return null; 70 | } 71 | // 72 | final int b_size = opts.getInt(B_SIZE, 512); 73 | final boolean autoTune = opts.getBoolean(AUTO_TUNE, true); 74 | final BplusTreeFile tree = new BplusTreeFile(autoTune, b_size, typeK, typeV, fileName); 75 | // 76 | tree.setMaxCacheSizeInBytes(opts.getInt(CACHE_SIZE, 8 * 1024 * 1024)); 77 | tree.setUseRedo(opts.getBoolean(USE_REDO, true)); 78 | tree.setUseRedoThread(opts.getBoolean(USE_REDO_THREAD, false)); 79 | tree.setDisablePopulateCache(opts.getBoolean(DISABLE_POPULATE_CACHE, false)); 80 | tree.setDisableAutoSyncStore(opts.getBoolean(DISABLE_AUTOSYNC_STORE, false)); 81 | if (opts.getBoolean(ENABLE_MMAP, false)) 82 | tree.enableMmap(); 83 | if (opts.getBoolean(ENABLE_MMAP_IF_SUPPORTED, false)) 84 | tree.enableMmapIfSupported(); 85 | if (opts.getBoolean(READONLY, false)) 86 | tree.setReadOnly(); 87 | if (opts.getBoolean(ENABLE_LOCKING, false)) 88 | tree.enableLocking(); 89 | // 90 | return tree; 91 | } 92 | 93 | // 94 | public Options createTreeOptionsDefault() { 95 | final Options opts = new Options(); 96 | // @formatter:off 97 | opts 98 | .set(AUTO_TUNE, true) 99 | .set(B_SIZE, 512) 100 | .set(USE_REDO, true) 101 | .set(USE_REDO_THREAD, false) 102 | .set(ENABLE_LOCKING, true) 103 | .set(CACHE_SIZE, 8 * 1024 * 1024); 104 | // @formatter:on 105 | return opts; 106 | } 107 | 108 | public Options createTreeOptionsSafe() { 109 | final Options opts = new Options(); 110 | // @formatter:off 111 | opts 112 | .set(AUTO_TUNE, true) 113 | .set(B_SIZE, 512) 114 | .set(USE_REDO, true) 115 | .set(USE_REDO_THREAD, false) 116 | .set(ENABLE_LOCKING, true) 117 | .set(CACHE_SIZE, 1 * 1024 * 1024); 118 | // @formatter:on 119 | return opts; 120 | } 121 | 122 | public Options createTreeOptionsPerformance() { 123 | final Options opts = new Options(); 124 | // @formatter:off 125 | opts 126 | .set(AUTO_TUNE, true) 127 | .set(B_SIZE, 1024) 128 | .set(USE_REDO, false) 129 | .set(USE_REDO_THREAD, false) 130 | .set(DISABLE_AUTOSYNC_STORE, true) 131 | .set(CACHE_SIZE, 128 * 1024 * 1024); 132 | // @formatter:on 133 | return opts; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/Options.java: -------------------------------------------------------------------------------- 1 | package org.javastack.kvstore; 2 | 3 | import java.util.HashMap; 4 | 5 | import org.apache.log4j.Logger; 6 | 7 | public class Options { 8 | private static final Logger log = Logger.getLogger(Options.class); 9 | private final HashMap opts = new HashMap(); 10 | 11 | // 12 | // String 13 | public Options set(final String key, final String value) { 14 | opts.put(key, value); 15 | return this; 16 | } 17 | 18 | public String getString(final String key) { 19 | return getString(key, null); 20 | } 21 | 22 | public String getString(final String key, final String defaultValue) { 23 | final String value = opts.get(key); 24 | if ((value != null) && !value.isEmpty()) { 25 | return value; 26 | } 27 | return defaultValue; 28 | } 29 | 30 | // 31 | // Integer 32 | public Options set(final String key, final int value) { 33 | opts.put(key, String.valueOf(value)); 34 | return this; 35 | } 36 | 37 | public int getInt(final String key) { 38 | return getInt(key, Integer.MIN_VALUE); 39 | } 40 | 41 | public int getInt(final String key, final int defaultValue) { 42 | try { 43 | final String value = opts.get(key); 44 | if (value != null) { 45 | return Integer.parseInt(value); 46 | } 47 | } catch (NumberFormatException e) { 48 | log.error("NumberFormatException in getInt(" + key + ")", e); 49 | } 50 | return defaultValue; 51 | } 52 | 53 | // 54 | // Boolean 55 | public Options set(final String key, final boolean value) { 56 | opts.put(key, String.valueOf(value)); 57 | return this; 58 | } 59 | 60 | public boolean getBoolean(final String key) { 61 | return getBoolean(key, false); 62 | } 63 | 64 | public boolean getBoolean(final String key, final boolean defaultValue) { 65 | final String value = opts.get(key); 66 | if (value != null) { 67 | return Boolean.parseBoolean(value); 68 | } 69 | return defaultValue; 70 | } 71 | 72 | // 73 | // Global 74 | public boolean contains(final String key) { 75 | return opts.containsKey(key); 76 | } 77 | 78 | public Options remove(final String key) { 79 | opts.remove(key); 80 | return this; 81 | } 82 | 83 | public String toString() { 84 | return opts.toString(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/holders/BooleanHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.holders; 16 | 17 | import java.nio.ByteBuffer; 18 | 19 | /** 20 | * Holder for Boolean values 21 | * 22 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 23 | */ 24 | public final class BooleanHolder extends DataHolder { 25 | public static final BooleanHolder TRUE = new BooleanHolder(true); 26 | public static final BooleanHolder FALSE = new BooleanHolder(false); 27 | 28 | private final boolean value; 29 | 30 | public static BooleanHolder valueOf(final boolean value) { 31 | return (value ? TRUE : FALSE); 32 | } 33 | 34 | /** 35 | * Constructor necesario para la deserializacion 36 | */ 37 | public BooleanHolder() { 38 | this(false); 39 | }; 40 | 41 | private BooleanHolder(final boolean value) { 42 | this.value = value; 43 | } 44 | 45 | public boolean booleanValue() { 46 | return value; 47 | } 48 | 49 | // ========= Basic Object methods ========= 50 | 51 | @Override 52 | public String toString() { 53 | return (value ? "true" : "false"); 54 | } 55 | 56 | @Override 57 | public int hashCode() { 58 | return (value ? 1231 : 1237); 59 | } 60 | 61 | // ========= Comparable ========= 62 | 63 | @Override 64 | public boolean equals(final Object obj) { 65 | if (obj instanceof BooleanHolder) { 66 | return value == ((BooleanHolder) obj).value; 67 | } 68 | return false; 69 | } 70 | 71 | @Override 72 | public int compareTo(final BooleanHolder another) { 73 | return (another.value == value ? 0 : (value ? 1 : -1)); 74 | } 75 | 76 | // ========= Serialization ========= 77 | 78 | @Override 79 | public final int byteLength() { 80 | return 1; 81 | } 82 | 83 | @Override 84 | public void serialize(final ByteBuffer buf) { 85 | buf.put((byte) (value ? 1 : 0)); 86 | } 87 | 88 | @Override 89 | public BooleanHolder deserialize(final ByteBuffer buf) { 90 | return valueOf((buf.get() == 0) ? false : true); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/holders/ByteArray8Holder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.holders; 16 | 17 | import java.nio.ByteBuffer; 18 | import java.util.Arrays; 19 | 20 | import org.javastack.kvstore.structures.hash.FixedIntHashMap; 21 | 22 | /** 23 | * Example Holder for byte[8] values 24 | * 25 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 26 | */ 27 | public final class ByteArray8Holder extends DataHolder { 28 | private static final FixedIntHashMap cache = new FixedIntHashMap( 29 | 4096, ByteArray8Holder.class); 30 | 31 | private final byte[] value; 32 | 33 | public static ByteArray8Holder valueOf(final byte[] value) { 34 | final int hash = Arrays.hashCode(value); 35 | final ByteArray8Holder cachedKey = cache.get(hash); 36 | if (cachedKey != null) { 37 | if (cachedKey.value == value) { 38 | return cachedKey; 39 | } 40 | } 41 | final ByteArray8Holder newKey = new ByteArray8Holder(value); 42 | cache.put(hash, newKey); 43 | return newKey; 44 | } 45 | 46 | /** 47 | * Constructor necesario para la deserializacion 48 | */ 49 | public ByteArray8Holder() { 50 | this(null); 51 | }; 52 | 53 | private ByteArray8Holder(final byte[] value) { 54 | this.value = value; 55 | } 56 | 57 | public byte[] getValue() { 58 | return value; 59 | } 60 | 61 | // ========= Basic Object methods ========= 62 | 63 | @Override 64 | public String toString() { 65 | return getHex(value); // Arrays.toString(value); 66 | } 67 | 68 | @Override 69 | public int hashCode() { 70 | return Arrays.hashCode(value); 71 | } 72 | 73 | private static final String HEXES = "0123456789ABCDEF"; 74 | 75 | public static String getHex(final byte[] raw) { 76 | if (raw == null) { 77 | return null; 78 | } 79 | final StringBuilder hex = new StringBuilder(2 * raw.length); 80 | for (final byte b : raw) { 81 | hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F))); 82 | } 83 | return hex.toString(); 84 | } 85 | 86 | // ========= Comparable ========= 87 | 88 | @Override 89 | public boolean equals(final Object obj) { 90 | if (obj instanceof ByteArray8Holder) { 91 | return Arrays.equals(value, ((ByteArray8Holder) obj).getValue()); 92 | } 93 | return false; 94 | } 95 | 96 | @Override 97 | public int compareTo(final ByteArray8Holder anotherLong) { 98 | final byte[] thisVal = this.value; 99 | final byte[] anotherVal = anotherLong.value; 100 | return compare(thisVal, anotherVal); 101 | } 102 | 103 | private final static int compare(final byte[] left, final byte[] right) { 104 | for (int i = 0, j = 0; i < left.length && j < right.length; i++, j++) { 105 | int a = (left[i] & 0xff); 106 | int b = (right[j] & 0xff); 107 | if (a != b) { 108 | return a - b; 109 | } 110 | } 111 | return left.length - right.length; 112 | } 113 | 114 | // ========= Serialization ========= 115 | 116 | @Override 117 | public final int byteLength() { 118 | return 8; 119 | } 120 | 121 | @Override 122 | public void serialize(final ByteBuffer buf) { 123 | buf.put(value, 0, Math.min(byteLength(), value.length)); 124 | } 125 | 126 | @Override 127 | public ByteArray8Holder deserialize(final ByteBuffer buf) { 128 | final byte[] newvalue = new byte[byteLength()]; 129 | buf.get(newvalue, 0, newvalue.length); 130 | return valueOf(newvalue); 131 | } 132 | 133 | // ========= 134 | 135 | /** 136 | * Simple Test 137 | */ 138 | public static void main(final String[] args) { 139 | ByteArray8Holder b1 = new ByteArray8Holder(new byte[] { 140 | 0, 0, 0, 0, 0, 0, 0, 1 141 | }); 142 | ByteArray8Holder b2 = new ByteArray8Holder(new byte[] { 143 | 0, 0, 0, 0, 0, 0, 0, 2 144 | }); 145 | ByteArray8Holder b2b = new ByteArray8Holder(new byte[] { 146 | 0, 0, 0, 0, 0, 0, 0, 2 147 | }); 148 | System.out.println("compareTo=" + Integer.valueOf(0).compareTo(Integer.valueOf(1))); 149 | System.out.println("b1.compareTo(b2)=" + b1.compareTo(b2)); 150 | System.out.println("b1.equals(b2)=" + b1.equals(b2)); 151 | System.out.println("b2.equals(b2b)=" + b2.equals(b2b)); 152 | System.out.println("b1.hashCode()=" + b1.hashCode()); 153 | System.out.println("b2.hashCode()=" + b2.hashCode()); 154 | System.out.println("b2b.hashCode()=" + b2b.hashCode()); 155 | System.out.println("b2b.toString()=" + b2b.toString()); 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/holders/ByteHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.holders; 16 | 17 | import java.nio.ByteBuffer; 18 | 19 | /** 20 | * Holder for Byte values 21 | * 22 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 23 | */ 24 | public final class ByteHolder extends DataHolder { 25 | private static final ByteHolder[] cache = new ByteHolder[256]; 26 | 27 | static { 28 | for (int i = 0; i < cache.length; i++) { 29 | cache[i] = new ByteHolder((byte) (i - 128)); 30 | } 31 | } 32 | 33 | private final byte value; 34 | 35 | public static ByteHolder valueOf(final byte value) { 36 | return cache[(int) value + 128]; 37 | } 38 | 39 | /** 40 | * Constructor necesario para la deserializacion 41 | */ 42 | public ByteHolder() { 43 | this((byte) 0); 44 | }; 45 | 46 | private ByteHolder(final byte value) { 47 | this.value = value; 48 | } 49 | 50 | public byte byteValue() { 51 | return value; 52 | } 53 | 54 | // ========= Bit Operation methods ========= 55 | 56 | /** 57 | * MSB-left (76543210), 0x7F=127=01111111 58 | * 59 | * @param bitIndex to check 60 | * @return true=1, false=0 61 | */ 62 | public boolean get(final int bitIndex) { 63 | if (bitIndex > 7 || bitIndex < 0) 64 | throw new IndexOutOfBoundsException(); 65 | return (((((int) value) >> bitIndex) & 1) == 1); 66 | } 67 | 68 | /** 69 | * MSB-left (76543210), 0x7F=127=01111111 70 | * 71 | * @param bitIndex to set 72 | * @return new ByteHolder with bits updated 73 | */ 74 | public ByteHolder set(final int bitIndex) { 75 | if (bitIndex > 7 || bitIndex < 0) 76 | throw new IndexOutOfBoundsException(); 77 | return valueOf((byte) (value | (1 << bitIndex))); 78 | } 79 | 80 | /** 81 | * MSB-left (76543210), 0x7F=127=01111111 82 | * 83 | * @param bitIndex to clear 84 | * @return new ByteHolder with bits updated 85 | */ 86 | public ByteHolder clear(final int bitIndex) { 87 | if (bitIndex > 7 || bitIndex < 0) 88 | throw new IndexOutOfBoundsException(); 89 | return valueOf((byte) (value & ~(1 << bitIndex))); 90 | } 91 | 92 | // ========= Basic Object methods ========= 93 | 94 | @Override 95 | public String toString() { 96 | return String.valueOf(value); 97 | } 98 | 99 | @Override 100 | public int hashCode() { 101 | return (int) value; 102 | } 103 | 104 | // ========= Comparable ========= 105 | 106 | @Override 107 | public boolean equals(final Object obj) { 108 | if (obj instanceof ByteHolder) { 109 | return value == ((ByteHolder) obj).byteValue(); 110 | } 111 | return false; 112 | } 113 | 114 | @Override 115 | public int compareTo(final ByteHolder another) { 116 | final byte thisVal = this.value; 117 | final byte anotherVal = another.value; 118 | return ((thisVal < anotherVal) ? -1 : ((thisVal == anotherVal) ? 0 : 1)); 119 | } 120 | 121 | // ========= Serialization ========= 122 | 123 | @Override 124 | public final int byteLength() { 125 | return 1; 126 | } 127 | 128 | @Override 129 | public void serialize(final ByteBuffer buf) { 130 | buf.put(value); 131 | } 132 | 133 | @Override 134 | public ByteHolder deserialize(final ByteBuffer buf) { 135 | return valueOf(buf.get()); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/holders/DataHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.holders; 16 | 17 | import java.nio.ByteBuffer; 18 | 19 | /** 20 | * Basic Holder for data (int, long,...) 21 | * 22 | * @param 23 | * 24 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 25 | */ 26 | public abstract class DataHolder implements Comparable, HolderSerializable { 27 | 28 | // ========= Basic Object methods ========= 29 | 30 | @Override 31 | abstract public String toString(); 32 | 33 | @Override 34 | abstract public int hashCode(); 35 | 36 | // ========= Comparable ========= 37 | 38 | @Override 39 | abstract public boolean equals(final Object obj); 40 | 41 | @Override 42 | abstract public int compareTo(final T another); 43 | 44 | // ========= Serialization ========= 45 | 46 | @Override 47 | abstract public int byteLength(); 48 | 49 | @Override 50 | abstract public void serialize(final ByteBuffer buf); 51 | 52 | @Override 53 | abstract public T deserialize(final ByteBuffer buf); 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/holders/HolderSerializable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.holders; 16 | 17 | import java.nio.ByteBuffer; 18 | 19 | /** 20 | * Interface for Serializable Holders 21 | * 22 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 23 | */ 24 | public interface HolderSerializable { 25 | 26 | // ========= Serialization ========= 27 | 28 | public int byteLength(); 29 | 30 | public void serialize(final ByteBuffer buf); 31 | 32 | public T deserialize(final ByteBuffer buf); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/holders/IntHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.holders; 16 | 17 | import java.nio.ByteBuffer; 18 | 19 | import org.javastack.kvstore.structures.hash.FixedIntHashMap; 20 | 21 | /** 22 | * Holder for Int values 23 | * 24 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 25 | */ 26 | public final class IntHolder extends DataHolder { 27 | private static final FixedIntHashMap cache = new FixedIntHashMap(4096, 28 | IntHolder.class); 29 | 30 | private final int value; 31 | 32 | public static IntHolder valueOf(final long value) { 33 | return valueOf((int) value); 34 | } 35 | 36 | public static IntHolder valueOf(final int value) { 37 | final IntHolder cachedKey = cache.get(value); 38 | if (cachedKey != null) { 39 | if (cachedKey.value == value) { 40 | return cachedKey; 41 | } 42 | } 43 | final IntHolder newKey = new IntHolder(value); 44 | cache.put(value, newKey); 45 | return newKey; 46 | } 47 | 48 | /** 49 | * Constructor necesario para la deserializacion 50 | */ 51 | public IntHolder() { 52 | this(0); 53 | }; 54 | 55 | private IntHolder(final int value) { 56 | this.value = value; 57 | } 58 | 59 | public int intValue() { 60 | return value; 61 | } 62 | 63 | // ========= Basic Object methods ========= 64 | 65 | @Override 66 | public String toString() { 67 | return String.valueOf(value); 68 | } 69 | 70 | @Override 71 | public int hashCode() { 72 | return value; 73 | } 74 | 75 | // ========= Comparable ========= 76 | 77 | @Override 78 | public boolean equals(final Object obj) { 79 | if (obj instanceof IntHolder) { 80 | return value == ((IntHolder) obj).intValue(); 81 | } 82 | return false; 83 | } 84 | 85 | @Override 86 | public int compareTo(final IntHolder anotherInt) { 87 | final int thisVal = this.value; 88 | final int anotherVal = anotherInt.value; 89 | return ((thisVal < anotherVal) ? -1 : ((thisVal == anotherVal) ? 0 : 1)); 90 | } 91 | 92 | // ========= Serialization ========= 93 | 94 | @Override 95 | public final int byteLength() { 96 | return 4; 97 | } 98 | 99 | @Override 100 | public void serialize(final ByteBuffer buf) { 101 | buf.putInt(value); 102 | } 103 | 104 | @Override 105 | public IntHolder deserialize(final ByteBuffer buf) { 106 | return valueOf(buf.getInt()); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/holders/LongHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.holders; 16 | 17 | import java.nio.ByteBuffer; 18 | 19 | import org.javastack.kvstore.structures.hash.FixedIntHashMap; 20 | 21 | /** 22 | * Holder for Long values 23 | * 24 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 25 | */ 26 | public final class LongHolder extends DataHolder { 27 | private static final FixedIntHashMap cache = new FixedIntHashMap(8192, 28 | LongHolder.class); 29 | 30 | private final long value; 31 | 32 | public static LongHolder valueOf(final long value) { 33 | final int hash = (int) (value ^ (value >>> 32)); 34 | final LongHolder cachedValue = cache.get(hash); 35 | if (cachedValue != null) { 36 | if (cachedValue.value == value) { 37 | return cachedValue; 38 | } 39 | } 40 | final LongHolder newValue = new LongHolder(value); 41 | cache.put(hash, newValue); 42 | return newValue; 43 | } 44 | 45 | /** 46 | * Constructor necesario para la deserializacion 47 | */ 48 | public LongHolder() { 49 | this(0); 50 | }; 51 | 52 | private LongHolder(final long value) { 53 | this.value = value; 54 | } 55 | 56 | public long longValue() { 57 | return (long) value; 58 | } 59 | 60 | // ========= Basic Object methods ========= 61 | 62 | @Override 63 | public String toString() { 64 | return String.valueOf(value); 65 | } 66 | 67 | @Override 68 | public int hashCode() { 69 | return (int) (value ^ (value >>> 32)); 70 | } 71 | 72 | // ========= Comparable ========= 73 | 74 | @Override 75 | public boolean equals(final Object obj) { 76 | if (obj instanceof LongHolder) { 77 | return value == ((LongHolder) obj).longValue(); 78 | } 79 | return false; 80 | } 81 | 82 | @Override 83 | public int compareTo(final LongHolder anotherLong) { 84 | final long thisVal = this.value; 85 | final long anotherVal = anotherLong.value; 86 | return ((thisVal < anotherVal) ? -1 : ((thisVal == anotherVal) ? 0 : 1)); 87 | } 88 | 89 | // ========= Serialization ========= 90 | 91 | @Override 92 | public final int byteLength() { 93 | return 8; 94 | } 95 | 96 | @Override 97 | public void serialize(final ByteBuffer buf) { 98 | buf.putLong(value); 99 | } 100 | 101 | @Override 102 | public LongHolder deserialize(final ByteBuffer buf) { 103 | return valueOf(buf.getLong()); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/holders/NullHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.holders; 16 | 17 | import java.nio.ByteBuffer; 18 | 19 | /** 20 | * Holder for Null value 21 | * 22 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 23 | */ 24 | public final class NullHolder extends DataHolder { 25 | public static final NullHolder NULL = new NullHolder(); 26 | 27 | public static NullHolder valueOf() { 28 | return NULL; 29 | } 30 | 31 | /** 32 | * Constructor necesario para la deserializacion 33 | */ 34 | public NullHolder() { 35 | }; 36 | 37 | // ========= Basic Object methods ========= 38 | 39 | @Override 40 | public String toString() { 41 | return "null"; 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | return 0; 47 | } 48 | 49 | // ========= Comparable ========= 50 | 51 | @Override 52 | public boolean equals(final Object obj) { 53 | if (obj instanceof NullHolder) { 54 | return true; 55 | } 56 | return false; 57 | } 58 | 59 | @Override 60 | public int compareTo(final NullHolder another) { 61 | return 0; 62 | } 63 | 64 | // ========= Serialization ========= 65 | 66 | @Override 67 | public final int byteLength() { 68 | return 0; 69 | } 70 | 71 | @Override 72 | public void serialize(final ByteBuffer buf) { 73 | } 74 | 75 | @Override 76 | public NullHolder deserialize(final ByteBuffer buf) { 77 | return valueOf(); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/holders/ShortHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.holders; 16 | 17 | import java.nio.ByteBuffer; 18 | 19 | import org.javastack.kvstore.structures.hash.FixedIntHashMap; 20 | 21 | /** 22 | * Holder for Short values 23 | * 24 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 25 | */ 26 | public final class ShortHolder extends DataHolder { 27 | private static final FixedIntHashMap cache = new FixedIntHashMap(4096, 28 | ShortHolder.class); 29 | 30 | private final short value; 31 | 32 | public static ShortHolder valueOf(final short value) { 33 | final ShortHolder cachedKey = cache.get(value); 34 | if (cachedKey != null) { 35 | if (cachedKey.value == value) { 36 | return cachedKey; 37 | } 38 | } 39 | final ShortHolder newKey = new ShortHolder(value); 40 | cache.put(value, newKey); 41 | return newKey; 42 | } 43 | 44 | /** 45 | * Constructor necesario para la deserializacion 46 | */ 47 | public ShortHolder() { 48 | this((short) 0); 49 | }; 50 | 51 | private ShortHolder(final short value) { 52 | this.value = value; 53 | } 54 | 55 | public short shortValue() { 56 | return value; 57 | } 58 | 59 | // ========= Basic Object methods ========= 60 | 61 | @Override 62 | public String toString() { 63 | return String.valueOf(value); 64 | } 65 | 66 | @Override 67 | public int hashCode() { 68 | return (int) value; 69 | } 70 | 71 | // ========= Comparable ========= 72 | 73 | @Override 74 | public boolean equals(final Object obj) { 75 | if (obj instanceof ShortHolder) { 76 | return value == ((ShortHolder) obj).shortValue(); 77 | } 78 | return false; 79 | } 80 | 81 | @Override 82 | public int compareTo(final ShortHolder another) { 83 | final short thisVal = this.value; 84 | final short anotherVal = another.value; 85 | return ((thisVal < anotherVal) ? -1 : ((thisVal == anotherVal) ? 0 : 1)); 86 | } 87 | 88 | // ========= Serialization ========= 89 | 90 | @Override 91 | public final int byteLength() { 92 | return 2; 93 | } 94 | 95 | @Override 96 | public void serialize(final ByteBuffer buf) { 97 | buf.putShort(value); 98 | } 99 | 100 | @Override 101 | public ShortHolder deserialize(final ByteBuffer buf) { 102 | return valueOf(buf.getShort()); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/holders/StringHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.holders; 16 | 17 | import java.nio.ByteBuffer; 18 | 19 | import org.javastack.kvstore.io.StringSerializer; 20 | import org.javastack.kvstore.pool.StringPool; 21 | 22 | /** 23 | * Holder for String values
24 | * WARNING: Dont use this with BplusTreeFile (file need fixed/constant length objects, like Long or 25 | * Int) 26 | * 27 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 28 | */ 29 | public final class StringHolder extends DataHolder { 30 | private final String value; 31 | 32 | public static StringHolder valueOf(final String value) { 33 | return new StringHolder(StringPool.getCanonicalVersion(value)); 34 | } 35 | 36 | /** 37 | * Constructor necesario para la deserializacion 38 | */ 39 | public StringHolder() { 40 | this(""); 41 | }; 42 | 43 | private StringHolder(final String value) { 44 | this.value = value; 45 | } 46 | 47 | public String getValue() { 48 | return value; 49 | } 50 | 51 | // ========= Basic Object methods ========= 52 | 53 | @Override 54 | public String toString() { 55 | return value; 56 | } 57 | 58 | @Override 59 | public int hashCode() { 60 | return value.hashCode(); 61 | } 62 | 63 | // ========= Comparable ========= 64 | 65 | @Override 66 | public boolean equals(final Object obj) { 67 | if (obj instanceof StringHolder) { 68 | return value.equals(((StringHolder) obj).value); 69 | } 70 | return false; 71 | } 72 | 73 | @Override 74 | public int compareTo(final StringHolder anotherString) { 75 | final String thisVal = this.value; 76 | final String anotherVal = anotherString.value; 77 | return thisVal.compareTo(anotherVal); 78 | } 79 | 80 | // ========= Serialization ========= 81 | 82 | @Override 83 | public final int byteLength() { 84 | throw new UnsupportedOperationException("StringHolder is variable length Object"); 85 | } 86 | 87 | @Override 88 | public void serialize(final ByteBuffer buf) { 89 | StringSerializer.fromStringToBuffer(buf, value); 90 | } 91 | 92 | @Override 93 | public StringHolder deserialize(final ByteBuffer buf) { 94 | return valueOf(StringSerializer.fromBufferToString(buf)); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/io/Constants.java: -------------------------------------------------------------------------------- 1 | package org.javastack.kvstore.io; 2 | 3 | public class Constants { 4 | /** 5 | * System property name for global disable of FileChannel Locking 6 | */ 7 | public static final String PROP_IO_LOCKING = Constants.class.getPackage().getName() + ".DisableLocking"; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/io/FileLongStore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.io; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.io.RandomAccessFile; 20 | import java.nio.ByteBuffer; 21 | import java.nio.channels.FileChannel; 22 | 23 | import org.apache.log4j.Logger; 24 | 25 | /** 26 | * File for store one Long value with history for crash recovery 27 | * This class is Thread-Safe 28 | * 29 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 30 | */ 31 | public class FileLongStore { 32 | private static final Logger log = Logger.getLogger(FileLongStore.class); 33 | /** 34 | * File associated to this store 35 | */ 36 | private File file = null; 37 | /** 38 | * RamdomAccessFile for this store 39 | */ 40 | private RandomAccessFile raf = null; 41 | /** 42 | * FileChannel for this store 43 | */ 44 | private FileChannel fc = null; 45 | /** 46 | * ByteBuffer (internal used) 47 | */ 48 | private final ByteBuffer buf = ByteBuffer.allocate(8); 49 | /** 50 | * In Valid State? 51 | */ 52 | private boolean validState = false; 53 | /** 54 | * Current Long 55 | */ 56 | private long value = 0; 57 | 58 | /** 59 | * Instantiate FileLongPointerStore 60 | * 61 | * @param file name of file to open 62 | */ 63 | public FileLongStore(final String file) { 64 | this(new File(file)); 65 | } 66 | 67 | /** 68 | * Instantiate FileLongPointerStore 69 | * 70 | * @param file file to open 71 | */ 72 | public FileLongStore(final File file) { 73 | this.file = file; 74 | } 75 | 76 | // ========= Open / Close ========= 77 | 78 | /** 79 | * Open file for read/write 80 | * 81 | * @return true if valid state 82 | */ 83 | public boolean open() { 84 | return open(false); 85 | } 86 | 87 | /** 88 | * Open file 89 | * 90 | * @param readOnly open in readOnly mode? 91 | * @return true if valid state 92 | */ 93 | public synchronized boolean open(final boolean readOnly) { 94 | if (isOpen()) { 95 | close(); 96 | } 97 | try { 98 | raf = new RandomAccessFile(file, readOnly ? "r" : "rw"); 99 | fc = raf.getChannel(); 100 | } catch (Exception e) { 101 | log.error("Exception in open()", e); 102 | try { 103 | close(); 104 | } catch (Exception ign) { 105 | } 106 | } 107 | validState = isOpen(); 108 | return validState; 109 | } 110 | 111 | /** 112 | * Close file 113 | */ 114 | public synchronized void close() { 115 | if (validState) { 116 | sync(); 117 | } 118 | try { 119 | fc.close(); 120 | } catch (Exception ign) { 121 | } 122 | try { 123 | raf.close(); 124 | } catch (Exception ign) { 125 | } 126 | raf = null; 127 | fc = null; 128 | // 129 | validState = false; 130 | } 131 | 132 | // ========= Info ========= 133 | 134 | /** 135 | * @return true if file is open 136 | */ 137 | public synchronized boolean isOpen() { 138 | try { 139 | if (fc != null) { 140 | return fc.isOpen(); 141 | } 142 | } catch (Exception ign) { 143 | } 144 | return false; 145 | } 146 | 147 | /** 148 | * Read value from file 149 | * 150 | * @throws IOException 151 | */ 152 | public synchronized boolean canRead() throws IOException { 153 | if (!validState) { 154 | throw new InvalidStateException(); 155 | } 156 | final long offset = ((fc.size() & ~7) - 8); 157 | return (offset >= 0); 158 | } 159 | 160 | /** 161 | * @return size of file in bytes 162 | * @see #getBlockSize() 163 | */ 164 | public synchronized long size() { 165 | try { 166 | return file.length(); 167 | } catch (Exception e) { 168 | log.error("Exception in size()", e); 169 | } 170 | return -1; 171 | } 172 | 173 | // ========= Destroy ========= 174 | 175 | /** 176 | * Truncate file 177 | */ 178 | public synchronized void clear() { 179 | if (!validState) { 180 | throw new InvalidStateException(); 181 | } 182 | try { 183 | buf.clear(); 184 | fc.position(0).truncate(0).force(true); 185 | close(); 186 | open(); 187 | } catch (Exception e) { 188 | log.error("Exception in clear()", e); 189 | } 190 | } 191 | 192 | /** 193 | * Delete file 194 | */ 195 | public synchronized void delete() { 196 | buf.clear(); 197 | close(); 198 | try { 199 | file.delete(); 200 | } catch (Exception ign) { 201 | } 202 | } 203 | 204 | // ========= Operations ========= 205 | 206 | /** 207 | * Set value 208 | */ 209 | public synchronized void set(final long value) { 210 | this.value = value; 211 | } 212 | 213 | /** 214 | * Get value 215 | */ 216 | public synchronized long get() { 217 | return value; 218 | } 219 | 220 | /** 221 | * Read value from file 222 | * 223 | * @throws IOException 224 | */ 225 | public synchronized void read() throws IOException { 226 | if (!validState) { 227 | throw new InvalidStateException(); 228 | } 229 | final long offset = ((fc.size() & ~7) - 8); 230 | if (offset < 0) { 231 | throw new IOException("Empty file"); 232 | } 233 | buf.clear(); 234 | int readed = fc.position(offset).read(buf); 235 | if (readed < 8) { // long 8 bytes 236 | throw new IOException("cant read long from file"); 237 | } 238 | buf.flip(); 239 | value = buf.getLong(); 240 | } 241 | 242 | /** 243 | * Write value to file 244 | * 245 | * @throws IOException 246 | */ 247 | public void write() throws IOException { 248 | write(false); 249 | } 250 | 251 | /** 252 | * Write value to file 253 | * 254 | * @param forceSync if true data must be synced to disk 255 | * @throws IOException 256 | */ 257 | public synchronized void write(final boolean forceSync) throws IOException { 258 | if (!validState) { 259 | throw new InvalidStateException(); 260 | } 261 | buf.clear(); 262 | buf.putLong(value); 263 | buf.flip(); 264 | fc.position(fc.size()).write(buf); // go end and write 265 | if (forceSync) { 266 | fc.force(false); 267 | } 268 | } 269 | 270 | /** 271 | * Write value to file and reduce size to minimal 272 | * 273 | * @throws IOException 274 | */ 275 | public synchronized void pack() throws IOException { 276 | if (!validState) { 277 | throw new InvalidStateException(); 278 | } 279 | buf.clear(); 280 | buf.putLong(value); 281 | buf.flip(); 282 | fc.position(0).write(buf); // go begin and write 283 | fc.truncate(8).force(true); 284 | } 285 | 286 | /** 287 | * Forces any updates to this file to be written to the storage device that contains it. 288 | * 289 | * @return false if exception occur 290 | */ 291 | public synchronized boolean sync() { 292 | if (!validState) { 293 | throw new InvalidStateException(); 294 | } 295 | try { 296 | fc.force(false); 297 | return true; 298 | } catch (Exception e) { 299 | log.error("Exception in sync()", e); 300 | } 301 | return false; 302 | } 303 | 304 | // ========= Exceptions ========= 305 | 306 | /** 307 | * Exception throwed when store is in invalid state (closed) 308 | */ 309 | public static class InvalidStateException extends RuntimeException { 310 | private static final long serialVersionUID = 42L; 311 | } 312 | 313 | // ========= END ========= 314 | } 315 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/io/StringSerializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.io; 16 | 17 | import java.nio.ByteBuffer; 18 | import java.nio.charset.Charset; 19 | 20 | /** 21 | * String Serializer (ByteBuffer) 22 | * 23 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 24 | */ 25 | public class StringSerializer { 26 | private static final Charset cs = Charset.forName("UTF-8"); 27 | 28 | public static final void fromStringToBuffer(final ByteBuffer out, final String str) { 29 | if (str == null) { 30 | out.putInt(Integer.MIN_VALUE); 31 | return; 32 | } 33 | final byte[] bytes = str.getBytes(cs); 34 | final int len = bytes.length; 35 | out.putInt(len); 36 | out.put(bytes, 0, len); 37 | } 38 | 39 | public static final String fromBufferToString(final ByteBuffer in) { 40 | final int len = in.getInt(); 41 | if (len == Integer.MIN_VALUE) { 42 | return null; 43 | } 44 | final byte[] bytes = new byte[len]; 45 | in.get(bytes, 0, len); 46 | return new String(bytes, 0, len, cs); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/pool/BufferStacker.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.pool; 16 | 17 | import java.nio.ByteBuffer; 18 | 19 | import org.javastack.kvstore.structures.hash.IntHashMap; 20 | 21 | /** 22 | * Pool of ByteBuffers 23 | * This class is Thread-Safe 24 | * 25 | * Efficient Java I/O: ByteBuffer 26 | * 27 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 28 | */ 29 | public class BufferStacker { 30 | // 31 | private static IntHashMap INSTANCES = new IntHashMap(BufferStacker.class); 32 | // 33 | private java.util.Deque stack = new java.util.ArrayDeque(); 34 | private final int bufferLen; 35 | private int created = 0; 36 | 37 | // 38 | private BufferStacker() { 39 | this(1024); 40 | } 41 | 42 | private BufferStacker(final int bufferLen) { 43 | this.bufferLen = bufferLen; 44 | } 45 | 46 | // 47 | public static BufferStacker getInstance(final int bufferLen, final boolean isDirect) { 48 | final int key = composeKey(bufferLen, isDirect); 49 | synchronized (INSTANCES) { 50 | BufferStacker bs = INSTANCES.get(key); 51 | if (bs == null) { 52 | bs = new BufferStacker(bufferLen); 53 | INSTANCES.put(key, bs); 54 | } 55 | return bs; 56 | } 57 | } 58 | 59 | // 60 | private static final int composeKey(final int bufferLen, final boolean isDirect) { 61 | return (((bufferLen & 0x3FFFFFFF) << 1) | (isDirect ? 1 : 0)); 62 | } 63 | 64 | // 65 | public synchronized void push(final ByteBuffer buf) { 66 | stack.addFirst(buf); 67 | } 68 | 69 | public synchronized ByteBuffer pop() { 70 | final ByteBuffer buf = stack.pollFirst(); 71 | if (buf == null) { 72 | created++; 73 | return ByteBuffer.allocate(bufferLen); 74 | } 75 | buf.clear(); 76 | return buf; 77 | } 78 | 79 | public String toString() { 80 | return super.toString() + ": created=" + created; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/pool/StringPool.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.pool; 16 | 17 | import org.javastack.kvstore.structures.hash.WeakSet; 18 | 19 | /** 20 | * This class is Thread-Safe 21 | * 22 | * @see java.util.WeakHashMap 23 | * @see java.lang.String#intern() 24 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 25 | */ 26 | public class StringPool { 27 | private static final WeakSet ws = new WeakSet(); 28 | 29 | public static final synchronized String getCanonicalVersion(final String str) { 30 | final String ref = ws.get(str); 31 | if (ref != null) { 32 | return ref; 33 | } 34 | ws.put(str); 35 | return str; 36 | } 37 | 38 | public static final synchronized int size() { 39 | return ws.size(); 40 | } 41 | 42 | public static final synchronized void clear() { 43 | ws.clear(); 44 | } 45 | 46 | public static final synchronized String get(final String str) { 47 | return ws.get(str); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/structures/btree/BplusTreeMemory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.structures.btree; 16 | 17 | import org.javastack.kvstore.holders.DataHolder; 18 | import org.javastack.kvstore.structures.hash.IntHashMap; 19 | 20 | /** 21 | * Implementation of B+Tree in Memory 22 | * This class is Thread-Safe 23 | * 24 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 25 | */ 26 | public final class BplusTreeMemory, V extends DataHolder> extends BplusTree { 27 | 28 | /** 29 | * Storage of nodes 30 | */ 31 | @SuppressWarnings("rawtypes") 32 | private final IntHashMap storeNodes = new IntHashMap(17, Node.class); 33 | 34 | /** 35 | * Current nodeid from underlying storage 36 | */ 37 | private int maxNodeID = 0; 38 | 39 | /** 40 | * Create B+Tree in Memory 41 | * 42 | * @param b_order is the b-order for leaf/internal nodes 43 | * @param typeK the class type of Keys 44 | * @param typeV the class type of Values 45 | * @throws InstantiationException 46 | * @throws IllegalAccessException 47 | */ 48 | public BplusTreeMemory(final int b_order, final Class typeK, final Class typeV) 49 | throws InstantiationException, IllegalAccessException { 50 | super(false, true, b_order, typeK, typeV); 51 | clearStates(); 52 | } 53 | 54 | @Override 55 | public int getHighestNodeId() { 56 | return maxNodeID; 57 | } 58 | 59 | @Override 60 | protected int allocNode(final boolean isLeaf) { 61 | final int id = ++maxNodeID; 62 | return (isLeaf ? id : -id); 63 | } 64 | 65 | @SuppressWarnings("unchecked") 66 | @Override 67 | protected Node getNode(final int nodeid) { 68 | return storeNodes.get(nodeid); 69 | } 70 | 71 | @Override 72 | protected void putNode(final Node node) { 73 | storeNodes.put(node.id, node); 74 | } 75 | 76 | @Override 77 | protected void freeNode(final Node node) { 78 | final int nodeid = node.id; 79 | if (nodeid == Node.NULL_ID) { 80 | System.out.println(this.getClass().getName() + "::freeNode(" + nodeid + ") ERROR"); 81 | return; 82 | } 83 | node.clear(); 84 | storeNodes.remove(nodeid); 85 | } 86 | 87 | @Override 88 | protected void releaseNodes() { 89 | // Nothing 90 | } 91 | 92 | @Override 93 | protected void submitRedoPut(final K key, final V value) { 94 | // Nothing 95 | } 96 | 97 | @Override 98 | protected void submitRedoRemove(final K key) { 99 | // Nothing 100 | } 101 | 102 | @Override 103 | protected void submitRedoMeta(final int value) { 104 | // Nothing 105 | } 106 | 107 | @Override 108 | protected boolean clearStorage() { 109 | storeNodes.clear(); 110 | return true; 111 | } 112 | 113 | @Override 114 | protected void clearStates() { 115 | maxNodeID = 0; 116 | // 117 | // Reset Root node 118 | super.clearStates(); 119 | // Sync changes 120 | validState = true; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/structures/btree/InternalNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.structures.btree; 16 | 17 | import java.nio.ByteBuffer; 18 | import java.util.Arrays; 19 | 20 | import org.apache.log4j.Logger; 21 | import org.javastack.kvstore.holders.DataHolder; 22 | 23 | /** 24 | * Internal Node of BplusTree 25 | * This class is NOT Thread-Safe 26 | * 27 | * @param key type (DataHolder) 28 | * @param value type (DataHolder) 29 | * 30 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 31 | */ 32 | public final class InternalNode, V extends DataHolder> extends Node { 33 | private static final Logger log = Logger.getLogger(InternalNode.class); 34 | public final int[] childs; 35 | 36 | protected InternalNode(final BplusTree tree) { 37 | super(tree); 38 | this.childs = new int[getBOrder() + 1]; 39 | } 40 | 41 | @Override 42 | public int getBOrder() { 43 | return tree.getBOrderInternal(); 44 | } 45 | 46 | @Override 47 | public boolean isLeaf() { 48 | return false; 49 | } 50 | 51 | @Override 52 | public boolean isFreeable() { 53 | return isEmpty() && (childs[0] == NULL_ID); 54 | } 55 | 56 | @Override 57 | public void clear() { 58 | super.clear(); 59 | Arrays.fill(childs, NULL_ID); 60 | } 61 | 62 | public int getSlotLeft(final int slot) { 63 | return (slot + 1 > allocated ? slot - 1 : slot); 64 | } 65 | 66 | public int getSlotRight(final int slot) { 67 | return (slot + 1 > allocated ? slot : slot + 1); 68 | } 69 | 70 | public boolean add(final K newKey, final int childId) { 71 | if (isFull()) { // node is full 72 | if (log.isDebugEnabled()) { 73 | log.debug("overflow"); 74 | } 75 | return false; 76 | } 77 | // TODO: Reparar 78 | int slot = findSlotByKey(newKey); 79 | if (slot >= 0) { 80 | if (log.isDebugEnabled()) { 81 | log.debug("key already exists: " + newKey); 82 | } 83 | return false; // key already exist 84 | } 85 | slot = (-slot) - 1; 86 | return add(slot, newKey, childId); 87 | } 88 | 89 | public boolean add(final int slot, final K newKey, final int childId) { 90 | if (log.isDebugEnabled()) { 91 | log.debug("add(" + newKey + ") i=" + slot); 92 | } 93 | if (slot < allocated) { 94 | moveElementsRight(keys, slot); 95 | moveChildsRight(slot + 1); 96 | } 97 | allocated++; 98 | keys[slot] = newKey; 99 | childs[slot + 1] = childId; 100 | return true; 101 | } 102 | 103 | @Override 104 | public boolean remove(final int slot) { 105 | if (slot < 0) { 106 | log.error("faking slot=" + slot + " allocated=" + allocated); 107 | return false; 108 | } 109 | if (slot < allocated) { 110 | moveElementsLeft(keys, slot); 111 | moveChildsLeft(slot + 1); 112 | } 113 | if (allocated > 0) { 114 | allocated--; 115 | } 116 | if (log.isDebugEnabled()) { 117 | log.debug("[" + id + "] erased up [" + allocated + "] key=" + keys[allocated] + " value=" 118 | + childs[allocated + 1]); 119 | } 120 | keys[allocated] = null; 121 | childs[allocated + 1] = NULL_ID; 122 | return true; 123 | } 124 | 125 | @Override 126 | public InternalNode split() { // TODO 127 | final InternalNode newHigh = tree.createInternalNode(); 128 | newHigh.allocId(); 129 | // int j = ((allocated >> 1) | (allocated & 1)); // dividir por dos y sumar el resto (0 o 1) 130 | int j = (allocated >> 1); // dividir por dos (libro) 131 | final int newsize = allocated - j; 132 | // if (log.isDebugEnabled()) log.debug("split j=" + j); 133 | System.arraycopy(keys, j, newHigh.keys, 0, newsize); 134 | System.arraycopy(childs, j + 1, newHigh.childs, 0, newsize); 135 | // TODO: Limpiar la parte alta de los arrays de referencias inutiles 136 | for (int i = j; i < j + newsize; i++) { 137 | keys[i] = null; 138 | childs[i + 1] = NULL_ID; 139 | } 140 | newHigh.allocated = newsize; 141 | allocated -= newsize; 142 | // Ahora habria que hacer un splitShiftKeysLeft para alinear los childs 143 | tree.putNode(this); // TODO FAKE 144 | tree.putNode(newHigh); // TODO FAKE 145 | return newHigh; 146 | } 147 | 148 | @Override 149 | public K splitShiftKeysLeft() { 150 | final K removed = keys[0]; 151 | moveElementsLeft(keys, 0); 152 | allocated--; 153 | // TODO: Limpiar la parte alta de los arrays de referencias inutiles 154 | keys[allocated] = null; 155 | childs[allocated + 1] = NULL_ID; 156 | return removed; 157 | } 158 | 159 | // insert child 160 | protected void moveChildsRight(final int srcPos) { 161 | // if (log.isDebugEnabled()) log.debug("moveKeysRight("+srcPos+") allocated=" + allocated + ":" + 162 | // keys.length + ":" + (allocated-srcPos) + ":" + (keys.length-srcPos-1)); 163 | System.arraycopy(childs, srcPos, childs, srcPos + 1, allocated - srcPos + 1); 164 | } 165 | 166 | // remove child 167 | protected void moveChildsLeft(final int srcPos) { 168 | // if (log.isDebugEnabled()) log.debug("moveKeysLeft("+srcPos+") allocated=" + allocated + ":" + 169 | // keys.length + ":" + (allocated-srcPos-1) + ":" + (keys.length-srcPos-1)); 170 | System.arraycopy(childs, srcPos + 1, childs, srcPos, allocated - srcPos); 171 | } 172 | 173 | public String toString() { 174 | final StringBuilder sb = new StringBuilder(); 175 | sb.append("[").append("I").append(id).append("]"); 176 | sb.append("(").append(allocated).append("){"); 177 | for (int i = 0; i < allocated; i++) { 178 | final K k = keys[i]; 179 | if (i == 0) { // left 180 | sb.append("c").append(childs[i]).append("<"); 181 | } else { 182 | sb.append("<"); 183 | } 184 | sb.append(k); 185 | sb.append(">c").append(childs[i + 1]); 186 | } 187 | sb.append("}"); 188 | return sb.toString(); 189 | } 190 | 191 | // ========= Remove Helpers ========= 192 | 193 | protected final void merge(final InternalNode nodeParent, final int slot, final Node nodeFROMx) { 194 | final InternalNode nodeFROM = (InternalNode) nodeFROMx; 195 | final InternalNode nodeTO = this; 196 | final int sizeTO = nodeTO.allocated; 197 | final int sizeFROM = nodeFROM.allocated; 198 | // copy keys from nodeFROM to nodeTO 199 | System.arraycopy(nodeFROM.keys, 0, nodeTO.keys, sizeTO + 1, sizeFROM); 200 | System.arraycopy(nodeFROM.childs, 0, nodeTO.childs, sizeTO + 1, sizeFROM + 1); 201 | // add key to nodeTO 202 | nodeTO.keys[sizeTO] = nodeParent.keys[slot]; 203 | nodeTO.allocated += sizeFROM + 1; // keys of FROM and key of nodeParent 204 | // remove key from nodeParent 205 | nodeParent.remove(slot); 206 | // Free nodeFROM 207 | tree.freeNode(nodeFROM); 208 | } 209 | 210 | protected final void shiftLR(final InternalNode nodeParent, final int slot, 211 | final Node nodeFROMx) { 212 | final InternalNode nodeFROM = (InternalNode) nodeFROMx; 213 | final InternalNode nodeTO = this; 214 | final int sizeTO = nodeTO.allocated; 215 | final int sizeFROM = nodeFROM.allocated; 216 | final int shift = ((sizeTO + sizeFROM) / 2) - sizeTO; // num. keys to shift from nodeFROM to nodeTO 217 | // make space for new keys in nodeTO 218 | System.arraycopy(nodeTO.keys, 0, nodeTO.keys, shift, sizeTO); 219 | System.arraycopy(nodeTO.childs, 0, nodeTO.childs, shift, sizeTO + 1); 220 | // move keys and children out of nodeFROM and into nodeTO (and nodeU) 221 | nodeTO.keys[shift - 1] = nodeParent.keys[slot]; 222 | nodeParent.keys[slot] = nodeFROM.keys[sizeFROM - shift]; 223 | System.arraycopy(nodeFROM.keys, sizeFROM - shift + 1, nodeTO.keys, 0, shift - 1); 224 | Arrays.fill(nodeFROM.keys, sizeFROM - shift, sizeFROM, null); 225 | System.arraycopy(nodeFROM.childs, sizeFROM - shift + 1, nodeTO.childs, 0, shift); 226 | Arrays.fill(nodeFROM.childs, sizeFROM - shift + 1, sizeFROM + 1, Node.NULL_ID); 227 | nodeTO.allocated += shift; 228 | nodeFROM.allocated -= shift; 229 | } 230 | 231 | protected final void shiftRL(final InternalNode nodeParent, final int slot, 232 | final Node nodeFROMx) { 233 | final InternalNode nodeFROM = (InternalNode) nodeFROMx; 234 | final InternalNode nodeTO = this; 235 | final int sizeTO = nodeTO.allocated; 236 | final int sizeFROM = nodeFROM.allocated; 237 | final int shift = ((sizeTO + sizeFROM) / 2) - sizeTO; // num. keys to shift from nodeFROM to nodeTO 238 | // shift keys and children from nodeFROM to nodeTO 239 | nodeTO.keys[sizeTO] = nodeParent.keys[slot]; 240 | System.arraycopy(nodeFROM.keys, 0, nodeTO.keys, sizeTO + 1, shift - 1); 241 | System.arraycopy(nodeFROM.childs, 0, nodeTO.childs, sizeTO + 1, shift); 242 | nodeParent.keys[slot] = nodeFROM.keys[shift - 1]; 243 | // delete keys and children from nodeFROM 244 | System.arraycopy(nodeFROM.keys, shift, nodeFROM.keys, 0, sizeFROM - shift); 245 | Arrays.fill(nodeFROM.keys, sizeFROM - shift, sizeFROM, null); 246 | System.arraycopy(nodeFROM.childs, shift, nodeFROM.childs, 0, sizeFROM - shift + 1); 247 | Arrays.fill(nodeFROM.childs, sizeFROM - shift + 1, sizeFROM + 1, Node.NULL_ID); 248 | nodeFROM.allocated -= shift; 249 | nodeTO.allocated += shift; 250 | } 251 | 252 | /** 253 | * Check if underflow occurred in child of slot 254 | * 255 | * @param slot the index of a child in nodeParent (this) 256 | */ 257 | protected boolean checkUnderflow(final int slot) { 258 | if (childs[slot] == NULL_ID) { 259 | return false; 260 | } 261 | if (slot == 0) { 262 | return checkUnderflowWithRight(slot); // use nodeParent right sibling 263 | } else { 264 | if (getSlotLeft(slot) == slot) { 265 | return checkUnderflowWithRight(slot); // use nodeParent right sibling 266 | } else { 267 | return checkUnderflowWithLeft(slot); // use nodeParent left sibling 268 | } 269 | } 270 | } 271 | 272 | /** 273 | * Check if underflow occurred in child of slot 274 | * 275 | * @param slot the index of a child in nodeParent 276 | */ 277 | private final boolean checkUnderflowWithLeft(final int slot) { 278 | final Node nodeRight = tree.getNode(childs[slot]); 279 | if (nodeRight.isUnderFlow()) { 280 | final Node nodeLeft = tree.getNode(childs[slot - 1]); 281 | if (nodeLeft.canMerge(nodeRight)) { 282 | nodeLeft.merge(this, slot - 1, nodeRight); 283 | } else { 284 | nodeRight.shiftLR(this, slot - 1, nodeLeft); 285 | } 286 | // 287 | // Update Changed Nodes 288 | tree.putNode(this); 289 | tree.putNode(nodeLeft); 290 | tree.putNode(nodeRight); 291 | return true; 292 | } 293 | return false; 294 | } 295 | 296 | /** 297 | * Check if underflow ocurred in child of slot 298 | * 299 | * @param nodeParent 300 | * @param slot the index of a child in nodeParent 301 | */ 302 | private final boolean checkUnderflowWithRight(final int slot) { 303 | final Node nodeLeft = tree.getNode(childs[slot]); 304 | if (nodeLeft.isUnderFlow()) { 305 | final Node nodeRight = tree.getNode(childs[slot + 1]); 306 | if (nodeLeft.canMerge(nodeRight)) { 307 | nodeLeft.merge(this, slot, nodeRight); 308 | childs[slot] = nodeLeft.id; 309 | } else { 310 | nodeLeft.shiftRL(this, slot, nodeRight); 311 | } 312 | // 313 | // Update Changed Nodes 314 | tree.putNode(this); 315 | tree.putNode(nodeRight); 316 | tree.putNode(nodeLeft); 317 | return true; 318 | } 319 | return false; 320 | } 321 | 322 | // ========= Serialization ========= 323 | 324 | @Override 325 | public int getStructMaxSize() { 326 | return super.getStructMaxSize() + (childs.length * 4); 327 | } 328 | 329 | @Override 330 | public int getStructEstimateSize(final int b) { 331 | return super.getStructEstimateSize(b) + ((b + 1) * 4); 332 | } 333 | 334 | @Override 335 | public void serialize(final ByteBuffer buf) { 336 | super.serialize(buf); 337 | for (int i = 0; i < allocated + 1; i++) { 338 | buf.putInt(childs[i]); // 4 bytes 339 | } 340 | buf.flip(); 341 | } 342 | 343 | @Override 344 | protected Node deserializeNode(final ByteBuffer buf) { 345 | super.deserializeNode(buf); 346 | for (int i = 0; i < allocated + 1; i++) { 347 | childs[i] = buf.getInt(); 348 | } 349 | return this; 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/structures/btree/LeafNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.structures.btree; 16 | 17 | import java.nio.ByteBuffer; 18 | import java.util.Arrays; 19 | 20 | import org.apache.log4j.Logger; 21 | import org.javastack.kvstore.holders.DataHolder; 22 | 23 | /** 24 | * Leaf Node of BplusTree 25 | * This class is NOT Thread-Safe 26 | * 27 | * @param key type (DataHolder) 28 | * @param value type (DataHolder) 29 | * 30 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 31 | */ 32 | public final class LeafNode, V extends DataHolder> extends Node { 33 | private static final Logger log = Logger.getLogger(LeafNode.class); 34 | public final V[] values; 35 | public int leftid = NULL_ID; 36 | public int rightid = NULL_ID; 37 | 38 | protected LeafNode(final BplusTree tree) { 39 | super(tree); 40 | this.values = tree.getGenericFactoryV().newArray(getBOrder()); 41 | } 42 | 43 | @Override 44 | public int getBOrder() { 45 | return tree.getBOrderLeaf(); 46 | } 47 | 48 | @Override 49 | public boolean isLeaf() { 50 | return true; 51 | } 52 | 53 | @Override 54 | public boolean isFreeable() { 55 | return isEmpty() && (values[0] == null); 56 | } 57 | 58 | @Override 59 | public void clear() { 60 | super.clear(); 61 | leftid = rightid = NULL_ID; 62 | Arrays.fill(values, null); 63 | } 64 | 65 | public V set(final int slot, final V newValue) { 66 | final V oldValue = values[slot]; 67 | values[slot] = newValue; 68 | return oldValue; 69 | } 70 | 71 | public boolean add(final K newKey, final V newValue) { 72 | if (isFull()) { // node is full 73 | if (log.isDebugEnabled()) { 74 | log.debug("overflow"); 75 | } 76 | return false; 77 | } 78 | // TODO: Reparar 79 | int slot = findSlotByKey(newKey); 80 | if (slot >= 0) { 81 | if (log.isDebugEnabled()) { 82 | log.debug("key already exists: " + newKey); 83 | } 84 | return false; // key already exist 85 | } 86 | slot = (-slot) - 1; 87 | return add(slot, newKey, newValue); 88 | } 89 | 90 | public boolean add(final int slot, final K newKey, final V newValue) { 91 | // if (log.isDebugEnabled()) log.debug("add("+newKey+") i=" + slot); 92 | if (slot < allocated) { 93 | moveElementsRight(keys, slot); 94 | moveElementsRight(values, slot); 95 | } 96 | allocated++; 97 | keys[slot] = newKey; 98 | values[slot] = newValue; 99 | return true; 100 | } 101 | 102 | @Override 103 | public boolean remove(final int slot) { 104 | if (slot < 0) { 105 | log.error("faking slot=" + slot + " allocated=" + allocated); 106 | return false; 107 | } 108 | if (slot < allocated) { 109 | moveElementsLeft(keys, slot); 110 | moveElementsLeft(values, slot); 111 | } 112 | if (allocated > 0) { 113 | allocated--; 114 | } 115 | if (log.isDebugEnabled()) { 116 | log.debug("erased up key=" + keys[allocated] + " value=" + values[allocated]); 117 | } 118 | keys[allocated] = null; 119 | values[allocated] = null; 120 | return true; 121 | } 122 | 123 | @Override 124 | public LeafNode split() { 125 | final LeafNode newHigh = tree.createLeafNode(); 126 | newHigh.allocId(); 127 | // int j = ((allocated >> 1) | (allocated & 1)); // dividir por dos y sumar el resto (0 o 1) 128 | int j = (allocated >> 1); // dividir por dos (libro) 129 | final int newsize = allocated - j; 130 | // if (log.isDebugEnabled()) log.debug("split j=" + j); 131 | System.arraycopy(keys, j, newHigh.keys, 0, newsize); 132 | System.arraycopy(values, j, newHigh.values, 0, newsize); 133 | // Limpiar la parte alta de los arrays de referencias inutiles 134 | for (int i = j; i < j + newsize; i++) { 135 | keys[i] = null; 136 | values[i] = null; 137 | } 138 | newHigh.allocated = newsize; 139 | allocated -= newsize; 140 | // Update Linked List (left) in old High 141 | if (rightid != NULL_ID) { 142 | final LeafNode oldHigh = (LeafNode) tree.getNode(rightid); 143 | oldHigh.leftid = newHigh.id; 144 | tree.putNode(oldHigh); 145 | } 146 | // Linked List (left) in new High 147 | newHigh.leftid = id; 148 | // Linked List (right) 149 | newHigh.rightid = rightid; 150 | rightid = newHigh.id; 151 | // update lowIdx on tree 152 | if (leftid == 0) { 153 | tree.lowIdx = id; 154 | } 155 | // update highIdx on tree 156 | if (newHigh.rightid == 0) { 157 | tree.highIdx = newHigh.id; 158 | } 159 | // 160 | tree.putNode(this); 161 | tree.putNode(newHigh); 162 | return newHigh; 163 | } 164 | 165 | @Override 166 | public K splitShiftKeysLeft() { 167 | return keys[0]; 168 | } 169 | 170 | /** 171 | * Return the previous LeafNode in LinkedList 172 | * 173 | * @return LeafNode previous node 174 | */ 175 | public LeafNode prevNode() { 176 | if (leftid == NULL_ID) { 177 | return null; 178 | } 179 | return (LeafNode) tree.getNode(leftid); 180 | } 181 | 182 | /** 183 | * Return the next LeafNode in LinkedList 184 | * 185 | * @return LeafNode next node 186 | */ 187 | public LeafNode nextNode() { 188 | if (rightid == NULL_ID) { 189 | return null; 190 | } 191 | return (LeafNode) tree.getNode(rightid); 192 | } 193 | 194 | public String toString() { 195 | final StringBuilder sb = new StringBuilder(); 196 | sb.append(leftid).append("<<"); 197 | sb.append("[").append("L").append(id).append("]"); 198 | sb.append(">>").append(rightid); 199 | sb.append("(").append(allocated).append("){"); 200 | for (int i = 0; i < allocated; i++) { 201 | final K k = keys[i]; 202 | final V v = values[i]; 203 | sb.append(k).append("=").append(v).append("|"); 204 | } 205 | if (allocated > 0) { 206 | sb.setLength(sb.length() - 1); 207 | } 208 | sb.append("}"); 209 | return sb.toString(); 210 | } 211 | 212 | // ========= Remove Helpers ========= 213 | 214 | protected final void merge(final InternalNode nodeParent, final int slot, final Node nodeFROMx) { 215 | final LeafNode nodeFROM = (LeafNode) nodeFROMx; 216 | final LeafNode nodeTO = this; 217 | final int sizeTO = nodeTO.allocated; 218 | final int sizeFROM = nodeFROM.allocated; 219 | // copy keys from nodeFROM to nodeTO 220 | System.arraycopy(nodeFROM.keys, 0, nodeTO.keys, sizeTO, sizeFROM); 221 | System.arraycopy(nodeFROM.values, 0, nodeTO.values, sizeTO, sizeFROM); 222 | nodeTO.allocated += sizeFROM; 223 | // remove key from nodeParent 224 | nodeParent.remove(slot); 225 | // Update Linked List (left) in new High 226 | if (nodeFROM.rightid != NULL_ID) { 227 | final LeafNode rightFROM = (LeafNode) tree.getNode(nodeFROM.rightid); 228 | rightFROM.leftid = id; 229 | tree.putNode(rightFROM); 230 | } 231 | // Update Linked List (right) 232 | rightid = nodeFROM.rightid; 233 | // update lowIdx on tree 234 | if (leftid == 0) { 235 | tree.lowIdx = id; 236 | } 237 | // update highIdx on tree 238 | if (rightid == 0) { 239 | tree.highIdx = id; 240 | } 241 | // 242 | // Free nodeFROM 243 | tree.freeNode(nodeFROM); 244 | } 245 | 246 | protected final void shiftLR(final InternalNode nodeParent, final int slot, 247 | final Node nodeFROMx) { 248 | final LeafNode nodeFROM = (LeafNode) nodeFROMx; 249 | final LeafNode nodeTO = this; 250 | final int sizeTO = nodeTO.allocated; 251 | final int sizeFROM = nodeFROM.allocated; 252 | final int shift = ((sizeTO + sizeFROM) / 2) - sizeTO; // num. keys to shift from nodeFROM to nodeTO 253 | // make space for new keys in nodeTO 254 | System.arraycopy(nodeTO.keys, 0, nodeTO.keys, shift, sizeTO); 255 | System.arraycopy(nodeTO.values, 0, nodeTO.values, shift, sizeTO); 256 | // move keys and children out of nodeFROM and into nodeTO (and nodeU) 257 | nodeTO.keys[shift - 1] = nodeParent.keys[slot]; 258 | nodeParent.keys[slot] = nodeFROM.keys[sizeFROM - shift]; 259 | System.arraycopy(nodeFROM.keys, sizeFROM - shift, nodeTO.keys, 0, shift); 260 | Arrays.fill(nodeFROM.keys, sizeFROM - shift, sizeFROM, null); 261 | System.arraycopy(nodeFROM.values, sizeFROM - shift, nodeTO.values, 0, shift); 262 | Arrays.fill(nodeFROM.values, sizeFROM - shift, sizeFROM, null); 263 | nodeTO.allocated += shift; 264 | nodeFROM.allocated -= shift; 265 | } 266 | 267 | protected final void shiftRL(final InternalNode nodeParent, final int slot, 268 | final Node nodeFROMx) { 269 | final LeafNode nodeFROM = (LeafNode) nodeFROMx; 270 | final LeafNode nodeTO = this; 271 | final int sizeTO = nodeTO.allocated; 272 | final int sizeFROM = nodeFROM.allocated; 273 | final int shift = ((sizeTO + sizeFROM) / 2) - sizeTO; // num. keys to shift from nodeFROM to nodeTO 274 | // shift keys and children from nodeFROM to nodeTO 275 | nodeTO.keys[sizeTO] = nodeParent.keys[slot]; 276 | System.arraycopy(nodeFROM.keys, 0, nodeTO.keys, sizeTO, shift); 277 | System.arraycopy(nodeFROM.values, 0, nodeTO.values, sizeTO, shift); 278 | nodeParent.keys[slot] = nodeFROM.keys[shift]; 279 | // delete keys and children from nodeFROM 280 | System.arraycopy(nodeFROM.keys, shift, nodeFROM.keys, 0, sizeFROM - shift); 281 | Arrays.fill(nodeFROM.keys, sizeFROM - shift, sizeFROM, null); 282 | System.arraycopy(nodeFROM.values, shift, nodeFROM.values, 0, sizeFROM - shift); 283 | Arrays.fill(nodeFROM.values, sizeFROM - shift, sizeFROM, null); 284 | nodeTO.allocated += shift; 285 | nodeFROM.allocated -= shift; 286 | } 287 | 288 | // ========= Serialization ========= 289 | 290 | @Override 291 | public int getStructMaxSize() { 292 | final V factoryV = tree.factoryV(); 293 | return super.getStructMaxSize() + (values.length * factoryV.byteLength()) + 4 + 4; 294 | } 295 | 296 | @Override 297 | public int getStructEstimateSize(final int b) { 298 | final V factoryV = tree.factoryV(); 299 | return super.getStructEstimateSize(b) + (b * factoryV.byteLength()) + 4 + 4; 300 | } 301 | 302 | @Override 303 | public void serialize(final ByteBuffer buf) { 304 | super.serialize(buf); 305 | for (int i = 0; i < allocated; i++) { 306 | values[i].serialize(buf); 307 | } 308 | buf.putInt(leftid); // 4 bytes 309 | buf.putInt(rightid); // 4 bytes 310 | buf.flip(); 311 | } 312 | 313 | @Override 314 | protected Node deserializeNode(final ByteBuffer buf) { 315 | super.deserializeNode(buf); 316 | final V factoryV = tree.factoryV(); 317 | // Arrays.fill(values, null); 318 | for (int i = 0; i < allocated; i++) { 319 | values[i] = factoryV.deserialize(buf); 320 | } 321 | leftid = buf.getInt(); 322 | rightid = buf.getInt(); 323 | return this; 324 | } 325 | 326 | } 327 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/structures/btree/Node.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.structures.btree; 16 | 17 | import java.nio.ByteBuffer; 18 | import java.util.Arrays; 19 | 20 | import org.apache.log4j.Logger; 21 | import org.javastack.kvstore.holders.DataHolder; 22 | 23 | /** 24 | * Generic Node definition 25 | * This class is NOT Thread-Safe 26 | * 27 | * @param 28 | * @param 29 | * 30 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 31 | */ 32 | public abstract class Node, V extends DataHolder> { 33 | private static final Logger log = Logger.getLogger(Node.class); 34 | public static final int NULL_ID = 0; 35 | 36 | public final BplusTree tree; 37 | public final K[] keys; 38 | 39 | public int id = Node.NULL_ID; 40 | public int allocated = 0; 41 | 42 | protected Node(final BplusTree tree) { 43 | this.tree = tree; 44 | this.keys = tree.getGenericFactoryK().newArray(getBOrder()); 45 | } 46 | 47 | public int allocId() { 48 | id = tree.allocNode(isLeaf()); 49 | // Dont do this here, do after allocId or after split 50 | // tree.putNode(this); 51 | return id; 52 | } 53 | 54 | public static boolean isLeaf(final int id) { 55 | return (id > 0); 56 | } 57 | 58 | /** 59 | * Searches using the binary search algorithm. {@link Arrays#binarySearch(Object[], int, int, Object)} 60 | * 61 | * @param key the value to be searched for 62 | * @return index of the search key, if it is contained in the array 63 | * within the specified range; 64 | * otherwise, (-(insertion point) - 1). The 65 | * insertion point is defined as the point at which the 66 | * key would be inserted into the array: the index of the first 67 | * element in the range greater than the key, 68 | * or toIndex if all 69 | * elements in the range are less than the specified key. Note 70 | * that this guarantees that the return value will be >= 0 if 71 | * and only if the key is found. 72 | */ 73 | public final int findSlotByKey(final K searchKey) { 74 | // return Arrays.binarySearch(keys, 0, allocated, searchKey); 75 | int low = 0; 76 | int high = allocated - 1; 77 | 78 | while (low <= high) { 79 | final int mid = (low + high) >>> 1; 80 | final K midVal = keys[mid]; 81 | final int cmp = midVal.compareTo(searchKey); 82 | 83 | if (cmp < 0) { 84 | low = mid + 1; 85 | } else if (cmp > 0) { 86 | high = mid - 1; 87 | } else { 88 | return mid; // key found 89 | } 90 | } 91 | return -(low + 1); // key not found. 92 | } 93 | 94 | public boolean isEmpty() { // node empty 95 | return (allocated <= 0); 96 | } 97 | 98 | public boolean isFull() { // node is full 99 | if (log.isDebugEnabled()) 100 | log.debug("allocated=" + allocated + " keys.length=" + keys.length); 101 | return (allocated >= keys.length); 102 | } 103 | 104 | public boolean isUnderFlow() { 105 | return (allocated < (keys.length >> 1)); 106 | } 107 | 108 | public boolean canMerge(final Node other) { 109 | return ((allocated + other.allocated + 1) < keys.length); // TODO: revisar el +1 110 | } 111 | 112 | protected void clear() { 113 | Arrays.fill(keys, null); 114 | allocated = 0; 115 | } 116 | 117 | protected void delete() { 118 | clear(); 119 | allocated = Integer.MIN_VALUE; 120 | } 121 | 122 | protected boolean isDeleted() { 123 | return (allocated == Integer.MIN_VALUE); 124 | } 125 | 126 | // insert element 127 | protected void moveElementsRight(final Object[] elements, final int srcPos) { 128 | if (log.isDebugEnabled()) { 129 | log.debug("moveElementsRight(" + srcPos + ") allocated=" + allocated + ":" + keys.length + ":" 130 | + (allocated - srcPos) + ":" + (keys.length - srcPos - 1)); 131 | } 132 | System.arraycopy(elements, srcPos, elements, srcPos + 1, allocated - srcPos); 133 | } 134 | 135 | // remove element 136 | protected void moveElementsLeft(final Object[] elements, final int srcPos) { 137 | if (log.isDebugEnabled()) { 138 | log.debug("moveElementsLeft(" + srcPos + ") allocated=" + allocated + ":" + keys.length + ":" 139 | + (allocated - srcPos - 1) + ":" + (keys.length - srcPos - 1)); 140 | } 141 | System.arraycopy(elements, srcPos + 1, elements, srcPos, allocated - srcPos - 1); 142 | } 143 | 144 | public int countKeys() { 145 | int low = 0, high = keys.length; 146 | while (high != low) { 147 | int middle = (high + low) / 2; 148 | if (keys[middle] == null) { 149 | high = middle; 150 | } else { 151 | low = middle + 1; 152 | } 153 | } 154 | return low; 155 | } 156 | 157 | // ========= Serialization ========= 158 | 159 | public int getStructMaxSize() { 160 | final K factoryK = tree.factoryK(); 161 | return (4 + 2 + (keys.length * factoryK.byteLength())); 162 | } 163 | 164 | public int getStructEstimateSize(final int b) { 165 | final K factoryK = tree.factoryK(); 166 | return (4 + 2 + (b * factoryK.byteLength())); 167 | } 168 | 169 | public void serialize(final ByteBuffer buf) { 170 | buf.clear(); 171 | buf.putInt(id); // 4 bytes 172 | buf.putShort((short) (allocated & 0x7FFF)); // 2 bytes 173 | for (int i = 0; i < allocated; i++) { // X bytes * b_order 174 | keys[i].serialize(buf); 175 | } 176 | } 177 | 178 | public final void clean(final ByteBuffer buf) { 179 | buf.clear(); 180 | // buf.putInt(0); // 4 bytes 181 | // buf.putShort((short) 0); // 2 bytes 182 | buf.putLong(0); // 8 bytes 183 | buf.flip(); 184 | } 185 | 186 | public static , V extends DataHolder> Node deserialize( 187 | final ByteBuffer buf, final BplusTree tree) { 188 | final int id = buf.getInt(); 189 | if (id == NULL_ID) { 190 | throw InvalidNodeID.NULL_ID; 191 | } 192 | final boolean isLeaf = isLeaf(id); 193 | final Node node = (isLeaf ? tree.createLeafNode() : tree.createInternalNode()); 194 | node.id = id; 195 | return node.deserializeNode(buf); 196 | } 197 | 198 | protected Node deserializeNode(final ByteBuffer buf) { 199 | final K factoryK = tree.factoryK(); 200 | allocated = buf.getShort(); 201 | // Arrays.fill(keys, null); 202 | for (int i = 0; i < allocated; i++) { 203 | keys[i] = factoryK.deserialize(buf); 204 | } 205 | return this; 206 | } 207 | 208 | // ========= Abstract ========= 209 | 210 | abstract public boolean remove(int slot); 211 | 212 | abstract public boolean isLeaf(); 213 | 214 | abstract public Node split(); 215 | 216 | abstract public int getBOrder(); 217 | 218 | abstract public boolean isFreeable(); 219 | 220 | abstract public K splitShiftKeysLeft(); 221 | 222 | /** 223 | * Merge two nodes, this node will absorb nodeFROM 224 | * 225 | * @param nodeParent a node parent for this & nodeFROM 226 | * @param nodeFROM a node (will be clean) 227 | */ 228 | abstract protected void merge(final InternalNode nodeParent, final int slot, 229 | final Node nodeFROM); 230 | 231 | /** 232 | * Shift keys from nodeFROM (left) into this node (right) 233 | * 234 | * @param nodeParent the parent of nodeFROM and this node 235 | * @param slot the index nodeTO in nodeParent.childs 236 | * @param nodeFROM the right sibling of nodeTO 237 | */ 238 | abstract protected void shiftLR(final InternalNode nodeParent, final int slot, 239 | final Node nodeFROM); 240 | 241 | /** 242 | * Shift keys from node nodeFROM (right) into this node (left) 243 | * 244 | * @param nodeParent the parent of nodeFROM and this node 245 | * @param slot the index nodeTO in nodeParent.childs 246 | * @param nodeFROM the left sibling of nodeTO 247 | */ 248 | abstract protected void shiftRL(final InternalNode nodeParent, final int slot, 249 | final Node nodeFROM); 250 | 251 | public static class InvalidNodeID extends RuntimeException { 252 | private static final long serialVersionUID = 42L; 253 | public static final InvalidNodeID NULL_ID = new InvalidNodeID("Invalid Node id=NULL_ID"); 254 | 255 | public InvalidNodeID(final String error) { 256 | super(error); 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/structures/hash/FixedIntHashMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.structures.hash; 16 | 17 | import java.util.Arrays; 18 | import java.util.Random; 19 | 20 | import org.apache.log4j.Logger; 21 | import org.javastack.kvstore.utils.GenericFactory; 22 | import org.javastack.kvstore.utils.PrimeFinder; 23 | 24 | /** 25 | * Native Integer HashMap with Fixed Size (no collision resolver); 26 | * on collision last key/value overwrite old key/value 27 | * Suitable only for caches 28 | * 29 | * This class is NOT Thread-Safe 30 | * 31 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 32 | */ 33 | public class FixedIntHashMap { 34 | private static final Logger log = Logger.getLogger(FixedIntHashMap.class); 35 | private int elementCount; 36 | 37 | private int[] elementKeys; 38 | private T[] elementValues; 39 | 40 | private final float loadFactor; 41 | private final GenericFactory arrayFactory; 42 | 43 | private int threshold; 44 | 45 | private int defaultSize = 16; 46 | private int collisions = 0; 47 | 48 | private int[] newKeyArray(final int size) { 49 | if (log.isDebugEnabled()) { 50 | log.debug(this.getClass().getName() + "::newKeyArray(" + size + ")"); 51 | } 52 | final int[] e = new int[size]; 53 | Arrays.fill(e, Integer.MIN_VALUE); 54 | return e; 55 | } 56 | 57 | private T[] newElementArray(final int size) { 58 | if (log.isDebugEnabled()) { 59 | log.debug(this.getClass().getName() + "::newElementArray(" + size + ")"); 60 | } 61 | final T[] e = arrayFactory.newArray(size); 62 | Arrays.fill(e, null); 63 | return e; 64 | } 65 | 66 | /** 67 | * Constructs a new {@code NativeFixedIntHashMap} instance with the specified capacity. 68 | * 69 | * @param capacity the initial capacity of this hash map. 70 | * @throws IllegalArgumentException when the capacity is less than zero. 71 | */ 72 | public FixedIntHashMap(final int capacity, final Class ctype) { 73 | arrayFactory = new GenericFactory(ctype); 74 | defaultSize = primeSize(capacity); 75 | if (capacity > 0) { 76 | elementCount = 0; 77 | elementKeys = newKeyArray(defaultSize); 78 | elementValues = newElementArray(defaultSize); 79 | loadFactor = 0.75f; // Default load factor of 0.75 80 | computeMaxSize(); 81 | } else { 82 | throw new IllegalArgumentException(); 83 | } 84 | } 85 | 86 | private static final int primeSize(final int capacity) { 87 | // return java.math.BigInteger.valueOf((long)capacity).nextProbablePrime().intValue(); 88 | return PrimeFinder.nextPrime(capacity); 89 | } 90 | 91 | /** 92 | * Removes all mappings from this hash map, leaving it empty. 93 | * 94 | * @see #isEmpty 95 | * @see #size 96 | */ 97 | public void clear(boolean shrink) { 98 | if (elementCount > 0) { 99 | elementCount = 0; 100 | } 101 | if (shrink && ((elementKeys.length > 1024) && (elementKeys.length > defaultSize))) { 102 | elementKeys = newKeyArray(defaultSize); 103 | elementValues = newElementArray(defaultSize); 104 | } else { 105 | Arrays.fill(elementKeys, Integer.MIN_VALUE); 106 | Arrays.fill(elementValues, null); 107 | } 108 | computeMaxSize(); 109 | } 110 | 111 | public int[] getKeys() { 112 | return elementKeys; 113 | } 114 | 115 | public T[] getValues() { 116 | return elementValues; 117 | } 118 | 119 | /** 120 | * Returns a shallow copy of this map. 121 | * 122 | * @return a shallow copy of this map. 123 | */ 124 | private void computeMaxSize() { 125 | threshold = (int) (elementKeys.length * loadFactor); 126 | if (log.isDebugEnabled()) { 127 | log.debug(this.getClass().getName() + "::computeMaxSize()=" + threshold + " collisions=" 128 | + collisions + " (" + (collisions * 100 / elementKeys.length) + ")"); 129 | } 130 | collisions = 0; 131 | } 132 | 133 | /** 134 | * Returns the value of the mapping with the specified key. 135 | * 136 | * @param key the key. 137 | * @return the value of the mapping with the specified key, or {@code -1} if no mapping for the specified 138 | * key is found. 139 | */ 140 | public T get(final int key) { 141 | int index = ((key & 0x7FFFFFFF) % elementKeys.length); 142 | 143 | long m = elementKeys[index]; 144 | if (key == m) { 145 | return elementValues[index]; 146 | } 147 | 148 | return null; 149 | } 150 | 151 | /** 152 | * Returns whether this map is empty. 153 | * 154 | * @return {@code true} if this map has no elements, {@code false} otherwise. 155 | * @see #size() 156 | */ 157 | public boolean isEmpty() { 158 | return (elementCount == 0); 159 | } 160 | 161 | /** 162 | * Maps the specified key to the specified value. 163 | * 164 | * @param key the key. 165 | * @param value the value. 166 | * @return the value of any previous mapping with the specified key or {@code -1} if there was no such 167 | * mapping. 168 | */ 169 | public T put(final int key, final T value) { 170 | int index = ((key & 0x7FFFFFFF) % elementKeys.length); 171 | T oldvalue = null; 172 | 173 | long entry = elementKeys[index]; 174 | if (entry == Integer.MIN_VALUE) { 175 | ++elementCount; 176 | } else { 177 | oldvalue = elementValues[index]; 178 | collisions++; 179 | } 180 | elementKeys[index] = key; 181 | elementValues[index] = value; 182 | return oldvalue; 183 | } 184 | 185 | /** 186 | * Removes the mapping with the specified key from this map. 187 | * 188 | * @param key the key of the mapping to remove. 189 | * @return the value of the removed mapping or {@code null} if no mapping 190 | * for the specified key was found. 191 | */ 192 | 193 | public T remove(final int key) { 194 | int index = ((key & 0x7FFFFFFF) % elementKeys.length); 195 | 196 | int entry = elementKeys[index]; 197 | if (key == entry) { 198 | final T oldvalue = elementValues[index]; 199 | elementKeys[index] = Integer.MIN_VALUE; 200 | elementValues[index] = null; 201 | elementCount--; 202 | return oldvalue; 203 | } 204 | return null; 205 | } 206 | 207 | /** 208 | * Returns the number of elements in this map. 209 | * 210 | * @return the number of elements in this map. 211 | */ 212 | 213 | public int size() { 214 | return elementCount; 215 | } 216 | 217 | public static void main(String[] args) throws Exception { 218 | FixedIntHashMap f = new FixedIntHashMap(1000000, Long.class); 219 | long ts, ts2; 220 | Random r = new Random(); 221 | ts = System.currentTimeMillis(); 222 | ts2 = ts; 223 | for (int i = 1; i < 1e3; i++) { 224 | f.put(i, (long) (i)); 225 | } 226 | for (int i = 1; i < 1e3; i++) { 227 | System.out.println(f.get(i)); 228 | } 229 | for (int i = 1; i < 1; i++) { 230 | f.put((int) (r.nextLong() & 0x7FFFFFFFL), r.nextLong() & 0x7FFFFFFFFFFFFFFFL); 231 | if (i % 10000 == 0) { 232 | System.out.println(i + "\t" + (System.currentTimeMillis() - ts2)); 233 | ts2 = System.currentTimeMillis(); 234 | } 235 | } 236 | System.out.println("INSERT: " + (System.currentTimeMillis() - ts)); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/structures/hash/IntHashMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | 16 | package org.javastack.kvstore.structures.hash; 17 | 18 | import java.util.ArrayDeque; 19 | import java.util.Arrays; 20 | import java.util.Iterator; 21 | import java.util.NoSuchElementException; 22 | 23 | import org.apache.log4j.Logger; 24 | import org.javastack.kvstore.utils.GenericFactory; 25 | import org.javastack.kvstore.utils.PrimeFinder; 26 | 27 | /** 28 | * Native Integer HashMap 29 | * This class is NOT Thread-Safe 30 | * 31 | * @param type of values 32 | * 33 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 34 | */ 35 | public class IntHashMap implements Iterable { 36 | private static final Logger log = Logger.getLogger(IntHashMap.class); 37 | 38 | private int elementCount; 39 | private IntEntry[] elementData; 40 | 41 | private final float loadFactor; 42 | private int threshold; 43 | private int defaultSize = 17; 44 | 45 | private GenericFactory factory; 46 | 47 | /** 48 | * Constructs a new {@code IntHashMap} instance with the specified capacity. 49 | * 50 | * @param capacity the initial capacity of this hash map. 51 | * @param type class for values 52 | */ 53 | public IntHashMap(final int capacity, final Class type) { 54 | factory = new GenericFactory(type); 55 | defaultSize = primeSize(capacity); 56 | if (capacity >= 0) { 57 | elementCount = 0; 58 | elementData = newElementArray(capacity == 0 ? 1 : capacity); 59 | loadFactor = 0.75f; // Default load factor of 0.75 60 | initCache(elementData.length); 61 | computeMaxSize(); 62 | } else { 63 | throw new IllegalArgumentException(); 64 | } 65 | } 66 | 67 | /** 68 | * Constructs a new {@code IntHashMap} instance with default capacity (17). 69 | * 70 | * @param type class for values 71 | */ 72 | public IntHashMap(final Class type) { 73 | this(17, type); 74 | } 75 | 76 | @SuppressWarnings("unchecked") 77 | private IntEntry[] newElementArray(int s) { 78 | return new IntEntry[s]; 79 | } 80 | 81 | /** 82 | * Removes all mappings from this hash map, leaving it empty. 83 | * 84 | * @see #isEmpty 85 | * @see #size 86 | */ 87 | public void clear() { 88 | clear(true); 89 | } 90 | 91 | /** 92 | * Clear the map 93 | * 94 | * @param shrink if true shrink the map to initial size 95 | */ 96 | @SuppressWarnings("unchecked") 97 | public void clear(final boolean shrink) { 98 | clearCache(); 99 | if (elementCount > 0) { 100 | elementCount = 0; 101 | } 102 | if (shrink && (elementData.length > 1024) && (elementData.length > defaultSize)) { 103 | elementData = new IntEntry[defaultSize]; 104 | } else { 105 | Arrays.fill(elementData, null); 106 | } 107 | computeMaxSize(); 108 | } 109 | 110 | private void computeMaxSize() { 111 | threshold = (int) (elementData.length * loadFactor); 112 | } 113 | 114 | /** 115 | * Returns the value of specified key. 116 | * 117 | * @param key the key. 118 | * @return the value of the mapping with the specified key, or {@code null} if no mapping for the 119 | * specified key is found. 120 | */ 121 | public V get(final int key) { 122 | final int index = (key & 0x7FFFFFFF) % elementData.length; 123 | 124 | IntEntry m = elementData[index]; 125 | while (m != null) { 126 | if (key == m.key) { 127 | return m.value; 128 | } 129 | m = m.nextInSlot; 130 | } 131 | return null; 132 | } 133 | 134 | /** 135 | * Returns whether this map is empty. 136 | * 137 | * @return {@code true} if this map has no elements, {@code false} otherwise. 138 | * @see #size() 139 | */ 140 | public boolean isEmpty() { 141 | return (elementCount == 0); 142 | } 143 | 144 | /** 145 | * Maps the specified key to the specified value. 146 | * 147 | * @param key the key. 148 | * @param value the value. 149 | * @return the value of any previous mapping with the specified key or {@code null} if there was no such 150 | * mapping. 151 | */ 152 | public V put(final int key, final V value) { 153 | int index = (key & 0x7FFFFFFF) % elementData.length; 154 | 155 | IntEntry entry = elementData[index]; 156 | while (entry != null && key != entry.key) { 157 | entry = entry.nextInSlot; 158 | } 159 | 160 | if (entry == null) { 161 | if (++elementCount > threshold) { 162 | rehash(); 163 | index = (key & 0x7FFFFFFF) % elementData.length; 164 | } 165 | entry = createHashedEntry(key, index); 166 | } 167 | 168 | V result = entry.value; 169 | entry.value = value; 170 | return result; 171 | } 172 | 173 | IntEntry createHashedEntry(final int key, final int index) { 174 | IntEntry entry = reuseAfterDelete(); 175 | if (entry == null) { 176 | entry = new IntEntry(key); 177 | } else { 178 | entry.key = key; 179 | entry.value = null; 180 | } 181 | 182 | entry.nextInSlot = elementData[index]; 183 | elementData[index] = entry; 184 | return entry; 185 | } 186 | 187 | void rehash(final int capacity) { 188 | final int length = primeSize(capacity == 0 ? 1 : capacity << 1); 189 | if (log.isDebugEnabled()) { 190 | log.debug(this.getClass().getName() + "::rehash() old=" + elementData.length + " new=" + length); 191 | } 192 | 193 | IntEntry[] newData = newElementArray(length); 194 | for (int i = 0; i < elementData.length; i++) { 195 | IntEntry entry = elementData[i]; 196 | while (entry != null) { 197 | int index = (entry.key & 0x7FFFFFFF) % length; 198 | IntEntry next = entry.nextInSlot; 199 | entry.nextInSlot = newData[index]; 200 | newData[index] = entry; 201 | entry = next; 202 | } 203 | } 204 | elementData = newData; 205 | computeMaxSize(); 206 | } 207 | 208 | void rehash() { 209 | rehash(elementData.length); 210 | } 211 | 212 | /** 213 | * Removes the mapping with the specified key from this map. 214 | * 215 | * @param key the key of the mapping to remove. 216 | * @return the value of the removed mapping or {@code null} if no mapping 217 | * for the specified key was found. 218 | */ 219 | public V remove(final int key) { 220 | IntEntry entry = removeEntry(key); 221 | if (entry == null) { 222 | return null; 223 | } 224 | V ret = entry.value; 225 | reuseAfterDelete(entry); 226 | 227 | return ret; 228 | } 229 | 230 | IntEntry removeEntry(final int key) { 231 | IntEntry last = null; 232 | 233 | final int index = (key & 0x7FFFFFFF) % elementData.length; 234 | IntEntry entry = elementData[index]; 235 | 236 | while (true) { 237 | if (entry == null) { 238 | return null; 239 | } 240 | 241 | if (key == entry.key) { 242 | if (last == null) { 243 | elementData[index] = entry.nextInSlot; 244 | } else { 245 | last.nextInSlot = entry.nextInSlot; 246 | } 247 | elementCount--; 248 | return entry; 249 | } 250 | 251 | last = entry; 252 | entry = entry.nextInSlot; 253 | } 254 | } 255 | 256 | /** 257 | * Returns the number of elements in this map. 258 | * 259 | * @return the number of elements in this map. 260 | */ 261 | public int size() { 262 | return elementCount; 263 | } 264 | 265 | // ========== Entry Cache 266 | 267 | private ArrayDeque> cache; 268 | 269 | private void initCache(final int size) { 270 | cache = new ArrayDeque>(size); 271 | } 272 | 273 | public void clearCache() { 274 | cache.clear(); 275 | } 276 | 277 | private IntEntry reuseAfterDelete() { 278 | return cache.pollLast(); 279 | } 280 | 281 | private void reuseAfterDelete(final IntEntry entry) { 282 | entry.clean(); 283 | cache.offerLast(entry); 284 | } 285 | 286 | // ========== Internal Entry 287 | 288 | static final class IntEntry { 289 | IntEntry nextInSlot; 290 | int key; 291 | V value; 292 | 293 | IntEntry(int theKey) { 294 | this.key = theKey; 295 | this.value = null; 296 | } 297 | 298 | void clean() { 299 | value = null; 300 | key = Integer.MIN_VALUE; 301 | nextInSlot = null; 302 | } 303 | } 304 | 305 | // ========== Prime Finder 306 | 307 | private static final int primeSize(final int capacity) { 308 | // return java.math.BigInteger.valueOf((long)capacity).nextProbablePrime().intValue(); 309 | return PrimeFinder.nextPrime(capacity); 310 | } 311 | 312 | // ========== Iterator 313 | 314 | /** 315 | * @returns iterator over values in map 316 | */ 317 | public Iterator iterator() { 318 | return new IntHashMapIterator(this); 319 | } 320 | 321 | static class IntHashMapIterator implements Iterator { 322 | private int position = 0; 323 | 324 | boolean canRemove = false; 325 | IntEntry entry; 326 | IntEntry lastEntry; 327 | final IntHashMap associatedMap; 328 | 329 | IntHashMapIterator(IntHashMap hm) { 330 | associatedMap = hm; 331 | } 332 | 333 | public boolean hasNext() { 334 | if (entry != null) { 335 | return true; 336 | } 337 | 338 | IntEntry[] elementData = associatedMap.elementData; 339 | int length = elementData.length; 340 | int newPosition = position; 341 | boolean result = false; 342 | 343 | while (newPosition < length) { 344 | if (elementData[newPosition] == null) { 345 | newPosition++; 346 | } else { 347 | result = true; 348 | break; 349 | } 350 | } 351 | 352 | position = newPosition; 353 | return result; 354 | } 355 | 356 | public V next() { 357 | if (!hasNext()) { 358 | throw new NoSuchElementException(); 359 | } 360 | 361 | IntEntry result; 362 | IntEntry _entry = entry; 363 | if (_entry == null) { 364 | result = lastEntry = associatedMap.elementData[position++]; 365 | entry = lastEntry.nextInSlot; 366 | } else { 367 | if (lastEntry.nextInSlot != _entry) { 368 | lastEntry = lastEntry.nextInSlot; 369 | } 370 | result = _entry; 371 | entry = _entry.nextInSlot; 372 | } 373 | canRemove = true; 374 | return result.value; 375 | } 376 | 377 | public void remove() { 378 | if (!canRemove) { 379 | throw new IllegalStateException(); 380 | } 381 | 382 | canRemove = false; 383 | 384 | if (lastEntry.nextInSlot == entry) { 385 | while (associatedMap.elementData[--position] == null) { 386 | // Skip 387 | } 388 | associatedMap.elementData[position] = associatedMap.elementData[position].nextInSlot; 389 | entry = null; 390 | } else { 391 | lastEntry.nextInSlot = entry; 392 | } 393 | if (lastEntry != null) { 394 | IntEntry reuse = lastEntry; 395 | lastEntry = null; 396 | associatedMap.reuseAfterDelete(reuse); 397 | } 398 | 399 | associatedMap.elementCount--; 400 | } 401 | } 402 | 403 | // ========================================= 404 | 405 | /** 406 | * Return an array with values in this map 407 | * 408 | * @return array with values 409 | */ 410 | public V[] getValues() { 411 | final V[] array = factory.newArray(elementCount); 412 | int i = 0; 413 | for (final V v : this) { 414 | array[i++] = v; 415 | } 416 | return array; 417 | } 418 | 419 | // ========================================= 420 | 421 | @SuppressWarnings("unused") 422 | public static void main(String[] args) { 423 | if (false) { 424 | long capacity = 1; 425 | int count = 1; 426 | while (capacity < Integer.MAX_VALUE) { 427 | capacity = java.math.BigInteger.valueOf(capacity).nextProbablePrime().longValue(); 428 | System.out.print(capacity + ", "); 429 | final double inc = Math.log(2) / Math.log(capacity << 5) * 10 + 1; 430 | // System.out.println(inc); 431 | capacity *= inc; 432 | if (count % 5 == 0) { 433 | System.out.println(); 434 | } 435 | count++; 436 | } 437 | System.out.println(Integer.MAX_VALUE); 438 | System.out.println("------"); 439 | 440 | System.out.println(count); 441 | System.out.println(PrimeFinder.nextPrime((int) 1e6)); 442 | } 443 | if (true) { 444 | IntHashMap hash = new IntHashMap(16, Integer.class); 445 | hash.put(1, 2); 446 | hash.put(2, 4); 447 | for (Integer i : hash.getValues()) { 448 | System.out.println(i); 449 | } 450 | } 451 | } 452 | } 453 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/structures/hash/IntLinkedHashMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.structures.hash; 16 | 17 | import java.util.ArrayDeque; 18 | import java.util.Arrays; 19 | import java.util.Iterator; 20 | import java.util.NoSuchElementException; 21 | 22 | import org.apache.log4j.Logger; 23 | import org.javastack.kvstore.utils.GenericFactory; 24 | import org.javastack.kvstore.utils.PrimeFinder; 25 | 26 | /** 27 | * Native Integer LinkedHashMap 28 | * This class is NOT Thread-Safe 29 | * 30 | * @param type of values 31 | * 32 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 33 | */ 34 | public class IntLinkedHashMap implements Iterable { 35 | private static final Logger log = Logger.getLogger(IntLinkedHashMap.class); 36 | 37 | private int elementCount; 38 | private IntLinkedEntry[] elementData; 39 | 40 | private final float loadFactor; 41 | private int threshold; 42 | private int defaultSize = 17; 43 | 44 | private GenericFactory factory; 45 | 46 | /** 47 | * The head of the doubly linked list. 48 | */ 49 | private transient IntLinkedEntry header; 50 | 51 | /** 52 | * The iteration ordering method for this linked hash map: true for access-order, false 53 | * for insertion-order. 54 | * 55 | * @serial 56 | */ 57 | private final boolean accessOrder; 58 | 59 | /** 60 | * Constructs a new {@code IntLinkedHashMap} instance with the specified capacity. 61 | * 62 | * @param capacity the initial capacity of this hash map. 63 | * @throws IllegalArgumentException when the capacity is less than zero. 64 | */ 65 | public IntLinkedHashMap(final Class type) { 66 | this(17, type, false); 67 | } 68 | 69 | public IntLinkedHashMap(final int capacity, final Class type) { 70 | this(capacity, type, false); 71 | } 72 | 73 | public IntLinkedHashMap(final int capacity, final Class type, final boolean accessOrder) { 74 | this.accessOrder = accessOrder; 75 | // 76 | factory = new GenericFactory(type); 77 | defaultSize = primeSize(capacity); 78 | if (capacity >= 0) { 79 | elementCount = 0; 80 | elementData = newElementArray(defaultSize); 81 | loadFactor = 0.75f; // Default load factor of 0.75 82 | initCache(elementData.length); 83 | computeMaxSize(); 84 | } else { 85 | throw new IllegalArgumentException(); 86 | } 87 | // Initializes the chain. 88 | initChain(); 89 | } 90 | 91 | @SuppressWarnings("unchecked") 92 | private IntLinkedEntry[] newElementArray(int s) { 93 | return new IntLinkedEntry[s]; 94 | } 95 | 96 | /** 97 | * Removes all mappings from this hash map, leaving it empty. 98 | * 99 | * @see #isEmpty 100 | * @see #size 101 | */ 102 | public void clear() { 103 | clear(true); 104 | } 105 | 106 | public void clear(final boolean shrink) { 107 | clearCache(); 108 | if (elementCount > 0) { 109 | elementCount = 0; 110 | } 111 | if (shrink && (elementData.length > 1024) && (elementData.length > defaultSize)) { 112 | elementData = newElementArray(defaultSize); 113 | } else { 114 | Arrays.fill(elementData, null); 115 | } 116 | computeMaxSize(); 117 | initChain(); 118 | } 119 | 120 | private void initChain() { 121 | // Initializes the chain. 122 | header = new IntLinkedEntry(-1); 123 | header.before = header.after = header; 124 | } 125 | 126 | private void computeMaxSize() { 127 | threshold = (int) (elementData.length * loadFactor); 128 | } 129 | 130 | /** 131 | * Returns the value of the mapping with the specified key. 132 | * 133 | * @param key the key. 134 | * @return the value of the mapping with the specified key, or {@code null} if no mapping for the 135 | * specified key is found. 136 | */ 137 | public V get(final int key) { 138 | final int index = (key & 0x7FFFFFFF) % elementData.length; 139 | 140 | IntLinkedEntry m = elementData[index]; 141 | while (m != null) { 142 | if (key == m.key) { 143 | if (accessOrder) { 144 | // if (log.isDebugEnabled()) log.debug("reliking " + this.key); 145 | m.remove(); 146 | m.addBefore(header); 147 | } 148 | return m.value; 149 | } 150 | m = m.nextInSlot; 151 | } 152 | return null; 153 | } 154 | 155 | /** 156 | * Returns whether this map is empty. 157 | * 158 | * @return {@code true} if this map has no elements, {@code false} otherwise. 159 | * @see #size() 160 | */ 161 | public boolean isEmpty() { 162 | return (elementCount == 0); 163 | } 164 | 165 | /** 166 | * Maps the specified key to the specified value. 167 | * 168 | * @param key the key. 169 | * @param value the value. 170 | * @return the value of any previous mapping with the specified key or {@code null} if there was no such 171 | * mapping. 172 | */ 173 | public V put(final int key, final V value) { 174 | int index = (key & 0x7FFFFFFF) % elementData.length; 175 | 176 | IntLinkedEntry entry = elementData[index]; 177 | while (entry != null && key != entry.key) { 178 | entry = entry.nextInSlot; 179 | } 180 | 181 | if (entry == null) { 182 | // Remove eldest entry if instructed, else grow capacity if appropriate 183 | IntLinkedEntry eldest = header.after; 184 | ++elementCount; 185 | if (removeEldestEntry(eldest)) { 186 | remove(eldest.key); 187 | } else { 188 | if (elementCount > threshold) { 189 | rehash(); 190 | index = (key & 0x7FFFFFFF) % elementData.length; 191 | } 192 | } 193 | entry = createHashedEntry(key, index); 194 | } 195 | 196 | V result = entry.value; 197 | entry.value = value; 198 | return result; 199 | } 200 | 201 | IntLinkedEntry createHashedEntry(final int key, final int index) { 202 | IntLinkedEntry entry = reuseAfterDelete(); 203 | if (entry == null) { 204 | entry = new IntLinkedEntry(key); 205 | } else { 206 | entry.key = key; 207 | entry.value = null; 208 | } 209 | 210 | entry.nextInSlot = elementData[index]; 211 | elementData[index] = entry; 212 | entry.addBefore(header); // LinkedList 213 | return entry; 214 | } 215 | 216 | void rehash(final int capacity) { 217 | final int length = primeSize(capacity == 0 ? 1 : capacity << 1); 218 | if (log.isDebugEnabled()) 219 | log.debug(this.getClass().getName() + "::rehash() old=" + elementData.length + " new=" + length); 220 | 221 | IntLinkedEntry[] newData = newElementArray(length); 222 | for (int i = 0; i < elementData.length; i++) { 223 | IntLinkedEntry entry = elementData[i]; 224 | while (entry != null) { 225 | int index = (entry.key & 0x7FFFFFFF) % length; 226 | IntLinkedEntry next = entry.nextInSlot; 227 | entry.nextInSlot = newData[index]; 228 | newData[index] = entry; 229 | entry = next; 230 | } 231 | } 232 | elementData = newData; 233 | computeMaxSize(); 234 | } 235 | 236 | void rehash() { 237 | rehash(elementData.length); 238 | } 239 | 240 | /** 241 | * Removes the mapping with the specified key from this map. 242 | * 243 | * @param key the key of the mapping to remove. 244 | * @return the value of the removed mapping or {@code null} if no mapping 245 | * for the specified key was found. 246 | */ 247 | public V remove(final int key) { 248 | IntLinkedEntry entry = removeEntry(key); 249 | if (entry == null) { 250 | return null; 251 | } 252 | V ret = entry.value; 253 | reuseAfterDelete(entry); 254 | 255 | return ret; 256 | } 257 | 258 | public V removeEldest() { 259 | final IntLinkedEntry eldest = header.after; 260 | V ret = eldest.value; 261 | remove(eldest.key); 262 | return ret; 263 | } 264 | 265 | IntLinkedEntry removeEntry(final int key) { 266 | IntLinkedEntry last = null; 267 | 268 | final int index = (key & 0x7FFFFFFF) % elementData.length; 269 | IntLinkedEntry entry = elementData[index]; 270 | 271 | while (true) { 272 | if (entry == null) { 273 | return null; 274 | } 275 | 276 | if (key == entry.key) { 277 | if (last == null) { 278 | elementData[index] = entry.nextInSlot; 279 | } else { 280 | last.nextInSlot = entry.nextInSlot; 281 | } 282 | --elementCount; 283 | entry.remove(); 284 | return entry; 285 | } 286 | 287 | last = entry; 288 | entry = entry.nextInSlot; 289 | } 290 | } 291 | 292 | /** 293 | * Returns the number of elements in this map. 294 | * 295 | * @return the number of elements in this map. 296 | */ 297 | public int size() { 298 | return elementCount; 299 | } 300 | 301 | // ========== Entry Cache 302 | 303 | private ArrayDeque> cache; 304 | 305 | private void initCache(final int size) { 306 | cache = new ArrayDeque>(size); 307 | } 308 | 309 | public void clearCache() { 310 | cache.clear(); 311 | } 312 | 313 | private IntLinkedEntry reuseAfterDelete() { 314 | final IntLinkedEntry reuse = cache.pollLast(); 315 | // if (reuse != null) { 316 | // if (log.isDebugEnabled()) { 317 | // log.debug("reusing IntLinkedEntry=" + reuse.hashCode() + " cacheSize=" + cache.size()); 318 | // } 319 | // } 320 | return reuse; 321 | } 322 | 323 | private void reuseAfterDelete(final IntLinkedEntry entry) { 324 | entry.clean(); 325 | cache.offerLast(entry); 326 | } 327 | 328 | // ========== Internal Entry 329 | 330 | protected static final class IntLinkedEntry { 331 | // These fields comprise the doubly linked list used for iteration. 332 | private IntLinkedEntry before, after; 333 | // 334 | private IntLinkedEntry nextInSlot; 335 | protected int key; 336 | protected V value; 337 | 338 | IntLinkedEntry(final int theKey) { 339 | this.key = theKey; 340 | this.value = null; 341 | } 342 | 343 | private void clean() { 344 | value = null; 345 | key = Integer.MIN_VALUE; 346 | nextInSlot = null; 347 | before = null; 348 | after = null; 349 | } 350 | 351 | /** 352 | * Removes this entry from the linked list. 353 | */ 354 | private void remove() { 355 | before.after = after; 356 | after.before = before; 357 | } 358 | 359 | /** 360 | * Inserts this entry before the specified existing entry in the list. 361 | */ 362 | private void addBefore(IntLinkedEntry existingEntry) { 363 | after = existingEntry; 364 | before = existingEntry.before; 365 | before.after = this; 366 | after.before = this; 367 | } 368 | 369 | /** 370 | * Returns the key corresponding to this entry. 371 | * 372 | * @return the key corresponding to this entry 373 | */ 374 | public int getKey() { 375 | return key; 376 | } 377 | 378 | /** 379 | * Returns the value corresponding to this entry. 380 | * 381 | * @return the value corresponding to this entry 382 | */ 383 | public V getValue() { 384 | return value; 385 | } 386 | } 387 | 388 | // ========== Linked List 389 | 390 | /** 391 | * Returns true if this map should remove its eldest entry. 392 | * This method is invoked by put and putAll after 393 | * inserting a new entry into the map. It provides the implementor 394 | * with the opportunity to remove the eldest entry each time a new one 395 | * is added. This is useful if the map represents a cache: it allows 396 | * the map to reduce memory consumption by deleting stale entries. 397 | * 398 | *

399 | * Sample use: this override will allow the map to grow up to 100 entries and then delete the eldest entry 400 | * each time a new entry is added, maintaining a steady state of 100 entries. 401 | * 402 | *

403 | 	 * private static final int MAX_ENTRIES = 100;
404 | 	 * 
405 | 	 * protected boolean removeEldestEntry(IntLinkedEntry eldest) {
406 | 	 * 	return size() > MAX_ENTRIES;
407 | 	 * }
408 | 	 * 
409 | * 410 | *

411 | * This method typically does not modify the map in any way, instead allowing the map to modify itself as 412 | * directed by its return value. It is permitted for this method to modify the map directly, but if 413 | * it does so, it must return false (indicating that the map should not attempt any 414 | * further modification). The effects of returning true after modifying the map from within this 415 | * method are unspecified. 416 | * 417 | *

418 | * This implementation merely returns false (so that this map acts like a normal map - the eldest 419 | * element is never removed). 420 | * 421 | * @param eldest The least recently inserted entry in the map, or if 422 | * this is an access-ordered map, the least recently accessed 423 | * entry. This is the entry that will be removed it this 424 | * method returns true. If the map was empty prior 425 | * to the put or putAll invocation resulting 426 | * in this invocation, this will be the entry that was just 427 | * inserted; in other words, if the map contains a single 428 | * entry, the eldest entry is also the newest. 429 | * @return true if the eldest entry should be removed 430 | * from the map; false if it should be retained. 431 | */ 432 | protected boolean removeEldestEntry(IntLinkedEntry eldest) { 433 | return false; 434 | } 435 | 436 | // ========== Prime Finder 437 | 438 | private static final int primeSize(final int capacity) { 439 | // return java.math.BigInteger.valueOf((long)capacity).nextProbablePrime().intValue(); 440 | return PrimeFinder.nextPrime(capacity); 441 | } 442 | 443 | // ========== Iterator 444 | 445 | /** 446 | * @returns iterator over values in map 447 | */ 448 | public Iterator iterator() { 449 | return new IntLinkedHashMapIterator(this); 450 | } 451 | 452 | static class IntLinkedHashMapIterator implements Iterator { 453 | final IntLinkedHashMap associatedMap; 454 | IntLinkedEntry nextEntry = null; 455 | IntLinkedEntry lastReturned = null; 456 | 457 | public IntLinkedHashMapIterator(final IntLinkedHashMap associatedMap) { 458 | this.associatedMap = associatedMap; 459 | nextEntry = associatedMap.header.after; 460 | } 461 | 462 | public boolean hasNext() { 463 | return nextEntry != associatedMap.header; 464 | } 465 | 466 | public void remove() { 467 | if (lastReturned == null) 468 | throw new IllegalStateException(); 469 | 470 | associatedMap.remove(lastReturned.key); 471 | lastReturned = null; 472 | } 473 | 474 | IntLinkedEntry nextEntry() { 475 | if (nextEntry == associatedMap.header) 476 | throw new NoSuchElementException(); 477 | 478 | IntLinkedEntry e = lastReturned = nextEntry; 479 | nextEntry = e.after; 480 | return e; 481 | } 482 | 483 | public V next() { 484 | return nextEntry().value; 485 | } 486 | } 487 | 488 | // ========================================= 489 | 490 | public V[] getValues() { 491 | final V[] array = factory.newArray(elementCount); 492 | int i = 0; 493 | for (final V v : this) { 494 | array[i++] = v; 495 | } 496 | return array; 497 | } 498 | 499 | // ========================================= 500 | 501 | public static void main(String[] args) { 502 | IntLinkedHashMap hash = new IntLinkedHashMap(16, Integer.class, true); 503 | for (int i = 1; i < 6; i++) { // 1...4 504 | hash.put(i, i); 505 | } 506 | hash.put(3, 3); 507 | hash.put(3, 3); 508 | hash.put(3, 3); 509 | hash.put(3, 3); 510 | hash.get(3); 511 | // hash.remove(3); 512 | for (Integer i : hash) { 513 | System.out.println(i); 514 | } 515 | System.out.println("---"); 516 | while (hash.size() > 0) { 517 | System.out.println("remove value=" + hash.removeEldest()); 518 | } 519 | System.out.println("---"); 520 | 521 | for (Integer i : hash) { 522 | System.out.println(i); 523 | } 524 | } 525 | } 526 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/structures/hash/WeakSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.structures.hash; 16 | 17 | import java.lang.ref.ReferenceQueue; 18 | import java.lang.ref.WeakReference; 19 | import java.util.Arrays; 20 | 21 | import org.javastack.kvstore.utils.PrimeFinder; 22 | 23 | /** 24 | * A hashtable-based Set implementation with weak values. An entry in a 25 | * WeakSet will automatically be removed when its value is no longer in 26 | * ordinary use. More precisely, the presence of a given value will 27 | * not prevent the value from being discarded by the garbage collector, that is, 28 | * made finalizable, finalized, and then reclaimed. When a value has been 29 | * discarded its entry is effectively removed from the set, so this class 30 | * behaves somewhat differently from other Set implementations. 31 | *

32 | * This class is NOT Thread-Safe 33 | * 34 | * @see java.util.WeakHashMap 35 | * 36 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 37 | */ 38 | public class WeakSet { 39 | 40 | private int elementCount; 41 | private Entry[] elementData; 42 | 43 | private final float loadFactor; 44 | private int threshold; 45 | private int defaultSize = 17; 46 | 47 | /** 48 | * Reference queue for cleared WeakEntry 49 | */ 50 | private final ReferenceQueue queue = new ReferenceQueue(); 51 | 52 | /** 53 | * Constructs a new {@code WeakSet} instance with the specified capacity. 54 | * 55 | * @param capacity the initial capacity of this set. 56 | * @param type class for values 57 | */ 58 | public WeakSet(final int capacity) { 59 | defaultSize = primeSize(capacity); 60 | if (capacity >= 0) { 61 | elementCount = 0; 62 | elementData = newElementArray(capacity < 0 ? 1 : capacity); 63 | loadFactor = 0.75f; // Default load factor of 0.75 64 | computeMaxSize(); 65 | } else { 66 | throw new IllegalArgumentException(); 67 | } 68 | } 69 | 70 | /** 71 | * Constructs a new {@code WeakSet} instance with default capacity (17). 72 | */ 73 | public WeakSet() { 74 | this(17); 75 | } 76 | 77 | /** 78 | * Check for equal objects 79 | * 80 | * @param o1 81 | * @param o2 82 | * @return true if equals 83 | */ 84 | private final static boolean eq(Object o1, Object o2) { 85 | return ((o1 == o2) || o1.equals(o2)); 86 | } 87 | 88 | /** 89 | * Removes all values from this WeakSet, leaving it empty. 90 | * 91 | * @see #isEmpty 92 | * @see #size 93 | */ 94 | public final void clear() { 95 | clear(true); 96 | } 97 | 98 | /** 99 | * Clear the set 100 | * 101 | * @param shrink if true, shrink the set to initial size 102 | */ 103 | public void clear(final boolean shrink) { 104 | while (queue.poll() != null) { 105 | ; 106 | } 107 | if (elementCount > 0) { 108 | elementCount = 0; 109 | } 110 | if (shrink && (elementData.length > 1024) && (elementData.length > defaultSize)) { 111 | elementData = newElementArray(defaultSize); 112 | } else { 113 | Arrays.fill(elementData, null); 114 | } 115 | computeMaxSize(); 116 | while (queue.poll() != null) { 117 | ; 118 | } 119 | } 120 | 121 | /** 122 | * Returns the specified value. 123 | * 124 | * @param value the value. 125 | * @return the value, or {@code null} if not found the specified value 126 | */ 127 | public T get(final T value) { 128 | expungeStaleEntries(); 129 | // 130 | final int index = (value.hashCode() & 0x7FFFFFFF) % elementData.length; 131 | Entry m = elementData[index]; 132 | while (m != null) { 133 | if (eq(value, m.get())) { 134 | return m.get(); 135 | } 136 | m = m.nextInSlot; 137 | } 138 | return null; 139 | } 140 | 141 | /** 142 | * Returns whether this set is empty. 143 | * 144 | * @return {@code true} if this set has no elements, {@code false} otherwise. 145 | * @see #size() 146 | */ 147 | public final boolean isEmpty() { 148 | return (size() == 0); 149 | } 150 | 151 | /** 152 | * Puts the specified value in the set. 153 | * 154 | * @param value 155 | * the value. 156 | * @return the value of any previous put or {@code null} if there was no such value. 157 | */ 158 | public T put(final T value) { 159 | expungeStaleEntries(); 160 | // 161 | final int hash = value.hashCode(); 162 | int index = (hash & 0x7FFFFFFF) % elementData.length; 163 | Entry entry = elementData[index]; 164 | while (entry != null && !eq(value, entry.get())) { 165 | entry = entry.nextInSlot; 166 | } 167 | 168 | if (entry == null) { 169 | if (++elementCount > threshold) { 170 | expandElementArray(elementData.length); 171 | index = (hash & 0x7FFFFFFF) % elementData.length; 172 | } 173 | entry = createHashedEntry(value, index); 174 | return null; 175 | } 176 | 177 | final T result = entry.get(); 178 | return result; 179 | } 180 | 181 | private final Entry createHashedEntry(final T valye, final int index) { 182 | Entry entry = new Entry(valye, queue); 183 | entry.nextInSlot = elementData[index]; 184 | elementData[index] = entry; 185 | return entry; 186 | } 187 | 188 | private final void computeMaxSize() { 189 | threshold = (int) (elementData.length * loadFactor); 190 | } 191 | 192 | @SuppressWarnings("unchecked") 193 | private final Entry[] newElementArray(int s) { 194 | return new Entry[s]; 195 | } 196 | 197 | private final void expandElementArray(final int capacity) { 198 | final int length = primeSize(capacity < 0 ? 1 : capacity << 1); 199 | final Entry[] newData = newElementArray(length); 200 | for (int i = 0; i < elementData.length; i++) { 201 | Entry entry = elementData[i]; 202 | elementData[i] = null; 203 | while (entry != null) { 204 | final Entry next = entry.nextInSlot; 205 | final T value = entry.get(); 206 | if (value == null) { 207 | entry.nextInSlot = null; 208 | elementCount--; 209 | } else { 210 | final int index = (entry.hash & 0x7FFFFFFF) % length; 211 | entry.nextInSlot = newData[index]; 212 | newData[index] = entry; 213 | } 214 | entry = next; 215 | } 216 | } 217 | elementData = newData; 218 | computeMaxSize(); 219 | } 220 | 221 | @SuppressWarnings("unchecked") 222 | private final void expungeStaleEntries() { 223 | Entry entry; 224 | while ((entry = (Entry) queue.poll()) != null) { 225 | final int i = (entry.hash & 0x7FFFFFFF) % elementData.length; 226 | 227 | Entry prev = elementData[i]; 228 | Entry p = prev; 229 | while (p != null) { 230 | Entry next = p.nextInSlot; 231 | if (p == entry) { 232 | if (prev == entry) { 233 | elementData[i] = next; 234 | } else { 235 | prev.nextInSlot = next; 236 | } 237 | entry.nextInSlot = null; 238 | elementCount--; 239 | break; 240 | } 241 | prev = p; 242 | p = next; 243 | } 244 | } 245 | } 246 | 247 | /** 248 | * Removes the specified value from this set. 249 | * 250 | * @param value the value to remove. 251 | * @return the value removed or {@code null} if not found 252 | */ 253 | public T remove(final T value) { 254 | expungeStaleEntries(); 255 | // 256 | final Entry entry = removeEntry(value); 257 | if (entry == null) { 258 | return null; 259 | } 260 | final T ret = entry.get(); 261 | return ret; 262 | } 263 | 264 | private final Entry removeEntry(final T value) { 265 | Entry last = null; 266 | 267 | final int index = (value.hashCode() & 0x7FFFFFFF) % elementData.length; 268 | Entry entry = elementData[index]; 269 | 270 | while (true) { 271 | if (entry == null) { 272 | return null; 273 | } 274 | 275 | if (eq(value, entry.get())) { 276 | if (last == null) { 277 | elementData[index] = entry.nextInSlot; 278 | } else { 279 | last.nextInSlot = entry.nextInSlot; 280 | } 281 | elementCount--; 282 | return entry; 283 | } 284 | 285 | last = entry; 286 | entry = entry.nextInSlot; 287 | } 288 | } 289 | 290 | /** 291 | * Returns the number of elements in this set. 292 | * 293 | * @return the number of elements in this set. 294 | */ 295 | public int size() { 296 | if (elementCount == 0) { 297 | return 0; 298 | } 299 | expungeStaleEntries(); 300 | return elementCount; 301 | } 302 | 303 | // ========== Internal Entry 304 | 305 | private static final class Entry extends WeakReference { 306 | private final int hash; 307 | private Entry nextInSlot; 308 | 309 | private Entry(final T value, ReferenceQueue queue) { 310 | super(value, queue); 311 | hash = (value.hashCode() & 0x7FFFFFFF); 312 | } 313 | } 314 | 315 | // ========== Prime Finder 316 | 317 | private static final int primeSize(final int capacity) { 318 | return PrimeFinder.nextPrime(capacity); 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/structures/set/SortedIntArraySet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.structures.set; 16 | 17 | import java.util.Arrays; 18 | 19 | import org.apache.log4j.Logger; 20 | 21 | /** 22 | * Native Int SortedArray 23 | * This class is NOT Thread-Safe 24 | * 25 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 26 | */ 27 | public class SortedIntArraySet { 28 | public static final int NULL_VALUE = Integer.MIN_VALUE; 29 | private static final Logger log = Logger.getLogger(SortedIntArraySet.class); 30 | 31 | public int[] keys; 32 | public int allocated = 0; 33 | 34 | /** 35 | * Create with initial size 36 | * 37 | * @param size 38 | */ 39 | public SortedIntArraySet(final int size) { 40 | allocArray(size); 41 | } 42 | 43 | /** 44 | * Alloc array 45 | * 46 | * @param size 47 | */ 48 | private final void allocArray(final int size) { 49 | keys = new int[size]; 50 | } 51 | 52 | /** 53 | * Resize array 54 | */ 55 | private final void resizeArray() { 56 | if (log.isDebugEnabled()) { 57 | log.debug("resizeArray size=" + keys.length + " newsize=" + (keys.length << 1)); 58 | } 59 | final int[] newkeys = new int[keys.length << 1]; // double space 60 | System.arraycopy(keys, 0, newkeys, 0, allocated); 61 | keys = newkeys; 62 | } 63 | 64 | /** 65 | * Find slot by key 66 | * 67 | * @param searchKey 68 | * @return 69 | */ 70 | private final int findSlotByKey(final int searchKey) { 71 | return Arrays.binarySearch(keys, 0, allocated, searchKey); 72 | } 73 | 74 | /** 75 | * Is empty? 76 | * 77 | * @return 78 | */ 79 | public boolean isEmpty() { // empty 80 | return (allocated <= 0); 81 | } 82 | 83 | /** 84 | * Is full? 85 | * 86 | * @return 87 | */ 88 | private final boolean isFull() { // full 89 | if (log.isDebugEnabled()) { 90 | log.debug("allocated=" + allocated + " keys.length=" + keys.length); 91 | } 92 | return (allocated >= keys.length); 93 | } 94 | 95 | /** 96 | * Clear all elements 97 | */ 98 | public void clear() { 99 | Arrays.fill(keys, NULL_VALUE); 100 | allocated = 0; 101 | } 102 | 103 | /** 104 | * insert element 105 | */ 106 | private final void moveElementsRight(final int[] elements, final int srcPos) { 107 | if (log.isDebugEnabled()) { 108 | log.debug("moveElementsRight(" + srcPos + ") allocated=" + allocated + ":" + keys.length + ":" 109 | + (allocated - srcPos) + ":" + (keys.length - srcPos - 1)); 110 | } 111 | System.arraycopy(elements, srcPos, elements, srcPos + 1, (allocated - srcPos)); 112 | } 113 | 114 | /** 115 | * remove element 116 | */ 117 | private final void moveElementsLeft(final int[] elements, final int srcPos) { 118 | if (log.isDebugEnabled()) { 119 | log.debug("moveElementsLeft(" + srcPos + ") allocated=" + allocated + ":" + keys.length + ":" 120 | + (allocated - srcPos - 1) + ":" + (keys.length - srcPos - 1)); 121 | } 122 | System.arraycopy(elements, srcPos + 1, elements, srcPos, (allocated - srcPos - 1)); 123 | } 124 | 125 | /** 126 | * remove slot 127 | * 128 | * @param slot 129 | * @return 130 | */ 131 | private final boolean removeSlot(final int slot) { 132 | if (slot < 0) { 133 | log.error("faking slot=" + slot + " allocated=" + allocated); 134 | return false; 135 | } 136 | if (slot < allocated) { 137 | moveElementsLeft(keys, slot); 138 | } 139 | if (allocated > 0) { 140 | allocated--; 141 | } 142 | if (log.isDebugEnabled()) { 143 | log.debug("erased up key=" + keys[allocated]); 144 | } 145 | keys[allocated] = NULL_VALUE; 146 | return true; 147 | } 148 | 149 | /** 150 | * remove key 151 | * 152 | * @param key 153 | * @return 154 | */ 155 | public boolean remove(final int key) { 156 | int slot = findSlotByKey(key); 157 | if (slot >= 0) { 158 | return removeSlot(slot); 159 | } 160 | return false; 161 | } 162 | 163 | /** 164 | * put key 165 | * 166 | * @param key 167 | * @return 168 | */ 169 | public boolean put(final int key) { 170 | if (isFull()) { // full 171 | resizeArray(); 172 | } 173 | int slot = findSlotByKey(key); 174 | if (slot >= 0) { 175 | if (log.isDebugEnabled()) { 176 | log.debug("key already exists: " + key); 177 | } 178 | return false; // key already exist 179 | } 180 | slot = ((-slot) - 1); 181 | return addSlot(slot, key); 182 | } 183 | 184 | /** 185 | * add slot 186 | * 187 | * @param slot 188 | * @param key 189 | * @return 190 | */ 191 | private final boolean addSlot(final int slot, final int key) { 192 | if (slot < allocated) { 193 | moveElementsRight(keys, slot); 194 | } 195 | allocated++; 196 | keys[slot] = key; 197 | return true; 198 | } 199 | 200 | /** 201 | * Returns the first (lowest) element currently in this set. 202 | */ 203 | public int first() { 204 | return keys[0]; 205 | } 206 | 207 | /** 208 | * Returns the last (highest) element currently in this set. 209 | */ 210 | public int last() { 211 | if (allocated == 0) { 212 | return NULL_VALUE; 213 | } 214 | return keys[allocated - 1]; 215 | } 216 | 217 | /** 218 | * Returns the greatest element in this set less than or equal to the given element, or NULL_VALUE if 219 | * there is no such element. 220 | * 221 | * @param key 222 | */ 223 | public int floor(final int key) { 224 | return getRoundKey(key, false, true); 225 | } 226 | 227 | /** 228 | * Returns the least element in this set greater than or equal to the given element, or NULL_VALUE if 229 | * there is no such element. 230 | * 231 | * @param key 232 | */ 233 | public int ceiling(final int key) { 234 | return getRoundKey(key, true, true); 235 | } 236 | 237 | /** 238 | * Returns the greatest element in this set strictly less than the given element, or NULL_VALUE if there 239 | * is no such element. 240 | * 241 | * @param key 242 | */ 243 | public int lower(final int key) { 244 | return getRoundKey(key, false, false); 245 | } 246 | 247 | /** 248 | * Returns the least element in this set strictly greater than the given element, or NULL_VALUE if there 249 | * is no such element. 250 | * 251 | * @param key 252 | */ 253 | public int higher(final int key) { 254 | return getRoundKey(key, true, false); 255 | } 256 | 257 | /** 258 | * find key 259 | * 260 | * @param key 261 | * @param upORdown 262 | * @param acceptEqual 263 | * @return 264 | */ 265 | private final int getRoundKey(final int key, final boolean upORdown, final boolean acceptEqual) { 266 | if (isEmpty()) 267 | return NULL_VALUE; 268 | int slot = findSlotByKey(key); 269 | if (upORdown) { 270 | // ceiling / higher 271 | slot = ((slot < 0) ? (-slot) - 1 : (acceptEqual ? slot : slot + 1)); 272 | if (slot >= allocated) { 273 | return NULL_VALUE; 274 | } 275 | } else { 276 | // floor / lower 277 | slot = ((slot < 0) ? (-slot) - 2 : (acceptEqual ? slot : slot - 1)); 278 | if (slot < 0) { 279 | return NULL_VALUE; 280 | } 281 | } 282 | return keys[slot]; 283 | } 284 | 285 | /** 286 | * Returns true if this set contains the specified element. 287 | * 288 | * @param key 289 | * @return 290 | */ 291 | public boolean contains(final int key) { 292 | int slot = findSlotByKey(key); 293 | return (slot >= 0); 294 | } 295 | 296 | /** 297 | * Returns the element at the specified position in his internal array. 298 | * 299 | * @param slot 300 | * @return 301 | */ 302 | public int get(final int slot) { 303 | if ((slot < 0) || (slot >= allocated)) { 304 | return NULL_VALUE; 305 | } 306 | return keys[slot]; 307 | } 308 | 309 | /** 310 | * Returns the number of elements in this set (its cardinality). 311 | * 312 | * @return 313 | */ 314 | public int size() { 315 | return allocated; 316 | } 317 | 318 | @Override 319 | public String toString() { 320 | final StringBuilder sb = new StringBuilder(); 321 | sb.append("["); 322 | for (int i = 0; i < allocated; i++) { 323 | final int k = keys[i]; 324 | sb.append(k).append("|"); 325 | } 326 | if (allocated > 0) { 327 | sb.setLength(sb.length() - 1); 328 | } 329 | sb.append("]"); 330 | return sb.toString(); 331 | } 332 | 333 | public IntIterator iterator() { 334 | return new IntIterator(this); 335 | } 336 | 337 | static class IntIterator { 338 | final SortedIntArraySet associatedSet; 339 | int nextEntry = 0; 340 | int lastReturned = -1; 341 | 342 | public IntIterator(final SortedIntArraySet associatedSet) { 343 | this.associatedSet = associatedSet; 344 | nextEntry = 0; 345 | } 346 | 347 | public boolean hasNext() { 348 | return (nextEntry < associatedSet.allocated); 349 | } 350 | 351 | public void remove() { 352 | if (lastReturned == -1) { 353 | throw new IllegalStateException(); 354 | } 355 | 356 | associatedSet.removeSlot(lastReturned); 357 | lastReturned = -1; 358 | } 359 | 360 | public int next() { 361 | lastReturned = nextEntry; 362 | return associatedSet.keys[nextEntry++]; 363 | } 364 | } 365 | 366 | /** 367 | * Test 368 | * 369 | * @param args 370 | */ 371 | public static void main(final String[] args) { 372 | SortedIntArraySet s = new SortedIntArraySet(3); 373 | s.put(90); 374 | s.put(10); 375 | s.put(20); 376 | s.put(30); 377 | System.out.println("toString()=" + s.toString()); 378 | s.remove(10); 379 | s.put(40); 380 | System.out.println("toString()=" + s.toString()); 381 | System.out.println("first=" + s.first()); 382 | System.out.println("last()=" + s.last()); 383 | System.out.println("floor(15)=" + s.floor(15)); 384 | System.out.println("ceiling(15)=" + s.ceiling(15)); 385 | System.out.println("lower(15)=" + s.lower(15)); 386 | System.out.println("higher(15)=" + s.higher(15)); 387 | System.out.println("floor(20)=" + s.floor(20)); 388 | System.out.println("ceiling(20)=" + s.ceiling(20)); 389 | System.out.println("lower(20)=" + s.lower(20)); 390 | System.out.println("higher(20)=" + s.higher(20)); 391 | System.out.println("floor(0)=" + s.floor(0)); 392 | System.out.println("ceiling(0)=" + s.ceiling(0)); 393 | System.out.println("lower(0)=" + s.lower(0)); 394 | System.out.println("higher(0)=" + s.higher(0)); 395 | System.out.println("floor(9999)=" + s.floor(9999)); 396 | System.out.println("ceiling(9999)=" + s.ceiling(9999)); 397 | System.out.println("lower(9999)=" + s.lower(9999)); 398 | System.out.println("higher(9999)=" + s.higher(9999)); 399 | System.out.println("contains(20)=" + s.contains(20)); 400 | System.out.println("contains(9999)=" + s.contains(9999)); 401 | System.out.println("size()=" + s.size()); 402 | System.out.println("-------- iter begin"); 403 | IntIterator iter = s.iterator(); 404 | while (iter.hasNext()) { 405 | System.out.println(iter.next()); 406 | } 407 | System.out.println("-------- iter end"); 408 | System.out.println("-------- for begin"); 409 | for (int i = -1; i <= s.size(); i++) { 410 | System.out.println(s.get(i)); 411 | } 412 | System.out.println("-------- for end"); 413 | s.clear(); 414 | System.out.println("first=" + s.first()); 415 | System.out.println("last()=" + s.last()); 416 | } 417 | } 418 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/structures/stack/IntStack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.structures.stack; 16 | 17 | import java.util.Arrays; 18 | 19 | /** 20 | * Native Int Stack 21 | * This class is NOT Thread-Safe 22 | * 23 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 24 | */ 25 | public class IntStack { 26 | private static final int DEFAULT_NULL = -1; 27 | private final int null_value; 28 | private int stackPointer; 29 | private int[] stack; 30 | 31 | /** 32 | * Instantiate Native Int Stack of specified size and default null value (-1) 33 | * 34 | * @param size size of stack 35 | */ 36 | public IntStack(final int size) { 37 | this(size, DEFAULT_NULL); 38 | } 39 | 40 | /** 41 | * Instantiate Native Int Stack of specified size and custom null value 42 | * 43 | * @param size size of stack 44 | * @param null_value to return if stack is empty 45 | */ 46 | public IntStack(final int size, final int null_value) { 47 | this.stack = new int[size]; 48 | this.null_value = null_value; 49 | } 50 | 51 | // Resize Stack 52 | private final void growToHold(final int length) { 53 | if (length > stack.length) { 54 | final int[] newStack = new int[Math.max(length, stack.length * 2)]; 55 | System.arraycopy(stack, 0, newStack, 0, stack.length); 56 | stack = newStack; 57 | } 58 | } 59 | 60 | /** 61 | * Removes all of the elements from this stack. The stack will be empty after this call returns. 62 | */ 63 | public void clear() { 64 | if (isEmpty()) { 65 | return; 66 | } 67 | stackPointer = 0; 68 | Arrays.fill(stack, null_value); 69 | } 70 | 71 | /** 72 | * Returns true if this stack contains no elements. 73 | * 74 | * @return true if this stack contains no elements. 75 | */ 76 | public boolean isEmpty() { 77 | return (stackPointer == 0); 78 | } 79 | 80 | /** 81 | * Push value on top of stack 82 | * 83 | * @param value to push 84 | */ 85 | public final void push(final int value) { 86 | growToHold(stackPointer + 1); 87 | stack[stackPointer++] = value; 88 | } 89 | 90 | /** 91 | * Pop value from top of stack 92 | * 93 | * @return int of value 94 | */ 95 | public final int pop() { 96 | if (stackPointer == 0) { 97 | return null_value; 98 | } 99 | final int element = stack[--stackPointer]; 100 | stack[stackPointer] = null_value; 101 | return element; 102 | } 103 | 104 | /** 105 | * Return the number of elements in stack 106 | * 107 | * @return int with number of elements 108 | */ 109 | public final int size() { 110 | return stackPointer; 111 | } 112 | 113 | public static void main(String[] args) { 114 | IntStack stack = new IntStack(16, -1); 115 | System.out.println(stack.pop()); 116 | stack.push(12); 117 | stack.push(99); 118 | stack.push(13); 119 | stack.push(98); 120 | System.out.println(stack.pop()); 121 | System.out.println(stack.pop()); 122 | System.out.println(stack.pop()); 123 | System.out.println(stack.pop()); 124 | System.out.println(stack.pop()); 125 | } 126 | } -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/structures/stack/ObjectStack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.structures.stack; 16 | 17 | import java.util.ArrayDeque; 18 | 19 | /** 20 | * Basic Stack using ArrayDeque 21 | * This class is NOT Thread-Safe 22 | * 23 | * @param 24 | * 25 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 26 | */ 27 | public final class ObjectStack { 28 | private ArrayDeque deque; 29 | 30 | /** 31 | * Constructs an empty stack with an initial capacity sufficient to 32 | * hold 8 elements. 33 | */ 34 | public ObjectStack() { 35 | this(8); 36 | } 37 | 38 | /** 39 | * Constructs an empty stack with an initial capacity sufficient to 40 | * hold the specified number of elements. 41 | * 42 | * @param size lower bound on initial capacity of the stack 43 | */ 44 | public ObjectStack(final int size) { 45 | deque = new ArrayDeque(size); 46 | } 47 | 48 | /** 49 | * Removes all of the elements from this stack. 50 | * The stack will be empty after this call returns. 51 | */ 52 | public final void clear() { 53 | if (deque.isEmpty()) { 54 | return; 55 | } 56 | deque.clear(); 57 | } 58 | 59 | /** 60 | * Returns true if this stack contains no elements. 61 | * 62 | * @return true if this stack contains no elements. 63 | */ 64 | public final boolean isEmpty() { 65 | return deque.isEmpty(); 66 | } 67 | 68 | /** 69 | * Retrieves and removes the last element of this stack, 70 | * or returns null if this stack is empty. 71 | * 72 | * @return the tail of this stack, or null if this stack is empty 73 | */ 74 | public final T pop() { 75 | return deque.pollLast(); 76 | } 77 | 78 | /** 79 | * Inserts the specified element at the end of this stack. 80 | * 81 | * @param e the element to add 82 | */ 83 | public final void push(final T e) { 84 | deque.offerLast(e); 85 | } 86 | 87 | /** 88 | * Return the number of elements in stack 89 | * 90 | * @return int with number of elements 91 | */ 92 | public final int size() { 93 | return deque.size(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/test/BenchMarkDiskStore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.test; 16 | 17 | import java.nio.ByteBuffer; 18 | 19 | import org.javastack.kvstore.io.FileBlockStore; 20 | import org.javastack.kvstore.io.FileStreamStore; 21 | import org.javastack.kvstore.io.StringSerializer; 22 | import org.javastack.kvstore.io.FileBlockStore.WriteBuffer; 23 | 24 | /** 25 | * Code for benchmark 26 | * 27 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 28 | */ 29 | public class BenchMarkDiskStore { 30 | private static final int TOTAL = (int) 1e6, TRACE_LEN = 100000; 31 | private static final String TEST_STREAM_FILE = "/tmp/data/stream"; 32 | private static final String TEST_BLOCK_FILE = "/tmp/data/block"; 33 | 34 | public long long1; 35 | public long long2; 36 | public long long3; 37 | public int int1; 38 | public int int2; 39 | public String str1; 40 | public String str2; 41 | public String str3; 42 | public String str4; 43 | 44 | public BenchMarkDiskStore() { 45 | } 46 | 47 | public BenchMarkDiskStore(final long long1, final long long2, final long long3, final int int1, 48 | final int int2, final String str1, final String str2, final String str3, final String str4) { 49 | this.long1 = long1; 50 | this.long2 = long2; 51 | this.long3 = long3; 52 | this.int1 = int1; 53 | this.int2 = int2; 54 | this.str1 = str1; 55 | this.str2 = str2; 56 | this.str3 = str3; 57 | this.str4 = str4; 58 | } 59 | 60 | // ByteBuffer 61 | public void serialize(final ByteBuffer out) { 62 | out.clear(); 63 | out.putLong(long1); 64 | out.putLong(long2); 65 | out.putLong(long3); 66 | out.putInt(int1); 67 | out.putInt(int2); 68 | StringSerializer.fromStringToBuffer(out, str1); 69 | StringSerializer.fromStringToBuffer(out, str2); 70 | StringSerializer.fromStringToBuffer(out, str3); 71 | StringSerializer.fromStringToBuffer(out, str4); 72 | } 73 | 74 | public void deserialize(final ByteBuffer in) { 75 | long1 = in.getLong(); 76 | long2 = in.getLong(); 77 | long3 = in.getLong(); 78 | int1 = in.getInt(); 79 | int2 = in.getInt(); 80 | str1 = StringSerializer.fromBufferToString(in); 81 | str2 = StringSerializer.fromBufferToString(in); 82 | str3 = StringSerializer.fromBufferToString(in); 83 | str4 = StringSerializer.fromBufferToString(in); 84 | } 85 | 86 | private static int c = 0; 87 | 88 | private final static BenchMarkDiskStore newData() { 89 | final String s1 = "S1.123456789.", s2 = "S2.123456789.", s3 = "S3.123456789.123456789."; 90 | final String s4 = "S4.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456"; 91 | c++; 92 | return new BenchMarkDiskStore(c + 1, c + 2, c + 3, c + 11, c + 12, s1, s2, s3, s4); 93 | } 94 | 95 | public static void doTest_FileStreamStore_Bench_WriteRead() throws Exception { 96 | final int BUFFER_LEN = 0x10000; 97 | final FileStreamStore fss = new FileStreamStore(TEST_STREAM_FILE, BUFFER_LEN); 98 | final ByteBuffer buf = ByteBuffer.allocate(BUFFER_LEN); 99 | final long[] offset = new long[TOTAL]; 100 | long ts, ts2; 101 | long newOffset; 102 | // 103 | fss.delete(); 104 | fss.open(); 105 | // Parameters 106 | fss.setFlushOnWrite(false); 107 | fss.setSyncOnFlush(false); 108 | fss.setAlignBlocks(true); 109 | // puts 110 | ts = System.currentTimeMillis(); 111 | ts2 = ts; 112 | for (int i = 0; i < TOTAL; i++) { 113 | buf.clear(); 114 | newData().serialize(buf); 115 | buf.flip(); 116 | offset[i] = fss.write(buf); 117 | if (((i + 1) % TRACE_LEN) == 0) { 118 | System.out.println("offset[" + i + "]=" + offset[i] + "\t" 119 | + (System.currentTimeMillis() - ts2) + "ms\t" 120 | + (TRACE_LEN / Math.max((System.currentTimeMillis() - ts2), 1)) + "k/s"); 121 | ts2 = System.currentTimeMillis(); 122 | } 123 | } 124 | System.out.println("registry length=" + buf.limit()); 125 | // 126 | fss.sync(); 127 | System.out.println("WRITE: " + (System.currentTimeMillis() - ts) + "\t" 128 | + (TOTAL / Math.max((System.currentTimeMillis() - ts), 1)) + "k/s"); 129 | // 130 | // gets 131 | final BenchMarkDiskStore bag = new BenchMarkDiskStore(); 132 | ts = System.currentTimeMillis(); 133 | ts2 = ts; 134 | newOffset = 0; 135 | for (int j = 0; j < offset.length; j++) { 136 | final long i = newOffset; 137 | buf.clear(); 138 | newOffset = fss.read(newOffset, buf); 139 | if (newOffset < 0) { 140 | System.out.println("Error trying read offset " + i + " size=" + fss.size()); 141 | break; 142 | } 143 | bag.deserialize(buf); 144 | if (((j + 1) % TRACE_LEN) == 0) { 145 | System.out.println("offset=[" + i + "] newOffset=[" + newOffset + "]" + "\t" 146 | + (System.currentTimeMillis() - ts2) + "ms\t" 147 | + (TRACE_LEN / Math.max((System.currentTimeMillis() - ts2), 1)) + "k/s"); 148 | ts2 = System.currentTimeMillis(); 149 | } 150 | } 151 | System.out.println("READ: " + (System.currentTimeMillis() - ts) + "\t" 152 | + (TOTAL / Math.max((System.currentTimeMillis() - ts), 1)) + "k/s"); 153 | // 154 | fss.close(); 155 | fss.delete(); 156 | } 157 | 158 | public static void doTest_FileBlockStore_Bench_WriteRead() throws Exception { 159 | final int BLOCK_SIZE = 512; 160 | final FileBlockStore fbs = new FileBlockStore(TEST_BLOCK_FILE, BLOCK_SIZE, false); 161 | long ts, ts2; 162 | // 163 | fbs.delete(); 164 | // fbs.enableMmap(); // Test MMAPED? 165 | fbs.open(); 166 | // puts 167 | ts = System.currentTimeMillis(); 168 | ts2 = ts; 169 | for (int i = 0; i < (TOTAL / 2); i++) { 170 | final WriteBuffer wbuf = fbs.set(i); 171 | final ByteBuffer buf = wbuf.buf(); 172 | newData().serialize(buf); 173 | newData().serialize(buf); 174 | buf.flip(); 175 | wbuf.save(); 176 | if (((i + 1) % TRACE_LEN) == 0) { 177 | System.out.println("block[" + i + "]" + "\t" + (System.currentTimeMillis() - ts2) + "ms\t" 178 | + (TRACE_LEN / Math.max((System.currentTimeMillis() - ts2), 1)) + "k/s"); 179 | ts2 = System.currentTimeMillis(); 180 | } 181 | } 182 | // 183 | fbs.sync(); 184 | System.out.println("WRITE: " + (System.currentTimeMillis() - ts) + "\t" 185 | + (TOTAL / Math.max((System.currentTimeMillis() - ts), 1)) + "k/s"); 186 | // 187 | // gets 188 | final BenchMarkDiskStore bag = new BenchMarkDiskStore(); 189 | ts = System.currentTimeMillis(); 190 | ts2 = ts; 191 | for (int j = 0; j < TOTAL; j++) { 192 | final ByteBuffer buf = fbs.get(j); 193 | if (buf == null) { 194 | System.out.println("Error trying read block " + j + " blocks=" + fbs.sizeInBlocks()); 195 | break; 196 | } 197 | bag.deserialize(buf); 198 | bag.deserialize(buf); 199 | if (((j + 1) % TRACE_LEN) == 0) { 200 | System.out.println("block=[" + j + "]" + "\t" + (System.currentTimeMillis() - ts2) + "ms\t" 201 | + (TRACE_LEN / Math.max((System.currentTimeMillis() - ts2), 1)) + "k/s"); 202 | ts2 = System.currentTimeMillis(); 203 | } 204 | } 205 | System.out.println("READ: " + (System.currentTimeMillis() - ts) + "\t" 206 | + (TOTAL / Math.max((System.currentTimeMillis() - ts), 1)) + "k/s"); 207 | // 208 | fbs.close(); 209 | fbs.delete(); 210 | } 211 | 212 | public static void main(final String[] args) throws Exception { 213 | System.out.println("------- BEGIN TEST -------"); 214 | System.out.println("------- Stream -------"); 215 | doTest_FileStreamStore_Bench_WriteRead(); 216 | System.out.println("------- Block -------"); 217 | doTest_FileBlockStore_Bench_WriteRead(); 218 | System.out.println("------- END TEST -------"); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/test/BenchMarkMemoryStructures.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.test; 16 | 17 | import java.nio.ByteBuffer; 18 | 19 | import org.javastack.kvstore.holders.DataHolder; 20 | import org.javastack.kvstore.holders.IntHolder; 21 | import org.javastack.kvstore.io.StringSerializer; 22 | import org.javastack.kvstore.structures.btree.BplusTreeMemory; 23 | import org.javastack.kvstore.structures.hash.IntHashMap; 24 | import org.javastack.kvstore.structures.hash.IntLinkedHashMap; 25 | 26 | /** 27 | * Code for benchmark 28 | * 29 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 30 | */ 31 | public class BenchMarkMemoryStructures extends DataHolder { 32 | private static final int TOTAL = (int) 2e6, TRACE_LEN = 100000; 33 | 34 | public long long1; 35 | public long long2; 36 | public long long3; 37 | public int int1; 38 | public int int2; 39 | public String str1; 40 | public String str2; 41 | public String str3; 42 | public String str4; 43 | 44 | public BenchMarkMemoryStructures() { 45 | } 46 | 47 | public BenchMarkMemoryStructures(final long long1, final long long2, final long long3, final int int1, 48 | final int int2, final String str1, final String str2, final String str3, final String str4) { 49 | this.long1 = long1; 50 | this.long2 = long2; 51 | this.long3 = long3; 52 | this.int1 = int1; 53 | this.int2 = int2; 54 | this.str1 = str1; 55 | this.str2 = str2; 56 | this.str3 = str3; 57 | this.str4 = str4; 58 | } 59 | 60 | // ByteBuffer 61 | public void serialize(final ByteBuffer out) { 62 | out.clear(); 63 | out.putLong(long1); 64 | out.putLong(long2); 65 | out.putLong(long3); 66 | out.putInt(int1); 67 | out.putInt(int2); 68 | StringSerializer.fromStringToBuffer(out, str1); 69 | StringSerializer.fromStringToBuffer(out, str2); 70 | StringSerializer.fromStringToBuffer(out, str3); 71 | StringSerializer.fromStringToBuffer(out, str4); 72 | } 73 | 74 | public BenchMarkMemoryStructures deserialize(final ByteBuffer in) { 75 | long1 = in.getLong(); 76 | long2 = in.getLong(); 77 | long3 = in.getLong(); 78 | int1 = in.getInt(); 79 | int2 = in.getInt(); 80 | str1 = StringSerializer.fromBufferToString(in); 81 | str2 = StringSerializer.fromBufferToString(in); 82 | str3 = StringSerializer.fromBufferToString(in); 83 | str4 = StringSerializer.fromBufferToString(in); 84 | return new BenchMarkMemoryStructures(long1, long2, long3, int1, int2, str1, str2, str3, str4); 85 | } 86 | 87 | private static int c = 0; 88 | 89 | private final static BenchMarkMemoryStructures newData() { 90 | final String s1 = "S1.123456789.", s2 = "S2.123456789.", s3 = "S3.123456789.123456789."; 91 | final String s4 = "S4.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456"; 92 | c++; 93 | return new BenchMarkMemoryStructures(c + 1, c + 2, c + 3, c + 11, c + 12, s1, s2, s3, s4); 94 | } 95 | 96 | public static void doTest_TreeMemory_Bench_PutGetRemove() throws Exception { 97 | final BplusTreeMemory tree = new BplusTreeMemory( 98 | 511, IntHolder.class, BenchMarkMemoryStructures.class); 99 | long ts, ts2; 100 | // 101 | // puts 102 | ts = System.currentTimeMillis(); 103 | ts2 = ts; 104 | for (int i = 0; i < TOTAL; i++) { 105 | tree.put(IntHolder.valueOf(i), newData()); 106 | if (((i + 1) % TRACE_LEN) == 0) { 107 | System.out.println("put[" + i + "]" + "\t" + (System.currentTimeMillis() - ts2) + "ms\t" 108 | + (TRACE_LEN / Math.max((System.currentTimeMillis() - ts2), 1)) + "k/s"); 109 | ts2 = System.currentTimeMillis(); 110 | } 111 | } 112 | // 113 | System.out.println("PUT: " + (System.currentTimeMillis() - ts) + "\t" 114 | + (TOTAL / Math.max((System.currentTimeMillis() - ts), 1)) + "k/s"); 115 | // 116 | // gets 117 | ts = System.currentTimeMillis(); 118 | ts2 = ts; 119 | for (int j = 0; j < TOTAL; j++) { 120 | final BenchMarkMemoryStructures bag = tree.get(IntHolder.valueOf(j)); 121 | if (bag == null) { 122 | System.out.println("Error trying get(" + j + ")"); 123 | break; 124 | } 125 | if (((j + 1) % TRACE_LEN) == 0) { 126 | System.out.println("get=[" + j + "]" + "\t" + (System.currentTimeMillis() - ts2) + "ms\t" 127 | + (TRACE_LEN / Math.max((System.currentTimeMillis() - ts2), 1)) + "k/s"); 128 | ts2 = System.currentTimeMillis(); 129 | } 130 | } 131 | System.out.println("GET: " + (System.currentTimeMillis() - ts) + "\t" 132 | + (TOTAL / Math.max((System.currentTimeMillis() - ts), 1)) + "k/s"); 133 | // 134 | // remove 135 | ts = System.currentTimeMillis(); 136 | ts2 = ts; 137 | for (int i = 0; i < TOTAL; i++) { 138 | tree.remove(IntHolder.valueOf(i)); 139 | if (((i + 1) % TRACE_LEN) == 0) { 140 | System.out.println("remove[" + i + "]" + "\t" + (System.currentTimeMillis() - ts2) + "ms\t" 141 | + (TRACE_LEN / Math.max((System.currentTimeMillis() - ts2), 1)) + "k/s"); 142 | ts2 = System.currentTimeMillis(); 143 | } 144 | } 145 | // 146 | System.out.println("REMOVE: " + (System.currentTimeMillis() - ts) + "\t" 147 | + (TOTAL / Math.max((System.currentTimeMillis() - ts), 1)) + "k/s"); 148 | // 149 | } 150 | 151 | public static void doTest_IntHashMemory_Bench_PutGetRemove() throws Exception { 152 | final IntHashMap hash = new IntHashMap( 153 | TOTAL * 2, BenchMarkMemoryStructures.class); 154 | long ts, ts2; 155 | // 156 | // puts 157 | ts = System.currentTimeMillis(); 158 | ts2 = ts; 159 | for (int i = 0; i < TOTAL; i++) { 160 | hash.put(i, newData()); 161 | if (((i + 1) % TRACE_LEN) == 0) { 162 | System.out.println("put[" + i + "]" + "\t" + (System.currentTimeMillis() - ts2) + "ms\t" 163 | + (TRACE_LEN / Math.max((System.currentTimeMillis() - ts2), 1)) + "k/s"); 164 | ts2 = System.currentTimeMillis(); 165 | } 166 | } 167 | // 168 | System.out.println("PUT: " + (System.currentTimeMillis() - ts) + "\t" 169 | + (TOTAL / Math.max((System.currentTimeMillis() - ts), 1)) + "k/s"); 170 | // 171 | // gets 172 | ts = System.currentTimeMillis(); 173 | ts2 = ts; 174 | for (int j = 0; j < TOTAL; j++) { 175 | final BenchMarkMemoryStructures bag = hash.get(j); 176 | if (bag == null) { 177 | System.out.println("Error trying get(" + j + ")"); 178 | break; 179 | } 180 | if (((j + 1) % TRACE_LEN) == 0) { 181 | System.out.println("get=[" + j + "]" + "\t" + (System.currentTimeMillis() - ts2) + "ms\t" 182 | + (TRACE_LEN / Math.max((System.currentTimeMillis() - ts2), 1)) + "k/s"); 183 | ts2 = System.currentTimeMillis(); 184 | } 185 | } 186 | System.out.println("GET: " + (System.currentTimeMillis() - ts) + "\t" 187 | + (TOTAL / Math.max((System.currentTimeMillis() - ts), 1)) + "k/s"); 188 | // 189 | // remove 190 | ts = System.currentTimeMillis(); 191 | ts2 = ts; 192 | for (int i = 0; i < TOTAL; i++) { 193 | hash.remove(i); 194 | if (((i + 1) % TRACE_LEN) == 0) { 195 | System.out.println("remove[" + i + "]" + "\t" + (System.currentTimeMillis() - ts2) + "ms\t" 196 | + (TRACE_LEN / Math.max((System.currentTimeMillis() - ts2), 1)) + "k/s"); 197 | ts2 = System.currentTimeMillis(); 198 | } 199 | } 200 | // 201 | System.out.println("REMOVE: " + (System.currentTimeMillis() - ts) + "\t" 202 | + (TOTAL / Math.max((System.currentTimeMillis() - ts), 1)) + "k/s"); 203 | // 204 | } 205 | 206 | public static void doTest_IntLinkedHashMemory_Bench_PutGetRemove() throws Exception { 207 | final IntLinkedHashMap hash = new IntLinkedHashMap( 208 | TOTAL * 2, BenchMarkMemoryStructures.class); 209 | long ts, ts2; 210 | // 211 | // puts 212 | ts = System.currentTimeMillis(); 213 | ts2 = ts; 214 | for (int i = 0; i < TOTAL; i++) { 215 | hash.put(i, newData()); 216 | if (((i + 1) % TRACE_LEN) == 0) { 217 | System.out.println("put[" + i + "]" + "\t" + (System.currentTimeMillis() - ts2) + "ms\t" 218 | + (TRACE_LEN / Math.max((System.currentTimeMillis() - ts2), 1)) + "k/s"); 219 | ts2 = System.currentTimeMillis(); 220 | } 221 | } 222 | // 223 | System.out.println("PUT: " + (System.currentTimeMillis() - ts) + "\t" 224 | + (TOTAL / Math.max((System.currentTimeMillis() - ts), 1)) + "k/s"); 225 | // 226 | // gets 227 | ts = System.currentTimeMillis(); 228 | ts2 = ts; 229 | for (int j = 0; j < TOTAL; j++) { 230 | final BenchMarkMemoryStructures bag = hash.get(j); 231 | if (bag == null) { 232 | System.out.println("Error trying get(" + j + ")"); 233 | break; 234 | } 235 | if (((j + 1) % TRACE_LEN) == 0) { 236 | System.out.println("get=[" + j + "]" + "\t" + (System.currentTimeMillis() - ts2) + "ms\t" 237 | + (TRACE_LEN / Math.max((System.currentTimeMillis() - ts2), 1)) + "k/s"); 238 | ts2 = System.currentTimeMillis(); 239 | } 240 | } 241 | System.out.println("GET: " + (System.currentTimeMillis() - ts) + "\t" 242 | + (TOTAL / Math.max((System.currentTimeMillis() - ts), 1)) + "k/s"); 243 | // 244 | // remove 245 | ts = System.currentTimeMillis(); 246 | ts2 = ts; 247 | for (int i = 0; i < TOTAL; i++) { 248 | hash.remove(i); 249 | if (((i + 1) % TRACE_LEN) == 0) { 250 | System.out.println("remove[" + i + "]" + "\t" + (System.currentTimeMillis() - ts2) + "ms\t" 251 | + (TRACE_LEN / Math.max((System.currentTimeMillis() - ts2), 1)) + "k/s"); 252 | ts2 = System.currentTimeMillis(); 253 | } 254 | } 255 | // 256 | System.out.println("REMOVE: " + (System.currentTimeMillis() - ts) + "\t" 257 | + (TOTAL / Math.max((System.currentTimeMillis() - ts), 1)) + "k/s"); 258 | // 259 | } 260 | 261 | // Not required for memory tree 262 | @Override 263 | public String toString() { 264 | throw new UnsupportedOperationException(); 265 | } 266 | 267 | @Override 268 | public int hashCode() { 269 | throw new UnsupportedOperationException(); 270 | } 271 | 272 | @Override 273 | public boolean equals(Object obj) { 274 | throw new UnsupportedOperationException(); 275 | } 276 | 277 | @Override 278 | public int compareTo(BenchMarkMemoryStructures another) { 279 | throw new UnsupportedOperationException(); 280 | } 281 | 282 | @Override 283 | public int byteLength() { 284 | throw new UnsupportedOperationException(); 285 | } 286 | 287 | public static void main(final String[] args) throws Exception { 288 | System.out.println("------- BEGIN TEST -------"); 289 | System.out.println("------- B+Tree -------"); 290 | doTest_TreeMemory_Bench_PutGetRemove(); 291 | System.out.println("------- IntHashMap -------"); 292 | doTest_IntHashMemory_Bench_PutGetRemove(); 293 | System.out.println("------- IntLinkedHashMap -------"); 294 | doTest_IntLinkedHashMemory_Bench_PutGetRemove(); 295 | System.out.println("------- END TEST -------"); 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/utils/CRC32.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.utils; 16 | 17 | import java.util.zip.Checksum; 18 | 19 | /** 20 | * Checksum CRC32 21 | * This class is Thread-Safe 22 | * 23 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 24 | */ 25 | public final class CRC32 implements Checksum { 26 | /** 27 | * CRC32 from Ethernet/ANSI X3.66/ITU-T V.42 28 | * 29 | *

 30 | 	 * The polynomial code used is 0x04C11DB7 (CRC32)
 31 | 	 * x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1
 32 | 	 * 
33 | * 34 | * poly=0x04c11db7 init=0xffffffff refin=true refout=true xorout=0xffffffff 35 | * 36 | * @url http://en.wikipedia.org/wiki/Cyclic_redundancy_check 37 | * @url http://reveng.sourceforge.net/crc-catalogue/17plus.htm 38 | */ 39 | // @formatter:off 40 | private static final int[] CRC_TABLE = { 41 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 42 | 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 43 | 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 44 | 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 45 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 46 | 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 47 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 48 | 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 49 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 50 | 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 51 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 52 | 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 53 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 54 | 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 55 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 56 | 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 57 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 58 | 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 59 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 60 | 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 61 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 62 | 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 63 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 64 | 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 65 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 66 | 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 67 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 68 | 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 69 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 70 | 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 71 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 72 | 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 73 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 74 | 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 75 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 76 | 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 77 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 78 | 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 79 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 80 | 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 81 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 82 | 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 83 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 84 | 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 85 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 86 | 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 87 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 88 | 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 89 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 90 | 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 91 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 92 | 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 93 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 94 | 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 95 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 96 | 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 97 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 98 | 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 99 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 100 | 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 101 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 102 | 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 103 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 104 | 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, 105 | }; 106 | // @formatter:on 107 | 108 | private int crc = ~0; 109 | 110 | public final void update(int d) { 111 | crc = ((crc >>> 8) ^ CRC_TABLE[(crc ^ (d & 0xFF)) & 0xFF]); 112 | } 113 | 114 | public final void update(final byte[] buffer, final int offset, int length) { 115 | for (int i = offset; length > 0; length--) 116 | update(buffer[i++]); 117 | } 118 | 119 | public final void update(final byte[] buffer) { 120 | for (int i = 0; i < buffer.length; i++) 121 | update(buffer[i]); 122 | } 123 | 124 | public final long getValue() { 125 | return ((long) (crc ^ 0xFFFFFFFF) & 0xFFFFFFFFL); 126 | } 127 | 128 | public final void reset() { 129 | crc = ~0; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/utils/CRC32C.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.utils; 16 | 17 | import java.util.zip.Checksum; 18 | 19 | /** 20 | * Checksum CRC32C 21 | * This class is Thread-Safe 22 | * 23 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 24 | */ 25 | public final class CRC32C implements Checksum { 26 | /** 27 | *
 28 | 	 * CRC32C from SCTP (D. Otis)
 29 | 	 * The x^32 term is implied and the x^0 term may also be shown as +1. 
 30 | 	 * The polynomial code used is 0x1EDC6F41. Castagnoli93
 31 | 	 * x32+x28+x27+x26+x25+x23+x22+x20+x19+x18+x14+x13+x11+x10+x9+x8+x6+x0 
 32 | 	 * Guy Castagnoli, Stefan Braeuer and Martin Herrman 
 33 | 	 * "Optimization of Cyclic Redundancy-Check Codes with 24 and 32 Parity Bits", 
 34 | 	 * IEEE Transactions on Communications, Vol.41, No.6, June 1993
 35 | 	 * 
36 | * 37 | * poly=0x1edc6f41 init=0xffffffff refin=true refout=true xorout=0xffffffff 38 | * 39 | * @url http://www.ietf.org/rfc/rfc3309.txt 40 | * @url http://reveng.sourceforge.net/crc-catalogue/17plus.htm 41 | */ 42 | // @formatter:off 43 | protected static final int[] CRC_TABLE = { 44 | 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 45 | 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, 46 | 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 47 | 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, 48 | 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 49 | 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, 50 | 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 51 | 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, 52 | 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 53 | 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, 54 | 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 55 | 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, 56 | 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 57 | 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, 58 | 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 59 | 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, 60 | 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 61 | 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, 62 | 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 63 | 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, 64 | 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 65 | 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, 66 | 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 67 | 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, 68 | 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 69 | 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, 70 | 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 71 | 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, 72 | 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 73 | 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, 74 | 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 75 | 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, 76 | 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 77 | 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, 78 | 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 79 | 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, 80 | 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 81 | 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, 82 | 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 83 | 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, 84 | 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 85 | 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, 86 | 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 87 | 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, 88 | 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 89 | 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, 90 | 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 91 | 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, 92 | 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 93 | 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, 94 | 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 95 | 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, 96 | 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 97 | 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, 98 | 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 99 | 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, 100 | 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 101 | 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, 102 | 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 103 | 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, 104 | 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 105 | 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, 106 | 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 107 | 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, 108 | }; 109 | // @formatter:on 110 | 111 | private int crc = ~0; 112 | 113 | public final void update(int d) { 114 | crc = ((crc >>> 8) ^ CRC_TABLE[(crc ^ (d & 0xFF)) & 0xFF]); 115 | } 116 | 117 | public final void update(final byte[] buffer, final int offset, int length) { 118 | for (int i = offset; length > 0; length--) 119 | update(buffer[i++]); 120 | } 121 | 122 | public final void update(final byte[] buffer) { 123 | for (int i = 0; i < buffer.length; i++) 124 | update(buffer[i]); 125 | } 126 | 127 | public final long getValue() { 128 | return ((long) (crc ^ 0xFFFFFFFF) & 0xFFFFFFFFL); 129 | } 130 | 131 | public final void reset() { 132 | crc = ~0; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/utils/Check64bitsJVM.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.utils; 16 | 17 | public class Check64bitsJVM { 18 | public static boolean JVMis64bits() { 19 | final String propOsArch = System.getProperty("os.arch"); 20 | final String propSunDataModel = System.getProperty("sun.arch.data.model"); 21 | // http://stackoverflow.com/questions/807263/how-do-i-detect-which-kind-of-jre-is-installed-32bit-vs-64bit 22 | if (propSunDataModel != null) { 23 | return propSunDataModel.equals("64"); 24 | } 25 | if (propOsArch != null) { 26 | return propOsArch.contains("64"); 27 | } 28 | return false; 29 | } 30 | 31 | public static void main(final String[] args) throws Throwable { 32 | System.out.println("JVM is 64bits?: " + JVMis64bits()); 33 | } 34 | } -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/utils/GenericFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.utils; 16 | 17 | import java.lang.reflect.Array; 18 | 19 | /** 20 | * Workarround for Java generics Reference: Generic array-creation 23 | * 24 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 25 | */ 26 | public class GenericFactory { 27 | public final Class type; 28 | 29 | public GenericFactory(final Class type) { 30 | this.type = type; 31 | } 32 | 33 | /** 34 | * Allocate a new array of objects of type T. 35 | * 36 | * @param size of the array to allocate 37 | * @return array allocated 38 | */ 39 | @SuppressWarnings({ 40 | "unchecked" 41 | }) 42 | public T[] newArray(final int size) { 43 | return (T[]) Array.newInstance(type, size); 44 | } 45 | 46 | /** 47 | * Create a new instance of type T 48 | * 49 | * @return new object of type T 50 | * @throws InstantiationException 51 | * @throws IllegalAccessException 52 | */ 53 | public T newInstance() throws InstantiationException, IllegalAccessException { 54 | return type.newInstance(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/utils/HexStrings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.utils; 16 | 17 | /** 18 | * Hex Strings 19 | * This class is Thread-Safe 20 | * 21 | * @author Guillermo Grandes / guillermo.grandes[at]gmail.com 22 | */ 23 | public final class HexStrings { 24 | /** 25 | * Basic table of hex characters 26 | */ 27 | private static final char[] HEX_TABLE = "0123456789abcdef".toCharArray(); 28 | 29 | /** 30 | * Return hex string (zero-left-padded) of an native byte 31 | * 32 | * @param input the number 33 | * @return string 34 | */ 35 | public static final String nativeAsHex(final byte input) { 36 | return nativeAsHex(input, 8); 37 | } 38 | 39 | /** 40 | * Return hex string (zero-left-padded) of an native short 41 | * 42 | * @param input the number 43 | * @return string 44 | */ 45 | public static final String nativeAsHex(final short input) { 46 | return nativeAsHex(input, 16); 47 | } 48 | 49 | /** 50 | * Return hex string (zero-left-padded) of an native int 51 | * 52 | * @param input the number 53 | * @return string 54 | */ 55 | public static final String nativeAsHex(final int input) { 56 | return nativeAsHex(input, 32); 57 | } 58 | 59 | /** 60 | * Return hex string (zero-left-padded) of an native long 61 | * 62 | * @param input the number 63 | * @return string 64 | */ 65 | public static final String nativeAsHex(final long input) { 66 | return nativeAsHex(input, 64); 67 | } 68 | 69 | /** 70 | * Return hex string (zero-left-padded) of an native number 71 | * 72 | * @param input the number 73 | * @param bits the size in bits for a number (64 for long, 32 for int, 16 for short, 8 for byte) 74 | * @return string 75 | */ 76 | public static final String nativeAsHex(final long input, final int bits) { 77 | final char[] sb = new char[(bits > 64 ? 64 : (bits < 8 ? 8 : bits)) >> 2]; 78 | final int len = (sb.length - 1); 79 | 80 | for (int i = 0; i <= len; i++) { // MSB 81 | sb[i] = HEX_TABLE[((int) (input >>> ((len - i) << 2))) & 0xF]; 82 | } 83 | return new String(sb); 84 | } 85 | 86 | /** 87 | * Return hexdump of byte-array 88 | * 89 | * @param buf is byte array to dump 90 | * @param limit size to dump from 0-offset to limit 91 | * @return String representation of hexdump 92 | */ 93 | public final static String byteArrayAsHex(final byte[] buf, final int limit) { 94 | final StringBuilder sb = new StringBuilder(); 95 | for (int i = 0; i < limit; ++i) { 96 | if ((i % 16) == 0) { // print offset 97 | sb.append(nativeAsHex(i, 32)).append(" "); 98 | } else if (((i) % 8) == 0) { // split on qword 99 | sb.append(" "); 100 | } 101 | sb.append(nativeAsHex((buf[i] & 0xFF), 8)).append(" "); // hex byte 102 | if (((i % 16) == 15) || (i == (buf.length - 1))) { 103 | for (int j = (16 - (i % 16)); j > 1; j--) { // padding non exist bytes 104 | sb.append(" "); 105 | } 106 | sb.append(" |"); // byte columns 107 | final int start = ((i / 16) * 16); 108 | final int end = ((buf.length < i + 1) ? buf.length : (i + 1)); 109 | for (int j = start; j < end; ++j) { 110 | if ((buf[j] >= 32) && (buf[j] <= 126)) { 111 | sb.append((char) buf[j]); 112 | } else { 113 | sb.append("."); // non-printable character 114 | } 115 | } 116 | sb.append("|\n"); // end column 117 | } 118 | } 119 | return sb.toString(); 120 | } 121 | 122 | public static void main(String[] args) { 123 | final byte[] buf = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis posuere massa vitae urna ultricies vitae tempor magna mollis." 124 | .getBytes(); 125 | System.out.println(byteArrayAsHex(buf, buf.length)); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/org/javastack/kvstore/utils/PrimeFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | * 14 | */ 15 | package org.javastack.kvstore.utils; 16 | 17 | import java.math.BigInteger; 18 | import java.util.Arrays; 19 | 20 | public final class PrimeFinder { 21 | /** 22 | * The prime number list 1-Integer.MAX_VALUE (steps). 23 | */ 24 | // @formatter:off 25 | private static final int[] primeCapacities = { 26 | 1, 2, 7, 17, 37, 79, 27 | 149, 271, 479, 827, 1399, 2309, 3739, 5981, 9391, 14557, 22291, 28 | 33757, 50627, 75181, 110647, 161507, 233939, 336397, 480409, 29 | 681647, 961273, 1347733, 1879151, 2606353, 3596851, 4940051, 30 | 6753841, 9193181, 12461041, 16822489, 22622599, 30309211, 31 | 40461959, 53828963, 71373193, 94330987, 124285687, 163260059, 32 | 213832237, 279280621, 363764831, 472551293, 612293659, 33 | 791381131, 1020370069, 1312518343, 1684445207, 2147483647 34 | }; 35 | // @formatter:on 36 | /** 37 | * The prime number list 1-8191 (detailed). 38 | */ 39 | // @formatter:off 40 | private static final short[] primeCapacities8191 = { 41 | 1,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83, 42 | 89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173, 43 | 179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269, 44 | 271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373, 45 | 379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467, 46 | 479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593, 47 | 599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691, 48 | 701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821, 49 | 823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937, 50 | 941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033, 51 | 1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117, 52 | 1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223, 53 | 1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,1301,1303, 54 | 1307,1319,1321,1327,1361,1367,1373,1381,1399,1409,1423,1427,1429, 55 | 1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499, 56 | 1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597,1601, 57 | 1607,1609,1613,1619,1621,1627,1637,1657,1663,1667,1669,1693,1697, 58 | 1699,1709,1721,1723,1733,1741,1747,1753,1759,1777,1783,1787,1789, 59 | 1801,1811,1823,1831,1847,1861,1867,1871,1873,1877,1879,1889,1901, 60 | 1907,1913,1931,1933,1949,1951,1973,1979,1987,1993,1997,1999,2003, 61 | 2011,2017,2027,2029,2039,2053,2063,2069,2081,2083,2087,2089,2099, 62 | 2111,2113,2129,2131,2137,2141,2143,2153,2161,2179,2203,2207,2213, 63 | 2221,2237,2239,2243,2251,2267,2269,2273,2281,2287,2293,2297,2309, 64 | 2311,2333,2339,2341,2347,2351,2357,2371,2377,2381,2383,2389,2393, 65 | 2399,2411,2417,2423,2437,2441,2447,2459,2467,2473,2477,2503,2521, 66 | 2531,2539,2543,2549,2551,2557,2579,2591,2593,2609,2617,2621,2633, 67 | 2647,2657,2659,2663,2671,2677,2683,2687,2689,2693,2699,2707,2711, 68 | 2713,2719,2729,2731,2741,2749,2753,2767,2777,2789,2791,2797,2801, 69 | 2803,2819,2833,2837,2843,2851,2857,2861,2879,2887,2897,2903,2909, 70 | 2917,2927,2939,2953,2957,2963,2969,2971,2999,3001,3011,3019,3023, 71 | 3037,3041,3049,3061,3067,3079,3083,3089,3109,3119,3121,3137,3163, 72 | 3167,3169,3181,3187,3191,3203,3209,3217,3221,3229,3251,3253,3257, 73 | 3259,3271,3299,3301,3307,3313,3319,3323,3329,3331,3343,3347,3359, 74 | 3361,3371,3373,3389,3391,3407,3413,3433,3449,3457,3461,3463,3467, 75 | 3469,3491,3499,3511,3517,3527,3529,3533,3539,3541,3547,3557,3559, 76 | 3571,3581,3583,3593,3607,3613,3617,3623,3631,3637,3643,3659,3671, 77 | 3673,3677,3691,3697,3701,3709,3719,3727,3733,3739,3761,3767,3769, 78 | 3779,3793,3797,3803,3821,3823,3833,3847,3851,3853,3863,3877,3881, 79 | 3889,3907,3911,3917,3919,3923,3929,3931,3943,3947,3967,3989,4001, 80 | 4003,4007,4013,4019,4021,4027,4049,4051,4057,4073,4079,4091,4093, 81 | 4099,4111,4127,4129,4133,4139,4153,4157,4159,4177,4201,4211,4217, 82 | 4219,4229,4231,4241,4243,4253,4259,4261,4271,4273,4283,4289,4297, 83 | 4327,4337,4339,4349,4357,4363,4373,4391,4397,4409,4421,4423,4441, 84 | 4447,4451,4457,4463,4481,4483,4493,4507,4513,4517,4519,4523,4547, 85 | 4549,4561,4567,4583,4591,4597,4603,4621,4637,4639,4643,4649,4651, 86 | 4657,4663,4673,4679,4691,4703,4721,4723,4729,4733,4751,4759,4783, 87 | 4787,4789,4793,4799,4801,4813,4817,4831,4861,4871,4877,4889,4903, 88 | 4909,4919,4931,4933,4937,4943,4951,4957,4967,4969,4973,4987,4993, 89 | 4999,5003,5009,5011,5021,5023,5039,5051,5059,5077,5081,5087,5099, 90 | 5101,5107,5113,5119,5147,5153,5167,5171,5179,5189,5197,5209,5227, 91 | 5231,5233,5237,5261,5273,5279,5281,5297,5303,5309,5323,5333,5347, 92 | 5351,5381,5387,5393,5399,5407,5413,5417,5419,5431,5437,5441,5443, 93 | 5449,5471,5477,5479,5483,5501,5503,5507,5519,5521,5527,5531,5557, 94 | 5563,5569,5573,5581,5591,5623,5639,5641,5647,5651,5653,5657,5659, 95 | 5669,5683,5689,5693,5701,5711,5717,5737,5741,5743,5749,5779,5783, 96 | 5791,5801,5807,5813,5821,5827,5839,5843,5849,5851,5857,5861,5867, 97 | 5869,5879,5881,5897,5903,5923,5927,5939,5953,5981,5987,6007,6011, 98 | 6029,6037,6043,6047,6053,6067,6073,6079,6089,6091,6101,6113,6121, 99 | 6131,6133,6143,6151,6163,6173,6197,6199,6203,6211,6217,6221,6229, 100 | 6247,6257,6263,6269,6271,6277,6287,6299,6301,6311,6317,6323,6329, 101 | 6337,6343,6353,6359,6361,6367,6373,6379,6389,6397,6421,6427,6449, 102 | 6451,6469,6473,6481,6491,6521,6529,6547,6551,6553,6563,6569,6571, 103 | 6577,6581,6599,6607,6619,6637,6653,6659,6661,6673,6679,6689,6691, 104 | 6701,6703,6709,6719,6733,6737,6761,6763,6779,6781,6791,6793,6803, 105 | 6823,6827,6829,6833,6841,6857,6863,6869,6871,6883,6899,6907,6911, 106 | 6917,6947,6949,6959,6961,6967,6971,6977,6983,6991,6997,7001,7013, 107 | 7019,7027,7039,7043,7057,7069,7079,7103,7109,7121,7127,7129,7151, 108 | 7159,7177,7187,7193,7207,7211,7213,7219,7229,7237,7243,7247,7253, 109 | 7283,7297,7307,7309,7321,7331,7333,7349,7351,7369,7393,7411,7417, 110 | 7433,7451,7457,7459,7477,7481,7487,7489,7499,7507,7517,7523,7529, 111 | 7537,7541,7547,7549,7559,7561,7573,7577,7583,7589,7591,7603,7607, 112 | 7621,7639,7643,7649,7669,7673,7681,7687,7691,7699,7703,7717,7723, 113 | 7727,7741,7753,7757,7759,7789,7793,7817,7823,7829,7841,7853,7867, 114 | 7873,7877,7879,7883,7901,7907,7919,7927,7933,7937,7949,7951,7963, 115 | 7993,8009,8011,8017,8039,8053,8059,8069,8081,8087,8089,8093,8101, 116 | 8111,8117,8123,8147,8161,8167,8171,8179,8191 117 | }; 118 | // @formatter:on 119 | 120 | /** 121 | * Returns a prime number which is very close to desiredCapacity 122 | * 123 | * @param desiredCapacity the capacity desired by the user. 124 | * @return the capacity which should be used for a hash. 125 | */ 126 | public static final int nextPrime(final int desiredCapacity) { 127 | final int i = Arrays.binarySearch(primeCapacities, desiredCapacity); 128 | return primeCapacities[((i < 0) ? ((-i) - 1) : i)]; 129 | } 130 | 131 | /** 132 | * Returns a prime number which is very close to desiredCapacity with detailed steps between 133 | * {1-8191} 134 | * 135 | * @param desiredCapacity the capacity desired by the user. 136 | * @return the capacity which should be used for a hash. 137 | */ 138 | public static final int nextPrime8191(final int desiredCapacity) { 139 | if (desiredCapacity < 0) 140 | return 1; 141 | if (desiredCapacity <= 8191) { 142 | final int i = Arrays.binarySearch(primeCapacities8191, (short) desiredCapacity); 143 | return primeCapacities8191[((i < 0) ? ((-i) - 1) : i)]; 144 | } 145 | return nextPrime(desiredCapacity); 146 | } 147 | 148 | /** 149 | * Prime Generator 150 | */ 151 | public static void main(final String[] args) { 152 | int min, max; 153 | min = 0; 154 | max = Integer.MAX_VALUE; 155 | // 156 | BigInteger x = BigInteger.valueOf(min); 157 | int i = min; 158 | final StringBuilder sb = new StringBuilder(64); 159 | while (i < max) { 160 | x = x.nextProbablePrime(); 161 | i = x.intValue(); 162 | sb.append(i).append(','); 163 | if (sb.length() > 64) { 164 | System.out.println(sb.toString()); 165 | sb.setLength(0); 166 | } 167 | } 168 | if (sb.length() > 0) { 169 | System.out.println(sb.toString()); 170 | sb.setLength(0); 171 | } 172 | } 173 | } 174 | --------------------------------------------------------------------------------