blob: f4d9cd4cabe6419eaede2ddd250076b89d189847 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cassandra.utils.btree;
import java.util.Arrays;
import java.util.Comparator;
public class BTreeRemoval
{
/**
* Remove |elem| from |btree|. If it's not present then return |btree| itself.
*/
public static <V> Object[] remove(final Object[] btree, final Comparator<? super V> comparator, final V elem)
{
if (BTree.isEmpty(btree))
return btree;
int index = -1;
V elemToSwap = null;
int lb = 0;
Object[] node = btree;
while (true)
{
int keyEnd = BTree.getKeyEnd(node);
int i = Arrays.binarySearch((V[]) node, 0, keyEnd, elem, comparator);
if (i >= 0)
{
if (BTree.isLeaf(node))
index = lb + i;
else
{
final int indexInNode = BTree.getSizeMap(node)[i];
index = lb + indexInNode - 1;
elemToSwap = BTree.findByIndex(node, indexInNode - 1);
}
break;
}
if (BTree.isLeaf(node))
return btree;
i = -1 - i;
if (i > 0)
lb += BTree.getSizeMap(node)[i - 1] + 1;
node = (Object[]) node[keyEnd + i];
}
if (BTree.size(btree) == 1)
return BTree.empty();
Object[] result = removeFromLeaf(btree, index);
if (elemToSwap != null)
BTree.replaceInSitu(result, index, elemToSwap);
return result;
}
/**
* Remove |elem| from |btree|. It has to be present and it has to reside in a leaf node.
*/
private static Object[] removeFromLeaf(Object[] node, int index)
{
Object[] result = null;
Object[] prevNode = null;
int prevI = -1;
boolean needsCopy = true;
while (!BTree.isLeaf(node))
{
final int keyEnd = BTree.getBranchKeyEnd(node);
int i = -1 - Arrays.binarySearch(BTree.getSizeMap(node), index);
if (i > 0)
index -= (1 + BTree.getSizeMap(node)[i - 1]);
Object[] nextNode = (Object[]) node[keyEnd + i];
boolean nextNodeNeedsCopy = true;
if (BTree.getKeyEnd(nextNode) > BTree.MINIMAL_NODE_SIZE)
node = copyIfNeeded(node, needsCopy);
else if (i > 0 && BTree.getKeyEnd((Object[]) node[keyEnd + i - 1]) > BTree.MINIMAL_NODE_SIZE)
{
node = copyIfNeeded(node, needsCopy);
final Object[] leftNeighbour = (Object[]) node[keyEnd + i - 1];
index++;
if (!BTree.isLeaf(leftNeighbour))
index += BTree.size((Object[])leftNeighbour[BTree.getChildEnd(leftNeighbour) - 1]);
nextNode = rotateLeft(node, i);
}
else if (i < keyEnd && BTree.getKeyEnd((Object[]) node[keyEnd + i + 1]) > BTree.MINIMAL_NODE_SIZE)
{
node = copyIfNeeded(node, needsCopy);
nextNode = rotateRight(node, i);
}
else
{
nextNodeNeedsCopy = false;
if (i > 0)
{
final Object[] leftNeighbour = (Object[]) node[keyEnd + i - 1];
final Object nodeKey = node[i - 1];
node = keyEnd == 1 ? null : copyWithKeyAndChildRemoved(node, i - 1, i - 1, false);
nextNode = merge(leftNeighbour, nextNode, nodeKey);
i = i - 1;
index += BTree.size(leftNeighbour) + 1;
}
else
{
final Object[] rightNeighbour = (Object[]) node[keyEnd + i + 1];
final Object nodeKey = node[i];
node = keyEnd == 1 ? null : copyWithKeyAndChildRemoved(node, i, i, false);
nextNode = merge(nextNode, rightNeighbour, nodeKey);
}
}
if (node != null)
{
final int[] sizeMap = BTree.getSizeMap(node);
for (int j = i; j < sizeMap.length; ++j)
sizeMap[j] -= 1;
if (prevNode != null)
prevNode[prevI] = node;
else
result = node;
prevNode = node;
prevI = BTree.getChildStart(node) + i;
}
node = nextNode;
needsCopy = nextNodeNeedsCopy;
}
final int keyEnd = BTree.getLeafKeyEnd(node);
final Object[] newLeaf = new Object[(keyEnd & 1) == 1 ? keyEnd : keyEnd - 1];
copyKeys(node, newLeaf, 0, index);
if (prevNode != null)
prevNode[prevI] = newLeaf;
else
result = newLeaf;
return result;
}
private static Object[] rotateRight(final Object[] node, final int i)
{
final int keyEnd = BTree.getBranchKeyEnd(node);
final Object[] nextNode = (Object[]) node[keyEnd + i];
final Object[] rightNeighbour = (Object[]) node[keyEnd + i + 1];
final boolean leaves = BTree.isLeaf(nextNode);
final int nextKeyEnd = BTree.getKeyEnd(nextNode);
final Object[] newChild = leaves ? null : (Object[]) rightNeighbour[BTree.getChildStart(rightNeighbour)];
final Object[] newNextNode =
copyWithKeyAndChildInserted(nextNode, nextKeyEnd, node[i], BTree.getChildCount(nextNode), newChild);
node[i] = rightNeighbour[0];
node[keyEnd + i + 1] = copyWithKeyAndChildRemoved(rightNeighbour, 0, 0, true);
BTree.getSizeMap(node)[i] +=
leaves ? 1 : 1 + BTree.size((Object[]) newNextNode[BTree.getChildEnd(newNextNode) - 1]);
return newNextNode;
}
private static Object[] rotateLeft(final Object[] node, final int i)
{
final int keyEnd = BTree.getBranchKeyEnd(node);
final Object[] nextNode = (Object[]) node[keyEnd + i];
final Object[] leftNeighbour = (Object[]) node[keyEnd + i - 1];
final int leftNeighbourEndKey = BTree.getKeyEnd(leftNeighbour);
final boolean leaves = BTree.isLeaf(nextNode);
final Object[] newChild = leaves ? null : (Object[]) leftNeighbour[BTree.getChildEnd(leftNeighbour) - 1];
final Object[] newNextNode = copyWithKeyAndChildInserted(nextNode, 0, node[i - 1], 0, newChild);
node[i - 1] = leftNeighbour[leftNeighbourEndKey - 1];
node[keyEnd + i - 1] = copyWithKeyAndChildRemoved(leftNeighbour, leftNeighbourEndKey - 1, leftNeighbourEndKey, true);
BTree.getSizeMap(node)[i - 1] -= leaves ? 1 : 1 + BTree.getSizeMap(newNextNode)[0];
return newNextNode;
}
private static <V> Object[] copyWithKeyAndChildInserted(final Object[] node, final int keyIndex, final V key, final int childIndex, final Object[] child)
{
final boolean leaf = BTree.isLeaf(node);
final int keyEnd = BTree.getKeyEnd(node);
final Object[] copy;
if (leaf)
copy = new Object[keyEnd + ((keyEnd & 1) == 1 ? 2 : 1)];
else
copy = new Object[node.length + 2];
if (keyIndex > 0)
System.arraycopy(node, 0, copy, 0, keyIndex);
copy[keyIndex] = key;
if (keyIndex < keyEnd)
System.arraycopy(node, keyIndex, copy, keyIndex + 1, keyEnd - keyIndex);
if (!leaf)
{
if (childIndex > 0)
System.arraycopy(node,
BTree.getChildStart(node),
copy,
keyEnd + 1,
childIndex);
copy[keyEnd + 1 + childIndex] = child;
if (childIndex <= keyEnd)
System.arraycopy(node,
BTree.getChildStart(node) + childIndex,
copy,
keyEnd + childIndex + 2,
keyEnd - childIndex + 1);
final int[] sizeMap = BTree.getSizeMap(node);
final int[] newSizeMap = new int[sizeMap.length + 1];
if (childIndex > 0)
System.arraycopy(sizeMap, 0, newSizeMap, 0, childIndex);
final int childSize = BTree.size(child);
newSizeMap[childIndex] = childSize + ((childIndex == 0) ? 0 : newSizeMap[childIndex - 1] + 1);
for (int i = childIndex + 1; i < newSizeMap.length; ++i)
newSizeMap[i] = sizeMap[i - 1] + childSize + 1;
copy[copy.length - 1] = newSizeMap;
}
return copy;
}
private static Object[] copyWithKeyAndChildRemoved(final Object[] node, final int keyIndex, final int childIndex, final boolean substractSize)
{
final boolean leaf = BTree.isLeaf(node);
final Object[] newNode;
if (leaf)
{
final int keyEnd = BTree.getKeyEnd(node);
newNode = new Object[keyEnd - ((keyEnd & 1) == 1 ? 0 : 1)];
}
else
{
newNode = new Object[node.length - 2];
}
int offset = copyKeys(node, newNode, 0, keyIndex);
if (!leaf)
{
offset = copyChildren(node, newNode, offset, childIndex);
final int[] nodeSizeMap = BTree.getSizeMap(node);
final int[] newNodeSizeMap = new int[nodeSizeMap.length - 1];
int pos = 0;
final int sizeToRemove = BTree.size((Object[])node[BTree.getChildStart(node) + childIndex]) + 1;
for (int i = 0; i < nodeSizeMap.length; ++i)
if (i != childIndex)
newNodeSizeMap[pos++] = nodeSizeMap[i] -
((substractSize && i > childIndex) ? sizeToRemove : 0);
newNode[offset] = newNodeSizeMap;
}
return newNode;
}
private static <V> Object[] merge(final Object[] left, final Object[] right, final V nodeKey)
{
assert BTree.getKeyEnd(left) == BTree.MINIMAL_NODE_SIZE;
assert BTree.getKeyEnd(right) == BTree.MINIMAL_NODE_SIZE;
final boolean leaves = BTree.isLeaf(left);
final Object[] result;
if (leaves)
result = new Object[BTree.MINIMAL_NODE_SIZE * 2 + 1];
else
result = new Object[left.length + right.length];
int offset = 0;
offset = copyKeys(left, result, offset);
result[offset++] = nodeKey;
offset = copyKeys(right, result, offset);
if (!leaves)
{
offset = copyChildren(left, result, offset);
offset = copyChildren(right, result, offset);
final int[] leftSizeMap = BTree.getSizeMap(left);
final int[] rightSizeMap = BTree.getSizeMap(right);
final int[] newSizeMap = new int[leftSizeMap.length + rightSizeMap.length];
offset = 0;
offset = copySizeMap(leftSizeMap, newSizeMap, offset, 0);
offset = copySizeMap(rightSizeMap, newSizeMap, offset, leftSizeMap[leftSizeMap.length - 1] + 1);
result[result.length - 1] = newSizeMap;
}
return result;
}
private static int copyKeys(final Object[] from, final Object[] to, final int offset)
{
final int keysCount = BTree.getKeyEnd(from);
System.arraycopy(from, 0, to, offset, keysCount);
return offset + keysCount;
}
private static int copyKeys(final Object[] from, final Object[] to, final int offset, final int skipIndex)
{
final int keysCount = BTree.getKeyEnd(from);
if (skipIndex > 0)
System.arraycopy(from, 0, to, offset, skipIndex);
if (skipIndex + 1 < keysCount)
System.arraycopy(from, skipIndex + 1, to, offset + skipIndex, keysCount - skipIndex - 1);
return offset + keysCount - 1;
}
private static int copyChildren(final Object[] from, final Object[] to, final int offset)
{
assert !BTree.isLeaf(from);
final int start = BTree.getChildStart(from);
final int childCount = BTree.getChildCount(from);
System.arraycopy(from, start, to, offset, childCount);
return offset + childCount;
}
private static int copyChildren(final Object[] from, final Object[] to, final int offset, final int skipIndex)
{
assert !BTree.isLeaf(from);
final int start = BTree.getChildStart(from);
final int childCount = BTree.getChildCount(from);
if (skipIndex > 0)
System.arraycopy(from, start, to, offset, skipIndex);
if (skipIndex + 1 <= childCount)
System.arraycopy(from, start + skipIndex + 1, to, offset + skipIndex, childCount - skipIndex - 1);
return offset + childCount - 1;
}
private static int copySizeMap(final int[] from, final int[] to, final int offset, final int extra)
{
for (int i = 0; i < from.length; ++i)
to[offset + i] = from[i] + extra;
return offset + from.length;
}
private static Object[] copyIfNeeded(final Object[] node, boolean needCopy)
{
if (!needCopy) return node;
final Object[] copy = new Object[node.length];
System.arraycopy(node, 0, copy, 0, node.length);
if (!BTree.isLeaf(node))
{
final int[] sizeMap = BTree.getSizeMap(node);
final int[] copySizeMap = new int[sizeMap.length];
System.arraycopy(sizeMap, 0, copySizeMap, 0, sizeMap.length);
copy[copy.length - 1] = copySizeMap;
}
return copy;
}
}