/*
 * 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.
/** * 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.
/** * 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; } }