├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── RELEASE.md ├── ezdb-api ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── ezdb │ ├── Db.java │ ├── DbException.java │ ├── comparator │ ├── ComparableComparator.java │ ├── LexicographicalComparator.java │ └── SerdeComparator.java │ ├── serde │ ├── ByteSerde.java │ ├── CalendarSerde.java │ ├── DateSerde.java │ ├── IntegerSerde.java │ ├── LongSerde.java │ ├── Serde.java │ ├── SerializingSerde.java │ ├── StringSerde.java │ ├── TypeDelegateSerde.java │ ├── VersionedSerde.java │ └── VoidSerde.java │ ├── table │ ├── Batch.java │ ├── EmptyTableIterator.java │ ├── RangeTableRow.java │ ├── RawTableRow.java │ ├── Table.java │ ├── TableRow.java │ └── range │ │ ├── EmptyRangeTableIterator.java │ │ ├── RangeBatch.java │ │ ├── RangeTable.java │ │ └── RawRangeTableRow.java │ └── util │ ├── LazyRangeKeysGetter.java │ ├── LazyValueGetter.java │ ├── ObjectRangeTableKey.java │ ├── ObjectRangeTableRow.java │ ├── ObjectTableRow.java │ ├── TableIterator.java │ └── Util.java ├── ezdb-leveldb-jni ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ └── ezdb │ │ └── leveldb │ │ ├── EzLevelDbJni.java │ │ ├── EzLevelDbJniFactory.java │ │ └── table │ │ ├── EzLevelDbJniBatch.java │ │ ├── EzLevelDbJniComparator.java │ │ ├── EzLevelDbJniTable.java │ │ └── range │ │ ├── EzLevelDbJniRangeBatch.java │ │ ├── EzLevelDbJniRangeComparator.java │ │ └── EzLevelDbJniRangeTable.java │ └── test │ └── java │ └── ezdb │ └── leveldb │ ├── FileUtils.java │ ├── MSFT.txt │ ├── TestEzLevelDb.java │ ├── TestEzLevelDbTorture.java │ └── TestStockData.java ├── ezdb-leveldb ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ ├── ezdb │ │ └── leveldb │ │ │ ├── EzLevelDbJava.java │ │ │ ├── EzLevelDbJavaFactory.java │ │ │ ├── table │ │ │ ├── EzLevelDbJavaBatch.java │ │ │ ├── EzLevelDbJavaComparator.java │ │ │ ├── EzLevelDbTable.java │ │ │ └── range │ │ │ │ ├── EzLevelDbJavaRangeBatch.java │ │ │ │ ├── EzLevelDbJavaRangeComparator.java │ │ │ │ └── EzLevelDbRangeTable.java │ │ │ └── util │ │ │ ├── EzDBIterator.java │ │ │ ├── EzDBRangeIterator.java │ │ │ ├── EzLevelDBIterator.java │ │ │ ├── EzLevelDBRangeIterator.java │ │ │ ├── Slices.java │ │ │ └── ZeroCopyDBComparator.java │ │ └── org │ │ └── iq80 │ │ └── leveldb │ │ ├── impl │ │ ├── ExtendedDbImpl.java │ │ └── ExtensibleDbImpl.java │ │ ├── iterator │ │ └── ExtendedDBIteratorAdapter.java │ │ └── table │ │ └── ExtendedCustomUserComparator.java │ └── test │ └── java │ └── ezdb │ └── leveldb │ ├── MSFT.txt │ ├── TestEzLevelDb.java │ ├── TestEzLevelDbTorture.java │ └── TestStockData.java ├── ezdb-lmdb-jnr ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ └── ezdb │ │ └── lmdb │ │ ├── EzLmDb.java │ │ ├── EzLmDbFactory.java │ │ ├── EzLmDbJnrFactory.java │ │ ├── table │ │ ├── EzLmDbBatch.java │ │ ├── EzLmDbComparator.java │ │ ├── EzLmDbTable.java │ │ └── range │ │ │ ├── EzLmDbRangeBatch.java │ │ │ ├── EzLmDbRangeComparator.java │ │ │ └── EzLmDbRangeTable.java │ │ └── util │ │ ├── EzDBIterator.java │ │ ├── EzDBRangeIterator.java │ │ ├── FileUtils.java │ │ ├── LmDBJnrDBIterator.java │ │ └── LmDBJnrDBRangeIterator.java │ └── test │ └── java │ └── ezdb │ └── lmdb │ ├── ADatabasePerformanceTest.java │ ├── LmdbPerformanceTest.java │ ├── MSFT.txt │ ├── TestEzLmDb.java │ ├── TestEzLmDbJni.java │ ├── TestEzLmDbJniTorture.java │ ├── TestInitializer.java │ ├── TestStockData.java │ └── TutorialTest.java ├── ezdb-lsmtree ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ └── ezdb │ │ └── lsmtree │ │ ├── EzLsmTreeDb.java │ │ ├── EzLsmTreeDbFactory.java │ │ ├── EzLsmTreeDbJavaFactory.java │ │ ├── EzdbSerializer.java │ │ └── table │ │ ├── EzLsmTreeDbComparator.java │ │ ├── LsmTreeTable.java │ │ └── range │ │ ├── EzLsmTreeDbRangeComparator.java │ │ ├── LsmTreeRangeTable.java │ │ └── ObjectTableKeySerializer.java │ └── test │ └── java │ └── ezdb │ └── lsmtree │ ├── FileUtils.java │ ├── MSFT.txt │ ├── TestEzLsmTreeDb.java │ ├── TestEzLsmTreeDbJni.java │ ├── TestEzLsmtTreeDbJniTorture.java │ ├── TestInitializer.java │ └── TestStockData.java ├── ezdb-rocksdb-jni ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ └── ezdb │ │ └── rocksdb │ │ ├── EzRocksDb.java │ │ ├── EzRocksDbFactory.java │ │ ├── EzRocksDbJniFactory.java │ │ ├── table │ │ ├── EzRocksDbBatch.java │ │ ├── EzRocksDbComparator.java │ │ ├── EzRocksDbTable.java │ │ └── range │ │ │ ├── EzRocksDbRangeBatch.java │ │ │ ├── EzRocksDbRangeComparator.java │ │ │ └── EzRocksDbRangeTable.java │ │ └── util │ │ ├── EzDBIterator.java │ │ ├── EzDBRangeIterator.java │ │ ├── FileUtils.java │ │ ├── RocksDBJniDBIterator.java │ │ └── RocksDBJniRangeDBIterator.java │ └── test │ └── java │ └── ezdb │ └── rocksdb │ ├── MSFT.txt │ ├── TestEzRocksDb.java │ ├── TestEzRocksDbJni.java │ ├── TestEzRocksDbJniTorture.java │ └── TestStockData.java ├── ezdb-treemap ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ └── ezdb │ │ └── treemap │ │ ├── bytes │ │ ├── EzBytesTreeMapDb.java │ │ └── table │ │ │ ├── BytesTreeMapTable.java │ │ │ └── range │ │ │ └── BytesTreeMapRangeTable.java │ │ └── object │ │ ├── EzObjectTreeMapDb.java │ │ └── table │ │ ├── EzObjectTreeMapDbComparator.java │ │ ├── ObjectTreeMapTable.java │ │ └── range │ │ ├── EzObjectTreeMapDbRangeComparator.java │ │ └── ObjectTreeMapRangeTable.java │ └── test │ └── java │ └── ezdb │ └── treemap │ ├── bytes │ ├── MSFT.txt │ ├── TestEzBytesTreeMapDb.java │ ├── TestEzBytesTreeMapDbJni.java │ ├── TestEzBytesTreeMapDbJniTorture.java │ └── TestStockData.java │ └── object │ ├── MSFT.txt │ ├── TestEzObjectTreeMapDb.java │ ├── TestEzObjectTreeMapDbJni.java │ ├── TestEzObjectTreeMapDbJniTorture.java │ └── TestStockData.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | target 3 | .classpath 4 | .project 5 | bin 6 | /.settings/ 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.5 2 | 3 | * Initial release. 4 | 5 | ## 1.0.6 6 | 7 | * Upgraded org.fusesource.leveldbjni:leveldbjni-all to version 1.5. 8 | 9 | ## 1.0.7 10 | 11 | * Moved TreeMap-backed EZDB implementation into its own subproject. 12 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | ### Release to Maven 2 | 3 | To release to Sonatype maven, follow these instructions: 4 | 5 | * https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide 6 | 7 | Make sure the version listed in the .pom files is a -SNAPSHOT version, then run: 8 | 9 | mvn clean deploy release:clean release:prepare release:perform 10 | 11 | Finally, release the published artifact. 12 | 13 | 1. Go to https://oss.sonatype.org 14 | 2. Login 15 | 3. Go to staging repositories 16 | 4. Find the plublished artifact 17 | 5. Click close 18 | 6. Click release 19 | 20 | The Sonatype to Maven central is set to sync every couple of hours. Here's the JIRA: 21 | 22 | * https://issues.sonatype.org/browse/OSSRH-5249 23 | 24 | ### Javadocs 25 | 26 | To release new Javadocs: 27 | 28 | mvn install javadoc:aggregate -DskipTests 29 | cp -r target/site/javadoc/apidocs /tmp 30 | git checkout gh-pages 31 | git pull 32 | rm -rf ezdb-api/ target/ ezdb-leveldb/ 33 | rm -rf javadocs/ 34 | cp -r /tmp/apidocs/ javadocs 35 | git add javadocs 36 | git commit -am"Updating javadocs for ezdb version X" 37 | git push 38 | git checkout master 39 | -------------------------------------------------------------------------------- /ezdb-api/.gitignore: -------------------------------------------------------------------------------- 1 | /.settings/ 2 | -------------------------------------------------------------------------------- /ezdb-api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.github.criccomini 9 | ezdb-parent 10 | 0.1.18-SNAPSHOT 11 | 12 | 13 | ezdb-api 14 | EZDB API 15 | jar 16 | 17 | 18 | 19 | io.netty 20 | netty-buffer 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/Db.java: -------------------------------------------------------------------------------- 1 | package ezdb; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Comparator; 5 | 6 | import ezdb.serde.Serde; 7 | import ezdb.table.Table; 8 | import ezdb.table.range.RangeTable; 9 | 10 | /** 11 | * The Db interface is used to create and delete tables. This is the entry point 12 | * into EZDB. 13 | * 14 | * @author criccomini 15 | */ 16 | public interface Db { 17 | /** 18 | * Get a simple key/value table with the specified name and serdes. If the table 19 | * does not exist, it should be created. If the table exists, it should be 20 | * returned. If the table exists but the serdes don't match the pre-existing 21 | * table, a runtime exception should occur when the table is used to read or 22 | * write data. 23 | * 24 | * @param tableName The name of the table. The table should be formatted to 25 | * be compatible with filenames on the host operating 26 | * system. 27 | * @param hashKeySerde The hash key serializer. 28 | * @param valueSerde The value serializer. 29 | * @return A simple key/value table. 30 | */ 31 | public Table getTable(String tableName, Serde hashKeySerde, Serde valueSerde); 32 | 33 | public Table getTable(String tableName, Serde hashKeySerde, Serde valueSerde, 34 | Comparator hashKeyComparator); 35 | 36 | /** 37 | * Get a hash/range table with the specified name and serdes. If the table does 38 | * not exist, it should be created. If the table exists, it should be returned. 39 | * If the table exists but the serdes don't match the pre-existing table, a 40 | * runtime exception should occur when the table is used to read or write data. 41 | * 42 | * @param tableName The name of the table. The table should be formatted to 43 | * be compatible with filenames on the host operating 44 | * system. 45 | * @param hashKeySerde The hash key serializer. 46 | * @param rangeKeySerde The range key serializer. If range keys are not used, 47 | * any serializer can be supplied, as it will be ignored. 48 | * @param valueSerde The value serializer. 49 | * @return A hash/range table. 50 | */ 51 | public RangeTable getRangeTable(String tableName, Serde hashKeySerde, Serde rangeKeySerde, 52 | Serde valueSerde); 53 | 54 | /** 55 | * Get a hash/range table with the specified name and serdes. If the table does 56 | * not exist, it should be created. If the table exists, it should be returned. 57 | * If the table exists but the serdes don't match the pre-existing table, a 58 | * runtime exception should occur when the table is used to read or write data. 59 | * 60 | * @param tableName The name of the table. The table should be 61 | * formatted to be compatible with filenames on the 62 | * host operating system. 63 | * @param hashKeySerde The hash key serializer. 64 | * @param rangeKeySerde The range key serializer. If range keys are not 65 | * used, any serializer can be supplied, as it will be 66 | * ignored. 67 | * @param valueSerde The value serializer. 68 | * @param hashKeyComparator A comparator to be used when sorting hash keys. 69 | * @param rangeKeyComparator A comparator to be used when sorting range keys 70 | * that have the same hash key. 71 | * @return A hash/range table. 72 | */ 73 | public RangeTable getRangeTable(String tableName, Serde hashKeySerde, Serde rangeKeySerde, 74 | Serde valueSerde, Comparator hashKeyComparator, Comparator rangeKeyComparator); 75 | 76 | /** 77 | * Delete a table from disk and memory. 78 | * 79 | * @param tableName The logical name of the table to delete. 80 | */ 81 | public void deleteTable(String tableName); 82 | 83 | } 84 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/DbException.java: -------------------------------------------------------------------------------- 1 | package ezdb; 2 | 3 | public class DbException extends RuntimeException { 4 | private static final long serialVersionUID = 1L; 5 | 6 | public DbException() { 7 | super(); 8 | } 9 | 10 | public DbException(String s, Throwable t) { 11 | super(s, t); 12 | } 13 | 14 | public DbException(String s) { 15 | super(s); 16 | } 17 | 18 | public DbException(Throwable t) { 19 | super(t); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/comparator/ComparableComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.comparator; 2 | 3 | import java.util.Comparator; 4 | 5 | public class ComparableComparator implements Comparator { 6 | 7 | private static final ComparableComparator INSTANCE = new ComparableComparator(); 8 | 9 | @SuppressWarnings("unchecked") 10 | public static Comparator get() { 11 | return (Comparator) INSTANCE; 12 | } 13 | 14 | @Override 15 | public int compare(Object o1, Object o2) { 16 | boolean o1NullOrEmpty = o1 == null; 17 | boolean o2NullOrEmpty = o2 == null; 18 | if (o1NullOrEmpty && o2NullOrEmpty) { 19 | return 0; 20 | } 21 | if (o1NullOrEmpty) { 22 | return -1; 23 | } 24 | if (o2NullOrEmpty) { 25 | // fix buffer underflow 26 | return 1; 27 | } 28 | Comparable co1 = toComparable(o1); 29 | Comparable co2 = toComparable(o2); 30 | return innerCompare(co1, co2); 31 | } 32 | 33 | /** 34 | * Override this to customize the comparation itself. E.g. inversing it. 35 | */ 36 | protected int innerCompare(Comparable co1, Comparable co2) { 37 | return co1.compareTo(co2); 38 | } 39 | 40 | /** 41 | * Override this to customize the comparable object. E.g. getting an inner 42 | * object. 43 | */ 44 | @SuppressWarnings("unchecked") 45 | protected Comparable toComparable(Object obj) { 46 | return (Comparable) obj; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/comparator/LexicographicalComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.comparator; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Comparator; 5 | 6 | /** 7 | * A comparator that compares bytes using lexicographical ordering. 8 | * 9 | * @author criccomini 10 | * 11 | */ 12 | public class LexicographicalComparator implements Comparator { 13 | public static final LexicographicalComparator get = new LexicographicalComparator(); 14 | 15 | @Override 16 | public int compare(final ByteBuffer bytes1, final ByteBuffer bytes2) { 17 | return bytes1.compareTo(bytes2); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/comparator/SerdeComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.comparator; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Comparator; 5 | 6 | import ezdb.serde.Serde; 7 | 8 | public class SerdeComparator implements Comparator { 9 | 10 | private final Serde serde; 11 | 12 | public SerdeComparator(final Serde serde) { 13 | this.serde = serde; 14 | } 15 | 16 | @Override 17 | public final int compare(final ByteBuffer o1, final ByteBuffer o2) { 18 | final boolean o1NullOrEmpty = o1 == null || o1.remaining() == 0; 19 | final boolean o2NullOrEmpty = o2 == null || o2.remaining() == 0; 20 | if (o1NullOrEmpty && o2NullOrEmpty) { 21 | return 0; 22 | } 23 | if (o1NullOrEmpty) { 24 | return -1; 25 | } 26 | if (o2NullOrEmpty) { 27 | // fix buffer underflow 28 | return 1; 29 | } 30 | final Comparable co1 = toComparable(serde.fromBuffer(o1)); 31 | final Comparable co2 = toComparable(serde.fromBuffer(o2)); 32 | return innerCompare(co1, co2); 33 | } 34 | 35 | /** 36 | * Override this to customize the comparation itself. E.g. inversing it. 37 | */ 38 | protected int innerCompare(final Comparable co1, final Comparable co2) { 39 | return co1.compareTo(co2); 40 | } 41 | 42 | /** 43 | * Override this to customize the comparable object. E.g. getting an inner 44 | * object. 45 | */ 46 | @SuppressWarnings("unchecked") 47 | protected Comparable toComparable(final Object fromBytes) { 48 | return (Comparable) fromBytes; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/serde/ByteSerde.java: -------------------------------------------------------------------------------- 1 | package ezdb.serde; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import io.netty.buffer.ByteBuf; 6 | 7 | public class ByteSerde implements Serde { 8 | public static final ByteSerde get = new ByteSerde(); 9 | 10 | @Override 11 | public byte[] fromBuffer(final ByteBuffer buffer) { 12 | final byte[] bytes = new byte[buffer.remaining()]; 13 | buffer.get(bytes); 14 | buffer.flip(); 15 | return bytes; 16 | } 17 | 18 | @Override 19 | public void toBuffer(final ByteBuffer buffer, final byte[] obj) { 20 | buffer.put(obj); 21 | buffer.flip(); 22 | } 23 | 24 | @Override 25 | public byte[] fromBuffer(final ByteBuf buffer) { 26 | final byte[] bytes = new byte[buffer.readableBytes()]; 27 | buffer.readBytes(bytes); 28 | return bytes; 29 | } 30 | 31 | @Override 32 | public void toBuffer(final ByteBuf buffer, final byte[] obj) { 33 | buffer.writeBytes(obj); 34 | } 35 | 36 | @Override 37 | public byte[] fromBytes(final byte[] bytes) { 38 | return bytes; 39 | } 40 | 41 | @Override 42 | public byte[] toBytes(final byte[] obj) { 43 | return obj; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/serde/CalendarSerde.java: -------------------------------------------------------------------------------- 1 | package ezdb.serde; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Calendar; 5 | import java.util.Date; 6 | 7 | import io.netty.buffer.ByteBuf; 8 | 9 | public class CalendarSerde implements Serde { 10 | 11 | public static final CalendarSerde get = new CalendarSerde(); 12 | 13 | @Override 14 | public Calendar fromBuffer(final ByteBuffer buffer) { 15 | final Date date = DateSerde.get.fromBuffer(buffer); 16 | if (date == null) { 17 | return null; 18 | } else { 19 | final Calendar cal = Calendar.getInstance(); 20 | cal.setTime(date); 21 | return cal; 22 | } 23 | } 24 | 25 | @Override 26 | public void toBuffer(final ByteBuffer buffer, final Calendar obj) { 27 | Date date; 28 | if (obj == null) { 29 | date = null; 30 | } else { 31 | date = obj.getTime(); 32 | } 33 | DateSerde.get.toBuffer(buffer, date); 34 | } 35 | 36 | @Override 37 | public Calendar fromBuffer(final ByteBuf buffer) { 38 | final Date date = DateSerde.get.fromBuffer(buffer); 39 | if (date == null) { 40 | return null; 41 | } else { 42 | final Calendar cal = Calendar.getInstance(); 43 | cal.setTime(date); 44 | return cal; 45 | } 46 | } 47 | 48 | @Override 49 | public void toBuffer(final ByteBuf buffer, final Calendar obj) { 50 | Date date; 51 | if (obj == null) { 52 | date = null; 53 | } else { 54 | date = obj.getTime(); 55 | } 56 | DateSerde.get.toBuffer(buffer, date); 57 | } 58 | 59 | @Override 60 | public Calendar fromBytes(final byte[] bytes) { 61 | final Date date = DateSerde.get.fromBytes(bytes); 62 | if (date == null) { 63 | return null; 64 | } else { 65 | final Calendar cal = Calendar.getInstance(); 66 | cal.setTime(date); 67 | return cal; 68 | } 69 | } 70 | 71 | @Override 72 | public byte[] toBytes(final Calendar obj) { 73 | Date date; 74 | if (obj == null) { 75 | date = null; 76 | } else { 77 | date = obj.getTime(); 78 | } 79 | return DateSerde.get.toBytes(date); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/serde/DateSerde.java: -------------------------------------------------------------------------------- 1 | package ezdb.serde; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Date; 5 | 6 | import io.netty.buffer.ByteBuf; 7 | 8 | public class DateSerde implements Serde { 9 | 10 | public static final DateSerde get = new DateSerde(); 11 | 12 | @Override 13 | public Date fromBuffer(final ByteBuffer buffer) { 14 | final Long time = LongSerde.get.fromBuffer(buffer); 15 | if (time == null) { 16 | return null; 17 | } else { 18 | return new Date(time); 19 | } 20 | } 21 | 22 | @Override 23 | public void toBuffer(final ByteBuffer buffer, final Date obj) { 24 | final Long time; 25 | if (obj == null) { 26 | time = null; 27 | } else { 28 | time = obj.getTime(); 29 | } 30 | LongSerde.get.toBuffer(buffer, time); 31 | } 32 | 33 | @Override 34 | public Date fromBuffer(final ByteBuf buffer) { 35 | final Long time = LongSerde.get.fromBuffer(buffer); 36 | if (time == null) { 37 | return null; 38 | } else { 39 | return new Date(time); 40 | } 41 | } 42 | 43 | @Override 44 | public void toBuffer(final ByteBuf buffer, final Date obj) { 45 | final Long time; 46 | if (obj == null) { 47 | time = null; 48 | } else { 49 | time = obj.getTime(); 50 | } 51 | LongSerde.get.toBuffer(buffer, time); 52 | } 53 | 54 | @Override 55 | public Date fromBytes(final byte[] bytes) { 56 | final Long time = LongSerde.get.fromBytes(bytes); 57 | if (time == null) { 58 | return null; 59 | } else { 60 | return new Date(time); 61 | } 62 | } 63 | 64 | @Override 65 | public byte[] toBytes(final Date obj) { 66 | final Long time; 67 | if (obj == null) { 68 | time = null; 69 | } else { 70 | time = obj.getTime(); 71 | } 72 | return LongSerde.get.toBytes(time); 73 | } 74 | 75 | } -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/serde/IntegerSerde.java: -------------------------------------------------------------------------------- 1 | package ezdb.serde; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import io.netty.buffer.ByteBuf; 6 | 7 | public class IntegerSerde implements Serde { 8 | public static final IntegerSerde get = new IntegerSerde(); 9 | 10 | @Override 11 | public Integer fromBuffer(final ByteBuf buffer) { 12 | return buffer.readInt(); 13 | } 14 | 15 | @Override 16 | public void toBuffer(final ByteBuf buffer, final Integer obj) { 17 | buffer.writeInt(obj); 18 | } 19 | 20 | @Override 21 | public Integer fromBuffer(final ByteBuffer buffer) { 22 | return buffer.getInt(buffer.position()); 23 | } 24 | 25 | @Override 26 | public void toBuffer(final ByteBuffer buffer, final Integer obj) { 27 | buffer.putInt(buffer.position(), obj); 28 | } 29 | 30 | @Override 31 | public Integer fromBytes(final byte[] bytes) { 32 | final ByteBuffer buffer = ByteBuffer.wrap(bytes); 33 | return buffer.getInt(); 34 | } 35 | 36 | @Override 37 | public byte[] toBytes(final Integer obj) { 38 | final ByteBuffer buffer = ByteBuffer.allocate(4); 39 | buffer.putInt(obj); 40 | return buffer.array(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/serde/LongSerde.java: -------------------------------------------------------------------------------- 1 | package ezdb.serde; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import io.netty.buffer.ByteBuf; 6 | 7 | public class LongSerde implements Serde { 8 | public static final LongSerde get = new LongSerde(); 9 | private static final byte[] EMPTY_BYTES = new byte[0]; 10 | 11 | @Override 12 | public Long fromBuffer(final ByteBuf buffer) { 13 | if (buffer == null || buffer.readableBytes() == 0) { 14 | return null; 15 | } 16 | return buffer.readLong(); 17 | } 18 | 19 | @Override 20 | public void toBuffer(final ByteBuf buffer, final Long obj) { 21 | if (obj == null) { 22 | return; 23 | } 24 | buffer.writeLong(obj); 25 | } 26 | 27 | @Override 28 | public Long fromBuffer(final ByteBuffer buffer) { 29 | if (buffer == null || buffer.remaining() == 0) { 30 | return null; 31 | } 32 | return buffer.getLong(buffer.position()); 33 | } 34 | 35 | @Override 36 | public void toBuffer(final ByteBuffer buffer, final Long obj) { 37 | if (obj == null) { 38 | return; 39 | } 40 | buffer.putLong(buffer.position(), obj); 41 | } 42 | 43 | @Override 44 | public Long fromBytes(final byte[] bytes) { 45 | if (bytes == null || bytes.length == 0) { 46 | return null; 47 | } 48 | final ByteBuffer buffer = ByteBuffer.wrap(bytes); 49 | return buffer.getLong(); 50 | } 51 | 52 | @Override 53 | public byte[] toBytes(final Long obj) { 54 | if (obj == null) { 55 | return EMPTY_BYTES; 56 | } 57 | final ByteBuffer buffer = ByteBuffer.allocate(8); 58 | buffer.putLong(obj); 59 | return buffer.array(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/serde/Serde.java: -------------------------------------------------------------------------------- 1 | package ezdb.serde; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import io.netty.buffer.ByteBuf; 6 | 7 | /** 8 | * A generic serializer that can be used to convert Java objects back and forth 9 | * to byte arrays. 10 | * 11 | * @author criccomini 12 | * 13 | * @param The type of the object that a serde can convert. 14 | */ 15 | public interface Serde { 16 | default O fromBuffer(final ByteBuf buffer) { 17 | final byte[] bytes = new byte[buffer.readableBytes()]; 18 | buffer.readBytes(bytes); 19 | return fromBytes(bytes); 20 | } 21 | 22 | default void toBuffer(final ByteBuf buffer, final O obj) { 23 | final byte[] bytes = toBytes(obj); 24 | buffer.writeBytes(bytes); 25 | } 26 | 27 | default O fromBuffer(final ByteBuffer buffer) { 28 | final byte[] bytes = new byte[buffer.remaining()]; 29 | buffer.get(bytes); 30 | buffer.flip(); 31 | return fromBytes(bytes); 32 | } 33 | 34 | default void toBuffer(final ByteBuffer buffer, final O obj) { 35 | final byte[] bytes = toBytes(obj); 36 | buffer.put(bytes); 37 | buffer.flip(); 38 | } 39 | 40 | public O fromBytes(byte[] bytes); 41 | 42 | public byte[] toBytes(O obj); 43 | 44 | @SuppressWarnings("unchecked") 45 | default T unwrap(final Class type) { 46 | if (type.isAssignableFrom(getClass())) { 47 | return (T) this; 48 | } else { 49 | return null; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/serde/SerializingSerde.java: -------------------------------------------------------------------------------- 1 | package ezdb.serde; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.ObjectInputStream; 8 | import java.io.ObjectOutputStream; 9 | import java.io.OutputStream; 10 | import java.io.Serializable; 11 | 12 | import ezdb.DbException; 13 | 14 | public class SerializingSerde implements Serde { 15 | 16 | @SuppressWarnings("rawtypes") 17 | private static final SerializingSerde INSTANCE = new SerializingSerde(); 18 | 19 | @SuppressWarnings("unchecked") 20 | public static SerializingSerde get() { 21 | return INSTANCE; 22 | } 23 | 24 | @SuppressWarnings("unchecked") 25 | @Override 26 | public E fromBytes(final byte[] bytes) { 27 | return (E) deserialize(bytes); 28 | } 29 | 30 | @Override 31 | public byte[] toBytes(final E obj) { 32 | return serialize((Serializable) obj); 33 | } 34 | 35 | private static void serialize(final Serializable obj, final OutputStream outputStream) { 36 | if (outputStream == null) { 37 | throw new IllegalArgumentException("The OutputStream must not be null"); 38 | } 39 | ObjectOutputStream out = null; 40 | try { 41 | // stream closed in the finally 42 | out = new ObjectOutputStream(outputStream); 43 | out.writeObject(obj); 44 | 45 | } catch (final IOException ex) { 46 | throw new DbException(ex); 47 | } finally { 48 | try { 49 | if (out != null) { 50 | out.close(); 51 | } 52 | } catch (final IOException ex) { 53 | // ignore close exception 54 | } 55 | } 56 | } 57 | 58 | private static byte[] serialize(final Serializable obj) { 59 | final ByteArrayOutputStream baos = new ByteArrayOutputStream(512); 60 | serialize(obj, baos); 61 | return baos.toByteArray(); 62 | } 63 | 64 | private static Object deserialize(final InputStream inputStream) { 65 | if (inputStream == null) { 66 | throw new IllegalArgumentException("The InputStream must not be null"); 67 | } 68 | ObjectInputStream in = null; 69 | try { 70 | // stream closed in the finally 71 | in = new ObjectInputStream(inputStream); 72 | return in.readObject(); 73 | 74 | } catch (final ClassNotFoundException ex) { 75 | throw new DbException(ex); 76 | } catch (final IOException ex) { 77 | throw new DbException(ex); 78 | } finally { 79 | try { 80 | if (in != null) { 81 | in.close(); 82 | } 83 | } catch (final IOException ex) { 84 | // ignore close exception 85 | } 86 | } 87 | } 88 | 89 | private static Object deserialize(final byte[] objectData) { 90 | if (objectData == null) { 91 | throw new IllegalArgumentException("The byte[] must not be null"); 92 | } 93 | final ByteArrayInputStream bais = new ByteArrayInputStream(objectData); 94 | return deserialize(bais); 95 | } 96 | 97 | } -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/serde/StringSerde.java: -------------------------------------------------------------------------------- 1 | package ezdb.serde; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.nio.ByteBuffer; 5 | 6 | import ezdb.DbException; 7 | import io.netty.buffer.ByteBuf; 8 | 9 | public class StringSerde implements Serde { 10 | public static final StringSerde get = new StringSerde(); 11 | 12 | @Override 13 | public String fromBuffer(final ByteBuf buffer) { 14 | try { 15 | final byte[] bytes = ByteSerde.get.fromBuffer(buffer); 16 | return new String(bytes, "UTF-8"); 17 | } catch (final UnsupportedEncodingException e) { 18 | throw new DbException(e); 19 | } 20 | } 21 | 22 | @Override 23 | public void toBuffer(final ByteBuf buffer, final String obj) { 24 | try { 25 | final byte[] bytes = obj.getBytes("UTF-8"); 26 | ByteSerde.get.toBuffer(buffer, bytes); 27 | } catch (final UnsupportedEncodingException e) { 28 | throw new DbException(e); 29 | } 30 | } 31 | 32 | @Override 33 | public String fromBuffer(final ByteBuffer buffer) { 34 | try { 35 | final byte[] bytes = ByteSerde.get.fromBuffer(buffer); 36 | return new String(bytes, "UTF-8"); 37 | } catch (final UnsupportedEncodingException e) { 38 | throw new DbException(e); 39 | } 40 | } 41 | 42 | @Override 43 | public void toBuffer(final ByteBuffer buffer, final String obj) { 44 | try { 45 | final byte[] bytes = obj.getBytes("UTF-8"); 46 | ByteSerde.get.toBuffer(buffer, bytes); 47 | } catch (final UnsupportedEncodingException e) { 48 | throw new DbException(e); 49 | } 50 | } 51 | 52 | @Override 53 | public String fromBytes(final byte[] bytes) { 54 | try { 55 | return new String(bytes, "UTF-8"); 56 | } catch (final UnsupportedEncodingException e) { 57 | throw new DbException(e); 58 | } 59 | } 60 | 61 | @Override 62 | public byte[] toBytes(final String obj) { 63 | try { 64 | return obj.getBytes("UTF-8"); 65 | } catch (final UnsupportedEncodingException e) { 66 | throw new DbException(e); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/serde/TypeDelegateSerde.java: -------------------------------------------------------------------------------- 1 | package ezdb.serde; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Calendar; 5 | import java.util.Date; 6 | 7 | import io.netty.buffer.ByteBuf; 8 | 9 | public class TypeDelegateSerde implements Serde { 10 | 11 | private final Serde delegate; 12 | 13 | @SuppressWarnings("unchecked") 14 | public TypeDelegateSerde(final Class type) { 15 | delegate = (Serde) newDelegate(type); 16 | } 17 | 18 | protected Serde newDelegate(final Class type) { 19 | if (Byte.class.isAssignableFrom(type) || byte.class.isAssignableFrom(type)) { 20 | return ByteSerde.get; 21 | } else if (Date.class.isAssignableFrom(type)) { 22 | return DateSerde.get; 23 | } else if (Long.class.isAssignableFrom(type) || long.class.isAssignableFrom(type)) { 24 | return LongSerde.get; 25 | } else if (Integer.class.isAssignableFrom(type) || int.class.isAssignableFrom(type)) { 26 | return IntegerSerde.get; 27 | } else if (Calendar.class.isAssignableFrom(type)) { 28 | return CalendarSerde.get; 29 | } else if (String.class.isAssignableFrom(type)) { 30 | return StringSerde.get; 31 | } else if (Void.class.isAssignableFrom(type) || void.class.isAssignableFrom(type)) { 32 | return VoidSerde.get; 33 | } else { 34 | // fallback to slower serialization 35 | // do not check type to gracefully fall back on interface types like List 36 | return SerializingSerde.get(); 37 | } 38 | } 39 | 40 | @Override 41 | public O fromBuffer(final ByteBuf buffer) { 42 | return delegate.fromBuffer(buffer); 43 | } 44 | 45 | @Override 46 | public void toBuffer(final ByteBuf buffer, final O obj) { 47 | delegate.toBuffer(buffer, obj); 48 | } 49 | 50 | @Override 51 | public O fromBuffer(final ByteBuffer buffer) { 52 | return delegate.fromBuffer(buffer); 53 | } 54 | 55 | @Override 56 | public void toBuffer(final ByteBuffer buffer, final O obj) { 57 | delegate.toBuffer(buffer, obj); 58 | } 59 | 60 | @Override 61 | public O fromBytes(final byte[] bytes) { 62 | return delegate.fromBytes(bytes); 63 | } 64 | 65 | @Override 66 | public byte[] toBytes(final O obj) { 67 | return delegate.toBytes(obj); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/serde/VersionedSerde.java: -------------------------------------------------------------------------------- 1 | package ezdb.serde; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import ezdb.serde.VersionedSerde.Versioned; 6 | import ezdb.util.Util; 7 | import io.netty.buffer.ByteBuf; 8 | import io.netty.buffer.ByteBufAllocator; 9 | 10 | /** 11 | * A wrapper serde that handles pre-pending some version number to a byte 12 | * payload. The format for the version byte array is: 13 | * 14 | *
 15 |  * [8 byte version]
 16 |  * [arbitrary object bytes]
 17 |  * 
