Merge pull request #1053 from adamretter/benchmark-java-comparator
Benchmark Java comparator vs C++ comparatormain
parent
f2c43a4a27
commit
200654067a
@ -0,0 +1,91 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under the BSD-style license found in the
|
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
// of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
|
||||||
|
package org.rocksdb.util; |
||||||
|
|
||||||
|
import org.rocksdb.*; |
||||||
|
|
||||||
|
import java.nio.ByteBuffer; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is a Java Native implementation of the C++ |
||||||
|
* equivalent BytewiseComparatorImpl using {@link Slice} |
||||||
|
* |
||||||
|
* The performance of Comparators implemented in Java is always |
||||||
|
* less than their C++ counterparts due to the bridging overhead, |
||||||
|
* as such you likely don't want to use this apart from benchmarking |
||||||
|
* and you most likely instead wanted |
||||||
|
* {@link org.rocksdb.BuiltinComparator#BYTEWISE_COMPARATOR} |
||||||
|
*/ |
||||||
|
public class BytewiseComparator extends Comparator { |
||||||
|
|
||||||
|
public BytewiseComparator(final ComparatorOptions copt) { |
||||||
|
super(copt); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String name() { |
||||||
|
return "rocksdb.java.BytewiseComparator"; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int compare(final Slice a, final Slice b) { |
||||||
|
return compare(a.data(), b.data()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String findShortestSeparator(final String start, |
||||||
|
final Slice limit) { |
||||||
|
final byte[] startBytes = start.getBytes(); |
||||||
|
final byte[] limitBytes = limit.data(); |
||||||
|
|
||||||
|
// Find length of common prefix
|
||||||
|
final int min_length = Math.min(startBytes.length, limit.size()); |
||||||
|
int diff_index = 0; |
||||||
|
while ((diff_index < min_length) && |
||||||
|
(startBytes[diff_index] == limitBytes[diff_index])) { |
||||||
|
diff_index++; |
||||||
|
} |
||||||
|
|
||||||
|
if (diff_index >= min_length) { |
||||||
|
// Do not shorten if one string is a prefix of the other
|
||||||
|
} else { |
||||||
|
final byte diff_byte = startBytes[diff_index]; |
||||||
|
if(diff_byte < 0xff && diff_byte + 1 < limitBytes[diff_index]) { |
||||||
|
final byte shortest[] = new byte[diff_index + 1]; |
||||||
|
System.arraycopy(startBytes, 0, shortest, 0, diff_index + 1); |
||||||
|
shortest[diff_index]++; |
||||||
|
assert(compare(shortest, limitBytes) < 0); |
||||||
|
return new String(shortest); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
private static int compare(final byte[] a, final byte[] b) { |
||||||
|
return ByteBuffer.wrap(a).compareTo(ByteBuffer.wrap(b)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String findShortSuccessor(final String key) { |
||||||
|
final byte[] keyBytes = key.getBytes(); |
||||||
|
|
||||||
|
// Find first character that can be incremented
|
||||||
|
final int n = keyBytes.length; |
||||||
|
for (int i = 0; i < n; i++) { |
||||||
|
final byte byt = keyBytes[i]; |
||||||
|
if (byt != 0xff) { |
||||||
|
final byte shortSuccessor[] = new byte[i + 1]; |
||||||
|
System.arraycopy(keyBytes, 0, shortSuccessor, 0, i + 1); |
||||||
|
shortSuccessor[i]++; |
||||||
|
return new String(shortSuccessor); |
||||||
|
} |
||||||
|
} |
||||||
|
// *key is a run of 0xffs. Leave it alone.
|
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,88 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under the BSD-style license found in the
|
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
// of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
|
||||||
|
package org.rocksdb.util; |
||||||
|
|
||||||
|
import org.rocksdb.ComparatorOptions; |
||||||
|
import org.rocksdb.DirectComparator; |
||||||
|
import org.rocksdb.DirectSlice; |
||||||
|
|
||||||
|
import java.nio.ByteBuffer; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is a Java Native implementation of the C++ |
||||||
|
* equivalent BytewiseComparatorImpl using {@link DirectSlice} |
||||||
|
* |
||||||
|
* The performance of Comparators implemented in Java is always |
||||||
|
* less than their C++ counterparts due to the bridging overhead, |
||||||
|
* as such you likely don't want to use this apart from benchmarking |
||||||
|
* and you most likely instead wanted |
||||||
|
* {@link org.rocksdb.BuiltinComparator#BYTEWISE_COMPARATOR} |
||||||
|
*/ |
||||||
|
public class DirectBytewiseComparator extends DirectComparator { |
||||||
|
|
||||||
|
public DirectBytewiseComparator(final ComparatorOptions copt) { |
||||||
|
super(copt); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String name() { |
||||||
|
return "rocksdb.java.DirectBytewiseComparator"; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int compare(final DirectSlice a, final DirectSlice b) { |
||||||
|
return a.data().compareTo(b.data()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String findShortestSeparator(final String start, |
||||||
|
final DirectSlice limit) { |
||||||
|
final byte[] startBytes = start.getBytes(); |
||||||
|
|
||||||
|
// Find length of common prefix
|
||||||
|
final int min_length = Math.min(startBytes.length, limit.size()); |
||||||
|
int diff_index = 0; |
||||||
|
while ((diff_index < min_length) && |
||||||
|
(startBytes[diff_index] == limit.get(diff_index))) { |
||||||
|
diff_index++; |
||||||
|
} |
||||||
|
|
||||||
|
if (diff_index >= min_length) { |
||||||
|
// Do not shorten if one string is a prefix of the other
|
||||||
|
} else { |
||||||
|
final byte diff_byte = startBytes[diff_index]; |
||||||
|
if(diff_byte < 0xff && diff_byte + 1 < limit.get(diff_index)) { |
||||||
|
final byte shortest[] = new byte[diff_index + 1]; |
||||||
|
System.arraycopy(startBytes, 0, shortest, 0, diff_index + 1); |
||||||
|
shortest[diff_index]++; |
||||||
|
assert(ByteBuffer.wrap(shortest).compareTo(limit.data()) < 0); |
||||||
|
return new String(shortest); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String findShortSuccessor(final String key) { |
||||||
|
final byte[] keyBytes = key.getBytes(); |
||||||
|
|
||||||
|
// Find first character that can be incremented
|
||||||
|
final int n = keyBytes.length; |
||||||
|
for (int i = 0; i < n; i++) { |
||||||
|
final byte byt = keyBytes[i]; |
||||||
|
if (byt != 0xff) { |
||||||
|
final byte shortSuccessor[] = new byte[i + 1]; |
||||||
|
System.arraycopy(keyBytes, 0, shortSuccessor, 0, i + 1); |
||||||
|
shortSuccessor[i]++; |
||||||
|
return new String(shortSuccessor); |
||||||
|
} |
||||||
|
} |
||||||
|
// *key is a run of 0xffs. Leave it alone.
|
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under the BSD-style license found in the
|
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
// of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
|
||||||
|
package org.rocksdb.util; |
||||||
|
|
||||||
|
import org.rocksdb.BuiltinComparator; |
||||||
|
import org.rocksdb.ComparatorOptions; |
||||||
|
import org.rocksdb.Slice; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is a Java Native implementation of the C++ |
||||||
|
* equivalent ReverseBytewiseComparatorImpl using {@link Slice} |
||||||
|
* |
||||||
|
* The performance of Comparators implemented in Java is always |
||||||
|
* less than their C++ counterparts due to the bridging overhead, |
||||||
|
* as such you likely don't want to use this apart from benchmarking |
||||||
|
* and you most likely instead wanted |
||||||
|
* {@link BuiltinComparator#REVERSE_BYTEWISE_COMPARATOR} |
||||||
|
*/ |
||||||
|
public class ReverseBytewiseComparator extends BytewiseComparator { |
||||||
|
|
||||||
|
public ReverseBytewiseComparator(final ComparatorOptions copt) { |
||||||
|
super(copt); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String name() { |
||||||
|
return "rocksdb.java.ReverseBytewiseComparator"; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int compare(final Slice a, final Slice b) { |
||||||
|
return -super.compare(a, b); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,480 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under the BSD-style license found in the
|
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
// of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
|
||||||
|
package org.rocksdb.util; |
||||||
|
|
||||||
|
import org.junit.Test; |
||||||
|
import org.rocksdb.*; |
||||||
|
import org.rocksdb.Comparator; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
import java.nio.file.FileVisitResult; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.nio.file.SimpleFileVisitor; |
||||||
|
import java.nio.file.attribute.BasicFileAttributes; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import static org.junit.Assert.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is a direct port of various C++ |
||||||
|
* tests from db/comparator_db_test.cc |
||||||
|
* and some code to adapt it to RocksJava |
||||||
|
*/ |
||||||
|
public class BytewiseComparatorTest { |
||||||
|
|
||||||
|
/** |
||||||
|
* Open the database using the C++ BytewiseComparatorImpl |
||||||
|
* and test the results against our Java BytewiseComparator |
||||||
|
*/ |
||||||
|
@Test |
||||||
|
public void java_vs_cpp_bytewiseComparator() |
||||||
|
throws IOException, RocksDBException { |
||||||
|
for(int rand_seed = 301; rand_seed < 306; rand_seed++) { |
||||||
|
final Path dbDir = Files.createTempDirectory("comparator_db_test"); |
||||||
|
try(final RocksDB db = openDatabase(dbDir, |
||||||
|
BuiltinComparator.BYTEWISE_COMPARATOR)) { |
||||||
|
final Random rnd = new Random(rand_seed); |
||||||
|
doRandomIterationTest( |
||||||
|
db, |
||||||
|
toJavaComparator(new BytewiseComparator(new ComparatorOptions())), |
||||||
|
Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i"), |
||||||
|
rnd, |
||||||
|
8, 100, 3 |
||||||
|
); |
||||||
|
} finally { |
||||||
|
removeData(dbDir); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Open the database using the Java BytewiseComparator |
||||||
|
* and test the results against another Java BytewiseComparator |
||||||
|
*/ |
||||||
|
@Test |
||||||
|
public void java_vs_java_bytewiseComparator() |
||||||
|
throws IOException, RocksDBException { |
||||||
|
for(int rand_seed = 301; rand_seed < 306; rand_seed++) { |
||||||
|
final Path dbDir = Files.createTempDirectory("comparator_db_test"); |
||||||
|
try(final RocksDB db = openDatabase(dbDir, new BytewiseComparator( |
||||||
|
new ComparatorOptions()))) { |
||||||
|
final Random rnd = new Random(rand_seed); |
||||||
|
doRandomIterationTest( |
||||||
|
db, |
||||||
|
toJavaComparator(new BytewiseComparator(new ComparatorOptions())), |
||||||
|
Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i"), |
||||||
|
rnd, |
||||||
|
8, 100, 3 |
||||||
|
); |
||||||
|
} finally { |
||||||
|
removeData(dbDir); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Open the database using the C++ BytewiseComparatorImpl |
||||||
|
* and test the results against our Java DirectBytewiseComparator |
||||||
|
*/ |
||||||
|
@Test |
||||||
|
public void java_vs_cpp_directBytewiseComparator() |
||||||
|
throws IOException, RocksDBException { |
||||||
|
for(int rand_seed = 301; rand_seed < 306; rand_seed++) { |
||||||
|
final Path dbDir = Files.createTempDirectory("comparator_db_test"); |
||||||
|
try(final RocksDB db = openDatabase(dbDir, |
||||||
|
BuiltinComparator.BYTEWISE_COMPARATOR)) { |
||||||
|
final Random rnd = new Random(rand_seed); |
||||||
|
doRandomIterationTest( |
||||||
|
db, |
||||||
|
toJavaComparator(new DirectBytewiseComparator( |
||||||
|
new ComparatorOptions()) |
||||||
|
), |
||||||
|
Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i"), |
||||||
|
rnd, |
||||||
|
8, 100, 3 |
||||||
|
); |
||||||
|
} finally { |
||||||
|
removeData(dbDir); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Open the database using the Java DirectBytewiseComparator |
||||||
|
* and test the results against another Java DirectBytewiseComparator |
||||||
|
*/ |
||||||
|
@Test |
||||||
|
public void java_vs_java_directBytewiseComparator() |
||||||
|
throws IOException, RocksDBException { |
||||||
|
for(int rand_seed = 301; rand_seed < 306; rand_seed++) { |
||||||
|
final Path dbDir = Files.createTempDirectory("comparator_db_test"); |
||||||
|
try(final RocksDB db = openDatabase(dbDir, new DirectBytewiseComparator( |
||||||
|
new ComparatorOptions()))) { |
||||||
|
final Random rnd = new Random(rand_seed); |
||||||
|
doRandomIterationTest( |
||||||
|
db, |
||||||
|
toJavaComparator(new DirectBytewiseComparator( |
||||||
|
new ComparatorOptions()) |
||||||
|
), |
||||||
|
Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i"), |
||||||
|
rnd, |
||||||
|
8, 100, 3 |
||||||
|
); |
||||||
|
} finally { |
||||||
|
removeData(dbDir); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Open the database using the C++ ReverseBytewiseComparatorImpl |
||||||
|
* and test the results against our Java ReverseBytewiseComparator |
||||||
|
*/ |
||||||
|
@Test |
||||||
|
public void java_vs_cpp_reverseBytewiseComparator() |
||||||
|
throws IOException, RocksDBException { |
||||||
|
for(int rand_seed = 301; rand_seed < 306; rand_seed++) { |
||||||
|
final Path dbDir = Files.createTempDirectory("comparator_db_test"); |
||||||
|
try(final RocksDB db = openDatabase(dbDir, |
||||||
|
BuiltinComparator.REVERSE_BYTEWISE_COMPARATOR)) { |
||||||
|
final Random rnd = new Random(rand_seed); |
||||||
|
doRandomIterationTest( |
||||||
|
db, |
||||||
|
toJavaComparator( |
||||||
|
new ReverseBytewiseComparator(new ComparatorOptions()) |
||||||
|
), |
||||||
|
Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i"), |
||||||
|
rnd, |
||||||
|
8, 100, 3 |
||||||
|
); |
||||||
|
} finally { |
||||||
|
removeData(dbDir); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Open the database using the Java ReverseBytewiseComparator |
||||||
|
* and test the results against another Java ReverseBytewiseComparator |
||||||
|
*/ |
||||||
|
@Test |
||||||
|
public void java_vs_java_reverseBytewiseComparator() |
||||||
|
throws IOException, RocksDBException { |
||||||
|
|
||||||
|
for(int rand_seed = 301; rand_seed < 306; rand_seed++) { |
||||||
|
final Path dbDir = Files.createTempDirectory("comparator_db_test"); |
||||||
|
try(final RocksDB db = openDatabase(dbDir, new ReverseBytewiseComparator( |
||||||
|
new ComparatorOptions()))) { |
||||||
|
final Random rnd = new Random(rand_seed); |
||||||
|
doRandomIterationTest( |
||||||
|
db, |
||||||
|
toJavaComparator( |
||||||
|
new ReverseBytewiseComparator(new ComparatorOptions()) |
||||||
|
), |
||||||
|
Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i"), |
||||||
|
rnd, |
||||||
|
8, 100, 3 |
||||||
|
); |
||||||
|
} finally { |
||||||
|
removeData(dbDir); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void doRandomIterationTest( |
||||||
|
final RocksDB db, final java.util.Comparator<String> javaComparator, |
||||||
|
final List<String> source_strings, final Random rnd, |
||||||
|
final int num_writes, final int num_iter_ops, |
||||||
|
final int num_trigger_flush) throws RocksDBException { |
||||||
|
|
||||||
|
final TreeMap<String, String> map = new TreeMap<>(javaComparator); |
||||||
|
|
||||||
|
for (int i = 0; i < num_writes; i++) { |
||||||
|
if (num_trigger_flush > 0 && i != 0 && i % num_trigger_flush == 0) { |
||||||
|
db.flush(new FlushOptions()); |
||||||
|
} |
||||||
|
|
||||||
|
final int type = rnd.nextInt(2); |
||||||
|
final int index = rnd.nextInt(source_strings.size()); |
||||||
|
final String key = source_strings.get(index); |
||||||
|
switch (type) { |
||||||
|
case 0: |
||||||
|
// put
|
||||||
|
map.put(key, key); |
||||||
|
db.put(new WriteOptions(), bytes(key), bytes(key)); |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
// delete
|
||||||
|
if (map.containsKey(key)) { |
||||||
|
map.remove(key); |
||||||
|
} |
||||||
|
db.remove(new WriteOptions(), bytes(key)); |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
fail("Should not be able to generate random outside range 1..2"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
try(final RocksIterator iter = db.newIterator(new ReadOptions())) { |
||||||
|
final KVIter<String, String> result_iter = new KVIter(map); |
||||||
|
|
||||||
|
boolean is_valid = false; |
||||||
|
for (int i = 0; i < num_iter_ops; i++) { |
||||||
|
// Random walk and make sure iter and result_iter returns the
|
||||||
|
// same key and value
|
||||||
|
final int type = rnd.nextInt(6); |
||||||
|
iter.status(); |
||||||
|
switch (type) { |
||||||
|
case 0: |
||||||
|
// Seek to First
|
||||||
|
iter.seekToFirst(); |
||||||
|
result_iter.seekToFirst(); |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
// Seek to last
|
||||||
|
iter.seekToLast(); |
||||||
|
result_iter.seekToLast(); |
||||||
|
break; |
||||||
|
case 2: { |
||||||
|
// Seek to random key
|
||||||
|
final int key_idx = rnd.nextInt(source_strings.size()); |
||||||
|
final String key = source_strings.get(key_idx); |
||||||
|
iter.seek(bytes(key)); |
||||||
|
result_iter.seek(bytes(key)); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 3: |
||||||
|
// Next
|
||||||
|
if (is_valid) { |
||||||
|
iter.next(); |
||||||
|
result_iter.next(); |
||||||
|
} else { |
||||||
|
continue; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 4: |
||||||
|
// Prev
|
||||||
|
if (is_valid) { |
||||||
|
iter.prev(); |
||||||
|
result_iter.prev(); |
||||||
|
} else { |
||||||
|
continue; |
||||||
|
} |
||||||
|
break; |
||||||
|
default: { |
||||||
|
assert (type == 5); |
||||||
|
final int key_idx = rnd.nextInt(source_strings.size()); |
||||||
|
final String key = source_strings.get(key_idx); |
||||||
|
final byte[] result = db.get(new ReadOptions(), bytes(key)); |
||||||
|
if (!map.containsKey(key)) { |
||||||
|
assertNull(result); |
||||||
|
} else { |
||||||
|
assertArrayEquals(bytes(map.get(key)), result); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals(result_iter.isValid(), iter.isValid()); |
||||||
|
|
||||||
|
is_valid = iter.isValid(); |
||||||
|
|
||||||
|
if (is_valid) { |
||||||
|
assertArrayEquals(bytes(result_iter.key()), iter.key()); |
||||||
|
|
||||||
|
//note that calling value on a non-valid iterator from the Java API
|
||||||
|
//results in a SIGSEGV
|
||||||
|
assertArrayEquals(bytes(result_iter.value()), iter.value()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Open the database using a C++ Comparator |
||||||
|
*/ |
||||||
|
private RocksDB openDatabase( |
||||||
|
final Path dbDir, final BuiltinComparator cppComparator) |
||||||
|
throws IOException, RocksDBException { |
||||||
|
final Options options = new Options() |
||||||
|
.setCreateIfMissing(true) |
||||||
|
.setComparator(cppComparator); |
||||||
|
return RocksDB.open(options, dbDir.toAbsolutePath().toString()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Open the database using a Java Comparator |
||||||
|
*/ |
||||||
|
private RocksDB openDatabase( |
||||||
|
final Path dbDir, |
||||||
|
final AbstractComparator<? extends AbstractSlice<?>> javaComparator) |
||||||
|
throws IOException, RocksDBException { |
||||||
|
final Options options = new Options() |
||||||
|
.setCreateIfMissing(true) |
||||||
|
.setComparator(javaComparator); |
||||||
|
return RocksDB.open(options, dbDir.toAbsolutePath().toString()); |
||||||
|
} |
||||||
|
|
||||||
|
private void closeDatabase(final RocksDB db) { |
||||||
|
db.close(); |
||||||
|
} |
||||||
|
|
||||||
|
private void removeData(final Path dbDir) throws IOException { |
||||||
|
Files.walkFileTree(dbDir, new SimpleFileVisitor<Path>() { |
||||||
|
@Override |
||||||
|
public FileVisitResult visitFile( |
||||||
|
final Path file, final BasicFileAttributes attrs) |
||||||
|
throws IOException { |
||||||
|
Files.delete(file); |
||||||
|
return FileVisitResult.CONTINUE; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public FileVisitResult postVisitDirectory( |
||||||
|
final Path dir, final IOException exc) throws IOException { |
||||||
|
Files.delete(dir); |
||||||
|
return FileVisitResult.CONTINUE; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private byte[] bytes(final String s) { |
||||||
|
return s.getBytes(StandardCharsets.UTF_8); |
||||||
|
} |
||||||
|
|
||||||
|
private java.util.Comparator<String> toJavaComparator( |
||||||
|
final Comparator rocksComparator) { |
||||||
|
return new java.util.Comparator<String>() { |
||||||
|
@Override |
||||||
|
public int compare(final String s1, final String s2) { |
||||||
|
return rocksComparator.compare(new Slice(s1), new Slice(s2)); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
private java.util.Comparator<String> toJavaComparator( |
||||||
|
final DirectComparator rocksComparator) { |
||||||
|
return new java.util.Comparator<String>() { |
||||||
|
@Override |
||||||
|
public int compare(final String s1, final String s2) { |
||||||
|
return rocksComparator.compare(new DirectSlice(s1), |
||||||
|
new DirectSlice(s2)); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
private class KVIter<K, V> implements RocksIteratorInterface { |
||||||
|
|
||||||
|
private final List<Map.Entry<K, V>> entries; |
||||||
|
private final java.util.Comparator<? super K> comparator; |
||||||
|
private int offset = -1; |
||||||
|
|
||||||
|
private int lastPrefixMatchIdx = -1; |
||||||
|
private int lastPrefixMatch = 0; |
||||||
|
|
||||||
|
public KVIter(final TreeMap<K, V> map) { |
||||||
|
this.entries = new ArrayList<>(); |
||||||
|
final Iterator<Map.Entry<K, V>> iterator = map.entrySet().iterator(); |
||||||
|
while(iterator.hasNext()) { |
||||||
|
entries.add(iterator.next()); |
||||||
|
} |
||||||
|
this.comparator = map.comparator(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isValid() { |
||||||
|
return offset > -1 && offset < entries.size(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void seekToFirst() { |
||||||
|
offset = 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void seekToLast() { |
||||||
|
offset = entries.size() - 1; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void seek(final byte[] target) { |
||||||
|
for(offset = 0; offset < entries.size(); offset++) { |
||||||
|
if(comparator.compare(entries.get(offset).getKey(), |
||||||
|
(K)new String(target, StandardCharsets.UTF_8)) >= 0) { |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Is `a` a prefix of `b` |
||||||
|
* |
||||||
|
* @return The length of the matching prefix, or 0 if it is not a prefix |
||||||
|
*/ |
||||||
|
private int isPrefix(final byte[] a, final byte[] b) { |
||||||
|
if(b.length >= a.length) { |
||||||
|
for(int i = 0; i < a.length; i++) { |
||||||
|
if(a[i] != b[i]) { |
||||||
|
return i; |
||||||
|
} |
||||||
|
} |
||||||
|
return a.length; |
||||||
|
} else { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void next() { |
||||||
|
if(offset < entries.size()) { |
||||||
|
offset++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void prev() { |
||||||
|
if(offset >= 0) { |
||||||
|
offset--; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void status() throws RocksDBException { |
||||||
|
if(offset < 0 || offset >= entries.size()) { |
||||||
|
throw new RocksDBException("Index out of bounds. Size is: " + |
||||||
|
entries.size() + ", offset is: " + offset); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public K key() { |
||||||
|
if(!isValid()) { |
||||||
|
if(entries.isEmpty()) { |
||||||
|
return (K)""; |
||||||
|
} else if(offset == -1){ |
||||||
|
return entries.get(0).getKey(); |
||||||
|
} else if(offset == entries.size()) { |
||||||
|
return entries.get(offset - 1).getKey(); |
||||||
|
} else { |
||||||
|
return (K)""; |
||||||
|
} |
||||||
|
} else { |
||||||
|
return entries.get(offset).getKey(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public V value() { |
||||||
|
if(!isValid()) { |
||||||
|
return (V)""; |
||||||
|
} else { |
||||||
|
return entries.get(offset).getValue(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue