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 extends TableRow> 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