18 | * 19 | * @author criccomini 20 | * 21 | * @param The type of object that we wish to version. 22 | */ 23 | public class VersionedSerde implements Serde> { 24 | 25 | private final Serde objectSerde; 26 | 27 | public VersionedSerde(final Serde objectSerde) { 28 | this.objectSerde = objectSerde; 29 | } 30 | 31 | @Override 32 | public Versioned fromBuffer(final ByteBuf buffer) { 33 | final long version = LongSerde.get.fromBuffer(buffer); 34 | final O object = objectSerde.fromBuffer(buffer); 35 | return new Versioned(object, version); 36 | } 37 | 38 | @Override 39 | public void toBuffer(final ByteBuf buffer, final Versioned versioned) { 40 | LongSerde.get.toBuffer(buffer, versioned.getVersion()); 41 | objectSerde.toBuffer(buffer, versioned.getObj()); 42 | } 43 | 44 | @Override 45 | public Versioned fromBuffer(final ByteBuffer buffer) { 46 | final int positionBefore = buffer.position(); 47 | final long version = LongSerde.get.fromBuffer(buffer); 48 | Util.position(buffer, positionBefore + Long.BYTES); 49 | final O object = objectSerde.fromBuffer(buffer); 50 | Util.position(buffer, positionBefore); 51 | return new Versioned(object, version); 52 | } 53 | 54 | @Override 55 | public void toBuffer(final ByteBuffer buffer, final Versioned versioned) { 56 | final int positionBefore = buffer.position(); 57 | LongSerde.get.toBuffer(buffer, versioned.getVersion()); 58 | Util.position(buffer, positionBefore + Long.BYTES); 59 | objectSerde.toBuffer(buffer, versioned.getObj()); 60 | Util.position(buffer, positionBefore); 61 | } 62 | 63 | @Override 64 | public byte[] toBytes(final Versioned obj) { 65 | final ByteBuf buffer = ByteBufAllocator.DEFAULT.heapBuffer(); 66 | try { 67 | LongSerde.get.toBuffer(buffer, obj.getVersion()); 68 | objectSerde.toBuffer(buffer, obj.getObj()); 69 | final byte[] bytes = new byte[buffer.readableBytes()]; 70 | buffer.readBytes(bytes); 71 | return bytes; 72 | } finally { 73 | buffer.release(buffer.refCnt()); 74 | } 75 | } 76 | 77 | @Override 78 | public Versioned fromBytes(final byte[] bytes) { 79 | final ByteBuffer buffer = ByteBuffer.wrap(bytes); 80 | return fromBuffer(buffer); 81 | } 82 | 83 | public static class Versioned { 84 | private final O obj; 85 | private final long version; 86 | 87 | public Versioned(final O obj, final long version) { 88 | this.obj = obj; 89 | this.version = version; 90 | } 91 | 92 | public O getObj() { 93 | return obj; 94 | } 95 | 96 | public long getVersion() { 97 | return version; 98 | } 99 | 100 | @Override 101 | public int hashCode() { 102 | final int prime = 31; 103 | int result = 1; 104 | result = prime * result + ((obj == null) ? 0 : obj.hashCode()); 105 | result = prime * result + (int) (version ^ (version >>> 32)); 106 | return result; 107 | } 108 | 109 | @SuppressWarnings("rawtypes") 110 | @Override 111 | public boolean equals(final Object obj) { 112 | if (this == obj) { 113 | return true; 114 | } 115 | if (obj == null) { 116 | return false; 117 | } 118 | if (getClass() != obj.getClass()) { 119 | return false; 120 | } 121 | final Versioned other = (Versioned) obj; 122 | if (this.obj == null) { 123 | if (other.obj != null) { 124 | return false; 125 | } 126 | } else if (!this.obj.equals(other.obj)) { 127 | return false; 128 | } 129 | if (version != other.version) { 130 | return false; 131 | } 132 | return true; 133 | } 134 | 135 | @Override 136 | public String toString() { 137 | return "Versioned [obj=" + obj + ", version=" + version + "]"; 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/serde/VoidSerde.java: -------------------------------------------------------------------------------- 1 | package ezdb.serde; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import ezdb.util.Util; 6 | import io.netty.buffer.ByteBuf; 7 | 8 | public final class VoidSerde implements Serde { 9 | 10 | public static final VoidSerde get = new VoidSerde(); 11 | 12 | private VoidSerde() { 13 | } 14 | 15 | @Override 16 | public Void fromBuffer(final ByteBuffer buffer) { 17 | return null; 18 | } 19 | 20 | @Override 21 | public void toBuffer(final ByteBuffer buffer, final Void obj) { 22 | } 23 | 24 | @Override 25 | public Void fromBuffer(final ByteBuf buffer) { 26 | return null; 27 | } 28 | 29 | @Override 30 | public void toBuffer(final ByteBuf buffer, final Void obj) { 31 | } 32 | 33 | @Override 34 | public Void fromBytes(final byte[] bytes) { 35 | return null; 36 | } 37 | 38 | @Override 39 | public byte[] toBytes(final Void obj) { 40 | return Util.EMPTY_BYTES; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/table/Batch.java: -------------------------------------------------------------------------------- 1 | package ezdb.table; 2 | 3 | import java.io.Closeable; 4 | 5 | public interface Batch extends Closeable { 6 | 7 | /** 8 | * Put a value into the table, keyed by the hash key. If a row already exists 9 | * for the hash key, it should be overwritten. 10 | * 11 | * @param hashKey 12 | * The unique key associated with a value. 13 | * @param value 14 | * The value to be persisted. 15 | */ 16 | void put(H hashKey, V value); 17 | 18 | /** 19 | * Delete a value for a given key. If no value exists for a given key, this 20 | * should be a no op. 21 | * 22 | * @param hashKey 23 | * The unique key associated with a value. 24 | */ 25 | void delete(H hashKey); 26 | 27 | /** 28 | * You need to call flush() before close() in order not to loose your data. 29 | */ 30 | void flush(); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/table/EmptyTableIterator.java: -------------------------------------------------------------------------------- 1 | package ezdb.table; 2 | 3 | import java.util.NoSuchElementException; 4 | 5 | import ezdb.util.TableIterator; 6 | 7 | @SuppressWarnings({ "rawtypes", "unchecked" }) 8 | public class EmptyTableIterator implements TableIterator> { 9 | 10 | private static final EmptyTableIterator INSTANCE = new EmptyTableIterator(); 11 | 12 | public static EmptyTableIterator get() { 13 | return INSTANCE; 14 | } 15 | 16 | @Override 17 | public boolean hasNext() { 18 | return false; 19 | } 20 | 21 | @Override 22 | public TableRow next() { 23 | throw new NoSuchElementException(); 24 | } 25 | 26 | @Override 27 | public void remove() { 28 | throw new NoSuchElementException(); 29 | } 30 | 31 | @Override 32 | public void close() { 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/table/RangeTableRow.java: -------------------------------------------------------------------------------- 1 | package ezdb.table; 2 | 3 | /** 4 | * A representation of a row in a table. 5 | * 6 | * @author criccomini 7 | * 8 | * @param The type of the hash key for this key/value table. 9 | * @param The type of the range key for this key/value table. 10 | * @param The type of the value for this key/value table. 11 | */ 12 | public interface RangeTableRow extends TableRow { 13 | 14 | /** 15 | * @return The range key for this row. If no range key exists, null should be 16 | * returned. 17 | */ 18 | public R getRangeKey(); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/table/Table.java: -------------------------------------------------------------------------------- 1 | package ezdb.table; 2 | 3 | import ezdb.util.TableIterator; 4 | 5 | /** 6 | * A key/value table. 7 | * 8 | * @author criccomini 9 | * 10 | * @param The type of the hash key for this key/value table. 11 | * @param The type of the value for this key/value table. 12 | */ 13 | public interface Table { 14 | 15 | public TableIterator> range(); 16 | 17 | /** 18 | * Put a value into the table, keyed by the hash key. If a row already exists 19 | * for the hash key, it should be overwritten. 20 | * 21 | * @param hashKey The unique key associated with a value. 22 | * @param value The value to be persisted. 23 | */ 24 | public void put(H hashKey, V value); 25 | 26 | /** 27 | * Get a value for a given key. 28 | * 29 | * @param hashKey The unique key associated with a value. 30 | * @return The value, or null if no item matches the hash key. 31 | */ 32 | public V get(H hashKey); 33 | 34 | /** 35 | * Delete a value for a given key. If no value exists for a given key, this 36 | * should be a no op. 37 | * 38 | * @param hashKey The unique key associated with a value. 39 | */ 40 | public void delete(H hashKey); 41 | 42 | /** 43 | * Close any open resources associated with this table. 44 | */ 45 | public void close(); 46 | 47 | /** 48 | * With this it is possible to do bulk/batch puts and deletes. 49 | * 50 | * @return a new batch enabled transaction object 51 | */ 52 | public Batch newBatch(); 53 | 54 | } 55 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/table/TableRow.java: -------------------------------------------------------------------------------- 1 | package ezdb.table; 2 | 3 | import java.util.Map.Entry; 4 | 5 | /** 6 | * A representation of a row in a table. 7 | * 8 | * @author criccomini 9 | * 10 | * @param The type of the hash key for this key/value table. 11 | * @param The type of the range key for this key/value table. 12 | * @param The type of the value for this key/value table. 13 | */ 14 | public interface TableRow extends Entry { 15 | /** 16 | * @return The hash key for this row. 17 | */ 18 | public H getHashKey(); 19 | 20 | /** 21 | * @return The value for this row. 22 | */ 23 | @Override 24 | public V getValue(); 25 | 26 | @Override 27 | default H getKey() { 28 | return getHashKey(); 29 | } 30 | 31 | @Override 32 | default V setValue(final V value) { 33 | throw new UnsupportedOperationException(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/table/range/EmptyRangeTableIterator.java: -------------------------------------------------------------------------------- 1 | package ezdb.table.range; 2 | 3 | import java.util.NoSuchElementException; 4 | 5 | import ezdb.table.RangeTableRow; 6 | import ezdb.util.TableIterator; 7 | 8 | @SuppressWarnings({ "rawtypes", "unchecked" }) 9 | public class EmptyRangeTableIterator implements TableIterator> { 10 | 11 | private static final EmptyRangeTableIterator INSTANCE = new EmptyRangeTableIterator(); 12 | 13 | public static EmptyRangeTableIterator get() { 14 | return INSTANCE; 15 | } 16 | 17 | @Override 18 | public boolean hasNext() { 19 | return false; 20 | } 21 | 22 | @Override 23 | public RangeTableRow next() { 24 | throw new NoSuchElementException(); 25 | } 26 | 27 | @Override 28 | public void remove() { 29 | throw new NoSuchElementException(); 30 | } 31 | 32 | @Override 33 | public void close() { 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/table/range/RangeBatch.java: -------------------------------------------------------------------------------- 1 | package ezdb.table.range; 2 | 3 | import ezdb.table.Batch; 4 | 5 | public interface RangeBatch extends Batch { 6 | 7 | /** 8 | * Put a value into the table, keyed by both the hash and range key. If a 9 | * row already exists for the hash/range pair, it should be overwritten. 10 | * 11 | * @param hashKey 12 | * A key used group rows together. 13 | * @param rangeKey 14 | * A secondary key used to sort rows within the same hash key 15 | * group. 16 | * @param value 17 | * The value to be persisted. 18 | */ 19 | void put(H hashKey, R rangeKey, V value); 20 | 21 | /** 22 | * Delete a value for a given hash/range pair. If no value exists for a 23 | * given hash/range pair, this should be a no op. 24 | * 25 | * @param hashKey 26 | * A key used group rows together. 27 | * @param rangeKey 28 | * A secondary key used to sort rows within the same hash key 29 | * group. 30 | */ 31 | void delete(H hashKey, R rangeKey); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/util/LazyRangeKeysGetter.java: -------------------------------------------------------------------------------- 1 | package ezdb.util; 2 | 3 | //@NotThreadSafe 4 | public abstract class LazyRangeKeysGetter { 5 | 6 | private boolean initialized; 7 | protected K hashKey; 8 | protected R rangeKey; 9 | 10 | private void maybeInitialize() { 11 | if (!initialized) { 12 | initialize(); 13 | } 14 | } 15 | 16 | protected abstract void initialize(); 17 | 18 | public final K getHashKey() { 19 | maybeInitialize(); 20 | return hashKey; 21 | } 22 | 23 | public final R getRangeKey() { 24 | maybeInitialize(); 25 | return rangeKey; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/util/LazyValueGetter.java: -------------------------------------------------------------------------------- 1 | package ezdb.util; 2 | 3 | import java.util.function.Supplier; 4 | 5 | //@NotThreadSafe 6 | public abstract class LazyValueGetter implements Supplier { 7 | 8 | private boolean initialized; 9 | private T value; 10 | 11 | @Override 12 | public final T get() { 13 | if (!initialized) { 14 | value = initialize(); 15 | initialized = true; 16 | } 17 | return value; 18 | } 19 | 20 | protected abstract T initialize(); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/util/ObjectRangeTableKey.java: -------------------------------------------------------------------------------- 1 | package ezdb.util; 2 | 3 | import java.util.Comparator; 4 | 5 | public final class ObjectRangeTableKey implements Comparable> { 6 | private final H hashKey; 7 | private final R rangeKey; 8 | private final Comparator> comparator; 9 | 10 | public ObjectRangeTableKey(final H hashKey, final R rangeKey, final Comparator> comparator) { 11 | this.hashKey = hashKey; 12 | this.rangeKey = rangeKey; 13 | this.comparator = comparator; 14 | } 15 | 16 | public H getHashKey() { 17 | return hashKey; 18 | } 19 | 20 | public R getRangeKey() { 21 | return rangeKey; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return getClass().getSimpleName() + " [hashKey=" + getHashKey() + ", rangeKey=" + getRangeKey() + "]"; 27 | } 28 | 29 | @Override 30 | public int compareTo(final ObjectRangeTableKey o) { 31 | return comparator.compare(this, o); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/util/ObjectRangeTableRow.java: -------------------------------------------------------------------------------- 1 | package ezdb.util; 2 | 3 | import java.util.Map.Entry; 4 | 5 | import ezdb.table.RangeTableRow; 6 | 7 | public final class ObjectRangeTableRow implements RangeTableRow { 8 | 9 | private H hashKey; 10 | private R rangeKey; 11 | private V value; 12 | 13 | public ObjectRangeTableRow(Entry, V> entry) { 14 | this(entry.getKey(), entry.getValue()); 15 | } 16 | 17 | public ObjectRangeTableRow(ObjectRangeTableKey key, V value) { 18 | this(key.getHashKey(), key.getRangeKey(), value); 19 | } 20 | 21 | public ObjectRangeTableRow(H hashKey, R rangeKey, V value) { 22 | this.hashKey = hashKey; 23 | this.rangeKey = rangeKey; 24 | this.value = value; 25 | } 26 | 27 | @Override 28 | public H getHashKey() { 29 | return hashKey; 30 | } 31 | 32 | @Override 33 | public R getRangeKey() { 34 | return rangeKey; 35 | } 36 | 37 | @Override 38 | public V getValue() { 39 | return value; 40 | } 41 | 42 | @Override 43 | public int hashCode() { 44 | final int prime = 31; 45 | int result = 1; 46 | result = prime * result 47 | + ((getHashKey() == null) ? 0 : getHashKey().hashCode()); 48 | result = prime * result 49 | + ((getRangeKey() == null) ? 0 : getRangeKey().hashCode()); 50 | result = prime * result 51 | + ((getValue() == null) ? 0 : getValue().hashCode()); 52 | return result; 53 | } 54 | 55 | @SuppressWarnings("rawtypes") 56 | @Override 57 | public boolean equals(Object obj) { 58 | if (this == obj) 59 | return true; 60 | if (obj == null) 61 | return false; 62 | if (getClass() != obj.getClass()) 63 | return false; 64 | ObjectRangeTableRow other = (ObjectRangeTableRow) obj; 65 | if (getHashKey() == null) { 66 | if (other.getHashKey() != null) 67 | return false; 68 | } else if (!getHashKey().equals(other.getHashKey())) 69 | return false; 70 | if (getRangeKey() == null) { 71 | if (other.getRangeKey() != null) 72 | return false; 73 | } else if (!getRangeKey().equals(other.getRangeKey())) 74 | return false; 75 | if (getValue() == null) { 76 | if (other.getValue() != null) 77 | return false; 78 | } else if (!getValue().equals(other.getValue())) 79 | return false; 80 | return true; 81 | } 82 | 83 | @Override 84 | public String toString() { 85 | return getClass().getSimpleName() + " [hashKey=" + getHashKey() 86 | + ", rangeKey=" + getRangeKey() + ", value=" + getValue() + "]"; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/util/ObjectTableRow.java: -------------------------------------------------------------------------------- 1 | package ezdb.util; 2 | 3 | import java.util.Map.Entry; 4 | 5 | import ezdb.table.TableRow; 6 | 7 | public final class ObjectTableRow implements TableRow { 8 | 9 | private H hashKey; 10 | private V value; 11 | 12 | public ObjectTableRow(final Entry entry) { 13 | this(entry.getKey(), entry.getValue()); 14 | } 15 | 16 | public ObjectTableRow(final H hashKey, final V value) { 17 | this.hashKey = hashKey; 18 | this.value = value; 19 | } 20 | 21 | @Override 22 | public H getHashKey() { 23 | return hashKey; 24 | } 25 | 26 | @Override 27 | public V getValue() { 28 | return value; 29 | } 30 | 31 | @Override 32 | public int hashCode() { 33 | final int prime = 31; 34 | int result = 1; 35 | result = prime * result + ((getHashKey() == null) ? 0 : getHashKey().hashCode()); 36 | result = prime * result + ((getValue() == null) ? 0 : getValue().hashCode()); 37 | return result; 38 | } 39 | 40 | @SuppressWarnings("rawtypes") 41 | @Override 42 | public boolean equals(final Object obj) { 43 | if (this == obj) { 44 | return true; 45 | } 46 | if (obj == null) { 47 | return false; 48 | } 49 | if (getClass() != obj.getClass()) { 50 | return false; 51 | } 52 | final ObjectTableRow other = (ObjectTableRow) obj; 53 | if (getHashKey() == null) { 54 | if (other.getHashKey() != null) { 55 | return false; 56 | } 57 | } else if (!getHashKey().equals(other.getHashKey())) { 58 | return false; 59 | } 60 | if (getValue() == null) { 61 | if (other.getValue() != null) { 62 | return false; 63 | } 64 | } else if (!getValue().equals(other.getValue())) { 65 | return false; 66 | } 67 | return true; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return getClass().getSimpleName() + " [hashKey=" + getHashKey() + ", value=" + getValue() + "]"; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /ezdb-api/src/main/java/ezdb/util/TableIterator.java: -------------------------------------------------------------------------------- 1 | package ezdb.util; 2 | 3 | import java.io.Closeable; 4 | import java.util.Iterator; 5 | 6 | import ezdb.table.TableRow; 7 | 8 | public interface TableIterator> extends Iterator, Closeable { 9 | /** 10 | * Close the iterator and release all resources. 11 | */ 12 | @Override 13 | public void close(); 14 | } 15 | -------------------------------------------------------------------------------- /ezdb-leveldb-jni/.gitignore: -------------------------------------------------------------------------------- 1 | /.settings/ 2 | -------------------------------------------------------------------------------- /ezdb-leveldb-jni/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.github.criccomini 9 | ezdb-parent 10 | 0.1.18-SNAPSHOT 11 | 12 | 13 | ezdb-leveldb-jni 14 | EZDB LevelDB JNI implementation 15 | jar 16 | 17 | 18 | 19 | com.github.criccomini 20 | ezdb-api 21 | 22 | 23 | com.github.criccomini 24 | ezdb-treemap 25 | test 26 | 27 | 28 | io.github.pcmind 29 | leveldb-api 30 | 31 | 32 | org.fusesource.leveldbjni 33 | leveldbjni-all 34 | 35 | 36 | junit 37 | junit 38 | test 39 | 40 | 41 | com.google.guava 42 | guava 43 | test 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /ezdb-leveldb-jni/src/main/java/ezdb/leveldb/EzLevelDbJni.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.ByteBuffer; 6 | import java.util.Comparator; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import org.iq80.leveldb.Options; 11 | 12 | import ezdb.Db; 13 | import ezdb.DbException; 14 | import ezdb.comparator.LexicographicalComparator; 15 | import ezdb.leveldb.table.EzLevelDbJniTable; 16 | import ezdb.leveldb.table.range.EzLevelDbJniRangeTable; 17 | import ezdb.serde.Serde; 18 | import ezdb.table.Table; 19 | import ezdb.table.range.RangeTable; 20 | 21 | /** 22 | * An implementation of Db that uses LevelDb tables to persist data. Each 23 | * "table" is just a LevelDB database persisted as a subdirectory inside of 24 | * EzLevelDb's root. 25 | * 26 | * @author criccomini 27 | * 28 | */ 29 | public class EzLevelDbJni implements Db { 30 | private final File root; 31 | private final Map> cache; 32 | private final EzLevelDbJniFactory factory; 33 | 34 | public EzLevelDbJni(final File root) { 35 | this(root, new EzLevelDbJniFactory()); 36 | } 37 | 38 | public EzLevelDbJni(final File root, final EzLevelDbJniFactory factory) { 39 | this.root = root; 40 | this.factory = factory; 41 | this.cache = new HashMap>(); 42 | } 43 | 44 | @Override 45 | public void deleteTable(final String tableName) { 46 | try { 47 | synchronized (cache) { 48 | cache.remove(tableName); 49 | factory.destroy(getFile(tableName), new Options()); 50 | } 51 | } catch (final IOException e) { 52 | throw new DbException(e); 53 | } 54 | } 55 | 56 | @Override 57 | public Table getTable(final String tableName, final Serde hashKeySerde, final Serde valueSerde) { 58 | return getTable(tableName, hashKeySerde, valueSerde, new LexicographicalComparator()); 59 | } 60 | 61 | @SuppressWarnings("unchecked") 62 | @Override 63 | public Table getTable(final String tableName, final Serde hashKeySerde, final Serde valueSerde, 64 | final Comparator hashKeyComparator) { 65 | synchronized (cache) { 66 | Table table = cache.get(tableName); 67 | 68 | if (table == null) { 69 | table = new EzLevelDbJniTable(new File(root, tableName), factory, hashKeySerde, valueSerde, 70 | hashKeyComparator); 71 | cache.put(tableName, table); 72 | } else if (!(table instanceof EzLevelDbJniTable)) { 73 | throw new IllegalStateException("Expected " + EzLevelDbJniTable.class.getSimpleName() + " but got " 74 | + table.getClass().getSimpleName() + " for: " + tableName); 75 | } 76 | 77 | return (Table) table; 78 | } 79 | } 80 | 81 | @Override 82 | public RangeTable getRangeTable(final String tableName, final Serde hashKeySerde, 83 | final Serde rangeKeySerde, final Serde valueSerde) { 84 | return getRangeTable(tableName, hashKeySerde, rangeKeySerde, valueSerde, new LexicographicalComparator(), 85 | new LexicographicalComparator()); 86 | } 87 | 88 | @SuppressWarnings("unchecked") 89 | @Override 90 | public RangeTable getRangeTable(final String tableName, final Serde hashKeySerde, 91 | final Serde rangeKeySerde, final Serde valueSerde, final Comparator hashKeyComparator, 92 | final Comparator rangeKeyComparator) { 93 | synchronized (cache) { 94 | RangeTable table = (RangeTable) cache.get(tableName); 95 | 96 | if (table == null) { 97 | table = new EzLevelDbJniRangeTable(new File(root, tableName), factory, hashKeySerde, 98 | rangeKeySerde, valueSerde, hashKeyComparator, rangeKeyComparator); 99 | cache.put(tableName, table); 100 | } else if (!(table instanceof EzLevelDbJniRangeTable)) { 101 | throw new IllegalStateException("Expected " + EzLevelDbJniRangeTable.class.getSimpleName() + " but got " 102 | + table.getClass().getSimpleName() + " for: " + tableName); 103 | } 104 | 105 | return (RangeTable) table; 106 | } 107 | } 108 | 109 | /** 110 | * A helper method used to convert a table name to the location on disk where 111 | * this LevelDB database will be persisted. 112 | * 113 | * @param tableName The logical name of the table. 114 | * @return The physical location of the directory where this table should be 115 | * persisted. 116 | */ 117 | private File getFile(final String tableName) { 118 | return new File(root, tableName); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /ezdb-leveldb-jni/src/main/java/ezdb/leveldb/EzLevelDbJniFactory.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import org.fusesource.leveldbjni.JniDBFactory; 7 | import org.iq80.leveldb.DB; 8 | import org.iq80.leveldb.Options; 9 | 10 | public class EzLevelDbJniFactory { 11 | public DB open(final File path, final Options options, final boolean rangeTable) throws IOException { 12 | return JniDBFactory.factory.open(path, options); 13 | } 14 | 15 | public void destroy(final File path, final Options options) throws IOException { 16 | JniDBFactory.factory.destroy(path, options); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ezdb-leveldb-jni/src/main/java/ezdb/leveldb/table/EzLevelDbJniBatch.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb.table; 2 | 3 | import java.io.IOException; 4 | 5 | import org.iq80.leveldb.DB; 6 | import org.iq80.leveldb.WriteBatch; 7 | 8 | import ezdb.serde.Serde; 9 | import ezdb.table.Batch; 10 | 11 | public class EzLevelDbJniBatch implements Batch { 12 | 13 | private final DB db; 14 | private final WriteBatch writeBatch; 15 | private final Serde hashKeySerde; 16 | private final Serde valueSerde; 17 | 18 | public EzLevelDbJniBatch(final DB db, final Serde hashKeySerde, final Serde valueSerde) { 19 | this.db = db; 20 | this.writeBatch = db.createWriteBatch(); 21 | this.hashKeySerde = hashKeySerde; 22 | this.valueSerde = valueSerde; 23 | } 24 | 25 | @Override 26 | public void put(final H hashKey, final V value) { 27 | final byte[] valueBytes = valueSerde.toBytes(value); 28 | final byte[] keyBytes = hashKeySerde.toBytes(hashKey); 29 | writeBatch.put(keyBytes, valueBytes); 30 | } 31 | 32 | @Override 33 | public void delete(final H hashKey) { 34 | writeBatch.delete(hashKeySerde.toBytes(hashKey)); 35 | } 36 | 37 | @Override 38 | public void flush() { 39 | db.write(writeBatch); 40 | } 41 | 42 | @Override 43 | public void close() throws IOException { 44 | writeBatch.close(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /ezdb-leveldb-jni/src/main/java/ezdb/leveldb/table/EzLevelDbJniComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb.table; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Comparator; 5 | 6 | import org.iq80.leveldb.DBComparator; 7 | 8 | import ezdb.util.Util; 9 | 10 | /** 11 | * LevelDb provides a comparator interface that we can use to handle hash/range 12 | * pairs. 13 | * 14 | * @author criccomini 15 | * 16 | */ 17 | public class EzLevelDbJniComparator implements DBComparator { 18 | public static final String name = EzLevelDbJniComparator.class.toString(); 19 | 20 | private final Comparator hashKeyComparator; 21 | 22 | public EzLevelDbJniComparator(final Comparator hashKeyComparator) { 23 | this.hashKeyComparator = hashKeyComparator; 24 | } 25 | 26 | @Override 27 | public int compare(final byte[] k1, final byte[] k2) { 28 | return Util.compareKeys(hashKeyComparator, ByteBuffer.wrap(k1), ByteBuffer.wrap(k2)); 29 | } 30 | 31 | @Override 32 | public byte[] findShortSuccessor(final byte[] key) { 33 | return key; 34 | } 35 | 36 | @Override 37 | public byte[] findShortestSeparator(final byte[] start, final byte[] limit) { 38 | return start; 39 | } 40 | 41 | @Override 42 | public String name() { 43 | return name; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ezdb-leveldb-jni/src/main/java/ezdb/leveldb/table/EzLevelDbJniTable.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb.table; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.ByteBuffer; 6 | import java.util.Comparator; 7 | import java.util.NoSuchElementException; 8 | 9 | import org.iq80.leveldb.DB; 10 | import org.iq80.leveldb.DBIterator; 11 | import org.iq80.leveldb.Options; 12 | 13 | import ezdb.DbException; 14 | import ezdb.leveldb.EzLevelDbJniFactory; 15 | import ezdb.serde.Serde; 16 | import ezdb.table.Batch; 17 | import ezdb.table.RawTableRow; 18 | import ezdb.table.Table; 19 | import ezdb.table.TableRow; 20 | import ezdb.util.TableIterator; 21 | 22 | public class EzLevelDbJniTable implements Table { 23 | private final DB db; 24 | private final Serde hashKeySerde; 25 | private final Serde valueSerde; 26 | private final Comparator hashKeyComparator; 27 | 28 | public EzLevelDbJniTable(final File path, final EzLevelDbJniFactory factory, final Serde hashKeySerde, 29 | final Serde valueSerde, final Comparator hashKeyComparator) { 30 | this.hashKeySerde = hashKeySerde; 31 | this.valueSerde = valueSerde; 32 | this.hashKeyComparator = hashKeyComparator; 33 | 34 | final Options options = new Options(); 35 | options.createIfMissing(true); 36 | options.comparator(new EzLevelDbJniComparator(hashKeyComparator)); 37 | 38 | try { 39 | this.db = factory.open(path, options, false); 40 | } catch (final IOException e) { 41 | throw new DbException(e); 42 | } 43 | } 44 | 45 | @Override 46 | public void put(final H hashKey, final V value) { 47 | db.put(hashKeySerde.toBytes(hashKey), valueSerde.toBytes(value)); 48 | } 49 | 50 | @Override 51 | public V get(final H hashKey) { 52 | final byte[] valueBytes = db.get(hashKeySerde.toBytes(hashKey)); 53 | 54 | if (valueBytes == null) { 55 | return null; 56 | } 57 | 58 | return valueSerde.fromBytes(valueBytes); 59 | } 60 | 61 | @Override 62 | public TableIterator> range() { 63 | final DBIterator iterator = db.iterator(); 64 | iterator.seekToFirst(); 65 | return new AutoClosingTableIterator(new TableIterator>() { 66 | @Override 67 | public boolean hasNext() { 68 | return iterator.hasNext(); 69 | } 70 | 71 | @Override 72 | public TableRow next() { 73 | if (hasNext()) { 74 | return RawTableRow.valueOfBytes(iterator.next(), hashKeySerde, valueSerde); 75 | } else { 76 | throw new NoSuchElementException(); 77 | } 78 | } 79 | 80 | @Override 81 | public void remove() { 82 | iterator.remove(); 83 | } 84 | 85 | @Override 86 | public void close() { 87 | try { 88 | iterator.close(); 89 | } catch (final Exception e) { 90 | throw new DbException(e); 91 | } 92 | } 93 | }); 94 | } 95 | 96 | @Override 97 | public void delete(final H hashKey) { 98 | this.db.delete(hashKeySerde.toBytes(hashKey)); 99 | } 100 | 101 | @Override 102 | public void close() { 103 | try { 104 | this.db.close(); 105 | } catch (final Exception e) { 106 | throw new DbException(e); 107 | } 108 | } 109 | 110 | private static class AutoClosingTableIterator<_H, _V> implements TableIterator> { 111 | 112 | private final TableIterator> delegate; 113 | private boolean closed; 114 | 115 | public AutoClosingTableIterator(final TableIterator> delegate) { 116 | this.delegate = delegate; 117 | } 118 | 119 | @Override 120 | public boolean hasNext() { 121 | if (closed) { 122 | return false; 123 | } 124 | final boolean hasNext = delegate.hasNext(); 125 | if (!hasNext) { 126 | close(); 127 | } 128 | return hasNext; 129 | } 130 | 131 | @Override 132 | public TableRow<_H, _V> next() { 133 | if (closed) { 134 | throw new NoSuchElementException(); 135 | } 136 | return delegate.next(); 137 | } 138 | 139 | @Override 140 | public void remove() { 141 | delegate.remove(); 142 | } 143 | 144 | @Override 145 | protected void finalize() throws Throwable { 146 | super.finalize(); 147 | close(); 148 | } 149 | 150 | @Override 151 | public void close() { 152 | if (!closed) { 153 | closed = true; 154 | delegate.close(); 155 | } 156 | } 157 | 158 | } 159 | 160 | @Override 161 | public Batch newBatch() { 162 | return new EzLevelDbJniBatch(db, hashKeySerde, valueSerde); 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /ezdb-leveldb-jni/src/main/java/ezdb/leveldb/table/range/EzLevelDbJniRangeBatch.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb.table.range; 2 | 3 | import java.io.IOException; 4 | 5 | import org.iq80.leveldb.DB; 6 | import org.iq80.leveldb.WriteBatch; 7 | 8 | import ezdb.serde.Serde; 9 | import ezdb.table.range.RangeBatch; 10 | import ezdb.util.Util; 11 | 12 | public class EzLevelDbJniRangeBatch implements RangeBatch { 13 | 14 | private final DB db; 15 | private final WriteBatch writeBatch; 16 | private final Serde hashKeySerde; 17 | private final Serde rangeKeySerde; 18 | private final Serde valueSerde; 19 | 20 | public EzLevelDbJniRangeBatch(final DB db, final Serde hashKeySerde, final Serde rangeKeySerde, 21 | final Serde valueSerde) { 22 | this.db = db; 23 | this.writeBatch = db.createWriteBatch(); 24 | this.hashKeySerde = hashKeySerde; 25 | this.rangeKeySerde = rangeKeySerde; 26 | this.valueSerde = valueSerde; 27 | } 28 | 29 | @Override 30 | public void put(final H hashKey, final V value) { 31 | put(hashKey, null, value); 32 | } 33 | 34 | @Override 35 | public void delete(final H hashKey) { 36 | delete(hashKey, null); 37 | } 38 | 39 | @Override 40 | public void flush() { 41 | db.write(writeBatch); 42 | } 43 | 44 | @Override 45 | public void close() throws IOException { 46 | writeBatch.close(); 47 | } 48 | 49 | @Override 50 | public void put(final H hashKey, final R rangeKey, final V value) { 51 | final byte[] valueBytes = valueSerde.toBytes(value); 52 | final byte[] keyBytes = Util.combineBytes(hashKeySerde, rangeKeySerde, hashKey, rangeKey); 53 | writeBatch.put(keyBytes, valueBytes); 54 | } 55 | 56 | @Override 57 | public void delete(final H hashKey, final R rangeKey) { 58 | writeBatch.delete(Util.combineBytes(hashKeySerde, rangeKeySerde, hashKey, rangeKey)); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /ezdb-leveldb-jni/src/main/java/ezdb/leveldb/table/range/EzLevelDbJniRangeComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb.table.range; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Comparator; 5 | 6 | import org.iq80.leveldb.DBComparator; 7 | 8 | import ezdb.util.Util; 9 | 10 | /** 11 | * LevelDb provides a comparator interface that we can use to handle hash/range 12 | * pairs. 13 | * 14 | * @author criccomini 15 | * 16 | */ 17 | public class EzLevelDbJniRangeComparator implements DBComparator { 18 | public static final String name = EzLevelDbJniRangeComparator.class.toString(); 19 | 20 | private final Comparator hashKeyComparator; 21 | private final Comparator rangeKeyComparator; 22 | 23 | public EzLevelDbJniRangeComparator(final Comparator hashKeyComparator, 24 | final Comparator rangeKeyComparator) { 25 | this.hashKeyComparator = hashKeyComparator; 26 | this.rangeKeyComparator = rangeKeyComparator; 27 | } 28 | 29 | @Override 30 | public int compare(final byte[] k1, final byte[] k2) { 31 | return Util.compareKeys(hashKeyComparator, rangeKeyComparator, ByteBuffer.wrap(k1), ByteBuffer.wrap(k2)); 32 | } 33 | 34 | @Override 35 | public byte[] findShortSuccessor(final byte[] key) { 36 | return key; 37 | } 38 | 39 | @Override 40 | public byte[] findShortestSeparator(final byte[] start, final byte[] limit) { 41 | return start; 42 | } 43 | 44 | @Override 45 | public String name() { 46 | return name; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ezdb-leveldb-jni/src/test/java/ezdb/leveldb/FileUtils.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb; 2 | 3 | import static com.google.common.base.Preconditions.checkArgument; 4 | import static java.util.Objects.requireNonNull; 5 | 6 | import java.io.File; 7 | import java.io.FilenameFilter; 8 | import java.io.IOException; 9 | 10 | import com.google.common.collect.ImmutableList; 11 | 12 | public final class FileUtils { 13 | private static final int TEMP_DIR_ATTEMPTS = 10000; 14 | 15 | private FileUtils() { 16 | } 17 | 18 | public static boolean isSymbolicLink(final File file) { 19 | try { 20 | final File canonicalFile = file.getCanonicalFile(); 21 | final File absoluteFile = file.getAbsoluteFile(); 22 | final File parentFile = file.getParentFile(); 23 | // a symbolic link has a different name between the canonical and absolute path 24 | return !canonicalFile.getName().equals(absoluteFile.getName()) || 25 | // or the canonical parent path is not the same as the file's parent path, 26 | // provided the file has a parent path 27 | parentFile != null && !parentFile.getCanonicalPath().equals(canonicalFile.getParent()); 28 | } catch (final IOException e) { 29 | // error on the side of caution 30 | return true; 31 | } 32 | } 33 | 34 | public static ImmutableList listFiles(final File dir) { 35 | final File[] files = dir.listFiles(); 36 | if (files == null) { 37 | return ImmutableList.of(); 38 | } 39 | return ImmutableList.copyOf(files); 40 | } 41 | 42 | public static ImmutableList listFiles(final File dir, final FilenameFilter filter) { 43 | final File[] files = dir.listFiles(filter); 44 | if (files == null) { 45 | return ImmutableList.of(); 46 | } 47 | return ImmutableList.copyOf(files); 48 | } 49 | 50 | public static File createTempDir(final String prefix) { 51 | return createTempDir(new File(System.getProperty("java.io.tmpdir")), prefix); 52 | } 53 | 54 | public static File createTempDir(final File parentDir, final String prefix) { 55 | String baseName = ""; 56 | if (prefix != null) { 57 | baseName += prefix + "-"; 58 | } 59 | 60 | baseName += System.currentTimeMillis() + "-"; 61 | for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) { 62 | final File tempDir = new File(parentDir, baseName + counter); 63 | if (tempDir.mkdir()) { 64 | return tempDir; 65 | } 66 | } 67 | throw new IllegalStateException("Failed to create directory within " + TEMP_DIR_ATTEMPTS + " attempts (tried " 68 | + baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')'); 69 | } 70 | 71 | public static boolean deleteDirectoryContents(final File directory) { 72 | checkArgument(directory.isDirectory(), "Not a directory: %s", directory); 73 | 74 | // Don't delete symbolic link directories 75 | if (isSymbolicLink(directory)) { 76 | return false; 77 | } 78 | 79 | boolean success = true; 80 | for (final File file : listFiles(directory)) { 81 | success = deleteRecursively(file) && success; 82 | } 83 | return success; 84 | } 85 | 86 | public static boolean deleteRecursively(final File file) { 87 | boolean success = true; 88 | if (file.isDirectory()) { 89 | success = deleteDirectoryContents(file); 90 | } 91 | 92 | return file.delete() && success; 93 | } 94 | 95 | public static File newFile(final String parent, final String... paths) { 96 | requireNonNull(parent, "parent is null"); 97 | requireNonNull(paths, "paths is null"); 98 | 99 | return newFile(new File(parent), ImmutableList.copyOf(paths)); 100 | } 101 | 102 | public static File newFile(final File parent, final String... paths) { 103 | requireNonNull(parent, "parent is null"); 104 | requireNonNull(paths, "paths is null"); 105 | 106 | return newFile(parent, ImmutableList.copyOf(paths)); 107 | } 108 | 109 | public static File newFile(final File parent, final Iterable paths) { 110 | requireNonNull(parent, "parent is null"); 111 | requireNonNull(paths, "paths is null"); 112 | 113 | File result = parent; 114 | for (final String path : paths) { 115 | result = new File(result, path); 116 | } 117 | return result; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /ezdb-leveldb/.gitignore: -------------------------------------------------------------------------------- 1 | /.settings/ 2 | -------------------------------------------------------------------------------- /ezdb-leveldb/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | com.github.criccomini 7 | ezdb-parent 8 | 0.1.18-SNAPSHOT 9 | 10 | 11 | ezdb-leveldb 12 | EZDB LevelDB implementation 13 | jar 14 | 15 | 16 | 17 | com.github.criccomini 18 | ezdb-api 19 | 20 | 21 | com.github.criccomini 22 | ezdb-treemap 23 | test 24 | 25 | 26 | io.github.pcmind 27 | leveldb 28 | 29 | 30 | junit 31 | junit 32 | test 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /ezdb-leveldb/src/main/java/ezdb/leveldb/EzLevelDbJava.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.ByteBuffer; 6 | import java.util.Comparator; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import org.iq80.leveldb.Options; 11 | 12 | import ezdb.Db; 13 | import ezdb.DbException; 14 | import ezdb.comparator.LexicographicalComparator; 15 | import ezdb.leveldb.table.EzLevelDbTable; 16 | import ezdb.leveldb.table.range.EzLevelDbRangeTable; 17 | import ezdb.serde.Serde; 18 | import ezdb.table.Table; 19 | import ezdb.table.range.RangeTable; 20 | 21 | /** 22 | * An implementation of Db that uses LevelDb tables to persist data. Each 23 | * "table" is just a LevelDB database persisted as a subdirectory inside of 24 | * EzLevelDb's root. 25 | * 26 | * @author criccomini 27 | * 28 | */ 29 | public class EzLevelDbJava implements Db { 30 | private final File root; 31 | private final Map> cache; 32 | private final EzLevelDbJavaFactory factory; 33 | 34 | public EzLevelDbJava(final File root) { 35 | this(root, new EzLevelDbJavaFactory()); 36 | } 37 | 38 | public EzLevelDbJava(final File root, final EzLevelDbJavaFactory factory) { 39 | this.root = root; 40 | this.factory = factory; 41 | this.cache = new HashMap>(); 42 | } 43 | 44 | @Override 45 | public void deleteTable(final String tableName) { 46 | try { 47 | synchronized (cache) { 48 | cache.remove(tableName); 49 | factory.destroy(getFile(tableName), new Options()); 50 | } 51 | } catch (final IOException e) { 52 | throw new DbException(e); 53 | } 54 | } 55 | 56 | @SuppressWarnings("unchecked") 57 | @Override 58 | public Table getTable(final String tableName, final Serde hashKeySerde, final Serde valueSerde) { 59 | return getTable(tableName, hashKeySerde, valueSerde, new LexicographicalComparator()); 60 | } 61 | 62 | @SuppressWarnings("unchecked") 63 | @Override 64 | public Table getTable(final String tableName, final Serde hashKeySerde, final Serde valueSerde, 65 | final Comparator hashKeyComparator) { 66 | synchronized (cache) { 67 | Table table = cache.get(tableName); 68 | 69 | if (table == null) { 70 | table = new EzLevelDbTable(new File(root, tableName), factory, hashKeySerde, valueSerde, 71 | hashKeyComparator); 72 | cache.put(tableName, table); 73 | } else if (!(table instanceof EzLevelDbTable)) { 74 | throw new IllegalStateException("Expected " + EzLevelDbTable.class.getSimpleName() + " but got " 75 | + table.getClass().getSimpleName() + " for: " + tableName); 76 | } 77 | 78 | return (Table) table; 79 | } 80 | } 81 | 82 | @Override 83 | public RangeTable getRangeTable(final String tableName, final Serde hashKeySerde, 84 | final Serde rangeKeySerde, final Serde valueSerde) { 85 | return getRangeTable(tableName, hashKeySerde, rangeKeySerde, valueSerde, new LexicographicalComparator(), 86 | new LexicographicalComparator()); 87 | } 88 | 89 | @SuppressWarnings("unchecked") 90 | @Override 91 | public RangeTable getRangeTable(final String tableName, final Serde hashKeySerde, 92 | final Serde rangeKeySerde, final Serde valueSerde, final Comparator hashKeyComparator, 93 | final Comparator rangeKeyComparator) { 94 | synchronized (cache) { 95 | RangeTable table = (RangeTable) cache.get(tableName); 96 | 97 | if (table == null) { 98 | table = new EzLevelDbRangeTable(new File(root, tableName), factory, hashKeySerde, 99 | rangeKeySerde, valueSerde, hashKeyComparator, rangeKeyComparator); 100 | cache.put(tableName, table); 101 | } else if (!(table instanceof EzLevelDbRangeTable)) { 102 | throw new IllegalStateException("Expected " + EzLevelDbRangeTable.class.getSimpleName() + " but got " 103 | + table.getClass().getSimpleName() + " for: " + tableName); 104 | } 105 | 106 | return (RangeTable) table; 107 | } 108 | } 109 | 110 | /** 111 | * A helper method used to convert a table name to the location on disk where 112 | * this LevelDB database will be persisted. 113 | * 114 | * @param tableName The logical name of the table. 115 | * @return The physical location of the directory where this table should be 116 | * persisted. 117 | */ 118 | private File getFile(final String tableName) { 119 | return new File(root, tableName); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /ezdb-leveldb/src/main/java/ezdb/leveldb/EzLevelDbJavaFactory.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import org.iq80.leveldb.Options; 7 | import org.iq80.leveldb.fileenv.EnvImpl; 8 | import org.iq80.leveldb.impl.ExtendedDbImpl; 9 | import org.iq80.leveldb.impl.Iq80DBFactory; 10 | 11 | public class EzLevelDbJavaFactory { 12 | public ExtendedDbImpl open(final File path, final Options options, final boolean rangeTable) throws IOException { 13 | return new ExtendedDbImpl(options, path.getAbsolutePath(), EnvImpl.createEnv()); 14 | } 15 | 16 | public boolean isVerifyChecksums() { 17 | return true; 18 | } 19 | 20 | public void destroy(final File path, final Options options) throws IOException { 21 | Iq80DBFactory.factory.destroy(path, options); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ezdb-leveldb/src/main/java/ezdb/leveldb/table/EzLevelDbJavaBatch.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb.table; 2 | 3 | import java.io.IOException; 4 | 5 | import org.iq80.leveldb.impl.ExtendedDbImpl; 6 | import org.iq80.leveldb.impl.WriteBatchImpl; 7 | 8 | import ezdb.serde.Serde; 9 | import ezdb.table.Batch; 10 | import ezdb.util.Util; 11 | 12 | public class EzLevelDbJavaBatch implements Batch { 13 | 14 | private final ExtendedDbImpl db; 15 | private final WriteBatchImpl writeBatch; 16 | private final Serde hashKeySerde; 17 | private final Serde valueSerde; 18 | 19 | public EzLevelDbJavaBatch(final ExtendedDbImpl db, final Serde hashKeySerde, final Serde valueSerde) { 20 | this.db = db; 21 | this.writeBatch = db.createWriteBatch(); 22 | this.hashKeySerde = hashKeySerde; 23 | this.valueSerde = valueSerde; 24 | } 25 | 26 | @Override 27 | public void put(final H hashKey, final V value) { 28 | final byte[] valueBytes = valueSerde.toBytes(value); 29 | final byte[] keyBytes = Util.combineBytes(hashKeySerde, hashKey); 30 | writeBatch.put(keyBytes, valueBytes); 31 | } 32 | 33 | @Override 34 | public void delete(final H hashKey) { 35 | writeBatch.delete(Util.combineBytes(hashKeySerde, hashKey)); 36 | } 37 | 38 | @Override 39 | public void flush() { 40 | db.write(writeBatch); 41 | } 42 | 43 | @Override 44 | public void close() throws IOException { 45 | writeBatch.close(); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /ezdb-leveldb/src/main/java/ezdb/leveldb/table/EzLevelDbJavaComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb.table; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Comparator; 5 | 6 | import org.iq80.leveldb.util.Slice; 7 | 8 | import ezdb.leveldb.util.Slices; 9 | import ezdb.leveldb.util.ZeroCopyDBComparator; 10 | 11 | /** 12 | * LevelDb provides a comparator interface that we can use to handle hash/range 13 | * pairs. 14 | * 15 | * @author criccomini 16 | * 17 | */ 18 | public class EzLevelDbJavaComparator implements ZeroCopyDBComparator { 19 | public static final String name = EzLevelDbJavaComparator.class.toString(); 20 | 21 | private final Comparator hashKeyComparator; 22 | 23 | public EzLevelDbJavaComparator(final Comparator hashKeyComparator) { 24 | this.hashKeyComparator = hashKeyComparator; 25 | } 26 | 27 | /** 28 | * LevelDBJni still uses this slow version 29 | */ 30 | @Deprecated 31 | @Override 32 | public int compare(final byte[] k1, final byte[] k2) { 33 | throw new UnsupportedOperationException("should use zero copy method"); 34 | } 35 | 36 | public int compare(final Slice k1, final Slice k2) { 37 | return Slices.compareKeys(hashKeyComparator, k1, k2); 38 | } 39 | 40 | @Override 41 | public byte[] findShortSuccessor(final byte[] key) { 42 | return key; 43 | } 44 | 45 | @Override 46 | public byte[] findShortestSeparator(final byte[] start, final byte[] limit) { 47 | return start; 48 | } 49 | 50 | @Override 51 | public String name() { 52 | return name; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ezdb-leveldb/src/main/java/ezdb/leveldb/table/EzLevelDbTable.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb.table; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.ByteBuffer; 6 | import java.util.Comparator; 7 | import java.util.NoSuchElementException; 8 | 9 | import org.iq80.leveldb.Options; 10 | import org.iq80.leveldb.ReadOptions; 11 | import org.iq80.leveldb.WriteOptions; 12 | import org.iq80.leveldb.impl.ExtendedDbImpl; 13 | import org.iq80.leveldb.util.Slice; 14 | 15 | import ezdb.DbException; 16 | import ezdb.leveldb.EzLevelDbJavaFactory; 17 | import ezdb.leveldb.util.EzLevelDBIterator; 18 | import ezdb.leveldb.util.Slices; 19 | import ezdb.serde.Serde; 20 | import ezdb.table.Batch; 21 | import ezdb.table.Table; 22 | import ezdb.table.TableRow; 23 | import ezdb.util.TableIterator; 24 | import io.netty.buffer.ByteBuf; 25 | import io.netty.buffer.ByteBufAllocator; 26 | 27 | public class EzLevelDbTable implements Table { 28 | 29 | private static final WriteOptions DEFAULT_WRITE_OPTIONS = new WriteOptions(); 30 | 31 | private final ReadOptions defaultReadOptions; 32 | 33 | private final ExtendedDbImpl db; 34 | private final Serde hashKeySerde; 35 | private final Serde valueSerde; 36 | 37 | public EzLevelDbTable(final File path, final EzLevelDbJavaFactory factory, final Serde hashKeySerde, 38 | final Serde valueSerde, final Comparator hashKeyComparator) { 39 | this.hashKeySerde = hashKeySerde; 40 | this.valueSerde = valueSerde; 41 | 42 | final Options options = new Options(); 43 | options.createIfMissing(true); 44 | this.defaultReadOptions = new ReadOptions().verifyChecksums(factory.isVerifyChecksums()); 45 | options.comparator(new EzLevelDbJavaComparator(hashKeyComparator)); 46 | 47 | try { 48 | this.db = factory.open(path, options, false); 49 | } catch (final IOException e) { 50 | throw new DbException(e); 51 | } 52 | } 53 | 54 | @Override 55 | public TableIterator> range() { 56 | final EzLevelDBIterator iterator = new EzLevelDBIterator(db.extendedIterator(defaultReadOptions), 57 | hashKeySerde, valueSerde); 58 | iterator.seekToFirst(); 59 | return new AutoClosingTableIterator(new TableIterator>() { 60 | @Override 61 | public boolean hasNext() { 62 | return iterator.hasNext(); 63 | } 64 | 65 | @Override 66 | public TableRow next() { 67 | if (hasNext()) { 68 | return iterator.next(); 69 | } else { 70 | throw new NoSuchElementException(); 71 | } 72 | } 73 | 74 | @Override 75 | public void remove() { 76 | iterator.remove(); 77 | } 78 | 79 | @Override 80 | public void close() { 81 | try { 82 | iterator.close(); 83 | } catch (final Exception e) { 84 | throw new DbException(e); 85 | } 86 | } 87 | }); 88 | } 89 | 90 | @Override 91 | public void put(final H hashKey, final V value) { 92 | db.put(hashKeySerde.toBytes(hashKey), valueSerde.toBytes(value), DEFAULT_WRITE_OPTIONS); 93 | } 94 | 95 | @Override 96 | public V get(final H hashKey) { 97 | final ByteBuf keyBuffer = ByteBufAllocator.DEFAULT.heapBuffer(); 98 | try { 99 | hashKeySerde.toBuffer(keyBuffer, hashKey); 100 | final Slice valueBytes = db.get(Slices.wrap(keyBuffer), defaultReadOptions); 101 | 102 | if (valueBytes == null) { 103 | return null; 104 | } 105 | 106 | return valueSerde.fromBuffer(Slices.unwrap(valueBytes)); 107 | } finally { 108 | keyBuffer.release(keyBuffer.refCnt()); 109 | } 110 | } 111 | 112 | @Override 113 | public void delete(final H hashKey) { 114 | this.db.delete(hashKeySerde.toBytes(hashKey), DEFAULT_WRITE_OPTIONS); 115 | } 116 | 117 | @Override 118 | public void close() { 119 | try { 120 | this.db.close(); 121 | } catch (final Exception e) { 122 | throw new DbException(e); 123 | } 124 | } 125 | 126 | @Override 127 | public Batch newBatch() { 128 | return new EzLevelDbJavaBatch(db, hashKeySerde, valueSerde); 129 | } 130 | 131 | private static class AutoClosingTableIterator<_H, _V> implements TableIterator> { 132 | 133 | private final TableIterator> delegate; 134 | private boolean closed; 135 | 136 | public AutoClosingTableIterator(final TableIterator> delegate) { 137 | this.delegate = delegate; 138 | } 139 | 140 | @Override 141 | public boolean hasNext() { 142 | if (closed) { 143 | return false; 144 | } 145 | final boolean hasNext = delegate.hasNext(); 146 | if (!hasNext) { 147 | close(); 148 | } 149 | return hasNext; 150 | } 151 | 152 | @Override 153 | public TableRow<_H, _V> next() { 154 | if (closed) { 155 | throw new NoSuchElementException(); 156 | } 157 | return delegate.next(); 158 | } 159 | 160 | @Override 161 | public void remove() { 162 | delegate.remove(); 163 | } 164 | 165 | @SuppressWarnings("deprecation") 166 | @Override 167 | protected void finalize() throws Throwable { 168 | super.finalize(); 169 | close(); 170 | } 171 | 172 | @Override 173 | public void close() { 174 | if (!closed) { 175 | closed = true; 176 | delegate.close(); 177 | } 178 | } 179 | 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /ezdb-leveldb/src/main/java/ezdb/leveldb/table/range/EzLevelDbJavaRangeBatch.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb.table.range; 2 | 3 | import java.io.IOException; 4 | 5 | import org.iq80.leveldb.impl.ExtendedDbImpl; 6 | import org.iq80.leveldb.impl.WriteBatchImpl; 7 | 8 | import ezdb.serde.Serde; 9 | import ezdb.table.range.RangeBatch; 10 | import ezdb.util.Util; 11 | 12 | public class EzLevelDbJavaRangeBatch implements RangeBatch { 13 | 14 | private final ExtendedDbImpl db; 15 | private final WriteBatchImpl writeBatch; 16 | private final Serde hashKeySerde; 17 | private final Serde rangeKeySerde; 18 | private final Serde valueSerde; 19 | // private final ByteBuf keyBuffer; 20 | // private final ByteBuf valueBuffer; 21 | 22 | public EzLevelDbJavaRangeBatch(final ExtendedDbImpl db, final Serde hashKeySerde, final Serde rangeKeySerde, 23 | final Serde valueSerde) { 24 | this.db = db; 25 | this.writeBatch = db.createWriteBatch(); 26 | this.hashKeySerde = hashKeySerde; 27 | this.rangeKeySerde = rangeKeySerde; 28 | this.valueSerde = valueSerde; 29 | // this.keyBuffer = ByteBufAllocator.DEFAULT.heapBuffer(); 30 | // this.valueBuffer = ByteBufAllocator.DEFAULT.heapBuffer(); 31 | } 32 | 33 | @Override 34 | public void put(final H hashKey, final V value) { 35 | put(hashKey, null, value); 36 | } 37 | 38 | @Override 39 | public void delete(final H hashKey) { 40 | delete(hashKey, null); 41 | } 42 | 43 | @Override 44 | public void flush() { 45 | db.write(writeBatch); 46 | } 47 | 48 | @Override 49 | public void close() throws IOException { 50 | writeBatch.close(); 51 | // this.keyBuffer.release(keyBuffer.refCnt()); 52 | // this.valueBuffer.release(valueBuffer.refCnt()); 53 | } 54 | 55 | @Override 56 | public void put(final H hashKey, final R rangeKey, final V value) { 57 | // keyBuffer.clear(); 58 | // Util.combineBuf(keyBuffer, hashKeySerde, rangeKeySerde, hashKey, rangeKey); 59 | // valueBuffer.clear(); 60 | // valueSerde.toBuffer(valueBuffer, value); 61 | // writeBatch.put(Slices.wrapCopy(keyBuffer), Slices.wrapCopy(valueBuffer)); 62 | // writing operations need to work with byte arrays 63 | final byte[] valueBytes = valueSerde.toBytes(value); 64 | final byte[] keyBytes = Util.combineBytes(hashKeySerde, rangeKeySerde, hashKey, rangeKey); 65 | writeBatch.put(keyBytes, valueBytes); 66 | } 67 | 68 | @Override 69 | public void delete(final H hashKey, final R rangeKey) { 70 | // keyBuffer.clear(); 71 | // Util.combineBuf(keyBuffer, hashKeySerde, rangeKeySerde, hashKey, rangeKey); 72 | // writeBatch.delete(Slices.wrapCopy(keyBuffer)); 73 | /* 74 | * delete does not work when we try zero copy here, maybe because the delete is 75 | * performed async? 76 | */ 77 | writeBatch.delete(Util.combineBytes(hashKeySerde, rangeKeySerde, hashKey, rangeKey)); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /ezdb-leveldb/src/main/java/ezdb/leveldb/table/range/EzLevelDbJavaRangeComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb.table.range; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Comparator; 5 | 6 | import org.iq80.leveldb.util.Slice; 7 | 8 | import ezdb.leveldb.util.Slices; 9 | import ezdb.leveldb.util.ZeroCopyDBComparator; 10 | 11 | /** 12 | * LevelDb provides a comparator interface that we can use to handle hash/range 13 | * pairs. 14 | * 15 | * @author criccomini 16 | * 17 | */ 18 | public class EzLevelDbJavaRangeComparator implements ZeroCopyDBComparator { 19 | public static final String name = EzLevelDbJavaRangeComparator.class.toString(); 20 | 21 | private final Comparator hashKeyComparator; 22 | private final Comparator rangeKeyComparator; 23 | 24 | public EzLevelDbJavaRangeComparator(final Comparator hashKeyComparator, 25 | final Comparator rangeKeyComparator) { 26 | this.hashKeyComparator = hashKeyComparator; 27 | this.rangeKeyComparator = rangeKeyComparator; 28 | } 29 | 30 | /** 31 | * LevelDBJni still uses this slow version 32 | */ 33 | @Deprecated 34 | @Override 35 | public int compare(final byte[] k1, final byte[] k2) { 36 | throw new UnsupportedOperationException("should use zero copy method"); 37 | } 38 | 39 | public int compare(final Slice k1, final Slice k2) { 40 | return Slices.compareKeys(hashKeyComparator, rangeKeyComparator, k1, k2); 41 | } 42 | 43 | @Override 44 | public byte[] findShortSuccessor(final byte[] key) { 45 | return key; 46 | } 47 | 48 | @Override 49 | public byte[] findShortestSeparator(final byte[] start, final byte[] limit) { 50 | return start; 51 | } 52 | 53 | @Override 54 | public String name() { 55 | return name; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ezdb-leveldb/src/main/java/ezdb/leveldb/util/EzDBIterator.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb.util; 2 | 3 | import java.io.Closeable; 4 | import java.util.Iterator; 5 | 6 | import org.iq80.leveldb.util.Slice; 7 | 8 | import ezdb.table.RawTableRow; 9 | 10 | //implementation taken from leveldbjni 11 | /** 12 | * @author Hiram Chirino 13 | */ 14 | public interface EzDBIterator extends Iterator>, Closeable { 15 | 16 | /** 17 | * Repositions the iterator so the key of the next BlockElement returned greater 18 | * than or equal to the specified targetKey. 19 | */ 20 | void seek(Slice key); 21 | 22 | /** 23 | * Repositions the iterator so is is at the beginning of the Database. 24 | */ 25 | void seekToFirst(); 26 | 27 | /** 28 | * Returns the next element in the iteration, without advancing the iteration. 29 | */ 30 | RawTableRow peekNext(); 31 | 32 | Slice peekNextKey(); 33 | 34 | /** 35 | * @return true if there is a previous entry in the iteration. 36 | */ 37 | boolean hasPrev(); 38 | 39 | /** 40 | * @return the previous element in the iteration and rewinds the iteration. 41 | */ 42 | RawTableRow prev(); 43 | 44 | Slice nextKey(); 45 | 46 | /** 47 | * @return the previous element in the iteration, without rewinding the 48 | * iteration. 49 | */ 50 | RawTableRow peekPrev(); 51 | 52 | Slice peekPrevKey(); 53 | 54 | /** 55 | * Repositions the iterator so it is at the end of of the Database. 56 | */ 57 | void seekToLast(); 58 | 59 | @Override 60 | void close(); 61 | 62 | } 63 | -------------------------------------------------------------------------------- /ezdb-leveldb/src/main/java/ezdb/leveldb/util/EzDBRangeIterator.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb.util; 2 | 3 | import java.io.Closeable; 4 | import java.util.Iterator; 5 | 6 | import org.iq80.leveldb.util.Slice; 7 | 8 | import ezdb.table.range.RawRangeTableRow; 9 | 10 | //implementation taken from leveldbjni 11 | /** 12 | * @author Hiram Chirino 13 | */ 14 | public interface EzDBRangeIterator extends Iterator>, Closeable { 15 | 16 | /** 17 | * Repositions the iterator so the key of the next BlockElement returned greater 18 | * than or equal to the specified targetKey. 19 | */ 20 | void seek(Slice key); 21 | 22 | /** 23 | * Repositions the iterator so is is at the beginning of the Database. 24 | */ 25 | void seekToFirst(); 26 | 27 | /** 28 | * Returns the next element in the iteration, without advancing the iteration. 29 | */ 30 | RawRangeTableRow peekNext(); 31 | 32 | Slice peekNextKey(); 33 | 34 | /** 35 | * @return true if there is a previous entry in the iteration. 36 | */ 37 | boolean hasPrev(); 38 | 39 | /** 40 | * @return the previous element in the iteration and rewinds the iteration. 41 | */ 42 | RawRangeTableRow prev(); 43 | 44 | Slice nextKey(); 45 | 46 | /** 47 | * @return the previous element in the iteration, without rewinding the 48 | * iteration. 49 | */ 50 | RawRangeTableRow peekPrev(); 51 | 52 | Slice peekPrevKey(); 53 | 54 | /** 55 | * Repositions the iterator so it is at the end of of the Database. 56 | */ 57 | void seekToLast(); 58 | 59 | @Override 60 | void close(); 61 | 62 | } 63 | -------------------------------------------------------------------------------- /ezdb-leveldb/src/main/java/ezdb/leveldb/util/EzLevelDBIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011, FuseSource Corp. All rights reserved. 3 | * 4 | * http://fusesource.com 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * * Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following disclaimer 14 | * in the documentation and/or other materials provided with the 15 | * distribution. 16 | * * Neither the name of FuseSource Corp. nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package ezdb.leveldb.util; 33 | 34 | import java.util.NoSuchElementException; 35 | 36 | import org.iq80.leveldb.iterator.ExtendedDBIteratorAdapter; 37 | import org.iq80.leveldb.util.Slice; 38 | 39 | import ezdb.serde.Serde; 40 | import ezdb.table.RawTableRow; 41 | 42 | //implementation taken from leveldbjni 43 | /** 44 | * @author Hiram Chirino 45 | */ 46 | public class EzLevelDBIterator implements EzDBIterator { 47 | 48 | private final ExtendedDBIteratorAdapter iterator; 49 | private final Serde hashKeySerde; 50 | private final Serde valueSerde; 51 | private boolean valid = false; 52 | 53 | public EzLevelDBIterator(final ExtendedDBIteratorAdapter iterator, final Serde hashKeySerde, 54 | final Serde valueSerde) { 55 | this.iterator = iterator; 56 | this.hashKeySerde = hashKeySerde; 57 | this.valueSerde = valueSerde; 58 | } 59 | 60 | @Override 61 | public void close() { 62 | iterator.close(); 63 | } 64 | 65 | @SuppressWarnings("deprecation") 66 | @Override 67 | protected void finalize() throws Throwable { 68 | super.finalize(); 69 | close(); 70 | } 71 | 72 | @Override 73 | public void remove() { 74 | throw new UnsupportedOperationException(); 75 | } 76 | 77 | @Override 78 | public void seek(final Slice key) { 79 | valid = iterator.seek(key); 80 | } 81 | 82 | @Override 83 | public void seekToFirst() { 84 | valid = iterator.seekToFirst(); 85 | } 86 | 87 | @Override 88 | public void seekToLast() { 89 | valid = iterator.seekToLast(); 90 | } 91 | 92 | @Override 93 | public RawTableRow peekNext() { 94 | if (!valid) { 95 | throw new NoSuchElementException(); 96 | } 97 | return Slices.newRawTableRow(iterator.getKey(), iterator.getValue(), hashKeySerde, valueSerde); 98 | } 99 | 100 | @Override 101 | public boolean hasNext() { 102 | return valid; 103 | } 104 | 105 | @Override 106 | public RawTableRow next() { 107 | final RawTableRow rc = peekNext(); 108 | valid = iterator.next(); 109 | return rc; 110 | } 111 | 112 | @Override 113 | public Slice nextKey() { 114 | final Slice rc = peekNextKey(); 115 | valid = iterator.next(); 116 | return rc; 117 | } 118 | 119 | @Override 120 | public boolean hasPrev() { 121 | if (!valid) { 122 | return false; 123 | } 124 | valid = iterator.prev(); 125 | try { 126 | return valid; 127 | } finally { 128 | if (valid) { 129 | valid = iterator.next(); 130 | } else { 131 | seekToFirst(); 132 | } 133 | } 134 | } 135 | 136 | @Override 137 | public RawTableRow peekPrev() { 138 | valid = iterator.prev(); 139 | try { 140 | return peekNext(); 141 | } finally { 142 | if (valid) { 143 | valid = iterator.next(); 144 | } else { 145 | seekToFirst(); 146 | } 147 | } 148 | } 149 | 150 | @Override 151 | public RawTableRow prev() { 152 | final RawTableRow rc = peekPrev(); 153 | valid = iterator.prev(); 154 | return rc; 155 | } 156 | 157 | @Override 158 | public Slice peekNextKey() { 159 | if (!valid) { 160 | throw new NoSuchElementException(); 161 | } 162 | return iterator.getKey(); 163 | } 164 | 165 | @Override 166 | public Slice peekPrevKey() { 167 | valid = iterator.prev(); 168 | try { 169 | return peekNextKey(); 170 | } finally { 171 | if (valid) { 172 | valid = iterator.next(); 173 | } else { 174 | seekToFirst(); 175 | } 176 | } 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /ezdb-leveldb/src/main/java/ezdb/leveldb/util/EzLevelDBRangeIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011, FuseSource Corp. All rights reserved. 3 | * 4 | * http://fusesource.com 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * * Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following disclaimer 14 | * in the documentation and/or other materials provided with the 15 | * distribution. 16 | * * Neither the name of FuseSource Corp. nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package ezdb.leveldb.util; 33 | 34 | import java.util.NoSuchElementException; 35 | 36 | import org.iq80.leveldb.iterator.ExtendedDBIteratorAdapter; 37 | import org.iq80.leveldb.util.Slice; 38 | 39 | import ezdb.serde.Serde; 40 | import ezdb.table.range.RawRangeTableRow; 41 | 42 | //implementation taken from leveldbjni 43 | /** 44 | * @author Hiram Chirino 45 | */ 46 | public class EzLevelDBRangeIterator implements EzDBRangeIterator { 47 | 48 | private final ExtendedDBIteratorAdapter iterator; 49 | private final Serde hashKeySerde; 50 | private final Serde rangeKeySerde; 51 | private final Serde valueSerde; 52 | private boolean valid = false; 53 | 54 | public EzLevelDBRangeIterator(final ExtendedDBIteratorAdapter iterator, final Serde hashKeySerde, 55 | final Serde rangeKeySerde, final Serde valueSerde) { 56 | this.iterator = iterator; 57 | this.hashKeySerde = hashKeySerde; 58 | this.rangeKeySerde = rangeKeySerde; 59 | this.valueSerde = valueSerde; 60 | } 61 | 62 | @Override 63 | public void close() { 64 | iterator.close(); 65 | } 66 | 67 | @SuppressWarnings("deprecation") 68 | @Override 69 | protected void finalize() throws Throwable { 70 | super.finalize(); 71 | close(); 72 | } 73 | 74 | @Override 75 | public void remove() { 76 | throw new UnsupportedOperationException(); 77 | } 78 | 79 | @Override 80 | public void seek(final Slice key) { 81 | valid = iterator.seek(key); 82 | } 83 | 84 | @Override 85 | public void seekToFirst() { 86 | valid = iterator.seekToFirst(); 87 | } 88 | 89 | @Override 90 | public void seekToLast() { 91 | valid = iterator.seekToLast(); 92 | } 93 | 94 | @Override 95 | public RawRangeTableRow peekNext() { 96 | if (!valid) { 97 | throw new NoSuchElementException(); 98 | } 99 | return Slices.newRawRangeTableRow(iterator.getKey(), iterator.getValue(), hashKeySerde, rangeKeySerde, valueSerde); 100 | } 101 | 102 | @Override 103 | public boolean hasNext() { 104 | return valid; 105 | } 106 | 107 | @Override 108 | public RawRangeTableRow next() { 109 | final RawRangeTableRow rc = peekNext(); 110 | valid = iterator.next(); 111 | return rc; 112 | } 113 | 114 | @Override 115 | public Slice nextKey() { 116 | final Slice rc = peekNextKey(); 117 | valid = iterator.next(); 118 | return rc; 119 | } 120 | 121 | @Override 122 | public boolean hasPrev() { 123 | if (!valid) { 124 | return false; 125 | } 126 | valid = iterator.prev(); 127 | try { 128 | return valid; 129 | } finally { 130 | if (valid) { 131 | valid = iterator.next(); 132 | } else { 133 | seekToFirst(); 134 | } 135 | } 136 | } 137 | 138 | @Override 139 | public RawRangeTableRow peekPrev() { 140 | valid = iterator.prev(); 141 | try { 142 | return peekNext(); 143 | } finally { 144 | if (valid) { 145 | valid = iterator.next(); 146 | } else { 147 | seekToFirst(); 148 | } 149 | } 150 | } 151 | 152 | @Override 153 | public RawRangeTableRow prev() { 154 | final RawRangeTableRow rc = peekPrev(); 155 | valid = iterator.prev(); 156 | return rc; 157 | } 158 | 159 | @Override 160 | public Slice peekNextKey() { 161 | if (!valid) { 162 | throw new NoSuchElementException(); 163 | } 164 | return iterator.getKey(); 165 | } 166 | 167 | @Override 168 | public Slice peekPrevKey() { 169 | valid = iterator.prev(); 170 | try { 171 | return peekNextKey(); 172 | } finally { 173 | if (valid) { 174 | valid = iterator.next(); 175 | } else { 176 | seekToFirst(); 177 | } 178 | } 179 | } 180 | 181 | } 182 | -------------------------------------------------------------------------------- /ezdb-leveldb/src/main/java/ezdb/leveldb/util/ZeroCopyDBComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.leveldb.util; 2 | 3 | import org.iq80.leveldb.DBComparator; 4 | import org.iq80.leveldb.util.Slice; 5 | 6 | public interface ZeroCopyDBComparator extends DBComparator { 7 | 8 | int compare(final Slice k1, final Slice k2); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /ezdb-leveldb/src/main/java/org/iq80/leveldb/impl/ExtendedDbImpl.java: -------------------------------------------------------------------------------- 1 | package org.iq80.leveldb.impl; 2 | 3 | import java.io.IOException; 4 | 5 | import org.iq80.leveldb.DBException; 6 | import org.iq80.leveldb.Options; 7 | import org.iq80.leveldb.ReadOptions; 8 | import org.iq80.leveldb.Snapshot; 9 | import org.iq80.leveldb.WriteOptions; 10 | import org.iq80.leveldb.env.Env; 11 | import org.iq80.leveldb.iterator.ExtendedDBIteratorAdapter; 12 | import org.iq80.leveldb.iterator.InternalIterator; 13 | import org.iq80.leveldb.iterator.SnapshotSeekingIterator; 14 | import org.iq80.leveldb.table.BytewiseComparator; 15 | import org.iq80.leveldb.table.ExtendedCustomUserComparator; 16 | import org.iq80.leveldb.table.UserComparator; 17 | import org.iq80.leveldb.util.Slice; 18 | 19 | import ezdb.leveldb.util.ZeroCopyDBComparator; 20 | 21 | @SuppressWarnings("AccessingNonPublicFieldOfAnotherObject") 22 | public class ExtendedDbImpl extends ExtensibleDbImpl { 23 | 24 | public ExtendedDbImpl(final Options rawOptions, final String dbname, final Env env) throws IOException { 25 | super(rawOptions, dbname, env); 26 | 27 | } 28 | 29 | @Override 30 | protected UserComparator newUserComparator() { 31 | final ZeroCopyDBComparator comparator = (ZeroCopyDBComparator) options.comparator(); 32 | if (comparator != null) { 33 | return new ExtendedCustomUserComparator(comparator); 34 | } else { 35 | return new BytewiseComparator(); 36 | } 37 | } 38 | 39 | public Slice get(final Slice key, final ReadOptions options) throws DBException { 40 | LookupKey lookupKey; 41 | LookupResult lookupResult; 42 | mutex.lock(); 43 | try { 44 | final long lastSequence = options.snapshot() != null ? snapshots.getSequenceFrom(options.snapshot()) 45 | : versions.getLastSequence(); 46 | lookupKey = new LookupKey(key, lastSequence); 47 | 48 | // First look in the memtable, then in the immutable memtable (if any). 49 | final MemTable memTable = this.memTable; 50 | final MemTable immutableMemTable = this.immutableMemTable; 51 | final Version current = versions.getCurrent(); 52 | current.retain(); 53 | ReadStats readStats = null; 54 | mutex.unlock(); 55 | try { 56 | lookupResult = memTable.get(lookupKey); 57 | if (lookupResult == null && immutableMemTable != null) { 58 | lookupResult = immutableMemTable.get(lookupKey); 59 | } 60 | 61 | if (lookupResult == null) { 62 | // Not in memTables; try live files in level order 63 | readStats = new ReadStats(); 64 | lookupResult = current.get(options, lookupKey, readStats); 65 | } 66 | 67 | // schedule compaction if necessary 68 | } finally { 69 | mutex.lock(); 70 | if (readStats != null && current.updateStats(readStats)) { 71 | maybeScheduleCompaction(); 72 | } 73 | current.release(); 74 | } 75 | } finally { 76 | mutex.unlock(); 77 | } 78 | 79 | if (lookupResult != null) { 80 | final Slice value = lookupResult.getValue(); 81 | if (value != null) { 82 | return value; 83 | } 84 | } 85 | return null; 86 | } 87 | 88 | public Snapshot put(final Slice key, final Slice value, final WriteOptions options) throws DBException { 89 | try (WriteBatchImpl writeBatch = new WriteBatchImpl()) { 90 | return writeInternal(writeBatch.put(key, value), options); 91 | } 92 | } 93 | 94 | public Snapshot delete(final Slice key, final WriteOptions options) throws DBException { 95 | try (WriteBatchImpl writeBatch = new WriteBatchImpl()) { 96 | return writeInternal(writeBatch.delete(key), options); 97 | } 98 | } 99 | 100 | public ExtendedDBIteratorAdapter extendedIterator(final ReadOptions options) { 101 | mutex.lock(); 102 | try { 103 | final InternalIterator rawIterator = internalIterator(options); 104 | 105 | // filter out any entries not visible in our snapshot 106 | final long snapshot = getSnapshot(options); 107 | final SnapshotSeekingIterator snapshotIterator = new SnapshotSeekingIterator(rawIterator, snapshot, 108 | internalKeyComparator.getUserComparator(), new RecordBytesListener()); 109 | return new ExtendedDBIteratorAdapter(snapshotIterator); 110 | } finally { 111 | mutex.unlock(); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /ezdb-leveldb/src/main/java/org/iq80/leveldb/iterator/ExtendedDBIteratorAdapter.java: -------------------------------------------------------------------------------- 1 | package org.iq80.leveldb.iterator; 2 | 3 | import java.io.Closeable; 4 | import java.util.concurrent.atomic.AtomicBoolean; 5 | 6 | import org.iq80.leveldb.DBException; 7 | import org.iq80.leveldb.util.Slice; 8 | 9 | public class ExtendedDBIteratorAdapter implements Closeable { 10 | 11 | private static final String ILLEGAL_STATE = "Illegal use of iterator after release"; 12 | private final SnapshotSeekingIterator seekingIterator; 13 | private final AtomicBoolean closed = new AtomicBoolean(false); 14 | private Direction direction = Direction.FORWARD; 15 | 16 | public ExtendedDBIteratorAdapter(final SnapshotSeekingIterator seekingIterator) { 17 | this.seekingIterator = seekingIterator; 18 | } 19 | 20 | public boolean seekToFirst() { 21 | if (direction == Direction.RELEASED) { 22 | throw new DBException(ILLEGAL_STATE); 23 | } 24 | direction = Direction.FORWARD; 25 | return seekingIterator.seekToFirst(); 26 | } 27 | 28 | public Slice getValue() { 29 | return seekingIterator.value(); 30 | } 31 | 32 | public Slice getKey() { 33 | return seekingIterator.key(); 34 | } 35 | 36 | public boolean seek(final Slice targetKey) { 37 | if (direction == Direction.RELEASED) { 38 | throw new DBException(ILLEGAL_STATE); 39 | } 40 | direction = Direction.FORWARD; 41 | return seekingIterator.seek(targetKey); 42 | } 43 | 44 | public boolean next() { 45 | if (direction == Direction.RELEASED) { 46 | throw new DBException(ILLEGAL_STATE); 47 | } 48 | if (direction != Direction.FORWARD) { 49 | direction = Direction.FORWARD; 50 | } 51 | return seekingIterator.next(); 52 | } 53 | 54 | @Override 55 | public void close() { 56 | // This is an end user API.. he might screw up and close multiple times. 57 | // but we don't want the close multiple times as reference counts go bad. 58 | if (closed.compareAndSet(false, true)) { 59 | direction = Direction.RELEASED; 60 | seekingIterator.close(); 61 | } 62 | } 63 | 64 | public void remove() { 65 | throw new UnsupportedOperationException(); 66 | } 67 | 68 | public boolean seekToLast() { 69 | if (direction == Direction.RELEASED) { 70 | throw new DBException(ILLEGAL_STATE); 71 | } 72 | direction = Direction.REVERSE; 73 | return seekingIterator.seekToLast(); 74 | } 75 | 76 | public boolean prev() { 77 | if (direction == Direction.RELEASED) { 78 | throw new DBException(ILLEGAL_STATE); 79 | } 80 | if (direction != Direction.REVERSE) { 81 | direction = Direction.REVERSE; 82 | } 83 | return seekingIterator.prev(); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /ezdb-leveldb/src/main/java/org/iq80/leveldb/table/ExtendedCustomUserComparator.java: -------------------------------------------------------------------------------- 1 | package org.iq80.leveldb.table; 2 | 3 | import static java.util.Objects.requireNonNull; 4 | 5 | import org.iq80.leveldb.util.Slice; 6 | 7 | import ezdb.leveldb.util.ZeroCopyDBComparator; 8 | 9 | public class ExtendedCustomUserComparator implements UserComparator { 10 | private final ZeroCopyDBComparator comparator; 11 | 12 | public ExtendedCustomUserComparator(final ZeroCopyDBComparator comparator) { 13 | requireNonNull(comparator.name(), "User Comparator name can't be null"); 14 | this.comparator = comparator; 15 | } 16 | 17 | @Override 18 | public String name() { 19 | return comparator.name(); 20 | } 21 | 22 | @Override 23 | public Slice findShortestSeparator(final Slice start, final Slice limit) { 24 | final byte[] shortestSeparator = comparator.findShortestSeparator(start.getBytes(), limit.getBytes()); 25 | requireNonNull(shortestSeparator, "User comparator returned null from findShortestSeparator()"); 26 | return new Slice(shortestSeparator); 27 | } 28 | 29 | @Override 30 | public Slice findShortSuccessor(final Slice key) { 31 | final byte[] shortSuccessor = comparator.findShortSuccessor(key.getBytes()); 32 | requireNonNull(comparator, "User comparator returned null from findShortSuccessor()"); 33 | return new Slice(shortSuccessor); 34 | } 35 | 36 | @Override 37 | public int compare(final Slice o1, final Slice o2) { 38 | return comparator.compare(o1, o2); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/.gitignore: -------------------------------------------------------------------------------- 1 | /.settings/ 2 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | com.github.criccomini 7 | ezdb-parent 8 | 0.1.18-SNAPSHOT 9 | 10 | 11 | ezdb-lmdb-jnr 12 | EZDB LMDB JNR implementation 13 | jar 14 | 15 | 16 | 17 | com.github.criccomini 18 | ezdb-api 19 | 20 | 21 | com.github.criccomini 22 | ezdb-treemap 23 | test 24 | 25 | 26 | org.lmdbjava 27 | lmdbjava 28 | 29 | 30 | com.google.guava 31 | guava 32 | 33 | 34 | junit 35 | junit 36 | test 37 | 38 | 39 | org.burningwave 40 | core 41 | test 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.apache.maven.plugins 49 | maven-compiler-plugin 50 | 3.1 51 | 52 | 1.8 53 | 1.8 54 | 1.8 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/src/main/java/ezdb/lmdb/EzLmDb.java: -------------------------------------------------------------------------------- 1 | package ezdb.lmdb; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.ByteBuffer; 6 | import java.util.Comparator; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import ezdb.Db; 11 | import ezdb.DbException; 12 | import ezdb.comparator.LexicographicalComparator; 13 | import ezdb.lmdb.table.EzLmDbTable; 14 | import ezdb.lmdb.table.range.EzLmDbRangeTable; 15 | import ezdb.serde.Serde; 16 | import ezdb.table.Table; 17 | import ezdb.table.range.RangeTable; 18 | 19 | /** 20 | * An implementation of Db that uses LevelDb tables to persist data. Each 21 | * "table" is just a LevelDB database persisted as a subdirectory inside of 22 | * EzLevelDb's root. 23 | * 24 | * @author criccomini 25 | * 26 | */ 27 | public class EzLmDb implements Db { 28 | private final File root; 29 | private final Map> cache; 30 | private final EzLmDbFactory factory; 31 | 32 | public EzLmDb(final File root) { 33 | this(root, new EzLmDbJnrFactory()); 34 | } 35 | 36 | public EzLmDb(final File root, final EzLmDbFactory factory) { 37 | this.root = root; 38 | this.factory = factory; 39 | this.cache = new HashMap>(); 40 | } 41 | 42 | @Override 43 | public void deleteTable(final String tableName) { 44 | try { 45 | synchronized (cache) { 46 | cache.remove(tableName); 47 | factory.destroy(getFile(tableName)); 48 | } 49 | } catch (final IOException e) { 50 | throw new DbException(e); 51 | } 52 | } 53 | 54 | @SuppressWarnings("unchecked") 55 | @Override 56 | public Table getTable(final String tableName, final Serde hashKeySerde, final Serde valueSerde) { 57 | return getTable(tableName, hashKeySerde, valueSerde, new LexicographicalComparator()); 58 | } 59 | 60 | @SuppressWarnings("unchecked") 61 | @Override 62 | public Table getTable(final String tableName, final Serde hashKeySerde, final Serde valueSerde, 63 | final Comparator hashKeyComparator) { 64 | synchronized (cache) { 65 | Table table = cache.get(tableName); 66 | 67 | if (table == null) { 68 | table = new EzLmDbTable(new File(root, tableName), factory, hashKeySerde, valueSerde, 69 | hashKeyComparator); 70 | cache.put(tableName, table); 71 | } else if (!(table instanceof EzLmDbTable)) { 72 | throw new IllegalStateException("Expected " + EzLmDbTable.class.getSimpleName() + " but got " 73 | + table.getClass().getSimpleName() + " for: " + tableName); 74 | } 75 | 76 | return (Table) table; 77 | } 78 | } 79 | 80 | @Override 81 | public RangeTable getRangeTable(final String tableName, final Serde hashKeySerde, 82 | final Serde rangeKeySerde, final Serde valueSerde) { 83 | return getRangeTable(tableName, hashKeySerde, rangeKeySerde, valueSerde, new LexicographicalComparator(), 84 | new LexicographicalComparator()); 85 | } 86 | 87 | @SuppressWarnings("unchecked") 88 | @Override 89 | public RangeTable getRangeTable(final String tableName, final Serde hashKeySerde, 90 | final Serde rangeKeySerde, final Serde valueSerde, final Comparator hashKeyComparator, 91 | final Comparator rangeKeyComparator) { 92 | synchronized (cache) { 93 | RangeTable table = (RangeTable) cache.get(tableName); 94 | 95 | if (table == null) { 96 | table = new EzLmDbRangeTable(new File(root, tableName), factory, hashKeySerde, rangeKeySerde, 97 | valueSerde, hashKeyComparator, rangeKeyComparator); 98 | cache.put(tableName, table); 99 | } else if (!(table instanceof EzLmDbRangeTable)) { 100 | throw new IllegalStateException("Expected " + EzLmDbRangeTable.class.getSimpleName() + " but got " 101 | + table.getClass().getSimpleName() + " for: " + tableName); 102 | } 103 | 104 | return (RangeTable) table; 105 | } 106 | } 107 | 108 | /** 109 | * A helper method used to convert a table name to the location on disk where 110 | * this LevelDB database will be persisted. 111 | * 112 | * @param tableName The logical name of the table. 113 | * @return The physical location of the directory where this table should be 114 | * persisted. 115 | */ 116 | private File getFile(final String tableName) { 117 | return new File(root, tableName); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/src/main/java/ezdb/lmdb/EzLmDbFactory.java: -------------------------------------------------------------------------------- 1 | package ezdb.lmdb; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.ByteBuffer; 6 | import java.util.Comparator; 7 | 8 | import org.lmdbjava.Dbi; 9 | import org.lmdbjava.DbiFlags; 10 | import org.lmdbjava.Env; 11 | import org.lmdbjava.EnvFlags; 12 | 13 | /** 14 | * An interface that allows us to inject either a JNI or pure-Java 15 | * implementation of LevelDB. EzLevelDb uses this class to open and delete 16 | * LevelDb instances. 17 | * 18 | * @author criccomi 19 | * 20 | */ 21 | public interface EzLmDbFactory { 22 | public Env create(File path, EnvFlags... envFlags) throws IOException; 23 | 24 | public Dbi open(String tableName, Env env, Comparator comparator, 25 | boolean rangeTable, DbiFlags... dbiFlags) throws IOException; 26 | 27 | public void destroy(File path) throws IOException; 28 | } 29 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/src/main/java/ezdb/lmdb/EzLmDbJnrFactory.java: -------------------------------------------------------------------------------- 1 | package ezdb.lmdb; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.ByteBuffer; 6 | import java.util.Comparator; 7 | 8 | import org.lmdbjava.Dbi; 9 | import org.lmdbjava.DbiFlags; 10 | import org.lmdbjava.Env; 11 | import org.lmdbjava.Env.Builder; 12 | import org.lmdbjava.EnvFlags; 13 | 14 | import ezdb.lmdb.util.FileUtils; 15 | 16 | public class EzLmDbJnrFactory implements EzLmDbFactory { 17 | @Override 18 | public Env create(final File path, final EnvFlags... envFlags) throws IOException { 19 | final Env env = newEnv().open(path.getAbsoluteFile(), envFlags); 20 | return env; 21 | } 22 | 23 | protected Builder newEnv() { 24 | return Env.create().setMaxDbs(1).setMapSize(newMapSize()).setMaxReaders(Integer.MAX_VALUE); 25 | } 26 | 27 | protected long newMapSize() { 28 | return Integer.MAX_VALUE; 29 | } 30 | 31 | @Override 32 | public Dbi open(final String tableName, final Env env, 33 | final Comparator comparator, final boolean rangeTable, final DbiFlags... dbiFlags) 34 | throws IOException { 35 | final Dbi dbi = env.openDbi(tableName, comparator, dbiFlags); 36 | return dbi; 37 | } 38 | 39 | @Override 40 | public void destroy(final File path) throws IOException { 41 | // implementation taken from java port of leveldb 42 | FileUtils.deleteRecursively(path); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/src/main/java/ezdb/lmdb/table/EzLmDbBatch.java: -------------------------------------------------------------------------------- 1 | package ezdb.lmdb.table; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | 6 | import org.lmdbjava.Dbi; 7 | import org.lmdbjava.Env; 8 | import org.lmdbjava.Txn; 9 | import org.lmdbjava.Txn.NotReadyException; 10 | 11 | import ezdb.serde.Serde; 12 | import ezdb.table.Batch; 13 | import io.netty.buffer.ByteBuf; 14 | import io.netty.buffer.ByteBufAllocator; 15 | 16 | public class EzLmDbBatch implements Batch { 17 | 18 | private final Dbi db; 19 | private final Txn txn; 20 | private final Serde hashKeySerde; 21 | private final Serde valueSerde; 22 | private final ByteBuf keyBuffer; 23 | private final ByteBuf valueBuffer; 24 | 25 | public EzLmDbBatch(final Env env, final Dbi db, final Serde hashKeySerde, 26 | final Serde valueSerde) { 27 | this.db = db; 28 | this.txn = env.txnWrite(); 29 | this.hashKeySerde = hashKeySerde; 30 | this.valueSerde = valueSerde; 31 | this.keyBuffer = ByteBufAllocator.DEFAULT.directBuffer(); 32 | this.valueBuffer = ByteBufAllocator.DEFAULT.directBuffer(); 33 | } 34 | 35 | @Override 36 | public void put(final H hashKey, final V value) { 37 | keyBuffer.clear(); 38 | hashKeySerde.toBuffer(keyBuffer, hashKey); 39 | valueBuffer.clear(); 40 | valueSerde.toBuffer(valueBuffer, value); 41 | db.put(txn, keyBuffer.nioBuffer(), valueBuffer.nioBuffer()); 42 | } 43 | 44 | @Override 45 | public void delete(final H hashKey) { 46 | keyBuffer.clear(); 47 | hashKeySerde.toBuffer(keyBuffer, hashKey); 48 | db.delete(txn, keyBuffer.nioBuffer()); 49 | } 50 | 51 | @Override 52 | public void flush() { 53 | try { 54 | txn.commit(); 55 | } catch (final NotReadyException e) { 56 | // ignore 57 | } 58 | } 59 | 60 | @Override 61 | public void close() throws IOException { 62 | flush(); 63 | txn.close(); 64 | this.keyBuffer.release(keyBuffer.refCnt()); 65 | this.valueBuffer.release(valueBuffer.refCnt()); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/src/main/java/ezdb/lmdb/table/EzLmDbComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.lmdb.table; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Comparator; 5 | 6 | import ezdb.util.Util; 7 | 8 | /** 9 | * LevelDb provides a comparator interface that we can use to handle hash/range 10 | * pairs. 11 | * 12 | * @author criccomini 13 | * 14 | */ 15 | public class EzLmDbComparator implements Comparator { 16 | 17 | private final Comparator hashKeyComparator; 18 | 19 | public EzLmDbComparator(final Comparator hashKeyComparator) { 20 | this.hashKeyComparator = hashKeyComparator; 21 | } 22 | 23 | @Override 24 | public int compare(final ByteBuffer a, final ByteBuffer b) { 25 | return Util.compareKeys(hashKeyComparator, a, b); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/src/main/java/ezdb/lmdb/table/range/EzLmDbRangeBatch.java: -------------------------------------------------------------------------------- 1 | package ezdb.lmdb.table.range; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | 6 | import org.lmdbjava.Dbi; 7 | import org.lmdbjava.Env; 8 | import org.lmdbjava.Txn; 9 | import org.lmdbjava.Txn.NotReadyException; 10 | 11 | import ezdb.serde.Serde; 12 | import ezdb.table.range.RangeBatch; 13 | import ezdb.util.Util; 14 | import io.netty.buffer.ByteBuf; 15 | import io.netty.buffer.ByteBufAllocator; 16 | 17 | public class EzLmDbRangeBatch implements RangeBatch { 18 | 19 | private final Dbi db; 20 | private final Txn txn; 21 | private final Serde hashKeySerde; 22 | private final Serde rangeKeySerde; 23 | private final Serde valueSerde; 24 | private final ByteBuf keyBuffer; 25 | private final ByteBuf valueBuffer; 26 | 27 | public EzLmDbRangeBatch(final Env env, final Dbi db, final Serde hashKeySerde, 28 | final Serde rangeKeySerde, final Serde valueSerde) { 29 | this.db = db; 30 | this.txn = env.txnWrite(); 31 | this.hashKeySerde = hashKeySerde; 32 | this.rangeKeySerde = rangeKeySerde; 33 | this.valueSerde = valueSerde; 34 | this.keyBuffer = ByteBufAllocator.DEFAULT.directBuffer(); 35 | this.valueBuffer = ByteBufAllocator.DEFAULT.directBuffer(); 36 | } 37 | 38 | @Override 39 | public void put(final H hashKey, final V value) { 40 | put(hashKey, null, value); 41 | } 42 | 43 | @Override 44 | public void delete(final H hashKey) { 45 | delete(hashKey, null); 46 | } 47 | 48 | @Override 49 | public void flush() { 50 | try { 51 | txn.commit(); 52 | } catch (final NotReadyException e) { 53 | // ignore 54 | } 55 | } 56 | 57 | @Override 58 | public void close() throws IOException { 59 | flush(); 60 | txn.close(); 61 | this.keyBuffer.release(keyBuffer.refCnt()); 62 | this.valueBuffer.release(valueBuffer.refCnt()); 63 | } 64 | 65 | @Override 66 | public void put(final H hashKey, final R rangeKey, final V value) { 67 | keyBuffer.clear(); 68 | Util.combineBuf(keyBuffer, hashKeySerde, rangeKeySerde, hashKey, rangeKey); 69 | valueBuffer.clear(); 70 | valueSerde.toBuffer(valueBuffer, value); 71 | db.put(txn, keyBuffer.nioBuffer(), valueBuffer.nioBuffer()); 72 | } 73 | 74 | @Override 75 | public void delete(final H hashKey, final R rangeKey) { 76 | keyBuffer.clear(); 77 | Util.combineBuf(keyBuffer, hashKeySerde, rangeKeySerde, hashKey, rangeKey); 78 | db.delete(txn, keyBuffer.nioBuffer()); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/src/main/java/ezdb/lmdb/table/range/EzLmDbRangeComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.lmdb.table.range; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Comparator; 5 | 6 | import ezdb.util.Util; 7 | 8 | /** 9 | * LevelDb provides a comparator interface that we can use to handle hash/range 10 | * pairs. 11 | * 12 | * @author criccomini 13 | * 14 | */ 15 | public class EzLmDbRangeComparator implements Comparator { 16 | 17 | private final Comparator hashKeyComparator; 18 | private final Comparator rangeKeyComparator; 19 | 20 | public EzLmDbRangeComparator(final Comparator hashKeyComparator, 21 | final Comparator rangeKeyComparator) { 22 | this.hashKeyComparator = hashKeyComparator; 23 | this.rangeKeyComparator = rangeKeyComparator; 24 | } 25 | 26 | @Override 27 | public int compare(final ByteBuffer a, final ByteBuffer b) { 28 | return Util.compareKeys(hashKeyComparator, rangeKeyComparator, a, b); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/src/main/java/ezdb/lmdb/util/EzDBIterator.java: -------------------------------------------------------------------------------- 1 | package ezdb.lmdb.util; 2 | 3 | import java.io.Closeable; 4 | import java.nio.ByteBuffer; 5 | import java.util.Iterator; 6 | 7 | import ezdb.table.RawTableRow; 8 | 9 | //implementation taken from leveldbjni 10 | /** 11 | * @author Hiram Chirino 12 | */ 13 | public interface EzDBIterator extends Iterator>, Closeable { 14 | 15 | /** 16 | * Repositions the iterator so the key of the next BlockElement returned greater 17 | * than or equal to the specified targetKey. 18 | */ 19 | public void seek(ByteBuffer key); 20 | 21 | /** 22 | * Repositions the iterator so is is at the beginning of the Database. 23 | */ 24 | public void seekToFirst(); 25 | 26 | /** 27 | * Returns the next element in the iteration, without advancing the iteration. 28 | */ 29 | public RawTableRow peekNext(); 30 | 31 | public ByteBuffer nextKey(); 32 | 33 | /** 34 | * @return true if there is a previous entry in the iteration. 35 | */ 36 | boolean hasPrev(); 37 | 38 | /** 39 | * @return the previous element in the iteration and rewinds the iteration. 40 | */ 41 | RawTableRow prev(); 42 | 43 | /** 44 | * @return the previous element in the iteration, without rewinding the 45 | * iteration. 46 | */ 47 | public RawTableRow peekPrev(); 48 | 49 | /** 50 | * Repositions the iterator so it is at the end of of the Database. 51 | */ 52 | public void seekToLast(); 53 | 54 | @Override 55 | public void close(); 56 | 57 | public ByteBuffer peekNextKey(); 58 | 59 | public ByteBuffer peekPrevKey(); 60 | 61 | } 62 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/src/main/java/ezdb/lmdb/util/EzDBRangeIterator.java: -------------------------------------------------------------------------------- 1 | package ezdb.lmdb.util; 2 | 3 | import java.io.Closeable; 4 | import java.nio.ByteBuffer; 5 | import java.util.Iterator; 6 | 7 | import ezdb.table.range.RawRangeTableRow; 8 | 9 | //implementation taken from leveldbjni 10 | /** 11 | * @author Hiram Chirino 12 | */ 13 | public interface EzDBRangeIterator extends Iterator>, Closeable { 14 | 15 | /** 16 | * Repositions the iterator so the key of the next BlockElement returned greater 17 | * than or equal to the specified targetKey. 18 | */ 19 | public void seek(ByteBuffer key); 20 | 21 | /** 22 | * Repositions the iterator so is is at the beginning of the Database. 23 | */ 24 | public void seekToFirst(); 25 | 26 | /** 27 | * Returns the next element in the iteration, without advancing the iteration. 28 | */ 29 | public RawRangeTableRow peekNext(); 30 | 31 | public ByteBuffer nextKey(); 32 | 33 | /** 34 | * @return true if there is a previous entry in the iteration. 35 | */ 36 | boolean hasPrev(); 37 | 38 | /** 39 | * @return the previous element in the iteration and rewinds the iteration. 40 | */ 41 | RawRangeTableRow prev(); 42 | 43 | /** 44 | * @return the previous element in the iteration, without rewinding the 45 | * iteration. 46 | */ 47 | public RawRangeTableRow peekPrev(); 48 | 49 | /** 50 | * Repositions the iterator so it is at the end of of the Database. 51 | */ 52 | public void seekToLast(); 53 | 54 | @Override 55 | public void close(); 56 | 57 | public ByteBuffer peekNextKey(); 58 | 59 | public ByteBuffer peekPrevKey(); 60 | 61 | } 62 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/src/main/java/ezdb/lmdb/util/LmDBJnrDBIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011, FuseSource Corp. All rights reserved. 3 | * 4 | * http://fusesource.com 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * * Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following disclaimer 14 | * in the documentation and/or other materials provided with the 15 | * distribution. 16 | * * Neither the name of FuseSource Corp. nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package ezdb.lmdb.util; 33 | 34 | import java.nio.ByteBuffer; 35 | import java.util.NoSuchElementException; 36 | 37 | import org.lmdbjava.Cursor; 38 | import org.lmdbjava.Dbi; 39 | import org.lmdbjava.Env; 40 | import org.lmdbjava.GetOp; 41 | import org.lmdbjava.Txn; 42 | 43 | import ezdb.serde.Serde; 44 | import ezdb.table.RawTableRow; 45 | 46 | //implementation taken from leveldbjni 47 | /** 48 | * @author Hiram Chirino 49 | */ 50 | public class LmDBJnrDBIterator implements EzDBIterator { 51 | 52 | private final Env env; 53 | private final Dbi dbi; 54 | private final Txn txn; 55 | private final Cursor cursor; 56 | private final Serde hashKeySerde; 57 | private final Serde valueSerde; 58 | private boolean valid = false; 59 | 60 | public LmDBJnrDBIterator(final Env env, final Dbi dbi, final Serde hashKeySerde, 61 | final Serde valueSerde) { 62 | this.env = env; 63 | this.dbi = dbi; 64 | this.txn = env.txnRead(); 65 | this.cursor = dbi.openCursor(txn); 66 | this.hashKeySerde = hashKeySerde; 67 | this.valueSerde = valueSerde; 68 | } 69 | 70 | @Override 71 | public void close() { 72 | cursor.close(); 73 | txn.close(); 74 | } 75 | 76 | @Override 77 | protected void finalize() throws Throwable { 78 | super.finalize(); 79 | close(); 80 | } 81 | 82 | @Override 83 | public void remove() { 84 | throw new UnsupportedOperationException(); 85 | } 86 | 87 | @Override 88 | public void seek(final ByteBuffer key) { 89 | valid = cursor.get(key, GetOp.MDB_SET_RANGE); 90 | } 91 | 92 | @Override 93 | public void seekToFirst() { 94 | valid = cursor.first(); 95 | } 96 | 97 | @Override 98 | public void seekToLast() { 99 | valid = cursor.last(); 100 | } 101 | 102 | @Override 103 | public RawTableRow peekNext() { 104 | if (!valid) { 105 | throw new NoSuchElementException(); 106 | } 107 | return RawTableRow.valueOfBuffer(cursor.key().duplicate(), cursor.val().duplicate(), hashKeySerde, valueSerde); 108 | } 109 | 110 | @Override 111 | public boolean hasNext() { 112 | return valid; 113 | } 114 | 115 | @Override 116 | public RawTableRow next() { 117 | final RawTableRow rc = peekNext(); 118 | valid = cursor.next(); 119 | return rc; 120 | } 121 | 122 | @Override 123 | public ByteBuffer nextKey() { 124 | final ByteBuffer rc = peekNextKey(); 125 | valid = cursor.next(); 126 | return rc; 127 | } 128 | 129 | @Override 130 | public boolean hasPrev() { 131 | if (!valid) { 132 | return false; 133 | } 134 | valid = cursor.prev(); 135 | try { 136 | return valid; 137 | } finally { 138 | if (valid) { 139 | valid = cursor.next(); 140 | } else { 141 | seekToFirst(); 142 | } 143 | } 144 | } 145 | 146 | @Override 147 | public RawTableRow peekPrev() { 148 | valid = cursor.prev(); 149 | try { 150 | return peekNext(); 151 | } finally { 152 | if (valid) { 153 | valid = cursor.next(); 154 | } else { 155 | seekToFirst(); 156 | } 157 | } 158 | } 159 | 160 | @Override 161 | public RawTableRow prev() { 162 | final RawTableRow rc = peekPrev(); 163 | valid = cursor.prev(); 164 | return rc; 165 | } 166 | 167 | @Override 168 | public ByteBuffer peekNextKey() { 169 | if (!valid) { 170 | throw new NoSuchElementException(); 171 | } 172 | return cursor.key().duplicate(); 173 | } 174 | 175 | @Override 176 | public ByteBuffer peekPrevKey() { 177 | valid = cursor.prev(); 178 | try { 179 | return peekNextKey(); 180 | } finally { 181 | if (valid) { 182 | valid = cursor.next(); 183 | } else { 184 | seekToFirst(); 185 | } 186 | } 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/src/main/java/ezdb/lmdb/util/LmDBJnrDBRangeIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011, FuseSource Corp. All rights reserved. 3 | * 4 | * http://fusesource.com 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * * Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following disclaimer 14 | * in the documentation and/or other materials provided with the 15 | * distribution. 16 | * * Neither the name of FuseSource Corp. nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package ezdb.lmdb.util; 33 | 34 | import java.nio.ByteBuffer; 35 | import java.util.NoSuchElementException; 36 | 37 | import org.lmdbjava.Cursor; 38 | import org.lmdbjava.Dbi; 39 | import org.lmdbjava.Env; 40 | import org.lmdbjava.GetOp; 41 | import org.lmdbjava.Txn; 42 | 43 | import ezdb.serde.Serde; 44 | import ezdb.table.range.RawRangeTableRow; 45 | 46 | //implementation taken from leveldbjni 47 | /** 48 | * @author Hiram Chirino 49 | */ 50 | public class LmDBJnrDBRangeIterator implements EzDBRangeIterator { 51 | 52 | private final Env env; 53 | private final Dbi dbi; 54 | private final Txn txn; 55 | private final Cursor cursor; 56 | private final Serde hashKeySerde; 57 | private final Serde rangeKeySerde; 58 | private final Serde valueSerde; 59 | private boolean valid = false; 60 | 61 | public LmDBJnrDBRangeIterator(final Env env, final Dbi dbi, final Serde hashKeySerde, 62 | final Serde rangeKeySerde, final Serde valueSerde) { 63 | this.env = env; 64 | this.dbi = dbi; 65 | this.txn = env.txnRead(); 66 | this.cursor = dbi.openCursor(txn); 67 | this.hashKeySerde = hashKeySerde; 68 | this.rangeKeySerde = rangeKeySerde; 69 | this.valueSerde = valueSerde; 70 | } 71 | 72 | @Override 73 | public void close() { 74 | cursor.close(); 75 | txn.close(); 76 | } 77 | 78 | @Override 79 | protected void finalize() throws Throwable { 80 | super.finalize(); 81 | close(); 82 | } 83 | 84 | @Override 85 | public void remove() { 86 | throw new UnsupportedOperationException(); 87 | } 88 | 89 | @Override 90 | public void seek(final ByteBuffer key) { 91 | valid = cursor.get(key, GetOp.MDB_SET_RANGE); 92 | } 93 | 94 | @Override 95 | public void seekToFirst() { 96 | valid = cursor.first(); 97 | } 98 | 99 | @Override 100 | public void seekToLast() { 101 | valid = cursor.last(); 102 | } 103 | 104 | @Override 105 | public RawRangeTableRow peekNext() { 106 | if (!valid) { 107 | throw new NoSuchElementException(); 108 | } 109 | return RawRangeTableRow.valueOfBuffer(cursor.key().duplicate(), cursor.val().duplicate(), hashKeySerde, 110 | rangeKeySerde, valueSerde); 111 | } 112 | 113 | @Override 114 | public boolean hasNext() { 115 | return valid; 116 | } 117 | 118 | @Override 119 | public RawRangeTableRow next() { 120 | final RawRangeTableRow rc = peekNext(); 121 | valid = cursor.next(); 122 | return rc; 123 | } 124 | 125 | @Override 126 | public ByteBuffer nextKey() { 127 | final ByteBuffer rc = peekNextKey(); 128 | valid = cursor.next(); 129 | return rc; 130 | } 131 | 132 | @Override 133 | public boolean hasPrev() { 134 | if (!valid) { 135 | return false; 136 | } 137 | valid = cursor.prev(); 138 | try { 139 | return valid; 140 | } finally { 141 | if (valid) { 142 | valid = cursor.next(); 143 | } else { 144 | seekToFirst(); 145 | } 146 | } 147 | } 148 | 149 | @Override 150 | public RawRangeTableRow peekPrev() { 151 | valid = cursor.prev(); 152 | try { 153 | return peekNext(); 154 | } finally { 155 | if (valid) { 156 | valid = cursor.next(); 157 | } else { 158 | seekToFirst(); 159 | } 160 | } 161 | } 162 | 163 | @Override 164 | public RawRangeTableRow prev() { 165 | final RawRangeTableRow rc = peekPrev(); 166 | valid = cursor.prev(); 167 | return rc; 168 | } 169 | 170 | @Override 171 | public ByteBuffer peekNextKey() { 172 | if (!valid) { 173 | throw new NoSuchElementException(); 174 | } 175 | return cursor.key().duplicate(); 176 | } 177 | 178 | @Override 179 | public ByteBuffer peekPrevKey() { 180 | valid = cursor.prev(); 181 | try { 182 | return peekNextKey(); 183 | } finally { 184 | if (valid) { 185 | valid = cursor.next(); 186 | } else { 187 | seekToFirst(); 188 | } 189 | } 190 | } 191 | 192 | } 193 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/src/test/java/ezdb/lmdb/ADatabasePerformanceTest.java: -------------------------------------------------------------------------------- 1 | package ezdb.lmdb; 2 | 3 | import java.util.Objects; 4 | 5 | public abstract class ADatabasePerformanceTest { 6 | 7 | static { 8 | TestInitializer.init(); 9 | } 10 | 11 | protected static final int READS = 10; 12 | protected static final int VALUES = 1_000_000; 13 | protected static final String HASH_KEY = "HASH_KEY"; 14 | protected static final int FLUSH_INTERVAL = 10_000; 15 | 16 | protected void printProgress(final String action, final long start, final double count, final double maxCount) { 17 | final long duration = System.currentTimeMillis() - start; 18 | System.out.println(action + ": " + count + "/" + maxCount + " (" + (count / maxCount * 100) + ") " 19 | + (count / duration) + "/ms during " + duration); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/src/test/java/ezdb/lmdb/LmdbPerformanceTest.java: -------------------------------------------------------------------------------- 1 | package ezdb.lmdb; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.NoSuchElementException; 7 | 8 | import javax.annotation.concurrent.NotThreadSafe; 9 | 10 | import org.junit.Ignore; 11 | import org.junit.Test; 12 | 13 | import ezdb.comparator.LexicographicalComparator; 14 | import ezdb.lmdb.table.range.EzLmDbRangeTable; 15 | import ezdb.lmdb.util.FileUtils; 16 | import ezdb.serde.LongSerde; 17 | import ezdb.serde.StringSerde; 18 | import ezdb.table.RangeTableRow; 19 | import ezdb.util.TableIterator; 20 | 21 | @NotThreadSafe 22 | @Ignore("manual test") 23 | public class LmdbPerformanceTest extends ADatabasePerformanceTest { 24 | 25 | protected static final File ROOT = FileUtils.createTempDir(TestEzLmDb.class.getSimpleName()); 26 | 27 | @Test 28 | public void testLevelDbPerformance() { 29 | final EzLmDbRangeTable table = new EzLmDbRangeTable(ROOT, new EzLmDbJnrFactory(), StringSerde.get, 30 | LongSerde.get, LongSerde.get, new LexicographicalComparator(), new LexicographicalComparator()); 31 | 32 | // RangeBatch batch = table.newRangeBatch(); 33 | final long writesStart = System.currentTimeMillis(); 34 | for (long i = 0; i < VALUES; i++) { 35 | table.put(HASH_KEY, i, i); 36 | if (i % FLUSH_INTERVAL == 0) { 37 | printProgress("Writes", writesStart, i, VALUES); 38 | // try { 39 | // batch.flush(); 40 | // batch.close(); 41 | // } catch (final IOException e) { 42 | // throw new RuntimeException(e); 43 | // } 44 | // batch = table.newRangeBatch(); 45 | } 46 | } 47 | // try { 48 | // batch.flush(); 49 | // batch.close(); 50 | // } catch (final IOException e) { 51 | // throw new RuntimeException(e); 52 | // } 53 | printProgress("WritesFinished", writesStart, VALUES, VALUES); 54 | 55 | readIterator(table); 56 | readGet(table); 57 | readGetLatest(table); 58 | } 59 | 60 | private void readIterator(final EzLmDbRangeTable table) { 61 | final long readsStart = System.currentTimeMillis(); 62 | for (int reads = 1; reads <= READS; reads++) { 63 | Long prevValue = null; 64 | final TableIterator> range = table.range(HASH_KEY); 65 | int count = 0; 66 | while (true) { 67 | try { 68 | final RangeTableRow value = range.next(); 69 | // if (prevValue != null) { 70 | // if (prevValue >= value.getValue()) { 71 | // throw new IllegalStateException(prevValue + " >= " + value.getValue()); 72 | // } 73 | // } 74 | prevValue = value.getValue(); 75 | count++; 76 | } catch (final NoSuchElementException e) { 77 | break; 78 | } 79 | } 80 | printProgress("Reads", readsStart, VALUES * reads, VALUES * READS); 81 | } 82 | printProgress("ReadsFinished", readsStart, VALUES * READS, VALUES * READS); 83 | } 84 | 85 | private void readGet(final EzLmDbRangeTable table) { 86 | final List values = newValues(); 87 | final long readsStart = System.currentTimeMillis(); 88 | for (int reads = 1; reads <= READS; reads++) { 89 | Long prevValue = null; 90 | for (int i = 0; i < values.size(); i++) { 91 | try { 92 | final Long value = table.get(HASH_KEY, values.get(i)); 93 | // if (prevValue != null) { 94 | // if (prevValue >= value) { 95 | // throw new IllegalStateException(prevValue + " >= " + value.getValue()); 96 | // } 97 | // } 98 | prevValue = value; 99 | } catch (final NoSuchElementException e) { 100 | break; 101 | } 102 | } 103 | printProgress("Gets", readsStart, VALUES * reads, VALUES * READS); 104 | } 105 | printProgress("GetsFinished", readsStart, VALUES * READS, VALUES * READS); 106 | } 107 | 108 | private List newValues() { 109 | final List values = new ArrayList(); 110 | for (long i = 0; i < VALUES; i++) { 111 | values.add(i); 112 | } 113 | return values; 114 | } 115 | 116 | private void readGetLatest(final EzLmDbRangeTable table) { 117 | final List values = newValues(); 118 | final long readsStart = System.currentTimeMillis(); 119 | for (int reads = 1; reads <= READS; reads++) { 120 | Long prevValue = null; 121 | for (int i = 0; i < values.size(); i++) { 122 | try { 123 | final RangeTableRow value = table.getLatest(HASH_KEY, values.get(i)); 124 | // if (prevValue != null) { 125 | // if (prevValue >= value.getValue()) { 126 | // throw new IllegalStateException(prevValue + " >= " + value.getValue()); 127 | // } 128 | // } 129 | prevValue = value.getValue(); 130 | } catch (final NoSuchElementException e) { 131 | break; 132 | } 133 | } 134 | printProgress("GetLatests", readsStart, VALUES * reads, VALUES * READS); 135 | } 136 | printProgress("GetLatestsFinished", readsStart, VALUES * READS, VALUES * READS); 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /ezdb-lmdb-jnr/src/test/java/ezdb/lmdb/TestInitializer.java: -------------------------------------------------------------------------------- 1 | package ezdb.lmdb; 2 | 3 | import org.burningwave.core.assembler.StaticComponentContainer; 4 | 5 | public class TestInitializer { 6 | 7 | private static boolean initialized = false; 8 | 9 | public static synchronized void init() { 10 | if (!initialized) { 11 | StaticComponentContainer.Modules.exportAllToAll(); 12 | initialized = true; 13 | } 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /ezdb-lsmtree/.gitignore: -------------------------------------------------------------------------------- 1 | /.settings/ 2 | -------------------------------------------------------------------------------- /ezdb-lsmtree/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.github.criccomini 8 | ezdb-parent 9 | 0.1.18-SNAPSHOT 10 | 11 | ezdb-lsmtree 12 | EZDB LsmTree 13 | jar 14 | 15 | 16 | com.github.criccomini 17 | ezdb-api 18 | 19 | 20 | com.github.criccomini 21 | ezdb-treemap 22 | test 23 | 24 | 25 | com.indeed 26 | lsmtree-core 27 | 28 | 29 | com.google.guava 30 | guava 31 | test 32 | 33 | 34 | com.google.guava 35 | guava 36 | test 37 | 38 | 39 | junit 40 | junit 41 | test 42 | 43 | 44 | org.burningwave 45 | core 46 | test 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /ezdb-lsmtree/src/main/java/ezdb/lsmtree/EzLsmTreeDb.java: -------------------------------------------------------------------------------- 1 | package ezdb.lsmtree; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.ByteBuffer; 6 | import java.util.Comparator; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import ezdb.Db; 11 | import ezdb.comparator.ComparableComparator; 12 | import ezdb.comparator.LexicographicalComparator; 13 | import ezdb.lsmtree.table.LsmTreeTable; 14 | import ezdb.lsmtree.table.range.LsmTreeRangeTable; 15 | import ezdb.serde.Serde; 16 | import ezdb.table.Table; 17 | import ezdb.table.range.RangeTable; 18 | 19 | public class EzLsmTreeDb implements Db { 20 | private final Map> cache; 21 | private final File root; 22 | private final EzLsmTreeDbFactory factory; 23 | 24 | public EzLsmTreeDb(final File root, final EzLsmTreeDbFactory factory) { 25 | this.cache = new HashMap>(); 26 | this.root = root; 27 | this.factory = factory; 28 | } 29 | 30 | @Override 31 | public void deleteTable(final String tableName) { 32 | synchronized (cache) { 33 | final Table removed = cache.remove(tableName); 34 | if (removed != null) { 35 | removed.close(); 36 | try { 37 | factory.destroy(getFile(tableName)); 38 | } catch (final IOException e) { 39 | throw new RuntimeException(e); 40 | } 41 | } 42 | } 43 | } 44 | 45 | @SuppressWarnings("unchecked") 46 | @Override 47 | public Table getTable(final String tableName, final Serde hashKeySerde, final Serde valueSerde) { 48 | return getTable(tableName, hashKeySerde, valueSerde, new ComparableComparator()); 49 | } 50 | 51 | @SuppressWarnings({ "unchecked", "rawtypes" }) 52 | @Override 53 | public Table getTable(final String tableName, final Serde hashKeySerde, final Serde valueSerde, 54 | final Comparator hashKeyComparator) { 55 | synchronized (cache) { 56 | Table table = cache.get(tableName); 57 | 58 | if (table == null) { 59 | table = newTable(tableName, hashKeySerde, valueSerde, (Comparator) hashKeyComparator); 60 | cache.put(tableName, table); 61 | } else if (!(table instanceof LsmTreeTable)) { 62 | throw new IllegalStateException("Expected " + LsmTreeTable.class.getSimpleName() + " but got " 63 | + table.getClass().getSimpleName() + " for: " + tableName); 64 | } 65 | 66 | return (Table) table; 67 | } 68 | } 69 | 70 | @Override 71 | public RangeTable getRangeTable(final String tableName, final Serde hashKeySerde, 72 | final Serde rangeKeySerde, final Serde valueSerde) { 73 | return getRangeTable(tableName, hashKeySerde, rangeKeySerde, valueSerde, new ComparableComparator(), 74 | new ComparableComparator()); 75 | } 76 | 77 | @SuppressWarnings({ "unchecked", "rawtypes" }) 78 | @Override 79 | public RangeTable getRangeTable(final String tableName, final Serde hashKeySerde, 80 | final Serde rangeKeySerde, final Serde valueSerde, final Comparator hashKeyComparator, 81 | final Comparator rangeKeyComparator) { 82 | synchronized (cache) { 83 | RangeTable table = (RangeTable) cache.get(tableName); 84 | 85 | if (table == null) { 86 | table = newRangeTable(tableName, hashKeySerde, rangeKeySerde, valueSerde, 87 | (Comparator) hashKeyComparator, (Comparator) rangeKeyComparator); 88 | cache.put(tableName, table); 89 | } else if (!(table instanceof LsmTreeRangeTable)) { 90 | throw new IllegalStateException("Expected " + LsmTreeRangeTable.class.getSimpleName() + " but got " 91 | + table.getClass().getSimpleName() + " for: " + tableName); 92 | } 93 | 94 | return (RangeTable) table; 95 | } 96 | } 97 | 98 | private LsmTreeTable newTable(final String tableName, final Serde hashKeySerde, 99 | final Serde valueSerde, final Comparator hashKeyComparator) { 100 | return new LsmTreeTable(new File(root, tableName), factory, hashKeySerde, valueSerde, hashKeyComparator); 101 | } 102 | 103 | private LsmTreeRangeTable newRangeTable(final String tableName, final Serde hashKeySerde, 104 | final Serde rangeKeySerde, final Serde valueSerde, final Comparator hashKeyComparator, 105 | final Comparator rangeKeyComparator) { 106 | return new LsmTreeRangeTable(new File(root, tableName), factory, hashKeySerde, rangeKeySerde, 107 | valueSerde, hashKeyComparator, rangeKeyComparator); 108 | } 109 | 110 | /** 111 | * A helper method used to convert a table name to the location on disk where 112 | * this LevelDB database will be persisted. 113 | * 114 | * @param tableName The logical name of the table. 115 | * @return The physical location of the directory where this table should be 116 | * persisted. 117 | */ 118 | private File getFile(final String tableName) { 119 | return new File(root, tableName); 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /ezdb-lsmtree/src/main/java/ezdb/lsmtree/EzLsmTreeDbFactory.java: -------------------------------------------------------------------------------- 1 | package ezdb.lsmtree; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.Comparator; 6 | 7 | import com.indeed.lsmtree.core.Store; 8 | import com.indeed.util.serialization.Serializer; 9 | 10 | /** 11 | * An interface that allows us to inject either a JNI or pure-Java 12 | * implementation of LevelDB. EzLevelDb uses this class to open and delete 13 | * LevelDb instances. 14 | * 15 | * @author criccomi 16 | * 17 | */ 18 | public interface EzLsmTreeDbFactory { 19 | 20 | public Store open(File path, final Serializer keySerializer, final Serializer valueSerializer, 21 | Comparator comparator) throws IOException; 22 | 23 | public void destroy(File path) throws IOException; 24 | } 25 | -------------------------------------------------------------------------------- /ezdb-lsmtree/src/main/java/ezdb/lsmtree/EzLsmTreeDbJavaFactory.java: -------------------------------------------------------------------------------- 1 | package ezdb.lsmtree; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.Comparator; 6 | 7 | import org.apache.commons.io.FileUtils; 8 | 9 | import com.indeed.lsmtree.core.StorageType; 10 | import com.indeed.lsmtree.core.Store; 11 | import com.indeed.lsmtree.core.StoreBuilder; 12 | import com.indeed.util.compress.CompressionCodec; 13 | import com.indeed.util.compress.SnappyCodec; 14 | import com.indeed.util.serialization.Serializer; 15 | 16 | public class EzLsmTreeDbJavaFactory implements EzLsmTreeDbFactory { 17 | 18 | protected StorageType newStorageType() { 19 | return StorageType.BLOCK_COMPRESSED; 20 | } 21 | 22 | protected CompressionCodec newCodec() { 23 | return new SnappyCodec(); 24 | } 25 | 26 | protected StoreBuilder configure(final StoreBuilder config) { 27 | return config; 28 | } 29 | 30 | @Override 31 | public void destroy(final File path) throws IOException { 32 | FileUtils.deleteQuietly(path); 33 | } 34 | 35 | @Override 36 | public Store open(final File path, final Serializer keySerializer, 37 | final Serializer valueSerializer, final Comparator comparator) throws IOException { 38 | final StoreBuilder config = new StoreBuilder<>(path, keySerializer, valueSerializer).setCodec(newCodec()) 39 | .setStorageType(newStorageType()); 40 | return configure(config).build(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /ezdb-lsmtree/src/main/java/ezdb/lsmtree/EzdbSerializer.java: -------------------------------------------------------------------------------- 1 | package ezdb.lsmtree; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | 7 | import javax.annotation.concurrent.Immutable; 8 | 9 | import com.indeed.util.serialization.Serializer; 10 | 11 | import ezdb.serde.Serde; 12 | import io.netty.buffer.ByteBuf; 13 | import io.netty.buffer.PooledByteBufAllocator; 14 | 15 | @Immutable 16 | public final class EzdbSerializer implements Serializer { 17 | 18 | private final Serde serde; 19 | 20 | public EzdbSerializer(final Serde serde) { 21 | this.serde = serde; 22 | } 23 | 24 | @Override 25 | public void write(final E t, final DataOutput out) throws IOException { 26 | final ByteBuf buffer = PooledByteBufAllocator.DEFAULT.heapBuffer(); 27 | try { 28 | writeWithBuffer(buffer, t, out); 29 | } finally { 30 | buffer.release(buffer.refCnt()); 31 | } 32 | } 33 | 34 | public void writeWithBuffer(final ByteBuf buffer, final E t, final DataOutput out) throws IOException { 35 | if (t == null) { 36 | out.writeInt(0); 37 | } else { 38 | serde.toBuffer(buffer, t); 39 | final int length = buffer.readableBytes(); 40 | out.writeInt(length); 41 | getBytesTo(buffer, out, length); 42 | } 43 | } 44 | 45 | @Override 46 | public E read(final DataInput in) throws IOException { 47 | final ByteBuf buffer = PooledByteBufAllocator.DEFAULT.heapBuffer(); 48 | try { 49 | return readWithBuffer(buffer, in); 50 | } finally { 51 | buffer.release(buffer.refCnt()); 52 | } 53 | } 54 | 55 | public E readWithBuffer(final ByteBuf buffer, final DataInput in) throws IOException { 56 | final int length = in.readInt(); 57 | if (length == 0) { 58 | return null; 59 | } else { 60 | ensureCapacity(buffer, length); 61 | putBytesTo(buffer, in, length); 62 | return serde.fromBuffer(buffer); 63 | } 64 | } 65 | 66 | public static void getBytesTo(final ByteBuf buffer, final DataOutput dst, final int length) throws IOException { 67 | int i = 0; 68 | while (i < length) { 69 | final byte b = buffer.getByte(i); 70 | dst.write(b); 71 | i++; 72 | } 73 | } 74 | 75 | public static void putBytesTo(final ByteBuf buffer, final DataInput src, final int length) throws IOException { 76 | ensureCapacity(buffer, length); 77 | int i = 0; 78 | while (i < length) { 79 | final byte b = src.readByte(); 80 | buffer.writeByte(b); 81 | i++; 82 | } 83 | } 84 | 85 | private static void ensureCapacity(final ByteBuf buffer, final int desiredCapacity) { 86 | if (buffer.capacity() < desiredCapacity) { 87 | buffer.capacity(desiredCapacity); 88 | } 89 | } 90 | 91 | @SuppressWarnings("unchecked") 92 | public static Serializer valueOf(final Serde delegate) { 93 | if (delegate == null) { 94 | return null; 95 | } 96 | final Serializer unwrapped = delegate.unwrap(Serializer.class); 97 | if (unwrapped != null) { 98 | return unwrapped; 99 | } else if (delegate instanceof Serializer) { 100 | return (Serializer) delegate; 101 | } else { 102 | return new EzdbSerializer(delegate); 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /ezdb-lsmtree/src/main/java/ezdb/lsmtree/table/EzLsmTreeDbComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.lsmtree.table; 2 | 3 | import java.util.Comparator; 4 | 5 | import ezdb.util.Util; 6 | 7 | public class EzLsmTreeDbComparator implements Comparator { 8 | 9 | private final Comparator hashKeyComparator; 10 | 11 | public EzLsmTreeDbComparator(final Comparator hashKeyComparator) { 12 | this.hashKeyComparator = hashKeyComparator; 13 | } 14 | 15 | @Override 16 | public int compare(final H a, final H b) { 17 | return Util.compareKeys(hashKeyComparator, a, b); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /ezdb-lsmtree/src/main/java/ezdb/lsmtree/table/LsmTreeTable.java: -------------------------------------------------------------------------------- 1 | package ezdb.lsmtree.table; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.Comparator; 6 | import java.util.Iterator; 7 | import java.util.NoSuchElementException; 8 | 9 | import com.indeed.lsmtree.core.Store; 10 | 11 | import ezdb.lsmtree.EzLsmTreeDbFactory; 12 | import ezdb.lsmtree.EzdbSerializer; 13 | import ezdb.serde.Serde; 14 | import ezdb.table.Batch; 15 | import ezdb.table.Table; 16 | import ezdb.table.TableRow; 17 | import ezdb.util.ObjectTableRow; 18 | import ezdb.util.TableIterator; 19 | 20 | public class LsmTreeTable implements Table { 21 | private final Store store; 22 | private final Comparator hashKeyComparator; 23 | private final EzLsmTreeDbComparator keyComparator; 24 | 25 | public LsmTreeTable(final File path, final EzLsmTreeDbFactory factory, final Serde hashKeySerde, 26 | final Serde valueSerde, final Comparator hashKeyComparator) { 27 | this.hashKeyComparator = hashKeyComparator; 28 | this.keyComparator = new EzLsmTreeDbComparator(hashKeyComparator); 29 | try { 30 | this.store = factory.open(path, EzdbSerializer.valueOf(hashKeySerde), EzdbSerializer.valueOf(valueSerde), 31 | keyComparator); 32 | } catch (final IOException e) { 33 | throw new RuntimeException(e); 34 | } 35 | } 36 | 37 | @Override 38 | public void put(final H hashKey, final V value) { 39 | try { 40 | store.put(hashKey, value); 41 | } catch (final IOException e) { 42 | throw new RuntimeException(e); 43 | } 44 | } 45 | 46 | @Override 47 | public V get(final H hashKey) { 48 | try { 49 | final V value = store.get(hashKey); 50 | return value; 51 | } catch (final IOException e) { 52 | throw new RuntimeException(e); 53 | } 54 | } 55 | 56 | @Override 57 | public TableIterator> range() { 58 | final Iterator> iterator; 59 | try { 60 | iterator = store.iterator(); 61 | } catch (final IOException e) { 62 | throw new RuntimeException(e); 63 | } 64 | return new TableIterator>() { 65 | private Store.Entry next = (iterator.hasNext()) ? iterator.next() : null; 66 | 67 | @Override 68 | public boolean hasNext() { 69 | return next != null; 70 | } 71 | 72 | @Override 73 | public TableRow next() { 74 | TableRow row = null; 75 | 76 | if (hasNext()) { 77 | row = new ObjectTableRow(next.getKey(), next.getValue()); 78 | } 79 | 80 | if (iterator.hasNext()) { 81 | next = iterator.next(); 82 | } else { 83 | next = null; 84 | } 85 | 86 | if (row != null) { 87 | return row; 88 | } else { 89 | throw new NoSuchElementException(); 90 | } 91 | } 92 | 93 | @Override 94 | public void remove() { 95 | if (next == null) { 96 | throw new IllegalStateException("next is null"); 97 | } 98 | try { 99 | store.delete(next.getKey()); 100 | } catch (final IOException e) { 101 | throw new RuntimeException(e); 102 | } 103 | next(); 104 | } 105 | 106 | @Override 107 | public void close() { 108 | next = null; 109 | } 110 | }; 111 | } 112 | 113 | @Override 114 | public void delete(final H hashKey) { 115 | try { 116 | store.delete(hashKey); 117 | } catch (final IOException e) { 118 | throw new RuntimeException(e); 119 | } 120 | } 121 | 122 | @Override 123 | public void close() { 124 | try { 125 | store.close(); 126 | } catch (final IOException e) { 127 | throw new RuntimeException(e); 128 | } 129 | } 130 | 131 | @Override 132 | public Batch newBatch() { 133 | return new Batch() { 134 | 135 | @Override 136 | public void close() throws IOException { 137 | } 138 | 139 | @Override 140 | public void put(final H hashKey, final V value) { 141 | LsmTreeTable.this.put(hashKey, value); 142 | } 143 | 144 | @Override 145 | public void delete(final H hashKey) { 146 | LsmTreeTable.this.delete(hashKey); 147 | } 148 | 149 | @Override 150 | public void flush() { 151 | } 152 | }; 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /ezdb-lsmtree/src/main/java/ezdb/lsmtree/table/range/EzLsmTreeDbRangeComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.lsmtree.table.range; 2 | 3 | import java.util.Comparator; 4 | 5 | import ezdb.util.ObjectRangeTableKey; 6 | import ezdb.util.Util; 7 | 8 | public class EzLsmTreeDbRangeComparator implements Comparator> { 9 | 10 | private final Comparator hashKeyComparator; 11 | private final Comparator rangeKeyComparator; 12 | 13 | public EzLsmTreeDbRangeComparator(final Comparator hashKeyComparator, final Comparator rangeKeyComparator) { 14 | this.hashKeyComparator = hashKeyComparator; 15 | this.rangeKeyComparator = rangeKeyComparator; 16 | } 17 | 18 | @Override 19 | public int compare(final ObjectRangeTableKey a, final ObjectRangeTableKey b) { 20 | return Util.compareKeys(hashKeyComparator, rangeKeyComparator, a, b); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /ezdb-lsmtree/src/main/java/ezdb/lsmtree/table/range/ObjectTableKeySerializer.java: -------------------------------------------------------------------------------- 1 | package ezdb.lsmtree.table.range; 2 | 3 | import java.io.DataInput; 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | import java.util.Comparator; 7 | 8 | import com.indeed.util.serialization.Serializer; 9 | 10 | import ezdb.lsmtree.EzdbSerializer; 11 | import ezdb.serde.Serde; 12 | import ezdb.util.ObjectRangeTableKey; 13 | import ezdb.util.Util; 14 | import io.netty.buffer.ByteBuf; 15 | import io.netty.buffer.PooledByteBufAllocator; 16 | 17 | public class ObjectTableKeySerializer implements Serializer> { 18 | 19 | private final EzdbSerializer hashKeySerializer; 20 | private final EzdbSerializer rangeKeySerializer; 21 | private final Comparator> comparator; 22 | 23 | public ObjectTableKeySerializer(final Serde hashKeySerde, final Serde rangeKeySerde, 24 | final Comparator> comparator) { 25 | if (hashKeySerde == null) { 26 | throw new NullPointerException("hashKeySerde should not be null"); 27 | } 28 | if (rangeKeySerde == null) { 29 | throw new NullPointerException("rangeKeySerde should not be null"); 30 | } 31 | if (comparator == null) { 32 | throw new NullPointerException("comparator should not be null"); 33 | } 34 | this.hashKeySerializer = new EzdbSerializer(hashKeySerde); 35 | this.rangeKeySerializer = new EzdbSerializer(rangeKeySerde); 36 | this.comparator = comparator; 37 | } 38 | 39 | @Override 40 | public void write(final ObjectRangeTableKey t, final DataOutput out) throws IOException { 41 | final ByteBuf buffer = PooledByteBufAllocator.DEFAULT.heapBuffer(); 42 | try { 43 | hashKeySerializer.writeWithBuffer(buffer, t.getHashKey(), out); 44 | buffer.clear(); 45 | rangeKeySerializer.writeWithBuffer(buffer, t.getRangeKey(), out); 46 | } finally { 47 | buffer.release(buffer.refCnt()); 48 | } 49 | } 50 | 51 | @Override 52 | public ObjectRangeTableKey read(final DataInput in) throws IOException { 53 | final ByteBuf buffer = PooledByteBufAllocator.DEFAULT.heapBuffer(); 54 | try { 55 | final H hashKey = hashKeySerializer.readWithBuffer(buffer, in); 56 | buffer.clear(); 57 | final R rangeKey = rangeKeySerializer.readWithBuffer(buffer, in); 58 | return Util.combine(hashKey, rangeKey, comparator); 59 | } finally { 60 | buffer.release(buffer.refCnt()); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /ezdb-lsmtree/src/test/java/ezdb/lsmtree/FileUtils.java: -------------------------------------------------------------------------------- 1 | package ezdb.lsmtree; 2 | 3 | import java.io.File; 4 | import java.io.FilenameFilter; 5 | import java.io.IOException; 6 | 7 | import com.google.common.base.Preconditions; 8 | import com.google.common.collect.ImmutableList; 9 | import com.google.common.io.Files; 10 | 11 | //implementation taken from leveldbjni 12 | public class FileUtils { 13 | private static final int TEMP_DIR_ATTEMPTS = 10000; 14 | 15 | public static boolean isSymbolicLink(final File file) { 16 | try { 17 | final File canonicalFile = file.getCanonicalFile(); 18 | final File absoluteFile = file.getAbsoluteFile(); 19 | final File parentFile = file.getParentFile(); 20 | // a symbolic link has a different name between the canonical and absolute path 21 | return !canonicalFile.getName().equals(absoluteFile.getName()) || 22 | // or the canonical parent path is not the same as the file's parent path, 23 | // provided the file has a parent path 24 | parentFile != null && !parentFile.getCanonicalPath().equals(canonicalFile.getParent()); 25 | } catch (final IOException e) { 26 | // error on the side of caution 27 | return true; 28 | } 29 | } 30 | 31 | public static ImmutableList listFiles(final File dir) { 32 | final File[] files = dir.listFiles(); 33 | if (files == null) { 34 | return ImmutableList.of(); 35 | } 36 | return ImmutableList.copyOf(files); 37 | } 38 | 39 | public static ImmutableList listFiles(final File dir, final FilenameFilter filter) { 40 | final File[] files = dir.listFiles(filter); 41 | if (files == null) { 42 | return ImmutableList.of(); 43 | } 44 | return ImmutableList.copyOf(files); 45 | } 46 | 47 | public static File createTempDir(final String prefix) { 48 | return createTempDir(new File(System.getProperty("java.io.tmpdir")), prefix); 49 | } 50 | 51 | public static File createTempDir(final File parentDir, final String prefix) { 52 | String baseName = ""; 53 | if (prefix != null) { 54 | baseName += prefix + "-"; 55 | } 56 | 57 | baseName += System.currentTimeMillis() + "-"; 58 | for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) { 59 | final File tempDir = new File(parentDir, baseName + counter); 60 | if (tempDir.mkdir()) { 61 | return tempDir; 62 | } 63 | } 64 | throw new IllegalStateException("Failed to create directory within " + TEMP_DIR_ATTEMPTS + " attempts (tried " 65 | + baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')'); 66 | } 67 | 68 | public static boolean deleteDirectoryContents(final File directory) { 69 | Preconditions.checkArgument(directory.isDirectory(), "Not a directory: %s", directory); 70 | 71 | // Don't delete symbolic link directories 72 | if (isSymbolicLink(directory)) { 73 | return false; 74 | } 75 | 76 | boolean success = true; 77 | for (final File file : listFiles(directory)) { 78 | success = deleteRecursively(file) && success; 79 | } 80 | return success; 81 | } 82 | 83 | public static boolean deleteRecursively(final File file) { 84 | boolean success = true; 85 | if (file.isDirectory()) { 86 | success = deleteDirectoryContents(file); 87 | } 88 | 89 | return file.delete() && success; 90 | } 91 | 92 | public static boolean copyDirectoryContents(final File src, final File target) { 93 | Preconditions.checkArgument(src.isDirectory(), "Source dir is not a directory: %s", src); 94 | 95 | // Don't delete symbolic link directories 96 | if (isSymbolicLink(src)) { 97 | return false; 98 | } 99 | 100 | target.mkdirs(); 101 | Preconditions.checkArgument(target.isDirectory(), "Target dir is not a directory: %s", src); 102 | 103 | boolean success = true; 104 | for (final File file : listFiles(src)) { 105 | success = copyRecursively(file, new File(target, file.getName())) && success; 106 | } 107 | return success; 108 | } 109 | 110 | public static boolean copyRecursively(final File src, final File target) { 111 | if (src.isDirectory()) { 112 | return copyDirectoryContents(src, target); 113 | } else { 114 | try { 115 | Files.copy(src, target); 116 | return true; 117 | } catch (final IOException e) { 118 | return false; 119 | } 120 | } 121 | } 122 | 123 | public static File newFile(final String parent, final String... paths) { 124 | Preconditions.checkNotNull(parent, "parent is null"); 125 | Preconditions.checkNotNull(paths, "paths is null"); 126 | 127 | return newFile(new File(parent), ImmutableList.copyOf(paths)); 128 | } 129 | 130 | public static File newFile(final File parent, final String... paths) { 131 | Preconditions.checkNotNull(parent, "parent is null"); 132 | Preconditions.checkNotNull(paths, "paths is null"); 133 | 134 | return newFile(parent, ImmutableList.copyOf(paths)); 135 | } 136 | 137 | public static File newFile(final File parent, final Iterable paths) { 138 | Preconditions.checkNotNull(parent, "parent is null"); 139 | Preconditions.checkNotNull(paths, "paths is null"); 140 | 141 | File result = parent; 142 | for (final String path : paths) { 143 | result = new File(result, path); 144 | } 145 | return result; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /ezdb-lsmtree/src/test/java/ezdb/lsmtree/TestInitializer.java: -------------------------------------------------------------------------------- 1 | package ezdb.lsmtree; 2 | 3 | import org.burningwave.core.assembler.StaticComponentContainer; 4 | 5 | public class TestInitializer { 6 | 7 | private static boolean initialized = false; 8 | 9 | public static synchronized void init() { 10 | if (!initialized) { 11 | StaticComponentContainer.Modules.exportAllToAll(); 12 | initialized = true; 13 | } 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /ezdb-rocksdb-jni/.gitignore: -------------------------------------------------------------------------------- 1 | /.settings/ 2 | -------------------------------------------------------------------------------- /ezdb-rocksdb-jni/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | com.github.criccomini 7 | ezdb-parent 8 | 0.1.18-SNAPSHOT 9 | 10 | 11 | ezdb-rocksdb-jni 12 | EZDB RocksDB JNI implementation 13 | jar 14 | 15 | 16 | 17 | com.github.criccomini 18 | ezdb-api 19 | 20 | 21 | com.github.criccomini 22 | ezdb-treemap 23 | test 24 | 25 | 26 | org.rocksdb 27 | rocksdbjni 28 | 29 | 30 | com.google.guava 31 | guava 32 | 33 | 34 | junit 35 | junit 36 | test 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /ezdb-rocksdb-jni/src/main/java/ezdb/rocksdb/EzRocksDb.java: -------------------------------------------------------------------------------- 1 | package ezdb.rocksdb; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.ByteBuffer; 6 | import java.util.Comparator; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import org.rocksdb.Options; 11 | 12 | import ezdb.Db; 13 | import ezdb.DbException; 14 | import ezdb.comparator.LexicographicalComparator; 15 | import ezdb.rocksdb.table.EzRocksDbTable; 16 | import ezdb.rocksdb.table.range.EzRocksDbRangeTable; 17 | import ezdb.serde.Serde; 18 | import ezdb.table.Table; 19 | import ezdb.table.range.RangeTable; 20 | 21 | /** 22 | * An implementation of Db that uses LevelDb tables to persist data. Each 23 | * "table" is just a LevelDB database persisted as a subdirectory inside of 24 | * EzLevelDb's root. 25 | * 26 | * @author criccomini 27 | * 28 | */ 29 | public class EzRocksDb implements Db { 30 | private final File root; 31 | private final Map> cache; 32 | private final EzRocksDbFactory factory; 33 | 34 | public EzRocksDb(final File root) { 35 | this(root, new EzRocksDbJniFactory()); 36 | } 37 | 38 | public EzRocksDb(final File root, final EzRocksDbFactory factory) { 39 | this.root = root; 40 | this.factory = factory; 41 | this.cache = new HashMap>(); 42 | } 43 | 44 | @Override 45 | public void deleteTable(final String tableName) { 46 | try { 47 | synchronized (cache) { 48 | cache.remove(tableName); 49 | factory.destroy(getFile(tableName), new Options()); 50 | } 51 | } catch (final IOException e) { 52 | throw new DbException(e); 53 | } 54 | } 55 | 56 | @SuppressWarnings("unchecked") 57 | @Override 58 | public Table getTable(final String tableName, final Serde hashKeySerde, final Serde valueSerde) { 59 | return getTable(tableName, hashKeySerde, valueSerde, new LexicographicalComparator()); 60 | } 61 | 62 | @SuppressWarnings("unchecked") 63 | @Override 64 | public Table getTable(final String tableName, final Serde hashKeySerde, final Serde valueSerde, 65 | final Comparator hashKeyComparator) { 66 | synchronized (cache) { 67 | Table table = cache.get(tableName); 68 | 69 | if (table == null) { 70 | table = new EzRocksDbTable(new File(root, tableName), factory, hashKeySerde, valueSerde, 71 | hashKeyComparator); 72 | cache.put(tableName, table); 73 | } else if (!(table instanceof EzRocksDbTable)) { 74 | throw new IllegalStateException("Expected " + EzRocksDbTable.class.getSimpleName() + " but got " 75 | + table.getClass().getSimpleName() + " for: " + tableName); 76 | } 77 | 78 | return (Table) table; 79 | } 80 | } 81 | 82 | @Override 83 | public RangeTable getRangeTable(final String tableName, final Serde hashKeySerde, 84 | final Serde rangeKeySerde, final Serde valueSerde) { 85 | return getRangeTable(tableName, hashKeySerde, rangeKeySerde, valueSerde, new LexicographicalComparator(), 86 | new LexicographicalComparator()); 87 | } 88 | 89 | @SuppressWarnings("unchecked") 90 | @Override 91 | public RangeTable getRangeTable(final String tableName, final Serde hashKeySerde, 92 | final Serde rangeKeySerde, final Serde valueSerde, final Comparator hashKeyComparator, 93 | final Comparator rangeKeyComparator) { 94 | synchronized (cache) { 95 | RangeTable table = (RangeTable) cache.get(tableName); 96 | 97 | if (table == null) { 98 | table = new EzRocksDbRangeTable(new File(root, tableName), factory, hashKeySerde, 99 | rangeKeySerde, valueSerde, hashKeyComparator, rangeKeyComparator); 100 | cache.put(tableName, table); 101 | } else if (!(table instanceof EzRocksDbRangeTable)) { 102 | throw new IllegalStateException("Expected " + EzRocksDbRangeTable.class.getSimpleName() + " but got " 103 | + table.getClass().getSimpleName() + " for: " + tableName); 104 | } 105 | 106 | return (RangeTable) table; 107 | } 108 | } 109 | 110 | /** 111 | * A helper method used to convert a table name to the location on disk where 112 | * this LevelDB database will be persisted. 113 | * 114 | * @param tableName The logical name of the table. 115 | * @return The physical location of the directory where this table should be 116 | * persisted. 117 | */ 118 | private File getFile(final String tableName) { 119 | return new File(root, tableName); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /ezdb-rocksdb-jni/src/main/java/ezdb/rocksdb/EzRocksDbFactory.java: -------------------------------------------------------------------------------- 1 | package ezdb.rocksdb; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import org.rocksdb.Options; 7 | import org.rocksdb.RocksDB; 8 | 9 | /** 10 | * An interface that allows us to inject either a JNI or pure-Java 11 | * implementation of LevelDB. EzLevelDb uses this class to open and delete 12 | * LevelDb instances. 13 | * 14 | * @author criccomi 15 | * 16 | */ 17 | public interface EzRocksDbFactory { 18 | public RocksDB open(File path, Options options, boolean rangeTable) throws IOException; 19 | 20 | public void destroy(File path, Options options) throws IOException; 21 | } 22 | -------------------------------------------------------------------------------- /ezdb-rocksdb-jni/src/main/java/ezdb/rocksdb/EzRocksDbJniFactory.java: -------------------------------------------------------------------------------- 1 | package ezdb.rocksdb; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import org.rocksdb.Options; 7 | import org.rocksdb.RocksDB; 8 | import org.rocksdb.RocksDBException; 9 | 10 | import ezdb.rocksdb.util.FileUtils; 11 | 12 | public class EzRocksDbJniFactory implements EzRocksDbFactory { 13 | @Override 14 | public RocksDB open(final File path, final Options options, final boolean rangeTable) throws IOException { 15 | try { 16 | return RocksDB.open(options, path.getAbsolutePath()); 17 | } catch (final RocksDBException e) { 18 | throw new IOException(e); 19 | } 20 | } 21 | 22 | @Override 23 | public void destroy(final File path, final Options options) throws IOException { 24 | // implementation taken from java port of leveldb 25 | FileUtils.deleteRecursively(path); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ezdb-rocksdb-jni/src/main/java/ezdb/rocksdb/table/EzRocksDbBatch.java: -------------------------------------------------------------------------------- 1 | package ezdb.rocksdb.table; 2 | 3 | import java.io.IOException; 4 | 5 | import org.rocksdb.RocksDB; 6 | import org.rocksdb.RocksDBException; 7 | import org.rocksdb.WriteBatch; 8 | import org.rocksdb.WriteOptions; 9 | 10 | import ezdb.DbException; 11 | import ezdb.serde.Serde; 12 | import ezdb.table.Batch; 13 | 14 | public class EzRocksDbBatch implements Batch { 15 | 16 | private final RocksDB db; 17 | private final WriteBatch writeBatch; 18 | private final Serde hashKeySerde; 19 | private final Serde valueSerde; 20 | private final WriteOptions writeOptions; 21 | 22 | public EzRocksDbBatch(final RocksDB db, final Serde hashKeySerde, final Serde valueSerde) { 23 | this.writeOptions = new WriteOptions(); 24 | this.db = db; 25 | this.writeBatch = new WriteBatch(); 26 | this.hashKeySerde = hashKeySerde; 27 | this.valueSerde = valueSerde; 28 | } 29 | 30 | @Override 31 | public void put(final H hashKey, final V value) { 32 | try { 33 | writeBatch.put(hashKeySerde.toBytes(hashKey), valueSerde.toBytes(value)); 34 | } catch (final RocksDBException e) { 35 | throw new RuntimeException(e); 36 | } 37 | } 38 | 39 | @Override 40 | public void delete(final H hashKey) { 41 | try { 42 | writeBatch.delete(hashKeySerde.toBytes(hashKey)); 43 | } catch (final RocksDBException e) { 44 | throw new RuntimeException(e); 45 | } 46 | } 47 | 48 | @Override 49 | public void flush() { 50 | try { 51 | db.write(writeOptions, writeBatch); 52 | } catch (final RocksDBException e) { 53 | throw new DbException(e); 54 | } 55 | } 56 | 57 | @Override 58 | public void close() throws IOException { 59 | writeBatch.close(); 60 | writeOptions.close(); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /ezdb-rocksdb-jni/src/main/java/ezdb/rocksdb/table/EzRocksDbComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.rocksdb.table; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Comparator; 5 | 6 | import org.rocksdb.ComparatorOptions; 7 | import org.rocksdb.Slice; 8 | 9 | import ezdb.util.Util; 10 | 11 | /** 12 | * LevelDb provides a comparator interface that we can use to handle hash/range 13 | * pairs. 14 | * 15 | * @author criccomini 16 | * 17 | */ 18 | public class EzRocksDbComparator extends org.rocksdb.Comparator { 19 | public static final String name = EzRocksDbComparator.class.toString(); 20 | 21 | private final Comparator hashKeyComparator; 22 | 23 | public EzRocksDbComparator(final Comparator hashKeyComparator) { 24 | super(new ComparatorOptions()); 25 | this.hashKeyComparator = hashKeyComparator; 26 | } 27 | 28 | @Override 29 | public int compare(final Slice a, final Slice b) { 30 | return Util.compareKeys(hashKeyComparator, ByteBuffer.wrap(a.data()), ByteBuffer.wrap(b.data())); 31 | } 32 | 33 | @Override 34 | public String name() { 35 | return name; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ezdb-rocksdb-jni/src/main/java/ezdb/rocksdb/table/EzRocksDbTable.java: -------------------------------------------------------------------------------- 1 | package ezdb.rocksdb.table; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.ByteBuffer; 6 | import java.util.Comparator; 7 | import java.util.NoSuchElementException; 8 | 9 | import org.rocksdb.Options; 10 | import org.rocksdb.RocksDB; 11 | import org.rocksdb.RocksDBException; 12 | 13 | import ezdb.DbException; 14 | import ezdb.rocksdb.EzRocksDbFactory; 15 | import ezdb.rocksdb.util.EzDBIterator; 16 | import ezdb.rocksdb.util.RocksDBJniDBIterator; 17 | import ezdb.serde.Serde; 18 | import ezdb.table.Batch; 19 | import ezdb.table.Table; 20 | import ezdb.table.TableRow; 21 | import ezdb.util.TableIterator; 22 | 23 | public class EzRocksDbTable implements Table { 24 | private final RocksDB db; 25 | private final Serde hashKeySerde; 26 | private final Serde valueSerde; 27 | private final Comparator hashKeyComparator; 28 | private final Options options; 29 | 30 | public EzRocksDbTable(final File path, final EzRocksDbFactory factory, final Serde hashKeySerde, 31 | final Serde valueSerde, final Comparator hashKeyComparator) { 32 | this.hashKeySerde = hashKeySerde; 33 | this.valueSerde = valueSerde; 34 | this.hashKeyComparator = hashKeyComparator; 35 | 36 | this.options = new Options(); 37 | 38 | options.setCreateIfMissing(true); 39 | options.setComparator(new EzRocksDbComparator(hashKeyComparator)); 40 | 41 | try { 42 | this.db = factory.open(path, options, false); 43 | } catch (final IOException e) { 44 | throw new DbException(e); 45 | } 46 | } 47 | 48 | @Override 49 | public void put(final H hashKey, final V value) { 50 | try { 51 | db.put(hashKeySerde.toBytes(hashKey), valueSerde.toBytes(value)); 52 | } catch (final RocksDBException e) { 53 | throw new DbException(e); 54 | } 55 | } 56 | 57 | @Override 58 | public V get(final H hashKey) { 59 | byte[] valueBytes; 60 | try { 61 | valueBytes = db.get(hashKeySerde.toBytes(hashKey)); 62 | } catch (final RocksDBException e) { 63 | throw new DbException(e); 64 | } 65 | 66 | if (valueBytes == null) { 67 | return null; 68 | } 69 | 70 | return valueSerde.fromBytes(valueBytes); 71 | } 72 | 73 | @Override 74 | public TableIterator> range() { 75 | final EzDBIterator iterator = new RocksDBJniDBIterator(db.newIterator(), hashKeySerde, valueSerde); 76 | iterator.seekToFirst(); 77 | return new AutoClosingTableIterator(new TableIterator>() { 78 | @Override 79 | public boolean hasNext() { 80 | return iterator.hasNext(); 81 | } 82 | 83 | @Override 84 | public TableRow next() { 85 | if (hasNext()) { 86 | return iterator.next(); 87 | } else { 88 | throw new NoSuchElementException(); 89 | } 90 | } 91 | 92 | @Override 93 | public void remove() { 94 | iterator.remove(); 95 | } 96 | 97 | @Override 98 | public void close() { 99 | try { 100 | iterator.close(); 101 | } catch (final Exception e) { 102 | throw new DbException(e); 103 | } 104 | } 105 | }); 106 | } 107 | 108 | @Override 109 | public void delete(final H hashKey) { 110 | try { 111 | this.db.delete(hashKeySerde.toBytes(hashKey)); 112 | } catch (final RocksDBException e) { 113 | throw new DbException(e); 114 | } 115 | } 116 | 117 | @Override 118 | public void close() { 119 | try { 120 | this.db.close(); 121 | this.options.close(); 122 | } catch (final Exception e) { 123 | throw new DbException(e); 124 | } 125 | } 126 | 127 | private static class AutoClosingTableIterator<_H, _V> implements TableIterator> { 128 | 129 | private final TableIterator> delegate; 130 | private boolean closed; 131 | 132 | public AutoClosingTableIterator(final TableIterator> delegate) { 133 | this.delegate = delegate; 134 | } 135 | 136 | @Override 137 | public boolean hasNext() { 138 | if (closed) { 139 | return false; 140 | } 141 | final boolean hasNext = delegate.hasNext(); 142 | if (!hasNext) { 143 | close(); 144 | } 145 | return hasNext; 146 | } 147 | 148 | @Override 149 | public TableRow<_H, _V> next() { 150 | if (closed) { 151 | throw new NoSuchElementException(); 152 | } 153 | return delegate.next(); 154 | } 155 | 156 | @Override 157 | public void remove() { 158 | delegate.remove(); 159 | } 160 | 161 | @SuppressWarnings("deprecation") 162 | @Override 163 | protected void finalize() throws Throwable { 164 | super.finalize(); 165 | close(); 166 | } 167 | 168 | @Override 169 | public void close() { 170 | if (!closed) { 171 | closed = true; 172 | delegate.close(); 173 | } 174 | } 175 | 176 | } 177 | 178 | @Override 179 | public Batch newBatch() { 180 | return new EzRocksDbBatch(db, hashKeySerde, valueSerde); 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /ezdb-rocksdb-jni/src/main/java/ezdb/rocksdb/table/range/EzRocksDbRangeBatch.java: -------------------------------------------------------------------------------- 1 | package ezdb.rocksdb.table.range; 2 | 3 | import java.io.IOException; 4 | 5 | import org.rocksdb.RocksDB; 6 | import org.rocksdb.RocksDBException; 7 | import org.rocksdb.WriteBatch; 8 | import org.rocksdb.WriteOptions; 9 | 10 | import ezdb.DbException; 11 | import ezdb.serde.Serde; 12 | import ezdb.table.range.RangeBatch; 13 | import ezdb.util.Util; 14 | 15 | public class EzRocksDbRangeBatch implements RangeBatch { 16 | 17 | private final RocksDB db; 18 | private final WriteBatch writeBatch; 19 | private final Serde hashKeySerde; 20 | private final Serde rangeKeySerde; 21 | private final Serde valueSerde; 22 | private final WriteOptions writeOptions; 23 | 24 | public EzRocksDbRangeBatch(final RocksDB db, final Serde hashKeySerde, final Serde rangeKeySerde, 25 | final Serde valueSerde) { 26 | this.writeOptions = new WriteOptions(); 27 | this.db = db; 28 | this.writeBatch = new WriteBatch(); 29 | this.hashKeySerde = hashKeySerde; 30 | this.rangeKeySerde = rangeKeySerde; 31 | this.valueSerde = valueSerde; 32 | } 33 | 34 | @Override 35 | public void put(final H hashKey, final V value) { 36 | put(hashKey, null, value); 37 | } 38 | 39 | @Override 40 | public void delete(final H hashKey) { 41 | delete(hashKey, null); 42 | } 43 | 44 | @Override 45 | public void flush() { 46 | try { 47 | db.write(writeOptions, writeBatch); 48 | } catch (final RocksDBException e) { 49 | throw new DbException(e); 50 | } 51 | } 52 | 53 | @Override 54 | public void close() throws IOException { 55 | writeBatch.close(); 56 | writeOptions.close(); 57 | } 58 | 59 | @Override 60 | public void put(final H hashKey, final R rangeKey, final V value) { 61 | try { 62 | writeBatch.put(Util.combineBytes(hashKeySerde, rangeKeySerde, hashKey, rangeKey), 63 | valueSerde.toBytes(value)); 64 | } catch (final RocksDBException e) { 65 | throw new RuntimeException(e); 66 | } 67 | } 68 | 69 | @Override 70 | public void delete(final H hashKey, final R rangeKey) { 71 | try { 72 | writeBatch.delete(Util.combineBytes(hashKeySerde, rangeKeySerde, hashKey, rangeKey)); 73 | } catch (final RocksDBException e) { 74 | throw new RuntimeException(e); 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /ezdb-rocksdb-jni/src/main/java/ezdb/rocksdb/table/range/EzRocksDbRangeComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.rocksdb.table.range; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Comparator; 5 | 6 | import org.rocksdb.ComparatorOptions; 7 | import org.rocksdb.Slice; 8 | 9 | import ezdb.util.Util; 10 | 11 | /** 12 | * LevelDb provides a comparator interface that we can use to handle hash/range 13 | * pairs. 14 | * 15 | * @author criccomini 16 | * 17 | */ 18 | public class EzRocksDbRangeComparator extends org.rocksdb.Comparator { 19 | public static final String name = EzRocksDbRangeComparator.class.toString(); 20 | 21 | private final Comparator hashKeyComparator; 22 | private final Comparator rangeKeyComparator; 23 | 24 | public EzRocksDbRangeComparator(final Comparator hashKeyComparator, 25 | final Comparator rangeKeyComparator) { 26 | super(new ComparatorOptions()); 27 | this.hashKeyComparator = hashKeyComparator; 28 | this.rangeKeyComparator = rangeKeyComparator; 29 | } 30 | 31 | @Override 32 | public int compare(final Slice a, final Slice b) { 33 | return Util.compareKeys(hashKeyComparator, rangeKeyComparator, ByteBuffer.wrap(a.data()), 34 | ByteBuffer.wrap(b.data())); 35 | } 36 | 37 | @Override 38 | public String name() { 39 | return name; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ezdb-rocksdb-jni/src/main/java/ezdb/rocksdb/util/EzDBIterator.java: -------------------------------------------------------------------------------- 1 | package ezdb.rocksdb.util; 2 | 3 | import java.io.Closeable; 4 | import java.util.Iterator; 5 | 6 | import ezdb.table.RawTableRow; 7 | 8 | //implementation taken from leveldbjni 9 | /** 10 | * @author Hiram Chirino 11 | */ 12 | public interface EzDBIterator extends Iterator>, Closeable { 13 | 14 | /** 15 | * Repositions the iterator so the key of the next BlockElement returned greater 16 | * than or equal to the specified targetKey. 17 | */ 18 | public void seek(byte[] key); 19 | 20 | /** 21 | * Repositions the iterator so is is at the beginning of the Database. 22 | */ 23 | public void seekToFirst(); 24 | 25 | /** 26 | * Returns the next element in the iteration, without advancing the iteration. 27 | */ 28 | public RawTableRow peekNext(); 29 | 30 | public byte[] nextKey(); 31 | 32 | public byte[] peekNextKey(); 33 | 34 | /** 35 | * @return true if there is a previous entry in the iteration. 36 | */ 37 | boolean hasPrev(); 38 | 39 | /** 40 | * @return the previous element in the iteration and rewinds the iteration. 41 | */ 42 | RawTableRow prev(); 43 | 44 | /** 45 | * @return the previous element in the iteration, without rewinding the 46 | * iteration. 47 | */ 48 | RawTableRow peekPrev(); 49 | 50 | public byte[] peekPrevKey(); 51 | 52 | /** 53 | * Repositions the iterator so it is at the end of of the Database. 54 | */ 55 | public void seekToLast(); 56 | 57 | @Override 58 | public void close(); 59 | 60 | } 61 | -------------------------------------------------------------------------------- /ezdb-rocksdb-jni/src/main/java/ezdb/rocksdb/util/EzDBRangeIterator.java: -------------------------------------------------------------------------------- 1 | package ezdb.rocksdb.util; 2 | 3 | import java.io.Closeable; 4 | import java.util.Iterator; 5 | 6 | import ezdb.table.range.RawRangeTableRow; 7 | 8 | //implementation taken from leveldbjni 9 | /** 10 | * @author Hiram Chirino 11 | */ 12 | public interface EzDBRangeIterator extends Iterator>, Closeable { 13 | 14 | /** 15 | * Repositions the iterator so the key of the next BlockElement returned greater 16 | * than or equal to the specified targetKey. 17 | */ 18 | public void seek(byte[] key); 19 | 20 | /** 21 | * Repositions the iterator so is is at the beginning of the Database. 22 | */ 23 | public void seekToFirst(); 24 | 25 | /** 26 | * Returns the next element in the iteration, without advancing the iteration. 27 | */ 28 | public RawRangeTableRow peekNext(); 29 | 30 | public byte[] nextKey(); 31 | 32 | public byte[] peekNextKey(); 33 | 34 | /** 35 | * @return true if there is a previous entry in the iteration. 36 | */ 37 | boolean hasPrev(); 38 | 39 | /** 40 | * @return the previous element in the iteration and rewinds the iteration. 41 | */ 42 | RawRangeTableRow prev(); 43 | 44 | /** 45 | * @return the previous element in the iteration, without rewinding the 46 | * iteration. 47 | */ 48 | RawRangeTableRow peekPrev(); 49 | 50 | public byte[] peekPrevKey(); 51 | 52 | /** 53 | * Repositions the iterator so it is at the end of of the Database. 54 | */ 55 | public void seekToLast(); 56 | 57 | @Override 58 | public void close(); 59 | 60 | } 61 | -------------------------------------------------------------------------------- /ezdb-rocksdb-jni/src/main/java/ezdb/rocksdb/util/RocksDBJniDBIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011, FuseSource Corp. All rights reserved. 3 | * 4 | * http://fusesource.com 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * * Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following disclaimer 14 | * in the documentation and/or other materials provided with the 15 | * distribution. 16 | * * Neither the name of FuseSource Corp. nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package ezdb.rocksdb.util; 33 | 34 | import java.util.NoSuchElementException; 35 | 36 | import org.rocksdb.RocksIterator; 37 | 38 | import ezdb.serde.Serde; 39 | import ezdb.table.RawTableRow; 40 | 41 | //implementation taken from leveldbjni 42 | /** 43 | * @author Hiram Chirino 44 | */ 45 | public class RocksDBJniDBIterator implements EzDBIterator { 46 | 47 | private final RocksIterator iterator; 48 | private final Serde hashKeySerde; 49 | private final Serde valueSerde; 50 | 51 | public RocksDBJniDBIterator(final RocksIterator iterator, final Serde hashKeySerde, final Serde valueSerde) { 52 | this.iterator = iterator; 53 | this.hashKeySerde = hashKeySerde; 54 | this.valueSerde = valueSerde; 55 | } 56 | 57 | @Override 58 | public void close() { 59 | iterator.close(); 60 | } 61 | 62 | @Override 63 | public void remove() { 64 | throw new UnsupportedOperationException(); 65 | } 66 | 67 | @Override 68 | public void seek(final byte[] key) { 69 | iterator.seek(key); 70 | } 71 | 72 | @Override 73 | public void seekToFirst() { 74 | iterator.seekToFirst(); 75 | } 76 | 77 | @Override 78 | public void seekToLast() { 79 | iterator.seekToLast(); 80 | } 81 | 82 | @Override 83 | public RawTableRow peekNext() { 84 | if (!iterator.isValid()) { 85 | throw new NoSuchElementException(); 86 | } 87 | return RawTableRow.valueOfBytes(iterator.key(), iterator.value(), hashKeySerde, valueSerde); 88 | } 89 | 90 | @Override 91 | public byte[] peekNextKey() { 92 | if (!iterator.isValid()) { 93 | throw new NoSuchElementException(); 94 | } 95 | return iterator.key(); 96 | } 97 | 98 | @Override 99 | public boolean hasNext() { 100 | return iterator.isValid(); 101 | } 102 | 103 | @Override 104 | public RawTableRow next() { 105 | final RawTableRow rc = peekNext(); 106 | iterator.next(); 107 | return rc; 108 | } 109 | 110 | @Override 111 | public byte[] nextKey() { 112 | final byte[] rc = peekNextKey(); 113 | iterator.next(); 114 | return rc; 115 | } 116 | 117 | @Override 118 | public boolean hasPrev() { 119 | if (!iterator.isValid()) { 120 | return false; 121 | } 122 | iterator.prev(); 123 | try { 124 | return iterator.isValid(); 125 | } finally { 126 | if (iterator.isValid()) { 127 | iterator.next(); 128 | } else { 129 | iterator.seekToFirst(); 130 | } 131 | } 132 | } 133 | 134 | @Override 135 | public RawTableRow peekPrev() { 136 | iterator.prev(); 137 | try { 138 | return peekNext(); 139 | } finally { 140 | if (iterator.isValid()) { 141 | iterator.next(); 142 | } else { 143 | iterator.seekToFirst(); 144 | } 145 | } 146 | } 147 | 148 | @Override 149 | public byte[] peekPrevKey() { 150 | iterator.prev(); 151 | try { 152 | return peekNextKey(); 153 | } finally { 154 | if (iterator.isValid()) { 155 | iterator.next(); 156 | } else { 157 | iterator.seekToFirst(); 158 | } 159 | } 160 | } 161 | 162 | @Override 163 | public RawTableRow prev() { 164 | final RawTableRow rc = peekPrev(); 165 | iterator.prev(); 166 | return rc; 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /ezdb-rocksdb-jni/src/main/java/ezdb/rocksdb/util/RocksDBJniRangeDBIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011, FuseSource Corp. All rights reserved. 3 | * 4 | * http://fusesource.com 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * * Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following disclaimer 14 | * in the documentation and/or other materials provided with the 15 | * distribution. 16 | * * Neither the name of FuseSource Corp. nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package ezdb.rocksdb.util; 33 | 34 | import java.util.NoSuchElementException; 35 | 36 | import org.rocksdb.RocksIterator; 37 | 38 | import ezdb.serde.Serde; 39 | import ezdb.table.range.RawRangeTableRow; 40 | 41 | //implementation taken from leveldbjni 42 | /** 43 | * @author Hiram Chirino 44 | */ 45 | public class RocksDBJniRangeDBIterator implements EzDBRangeIterator { 46 | 47 | private final RocksIterator iterator; 48 | private final Serde hashKeySerde; 49 | private final Serde rangeKeySerde; 50 | private final Serde valueSerde; 51 | 52 | public RocksDBJniRangeDBIterator(final RocksIterator iterator, final Serde hashKeySerde, final Serde rangeKeySerde, 53 | final Serde valueSerde) { 54 | this.iterator = iterator; 55 | this.hashKeySerde = hashKeySerde; 56 | this.rangeKeySerde = rangeKeySerde; 57 | this.valueSerde = valueSerde; 58 | } 59 | 60 | @Override 61 | public void close() { 62 | iterator.close(); 63 | } 64 | 65 | @Override 66 | public void remove() { 67 | throw new UnsupportedOperationException(); 68 | } 69 | 70 | @Override 71 | public void seek(final byte[] key) { 72 | iterator.seek(key); 73 | } 74 | 75 | @Override 76 | public void seekToFirst() { 77 | iterator.seekToFirst(); 78 | } 79 | 80 | @Override 81 | public void seekToLast() { 82 | iterator.seekToLast(); 83 | } 84 | 85 | @Override 86 | public RawRangeTableRow peekNext() { 87 | if (!iterator.isValid()) { 88 | throw new NoSuchElementException(); 89 | } 90 | return RawRangeTableRow.valueOfBytes(iterator.key(), iterator.value(), hashKeySerde, rangeKeySerde, valueSerde); 91 | } 92 | 93 | @Override 94 | public byte[] peekNextKey() { 95 | if (!iterator.isValid()) { 96 | throw new NoSuchElementException(); 97 | } 98 | return iterator.key(); 99 | } 100 | 101 | @Override 102 | public boolean hasNext() { 103 | return iterator.isValid(); 104 | } 105 | 106 | @Override 107 | public RawRangeTableRow next() { 108 | final RawRangeTableRow rc = peekNext(); 109 | iterator.next(); 110 | return rc; 111 | } 112 | 113 | @Override 114 | public byte[] nextKey() { 115 | final byte[] rc = peekNextKey(); 116 | iterator.next(); 117 | return rc; 118 | } 119 | 120 | @Override 121 | public boolean hasPrev() { 122 | if (!iterator.isValid()) { 123 | return false; 124 | } 125 | iterator.prev(); 126 | try { 127 | return iterator.isValid(); 128 | } finally { 129 | if (iterator.isValid()) { 130 | iterator.next(); 131 | } else { 132 | iterator.seekToFirst(); 133 | } 134 | } 135 | } 136 | 137 | @Override 138 | public RawRangeTableRow peekPrev() { 139 | iterator.prev(); 140 | try { 141 | return peekNext(); 142 | } finally { 143 | if (iterator.isValid()) { 144 | iterator.next(); 145 | } else { 146 | iterator.seekToFirst(); 147 | } 148 | } 149 | } 150 | 151 | @Override 152 | public byte[] peekPrevKey() { 153 | iterator.prev(); 154 | try { 155 | return peekNextKey(); 156 | } finally { 157 | if (iterator.isValid()) { 158 | iterator.next(); 159 | } else { 160 | iterator.seekToFirst(); 161 | } 162 | } 163 | } 164 | 165 | @Override 166 | public RawRangeTableRow prev() { 167 | final RawRangeTableRow rc = peekPrev(); 168 | iterator.prev(); 169 | return rc; 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /ezdb-treemap/.gitignore: -------------------------------------------------------------------------------- 1 | /.settings/ 2 | -------------------------------------------------------------------------------- /ezdb-treemap/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | com.github.criccomini 7 | ezdb-parent 8 | 0.1.18-SNAPSHOT 9 | 10 | 11 | ezdb-treemap 12 | EZDB TreeMap 13 | jar 14 | 15 | 16 | 17 | com.github.criccomini 18 | ezdb-api 19 | 20 | 21 | com.google.guava 22 | guava 23 | test 24 | 25 | 26 | junit 27 | junit 28 | test 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ezdb-treemap/src/main/java/ezdb/treemap/bytes/EzBytesTreeMapDb.java: -------------------------------------------------------------------------------- 1 | package ezdb.treemap.bytes; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Comparator; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import ezdb.Db; 9 | import ezdb.comparator.LexicographicalComparator; 10 | import ezdb.serde.Serde; 11 | import ezdb.table.Table; 12 | import ezdb.table.range.RangeTable; 13 | import ezdb.treemap.bytes.table.BytesTreeMapTable; 14 | import ezdb.treemap.bytes.table.range.BytesTreeMapRangeTable; 15 | 16 | public class EzBytesTreeMapDb implements Db { 17 | private final Map> cache; 18 | 19 | public EzBytesTreeMapDb() { 20 | this.cache = new HashMap>(); 21 | } 22 | 23 | @Override 24 | public void deleteTable(final String tableName) { 25 | synchronized (cache) { 26 | cache.remove(tableName); 27 | } 28 | } 29 | 30 | @Override 31 | public Table getTable(final String tableName, final Serde hashKeySerde, final Serde valueSerde) { 32 | return getTable(tableName, hashKeySerde, valueSerde, new LexicographicalComparator()); 33 | } 34 | 35 | @SuppressWarnings("unchecked") 36 | @Override 37 | public Table getTable(final String tableName, final Serde hashKeySerde, final Serde valueSerde, 38 | final Comparator hashKeyComparator) { 39 | synchronized (cache) { 40 | Table table = cache.get(tableName); 41 | 42 | if (table == null) { 43 | table = newTable(hashKeySerde, valueSerde, hashKeyComparator); 44 | cache.put(tableName, table); 45 | } else if (!(table instanceof BytesTreeMapTable)) { 46 | throw new IllegalStateException("Expected " + BytesTreeMapTable.class.getSimpleName() + " but got " 47 | + table.getClass().getSimpleName() + " for: " + tableName); 48 | } 49 | 50 | return (Table) table; 51 | } 52 | } 53 | 54 | @Override 55 | public RangeTable getRangeTable(final String tableName, final Serde hashKeySerde, 56 | final Serde rangeKeySerde, final Serde valueSerde) { 57 | return getRangeTable(tableName, hashKeySerde, rangeKeySerde, valueSerde, new LexicographicalComparator(), 58 | new LexicographicalComparator()); 59 | } 60 | 61 | @SuppressWarnings("unchecked") 62 | @Override 63 | public RangeTable getRangeTable(final String tableName, final Serde hashKeySerde, 64 | final Serde rangeKeySerde, final Serde valueSerde, final Comparator hashKeyComparator, 65 | final Comparator rangeKeyComparator) { 66 | synchronized (cache) { 67 | RangeTable table = (RangeTable) cache.get(tableName); 68 | 69 | if (table == null) { 70 | table = newRangeTable(hashKeySerde, rangeKeySerde, valueSerde, hashKeyComparator, rangeKeyComparator); 71 | cache.put(tableName, table); 72 | } else if (!(table instanceof BytesTreeMapRangeTable)) { 73 | throw new IllegalStateException("Expected " + BytesTreeMapRangeTable.class.getSimpleName() + " but got " 74 | + table.getClass().getSimpleName() + " for: " + tableName); 75 | } 76 | 77 | return (RangeTable) table; 78 | } 79 | } 80 | 81 | private BytesTreeMapRangeTable newRangeTable(final Serde hashKeySerde, 82 | final Serde rangeKeySerde, final Serde valueSerde, final Comparator hashKeyComparator, 83 | final Comparator rangeKeyComparator) { 84 | return new BytesTreeMapRangeTable(hashKeySerde, rangeKeySerde, valueSerde, hashKeyComparator, 85 | rangeKeyComparator); 86 | } 87 | 88 | private BytesTreeMapTable newTable(final Serde hashKeySerde, final Serde valueSerde, 89 | final Comparator hashKeyComparator) { 90 | return new BytesTreeMapTable(hashKeySerde, valueSerde, hashKeyComparator); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /ezdb-treemap/src/main/java/ezdb/treemap/bytes/table/BytesTreeMapTable.java: -------------------------------------------------------------------------------- 1 | package ezdb.treemap.bytes.table; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | import java.util.Comparator; 6 | import java.util.Iterator; 7 | import java.util.Map; 8 | import java.util.NoSuchElementException; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | import ezdb.serde.Serde; 12 | import ezdb.table.Batch; 13 | import ezdb.table.RawTableRow; 14 | import ezdb.table.Table; 15 | import ezdb.table.TableRow; 16 | import ezdb.util.TableIterator; 17 | import ezdb.util.Util; 18 | 19 | public class BytesTreeMapTable implements Table { 20 | 21 | private final Serde hashKeySerde; 22 | private final Serde valueSerde; 23 | private final Map map; 24 | private final Comparator hashKeyComparator; 25 | 26 | public BytesTreeMapTable(final Serde hashKeySerde, final Serde valueSerde, 27 | final Comparator hashKeyComparator) { 28 | this.hashKeySerde = hashKeySerde; 29 | this.valueSerde = valueSerde; 30 | this.hashKeyComparator = hashKeyComparator; 31 | final Comparator comparator = new Comparator() { 32 | @Override 33 | public int compare(final ByteBuffer k1, final ByteBuffer k2) { 34 | return Util.compareKeys(hashKeyComparator, k1, k2); 35 | } 36 | }; 37 | this.map = newMap(comparator); 38 | } 39 | 40 | protected Map newMap(final Comparator comparator) { 41 | // return new ConcurrentSkipListMap(comparator); 42 | // we can also use the faster alternative here 43 | return new ConcurrentHashMap(); 44 | } 45 | 46 | @Override 47 | public void put(final H hashKey, final V value) { 48 | map.put(ByteBuffer.wrap(hashKeySerde.toBytes(hashKey)), ByteBuffer.wrap(valueSerde.toBytes(value))); 49 | } 50 | 51 | @Override 52 | public V get(final H hashKey) { 53 | final ByteBuffer valueBytes = map.get(ByteBuffer.wrap(hashKeySerde.toBytes(hashKey))); 54 | if (valueBytes != null) { 55 | valueBytes.clear(); 56 | return valueSerde.fromBuffer(valueBytes); 57 | } 58 | return null; 59 | } 60 | 61 | @Override 62 | public TableIterator> range() { 63 | final Iterator> iterator = map.entrySet().iterator(); 64 | return new TableIterator>() { 65 | Map.Entry next = (iterator.hasNext()) ? iterator.next() : null; 66 | 67 | @Override 68 | public boolean hasNext() { 69 | return next != null; 70 | } 71 | 72 | @Override 73 | public TableRow next() { 74 | TableRow row = null; 75 | 76 | if (hasNext()) { 77 | row = RawTableRow.valueOfBuffer(next, hashKeySerde, valueSerde); 78 | } 79 | 80 | if (iterator.hasNext()) { 81 | next = iterator.next(); 82 | } else { 83 | next = null; 84 | } 85 | 86 | if (row != null) { 87 | return row; 88 | } else { 89 | throw new NoSuchElementException(); 90 | } 91 | } 92 | 93 | @Override 94 | public void remove() { 95 | if (next == null) { 96 | throw new IllegalStateException("next is null"); 97 | } 98 | map.remove(next.getKey()); 99 | next(); 100 | } 101 | 102 | @Override 103 | public void close() { 104 | next = null; 105 | } 106 | }; 107 | } 108 | 109 | @Override 110 | public void delete(final H hashKey) { 111 | map.remove(ByteBuffer.wrap(hashKeySerde.toBytes(hashKey))); 112 | } 113 | 114 | @Override 115 | public void close() { 116 | } 117 | 118 | @Override 119 | public Batch newBatch() { 120 | return new Batch() { 121 | 122 | @Override 123 | public void close() throws IOException { 124 | } 125 | 126 | @Override 127 | public void put(final H hashKey, final V value) { 128 | BytesTreeMapTable.this.put(hashKey, value); 129 | } 130 | 131 | @Override 132 | public void delete(final H hashKey) { 133 | BytesTreeMapTable.this.delete(hashKey); 134 | } 135 | 136 | @Override 137 | public void flush() { 138 | } 139 | }; 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /ezdb-treemap/src/main/java/ezdb/treemap/object/EzObjectTreeMapDb.java: -------------------------------------------------------------------------------- 1 | package ezdb.treemap.object; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Comparator; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import ezdb.Db; 9 | import ezdb.comparator.ComparableComparator; 10 | import ezdb.comparator.LexicographicalComparator; 11 | import ezdb.serde.Serde; 12 | import ezdb.table.Table; 13 | import ezdb.table.range.RangeTable; 14 | import ezdb.treemap.object.table.ObjectTreeMapTable; 15 | import ezdb.treemap.object.table.range.ObjectTreeMapRangeTable; 16 | 17 | public class EzObjectTreeMapDb implements Db { 18 | private final Map> cache; 19 | 20 | public EzObjectTreeMapDb() { 21 | this.cache = new HashMap>(); 22 | } 23 | 24 | @Override 25 | public void deleteTable(final String tableName) { 26 | synchronized (cache) { 27 | cache.remove(tableName); 28 | } 29 | } 30 | 31 | @SuppressWarnings("unchecked") 32 | @Override 33 | public Table getTable(final String tableName, final Serde hashKeySerde, final Serde valueSerde) { 34 | return getTable(tableName, hashKeySerde, valueSerde, new ComparableComparator()); 35 | } 36 | 37 | @SuppressWarnings("unchecked") 38 | @Override 39 | public Table getTable(final String tableName, final Serde hashKeySerde, final Serde valueSerde, 40 | final Comparator hashKeyComparator) { 41 | synchronized (cache) { 42 | Table table = cache.get(tableName); 43 | 44 | if (table == null) { 45 | table = newTable(hashKeyComparator); 46 | cache.put(tableName, table); 47 | } else if (!(table instanceof ObjectTreeMapTable)) { 48 | throw new IllegalStateException("Expected " + ObjectTreeMapTable.class.getSimpleName() + " but got " 49 | + table.getClass().getSimpleName() + " for: " + tableName); 50 | } 51 | 52 | return (Table) table; 53 | } 54 | } 55 | 56 | @Override 57 | public RangeTable getRangeTable(final String tableName, final Serde hashKeySerde, 58 | final Serde rangeKeySerde, final Serde valueSerde) { 59 | return getRangeTable(tableName, hashKeySerde, rangeKeySerde, valueSerde, new ComparableComparator(), 60 | new ComparableComparator()); 61 | } 62 | 63 | @SuppressWarnings("unchecked") 64 | @Override 65 | public RangeTable getRangeTable(final String tableName, final Serde hashKeySerde, 66 | final Serde rangeKeySerde, final Serde valueSerde, final Comparator hashKeyComparator, 67 | final Comparator rangeKeyComparator) { 68 | synchronized (cache) { 69 | RangeTable table = (RangeTable) cache.get(tableName); 70 | 71 | if (table == null) { 72 | table = newRangeTable(hashKeyComparator, rangeKeyComparator); 73 | cache.put(tableName, table); 74 | } else if (!(table instanceof ObjectTreeMapRangeTable)) { 75 | throw new IllegalStateException("Expected " + ObjectTreeMapRangeTable.class.getSimpleName() 76 | + " but got " + table.getClass().getSimpleName() + " for: " + tableName); 77 | } 78 | 79 | return (RangeTable) table; 80 | } 81 | } 82 | 83 | private ObjectTreeMapRangeTable newRangeTable(final Comparator hashKeyComparator, 84 | final Comparator rangeKeyComparator) { 85 | return new ObjectTreeMapRangeTable(hashKeyComparator, rangeKeyComparator); 86 | } 87 | 88 | private ObjectTreeMapTable newTable(final Comparator hashKeyComparator) { 89 | return new ObjectTreeMapTable(hashKeyComparator); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /ezdb-treemap/src/main/java/ezdb/treemap/object/table/EzObjectTreeMapDbComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.treemap.object.table; 2 | 3 | import java.util.Comparator; 4 | 5 | import ezdb.util.Util; 6 | 7 | public class EzObjectTreeMapDbComparator implements Comparator { 8 | 9 | private final Comparator hashKeyComparator; 10 | 11 | public EzObjectTreeMapDbComparator(final Comparator hashKeyComparator) { 12 | this.hashKeyComparator = hashKeyComparator; 13 | } 14 | 15 | @Override 16 | public int compare(final H a, final H b) { 17 | return Util.compareKeys(hashKeyComparator, a, b); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /ezdb-treemap/src/main/java/ezdb/treemap/object/table/ObjectTreeMapTable.java: -------------------------------------------------------------------------------- 1 | package ezdb.treemap.object.table; 2 | 3 | import java.io.IOException; 4 | import java.util.Comparator; 5 | import java.util.Iterator; 6 | import java.util.Map; 7 | import java.util.Map.Entry; 8 | import java.util.NoSuchElementException; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | import ezdb.table.Batch; 12 | import ezdb.table.Table; 13 | import ezdb.table.TableRow; 14 | import ezdb.util.ObjectTableRow; 15 | import ezdb.util.TableIterator; 16 | 17 | public class ObjectTreeMapTable implements Table { 18 | private final Map map; 19 | private final Comparator hashKeyComparator; 20 | private final EzObjectTreeMapDbComparator keyComparator; 21 | 22 | public ObjectTreeMapTable(final Comparator hashKeyComparator) { 23 | this.hashKeyComparator = hashKeyComparator; 24 | this.keyComparator = new EzObjectTreeMapDbComparator(hashKeyComparator); 25 | this.map = newMap(keyComparator); 26 | } 27 | 28 | protected Map newMap(final Comparator comparator) { 29 | // return new ConcurrentSkipListMap<>(comparator); 30 | // we can also use the faster alternative here 31 | return new ConcurrentHashMap(); 32 | } 33 | 34 | @Override 35 | public void put(final H hashKey, final V value) { 36 | map.put(hashKey, value); 37 | } 38 | 39 | @Override 40 | public V get(final H hashKey) { 41 | final V value = map.get(hashKey); 42 | return value; 43 | } 44 | 45 | @Override 46 | public TableIterator> range() { 47 | final Iterator> iterator = map.entrySet().iterator(); 48 | return new TableIterator>() { 49 | Map.Entry next = (iterator.hasNext()) ? iterator.next() : null; 50 | 51 | @Override 52 | public boolean hasNext() { 53 | return next != null; 54 | } 55 | 56 | @Override 57 | public TableRow next() { 58 | TableRow row = null; 59 | 60 | if (hasNext()) { 61 | row = new ObjectTableRow(next); 62 | } 63 | 64 | if (iterator.hasNext()) { 65 | next = iterator.next(); 66 | } else { 67 | next = null; 68 | } 69 | 70 | if (row != null) { 71 | return row; 72 | } else { 73 | throw new NoSuchElementException(); 74 | } 75 | } 76 | 77 | @Override 78 | public void remove() { 79 | if (next == null) { 80 | throw new IllegalStateException("next is null"); 81 | } 82 | map.remove(next.getKey()); 83 | next(); 84 | } 85 | 86 | @Override 87 | public void close() { 88 | next = null; 89 | } 90 | }; 91 | } 92 | 93 | @Override 94 | public void delete(final H hashKey) { 95 | map.remove(hashKey); 96 | } 97 | 98 | @Override 99 | public void close() { 100 | } 101 | 102 | @Override 103 | public Batch newBatch() { 104 | return new Batch() { 105 | 106 | @Override 107 | public void close() throws IOException { 108 | } 109 | 110 | @Override 111 | public void put(final H hashKey, final V value) { 112 | ObjectTreeMapTable.this.put(hashKey, value); 113 | } 114 | 115 | @Override 116 | public void delete(final H hashKey) { 117 | ObjectTreeMapTable.this.delete(hashKey); 118 | } 119 | 120 | @Override 121 | public void flush() { 122 | } 123 | }; 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /ezdb-treemap/src/main/java/ezdb/treemap/object/table/range/EzObjectTreeMapDbRangeComparator.java: -------------------------------------------------------------------------------- 1 | package ezdb.treemap.object.table.range; 2 | 3 | import java.util.Comparator; 4 | 5 | import ezdb.util.ObjectRangeTableKey; 6 | import ezdb.util.Util; 7 | 8 | public class EzObjectTreeMapDbRangeComparator implements Comparator> { 9 | 10 | private final Comparator hashKeyComparator; 11 | private final Comparator rangeKeyComparator; 12 | 13 | public EzObjectTreeMapDbRangeComparator(final Comparator hashKeyComparator, final Comparator rangeKeyComparator) { 14 | this.hashKeyComparator = hashKeyComparator; 15 | this.rangeKeyComparator = rangeKeyComparator; 16 | } 17 | 18 | @Override 19 | public int compare(final ObjectRangeTableKey a, final ObjectRangeTableKey b) { 20 | return Util.compareKeys(hashKeyComparator, rangeKeyComparator, a, b); 21 | } 22 | 23 | } 24 | --------------------------------------------------------------------------------