public bool RedistributeFromRight(InternalNode rightSibling, byte[] joinKey, byte [] newJoinKey) { int required = _config.InternalSplitIndex - _keyCount; if (rightSibling.KeyCount - required < _config.InternalSplitIndex) { return(false); } // Copy keys and child pointers SetKey(_keyCount, joinKey); CopyKeys(rightSibling._keys, 0, _keyCount + 1, required - 1); //Array.Copy(rightSibling._keys, 0, _keys, _keyCount + 1, required - 1); Array.Copy(rightSibling._childPointers, 0, _childPointers, _keyCount + 1, required); Array.Copy(rightSibling._keys[required - 1], newJoinKey, _config.KeySize); // Shift up remaining keys and child pointers in the right node for (int i = 0; i < rightSibling._keyCount - required; i++) { rightSibling._keys[i] = rightSibling._keys[i + required]; rightSibling._childPointers[i] = rightSibling.ChildPointers[i + required]; } rightSibling._childPointers[rightSibling._keyCount - required] = rightSibling._childPointers[rightSibling.KeyCount]; rightSibling._keyCount -= required; _keyCount += required; return(true); }
/// <summary> /// Attempts to ensure that the minimum size for this node is achieved by transferring entries from the left-hand sibling /// </summary> /// <param name="leftSibling">The left-hand sibling that will provide entries</param> /// <param name="joinKey">The value of the key that is present in the parent node between the pointer to this node and its left sibling</param> /// <param name="newJoinKey">The replacement value for the join key in the parent node</param> /// <returns>True if the node achieves its minimum size by the redistribution process, false otherwise</returns> public bool RedistributeFromLeft(InternalNode leftSibling, byte[] joinKey, byte[] newJoinKey) { int required = _config.InternalSplitIndex - _keyCount; if (leftSibling.KeyCount - required < _config.InternalSplitIndex) { // Can't fulfill requirements with a borrow from left sibling return(false); } int evenOut = (_keyCount + leftSibling._keyCount) / 2 - _keyCount; if (leftSibling.KeyCount - evenOut > _config.InternalSplitIndex) { required = evenOut; } // Make space for new keys and child pointers _childPointers[_keyCount + required] = _childPointers[_keyCount]; for (int i = _keyCount - 1; i >= 0; i--) { _keys[i + required] = _keys[i]; _childPointers[i + required] = _childPointers[i]; } SetKey(required - 1, joinKey); CopyKeys(leftSibling._keys, (leftSibling._keyCount - required) + 1, 0, required - 1); //Array.Copy(leftSibling._keys, (leftSibling._keyCount - required) + 1, _keys, 0, required-1); Array.Copy(leftSibling._childPointers, (leftSibling._keyCount - required) + 1, _childPointers, 0, required); Array.Copy(leftSibling._keys[leftSibling.KeyCount - required], newJoinKey, _config.KeySize); _keyCount += required; leftSibling._keyCount -= required; return(true); }
public INode GetNode(ulong nodeId, BrightstarProfiler profiler) { using (profiler.Step("BPlusTree.GetNode")) { INode ret; if (_modifiedNodes.TryGetValue(nodeId, out ret)) { profiler.Incr("NodeCache Hit"); return(ret); } if (_nodeCache.TryGetValue(nodeId, out ret)) { profiler.Incr("NodeCache Hit"); return(ret); } profiler.Incr("NodeCache Miss"); using (profiler.Step("Load Node")) { var nodePage = _pageStore.Retrieve(nodeId, profiler); var header = BitConverter.ToInt32(nodePage, 0); if (header < 0) { ret = new InternalNode(nodeId, nodePage, ~header, _config); } else { ret = new LeafNode(nodeId, nodePage, header, _config); } _nodeCache.Add(ret); return(ret); } } }
/// <summary> /// Splits this node into two internal nodes, creating a new node for the right-hand (upper) half of the keys /// </summary> /// <param name="txnId"></param> /// <param name="rightNodePage">The new page reserved to receive the newly created internal node</param> /// <param name="splitKey">Receives the value of the key used for the split</param> /// <returns>The new right-hand node</returns> public IInternalNode Split(ulong txnId, IPage rightNodePage, out byte[] splitKey) { #if DEBUG_BTREE _config.BTreeDebug("InternalNode.Split. Id={0}. Structure Before: {1}", PageId, Dump()); #endif EnsureWriteable(txnId); var splitIndex = _config.InternalSplitIndex; splitKey = GetKey(splitIndex); rightNodePage.SetData(_page.Data, KeyOffset(splitIndex + 1), KeyOffset(0), (KeyCount - (splitIndex + 1)) * _config.KeySize); var pointerCopyStart = PointerOffset(splitIndex + 1); var pointerCopyLength = (KeyCount - splitIndex) * 8; rightNodePage.SetData(_page.Data, pointerCopyStart, PointerOffset(0), pointerCopyLength); var rightNodeKeyCount = KeyCount - (splitIndex + 1); rightNodePage.SetData(BitConverter.GetBytes(~rightNodeKeyCount), 0, 0, 4); var rightNode = new InternalNode(rightNodePage, rightNodeKeyCount, _config); KeyCount = splitIndex; #if DEBUG_BTREE _config.BTreeDebug("InternalNode.Split. Structure After: Id={0} {1}\nRight Node After: Id={2} {3}", PageId, Dump(), rightNode.PageId, rightNode.Dump()); #endif return(rightNode); }
private IEnumerable <KeyValuePair <byte[], ulong> > MakeInternalNodes(ulong txnId, IEnumerable <KeyValuePair <byte[], ulong> > children, BrightstarProfiler profiler) { var enumerator = children.GetEnumerator(); var childList = enumerator.Next(_internalBranchFactor).ToList(); if (childList.Count == 1) { yield return(childList[0]); yield break; } byte[] prevNodeKey = childList[0].Key; InternalNode prevNode = MakeInternalNode(childList); childList = enumerator.Next(_internalBranchFactor).ToList(); while (childList.Count > 0) { InternalNode nextNode = MakeInternalNode(childList); var nextNodeKey = childList[0].Key; if (nextNode.NeedJoin) { nextNodeKey = new byte[_config.KeySize]; nextNode.RedistributeFromLeft(prevNode, childList[0].Key, nextNodeKey); } yield return(WriteNode(txnId, prevNode, prevNodeKey, profiler)); prevNode = nextNode; prevNodeKey = nextNodeKey; childList = enumerator.Next(_internalBranchFactor).ToList(); } /* * if (enumerator.MoveNext()) * { * firstChild = enumerator.Current; * InternalNode nextNode; * var nextNodeKey = firstChild.Key; * if (enumerator.MoveNext()) * { * nextNode = MakeInternalNode(firstChild, enumerator, _internalBranchFactor); * } * else * { * nextNode = MakeInternalNode(firstChild); * } * if (nextNode.NeedJoin) * { * nextNodeKey = new byte[_config.KeySize]; * nextNode.RedistributeFromLeft(prevNode, firstChild.Key, nextNodeKey); * } * yield return WriteNode(txnId, prevNode, prevNodeKey, profiler); * prevNode = nextNode; * prevNodeKey = nextNodeKey; * } */ yield return(WriteNode(txnId, prevNode, prevNodeKey, profiler)); }
public void TestRightShiftFrom() { var pageStore = new MemoryPageStore(1024); var config = new BPlusTreeConfiguration( pageStore, 8, 8, 1024); var n = new InternalNode(pageStore.Create(0), BitConverter.GetBytes(50ul), 2,3, config); n.Insert(1, BitConverter.GetBytes(100ul), 4); Assert.AreEqual(50ul, BitConverter.ToUInt64(n.LeftmostKey, 0)); Assert.AreEqual(100ul, BitConverter.ToUInt64(n.RightmostKey, 0)); }
public void Insert(ulong txnId, byte[] key, byte[] value, bool overwrite = false, BrightstarProfiler profiler = null) { using (profiler.Step("BPlusTree.Insert")) { bool splitRoot; INode rightNode; byte[] rootSplitKey; Insert(txnId, _root, key, value, out splitRoot, out rightNode, out rootSplitKey, overwrite, profiler); if (splitRoot) { var newRoot = new InternalNode(_pageStore.Create(), rootSplitKey, _root.PageId, rightNode.PageId, _config); MarkDirty(txnId, _root, profiler); _modifiedNodes[newRoot.PageId] = newRoot; _root = newRoot; } } }
/// <summary> /// Attempts to ensure that the minimum size for this node is achieved by transferring entries from the left-hand sibling /// </summary> /// <param name="txnId"></param> /// <param name="leftSibling">The left-hand sibling that will provide entries</param> /// <param name="joinKey">The value of the key that is present in the parent node between the pointer to this node and its left sibling</param> /// <param name="newJoinKey">The replacement value for the join key in the parent node</param> /// <returns>True if the node achieves its minimum size by the redistribution process, false otherwise</returns> public bool RedistributeFromLeft(ulong txnId, IInternalNode leftSibling, byte[] joinKey, byte[] newJoinKey) { InternalNode left = leftSibling as InternalNode; if (left == null) { throw new ArgumentException("Expected a InternalNode as left sibling", "leftSibling"); } int required = _config.InternalSplitIndex - KeyCount; if (leftSibling.KeyCount - required < _config.InternalSplitIndex) { // Can't fulfill requirements with a borrow from left sibling return(false); } int evenOut = (KeyCount + left.KeyCount) / 2 - KeyCount; if (leftSibling.KeyCount - evenOut > _config.InternalSplitIndex) { required = evenOut; } EnsureWriteable(txnId); left.EnsureWriteable(txnId); // Make space for new keys and child pointers RightShift(required); _page.SetData(joinKey, 0, KeyOffset(required - 1), _config.KeySize); _page.SetData(left.GetData(), KeyOffset(left.KeyCount - (required - 1)), KeyOffset(0), (required - 1) * _config.KeySize); _page.SetData(left.GetData(), PointerOffset(left.KeyCount - (required - 1)), PointerOffset(0), (required) * 8); Array.Copy(left.GetData(), KeyOffset(left.KeyCount - required), newJoinKey, 0, _config.KeySize); KeyCount += required; left.KeyCount -= required; return(true); }
/// <summary> /// Splits this node into two internal nodes, creating a new node for the right-hand (upper) half of the keys /// </summary> /// <param name="rightNodeId">The ID of the page reserved to receive the newly created internal node</param> /// <param name="splitKey">Receives the value of the key used for the split</param> /// <returns>The new right-hand node</returns> public InternalNode Split(ulong rightNodeId, out byte[] splitKey) { var rightNode = new InternalNode(rightNodeId, _config); var splitIndex = _config.InternalSplitIndex; splitKey = _keys[splitIndex]; Array.Copy(_keys, splitIndex + 1, rightNode._keys, 0, _keyCount - (splitIndex + 1)); int pointerCopyStart = splitIndex + 1; int pointerCopyLength = (_keyCount - splitIndex); Array.Copy(_childPointers, pointerCopyStart, rightNode._childPointers, 0, pointerCopyLength); rightNode._keyCount = _keyCount - (splitIndex + 1); _keyCount = splitIndex; return(rightNode); }
public INode GetNode(ulong nodeId, BrightstarProfiler profiler) { using (profiler.Step("BPlusTree.GetNode")) { INode ret; if (_modifiedNodes.TryGetValue(nodeId, out ret)) { profiler.Incr("NodeCache Hit"); return ret; } if (_nodeCache.TryGetValue(nodeId, out ret)) { profiler.Incr("NodeCache Hit"); return ret; } profiler.Incr("NodeCache Miss"); using (profiler.Step("Load Node")) { var nodePage = _pageStore.Retrieve(nodeId, profiler); var header = BitConverter.ToInt32(nodePage, 0); if (header < 0) { ret = new InternalNode(nodeId, nodePage, ~header, _config); } else { ret = new LeafNode(nodeId, nodePage, header, _config); } _nodeCache.Add(ret); return ret; } } }
private void Delete(ulong txnId, InternalNode parentInternalNode, byte[] key, out bool underAllocation, BrightstarProfiler profiler) { if (parentInternalNode.RightmostKey == null) { throw new ArgumentException("Parent node right key is null"); } var childNodeId = parentInternalNode.GetChildNodeId(key); var childNode = GetNode(childNodeId, profiler); if (childNode is LeafNode) { var childLeafNode = childNode as LeafNode; // Delete the key and mark the node as updated. This may update the child node id childLeafNode.Delete(key); MarkDirty(txnId, childLeafNode, profiler); if (childLeafNode.PageId != childNodeId) { parentInternalNode.UpdateChildPointer(childNodeId, childLeafNode.PageId); childNodeId = childLeafNode.PageId; } if (childLeafNode.NeedsJoin) { ulong leftSiblingId, rightSiblingId; LeafNode leftSibling = null, rightSibling = null; bool hasLeftSibling = parentInternalNode.GetLeftSibling(childNodeId, out leftSiblingId); if (hasLeftSibling) { leftSibling = GetNode(leftSiblingId, profiler) as LeafNode; if (childLeafNode.RedistributeFromLeft(leftSibling)) { parentInternalNode.SetLeftKey(childLeafNode.PageId, childLeafNode.LeftmostKey); MarkDirty(txnId, parentInternalNode, profiler); MarkDirty(txnId, leftSibling, null); parentInternalNode.UpdateChildPointer(leftSiblingId, leftSibling.PageId); underAllocation = false; return; } } bool hasRightSibling = parentInternalNode.GetRightSiblingId(childNodeId, out rightSiblingId); if (hasRightSibling) { rightSibling = GetNode(rightSiblingId, profiler) as LeafNode; #if DEBUG if (rightSibling.LeftmostKey.Compare(childLeafNode.RightmostKey) <= 0) { throw new Exception("Right-hand sibling has a left key lower than this nodes right key."); } #endif if (childLeafNode.RedistributeFromRight(rightSibling)) { MarkDirty(txnId, rightSibling, profiler); parentInternalNode.UpdateChildPointer(rightSiblingId, rightSibling.PageId); parentInternalNode.SetLeftKey(rightSibling.PageId, rightSibling.LeftmostKey); MarkDirty(txnId, parentInternalNode, profiler); underAllocation = false; return; } } if (hasLeftSibling && childLeafNode.Merge(leftSibling)) { parentInternalNode.RemoveChildPointer(leftSiblingId); parentInternalNode.SetLeftKey(childLeafNode.PageId, childLeafNode.LeftmostKey); MarkDirty(txnId, parentInternalNode, profiler); underAllocation = parentInternalNode.NeedJoin; return; } if (hasRightSibling && childLeafNode.Merge(rightSibling)) { byte[] nodeKey = parentInternalNode.RemoveChildPointer(rightSiblingId); if (nodeKey == null) { // We merged in the right-most node, so we need to generate a key nodeKey = new byte[_config.KeySize]; Array.Copy(rightSibling.RightmostKey, nodeKey, _config.KeySize); ByteArrayHelper.Increment(nodeKey); } parentInternalNode.SetKey(childLeafNode.PageId, nodeKey); MarkDirty(txnId, parentInternalNode, profiler); underAllocation = parentInternalNode.NeedJoin; return; } } underAllocation = false; return; } if (childNode is InternalNode) { bool childUnderAllocated; var childInternalNode = childNode as InternalNode; Delete(txnId, childInternalNode, key, out childUnderAllocated, profiler); if (childInternalNode.PageId != childNodeId) { // Child node page changed parentInternalNode.UpdateChildPointer(childNodeId, childInternalNode.PageId); MarkDirty(txnId, parentInternalNode, profiler); childNodeId = childInternalNode.PageId; } if (childUnderAllocated) { InternalNode leftSibling = null, rightSibling = null; ulong leftSiblingId, rightSiblingId; // Redistribute values from left-hand sibling bool hasLeftSibling = parentInternalNode.GetLeftSibling(childNodeId, out leftSiblingId); if (hasLeftSibling) { leftSibling = GetNode(leftSiblingId, profiler) as InternalNode; byte[] joinKey = parentInternalNode.GetKey(leftSiblingId); var newJoinKey = new byte[_config.KeySize]; if (childInternalNode.RedistributeFromLeft(leftSibling, joinKey, newJoinKey)) { MarkDirty(txnId, leftSibling, profiler); parentInternalNode.UpdateChildPointer(leftSiblingId, leftSibling.PageId); parentInternalNode.SetKey(leftSibling.PageId, newJoinKey); MarkDirty(txnId, parentInternalNode, profiler); underAllocation = false; return; } } // Redistribute values from right-hand sibling bool hasRightSibling = parentInternalNode.GetRightSiblingId(childNodeId, out rightSiblingId); if (hasRightSibling) { rightSibling = GetNode(rightSiblingId, profiler) as InternalNode; byte[] joinKey = parentInternalNode.GetKey(childInternalNode.PageId); byte[] newJoinKey = new byte[_config.KeySize]; if (childInternalNode.RedistributeFromRight(rightSibling, joinKey, newJoinKey)) { MarkDirty(txnId, rightSibling, profiler); parentInternalNode.UpdateChildPointer(rightSiblingId, rightSibling.PageId); // parentInternalNode.SetKey(rightSibling.PageId, newJoinKey); -- think this is wrong should be: parentInternalNode.SetKey(childInternalNode.PageId, newJoinKey); MarkDirty(txnId, parentInternalNode, profiler); underAllocation = false; return; } } // Merge with left-hand sibling if (hasLeftSibling) { // Attempt to merge child node into its left sibling var joinKey = parentInternalNode.GetKey(leftSibling.PageId); var mergedNodeKey = parentInternalNode.GetKey(childInternalNode.PageId); if (mergedNodeKey == null) { mergedNodeKey = new byte[_config.KeySize]; Array.Copy(childInternalNode.RightmostKey, mergedNodeKey, _config.KeySize); ByteArrayHelper.Increment(mergedNodeKey); } if (leftSibling.Merge(childInternalNode, joinKey)) { MarkDirty(txnId, leftSibling, profiler); if (leftSibling.PageId != leftSiblingId) { // We have a new page id (append-only stores will do this) parentInternalNode.UpdateChildPointer(leftSiblingId, leftSibling.PageId); } parentInternalNode.RemoveChildPointer(childInternalNode.PageId); parentInternalNode.SetKey(leftSibling.PageId, mergedNodeKey); MarkDirty(txnId, parentInternalNode, profiler); underAllocation = parentInternalNode.NeedJoin; return; } } // Merge with right-hand sibling if (hasRightSibling) { // Attempt to merge right sibling into child node var joinKey = parentInternalNode.GetKey(childNodeId); if (childInternalNode.Merge(rightSibling, joinKey)) { MarkDirty(txnId, childInternalNode, profiler); var nodeKey = parentInternalNode.RemoveChildPointer(rightSiblingId); if (childInternalNode.PageId != childNodeId) { // We have a new page id for the child node (append-only stores will do this) parentInternalNode.UpdateChildPointer(childNodeId, childInternalNode.PageId); } if (nodeKey == null) { // We merged in the right-most node, so we need to generate a key nodeKey = new byte[_config.KeySize]; Array.Copy(rightSibling.RightmostKey, nodeKey, _config.KeySize); ByteArrayHelper.Increment(nodeKey); } parentInternalNode.SetKey(childInternalNode.PageId, nodeKey); MarkDirty(txnId, parentInternalNode, profiler); underAllocation = parentInternalNode.NeedJoin; return; } } throw new NotImplementedException( "Not yet implemented handling for internal node becoming under allocated"); } underAllocation = false; } else { throw new BrightstarInternalException(String.Format("Unrecognised B+ Tree node class : {0}", childNode.GetType())); } }
/// <summary> /// Splits this node into two internal nodes, creating a new node for the right-hand (upper) half of the keys /// </summary> /// <param name="txnId"></param> /// <param name="rightNodePage">The new page reserved to receive the newly created internal node</param> /// <param name="splitKey">Receives the value of the key used for the split</param> /// <returns>The new right-hand node</returns> public IInternalNode Split(ulong txnId, IPage rightNodePage, out byte[] splitKey) { #if DEBUG_BTREE _config.BTreeDebug("InternalNode.Split. Id={0}. Structure Before: {1}", PageId, Dump()); #endif EnsureWriteable(txnId); var splitIndex = _config.InternalSplitIndex; splitKey = GetKey(splitIndex); rightNodePage.SetData(_page.Data, KeyOffset(splitIndex + 1), KeyOffset(0), (KeyCount - (splitIndex + 1))*_config.KeySize); var pointerCopyStart = PointerOffset(splitIndex + 1); var pointerCopyLength = (KeyCount - splitIndex)*8; rightNodePage.SetData(_page.Data, pointerCopyStart, PointerOffset(0), pointerCopyLength); var rightNodeKeyCount = KeyCount - (splitIndex + 1); rightNodePage.SetData(BitConverter.GetBytes(~rightNodeKeyCount), 0, 0, 4); var rightNode = new InternalNode(rightNodePage, rightNodeKeyCount, _config); KeyCount = splitIndex; #if DEBUG_BTREE _config.BTreeDebug("InternalNode.Split. Structure After: Id={0} {1}\nRight Node After: Id={2} {3}", PageId, Dump(), rightNode.PageId, rightNode.Dump()); #endif return rightNode; }
/// <summary> /// Splits this node into two internal nodes, creating a new node for the right-hand (upper) half of the keys /// </summary> /// <param name="rightNodeId">The ID of the page reserved to receive the newly created internal node</param> /// <param name="splitKey">Receives the value of the key used for the split</param> /// <returns>The new right-hand node</returns> public InternalNode Split(ulong rightNodeId, out byte[] splitKey) { var rightNode = new InternalNode(rightNodeId, _config); var splitIndex = _config.InternalSplitIndex; splitKey = _keys[splitIndex]; Array.Copy(_keys, splitIndex + 1, rightNode._keys, 0, _keyCount - (splitIndex + 1)); int pointerCopyStart = splitIndex + 1; int pointerCopyLength = (_keyCount - splitIndex); Array.Copy(_childPointers, pointerCopyStart, rightNode._childPointers, 0, pointerCopyLength); rightNode._keyCount = _keyCount - (splitIndex + 1); _keyCount = splitIndex; return rightNode; }
private KeyValuePair <byte[], ulong> WriteNode(ulong txnId, InternalNode node, byte[] lowestLeafKey, BrightstarProfiler profiler) { _pageStore.Write(txnId, node.PageId, node.GetData(), profiler: profiler); return(new KeyValuePair <byte[], ulong>(lowestLeafKey, node.PageId)); }
private KeyValuePair<byte[], ulong > WriteNode(ulong txnId, InternalNode node, byte[] lowestLeafKey, BrightstarProfiler profiler) { _pageStore.Write(txnId, node.PageId, node.GetData(), profiler:profiler); return new KeyValuePair<byte[], ulong>(lowestLeafKey, node.PageId); }
/// <summary> /// Attempts to ensure that the minimum size for this node is achieved by transferring entries from the left-hand sibling /// </summary> /// <param name="leftSibling">The left-hand sibling that will provide entries</param> /// <param name="joinKey">The value of the key that is present in the parent node between the pointer to this node and its left sibling</param> /// <param name="newJoinKey">The replacement value for the join key in the parent node</param> /// <returns>True if the node achieves its minimum size by the redistribution process, false otherwise</returns> public bool RedistributeFromLeft(InternalNode leftSibling, byte[] joinKey, byte[] newJoinKey) { int required = _config.InternalSplitIndex - _keyCount; if (leftSibling.KeyCount - required < _config.InternalSplitIndex) { // Can't fulfill requirements with a borrow from left sibling return false; } int evenOut = (_keyCount + leftSibling._keyCount) / 2 - _keyCount; if (leftSibling.KeyCount - evenOut > _config.InternalSplitIndex) { required = evenOut; } // Make space for new keys and child pointers _childPointers[_keyCount + required] = _childPointers[_keyCount]; for(int i = _keyCount -1; i >= 0; i-- ) { _keys[i + required] = _keys[i]; _childPointers[i + required] = _childPointers[i]; } SetKey(required-1, joinKey); CopyKeys(leftSibling._keys, (leftSibling._keyCount - required) + 1, 0, required - 1); //Array.Copy(leftSibling._keys, (leftSibling._keyCount - required) + 1, _keys, 0, required-1); Array.Copy(leftSibling._childPointers, (leftSibling._keyCount-required) + 1, _childPointers, 0, required); Array.Copy(leftSibling._keys[leftSibling.KeyCount-required], newJoinKey, _config.KeySize); _keyCount += required; leftSibling._keyCount -= required; return true; }
public bool RedistributeFromRight(InternalNode rightSibling, byte[] joinKey, byte [] newJoinKey) { int required = _config.InternalSplitIndex - _keyCount; if (rightSibling.KeyCount - required < _config.InternalSplitIndex) { return false; } // Copy keys and child pointers SetKey(_keyCount, joinKey); CopyKeys(rightSibling._keys, 0, _keyCount + 1, required - 1); //Array.Copy(rightSibling._keys, 0, _keys, _keyCount + 1, required - 1); Array.Copy(rightSibling._childPointers, 0, _childPointers, _keyCount + 1, required); Array.Copy(rightSibling._keys[required - 1], newJoinKey, _config.KeySize); // Shift up remaining keys and child pointers in the right node for(int i = 0; i < rightSibling._keyCount - required; i++) { rightSibling._keys[i] = rightSibling._keys[i + required]; rightSibling._childPointers[i] = rightSibling.ChildPointers[i + required]; } rightSibling._childPointers[rightSibling._keyCount - required] = rightSibling._childPointers[rightSibling.KeyCount]; rightSibling._keyCount -= required; _keyCount += required; return true; }