Skip to content
Open
62 changes: 52 additions & 10 deletions src/main/java/btree4j/BTree.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@

import javax.annotation.CheckForNull;

import com.google.common.annotations.Beta;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

Expand Down Expand Up @@ -238,6 +239,10 @@ public synchronized long[] removeValue(Value value, long pointer) throws BTreeEx
}
}

public synchronized void removeValueFrom(Value value) throws BTreeException {
_rootNode.removeFrom(value);
}

/**
* findValue finds a Value in the BTree and returns the associated pointer for it.
*
Expand Down Expand Up @@ -650,6 +655,39 @@ private int searchRightmostKey(final Value[] ary, final Value key, final int to)
return -(low + 1); // key not found.
}

/**
* remove all values greater than a threshold
* @return variable indicating if it should delete the child node as well
*/
@Beta
void removeFrom(Value searchKey) throws BTreeException {
resetDataLength();
int leftIdx = searchLeftmostKey(keys, searchKey, keys.length);
switch (ph.getStatus()) {
case BRANCH:
leftIdx = (leftIdx < 0) ? -(leftIdx + 1) : leftIdx;
getChildNode(leftIdx).removeFrom(searchKey);

if (leftIdx < keys.length && leftIdx + 1 < ptrs.length)
set(ArrayUtils.removeFrom(keys, leftIdx), ArrayUtils.removeFrom(ptrs, leftIdx + 1));
break;
case LEAF:
leftIdx = (leftIdx < 0) ? -(leftIdx + 1) : leftIdx;
if (leftIdx < keys.length && leftIdx < ptrs.length)
set(ArrayUtils.removeFrom(keys, leftIdx), ArrayUtils.removeFrom(ptrs, leftIdx));
this._next = -1;
break;
default:
throw new BTreeCorruptException(
"Invalid page type '" + ph.getStatus() + "' in removeValue");
}

if (getParent() == null)
while (_rootNode.ptrs.length == 1) {
_rootNode = _rootNode.getChildNode(0);
}
}

