diff --git a/pom.xml b/pom.xml
index ef5d00b..6471512 100644
--- a/pom.xml
+++ b/pom.xml
@@ -101,7 +101,7 @@
-
+
diff --git a/src/main/java/com/intel/pmem/llpl/AnyHeap.java b/src/main/java/com/intel/pmem/llpl/AnyHeap.java
index 243e029..ae47ecb 100644
--- a/src/main/java/com/intel/pmem/llpl/AnyHeap.java
+++ b/src/main/java/com/intel/pmem/llpl/AnyHeap.java
@@ -10,7 +10,7 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.SortedMap;
-import java.util.TreeMap;
+import java.util.concurrent.ConcurrentSkipListMap;
import java.util.Arrays;
import java.util.MissingResourceException;
import java.io.File;
@@ -97,7 +97,7 @@ public abstract class AnyHeap {
AnyHeap(String path, long requestedSize) {
this.path = path;
- userSizes = new TreeMap();
+ userSizes = new ConcurrentSkipListMap();
allocationClasses = new long[TOTAL_ALLOCATION_CLASSES];
poolHandle = nativeCreateHeap(path, requestedSize, allocationClasses, this.getHeapLayoutID());
if (poolHandle == 0) throw new HeapException("Failed to create heap.");
@@ -109,7 +109,7 @@ public abstract class AnyHeap {
AnyHeap(String path) {
this.path = path;
- userSizes = new TreeMap();
+ userSizes = new ConcurrentSkipListMap();
allocationClasses = new long[TOTAL_ALLOCATION_CLASSES];
poolHandle = nativeOpenHeap(path, allocationClasses, this.getHeapLayoutID());
if (poolHandle == 0) throw new HeapException("Failed to open heap.");
@@ -191,17 +191,19 @@ void close() {
* @return true if size was successfully registered
* @throws HeapException if the allocation size could not be registered
*/
- public synchronized boolean registerAllocationSize(long size, boolean compact) {
+ public boolean registerAllocationSize(long size, boolean compact) {
if (userSizes.size() == MAX_USER_CLASSES) throw new HeapException("Max number of allocation sizes reached.");
if (size <= 0) throw new IllegalArgumentException();
long effectiveSize;
if (!userSizes.containsKey(effectiveSize = (size + (compact ? 0L : Long.BYTES)))) {
int id = nativeRegisterAllocationClass(poolHandle, effectiveSize);
if (id != -1) {
- int i = USER_CLASS_INDEX + (userSizes.size() * 2);
userSizes.put(effectiveSize, id);
- allocationClasses[i++] = effectiveSize;
- allocationClasses[i] = id;
+ return true;
+ }
+ int index = (int)((effectiveSize / 8) - 1);
+ if (effectiveSize % 8 == 0 && effectiveSize < 128 && allocationClasses[index] > 0) {
+ userSizes.put(effectiveSize, (int)allocationClasses[index]);
return true;
}
return false;
diff --git a/src/main/java/com/intel/pmem/llpl/util/LongART.java b/src/main/java/com/intel/pmem/llpl/util/LongART.java
index 593623f..4e19b25 100755
--- a/src/main/java/com/intel/pmem/llpl/util/LongART.java
+++ b/src/main/java/com/intel/pmem/llpl/util/LongART.java
@@ -158,6 +158,7 @@ private void setMaxKeyLength(int length) {
public byte[] firstKey() {
Node n = root.getChild();
if (n == null) throw new NoSuchElementException();
+ if (n.isLeaf()) return n.getPrefix();
ByteBuffer firstKey = ByteBuffer.allocate(maxKeyLen);
SearchHelper h = (Node parent, Node child, Byte radix, Consumer cleanerFunction) -> {
byte[] ba = parent.getPrefix();
@@ -177,6 +178,7 @@ public byte[] firstKey() {
public byte[] lastKey() {
Node n = root.getChild();
if (n == null) throw new NoSuchElementException();
+ if (n.isLeaf()) return n.getPrefix();
ByteBuffer lastKey = ByteBuffer.allocate(maxKeyLen);
SearchHelper h = (Node parent, Node child, Byte radix, Consumer cleanerFunction) -> {
byte[] ba = parent.getPrefix();
@@ -467,7 +469,6 @@ void mergeTree(LongART tree) {
}
}
}
-
}
@SuppressWarnings("unchecked")
@@ -477,7 +478,7 @@ private long search2(Node node, byte[] key, int depth, SearchHelper helper, Cons
}
Node next;
int matchedLength = (key.length == 0) ? node.getPrefixLength() : node.checkPrefix(key, depth);
- if (matchedLength != node.getPrefixLength()) {
+ if (matchedLength != node.getPrefixLength() && !node.isLeaf()) {
return -1;
}
if (node.isLeaf())
@@ -569,6 +570,7 @@ public void clear(Consumer cleanerFunction) {
if (cleanerFunction == null) throw new IllegalArgumentException("cleaner function cannot be null");
heap.execute(() -> {
root.destroy(cleanerFunction);
+ root.setCount(0);
count = 0;
});
}
@@ -607,7 +609,20 @@ else if (((InternalNode)child).getChildrenCount() == 0) {
public long remove(byte[] key, Consumer cleanerFunction) {
if (cleanerFunction == null) throw new NullPointerException("cleaner function cannot be null");
if (key.length == 0) throw new IllegalArgumentException("Invalid key");
- return search(root.getChild(), key, 0, this::deleteNodes , cleanerFunction);
+ Node n = root.getChild();
+ if (n != null && n.isLeaf() && Arrays.equals(key, n.getPrefix())) {
+ heap.execute(() -> {
+ root.destroy(cleanerFunction);
+ root.setCount(0);
+ count = 0;
+ });
+ return ((SimpleLeaf)n).getValue();
+ }
+ else {
+ long ret = search(n, key, 0, this::deleteNodes , cleanerFunction);
+ if (n != null && !n.isLeaf() && ((InternalNode)n).getChildrenCount() == 0) root.deleteChild();
+ return ret;
+ }
}
/**
@@ -660,7 +675,21 @@ public Iterator getReverseEntryIterator(byte[] firstKey, boolean
*/
public Iterator getHeadEntryIterator(byte[] lastKey, boolean lastInclusive) {
if (lastKey == null || lastKey.length == 0) throw new IllegalArgumentException();
- return new EntryIterator(lastKey, lastInclusive);
+ return new EntryIterator(lastKey, lastInclusive, false);
+ }
+
+ /**
+ * Returns a descending-order iterator over entries in this radix tree;
+ * the iterator will include entries whose keys are lower than (or equal to,
+ * if {@code firstInclusive} is true) {@code firstKey}.
+ * @param firstKey high endpoint of the keys in the returned iterator
+ * @param firstInclusive true if the highest key is to be included in the returned iterator
+ * @return the iterator
+ * @throws IllegalArgumentException if lastKey has zero length
+ */
+ public Iterator getReverseHeadEntryIterator(byte[] firstKey, boolean firstInclusive) {
+ if (firstKey == null || firstKey.length == 0) throw new IllegalArgumentException();
+ return new EntryIterator(firstKey, firstInclusive, null, false, true);
}
/**
@@ -670,13 +699,27 @@ public Iterator getHeadEntryIterator(byte[] lastKey, boolean lastInclusiv
* @param firstKey low endpoint of the keys in the returned iterator
* @param firstInclusive true if the lowest key is to be included in the returned iterator
* @return the iterator
- * @throws IllegalArgumentException if lastKey has zero length
+ * @throws IllegalArgumentException if firstKey has zero length
*/
public Iterator getTailEntryIterator(byte[] firstKey, boolean firstInclusive) {
if (firstKey == null || firstKey.length == 0) throw new IllegalArgumentException();
return new EntryIterator(firstKey, firstInclusive, null, false, false);
}
+ /**
+ * Returns a descending-order iterator over entries in this radix tree;
+ * the iterator will include entries whose keys are higher than (or equal to,
+ * if {@code lastInclusive} is true) {@code lastKey}.
+ * @param lastKey low endpoint of the keys in the returned iterator
+ * @param lastInclusive true if the lowest key is to be included in the returned iterator
+ * @return the iterator
+ * @throws IllegalArgumentException if lastKey has zero length
+ */
+ public Iterator getReverseTailEntryIterator(byte[] lastKey, boolean lastInclusive) {
+ if (lastKey == null || lastKey.length == 0) throw new IllegalArgumentException();
+ return new EntryIterator(lastKey, lastInclusive, true);
+ }
+
/**
* Returns an ascending-order iterator over entries in this radix tree;
* the iterator will include entries whose keys range from {@code firstKey} to {@code lastKey}.
@@ -713,7 +756,7 @@ public StackItem(NodeEntry[] entries, int prefixLen, Byte radix, boolean hasBlan
this.hasBlank=hasBlank;
this.reversed = reversed;
if (radix != entries[0].radix) this.index = calcIndex(radix);
- if (reversed && index == entries.length) index = entries.length - 1;
+ if (index == entries.length) index = entries.length - 1;
}
int calcIndex(byte radix) {
@@ -812,7 +855,6 @@ public EntryIterator(boolean reversed) {
void buildCache(Node parent, Node child, Byte radix, Consumer cleanerFunction) {
StackItem item;
- if (reversed && child == null) return;
NodeEntry[] entries = ((InternalNode)parent).getEntries();
if (radix == null && child != null) item = new StackItem(entries, parent.getPrefixLength(), reversed);
else if (child == null) {
@@ -827,37 +869,39 @@ else if (child == null) {
if (radix != null && child != null && !child.isLeaf()) keyBuf.put(radix);
}
- public EntryIterator(byte[] lastKey, boolean lastInclusive) {
- this();
+ public EntryIterator(byte[] lastKey, boolean lastInclusive, boolean reversed) {
+ this(reversed);
this.lastKey = lastKey;
this.lastInclusive = lastInclusive;
}
public EntryIterator(byte[] firstKey, boolean firstInclusive, byte[] lastKey, boolean lastInclusive, boolean reversed) {
cache = new ArrayDeque<>();
- this.reversed = reversed;
Node first = root.getChild();
// lastKey can be null, firstkey is never null as it is checked before calling this
if (lastKey != null) {
int comp = keyCompare(firstKey, lastKey);
if (comp == 0) {
- long val = get(firstKey);
- next = (val == 0) ? null : new Entry(firstKey, val);
+ if (firstInclusive && lastInclusive) {
+ long val = get(firstKey);
+ next = (val == 0) ? null : new Entry(firstKey, val);
+ }
return;
}
- if (!reversed && comp > 0) throw new IllegalArgumentException();
- if (reversed && comp < 0) throw new IllegalArgumentException();
- }
- if (reversed) {
- this.lastKey = firstKey;
- firstKey = lastKey;
- this.lastInclusive = firstInclusive;
- firstInclusive = lastInclusive;
- }
- else {
- this.lastKey = lastKey;
- this.lastInclusive = lastInclusive;
+ if (comp > 0) throw new IllegalArgumentException();
+ if (reversed) {
+ this.lastKey = firstKey;
+ firstKey = lastKey;
+ this.lastInclusive = firstInclusive;
+ firstInclusive = lastInclusive;
+ }
+ else {
+ this.lastKey = lastKey;
+ this.lastInclusive = lastInclusive;
+ }
}
+ this.reversed = reversed;
+
if (first != null) {
if (first.isLeaf()) {
SimpleLeaf leaf = (SimpleLeaf)first;
@@ -947,7 +991,10 @@ void advanceTo(byte[] firstKey, boolean inclusive) {
if ((inclusive && x == 0) || x < 0) break;
while (cursor.isDone() && cache.size() > 0) {
pop();
- if (cursor == null) break;
+ if (cursor == null) {
+ if (keyBuf.position() == 0) next = null;
+ break;
+ }
}
}
}
diff --git a/src/test/java/com/intel/pmem/llpl/LongARTTests.java b/src/test/java/com/intel/pmem/llpl/LongARTTests.java
index 6be8f46..a60347d 100644
--- a/src/test/java/com/intel/pmem/llpl/LongARTTests.java
+++ b/src/test/java/com/intel/pmem/llpl/LongARTTests.java
@@ -16,6 +16,7 @@
import org.testng.Assert;
import java.util.NoSuchElementException;
import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
import java.util.Map;
@@ -28,6 +29,9 @@ public class LongARTTests {
static byte[] firstKey;
static byte[] lastKey;
static byte[] prefixKey;
+ static byte[] fixedLengthFirstKey;
+ static byte[] fixedLengthLastKey;
+ static byte[] fixedLengthPrefixKey;
static long firstValue;
static long lastValue;
static long prefixValue;
@@ -41,6 +45,11 @@ public void initialize() {
firstKey = new byte[8]; Arrays.fill(firstKey, (byte)0);
lastKey = new byte[20]; Arrays.fill(lastKey, (byte)0xff);
prefixKey = Arrays.copyOfRange(lastKey, 0, 12);
+ fixedLengthPrefixKey = new byte[10]; rnd.nextBytes(fixedLengthPrefixKey);
+ fixedLengthFirstKey = new byte[10]; Arrays.fill(fixedLengthFirstKey, (byte)0);
+ System.arraycopy(fixedLengthPrefixKey, 0, fixedLengthFirstKey, 0, 6);
+ fixedLengthLastKey = new byte[10]; Arrays.fill(fixedLengthLastKey, (byte)0xff);
+ System.arraycopy(prefixKey, 0, fixedLengthLastKey, 0, 6);
firstValue = 0x1234L << 8 | (byte)1;
lastValue = 0x1234L << 8 | (byte)2;
prefixValue = 0x1234L << 8 | (byte)3;
@@ -92,19 +101,33 @@ static void fill (LongART tree, ConcurrentSkipListMap control) {
tree.put(firstKey, firstValue);
tree.put(lastKey, lastValue);
tree.put(prefixKey, prefixValue);
+ tree.put(fixedLengthFirstKey, firstValue);
+ tree.put(fixedLengthLastKey, lastValue);
+ tree.put(fixedLengthPrefixKey, prefixValue);
control.put(new KeyBytes(firstKey), firstValue);
control.put(new KeyBytes(lastKey), lastValue);
control.put(new KeyBytes(prefixKey), prefixValue);
+ control.put(new KeyBytes(fixedLengthFirstKey), firstValue);
+ control.put(new KeyBytes(fixedLengthLastKey), lastValue);
+ control.put(new KeyBytes(fixedLengthPrefixKey), prefixValue);
byte[] key;
long val = 0;
rnd.setSeed(SEED);
- for (int i = 3; i < TREESIZE; i++) {
+ for (int i = 3; i < TREESIZE - 20; i++) {
key = new byte[8 + rnd.nextInt(12)];
rnd.nextBytes(key);
val = 0x1234L << 8 | (byte)i;
tree.put(key, val);
control.put(new KeyBytes(key), val);
}
+ for (int i = 3; i < 20; i++) {
+ key = new byte[10];
+ rnd.nextBytes(key);
+ System.arraycopy(fixedLengthPrefixKey, 0, key, 0, 6);
+ val = 0x1234L << 8 | (byte)i;
+ tree.put(key, val);
+ control.put(new KeyBytes(key), val);
+ }
}
static void fill (LongART tree) {
@@ -315,7 +338,43 @@ public void testLastkeyRecreatedEmptyART() {
Assert.assertTrue(true);
}
}
+
+ @Test
+ public void testFirstkeySingleEntryShortKey() {
+ LongART art = new LongART(heap);
+ byte[] shortKey = Arrays.copyOf(prefixKey, 8);
+ art.put(shortKey, prefixValue);
+ Assert.assertEquals(art.firstKey(), shortKey);
+ }
+ @Test
+ public void testLastkeySingleEntryART() {
+ LongART art = new LongART(heap);
+ byte[] shortKey = Arrays.copyOf(lastKey, 8);
+ art.put(shortKey, lastValue);
+ Assert.assertEquals(art.lastKey(), shortKey);
+ }
+
+ @Test
+ public void testFirstkeyRecreatedSingleEntryART() {
+ LongART oldArt = new LongART(heap);
+ byte[] shortKey = Arrays.copyOf(prefixKey, 8);
+ oldArt.put(shortKey, prefixValue);
+ long artHandle = (oldArt.handle());
+ LongART art = LongART.fromHandle(heap, artHandle);
+ Assert.assertEquals(art.firstKey(), shortKey);
+ }
+
+ @Test
+ public void testLastkeyRecreatedSingleEntryART() {
+ LongART oldArt = new LongART(heap);
+ byte[] shortKey = Arrays.copyOf(prefixKey, 8);
+ oldArt.put(shortKey, prefixValue);
+ long artHandle = (oldArt.handle());
+ LongART art = LongART.fromHandle(heap, artHandle);
+ Assert.assertEquals(art.lastKey(), shortKey);
+ }
+
@Test
public void testFirstkeyFilledART() {
LongART art = new LongART(heap);
@@ -851,6 +910,50 @@ public void testRangeReverseEntryIteratorZeroLengthToKey() {
}
}
+ @Test
+ public void testRangeReverseHeadEntryIteratorNullKey() {
+ LongART art = new LongART(heap);
+ try {
+ Iterator it = art.getReverseHeadEntryIterator(null, true);
+ Assert.fail("IllegalArgumentException was not thrown");
+ } catch (IllegalArgumentException e) {
+ Assert.assertTrue(true);
+ }
+ }
+
+ @Test
+ public void testRangeReverseHeadEntryIteratorZeroLengthKey() {
+ LongART art = new LongART(heap);
+ try {
+ Iterator it = art.getReverseHeadEntryIterator(new byte[]{}, true);
+ Assert.fail("IllegalArgumentException was not thrown");
+ } catch (IllegalArgumentException e) {
+ Assert.assertTrue(true);
+ }
+ }
+
+ @Test
+ public void testRangeReverseTailEntryIteratorNullKey() {
+ LongART art = new LongART(heap);
+ try {
+ Iterator it = art.getReverseTailEntryIterator(null, true);
+ Assert.fail("IllegalArgumentException was not thrown");
+ } catch (IllegalArgumentException e) {
+ Assert.assertTrue(true);
+ }
+ }
+
+ @Test
+ public void testRangeReverseTailEntryIteratorZeroLengthKey() {
+ LongART art = new LongART(heap);
+ try {
+ Iterator it = art.getReverseTailEntryIterator(new byte[]{}, true);
+ Assert.fail("IllegalArgumentException was not thrown");
+ } catch (IllegalArgumentException e) {
+ Assert.assertTrue(true);
+ }
+ }
+
@Test
public void testHasNextHeadEntryIteratorNewEmptyART() {
LongART art = new LongART(heap);
@@ -1128,6 +1231,24 @@ public void testIterateRangeSingleEntryIteratorEq() {
}
}
+ @Test
+ public void testIterateRangeSingleEntryShortKeyIteratorEq() {
+ LongART art = new LongART(heap);
+ ConcurrentSkipListMap control = new ConcurrentSkipListMap();
+ byte[] key = getKey(7);
+ art.put(key, firstValue);
+ control.put(new KeyBytes(key), firstValue);
+
+ Iterator it = art.getEntryIterator(key, true, lastKey, true);
+ LongART.Entry entry = null;
+
+ for (Map.Entry e : control.subMap(new KeyBytes(firstKey), true, new KeyBytes(lastKey), true).entrySet()) {
+ entry = it.next();
+ Assert.assertEquals(entry.getKey(), e.getKey().get());
+ Assert.assertEquals(entry.getValue(), e.getValue().longValue());
+ }
+ }
+
@Test
public void testIterateRangeSingleEntryIteratorLT() {
LongART art = new LongART(heap);
@@ -1146,6 +1267,25 @@ public void testIterateRangeSingleEntryIteratorLT() {
}
}
+ @Test
+ public void testIterateRangeSingleEntryShortKeyIteratorLT() {
+ LongART art = new LongART(heap);
+ ConcurrentSkipListMap control = new ConcurrentSkipListMap();
+ byte[] key = getKey(7);
+ art.put(key, firstValue);
+ control.put(new KeyBytes(key), firstValue);
+ byte[] fromKey = Arrays.copyOf(firstKey, 4);
+
+ Iterator it = art.getEntryIterator(fromKey, true, lastKey, true);
+ LongART.Entry entry = null;
+
+ for (Map.Entry e : control.subMap(new KeyBytes(fromKey), true, new KeyBytes(lastKey), true).entrySet()) {
+ entry = it.next();
+ Assert.assertEquals(entry.getKey(), e.getKey().get());
+ Assert.assertEquals(entry.getValue(), e.getValue().longValue());
+ }
+ }
+
@Test
public void testIterateRangeSingleEntryIteratorPrefix() {
LongART art = new LongART(heap);
@@ -1163,6 +1303,24 @@ public void testIterateRangeSingleEntryIteratorPrefix() {
}
}
+ @Test
+ public void testIterateRangeSingleEntryShortKeyIteratorPrefix() {
+ LongART art = new LongART(heap);
+ ConcurrentSkipListMap control = new ConcurrentSkipListMap();
+ byte[] key = getKey(7);
+ art.put(key, firstValue);
+ control.put(new KeyBytes(key), firstValue);
+
+ Iterator it = art.getEntryIterator(prefixKey, true, lastKey, true);
+ LongART.Entry entry = null;
+
+ for (Map.Entry e : control.subMap(new KeyBytes(prefixKey), true, new KeyBytes(lastKey), true).entrySet()) {
+ entry = it.next();
+ Assert.assertEquals(entry.getKey(), e.getKey().get());
+ Assert.assertEquals(entry.getValue(), e.getValue().longValue());
+ }
+ }
+
@Test
public void testIterateRangeEntryIterator() {
LongART art = new LongART(heap);
@@ -1268,11 +1426,60 @@ public void testIterateRangeEntryIteratorFromKeyAbsentInRange() {
}
@Test
- public void testIterateRangeEntryIteratorFromKeyAbsentInRangePartialPrefixMatch() {
+ public void testIterateRangeReverseHeadEntryIteratorFromKeyAbsentInRange() {
+ LongART art = new LongART(heap);
+ ConcurrentSkipListMap control = new ConcurrentSkipListMap();
+ fill(art, control);
+ byte[] fromKey = getKey(20);
+ Iterator it = art.getReverseHeadEntryIterator(fromKey, true);
+
+ LongART.Entry entry = null;
+ for (Map.Entry e : control.headMap(new KeyBytes(fromKey), true).descendingMap().entrySet()) {
+ entry = it.next();
+ Assert.assertEquals(entry.getKey(), e.getKey().get());
+ Assert.assertEquals(entry.getValue(), e.getValue().longValue());
+ }
+ }
+
+
+ @Test
+ public void testIterateRangeEntryIteratorFromKeyAbsentInRangePartialPrefixMatch1() {
+ LongART art = new LongART(heap);
+ ConcurrentSkipListMap control = new ConcurrentSkipListMap();
+ fill(art, control);
+ byte[] fromKey = Arrays.copyOfRange(lastKey, 0, 8);
+ Iterator it = art.getEntryIterator(fromKey, true, lastKey, true);
+
+ LongART.Entry entry = null;
+ for (Map.Entry e : control.subMap(new KeyBytes(fromKey), true, new KeyBytes(lastKey), true).entrySet()) {
+ entry = it.next();
+ Assert.assertEquals(entry.getKey(), e.getKey().get());
+ Assert.assertEquals(entry.getValue(), e.getValue().longValue());
+ }
+ }
+
+ @Test
+ public void testIterateRangeReverseHeadEntryIteratorFromKeyAbsentInRangePartialPrefixMatch1() {
LongART art = new LongART(heap);
ConcurrentSkipListMap control = new ConcurrentSkipListMap();
fill(art, control);
byte[] fromKey = Arrays.copyOfRange(lastKey, 0, 8);
+ Iterator it = art.getReverseHeadEntryIterator(fromKey, true);
+
+ LongART.Entry entry = null;
+ for (Map.Entry e : control.headMap(new KeyBytes(fromKey), true).descendingMap().entrySet()) {
+ entry = it.next();
+ Assert.assertEquals(entry.getKey(), e.getKey().get());
+ Assert.assertEquals(entry.getValue(), e.getValue().longValue());
+ }
+ }
+ // Tests for searchkey with common prefix that differs in the leaf prefix
+ @Test
+ public void testIterateRangeEntryIteratorFromKeyAbsentInRangePartialPrefixMatch2() {
+ LongART art = new LongART(heap);
+ ConcurrentSkipListMap control = new ConcurrentSkipListMap();
+ fill(art, control);
+ byte[] fromKey = Arrays.copyOfRange(lastKey, 0, lastKey.length - 1);
Iterator it = art.getEntryIterator(fromKey, true, lastKey, true);
LongART.Entry entry = null;
@@ -1283,6 +1490,40 @@ public void testIterateRangeEntryIteratorFromKeyAbsentInRangePartialPrefixMatch(
}
}
+ // Tests for searchkey with common prefix that differs in the leaf prefix
+ @Test
+ public void testIterateRangeReverseHeadEntryIteratorFromKeyAbsentInRangePartialPrefixMatch2() {
+ LongART art = new LongART(heap);
+ ConcurrentSkipListMap control = new ConcurrentSkipListMap();
+ fill(art, control);
+ byte[] fromKey = Arrays.copyOfRange(lastKey, 0, lastKey.length - 1);
+ Iterator it = art.getReverseHeadEntryIterator(fromKey, true);
+
+ LongART.Entry entry = null;
+ for (Map.Entry e : control.headMap(new KeyBytes(fromKey), true).descendingMap().entrySet()) {
+ entry = it.next();
+ Assert.assertEquals(entry.getKey(), e.getKey().get());
+ Assert.assertEquals(entry.getValue(), e.getValue().longValue());
+ }
+ }
+
+ // Tests for searchkey with common prefix that differs in the leaf prefix
+ @Test
+ public void testIterateRangeReverseHeadEntryIteratorFromKeyAbsentInRangePartialPrefixMatch3() {
+ LongART art = new LongART(heap);
+ ConcurrentSkipListMap control = new ConcurrentSkipListMap();
+ fill(art, control);
+ byte[] fromKey = Arrays.copyOfRange(lastKey, 0, lastKey.length - 9);
+ Iterator it = art.getReverseHeadEntryIterator(fromKey, true);
+
+ LongART.Entry entry = null;
+ for (Map.Entry e : control.headMap(new KeyBytes(fromKey), true).descendingMap().entrySet()) {
+ entry = it.next();
+ Assert.assertEquals(entry.getKey(), e.getKey().get());
+ Assert.assertEquals(entry.getValue(), e.getValue().longValue());
+ }
+ }
+
@Test
public void testIterateRangeTailEntryIteratorFromKeyAbsentPostRange() {
LongART art = new LongART(heap);
@@ -1427,7 +1668,158 @@ public void testIterateRangeReverseEntryIteratorPrefix() {
Assert.assertEquals(entry.getValue(), e.getValue().longValue());
}
}
-
+
+ @Test
+ public void testIterateReverseHeadEntryIterator() {
+ LongART art = new LongART(heap);
+ ConcurrentSkipListMap control = new ConcurrentSkipListMap();
+ fill(art, control);
+
+ Iterator it = art.getReverseHeadEntryIterator(prefixKey, true);
+ LongART.Entry entry = null;
+
+ for (Map.Entry e : control.headMap(new KeyBytes(prefixKey), true).descendingMap().entrySet()) {
+ entry = it.next();
+ Assert.assertEquals(entry.getKey(), e.getKey().get());
+ Assert.assertEquals(entry.getValue(), e.getValue().longValue());
+ }
+ }
+
+ @Test
+ public void testIterateReverseTailEntryIterator() {
+ LongART art = new LongART(heap);
+ ConcurrentSkipListMap control = new ConcurrentSkipListMap();
+ fill(art, control);
+
+ Iterator it = art.getReverseTailEntryIterator(prefixKey, true);
+ LongART.Entry entry = null;
+
+ for (Map.Entry e : control.tailMap(new KeyBytes(prefixKey), true).descendingMap().entrySet()) {
+ entry = it.next();
+ Assert.assertEquals(entry.getKey(), e.getKey().get());
+ Assert.assertEquals(entry.getValue(), e.getValue().longValue());
+ }
+ }
+
+ //EmptyRange tests
+ public void testEmptyRangeIterationPost() {
+ LongART art = new LongART(heap);
+ fill(art);
+ Iterator it = art.getEntryIterator(lastKey, false, lastKey, false);
+ Assert.assertFalse(it.hasNext());
+ }
+
+ public void testEmptyRangeIterationPre() {
+ LongART art = new LongART(heap);
+ fill(art);
+ Iterator it = art.getEntryIterator(Arrays.copyOf(firstKey, 1), true, Arrays.copyOf(firstKey, 2), true);
+ Assert.assertFalse(it.hasNext());
+ }
+
+ public void testEmptyReverseRangeIterationPost() {
+ LongART art = new LongART(heap);
+ fill(art);
+ Iterator it = art.getReverseEntryIterator(lastKey, false, lastKey, false);
+ Assert.assertFalse(it.hasNext());
+ }
+
+ public void testEmptyReverseRangeIterationPre() {
+ LongART art = new LongART(heap);
+ fill(art);
+ Iterator it = art.getReverseEntryIterator(Arrays.copyOf(firstKey, 1), true, Arrays.copyOf(firstKey, 2), true);
+ Assert.assertFalse(it.hasNext());
+ }
+
+ public void testEmptyHeadIteration() {
+ LongART art = new LongART(heap);
+ fill(art);
+ Iterator it = art.getHeadEntryIterator(Arrays.copyOf(firstKey, 1), true);
+ Assert.assertFalse(it.hasNext());
+ }
+
+ public void testEmptyReverseHeadIteration() {
+ LongART art = new LongART(heap);
+ fill(art);
+ Iterator it = art.getReverseHeadEntryIterator(Arrays.copyOf(firstKey, 1), true);
+ Assert.assertFalse(it.hasNext());
+ }
+
+ public void testEmptyTailIteration() {
+ LongART art = new LongART(heap);
+ fill(art);
+ byte[] key = new byte[22]; Arrays.fill(key, (byte)0xff);
+ Iterator it = art.getTailEntryIterator(key, true);
+ Assert.assertFalse(it.hasNext());
+ }
+
+ public void testEmptyReverseTailIteration() {
+ LongART art = new LongART(heap);
+ fill(art);
+ byte[] key = new byte[22]; Arrays.fill(key, (byte)0xff);
+ Iterator it = art.getReverseTailEntryIterator(key, true);
+ Assert.assertFalse(it.hasNext());
+ }
+
+/*
+ //SingleEntryShortKey tests
+ public void testSingleEntryIterationPost() {
+ LongART art = new LongART(heap);
+ art.put(Arrays.copy
+ Iterator it = art.getEntryIterator(lastKey, false, lastKey, false);
+ Assert.assertFalse(it.hasNext());
+ }
+ public void testEmptyRangeIterationPre() {
+ LongART art = new LongART(heap);
+ fill(art);
+ Iterator it = art.getEntryIterator(Arrays.copyOf(firstKey, 1), true, Arrays.copyOf(firstKey, 2), true);
+ Assert.assertFalse(it.hasNext());
+ }
+
+ public void testEmptyReverseRangeIterationPost() {
+ LongART art = new LongART(heap);
+ fill(art);
+ Iterator it = art.getReverseEntryIterator(lastKey, false, lastKey, false);
+ Assert.assertFalse(it.hasNext());
+ }
+
+ public void testEmptyReverseRangeIterationPre() {
+ LongART art = new LongART(heap);
+ fill(art);
+ Iterator it = art.getReverseEntryIterator(Arrays.copyOf(firstKey, 1), true, Arrays.copyOf(firstKey, 2), true);
+ Assert.assertFalse(it.hasNext());
+ }
+
+ public void testEmptyHeadIteration() {
+ LongART art = new LongART(heap);
+ fill(art);
+ Iterator it = art.getHeadEntryIterator(Arrays.copyOf(firstKey, 1), true);
+ Assert.assertFalse(it.hasNext());
+ }
+
+ public void testEmptyReverseHeadIteration() {
+ LongART art = new LongART(heap);
+ fill(art);
+ Iterator it = art.getReverseHeadEntryIterator(Arrays.copyOf(firstKey, 1), true);
+ Assert.assertFalse(it.hasNext());
+ }
+
+ public void testEmptyTailIteration() {
+ LongART art = new LongART(heap);
+ fill(art);
+ byte[] key = new byte[22]; Arrays.fill(key, (byte)0xff);
+ Iterator it = art.getTailEntryIterator(key, true);
+ Assert.assertFalse(it.hasNext());
+ }
+
+ public void testEmptyReverseTailIteration() {
+ LongART art = new LongART(heap);
+ fill(art);
+ byte[] key = new byte[22]; Arrays.fill(key, (byte)0xff);
+ Iterator it = art.getReverseTailEntryIterator(key, true);
+ Assert.assertFalse(it.hasNext());
+ }
+*/
+
// Delete tests
public void testDeleteNullKey() {
LongART art = new LongART(heap);
@@ -1493,6 +1885,43 @@ public void testDeleteAfterFree() {
Assert.assertTrue(true);
}
}
+
+ public void testDeleteSingleEntry() {
+ LongART art = new LongART(heap);
+ art.put(firstKey, firstValue);
+ art.remove(firstKey, c -> {});
+ art.remove(firstKey, (Long l) -> { Assert.assertTrue(l == 0L); });
+ Iterator it = art.getEntryIterator();
+ Assert.assertFalse(it.hasNext());
+ }
+
+ public void testSizeAfterDelete() {
+ LongART art = new LongART(heap);
+ fill(art);
+ long size = art.size();
+ for (long i = 0; i < size; i++) {
+ art.remove(art.firstKey(), c -> {});
+ Assert.assertEquals(art.size(), --size);
+ }
+ }
+
+ public void testIterateAfterDelete() {
+ LongART art = new LongART(heap);
+ fill(art);
+ ArrayList list = new ArrayList((int)art.size());
+ Iterator it = art.getEntryIterator();
+ while (it.hasNext()) list.add(it.next().getKey());
+ list.forEach((byte[] arr) -> { art.remove(arr, c -> {}); });
+ it = art.getEntryIterator();
+ Assert.assertFalse(it.hasNext());
+ }
+
+ public void testSizeAfterDeleteSingleEntry() {
+ LongART art = new LongART(heap);
+ art.put(firstKey, firstValue);
+ art.remove(firstKey, c -> {});
+ Assert.assertEquals(art.size(), 0L);
+ }
// Split tests
public void testSplitEmptyART() {