Skip to content
Open
76 changes: 62 additions & 14 deletions src/main/java/btree4j/BTree.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,15 @@
import btree4j.utils.io.FastMultiByteArrayOutputStream;
import btree4j.utils.lang.ArrayUtils;
import btree4j.utils.lang.Primitives;
import com.google.common.annotations.Beta;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import javax.annotation.CheckForNull;
import java.io.*;
import java.nio.ByteBuffer;
import java.util.Arrays;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* BTree represents a Variable Magnitude Simple-Prefix B+Tree File.
*/
Expand Down Expand Up @@ -249,6 +241,10 @@ public synchronized int removeValue(@Nonnull Value key, long pointer) throws BTr
}
}

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 @@ -532,6 +528,14 @@ protected BTreeNode(final BTreeRootInfo root, final Page page) {
this.ph = (BTreePageHeader) page.getPageHeader();
}

private void clearParent() {
if (parentCache != null || ph.parentPage != Paged.NO_PAGE) {
ph.parentPage = Paged.NO_PAGE;
this.parentCache = null;
this.dirty = true;
}
}

private BTreeNode getParent() {
if (parentCache != null) {
return parentCache;
Expand Down Expand Up @@ -655,6 +659,43 @@ 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.copyOf(keys, leftIdx), ArrayUtils.copyOf(ptrs, leftIdx + 1));
break;
case LEAF:
leftIdx = (leftIdx < 0) ? -(leftIdx + 1) : leftIdx;
if (leftIdx < keys.length && leftIdx < ptrs.length) {
set(ArrayUtils.copyOf(keys, leftIdx), ArrayUtils.copyOf(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);
}
_rootNode.clearParent();
}
calculateDataLength();
}

/** @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 @@ -1064,6 +1105,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 @@ -1116,7 +1161,10 @@ private void decrDataLength(final Value value) {
}

/** find lest-most value which matches to the key */
long findValue(@Nonnull Value searchKey) throws BTreeException {
long findValue(Value searchKey) throws BTreeException {
if (searchKey == null) {
throw new BTreeException("Can't search on null Value");
}
int idx = searchLeftmostKey(keys, searchKey, keys.length);
switch (ph.getStatus()) {
case BRANCH:
Expand Down
100 changes: 100 additions & 0 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 @@ -389,6 +390,105 @@ public static boolean equals(final char[] a, final char[] a2, final int off, fin
return true;
}

public static char[] copyOfRange(final char[] original, final int from, final int to) {
final int newLength = to - from;
if (newLength < 0) {
throw new IllegalArgumentException(from + " > " + to);
}
final char[] copy = new char[newLength];
System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
return copy;
}

public static byte[] copyOfRange(final byte[] original, final int from, final int to) {
final int newLength = to - from;
if (newLength < 0) {
throw new IllegalArgumentException(from + " > " + to);
}
final byte[] copy = new byte[newLength];
System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
return copy;
}

public static int[] copyOfRange(final int[] original, final int from, final int to) {
final int newLength = to - from;
if (newLength < 0) {
throw new IllegalArgumentException(from + " > " + to);
}
final int[] copy = new int[newLength];
System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
return copy;
}

public static long[] copyOfRange(final long[] original, final int from, final int to) {
final int newLength = to - from;
if (newLength < 0) {
throw new IllegalArgumentException(from + " > " + to);
}
final long[] copy = new long[newLength];
System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
return copy;
}

@SuppressWarnings("unchecked")
public static <T> T[] copyOfRange(final T[] original, final int from, final int to) {
return copyOfRange(original, from, to, (Class<T[]>) original.getClass());
}

@SuppressWarnings("unchecked")
private static <T, U> T[] copyOfRange(final U[] original, final int from, final int to,
final Class<? extends T[]> newType) {
final int newLength = to - from;
if (newLength < 0) {
throw new IllegalArgumentException(from + " > " + to);
}
final T[] copy = ((Object) newType == (Object) Object[].class) ? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
return copy;
}

public static byte[] copyOf(final byte[] original, final int newLength) {
final byte[] copy = new byte[newLength];
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}

public static int[] copyOf(final int[] original, final int newLength) {
final int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}

public static long[] copyOf(final long[] vals, final int idx) {
return Arrays.copyOf(vals, idx);

}

public static char[] copyOf(final char[] original, final int newLength) {
final char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}

@SuppressWarnings("unchecked")
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}

@SuppressWarnings("unchecked")
public static <T, U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
final T[] copy = ((Object) newType == (Object) Object[].class) ? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
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
93 changes: 93 additions & 0 deletions src/test/java/btree4j/ArrayUtilsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package btree4j;

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

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);
}

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

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

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

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

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

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