├── .gitignore ├── .travis.yml ├── README.md ├── leveldb-api ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── iq80 │ └── leveldb │ ├── CompressionType.java │ ├── DB.java │ ├── DBComparator.java │ ├── DBException.java │ ├── DBFactory.java │ ├── DBIterator.java │ ├── Logger.java │ ├── Options.java │ ├── Range.java │ ├── ReadOptions.java │ ├── Snapshot.java │ ├── WriteBatch.java │ └── WriteOptions.java ├── leveldb-benchmark ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── iq80 │ └── leveldb │ └── benchmark │ └── DbBenchmark.java ├── leveldb ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── iq80 │ │ │ └── leveldb │ │ │ ├── impl │ │ │ ├── Compaction.java │ │ │ ├── DbConstants.java │ │ │ ├── DbImpl.java │ │ │ ├── DbLock.java │ │ │ ├── FileChannelLogWriter.java │ │ │ ├── FileMetaData.java │ │ │ ├── Filename.java │ │ │ ├── InternalEntry.java │ │ │ ├── InternalKey.java │ │ │ ├── InternalKeyComparator.java │ │ │ ├── InternalUserComparator.java │ │ │ ├── Iq80DBFactory.java │ │ │ ├── Level.java │ │ │ ├── Level0.java │ │ │ ├── LogChunkType.java │ │ │ ├── LogConstants.java │ │ │ ├── LogMonitor.java │ │ │ ├── LogMonitors.java │ │ │ ├── LogReader.java │ │ │ ├── LogWriter.java │ │ │ ├── Logs.java │ │ │ ├── LookupKey.java │ │ │ ├── LookupResult.java │ │ │ ├── MMapLogWriter.java │ │ │ ├── MemTable.java │ │ │ ├── ReadStats.java │ │ │ ├── SeekingIterable.java │ │ │ ├── SeekingIterator.java │ │ │ ├── SeekingIteratorAdapter.java │ │ │ ├── SequenceNumber.java │ │ │ ├── SnapshotImpl.java │ │ │ ├── SnapshotSeekingIterator.java │ │ │ ├── TableCache.java │ │ │ ├── ValueType.java │ │ │ ├── Version.java │ │ │ ├── VersionEdit.java │ │ │ ├── VersionEditTag.java │ │ │ ├── VersionSet.java │ │ │ └── WriteBatchImpl.java │ │ │ ├── table │ │ │ ├── Block.java │ │ │ ├── BlockBuilder.java │ │ │ ├── BlockEntry.java │ │ │ ├── BlockHandle.java │ │ │ ├── BlockIterator.java │ │ │ ├── BlockTrailer.java │ │ │ ├── BytewiseComparator.java │ │ │ ├── CustomUserComparator.java │ │ │ ├── FileChannelTable.java │ │ │ ├── Footer.java │ │ │ ├── MMapTable.java │ │ │ ├── Table.java │ │ │ ├── TableBuilder.java │ │ │ └── UserComparator.java │ │ │ └── util │ │ │ ├── AbstractSeekingIterator.java │ │ │ ├── BasicSliceOutput.java │ │ │ ├── ByteBufferSupport.java │ │ │ ├── Closeables.java │ │ │ ├── DbIterator.java │ │ │ ├── DynamicSliceOutput.java │ │ │ ├── FileUtils.java │ │ │ ├── Finalizer.java │ │ │ ├── IntVector.java │ │ │ ├── InternalIterator.java │ │ │ ├── InternalTableIterator.java │ │ │ ├── Level0Iterator.java │ │ │ ├── LevelIterator.java │ │ │ ├── MergingIterator.java │ │ │ ├── PureJavaCrc32C.java │ │ │ ├── SizeOf.java │ │ │ ├── Slice.java │ │ │ ├── SliceComparator.java │ │ │ ├── SliceInput.java │ │ │ ├── SliceOutput.java │ │ │ ├── Slices.java │ │ │ ├── Snappy.java │ │ │ ├── TableIterator.java │ │ │ ├── VariableLengthQuantity.java │ │ │ └── Zlib.java │ └── resources │ │ └── org │ │ └── iq80 │ │ └── leveldb │ │ └── impl │ │ └── version.txt │ └── test │ └── java │ └── org │ └── iq80 │ └── leveldb │ ├── impl │ ├── ApiTest.java │ ├── DbImplTest.java │ ├── LogTest.java │ ├── NativeInteropTest.java │ ├── TestFileChannelLogWriter.java │ └── TestMMapLogWriter.java │ ├── table │ ├── BlockHelper.java │ ├── BlockTest.java │ ├── FileChannelTableTest.java │ ├── MMapTableTest.java │ └── TableTest.java │ └── util │ ├── PureJavaCrc32CTest.java │ ├── SliceComparatorTest.java │ └── VariableLengthQuantityTest.java ├── license.txt ├── notice.md ├── pom.xml └── src ├── checkstyle └── checks.xml ├── license └── LICENSE-HEADER.txt └── site └── site.xml /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | /var 3 | pom.xml.versionsBackup 4 | test-output/ 5 | /atlassian-ide-plugin.x 6 | .idea 7 | .*.swp 8 | .*.swo 9 | leveldb-c 10 | *~ 11 | *.swp 12 | .idea 13 | .idea/* 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .DS_Store 18 | .scala_dependencies 19 | .project 20 | .classpath 21 | .settings 22 | eclipse-classes 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LevelDB in Java 2 | 3 | This is a rewrite (port) of [LevelDB](http://code.google.com/p/leveldb/) in 4 | Java. This goal is to have a feature complete implementation that is within 5 | 10% of the performance of the C++ original and produces byte-for-byte exact 6 | copies of the C++ code. 7 | 8 | # Current status 9 | 10 | Currently the code base is basically functional, but only trivially tested. 11 | In some places, this code is a literal conversion of the C++ code and in 12 | others it has been converted to a more natural Java style. The plan is to 13 | leave the code closer to the C++ original until the baseline performance has 14 | been established. 15 | 16 | ## API Usage: 17 | 18 | Recommended Package imports: 19 | 20 | ```java 21 | import org.iq80.leveldb.*; 22 | import static org.iq80.leveldb.impl.Iq80DBFactory.*; 23 | import java.io.*; 24 | ``` 25 | 26 | Opening and closing the database. 27 | 28 | ```java 29 | Options options = new Options(); 30 | options.createIfMissing(true); 31 | DB db = factory.open(new File("example"), options); 32 | try { 33 | // Use the db in here.... 34 | } finally { 35 | // Make sure you close the db to shutdown the 36 | // database and avoid resource leaks. 37 | db.close(); 38 | } 39 | ``` 40 | 41 | Putting, Getting, and Deleting key/values. 42 | 43 | ```java 44 | db.put(bytes("Tampa"), bytes("rocks")); 45 | String value = asString(db.get(bytes("Tampa"))); 46 | db.delete(bytes("Tampa"), wo); 47 | ``` 48 | 49 | Performing Batch/Bulk/Atomic Updates. 50 | 51 | ```java 52 | WriteBatch batch = db.createWriteBatch(); 53 | try { 54 | batch.delete(bytes("Denver")); 55 | batch.put(bytes("Tampa"), bytes("green")); 56 | batch.put(bytes("London"), bytes("red")); 57 | 58 | db.write(batch); 59 | } finally { 60 | // Make sure you close the batch to avoid resource leaks. 61 | batch.close(); 62 | } 63 | ``` 64 | 65 | Iterating key/values. 66 | 67 | ```java 68 | DBIterator iterator = db.iterator(); 69 | try { 70 | for(iterator.seekToFirst(); iterator.hasNext(); iterator.next()) { 71 | String key = asString(iterator.peekNext().getKey()); 72 | String value = asString(iterator.peekNext().getValue()); 73 | System.out.println(key+" = "+value); 74 | } 75 | } finally { 76 | // Make sure you close the iterator to avoid resource leaks. 77 | iterator.close(); 78 | } 79 | ``` 80 | 81 | Working against a Snapshot view of the Database. 82 | 83 | ```java 84 | ReadOptions ro = new ReadOptions(); 85 | ro.snapshot(db.getSnapshot()); 86 | try { 87 | 88 | // All read operations will now use the same 89 | // consistent view of the data. 90 | ... = db.iterator(ro); 91 | ... = db.get(bytes("Tampa"), ro); 92 | 93 | } finally { 94 | // Make sure you close the snapshot to avoid resource leaks. 95 | ro.snapshot().close(); 96 | } 97 | ``` 98 | 99 | Using a custom Comparator. 100 | 101 | ```java 102 | DBComparator comparator = new DBComparator(){ 103 | public int compare(byte[] key1, byte[] key2) { 104 | return new String(key1).compareTo(new String(key2)); 105 | } 106 | public String name() { 107 | return "simple"; 108 | } 109 | public byte[] findShortestSeparator(byte[] start, byte[] limit) { 110 | return start; 111 | } 112 | public byte[] findShortSuccessor(byte[] key) { 113 | return key; 114 | } 115 | }; 116 | Options options = new Options(); 117 | options.comparator(comparator); 118 | DB db = factory.open(new File("example"), options); 119 | ``` 120 | 121 | Disabling Compression 122 | 123 | ```java 124 | Options options = new Options(); 125 | options.compressionType(CompressionType.NONE); 126 | DB db = factory.open(new File("example"), options); 127 | ``` 128 | 129 | Configuring the Cache 130 | 131 | ```java 132 | Options options = new Options(); 133 | options.cacheSize(100 * 1048576); // 100MB cache 134 | DB db = factory.open(new File("example"), options); 135 | ``` 136 | 137 | Getting approximate sizes. 138 | 139 | ```java 140 | long[] sizes = db.getApproximateSizes(new Range(bytes("a"), bytes("k")), new Range(bytes("k"), bytes("z"))); 141 | System.out.println("Size: "+sizes[0]+", "+sizes[1]); 142 | ``` 143 | 144 | Getting database status. 145 | 146 | ```java 147 | String stats = db.getProperty("leveldb.stats"); 148 | System.out.println(stats); 149 | ``` 150 | 151 | Getting informational log messages. 152 | 153 | ```java 154 | Logger logger = new Logger() { 155 | public void log(String message) { 156 | System.out.println(message); 157 | } 158 | }; 159 | Options options = new Options(); 160 | options.logger(logger); 161 | DB db = factory.open(new File("example"), options); 162 | ``` 163 | 164 | Destroying a database. 165 | 166 | ```java 167 | Options options = new Options(); 168 | factory.destroy(new File("example"), options); 169 | ``` 170 | 171 | # Projects using this port of LevelDB 172 | 173 | * [ActiveMQ Apollo](http://activemq.apache.org/apollo/): Defaults to using leveldbjni, but falls 174 | back to this port if the jni port is not available on your platform. 175 | -------------------------------------------------------------------------------- /leveldb-api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | com.tinfoiled.mcpe.leveldb 7 | leveldb-project 8 | 0.8-SNAPSHOT 9 | 10 | 11 | leveldb-api 12 | ${project.artifactId} 13 | High level Java API for LevelDB 14 | 15 | 16 | ${project.parent.basedir} 17 | 18 | 19 | -------------------------------------------------------------------------------- /leveldb-api/src/main/java/org/iq80/leveldb/CompressionType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb; 19 | 20 | public enum CompressionType 21 | { 22 | NONE(0x00), 23 | SNAPPY(0x01), 24 | // MCPE-specific (as are all code blocks depending on it). 25 | ZLIB(0x02); 26 | 27 | public static CompressionType getCompressionTypeByPersistentId(int persistentId) 28 | { 29 | for (CompressionType compressionType : CompressionType.values()) { 30 | if (compressionType.persistentId == persistentId) { 31 | return compressionType; 32 | } 33 | } 34 | throw new IllegalArgumentException("Unknown persistentId " + persistentId); 35 | } 36 | 37 | private final int persistentId; 38 | 39 | CompressionType(int persistentId) 40 | { 41 | this.persistentId = persistentId; 42 | } 43 | 44 | public int persistentId() 45 | { 46 | return persistentId; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /leveldb-api/src/main/java/org/iq80/leveldb/DB.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb; 19 | 20 | import java.io.Closeable; 21 | import java.util.Map; 22 | 23 | /** 24 | * @author Hiram Chirino 25 | */ 26 | public interface DB 27 | extends Iterable>, Closeable 28 | { 29 | byte[] get(byte[] key) 30 | throws DBException; 31 | 32 | byte[] get(byte[] key, ReadOptions options) 33 | throws DBException; 34 | 35 | @Override 36 | DBIterator iterator(); 37 | 38 | DBIterator iterator(ReadOptions options); 39 | 40 | void put(byte[] key, byte[] value) 41 | throws DBException; 42 | 43 | void delete(byte[] key) 44 | throws DBException; 45 | 46 | void write(WriteBatch updates) 47 | throws DBException; 48 | 49 | WriteBatch createWriteBatch(); 50 | 51 | /** 52 | * @return null if options.isSnapshot()==false otherwise returns a snapshot 53 | * of the DB after this operation. 54 | */ 55 | Snapshot put(byte[] key, byte[] value, WriteOptions options) 56 | throws DBException; 57 | 58 | /** 59 | * @return null if options.isSnapshot()==false otherwise returns a snapshot 60 | * of the DB after this operation. 61 | */ 62 | Snapshot delete(byte[] key, WriteOptions options) 63 | throws DBException; 64 | 65 | /** 66 | * @return null if options.isSnapshot()==false otherwise returns a snapshot 67 | * of the DB after this operation. 68 | */ 69 | Snapshot write(WriteBatch updates, WriteOptions options) 70 | throws DBException; 71 | 72 | Snapshot getSnapshot(); 73 | 74 | long[] getApproximateSizes(Range... ranges); 75 | 76 | String getProperty(String name); 77 | 78 | /** 79 | * Suspends any background compaction threads. This methods 80 | * returns once the background compactions are suspended. 81 | */ 82 | void suspendCompactions() 83 | throws InterruptedException; 84 | 85 | /** 86 | * Resumes the background compaction threads. 87 | */ 88 | void resumeCompactions(); 89 | 90 | /** 91 | * Force a compaction of the specified key range. 92 | * 93 | * @param begin if null then compaction start from the first key 94 | * @param end if null then compaction ends at the last key 95 | */ 96 | void compactRange(byte[] begin, byte[] end) 97 | throws DBException; 98 | } 99 | -------------------------------------------------------------------------------- /leveldb-api/src/main/java/org/iq80/leveldb/DBComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb; 19 | 20 | import java.util.Comparator; 21 | 22 | /** 23 | * @author Hiram Chirino 24 | */ 25 | public interface DBComparator 26 | extends Comparator 27 | { 28 | String name(); 29 | 30 | /** 31 | * If start < limit, returns a short key in [start,limit). 32 | * Simple comparator implementations should return start unchanged, 33 | */ 34 | byte[] findShortestSeparator(byte[] start, byte[] limit); 35 | 36 | /** 37 | * returns a 'short key' where the 'short key' >= key. 38 | * Simple comparator implementations should return key unchanged, 39 | */ 40 | byte[] findShortSuccessor(byte[] key); 41 | } 42 | -------------------------------------------------------------------------------- /leveldb-api/src/main/java/org/iq80/leveldb/DBException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb; 19 | 20 | /** 21 | * @author Hiram Chirino 22 | */ 23 | public class DBException 24 | extends RuntimeException 25 | { 26 | public DBException() 27 | { 28 | } 29 | 30 | public DBException(String s) 31 | { 32 | super(s); 33 | } 34 | 35 | public DBException(String s, Throwable throwable) 36 | { 37 | super(s, throwable); 38 | } 39 | 40 | public DBException(Throwable throwable) 41 | { 42 | super(throwable); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /leveldb-api/src/main/java/org/iq80/leveldb/DBFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb; 19 | 20 | import java.io.File; 21 | import java.io.IOException; 22 | 23 | /** 24 | * @author Hiram Chirino 25 | */ 26 | public interface DBFactory 27 | { 28 | DB open(File path, Options options) 29 | throws IOException; 30 | 31 | void destroy(File path, Options options) 32 | throws IOException; 33 | 34 | void repair(File path, Options options) 35 | throws IOException; 36 | } 37 | -------------------------------------------------------------------------------- /leveldb-api/src/main/java/org/iq80/leveldb/DBIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb; 19 | 20 | import java.io.Closeable; 21 | import java.util.Iterator; 22 | import java.util.Map; 23 | 24 | /** 25 | * @author Hiram Chirino 26 | */ 27 | public interface DBIterator 28 | extends Iterator>, Closeable 29 | { 30 | /** 31 | * Repositions the iterator so the key of the next BlockElement 32 | * returned greater than or equal to the specified targetKey. 33 | */ 34 | void seek(byte[] key); 35 | 36 | /** 37 | * Repositions the iterator so is is at the beginning of the Database. 38 | */ 39 | void seekToFirst(); 40 | 41 | /** 42 | * Returns the next element in the iteration, without advancing the iteration. 43 | */ 44 | Map.Entry peekNext(); 45 | 46 | /** 47 | * @return true if there is a previous entry in the iteration. 48 | */ 49 | boolean hasPrev(); 50 | 51 | /** 52 | * @return the previous element in the iteration and rewinds the iteration. 53 | */ 54 | Map.Entry prev(); 55 | 56 | /** 57 | * @return the previous element in the iteration, without rewinding the iteration. 58 | */ 59 | Map.Entry peekPrev(); 60 | 61 | /** 62 | * Repositions the iterator so it is at the end of of the Database. 63 | */ 64 | void seekToLast(); 65 | } 66 | -------------------------------------------------------------------------------- /leveldb-api/src/main/java/org/iq80/leveldb/Logger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb; 19 | 20 | /** 21 | * @author Hiram Chirino 22 | */ 23 | public interface Logger 24 | { 25 | void log(String message); 26 | } 27 | -------------------------------------------------------------------------------- /leveldb-api/src/main/java/org/iq80/leveldb/Options.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb; 19 | 20 | public class Options 21 | { 22 | private boolean createIfMissing = true; 23 | private boolean errorIfExists; 24 | private int writeBufferSize = 4 << 20; 25 | 26 | private int maxOpenFiles = 1000; 27 | 28 | private int blockRestartInterval = 16; 29 | private int blockSize = 4 * 1024; 30 | // leveldb default compression is normally SNAPPY. 31 | private CompressionType compressionType = CompressionType.ZLIB; 32 | private boolean verifyChecksums = true; 33 | private boolean paranoidChecks; 34 | private DBComparator comparator; 35 | private Logger logger; 36 | private long cacheSize; 37 | 38 | static void checkArgNotNull(Object value, String name) 39 | { 40 | if (value == null) { 41 | throw new IllegalArgumentException("The " + name + " argument cannot be null"); 42 | } 43 | } 44 | 45 | public boolean createIfMissing() 46 | { 47 | return createIfMissing; 48 | } 49 | 50 | public Options createIfMissing(boolean createIfMissing) 51 | { 52 | this.createIfMissing = createIfMissing; 53 | return this; 54 | } 55 | 56 | public boolean errorIfExists() 57 | { 58 | return errorIfExists; 59 | } 60 | 61 | public Options errorIfExists(boolean errorIfExists) 62 | { 63 | this.errorIfExists = errorIfExists; 64 | return this; 65 | } 66 | 67 | public int writeBufferSize() 68 | { 69 | return writeBufferSize; 70 | } 71 | 72 | public Options writeBufferSize(int writeBufferSize) 73 | { 74 | this.writeBufferSize = writeBufferSize; 75 | return this; 76 | } 77 | 78 | public int maxOpenFiles() 79 | { 80 | return maxOpenFiles; 81 | } 82 | 83 | public Options maxOpenFiles(int maxOpenFiles) 84 | { 85 | this.maxOpenFiles = maxOpenFiles; 86 | return this; 87 | } 88 | 89 | public int blockRestartInterval() 90 | { 91 | return blockRestartInterval; 92 | } 93 | 94 | public Options blockRestartInterval(int blockRestartInterval) 95 | { 96 | this.blockRestartInterval = blockRestartInterval; 97 | return this; 98 | } 99 | 100 | public int blockSize() 101 | { 102 | return blockSize; 103 | } 104 | 105 | public Options blockSize(int blockSize) 106 | { 107 | this.blockSize = blockSize; 108 | return this; 109 | } 110 | 111 | public CompressionType compressionType() 112 | { 113 | return compressionType; 114 | } 115 | 116 | public Options compressionType(CompressionType compressionType) 117 | { 118 | checkArgNotNull(compressionType, "compressionType"); 119 | this.compressionType = compressionType; 120 | return this; 121 | } 122 | 123 | public boolean verifyChecksums() 124 | { 125 | return verifyChecksums; 126 | } 127 | 128 | public Options verifyChecksums(boolean verifyChecksums) 129 | { 130 | this.verifyChecksums = verifyChecksums; 131 | return this; 132 | } 133 | 134 | public long cacheSize() 135 | { 136 | return cacheSize; 137 | } 138 | 139 | public Options cacheSize(long cacheSize) 140 | { 141 | this.cacheSize = cacheSize; 142 | return this; 143 | } 144 | 145 | public DBComparator comparator() 146 | { 147 | return comparator; 148 | } 149 | 150 | public Options comparator(DBComparator comparator) 151 | { 152 | this.comparator = comparator; 153 | return this; 154 | } 155 | 156 | public Logger logger() 157 | { 158 | return logger; 159 | } 160 | 161 | public Options logger(Logger logger) 162 | { 163 | this.logger = logger; 164 | return this; 165 | } 166 | 167 | public boolean paranoidChecks() 168 | { 169 | return paranoidChecks; 170 | } 171 | 172 | public Options paranoidChecks(boolean paranoidChecks) 173 | { 174 | this.paranoidChecks = paranoidChecks; 175 | return this; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /leveldb-api/src/main/java/org/iq80/leveldb/Range.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb; 19 | 20 | /** 21 | * @author Hiram Chirino 22 | */ 23 | public class Range 24 | { 25 | private final byte[] start; 26 | private final byte[] limit; 27 | 28 | public byte[] limit() 29 | { 30 | return limit; 31 | } 32 | 33 | public byte[] start() 34 | { 35 | return start; 36 | } 37 | 38 | public Range(byte[] start, byte[] limit) 39 | { 40 | Options.checkArgNotNull(start, "start"); 41 | Options.checkArgNotNull(limit, "limit"); 42 | this.limit = limit; 43 | this.start = start; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /leveldb-api/src/main/java/org/iq80/leveldb/ReadOptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb; 19 | 20 | public class ReadOptions 21 | { 22 | private boolean verifyChecksums; 23 | private boolean fillCache = true; 24 | private Snapshot snapshot; 25 | 26 | public Snapshot snapshot() 27 | { 28 | return snapshot; 29 | } 30 | 31 | public ReadOptions snapshot(Snapshot snapshot) 32 | { 33 | this.snapshot = snapshot; 34 | return this; 35 | } 36 | 37 | public boolean fillCache() 38 | { 39 | return fillCache; 40 | } 41 | 42 | public ReadOptions fillCache(boolean fillCache) 43 | { 44 | this.fillCache = fillCache; 45 | return this; 46 | } 47 | 48 | public boolean verifyChecksums() 49 | { 50 | return verifyChecksums; 51 | } 52 | 53 | public ReadOptions verifyChecksums(boolean verifyChecksums) 54 | { 55 | this.verifyChecksums = verifyChecksums; 56 | return this; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /leveldb-api/src/main/java/org/iq80/leveldb/Snapshot.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb; 19 | 20 | import java.io.Closeable; 21 | 22 | public interface Snapshot 23 | extends Closeable 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /leveldb-api/src/main/java/org/iq80/leveldb/WriteBatch.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb; 19 | 20 | import java.io.Closeable; 21 | 22 | /** 23 | * @author Hiram Chirino 24 | */ 25 | public interface WriteBatch 26 | extends Closeable 27 | { 28 | WriteBatch put(byte[] key, byte[] value); 29 | 30 | WriteBatch delete(byte[] key); 31 | } 32 | -------------------------------------------------------------------------------- /leveldb-api/src/main/java/org/iq80/leveldb/WriteOptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb; 19 | 20 | public class WriteOptions 21 | { 22 | private boolean sync; 23 | private boolean snapshot; 24 | 25 | public boolean sync() 26 | { 27 | return sync; 28 | } 29 | 30 | public WriteOptions sync(boolean sync) 31 | { 32 | this.sync = sync; 33 | return this; 34 | } 35 | 36 | public boolean snapshot() 37 | { 38 | return snapshot; 39 | } 40 | 41 | public WriteOptions snapshot(boolean snapshot) 42 | { 43 | this.snapshot = snapshot; 44 | return this; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /leveldb-benchmark/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 4.0.0 20 | 21 | 22 | leveldb-project 23 | org.iq80.leveldb 24 | 0.8-SNAPSHOT 25 | 26 | 27 | leveldb-benchmark 28 | leveldb-benchmark 29 | Port of LevelDB Benchmarks to Java 30 | 31 | 32 | ${project.parent.basedir} 33 | 34 | 35 | 36 | 37 | org.iq80.leveldb 38 | leveldb-api 39 | 40 | 41 | org.iq80.leveldb 42 | leveldb 43 | 44 | 45 | com.google.guava 46 | guava 47 | 48 | 49 | junit 50 | junit 51 | 4.11 52 | test 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.codehaus.mojo 61 | exec-maven-plugin 62 | 1.2.1 63 | 64 | 65 | 66 | java 67 | 68 | 69 | 70 | 71 | org.iq80.leveldb.benchmark.DbBenchmark 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/DbConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | public final class DbConstants 21 | { 22 | public static final int MAJOR_VERSION = 0; 23 | public static final int MINOR_VERSION = 1; 24 | 25 | // todo this should be part of the configuration 26 | 27 | /** 28 | * Max number of levels 29 | */ 30 | public static final int NUM_LEVELS = 7; 31 | 32 | /** 33 | * Level-0 compaction is started when we hit this many files. 34 | */ 35 | public static final int L0_COMPACTION_TRIGGER = 4; 36 | 37 | /** 38 | * Soft limit on number of level-0 files. We slow down writes at this point. 39 | */ 40 | public static final int L0_SLOWDOWN_WRITES_TRIGGER = 8; 41 | 42 | /** 43 | * Maximum number of level-0 files. We stop writes at this point. 44 | */ 45 | public static final int L0_STOP_WRITES_TRIGGER = 12; 46 | 47 | /** 48 | * Maximum level to which a new compacted memtable is pushed if it 49 | * does not create overlap. We try to push to level 2 to avoid the 50 | * relatively expensive level 0=>1 compactions and to avoid some 51 | * expensive manifest file operations. We do not push all the way to 52 | * the largest level since that can generate a lot of wasted disk 53 | * space if the same key space is being repeatedly overwritten. 54 | */ 55 | public static final int MAX_MEM_COMPACT_LEVEL = 2; 56 | 57 | private DbConstants() 58 | { 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/DbLock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import com.google.common.base.Preconditions; 21 | import com.google.common.base.Throwables; 22 | import org.iq80.leveldb.util.Closeables; 23 | 24 | import java.io.File; 25 | import java.io.IOException; 26 | import java.io.RandomAccessFile; 27 | import java.nio.channels.FileChannel; 28 | import java.nio.channels.FileLock; 29 | 30 | import static java.lang.String.format; 31 | 32 | public class DbLock 33 | { 34 | private final File lockFile; 35 | private final FileChannel channel; 36 | private final FileLock lock; 37 | 38 | public DbLock(File lockFile) 39 | throws IOException 40 | { 41 | Preconditions.checkNotNull(lockFile, "lockFile is null"); 42 | this.lockFile = lockFile; 43 | 44 | // open and lock the file 45 | channel = new RandomAccessFile(lockFile, "rw").getChannel(); 46 | try { 47 | lock = channel.tryLock(); 48 | } 49 | catch (IOException e) { 50 | Closeables.closeQuietly(channel); 51 | throw e; 52 | } 53 | 54 | if (lock == null) { 55 | throw new IOException(format("Unable to acquire lock on '%s'", lockFile.getAbsolutePath())); 56 | } 57 | } 58 | 59 | public boolean isValid() 60 | { 61 | return lock.isValid(); 62 | } 63 | 64 | public void release() 65 | { 66 | try { 67 | lock.release(); 68 | } 69 | catch (IOException e) { 70 | Throwables.propagate(e); 71 | } 72 | finally { 73 | Closeables.closeQuietly(channel); 74 | } 75 | } 76 | 77 | @Override 78 | public String toString() 79 | { 80 | StringBuilder sb = new StringBuilder(); 81 | sb.append("DbLock"); 82 | sb.append("{lockFile=").append(lockFile); 83 | sb.append(", lock=").append(lock); 84 | sb.append('}'); 85 | return sb.toString(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/FileMetaData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import com.google.common.base.Function; 21 | 22 | import java.util.concurrent.atomic.AtomicInteger; 23 | 24 | public class FileMetaData 25 | { 26 | public static final Function GET_LARGEST_USER_KEY = new Function() 27 | { 28 | @Override 29 | public InternalKey apply(FileMetaData fileMetaData) 30 | { 31 | return fileMetaData.getLargest(); 32 | } 33 | }; 34 | 35 | private final long number; 36 | 37 | /** 38 | * File size in bytes 39 | */ 40 | private final long fileSize; 41 | 42 | /** 43 | * Smallest internal key served by table 44 | */ 45 | private final InternalKey smallest; 46 | 47 | /** 48 | * Largest internal key served by table 49 | */ 50 | private final InternalKey largest; 51 | 52 | /** 53 | * Seeks allowed until compaction 54 | */ 55 | // todo this mutable state should be moved elsewhere 56 | private final AtomicInteger allowedSeeks = new AtomicInteger(1 << 30); 57 | 58 | public FileMetaData(long number, long fileSize, InternalKey smallest, InternalKey largest) 59 | { 60 | this.number = number; 61 | this.fileSize = fileSize; 62 | this.smallest = smallest; 63 | this.largest = largest; 64 | } 65 | 66 | public long getFileSize() 67 | { 68 | return fileSize; 69 | } 70 | 71 | public long getNumber() 72 | { 73 | return number; 74 | } 75 | 76 | public InternalKey getSmallest() 77 | { 78 | return smallest; 79 | } 80 | 81 | public InternalKey getLargest() 82 | { 83 | return largest; 84 | } 85 | 86 | public int getAllowedSeeks() 87 | { 88 | return allowedSeeks.get(); 89 | } 90 | 91 | public void setAllowedSeeks(int allowedSeeks) 92 | { 93 | this.allowedSeeks.set(allowedSeeks); 94 | } 95 | 96 | public void decrementAllowedSeeks() 97 | { 98 | allowedSeeks.getAndDecrement(); 99 | } 100 | 101 | @Override 102 | public String toString() 103 | { 104 | StringBuilder sb = new StringBuilder(); 105 | sb.append("FileMetaData"); 106 | sb.append("{number=").append(number); 107 | sb.append(", fileSize=").append(fileSize); 108 | sb.append(", smallest=").append(smallest); 109 | sb.append(", largest=").append(largest); 110 | sb.append(", allowedSeeks=").append(allowedSeeks); 111 | sb.append('}'); 112 | return sb.toString(); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/InternalEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import com.google.common.base.Preconditions; 21 | import org.iq80.leveldb.util.Slice; 22 | 23 | import java.util.Map.Entry; 24 | 25 | import static com.google.common.base.Charsets.UTF_8; 26 | 27 | public class InternalEntry 28 | implements Entry 29 | { 30 | private final InternalKey key; 31 | private final Slice value; 32 | 33 | public InternalEntry(InternalKey key, Slice value) 34 | { 35 | Preconditions.checkNotNull(key, "key is null"); 36 | Preconditions.checkNotNull(value, "value is null"); 37 | this.key = key; 38 | this.value = value; 39 | } 40 | 41 | @Override 42 | public InternalKey getKey() 43 | { 44 | return key; 45 | } 46 | 47 | @Override 48 | public Slice getValue() 49 | { 50 | return value; 51 | } 52 | 53 | /** 54 | * @throws UnsupportedOperationException always 55 | */ 56 | @Override 57 | public final Slice setValue(Slice value) 58 | { 59 | throw new UnsupportedOperationException(); 60 | } 61 | 62 | @Override 63 | public boolean equals(Object o) 64 | { 65 | if (this == o) { 66 | return true; 67 | } 68 | if (o == null || getClass() != o.getClass()) { 69 | return false; 70 | } 71 | 72 | InternalEntry entry = (InternalEntry) o; 73 | 74 | if (!key.equals(entry.key)) { 75 | return false; 76 | } 77 | if (!value.equals(entry.value)) { 78 | return false; 79 | } 80 | 81 | return true; 82 | } 83 | 84 | @Override 85 | public int hashCode() 86 | { 87 | int result = key.hashCode(); 88 | result = 31 * result + value.hashCode(); 89 | return result; 90 | } 91 | 92 | @Override 93 | public String toString() 94 | { 95 | StringBuilder sb = new StringBuilder(); 96 | sb.append("InternalEntry"); 97 | sb.append("{key=").append(key); // todo don't print the real value 98 | sb.append(", value=").append(value.toString(UTF_8)); 99 | sb.append('}'); 100 | return sb.toString(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/InternalKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import com.google.common.base.Preconditions; 21 | import org.iq80.leveldb.util.Slice; 22 | import org.iq80.leveldb.util.SliceOutput; 23 | import org.iq80.leveldb.util.Slices; 24 | 25 | import static com.google.common.base.Charsets.UTF_8; 26 | import static org.iq80.leveldb.util.SizeOf.SIZE_OF_LONG; 27 | 28 | public class InternalKey 29 | { 30 | private final Slice userKey; 31 | private final long sequenceNumber; 32 | private final ValueType valueType; 33 | 34 | public InternalKey(Slice userKey, long sequenceNumber, ValueType valueType) 35 | { 36 | Preconditions.checkNotNull(userKey, "userKey is null"); 37 | Preconditions.checkArgument(sequenceNumber >= 0, "sequenceNumber is negative"); 38 | Preconditions.checkNotNull(valueType, "valueType is null"); 39 | 40 | this.userKey = userKey; 41 | this.sequenceNumber = sequenceNumber; 42 | this.valueType = valueType; 43 | } 44 | 45 | public InternalKey(Slice data) 46 | { 47 | Preconditions.checkNotNull(data, "data is null"); 48 | Preconditions.checkArgument(data.length() >= SIZE_OF_LONG, "data must be at least %s bytes", SIZE_OF_LONG); 49 | this.userKey = getUserKey(data); 50 | long packedSequenceAndType = data.getLong(data.length() - SIZE_OF_LONG); 51 | this.sequenceNumber = SequenceNumber.unpackSequenceNumber(packedSequenceAndType); 52 | this.valueType = SequenceNumber.unpackValueType(packedSequenceAndType); 53 | } 54 | 55 | public InternalKey(byte[] data) 56 | { 57 | this(Slices.wrappedBuffer(data)); 58 | } 59 | 60 | public Slice getUserKey() 61 | { 62 | return userKey; 63 | } 64 | 65 | public long getSequenceNumber() 66 | { 67 | return sequenceNumber; 68 | } 69 | 70 | public ValueType getValueType() 71 | { 72 | return valueType; 73 | } 74 | 75 | public Slice encode() 76 | { 77 | Slice slice = Slices.allocate(userKey.length() + SIZE_OF_LONG); 78 | SliceOutput sliceOutput = slice.output(); 79 | sliceOutput.writeBytes(userKey); 80 | sliceOutput.writeLong(SequenceNumber.packSequenceAndValueType(sequenceNumber, valueType)); 81 | return slice; 82 | } 83 | 84 | @Override 85 | public boolean equals(Object o) 86 | { 87 | if (this == o) { 88 | return true; 89 | } 90 | if (o == null || getClass() != o.getClass()) { 91 | return false; 92 | } 93 | 94 | InternalKey that = (InternalKey) o; 95 | 96 | if (sequenceNumber != that.sequenceNumber) { 97 | return false; 98 | } 99 | if (userKey != null ? !userKey.equals(that.userKey) : that.userKey != null) { 100 | return false; 101 | } 102 | if (valueType != that.valueType) { 103 | return false; 104 | } 105 | 106 | return true; 107 | } 108 | 109 | private int hash; 110 | 111 | @Override 112 | public int hashCode() 113 | { 114 | if (hash == 0) { 115 | int result = userKey != null ? userKey.hashCode() : 0; 116 | result = 31 * result + (int) (sequenceNumber ^ (sequenceNumber >>> 32)); 117 | result = 31 * result + (valueType != null ? valueType.hashCode() : 0); 118 | if (result == 0) { 119 | result = 1; 120 | } 121 | hash = result; 122 | } 123 | return hash; 124 | } 125 | 126 | @Override 127 | public String toString() 128 | { 129 | StringBuilder sb = new StringBuilder(); 130 | sb.append("InternalKey"); 131 | sb.append("{key=").append(getUserKey().toString(UTF_8)); // todo don't print the real value 132 | sb.append(", sequenceNumber=").append(getSequenceNumber()); 133 | sb.append(", valueType=").append(getValueType()); 134 | sb.append('}'); 135 | return sb.toString(); 136 | } 137 | 138 | private static Slice getUserKey(Slice data) 139 | { 140 | return data.slice(0, data.length() - SIZE_OF_LONG); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/InternalKeyComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import com.google.common.primitives.Longs; 21 | import org.iq80.leveldb.table.UserComparator; 22 | 23 | import java.util.Arrays; 24 | import java.util.Comparator; 25 | import java.util.Iterator; 26 | 27 | public class InternalKeyComparator 28 | implements Comparator 29 | { 30 | private final UserComparator userComparator; 31 | 32 | public InternalKeyComparator(UserComparator userComparator) 33 | { 34 | this.userComparator = userComparator; 35 | } 36 | 37 | public UserComparator getUserComparator() 38 | { 39 | return userComparator; 40 | } 41 | 42 | public String name() 43 | { 44 | return this.userComparator.name(); 45 | } 46 | 47 | @Override 48 | public int compare(InternalKey left, InternalKey right) 49 | { 50 | int result = userComparator.compare(left.getUserKey(), right.getUserKey()); 51 | if (result != 0) { 52 | return result; 53 | } 54 | 55 | return Longs.compare(right.getSequenceNumber(), left.getSequenceNumber()); // reverse sorted version numbers 56 | } 57 | 58 | /** 59 | * Returns {@code true} if each element in {@code iterable} after the first is 60 | * greater than or equal to the element that preceded it, according to this 61 | * ordering. Note that this is always true when the iterable has fewer than 62 | * two elements. 63 | */ 64 | public boolean isOrdered(InternalKey... keys) 65 | { 66 | return isOrdered(Arrays.asList(keys)); 67 | } 68 | 69 | /** 70 | * Returns {@code true} if each element in {@code iterable} after the first is 71 | * greater than or equal to the element that preceded it, according to this 72 | * ordering. Note that this is always true when the iterable has fewer than 73 | * two elements. 74 | */ 75 | public boolean isOrdered(Iterable keys) 76 | { 77 | Iterator iterator = keys.iterator(); 78 | if (!iterator.hasNext()) { 79 | return true; 80 | } 81 | 82 | InternalKey previous = iterator.next(); 83 | while (iterator.hasNext()) { 84 | InternalKey next = iterator.next(); 85 | if (compare(previous, next) > 0) { 86 | return false; 87 | } 88 | previous = next; 89 | } 90 | return true; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/InternalUserComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import com.google.common.base.Preconditions; 21 | import org.iq80.leveldb.table.UserComparator; 22 | import org.iq80.leveldb.util.Slice; 23 | 24 | import static org.iq80.leveldb.impl.SequenceNumber.MAX_SEQUENCE_NUMBER; 25 | 26 | public class InternalUserComparator 27 | implements UserComparator 28 | { 29 | private final InternalKeyComparator internalKeyComparator; 30 | 31 | public InternalUserComparator(InternalKeyComparator internalKeyComparator) 32 | { 33 | this.internalKeyComparator = internalKeyComparator; 34 | } 35 | 36 | @Override 37 | public int compare(Slice left, Slice right) 38 | { 39 | return internalKeyComparator.compare(new InternalKey(left), new InternalKey(right)); 40 | } 41 | 42 | @Override 43 | public String name() 44 | { 45 | return internalKeyComparator.name(); 46 | } 47 | 48 | @Override 49 | public Slice findShortestSeparator( 50 | Slice start, 51 | Slice limit) 52 | { 53 | // Attempt to shorten the user portion of the key 54 | Slice startUserKey = new InternalKey(start).getUserKey(); 55 | Slice limitUserKey = new InternalKey(limit).getUserKey(); 56 | 57 | Slice shortestSeparator = internalKeyComparator.getUserComparator().findShortestSeparator(startUserKey, limitUserKey); 58 | 59 | if (internalKeyComparator.getUserComparator().compare(startUserKey, shortestSeparator) < 0) { 60 | // User key has become larger. Tack on the earliest possible 61 | // number to the shortened user key. 62 | InternalKey newInternalKey = new InternalKey(shortestSeparator, MAX_SEQUENCE_NUMBER, ValueType.VALUE); 63 | Preconditions.checkState(compare(start, newInternalKey.encode()) < 0); // todo 64 | Preconditions.checkState(compare(newInternalKey.encode(), limit) < 0); // todo 65 | 66 | return newInternalKey.encode(); 67 | } 68 | 69 | return start; 70 | } 71 | 72 | @Override 73 | public Slice findShortSuccessor(Slice key) 74 | { 75 | Slice userKey = new InternalKey(key).getUserKey(); 76 | Slice shortSuccessor = internalKeyComparator.getUserComparator().findShortSuccessor(userKey); 77 | 78 | if (internalKeyComparator.getUserComparator().compare(userKey, shortSuccessor) < 0) { 79 | // User key has become larger. Tack on the earliest possible 80 | // number to the shortened user key. 81 | InternalKey newInternalKey = new InternalKey(shortSuccessor, MAX_SEQUENCE_NUMBER, ValueType.VALUE); 82 | Preconditions.checkState(compare(key, newInternalKey.encode()) < 0); // todo 83 | 84 | return newInternalKey.encode(); 85 | } 86 | 87 | return key; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/Iq80DBFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import org.iq80.leveldb.DB; 21 | import org.iq80.leveldb.DBFactory; 22 | import org.iq80.leveldb.Options; 23 | import org.iq80.leveldb.util.FileUtils; 24 | 25 | import java.io.BufferedReader; 26 | import java.io.File; 27 | import java.io.IOException; 28 | import java.io.InputStream; 29 | import java.io.InputStreamReader; 30 | import java.io.UnsupportedEncodingException; 31 | 32 | /** 33 | * @author Hiram Chirino 34 | */ 35 | public class Iq80DBFactory 36 | implements DBFactory 37 | { 38 | public static final int CPU_DATA_MODEL = Integer.getInteger("sun.arch.data.model"); 39 | 40 | // We only use MMAP on 64 bit systems since it's really easy to run out of 41 | // virtual address space on a 32 bit system when all the data is getting mapped 42 | // into memory. If you really want to use MMAP anyways, use -Dleveldb.mmap=true 43 | public static final boolean USE_MMAP = Boolean.parseBoolean(System.getProperty("leveldb.mmap", "" + (CPU_DATA_MODEL > 32))); 44 | 45 | public static final String VERSION; 46 | 47 | static { 48 | String v = "unknown"; 49 | InputStream is = Iq80DBFactory.class.getResourceAsStream("version.txt"); 50 | try { 51 | v = new BufferedReader(new InputStreamReader(is, "UTF-8")).readLine(); 52 | } 53 | catch (Throwable e) { 54 | } 55 | finally { 56 | try { 57 | is.close(); 58 | } 59 | catch (Throwable e) { 60 | } 61 | } 62 | VERSION = v; 63 | } 64 | 65 | public static final Iq80DBFactory factory = new Iq80DBFactory(); 66 | 67 | @Override 68 | public DB open(File path, Options options) 69 | throws IOException 70 | { 71 | return new DbImpl(options, path); 72 | } 73 | 74 | @Override 75 | public void destroy(File path, Options options) 76 | throws IOException 77 | { 78 | // TODO: This should really only delete leveldb-created files. 79 | FileUtils.deleteRecursively(path); 80 | } 81 | 82 | @Override 83 | public void repair(File path, Options options) 84 | throws IOException 85 | { 86 | throw new UnsupportedOperationException(); 87 | } 88 | 89 | @Override 90 | public String toString() 91 | { 92 | return String.format("iq80 leveldb version %s", VERSION); 93 | } 94 | 95 | public static byte[] bytes(String value) 96 | { 97 | if (value == null) { 98 | return null; 99 | } 100 | try { 101 | return value.getBytes("UTF-8"); 102 | } 103 | catch (UnsupportedEncodingException e) { 104 | throw new RuntimeException(e); 105 | } 106 | } 107 | 108 | public static String asString(byte[] value) 109 | { 110 | if (value == null) { 111 | return null; 112 | } 113 | try { 114 | return new String(value, "UTF-8"); 115 | } 116 | catch (UnsupportedEncodingException e) { 117 | throw new RuntimeException(e); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/LogChunkType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import com.google.common.base.Preconditions; 21 | 22 | public enum LogChunkType 23 | { 24 | ZERO_TYPE(0), 25 | FULL(1), 26 | FIRST(2), 27 | MIDDLE(3), 28 | LAST(4), 29 | EOF, 30 | BAD_CHUNK, 31 | UNKNOWN; 32 | 33 | public static LogChunkType getLogChunkTypeByPersistentId(int persistentId) 34 | { 35 | for (LogChunkType logChunkType : LogChunkType.values()) { 36 | if (logChunkType.persistentId != null && logChunkType.persistentId == persistentId) { 37 | return logChunkType; 38 | } 39 | } 40 | return UNKNOWN; 41 | } 42 | 43 | private final Integer persistentId; 44 | 45 | LogChunkType() 46 | { 47 | this.persistentId = null; 48 | } 49 | 50 | LogChunkType(int persistentId) 51 | { 52 | this.persistentId = persistentId; 53 | } 54 | 55 | public int getPersistentId() 56 | { 57 | Preconditions.checkArgument(persistentId != null, "%s is not a persistent chunk type", name()); 58 | return persistentId; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/LogConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import static org.iq80.leveldb.util.SizeOf.SIZE_OF_BYTE; 21 | import static org.iq80.leveldb.util.SizeOf.SIZE_OF_INT; 22 | import static org.iq80.leveldb.util.SizeOf.SIZE_OF_SHORT; 23 | 24 | public final class LogConstants 25 | { 26 | // todo find new home for these 27 | 28 | public static final int BLOCK_SIZE = 32768; 29 | 30 | // Header is checksum (4 bytes), type (1 byte), length (2 bytes). 31 | public static final int HEADER_SIZE = SIZE_OF_INT + SIZE_OF_BYTE + SIZE_OF_SHORT; 32 | 33 | private LogConstants() 34 | { 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/LogMonitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | public interface LogMonitor 21 | { 22 | void corruption(long bytes, String reason); 23 | 24 | void corruption(long bytes, Throwable reason); 25 | } 26 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/LogMonitors.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | public final class LogMonitors 21 | { 22 | public static LogMonitor throwExceptionMonitor() 23 | { 24 | return new LogMonitor() 25 | { 26 | @Override 27 | public void corruption(long bytes, String reason) 28 | { 29 | throw new RuntimeException(String.format("corruption of %s bytes: %s", bytes, reason)); 30 | } 31 | 32 | @Override 33 | public void corruption(long bytes, Throwable reason) 34 | { 35 | throw new RuntimeException(String.format("corruption of %s bytes", bytes), reason); 36 | } 37 | }; 38 | } 39 | 40 | // todo implement real logging 41 | public static LogMonitor logMonitor() 42 | { 43 | return new LogMonitor() 44 | { 45 | @Override 46 | public void corruption(long bytes, String reason) 47 | { 48 | System.out.println(String.format("corruption of %s bytes: %s", bytes, reason)); 49 | } 50 | 51 | @Override 52 | public void corruption(long bytes, Throwable reason) 53 | { 54 | System.out.println(String.format("corruption of %s bytes", bytes)); 55 | reason.printStackTrace(); 56 | } 57 | }; 58 | } 59 | 60 | private LogMonitors() 61 | { 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/LogWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import org.iq80.leveldb.util.Slice; 21 | 22 | import java.io.File; 23 | import java.io.IOException; 24 | 25 | public interface LogWriter 26 | { 27 | boolean isClosed(); 28 | 29 | void close() 30 | throws IOException; 31 | 32 | void delete() 33 | throws IOException; 34 | 35 | File getFile(); 36 | 37 | long getFileNumber(); 38 | 39 | // Writes a stream of chunks such that no chunk is split across a block boundary 40 | void addRecord(Slice record, boolean force) 41 | throws IOException; 42 | } 43 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/Logs.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import org.iq80.leveldb.util.PureJavaCrc32C; 21 | import org.iq80.leveldb.util.Slice; 22 | 23 | import java.io.File; 24 | import java.io.IOException; 25 | 26 | public final class Logs 27 | { 28 | private Logs() 29 | { 30 | } 31 | 32 | public static LogWriter createLogWriter(File file, long fileNumber) 33 | throws IOException 34 | { 35 | if (Iq80DBFactory.USE_MMAP) { 36 | return new MMapLogWriter(file, fileNumber); 37 | } 38 | else { 39 | return new FileChannelLogWriter(file, fileNumber); 40 | } 41 | } 42 | 43 | public static int getChunkChecksum(int chunkTypeId, Slice slice) 44 | { 45 | return getChunkChecksum(chunkTypeId, slice.getRawArray(), slice.getRawOffset(), slice.length()); 46 | } 47 | 48 | public static int getChunkChecksum(int chunkTypeId, byte[] buffer, int offset, int length) 49 | { 50 | // Compute the crc of the record type and the payload. 51 | PureJavaCrc32C crc32C = new PureJavaCrc32C(); 52 | crc32C.update(chunkTypeId); 53 | crc32C.update(buffer, offset, length); 54 | return crc32C.getMaskedValue(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/LookupKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import org.iq80.leveldb.util.Slice; 21 | 22 | public class LookupKey 23 | { 24 | private final InternalKey key; 25 | 26 | public LookupKey(Slice userKey, long sequenceNumber) 27 | { 28 | key = new InternalKey(userKey, sequenceNumber, ValueType.VALUE); 29 | } 30 | 31 | public InternalKey getInternalKey() 32 | { 33 | return key; 34 | } 35 | 36 | public Slice getUserKey() 37 | { 38 | return key.getUserKey(); 39 | } 40 | 41 | @Override 42 | public String toString() 43 | { 44 | return key.toString(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/LookupResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import com.google.common.base.Preconditions; 21 | import org.iq80.leveldb.util.Slice; 22 | 23 | public class LookupResult 24 | { 25 | public static LookupResult ok(LookupKey key, Slice value) 26 | { 27 | return new LookupResult(key, value, false); 28 | } 29 | 30 | public static LookupResult deleted(LookupKey key) 31 | { 32 | return new LookupResult(key, null, true); 33 | } 34 | 35 | private final LookupKey key; 36 | private final Slice value; 37 | private final boolean deleted; 38 | 39 | private LookupResult(LookupKey key, Slice value, boolean deleted) 40 | { 41 | Preconditions.checkNotNull(key, "key is null"); 42 | this.key = key; 43 | if (value != null) { 44 | this.value = value.slice(); 45 | } 46 | else { 47 | this.value = null; 48 | } 49 | this.deleted = deleted; 50 | } 51 | 52 | public LookupKey getKey() 53 | { 54 | return key; 55 | } 56 | 57 | public Slice getValue() 58 | { 59 | if (value == null) { 60 | return null; 61 | } 62 | return value; 63 | } 64 | 65 | public boolean isDeleted() 66 | { 67 | return deleted; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/MemTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import com.google.common.base.Preconditions; 21 | import com.google.common.collect.Iterators; 22 | import com.google.common.collect.PeekingIterator; 23 | import org.iq80.leveldb.util.InternalIterator; 24 | import org.iq80.leveldb.util.Slice; 25 | 26 | import java.util.Map.Entry; 27 | import java.util.concurrent.ConcurrentSkipListMap; 28 | import java.util.concurrent.atomic.AtomicLong; 29 | 30 | import static org.iq80.leveldb.util.SizeOf.SIZE_OF_LONG; 31 | 32 | public class MemTable 33 | implements SeekingIterable 34 | { 35 | private final ConcurrentSkipListMap table; 36 | private final AtomicLong approximateMemoryUsage = new AtomicLong(); 37 | 38 | public MemTable(InternalKeyComparator internalKeyComparator) 39 | { 40 | table = new ConcurrentSkipListMap<>(internalKeyComparator); 41 | } 42 | 43 | public boolean isEmpty() 44 | { 45 | return table.isEmpty(); 46 | } 47 | 48 | public long approximateMemoryUsage() 49 | { 50 | return approximateMemoryUsage.get(); 51 | } 52 | 53 | public void add(long sequenceNumber, ValueType valueType, Slice key, Slice value) 54 | { 55 | Preconditions.checkNotNull(valueType, "valueType is null"); 56 | Preconditions.checkNotNull(key, "key is null"); 57 | Preconditions.checkNotNull(valueType, "valueType is null"); 58 | 59 | InternalKey internalKey = new InternalKey(key, sequenceNumber, valueType); 60 | table.put(internalKey, value); 61 | 62 | approximateMemoryUsage.addAndGet(key.length() + SIZE_OF_LONG + value.length()); 63 | } 64 | 65 | public LookupResult get(LookupKey key) 66 | { 67 | Preconditions.checkNotNull(key, "key is null"); 68 | 69 | InternalKey internalKey = key.getInternalKey(); 70 | Entry entry = table.ceilingEntry(internalKey); 71 | if (entry == null) { 72 | return null; 73 | } 74 | 75 | InternalKey entryKey = entry.getKey(); 76 | if (entryKey.getUserKey().equals(key.getUserKey())) { 77 | if (entryKey.getValueType() == ValueType.DELETION) { 78 | return LookupResult.deleted(key); 79 | } 80 | else { 81 | return LookupResult.ok(key, entry.getValue()); 82 | } 83 | } 84 | return null; 85 | } 86 | 87 | @Override 88 | public MemTableIterator iterator() 89 | { 90 | return new MemTableIterator(); 91 | } 92 | 93 | public class MemTableIterator 94 | implements InternalIterator 95 | { 96 | private PeekingIterator> iterator; 97 | 98 | public MemTableIterator() 99 | { 100 | iterator = Iterators.peekingIterator(table.entrySet().iterator()); 101 | } 102 | 103 | @Override 104 | public boolean hasNext() 105 | { 106 | return iterator.hasNext(); 107 | } 108 | 109 | @Override 110 | public void seekToFirst() 111 | { 112 | iterator = Iterators.peekingIterator(table.entrySet().iterator()); 113 | } 114 | 115 | @Override 116 | public void seek(InternalKey targetKey) 117 | { 118 | iterator = Iterators.peekingIterator(table.tailMap(targetKey).entrySet().iterator()); 119 | } 120 | 121 | @Override 122 | public InternalEntry peek() 123 | { 124 | Entry entry = iterator.peek(); 125 | return new InternalEntry(entry.getKey(), entry.getValue()); 126 | } 127 | 128 | @Override 129 | public InternalEntry next() 130 | { 131 | Entry entry = iterator.next(); 132 | return new InternalEntry(entry.getKey(), entry.getValue()); 133 | } 134 | 135 | @Override 136 | public void remove() 137 | { 138 | throw new UnsupportedOperationException(); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/ReadStats.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | public class ReadStats 21 | { 22 | private int seekFileLevel = -1; 23 | private FileMetaData seekFile; 24 | 25 | public void clear() 26 | { 27 | seekFileLevel = -1; 28 | seekFile = null; 29 | } 30 | 31 | public int getSeekFileLevel() 32 | { 33 | return seekFileLevel; 34 | } 35 | 36 | public void setSeekFileLevel(int seekFileLevel) 37 | { 38 | this.seekFileLevel = seekFileLevel; 39 | } 40 | 41 | public FileMetaData getSeekFile() 42 | { 43 | return seekFile; 44 | } 45 | 46 | public void setSeekFile(FileMetaData seekFile) 47 | { 48 | this.seekFile = seekFile; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/SeekingIterable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import java.util.Map.Entry; 21 | 22 | public interface SeekingIterable 23 | extends Iterable> 24 | { 25 | @Override 26 | SeekingIterator iterator(); 27 | } 28 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/SeekingIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import com.google.common.collect.PeekingIterator; 21 | 22 | import java.util.Map.Entry; 23 | 24 | public interface SeekingIterator 25 | extends PeekingIterator> 26 | { 27 | /** 28 | * Repositions the iterator so the beginning of this block. 29 | */ 30 | void seekToFirst(); 31 | 32 | /** 33 | * Repositions the iterator so the key of the next BlockElement returned greater than or equal to the specified targetKey. 34 | */ 35 | void seek(K targetKey); 36 | } 37 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/SeekingIteratorAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import com.google.common.base.Preconditions; 21 | import org.iq80.leveldb.DBIterator; 22 | import org.iq80.leveldb.util.Slice; 23 | import org.iq80.leveldb.util.Slices; 24 | 25 | import java.util.Map.Entry; 26 | import java.util.concurrent.atomic.AtomicBoolean; 27 | 28 | public class SeekingIteratorAdapter 29 | implements DBIterator 30 | { 31 | private final SnapshotSeekingIterator seekingIterator; 32 | private final AtomicBoolean closed = new AtomicBoolean(false); 33 | 34 | public SeekingIteratorAdapter(SnapshotSeekingIterator seekingIterator) 35 | { 36 | this.seekingIterator = seekingIterator; 37 | } 38 | 39 | @Override 40 | public void seekToFirst() 41 | { 42 | seekingIterator.seekToFirst(); 43 | } 44 | 45 | @Override 46 | public void seek(byte[] targetKey) 47 | { 48 | seekingIterator.seek(Slices.wrappedBuffer(targetKey)); 49 | } 50 | 51 | @Override 52 | public boolean hasNext() 53 | { 54 | return seekingIterator.hasNext(); 55 | } 56 | 57 | @Override 58 | public DbEntry next() 59 | { 60 | return adapt(seekingIterator.next()); 61 | } 62 | 63 | @Override 64 | public DbEntry peekNext() 65 | { 66 | return adapt(seekingIterator.peek()); 67 | } 68 | 69 | @Override 70 | public void close() 71 | { 72 | // This is an end user API.. he might screw up and close multiple times. 73 | // but we don't want the close multiple times as reference counts go bad. 74 | if (closed.compareAndSet(false, true)) { 75 | seekingIterator.close(); 76 | } 77 | } 78 | 79 | @Override 80 | public void remove() 81 | { 82 | throw new UnsupportedOperationException(); 83 | } 84 | 85 | private DbEntry adapt(Entry entry) 86 | { 87 | return new DbEntry(entry.getKey(), entry.getValue()); 88 | } 89 | 90 | // 91 | // todo Implement reverse iterator 92 | // 93 | 94 | @Override 95 | public void seekToLast() 96 | { 97 | throw new UnsupportedOperationException(); 98 | } 99 | 100 | @Override 101 | public boolean hasPrev() 102 | { 103 | throw new UnsupportedOperationException(); 104 | } 105 | 106 | @Override 107 | public DbEntry prev() 108 | { 109 | throw new UnsupportedOperationException(); 110 | } 111 | 112 | @Override 113 | public DbEntry peekPrev() 114 | { 115 | throw new UnsupportedOperationException(); 116 | } 117 | 118 | public static class DbEntry 119 | implements Entry 120 | { 121 | private final Slice key; 122 | private final Slice value; 123 | 124 | public DbEntry(Slice key, Slice value) 125 | { 126 | Preconditions.checkNotNull(key, "key is null"); 127 | Preconditions.checkNotNull(value, "value is null"); 128 | this.key = key; 129 | this.value = value; 130 | } 131 | 132 | @Override 133 | public byte[] getKey() 134 | { 135 | return key.getBytes(); 136 | } 137 | 138 | public Slice getKeySlice() 139 | { 140 | return key; 141 | } 142 | 143 | @Override 144 | public byte[] getValue() 145 | { 146 | return value.getBytes(); 147 | } 148 | 149 | public Slice getValueSlice() 150 | { 151 | return value; 152 | } 153 | 154 | @Override 155 | public byte[] setValue(byte[] value) 156 | { 157 | throw new UnsupportedOperationException(); 158 | } 159 | 160 | @Override 161 | public boolean equals(Object object) 162 | { 163 | if (object instanceof Entry) { 164 | Entry that = (Entry) object; 165 | return key.equals(that.getKey()) && 166 | value.equals(that.getValue()); 167 | } 168 | return false; 169 | } 170 | 171 | @Override 172 | public int hashCode() 173 | { 174 | return key.hashCode() ^ value.hashCode(); 175 | } 176 | 177 | /** 178 | * Returns a string representation of the form {key}={value}. 179 | */ 180 | @Override 181 | public String toString() 182 | { 183 | return key + "=" + value; 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/SequenceNumber.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import com.google.common.base.Preconditions; 21 | 22 | public final class SequenceNumber 23 | { 24 | // We leave eight bits empty at the bottom so a type and sequence# 25 | // can be packed together into 64-bits. 26 | public static final long MAX_SEQUENCE_NUMBER = ((0x1L << 56) - 1); 27 | 28 | private SequenceNumber() 29 | { 30 | } 31 | 32 | public static long packSequenceAndValueType(long sequence, ValueType valueType) 33 | { 34 | Preconditions.checkArgument(sequence <= MAX_SEQUENCE_NUMBER, "Sequence number is greater than MAX_SEQUENCE_NUMBER"); 35 | Preconditions.checkNotNull(valueType, "valueType is null"); 36 | 37 | return (sequence << 8) | valueType.getPersistentId(); 38 | } 39 | 40 | public static ValueType unpackValueType(long num) 41 | { 42 | return ValueType.getValueTypeByPersistentId((byte) num); 43 | } 44 | 45 | public static long unpackSequenceNumber(long num) 46 | { 47 | return num >>> 8; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/SnapshotImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import org.iq80.leveldb.Snapshot; 21 | 22 | import java.util.concurrent.atomic.AtomicBoolean; 23 | 24 | public class SnapshotImpl 25 | implements Snapshot 26 | { 27 | private final AtomicBoolean closed = new AtomicBoolean(); 28 | private final Version version; 29 | private final long lastSequence; 30 | 31 | SnapshotImpl(Version version, long lastSequence) 32 | { 33 | this.version = version; 34 | this.lastSequence = lastSequence; 35 | this.version.retain(); 36 | } 37 | 38 | @Override 39 | public void close() 40 | { 41 | // This is an end user API.. he might screw up and close multiple times. 42 | // but we don't want the version reference count going bad. 43 | if (closed.compareAndSet(false, true)) { 44 | this.version.release(); 45 | } 46 | } 47 | 48 | public long getLastSequence() 49 | { 50 | return lastSequence; 51 | } 52 | 53 | public Version getVersion() 54 | { 55 | return version; 56 | } 57 | 58 | @Override 59 | public String toString() 60 | { 61 | return Long.toString(lastSequence); 62 | } 63 | 64 | @Override 65 | public boolean equals(Object o) 66 | { 67 | if (this == o) { 68 | return true; 69 | } 70 | if (o == null || getClass() != o.getClass()) { 71 | return false; 72 | } 73 | 74 | SnapshotImpl snapshot = (SnapshotImpl) o; 75 | 76 | if (lastSequence != snapshot.lastSequence) { 77 | return false; 78 | } 79 | if (!version.equals(snapshot.version)) { 80 | return false; 81 | } 82 | 83 | return true; 84 | } 85 | 86 | @Override 87 | public int hashCode() 88 | { 89 | int result = version.hashCode(); 90 | result = 31 * result + (int) (lastSequence ^ (lastSequence >>> 32)); 91 | return result; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/SnapshotSeekingIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import com.google.common.collect.Maps; 21 | import org.iq80.leveldb.util.AbstractSeekingIterator; 22 | import org.iq80.leveldb.util.DbIterator; 23 | import org.iq80.leveldb.util.Slice; 24 | 25 | import java.util.Comparator; 26 | import java.util.Map.Entry; 27 | 28 | public final class SnapshotSeekingIterator 29 | extends AbstractSeekingIterator 30 | { 31 | private final DbIterator iterator; 32 | private final SnapshotImpl snapshot; 33 | private final Comparator userComparator; 34 | 35 | public SnapshotSeekingIterator(DbIterator iterator, SnapshotImpl snapshot, Comparator userComparator) 36 | { 37 | this.iterator = iterator; 38 | this.snapshot = snapshot; 39 | this.userComparator = userComparator; 40 | this.snapshot.getVersion().retain(); 41 | } 42 | 43 | public void close() 44 | { 45 | this.snapshot.getVersion().release(); 46 | } 47 | 48 | @Override 49 | protected void seekToFirstInternal() 50 | { 51 | iterator.seekToFirst(); 52 | findNextUserEntry(null); 53 | } 54 | 55 | @Override 56 | protected void seekInternal(Slice targetKey) 57 | { 58 | iterator.seek(new InternalKey(targetKey, snapshot.getLastSequence(), ValueType.VALUE)); 59 | findNextUserEntry(null); 60 | } 61 | 62 | @Override 63 | protected Entry getNextElement() 64 | { 65 | if (!iterator.hasNext()) { 66 | return null; 67 | } 68 | 69 | Entry next = iterator.next(); 70 | 71 | // find the next user entry after the key we are about to return 72 | findNextUserEntry(next.getKey().getUserKey()); 73 | 74 | return Maps.immutableEntry(next.getKey().getUserKey(), next.getValue()); 75 | } 76 | 77 | private void findNextUserEntry(Slice deletedKey) 78 | { 79 | // if there are no more entries, we are done 80 | if (!iterator.hasNext()) { 81 | return; 82 | } 83 | 84 | do { 85 | // Peek the next entry and parse the key 86 | InternalKey internalKey = iterator.peek().getKey(); 87 | 88 | // skip entries created after our snapshot 89 | if (internalKey.getSequenceNumber() > snapshot.getLastSequence()) { 90 | iterator.next(); 91 | continue; 92 | } 93 | 94 | // if the next entry is a deletion, skip all subsequent entries for that key 95 | if (internalKey.getValueType() == ValueType.DELETION) { 96 | deletedKey = internalKey.getUserKey(); 97 | } 98 | else if (internalKey.getValueType() == ValueType.VALUE) { 99 | // is this value masked by a prior deletion record? 100 | if (deletedKey == null || userComparator.compare(internalKey.getUserKey(), deletedKey) > 0) { 101 | return; 102 | } 103 | } 104 | iterator.next(); 105 | } while (iterator.hasNext()); 106 | } 107 | 108 | @Override 109 | public String toString() 110 | { 111 | final StringBuilder sb = new StringBuilder(); 112 | sb.append("SnapshotSeekingIterator"); 113 | sb.append("{snapshot=").append(snapshot); 114 | sb.append(", iterator=").append(iterator); 115 | sb.append('}'); 116 | return sb.toString(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/TableCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import com.google.common.base.Preconditions; 21 | import com.google.common.cache.CacheBuilder; 22 | import com.google.common.cache.CacheLoader; 23 | import com.google.common.cache.LoadingCache; 24 | import com.google.common.cache.RemovalListener; 25 | import com.google.common.cache.RemovalNotification; 26 | import org.iq80.leveldb.table.FileChannelTable; 27 | import org.iq80.leveldb.table.MMapTable; 28 | import org.iq80.leveldb.table.Table; 29 | import org.iq80.leveldb.table.UserComparator; 30 | import org.iq80.leveldb.util.Closeables; 31 | import org.iq80.leveldb.util.Finalizer; 32 | import org.iq80.leveldb.util.InternalTableIterator; 33 | import org.iq80.leveldb.util.Slice; 34 | 35 | import java.io.File; 36 | import java.io.FileInputStream; 37 | import java.io.IOException; 38 | import java.nio.channels.FileChannel; 39 | import java.util.concurrent.ExecutionException; 40 | 41 | public class TableCache 42 | { 43 | private final LoadingCache cache; 44 | private final Finalizer finalizer = new Finalizer<>(1); 45 | 46 | public TableCache(final File databaseDir, int tableCacheSize, final UserComparator userComparator, final boolean verifyChecksums) 47 | { 48 | Preconditions.checkNotNull(databaseDir, "databaseName is null"); 49 | 50 | cache = CacheBuilder.newBuilder() 51 | .maximumSize(tableCacheSize) 52 | .removalListener(new RemovalListener() 53 | { 54 | @Override 55 | public void onRemoval(RemovalNotification notification) 56 | { 57 | Table table = notification.getValue().getTable(); 58 | finalizer.addCleanup(table, table.closer()); 59 | } 60 | }) 61 | .build(new CacheLoader() 62 | { 63 | @Override 64 | public TableAndFile load(Long fileNumber) 65 | throws IOException 66 | { 67 | return new TableAndFile(databaseDir, fileNumber, userComparator, verifyChecksums); 68 | } 69 | }); 70 | } 71 | 72 | public InternalTableIterator newIterator(FileMetaData file) 73 | { 74 | return newIterator(file.getNumber()); 75 | } 76 | 77 | public InternalTableIterator newIterator(long number) 78 | { 79 | return new InternalTableIterator(getTable(number).iterator()); 80 | } 81 | 82 | public long getApproximateOffsetOf(FileMetaData file, Slice key) 83 | { 84 | return getTable(file.getNumber()).getApproximateOffsetOf(key); 85 | } 86 | 87 | private Table getTable(long number) 88 | { 89 | Table table; 90 | try { 91 | table = cache.get(number).getTable(); 92 | } 93 | catch (ExecutionException e) { 94 | Throwable cause = e; 95 | if (e.getCause() != null) { 96 | cause = e.getCause(); 97 | } 98 | throw new RuntimeException("Could not open table " + number, cause); 99 | } 100 | return table; 101 | } 102 | 103 | public void close() 104 | { 105 | cache.invalidateAll(); 106 | finalizer.destroy(); 107 | } 108 | 109 | public void evict(long number) 110 | { 111 | cache.invalidate(number); 112 | } 113 | 114 | private static final class TableAndFile 115 | { 116 | private final Table table; 117 | private final FileChannel fileChannel; 118 | 119 | private TableAndFile(File databaseDir, long fileNumber, UserComparator userComparator, boolean verifyChecksums) 120 | throws IOException 121 | { 122 | String tableFileName = Filename.tableFileName(fileNumber); 123 | File tableFile = new File(databaseDir, tableFileName); 124 | fileChannel = new FileInputStream(tableFile).getChannel(); 125 | try { 126 | if (Iq80DBFactory.USE_MMAP) { 127 | table = new MMapTable(tableFile.getAbsolutePath(), fileChannel, userComparator, verifyChecksums); 128 | } 129 | else { 130 | table = new FileChannelTable(tableFile.getAbsolutePath(), fileChannel, userComparator, verifyChecksums); 131 | } 132 | } 133 | catch (IOException e) { 134 | Closeables.closeQuietly(fileChannel); 135 | throw e; 136 | } 137 | } 138 | 139 | public Table getTable() 140 | { 141 | return table; 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/ValueType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | public enum ValueType 21 | { 22 | DELETION(0x00), 23 | VALUE(0x01); 24 | 25 | public static ValueType getValueTypeByPersistentId(int persistentId) 26 | { 27 | switch (persistentId) { 28 | case 0: 29 | return DELETION; 30 | case 1: 31 | return VALUE; 32 | default: 33 | throw new IllegalArgumentException("Unknown persistentId " + persistentId); 34 | } 35 | } 36 | 37 | private final int persistentId; 38 | 39 | ValueType(int persistentId) 40 | { 41 | this.persistentId = persistentId; 42 | } 43 | 44 | public int getPersistentId() 45 | { 46 | return persistentId; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/impl/WriteBatchImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import com.google.common.base.Preconditions; 21 | import com.google.common.collect.Maps; 22 | import org.iq80.leveldb.WriteBatch; 23 | import org.iq80.leveldb.util.Slice; 24 | import org.iq80.leveldb.util.Slices; 25 | 26 | import java.util.List; 27 | import java.util.Map.Entry; 28 | 29 | import static com.google.common.collect.Lists.newArrayList; 30 | 31 | public class WriteBatchImpl 32 | implements WriteBatch 33 | { 34 | private final List> batch = newArrayList(); 35 | private int approximateSize; 36 | 37 | public int getApproximateSize() 38 | { 39 | return approximateSize; 40 | } 41 | 42 | public int size() 43 | { 44 | return batch.size(); 45 | } 46 | 47 | @Override 48 | public WriteBatchImpl put(byte[] key, byte[] value) 49 | { 50 | Preconditions.checkNotNull(key, "key is null"); 51 | Preconditions.checkNotNull(value, "value is null"); 52 | batch.add(Maps.immutableEntry(Slices.wrappedBuffer(key), Slices.wrappedBuffer(value))); 53 | approximateSize += 12 + key.length + value.length; 54 | return this; 55 | } 56 | 57 | public WriteBatchImpl put(Slice key, Slice value) 58 | { 59 | Preconditions.checkNotNull(key, "key is null"); 60 | Preconditions.checkNotNull(value, "value is null"); 61 | batch.add(Maps.immutableEntry(key, value)); 62 | approximateSize += 12 + key.length() + value.length(); 63 | return this; 64 | } 65 | 66 | @Override 67 | public WriteBatchImpl delete(byte[] key) 68 | { 69 | Preconditions.checkNotNull(key, "key is null"); 70 | batch.add(Maps.immutableEntry(Slices.wrappedBuffer(key), (Slice) null)); 71 | approximateSize += 6 + key.length; 72 | return this; 73 | } 74 | 75 | public WriteBatchImpl delete(Slice key) 76 | { 77 | Preconditions.checkNotNull(key, "key is null"); 78 | batch.add(Maps.immutableEntry(key, (Slice) null)); 79 | approximateSize += 6 + key.length(); 80 | return this; 81 | } 82 | 83 | @Override 84 | public void close() 85 | { 86 | } 87 | 88 | public void forEach(Handler handler) 89 | { 90 | for (Entry entry : batch) { 91 | Slice key = entry.getKey(); 92 | Slice value = entry.getValue(); 93 | if (value != null) { 94 | handler.put(key, value); 95 | } 96 | else { 97 | handler.delete(key); 98 | } 99 | } 100 | } 101 | 102 | public interface Handler 103 | { 104 | void put(Slice key, Slice value); 105 | 106 | void delete(Slice key); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/table/Block.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.table; 19 | 20 | import com.google.common.base.Preconditions; 21 | import org.iq80.leveldb.impl.SeekingIterable; 22 | import org.iq80.leveldb.util.Slice; 23 | import org.iq80.leveldb.util.Slices; 24 | 25 | import java.util.Comparator; 26 | 27 | import static org.iq80.leveldb.util.SizeOf.SIZE_OF_INT; 28 | 29 | /** 30 | * Binary Structure 31 | *
32 | * 33 | * 34 | * 35 | * 36 | * 37 | * 38 | * 39 | * 40 | * 41 | *

42 | *

43 | * 44 | * 45 | * 46 | * 47 | * 48 | * 49 | * 50 | * 51 | * 52 | * 53 | * 54 | * 55 | * 56 | * 57 | * 58 | * 59 | * 60 | * 61 | *
nameoffsetlengthdescription
entries4varyEntries in order by key
restart indexvary4 * restart countIndex of prefix compression restarts
restart count04Number of prefix compression restarts (used as index into entries)
62 | */ 63 | public class Block 64 | implements SeekingIterable 65 | { 66 | private final Slice block; 67 | private final Comparator comparator; 68 | 69 | private final Slice data; 70 | private final Slice restartPositions; 71 | 72 | public Block(Slice block, Comparator comparator) 73 | { 74 | Preconditions.checkNotNull(block, "block is null"); 75 | Preconditions.checkArgument(block.length() >= SIZE_OF_INT, "Block is corrupt: size must be at least %s block", SIZE_OF_INT); 76 | Preconditions.checkNotNull(comparator, "comparator is null"); 77 | 78 | block = block.slice(); 79 | this.block = block; 80 | this.comparator = comparator; 81 | 82 | // Keys are prefix compressed. Every once in a while the prefix compression is restarted and the full key is written. 83 | // These "restart" locations are written at the end of the file, so you can seek to key without having to read the 84 | // entire file sequentially. 85 | 86 | // key restart count is the last int of the block 87 | int restartCount = block.getInt(block.length() - SIZE_OF_INT); 88 | 89 | if (restartCount > 0) { 90 | // restarts are written at the end of the block 91 | int restartOffset = block.length() - (1 + restartCount) * SIZE_OF_INT; 92 | Preconditions.checkArgument(restartOffset < block.length() - SIZE_OF_INT, "Block is corrupt: restart offset count is greater than block size"); 93 | restartPositions = block.slice(restartOffset, restartCount * SIZE_OF_INT); 94 | 95 | // data starts at 0 and extends to the restart index 96 | data = block.slice(0, restartOffset); 97 | } 98 | else { 99 | data = Slices.EMPTY_SLICE; 100 | restartPositions = Slices.EMPTY_SLICE; 101 | } 102 | } 103 | 104 | public long size() 105 | { 106 | return block.length(); 107 | } 108 | 109 | @Override 110 | public BlockIterator iterator() 111 | { 112 | return new BlockIterator(data, restartPositions, comparator); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/table/BlockEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.table; 19 | 20 | import com.google.common.base.Preconditions; 21 | import org.iq80.leveldb.util.Slice; 22 | 23 | import java.util.Map.Entry; 24 | 25 | import static com.google.common.base.Charsets.UTF_8; 26 | 27 | /** 28 | * Binary Structure 29 | * 30 | * 31 | * 32 | * 33 | * 34 | * 35 | * 36 | * 37 | * 38 | * 39 | *

40 | *

41 | * 42 | * 43 | * 44 | * 45 | * 46 | * 47 | * 48 | * 49 | * 50 | * 51 | * 52 | * 53 | * 54 | * 55 | * 56 | * 57 | * 58 | * 59 | * 60 | * 61 | * 62 | * 63 | * 64 | * 65 | * 66 | * 67 | * 68 | * 69 | * 70 | * 71 | *
nameoffsetlengthdescription
shared key length0varyvariable length encoded int: size of shared key prefix with the key from the previous entry
non-shared key lengthvaryvaryvariable length encoded int: size of non-shared key suffix in this entry
value lengthvaryvaryvariable length encoded int: size of value in this entry
non-shared keyvarynon-shared key lengthnon-shared key data
valuevaryvalue lengthvalue data
72 | */ 73 | public class BlockEntry 74 | implements Entry 75 | { 76 | private final Slice key; 77 | private final Slice value; 78 | 79 | public BlockEntry(Slice key, Slice value) 80 | { 81 | Preconditions.checkNotNull(key, "key is null"); 82 | Preconditions.checkNotNull(value, "value is null"); 83 | this.key = key; 84 | this.value = value; 85 | } 86 | 87 | @Override 88 | public Slice getKey() 89 | { 90 | return key; 91 | } 92 | 93 | @Override 94 | public Slice getValue() 95 | { 96 | return value; 97 | } 98 | 99 | /** 100 | * @throws UnsupportedOperationException always 101 | */ 102 | @Override 103 | public final Slice setValue(Slice value) 104 | { 105 | throw new UnsupportedOperationException(); 106 | } 107 | 108 | @Override 109 | public boolean equals(Object o) 110 | { 111 | if (this == o) { 112 | return true; 113 | } 114 | if (o == null || getClass() != o.getClass()) { 115 | return false; 116 | } 117 | 118 | BlockEntry entry = (BlockEntry) o; 119 | 120 | if (!key.equals(entry.key)) { 121 | return false; 122 | } 123 | if (!value.equals(entry.value)) { 124 | return false; 125 | } 126 | 127 | return true; 128 | } 129 | 130 | @Override 131 | public int hashCode() 132 | { 133 | int result = key.hashCode(); 134 | result = 31 * result + value.hashCode(); 135 | return result; 136 | } 137 | 138 | @Override 139 | public String toString() 140 | { 141 | StringBuilder sb = new StringBuilder(); 142 | sb.append("BlockEntry"); 143 | sb.append("{key=").append(key.toString(UTF_8)); // todo don't print the real value 144 | sb.append(", value=").append(value.toString(UTF_8)); 145 | sb.append('}'); 146 | return sb.toString(); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/table/BlockHandle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.table; 19 | 20 | import org.iq80.leveldb.util.Slice; 21 | import org.iq80.leveldb.util.SliceInput; 22 | import org.iq80.leveldb.util.SliceOutput; 23 | import org.iq80.leveldb.util.Slices; 24 | import org.iq80.leveldb.util.VariableLengthQuantity; 25 | 26 | public class BlockHandle 27 | { 28 | public static final int MAX_ENCODED_LENGTH = 10 + 10; 29 | 30 | private final long offset; 31 | private final int dataSize; 32 | 33 | BlockHandle(long offset, int dataSize) 34 | { 35 | this.offset = offset; 36 | this.dataSize = dataSize; 37 | } 38 | 39 | public long getOffset() 40 | { 41 | return offset; 42 | } 43 | 44 | public int getDataSize() 45 | { 46 | return dataSize; 47 | } 48 | 49 | public int getFullBlockSize() 50 | { 51 | return dataSize + BlockTrailer.ENCODED_LENGTH; 52 | } 53 | 54 | @Override 55 | public boolean equals(Object o) 56 | { 57 | if (this == o) { 58 | return true; 59 | } 60 | if (o == null || getClass() != o.getClass()) { 61 | return false; 62 | } 63 | 64 | BlockHandle that = (BlockHandle) o; 65 | 66 | if (dataSize != that.dataSize) { 67 | return false; 68 | } 69 | if (offset != that.offset) { 70 | return false; 71 | } 72 | 73 | return true; 74 | } 75 | 76 | @Override 77 | public int hashCode() 78 | { 79 | int result = (int) (offset ^ (offset >>> 32)); 80 | result = 31 * result + dataSize; 81 | return result; 82 | } 83 | 84 | @Override 85 | public String toString() 86 | { 87 | StringBuilder sb = new StringBuilder(); 88 | sb.append("BlockHandle"); 89 | sb.append("{offset=").append(offset); 90 | sb.append(", dataSize=").append(dataSize); 91 | sb.append('}'); 92 | return sb.toString(); 93 | } 94 | 95 | public static BlockHandle readBlockHandle(SliceInput sliceInput) 96 | { 97 | long offset = VariableLengthQuantity.readVariableLengthLong(sliceInput); 98 | long size = VariableLengthQuantity.readVariableLengthLong(sliceInput); 99 | 100 | if (size > Integer.MAX_VALUE) { 101 | throw new IllegalArgumentException("Blocks can not be larger than Integer.MAX_VALUE"); 102 | } 103 | 104 | return new BlockHandle(offset, (int) size); 105 | } 106 | 107 | public static Slice writeBlockHandle(BlockHandle blockHandle) 108 | { 109 | Slice slice = Slices.allocate(MAX_ENCODED_LENGTH); 110 | SliceOutput sliceOutput = slice.output(); 111 | writeBlockHandleTo(blockHandle, sliceOutput); 112 | return slice.slice(); 113 | } 114 | 115 | public static void writeBlockHandleTo(BlockHandle blockHandle, SliceOutput sliceOutput) 116 | { 117 | VariableLengthQuantity.writeVariableLengthLong(blockHandle.offset, sliceOutput); 118 | VariableLengthQuantity.writeVariableLengthLong(blockHandle.dataSize, sliceOutput); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/table/BlockTrailer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.table; 19 | 20 | import com.google.common.base.Preconditions; 21 | import org.iq80.leveldb.CompressionType; 22 | import org.iq80.leveldb.util.Slice; 23 | import org.iq80.leveldb.util.SliceInput; 24 | import org.iq80.leveldb.util.SliceOutput; 25 | import org.iq80.leveldb.util.Slices; 26 | 27 | public class BlockTrailer 28 | { 29 | public static final int ENCODED_LENGTH = 5; 30 | 31 | private final CompressionType compressionType; 32 | private final int crc32c; 33 | 34 | public BlockTrailer(CompressionType compressionType, int crc32c) 35 | { 36 | Preconditions.checkNotNull(compressionType, "compressionType is null"); 37 | 38 | this.compressionType = compressionType; 39 | this.crc32c = crc32c; 40 | } 41 | 42 | public CompressionType getCompressionType() 43 | { 44 | return compressionType; 45 | } 46 | 47 | public int getCrc32c() 48 | { 49 | return crc32c; 50 | } 51 | 52 | @Override 53 | public boolean equals(Object o) 54 | { 55 | if (this == o) { 56 | return true; 57 | } 58 | if (o == null || getClass() != o.getClass()) { 59 | return false; 60 | } 61 | 62 | BlockTrailer that = (BlockTrailer) o; 63 | 64 | if (crc32c != that.crc32c) { 65 | return false; 66 | } 67 | if (compressionType != that.compressionType) { 68 | return false; 69 | } 70 | 71 | return true; 72 | } 73 | 74 | @Override 75 | public int hashCode() 76 | { 77 | int result = compressionType.hashCode(); 78 | result = 31 * result + crc32c; 79 | return result; 80 | } 81 | 82 | @Override 83 | public String toString() 84 | { 85 | StringBuilder sb = new StringBuilder(); 86 | sb.append("BlockTrailer"); 87 | sb.append("{compressionType=").append(compressionType); 88 | sb.append(", crc32c=0x").append(Integer.toHexString(crc32c)); 89 | sb.append('}'); 90 | return sb.toString(); 91 | } 92 | 93 | public static BlockTrailer readBlockTrailer(Slice slice) 94 | { 95 | SliceInput sliceInput = slice.input(); 96 | CompressionType compressionType = CompressionType.getCompressionTypeByPersistentId(sliceInput.readUnsignedByte()); 97 | int crc32c = sliceInput.readInt(); 98 | return new BlockTrailer(compressionType, crc32c); 99 | } 100 | 101 | public static Slice writeBlockTrailer(BlockTrailer blockTrailer) 102 | { 103 | Slice slice = Slices.allocate(ENCODED_LENGTH); 104 | writeBlockTrailer(blockTrailer, slice.output()); 105 | return slice; 106 | } 107 | 108 | public static void writeBlockTrailer(BlockTrailer blockTrailer, SliceOutput sliceOutput) 109 | { 110 | sliceOutput.writeByte(blockTrailer.getCompressionType().persistentId()); 111 | sliceOutput.writeInt(blockTrailer.getCrc32c()); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/table/BytewiseComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.table; 19 | 20 | import org.iq80.leveldb.util.Slice; 21 | 22 | public class BytewiseComparator 23 | implements UserComparator 24 | { 25 | @Override 26 | public String name() 27 | { 28 | return "leveldb.BytewiseComparator"; 29 | } 30 | 31 | @Override 32 | public int compare(Slice sliceA, Slice sliceB) 33 | { 34 | return sliceA.compareTo(sliceB); 35 | } 36 | 37 | @Override 38 | public Slice findShortestSeparator( 39 | Slice start, 40 | Slice limit) 41 | { 42 | // Find length of common prefix 43 | int sharedBytes = BlockBuilder.calculateSharedBytes(start, limit); 44 | 45 | // Do not shorten if one string is a prefix of the other 46 | if (sharedBytes < Math.min(start.length(), limit.length())) { 47 | // if we can add one to the last shared byte without overflow and the two keys differ by more than 48 | // one increment at this location. 49 | int lastSharedByte = start.getUnsignedByte(sharedBytes); 50 | if (lastSharedByte < 0xff && lastSharedByte + 1 < limit.getUnsignedByte(sharedBytes)) { 51 | Slice result = start.copySlice(0, sharedBytes + 1); 52 | result.setByte(sharedBytes, lastSharedByte + 1); 53 | 54 | assert (compare(result, limit) < 0) : "start must be less than last limit"; 55 | return result; 56 | } 57 | } 58 | return start; 59 | } 60 | 61 | @Override 62 | public Slice findShortSuccessor(Slice key) 63 | { 64 | // Find first character that can be incremented 65 | for (int i = 0; i < key.length(); i++) { 66 | int b = key.getUnsignedByte(i); 67 | if (b != 0xff) { 68 | Slice result = key.copySlice(0, i + 1); 69 | result.setByte(i, b + 1); 70 | return result; 71 | } 72 | } 73 | // key is a run of 0xffs. Leave it alone. 74 | return key; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/table/CustomUserComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.table; 19 | 20 | import org.iq80.leveldb.DBComparator; 21 | import org.iq80.leveldb.util.Slice; 22 | 23 | public class CustomUserComparator 24 | implements UserComparator 25 | { 26 | private final DBComparator comparator; 27 | 28 | public CustomUserComparator(DBComparator comparator) 29 | { 30 | this.comparator = comparator; 31 | } 32 | 33 | @Override 34 | public String name() 35 | { 36 | return comparator.name(); 37 | } 38 | 39 | @Override 40 | public Slice findShortestSeparator(Slice start, Slice limit) 41 | { 42 | return new Slice(comparator.findShortestSeparator(start.getBytes(), limit.getBytes())); 43 | } 44 | 45 | @Override 46 | public Slice findShortSuccessor(Slice key) 47 | { 48 | return new Slice(comparator.findShortSuccessor(key.getBytes())); 49 | } 50 | 51 | @Override 52 | public int compare(Slice o1, Slice o2) 53 | { 54 | return comparator.compare(o1.getBytes(), o2.getBytes()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/table/FileChannelTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.table; 19 | 20 | import org.iq80.leveldb.util.Slice; 21 | import org.iq80.leveldb.util.Slices; 22 | import org.iq80.leveldb.util.Snappy; 23 | import org.iq80.leveldb.util.Zlib; 24 | 25 | import java.io.IOException; 26 | import java.nio.ByteBuffer; 27 | import java.nio.channels.FileChannel; 28 | import java.util.Comparator; 29 | 30 | import static org.iq80.leveldb.CompressionType.SNAPPY; 31 | import static org.iq80.leveldb.CompressionType.ZLIB; 32 | 33 | public class FileChannelTable 34 | extends Table 35 | { 36 | public FileChannelTable(String name, FileChannel fileChannel, Comparator comparator, boolean verifyChecksums) 37 | throws IOException 38 | { 39 | super(name, fileChannel, comparator, verifyChecksums); 40 | } 41 | 42 | @Override 43 | protected Footer init() 44 | throws IOException 45 | { 46 | long size = fileChannel.size(); 47 | ByteBuffer footerData = read(size - Footer.ENCODED_LENGTH, Footer.ENCODED_LENGTH); 48 | return Footer.readFooter(Slices.copiedBuffer(footerData)); 49 | } 50 | 51 | @SuppressWarnings({"AssignmentToStaticFieldFromInstanceMethod", "NonPrivateFieldAccessedInSynchronizedContext"}) 52 | @Override 53 | protected Block readBlock(BlockHandle blockHandle) 54 | throws IOException 55 | { 56 | // read block trailer 57 | ByteBuffer trailerData = read(blockHandle.getOffset() + blockHandle.getDataSize(), BlockTrailer.ENCODED_LENGTH); 58 | BlockTrailer blockTrailer = BlockTrailer.readBlockTrailer(Slices.copiedBuffer(trailerData)); 59 | 60 | // todo re-enable crc check when ported to support direct buffers 61 | // // only verify check sums if explicitly asked by the user 62 | // if (verifyChecksums) { 63 | // // checksum data and the compression type in the trailer 64 | // PureJavaCrc32C checksum = new PureJavaCrc32C(); 65 | // checksum.update(data.getRawArray(), data.getRawOffset(), blockHandle.getDataSize() + 1); 66 | // int actualCrc32c = checksum.getMaskedValue(); 67 | // 68 | // Preconditions.checkState(blockTrailer.getCrc32c() == actualCrc32c, "Block corrupted: checksum mismatch"); 69 | // } 70 | 71 | // decompress data 72 | 73 | ByteBuffer uncompressedBuffer = read(blockHandle.getOffset(), blockHandle.getDataSize()); 74 | Slice uncompressedData; 75 | if (blockTrailer.getCompressionType() == ZLIB) { 76 | synchronized (FileChannelTable.class) { 77 | int uncompressedLength = uncompressedLength(uncompressedBuffer); 78 | if (uncompressedScratch.capacity() < uncompressedLength) { 79 | uncompressedScratch = ByteBuffer.allocateDirect(uncompressedLength); 80 | } 81 | uncompressedScratch.clear(); 82 | 83 | Zlib.uncompress(uncompressedBuffer, uncompressedScratch); 84 | uncompressedData = Slices.copiedBuffer(uncompressedScratch); 85 | } 86 | } 87 | else if (blockTrailer.getCompressionType() == SNAPPY) { 88 | synchronized (FileChannelTable.class) { 89 | int uncompressedLength = uncompressedLength(uncompressedBuffer); 90 | if (uncompressedScratch.capacity() < uncompressedLength) { 91 | uncompressedScratch = ByteBuffer.allocateDirect(uncompressedLength); 92 | } 93 | uncompressedScratch.clear(); 94 | 95 | Snappy.uncompress(uncompressedBuffer, uncompressedScratch); 96 | uncompressedData = Slices.copiedBuffer(uncompressedScratch); 97 | } 98 | } 99 | else { 100 | uncompressedData = Slices.copiedBuffer(uncompressedBuffer); 101 | } 102 | 103 | return new Block(uncompressedData, comparator); 104 | } 105 | 106 | private ByteBuffer read(long offset, int length) 107 | throws IOException 108 | { 109 | ByteBuffer uncompressedBuffer = ByteBuffer.allocate(length); 110 | fileChannel.read(uncompressedBuffer, offset); 111 | if (uncompressedBuffer.hasRemaining()) { 112 | throw new IOException("Could not read all the data"); 113 | } 114 | uncompressedBuffer.clear(); 115 | return uncompressedBuffer; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/table/Footer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.table; 19 | 20 | import com.google.common.base.Preconditions; 21 | import org.iq80.leveldb.util.Slice; 22 | import org.iq80.leveldb.util.SliceInput; 23 | import org.iq80.leveldb.util.SliceOutput; 24 | import org.iq80.leveldb.util.Slices; 25 | 26 | import static org.iq80.leveldb.table.BlockHandle.readBlockHandle; 27 | import static org.iq80.leveldb.table.BlockHandle.writeBlockHandleTo; 28 | import static org.iq80.leveldb.util.SizeOf.SIZE_OF_LONG; 29 | 30 | public class Footer 31 | { 32 | public static final int ENCODED_LENGTH = (BlockHandle.MAX_ENCODED_LENGTH * 2) + SIZE_OF_LONG; 33 | 34 | private final BlockHandle metaindexBlockHandle; 35 | private final BlockHandle indexBlockHandle; 36 | 37 | Footer(BlockHandle metaindexBlockHandle, BlockHandle indexBlockHandle) 38 | { 39 | this.metaindexBlockHandle = metaindexBlockHandle; 40 | this.indexBlockHandle = indexBlockHandle; 41 | } 42 | 43 | public BlockHandle getMetaindexBlockHandle() 44 | { 45 | return metaindexBlockHandle; 46 | } 47 | 48 | public BlockHandle getIndexBlockHandle() 49 | { 50 | return indexBlockHandle; 51 | } 52 | 53 | public static Footer readFooter(Slice slice) 54 | { 55 | Preconditions.checkNotNull(slice, "slice is null"); 56 | Preconditions.checkArgument(slice.length() == ENCODED_LENGTH, "Expected slice.size to be %s but was %s", ENCODED_LENGTH, slice.length()); 57 | 58 | SliceInput sliceInput = slice.input(); 59 | 60 | // read metaindex and index handles 61 | BlockHandle metaindexBlockHandle = readBlockHandle(sliceInput); 62 | BlockHandle indexBlockHandle = readBlockHandle(sliceInput); 63 | 64 | // skip padding 65 | sliceInput.setPosition(ENCODED_LENGTH - SIZE_OF_LONG); 66 | 67 | // verify magic number 68 | long magicNumber = sliceInput.readUnsignedInt() | (sliceInput.readUnsignedInt() << 32); 69 | Preconditions.checkArgument(magicNumber == TableBuilder.TABLE_MAGIC_NUMBER, "File is not a table (bad magic number)"); 70 | 71 | return new Footer(metaindexBlockHandle, indexBlockHandle); 72 | } 73 | 74 | public static Slice writeFooter(Footer footer) 75 | { 76 | Slice slice = Slices.allocate(ENCODED_LENGTH); 77 | writeFooter(footer, slice.output()); 78 | return slice; 79 | } 80 | 81 | public static void writeFooter(Footer footer, SliceOutput sliceOutput) 82 | { 83 | // remember the starting write index so we can calculate the padding 84 | int startingWriteIndex = sliceOutput.size(); 85 | 86 | // write metaindex and index handles 87 | writeBlockHandleTo(footer.getMetaindexBlockHandle(), sliceOutput); 88 | writeBlockHandleTo(footer.getIndexBlockHandle(), sliceOutput); 89 | 90 | // write padding 91 | sliceOutput.writeZero(ENCODED_LENGTH - SIZE_OF_LONG - (sliceOutput.size() - startingWriteIndex)); 92 | 93 | // write magic number as two (little endian) integers 94 | sliceOutput.writeInt((int) TableBuilder.TABLE_MAGIC_NUMBER); 95 | sliceOutput.writeInt((int) (TableBuilder.TABLE_MAGIC_NUMBER >>> 32)); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/table/Table.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.table; 19 | 20 | import com.google.common.base.Preconditions; 21 | import com.google.common.base.Throwables; 22 | import org.iq80.leveldb.impl.SeekingIterable; 23 | import org.iq80.leveldb.util.Closeables; 24 | import org.iq80.leveldb.util.Slice; 25 | import org.iq80.leveldb.util.TableIterator; 26 | import org.iq80.leveldb.util.VariableLengthQuantity; 27 | 28 | import java.io.Closeable; 29 | import java.io.IOException; 30 | import java.nio.ByteBuffer; 31 | import java.nio.channels.FileChannel; 32 | import java.util.Comparator; 33 | import java.util.concurrent.Callable; 34 | 35 | public abstract class Table 36 | implements SeekingIterable 37 | { 38 | protected final String name; 39 | protected final FileChannel fileChannel; 40 | protected final Comparator comparator; 41 | protected final boolean verifyChecksums; 42 | protected final Block indexBlock; 43 | protected final BlockHandle metaindexBlockHandle; 44 | 45 | public Table(String name, FileChannel fileChannel, Comparator comparator, boolean verifyChecksums) 46 | throws IOException 47 | { 48 | Preconditions.checkNotNull(name, "name is null"); 49 | Preconditions.checkNotNull(fileChannel, "fileChannel is null"); 50 | long size = fileChannel.size(); 51 | Preconditions.checkArgument(size >= Footer.ENCODED_LENGTH, "File is corrupt: size must be at least %s bytes", Footer.ENCODED_LENGTH); 52 | Preconditions.checkArgument(size <= Integer.MAX_VALUE, "File must be smaller than %s bytes", Integer.MAX_VALUE); 53 | Preconditions.checkNotNull(comparator, "comparator is null"); 54 | 55 | this.name = name; 56 | this.fileChannel = fileChannel; 57 | this.verifyChecksums = verifyChecksums; 58 | this.comparator = comparator; 59 | 60 | Footer footer = init(); 61 | indexBlock = readBlock(footer.getIndexBlockHandle()); 62 | metaindexBlockHandle = footer.getMetaindexBlockHandle(); 63 | } 64 | 65 | protected abstract Footer init() 66 | throws IOException; 67 | 68 | @Override 69 | public TableIterator iterator() 70 | { 71 | return new TableIterator(this, indexBlock.iterator()); 72 | } 73 | 74 | public Block openBlock(Slice blockEntry) 75 | { 76 | BlockHandle blockHandle = BlockHandle.readBlockHandle(blockEntry.input()); 77 | Block dataBlock; 78 | try { 79 | dataBlock = readBlock(blockHandle); 80 | } 81 | catch (IOException e) { 82 | throw Throwables.propagate(e); 83 | } 84 | return dataBlock; 85 | } 86 | 87 | protected static ByteBuffer uncompressedScratch = ByteBuffer.allocateDirect(4 * 1024 * 1024); 88 | 89 | protected abstract Block readBlock(BlockHandle blockHandle) 90 | throws IOException; 91 | 92 | protected int uncompressedLength(ByteBuffer data) 93 | throws IOException 94 | { 95 | int length = VariableLengthQuantity.readVariableLengthInt(data.duplicate()); 96 | return length; 97 | } 98 | 99 | /** 100 | * Given a key, return an approximate byte offset in the file where 101 | * the data for that key begins (or would begin if the key were 102 | * present in the file). The returned value is in terms of file 103 | * bytes, and so includes effects like compression of the underlying data. 104 | * For example, the approximate offset of the last key in the table will 105 | * be close to the file length. 106 | */ 107 | public long getApproximateOffsetOf(Slice key) 108 | { 109 | BlockIterator iterator = indexBlock.iterator(); 110 | iterator.seek(key); 111 | if (iterator.hasNext()) { 112 | BlockHandle blockHandle = BlockHandle.readBlockHandle(iterator.next().getValue().input()); 113 | return blockHandle.getOffset(); 114 | } 115 | 116 | // key is past the last key in the file. Approximate the offset 117 | // by returning the offset of the metaindex block (which is 118 | // right near the end of the file). 119 | return metaindexBlockHandle.getOffset(); 120 | } 121 | 122 | @Override 123 | public String toString() 124 | { 125 | StringBuilder sb = new StringBuilder(); 126 | sb.append("Table"); 127 | sb.append("{name='").append(name).append('\''); 128 | sb.append(", comparator=").append(comparator); 129 | sb.append(", verifyChecksums=").append(verifyChecksums); 130 | sb.append('}'); 131 | return sb.toString(); 132 | } 133 | 134 | public Callable closer() 135 | { 136 | return new Closer(fileChannel); 137 | } 138 | 139 | private static class Closer 140 | implements Callable 141 | { 142 | private final Closeable closeable; 143 | 144 | public Closer(Closeable closeable) 145 | { 146 | this.closeable = closeable; 147 | } 148 | 149 | @Override 150 | public Void call() 151 | { 152 | Closeables.closeQuietly(closeable); 153 | return null; 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/table/UserComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.table; 19 | 20 | import org.iq80.leveldb.util.Slice; 21 | 22 | import java.util.Comparator; 23 | 24 | // todo this interface needs more thought 25 | public interface UserComparator 26 | extends Comparator 27 | { 28 | String name(); 29 | 30 | Slice findShortestSeparator(Slice start, Slice limit); 31 | 32 | Slice findShortSuccessor(Slice key); 33 | } 34 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/util/AbstractSeekingIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.util; 19 | 20 | import org.iq80.leveldb.impl.SeekingIterator; 21 | 22 | import java.util.Map.Entry; 23 | import java.util.NoSuchElementException; 24 | 25 | public abstract class AbstractSeekingIterator 26 | implements SeekingIterator 27 | { 28 | private Entry nextElement; 29 | 30 | @Override 31 | public final void seekToFirst() 32 | { 33 | nextElement = null; 34 | seekToFirstInternal(); 35 | } 36 | 37 | @Override 38 | public final void seek(K targetKey) 39 | { 40 | nextElement = null; 41 | seekInternal(targetKey); 42 | } 43 | 44 | @Override 45 | public final boolean hasNext() 46 | { 47 | if (nextElement == null) { 48 | nextElement = getNextElement(); 49 | } 50 | return nextElement != null; 51 | } 52 | 53 | @Override 54 | public final Entry next() 55 | { 56 | if (nextElement == null) { 57 | nextElement = getNextElement(); 58 | if (nextElement == null) { 59 | throw new NoSuchElementException(); 60 | } 61 | } 62 | 63 | Entry result = nextElement; 64 | nextElement = null; 65 | return result; 66 | } 67 | 68 | @Override 69 | public final Entry peek() 70 | { 71 | if (nextElement == null) { 72 | nextElement = getNextElement(); 73 | if (nextElement == null) { 74 | throw new NoSuchElementException(); 75 | } 76 | } 77 | 78 | return nextElement; 79 | } 80 | 81 | @Override 82 | public final void remove() 83 | { 84 | throw new UnsupportedOperationException(); 85 | } 86 | 87 | protected abstract void seekToFirstInternal(); 88 | 89 | protected abstract void seekInternal(K targetKey); 90 | 91 | protected abstract Entry getNextElement(); 92 | } 93 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/util/ByteBufferSupport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.util; 19 | 20 | import com.google.common.base.Throwables; 21 | import sun.nio.ch.FileChannelImpl; 22 | 23 | import java.lang.reflect.Method; 24 | import java.nio.MappedByteBuffer; 25 | 26 | public final class ByteBufferSupport 27 | { 28 | private static final Method unmap; 29 | 30 | static { 31 | Method x; 32 | try { 33 | x = FileChannelImpl.class.getDeclaredMethod("unmap", MappedByteBuffer.class); 34 | } 35 | catch (NoSuchMethodException e) { 36 | throw new AssertionError(e); 37 | } 38 | x.setAccessible(true); 39 | unmap = x; 40 | } 41 | 42 | private ByteBufferSupport() 43 | { 44 | } 45 | 46 | public static void unmap(MappedByteBuffer buffer) 47 | { 48 | try { 49 | unmap.invoke(null, buffer); 50 | } 51 | catch (Exception ignored) { 52 | throw Throwables.propagate(ignored); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/util/Closeables.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.util; 19 | 20 | import java.io.Closeable; 21 | import java.io.IOException; 22 | 23 | public final class Closeables 24 | { 25 | private Closeables() 26 | { 27 | } 28 | 29 | public static void closeQuietly(Closeable closeable) 30 | { 31 | if (closeable == null) { 32 | return; 33 | } 34 | try { 35 | closeable.close(); 36 | } 37 | catch (IOException ignored) { 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/util/IntVector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.util; 19 | 20 | import com.google.common.base.Preconditions; 21 | 22 | import java.util.Arrays; 23 | 24 | public class IntVector 25 | { 26 | private int size; 27 | private int[] values; 28 | 29 | public IntVector(int initialCapacity) 30 | { 31 | this.values = new int[initialCapacity]; 32 | } 33 | 34 | public int size() 35 | { 36 | return size; 37 | } 38 | 39 | public void clear() 40 | { 41 | size = 0; 42 | } 43 | 44 | public void add(int value) 45 | { 46 | Preconditions.checkArgument(size + 1 >= 0, "Invalid minLength: %s", size + 1); 47 | 48 | ensureCapacity(size + 1); 49 | 50 | values[size++] = value; 51 | } 52 | 53 | private void ensureCapacity(int minCapacity) 54 | { 55 | if (values.length >= minCapacity) { 56 | return; 57 | } 58 | 59 | int newLength = values.length; 60 | if (newLength == 0) { 61 | newLength = 1; 62 | } 63 | else { 64 | newLength <<= 1; 65 | 66 | } 67 | values = Arrays.copyOf(values, newLength); 68 | } 69 | 70 | public int[] values() 71 | { 72 | return Arrays.copyOf(values, size); 73 | } 74 | 75 | public void write(SliceOutput sliceOutput) 76 | { 77 | for (int index = 0; index < size; index++) { 78 | sliceOutput.writeInt(values[index]); 79 | } 80 | } 81 | 82 | @Override 83 | public String toString() 84 | { 85 | StringBuilder sb = new StringBuilder(); 86 | sb.append("IntVector"); 87 | sb.append("{size=").append(size); 88 | sb.append(", values=").append(Arrays.toString(values)); 89 | sb.append('}'); 90 | return sb.toString(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/util/InternalIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.util; 19 | 20 | import org.iq80.leveldb.impl.InternalKey; 21 | import org.iq80.leveldb.impl.SeekingIterator; 22 | 23 | /** 24 | *

A common interface for internal iterators.

25 | * 26 | * @author Hiram Chirino 27 | */ 28 | public interface InternalIterator 29 | extends SeekingIterator 30 | { 31 | } 32 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/util/InternalTableIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.util; 19 | 20 | import com.google.common.collect.Maps; 21 | import org.iq80.leveldb.impl.InternalKey; 22 | 23 | import java.util.Map.Entry; 24 | 25 | public class InternalTableIterator 26 | extends AbstractSeekingIterator 27 | implements InternalIterator 28 | { 29 | private final TableIterator tableIterator; 30 | 31 | public InternalTableIterator(TableIterator tableIterator) 32 | { 33 | this.tableIterator = tableIterator; 34 | } 35 | 36 | @Override 37 | protected void seekToFirstInternal() 38 | { 39 | tableIterator.seekToFirst(); 40 | } 41 | 42 | @Override 43 | public void seekInternal(InternalKey targetKey) 44 | { 45 | tableIterator.seek(targetKey.encode()); 46 | } 47 | 48 | @Override 49 | protected Entry getNextElement() 50 | { 51 | if (tableIterator.hasNext()) { 52 | Entry next = tableIterator.next(); 53 | return Maps.immutableEntry(new InternalKey(next.getKey()), next.getValue()); 54 | } 55 | return null; 56 | } 57 | 58 | @Override 59 | public String toString() 60 | { 61 | StringBuilder sb = new StringBuilder(); 62 | sb.append("InternalTableIterator"); 63 | sb.append("{fromIterator=").append(tableIterator); 64 | sb.append('}'); 65 | return sb.toString(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/util/LevelIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.util; 19 | 20 | import org.iq80.leveldb.impl.FileMetaData; 21 | import org.iq80.leveldb.impl.InternalKey; 22 | import org.iq80.leveldb.impl.InternalKeyComparator; 23 | import org.iq80.leveldb.impl.TableCache; 24 | 25 | import java.util.List; 26 | import java.util.Map.Entry; 27 | 28 | public final class LevelIterator 29 | extends AbstractSeekingIterator 30 | implements InternalIterator 31 | { 32 | private final TableCache tableCache; 33 | private final List files; 34 | private final InternalKeyComparator comparator; 35 | private InternalTableIterator current; 36 | private int index; 37 | 38 | public LevelIterator(TableCache tableCache, List files, InternalKeyComparator comparator) 39 | { 40 | this.tableCache = tableCache; 41 | this.files = files; 42 | this.comparator = comparator; 43 | } 44 | 45 | @Override 46 | protected void seekToFirstInternal() 47 | { 48 | // reset index to before first and clear the data iterator 49 | index = 0; 50 | current = null; 51 | } 52 | 53 | @Override 54 | protected void seekInternal(InternalKey targetKey) 55 | { 56 | // seek the index to the block containing the key 57 | if (files.isEmpty()) { 58 | return; 59 | } 60 | 61 | // todo replace with Collections.binarySearch 62 | int left = 0; 63 | int right = files.size() - 1; 64 | 65 | // binary search restart positions to find the restart position immediately before the targetKey 66 | while (left < right) { 67 | int mid = (left + right) / 2; 68 | 69 | if (comparator.compare(files.get(mid).getLargest(), targetKey) < 0) { 70 | // Key at "mid.largest" is < "target". Therefore all 71 | // files at or before "mid" are uninteresting. 72 | left = mid + 1; 73 | } 74 | else { 75 | // Key at "mid.largest" is >= "target". Therefore all files 76 | // after "mid" are uninteresting. 77 | right = mid; 78 | } 79 | } 80 | index = right; 81 | 82 | // if the index is now pointing to the last block in the file, check if the largest key 83 | // in the block is than the the target key. If so, we need to seek beyond the end of this file 84 | if (index == files.size() - 1 && comparator.compare(files.get(index).getLargest(), targetKey) < 0) { 85 | index++; 86 | } 87 | 88 | // if indexIterator does not have a next, it mean the key does not exist in this iterator 89 | if (index < files.size()) { 90 | // seek the current iterator to the key 91 | current = openNextFile(); 92 | current.seek(targetKey); 93 | } 94 | else { 95 | current = null; 96 | } 97 | } 98 | 99 | @Override 100 | protected Entry getNextElement() 101 | { 102 | // note: it must be here & not where 'current' is assigned, 103 | // because otherwise we'll have called inputs.next() before throwing 104 | // the first NPE, and the next time around we'll call inputs.next() 105 | // again, incorrectly moving beyond the error. 106 | boolean currentHasNext = false; 107 | while (true) { 108 | if (current != null) { 109 | currentHasNext = current.hasNext(); 110 | } 111 | if (!(currentHasNext)) { 112 | if (index < files.size()) { 113 | current = openNextFile(); 114 | } 115 | else { 116 | break; 117 | } 118 | } 119 | else { 120 | break; 121 | } 122 | } 123 | if (currentHasNext) { 124 | return current.next(); 125 | } 126 | else { 127 | // set current to empty iterator to avoid extra calls to user iterators 128 | current = null; 129 | return null; 130 | } 131 | } 132 | 133 | private InternalTableIterator openNextFile() 134 | { 135 | FileMetaData fileMetaData = files.get(index); 136 | index++; 137 | return tableCache.newIterator(fileMetaData); 138 | } 139 | 140 | @Override 141 | public String toString() 142 | { 143 | StringBuilder sb = new StringBuilder(); 144 | sb.append("ConcatenatingIterator"); 145 | sb.append("{index=").append(index); 146 | sb.append(", files=").append(files); 147 | sb.append(", current=").append(current); 148 | sb.append('}'); 149 | return sb.toString(); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/util/SizeOf.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.util; 19 | 20 | public final class SizeOf 21 | { 22 | public static final byte SIZE_OF_BYTE = 1; 23 | public static final byte SIZE_OF_SHORT = 2; 24 | public static final byte SIZE_OF_INT = 4; 25 | public static final byte SIZE_OF_LONG = 8; 26 | 27 | private SizeOf() 28 | { 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/util/SliceComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.util; 19 | 20 | import java.util.Comparator; 21 | 22 | public final class SliceComparator 23 | implements Comparator 24 | { 25 | public static final SliceComparator SLICE_COMPARATOR = new SliceComparator(); 26 | 27 | @Override 28 | public int compare(Slice sliceA, Slice sliceB) 29 | { 30 | return sliceA.compareTo(sliceB); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/util/TableIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.util; 19 | 20 | import org.iq80.leveldb.table.Block; 21 | import org.iq80.leveldb.table.BlockIterator; 22 | import org.iq80.leveldb.table.Table; 23 | 24 | import java.util.Map.Entry; 25 | 26 | public final class TableIterator 27 | extends AbstractSeekingIterator 28 | { 29 | private final Table table; 30 | private final BlockIterator blockIterator; 31 | private BlockIterator current; 32 | 33 | public TableIterator(Table table, BlockIterator blockIterator) 34 | { 35 | this.table = table; 36 | this.blockIterator = blockIterator; 37 | current = null; 38 | } 39 | 40 | @Override 41 | protected void seekToFirstInternal() 42 | { 43 | // reset index to before first and clear the data iterator 44 | blockIterator.seekToFirst(); 45 | current = null; 46 | } 47 | 48 | @Override 49 | protected void seekInternal(Slice targetKey) 50 | { 51 | // seek the index to the block containing the key 52 | blockIterator.seek(targetKey); 53 | 54 | // if indexIterator does not have a next, it mean the key does not exist in this iterator 55 | if (blockIterator.hasNext()) { 56 | // seek the current iterator to the key 57 | current = getNextBlock(); 58 | current.seek(targetKey); 59 | } 60 | else { 61 | current = null; 62 | } 63 | } 64 | 65 | @Override 66 | protected Entry getNextElement() 67 | { 68 | // note: it must be here & not where 'current' is assigned, 69 | // because otherwise we'll have called inputs.next() before throwing 70 | // the first NPE, and the next time around we'll call inputs.next() 71 | // again, incorrectly moving beyond the error. 72 | boolean currentHasNext = false; 73 | while (true) { 74 | if (current != null) { 75 | currentHasNext = current.hasNext(); 76 | } 77 | if (!(currentHasNext)) { 78 | if (blockIterator.hasNext()) { 79 | current = getNextBlock(); 80 | } 81 | else { 82 | break; 83 | } 84 | } 85 | else { 86 | break; 87 | } 88 | } 89 | if (currentHasNext) { 90 | return current.next(); 91 | } 92 | else { 93 | // set current to empty iterator to avoid extra calls to user iterators 94 | current = null; 95 | return null; 96 | } 97 | } 98 | 99 | private BlockIterator getNextBlock() 100 | { 101 | Slice blockHandle = blockIterator.next().getValue(); 102 | Block dataBlock = table.openBlock(blockHandle); 103 | return dataBlock.iterator(); 104 | } 105 | 106 | @Override 107 | public String toString() 108 | { 109 | StringBuilder sb = new StringBuilder(); 110 | sb.append("ConcatenatingIterator"); 111 | sb.append("{blockIterator=").append(blockIterator); 112 | sb.append(", current=").append(current); 113 | sb.append('}'); 114 | return sb.toString(); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /leveldb/src/main/java/org/iq80/leveldb/util/VariableLengthQuantity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.util; 19 | 20 | import java.nio.ByteBuffer; 21 | 22 | public final class VariableLengthQuantity 23 | { 24 | private VariableLengthQuantity() 25 | { 26 | } 27 | 28 | public static int variableLengthSize(int value) 29 | { 30 | int size = 1; 31 | while ((value & (~0x7f)) != 0) { 32 | value >>>= 7; 33 | size++; 34 | } 35 | return size; 36 | } 37 | 38 | public static int variableLengthSize(long value) 39 | { 40 | int size = 1; 41 | while ((value & (~0x7f)) != 0) { 42 | value >>>= 7; 43 | size++; 44 | } 45 | return size; 46 | } 47 | 48 | public static void writeVariableLengthInt(int value, SliceOutput sliceOutput) 49 | { 50 | int highBitMask = 0x80; 51 | if (value < (1 << 7) && value >= 0) { 52 | sliceOutput.writeByte(value); 53 | } 54 | else if (value < (1 << 14) && value > 0) { 55 | sliceOutput.writeByte(value | highBitMask); 56 | sliceOutput.writeByte(value >>> 7); 57 | } 58 | else if (value < (1 << 21) && value > 0) { 59 | sliceOutput.writeByte(value | highBitMask); 60 | sliceOutput.writeByte((value >>> 7) | highBitMask); 61 | sliceOutput.writeByte(value >>> 14); 62 | } 63 | else if (value < (1 << 28) && value > 0) { 64 | sliceOutput.writeByte(value | highBitMask); 65 | sliceOutput.writeByte((value >>> 7) | highBitMask); 66 | sliceOutput.writeByte((value >>> 14) | highBitMask); 67 | sliceOutput.writeByte(value >>> 21); 68 | } 69 | else { 70 | sliceOutput.writeByte(value | highBitMask); 71 | sliceOutput.writeByte((value >>> 7) | highBitMask); 72 | sliceOutput.writeByte((value >>> 14) | highBitMask); 73 | sliceOutput.writeByte((value >>> 21) | highBitMask); 74 | sliceOutput.writeByte(value >>> 28); 75 | } 76 | } 77 | 78 | public static void writeVariableLengthLong(long value, SliceOutput sliceOutput) 79 | { 80 | // while value more than the first 7 bits set 81 | while ((value & (~0x7f)) != 0) { 82 | sliceOutput.writeByte((int) ((value & 0x7f) | 0x80)); 83 | value >>>= 7; 84 | } 85 | sliceOutput.writeByte((int) value); 86 | } 87 | 88 | public static int readVariableLengthInt(SliceInput sliceInput) 89 | { 90 | int result = 0; 91 | for (int shift = 0; shift <= 28; shift += 7) { 92 | int b = sliceInput.readUnsignedByte(); 93 | 94 | // add the lower 7 bits to the result 95 | result |= ((b & 0x7f) << shift); 96 | 97 | // if high bit is not set, this is the last byte in the number 98 | if ((b & 0x80) == 0) { 99 | return result; 100 | } 101 | } 102 | throw new NumberFormatException("last byte of variable length int has high bit set"); 103 | } 104 | 105 | public static int readVariableLengthInt(ByteBuffer sliceInput) 106 | { 107 | int result = 0; 108 | for (int shift = 0; shift <= 28; shift += 7) { 109 | int b = sliceInput.get(); 110 | 111 | // add the lower 7 bits to the result 112 | result |= ((b & 0x7f) << shift); 113 | 114 | // if high bit is not set, this is the last byte in the number 115 | if ((b & 0x80) == 0) { 116 | return result; 117 | } 118 | } 119 | throw new NumberFormatException("last byte of variable length int has high bit set"); 120 | } 121 | 122 | public static long readVariableLengthLong(SliceInput sliceInput) 123 | { 124 | long result = 0; 125 | for (int shift = 0; shift <= 63; shift += 7) { 126 | long b = sliceInput.readUnsignedByte(); 127 | 128 | // add the lower 7 bits to the result 129 | result |= ((b & 0x7f) << shift); 130 | 131 | // if high bit is not set, this is the last byte in the number 132 | if ((b & 0x80) == 0) { 133 | return result; 134 | } 135 | } 136 | throw new NumberFormatException("last byte of variable length int has high bit set"); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /leveldb/src/main/resources/org/iq80/leveldb/impl/version.txt: -------------------------------------------------------------------------------- 1 | ${project.version} -------------------------------------------------------------------------------- /leveldb/src/test/java/org/iq80/leveldb/impl/ApiTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import org.iq80.leveldb.CompressionType; 21 | import org.iq80.leveldb.DB; 22 | import org.iq80.leveldb.DBException; 23 | import org.iq80.leveldb.DBFactory; 24 | import org.iq80.leveldb.Options; 25 | import org.iq80.leveldb.util.FileUtils; 26 | import org.testng.annotations.Test; 27 | 28 | import java.io.File; 29 | import java.io.IOException; 30 | import java.io.UnsupportedEncodingException; 31 | import java.util.Arrays; 32 | 33 | import static org.testng.Assert.assertTrue; 34 | 35 | /** 36 | * Test the implemenation via the org.iq80.leveldb API. 37 | * 38 | * @author Hiram Chirino 39 | */ 40 | public class ApiTest 41 | { 42 | private final File databaseDir = FileUtils.createTempDir("leveldb"); 43 | 44 | public static byte[] bytes(String value) 45 | { 46 | if (value == null) { 47 | return null; 48 | } 49 | try { 50 | return value.getBytes("UTF-8"); 51 | } 52 | catch (UnsupportedEncodingException e) { 53 | throw new RuntimeException(e); 54 | } 55 | } 56 | 57 | public static String asString(byte[] value) 58 | { 59 | if (value == null) { 60 | return null; 61 | } 62 | try { 63 | return new String(value, "UTF-8"); 64 | } 65 | catch (UnsupportedEncodingException e) { 66 | throw new RuntimeException(e); 67 | } 68 | } 69 | 70 | public void assertEquals(byte[] arg1, byte[] arg2) 71 | { 72 | assertTrue(Arrays.equals(arg1, arg2), asString(arg1) + " != " + asString(arg2)); 73 | } 74 | 75 | private final DBFactory factory = Iq80DBFactory.factory; 76 | 77 | File getTestDirectory(String name) 78 | throws IOException 79 | { 80 | File rc = new File(databaseDir, name); 81 | factory.destroy(rc, new Options().createIfMissing(true)); 82 | rc.mkdirs(); 83 | return rc; 84 | } 85 | 86 | @Test 87 | public void testCompaction() 88 | throws IOException, DBException 89 | { 90 | Options options = new Options().createIfMissing(true).compressionType(CompressionType.NONE); 91 | 92 | File path = getTestDirectory("testCompaction"); 93 | DB db = factory.open(path, options); 94 | 95 | System.out.println("Adding"); 96 | for (int i = 0; i < 1000 * 1000; i++) { 97 | if (i % 100000 == 0) { 98 | System.out.println(" at: " + i); 99 | } 100 | db.put(bytes("key" + i), bytes("value" + i)); 101 | } 102 | 103 | db.close(); 104 | db = factory.open(path, options); 105 | 106 | System.out.println("Deleting"); 107 | for (int i = 0; i < 1000 * 1000; i++) { 108 | if (i % 100000 == 0) { 109 | System.out.println(" at: " + i); 110 | } 111 | db.delete(bytes("key" + i)); 112 | } 113 | 114 | db.close(); 115 | db = factory.open(path, options); 116 | 117 | System.out.println("Adding"); 118 | for (int i = 0; i < 1000 * 1000; i++) { 119 | if (i % 100000 == 0) { 120 | System.out.println(" at: " + i); 121 | } 122 | db.put(bytes("key" + i), bytes("value" + i)); 123 | } 124 | 125 | db.close(); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /leveldb/src/test/java/org/iq80/leveldb/impl/NativeInteropTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import org.iq80.leveldb.DB; 21 | import org.iq80.leveldb.DBException; 22 | import org.iq80.leveldb.DBFactory; 23 | import org.iq80.leveldb.Options; 24 | import org.iq80.leveldb.ReadOptions; 25 | import org.iq80.leveldb.WriteOptions; 26 | import org.iq80.leveldb.util.FileUtils; 27 | import org.testng.annotations.Test; 28 | 29 | import java.io.File; 30 | import java.io.IOException; 31 | import java.io.UnsupportedEncodingException; 32 | import java.util.Arrays; 33 | import java.util.concurrent.atomic.AtomicInteger; 34 | 35 | import static org.testng.Assert.assertNull; 36 | import static org.testng.Assert.assertTrue; 37 | 38 | /** 39 | * @author Hiram Chirino 40 | */ 41 | public class NativeInteropTest 42 | { 43 | private static final AtomicInteger NEXT_ID = new AtomicInteger(); 44 | 45 | private final File databaseDir = FileUtils.createTempDir("leveldb"); 46 | 47 | public static byte[] bytes(String value) 48 | { 49 | if (value == null) { 50 | return null; 51 | } 52 | try { 53 | return value.getBytes("UTF-8"); 54 | } 55 | catch (UnsupportedEncodingException e) { 56 | throw new RuntimeException(e); 57 | } 58 | } 59 | 60 | public static String asString(byte[] value) 61 | { 62 | if (value == null) { 63 | return null; 64 | } 65 | try { 66 | return new String(value, "UTF-8"); 67 | } 68 | catch (UnsupportedEncodingException e) { 69 | throw new RuntimeException(e); 70 | } 71 | } 72 | 73 | public static void assertEquals(byte[] arg1, byte[] arg2) 74 | { 75 | assertTrue(Arrays.equals(arg1, arg2), asString(arg1) + " != " + asString(arg2)); 76 | } 77 | 78 | private final DBFactory iq80factory = Iq80DBFactory.factory; 79 | private final DBFactory jnifactory; 80 | 81 | public NativeInteropTest() 82 | { 83 | DBFactory jnifactory = Iq80DBFactory.factory; 84 | try { 85 | ClassLoader cl = NativeInteropTest.class.getClassLoader(); 86 | jnifactory = (DBFactory) cl.loadClass("org.fusesource.leveldbjni.JniDBFactory").newInstance(); 87 | } 88 | catch (Throwable e) { 89 | // We cannot create a JniDBFactory on windows :( so just use a Iq80DBFactory for both 90 | // to avoid test failures. 91 | } 92 | this.jnifactory = jnifactory; 93 | } 94 | 95 | File getTestDirectory(String name) 96 | throws IOException 97 | { 98 | File rc = new File(databaseDir, name); 99 | iq80factory.destroy(rc, new Options().createIfMissing(true)); 100 | rc.mkdirs(); 101 | return rc; 102 | } 103 | 104 | @Test 105 | public void testCRUDviaIQ80() 106 | throws IOException, DBException 107 | { 108 | crud(iq80factory, iq80factory); 109 | } 110 | 111 | @Test 112 | public void testCRUDviaJNI() 113 | throws IOException, DBException 114 | { 115 | crud(jnifactory, jnifactory); 116 | } 117 | 118 | @Test 119 | public void testCRUDviaIQ80thenJNI() 120 | throws IOException, DBException 121 | { 122 | crud(iq80factory, jnifactory); 123 | } 124 | 125 | @Test 126 | public void testCRUDviaJNIthenIQ80() 127 | throws IOException, DBException 128 | { 129 | crud(jnifactory, iq80factory); 130 | } 131 | 132 | public void crud(DBFactory firstFactory, DBFactory secondFactory) 133 | throws IOException, DBException 134 | { 135 | Options options = new Options().createIfMissing(true); 136 | 137 | File path = getTestDirectory(getClass().getName() + "_" + NEXT_ID.incrementAndGet()); 138 | DB db = firstFactory.open(path, options); 139 | 140 | WriteOptions wo = new WriteOptions().sync(false); 141 | ReadOptions ro = new ReadOptions().fillCache(true).verifyChecksums(true); 142 | db.put(bytes("Tampa"), bytes("green")); 143 | db.put(bytes("London"), bytes("red")); 144 | db.put(bytes("New York"), bytes("blue")); 145 | 146 | db.close(); 147 | db = secondFactory.open(path, options); 148 | 149 | assertEquals(db.get(bytes("Tampa"), ro), bytes("green")); 150 | assertEquals(db.get(bytes("London"), ro), bytes("red")); 151 | assertEquals(db.get(bytes("New York"), ro), bytes("blue")); 152 | 153 | db.delete(bytes("New York"), wo); 154 | 155 | assertEquals(db.get(bytes("Tampa"), ro), bytes("green")); 156 | assertEquals(db.get(bytes("London"), ro), bytes("red")); 157 | assertNull(db.get(bytes("New York"), ro)); 158 | 159 | db.close(); 160 | db = firstFactory.open(path, options); 161 | 162 | assertEquals(db.get(bytes("Tampa"), ro), bytes("green")); 163 | assertEquals(db.get(bytes("London"), ro), bytes("red")); 164 | assertNull(db.get(bytes("New York"), ro)); 165 | 166 | db.close(); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /leveldb/src/test/java/org/iq80/leveldb/impl/TestFileChannelLogWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import org.iq80.leveldb.util.Slice; 21 | import org.testng.annotations.Test; 22 | 23 | import java.io.File; 24 | import java.io.FileInputStream; 25 | import java.nio.channels.FileChannel; 26 | 27 | import static org.testng.Assert.assertEquals; 28 | import static org.testng.Assert.fail; 29 | 30 | public class TestFileChannelLogWriter 31 | { 32 | @Test 33 | public void testLogRecordBounds() 34 | throws Exception 35 | { 36 | File file = File.createTempFile("test", ".log"); 37 | try { 38 | int recordSize = LogConstants.BLOCK_SIZE - LogConstants.HEADER_SIZE; 39 | Slice record = new Slice(recordSize); 40 | 41 | LogWriter writer = new FileChannelLogWriter(file, 10); 42 | writer.addRecord(record, false); 43 | writer.close(); 44 | 45 | LogMonitor logMonitor = new AssertNoCorruptionLogMonitor(); 46 | 47 | FileChannel channel = new FileInputStream(file).getChannel(); 48 | 49 | LogReader logReader = new LogReader(channel, logMonitor, true, 0); 50 | 51 | int count = 0; 52 | for (Slice slice = logReader.readRecord(); slice != null; slice = logReader.readRecord()) { 53 | assertEquals(slice.length(), recordSize); 54 | count++; 55 | } 56 | assertEquals(count, 1); 57 | } 58 | finally { 59 | file.delete(); 60 | } 61 | } 62 | 63 | private static class AssertNoCorruptionLogMonitor 64 | implements LogMonitor 65 | { 66 | @Override 67 | public void corruption(long bytes, String reason) 68 | { 69 | fail("corruption at " + bytes + " reason: " + reason); 70 | } 71 | 72 | @Override 73 | public void corruption(long bytes, Throwable reason) 74 | { 75 | fail("corruption at " + bytes + " reason: " + reason); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /leveldb/src/test/java/org/iq80/leveldb/impl/TestMMapLogWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.impl; 19 | 20 | import org.iq80.leveldb.util.Slice; 21 | import org.testng.annotations.Test; 22 | 23 | import java.io.File; 24 | import java.io.FileInputStream; 25 | import java.nio.channels.FileChannel; 26 | 27 | import static org.testng.Assert.assertEquals; 28 | import static org.testng.Assert.fail; 29 | 30 | public class TestMMapLogWriter 31 | { 32 | @Test 33 | public void testLogRecordBounds() 34 | throws Exception 35 | { 36 | File file = File.createTempFile("test", ".log"); 37 | try { 38 | int recordSize = LogConstants.BLOCK_SIZE - LogConstants.HEADER_SIZE; 39 | Slice record = new Slice(recordSize); 40 | 41 | LogWriter writer = new MMapLogWriter(file, 10); 42 | writer.addRecord(record, false); 43 | writer.close(); 44 | 45 | LogMonitor logMonitor = new AssertNoCorruptionLogMonitor(); 46 | 47 | FileChannel channel = new FileInputStream(file).getChannel(); 48 | 49 | LogReader logReader = new LogReader(channel, logMonitor, true, 0); 50 | 51 | int count = 0; 52 | for (Slice slice = logReader.readRecord(); slice != null; slice = logReader.readRecord()) { 53 | assertEquals(slice.length(), recordSize); 54 | count++; 55 | } 56 | assertEquals(count, 1); 57 | } 58 | finally { 59 | file.delete(); 60 | } 61 | } 62 | 63 | private static class AssertNoCorruptionLogMonitor 64 | implements LogMonitor 65 | { 66 | @Override 67 | public void corruption(long bytes, String reason) 68 | { 69 | fail("corruption at " + bytes + " reason: " + reason); 70 | } 71 | 72 | @Override 73 | public void corruption(long bytes, Throwable reason) 74 | { 75 | fail("corruption at " + bytes + " reason: " + reason); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /leveldb/src/test/java/org/iq80/leveldb/table/BlockTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.table; 19 | 20 | import org.iq80.leveldb.util.Slice; 21 | import org.iq80.leveldb.util.Slices; 22 | import org.testng.annotations.Test; 23 | 24 | import java.util.Collections; 25 | import java.util.List; 26 | 27 | import static java.util.Arrays.asList; 28 | import static org.testng.Assert.assertEquals; 29 | 30 | public class BlockTest 31 | { 32 | @Test(expectedExceptions = IllegalArgumentException.class) 33 | public void testEmptyBuffer() 34 | throws Exception 35 | { 36 | new Block(Slices.EMPTY_SLICE, new BytewiseComparator()); 37 | } 38 | 39 | @Test 40 | public void testEmptyBlock() 41 | throws Exception 42 | { 43 | blockTest(Integer.MAX_VALUE); 44 | } 45 | 46 | @Test 47 | public void testSingleEntry() 48 | throws Exception 49 | { 50 | blockTest(Integer.MAX_VALUE, 51 | BlockHelper.createBlockEntry("name", "dain sundstrom")); 52 | } 53 | 54 | @Test 55 | public void testMultipleEntriesWithNonSharedKey() 56 | throws Exception 57 | { 58 | blockTest(Integer.MAX_VALUE, 59 | BlockHelper.createBlockEntry("beer", "Lagunitas IPA"), 60 | BlockHelper.createBlockEntry("scotch", "Highland Park")); 61 | } 62 | 63 | @Test 64 | public void testMultipleEntriesWithSharedKey() 65 | throws Exception 66 | { 67 | blockTest(Integer.MAX_VALUE, 68 | BlockHelper.createBlockEntry("beer/ale", "Lagunitas Little Sumpin’ Sumpin’"), 69 | BlockHelper.createBlockEntry("beer/ipa", "Lagunitas IPA"), 70 | BlockHelper.createBlockEntry("scotch", "Highland Park")); 71 | } 72 | 73 | @Test 74 | public void testMultipleEntriesWithNonSharedKeyAndRestartPositions() 75 | throws Exception 76 | { 77 | List entries = asList( 78 | BlockHelper.createBlockEntry("ale", "Lagunitas Little Sumpin’ Sumpin’"), 79 | BlockHelper.createBlockEntry("ipa", "Lagunitas IPA"), 80 | BlockHelper.createBlockEntry("stout", "Lagunitas Imperial Stout"), 81 | BlockHelper.createBlockEntry("strong", "Lagavulin")); 82 | 83 | for (int i = 1; i < entries.size(); i++) { 84 | blockTest(i, entries); 85 | } 86 | } 87 | 88 | @Test 89 | public void testMultipleEntriesWithSharedKeyAndRestartPositions() 90 | throws Exception 91 | { 92 | List entries = asList( 93 | BlockHelper.createBlockEntry("beer/ale", "Lagunitas Little Sumpin’ Sumpin’"), 94 | BlockHelper.createBlockEntry("beer/ipa", "Lagunitas IPA"), 95 | BlockHelper.createBlockEntry("beer/stout", "Lagunitas Imperial Stout"), 96 | BlockHelper.createBlockEntry("scotch/light", "Oban 14"), 97 | BlockHelper.createBlockEntry("scotch/medium", "Highland Park"), 98 | BlockHelper.createBlockEntry("scotch/strong", "Lagavulin")); 99 | 100 | for (int i = 1; i < entries.size(); i++) { 101 | blockTest(i, entries); 102 | } 103 | } 104 | 105 | private static void blockTest(int blockRestartInterval, BlockEntry... entries) 106 | { 107 | blockTest(blockRestartInterval, asList(entries)); 108 | } 109 | 110 | private static void blockTest(int blockRestartInterval, List entries) 111 | { 112 | BlockBuilder builder = new BlockBuilder(256, blockRestartInterval, new BytewiseComparator()); 113 | 114 | for (BlockEntry entry : entries) { 115 | builder.add(entry); 116 | } 117 | 118 | assertEquals(builder.currentSizeEstimate(), BlockHelper.estimateBlockSize(blockRestartInterval, entries)); 119 | Slice blockSlice = builder.finish(); 120 | assertEquals(builder.currentSizeEstimate(), BlockHelper.estimateBlockSize(blockRestartInterval, entries)); 121 | 122 | Block block = new Block(blockSlice, new BytewiseComparator()); 123 | assertEquals(block.size(), BlockHelper.estimateBlockSize(blockRestartInterval, entries)); 124 | 125 | BlockIterator blockIterator = block.iterator(); 126 | BlockHelper.assertSequence(blockIterator, entries); 127 | 128 | blockIterator.seekToFirst(); 129 | BlockHelper.assertSequence(blockIterator, entries); 130 | 131 | for (BlockEntry entry : entries) { 132 | List nextEntries = entries.subList(entries.indexOf(entry), entries.size()); 133 | blockIterator.seek(entry.getKey()); 134 | BlockHelper.assertSequence(blockIterator, nextEntries); 135 | 136 | blockIterator.seek(BlockHelper.before(entry)); 137 | BlockHelper.assertSequence(blockIterator, nextEntries); 138 | 139 | blockIterator.seek(BlockHelper.after(entry)); 140 | BlockHelper.assertSequence(blockIterator, nextEntries.subList(1, nextEntries.size())); 141 | } 142 | 143 | blockIterator.seek(Slices.wrappedBuffer(new byte[] {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF})); 144 | BlockHelper.assertSequence(blockIterator, Collections.emptyList()); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /leveldb/src/test/java/org/iq80/leveldb/table/FileChannelTableTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.table; 19 | 20 | import org.iq80.leveldb.util.Slice; 21 | 22 | import java.io.IOException; 23 | import java.nio.channels.FileChannel; 24 | import java.util.Comparator; 25 | 26 | public class FileChannelTableTest 27 | extends TableTest 28 | { 29 | @Override 30 | protected Table createTable(String name, FileChannel fileChannel, Comparator comparator, boolean verifyChecksums) 31 | throws IOException 32 | { 33 | return new FileChannelTable(name, fileChannel, comparator, verifyChecksums); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /leveldb/src/test/java/org/iq80/leveldb/table/MMapTableTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.table; 19 | 20 | import org.iq80.leveldb.util.Slice; 21 | 22 | import java.io.IOException; 23 | import java.nio.channels.FileChannel; 24 | import java.util.Comparator; 25 | 26 | public class MMapTableTest 27 | extends TableTest 28 | { 29 | @Override 30 | protected Table createTable(String name, FileChannel fileChannel, Comparator comparator, boolean verifyChecksums) 31 | throws IOException 32 | { 33 | return new MMapTable(name, fileChannel, comparator, verifyChecksums); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /leveldb/src/test/java/org/iq80/leveldb/util/PureJavaCrc32CTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.util; 19 | 20 | import com.google.common.base.Function; 21 | import org.testng.annotations.DataProvider; 22 | import org.testng.annotations.Test; 23 | 24 | import java.io.UnsupportedEncodingException; 25 | import java.util.Arrays; 26 | 27 | import static org.iq80.leveldb.util.PureJavaCrc32C.mask; 28 | import static org.iq80.leveldb.util.PureJavaCrc32C.unmask; 29 | import static org.testng.Assert.assertEquals; 30 | import static org.testng.Assert.assertFalse; 31 | 32 | public class PureJavaCrc32CTest 33 | { 34 | @Test(dataProvider = "crcs") 35 | public void testCrc(int expectedCrc, byte[] data) 36 | { 37 | assertEquals(expectedCrc, computeCrc(data)); 38 | } 39 | 40 | @DataProvider(name = "crcs") 41 | public Object[][] data() 42 | { 43 | return new Object[][] { 44 | new Object[] {0x8a9136aa, arrayOf(32, (byte) 0)}, 45 | new Object[] {0x62a8ab43, arrayOf(32, (byte) 0xff)}, 46 | new Object[] {0x46dd794e, arrayOf(32, new Function() 47 | { 48 | @Override 49 | public Byte apply(Integer position) 50 | { 51 | return (byte) position.intValue(); 52 | } 53 | })}, 54 | new Object[] {0x113fdb5c, arrayOf(32, new Function() 55 | { 56 | @Override 57 | public Byte apply(Integer position) 58 | { 59 | return (byte) (31 - position); 60 | } 61 | })}, 62 | new Object[] {0xd9963a56, arrayOf(new int[] { 63 | 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 64 | 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 65 | 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00, 66 | 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})} 67 | }; 68 | } 69 | 70 | @Test 71 | public void testProducesDifferentCrcs() 72 | throws UnsupportedEncodingException 73 | { 74 | assertFalse(computeCrc("a".getBytes("ASCII")) == computeCrc("foo".getBytes("ASCII"))); 75 | } 76 | 77 | @Test 78 | public void testComposes() 79 | throws UnsupportedEncodingException 80 | { 81 | PureJavaCrc32C crc = new PureJavaCrc32C(); 82 | crc.update("hello ".getBytes("ASCII"), 0, 6); 83 | crc.update("world".getBytes("ASCII"), 0, 5); 84 | 85 | assertEquals(crc.getIntValue(), computeCrc("hello world".getBytes("ASCII"))); 86 | } 87 | 88 | @Test 89 | public void testMask() 90 | throws UnsupportedEncodingException 91 | { 92 | PureJavaCrc32C crc = new PureJavaCrc32C(); 93 | crc.update("foo".getBytes("ASCII"), 0, 3); 94 | 95 | assertEquals(crc.getMaskedValue(), mask(crc.getIntValue())); 96 | assertFalse(crc.getIntValue() == crc.getMaskedValue(), "crc should not match masked crc"); 97 | assertFalse(crc.getIntValue() == mask(crc.getMaskedValue()), "crc should not match double masked crc"); 98 | assertEquals(crc.getIntValue(), unmask(crc.getMaskedValue())); 99 | assertEquals(crc.getIntValue(), unmask(unmask(mask(crc.getMaskedValue())))); 100 | } 101 | 102 | private static int computeCrc(byte[] data) 103 | { 104 | PureJavaCrc32C crc = new PureJavaCrc32C(); 105 | crc.update(data, 0, data.length); 106 | return crc.getIntValue(); 107 | } 108 | 109 | private static byte[] arrayOf(int size, byte value) 110 | { 111 | byte[] result = new byte[size]; 112 | Arrays.fill(result, value); 113 | return result; 114 | } 115 | 116 | @SuppressWarnings("ConstantConditions") 117 | private static byte[] arrayOf(int size, Function generator) 118 | { 119 | byte[] result = new byte[size]; 120 | for (int i = 0; i < result.length; ++i) { 121 | result[i] = generator.apply(i); 122 | } 123 | 124 | return result; 125 | } 126 | 127 | private static byte[] arrayOf(int[] bytes) 128 | { 129 | byte[] result = new byte[bytes.length]; 130 | for (int i = 0; i < result.length; ++i) { 131 | result[i] = (byte) bytes[i]; 132 | } 133 | 134 | return result; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /leveldb/src/test/java/org/iq80/leveldb/util/SliceComparatorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.util; 19 | 20 | import com.google.common.base.Charsets; 21 | import org.testng.annotations.Test; 22 | 23 | import static org.iq80.leveldb.util.SliceComparator.SLICE_COMPARATOR; 24 | import static org.testng.Assert.assertEquals; 25 | import static org.testng.Assert.assertTrue; 26 | 27 | public class SliceComparatorTest 28 | { 29 | @Test 30 | public void testSliceComparison() 31 | { 32 | assertTrue(SLICE_COMPARATOR.compare( 33 | Slices.copiedBuffer("beer/ipa", Charsets.UTF_8), 34 | Slices.copiedBuffer("beer/ale", Charsets.UTF_8)) 35 | > 0); 36 | 37 | assertTrue(SLICE_COMPARATOR.compare( 38 | Slices.wrappedBuffer(new byte[] {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}), 39 | Slices.wrappedBuffer(new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00})) 40 | > 0); 41 | 42 | assertTrue(SLICE_COMPARATOR.compare( 43 | Slices.wrappedBuffer(new byte[] {(byte) 0xFF}), 44 | Slices.wrappedBuffer(new byte[] {(byte) 0x00})) 45 | > 0); 46 | 47 | assertAllEqual(Slices.copiedBuffer("abcdefghijklmnopqrstuvwxyz", Charsets.UTF_8), 48 | Slices.copiedBuffer("abcdefghijklmnopqrstuvwxyz", Charsets.UTF_8)); 49 | } 50 | 51 | public static void assertAllEqual(Slice left, Slice right) 52 | { 53 | for (int i = 0; i < left.length(); i++) { 54 | assertEquals(SLICE_COMPARATOR.compare(left.slice(0, i), right.slice(0, i)), 0); 55 | assertEquals(SLICE_COMPARATOR.compare(right.slice(0, i), left.slice(0, i)), 0); 56 | } 57 | // differ in last byte only 58 | for (int i = 1; i < left.length(); i++) { 59 | Slice slice = right.slice(0, i); 60 | int lastReadableByte = slice.length() - 1; 61 | slice.setByte(lastReadableByte, slice.getByte(lastReadableByte) + 1); 62 | assertTrue(SLICE_COMPARATOR.compare(left.slice(0, i), slice) < 0); 63 | assertTrue(SLICE_COMPARATOR.compare(slice, left.slice(0, i)) > 0); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /leveldb/src/test/java/org/iq80/leveldb/util/VariableLengthQuantityTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 the original author or authors. 3 | * See the notice.md file distributed with this work for additional 4 | * information regarding copyright ownership. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.iq80.leveldb.util; 19 | 20 | import org.testng.annotations.Test; 21 | 22 | import static org.testng.Assert.assertEquals; 23 | 24 | public class VariableLengthQuantityTest 25 | { 26 | @Test 27 | public void testWriteVariableLengthInt() 28 | { 29 | testVariableLengthInt(0x0); 30 | testVariableLengthInt(0xf); 31 | testVariableLengthInt(0xff); 32 | testVariableLengthInt(0xfff); 33 | testVariableLengthInt(0xffff); 34 | testVariableLengthInt(0xfffff); 35 | testVariableLengthInt(0xffffff); 36 | testVariableLengthInt(0xfffffff); 37 | testVariableLengthInt(0xffffffff); 38 | } 39 | 40 | private static void testVariableLengthInt(int value) 41 | { 42 | SliceOutput output = Slices.allocate(5).output(); 43 | VariableLengthQuantity.writeVariableLengthInt(value, output); 44 | assertEquals(output.size(), VariableLengthQuantity.variableLengthSize(value)); 45 | int actual = VariableLengthQuantity.readVariableLengthInt(output.slice().input()); 46 | assertEquals(actual, value); 47 | } 48 | 49 | @Test 50 | public void testWriteVariableLengthLong() 51 | { 52 | testVariableLengthLong(0x0L); 53 | testVariableLengthLong(0xfL); 54 | testVariableLengthLong(0xffL); 55 | testVariableLengthLong(0xfffL); 56 | testVariableLengthLong(0xffffL); 57 | testVariableLengthLong(0xfffffL); 58 | testVariableLengthLong(0xffffffL); 59 | testVariableLengthLong(0xfffffffL); 60 | testVariableLengthLong(0xffffffffL); 61 | testVariableLengthLong(0xfffffffffL); 62 | testVariableLengthLong(0xffffffffffL); 63 | testVariableLengthLong(0xfffffffffffL); 64 | testVariableLengthLong(0xffffffffffffL); 65 | testVariableLengthLong(0xfffffffffffffL); 66 | testVariableLengthLong(0xffffffffffffffL); 67 | testVariableLengthLong(0xfffffffffffffffL); 68 | testVariableLengthLong(0xffffffffffffffffL); 69 | } 70 | 71 | private static void testVariableLengthLong(long value) 72 | { 73 | SliceOutput output = Slices.allocate(12).output(); 74 | VariableLengthQuantity.writeVariableLengthLong(value, output); 75 | assertEquals(output.size(), VariableLengthQuantity.variableLengthSize(value)); 76 | long actual = VariableLengthQuantity.readVariableLengthLong(output.slice().input()); 77 | assertEquals(actual, value); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /notice.md: -------------------------------------------------------------------------------- 1 | LevelDB Copyright Notices 2 | ========================= 3 | 4 | * Copyright 2011 Dain Sundstrom 5 | * Copyright 2011 FuseSource Corp. http://fusesource.com 6 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | io.airlift 7 | airbase 8 | 28 9 | 10 | 11 | com.tinfoiled.mcpe.leveldb 12 | leveldb-project 13 | 0.8-SNAPSHOT 14 | pom 15 | 16 | Port of LevelDB to Java 17 | http://github.com/dain/leveldb 18 | 19 | 20 | leveldb-api 21 | leveldb 22 | 23 | 24 | 2011 25 | 26 | 27 | 28 | Apache License 2.0 29 | http://www.apache.org/licenses/LICENSE-2.0.html 30 | repo 31 | 32 | 33 | 34 | 35 | 36 | dain 37 | Dain Sundstrom 38 | dain@iq80.com 39 | 40 | 41 | chirino 42 | Hiram Chirino 43 | hiram@hiramchirino.com 44 | http://hiramchirino.com 45 | -5 46 | 47 | 48 | 49 | 50 | scm:git:git://github.com/Tinfoiled/leveldb.git 51 | scm:git:git@github.com:Tinfoiled/leveldb.git 52 | http://github.com/Tinfoiled/leveldb/tree/master 53 | 54 | 55 | 56 | true 57 | false 58 | 59 | true 60 | false 61 | 62 | 63 | 64 | 65 | 66 | com.tinfoiled.mcpe.leveldb 67 | leveldb-api 68 | ${project.version} 69 | 70 | 71 | 72 | com.tinfoiled.mcpe.leveldb 73 | leveldb 74 | ${project.version} 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-checkstyle-plugin 84 | 2.11 85 | 86 | 87 | validate 88 | 89 | check 90 | 91 | 92 | ${air.check.skip-checkstyle} 93 | ${air.check.fail-checkstyle} 94 | true 95 | true 96 | ${air.main.basedir}/src/checkstyle/checks.xml 97 | **/com/facebook/presto/operator/PagesIndexOrdering.java 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /src/checkstyle/checks.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /src/license/LICENSE-HEADER.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 the original author or authors. 2 | See the notice.md file distributed with this work for additional 3 | information regarding copyright ownership. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | -------------------------------------------------------------------------------- /src/site/site.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 22 | com.googlecode.fluido-skin 23 | fluido-skin 24 | 1.3 25 | 26 | 27 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | --------------------------------------------------------------------------------