/** @return pointer of left-most matched item */
long removeValue(Value searchKey) throws IOException, BTreeException {
int leftIdx = searchLeftmostKey(keys, searchKey, keys.length);
Expand Down Expand Up @@ -929,12 +967,12 @@ private void set(final Value[] values, final long[] ptrs) {
this.ptrs = ptrs;
this.ph.setValueCount((short) vlen);
if (vlen > 1) {
final int prevPreixLen = ph.getPrefixLength();
final int prevPrefixLen = ph.getPrefixLength();
this.prefix = getPrefix(values[0], values[vlen - 1]);
final int prefixLen = prefix.getLength();
assert (prefixLen <= Short.MAX_VALUE) : prefixLen;
if (prefixLen != prevPreixLen) {
int diff = prefixLen - prevPreixLen;
if (prefixLen != prevPrefixLen) {
int diff = prefixLen - prevPrefixLen;
currentDataLen += diff;
ph.setPrefixLength((short) prefixLen);
}
Expand Down Expand Up @@ -1063,6 +1101,10 @@ private void write() throws IOException, BTreeException {
setDirty(false);
}

private void resetDataLength() {
currentDataLen = -1;
}

private int calculateDataLength() {
if (currentDataLen > 0) {
return currentDataLen;
Expand Down Expand Up @@ -1115,15 +1157,15 @@ private void decrDataLength(final Value value) {
}

/** find lest-most value which matches to the key */
long findValue(Value serarchKey) throws BTreeException {
if (serarchKey == null) {
long findValue(Value searchKey) throws BTreeException {
if (searchKey == null) {
throw new BTreeException("Can't search on null Value");
}
int idx = searchLeftmostKey(keys, serarchKey, keys.length);
int idx = searchLeftmostKey(keys, searchKey, keys.length);
switch (ph.getStatus()) {
case BRANCH:
idx = idx < 0 ? -(idx + 1) : idx + 1;
return getChildNode(idx).findValue(serarchKey);
return getChildNode(idx).findValue(searchKey);
case LEAF:
if (idx < 0) {
return KEY_NOT_FOUND;
Expand All @@ -1134,7 +1176,7 @@ long findValue(Value serarchKey) throws BTreeException {
leftmostNode = getBTreeNode(root, leftmostNode._prev);
final Value[] lmKeys = leftmostNode.keys;
assert (lmKeys.length > 0);
if (!lmKeys[0].equals(serarchKey)) {
if (!lmKeys[0].equals(searchKey)) {
break;
}
final int prevLookup = leftmostNode.ph.getLeftLookup();
Expand All @@ -1143,11 +1185,11 @@ long findValue(Value serarchKey) throws BTreeException {
}
}
final Value[] lmKeys = leftmostNode.keys;
final int lmIdx = leftmostNode.searchLeftmostKey(lmKeys, serarchKey,
final int lmIdx = leftmostNode.searchLeftmostKey(lmKeys, searchKey,
lmKeys.length);
if (lmIdx < 0) {
throw new BTreeCorruptException(
"Duplicated key was not found: " + serarchKey);
"Duplicated key was not found: " + searchKey);
}
final long[] leftmostPtrs = leftmostNode.ptrs;
return leftmostPtrs[lmIdx];
Expand Down
66 changes: 44 additions & 22 deletions src/main/java/btree4j/utils/lang/ArrayUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
package btree4j.utils.lang;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Random;

Expand Down Expand Up @@ -64,11 +65,11 @@ public static int[] copy(final int[] original) {
* </p>
*
* <pre>
* ArrayUtils.getLength(null) = 0
* ArrayUtils.getLength([]) = 0
* ArrayUtils.getLength([null]) = 1
* ArrayUtils.getLength([true, false]) = 2
* ArrayUtils.getLength([1, 2, 3]) = 3
* ArrayUtils.getLength(null) = 0
* ArrayUtils.getLength([]) = 0
* ArrayUtils.getLength([null]) = 1
* ArrayUtils.getLength([true, false]) = 2
* ArrayUtils.getLength([1, 2, 3]) = 3
* ArrayUtils.getLength(["a", "b", "c"]) = 3
* </pre>
*
Expand Down Expand Up @@ -151,11 +152,11 @@ public static int indexOf(final byte[] array, final byte valueToFind, int startI
* since arrays indices are 0-based. </p>
*
* <pre>
* ArrayUtils.lastIndex(null) = -1
* ArrayUtils.lastIndex([]) = -1
* ArrayUtils.lastIndex([null]) = 0
* ArrayUtils.lastIndex([true, false]) = 1
* ArrayUtils.lastIndex([1, 2, 3]) = 2
* ArrayUtils.lastIndex(null) = -1
* ArrayUtils.lastIndex([]) = -1
* ArrayUtils.lastIndex([null]) = 0
* ArrayUtils.lastIndex([true, false]) = 1
* ArrayUtils.lastIndex([1, 2, 3]) = 2
* ArrayUtils.lastIndex(["a", "b", "c"]) = 2
* </pre>
*
Expand Down Expand Up @@ -186,10 +187,10 @@ public static int lastIndex(final Object array) {
* </p>
*
* <pre>
* ArrayUtils.insert(null, 0, null) = [null]
* ArrayUtils.insert(null, 0, "a") = ["a"]
* ArrayUtils.insert(["a"], 1, null) = ["a", null]
* ArrayUtils.insert(["a"], 1, "b") = ["a", "b"]
* ArrayUtils.insert(null, 0, null) = [null]
* ArrayUtils.insert(null, 0, "a") = ["a"]
* ArrayUtils.insert(["a"], 1, null) = ["a", null]
* ArrayUtils.insert(["a"], 1, "b") = ["a", "b"]
* ArrayUtils.insert(["a", "b"], 3, "c") = ["a", "b", "c"]
* </pre>
*
Expand Down Expand Up @@ -300,21 +301,29 @@ public static int[] append(final int[] left, final int[] right) {
* @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >=
* array.length), or if the array is <code>null</code>.
* @since 2.1
* @implNote Its dependance on removeFrom may slow down the function.
*/
@SuppressWarnings("unchecked")
public static <T> T[] remove(final T[] array, final int index) {
int length = getLength(array);
Object result = Arrays.copyOf(removeFrom(array, index), length - 1);
if (index < length - 1) {
System.arraycopy(array, index + 1, result, index, length - index - 1);
}
return (T[]) result;
}

public static <T> T[] removeFrom(final T[] array, final int index) {
int length = getLength(array);
if (index < 0 || index >= length) {
throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length);
}
Object result = Array.newInstance(array.getClass().getComponentType(), length - 1);
Object result = Array.newInstance(array.getClass().getComponentType(), index);
System.arraycopy(array, 0, result, 0, index);
if (index < length - 1) {
System.arraycopy(array, index + 1, result, index, length - index - 1);
}
return (T[]) result;
}


@SuppressWarnings("unchecked")
public static <T> T[] remove(final T[] array, final int from, final int to) {
assert (to >= from) : to + " - " + from;
Expand All @@ -332,17 +341,25 @@ public static <T> T[] remove(final T[] array, final int from, final int to) {
return (T[]) result;
}

/**
* @implNote Its dependance on removeFrom may slow down the function.
*/
public static long[] remove(final long[] vals, final int idx) {
long[] newVals = new long[vals.length - 1];
if (idx > 0) {
System.arraycopy(vals, 0, newVals, 0, idx);
}
long[] newVals = Arrays.copyOf(removeFrom(vals, idx), vals.length - 1);
if (idx < newVals.length) {
System.arraycopy(vals, idx + 1, newVals, idx, newVals.length - idx);
}
return newVals;
}

public static long[] removeFrom(final long[] vals, final int idx) {
long[] newVals = new long[idx];
if (idx > 0) {
System.arraycopy(vals, 0, newVals, 0, idx);
}
return newVals;
}

public static long[] remove(final long[] vals, final int from, final int to) {
int remsize = to - from + 1;
long[] newVals = new long[vals.length - remsize];
Expand Down Expand Up @@ -484,6 +501,11 @@ public static <T, U> T[] copyOf(U[] original, int newLength, Class<? extends T[]
return copy;
}

/**
* @param fromIndex Inclusive first index
* @param toIndex Non-inclusive last index
* @return returns either the found index or -1 * (the index of the first key greater than key + 1)
*/
public static <T extends Comparable<T>> int binarySearch(final T[] a, final int fromIndex,
final int toIndex, final T key) {
int low = fromIndex;
Expand Down
95 changes: 95 additions & 0 deletions src/test/java/btree4j/ArrayUtilsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package btree4j;

import btree4j.utils.lang.ArrayUtils;
import org.junit.Assert;
import org.junit.Test;

import java.util.ArrayList;

public class ArrayUtilsTest {
// --------------- remove
@Test
public void shouldRemoveFirstValue() {
Integer[] a = {1, 2, 3};
a = ArrayUtils.remove(a, 0);
Assert.assertArrayEquals(new Integer[]{2, 3}, a);
}

@Test
public void shouldRemoveLastValue() {
Integer[] a = {1, 2, 3};
a = ArrayUtils.remove(a, 2);
Assert.assertArrayEquals(new Integer[]{1, 2}, a);
}

@Test
public void shouldRemoveMiddleValue() {
Integer[] a = {1, 2, 3};
a = ArrayUtils.remove(a, 1);
Assert.assertArrayEquals(new Integer[]{1, 3}, a);
}

@Test
public void shouldRemoveFirstValuePrimitive() {
long[] a = {1, 2, 3};
a = ArrayUtils.remove(a, 0);
Assert.assertArrayEquals(new long[]{2, 3}, a);
}

@Test
public void shouldRemoveLastValuePrimitive() {
long[] a = {1, 2, 3};
a = ArrayUtils.remove(a, 2);
Assert.assertArrayEquals(new long[]{1, 2}, a);
}

@Test
public void shouldRemoveMiddleValuePrimitive() {
long[] a = {1, 2, 3};
a = ArrayUtils.remove(a, 1);
Assert.assertArrayEquals(new long[]{1, 3}, a);
}

// --------------- removeFrom
@Test
public void shouldRemoveFromFirstValue() {
Integer[] a = {1, 2, 3};
a = ArrayUtils.removeFrom(a, 0);
Assert.assertArrayEquals(new Integer[]{}, a);
}

@Test
public void shouldRemoveFromLastValue() {
Integer[] a = {1, 2, 3};
a = ArrayUtils.removeFrom(a, 2);
Assert.assertArrayEquals(new Integer[]{1, 2}, a);
}

@Test
public void shouldRemoveFromMiddleValue() {
Integer[] a = {1, 2, 3};
a = ArrayUtils.removeFrom(a, 1);
Assert.assertArrayEquals(new Integer[]{1}, a);
}

@Test
public void shouldRemoveFromFirstValuePrimitive() {
long[] a = {1, 2, 3};
a = ArrayUtils.removeFrom(a, 0);
Assert.assertArrayEquals(new long[]{}, a);
}

@Test
public void shouldRemoveFromLastValuePrimitive() {
long[] a = {1, 2, 3};
a = ArrayUtils.removeFrom(a, 2);
Assert.assertArrayEquals(new long[]{1, 2}, a);
}

@Test
public void shouldRemoveFromMiddleValuePrimitive() {
long[] a = {1, 2, 3};
a = ArrayUtils.removeFrom(a, 1);
Assert.assertArrayEquals(new long[]{1}, a);
}
}
Loading